From 0621a7000bca70330d9fe74a9366f63172ca4e45 Mon Sep 17 00:00:00 2001 From: Alexander Emelin Date: Sat, 25 Nov 2023 14:46:15 +0200 Subject: [PATCH] Deploy website - based on b4bd57f3b009e000cc7f8c7776b17398a4888625 --- 404.html | 4 ++-- assets/js/{bbb9e52d.af338c23.js => bbb9e52d.f1f4a6f7.js} | 2 +- .../{runtime~main.c859f665.js => runtime~main.c8f2b590.js} | 2 +- blog.html | 4 ++-- blog/2020/02/10/million-connections-with-centrifugo.html | 4 ++-- blog/2020/10/16/experimenting-with-quic-transport.html | 4 ++-- blog/2020/11/12/scaling-websocket.html | 4 ++-- blog/2021/01/15/centrifuge-intro.html | 4 ++-- blog/2021/08/31/hello-centrifugo-v3.html | 4 ++-- blog/2021/10/18/integrating-with-nodejs.html | 4 ++-- .../integrating-with-django-building-chat-application.html | 4 ++-- blog/2021/12/14/laravel-multi-room-chat-tutorial.html | 4 ++-- blog/2022/07/19/centrifugo-v4-released.html | 4 ++-- blog/2022/07/29/101-way-to-subscribe.html | 4 ++-- blog/2022/12/20/improving-redis-engine-performance.html | 4 ++-- blog/2023/03/31/keycloak-sso-centrifugo.html | 4 ++-- blog/2023/06/29/centrifugo-v5-released.html | 4 ++-- ...ronous-message-streaming-to-centrifugo-with-benthos.html | 4 ++-- blog/2023/08/29/using-centrifugo-in-rabbitx.html | 4 ++-- .../29/discovering-centrifugo-pro-push-notifications.html | 4 ++-- blog/archive.html | 4 ++-- blog/tags.html | 4 ++-- blog/tags/authentication.html | 4 ++-- blog/tags/benthos.html | 4 ++-- blog/tags/centrifuge.html | 4 ++-- blog/tags/centrifugo.html | 4 ++-- blog/tags/django.html | 4 ++-- blog/tags/go.html | 4 ++-- blog/tags/interview.html | 4 ++-- blog/tags/keycloak.html | 4 ++-- blog/tags/laravel.html | 4 ++-- blog/tags/php.html | 4 ++-- blog/tags/pro.html | 4 ++-- blog/tags/proxy.html | 4 ++-- blog/tags/push-notifications.html | 4 ++-- blog/tags/quic.html | 4 ++-- blog/tags/redis.html | 4 ++-- blog/tags/release.html | 4 ++-- blog/tags/sso.html | 4 ++-- blog/tags/tutorial.html | 4 ++-- blog/tags/usecase.html | 4 ++-- blog/tags/websocket.html | 4 ++-- blog/tags/webtransport.html | 4 ++-- components/Highlight.html | 4 ++-- components/logo.html | 4 ++-- components/logos/Badoo.html | 4 ++-- components/logos/Grafana.html | 4 ++-- components/logos/ManyChat.html | 4 ++-- components/logos/OpenWeb.html | 4 ++-- docs/3/attributions.html | 4 ++-- docs/3/ecosystem/centrifuge.html | 4 ++-- docs/3/ecosystem/integrations.html | 4 ++-- docs/3/faq.html | 4 ++-- docs/3/flow_diagrams.html | 4 ++-- docs/3/getting-started/client_api.html | 4 ++-- docs/3/getting-started/design.html | 4 ++-- docs/3/getting-started/highlights.html | 4 ++-- docs/3/getting-started/installation.html | 4 ++-- docs/3/getting-started/integration.html | 4 ++-- docs/3/getting-started/introduction.html | 4 ++-- docs/3/getting-started/migration_v3.html | 4 ++-- docs/3/getting-started/quickstart.html | 4 ++-- docs/3/pro/analytics.html | 4 ++-- docs/3/pro/db_namespaces.html | 4 ++-- docs/3/pro/install_and_run.html | 4 ++-- docs/3/pro/overview.html | 4 ++-- docs/3/pro/performance.html | 4 ++-- docs/3/pro/process_stats.html | 4 ++-- docs/3/pro/singleflight.html | 4 ++-- docs/3/pro/throttling.html | 4 ++-- docs/3/pro/token_revocation.html | 4 ++-- docs/3/pro/tracing.html | 4 ++-- docs/3/pro/user_block.html | 4 ++-- docs/3/pro/user_connections.html | 4 ++-- docs/3/pro/user_status.html | 4 ++-- docs/3/server/admin_web.html | 4 ++-- docs/3/server/authentication.html | 4 ++-- docs/3/server/channels.html | 4 ++-- docs/3/server/codes.html | 4 ++-- docs/3/server/configuration.html | 4 ++-- docs/3/server/console_commands.html | 4 ++-- docs/3/server/engines.html | 4 ++-- docs/3/server/history_and_recovery.html | 4 ++-- docs/3/server/infra_tuning.html | 4 ++-- docs/3/server/load_balancing.html | 4 ++-- docs/3/server/monitoring.html | 4 ++-- docs/3/server/private_channels.html | 4 ++-- docs/3/server/proxy.html | 4 ++-- docs/3/server/server_api.html | 4 ++-- docs/3/server/server_subs.html | 4 ++-- docs/3/server/tls.html | 4 ++-- docs/3/transports/client_protocol.html | 4 ++-- docs/3/transports/client_sdk.html | 4 ++-- docs/3/transports/overview.html | 4 ++-- docs/3/transports/sockjs.html | 4 ++-- docs/3/transports/uni_grpc.html | 4 ++-- docs/3/transports/uni_http_stream.html | 4 ++-- docs/3/transports/uni_sse.html | 4 ++-- docs/3/transports/uni_websocket.html | 4 ++-- docs/3/transports/websocket.html | 4 ++-- docs/4/attributions.html | 4 ++-- docs/4/faq.html | 4 ++-- docs/4/flow_diagrams.html | 4 ++-- docs/4/getting-started/client_api.html | 4 ++-- docs/4/getting-started/community.html | 4 ++-- docs/4/getting-started/design.html | 4 ++-- docs/4/getting-started/ecosystem.html | 4 ++-- docs/4/getting-started/highlights.html | 4 ++-- docs/4/getting-started/installation.html | 4 ++-- docs/4/getting-started/integration.html | 4 ++-- docs/4/getting-started/introduction.html | 4 ++-- docs/4/getting-started/migration_v4.html | 4 ++-- docs/4/getting-started/quickstart.html | 4 ++-- docs/4/pro/analytics.html | 4 ++-- docs/4/pro/capabilities.html | 4 ++-- docs/4/pro/cel_expressions.html | 4 ++-- docs/4/pro/channel_patterns.html | 4 ++-- docs/4/pro/client_message_batching.html | 4 ++-- docs/4/pro/connections.html | 4 ++-- docs/4/pro/install_and_run.html | 4 ++-- docs/4/pro/overview.html | 4 ++-- docs/4/pro/performance.html | 4 ++-- docs/4/pro/process_stats.html | 4 ++-- docs/4/pro/push_notifications.html | 4 ++-- docs/4/pro/singleflight.html | 4 ++-- docs/4/pro/throttling.html | 4 ++-- docs/4/pro/token_revocation.html | 4 ++-- docs/4/pro/tracing.html | 4 ++-- docs/4/pro/user_block.html | 4 ++-- docs/4/pro/user_status.html | 4 ++-- docs/4/server/admin_web.html | 4 ++-- docs/4/server/authentication.html | 4 ++-- docs/4/server/channel_permissions.html | 4 ++-- docs/4/server/channel_token_auth.html | 4 ++-- docs/4/server/channels.html | 4 ++-- docs/4/server/codes.html | 4 ++-- docs/4/server/configuration.html | 4 ++-- docs/4/server/console_commands.html | 4 ++-- docs/4/server/engines.html | 4 ++-- docs/4/server/history_and_recovery.html | 4 ++-- docs/4/server/infra_tuning.html | 4 ++-- docs/4/server/load_balancing.html | 4 ++-- docs/4/server/monitoring.html | 4 ++-- docs/4/server/presence.html | 4 ++-- docs/4/server/proxy.html | 4 ++-- docs/4/server/server_api.html | 4 ++-- docs/4/server/server_subs.html | 4 ++-- docs/4/server/tls.html | 4 ++-- docs/4/transports/client_api.html | 4 ++-- docs/4/transports/client_protocol.html | 4 ++-- docs/4/transports/client_sdk.html | 4 ++-- docs/4/transports/http_stream.html | 4 ++-- docs/4/transports/overview.html | 4 ++-- docs/4/transports/sockjs.html | 4 ++-- docs/4/transports/sse.html | 4 ++-- docs/4/transports/uni_grpc.html | 4 ++-- docs/4/transports/uni_http_stream.html | 4 ++-- docs/4/transports/uni_sse.html | 4 ++-- docs/4/transports/uni_websocket.html | 4 ++-- docs/4/transports/websocket.html | 4 ++-- docs/4/transports/webtransport.html | 4 ++-- docs/attributions.html | 4 ++-- docs/faq.html | 4 ++-- docs/flow_diagrams.html | 4 ++-- docs/getting-started/client_api.html | 4 ++-- docs/getting-started/community.html | 4 ++-- docs/getting-started/comparisons.html | 4 ++-- docs/getting-started/design.html | 4 ++-- docs/getting-started/ecosystem.html | 4 ++-- docs/getting-started/highlights.html | 4 ++-- docs/getting-started/installation.html | 4 ++-- docs/getting-started/integration.html | 4 ++-- docs/getting-started/introduction.html | 4 ++-- docs/getting-started/migration_v4.html | 4 ++-- docs/getting-started/migration_v5.html | 4 ++-- docs/getting-started/quickstart.html | 4 ++-- docs/pro/analytics.html | 4 ++-- docs/pro/capabilities.html | 4 ++-- docs/pro/cel_expressions.html | 4 ++-- docs/pro/channel_patterns.html | 4 ++-- docs/pro/channel_state_events.html | 4 ++-- docs/pro/client_message_batching.html | 4 ++-- docs/pro/connections.html | 4 ++-- docs/pro/distributed_rate_limit.html | 4 ++-- docs/pro/install_and_run.html | 6 +++--- docs/pro/observability_enhancements.html | 4 ++-- docs/pro/overview.html | 4 ++-- docs/pro/performance.html | 4 ++-- docs/pro/process_stats.html | 4 ++-- docs/pro/push_notifications.html | 4 ++-- docs/pro/rate_limiting.html | 4 ++-- docs/pro/singleflight.html | 4 ++-- docs/pro/token_revocation.html | 4 ++-- docs/pro/tracing.html | 4 ++-- docs/pro/user_block.html | 4 ++-- docs/pro/user_status.html | 4 ++-- docs/server/admin_web.html | 4 ++-- docs/server/authentication.html | 4 ++-- docs/server/channel_permissions.html | 4 ++-- docs/server/channel_token_auth.html | 4 ++-- docs/server/channels.html | 4 ++-- docs/server/codes.html | 4 ++-- docs/server/configuration.html | 4 ++-- docs/server/console_commands.html | 4 ++-- docs/server/engines.html | 4 ++-- docs/server/history_and_recovery.html | 4 ++-- docs/server/infra_tuning.html | 4 ++-- docs/server/load_balancing.html | 4 ++-- docs/server/monitoring.html | 4 ++-- docs/server/observability.html | 4 ++-- docs/server/presence.html | 4 ++-- docs/server/proxy.html | 4 ++-- docs/server/proxy_streams.html | 4 ++-- docs/server/server_api.html | 4 ++-- docs/server/server_subs.html | 4 ++-- docs/server/tls.html | 4 ++-- docs/transports/client_api.html | 4 ++-- docs/transports/client_protocol.html | 4 ++-- docs/transports/client_sdk.html | 4 ++-- docs/transports/http_stream.html | 4 ++-- docs/transports/overview.html | 4 ++-- docs/transports/sockjs.html | 4 ++-- docs/transports/sse.html | 4 ++-- docs/transports/uni_client_protocol.html | 4 ++-- docs/transports/uni_grpc.html | 4 ++-- docs/transports/uni_http_stream.html | 4 ++-- docs/transports/uni_sse.html | 4 ++-- docs/transports/uni_websocket.html | 4 ++-- docs/transports/websocket.html | 4 ++-- docs/transports/webtransport.html | 4 ++-- index.html | 4 ++-- license.html | 4 ++-- license_exchange_lemon.html | 4 ++-- search-index.json | 2 +- search.html | 4 ++-- 235 files changed, 468 insertions(+), 468 deletions(-) rename assets/js/{bbb9e52d.af338c23.js => bbb9e52d.f1f4a6f7.js} (95%) rename assets/js/{runtime~main.c859f665.js => runtime~main.c8f2b590.js} (99%) diff --git a/404.html b/404.html index 2eefd61b6..dd5df3677 100644 --- a/404.html +++ b/404.html @@ -16,13 +16,13 @@ - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/assets/js/bbb9e52d.af338c23.js b/assets/js/bbb9e52d.f1f4a6f7.js similarity index 95% rename from assets/js/bbb9e52d.af338c23.js rename to assets/js/bbb9e52d.f1f4a6f7.js index be1eb15e5..a13d3fd47 100644 --- a/assets/js/bbb9e52d.af338c23.js +++ b/assets/js/bbb9e52d.f1f4a6f7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkcentrifugal_dev=self.webpackChunkcentrifugal_dev||[]).push([[9240],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),u=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=u(n),f=a,m=d["".concat(s,".").concat(f)]||d[f]||p[f]||i;return n?r.createElement(m,o(o({ref:t},c),{},{components:n})):r.createElement(m,o({ref:t},c))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var r=n(7462),a=(n(7294),n(3905));const i={id:"install_and_run",title:"Install and run PRO version"},o=void 0,l={unversionedId:"pro/install_and_run",id:"pro/install_and_run",title:"Install and run PRO version",description:"Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.",source:"@site/docs/pro/install_and_run.md",sourceDirName:"pro",slug:"/pro/install_and_run",permalink:"/docs/pro/install_and_run",draft:!1,editUrl:"https://github.com/centrifugal/centrifugal.dev/edit/main/docs/pro/install_and_run.md",tags:[],version:"current",frontMatter:{id:"install_and_run",title:"Install and run PRO version"},sidebar:"Pro",previous:{title:"Centrifugo PRO",permalink:"/docs/pro/overview"},next:{title:"User and channel tracing",permalink:"/docs/pro/tracing"}},s={},u=[{value:"Binary release",id:"binary-release",level:3},{value:"Docker image",id:"docker-image",level:3},{value:"Kubernetes",id:"kubernetes",level:3},{value:"Debian and Ubuntu",id:"debian-and-ubuntu",level:3},{value:"Centos",id:"centos",level:3},{value:"Setting PRO license key",id:"setting-pro-license-key",level:2}],c={toc:u};function p(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{title:"Centrifugo PRO license agreement",type:"caution"},(0,a.kt)("p",{parentName:"admonition"},"Centrifugo PRO is distributed by Centrifugal Labs LTD under ",(0,a.kt)("a",{parentName:"p",href:"/license"},"commercial license")," which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.")),(0,a.kt)("h3",{id:"binary-release"},"Binary release"),(0,a.kt)("p",null,"Centrifugo PRO binary releases ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/centrifugo-pro/releases"},"available on Github"),". Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key ",(0,a.kt)("a",{parentName:"p",href:"#setting-pro-license-key"},"below"),")."),(0,a.kt)("p",null,"If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack ",(0,a.kt)("inlineCode",{parentName:"p"},"centrifugo")," binary to your current working directory:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-shell"},"curl -sSLf https://centrifugal.dev/install_pro.sh | sh\n")),(0,a.kt)("h3",{id:"docker-image"},"Docker image"),(0,a.kt)("p",null,"Centrifugo PRO uses a different image from OSS version \u2013 ",(0,a.kt)("a",{parentName:"p",href:"https://hub.docker.com/repository/docker/centrifugo/centrifugo-pro"},"centrifugo/centrifugo-pro"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v5.1.1 centrifugo -c config.json\n")),(0,a.kt)("h3",{id:"kubernetes"},"Kubernetes"),(0,a.kt)("p",null,"You can use our ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/helm-charts"},"official Helm chart")," but make sure you changed Docker image to use PRO version and point to the correct image tag:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml",metastring:'title="values.yaml"',title:'"values.yaml"'},"...\nimage:\n registry: docker.io\n repository: centrifugo/centrifugo-pro\n tag: v5.1.1\n")),(0,a.kt)("h3",{id:"debian-and-ubuntu"},"Debian and Ubuntu"),(0,a.kt)("p",null,"DEB package ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/centrifugo-pro/releases"},"available in release assets"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.1/centrifugo-pro_5.1.1_amd64.deb\nsudo dpkg -i centrifugo-pro_5.1.1_amd64.deb\n")),(0,a.kt)("h3",{id:"centos"},"Centos"),(0,a.kt)("p",null,"RPM package ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/centrifugo-pro/releases"},"available in release assets"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.1/centrifugo-pro-5.1.1.x86_64.rpm\nsudo yum install centrifugo-pro-5.1.1.x86_64.rpm\n")),(0,a.kt)("h2",{id:"setting-pro-license-key"},"Setting PRO license key"),(0,a.kt)("p",null,"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits."),(0,a.kt)("p",null,"Once you have installed a PRO version and have a license key you can set it in configuration over ",(0,a.kt)("inlineCode",{parentName:"p"},"license")," field, or pass over environment variables as ",(0,a.kt)("inlineCode",{parentName:"p"},"CENTRIFUGO_LICENSE"),". Like this:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-json",metastring:'title="config.json"',title:'"config.json"'},'{\n ...\n "license": ""\n}\n')),(0,a.kt)("admonition",{type:"tip"},(0,a.kt)("p",{parentName:"admonition"},"If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.")))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkcentrifugal_dev=self.webpackChunkcentrifugal_dev||[]).push([[9240],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),u=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=u(n),f=a,m=d["".concat(s,".").concat(f)]||d[f]||p[f]||i;return n?r.createElement(m,o(o({ref:t},c),{},{components:n})):r.createElement(m,o({ref:t},c))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var r=n(7462),a=(n(7294),n(3905));const i={id:"install_and_run",title:"Install and run PRO version"},o=void 0,l={unversionedId:"pro/install_and_run",id:"pro/install_and_run",title:"Install and run PRO version",description:"Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.",source:"@site/docs/pro/install_and_run.md",sourceDirName:"pro",slug:"/pro/install_and_run",permalink:"/docs/pro/install_and_run",draft:!1,editUrl:"https://github.com/centrifugal/centrifugal.dev/edit/main/docs/pro/install_and_run.md",tags:[],version:"current",frontMatter:{id:"install_and_run",title:"Install and run PRO version"},sidebar:"Pro",previous:{title:"Centrifugo PRO",permalink:"/docs/pro/overview"},next:{title:"User and channel tracing",permalink:"/docs/pro/tracing"}},s={},u=[{value:"Binary release",id:"binary-release",level:3},{value:"Docker image",id:"docker-image",level:3},{value:"Kubernetes",id:"kubernetes",level:3},{value:"Debian and Ubuntu",id:"debian-and-ubuntu",level:3},{value:"Centos",id:"centos",level:3},{value:"Setting PRO license key",id:"setting-pro-license-key",level:2}],c={toc:u};function p(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{title:"Centrifugo PRO license agreement",type:"caution"},(0,a.kt)("p",{parentName:"admonition"},"Centrifugo PRO is distributed by Centrifugal Labs LTD under ",(0,a.kt)("a",{parentName:"p",href:"/license"},"commercial license")," which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.")),(0,a.kt)("h3",{id:"binary-release"},"Binary release"),(0,a.kt)("p",null,"Centrifugo PRO binary releases ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/centrifugo-pro/releases"},"available on Github"),". Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key ",(0,a.kt)("a",{parentName:"p",href:"#setting-pro-license-key"},"below"),")."),(0,a.kt)("p",null,"If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack ",(0,a.kt)("inlineCode",{parentName:"p"},"centrifugo")," binary to your current working directory:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-shell"},"curl -sSLf https://centrifugal.dev/install_pro.sh | sh\n")),(0,a.kt)("h3",{id:"docker-image"},"Docker image"),(0,a.kt)("p",null,"Centrifugo PRO uses a different image from OSS version \u2013 ",(0,a.kt)("a",{parentName:"p",href:"https://hub.docker.com/repository/docker/centrifugo/centrifugo-pro"},"centrifugo/centrifugo-pro"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v5.1.2 centrifugo -c config.json\n")),(0,a.kt)("h3",{id:"kubernetes"},"Kubernetes"),(0,a.kt)("p",null,"You can use our ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/helm-charts"},"official Helm chart")," but make sure you changed Docker image to use PRO version and point to the correct image tag:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml",metastring:'title="values.yaml"',title:'"values.yaml"'},"...\nimage:\n registry: docker.io\n repository: centrifugo/centrifugo-pro\n tag: v5.1.2\n")),(0,a.kt)("h3",{id:"debian-and-ubuntu"},"Debian and Ubuntu"),(0,a.kt)("p",null,"DEB package ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/centrifugo-pro/releases"},"available in release assets"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.2/centrifugo-pro_5.1.2_amd64.deb\nsudo dpkg -i centrifugo-pro_5.1.2_amd64.deb\n")),(0,a.kt)("h3",{id:"centos"},"Centos"),(0,a.kt)("p",null,"RPM package ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/centrifugal/centrifugo-pro/releases"},"available in release assets"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.2/centrifugo-pro-5.1.2.x86_64.rpm\nsudo yum install centrifugo-pro-5.1.2.x86_64.rpm\n")),(0,a.kt)("h2",{id:"setting-pro-license-key"},"Setting PRO license key"),(0,a.kt)("p",null,"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits."),(0,a.kt)("p",null,"Once you have installed a PRO version and have a license key you can set it in configuration over ",(0,a.kt)("inlineCode",{parentName:"p"},"license")," field, or pass over environment variables as ",(0,a.kt)("inlineCode",{parentName:"p"},"CENTRIFUGO_LICENSE"),". Like this:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-json",metastring:'title="config.json"',title:'"config.json"'},'{\n ...\n "license": ""\n}\n')),(0,a.kt)("admonition",{type:"tip"},(0,a.kt)("p",{parentName:"admonition"},"If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.c859f665.js b/assets/js/runtime~main.c8f2b590.js similarity index 99% rename from assets/js/runtime~main.c859f665.js rename to assets/js/runtime~main.c8f2b590.js index 6339c1bfc..4c204a35f 100644 --- a/assets/js/runtime~main.c859f665.js +++ b/assets/js/runtime~main.c8f2b590.js @@ -1 +1 @@ -(()=>{"use strict";var e,d,f,a,c,b={},t={};function r(e){var d=t[e];if(void 0!==d)return d.exports;var f=t[e]={exports:{}};return b[e].call(f.exports,f,f.exports,r),f.exports}r.m=b,e=[],r.O=(d,f,a,c)=>{if(!f){var b=1/0;for(i=0;i=c)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,c0&&e[i-1][2]>c;i--)e[i]=e[i-1];e[i]=[f,a,c]},r.n=e=>{var d=e&&e.__esModule?()=>e.default:()=>e;return r.d(d,{a:d}),d},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var c=Object.create(null);r.r(c);var b={};d=d||[null,f({}),f([]),f(f)];for(var t=2&a&&e;"object"==typeof t&&!~d.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((d=>b[d]=()=>e[d]));return b.default=()=>e,r.d(c,b),c},r.d=(e,d)=>{for(var f in d)r.o(d,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:d[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((d,f)=>(r.f[f](e,d),d)),[])),r.u=e=>"assets/js/"+({8:"23855fe2",53:"935f2afb",70:"dc4f2258",102:"52bba951",152:"54f44165",205:"83d480e9",216:"3630fee7",347:"54aee988",408:"b325d9c4",410:"84a9b932",430:"c9a3329e",477:"84b8e2b1",509:"629b5641",532:"d1cb7448",533:"b2b675dd",562:"a2d1b113",624:"ab8e6500",628:"0a4443e6",630:"5ffc8930",674:"bd17143e",683:"40537b69",811:"893c1918",895:"4268d52f",902:"d4ca9753",926:"70aa60b8",992:"0fe76b3c",1002:"fe6343fd",1028:"79ee6175",1151:"9475880e",1152:"0136d6f0",1178:"f90eb0d6",1196:"6e37598f",1313:"2114e513",1385:"3a890c2d",1420:"5fe24874",1428:"4ab79476",1439:"b301b932",1477:"b2f554cd",1530:"a728857c",1616:"0d0ff016",1633:"1fadf4c6",1658:"07703c67",1695:"d2c1944d",1713:"a7023ddc",1714:"45dfef24",1752:"a05caef7",1888:"d4dfc5db",1977:"9db3c45b",1983:"d6627831",1994:"f1b7a7af",2005:"3dfd29d6",2026:"21e8a749",2040:"7a7ba156",2089:"945c690d",2128:"be4c395a",2157:"dd818855",2165:"6b2be476",2203:"46627d28",2278:"18793598",2291:"29839967",2292:"3b028b51",2309:"ba0d3b30",2321:"0a1a814f",2352:"6e07cb60",2353:"9ff4038f",2413:"3bb37b67",2438:"bfbfeea3",2442:"ee88d6e4",2469:"d1c7a4f7",2479:"ed785809",2535:"814f3328",2540:"211f1d7a",2569:"c3677326",2578:"71fc0044",2605:"6e81f787",2624:"03e522d9",2635:"7bd30152",2637:"6ef9986d",2639:"24f2bde3",2735:"ff64321a",2757:"2b147458",2853:"43c444d2",2865:"ad7c169e",2901:"a4ddbaa1",2906:"60271c2c",2918:"332362b2",2925:"555afbe5",2983:"3529cd5b",3001:"bbd14fff",3039:"fbd7a87c",3073:"b2eaf182",3085:"1f391b9e",3089:"a6aa9e1f",3165:"cac93e67",3190:"4ebb2955",3205:"21c2d27e",3255:"06f11616",3321:"6aa961d8",3343:"25c94216",3352:"69d81c34",3461:"7df4dfbf",3468:"f26176d2",3556:"984c0c66",3581:"2dbf7ee0",3608:"9e4087bc",3655:"694566b3",3672:"1248e41e",3676:"c0434fb9",3711:"56e32e60",3784:"041d543b",3812:"41068141",3895:"4f64b982",3981:"1d3c9151",4013:"01a85c17",4016:"d9829201",4020:"e8314be4",4035:"2b8ad662",4073:"3e15fc9c",4085:"230ca58d",4114:"631e3db1",4131:"238ce909",4156:"64e125c9",4160:"3c51ccb2",4225:"633b2ed2",4282:"e6b6a8f8",4295:"237c01c3",4435:"a82fa8b7",4491:"e6afaed9",4566:"b3216779",4586:"c98fa109",4633:"2f70c421",4655:"776d934d",4691:"0a7ca2d6",4787:"1117f49a",4841:"0312644c",4864:"ee10dcb9",4890:"b4b43a34",4901:"8a978eb4",4964:"ea108d2f",4968:"2eb9c429",5028:"94d5cf4c",5048:"0eae5577",5069:"192a8b1e",5074:"77e23114",5107:"936398dc",5146:"ffdd667d",5148:"679046a6",5180:"d016d150",5214:"c318ab3c",5270:"13f6d888",5304:"30b3ad4a",5312:"fd3209d2",5343:"1335c7a1",5358:"3c4ec49c",5391:"369bd8f8",5407:"56231886",5421:"6eaeadff",5432:"16c9b55b",5484:"ee78c395",5507:"9179a2e2",5552:"6c37c4d1",5625:"39d4d18a",5736:"89734ed6",5755:"a564e6ff",5758:"85196f1f",5847:"c1817076",5853:"aa73fb9a",5859:"369aea06",5861:"9c1ee1d6",5863:"f178572b",5878:"d835c886",5901:"d2fe6fea",5990:"32e1c903",6e3:"84b1c6a7",6050:"81e12894",6103:"ccc49370",6253:"3f0e28d9",6274:"a41a0a70",6295:"ecd5d374",6386:"f346273e",6447:"cce51cf2",6484:"4bd4488a",6515:"fcb790ab",6533:"1cd70467",6562:"f762c5da",6567:"a74df3cd",6685:"db2f115c",6686:"267fbc4e",6705:"fe52e117",6709:"0295581e",6720:"6574fcee",6738:"861598a7",6791:"2daf4703",6867:"93f9db65",6877:"91c5cac2",6889:"aacb0ae1",6953:"b4f0bebf",6979:"5f78e650",6990:"0dc36dc4",7040:"ab6f12ff",7049:"c2f60b05",7054:"9dd8a0d2",7092:"b05011d9",7115:"2a42cb18",7140:"e19d40c8",7262:"e66faea1",7318:"f2e9cf2f",7330:"b5547432",7339:"3270d7e8",7417:"18b1e3cf",7422:"11a20880",7423:"58246c43",7438:"9c021584",7445:"a8fc9e46",7459:"05ba4f60",7482:"66eb7538",7566:"8e068dda",7572:"6fbe284c",7659:"fc3deafd",7660:"f4f2dadf",7670:"91116fee",7717:"01eca2db",7725:"b9cceeee",7765:"fe91fc6f",7810:"19e7756f",7815:"2bf25d27",7824:"c5dc0dc4",7918:"17896441",7920:"1a4e3797",7965:"4a4109ec",8007:"5706869d",8072:"31c58a66",8238:"4fc58f03",8246:"0d57d15e",8356:"04ac3258",8375:"5de4a79c",8499:"b479c509",8523:"f2f7592a",8589:"7672fb2a",8599:"91fdfcd5",8610:"6875c492",8648:"8e9fe0eb",8650:"cadfeb4f",8655:"c8380abd",8665:"9b9e219e",8693:"b0ea8d09",8702:"73c943f6",8791:"e257283f",8896:"06f9ead7",8951:"09382599",8982:"5934e2d7",8983:"79276c30",9027:"9b70d0cc",9040:"02080b57",9054:"b89c2c0a",9093:"386a3726",9109:"a1538072",9201:"4efbf0bc",9217:"fd93cfee",9226:"2acc7b89",9235:"e7893f84",9240:"bbb9e52d",9247:"92b58ac1",9309:"250ad80d",9334:"9c87bba9",9347:"b1f4df52",9362:"8db697a0",9443:"155d95c4",9474:"b6f2a3eb",9476:"498554e3",9514:"1be78505",9523:"2391cf3d",9565:"fd1fdc14",9571:"195b633a",9602:"b62a3811",9604:"49012ebf",9620:"58b29436",9630:"e9cbd346",9668:"267a22d2",9727:"1d4d4d48",9804:"209857dd",9878:"7747d83f",9925:"73e61bcc",9934:"1d223b96"}[e]||e)+"."+{8:"804364f5",53:"2e89eb68",70:"bf0ff083",102:"93002144",152:"f1caf464",205:"dbbb85ff",216:"e1c0d285",347:"10b323a0",408:"4908659c",410:"e42e28e9",430:"04b511ca",477:"e3670801",509:"f849dc03",532:"3d39d0ab",533:"4b6d240d",562:"92a1c228",624:"a4014845",628:"7afbef61",630:"023bfc41",674:"4acc788a",683:"b722507b",811:"85adbaa4",817:"e3708d5c",895:"ac5ed8f2",902:"cf1accac",926:"e14676d0",992:"b393c2fa",1002:"c258a0fa",1028:"3edff402",1151:"9370d5d3",1152:"e7c2d467",1178:"a143d2f6",1196:"4aeaa175",1313:"cfbb3155",1385:"19eebd07",1420:"5fbe60e3",1428:"26fd723c",1439:"a7ca21f9",1477:"14f98641",1530:"46748f4a",1616:"69b25479",1633:"fcb34adb",1658:"b649e60e",1695:"05c586cf",1713:"2b88788e",1714:"14e31b78",1752:"85ab1537",1888:"4b233a1a",1977:"0e9d305d",1983:"02fc1d4c",1994:"86a6dcf7",2005:"0f47dfcf",2026:"83a1763a",2040:"114968c1",2089:"9cedea09",2128:"18ab1997",2157:"230261c6",2165:"50633fcb",2203:"e8bbd4e5",2278:"48a18137",2291:"3dff63e3",2292:"ec013974",2309:"5d871ebf",2321:"6a30be9a",2352:"e5136f35",2353:"51353876",2413:"15ebd57c",2438:"b50f04b6",2442:"5eb64251",2469:"a70e20a3",2479:"2851689b",2535:"08004b39",2540:"e3490c14",2569:"4122d995",2578:"12b5b5e8",2605:"3cfba636",2624:"f3d09f74",2635:"90e2e29a",2637:"3ce1c24c",2639:"f0664aef",2735:"f6873296",2757:"9e6d649b",2853:"b556afc4",2865:"508e57c5",2901:"75d5737c",2906:"f8fdab63",2918:"068f4c18",2925:"a44a225e",2983:"95bf0169",3001:"cb864c44",3039:"0257f6ba",3073:"de83998b",3085:"2fc22f8b",3089:"2df4caa2",3165:"92aba545",3190:"c36cf896",3205:"ab908fee",3255:"eb65ada6",3321:"399e2ae0",3343:"88707534",3352:"bc7114de",3461:"92d7ec1c",3468:"d53f7b58",3473:"e37c10cf",3556:"1c3eeab6",3581:"ac572de0",3608:"451b649c",3655:"9efe6013",3672:"5b191133",3676:"83bf5953",3711:"df4673ac",3784:"e30d4e28",3812:"cbb2521d",3895:"7da354c4",3981:"5d0ca252",4013:"d1dc79e6",4016:"0e527f61",4020:"17166c9e",4035:"7965360f",4073:"ef90bfd3",4085:"28281215",4114:"7ae7cd9f",4131:"10fe3296",4156:"df2dae73",4160:"5b4bb246",4225:"e27bd938",4282:"f7150aae",4295:"77340146",4435:"7afa90af",4491:"4f333d79",4566:"083b272b",4586:"4d5a39ea",4633:"8fc75b1d",4655:"faea4359",4691:"eb3b0105",4787:"5512dc61",4841:"004f45fc",4864:"31618c72",4890:"0ed2a654",4901:"3535bf39",4964:"ac39a9af",4968:"ee0ff256",4972:"93b1b404",5028:"db50a1e3",5048:"b54357ff",5069:"75259510",5074:"1e068fa4",5107:"eacb6d71",5146:"3fddbb1e",5148:"adf0aacc",5180:"d5e90542",5214:"1a1d4a0e",5270:"32a37e70",5304:"cf8fcaa6",5312:"5e115a35",5343:"6a19a9e7",5358:"2a61e241",5391:"7633e7d8",5407:"aa9317a2",5421:"4c3ca1b7",5432:"e26367ca",5484:"cc9df9a5",5507:"1fa51a5f",5525:"d713c798",5552:"060f85c4",5625:"556ec218",5736:"a58c85b7",5755:"454d74de",5758:"a47b0116",5847:"5d0a9e5a",5853:"e1bbe2f9",5859:"66ee6832",5861:"41be56bd",5863:"895c92b7",5878:"dc984b31",5901:"cac711dd",5990:"30cfd410",6e3:"4aa23b34",6050:"3b9f7ec7",6103:"0a8cd0b6",6253:"761d774c",6274:"50103f53",6295:"6743cbc1",6386:"48662908",6447:"c24ad1aa",6484:"c5d2c01e",6515:"5351f2ce",6533:"7afa16b9",6562:"01ac4fff",6567:"063adaad",6685:"f236266c",6686:"26a09873",6705:"1171e6b8",6709:"7db86275",6720:"2efe773d",6738:"fa3e72f3",6791:"5abaa388",6867:"22253278",6877:"0621ee24",6889:"1890830a",6953:"8456ffa0",6979:"ebf7e53a",6990:"4aa24236",7040:"f431b130",7049:"98483cc2",7054:"e53ee634",7092:"50008256",7115:"806fde59",7140:"8783f36c",7262:"dbceb516",7318:"5f885f71",7330:"3afb1524",7339:"710e4b4b",7417:"6b1a4f7f",7422:"ee6f71d4",7423:"3e2a460a",7438:"acbe7acb",7445:"06e9997f",7459:"afcfa4a5",7482:"9c07f11b",7566:"4aa6f456",7572:"27532e97",7659:"4fbdcbc5",7660:"d97272b4",7670:"874df815",7717:"d2b79427",7725:"fe5730dc",7765:"9d9ab75b",7810:"e097ea6d",7815:"249d5b90",7824:"57d07aed",7918:"94571392",7920:"89cd878b",7965:"313aac26",8007:"b956215b",8072:"c0c8f288",8238:"5a227d72",8246:"5c40b2ae",8356:"f62e0861",8375:"43f6bd40",8443:"0e81c6a4",8499:"207e23ff",8523:"54465faa",8589:"007b074c",8599:"e540f496",8610:"e8fd200d",8648:"acc7593b",8650:"7bad0cda",8655:"3a4ba898",8665:"f8b98207",8693:"87a2b4eb",8702:"c21fae7b",8791:"6d122e42",8896:"1e9608c1",8951:"8479e370",8982:"b558ca56",8983:"e24bfa84",9027:"fa4b3cb6",9040:"1d359636",9054:"f73e5d86",9093:"a593040a",9109:"8e2211ef",9201:"8aca2057",9217:"30d0e894",9226:"8625b473",9235:"eb043d1e",9240:"af338c23",9247:"ef15ee16",9309:"cf35be89",9334:"452817e4",9347:"632fe9ef",9362:"ee6786de",9443:"3a772a46",9474:"8ec69f70",9476:"ba63c21c",9514:"db3ff95d",9523:"b064116b",9565:"52059a16",9571:"4a94d01d",9602:"88f838ff",9604:"e8c2de30",9620:"2a349974",9630:"054eec02",9668:"10bbd1ea",9727:"1cfd95ad",9804:"3bf80b30",9878:"768b81b9",9925:"ea74ac6a",9934:"9423bdb3"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,d)=>Object.prototype.hasOwnProperty.call(e,d),a={},c="centrifugal.dev:",r.l=(e,d,f,b)=>{if(a[e])a[e].push(d);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var c=a[e];if(delete a[e],t.parentNode&&t.parentNode.removeChild(t),c&&c.forEach((e=>e(f))),d)return d(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"7918",18793598:"2278",29839967:"2291",41068141:"3812",56231886:"5407","23855fe2":"8","935f2afb":"53",dc4f2258:"70","52bba951":"102","54f44165":"152","83d480e9":"205","3630fee7":"216","54aee988":"347",b325d9c4:"408","84a9b932":"410",c9a3329e:"430","84b8e2b1":"477","629b5641":"509",d1cb7448:"532",b2b675dd:"533",a2d1b113:"562",ab8e6500:"624","0a4443e6":"628","5ffc8930":"630",bd17143e:"674","40537b69":"683","893c1918":"811","4268d52f":"895",d4ca9753:"902","70aa60b8":"926","0fe76b3c":"992",fe6343fd:"1002","79ee6175":"1028","9475880e":"1151","0136d6f0":"1152",f90eb0d6:"1178","6e37598f":"1196","2114e513":"1313","3a890c2d":"1385","5fe24874":"1420","4ab79476":"1428",b301b932:"1439",b2f554cd:"1477",a728857c:"1530","0d0ff016":"1616","1fadf4c6":"1633","07703c67":"1658",d2c1944d:"1695",a7023ddc:"1713","45dfef24":"1714",a05caef7:"1752",d4dfc5db:"1888","9db3c45b":"1977",d6627831:"1983",f1b7a7af:"1994","3dfd29d6":"2005","21e8a749":"2026","7a7ba156":"2040","945c690d":"2089",be4c395a:"2128",dd818855:"2157","6b2be476":"2165","46627d28":"2203","3b028b51":"2292",ba0d3b30:"2309","0a1a814f":"2321","6e07cb60":"2352","9ff4038f":"2353","3bb37b67":"2413",bfbfeea3:"2438",ee88d6e4:"2442",d1c7a4f7:"2469",ed785809:"2479","814f3328":"2535","211f1d7a":"2540",c3677326:"2569","71fc0044":"2578","6e81f787":"2605","03e522d9":"2624","7bd30152":"2635","6ef9986d":"2637","24f2bde3":"2639",ff64321a:"2735","2b147458":"2757","43c444d2":"2853",ad7c169e:"2865",a4ddbaa1:"2901","60271c2c":"2906","332362b2":"2918","555afbe5":"2925","3529cd5b":"2983",bbd14fff:"3001",fbd7a87c:"3039",b2eaf182:"3073","1f391b9e":"3085",a6aa9e1f:"3089",cac93e67:"3165","4ebb2955":"3190","21c2d27e":"3205","06f11616":"3255","6aa961d8":"3321","25c94216":"3343","69d81c34":"3352","7df4dfbf":"3461",f26176d2:"3468","984c0c66":"3556","2dbf7ee0":"3581","9e4087bc":"3608","694566b3":"3655","1248e41e":"3672",c0434fb9:"3676","56e32e60":"3711","041d543b":"3784","4f64b982":"3895","1d3c9151":"3981","01a85c17":"4013",d9829201:"4016",e8314be4:"4020","2b8ad662":"4035","3e15fc9c":"4073","230ca58d":"4085","631e3db1":"4114","238ce909":"4131","64e125c9":"4156","3c51ccb2":"4160","633b2ed2":"4225",e6b6a8f8:"4282","237c01c3":"4295",a82fa8b7:"4435",e6afaed9:"4491",b3216779:"4566",c98fa109:"4586","2f70c421":"4633","776d934d":"4655","0a7ca2d6":"4691","1117f49a":"4787","0312644c":"4841",ee10dcb9:"4864",b4b43a34:"4890","8a978eb4":"4901",ea108d2f:"4964","2eb9c429":"4968","94d5cf4c":"5028","0eae5577":"5048","192a8b1e":"5069","77e23114":"5074","936398dc":"5107",ffdd667d:"5146","679046a6":"5148",d016d150:"5180",c318ab3c:"5214","13f6d888":"5270","30b3ad4a":"5304",fd3209d2:"5312","1335c7a1":"5343","3c4ec49c":"5358","369bd8f8":"5391","6eaeadff":"5421","16c9b55b":"5432",ee78c395:"5484","9179a2e2":"5507","6c37c4d1":"5552","39d4d18a":"5625","89734ed6":"5736",a564e6ff:"5755","85196f1f":"5758",c1817076:"5847",aa73fb9a:"5853","369aea06":"5859","9c1ee1d6":"5861",f178572b:"5863",d835c886:"5878",d2fe6fea:"5901","32e1c903":"5990","84b1c6a7":"6000","81e12894":"6050",ccc49370:"6103","3f0e28d9":"6253",a41a0a70:"6274",ecd5d374:"6295",f346273e:"6386",cce51cf2:"6447","4bd4488a":"6484",fcb790ab:"6515","1cd70467":"6533",f762c5da:"6562",a74df3cd:"6567",db2f115c:"6685","267fbc4e":"6686",fe52e117:"6705","0295581e":"6709","6574fcee":"6720","861598a7":"6738","2daf4703":"6791","93f9db65":"6867","91c5cac2":"6877",aacb0ae1:"6889",b4f0bebf:"6953","5f78e650":"6979","0dc36dc4":"6990",ab6f12ff:"7040",c2f60b05:"7049","9dd8a0d2":"7054",b05011d9:"7092","2a42cb18":"7115",e19d40c8:"7140",e66faea1:"7262",f2e9cf2f:"7318",b5547432:"7330","3270d7e8":"7339","18b1e3cf":"7417","11a20880":"7422","58246c43":"7423","9c021584":"7438",a8fc9e46:"7445","05ba4f60":"7459","66eb7538":"7482","8e068dda":"7566","6fbe284c":"7572",fc3deafd:"7659",f4f2dadf:"7660","91116fee":"7670","01eca2db":"7717",b9cceeee:"7725",fe91fc6f:"7765","19e7756f":"7810","2bf25d27":"7815",c5dc0dc4:"7824","1a4e3797":"7920","4a4109ec":"7965","5706869d":"8007","31c58a66":"8072","4fc58f03":"8238","0d57d15e":"8246","04ac3258":"8356","5de4a79c":"8375",b479c509:"8499",f2f7592a:"8523","7672fb2a":"8589","91fdfcd5":"8599","6875c492":"8610","8e9fe0eb":"8648",cadfeb4f:"8650",c8380abd:"8655","9b9e219e":"8665",b0ea8d09:"8693","73c943f6":"8702",e257283f:"8791","06f9ead7":"8896","09382599":"8951","5934e2d7":"8982","79276c30":"8983","9b70d0cc":"9027","02080b57":"9040",b89c2c0a:"9054","386a3726":"9093",a1538072:"9109","4efbf0bc":"9201",fd93cfee:"9217","2acc7b89":"9226",e7893f84:"9235",bbb9e52d:"9240","92b58ac1":"9247","250ad80d":"9309","9c87bba9":"9334",b1f4df52:"9347","8db697a0":"9362","155d95c4":"9443",b6f2a3eb:"9474","498554e3":"9476","1be78505":"9514","2391cf3d":"9523",fd1fdc14:"9565","195b633a":"9571",b62a3811:"9602","49012ebf":"9604","58b29436":"9620",e9cbd346:"9630","267a22d2":"9668","1d4d4d48":"9727","209857dd":"9804","7747d83f":"9878","73e61bcc":"9925","1d223b96":"9934"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,3312:0};r.f.j=(d,f)=>{var a=r.o(e,d)?e[d]:void 0;if(0!==a)if(a)f.push(a[2]);else if(/^(1303|3312)$/.test(d))e[d]=0;else{var c=new Promise(((f,c)=>a=e[d]=[f,c]));f.push(a[2]=c);var b=r.p+r.u(d),t=new Error;r.l(b,(f=>{if(r.o(e,d)&&(0!==(a=e[d])&&(e[d]=void 0),a)){var c=f&&("load"===f.type?"missing":f.type),b=f&&f.target&&f.target.src;t.message="Loading chunk "+d+" failed.\n("+c+": "+b+")",t.name="ChunkLoadError",t.type=c,t.request=b,a[1](t)}}),"chunk-"+d,d)}},r.O.j=d=>0===e[d];var d=(d,f)=>{var a,c,b=f[0],t=f[1],o=f[2],n=0;if(b.some((d=>0!==e[d]))){for(a in t)r.o(t,a)&&(r.m[a]=t[a]);if(o)var i=o(r)}for(d&&d(f);n{"use strict";var e,d,f,a,c,b={},t={};function r(e){var d=t[e];if(void 0!==d)return d.exports;var f=t[e]={exports:{}};return b[e].call(f.exports,f,f.exports,r),f.exports}r.m=b,e=[],r.O=(d,f,a,c)=>{if(!f){var b=1/0;for(i=0;i=c)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,c0&&e[i-1][2]>c;i--)e[i]=e[i-1];e[i]=[f,a,c]},r.n=e=>{var d=e&&e.__esModule?()=>e.default:()=>e;return r.d(d,{a:d}),d},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var c=Object.create(null);r.r(c);var b={};d=d||[null,f({}),f([]),f(f)];for(var t=2&a&&e;"object"==typeof t&&!~d.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((d=>b[d]=()=>e[d]));return b.default=()=>e,r.d(c,b),c},r.d=(e,d)=>{for(var f in d)r.o(d,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:d[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((d,f)=>(r.f[f](e,d),d)),[])),r.u=e=>"assets/js/"+({8:"23855fe2",53:"935f2afb",70:"dc4f2258",102:"52bba951",152:"54f44165",205:"83d480e9",216:"3630fee7",347:"54aee988",408:"b325d9c4",410:"84a9b932",430:"c9a3329e",477:"84b8e2b1",509:"629b5641",532:"d1cb7448",533:"b2b675dd",562:"a2d1b113",624:"ab8e6500",628:"0a4443e6",630:"5ffc8930",674:"bd17143e",683:"40537b69",811:"893c1918",895:"4268d52f",902:"d4ca9753",926:"70aa60b8",992:"0fe76b3c",1002:"fe6343fd",1028:"79ee6175",1151:"9475880e",1152:"0136d6f0",1178:"f90eb0d6",1196:"6e37598f",1313:"2114e513",1385:"3a890c2d",1420:"5fe24874",1428:"4ab79476",1439:"b301b932",1477:"b2f554cd",1530:"a728857c",1616:"0d0ff016",1633:"1fadf4c6",1658:"07703c67",1695:"d2c1944d",1713:"a7023ddc",1714:"45dfef24",1752:"a05caef7",1888:"d4dfc5db",1977:"9db3c45b",1983:"d6627831",1994:"f1b7a7af",2005:"3dfd29d6",2026:"21e8a749",2040:"7a7ba156",2089:"945c690d",2128:"be4c395a",2157:"dd818855",2165:"6b2be476",2203:"46627d28",2278:"18793598",2291:"29839967",2292:"3b028b51",2309:"ba0d3b30",2321:"0a1a814f",2352:"6e07cb60",2353:"9ff4038f",2413:"3bb37b67",2438:"bfbfeea3",2442:"ee88d6e4",2469:"d1c7a4f7",2479:"ed785809",2535:"814f3328",2540:"211f1d7a",2569:"c3677326",2578:"71fc0044",2605:"6e81f787",2624:"03e522d9",2635:"7bd30152",2637:"6ef9986d",2639:"24f2bde3",2735:"ff64321a",2757:"2b147458",2853:"43c444d2",2865:"ad7c169e",2901:"a4ddbaa1",2906:"60271c2c",2918:"332362b2",2925:"555afbe5",2983:"3529cd5b",3001:"bbd14fff",3039:"fbd7a87c",3073:"b2eaf182",3085:"1f391b9e",3089:"a6aa9e1f",3165:"cac93e67",3190:"4ebb2955",3205:"21c2d27e",3255:"06f11616",3321:"6aa961d8",3343:"25c94216",3352:"69d81c34",3461:"7df4dfbf",3468:"f26176d2",3556:"984c0c66",3581:"2dbf7ee0",3608:"9e4087bc",3655:"694566b3",3672:"1248e41e",3676:"c0434fb9",3711:"56e32e60",3784:"041d543b",3812:"41068141",3895:"4f64b982",3981:"1d3c9151",4013:"01a85c17",4016:"d9829201",4020:"e8314be4",4035:"2b8ad662",4073:"3e15fc9c",4085:"230ca58d",4114:"631e3db1",4131:"238ce909",4156:"64e125c9",4160:"3c51ccb2",4225:"633b2ed2",4282:"e6b6a8f8",4295:"237c01c3",4435:"a82fa8b7",4491:"e6afaed9",4566:"b3216779",4586:"c98fa109",4633:"2f70c421",4655:"776d934d",4691:"0a7ca2d6",4787:"1117f49a",4841:"0312644c",4864:"ee10dcb9",4890:"b4b43a34",4901:"8a978eb4",4964:"ea108d2f",4968:"2eb9c429",5028:"94d5cf4c",5048:"0eae5577",5069:"192a8b1e",5074:"77e23114",5107:"936398dc",5146:"ffdd667d",5148:"679046a6",5180:"d016d150",5214:"c318ab3c",5270:"13f6d888",5304:"30b3ad4a",5312:"fd3209d2",5343:"1335c7a1",5358:"3c4ec49c",5391:"369bd8f8",5407:"56231886",5421:"6eaeadff",5432:"16c9b55b",5484:"ee78c395",5507:"9179a2e2",5552:"6c37c4d1",5625:"39d4d18a",5736:"89734ed6",5755:"a564e6ff",5758:"85196f1f",5847:"c1817076",5853:"aa73fb9a",5859:"369aea06",5861:"9c1ee1d6",5863:"f178572b",5878:"d835c886",5901:"d2fe6fea",5990:"32e1c903",6e3:"84b1c6a7",6050:"81e12894",6103:"ccc49370",6253:"3f0e28d9",6274:"a41a0a70",6295:"ecd5d374",6386:"f346273e",6447:"cce51cf2",6484:"4bd4488a",6515:"fcb790ab",6533:"1cd70467",6562:"f762c5da",6567:"a74df3cd",6685:"db2f115c",6686:"267fbc4e",6705:"fe52e117",6709:"0295581e",6720:"6574fcee",6738:"861598a7",6791:"2daf4703",6867:"93f9db65",6877:"91c5cac2",6889:"aacb0ae1",6953:"b4f0bebf",6979:"5f78e650",6990:"0dc36dc4",7040:"ab6f12ff",7049:"c2f60b05",7054:"9dd8a0d2",7092:"b05011d9",7115:"2a42cb18",7140:"e19d40c8",7262:"e66faea1",7318:"f2e9cf2f",7330:"b5547432",7339:"3270d7e8",7417:"18b1e3cf",7422:"11a20880",7423:"58246c43",7438:"9c021584",7445:"a8fc9e46",7459:"05ba4f60",7482:"66eb7538",7566:"8e068dda",7572:"6fbe284c",7659:"fc3deafd",7660:"f4f2dadf",7670:"91116fee",7717:"01eca2db",7725:"b9cceeee",7765:"fe91fc6f",7810:"19e7756f",7815:"2bf25d27",7824:"c5dc0dc4",7918:"17896441",7920:"1a4e3797",7965:"4a4109ec",8007:"5706869d",8072:"31c58a66",8238:"4fc58f03",8246:"0d57d15e",8356:"04ac3258",8375:"5de4a79c",8499:"b479c509",8523:"f2f7592a",8589:"7672fb2a",8599:"91fdfcd5",8610:"6875c492",8648:"8e9fe0eb",8650:"cadfeb4f",8655:"c8380abd",8665:"9b9e219e",8693:"b0ea8d09",8702:"73c943f6",8791:"e257283f",8896:"06f9ead7",8951:"09382599",8982:"5934e2d7",8983:"79276c30",9027:"9b70d0cc",9040:"02080b57",9054:"b89c2c0a",9093:"386a3726",9109:"a1538072",9201:"4efbf0bc",9217:"fd93cfee",9226:"2acc7b89",9235:"e7893f84",9240:"bbb9e52d",9247:"92b58ac1",9309:"250ad80d",9334:"9c87bba9",9347:"b1f4df52",9362:"8db697a0",9443:"155d95c4",9474:"b6f2a3eb",9476:"498554e3",9514:"1be78505",9523:"2391cf3d",9565:"fd1fdc14",9571:"195b633a",9602:"b62a3811",9604:"49012ebf",9620:"58b29436",9630:"e9cbd346",9668:"267a22d2",9727:"1d4d4d48",9804:"209857dd",9878:"7747d83f",9925:"73e61bcc",9934:"1d223b96"}[e]||e)+"."+{8:"804364f5",53:"2e89eb68",70:"bf0ff083",102:"93002144",152:"f1caf464",205:"dbbb85ff",216:"e1c0d285",347:"10b323a0",408:"4908659c",410:"e42e28e9",430:"04b511ca",477:"e3670801",509:"f849dc03",532:"3d39d0ab",533:"4b6d240d",562:"92a1c228",624:"a4014845",628:"7afbef61",630:"023bfc41",674:"4acc788a",683:"b722507b",811:"85adbaa4",817:"e3708d5c",895:"ac5ed8f2",902:"cf1accac",926:"e14676d0",992:"b393c2fa",1002:"c258a0fa",1028:"3edff402",1151:"9370d5d3",1152:"e7c2d467",1178:"a143d2f6",1196:"4aeaa175",1313:"cfbb3155",1385:"19eebd07",1420:"5fbe60e3",1428:"26fd723c",1439:"a7ca21f9",1477:"14f98641",1530:"46748f4a",1616:"69b25479",1633:"fcb34adb",1658:"b649e60e",1695:"05c586cf",1713:"2b88788e",1714:"14e31b78",1752:"85ab1537",1888:"4b233a1a",1977:"0e9d305d",1983:"02fc1d4c",1994:"86a6dcf7",2005:"0f47dfcf",2026:"83a1763a",2040:"114968c1",2089:"9cedea09",2128:"18ab1997",2157:"230261c6",2165:"50633fcb",2203:"e8bbd4e5",2278:"48a18137",2291:"3dff63e3",2292:"ec013974",2309:"5d871ebf",2321:"6a30be9a",2352:"e5136f35",2353:"51353876",2413:"15ebd57c",2438:"b50f04b6",2442:"5eb64251",2469:"a70e20a3",2479:"2851689b",2535:"08004b39",2540:"e3490c14",2569:"4122d995",2578:"12b5b5e8",2605:"3cfba636",2624:"f3d09f74",2635:"90e2e29a",2637:"3ce1c24c",2639:"f0664aef",2735:"f6873296",2757:"9e6d649b",2853:"b556afc4",2865:"508e57c5",2901:"75d5737c",2906:"f8fdab63",2918:"068f4c18",2925:"a44a225e",2983:"95bf0169",3001:"cb864c44",3039:"0257f6ba",3073:"de83998b",3085:"2fc22f8b",3089:"2df4caa2",3165:"92aba545",3190:"c36cf896",3205:"ab908fee",3255:"eb65ada6",3321:"399e2ae0",3343:"88707534",3352:"bc7114de",3461:"92d7ec1c",3468:"d53f7b58",3473:"e37c10cf",3556:"1c3eeab6",3581:"ac572de0",3608:"451b649c",3655:"9efe6013",3672:"5b191133",3676:"83bf5953",3711:"df4673ac",3784:"e30d4e28",3812:"cbb2521d",3895:"7da354c4",3981:"5d0ca252",4013:"d1dc79e6",4016:"0e527f61",4020:"17166c9e",4035:"7965360f",4073:"ef90bfd3",4085:"28281215",4114:"7ae7cd9f",4131:"10fe3296",4156:"df2dae73",4160:"5b4bb246",4225:"e27bd938",4282:"f7150aae",4295:"77340146",4435:"7afa90af",4491:"4f333d79",4566:"083b272b",4586:"4d5a39ea",4633:"8fc75b1d",4655:"faea4359",4691:"eb3b0105",4787:"5512dc61",4841:"004f45fc",4864:"31618c72",4890:"0ed2a654",4901:"3535bf39",4964:"ac39a9af",4968:"ee0ff256",4972:"93b1b404",5028:"db50a1e3",5048:"b54357ff",5069:"75259510",5074:"1e068fa4",5107:"eacb6d71",5146:"3fddbb1e",5148:"adf0aacc",5180:"d5e90542",5214:"1a1d4a0e",5270:"32a37e70",5304:"cf8fcaa6",5312:"5e115a35",5343:"6a19a9e7",5358:"2a61e241",5391:"7633e7d8",5407:"aa9317a2",5421:"4c3ca1b7",5432:"e26367ca",5484:"cc9df9a5",5507:"1fa51a5f",5525:"d713c798",5552:"060f85c4",5625:"556ec218",5736:"a58c85b7",5755:"454d74de",5758:"a47b0116",5847:"5d0a9e5a",5853:"e1bbe2f9",5859:"66ee6832",5861:"41be56bd",5863:"895c92b7",5878:"dc984b31",5901:"cac711dd",5990:"30cfd410",6e3:"4aa23b34",6050:"3b9f7ec7",6103:"0a8cd0b6",6253:"761d774c",6274:"50103f53",6295:"6743cbc1",6386:"48662908",6447:"c24ad1aa",6484:"c5d2c01e",6515:"5351f2ce",6533:"7afa16b9",6562:"01ac4fff",6567:"063adaad",6685:"f236266c",6686:"26a09873",6705:"1171e6b8",6709:"7db86275",6720:"2efe773d",6738:"fa3e72f3",6791:"5abaa388",6867:"22253278",6877:"0621ee24",6889:"1890830a",6953:"8456ffa0",6979:"ebf7e53a",6990:"4aa24236",7040:"f431b130",7049:"98483cc2",7054:"e53ee634",7092:"50008256",7115:"806fde59",7140:"8783f36c",7262:"dbceb516",7318:"5f885f71",7330:"3afb1524",7339:"710e4b4b",7417:"6b1a4f7f",7422:"ee6f71d4",7423:"3e2a460a",7438:"acbe7acb",7445:"06e9997f",7459:"afcfa4a5",7482:"9c07f11b",7566:"4aa6f456",7572:"27532e97",7659:"4fbdcbc5",7660:"d97272b4",7670:"874df815",7717:"d2b79427",7725:"fe5730dc",7765:"9d9ab75b",7810:"e097ea6d",7815:"249d5b90",7824:"57d07aed",7918:"94571392",7920:"89cd878b",7965:"313aac26",8007:"b956215b",8072:"c0c8f288",8238:"5a227d72",8246:"5c40b2ae",8356:"f62e0861",8375:"43f6bd40",8443:"0e81c6a4",8499:"207e23ff",8523:"54465faa",8589:"007b074c",8599:"e540f496",8610:"e8fd200d",8648:"acc7593b",8650:"7bad0cda",8655:"3a4ba898",8665:"f8b98207",8693:"87a2b4eb",8702:"c21fae7b",8791:"6d122e42",8896:"1e9608c1",8951:"8479e370",8982:"b558ca56",8983:"e24bfa84",9027:"fa4b3cb6",9040:"1d359636",9054:"f73e5d86",9093:"a593040a",9109:"8e2211ef",9201:"8aca2057",9217:"30d0e894",9226:"8625b473",9235:"eb043d1e",9240:"f1f4a6f7",9247:"ef15ee16",9309:"cf35be89",9334:"452817e4",9347:"632fe9ef",9362:"ee6786de",9443:"3a772a46",9474:"8ec69f70",9476:"ba63c21c",9514:"db3ff95d",9523:"b064116b",9565:"52059a16",9571:"4a94d01d",9602:"88f838ff",9604:"e8c2de30",9620:"2a349974",9630:"054eec02",9668:"10bbd1ea",9727:"1cfd95ad",9804:"3bf80b30",9878:"768b81b9",9925:"ea74ac6a",9934:"9423bdb3"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,d)=>Object.prototype.hasOwnProperty.call(e,d),a={},c="centrifugal.dev:",r.l=(e,d,f,b)=>{if(a[e])a[e].push(d);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var c=a[e];if(delete a[e],t.parentNode&&t.parentNode.removeChild(t),c&&c.forEach((e=>e(f))),d)return d(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"7918",18793598:"2278",29839967:"2291",41068141:"3812",56231886:"5407","23855fe2":"8","935f2afb":"53",dc4f2258:"70","52bba951":"102","54f44165":"152","83d480e9":"205","3630fee7":"216","54aee988":"347",b325d9c4:"408","84a9b932":"410",c9a3329e:"430","84b8e2b1":"477","629b5641":"509",d1cb7448:"532",b2b675dd:"533",a2d1b113:"562",ab8e6500:"624","0a4443e6":"628","5ffc8930":"630",bd17143e:"674","40537b69":"683","893c1918":"811","4268d52f":"895",d4ca9753:"902","70aa60b8":"926","0fe76b3c":"992",fe6343fd:"1002","79ee6175":"1028","9475880e":"1151","0136d6f0":"1152",f90eb0d6:"1178","6e37598f":"1196","2114e513":"1313","3a890c2d":"1385","5fe24874":"1420","4ab79476":"1428",b301b932:"1439",b2f554cd:"1477",a728857c:"1530","0d0ff016":"1616","1fadf4c6":"1633","07703c67":"1658",d2c1944d:"1695",a7023ddc:"1713","45dfef24":"1714",a05caef7:"1752",d4dfc5db:"1888","9db3c45b":"1977",d6627831:"1983",f1b7a7af:"1994","3dfd29d6":"2005","21e8a749":"2026","7a7ba156":"2040","945c690d":"2089",be4c395a:"2128",dd818855:"2157","6b2be476":"2165","46627d28":"2203","3b028b51":"2292",ba0d3b30:"2309","0a1a814f":"2321","6e07cb60":"2352","9ff4038f":"2353","3bb37b67":"2413",bfbfeea3:"2438",ee88d6e4:"2442",d1c7a4f7:"2469",ed785809:"2479","814f3328":"2535","211f1d7a":"2540",c3677326:"2569","71fc0044":"2578","6e81f787":"2605","03e522d9":"2624","7bd30152":"2635","6ef9986d":"2637","24f2bde3":"2639",ff64321a:"2735","2b147458":"2757","43c444d2":"2853",ad7c169e:"2865",a4ddbaa1:"2901","60271c2c":"2906","332362b2":"2918","555afbe5":"2925","3529cd5b":"2983",bbd14fff:"3001",fbd7a87c:"3039",b2eaf182:"3073","1f391b9e":"3085",a6aa9e1f:"3089",cac93e67:"3165","4ebb2955":"3190","21c2d27e":"3205","06f11616":"3255","6aa961d8":"3321","25c94216":"3343","69d81c34":"3352","7df4dfbf":"3461",f26176d2:"3468","984c0c66":"3556","2dbf7ee0":"3581","9e4087bc":"3608","694566b3":"3655","1248e41e":"3672",c0434fb9:"3676","56e32e60":"3711","041d543b":"3784","4f64b982":"3895","1d3c9151":"3981","01a85c17":"4013",d9829201:"4016",e8314be4:"4020","2b8ad662":"4035","3e15fc9c":"4073","230ca58d":"4085","631e3db1":"4114","238ce909":"4131","64e125c9":"4156","3c51ccb2":"4160","633b2ed2":"4225",e6b6a8f8:"4282","237c01c3":"4295",a82fa8b7:"4435",e6afaed9:"4491",b3216779:"4566",c98fa109:"4586","2f70c421":"4633","776d934d":"4655","0a7ca2d6":"4691","1117f49a":"4787","0312644c":"4841",ee10dcb9:"4864",b4b43a34:"4890","8a978eb4":"4901",ea108d2f:"4964","2eb9c429":"4968","94d5cf4c":"5028","0eae5577":"5048","192a8b1e":"5069","77e23114":"5074","936398dc":"5107",ffdd667d:"5146","679046a6":"5148",d016d150:"5180",c318ab3c:"5214","13f6d888":"5270","30b3ad4a":"5304",fd3209d2:"5312","1335c7a1":"5343","3c4ec49c":"5358","369bd8f8":"5391","6eaeadff":"5421","16c9b55b":"5432",ee78c395:"5484","9179a2e2":"5507","6c37c4d1":"5552","39d4d18a":"5625","89734ed6":"5736",a564e6ff:"5755","85196f1f":"5758",c1817076:"5847",aa73fb9a:"5853","369aea06":"5859","9c1ee1d6":"5861",f178572b:"5863",d835c886:"5878",d2fe6fea:"5901","32e1c903":"5990","84b1c6a7":"6000","81e12894":"6050",ccc49370:"6103","3f0e28d9":"6253",a41a0a70:"6274",ecd5d374:"6295",f346273e:"6386",cce51cf2:"6447","4bd4488a":"6484",fcb790ab:"6515","1cd70467":"6533",f762c5da:"6562",a74df3cd:"6567",db2f115c:"6685","267fbc4e":"6686",fe52e117:"6705","0295581e":"6709","6574fcee":"6720","861598a7":"6738","2daf4703":"6791","93f9db65":"6867","91c5cac2":"6877",aacb0ae1:"6889",b4f0bebf:"6953","5f78e650":"6979","0dc36dc4":"6990",ab6f12ff:"7040",c2f60b05:"7049","9dd8a0d2":"7054",b05011d9:"7092","2a42cb18":"7115",e19d40c8:"7140",e66faea1:"7262",f2e9cf2f:"7318",b5547432:"7330","3270d7e8":"7339","18b1e3cf":"7417","11a20880":"7422","58246c43":"7423","9c021584":"7438",a8fc9e46:"7445","05ba4f60":"7459","66eb7538":"7482","8e068dda":"7566","6fbe284c":"7572",fc3deafd:"7659",f4f2dadf:"7660","91116fee":"7670","01eca2db":"7717",b9cceeee:"7725",fe91fc6f:"7765","19e7756f":"7810","2bf25d27":"7815",c5dc0dc4:"7824","1a4e3797":"7920","4a4109ec":"7965","5706869d":"8007","31c58a66":"8072","4fc58f03":"8238","0d57d15e":"8246","04ac3258":"8356","5de4a79c":"8375",b479c509:"8499",f2f7592a:"8523","7672fb2a":"8589","91fdfcd5":"8599","6875c492":"8610","8e9fe0eb":"8648",cadfeb4f:"8650",c8380abd:"8655","9b9e219e":"8665",b0ea8d09:"8693","73c943f6":"8702",e257283f:"8791","06f9ead7":"8896","09382599":"8951","5934e2d7":"8982","79276c30":"8983","9b70d0cc":"9027","02080b57":"9040",b89c2c0a:"9054","386a3726":"9093",a1538072:"9109","4efbf0bc":"9201",fd93cfee:"9217","2acc7b89":"9226",e7893f84:"9235",bbb9e52d:"9240","92b58ac1":"9247","250ad80d":"9309","9c87bba9":"9334",b1f4df52:"9347","8db697a0":"9362","155d95c4":"9443",b6f2a3eb:"9474","498554e3":"9476","1be78505":"9514","2391cf3d":"9523",fd1fdc14:"9565","195b633a":"9571",b62a3811:"9602","49012ebf":"9604","58b29436":"9620",e9cbd346:"9630","267a22d2":"9668","1d4d4d48":"9727","209857dd":"9804","7747d83f":"9878","73e61bcc":"9925","1d223b96":"9934"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,3312:0};r.f.j=(d,f)=>{var a=r.o(e,d)?e[d]:void 0;if(0!==a)if(a)f.push(a[2]);else if(/^(1303|3312)$/.test(d))e[d]=0;else{var c=new Promise(((f,c)=>a=e[d]=[f,c]));f.push(a[2]=c);var b=r.p+r.u(d),t=new Error;r.l(b,(f=>{if(r.o(e,d)&&(0!==(a=e[d])&&(e[d]=void 0),a)){var c=f&&("load"===f.type?"missing":f.type),b=f&&f.target&&f.target.src;t.message="Loading chunk "+d+" failed.\n("+c+": "+b+")",t.name="ChunkLoadError",t.type=c,t.request=b,a[1](t)}}),"chunk-"+d,d)}},r.O.j=d=>0===e[d];var d=(d,f)=>{var a,c,b=f[0],t=f[1],o=f[2],n=0;if(b.some((d=>0!==e[d]))){for(a in t)r.o(t,a)&&(r.m[a]=t[a]);if(o)var i=o(r)}for(d&&d(f);n - +
October 29, 2023 by Alexander Emelin
We start talking more about recently launched Centrifugo PRO. In this post, we share details about Centrifugo PRO push notification API implementation - how it works and what makes it special and practical.
August 29, 2023 by Centrifugal + RabbitX
In this post, the engineering team of RabbitX platform shares details about the usage of Centrifugo in their product.
August 19, 2023 by Alexander Emelin
In this post, we'll demonstrate how to asynchronously stream messages into Centrifugo channels from external data providers using Benthos tool. We also highlight some pitfalls which become more important in asynchronous publishing scenario.
June 29, 2023 by Centrifugal team
We are excited to announce a new version of Centrifugo. It's an evolutionary step which makes Centrifugo cleaner and more intuitive to use.
July 19, 2022 by Centrifugal team
Centrifugo v4 provides an optimized client protocol, modern WebSocket emulation, improved channel security, redesigned client SDK behavior, experimental HTTP/3 and WebTransport support.
October 18, 2021 by Alexander Emelin
In this tutorial we are integrating Centrifugo with NodeJS. We are using Centrifugo connect proxy feature to authenticate connections over standard Express.js session middleware.
August 31, 2021 by Centrifugal team
Centrifugo v3 released with lots of exciting improvements
November 12, 2020 by Alexander Emelin
The post describes techniques to write scalable WebSocket servers within Go ecosystem and beyond it
February 10, 2020 by Centrifugal team
Describing a test stand in Kubernetes where we connect one million websocket connections to a server, using Redis to scale nodes, and providing insights about hardware resources required to achieve 500k messages per second
- + \ No newline at end of file diff --git a/blog/2020/02/10/million-connections-with-centrifugo.html b/blog/2020/02/10/million-connections-with-centrifugo.html index 597a2bdca..38a73d3d0 100644 --- a/blog/2020/02/10/million-connections-with-centrifugo.html +++ b/blog/2020/02/10/million-connections-with-centrifugo.html @@ -16,13 +16,13 @@ - +

Million connections with Centrifugo

· 4 min read
Centrifugal team

In order to get an understanding about possible hardware requirements for reasonably massive Centrifugo setup we made a test stand inside Kubernetes.

Our goal was to run server based on Centrifuge library (the core of Centrifugo server) with one million WebSocket connections and send many messages to connected clients. While sending many messages we have been looking at delivery time latency. In fact we will see that about 30 million messages per minute (500k messages per second) will be delivered to connected clients and latency won't be larger than 200ms in 99 percentile.

Server nodes have been run on machines with the following configuration:

  • CPU Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz
  • Linux Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64 GNU/Linux

Some sysctl values:

fs.file-max = 3276750
fs.nr_open = 1048576
net.ipv4.tcp_mem = 3086496 4115330 6172992
net.ipv4.tcp_rmem = 8192 8388608 16777216
net.ipv4.tcp_wmem = 4096 4194394 16777216
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432

Kubernetes used these machines as its nodes.

We started 20 Centrifuge-based server pods. Our clients connected to server pods using Centrifuge Protobuf protocol. To scale horizontally we used Redis Engine and sharded it to 5 different Redis instances (each Redis instance consumes 1 CPU max).

To achieve many client connections we used 100 Kubernetes pods each generating about 10k client connections to server.

Here are some numbers we achieved:

  • 1 million WebSocket connections
  • Each connection subscribed to 2 channels: one personal channel and one group channel (with 10 subscribers in it), i.e. we had about 1.1 million active channels at each moment.
  • 28 million messages per minute (about 500k per second) delivered to clients
  • 200k per minute constant connect/disconnect rate to simulate real-life situation where clients connect/disconnect from server
  • 200ms delivery latency in 99 percentile
  • The size of each published message was about 100 bytes

And here are some numbers about final resource usage on server side (we don't actually interested in client side resource usage here):

  • 40 CPU total for server nodes when load achieved values claimed above (20 pods, ~2 CPU each)
  • 27 GB of RAM used mostly to handle 1 mln WebSocket connections, i.e. about 30kb RAM per connection
  • 0.32 CPU usage on every Redis instance
  • 100 mbit/sec rx и 150 mbit/sec tx of network used on each server pod

The picture that demonstrates experiment (better to open image in new tab):

Benchmark

This also demonstrates that to handle one million of WebSocket connections without many messages sent to clients you need about 10 CPU total for server nodes and about 5% of CPU on each of Redis instances. In this case CPU mostly spent on connect/disconnect flow, ping/pong frames, subscriptions to channels.

If we enable history and history message recovery features we see an increased Redis CPU usage: 64% instead of 32% on the same workload. Other resources usage is pretty the same.

The results mean that one can theoretically achieve the comparable numbers on single modern server machine. But numbers can vary a lot in case of different load scenarios. In this benchmark we looked at basic use case where we only connect many clients and send Publications to them. There are many features in Centrifuge library and in Centrifugo not covered by this artificial experiment. Also note that though benchmark was made for Centrifuge library for Centrifugo you can expect similar results.

Read and write buffer sizes of websocket connections were set to 512 kb on server side (sizes of buffers affect memory usage), with Centrifugo this means that to reproduce the same configuration you need to set:

{
...
"websocket_read_buffer_size": 512,
"websocket_write_buffer_size": 512
}
- + \ No newline at end of file diff --git a/blog/2020/10/16/experimenting-with-quic-transport.html b/blog/2020/10/16/experimenting-with-quic-transport.html index 95bc56cc0..ca2853157 100644 --- a/blog/2020/10/16/experimenting-with-quic-transport.html +++ b/blog/2020/10/16/experimenting-with-quic-transport.html @@ -16,13 +16,13 @@ - +

Experimenting with QUIC and WebTransport

· 15 min read
Alexander Emelin

post-cover

UPDATE: WebTransport spec is still evolving. Most information here is not actual anymore. For example the working group has no plan to implement both QuicTransport and HTTP3-based transports – only HTTP3 based WebTransport is going to be implemented. Maybe we will publish a follow-up of this post at some point.

Overview

WebTransport is a new browser API offering low-latency, bidirectional, client-server messaging. If you have not heard about it before I suggest to first read a post called Experimenting with QuicTransport published recently on web.dev – it gives a nice overview to WebTransport and shows client-side code examples. Here we will concentrate on implementing server side.

Some key points about WebTransport spec:

  • WebTransport standard will provide a possibility to use streaming client-server communication using modern transports such as QUIC and HTTP/3
  • It can be a good alternative to WebSocket messaging, standard provides some capabilities that are not possible with current WebSocket spec: possibility to get rid of head-of-line blocking problems using individual streams for different data, the possibility to reuse a single connection to a server in different browser tabs
  • WebTransport also defines an unreliable stream API using UDP datagrams (which is possible since QUIC is UDP-based) – which is what browsers did not have before without a rather complex WebRTC setup involving ICE, STUN, etc. This is sweet for in-browser real-time games.

To help you figure out things here are links to current WebTransport specs:

  • WebTransport overview – this spec gives an overview of WebTransport and provides requirements to transport layer
  • WebTransport over QUIC – this spec describes QUIC-based transport for WebTransport
  • WebTransport over HTTP/3 – this spec describes HTTP/3-based transport for WebTransport (actually HTTP/3 is a protocol defined on top of QUIC)

At moment Chrome only implements trial possibility to try out WebTransport standard and only implements WebTransport over QUIC. Developers can initialize transport with code like this:

const transport = new QuicTransport('quic-transport://localhost:4433/path');

In case of HTTP/3 transport one will use URL like 'https://localhost:4433/path' in transport constructor. All WebTransport underlying transports should support instantiation over URL – that's one of the spec requirements.

I decided that this is a cool possibility to finally play with QUIC protocol and its Go implementation github.com/lucas-clemente/quic-go.

danger

Please keep in mind that all things described in this post are work in progress. WebTransport drafts, Quic-Go library, even QUIC protocol itself are subjects to change. You should not use it in production yet.

Experimenting with QuicTransport post contains links to a client example and companion Python server implementation.

client example

We will use a linked client example to connect to a server that runs on localhost and uses github.com/lucas-clemente/quic-go library. To make our example work we need to open client example in Chrome, and actually, at this moment we need to install Chrome Canary. The reason behind this is that the quic-go library supports QUIC draft-29 while Chrome < 85 implements QuicTransport over draft-27. If you read this post at a time when Chrome stable 85 already released then most probably you don't need to install Canary release and just use your stable Chrome.

We also need to generate self-signed certificates since WebTransport only works with a TLS layer, and we should make Chrome trust our certificates. Let's prepare our client environment before writing a server and first install Chrome Canary.

Install Chrome Canary

Go to https://www.google.com/intl/en/chrome/canary/, download and install Chrome Canary. We will use it to open client example.

note

If you have Chrome >= 85 then most probably you can skip this step.

Generate self-signed TLS certificates

Since WebTransport based on modern network transports like QUIC and HTTP/3 security is a keystone. For our experiment we will create a self-signed TLS certificate using openssl.

Make sure you have openssl installed:

$ which openssl
/usr/bin/openssl

Then run:

openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
openssl rsa -passin pass:x -in server.pass.key -out server.key
rm server.pass.key
openssl req -new -key server.key -out server.csr

Set localhost for Common Name when asked.

The self-signed TLS certificate generated from the server.key private key and server.csr files:

openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

After these manipulations you should have server.crt and server.key files in your working directory.

To help you with process here is my console output during these steps (click to open):

??? example "My console output generating self-signed certificates"

```bash
$ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
Generating RSA private key, 2048 bit long modulus
...........................................................................................+++
.....................+++
e is 65537 (0x10001)

$ ls
server.pass.key

$ openssl rsa -passin pass:x -in server.pass.key -out server.key
writing RSA key

$ ls
server.key server.pass.key

$ rm server.pass.key

$ openssl req -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:RU
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:localhost
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
Signature ok
subject=/C=RU/CN=localhost
Getting Private key

$ ls
server.crt server.csr server.key
```

Run client example

Now the last step. What we need to do is run Chrome Canary with some flags that will allow it to trust our self-signed certificates. I suppose there is an alternative way making Chrome trust your certificates, but I have not tried it.

First let's find out a fingerprint of our cert:

openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

In my case base64 fingerprint was pe2P0fQwecKFMc6kz3+Y5MuVwVwEtGXyST5vJeaOO/M=, yours will be different.

Then run Chrome Canary with some additional flags that will make it trust out certs (close other Chrome Canary instances before running it):

$ /Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary \
--origin-to-force-quic-on=localhost:4433 \
--ignore-certificate-errors-spki-list=pe2P0fQwecKFMc6kz3+Y5MuVwVwEtGXyST5vJeaOO/M=

This example is for MacOS, for your system see docs on how to run Chrome/Chromium with custom flags.

Now you can open https://googlechrome.github.io/samples/quictransport/client.html URL in started browser and click Connect button. What? Connection not established? OK, this is fine since we need to run our server :)

Writing a QUIC server

Maybe in future we will have libraries that are specified to work with WebTransport over QUIC or HTTP/3, but for now we should implement server manually. As said above we will use github.com/lucas-clemente/quic-go library to do this.

Server skeleton

First, let's define a simple skeleton for our server:

package main

import (
"errors"
"log"

"github.com/lucas-clemente/quic-go"
)

// Config for WebTransportServerQuic.
type Config struct {
// ListenAddr sets an address to bind server to.
ListenAddr string
// TLSCertPath defines a path to .crt cert file.
TLSCertPath string
// TLSKeyPath defines a path to .key cert file
TLSKeyPath string
// AllowedOrigins represents list of allowed origins to connect from.
AllowedOrigins []string
}

// WebTransportServerQuic can handle WebTransport QUIC connections according
// to https://tools.ietf.org/html/draft-vvv-webtransport-quic-02.
type WebTransportServerQuic struct {
config Config
}

// NewWebTransportServerQuic creates new WebTransportServerQuic.
func NewWebTransportServerQuic(config Config) *WebTransportServerQuic {
return &WebTransportServerQuic{
config: config,
}
}

// Run server.
func (s *WebTransportServerQuic) Run() error {
return errors.New("not implemented")
}

func main() {
server := NewWebTransportServerQuic(Config{
ListenAddr: "0.0.0.0:4433",
TLSCertPath: "server.crt",
TLSKeyPath: "server.key",
AllowedOrigins: []string{"localhost", "googlechrome.github.io"},
})
if err := server.Run(); err != nil {
log.Fatal(err)
}
}

Accept QUIC connections

Let's concentrate on implementing Run method. We need to accept QUIC client connections. This can be done by creating quic.Listener instance and using its .Accept method to accept incoming client sessions.

// Run server.
func (s *WebTransportServerQuic) Run() error {
listener, err := quic.ListenAddr(s.config.ListenAddr, s.generateTLSConfig(), nil)
if err != nil {
return err
}
for {
sess, err := listener.Accept(context.Background())
if err != nil {
return err
}
log.Printf("session accepted: %s", sess.RemoteAddr().String())
go func() {
defer func() {
_ = sess.CloseWithError(0, "bye")
log.Println("close session")
}()
s.handleSession(sess)
}()
}
}

func (s *WebTransportServerQuic) handleSession(sess quic.Session) {
// Not implemented yet.
}

An interesting thing to note is that QUIC allows closing connection with specific application-level integer code and custom string reason. Just like WebSocket if you worked with it.

Also note, that we are starting our Listener with TLS configuration returned by s.generateTLSConfig() method. Let's take a closer look at how this method can be implemented.

// https://tools.ietf.org/html/draft-vvv-webtransport-quic-02#section-3.1
const alpnQuicTransport = "wq-vvv-01"

func (s *WebTransportServerQuic) generateTLSConfig() *tls.Config {
cert, err := tls.LoadX509KeyPair(s.config.TLSCertPath, s.config.TLSKeyPath)
if err != nil {
log.Fatal(err)
}
return &tls.Config{
Certificates: []tls.Certificate{cert},
NextProtos: []string{alpnQuicTransport},
}
}

Inside generateTLSConfig we load x509 certs from cert files generated above. WebTransport uses ALPN (Application-Layer Protocol Negotiation to prevent handshakes with a server that does not support WebTransport spec. This is just a string wq-vvv-01 inside NextProtos slice of our *tls.Config.

Connection Session handling

At this moment if you run a server and open a client example in Chrome then click Connect button – you should see that connection successfully established in event log area:

client example

Now if you try to send data to a server nothing will happen. That's because we have not implemented reading data from session streams.

Streams in QUIC provide a lightweight, ordered byte-stream abstraction to an application. Streams can be unidirectional or bidirectional.

Streams can be short-lived, streams can also be long-lived and can last the entire duration of a connection.

Client example provides three possible ways to communicate with a server:

  • Send a datagram
  • Open a unidirectional stream
  • Open a bidirectional stream

Unfortunately, quic-go library does not support sending UDP datagrams at this moment. To do this quic-go should implement one more draft called An Unreliable Datagram Extension to QUIC. There is already an ongoing pull request that implements it. This means that it's too early for us to experiment with unreliable UDP WebTransport client-server communication in Go. By the way, the interesting facts about UDP over QUIC are that QUIC congestion control mechanism will still apply and QUIC datagrams can support acknowledgements.

Implementing a unidirectional stream is possible with quic-go since the library supports creating and accepting unidirectional streams, but I'll leave this for a reader (though we will need accepting one unidirectional stream for parsing client indication anyway – see below).

Here we will only concentrate on implementing a server for a bidirectional case. We are in the Centrifugo blog, and this is the most interesting type of stream for me personally.

Parsing client indication

According to section-3.2 of Quic WebTransport spec in order to verify that the client's origin allowed connecting to the server, the user agent has to communicate the origin to the server. This is accomplished by sending a special message, called client indication, on stream 2, which is the first client-initiated unidirectional stream.

Here we will implement this. In the beginning of our session handler we will accept a unidirectional stream initiated by a client.

At moment spec defines two client indication keys: Origin and Path. In our case an origin value will be https://googlechrome.github.io and path will be /counter.

Let's define some constants and structures:

// client indication stream can not exceed 65535 bytes in length.
// https://tools.ietf.org/html/draft-vvv-webtransport-quic-02#section-3.2
const maxClientIndicationLength = 65535

// define known client indication keys.
type clientIndicationKey int16

const (
clientIndicationKeyOrigin clientIndicationKey = 0
clientIndicationKeyPath = 1
)

// ClientIndication container.
type ClientIndication struct {
// Origin client indication value.
Origin string
// Path client indication value.
Path string
}

Now what we should do is accept unidirectional stream inside session handler:

func (s *WebTransportServerQuic) handleSession(sess quic.Session) {
stream, err := sess.AcceptUniStream(context.Background())
if err != nil {
log.Println(err)
return
}
log.Printf("uni stream accepted, id: %d", stream.StreamID())

indication, err := receiveClientIndication(stream)
if err != nil {
log.Println(err)
return
}
log.Printf("client indication: %+v", indication)

if err := s.validateClientIndication(indication); err != nil {
log.Println(err)
return
}

// this method blocks.
if err := s.communicate(sess); err != nil {
log.Println(err)
}
}

func receiveClientIndication(stream quic.ReceiveStream) (ClientIndication, error) {
return ClientIndication{}, errors.New("not implemented yet")
}

func (s *WebTransportServerQuic) validateClientIndication(indication ClientIndication) error {
return errors.New("not implemented yet")
}

func (s *WebTransportServerQuic) communicate(sess quic.Session) error {
return errors.New("not implemented yet")
}

As you can see to accept a unidirectional stream with data we can use .AcceptUniStream method of quic.Session. After accepting a stream we should read client indication data from it.

According to spec it will contain a client indication in the following format:

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key (16) | Length (16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Value (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The code below parses client indication out of a stream data, we decode key-value pairs from uni stream until an end of stream (indicated by EOF):

func receiveClientIndication(stream quic.ReceiveStream) (ClientIndication, error) {
var clientIndication ClientIndication

// read no more than maxClientIndicationLength bytes.
reader := io.LimitReader(stream, maxClientIndicationLength)

done := false

for {
if done {
break
}
var key int16
err := binary.Read(reader, binary.BigEndian, &key)
if err != nil {
if err == io.EOF {
done = true
} else {
return clientIndication, err
}
}
var valueLength int16
err = binary.Read(reader, binary.BigEndian, &valueLength)
if err != nil {
return clientIndication, err
}
buf := make([]byte, valueLength)
n, err := reader.Read(buf)
if err != nil {
if err == io.EOF {
// still need to process indication value.
done = true
} else {
return clientIndication, err
}
}
if int16(n) != valueLength {
return clientIndication, errors.New("read less than expected")
}
value := string(buf)

switch clientIndicationKey(key) {
case clientIndicationKeyOrigin:
clientIndication.Origin = value
case clientIndicationKeyPath:
clientIndication.Path = value
default:
log.Printf("skip unknown client indication key: %d: %s", key, value)
}
}
return clientIndication, nil
}

We also validate Origin inside validateClientIndication method of our server:

var errBadOrigin = errors.New("bad origin")

func (s *WebTransportServerQuic) validateClientIndication(indication ClientIndication) error {
u, err := url.Parse(indication.Origin)
if err != nil {
return errBadOrigin
}
if !stringInSlice(u.Host, s.config.AllowedOrigins) {
return errBadOrigin
}
return nil
}

func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}

Do you have stringInSlice function in every Go project? I do :)

Communicating over bidirectional streams

The final part here is accepting a bidirectional stream from a client, reading it, and sending responses back. Here we will just echo everything a client sends to a server back to a client. You can implement whatever bidirectional communication you want actually.

Very similar to unidirectional case we can call .AcceptStream method of session to accept a bidirectional stream.

func (s *WebTransportServerQuic) communicate(sess quic.Session) error {
for {
stream, err := sess.AcceptStream(context.Background())
if err != nil {
return err
}
log.Printf("stream accepted: %d", stream.StreamID())
if _, err := io.Copy(stream, stream); err != nil {
return err
}
}
}

When you press Send button in client example it creates a bidirectional stream, sends data to it, then closes stream. Thus our code is sufficient. For a more complex communication that involves many concurrent streams you will have to write a more complex code that allows working with streams concurrently on server side.

client example

Full server example

Full server code can be found in a Gist. Again – this is a toy example based on things that all work in progress.

Conclusion

WebTransport is an interesting technology that can open new possibilities in modern Web development. At this moment it's possible to play with it using QUIC transport – here we looked at how one can do that. Though we still have to wait a bit until all these things will be suitable for production usage.

Also, even when ready we will still have to think about WebTransport fallback options – since wide adoption of browsers that support some new technology and infrastructure takes time. Actually WebTransport spec authors consider fallback options in design. This was mentioned in IETF slides (PDF, 2.6MB), but I have not found any additional information beyond that.

Personally, I think the most exciting thing about WebTransport is the possibility to exchange UDP datagrams, which can help a lot to in-browser gaming. Unfortunately, we can't test it at this moment with Go (but it's already possible using Python as server as shown in the example).

WebTransport could be a nice candidate for a new Centrifugo transport next to WebSocket and SockJS – time will show.

- + \ No newline at end of file diff --git a/blog/2020/11/12/scaling-websocket.html b/blog/2020/11/12/scaling-websocket.html index efdc1064b..374d6ca3d 100644 --- a/blog/2020/11/12/scaling-websocket.html +++ b/blog/2020/11/12/scaling-websocket.html @@ -16,14 +16,14 @@ - +

Scaling WebSocket in Go and beyond

· 19 min read
Alexander Emelin

gopher-broker

I believe that in 2020 WebSocket is still an entertaining technology which is not so well-known and understood like HTTP. In this blog post I'd like to tell about state of WebSocket in Go language ecosystem, and a way we could write scalable WebSocket servers with Go and beyond Go.

We won't talk a lot about WebSocket transport pros and cons – I'll provide links to other resources on this topic. Most advices here are generic enough and can be easily approximated to other programming languages. Also in this post we won't talk about ready to use solutions (if you are looking for it – check out Real-time Web Technologies guide by Phil Leggetter), just general considerations. There is not so much information about scaling WebSocket on the internet so if you are interested in WebSocket and real-time messaging technologies - keep on reading.

If you don't know what WebSocket is – check out the following curious links:

As soon as you know WebSocket basics – we can proceed.

WebSocket server tasks

Speaking about scalable servers that work with many persistent WebSocket connections – I found several important tasks such a server should be able to do:

  • Maintain many active connections
  • Send many messages to clients
  • Support WebSocket fallback to scale to every client
  • Authenticate incoming connections and invalidate connections
  • Survive massive reconnect of all clients without loosing messages
note

Of course not all of these points equally important in various situations.

Below we will look at some tips which relate to these points.

one_hour_scale

WebSocket libraries

In Go language ecosystem we have several libraries which can be used as a building block for a WebSocket server.

Package golang.org/x/net/websocket is considered deprecated.

The default choice in the community is gorilla/websocket library. Made by Gary Burd (who also gifted us an awesome Redigo package to communicate with Redis) – it's widely used, performs well, has a very good API – so in most cases you should go with it. Some people think that library not actively maintained at moment – but this is not quite true, it implements full WebSocket RFC, so actually it can be considered done.

In 2018 my ex-colleague Sergey Kamardin open-sourced gobwas/ws library. It provides a bit lower-level API than gorilla/websocket thus allows reducing RAM usage per connection and has nice optimizations for WebSocket upgrade process. It does not support WebSocket permessage-deflate compression but otherwise a good alternative you can consider using. If you have not read Sergey's famous post A Million WebSockets and Go – make a bookmark!

One more library is nhooyr/websocket. It's the youngest one and actively maintained. It compiles to WASM which can be a cool thing for someone. The API is a bit different from what gorilla/websocket offers, and one of the big advantages I see is that it solves a problem with a proper WebSocket closing handshake which is a bit hard to do right with Gorilla WebSocket.

You can consider all listed libraries except one from x/net for your project. Take a library, follow its examples (make attention to goroutine-safety of various API operations). Personally I prefer Gorilla WebSocket at moment since it's feature-complete and battle tested by tons of projects around Go world.

OS tuning

OK, so you have chosen a library and built a server on top of it. As soon as you put it in production the interesting things start happening.

Let's start with several OS specific key things you should do to prepare for many connections from WebSocket clients.

Every connection will cost you an open file descriptor, so you should tune a maximum number of open file descriptors your process can use. An errors like too many open files raise due to OS limit on file descriptors which is usually 256-1024 by default (see with ulimit -n on Unix). A nice overview on how to do this on different systems can be found in Riak docs. Wanna more connections? Make this limit higher.

Nice tip here is to limit a maximum number of connections your process can serve – making it less than known file descriptor limit:

// ulimit -n == 65535
if conns.Len() >= 65500 {
return errors.New("connection limit reached")
}
conns.Add(conn)

– otherwise you have a risk to not even able to look at pprof when things go bad. And you always need monitoring of open file descriptors.

You can also consider using netutil.LimitListener for this task, but don't forget to put pprof on another port with another HTTP server instance in this case.

Keep attention on Ephemeral ports problem which is often happens between your load balancer and your WebSocket server. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple:

source ip | source port | destination ip | destination port

On balancer/server boundary you are limited in 65536 possible variants by default. But actually due to some OS limits and sockets in TIME_WAIT state the number is even less. A very good explanation and how to deal with it can be found in Pusher blog.

Your possible number of connections also limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.

One more thing you can do is tune your network stack for performance. Do this only if you understand that you need it. Maybe start with this gist, but don't optimize without full understanding why you are doing this.

Sending many messages

Now let's speak about sending many messages. The general tips follows.

Make payload smaller. This is obvious – fewer data means more effective work on all layers. BTW WebSocket framing overhead is minimal and adds only 2-8 bytes to your payload. You can read detailed dedicated research in Dissecting WebSocket's Overhead article. You can reduce an amount of data traveling over network with permessage-deflate WebSocket extension, so your data will be compressed. Though using permessage-deflate is not always a good thing for server due to poor performance of flate, so you should be prepared for a CPU and RAM resource usage on server side. While Gorilla WebSocket has a lot of optimizations internally by reusing flate writers, overhead is still noticeable. The increase value heavily depends on your load profile.

Make less system calls. Every syscall will have a constant overhead, and actually in WebSocket server under load you will mostly see read and write system calls in your CPU profiles. An advice here – try to use client-server protocol that supports message batching, so you can join individual messages together.

Use effective message serialization protocol. Maybe use code generation for JSON to avoid extensive usage of reflect package done by Go std lib. Maybe use sth like gogo/protobuf package which allows to speedup Protobuf marshalling and unmarshalling. Unfortunately Gogo Protobuf is going through hard times at this moment. Try to serialize a message only once when sending to many subscribers.

Have a way to scale to several machines - more power, more possible messages. We will talk about this very soon.

WebSocket fallback transport

ie

Even in 2020 there are still users which cannot establish connection with WebSocket server. Actually the problem mostly appears with browsers. Some users still use old browsers. But they have a choice – install a newer browser. Still, there could also be users behind corporate proxies. Employees can have a trusted certificate installed on their machine so company proxy can re-encrypt even TLS traffic. Also, some browser extensions can block WebSocket traffic.

One ready solution to this is Sockjs-Go library. This is a mature library that provides fallback transport for WebSocket. If client does not succeed with WebSocket connection establishment then client can use some of HTTP transports for client-server communication: EventSource aka Server-Sent Events, XHR-streaming, Long-Polling etc. The downside with those transports is that to achieve bidirectional communication you should use sticky sessions on your load balancer since SockJS keeps connection session state in process memory. We will talk about many instances of your WebSocket server very soon.

You can implement WebSocket fallback yourself, this should be simple if you have a sliding window message stream on your backend which we will discuss very soon.

Maybe look at GRPC, depending on application it could be better or worse than WebSocket – in general you can expect a better performance and less resource consumption from WebSocket for bidirectional communication case. My measurements for a bidirectional scenario showed 3x win for WebSocket (binary + GOGO protobuf) in terms of server CPU consumption and 4 times less RAM per connection. Though if you only need RPC then GRPC can be a better choice. But you need additional proxy to work with GRPC from a browser.

Performance is not scalability

You can optimize client-server protocol, tune your OS, but at some point you won't be able to use only one process on one server machine. You need to scale connections and work your server does over different server machines. Horizontal scaling is also good for a server high availability. Actually there are some sort of real-time applications where a single isolated process makes sense - for example multiplayer games where limited number of players play independent game rounds.

many_instances

As soon as you distribute connections over several machines you have to find a way to deliver a message to a certain user. The basic approach here is to publish messages to all server instances. This can work but this does not scale well. You need a sort of instance discovery to make this less painful.

Here comes PUB/SUB, where you can connect WebSocket server instances over central PUB/SUB broker. Clients that establish connections with your WebSocket server subscribe to topics (channels) in a broker, and as soon as you publish a message to that topic it will be delivered to all active subscribers on WebSocket server instances. If server node does not have interested subscriber then it won't get a message from a broker thus you are getting effective network communication.

Actually the main picture of this post illustrates exactly this architecture:

gopher-broker

Let's think about requirements for a broker for real-time messaging application. We want a broker:

  • with reasonable performance and possibility to scale
  • which maintains message order in topics
  • can support millions of topics, where each topic should be ephemeral and lightweight – topics can be created when user comes to application and removed after user goes away
  • possibility to keep a sliding window of messages inside channel to help us survive massive reconnect scenario (will talk about this later below, can be a separate part from broker actually)

Personally when we talk about such brokers here are some options that come into my mind:

Sure there are more exist including libraries like ZeroMQ or nanomsg.

Below I'll try to consider these solutions for the task of making scalable WebSocket server facing many user connections from Internet.

If you are looking for unreliable at most once PUB/SUB then any of solutions mentioned above should be sufficient. Many real-time messaging apps are ok with at most once guarantee delivery.

If you don't want to miss messages then things are a bit harder. Let's try to evaluate these options for a task where application has lots of different topics from which it wants to receive messages with at least once guarantee (having a personal topic per client is common thing in applications). A short analysis below can be a bit biased, but I believe thoughts are reasonable enough. I did not found enough information on the internet about scaling WebSocket beyond a single server process, so I'll try to fill the gap a little based on my personal knowledge without pretending to be absolutely objective in these considerations.

In some posts on the internet about scaling WebSocket I saw advices to use RabbitMQ for PUB/SUB stuff in real-time messaging server. While this is a great messaging server, it does not like a high rate of queue bind and unbind type of load. It will work, but you will need to use a lot of server resources for not so big number of clients (imagine having millions of queues inside RabbitMQ). I have an example from my practice where RabbitMQ consumed about 70 CPU cores to serve real-time messages for 100k online connections. After replacing it with Redis keeping the same message delivery semantics we got only 0.3 CPU consumption on broker side.

Kafka and Pulsar are great solutions, but not for this task I believe. The problem is again in dynamic ephemeral nature of our topics. Kafka also likes a more stable configuration of its topics. Keeping messages on disk can be an overkill for real-time messaging task. Also your consumers on Kafka server should pull from millions of different topics, not sure how well it performs, but my thoughts at moment - this should not perform very well. Kafka itself scales perfectly, you will definitely be able to achieve a goal but resource usage will be significant. Here is a post from Trello where they moved from RabbitMQ to Kafka for similar real-time messaging task and got about 5x resource usage improvements. Note also that the more partitions you have the more heavy failover process you get.

Nats and Nats-Streaming. Raw Nats can only provide at most once guarantee. BTW recently Nats developers released native WebSocket support, so you can consider it for your application. Nats-Streaming server as broker will allow you to not lose messages. To be fair I don't have enough information about how well Nats-Streaming scales to millions of topics. An upcoming Jetstream which will be a part of Nats server can also be an interesting option – like Kafka it provides a persistent stream of messages for at least once delivery semantics. But again, it involves disk storage, a nice thing for backend microservices communication but can be an overkill for real-time messaging task.

Sure Tarantool can fit to this task well too. It's fast, im-memory and flexible. Some possible problems with Tarantool are not so healthy state of its client libraries, complexity and the fact that it's heavily enterprise-oriented. You should invest enough time to benefit from it, but this can worth it actually. See an article on how to do a performant broker for WebSocket applications with Tarantool.

Building PUB/SUB system on top of ZeroMQ will require you to build separate broker yourself. This could be an unnecessary complexity for your system. It's possible to implement PUB/SUB pattern with ZeroMQ and nanomsg without a central broker, but in this case messages without active subscribers on a server will be dropped on a consumer side thus all publications will travel to all server nodes.

My personal choice at moment is Redis. While Redis PUB/SUB itself provides at most once guarantee, you can build at least once delivery on top of PUB/SUB and Redis data structures (though this can be challenging enough). Redis is very fast (especially when using pipelining protocol feature), and what is more important – very predictable. It gives you a good understanding of operation time complexity. You can shard topics over different Redis instances running in HA setup - with Sentinel or with Redis Cluster. It allows writing LUA procedures with some advanced logic which can be uploaded over client protocol thus feels like ordinary commands. You can use Redis to keep sliding window event stream which gives you access to missed messages from a certain position. We will talk about this later.

OK, the end of opinionated thoughts here :)

Depending on your choice the implementation of your system will vary and will have different properties – so try to evaluate possible solutions based on your application requirements. Anyway, whatever broker will be your choice, try to follow this rules to build effective PUB/SUB system:

  • take into account message delivery guarantees of your system: at most once or at least once, ideally you should have an option to have both for different real-time features in your app
  • make sure to use one or pool of connections between your server and a broker, don't create new connection per each client or topic that comes to your WebSocket server
  • use effective serialization format between your WebSocket server and broker

Massive reconnect

mass_reconnect

Let's talk about one more problem that is unique for Websocket servers compared to HTTP. Your app can have thousands or millions of active WebSocket connections. In contract to stateless HTTP APIs your application is stateful. It uses push model. As soon as you deploying your WebSocket server or reload your load balancer (Nginx maybe) – connections got dropped and all that army of users start reconnecting. And this can be like an avalanche actually. How to survive?

First of all - use exponential backoff strategies on client side. I.e. reconnect with intervals like 1, 2, 4, 8, 16 seconds with some random jitter.

Turn on various rate limiting strategies on your WebSocket server, some of them should be turned on your backend load balancer level (like controlling TCP connection establishment rate), some are application specific (maybe limit an amount of requests from certain user).

One more interesting technique to survive massive reconnect is using JWT (JSON Web Token) for authentication. I'll try to explain why this can be useful.

jwt

As soon as your client start reconnecting you will have to authenticate each connection. In massive setups with many persistent connection this can be a very significant load on your Session backend. Since you need an extra request to your session storage for every client coming back. This can be a no problem for some infrastructures but can be really disastrous for others. JWT allows to reduce this spike in load on session storage since it can have all required authentication information inside its payload. When using JWT make sure you have chosen a reasonable JWT expiration time – expiration interval depends on your application nature and just one of trade-offs you should deal with as developer.

Don't forget about making an effective connection between your WebSocket server and broker – as soon as all clients start reconnecting you should resubscribe your server nodes to all topics as fast as possible. Use techniques like smart batching at this moment.

Let's look at a small piece of code that demonstrates this technique. Imagine we have a source channel from which we get items to process. We don’t want to process items individually but in batch. For this we wait for first item coming from channel, then try to collect as many items from channel buffer as we want without blocking and timeouts involved. And then process slice of items we collected at once. For example build Redis pipeline from them and send to Redis in one connection write call.

maxBatchSize := 50

for {
select {
case item := <-sourceCh:
batch := []string{item}
loop:
for len(batch) < maxBatchSize {
select {
case item := <-sourceCh:
batch = append(batch, item)
default:
break loop
}
}
// Do sth with collected batch of items.
println(len(batch))
}
}

Look at a complete example in a Go playground: https://play.golang.org/p/u7SAGOLmDke.

I also made a repo where I demonstrate how this technique together with Redis pipelining feature allows to fully utilize connection for a good performance https://github.com/FZambia/redigo-smart-batching.

Another advice for those who run WebSocket services in Kubernetes. Learn how your ingress behaves – for example Nginx ingress can reload its configuration on every change inside Kubernetes services map resulting into closing all active WebSocket connections. Proxies like Envoy don't have this behaviour, so you can reduce number of mass disconnections in your system. You can also proxy WebSocket without using ingress at all over configured WebSocket service NodePort.

Message event stream benefits

Here comes a final part of this post. Maybe the most important one.

Not only mass client re-connections could create a significant load on a session backend but also a huge load on your main application database. Why? Because WebSocket applications are stateful. Clients rely on a stream of messages coming from a backend to maintain its state actual. As soon as connection dropped client tries to reconnect. In some scenarios it also wants to restore its actual state. What if client reconnected after 3 seconds? How many state updates it could miss? Nobody knows. So to make sure state is actual client tries to get it from application database. This is again a significant spike in load on your main database in massive reconnect scenario. In can be really painful with many active connections.

So what I think is nice to have for scenarios where we can't afford to miss messages (like in chat-like apps for example) is having effective and performant stream of messages inside each channel. Keep this stream in fast in-memory storage. This stream can have time retention and be limited in size (think about it as a sliding window of messages). I already mentioned that Redis can do this – it's possible to keep messages in Redis List or Redis Stream data structures. Other broker solutions could give you access to such a stream inside each channel out of the box.

So as soon as client reconnects it can restore its state from fast in-memory event stream without even querying your database. Actually to survive mass reconnect scenario you don't need to keep such a stream for a long time – several minutes should be enough. You can even create your own Websocket fallback implementation (like Long-Polling) utilizing event stream with limited retention.

Conclusion

Hope advices given here will be useful for a reader and will help writing a more robust and more scalable real-time application backends.

Centrifugo server and Centrifuge library for Go language have most of the mechanics described here including the last one – message stream for topics limited by size and retention period. Both also have techniques to prevent message loss due to at most once nature of Redis PUB/SUB giving at least once delivery guarantee inside message history window size and retention period.

- + \ No newline at end of file diff --git a/blog/2021/01/15/centrifuge-intro.html b/blog/2021/01/15/centrifuge-intro.html index 2fe0607b9..c5375e14d 100644 --- a/blog/2021/01/15/centrifuge-intro.html +++ b/blog/2021/01/15/centrifuge-intro.html @@ -16,13 +16,13 @@ - +

Centrifuge – real-time messaging with Go

· 23 min read
Alexander Emelin

Centrifuge

In this post I'll try to introduce Centrifuge - the heart of Centrifugo.

Centrifuge is a real-time messaging library for the Go language.

This post is going to be pretty long (looks like I am a huge fan of long reads) – so make sure you also have a drink (probably two) and let's go!

How it's all started

I wrote several blog posts before (for example this one – yep, it's on Medium...) about an original motivation of Centrifugo server.

danger

Centrifugo server is not the same as Centrifuge library for Go. It's a full-featured project built on top of Centrifuge library. Naming can be confusing, but it's not too hard once you spend some time with ecosystem.

In short – Centrifugo was implemented to help traditional web frameworks dealing with many persistent connections (like WebSocket or SockJS HTTP transports). So frameworks like Django or Ruby on Rails, or frameworks from the PHP world could be used on a backend but still provide real-time messaging features like chats, multiplayer browser games, etc for users. With a little help from Centrifugo.

Now there are cases when Centrifugo server used in conjunction even with a backend written in Go. While Go mostly has no problems dealing with many concurrent connections – Centrifugo provides some features beyond simple message passing between a client and a server. That makes it useful, especially since design is pretty non-obtrusive and fits well microservices world. Centrifugo is used in some well-known projects (like ManyChat, Yoola.io, Spot.im, Badoo etc).

At the end of 2018, I released Centrifugo v2 based on a real-time messaging library for Go language – Centrifuge – the subject of this post.

It was a pretty hard experience to decouple Centrifuge out of the monolithic Centrifugo server – I was unable to make all the things right immediately, so Centrifuge library API went through several iterations where I introduced backward-incompatible changes. All those changes targeted to make Centrifuge a more generic tool and remove opinionated or limiting parts.

So what is Centrifuge?

This is ... well, a framework to build real-time messaging applications with Go language. If you ever heard about socket.io – then you can think about Centrifuge as an analogue. I think the most popular applications these days are chats of different forms, but I want to emphasize that Centrifuge is not a framework to build chats – it's a generic instrument that can be used to create different sorts of real-time applications – real-time charts, multiplayer games.

The obvious choice for real-time messaging transport to achieve fast and cross-platform bidirectional communication these days is WebSocket. Especially if you are targeting a browser environment. You mostly don't need to use WebSocket HTTP polyfills in 2021 (though there are still corner cases so Centrifuge supports SockJS polyfill).

Centrifuge has its own custom protocol on top of plain WebSocket or SockJS frames.

The reason why Centrifuge has its own protocol on top of underlying transport is that it provides several useful primitives to build real-time applications. The protocol described as strict Protobuf schema. It's possible to pass JSON or binary Protobuf-encoded data over the wire with Centrifuge.

note

GRPC is very handy these days too (and can be used in a browser with a help of additional proxies), some developers prefer using it for real-time messaging apps – especially when one-way communication needed. It can be a bit better from integration perspective but more resource-consuming on server side and a bit trickier to deploy.

note

Take a look at WebTransport – a brand-new spec for web browsers to allow fast communication between a client and a server on top of QUIC – it may be a good alternative to WebSocket in the future. This in a draft status at the moment, but it's already possible to play with in Chrome.

Own protocol is one of the things that prove the framework status of Centrifuge. This dictates certain limits (for example, you can't just use an alternative message encoding) and makes developers use custom client connectors on a front-end side to communicate with a Centrifuge-based server (see more about connectors in ecosystem part).

But protocol solves many practical tasks – and here we are going to look at real-time features it provides for a developer.

Centrifuge Node

To start working with Centrifuge you need to start Centrifuge server Node. Node is a core of Centrifuge – it has many useful methods – set event handlers, publish messages to channels, etc. We will look at some events and channels concept very soon.

Also, Node abstracts away scalability aspects, so you don't need to think about how to scale WebSocket connections over different server instances and still have a way to deliver published messages to interested clients.

For now, let's start a single instance of Node that will serve connections for us:

node, err := centrifuge.New(centrifuge.DefaultConfig)
if err != nil {
log.Fatal(err)
}

if err := node.Run(); err != nil {
log.Fatal(err)
}

It's also required to serve a WebSocket handler – this is possible just by registering centrifuge.WebsocketHandler in HTTP mux:

wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})
http.Handle("/connection/websocket", wsHandler)

Now it's possible to connect to a server (using Centrifuge connector for a browser called centrifuge-js):

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.connect();

Though connection will be rejected by the server since we also need to provide authentication details – Centrifuge expects explicitly provided connection Credentials to accept connection.

Authentication

Let's look at how we can tell Centrifuge details about connected user identity, so it could accept an incoming connection.

There are two main ways to authenticate client connection in Centrifuge.

The first one is over the native middleware mechanism. It's possible to wrap centrifuge.WebsocketHandler or centrifuge.SockjsHandler with middleware that checks user authentication and tells Centrifuge current user ID over context.Context:

func auth(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cred := &centrifuge.Credentials{
UserID: "42",
}
newCtx := centrifuge.SetCredentials(r.Context(), cred)
r = r.WithContext(newCtx)
h.ServeHTTP(w, r)
})
}

So WebsocketHandler can be registered this way (note that a handler now wrapped by auth middleware):

wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})
http.Handle("/connection/websocket", auth(wsHandler))

Another authentication way is a bit more generic – developers can authenticate connection based on custom token sent from a client inside first WebSocket/SockJS frame. This is called connect frame in terms of Centrifuge protocol. Any string token can be set – this opens a way to use JWT, Paceto, and any other kind of authentication tokens. For example see an authenticaton with JWT.

note

BTW it's also possible to pass any information from client side with a first connect message from client to server and return custom information about server state to a client. This is out of post scope though.

Nothing prevents you to integrate Centrifuge with OAuth2 or another framework session mechanism – like Gin for example.

Channel subscriptions

As soon as a client connected and successfully authenticated it can subscribe to channels. Channel (room or topic in other systems) is a lightweight and ephemeral entity in Centrifuge. Channel can have different features (we will look at some channel features below). Channels created automatically as soon as the first subscriber joins and destroyed as soon as the last subscriber left.

The application can have many real-time features – even on one app screen. So sometimes client subscribes to several channels – each related to a specific real-time feature (for example one channel for chat updates, one channel likes notification stream, etc).

Channel is just an ASCII string. A developer is responsible to find the best channel naming convention suitable for an application. Channel naming convention is an important aspect since in many cases developers want to authorize subscription to a channel on the server side – so only authorized users could listen to specific channel updates.

Let's look at a basic subscription example on the client-side:

centrifuge.subscribe('example', function(msgCtx) {
console.log(msgCtx)
})

On the server-side, you need to define subscribe event handler. If subscribe event handler not set then the connection won't be able to subscribe to channels at all. Subscribe event handler is where a developer may check permissions of the current connection to read channel updates. Here is a basic example of subscribe event handler that simply allows subscriptions to channel example for all authenticated connections and reject subscriptions to all other channels:

node.OnConnect(func(client *centrifuge.Client) {
client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) {
if e.Channel != "example" {
cb(centrifuge.SubscribeReply{}, centrifuge.ErrorPermissionDenied)
return
}
cb(centrifuge.SubscribeReply{}, nil)
})
})

You may notice a callback style of reacting to connection related things. While not being very idiomatic for Go it's very practical actually. The reason why we use callback style inside client event handlers is that it gives a developer possibility to control operation concurrency (i.e. process sth in separate goroutines or goroutine pool) and still control the order of events. See an example that demonstrates concurrency control in action.

Now if some event published to a channel:

// Here is how we can publish data to a channel.
node.Publish("example", []byte(`{"input": "hello"}`))

– data will be delivered to a subscribed client, and message will be printed to Javascript console. PUB/SUB in its usual form.

note

Though Centrifuge protocol based on Protobuf schema in example above we published a JSON message into a channel. By default, we can only send JSON to connections since default protocol format is JSON. But we can switch to Protobuf-based binary protocol by connecting to ws://localhost:8000/connection/websocket?format=protobuf endpoint – then it's possible to send binary data to clients.

Async message passing

While Centrifuge mostly shines when you need channel semantics it's also possible to send any data to connection directly – to achieve bidirectional asynchronous communication, just what a native WebSocket provides.

To send a message to a server one can use the send method on the client-side:

centrifuge.send({"input": "hello"});

On the server-side data will be available inside a message handler:

client.OnMessage(func(e centrifuge.MessageEvent) {
log.Printf("message from client: %s", e.Data)
})

And vice-versa, to send data to a client use Send method of centrifuge.Client:

client.Send([]byte(`{"input": "hello"}`))

To listen to it on the client-side:

centrifuge.on('message', function(data) {
console.log(data);
});

RPC

RPC is a primitive for sending a request from a client to a server and waiting for a response (in this case all communication still happens via asynchronous message passing internally, but Centrifuge takes care of matching response data to request previously sent).

On client side it's as simple as:

const resp = await centrifuge.namedRPC('my_method', {});

On server side RPC event handler should be set to make calls available:

client.OnRPC(func(e centrifuge.RPCEvent, cb centrifuge.RPCCallback) {
if e.Method == "my_method" {
cb(centrifuge.RPCReply{Data: []byte(`{"result": "42"}`)}, nil)
return
}
cb(centrifuge.RPCReply{}, centrifuge.ErrorMethodNotFound)
})

Note, that it's possible to pass the name of RPC and depending on it and custom request params return different results to a client – just like a regular HTTP request but over asynchronous WebSocket (or SockJS) connection.

Server-side subscriptions

In many cases, a client is a source of knowledge which channels it wants to subscribe to on a specific application screen. But sometimes you want to control subscriptions to channels on a server-side. This is also possible in Centrifuge.

It's possible to provide a slice of channels to subscribe connection to at the moment of connection establishment phase:

node.OnConnecting(func(ctx context.Context, e centrifuge.ConnectEvent) (centrifuge.ConnectReply, error) {
return centrifuge.ConnectReply{
Subscriptions: map[string]centrifuge.SubscribeOptions{
"example": {},
},
}, nil
})

Note, that OnConnecting does not follow callback-style – this is because it can only happen once at the start of each connection – so there is no need to control operation concurrency.

In this case on the client-side you will have access to messages published to channels by listening to on('publish') event:

centrifuge.on('publish', function(msgCtx) {
console.log(msgCtx);
});

Also, centrifuge.Client has Subscribe and Unsubscribe methods so it's possible to subscribe/unsubscribe client to/from channel somewhere in the middle of its long WebSocket session.

Windowed history in channel

Every time a message published to a channel it's possible to provide custom history options. For example:

node.Publish(
"example",
[]byte(`{"input": "hello"}`),
centrifuge.WithHistory(300, time.Minute),
)

In this case, Centrifuge will maintain a windowed Publication cache for a channel - or in other words, maintain a publication stream. This stream will have time retention (one minute in the example above) and the maximum size will be limited to the value provided during Publish (300 in the example above).

Every message inside a history stream has an incremental offset field. Also, a stream has a field called epoch – this is a unique identifier of stream generation - thus client will have a possibility to distinguish situations where a stream is completely removed and there is no guarantee that no messages have been lost in between even if offset looks fine.

Client protocol provides a possibility to paginate over a stream from a certain position with a limit:

const streamPosition = {'offset': 0, epoch: 'xyz'} 
resp = await sub.history({since: streamPosition, limit: 10});

Iteration over history stream is a new feature which is just merged into Centrifuge master branch and can only be used from Javascript client at the moment.

Also, Centrifuge has an automatic message recovery feature. Automatic recovery is very useful in scenarios when tons of persistent connections start reconnecting at once. I already described why this is useful in one of my previous posts about Websocket scalability. In short – since WebSocket connections are stateful then at the moment of mass reconnect they can create a very big spike in load on your main application database. Such mass reconnects are a usual thing in practice - for example when you reload your load balancers or re-deploying the Websocket server (new code version).

Of course, recovery can also be useful for regular short network disconnects - when a user travels in the subway for example. But you always need a way to load an actual state from the main application database in case of an unsuccessful recovery.

To enable automatic recovery you can provide the Recover flag in subscribe options:

client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) {
cb(centrifuge.SubscribeReply{
Options: centrifuge.SubscribeOptions{
Recover: true,
},
}, nil)
})

Obviously, recovery will work only for channels where history stream maintained. The limitation in recovery is that all missed publications sent to client in one protocol frame – pagination is not supported during recovery process. This means that recovery is mostly effective for not too long offline time without tons of missed messages.

Online presence and presence stats

Another cool thing Centrifuge exposes to developers is online presence information for channels. Presence information contains a list of active channel subscribers. This is useful to show the online status of players in a game for example.

Also, it's possible to turn on Join/Leave message feature inside channels: so each time connection subscribes to a channel all channel subscribers receive a Join message with client information (client ID, user ID). As soon as the client unsubscribes Leave message is sent to remaining channel subscribers with information who left a channel.

Here is how to enable both online presence and join/leave features for a subscription to channel:

client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) {
cb(centrifuge.SubscribeReply{
Options: centrifuge.SubscribeOptions{
Presence: true,
JoinLeave: true,
},
}, nil)
})

On a client-side then it's possible to call for the presence and setting event handler for join/leave messages.

The important thing to be aware of when using Join/Leave messages is that this feature can dramatically increase CPU utilization and overall traffic in channels with a big number of active subscribers – since on every client connect/disconnect event such Join or Leave message must be sent to all subscribers. The advice here – avoid using Join/Leave messages or be ready to scale (Join/Leave messages scale well when adding more Centrifuge Nodes – more about scalability below).

One more thing to remember is that online presence information can also be pretty expensive to request in channels with many active subscribers – since it returns information about all connections – thus payload in response can be large. To help a bit with this situation Centrifuge has a presence stats client API method. Presence stats only contain two counters: the number of active connections in the channel and amount of unique users in the channel.

If you still need to somehow process online presence in rooms with a massive number of active subscribers – then I think you better do it in near real-time - for example with fast OLAP like ClickHouse.

Scalability aspects

To be fair it's not too hard to implement most of the features above inside one in-memory process. Yes, it takes time, but the code is mostly straightforward. When it comes to scalability things tend to be a bit harder.

Centrifuge designed with the idea in mind that one machine is not enough to handle all application WebSocket connections. Connections should scale over application backend instances, and it should be simple to add more application nodes when the amount of users (connections) grows.

Centrifuge abstracts scalability over the Node instance and two interfaces: Broker interface and PresenceManager interface.

A broker is responsible for PUB/SUB and streaming semantics:

type Broker interface {
Run(BrokerEventHandler) error
Subscribe(ch string) error
Unsubscribe(ch string) error
Publish(ch string, data []byte, opts PublishOptions) (StreamPosition, error)
PublishJoin(ch string, info *ClientInfo) error
PublishLeave(ch string, info *ClientInfo) error
PublishControl(data []byte, nodeID string) error
History(ch string, filter HistoryFilter) ([]*Publication, StreamPosition, error)
RemoveHistory(ch string) error
}

See full version with comments in source code.

Every Centrifuge Node subscribes to channels via a broker. This provides a possibility to scale connections over many node instances – published messages will flow only to nodes with active channel subscribers.

It's and important thing to combine PUB/SUB with history inside a Broker implementation to achieve an atomicity of saving message into history stream and publishing it to PUB/SUB with generated offset.

PresenceManager is responsible for online presence information management:

type PresenceManager interface {
Presence(ch string) (map[string]*ClientInfo, error)
PresenceStats(ch string) (PresenceStats, error)
AddPresence(ch string, clientID string, info *ClientInfo, expire time.Duration) error
RemovePresence(ch string, clientID string) error
}

Full code with comments.

Broker and PresenceManager together form an Engine interface:

type Engine interface {
Broker
PresenceManager
}

By default, Centrifuge uses MemoryEngine that does not use any external services but limits developers to using only one Centrifuge Node (i.e. one server instance). Memory Engine is fast and can be suitable for some scenarios - even in production (with configured backup instance) – but as soon as the number of connections grows – you may need to load balance connections to different server instances. Here comes the Redis Engine.

Redis Engine utilizes Redis for Broker and PresenceManager parts.

History cache saved to Redis STREAM or Redis LIST data structures. For presence, Centrifuge uses a combination of HASH and ZSET structures.

Centrifuge tries to fully utilize the connection between Node and Redis by using pipelining where possible and smart batching technique. All operations done in a single RTT with the help of Lua scripts loaded automatically to Redis on engine start.

Redis is pretty fast and will allow your app to scale to some limits. When Redis starts being a bottleneck it's possible to shard data over different Redis instances. Client-side consistent sharding is built-in in Centrifuge and allows scaling further.

It's also possible to achieve Redis's high availability with built-in Sentinel support. Redis Cluster supported too. So Redis Engine covers many options to communicate with Redis deployed in different ways.

At Avito we served about 800k active connections in the messenger app with ease using a slightly adapted Centrifuge Redis Engine, so an approach proved to be working for rather big applications. We will look at some more concrete numbers below in the performance section.

Both Broker and PresenceManager are pluggable, so it's possible to replace them with alternative implementations. Examples show how to use Nats server for at most once only PUB/SUB together with Centrifuge. Also, we have an example of full-featured Engine for Tarantool database – Tarantool Engine shows even better throughput for history and presence operations than Redis-based Engine (up to 10x for some ops).

Order and delivery properties

Since Centrifuge is a messaging system I also want to describe its order and message delivery guarantees.

Message ordering in channels supported. As soon as you publish messages into channels one after another of course.

Message delivery model is at most once by default. This is mostly comes from PUB/SUB model – message can be dropped on Centrifuge level if subscriber is offline or simply on broker level – since Redis PUB/SUB also works with at most once guarantee.

Though if you maintain history stream inside a channel then things become a bit different. In this case you can tell Centrifuge to check client position inside stream. Since every publication has a unique incremental offset Centrifuge can track that client has correct offset inside a channel stream. If Centrifuge detects any missed messages it disconnects a client with special code – thus make it reconnect and recover messages from history stream. Since a message first saved to history stream and then published to PUB/SUB inside broker these mechanisms allow achieving at least once message delivery guarantee.

What happens on publish

Even if stream completely expired or dropped from broker memory Centrifuge will give a client a tip that messages could be lost – so client has a chance to restore state from a main application database.

Ecosystem

Here I want to be fair with my readers – Centrifuge is not ideal. This is a project maintained mostly by one person at the moment with all consequences. This hits an ecosystem a lot, can make some design choices opinionated or non-optimal.

I mentioned in the first post that Centrifuge built on top of the custom protocol. The protocol is based on a strict Protobuf schema, works with JSON and binary data transfer, supports many features. But – this means that to connect to the Centrifuge-based server developers have to use custom connectors that can speak with Centrifuge over its custom protocol.

The difficulty here is that protocol is asynchronous. Asynchronous protocols are harder to implement than synchronous ones. Multiplexing frames allows achieving good performance and fully utilize a single connection – but it hurts simplicity.

At this moment Centrifuge has client connectors for:

Not all clients support all protocol features. Another drawback is that all clients do not have a persistent maintainer – I mostly maintain everything myself. Connectors can have non-idiomatic and pretty dumb code since I had no previous experience with mobile development, they lack proper tests and documentation. This is unfortunate.

The good thing is that all connectors feel very similar, I am quickly releasing new versions when someone sends a pull request with improvements or bug fixes. So all connectors are alive.

I maintain a feature matrix in connectors to let users understand what's supported. Actually feature support is pretty nice throughout all these connectors - there are only several things missing and not so much work required to make all connectors full-featured. But I really need help here.

It will be a big mistake to not mention Centrifugo as a big plus for Centrifuge library ecosystem. Centrifugo is a server deployed in many projects throughout the world. Many features of Centrifuge library and its connectors have already been tested by Centrifugo users.

One more thing to mention is that Centrifuge does not have v1 release. It still evolves – I believe that the most dramatic changes have already been made and backward compatibility issues will be minimal in the next releases – but can't say for sure.

Performance

I made a test stand in Kubernetes with one million connections.

I can't call this a proper benchmark – since in a benchmark your main goal is to destroy a system, in my test I just achieved some reasonable numbers on limited hardware. These numbers should give a good insight into a possible throughput, latency, and estimate hardware requirements (at least approximately).

Connections landed on different server pods, 5 Redis instances have been used to scale connections between pods.

The detailed test stand description can be found in Centrifugo documentation.

Benchmark

Some quick conclusions are:

  • One connection costs about 30kb of RAM
  • Redis broker CPU utilization increases linearly with more messages traveling around
  • 1 million connections with 500k delivered messages per second with 200ms delivery latency in 99 percentile can be served with hardware amount equal to one modern physical server machine. The possible amount of messages can vary a lot depending on the number of channel subscribers though.

Limitations

Centrifuge does not allow subscribing on the same channel twice inside a single connection. It's not simple to add due to design decisions made – though there was no single user report about this in seven years of Centrifugo/Centrifuge history.

Centrifuge does not support wildcard subscriptions. Not only because I never needed this myself but also due to some design choices made – so be aware of this.

SockJS fallback does not support binary data - only JSON. If you want to use binary in your application then you can only use WebSocket with Centrifuge - there is no built-in fallback transport in this case.

SockJS also requires sticky session support from your load balancer to emulate a stateful bidirectional connection with its HTTP fallback transports. Ideally, Centrifuge will go away from SockJS at some point, maybe when WebTransport becomes mature so users will have a choice between WebTransport or WebSocket.

Websocket permessage-deflate compression supported (thanks to Gorilla WebSocket), but it can be pretty expensive in terms of CPU utilization and memory usage – the overhead depends on usage pattern, it's pretty hard to estimate in numbers.

As said above you cannot only rely on Centrifuge for state recovery – it's still required to have a way to fully load application state from the main database.

Also, I am not very happy with current error and disconnect handling throughout the connector ecosystem – this can be improved though, and I have some ideas for the future.

Examples

I am adding examples to _examples folder of Centrifuge repo. These examples completely cover Centrifuge API - including things not mentioned here.

Check out the tips & tricks section of README – it contains some additional insights about an implementation.

Conclusion

I think Centrifuge could be a nice alternative to socket.io - with a better performance, main server implementation in Go language, and even more builtin features to build real-time apps.

Centrifuge ecosystem definitely needs more work, especially in client connectors area, tutorials, community, stabilizing API, etc.

Centrifuge fits pretty well proprietary application development where time matters and deadlines are close, so developers tend to choose a ready solution instead of writing their own. I believe Centrifuge can be a great time saver here.

For Centrifugo server users Centrifuge package provides a way to write a more flexible server code adapted for business requirements but still use the same real-time core and have the same protocol features.

- + \ No newline at end of file diff --git a/blog/2021/08/31/hello-centrifugo-v3.html b/blog/2021/08/31/hello-centrifugo-v3.html index 863cc3277..55eb30124 100644 --- a/blog/2021/08/31/hello-centrifugo-v3.html +++ b/blog/2021/08/31/hello-centrifugo-v3.html @@ -16,13 +16,13 @@ - +

Centrifugo v3 released

· 15 min read
Centrifugal team

Centrifuge

After almost three years of Centrifugo v2 life cycle we are happy to announce the next major release of Centrifugo. During the last several months deep in our Centrifugal laboratory we had been synthesizing an improved version of the server.

New Centrifugo v3 is targeting to improve Centrifugo adoption for basic real-time application cases, improves server performance and extends existing features with new functionality. It comes with unidirectional real-time transports, protocol speedups, super-fast engine implementation based on Tarantool, new documentation site, GRPC proxy, API extensions and PRO version which provides unique possibilities for business adopters.

Centrifugo v2 flashbacks

Centrifugo v2 life cycle has come to an end. Before discussing v3 let's look back at what has been done during the last three years.

Centrifugo v2 was a pretty huge refactoring of v1. Since the v2 release, Centrifugo is built on top of new Centrifuge library for Go language. Centrifuge library evolved significantly since its initial release and now powers Grafana v8 real-time streaming among other things.

Here is an awesome demo made by my colleague Alexander Zobnin that demonstrates real-time telemetry of Assetto Corsa sports car streamed in real-time to Grafana dashboard:

Centrifugo integrated with Redis Streams, got Redis Cluster support, can now work with Nats server as a PUB/SUB broker. Notable additions of Centrifugo v2 were server-side subscriptions with some interesting features on top – like maintaining a single global connection from one user and automatic personal channel subscription upon user connect.

A very good addition which increased Centrifugo adoption a lot was introduction of proxy to backend. This made Centrifugo fit many setups where JWT authentication and existing subscription permission model did not suit well before.

Client ecosystem improved significantly. The fact that client protocol migrated to a strict Protobuf schema allowed to introduce binary protocol format (in addition to JSON) and simplify building client connectors. We now have much better and complete client libraries (compared to v1 situation).

We also have an official Helm chart, Grafana dashboard for Prometheus datasource, and so on.

Centrifugo is becoming more noticeable in a wider real-time technology community. For example, it was included in a periodic table of real-time created by Ably.com (one of the most powerful real-time messaging cloud services at the moment):

Of course, there are many aspects where Centrifugo can be improved. And v3 addresses some of them. Below we will look at the most notable features and changes of the new major Centrifugo version.

Backwards compatibility

Let's start with the most important thing – backwards compatibility concerns.

In Centrifugo v3 client protocol mostly stayed the same. We expect that most applications will be able to update without any change on a client-side. This was an important concern for v3 given how painful the update cycle can be on mobile devices and lessons learned from v1 to v2 migration. There is one breaking change though which can affect users who use history API manually from a client-side (we provide a temporary workaround to give apps a chance to migrate smoothly).

On a server-side, much more changes happened, especially in the configuration: some options were renamed, some were removed. We provide a v2 to v3 configuration converter which can help dealing with changes. In most cases, all you should do is adapt Centrifugo configuration to match v3 changes and redeploy Centrifugo using v3 build instead of v2. All features are still there (or a replacement exists, like for channels API).

For more details, refer to the v3 migration guide.

License change

As some of you know we considered changing Centrifugo license to AGPL v3 for a new release. After thinking a lot about this we decided to not step into this area.

But the license has been changed: the license of OSS Centrifugo is now Apache 2.0 instead of MIT. Apache 2.0 is also a permissive OSS license, it's just a bit more concrete in some aspects.

Unidirectional real-time transports

Server-side subscriptions introduced in Centrifugo v2 and recent improvements in the underlying Centrifuge library opened a road for a unidirectional approach.

This means that Centrifugo v3 provides a set of unidirectional real-time transports where messages flow only in one direction – from a server to a client. Why is this change important?

Centrifugo originally concentrated on using bidirectional transports for client-server communication. Like WebSocket and SockJS. Bidirectional transports allow implementing some great protocol features since a client can communicate with a server in various ways after establishing a persistent connection. While this is a great opportunity this also leads to an increased complexity.

Centrifugo users had to use special client connector libraries which abstracted underlying work into a simple public API. But internally connectors do many things: matching requests to responses, handling timeouts, handling an ordering, queuing operations, error handling. So the client connector is a pretty complex piece of software.

But what if a user just needs to receive real-time updates from a stable set of channels known in connection time? Can we simplify everything and avoid using custom software on a client-side?

With unidirectional transports, the answer is yes. Clients can now connect to Centrifugo using a bunch of unidirectional transports. And the greatest thing is that in this case, developers should not depend on Centrifugo client connectors at all – just use native browser APIs or GRPC-generated code. It's finally possible to consume events from Centrifugo using CURL (see an example).

Using unidirectional transports you can still benefit from Centrifugo built-in scalability with various engines, utilize built-in authentication over JWT or the connect proxy feature.

With subscribe server API (see below) it's even possible to subscribe unidirectional client to server-side channels dynamically. With refresh server API or the refresh proxy feature it's possible to manage a connection expiration.

Centrifugo supports the following unidirectional transports:

We expect that introducing unidirectional transports will significantly increase Centrifugo adoption.

History iteration API

There was a rather important limitation of Centrifugo history API – it was not very suitable for keeping large streams because a call to a history could only return the entire channel history.

Centrifugo v3 introduces an API to iterate over a stream. It's possible to do from the current stream beginning or end, in both directions – forward and backward, with configured limit. Also with certain starting stream position if it's known.

This, among other things, can help to implement manual missed message recovery on a client-side to reduce the load on the application backend.

Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun):

// Iterate by 10.
limit := 10
// Paginate in reversed order first, then invert it.
reverse := true
// Start with nil StreamPosition, then fill it with value while paginating.
var sp *gocent.StreamPosition

for {
historyResult, err = c.History(
ctx,
channel,
gocent.WithLimit(limit),
gocent.WithReverse(reverse),
gocent.WithSince(sp),
)
if err != nil {
log.Fatalf("Error calling history: %v", err)
}
for _, pub := range historyResult.Publications {
log.Println(pub.Offset, "=>", string(pub.Data))
sp = &gocent.StreamPosition{
Offset: pub.Offset,
Epoch: historyResult.Epoch,
}
}
if len(historyResult.Publications) < limit {
// Got all pubs, invert pagination direction.
reverse = !reverse
log.Println("end of stream reached, change iteration direction")
}
}
caution

This new API does not remove the need in having the main application database – that's still mandatory for idiomatic Centrifugo usage.

Redis Streams by default

In Centrifugo v3 Redis engine uses Redis Stream data structure by default for keeping channel history. Before v3 Redis Streams were supported by not enabled by default so almost nobody used them. This change is important in terms of introducing history iteration API described above – since Redis Streams allow doing iteration effectively.

Tarantool engine

As you may know, Centrifugo has several built-in engines that allow scaling Centrifugo nodes (using PUB/SUB) and keep shared history and presence state. Before v3 Centrifugo had in-memory and Redis (or KeyDB) engines available.

Introducing a new engine to Centrifugo is pretty hard since the engine should provide a very robust PUB/SUB performance, fast history and presence operations, possibility to publish a message to PUB/SUB and save to history atomically. It also should allow dealing with ephemeral frequently changing subscriptions. It's typical for Centrifugo use case to have millions of users each subscribed to a unique channel and constantly connecting/disconnecting (thus subscribing/unsubscribing).

In v3 we added experimental support for the Tarantool engine. It fits nicely all the requirements above and provides a huge performance speedup for history and presence operations compared to Redis. According to our benchmarks, the speedup can be up to 4-10x depending on operation. The PUB/SUB performance of Tarantool is comparable with Redis (10-20% worse according to our internal benchmarks to be exact, but that's pretty much the same).

For example, let's look at Centrifugo benchmark where we recover zero messages (i.e. emulate a situations when many connections disconnected for a very short time interval due to load balancer reload).

For Redis engine:

Redis engine, single Redis instance
BenchmarkRedisRecover       26883 ns/op     1204 B/op      28 allocs/op

Compare it with the same operation measured with Tarantool engine:

Tarantool engine, single Tarantool instance
BenchmarkTarantoolRecover    6292 ns/op      563 B/op      10 allocs/op

Tarantool can provide new storage properties (like synchronous replication), new adoption. We are pretty excited about adding it as an option.

The reason why Tarantool support is experimental is because Tarantool integration involves one more moving piece – the Centrifuge Lua module which should be run by a Tarantool server.

This increases deployment complexity and given the fact that many users have their own best practices in Tarantool deployment we are still evaluating a sufficient way to distribute Lua part. For now, we are targeting standalone (see examples in centrifugal/tarantool-centrifuge) and Cartridge Tarantool setups (with centrifugal/rotor).

Refer to the Tarantool Engine documentation for more details.

GRPC proxy

Centrifugo can now transform events received over persistent connections from users into GRPC calls to the application backend (in addition to the HTTP proxy available in v2).

GRPC support should make Centrifugo ready for today's microservice architecture where GRPC is a huge player for inter-service communication.

So we mostly just provide more choices for Centrifugo users here. GRPC has some good advantages – for example an application backend RPC layer which is responsible for communication with Centrifugo can now be generated from Protobuf definitions for all popular programming languages.

Server API improvements

Centrifugo v3 has some valuable server API improvements.

The new subscribe API method allows subscribing connection to a channel at any point in time. This works by utilizing server-side subscriptions. So it's not only possible to subscribe connection to a list of server-side channels during the connection establishment phase – but also later during the connection lifetime. This may be very useful for the unidirectional approach - by emulating client-side subscribe call over request to application backend which in turn calls subscribe Centrifugo server API.

Publish API now returns the current top stream position (offset and epoch) for channels with history enabled.

Server history API inherited iteration possibilities described above.

Channels command now returns a number of clients in a channel, also supports channel filtering by a pattern. Since we changed how channels call implemented internally there is no limitation anymore to call it when using Redis cluster.

Admin web UI has been updated too to support new API methods, so you can play with new API from its actions tab.

Better clustering

Centrifugo behaves a bit better in cluster mode: as soon as a node leaves a cluster gracefully (upon graceful termination) it sends a shutdown signal to the control channel thus giving other nodes a chance to immediately delete that node from the local registry.

Client improvements

While preparing the v3 release we improved client connectors too. All existing client connectors now actualized to the latest protocol, support server-side subscriptions, history API.

One important detail is that it's not required to set ?format=protobuf URL param now when connecting to Centrifugo from mobile devices - this is now managed internally by using the WebSocket subprotocol mechanism (requires using the latest client connector version and Centrifugo v3).

New documentation site

You are reading this post on a new project site. It's built with amazing Docusaurus.

A lot of documents were actualized, extended, and rewritten. We also now have new chapters like:

Server API and proxy documentation have been improved significantly.

Performance improvements

Centrifugo v3 has some notable performance improvements.

JSON client protocol now utilizes a couple of libraries (easyjson for encoding and segmentio/encoding for unmarshaling). Actually we use a slightly customized version of easyjson library to achieve even faster performance than it provides out-of-the-box. Changes allowed to speed up JSON encoding and decoding up to 4-5x for small messages. For large payloads speed up can be even more noticeable – we observed up to 30x performance boost when serializing 5kb messages.

For example, let's look at a JSON serialization benchmark result for 256 byte payload. Here is what we had before:

Centrifugo v2 JSON encoding/decoding
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkMarshal-12 5883 ns/op 1121 B/op 6 allocs/op
BenchmarkMarshalParallel-12 1009 ns/op 1121 B/op 6 allocs/op
BenchmarkUnmarshal-12 1717 ns/op 1328 B/op 16 allocs/op
BenchmarkUnmarshalParallel-12 492.2 ns/op 1328 B/op 16 allocs/op

And what we have now with mentioned JSON optimizations:

Centrifugo v3 JSON encoding/decoding
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkMarshal-12 461.3 ns/op 928 B/op 3 allocs/op
BenchmarkMarshalParallel-12 250.6 ns/op 928 B/op 3 allocs/op
BenchmarkUnmarshal-12 476.5 ns/op 136 B/op 3 allocs/op
BenchmarkUnmarshalParallel-12 107.2 ns/op 136 B/op 3 allocs/op
tip

Centrifugo Protobuf protocol is still faster than JSON for encoding/decoding on a server-side.

Of course, JSON encoding is only one part of Centrifugo – so you should not expect overall 4x performance improvement. But loaded setups should notice the difference and this should also be a good thing for reducing garbage collection pauses.

Centrifugo inherited a couple of other improvements from the Centrifuge library.

In-memory connection hub is now sharded – this should reduce lock contention between operations in different channels. In our artificial benchmarks we noticed a 3x better hub throughput, but in reality the benefit is heavily depends on the usage pattern.

Centrifugo now allocates less during message broadcasting to a large number of subscribers.

Also, an upgrade to Go 1.17 for builds results in ~5% performance boost overall, thanks to a new way of passing function arguments and results using registers instead of the stack introduced in Go 1.17.

Centrifugo PRO

The final notable thing is an introduction of Centrifugo PRO. This is an extended version of Centrifugo built on top of the OSS version. It provides some unique features targeting business adopters.

Those who followed Centrifugo for a long time know that there were some attempts to make project development sustainable. Buy me a coffee and Opencollective approaches were not successful, during a year we got ~300$ of total contributions. While we appreciate these contributions a lot - this does not fairly justify a time spent on Centrifugo maintenance these days and does not allow bringing it to the next level. So here is an another attempt to monetize Centrifugo.

Centrifugo PRO details and features described here in docs. Let's see how it goes. We believe that a set of additional functionality can provide great advantages for both small and large-scale Centrifugo setups. PRO features can give useful insights on a system, protect from client API misusing, reduce server resource usage, and more.

PRO version will be released soon after Centrifugo v3 OSS.

Conclusion

There are some other changes introduced in v3 but not mentioned here. The full list can be found in the release notes and the migration guide.

Hope we stepped into an exciting time of the v3 life cycle and many improvements will follow. Join our communities in Telegram and Discord if you have questions or want to follow Centrifugo development:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

Enjoy Centrifugo v3, and let the Centrifugal force be with you.

Special thanks

Special thanks to Anton Silischev for the help with v3 tests, examples and CI. To Leon Sorokin for the spinning CSS Centrifugo logo. To Michael Filonenko for the help with Tarantool. To German Saprykin for Dart magic.

Thanks to the community members who tested out Centrifugo v3 beta, found bugs and sent improvements.

Icons used here made by wanicon from www.flaticon.com
- + \ No newline at end of file diff --git a/blog/2021/10/18/integrating-with-nodejs.html b/blog/2021/10/18/integrating-with-nodejs.html index f37f8e8af..1cd6ba356 100644 --- a/blog/2021/10/18/integrating-with-nodejs.html +++ b/blog/2021/10/18/integrating-with-nodejs.html @@ -16,13 +16,13 @@ - +

Centrifugo integration with NodeJS tutorial

· 7 min read
Alexander Emelin

Centrifuge

Centrifugo is a scalable real-time messaging server in a language-agnostic way. In this tutorial we will integrate Centrifugo with NodeJS backend using a connect proxy feature of Centrifugo for user authentication and native session middleware of ExpressJS framework.

Why would NodeJS developers want to integrate a project with Centrifugo? This is a good question especially since there are lots of various tools for real-time messaging available in NodeJS ecosystem.

caution

This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step.

I found several points which could be a good motivation:

  • Centrifugo scales well – we have a very optimized Redis Engine with client-side sharding and Redis Cluster support. We can also scale with KeyDB, Nats, or Tarantool. Centrifugo can scale to millions connections distributed over different server nodes.
  • Centrifugo is pretty fast (written in Go) and can handle thousands of clients per node. Client protocol is optimized for thousands of messages per second.
  • Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for real-time servers that scale to many nodes.
  • Centrifugo works as a separate service – so can be a universal tool in developer's pocket, can migrate from one project to another, no matter what programming language or framework is used for a business logic.

Having said this all – let's move to a tutorial itself.

What we are building

Not a super-cool app to be honest. Our goal here is to give a reader an idea how integration with Centrifugo could look like. There are many possible apps which could be built on top of this knowledge.

The end result here will allow application user to authenticate and once authenticated – connect to Centrifugo. Centrifugo will proxy connection requests to NodeJS backend and native ExpressJS session middleware will be used for connection authentication. We will also send some periodical real-time messages to a user personal channel.

The full source code of this tutorial located on Github. You can clone examples repo and run this demo by simply writing:

docker compose up

Creating Express.js app

Start new NodeJS app:

npm init

Install dependencies:

npm install express express-session cookie-parser axios morgan

Create index.js file.

index.js
const express = require('express');
const cookieParser = require("cookie-parser");
const sessions = require('express-session');
const morgan = require('morgan');
const axios = require('axios');

const app = express();
const port = 3000;
app.use(express.json());

const oneDay = 1000 * 60 * 60 * 24;

app.use(sessions({
secret: "this_is_my_secret_key",
saveUninitialized: true,
cookie: { maxAge: oneDay },
resave: false
}));
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
app.use(express.static('static'));
app.use(morgan('dev'));

app.get('/', (req, res) => {
if (req.session.userid) {
res.sendFile('views/app.html', { root: __dirname });
} else
res.sendFile('views/login.html', { root: __dirname })
});

app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});

Create login.html file in views folder:

views/login.html
<html>

<body>
<form action="/login" method="post">
<h2>Login (username: demo-user, password: demo-pass)</h2>
<div class="input-field">
<input type="text" name="username" id="username" placeholder="Enter Username">
</div>
<div class="input-field">
<input type="password" name="password" id="password" placeholder="Enter Password">
</div>
<input type="submit" value="Log in">
</form>
</body>

</html>

Also create app.html file in views folder:

views/app.html
<html>

<head>
<link rel="stylesheet" href="app.css">
<script src="https://cdn.jsdelivr.net/gh/centrifugal/centrifuge-js@2.8.3/dist/centrifuge.min.js"></script>
</head>

<body>
<div>
<a href='/logout'>Click to logout</a>
</div>
<div id="log"></div>
</body>

</html>

Make attention that we import centrifuge-js client here which abstracts away Centrifugo bidirectional WebSocket protocol.

Let's write an HTTP handler for login form:

index.js
const myusername = 'demo-user'
const mypassword = 'demo-pass'

app.post('/login', (req, res) => {
if (req.body.username == myusername && req.body.password == mypassword) {
req.session.userid = req.body.username;
res.redirect('/');
} else {
res.send('Invalid username or password');
}
});

In this example we use hardcoded username and password for out single user. Of course in real app you will have a database with user credentials. But since our goal is only show integration with Centrifugo – we are skipping these hard parts here.

Also create a handler for a logout request:

index.js
app.get('/logout', (req, res) => {
req.session.destroy();
res.redirect('/');
});

Now if you run an app with node index.js you will see a login form using which you can authenticate. At this point this is a mostly convenient NodeJS application, let's add Centrifugo integration.

Starting Centrifugo

Run Centrifugo with config.json like this:

config.json
{
"token_hmac_secret_key": "secret",
"admin": true,
"admin_password": "password",
"admin_secret": "my_admin_secret",
"api_key": "my_api_key",
"allowed_origins": [
"http://localhost:9000"
],
"user_subscribe_to_personal": true,
"proxy_connect_endpoint": "http://localhost:3000/centrifugo/connect",
"proxy_http_headers": [
"Cookie"
]
}

I.e.:

./centrifugo -c config.json

Create app.js file in static folder:

static/app.js
function drawText(text) {
const div = document.createElement('div');
div.innerHTML = text;
document.getElementById('log').appendChild(div);
}

const centrifuge = new Centrifuge('ws://localhost:9000/connection/websocket');

centrifuge.on('connect', function () {
drawText('Connected to Centrifugo');
});

centrifuge.on('disconnect', function () {
drawText('Disconnected from Centrifugo');
});

centrifuge.on('publish', function (ctx) {
drawText('Publication, time = ' + ctx.data.time);
});

centrifuge.connect();

Adding Nginx

Since we are going to use native session auth of ExpressJS we can't just connect from localhost:3000 (where our NodeJS app is served) to Centrifugo running on localhost:8000 – browser won't send a Cookie header to Centrifugo in this case. Due to this reason we need a reverse proxy which will terminate a traffic from frontend and proxy requests to NodeJS process or to Centrifugo depending on URL path. In this case both browser and NodeJS app will share the same origin – so Cookie will be sent to Centrifugo in WebSocket Upgrade request.

tip

Alternatively, we could also use JWT authentication of Centrifugo but that's a topic for another tutorial. Here we are using connect proxy feature for auth.

Nginx config will look like this:

server {
listen 9000;

server_name localhost;

location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /connection {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}

Run Nginx and open http://localhost:9000. After authenticating in app you should see an attempt to connect to a WebSocket endpoint. But connection will fail since we need to implement connect proxy handler in NodeJS app.

index.js
app.post('/centrifugo/connect', (req, res) => {
if (req.session.userid) {
res.json({
result: {
user: req.session.userid
}
});
} else
res.json({
disconnect: {
code: 1000,
reason: "unauthorized",
reconnect: false
}
});
});

Restart NodeJS process and try opening an app again. Application should now successfully connect to Centrifugo.

Send real-time messages

Let's also periodically publish current server time to a client's personal channel. In Centrifugo configuration we set a user_subscribe_to_personal option which turns on automatic subscription to a personal channel for each connected user. We can use axios library and send publish API requests to Centrifugo periodically (according to API docs):

index.js
const centrifugoApiClient = axios.create({
baseURL: `http://centrifugo:8000/api`,
headers: {
Authorization: `apikey my_api_key`,
'Content-Type': 'application/json',
},
});

setInterval(async () => {
try {
await centrifugoApiClient.post('', {
method: 'publish',
params: {
channel: '#' + myusername, // construct personal channel name.
data: {
time: Math.floor(new Date().getTime() / 1000),
},
},
});
} catch (e) {
console.error(e.message);
}
}, 5000);

After restarting NodeJS you should see periodical updates on application web page.

You can also log in into Centrifugo admin web UI http://localhost:8000 using password password - and play with other available server API from within web interface.

Conclusion

While not being super useful this example can help understanding core concepts of Centrifugo - specifically connect proxy feature and server API.

It's possible to use unidirectional Centrifugo transports instead of bidrectional WebSocket used here – in this case you can go without using centrifuge-js at all.

This application scales perfectly if you need to handle more connections – thanks to Centrifugo builtin PUB/SUB engines.

It's also possible to use client-side subscriptions, keep channel history cache, enable channel presence and more. All the power of Centrifugo is in your hands.

- + \ No newline at end of file diff --git a/blog/2021/11/04/integrating-with-django-building-chat-application.html b/blog/2021/11/04/integrating-with-django-building-chat-application.html index b073ccdea..fa9e0d3d8 100644 --- a/blog/2021/11/04/integrating-with-django-building-chat-application.html +++ b/blog/2021/11/04/integrating-with-django-building-chat-application.html @@ -16,13 +16,13 @@ - +

Centrifugo integration with Django – building a basic chat application

· 16 min read
Alexander Emelin

Centrifuge

In this tutorial, we will create a basic chat server using the Django framework and Centrifugo. Our chat application will have two pages:

  1. A page that lets you type the name of a chat room to join.
  2. A room view that lets you see messages posted in a chat room you joined.

The room view will use a WebSocket to communicate with the Django server (with help from Centrifugo) and listen for any messages that are published to the room channel.

caution

This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step.

The result will look like this:

demo

tip

Some of you will notice that this tutorial looks very similar to Chat app tutorial of Django Channels. This is intentional to let Pythonistas already familiar with Django Channels feel how Centrifugo compares to Channels in terms of the integration process.

Why integrate Django with Centrifugo

Why would Django developers want to integrate a project with Centrifugo for real-time messaging functionality? This is a good question especially since there is a popular Django Channels project which solves the same task.

I found several points which could be a good motivation:

  • Centrifugo is fast and scales well. We have an optimized Redis Engine with client-side sharding and Redis Cluster support. Centrifugo can also scale with KeyDB, Nats, or Tarantool. So it's possible to handle millions of connections distributed over different server nodes.
  • Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for real-time servers that scale to many nodes. Check out our doc!
  • With Centrifugo you don't need to rewrite the existing application to introduce real-time messaging features to your users.
  • Centrifugo works as a separate service – so can be a universal tool in the developer's pocket, can migrate from one project to another, no matter what programming language or framework is used for business logic.

Prerequisites

We assume that you are already familiar with basic Django concepts. If not take a look at the official Django tutorial first and then come back to this tutorial.

Also, make sure you read a bit about Centrifugo – introduction and quickstart tutorial.

We also assume that you have Django installed already.

One possible way to quickly install Django locally is to create virtualenv, activate it, and install Django:

python3 -m venv env
. env/bin/activate
pip install django

Alos, make sure you have Centrifugo v3 installed already.

This tutorial also uses Docker to run Redis. We use Redis as a Centrifugo engine – this allows us to have a scalable solution in the end. Using Redis is optional actually, Centrifugo uses a Memory engine by default (but it does not allow scaling Centrifugo nodes). We will also run Nginx with Docker to serve the entire app. Install Docker from its official website but I am sure you already have one.

Creating a project

First, let's create a Django project.

From the command line, cd into a directory where you’d like to store your code, then run the following command:

django-admin startproject mysite

This will create a mysite directory in your current directory with the following contents:

❯ tree mysite
mysite
├── manage.py
└── mysite
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py

Creating the chat app

We will put the code for the chat server inside chat app.

Make sure you’re in the same directory as manage.py and type this command:

python3 manage.py startapp chat

That’ll create a directory chat, which is laid out like this:

❯ tree chat
chat
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py

For this tutorial, we will only be working with chat/views.py and chat/__init__.py. Feel free to remove all other files from the chat directory.

After removing unnecessary files, the chat directory should look like this:

❯ tree chat
chat
├── __init__.py
└── views.py

We need to tell our project that the chat app is installed. Edit the mysite/settings.py file and add 'chat' to the INSTALLED_APPS setting. It’ll look like this:

# mysite/settings.py
INSTALLED_APPS = [
'chat',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

Add the index view

We will now create the first view, an index view that lets you type the name of a chat room to join.

Create a templates directory in your chat directory. Within the templates directory, you have just created, create another directory called chat, and within that create a file called index.html to hold the template for the index view.

Your chat directory should now look like this:

❯ tree chat
chat
├── __init__.py
├── templates
│ └── chat
│ └── index.html
└── views.py

Put the following code in chat/templates/chat/index.html:

chat/templates/chat/index.html
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title>Select a chat room</title>
</head>

<body>
<div class="center">
<div class="input-wrapper">
<input type="text" id="room-name-input" />
</div>
<div class="input-help">
Type a room name to <a id="room-name-submit" href="#">JOIN</a>
</div>
</div>
<script>
const nameInput = document.querySelector('#room-name-input');
const nameSubmit = document.querySelector('#room-name-submit');
nameInput.focus();
nameInput.onkeyup = function (e) {
if (e.keyCode === 13) { // enter, return
nameSubmit.click();
}
};
nameSubmit.onclick = function (e) {
e.preventDefault();
var roomName = nameInput.value;
if (!roomName) {
return;
}
window.location.pathname = '/chat/room/' + roomName + '/';
};
</script>
</body>

</html>

Create the view function for the room view. Put the following code in chat/views.py:

chat/views.py
from django.shortcuts import render

def index(request):
return render(request, 'chat/index.html')

To call the view, we need to map it to a URL - and for this, we need a URLconf.

To create a URLconf in the chat directory, create a file called urls.py. Your app directory should now look like this:

❯ tree chat
chat
├── __init__.py
├── templates
│ └── chat
│ └── index.html
└── views.py
└── urls.py

In the chat/urls.py file include the following code:

chat/urls.py
from django.urls import path

from . import views

urlpatterns = [
path('', views.index, name='index'),
]

The next step is to point the root URLconf at the chat.urls module. In mysite/urls.py, add an import for django.conf.urls.include and insert an include() in the urlpatterns list, so you have:

mysite/urls.py
from django.conf.urls import include
from django.urls import path
from django.contrib import admin

urlpatterns = [
path('chat/', include('chat.urls')),
path('admin/', admin.site.urls),
]

Let’s verify that the index view works. Run the following command:

python3 manage.py runserver

You’ll see the following output on the command line:

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
October 21, 2020 - 18:49:39
Django version 3.1.2, using settings 'mysite.settings'
Starting development server at http://localhost:8000/
Quit the server with CONTROL-C.

Go to http://localhost:8000/chat/ in your browser and you should see the a text input to provide a room name.

Type in "lobby" as the room name and press Enter. You should be redirected to the room view at http://localhost:8000/chat/room/lobby/ but we haven’t written the room view yet, so you’ll get a "Page not found" error page.

Go to the terminal where you ran the runserver command and press Control-C to stop the server.

Add the room view

We will now create the second view, a room view that lets you see messages posted in a particular chat room.

Create a new file chat/templates/chat/room.html. Your app directory should now look like this:

chat
├── __init__.py
├── templates
│ └── chat
│ ├── index.html
│ └── room.html
├── urls.py
└── views.py

Create the view template for the room view in chat/templates/chat/room.html:

chat/templates/chat/room.html
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title>Chat Room</title>
<script src="https://cdn.jsdelivr.net/gh/centrifugal/centrifuge-js@2.8.3/dist/centrifuge.min.js"></script>
</head>

<body>
<ul id="chat-thread" class="chat-thread"></ul>
<div class="chat-message">
<input id="chat-message-input" class="chat-message-input" type="text" autocomplete="off" autofocus />
</div>
{{ room_name|json_script:"room-name" }}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatThread = document.querySelector('#chat-thread');
const messageInput = document.querySelector('#chat-message-input');

const centrifuge = new Centrifuge("ws://" + window.location.host + "/connection/websocket");

centrifuge.on('connect', function (ctx) {
console.log("connected", ctx);
});

centrifuge.on('disconnect', function (ctx) {
console.log("disconnected", ctx);
});

const sub = centrifuge.subscribe('rooms:' + roomName, function (ctx) {
const chatNewThread = document.createElement('li');
const chatNewMessage = document.createTextNode(ctx.data.message);
chatNewThread.appendChild(chatNewMessage);
chatThread.appendChild(chatNewThread);
chatThread.scrollTop = chatThread.scrollHeight;
});

centrifuge.connect();

messageInput.focus();
messageInput.onkeyup = function (e) {
if (e.keyCode === 13) { // enter, return
e.preventDefault();
const message = messageInput.value;
if (!message) {
return;
}
sub.publish({ 'message': message });
messageInput.value = '';
}
};
</script>
</body>

</html>

Create the view function for the room view in chat/views.py:

chat/views.py
from django.shortcuts import render


def index(request):
return render(request, 'chat/index.html')


def room(request, room_name):
return render(request, 'chat/room.html', {
'room_name': room_name
})

Create the route for the room view in chat/urls.py:

# chat/urls.py
from django.urls import path, re_path

from . import views

urlpatterns = [
path('', views.index, name='index'),
re_path('room/(?P<room_name>[A-z0-9_-]+)/', views.room, name='room'),
]

Start the development server:

python3 manage.py runserver

Go to http://localhost:8000/chat/ in your browser and to see the index page.

Type in "lobby" as the room name and press enter. You should be redirected to the room page at http://localhost:8000/chat/lobby/ which now displays an empty chat log.

Type the message "hello" and press Enter. Nothing happens! In particular, the message does not appear in the chat log. Why?

The room view is trying to open a WebSocket connection with Centrifugo using the URL ws://localhost:8000/connection/websocket but we haven’t started Centrifugo to accept WebSocket connections yet. If you open your browser’s JavaScript console, you should see an error that looks like this:

WebSocket connection to 'ws://localhost:8000/connection/websocket' failed

And since port 8000 has already been allocated we will start Centrifugo at a different port actually.

Starting Centrifugo server

As promised we will use Centrifugo with Redis engine. So first thing to do before running Centrifugo is to start Redis:

docker run -it --rm -p 6379:6379 redis:6

Then create a configuration file for Centrifugo:

{
"port": 8001,
"engine": "redis",
"redis_address": "redis://localhost:6379",
"allowed_origins": "http://localhost:9000",
"proxy_connect_endpoint": "http://localhost:8000/chat/centrifugo/connect/",
"proxy_publish_endpoint": "http://localhost:8000/chat/centrifugo/publish/",
"proxy_subscribe_endpoint": "http://localhost:8000/chat/centrifugo/subscribe/",
"proxy_http_headers": ["Cookie"],
"namespaces": [
{
"name": "rooms",
"publish": true,
"proxy_publish": true,
"proxy_subscribe": true
}
]
}

And run Centrifugo with it like this:

centrifugo -c config.json

Let's describe some options we used here:

  • port - sets the port Centrifugo runs on since we are running everything on localhost we make it different (8001) from the port allocated for the Django server (8000).
  • engine - as promised we are using Redis engine so we can easily scale Centrifigo nodes to handle lots of WebSocket connections
  • redis_address allows setting Redis address
  • allowed_origins - we will connect from http://localhost:9000 so we need to allow it
  • namespaces – we are using rooms: prefix when subscribing to a channel, i.e. using Centrifugo rooms namespace. Here we define this namespace and tell Centrifigo to proxy subscribe and publish events for channels in the namespace.
tip

It's a good practice to use different namespaces in Centrifugo for different real-time features as this allows enabling only required options for a specific task.

Also, config has some options related to Centrifugo proxy feature. This feature allows proxying WebSocket events to the configured endpoints. We will proxy three types of events:

  1. Connect (called when a user establishes WebSocket connection with Centrifugo)
  2. Subscribe (called when a user wants to subscribe on a channel)
  3. Publish (called when a user tries to publish data to a channel)

Adding Nginx

In Centrifugo config we set endpoints which we will soon implement inside our Django app. You may notice that the allowed origin has a URL with port 9000. That's because we want to proxy Cookie headers from a persistent connection established with Centrifugo to the Django app and need Centrifugo and Django to share the same origin (so browsers can send Django session cookies to Centrifugo).

While not used in this tutorial (we will use fake tutorial-user as user ID here) – this can be useful if you decide to authenticate connections using Django native sessions framework later. To achieve this we should also add Nginx with a configuration like this:

nginx.conf
events {
worker_connections 1024;
}

error_log /dev/stdout info;

http {
access_log /dev/stdout;

server {
listen 9000;

server_name localhost;

location / {
proxy_pass http://host.docker.internal:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /connection/websocket {
proxy_pass http://host.docker.internal:8001;
proxy_http_version 1.1;
proxy_buffering off;
keepalive_timeout 65;
proxy_read_timeout 60s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
}

Start Nginx (replace the path to nginx.conf to yours):

docker run -it --rm -v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro -p 9000:9000 --add-host=host.docker.internal:host-gateway nginx

Note that we are exposing port 9000 to localhost and use a possibility to use host.docker.internal host to communicate from inside Docker network with services which are running on localhost (on the host machine). See this answer on SO.

Open http://localhost:9000. Nginx should now properly proxy requests to Django server and to Centrifugo, but we still need to do some things.

Implementing proxy handlers

Well, now if you try to open a chat page with Nginx, Centrifugo, Django, and Redis running you will notice some errors in Centrifugo logs. That's because Centrifugo tries to proxy WebSocket connect events to Django to authenticate them but we have not created event handlers in Django yet. Let's fix this.

Extend chat/urls.py:

chat/urls.py
from django.urls import path, re_path

from . import views

urlpatterns = [
path('', views.index, name='index'),
re_path('room/(?P<room_name>[A-z0-9_-]+)/', views.room, name='room'),
path('centrifugo/connect/', views.connect, name='connect'),
path('centrifugo/subscribe/', views.subscribe, name='subscribe'),
path('centrifugo/publish/', views.publish, name='publish'),
]

Extend chat/views.py:

chat/views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def connect(request):
# In connect handler we must authenticate connection.
# Here we return a fake user ID to Centrifugo to keep tutorial short.
# More details about connect result format can be found in proxy docs:
# https://centrifugal.dev/docs/server/proxy#connect-proxy
logger.debug(request.body)
response = {
'result': {
'user': 'tutorial-user'
}
}
return JsonResponse(response)

@csrf_exempt
def publish(request):
# In publish handler we can validate publication request initialted by a user.
# Here we return an empty object – thus allowing publication.
# More details about publish result format can be found in proxy docs:
# https://centrifugal.dev/docs/server/proxy#publish-proxy
response = {
'result': {}
}
return JsonResponse(response)

@csrf_exempt
def subscribe(request):
# In subscribe handler we can validate user subscription request to a channel.
# Here we return an empty object – thus allowing subscription.
# More details about subscribe result format can be found in proxy docs:
# https://centrifugal.dev/docs/server/proxy#subscribe-proxy
response = {
'result': {}
}
return JsonResponse(response)

connect view will accept all connections and return user ID as tutorial-user. In real app you most probably want to use Django sessions and return real authenticated user ID instead of tutorial-user. Since we told Centrifugo to proxy connection Cookie headers native Django user authentication will work just fine.

Restart Django and try the chat app again. You should now successfully connect. Open a browser tab to the room page at http://localhost:9000/chat/room/lobby/. Open a second browser tab to the same room page.

In the second browser tab, type the message "hello" and press Enter. You should now see "hello" echoed in the chat log in both the second browser tab and in the first browser tab.

You now have a basic fully-functional chat server!

What could be improved

The list is large, but it's fun to do. To name some possible improvements:

  • Replace tutorial-user used here with native Django session framework. We already proxying the Cookie header to Django from Centrifugo, so you can reuse native Django authentication. Only allow authenticated users to join rooms.
  • Create Room model and add users to it – thus you will be able to check permissions inside subscribe and publish handlers.
  • Create Message model to display chat history in Room.
  • Replace Django devserver with something more suitable for production like Gunicorn.
  • Check out Centrifugo possibilities like presence to display online users.
  • Use cent Centrifugo HTTP API library to publish something to a user on behalf of a server. In this case you can avoid using publish proxy, publish messages to Django over convinient AJAX call - and then call Centrifugo HTTP API to publish message into a channel.
  • You can replace connect proxy (which is an HTTP call from Centrifugo to Django on each connect) with JWT authentication. JWT authentication may result in a better application performance (since no additional proxy requests will be issued on connect). It can allow your Django app to handle millions of users on a reasonably small hardware and survive mass reconnects from all those users. More details can be found in Scaling WebSocket in Go and beyond blog post.
  • Instead of using subscribe proxy you can put channel into connect proxy result or into JWT – thus using server-side subscriptions and avoid subscribe proxy HTTP call.

One more thing I'd like to note is that if you aim to build a chat application like WhatsApp or Telegram where you have a screen with list of chats (which can be pretty long!) you should not create a separate channel for each room. In this case using separate channel per room does not scale well and you better use personal channel for each user to receive all user-related messages. And as soon as message published to a chat you can send message to each participant's channel. In this case, take a look at Centrifugo broadcast API.

Tutorial source code with docker-compose

The full example which can run by issuing a single docker compose up can be found on Github. It also has some CSS styles so that the chat looks like shown in the beginning.

Conclusion

Here we implemented a basic chat app with Django and Centrifugo.

While a chat still requires work to be suitable for production this example can help understand core concepts of Centrifugo - specifically channel namespaces and proxy features.

It's possible to use unidirectional Centrifugo transports instead of bidirectional WebSocket used here – in this case, you can go without using centrifuge-js at all.

Centrifugo scales perfectly if you need to handle more connections – thanks to Centrifugo built-in PUB/SUB engines.

It's also possible to use server-side subscriptions, keep channel history cache, use JWT authentication instead of connect proxy, enable channel presence, and more. All the power of Centrifugo is in your hands.

Hope you enjoyed this tutorial. And let the Centrifugal force be with you!

Join our community channels in case of any questions left after reading this.

- + \ No newline at end of file diff --git a/blog/2021/12/14/laravel-multi-room-chat-tutorial.html b/blog/2021/12/14/laravel-multi-room-chat-tutorial.html index acd661de9..21ebcef8e 100644 --- a/blog/2021/12/14/laravel-multi-room-chat-tutorial.html +++ b/blog/2021/12/14/laravel-multi-room-chat-tutorial.html @@ -16,13 +16,13 @@ - +

Building a multi-room chat application with Laravel and Centrifugo

· 11 min read
Anton Silischev

Image

In this tutorial, we will create a multi-room chat server using Laravel framework and Centrifugo real-time messaging server.

Authenticated users of our chat app will be able to create new chat rooms, join existing rooms and instantly communicate inside rooms with the help of Centrifugo WebSocket real-time transport.

caution

This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step.

Application overview

The result will look like this:

For the backend, we are using Laravel (version 8.65) as one of the most popular PHP frameworks. Centrifugo v3 will accept WebSocket client connections. And we will implement an integration layer between Laravel and Centrifugo.

For CSS styles we are using recently released Bootstrap 5. Also, some vanilla JS instead of frameworks like React/Vue/whatever to make frontend Javascript code simple – so most developers out there could understand the mechanics.

We are also using a bit old-fashioned server rendering here where server renders templates for different room routes (URLs) – i.e. our app is not a SPA app – mostly for the same reasons: to keep example short and let reader focus on Centrifugo and Laravel integration parts.

To generate fake user avatars we are requesting images from https://robohash.org/ which can generate unique robot puctures based on some input string (username in our case). Robots like to chat with each other!




tip

We also have some ideas on further possible app improvements at the end of this post.

Why integrate Laravel with Centrifugo?

Why would Laravel developers want to integrate a project with Centrifugo for real-time messaging functionality? That's a good question. There are several points which could be a good motivation:

  • Centrifugo is open-source and self-hosted. So you can run it on your own infrastructure. Popular Laravel real-time broadcasting intergrations (Pusher and Ably) are paid cloud solutions. At scale Centrifugo will cost you less than cloud solutions. Of course cloud solutions do not require additional server setup – but everything is a trade-off right? So you should decide for youself.
  • Centrifugo is fast and scales well. It has an optimized Redis Engine with client-side sharding and Redis Cluster support. Centrifugo can also scale with KeyDB, Nats, or Tarantool. So it's possible to handle millions of connections distributed over different Centrifugo nodes.
  • Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for self-hosted real-time servers that scale to many nodes (like fast message history cache, or maintaining single user connection, both client-side and server-side subscriptions, etc).
  • Centrifugo is lightweight, single binary server which works as a separate service – it can be a universal tool in the developer's pocket, can migrate with you from one project to another, no matter what programming language or framework is used for business logic.

Hope this makes sense as a good motivation to give Centrifugo a try in your Laravel project. Let's get started!

Setup and start a project

For the convenience of working with the example, we wrapped the end result into docker compose.

To start the app clone examples repo, cd into v3/php_laravel_chat_tutorial directory and run:

docker compose up

At the first launch, the necessary images will be downloaded (will take some time and network bytes). When the main service is started, you should see something like this in container logs:

...
app | Database seeding completed successfully.
app | [10-Dec-2021 12:25:05] NOTICE: fpm is running, pid 112
app | [10-Dec-2021 12:25:05] NOTICE: ready to handle connections

Then go to http://localhost/ – you should see:

Image

Register (using some fake credentials) or sign up – and proceed to the chat rooms.

Pay attention to the configuration of Centrifugo and Nginx. Also, on entrypoint which does some things:

  • dependencies are installed via composer
  • copying settings from .env.example
  • db migrations are performed and the necessary npm packages are installed
  • php-fpm starts

Application structure

We assume you already familar with Laravel concepts, so we will just point you to some core aspects of the Laravel application structure and will pay more attention to Centrifugo integration parts.

Environment settings

After the first launch of the application, all settings will be copied from the file .env.example to .env. Next, we will take a closer look at some settings.

Database migrations and models

You can view the database structure here.

We will use the following tables which will be then translated to the application models:

Broadcasting

For broadcasting we are using laravel-centrifugo library. It helps to simplify interaction between Laravel and Centrifugo by providing some convenient wrappers.

Step-by-step configuration can be viewed in the readme file of this library.

Pay attention to the CENTRIFUGO_API_KEY setting. It is used to send API requests from Laravel to Centrifugo and must match in .env and centrifugo.json files. And we also telling laravel-centrifugo the URL of Centrifugo. That's all we need to configure for this example app.

See more information about Laravel broadcasting here.

tip

As an alternative to laravel-centrifugo, you can use phpcent – it's an official generic API client which allows publishing to Centrifugo HTTP API. But it does know nothing about Laravel broadcasting specifics.

Interaction with Centrifugo

When user opens a chat app it connects to Centrifugo over WebSocket transport.

Let's take a closer look at Centrifugo server configuration file we use for this example app:

{
"port": 8000,
"engine": "memory",
"api_key": "some-long-api-key-which-you-should-keep-secret",
"allowed_origins": [
"http://localhost",
],
"proxy_connect_endpoint": "http://nginx/centrifugo/connect/",
"proxy_http_headers": [
"Cookie"
],
"namespaces": [
{
"name": "personal"
}
]
}

This configuration defines a connect proxy endpoint which is targeting Nginx and then proxied to Laravel. Centrifugo will proxy Cookie header of WebSocket HTTP Upgrade requests to Laravel – this allows using native Laravel authentication.

We also defined a "personal" namespace – we will subscribe each user to a personal channel in this namespace inside connect proxy handler. Using namespaces for different real-time features is one of Centrifugo best-practices.

Allowed origins must be properly set to prevent cross-site WebSocket connection hijacking.

Connect proxy controller

To use native Laravel user authentication middlewares, we will use Centrifugo proxy feature.

When user connects to Centrifugo it's connection attempt will be transformed into HTTP request from Centrifugo to Laravel and will hit the connect proxy controller:

class CentrifugoProxyController extends Controller
{
public function connect()
{
return new JsonResponse([
'result' => [
'user' => (string) Auth::user()->id,
'channels' => ["personal:#".Auth::user()->id],
]
]);
}
}

This controller protected by auth middleware.

Since Centrifugo proxies Cookie header of initial WebSocket HTTP Upgrade request Laravel auth layer will work just fine. So in a controller you already has access to the current authenticated user.

In the response from controller we tell Centrifugo the ID of connecting user and subscribe user to its personal channel (using user-limited channel feature of Centrifugo). Returning a channel in such way will subscribe user to it using server-side subscriptions mechanism.

tip

Note, that in our chat app we are using a single personal channel for each user to receive real-time updates from all rooms. We are not creating separate subscriptions for each room user joined too. This will allow us to scale more easily in the future, and basically the only viable solution in case of room list pagination in chat application like this. It does not mean you can not combine personal user channels and separate room channels for different tasks though.

Some additional tips can be found in Centrifugo FAQ.

Room controller

In RoomController we perform various actions with rooms:

  • displaying rooms
  • create rooms
  • join users to rooms
  • publish messages

When we publish a message in a room, we send a message to the personal channel of all users joined to the room using the broadcast method of Centrifugo API. It allows publishing the same message into many channels.

$message = Message::create([
'sender_id' => Auth::user()->id,
'message' => $requestData["message"],
'room_id' => $id,
]);

$room = Room::with('users')->find($id);

$channels = [];
foreach ($room->users as $user) {
$channels[] = "personal:#" . $user->id;
}

$this->centrifugo->broadcast($channels, [
"text" => $message->message,
"createdAt" => $message->created_at->toDateTimeString(),
"roomId" => $id,
"senderId" => Auth::user()->id,
"senderName" => Auth::user()->name,
]);

We also add some fields to the published message which will be used when dynamically displaying a message coming from a WebSocket connection (see Client side below).

Client side

Our chat is basically a one page with some variations dependng on the current route. So we use a single view for the entire chat app.

On the page we have a form for creating rooms. The user who created the room automatically joins it upon creation. Other users need to join manually (using join button in the room).

When sending a message (using the chat room message input), we make an AJAX request that hits RoomController shown above. A message saved into the database and then broadcasted to all users who joined this room. Here is a code that processes sending on ENTER:

messageInput.onkeyup = function(e) {
if (e.keyCode === 13) {
e.preventDefault();
const message = messageInput.value;
if (!message) {
return;
}
const xhttp = new XMLHttpRequest();
xhttp.open("POST", "/rooms/" + roomId + "/publish");
xhttp.setRequestHeader("X-CSRF-TOKEN", csrfToken);
xhttp.send(JSON.stringify({
message: message
}));
messageInput.value = '';
}
};

After the message is processed on the server and broadcasted to Centrifugo it instantly comes to client-side. To receive the message we are connecting to Centrifugo WebSocket endpoint and wait for a message in the publish event handler:

const url = "ws://" + window.location.host + "/connection/websocket";
const centrifuge = new Centrifuge(url);

centrifuge.on('connect', function(ctx) {
console.log("connected to Centrifugo", ctx);
});

centrifuge.on('disconnect', function(ctx) {
console.log("disconnected from Centrifugo", ctx);
});

centrifuge.on('publish', function(ctx) {
if (ctx.data.roomId.toString() === currentRoomId) {
addMessage(ctx.data);
scrollToLastMessage();
}
addRoomLastMessage(ctx.data);
});

centrifuge.connect();

We are using centrifuge-js client connector library to communicate with Centrifugo. This client abstracts away bidirectional asynchronous protocol complexity for us providing a simple way to listen connect, disconnect events and communicate with a server in various ways.

In publish event handler we check whether the message belongs to the room the user is currently in. If yes, then we add it to the message history of the room. We also add this message to the room in the list on the left as the last chat message in room. If necessary, we crop the text for normal display.

tip

In our example we only subscribe each user to a single channel, but user can be subscribed to several server-side channels. To distinguish between them use ctx.channel inside publish event handler.

And that's it! We went through all the main parts of the integration.

Possible improvements

As promised, here is a list with several possible app improvements:

  • Transform to a single page app, use productive Javascript frameworks like React or VueJS instead of vanilla JS.
  • Add message read statuses - as soon as one of the chat participants read the message mark it read in the database.
  • Introduce user-to-user chats.
  • Support pagination for the message history, maybe for chat room list also.
  • Don't show all rooms in the system – add functionality to search room by name.
  • Horizontal scaling (using multiple nodes of Centrifugo, for example with Redis Engine) – mostly one line in Centrifugo config if you have Redis running.
  • Gracefully handle temporary disconnects by loading missed messages from the database or Centrifugo channel history cache.
  • Optionally replace connect proxy with JWT authentication to reduce HTTP calls from Centrifugo to Laravel. This may drastically reduce resources for Laravel backend at scale.
  • Try using Centrifugo RPC proxy feature to use WebSocket connection for message publish instead of issuing AJAX request.

Conclusion

We built a chat app with Laravel and Centrifugo. While there is still an area for improvements, this example is not really the basic. It's already valuable in the current form and may be transformed into part of your production system with minimal tweaks.

Hope you enjoyed this tutorial. If you have any questions after reading – join our community channels. We touched only part of Centrifugo concepts here – take a look at detailed Centrifugo docs nearby. And let the Centrifugal force be with you!

- + \ No newline at end of file diff --git a/blog/2022/07/19/centrifugo-v4-released.html b/blog/2022/07/19/centrifugo-v4-released.html index 743135971..14e6652ea 100644 --- a/blog/2022/07/19/centrifugo-v4-released.html +++ b/blog/2022/07/19/centrifugo-v4-released.html @@ -16,13 +16,13 @@ - +

Centrifugo v4 released – a little revolution

· 21 min read
Centrifugal team

Centrifuge

Today we are excited to announce the next generation of Centrifugo – Centrifugo v4. The release takes Centrifugo to the next level in terms of client protocol performance, WebSocket fallback simplicity, SDK ecosystem and channel security model. It also comes with a couple of cutting-edge technologies to experiment with such as HTTP/3 and WebTransport.

About Centrifugo

If you've never heard of Centrifugo before, it's an open-source scalable real-time messaging server written in Go language. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, GRPC, SockJS). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.

Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.

Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.

Centrifugo v3 flashbacks

Let's start from looking back a bit. Centrifugo v3 was released last year. It had a great list of improvements – like unidirectional transports support (EventSource, HTTP-streaming and GRPC), GRPC transport for proxy, history iteration API, faster JSON protocol, super-fast but experimental Tarantool engine implementation, and others.

During the Centrifugo v3 lifecycle we added even more JSON protocol optimizations and introduced a granular proxy mode. Experimental Tarantool engine has also evolved a bit.

But Centrifugo v3 did not contain anything... let's say revolutional. Revolutional for Centrifugo itself, community, or even the entire field of open-source real-time messaging.

With this release, we feel that we bring innovation to the ecosystem. Now let's talk about it and introduce all the major things of the brand new v4 release.

Unified client SDK API

The most challenging part of Centrifugo project is not a server itself. Client SDKs are the hardest part of the ecosystem. We try to time additional improvements to the SDKs with each major release of the server. But this time the SDKs are the centerpiece of the v4 release.

Centrifugo uses bidirectional asynchronous protocol between client and server. On top of this protocol SDK provides a request-response over an asynchronous connection, reconnection logic, subscription management and multiplexing, timeout and error handling, ping-pong, token refresh, etc. Some of these things are not that trivial to implement. And all this should be implemented in different programming languages. As you may know, we have official real-time SDKs in Javascript, Dart, Swift, Java and Go.

While implementing the same protocol and same functions, all SDKs behaved slightly differently. That was the result of the missing SDK specification. Without a strict SDK spec, it was hard to document things, hard to explain the exact details of the real-time SDK behavior. What we did earlier in the Centrifugo documentation – was pointing users to specific SDK Github repo to look for behaviour details.

The coolest thing about Centrifugo v4 is the next generation SDK API. We now have a client SDK API specification. It's a source of truth for SDKs behavior which try to follow the spec closely.

The new SDK API is the result of several iterations and reflections on possible states, transitions, token refresh mechanism, etc. Users in our Telegram group may remember how it all started:

Centrifugo scheme

And after several iterations these prototypes turned into working mechanisms with well-defined behaviour:

Centrifugo scheme

A few things that have been revised from the ground up:

  • Client states, transitions, events
  • Subscription states, transitions, events
  • Connection and subscription token refresh behavior
  • Ping-pong behavior (see details below)
  • Resubscribe logic (SDKs can now resubscribe with backoff)
  • Error handling
  • Unified backoff behavior (based on full jitter technique)

We now also have a separation between temporary and non-temporary protocol errors – this allows us to handle subscription internal server errors on the SDK level, making subscriptions more resilient, with automatic resubscriptions, and to ensure individual subscription failures do not affect the entire connection.

The mechanics described in the client SDK API specification are now implemented in all of our official SDKs. The SDKs now support all major client protocol features that currently exist. We believe this is a big step forward for the Centrifugo ecosystem and community.

Modern WebSocket emulation in Javascript

WebSocket is supported almost everywhere these days. But there is a case that we believe is the last one preventing users to connect over WebSocket - corporate proxies. With the root certificate installed on employee computer machines, these proxies can block WebSocket traffic, even if it's wrapped in a TLS layer. That's really annoying, and often developers choose to not support clients connecting from such "broken" environments at all.

Prior to v4, Centrifugo users could use the SockJS polyfill library to fill this gap.

SockJS is great software – stable and field proven. It is still used by some huge real-time messaging players out there to polyfill the WebSocket transport.

But SockJS is an extra frontend dependency with a bunch of legacy transports, and the future of it is unknown.

SockJS comes with a notable overhead – it's an aditional protocol wrapper, consumes more memory per connection on a server (at least when using SockJS-Go library – the only choice for implementing SockJS server in Go language these days). When using SockJS, Centrifugo users were losing the ability to use our main pure WebSocket transport because SockJS uses its own WebSocket implementation on a server side.

SockJS does not support binary data transfer – only JSON format can be used with it. As you know, our main WebSocket transport works fine with binary in case of using Protobuf protocol format. So with SockJS we don't have fallback for WebSocket with a binary data transfer.

And finally, if you want to use SockJS with a distributed backend, you must enable sticky session support on the load-balancer level. This way you can point requests from the client to the server to the correct server node – the one which maintains a persistent unidirectional HTTP connection.

We danced around the idea of replacing SockJS for a long time. But only now we are ready to provide our alternative to it – meet Centrifugo own bidirectional emulation layer. It's based on two additional transports:

  • HTTP-streaming (using modern browser ReadableStream API in JavaScript, supports both binary Protobuf and JSON transfer)
  • Eventsource (Server-Sent Events, SSE) – while a bit older choice and works with JSON only EventSource transport is loved by many developers and can provide fallback in slightly older browsers which don't have ReadableStream, so we implemented bidirectional emulation with it too.

So when the fallback is used, you always have a real-time, persistent connection in server -> to -> client direction. Requests in client -> to -> server direction are regular HTTP – similar to how SockJS works. But our bidirectional emulation layer does not require sticky sessions – Centrifugo can proxy client-to-server requests to the correct node in the cluster. Having sticky sessions is an optimization for Centrifugo bidirectional emulation layer, not a requirement. We believe that this is a game changer for our users – no need to bother about proper load balancing, especially since in most cases 95% or even more users will be able to connect using the WebSocket transport.

Here is a simplified diagram of how it works:

Scheme

The bidirectional emulation layer is only supported by the Javascript SDK (centrifuge-js) – as we think fallbacks mostly make sense for browsers. If we find use cases where other SDKs can benefit from HTTP based transport – we can expand on them later.

Let's look at example of using this feature from the Javascript side. To use fallbacks, all you need to do is to set up a list of desired transports with endpoints:

const transports = [
{
transport: 'websocket',
endpoint: 'wss://your_centrifugo.com/connection/websocket'
},
{
transport: 'http_stream',
endpoint: 'https://your_centrifugo.com/connection/http_stream'
},
{
transport: 'sse',
endpoint: 'https://your_centrifugo.com/connection/sse'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()
note

We are using explicit transport endpoints in the above example due to the fact that transport endpoints can be configured separately in Centrifugo – there is no single entry point for all transports. Like the one in Socket.IO or SockJS when developer can only point client to the base address. In Centrifugo case, we are requesting an explicit transport/endpoint configuration from the SDK user.

By the way, a few advantages of HTTP-based transport over WebSocket:

  • Sessions can be automatically multiplexed within a single connection by the browser when the server is running over HTTP/2, while with WebSocket browsers open a separate connection in each browser tab
  • Better compression support (may be enabled on load balancer level)
  • WebSocket requires special configuration in some load balancers to get started (ex. Nginx)

SockJS is still supported by Centrifugo and centrifuge-js, but it's now DEPRECATED.

No layering in client protocol

Not only the API of client SDK has changed, but also the format of Centrifugo protocol messages. New format is more human-readable (in JSON case, of course), has a more compact ping message size (more on that below).

The client protocol is now one-shot encode/decode compatible. Previously, Centrifugo protocol had a layered structure and we had to encode some messages before appending them to the top-level message. Or decode two or three times to unwrap the message envelope. To achieve good performance when encoding and decoding client protocol messages, Centrifugo had to use various optimization techniques – like buffer memory pools, byte slice memory pools.

By restructuring the message format, we were able to avoid layering, which allowed us to slightly increase the performance of encoding/decoding without additional optimization tricks.

Scheme

We also simplified the client protocol documentation overview a bit.

Redesigned PING-PONG

In many cases in practice (when dealing with persistent connections like WebSocket), pings and pongs are the most dominant types of messages passed between client and server. Your application may have many concurrent connections, but only a few of them receive the useful payload. But at the same time, we still need to send pings and respond with pongs. Thus, optimizing the ping-pong process can significantly reduce server resource usage.

One optimization comes from the revised PING-PONG behaviour. Previous versions of Centrifugo and SDKs sent ping/pong in both "client->to->server" and "server->to->client" directions (for WebSocket transport). This allowed finding non-active connections on both client and server sides.

In Centrifugo v4 we only send pings from a server to a client and expect pong from a client. On the client-side, we have a timer which fires if there hasn't been a ping from the server within the configured time, so we still have a way to detect closed connections.

Sending pings only in one direction results in 2 times less ping-pong messages - and this should be really noticable for Centrifugo installations with thousands of concurrent connections. In our experiments with 10k connections, server CPU usage was reduced by 30% compared to Centrifugo v3.

Scheme

Pings and pongs are application-level messages. Ping is just an empty asynchronous reply – for example in JSON case it's a 2-byte message: {}. Pong is an empty command – also, {} in JSON case. Having application-level pings from the server also allows unifying the PING format for all unidirectional transports.

Another improvement is that Centrifugo now randomizes the time it sends first ping to the client (but no longer than the configured ping interval). This allows to spread ping-pongs in time, providing a smoother CPU profile, especially after a massive reconnect scenario.

Secure by default channel namespaces

Data security and privacy are more important than ever in today's world. And as Centrifugo becomes more popular and widely used, the need to be secure by default only increases.

Previously, by default, clients could subcribe to all channels in a namespace (except private channels, which are now revised – see details below). It was possible to use "protected": true option to make namespace protected, but we are not sure if everyone did that. This is extra configuration and additional knowledge on how Centrifugo works.

Also, a common confusion we ran into: if server-side subscriptions were dictated by a connection JWT, many users would expect client-side subscriptions to those channels to not work. But without the protected option enabled, this was not the case.

In Centrifugo v4, by default, it is not possible to subscribe to a channel in a namespace. The namespace must be configured to allow subscriptions from clients, or token authorization must be used. There are a bunch of new namespace options to tune the namespace behavior. Also the ability to provide a regular expression for channels in the namespace.

The new permission-related channel option names better reflect the purpose of the option. For example, compare "publish": true and "allow_publish_for_client": true. The second one is more readable and provides a better understanding of the effect once turned on.

Centrifugo is now more strict when checking channel name. Only ASCII symbols allowed – it was already mentioned in docs before, but wasn't actually enforced. Now we are fixing this.

We understand that these changes will make running Centrifugo more of a challenge, especially when all you want is a public access to all the channels without worrying too much about permissions. It's still possible to achieve, but now the intent must be expicitly expressed in the config.

Check out the updated documentation about channels and namespaces. Our v4 migration guide contains an automatic converter for channel namespace options.

Private channel concept revised

A private channel is a special channel starting with $ that could not be subscribed to without a subscription JWT. Prior to v4, having a known prefix allowed us to distinguish between public channels and private channels. But since namespaces are now non-public by default, this distinction is not really important.

This means 2 things:

  • it's now possible to subscribe to any channel by having a valid subscription JWT (not just those that start with $)
  • channels beginning with $ can only be subscribed with a subscription JWT, even if they belong to a namespace where subscriptions allowed for all clients. This is for security compatibility between v3 and v4.

Another notable change in a subscription JWT – client claim is now DEPRECATED. There is no need to put it in the subscription token anymore. Centrifugo supports it only for backwards compatibility, but it will be completely removed in the future releases.

The reason we're removing client claim is actually interesting. Due to the fact that client claim was a required part of the subscription JWT applications could run into a situation where during the massive reconnect scenario (say, million connections reconnect) many requests for new subscription tokens can be generated because the subscription token must contain the client ID generated by Centrifugo for the new connection. That could make it unusually hard for the application backend to handle the load. With a connection JWT we had no such problem – as connections could simply reuse the previous token to reconnect to Centrifugo.

Now the subscription token behaves just like the connection token, so we get a scalable solution for token-based subscriptions as well.

What's more, this change paved the way for another big improvement...

Optimistic subscriptions

The improvement we just mentioned is called optimistic subscriptions. If any of you are familiar with the QUIC protocol, then optimistic subscriptions are somewhat similar to the 0-RTT feature in QUIC. The idea is simple – we can include subscription commands to the first frame sent to the server.

Previously, we sent subscriptions only after receiving a successful Connect Reply to a Connect Command from a server. But with the new changes in token behaviour, it seems so logical to put subscribe commands within the initial connect frame. Especially since Centrifugo protocol always supported batching of commands. Even token-based subscriptions can now be included into the initial frame during reconnect process, since the previous token can be reused now.

The benefit is awesome – in most scenarios, we save one RTT of latency when connecting to Centrifugo and subscribing to channels (which is actually the most common way to use Centrifugo). While not visible on localhost, this is pretty important in real-life. And this is less syscalls for the server after all, resulting in less CPU usage.

Optimistic subscriptions are also great for bidirectional emulation with HTTP, as they avoid the long path of proxying a request to the correct Centrifugo node when connecting.

Optimistic subscriptions are now only part of centrifuge-js. At some point, we plan to roll out this important optimization to all other client SDKs.

Channel capabilities

The channel capabilities feature is introduced as part of Centrifugo PRO. Initially, we aimed to make it a part of the OSS version. But the lack of feedback on this feature made us nervous it's really needed. So adding it to PRO, where we still have room to evaluate the idea, seemed like the safer decision at the moment.

Centrifugo allows configuring channel permissions on a per-namespace level. When creating a new real-time feature, it is recommended to create a new namespace for it and configure permissions. But to achieve a better channel permission control within a namespace the Channel capabilities can be used now.

The channel capability feature provides a possibility to set capabilities on an individual connection basis, or an individual channel subscription basis.

For example, in a connection JWT developers can set sth like:

{
"caps": [
{
"channels": ["news", "user_42"],
"allow": ["sub"]
}
]
}

And this tells Centrifugo that the connection is able to subscribe on channels news or user_42 using client-side subscriptionsat any time while the connection is active. Centrifugo also supports wildcard and regex channel matches.

Subscription JWT can provide capabilities for the channel too, so permissions may be controlled on an individual subscription basis, ex. the ability to publish and call history API may be expressed with allow claim in subscription JWT:

{
"allow": ["pub", "hst"]
}

Read more about this mechanism in Channel capabilities chapter.

Better connections API

Another addition to Centrifugo PRO is the improved connection API. Previously, we could only return all connections from a specific user.

The API now supports filtering all connections: by user ID, by subscribed channel, by additional meta information attached to the connection.

The filtering works by user ID or with a help of CEL expressions (Common Expression Language). CEL expressions provide a developer-friendly, fast and secure (as they are not Turing-complete) way to evaluate some conditions. They are used in some Google services (ex. Firebase), in Envoy RBAC configuration, etc. If you've never seen it before – take a look, cool project. We are also evaluating how to use CEL expressions for a dynamic and efficient channel permission checks, but that's an early story.

The connections API call result contains more useful information: a list of client's active channels, information about the tokens used to connect and subscribe, meta information attached to the connection.

Javascript client moved to TypeScript

It's no secret that centrifuge-js is the most popular SDK in the Centrifugo ecosystem. We put additional love to it – and centrifuge-js is now fully written in Typescript ❤️

This was a long awaited improvement, and it finally happened! The entire public API is strictly typed. The cool thing is that even EventEmitter events and event handlers are the subject to type checks - this should drastically simplify and speedup development and also help to reduce error possibility.

Experimenting with HTTP/3

Centrifugo v4 has an experimental HTTP/3 support. Once TLS is enabled and "http3": true option is set all the endpoints on an external port will be served by a HTTP/3 server based on lucas-clemente/quic-go implementation.

It's worth noting that WebSocket will still use HTTP/1.1 for its Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and EventSource should work just fine with HTTP/3.

HTTP/3 does not currently work with our ACME autocert TLS - i.e. you need to explicitly provide paths to cert and key files as described here.

Experimenting with WebTransport

Having HTTP/3 on board allowed us to make one more thing. Some of you may remember the post Experimenting with QUIC and WebTransport published in our blog before. We danced around the idea to add WebTransport to Centrifugo since then. WebTransport IETF specification is still a draft, it changed a lot since our first blog post about it. But WebTransport object is already part of Chrome (since v97) and things seem to be very close to the release.

So we added experimental WebTransport support to Centrifugo v4. This is made possible with the help of the marten-seemann/webtransport-go library.

To use WebTransport you need to run HTTP/3 experimental server and enable WebTransport endpoint with "webtransport": true option in the configuration. Then you can connect to that endpoint using centrifuge-js. For example, let's enable WebTransport and use WebSocket as a fallback option:

const transports = [
{
transport: 'webtransport',
endpoint: 'https://your_centrifugo.com/connection/webtransport'
},
{
transport: 'websocket',
endpoint: 'wss://your_centrifugo.com/connection/websocket'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()

Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in HTTP/3 and specifically WebTransport non-TLS communication is simply not supported by the specification.

In Centrifugo case, we utilize the bidirectional reliable stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication formats are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional.

Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification we can not recommended it for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket, now we are more confident that Centrifugo will be able to provide a proper support of it.

Migration guide

The migration guide contains steps to upgrade your Centrifugo from version 3 to version 4. While there are many changes in the v4 release, it should be possible to migrate to Centrifugo v4 without changing the code on the client side at all. And then, after updating the server, gradually update the client-side to the latest version of the stack.

Conclusion

To sum it up, here are some benefits of Centrifugo v4:

  • unified experience thoughout application frontend environments
  • an optimized protocol which is generally faster, more compact and human-readable in JSON case, provides more resilient behavior for subscriptions
  • revised channel namespace security model, more granular permission control
  • more efficient and flexible use of subscription tokens
  • better initial latency – thanks to optimistic subscriptions and the ability to pre-create subscription tokens (as the client claim not needed anymore)
  • the ability to use more efficient WebSocket bidirectional emulation in the browser without having to worry about sticky sessions, unless you want to optimize the real-time infrastructure

That's it. We now begin the era of v4 and it is going to be awesome, no doubt.

Join community

The release contains many changes that strongly affect developing with Centrifugo. And of course you may have some questions or issues regarding new or changed concepts. Join our communities in Telegram (the most active) and Discord:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

Enjoy Centrifugo v4, and let the Centrifugal force be with you.

Special thanks

The refactoring of client SDKs and introducing unified behavior based on the common spec was the hardest part of Centrifugo v4 release. Many thanks to Vitaly Puzrin (who is the author of several popular open-source libraries such as markdown-it, fontello, and others). We had a series of super productive sessions with him on client SDK API design. Some great ideas emerged from these sessions and the result seems like a huge step forward for Centrifugal projects.

Also, thanks to Anton Silischev who helped a lot with WebTransport prototypes earlier this year, so we could quickly adopt WebTransport for v4.

tip

As some of you know, Centrifugo server is built on top of the Centrifuge library for Go. Most of the optimizations and improvements described here are now also part of Centrifuge library.

With its new unified SDK behavior and bidirectional emulation layer, it seems a solid alternative to Socket.IO in the Go language ecosystem.

In some cases, Centrifuge library can be a more flexible solution than Centrifugo, since Centrifugo (as a standalone server) dictates some mechanics and rules that must be followed. In the case of Centrifugo, the business logic must live on the application backend side, with Centrifuge library it can be kept closer to the real-time transport layer.

Attributions

This post used images from freepik.com: background by liuzishan. Also image by kenshinstock.

- + \ No newline at end of file diff --git a/blog/2022/07/29/101-way-to-subscribe.html b/blog/2022/07/29/101-way-to-subscribe.html index 9b1ef6b46..ae7fd2a80 100644 --- a/blog/2022/07/29/101-way-to-subscribe.html +++ b/blog/2022/07/29/101-way-to-subscribe.html @@ -16,13 +16,13 @@ - +

101 ways to subscribe user on a personal channel in Centrifugo

· 11 min read
Alexander Emelin

Centrifuge

Let's say you develop an application and want a real-time connection which is subscribed to one channel. Let's also assume that this channel is used for user personal notifications. So only one user in the application can subcribe to that channel to receive its notifications in real-time.

In this post we will look at various ways to achieve this with Centrifugo, and consider trade-offs of the available approaches. The main goal of this tutorial is to help Centrifugo newcomers be aware of all the ways to control channel permissions by reading just one document.

And... well, there are actually 8 ways I found, not 101 😇

Setup

To make the post a bit easier to consume let's setup some things. Let's assume that the user for which we provide all the examples in this post has ID "17". Of course in real-life the examples given here can be extrapolated to any user ID.

When you create a real-time connection to Centrifugo the connection is authenticated using the one of the following ways:

  • using connection JWT
  • using connection request proxy from Centrifugo to the configured endpoint of the application backend (connect proxy)

As soon as the connection is successfully established and authenticated Centrifugo knows the ID of connected user. This is important to understand.

And let's define a namespace in Centrifugo configuration which will be used for personal user channels:

{
...
"namespaces": [
{
"name": "personal",
"presence": true
}
]
}

Defining namespaces for each new real-time feature is a good practice in Centrifugo. As an awesome improvement we also enabled presence in the personal namespace, so whenever users subscribe to a channel in this namespace Centrifugo will maintain online presence information for each channel. So you can find out all connections of the specific user existing at any moment. Defining presence is fully optional though - turn it of if you don't need presence information and don't want to spend additional server resources on maintaining presence.

#1 – user-limited channel

tip

Probably the most performant approach.

All you need to do is to extend namespace configuration with allow_user_limited_channels option:

{
"namespaces": [
{
"name": "personal",
"presence": true,
"allow_user_limited_channels": true
}
]
}

On the client side you need to have sth like this (of course the ID of current user will be dynamic in real-life):

const sub = centrifuge.newSubscription('personal:#17');
sub.on('publication', function(ctx) {
console.log(ctx.data);
})
sub.subscribe();

Here you are subscribing to a channel in personal namespace and listening to publications coming from a channel. Having # in channel name tells Centrifugo that this is a user-limited channel (because # is a special symbol that is treated in a special way by Centrifugo as soon as allow_user_limited_channels enabled).

In this case the user ID part of user-limited channel is "17". So Centrifugo allows user with ID "17" to subscribe on personal:#17 channel. Other users won't be able to subscribe on it.

To publish updates to subscription all you need to do is to publish to personal:#17 using server publish API (HTTP or GRPC).

#2 - channel token authorization

tip

Probably the most flexible approach, with reasonably good performance characteristics.

Another way we will look at is using subscription JWT for subscribing. When you create Subscription object on the client side you can pass it a subscription token, and also provide a function to retrieve subscription token (useful to automatically handle token refresh, it also handles initial token loading).

const token = await getSubscriptionToken('personal:17');

const sub = centrifuge.newSubscription('personal:17', {
token: token
});
sub.on('publication', function(ctx) {
console.log(ctx.data);
})
sub.subscribe();

Inside getSubscriptionToken you can issue a request to the backend, for example in browser it's possible to do with fetch API.

On the backend side you know the ID of current user due to the native session mechanism of your app, so you can decide whether current user has permission to subsribe on personal:17 or not. If yes – return subscription JWT according to our rules. If not - return empty string so subscription will go to unsubscribed state with unauthorized reason.

Here are examples for generating subscription HMAC SHA-256 JWTs for channel personal:17 and HMAC secret key secret:

import jwt
import time

claims = {
"sub": "17",
"channel": "personal:17"
"exp": int(time.time()) + 30*60
}

token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Since we set expiration time for subscription JWT tokens we also need to provide a getToken function to a client on the frontend side:

const sub = centrifuge.newSubscription('personal:17', {
getToken: async function (ctx) {
const token = await getSubscriptionToken('personal:17');
return token;
}
});
sub.on('publication', function(ctx) {
console.log(ctx.data);
})
sub.subscribe();

This function will be called by SDK automatically to refresh subscription token when it's going to expire. And note that we omitted setting token option here – since SDK is smart enough to call provided getToken function to extract initial subscription token from the backend.

The good thing in using subscription JWT approach is that you can provide token expiration time, so permissions to subscribe on a channel will be validated from time to time while connection is active. You can also provide additional channel context info which will be attached to presence information (using info claim of subscription JWT). And you can granularly control channel permissions using allow claim of token – and give client capabilities to publish, call history or presence information (this is Centrifugo PRO feature at this point). Token also allows to override some namespace options on per-subscription basis (with override claim).

Using subscription tokens is a general approach for any channels where you need to check access first, not only for personal user channels.

#3 - subscribe proxy

tip

Probably the most secure approach.

Subscription JWT gives client a way to subscribe on a channel, and avoid requesting your backend for permission on every resubscribe. Token approach is very good in massive reconnect scenario, when you have many connections and they all resubscribe at once (due to your load balancer reload, for example). But this means that if you unsubscribed client from a channel using server API, client can still resubscribe with token again - until token will expire. In some cases you may want to avoid this.

Also, in some cases you want to be notified when someone subscribes to a channel.

In this case you may use subscribe proxy feature. When using subscribe proxy every attempt of a client to subscribe on a channel will be translated to request (HTTP or GRPC) from Centrifugo to the application backend. Application backend can decide whether client is allowed to subscribe or not.

One advantage of using subscribe proxy is that backend can additionally provide initial channel data for the subscribing client. This is possible using data field of subscribe result generated by backend subscribe handler.

{
"proxy_subscribe_endpoint": "http://localhost:9000/centrifugo/subscribe",
"namespaces": [
{
"name": "personal",
"presence": true,
"proxy_subscribe": true
}
]
}

And on the backend side define a route /centrifugo/subscribe, check permissions of user upon subscription and return result to Centrifugo according to our subscribe proxy docs. Or simply run GRPC server using our proxy definitions and react on subscription attempt sent from Centrifugo to backend over GRPC.

On the client-side code is as simple as:

const sub = centrifuge.newSubscription('personal:17');
sub.on('publication', function(ctx) {
console.log(ctx.data);
})
sub.subscribe();

#4 - server-side channel in connection JWT

tip

The approach where you don't need to manage client-side subscriptions.

Server-side subscriptions is a way to consume publications from channels without even create Subscription objects on the client side. In general, client side Subscription objects provide a more flexible and controllable way to work with subscriptions. Clients can subscribe/unsubscribe on channels at any point. Client-side subscriptions provide more details about state transitions.

With server-side subscriptions though you are consuming publications directly from Client instance:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {
token: 'CONNECTION-JWT'
});
client.on('publication', function(ctx) {
console.log('publication received from server-side channel', ctx.channel, ctx.data);
});
client.connect();

In this case you don't have separate Subscription objects and need to look at ctx.channel upon receiving publication or to publication content to decide how to handle it. Server-side subscriptions could be a good choice if you are using Centrifugo unidirectional transports and don't need dynamic subscribe/unsubscribe behavior.

The first way to subscribe client on a server-side channel is to include channels claim into connection JWT:

{
"sub": "17",
"channels": ["personal:17"]
}

Upon successful connection user will be subscribed to a server-side channel by Centrifugo. One downside of using server-side channels is that errors in one server-side channel (like impossible to recover missed messages) may affect the entire connection and result into reconnects, while with client-side subscriptions individual subsription failures do not affect the entire connection.

But having one server-side channel per-connection seems a very reasonable idea to me in many cases. And if you have stable set of subscriptions which do not require lifetime state management – this can be a nice approach without additional protocol/network overhead involved.

#5 - server-side channel in connect proxy

Similar to the previous one for cases when you are authenticating connections over connect proxy instead of using JWT.

This is possible using channels field of connect proxy handler result. The code on the client-side is the same as in Option #4 – since we only change the way how list of server-side channels is provided.

#6 - automatic personal channel subscription

tip

Almost no code approach.

As we pointed above Centrifugo knows an ID of the user due to authentication process. So why not combining this knowledge with automatic server-side personal channel subscription? Centrifugo provides exactly this with user personal channel feature.

{
"user_subscribe_to_personal": true,
"user_personal_channel_namespace": "personal",
"namespaces": [
{
"name": "personal",
"presence": true
}
]
}

This feature only subscribes non-anonymous users to personal channels (those with non-empty user ID). The configuration above will subscribe our user "17" to channel personal:#17 automatically after successful authentication.

#7 – capabilities in connection JWT

Allows using client-side subscriptions, but skip receiving subscription token. This is only available in Centrifugo PRO at this point.

So when generating JWT you can provide additional caps claim which contains channel resource capabilities:

import jwt
import time

claims = {
"sub": "17",
"exp": int(time.time()) + 30*60,
"caps": [
{
"channels": ["personal:17"],
"allow": ["sub"]
}
]
}

token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

While in case of single channel the benefit of using this approach is not really obvious, it can help when you are using several channels with stric access permissions per connection, where providing capabilities can help to save some traffic and CPU resources since we avoid generating subscription token for each individual channel.

#8 – capabilities in connect proxy

This is very similar to the previous approach, but capabilities are passed to Centrifugo in connect proxy result. So if you are using connect proxy for auth then you can still provide capabilities in the same form as in JWT. This is also a Centrifugo PRO feature.

Teardown

Which way to choose? Well, it depends. Since your application will have more than only a personal user channel in many cases you should decide which approach suits you better in each particular case – it's hard to give the universal advice.

Client-side subscriptions are more flexible in general, so I'd suggest using them whenever possible. Though you may use unidirectional transports of Centrifugo where subscribing to channels from the client side is not simple to achieve (though still possible using our server subscribe API). Server-side subscriptions make more sense there.

The good news is that all our official bidirectional client SDKs support all the approaches mentioned in this post. Hope designing the channel configuration on top of Centrifugo will be a pleasant experience for you.

- + \ No newline at end of file diff --git a/blog/2022/12/20/improving-redis-engine-performance.html b/blog/2022/12/20/improving-redis-engine-performance.html index 2a202deb4..7c297f529 100644 --- a/blog/2022/12/20/improving-redis-engine-performance.html +++ b/blog/2022/12/20/improving-redis-engine-performance.html @@ -16,14 +16,14 @@ - +

Improving Centrifugo Redis Engine throughput and allocation efficiency with Rueidis Go library

· 29 min read
Alexander Emelin

Centrifugo_Redis_Engine_Improvements

The main objective of Centrifugo is to manage persistent client connections established over various real-time transports (including WebSocket, HTTP-Streaming, SSE, WebTransport, etc – see here) and offer an API for publishing data towards established connections. Clients subscribe to channels, hence Centrifugo implements PUB/SUB mechanics to transmit published data to all online channel subscribers.

Centrifugo employs Redis as its primary scalability option – so that it's possible to distribute client connections amongst numerous Centrifugo nodes without worrying about channel subscribers connected to separate nodes. Redis is incredibly mature, simple, and fast in-memory storage. Due to various built-in data structures and PUB/SUB support Redis is a perfect fit to be both Centrifugo Broker and PresenceManager (we will describe what's this shortly).

In Centrifugo v4.1.0 we introduced an updated implementation of our Redis Engine (Engine in Centrifugo == Broker + PresenceManager) which provides sufficient performance improvements to our users. This post discusses the factors that prompted us to update Redis Engine implementation and provides some insight into the results we managed to achieve. We'll examine a few well-known Go libraries for Redis communication and contrast them against Centrifugo tasks.

Broker and PresenceManager

Before we get started, let's define what Centrifugo's Broker and PresenceManager terms mean.

Broker is an interface responsible for maintaining subscriptions from different Centrifugo nodes (initiated by client connections). That helps to scale client connections over many Centrifugo instances and not worry about the same channel subscribers being connected to different nodes – since all Centrifugo nodes connected with PUB/SUB. Messages published to one node are delivered to a channel subscriber connected to another node.

Another major part of Broker is keeping an expiring publication history for channels (streams). So that Centrifugo may provide a fast cache for messages missed by clients upon going offline for a short period and compensate at most once delivery of Redis PUB/SUB using Publication incremental offsets. Centrifugo uses STREAM and HASH data structures in Redis to store channel history and stream meta information.

In general Centrifugo architecture may be perfectly illustrated by this picture (Gophers are Centrifugo nodes all connected to Broker, and sockets are WebSockets):

gopher-broker

PresenceManager is an interface responsible for managing online presence information - list of currently active channel subscribers. While the connection is alive we periodically update presence entries for channels connection subscribed to (for channels where presence is enabled). Presence data should expire if not updated by a client connection for some time. Centrifugo uses two Redis data structures for managing presence in channels - HASH and ZSET.

Redigo

For a long time, the gomodule/redigo package served as the foundation for the Redis Engine implementation in Centrifugo. Huge props go to Mr Gary Burd for creating it.

Redigo offers a connection Pool to Redis. A simple usage of it involves getting the connection from the pool, issuing request to Redis over that connection, and then putting the connection back to the pool after receiving the result from Redis.

Let's write a simple benchmark which demonstrates simple usage of Redigo and measures SET operation performance:

func BenchmarkRedigo(b *testing.B) {
pool := redigo.Pool{
MaxIdle: 128,
MaxActive: 128,
Wait: true,
Dial: func() (redigo.Conn, error) {
return redigo.Dial("tcp", ":6379")
},
}
defer pool.Close()

b.ResetTimer()
b.SetParallelism(128)
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
c := pool.Get()
_, err := c.Do("SET", "redigo", "test")
if err != nil {
b.Fatal(err)
}
c.Close()
}
})
}

Let's run it:

BenchmarkRedigo-8        228804        4648 ns/op        62 B/op         2 allocs/op

Seems pretty fast, but we can improve it further.

Redigo with pipelining

To increase a throughput in Centrifugo, instead of using Redigo's Pool for each operation, we acquired a dedicated connection from the Pool and utilized Redis pipelining to send multiple commands where possible.

Redis pipelining improves performance by executing multiple commands using a single client-server-client round trip. Instead of executing many commands one by one, you can queue the commands in a pipeline and then execute the queued commands as if it is a single command. Redis processes commands in order and sends individual response for each command. Given a single CPU nature of Redis, reducing the number of active connections when using pipelining has a positive impact on throughput – therefore pipelining is beneficial from this angle as well.

Redis pipeline

You can quickly estimate the benefits of pipelining by running Redis locally and running redis-benchmark which comes with Redis distribution over it:

> redis-benchmark -n 100000 set key value

Summary:
throughput summary: 84674.01 requests per second

And with pipelining:

> redis-benchmark -n 100000 -P 64 set key value

Summary:
throughput summary: 666880.00 requests per second

In Centrifugo we are using smart batching technique for collecting pipeline (also described in one of the previous posts in this blog).

To demonstrate benefits from using pipelining let's look at the following benchmark:

const (
maxCommandsInPipeline = 512
numPipelineWorkers = 1
)

type command struct {
errCh chan error
}

type sender struct {
cmdCh chan command
pool redigo.Pool
}

func newSender(pool redigo.Pool) *sender {
p := &sender{
cmdCh: make(chan command),
pool: pool,
}
go func() {
for {
for i := 0; i < numPipelineWorkers; i++ {
p.runPipelineRoutine()
}
}
}()
return p
}

func (s *sender) send() error {
errCh := make(chan error, 1)
cmd := command{
errCh: errCh,
}
// Submit command to be executed by runPipelineRoutine.
s.cmdCh <- cmd
return <-errCh
}

func (s *sender) runPipelineRoutine() {
conn := p.pool.Get()
defer conn.Close()
for {
select {
case cmd := <-s.cmdCh:
commands := []command{cmd}
conn.Send("set", "redigo", "test")
loop:
// Collect batch of commands to send to Redis in one RTT.
for i := 0; i < maxCommandsInPipeline; i++ {
select {
case cmd := <-s.cmdCh:
commands = append(commands, cmd)
conn.Send("set", "redigo", "test")
default:
break loop
}
}
// Flush all collected commands to the network.
err := conn.Flush()
if err != nil {
for i := 0; i < len(commands); i++ {
commands[i].errCh <- err
}
continue
}
// Read responses to commands, they come in order.
for i := 0; i < len(commands); i++ {
_, err := conn.Receive()
commands[i].errCh <- err
}
}
}
}

func BenchmarkRedigoPipelininig(b *testing.B) {
pool := redigo.Pool{
Wait: true,
Dial: func() (redigo.Conn, error) {
return redigo.Dial("tcp", ":6379")
},
}
defer pool.Close()

sender := newSender(pool)

b.ResetTimer()
b.SetParallelism(128)
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
err := sender.send()
if err != nil {
b.Fatal(err)
}
}
})
}

This is a strategy that we employed in Centrifugo for a long time. As you can see code with automatic pipelining gets more complex, and in real life it's even more complicated to support different types of commands, channel send timeouts, and server shutdowns.

What about the performance of this approach?

BenchmarkRedigo-8               228804      4648 ns/op       62 B/op     2 allocs/op
BenchmarkRedigoPipelininig-8 1840758 604.7 ns/op 176 B/op 4 allocs/op

Operation latency reduced from 4648 ns/op to 604.7 ns/op – not bad right?

It's worth mentioning that upon increased RTT between application and Redis the approach with pipelining will provide worse throughput. But it still can be better than in pool-based approach. Let's say we have latency 5ms between app and Redis. This means that with pool size of 128 you will be able to issue up to 128 * (1000 / 5) = 25600 requests per second over 128 connections. With the pipelining approach above the theoretical limit is 512 * (1000 / 5) = 102400 requests per second over a single connection (though in case of using code for pipelining shown above we need to have larger parallelism, say 512 instead of 128). And it can scale further if you increase numPipelineWorkers to work over several connections in paralell. Though increasing numPipelineWorkers has negative effect on CPU – we will discuss this later in this post.

Redigo is an awesome battle-tested library that served us great for a long time.

Motivation to migrate

There are three modes in which Centrifugo can work with Redis these days:

  1. Connecting to a standalone single Redis instance
  2. Connecting to Redis in master-replica configuration, where Redis Sentinel controls the failover process
  3. Connecting to Redis Cluster

All modes additionally can be used with client-side consistent sharding. So it's possible to scale Redis even without a Redis Cluster setup.

Unfortunately, with pure Redigo library, it's only possible to implement [ 1 ] – i.e. connecting to a single standalone Redis instance.

To support the scheme with Sentinel you whether need to have a proxy between the application and Redis which proxies the connection to Redis master. For example, with Haproxy it's possible in this way:

listen redis
server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 on-marked-down shutdown-sessions on-marked-up shutdown-backup-sessions
server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup
bind *:6379
mode tcp
option tcpka
option tcplog
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
balance roundrobin

Or, you need to additionally import FZambia/sentinel library - which provides a communication layer with Redis Sentinel on top of Redigo's connection Pool.

For communicating with Redis Cluster one more library may be used – mna/redisc which is also a layer on top of redigo basic functionality.

Combining redigo + FZambia/sentinel + mna/redisc we managed to implement all three connection modes. This worked, though resulted in rather tricky Redis setup. Also, it was difficult to re-use existing pipelining code we had for a standalone Redis with Redis Cluster. As a result, Centrifugo only used pipelining in a standalone or Sentinel Redis cases. When using Redis Cluster, however, Centrifugo merely used the connection pool to issue requests thus not benefiting from request pipelining. Due to this we had some code duplication to send the same requests in various Redis configurations.

Another thing is that Redigo uses interface{} for command construction. To send command to Redis Redigo has Do method which accepts name of the command and variadic interface{} arguments to construct command arguments:

Do(commandName string, args ...interface{}) (reply interface{}, err error)

While this works well and you can issue any command to Redis, you need to be very accurate when constructing a command. This also adds some allocation overhead. As we know more memory allocations lead to the increased CPU utilization because the allocation process itself requires more processing power and the GC is under more strain.

At some point we felt that eliminating additional dependencies (even though I am the author of one of them) and reducing allocations in Redis communication layer is a nice step forward for Centrifugo. So we started looking around for redigo alternatives.

To summarize, here is what we wanted from Redis library:

  • Possibility to work with all three Redis setup options we support: standalone, master-replica(s) with Sentinel, Redis Cluster, so we can depend on one library instead of three
  • Less memory allocations (and more type-safety API is a plus)
  • Support working with RESP2-only Redis servers as we need that for backwards compatibility. And some vendors like Redis Enterprise still support RESP2 protocol only
  • The library should be actively maintained

Go-redis/redis

The most obvious alternative to Redigo is go-redis/redis package. It's popular, regularly gets updates, used by a huge amount of Go projects (Grafana, Thanos, etc.). And maintained by Vladimir Mihailenco who created several more awesome Go libraries, like msgpack for example. I personally successfully used go-redis/redis in several other projects I worked on.

To avoid setup boilerplate for various Redis installation variations go-redis/redis has UniversalClient. From docs:

UniversalClient is a wrapper client which, based on the provided options, represents either a ClusterClient, a FailoverClient, or a single-node Client. This can be useful for testing cluster-specific applications locally or having different clients in different environments.

In terms of implementation go-redis/redis also has internal pool of connections to Redis, similar to redigo. It's also possible to use Client.Pipeline method to allocate a Pipeliner interface and use it for pipelining. So UniversalClient reduces setup boilerplate for different Redis installation types and number of dependencies we had, and it provide very similar way to pipeline requests so we could easily re-implement things we had with Redigo.

Go-redis also provides more type-safety when constructing commands compared to Redigo, almost every command in Redis is implemented as a separate method of Client, for example Publish defined as:

func (c Client) Publish(ctx context.Context, channel string, message interface{}) *IntCmd

You can see though that we still have interface{} here for message argument type. I suppose this was implemented in such way for convenience – to pass both string or []byte. But it still produces some extra allocations.

Without pipelining the simplest program with go-redis/redis may look like this:

func BenchmarkGoredis(b *testing.B) {
client := redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{":6379"},
PoolSize: 128,
})
defer client.Close()

b.ResetTimer()
b.SetParallelism(128)
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
resp := client.Set(context.Background(), "goredis", "test", 0)
if resp.Err() != nil {
b.Fatal(resp.Err())
}
}
})
}

Let's run it:

BenchmarkRedigo-8        228804        4648 ns/op        62 B/op         2 allocs/op
BenchmarkGoredis-8 268444 4561 ns/op 244 B/op 8 allocs/op

Result is pretty comparable to Redigo, though Go-redis allocates more (btw most of allocations come from the connection liveness check upon getting from the pool which can not be turned off).

It's interesting – if we dive deeper into what is it we can discover that this is the only way in Go to check connection was closed without reading data from it. The approach was originally introduced by go-sql-driver/mysql, it's not cross-platform, and related issue may be found in Go issue tracker.

But as I said in Centrifugo we already used pipelining over the dedicated connection for all operations so we avoid frequently getting connections from the pool. And early experiments proved that go-redis may provide some performance benefits for our use case.

At some point @j178 sent a pull request to Centrifuge library with Broker and PresenceManager implementations based on go-redis/redis. The amount of code to cover all the various Redis setups was reduced, we got only one dependency instead of three 🔥

But what about performance? Here we will show results for several operations which are typical for Centrifugo:

  1. Publish a message to a channel without saving it to the history - this is just a Redis PUBLISH command going through Redis PUB/SUB system (RedisPublish)
  2. Publish message to a channel with saving it to history - this involves executing the LUA script on Redis side where we add a publication to STREAM data structure, update meta information HASH, and finally PUBLISH to PUB/SUB (RedisPublish_History)
  3. Subscribe to a channel - that's a SUBSCRIBE Redis command, this is important to have it fast as Centrifugo should be able to re-subscribe to all the channels in the system upon mass client reconnect scenario (RedisSubscribe)
  4. Recovering missed publication state from channel STREAM, this is again may be called lots of times when all clients reconnect at once (RedisRecover).
  5. Updating connection presence information - many connections may periodically update their channel online presence information in Redis (RedisAddPresence)

Here are the benchmark results we got when comparing redigo (v1.8.9) implementation (old) and go-redis/redis (v9.0.0-rc.2) implementation (new) with Redis v6.2.7 on Mac with M1 processor and benchmark paralellism 128:

❯ benchstat redigo_p128.txt goredis_p128.txt
name old time/op new time/op delta
RedisPublish-8 1.45µs ±10% 1.88µs ± 4% +29.32% (p=0.000 n=10+10)
RedisPublish_History-8 12.5µs ± 6% 9.7µs ± 3% -22.77% (p=0.000 n=10+10)
RedisSubscribe-8 1.47µs ±24% 1.47µs ±10% ~ (p=0.469 n=10+10)
RedisRecover-8 18.4µs ± 2% 6.3µs ± 0% -65.78% (p=0.000 n=10+8)
RedisAddPresence-8 3.72µs ± 1% 3.40µs ± 1% -8.74% (p=0.000 n=10+10)

name old alloc/op new alloc/op delta
RedisPublish-8 483B ± 0% 499B ± 0% +3.37% (p=0.000 n=9+10)
RedisPublish_History-8 1.30kB ± 0% 1.08kB ± 0% -16.67% (p=0.000 n=10+10)
RedisSubscribe-8 892B ± 2% 662B ± 6% -25.83% (p=0.000 n=10+10)
RedisRecover-8 1.25kB ± 1% 1.00kB ± 0% -19.91% (p=0.000 n=10+10)
RedisAddPresence-8 907B ± 0% 827B ± 0% -8.82% (p=0.002 n=7+8)

name old allocs/op new allocs/op delta
RedisPublish-8 10.0 ± 0% 9.0 ± 0% -10.00% (p=0.000 n=10+10)
RedisPublish_History-8 29.0 ± 0% 25.0 ± 0% -13.79% (p=0.000 n=10+10)
RedisSubscribe-8 22.0 ± 0% 14.0 ± 0% -36.36% (p=0.000 n=8+7)
RedisRecover-8 29.0 ± 0% 23.0 ± 0% -20.69% (p=0.000 n=10+10)
RedisAddPresence-8 18.0 ± 0% 17.0 ± 0% -5.56% (p=0.000 n=10+10)
danger

Please note that this benchmark is not a pure performance comparison of two Go libraries for Redis – it's a performance comparison of Centrifugo Engine methods upon switching to a new library.

Or visualized in Grafana:

note

Centrifugo benchmarks results shown in the post use parallelism 128. If someone interested to check numbers for paralellism 1 or 16 – check out this comment on Github.

We observe a noticeable reduction in allocations in these benchmarks and in most benchmarks (presented here and other not listed in this post) we observed a reduced latency.

Overall, results convinced us that the migration from redigo to go-redis/redis may provide Centrifugo with everything we aimed for – all the goals for a redigo alternative outlined above were successfully fullfilled.

One good thing go-redis/redis allowed us to do is to use Redis pipelining also in a Redis Cluster case. It's possible due to the fact that go-redis/redis re-maps pipeline objects internally based on keys to execute pipeline on the correct node of Redis Cluster. Actually, we could do the same based on redigo + mna/redisc, but here we got it for free.

BTW, there is a page with comparison between redigo and go-redis/redis in go-redis/redis docs which outlines some things I mentioned here and some others.

But we have not migrated to go-redis/redis in the end. And the reason is another library – rueidis.

Rueidis

While results were good with go-redis/redis we also made an attempt to implement Redis Engine on top of rueian/rueidis library written by @rueian. According to docs, rueidis is:

A fast Golang Redis client that supports Client Side Caching, Auto Pipelining, Generics OM, RedisJSON, RedisBloom, RediSearch, RedisAI, RedisGears, etc.

The readme of rueidis contains benchmark results where it hugely outperforms go-redis/redis in terms of operation latency/throughput in both single Redis and Redis Custer setups:

rueidis works with standalone Redis, Sentinel Redis and Redis Cluster out of the box. Just like UniversalClient of go-redis/redis. So it also allowed us to reduce code boilerplate to work with all these setups.

Again, let's try to write a simple program like we had for Redigo and Go-redis above:

func BenchmarkRueidis(b *testing.B) {
client, err := rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{":6379"},
})
if err != nil {
b.Fatal(err)
}

b.ResetTimer()
b.SetParallelism(128)
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
cmd := client.B().Set().Key("rueidis").Value("test").Build()
res := client.Do(context.Background(), cmd)
if res.Error() != nil {
b.Fatal(res.Error())
}
}
})
}

And run it:

BenchmarkRedigo-8        228804        4648 ns/op        62 B/op         2 allocs/op
BenchmarkGoredis-8 268444 4561 ns/op 244 B/op 8 allocs/op
BenchmarkRueidis-8 2908591 418.5 ns/op 4 B/op 1 allocs/op

rueidis library comes with automatic implicit pipelining, so you can send each request in isolated way while rueidis makes sure the request becomes part of the pipeline sent to Redis – thus utilizing the connection between an application and Redis most efficiently with maximized throughput. The idea of implicit pipelining with Redis is not new and Go ecosystem already had joomcode/redispipe library which implemented it (though it comes with some limitations which made it unsuitable for Centrifugo use case).

So applications that use a pool-based approach for communication with Redis may observe dramatic improvements in latency and throughput when switching to the Rueidis library.

For Centrifugo we didn't expect such a huge speed-up as shown in the above graphs since we already used pipelining in Redis Engine. But rueidis implements some ideas which allow it to be efficient. Insights about these ideas are provided by Rueidis author in a "Writing a High-Performance Golang Client Library" series of posts on Medium:

I did some prototypes with rueidis which were super-promising in terms of performance. There were some issues found during that early prototyping (mostly with PUB/SUB) – but all of them were quickly resolved by Rueian.

Until v0.0.80 release rueidis did not support RESP2 though, so we could not replace our Redis Engine implementation with it. But as soon as it got RESP2 support we opened a pull request with alternative implementation.

Since auto-pipelining is used in rueidis by default we were able to remove some of our own pipelining management code – so the Engine implementation is more concise now. One more thing to mention is a simpler PUB/SUB code we were able to write with rueidis. One example is that in redigo case we had to periodically PING PUB/SUB connection to maintain it alive, rueidis does this automatically.

Regarding performance, here are the benchmark results we got when comparing redigo (v1.8.9) implementation (old) and rueidis (v0.0.90) implementation (new):

❯ benchstat redigo_p128.txt rueidis_p128.txt
name old time/op new time/op delta
RedisPublish-8 1.45µs ±10% 0.56µs ± 1% -61.53% (p=0.000 n=10+9)
RedisPublish_History-8 12.5µs ± 6% 9.7µs ± 1% -22.43% (p=0.000 n=10+9)
RedisSubscribe-8 1.47µs ±24% 1.45µs ± 1% ~ (p=0.484 n=10+9)
RedisRecover-8 18.4µs ± 2% 6.2µs ± 1% -66.08% (p=0.000 n=10+10)
RedisAddPresence-8 3.72µs ± 1% 3.60µs ± 1% -3.34% (p=0.000 n=10+10)

name old alloc/op new alloc/op delta
RedisPublish-8 483B ± 0% 91B ± 0% -81.16% (p=0.000 n=9+10)
RedisPublish_History-8 1.30kB ± 0% 0.39kB ± 0% -70.08% (p=0.000 n=10+8)
RedisSubscribe-8 892B ± 2% 360B ± 0% -59.66% (p=0.000 n=10+10)
RedisRecover-8 1.25kB ± 1% 0.36kB ± 1% -71.52% (p=0.000 n=10+10)
RedisAddPresence-8 907B ± 0% 151B ± 1% -83.34% (p=0.000 n=7+9)

name old allocs/op new allocs/op delta
RedisPublish-8 10.0 ± 0% 2.0 ± 0% -80.00% (p=0.000 n=10+10)
RedisPublish_History-8 29.0 ± 0% 10.0 ± 0% -65.52% (p=0.000 n=10+10)
RedisSubscribe-8 22.0 ± 0% 6.0 ± 0% -72.73% (p=0.002 n=8+10)
RedisRecover-8 29.0 ± 0% 7.0 ± 0% -75.86% (p=0.000 n=10+10)
RedisAddPresence-8 18.0 ± 0% 3.0 ± 0% -83.33% (p=0.000 n=10+10)

Or visualized in Grafana:

2.5x times more publication throughput than we had before! Instead of 700k publications/sec, we went towards 1.7 million publications/sec due to drastically decreased publish operation latency (1.45µs -> 0.59µs). This means that our previous Engine implementation under-utilized Redis, and Rueidis just pushes us towards Redis limits. The latency of most other operations is also reduced.

The allocation effectiveness of the implementation based on "rueidis" is best. As you can see rueidis helped us to generate sufficiently fewer memory allocations for all our Redis operations. Allocation improvements directly affect Centrifugo node CPU usage. Though we will talk about CPU more later below.

For Redis Cluster case we also got benchmark results similar to the standalone Redis results above.

I might add that I enjoyed building commands with rueidis. All Redis commands may be constructed using a builder approach. Rueidis comes with builders generated for all Redis commands. As an illustration, this is a process of building a PUBLISH Redis command:

This drastically reduces a chance to make a stupid mistake while constructing a command. Instead of always opening Redis docs to see a command syntax it's now possible to just start typing - and quickly come to the complete command to send.

Switching to Rueidis: reducing CPU usage

After making all these benchmarks and implementing Engine in Rueidis I decided to check whether Centrifugo consumes less CPU with it. I expected a notable CPU reduction as Rueidis Engine implementation allocates much less than Redigo-based. Turned out it's not that simple.

I ran Centrifugo with some artificial load and noticed that CPU consumption of the new implementation is actually... worse than we had with Redigo-based engine under equal conditions!😩 But why?

As I mentioned above Redis pipelining is a technique when several commands may be combined into one batch to send over the network. In case of automatic pipelining the size of generated batches start playing a crucial role in application and Redis CPU usage – since smaller command batches result into more read/write system calls to the kernel on both application and Redis server sides. That's why projects like Twemproxy which sit between app and Redis have sich a good effect on Redis CPU usage among other things.

As we have seen above, Rueidis provides a better throughput and latency, but it's more agressive in terms of flushing data to the network. So in its default configuration we get smaller batches under th equal conditions than we had before in our own pipelining implementation based on Redigo (shown in the beginning of this post).

Luckily, there is an option in Rueidis called MaxFlushDelay which allows to slow down write loop a bit to give Rueidis a chance to collect more commands to send in one batch. When this option is used Rueidis will make a pause after each network flush not bigger than selected value of MaxFlushDelay (please note, that this is a delay after flushing collected pipeline commands, not an additional delay for each request). Using some reasonable value it's possible to drastically reduce both application and Redis CPU utilization.

To demonstrate this I created a repo: https://github.com/FZambia/pipelines.

This repo contains three benchmarks where we use automatic pipelining: based on redigo, based on go-redis/redis and rueidis. In these benchmarks we produce concurrent requests, but instead of pushing the system towards the limits we are limiting number of requests sent to Redis, so we put all libraries in equal conditions.

To rate limit requests we are using uber-go/ratelimit library. For example, to allow rate no more than 100k commands per second we can do sth like this:

rl := ratelimit.New(100, ratelimit.Per(time.Millisecond))
for {
rl.Take()
...
}

We limit requests per second we could actually just write ratelimit.New(100000) – but we aim to get a more smooth distribution of requests over time - so using millisecond resolution.

Let's run all the benchmarks in the default configuration:

Average CPU usage during the test (a bit rough but enough for demonstration):

RedigoGo-redis/redisRueidis
Application CPU, %9599116
Redis CPU, %363542

OK, Rueidis-based implementation is the worst here despite of allocating less than others. So let's try to change this by setting MaxFlushDelay to sth like 100 microseconds:

Now CPU usage is:

RedigoGo-redis/redisRueidis
Application CPU, %959959
Redis CPU, %363512

So we can achieve great CPU usage reduction. CPU went from 116% to 59% for the application side, and from 42% to only 12% for Redis! We are sacrificing latency though. Given the fact the CPU utilization reduction is very notable the trade-off is pretty fair.

caution

It's definitely possible to improve CPU usage in Redigo and Go-redis/redis cases too – using similar technique. But the goal here was to improve Rueidis-based engine implementation to make it comparable or better than our Redigo-based implementation in terms of CPU utilization.

As you can see we were able to achieve better CPU results just by using 100 microseconds delay after each network flush. In real life, where we are not running Redis on localhost and have some network latency in between application and Redis, this delay should be insignificant at all. Indeed, adding MaxFlushDelay can even improve (!) the latency you have. You may wonder what happened with benchmarks we showed above after we added MaxFlushDelay option. In Centrifugo we chose default value 100 microseconds, and here are results on localhost (old without delay, new with delay):

> benchstat rueidis_p128.txt rueidis_delay_p128.txt
name old time/op new time/op delta
RedisPublish-8 559ns ± 1% 468ns ± 0% -16.35% (p=0.000 n=9+8)
RedisPublish_History-8 9.72µs ± 1% 9.67µs ± 1% -0.52% (p=0.007 n=9+8)
RedisSubscribe-8 1.45µs ± 1% 1.27µs ± 1% -12.49% (p=0.000 n=9+10)
RedisRecover-8 6.25µs ± 1% 5.85µs ± 0% -6.32% (p=0.000 n=10+10)
RedisAddPresence-8 3.60µs ± 1% 3.33µs ± 1% -7.52% (p=0.000 n=10+10)

(rest is not important here...)

It's even better for this set of benchmarks. Though while it's better for these benchmarks the numbers may differ for other under different conditions. For example, in the benchmarks we run we use concurrency 128, if we reduce concurrency we will notice reduced throughput – as batches Rueidis collects become smaller. Smaller batches + some delay to collect = less requests per second to send.

The problem is that the value to pause Rueidis write loop is a very use case specific, it's pretty hard to provide a reasonable default for it. Depending on request rate/size, network latency etc. you may choose a larger or smaller delay. In v4.1.0 we start with hardcoded 100 microsecond MaxFlushDelay which seems sufficient for most use cases and showed good results in the benchmarks - though possibly we will have to make it tunable later.

To check that Centrifugo benchmarks also utilize less CPU I added rate limiter (50k rps per second) to benchmarks and compared version without MaxFlushDelay and with 100 microsecond MaxFlushDelay:

50k req per secondWithout delayWith 100mks delay
BenchmarkPublishCentrifugo - 75%, Redis - 24%Centrifugo - 44%, Redis - 9%
BenchmarkPublish_HistoryCentrifugo - 80% , Redis - 67%Centrifugo - 55%, Redis - 50%
BenchmarkSubscribeCentrifugo - 80%, Redis - 30%Centrifugo - 45% , Redis - 14%
BenchmarkRecoverCentrifugo - 84%, Redis - 51%Centrifugo - 51%, Redis - 36%
BenchmarkPresenceCentrifugo - 114%, Redis - 69%Centrifugo - 90%, Redis - 60%
note

In this test I replaced BenchmarkAddPresence with BenchmarkPresence (get information about all online subscribers in channel) to also make sure we have CPU reduction when using read-intensive method, i.e. when Redis response is reasonably large.

We observe a notable CPU usage improvement here.

Hope you understand now why increasing numPipelineWorkers value in the pipelining code showed before results into increased CPU usage on app and Redis sides – due to smaller batch sizes and more read/write system calls as the consequence.

note

BTW, would it be a nice thing if Go benchmarking suite could show a CPU usage of the process in addition to time and alloc stats? 🤔

Adding latency

The last thing to check is how new implementation works upon increased RTT between application and Redis. To add artificial latency on localhost on Linux one can use tc tool as shown here by Daniel Stenberg. But I am on MacOS so the simplest way I found was using Shopify/toxiproxy. Sth like running a server:

toxyproxy-server

And then in another terminal I used toxiproxy-cli to create toxic Redis proxy with additional latency on port 26379:

toxiproxy-cli create -l localhost:26379 -u localhost:6379 toxic_redis
toxiproxy-cli toxic add -t latency -a latency=5 toxic_redis

The benchmark results are (old is Redigo-based, new is Rueidis-based):

> benchstat redigo_latency_p128.txt rueidis_delay_latency_p128.txt
name old time/op new time/op delta
RedisPublish-8 31.5µs ± 1% 5.6µs ± 3% -82.26% (p=0.000 n=9+10)
RedisPublish_History-8 62.8µs ± 3% 10.6µs ± 4% -83.05% (p=0.000 n=10+10)
RedisSubscribe-8 1.52µs ± 5% 6.05µs ± 8% +298.70% (p=0.000 n=8+10)
RedisRecover-8 48.3µs ± 3% 7.3µs ± 4% -84.80% (p=0.000 n=10+10)
RedisAddPresence-8 52.3µs ± 4% 5.8µs ± 2% -88.94% (p=0.000 n=10+10)

(rest is not important here...)

We see that new Engine implementation behaves much better for most cases. But what happened to Subscribe operation? It did not change at all in Redigo case – the same performance as if there is no additional latency involved!

Turned out that when we call Subscribe in Redigo case, Redigo only flushes data to the network without waiting synchronously for subscribe result.

It makes sense in general and we can listen to subscribe notifications asynchronously, but in Centrifugo we relied on the returned error thinking that it includes succesful subscription result from Redis - meaning that we already subscribed to a channel at that point. And this could theoretically lead to some rare bugs in Centrifugo.

Rueidis library waits for subscribe response. So here the behavior of rueidis while differs from redigo in terms of throughput under increased latency just fits Centrifugo better in terms of behavior. So we go with it.

Conclusion

Migrating from Redigo to Rueidis library was not just a task of rewriting code, we had to carefully test various aspects of Redis Engine behaviour – latency, throughput, CPU utilization of application, and even CPU utilization of Redis itself under the equal application load conditions.

I think that we will find more projects in Go ecosystem using rueidis library shortly. Not just because of its allocation efficiency and out-of-the-box throughput, but also due to a convenient type-safe command API.

For most Centrifugo users this migration means more efficient CPU usage as new implementation allocates less memory (less work to allocate and less strain on GC) and we tried to find a reasonable batch size to reduce the number of system calls for common operations. While latency and throughput of single Centrifugo node should be better as we make concurrent Redis calls from many goroutines.

Hopefully readers will learn some tips from this post which can help to achieve effective communication with Redis from Go or another programming language.

A few key takeaways:

  • Redis pipelining may increase throughput and reduce latency, it can also reduce CPU utilization of Redis
  • Don't blindly trust Go benchmark numbers but also think about CPU effect of changes you made (sometimes of the external system also)
  • Reduce the number of system calls to decrease CPU utilization
  • Everything is a trade-off – latency or resource usage? Your own WebSocket server or Centrifugo?
  • Don't rely on someone's else benchmarks, including those published here. Measure for your own use case. Take into account your load profile, paralellism, network latency, data size, etc.

P.S. One thing worth mentioning and which may be helpful for someone is that during our comparison experiments we discovered that Redis 7 has a major latency increase compared to Redis 6 when executing Lua scripts. So if you have performance sensitive code with Lua scripts take a look at this Redis issue. With the help of Redis developers some things already improved in unstable Redis branch, hopefully that issue will be closed at the time you read this post.

- + \ No newline at end of file diff --git a/blog/2023/03/31/keycloak-sso-centrifugo.html b/blog/2023/03/31/keycloak-sso-centrifugo.html index 3b689f530..8759fa06a 100644 --- a/blog/2023/03/31/keycloak-sso-centrifugo.html +++ b/blog/2023/03/31/keycloak-sso-centrifugo.html @@ -16,13 +16,13 @@ - +

Setting up Keycloak SSO authentication flow and connecting to Centrifugo WebSocket

· 5 min read
Alexander Emelin

Securing user authentication and management can often be a challenging task when developing a modern application. As a result, many developers choose to delegate this responsibility to third-party identity providers, such as Okta, Auth0, or Keycloak.

In this blog post, we'll go through the process of setting up Single Sign-On (SSO) authentication using Keycloak - popular and powerful identity provider. After setting up SSO we will create React application and connect to Centrifugo using access token generated by Keycloak for our test user:

TLDR

The integraion is possible since Centrifugo works with standard JWT for authentication and additionally supports JSON Web Key specification.

Here is a final source code.

Keycloak

First, run Keycloak using the following Docker command:

docker run --rm -it -p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:21.0.1 start-dev

After starting Keycloak, go to http://localhost:8080/admin and login. Then perform the following tasks:

  1. Create a new realm named myrealm.
  2. Create a new client named myclient. Set valid redirect URIs to http://localhost:5173/*, and web origins as http://localhost:5173.
  3. Create a user named myuser and set a password for it (in Credentials tab).

See this guide for additional details and illustrations of the process.

Make sure your created client is public (this is default) since we will request token directly from the web application.

Centrifugo

Next, run Centrifugo using the following Docker command:

docker run --rm -it -p 8000:8000 \
-e CENTRIFUGO_ALLOWED_ORIGINS="http://localhost:5173" \
-e CENTRIFUGO_TOKEN_JWKS_PUBLIC_ENDPOINT="http://host.docker.internal:8080/realms/myrealm/protocol/openid-connect/certs" \
-e CENTRIFUGO_ALLOW_USER_LIMITED_CHANNELS=true \
-e CENTRIFUGO_ADMIN=true \
-e CENTRIFUGO_ADMIN_SECRET=secret \
-e CENTRIFUGO_ADMIN_PASSWORD=admin \
centrifugo/centrifugo:v4.1.2 centrifugo

Some comments about environment variables used here:

  • CENTRIFUGO_TOKEN_JWKS_PUBLIC_ENDPOINT allows tell Centrifugo to use JSON Web Key spec when validating tokens, we point to Keycloak's JWKS endpoint
  • CENTRIFUGO_ALLOWED_ORIGINS is required since we will build Vite + React based app running on http://localhost:5173
  • CENTRIFUGO_ALLOW_USER_LIMITED_CHANNELS - not required to connect, but you will see in the source code that we additionally subscribe to a user personal channel
  • CENTRIFUGO_ADMIN, CENTRIFUGO_ADMIN_SECRET, CENTRIFUGO_ADMIN_PASSWORD - to enable Centrifugo admin web UI

Also note we are using host.docker.internal to access host port from inside the Docker network.

React app with Vite

Now, let's create a new React app using very popular Vite tool:

npm create vite@latest keycloak_sso_auth -- --template react
cd keycloak_sso_auth
npm install

Also, install the necessary additional packages for the React app:

npm install --save @react-keycloak/web centrifuge keycloak-js

And start the development server:

npm run dev

Navigate to http://localhost:5173/. You should see default Vite template working, we are going to modify it a bit.

caution

Use localhost, not 127.0.0.1 - since we used localhost for Keyloak and Centrifugo configurations above.

Add the following into main.jsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import { ReactKeycloakProvider } from '@react-keycloak/web'
import App from './App'
import './index.css'

import Keycloak from "keycloak-js";

const keycloakClient = new Keycloak({
url: "http://localhost:8080",
realm: "myrealm",
clientId: "myclient"
})

ReactDOM.createRoot(document.getElementById('root')).render(
<ReactKeycloakProvider authClient={keycloakClient}>
<React.StrictMode>
<App />
</React.StrictMode>
</ReactKeycloakProvider>,
)

Note that we configured Keycloak instance pointing it to our Keycloak server. We also use @react-keycloak/web package to wrap React app into ReactKeycloakProvider component. It simplifies working with Keycloak by providing some useful hooks - we are using this hook below.

Our App component inside App.jsx may look like this:

import React, { useState, useEffect } from 'react';
import logo from './assets/centrifugo.svg'
import { Centrifuge } from "centrifuge";
import { useKeycloak } from '@react-keycloak/web'
import './App.css'

function App() {
const { keycloak, initialized } = useKeycloak()

if (!initialized) {
return null;
}

return (
<div>
<header>
<p>
SSO with Keycloak and Centrifugo
</p>
{keycloak.authenticated ? (
<div>
<p>Logged in as {keycloak.tokenParsed?.preferred_username}</p>
<button type="button" onClick={() => keycloak.logout()}>
Logout
</button>
</div>
) : (
<button type="button" onClick={() => keycloak.login()}>
Login
</button>
)}
</header>
</div >
);
}

export default App

This is actually enough for SSO flow to start working! You can click on login button and make sure that it's possible to use myuser credentials to log into the application. And log out after that.

The only missing part is Centrifugo. We can initialize connection inside useEffect hook of App component:

useEffect(() => {
if (!initialized || !keycloak.authenticated) {
return;
}
const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: keycloak.token,
getToken: function () {
return new Promise((resolve, reject) => {
keycloak.updateToken(5).then(function () {
resolve(keycloak.token);
}).catch(function (err) {
reject(err);
keycloak.logout();
});
})
}
});

centrifuge.connect();

return () => {
centrifuge.disconnect();
};
}, [keycloak, initialized]);

The important thing here is how we configure tokens: we are using Keycloak client methods to set initial token and refresh the token when required.

I also added some extra elements to the code to make it look a bit nicer. For example, we can listen to Centriffuge client state changes and show connection indicator on the page:

function App() {
const [connectionState, setConnectionState] = useState("disconnected");
const stateToEmoji = {
"disconnected": "🔴",
"connecting": "🟠",
"connected": "🟢"
}
...

useEffect(() => {
...
centrifuge.on('state', function (ctx) {
setConnectionState(ctx.newState);
})
...

return (
...
<span className={"connectionState " + connectionState}>
{stateToEmoji[connectionState]}
</span>

You can find more details about Centrifugo client SDK API and states in client SDK spec.

If you look at source code on Github - you will also find an example of channel subscription to a user personal channel:

function App() {
...
const [publishedData, setPublishedData] = useState("");
...

useEffect(() => {
...
const userChannel = "#" + keycloak.tokenParsed?.sub;
const sub = centrifuge.newSubscription(userChannel);
sub.on("publication", function (ctx) {
setPublishedData(JSON.stringify(ctx.data));
}).subscribe();
...

You can now:

  • test the SSO setup by logging into application
  • making sure connection is successful
  • try publishing a message into a user channel via the Centrifugo Web UI. The published message will appear on application screen in real-time.

That's it! We have successfully set up Keycloak SSO authentication with Centrifugo and a React application. Again, source code is on Github.

- + \ No newline at end of file diff --git a/blog/2023/06/29/centrifugo-v5-released.html b/blog/2023/06/29/centrifugo-v5-released.html index d9e4118f4..151aa9f7b 100644 --- a/blog/2023/06/29/centrifugo-v5-released.html +++ b/blog/2023/06/29/centrifugo-v5-released.html @@ -16,13 +16,13 @@ - +

Centrifugo v5 released

· 13 min read
Centrifugal team

In Centrifugo v5 we're phasing out old client protocol support, introducing a more intuitive HTTP API, adjusting token management behaviour in SDKs, improving configuration process, and refactoring the history meta ttl option. As the result you get a cleaner, more user-friendly, and optimized Centrifugo experience. And we have important news about the project - check it out in the end of this post.

What is Centrifugo?

Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, GRPC, SockJS, WebTransport). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.

Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.

Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.

Let's proceed and take a look at most notable changes of Centrifugo v5.

Dropping old client protocol

With the introduction of Centrifugo v4, our previous major release, we rolled out a new version of the client protocol along with a set of client SDKs designed to work in conjunction with it. Nevertheless, we maintained support for the old client protocol in Centrifugo v4 to facilitate a seamless migration of applications.

In Centrifugo v5 we are discontinuing support for the old protocol. If you have been using Centrifugo v4 with the latest SDKs, this change should have no impact on you. From our perspective, removing support for the old protocol allows us to eliminate a considerable amount of non-obvious branching in the source code and cleanup Protobuf schema definitions.

Token behaviour adjustments in SDKs

In Centrifugo v5 we are adjusting client SDK specification in the aspect of connection token management. Previously, returning an empty token string from getToken callback resulted in client disconnection with unauthorized reason.

There was some problem with it though. We did not take into account the fact that empty token may be a valid scenario actually. Centrifugo supports options to avoid using token at all for anonymous access. So the lack of possibility to switch between token/no token scenarios did not allow users to easily implement login/logout workflow. The only way was re-initializing SDK.

Now returning an empty string from getToken is a valid scenario which won't result into disconnect on the client side. It's still possible to disconnect client by returning a special error from getToken function.

And we are putting back setToken method to our SDKs – so it's now possible to reset the token to be empty upon user logout.

An abstract example in Javascript which demonstrates how login/logout flow may be now implemented with our SDK:

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', {
// Provide function which returns empty string for anonymous users,
// and proper JWT for authenticated users.
getToken: getTokenImplementation
});
centrifuge.connect();

loginButton.addEventListener('click', function() {
centrifuge.disconnect();
// Do actual login here.
centrifuge.connect();
});

logoutButton.addEventListener('click', function() {
centrifuge.disconnect();
// Reset token - so that getToken will be called on next connect attempt.
centrifuge.setToken("");
// Do actual logout here.
centrifuge.connect();
});

We updated all our SDKs to inherit described behaviour - check out v5 migration guide for more details.

history_meta_ttl refactoring

One of Centrifugo's key advantages for real-time messaging tasks is its ephemeral channels and per-channel history. In version 5, we've improved one aspect of handling history by offering users the ability to tune the history meta TTL option at the channel namespace level.

The history meta TTL is the duration Centrifugo retains meta information about each channel stream, such as the latest offset and current stream epoch. This data allows users to successfully restore connections upon reconnection, particularly useful when subscribed to mostly inactive channels where publications are infrequent. Although the history meta TTL can usually be reasonably large (significantly larger than history TTL), there are certain scenarios where it's beneficial to minimize it as much as possible.

One such use case is illustrated in this example. Using Centrifugo SDK and channels with history, it's possible to reliably stream results of asynchronous tasks to clients.

As another example, consider a ChatGPT use case where clients ask questions, subscribe to a channel with the answer, and then the response is streamed towards the client token by token. This all may be done over a secure, separate channel protected with a token. With the ability to use a relatively small history meta TTL in the channel namespace, implementing such things is now simpler.

Hence, history_meta_ttl is now an option at the channel namespace level (instead of per-engine). However, setting it is optional as we have a global default value for it - see details in the doc.

Node communication protocol update

When running in cluster Centrifugo nodes can communicate between each other using broker's PUB/SUB. Nodes exchange some information - like statistics, emulation requests, etc.

In Centrifugo v5 we are simplifying and making inter-node communication protocol slightly faster by removing extra encoding layers from it's format. Something similar to what we did for our client protocol in Centrifugo v4.

This change, however, leads to an incompatibility between Centrifugo v4 and v5 nodes in terms of their communication protocols. Consequently, Centrifugo v5 cannot be part of a cluster with Centrifugo v4 nodes.

New HTTP API format

From the beginning Centrifugo HTTP API exposed one /api endpoint to make requests with all command types.

To work properly HTTP API had to add one additional layer to request JSON payload to be able to distinguish between different API methods:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey API_KEY" \
--request POST \
--data '{"method": "publish", "params": {"channel": "test", "data": {"x": 1}}}' \
http://localhost:8000/api

And it worked fine. It additionally supported request batching where users could send many commands to Centrifugo in one request using line-delimited JSON.

However, the fact that we were accommodating various commands via a single API endpoint resulted in nested serialized payloads for each command. The top-level method would determine the structure of the params. We addressed this issue in the client protocol in Centrifugo v4, and now we're addressing a similar issue in the inter-node communication protocol in Centrifugo v5.

At some point we introduced GRPC API in Centrifugo. In GRPC case we don't have a way to send batches of commands without defining a separate method to do so.

These developments highlighted the need for us to align the HTTP API format more closely with the GRPC API. Specifically, we need to separate the command method from the actual method payload, moving towards a structure like this:

curl --header "Content-Type: application/json" \
--header "X-API-Key: API_KEY" \
--request POST \
--data '{"channel": "test", "data": {"x": 1}}' \
http://localhost:8000/api/publish

Note:

  • /api/publish instead of /api in path
  • payload does not include method and params keys anymore
  • we also support X-API-Key header for setting API key to be closer to OpenAPI specification (see more about OpenAPI below)

In v5 we implemented the approach above. Many thanks to @StringNick for the help with the implementation and discussions.

Our HTTP and GRPC API are very similar now. We've also introduced a new batch method to send multiple commands in both HTTP and GRPC APIs, a feature that was previously unavailable in GRPC.

Documentation for v5 was updated to reflect this change. But it worth noting - old API format id still supported. It will be supported for some time while we are migrating our HTTP API libraries to use modern API version. Hopefully users won't be affected by this migration a lot, just switching to a new version of library at some point.

OpenAPI spec and Swagger UI

One additional benefit of moving to the new HTTP format is the possibility to define a clear OpenAPI schema for each API method Centrifugo has. It was previously quite tricky due to the fact we had one endpoint capable to work with all kinds of commands.

This change paves the way for generating HTTP clients based on our OpenAPI specification.

We now have Swagger UI built-in. To access it, launch Centrifugo with the "swagger": true option and navigate to http://localhost:8000/swagger.

The Swagger UI operates on the internal port, so if you're running Centrifugo using our Kubernetes Helm chart, it won't be exposed to the same ingress as client connection endpoints. This is similar to how our Prometheus, admin, API, and debug endpoints currently work.

OpenTelemetry for server API

Another good addition is an OpenTelemetry tracing support for HTTP and GRPC server API requests. If you are using OpenTelemetry in your services you can now now enable tracing export in Centrifugo and find Centrifugo API request exported traces in your tracing collector UI.

Description and simple example with Jaeger may be found in observability chapter. We only support OTLP HTTP export format and trace format defined in W3C spec: https://www.w3.org/TR/trace-context/.

Separate config for subscription token

With the introduction of JWKS support in Centrifugo v4 (a way to validate JWT tokens using a remote endpoint which manages keys and key rotation - see JWK spec) Centrifugo users can rely on JWKS provider (like Keycloak, AWS Cognito) for making authentication.

But at the same time developers may want to work with channels using subscription tokens managed in a custom way – without using the same JWKS configuration used for connection tokens.

Centrifugo v5 allows doing by introducing the separate_subscription_token_config option.

When separate_subscription_token_config is true Centrifugo does not look at general token options at all when verifying subscription tokens and uses config options starting from subscription_token_ prefix instead.

Here is an example how to use JWKS for connection tokens, but have HMAC-based verification for subscription tokens:

config.json
{
"token_jwks_public_endpoint": "https://example.com/openid-connect/certs",
"separate_subscription_token_config": true,
"subscription_token_hmac_secret_key": "separate_secret_which_must_be_strong"
}

Unknown config keys warnings

With every release, Centrifugo offers more and more options. One thing we've noticed is that some options from previous Centrifugo options, which were already removed, still persist in the user's configuration file.

Another issue is that a single typo in the configuration key can cost hours of debugging especially for Centrifugo new users. What is worse, the typo might result in unexpected behavior if the feature isn't properly tested before being run in production.

In Centrifugo v5, we are addressing these problems. Now, Centrifugo logs on WARN level about unknown keys found in the configuration upon server start-up. Not only in the configuration file but also verifying the validity of environment variables (looking at those starting with CENTRIFUGO_ prefix). This should help clean up the configuration to align with the latest Centrifugo release and catch typos at an early stage.

It looks like this:

08:25:33 [WRN] unknown key found in the namespace object key=watch namespace_name=xx
08:25:33 [WRN] unknown key found in the proxy object key=type proxy_name=connect
08:25:33 [WRN] unknown key found in the configuration file key=granulr_proxy_mode
08:25:33 [WRN] unknown key found in the environment key=CENTRIFUGO_ADDRES

These warnings do not prevent server to start so you can gradually clean up the configuration.

Simplifying protocol debug with Postman

Centrifugo v5 supports a special url parameter for bidirectional websocket which turns on using native WebSocket frame ping-pong mechanism instead of server-to-client application level pings Centrifugo uses by default. This simplifies debugging Centrifugo protocol with tools like Postman, wscat, websocat, etc.

Previously it was inconvenient due to the fact Centrifugo sends periodic ping message to the client ({} in JSON protocol scenario) and expects pong response back within some time period. Otherwise Centrifugo closes connection. This results in problems with mentioned tools because you had to manually send {} pong message upon ping message. So typical session in wscat could look like this:

❯ wscat --connect ws://localhost:8000/connection/websocket
Connected (press CTRL+C to quit)
> {"id": 1, "connect": {}}
< {"id":1,"connect":{"client":"9ac9de4e-5289-4ad6-9aa7-8447f007083e","version":"0.0.0","ping":25,"pong":true}}
< {}
Disconnected (code: 3012, reason: "no pong")

The parameter is called cf_ws_frame_ping_pong, to use it connect to Centrifugo bidirectional WebSocket endpoint like ws://localhost:8000/websocket/connection?cf_ws_frame_ping_pong=true. Here is an example which demonstrates working with Postman WebSocket where we connect to local Centrifugo and subscribe to two channels test1 and test2:

You can then proceed to Centrifugo admin web UI, publish something to these channels and see publications in Postman.

Note, how we sent several JSON commands in one WebSocket frame to Centrifugo from Postman in the example above - this is possible since Centrifugo protocol supports batches of commands in line-delimited format.

We consider this feature to be used only for debugging, in production prefer using our SDKs without using cf_ws_frame_ping_pong parameter – because app-level ping-pong is more efficient and our SDKs detect broken connections due to it.

The future of SockJS

As you know SockJS is deprecated in Centrifugal ecosystem since Centrifugo v4. In this release we are not removing support for it – but we may do this in the next release.

Unfortunately, SockJS client repo is poorly maintained these days. And some of its iframe-based transports are becoming archaic. If you depend on SockJS and you really need fallback for WebSocket – consider switching to Centrifugo own bidirectional emulation for the browser which works over HTTP-streaming (using modern fetch API with Readable streams) or SSE. It should be more performant and work without sticky sessions requirement (sticky sessions is an optimization in our implementation). More details may be found in Centrifugo v4 release post.

If you think SockJS is still required for your use case - reach us out so we could think about the future steps together.

Introducing Centrifugal Labs LTD

Finally, some important news we mentioned in the beginning!

Centrifugo is now backed by the company called Centrifugal Labs LTD - a Cyprus-registered technology company. This should help us to finally launch Centrifugo PRO offering – the product we have been working on for a couple of years now and which has some unique and powerful features like real-time analytics or push notification API.

As a Centrifugo user you will start noticing mentions of Centrifugal Labs LTD in our licenses, Github organization, throughout this web site. And that's mostly it - no radical changes at this point. We will still be working on improving Centrifugo trying to find a balance between OSS and PRO versions. Which is difficult TBH – but we will try.

An ideal plan for us – make Centrifugo development sustainable enough to have the possibility for features from the PRO version flow to the OSS version eventually. The reality may be harder than this of course.

Conclusion

That's all about most notable things happened in Centrifugo v5. We updated documentation to reflect the changes in v5, also some documentation chapters were rewritten. For example, take a look at the refreshed Design overview. Several more changes and details outlined in the migration guide for Centifugo v5. Please feel free to contact in the community rooms if you have questions about the release. And as usual, let the Centrifugal force be with you!

- + \ No newline at end of file diff --git a/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos.html b/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos.html index 475bb0a2d..e45a035da 100644 --- a/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos.html +++ b/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos.html @@ -16,13 +16,13 @@ - +

Asynchronous message streaming to Centrifugo with Benthos

· 8 min read
Alexander Emelin

Centrifugo provides HTTP and GRPC APIs for publishing messages into channels. Publish server API is very straighforward to use - it's a simple request with a channel and data to be delivered to active WebSocket connections subscribed to a channel.

Sometimes though Centrifugo users want to avoid synchronous calls to Centrifugo API delegating this work to asynchronous tasks. Many companies have convenient infrastructure for messaging processing tasks - like Kafka, Nats Jetstream, Redis, RabbitMQ, etc. Some using transactional outbox pattern to reliably deliver events upon database changes and have a ready infrastructure to push such events to some queue. From which want to re-publish events to Centrifugo.

In this post we get familiar with a tool called Benthos and show how it may simplify integrating your asynchronous message flow with Centrifugo. And we discuss some non-obvious pitfalls of asynchronous publishing approach in regards to real-time applications.

Start Centrifugo

First start Centrifugo (with debug logging to see incoming API requests in logs):

centrifugo genconfig
centrifugo -c config.json --log_level debug

Hope this step is already simple for you, if not - check out Quickstart tutorial.

Install and run Benthos

Benthos is an awesome tool which allows consuming data from various inputs, process data, then output it into configured outputs. See more detailed description on Benthos' website.

The number of inputs supported by Benthos is huge: check it out here. Most of the major systems widely used these days are supported. Benthos also supports many outputs – but here we only interested in message transfer to Centrifugo. There is no built-in Centrifugo output in Benthos but it provides a generic http_client output which may be used to send requests to any HTTP server. Benthos may also help with retries, provides tools for additional data processing and transformations.

Just like Centrifugo Benthos written in Go language – so its installation is very straighforward and similar to Centrifugo. See official instructions.

Let's assume you've installed Benthos and have benthos executable available in your system. Let's create Benthos configuration file:

benthos create > config.yaml

Take a look at generated config.yaml - it contains various options for Benthos instance, the most important (for the context of this post) are input and output sections.

And after that you can start Benthos instance with:

benthos -c config.yaml

Now we need to tell Benthos from where to get data and how to send it to Centrifugo.

Configure Benthos input and output

For our example here we will user Redis List as an input, won't add any additional data processing and will output messages consumed from Redis List into Centrifugo publish server HTTP API.

To achieve this add the following as input in Benthos config.yaml:

input:
label: "centrifugo_redis_consumer"
redis_list:
url: "redis://127.0.0.1:6379"
key: "centrifugo.publish"

And configure the output like this:

output:
label: "centrifugo_http_publisher"
http_client:
url: "http://localhost:8000/api/publish"
verb: POST
headers:
X-API-Key: "<CENTRIFUGO_API_KEY>"
timeout: 5s
max_in_flight: 20

The output points to Centrifugo publish HTTP API. Replace <CENTRIFUGO_API_KEY> with your Centrifugo api_key (found in Centrifugo configuration file).

Push messages to Redis queue

Start Benthos instance:

benthos -c config.yaml

You will see errors while Benthos tries to connect to input Redis source. So start Redis server:

docker run --rm -it --name redis redis:7

Now connect to Redis (using redis-cli):

docker exec -it redis redis-cli

And push command to Redis list:

127.0.0.1:6379> rpush centrifugo.publish '{"channel": "chat", "data": {"input": "test"}}'
(integer) 1

This message will be consumed from Redis list by Benthos and published to Centrifugo HTTP API. If you have active subscribers to channel chat – you will see messages delivered to them. That's it!

tip

When using Redis List input you can scale out Benthos instances to run several of them if needed.

Demo

Here is a quick demonstration of the described integration. See how we push messages into Redis List and those are delivered to WebSocket clients:

Pitfalls of async publishing

This all seems simple. But publishing messages asynchronously may highlight some pitfalls not visible or not applicable for synchronous publishing to Centrifugo API.

Late delivery

Most of the time it will work just fine. But one day you can come across intermediate queue growth and increased delivery lag. This may happen due to temporary Centrifugo or worker process unavailability. As soon as system comes back to normal queued messages will be delivered.

Depending on the real-time feature implemented this may be a concern to think about and decide whether this is desired or not. Your application should be designed accordingly to deal with late delivery.

BTW late delivery may be a case even with synchronous publishing – it just almost never strikes. But theoretically client can reload browser page and load initial app state while message flying from the backend to client over Centrifugo. It's not Centrifugo specific actually - it's just a nature of networks and involved latencies.

In general solution to prevent late delivery UX issues completely is using object versioning. Object version should be updated in the database on every change from which the real-time event is generated. Attach object version information to every real-time message. Also get object version on initial state load. This way you can compare versions and drop non-actual real-time messages on client side.

Possible strategy may be using synchronous API for real-time features where at most once delivery is acceptable and use asynchronous delivery where you need to deliver messages with at least once guarantees. In a latter case you most probably designed proper idempotency behaviour on client side anyway.

Ordering concerns

Another thing to consider is message ordering. Centrifugo itself may provide message ordering in channels. If you published one message to Centrifugo API, then another one – you can expect that messages will be delivered to a client in the same order. But as soon as you have an intermediary layer like Benthos or any other asynchronous worker process – then you must be careful about ordering. In case of Benthos and example here you can set max_in_flight parameter to 1 instead of 20 and keep only one instance of Benthos running to preserve ordering.

In case of streaming from Kafka you can rely on Kafka message partitioning feature to preserve message ordering.

Throughput when ordering preserved

If you preserved ordering in your asynchronous workers then the next thing to consider is throughput limitations.

You have a limited number of workers, these workers send requests to Centrifugo one by one. In this case throughput is limited by the number of workers and RTT (round-trip time) between worker process and Centrifugo.

If we talk about using Redis List structure as a queue - you can possibly shard data amongst different Redis List queues by some key to improve throughput. In this case you need to push messages where order should be preserved into a specific queue. In this case your get a setup similar to Kafka partitioning.

In case of using manually partitioned queues or using Kafka you can have parallelism equal to the number of partitions.

Let's say you have 20 workers which can publish messages in parallel and 5ms RTT time between worker and Centrifugo. In this case you can publish 20*(1000/5) = 4000 messages per second max.

To improve throughput futher consider increasing worker number or batching publish requests to Centrifugo (using Centrifugo's batch API).

Error handling

When publishing asynchronously you should also don't forget about error handling. Benthos will handle network errors automatically for you. But there could be internal errors from Centrifugo returned as part of response. It's not very convenient to handle with Benthos out of the box – so we think about adding transport-level error mode to Centrifugo.

Conclusion

Sometimes you want to publish to Centrifugo asynchronously using messaging systems convenient for your company. Usually you can write worker process to re-publish messages to Centrifugo. Sometimes it may be simplified using helpful tools like Benthos.

Here we've shown how Benthos may be used to transfer messages from Redis List queue to Centrifugo API. With some modifications you can achieve the same for other input sources - such as Kafka, RabbitMQ, Nats Jetstream, etc.

But publishing messages asynchronously highlights several pifalls - like late delivery, ordering issues, throughput considerations and error handling – which should be carefully addressed. Different real-time features may require different strategies.

- + \ No newline at end of file diff --git a/blog/2023/08/29/using-centrifugo-in-rabbitx.html b/blog/2023/08/29/using-centrifugo-in-rabbitx.html index 501a5006c..2ba9087f6 100644 --- a/blog/2023/08/29/using-centrifugo-in-rabbitx.html +++ b/blog/2023/08/29/using-centrifugo-in-rabbitx.html @@ -16,13 +16,13 @@ - +

Using Centrifugo in RabbitX

· 4 min read
Centrifugal + RabbitX

This post introduces a new format in Centrifugal blog – interview with a Centrifugo user! Let's dive into an exciting chat with the engineering team of RabbitX platform, a global permissionless perpetuals exchange powered on Starknet. We will discover how Centrifugo helped RabbitX to build a broker platform with current trading volume of 25 million USD daily! 🚀🎉

[Q] Hey team - thanks for your desire to share your Centrifugo use case. First of all, could you provide some information about RabbitX - what is it?

RabbitX is a global permissionless perpetuals exchange built on Starknet. RabbitX is building the most secure and liquid global derivatives network, giving you 24/7 access to global markets anywhere in the world, with 20x leverage. In its core there is an orderbook - where traders match against market makers, which require to support high throughput and low latency tech stack.

The technologies that we are using:

  • Tarantool as in-memory database and business logic server
  • Centrifugo as our major websocket server
  • Different stark tech to support decentralized settlement

[Q] Great! What is the goal of Centrifugo in your project? Which real-time features you have?

Almost all the information users see in our terminal is streamed over Centrifugo. We use it for financial order books, candlestick chart updates, and stat number updates. We can also send real-time personal user notifications via Centrifugo. Instead of all the words, here is a short recording of our terminal trading BTC:

[Q] We know that you are using Centrifugo Tarantool engine - could you explain why and how it works in your case?

Well, that's an interesting thing. We heavily use Tarantool in our system. It grants us immense flexibility, performance, and the power to craft whatever we envision. It ensures the atomicity essential for trading match-making.

When we were in search of a WebSocket real-time bus for messages, we were pleasantly surprised to discover that Centrifugo integrates with Tarantool. In our scenario, this allowed us to bypass additional network round-trips, as we can stream data directly from Tarantool to Centrifugo channels. Reducing latency is paramount for financial instruments.

Furthermore, I can mention that over our nine months in production, we didn't encounter any issues with Centrifugo – it performed flawlessly!

Regarding authentication, we employ Centrifugo's JWT authentication and subscribe proxy. Thus, subscriptions are authorized on our specialized service written in Go. We're also actively using Centrifugo possibility to send initial channel data in the subscribe proxy response.

One challenge we overcame was bridging the gap between the subscription's initial request and the continuous message stream in the order book component. To address this, we employed our own sequence numbers in events, coupled with Centrifugo's channel history – this allowed us to deal with missed events when needed. Actually the gaps in event stream are rare in practice and our workaround not needed most of the time, but now we're confident our users never experience this issue.

[Q] Looking at RabbitX terminal app we see quite modern UI - could you share more details about it too?

Our frontend is built on top of React in combination with TradingView Supercharts. And of course we are using centrifuge-js SDK for establishing connections with Centrifugo.

At this point we can have up to a thousand active concurrent traders and send more than 60 messages per second towards one client in peak hours. All the load is served with a single Centrifugo instance (and we have one standby instance).

[Q] Anything else you want to share with readers of Centrifugal blog?

When we designed the system the main goal was to have a homogeneous tech zoo, with a small amount of different technologies, to keep the number of failure points as small as possible. Tarantool is a sort of technology that really allows us to achieve this, we were able to add different decentralized mechanics to our system because of that. It’s not only an in-memory database, but in reality the app server as well.

In our case, the fact Centrifugo supports Tarantool broker was a big discovery – the integration went smoothly, and everything has been working great since then.

- + \ No newline at end of file diff --git a/blog/2023/10/29/discovering-centrifugo-pro-push-notifications.html b/blog/2023/10/29/discovering-centrifugo-pro-push-notifications.html index 3b9fcfd10..6b9310536 100644 --- a/blog/2023/10/29/discovering-centrifugo-pro-push-notifications.html +++ b/blog/2023/10/29/discovering-centrifugo-pro-push-notifications.html @@ -16,13 +16,13 @@ - +

Discovering Centrifugo PRO: push notifications API

· 14 min read
Alexander Emelin

In our v5 release post, we announced the upcoming launch of Centrifugo PRO. We are happy to say that it was released soon after that, and at this point, we already have several customers of the PRO version.

I think it's time to look at the current state of the PRO version and finally start talking more about its benefits. In this post, we will talk more about one of the coolest PRO features we have at this point: the push notifications API.

Centrifugo PRO goals

When Centrifugo was originally created, its main goal was to help introduce real-time messaging features to existing systems, written in traditional frameworks which work on top of the worker/thread model. Serving many concurrent connections is a non-trivial task in general, and without native efficient concurrency support, it becomes mostly impossible without a shift in the technology stack. Integrating with Centrifugo makes it simple to introduce an efficient real-time layer, while keeping the existing application architecture.

As time went on, Centrifugo got some unique features which now justify its usage even in conjunction with languages/frameworks with good concurrency support. Simply using Centrifugo for at-most-once PUB/SUB may already save a lot of development time. The task, which seems trivial at first glance, has a lot of challenges in practice: client SDKs with reconnect and channel multiplexing, scalability to many nodes, WebSocket fallbacks, etc.

The combination of useful possibilities has made Centrifugo an attractive component for building enterprise-level applications. Let's be honest here - for pet projects, developers often prefer writing WebSocket communications themselves, and Centrifugo may be too heavy and an extra dependency. But in a corporate environment, the decision on which technology to use should take into account a lot of factors, like those we just mentioned above. Using a mature technology is often preferred to building from scratch and making all the mistakes along the way.

With the PRO version, our goal is to provide even more value for established businesses when switching to Centrifugo. We want to solve tricky cases and simplify them for our customers; we want to step into related areas where we see we can provide sufficient value.

One rule we try to follow for PRO features that extend Centrifugo’s scope is this: we are not trying to replicate something that already exists in other systems, but rather, we strive to improve upon it. We focus on solving practical issues that we observe, providing a unique value proposition for our customers. This post describes one such example — we will demonstrate our approach to push notifications, which is one the features of Centrifugo PRO.

Why providing push notifications API

Why provide a push notifications API at all? Well, actually, real-time messages and push notifications are so close that many developers hardly see the difference before starting to work with both more closely.

I’ve heard several stories where chat functionality on mobile devices was implemented using only native push notifications — without using a separate real-time transport like WebSocket while the app is in the foreground. While this is not a recommended approach due to the delivery properties of push notifications, it proves that real-time messages and push notifications are closely related concepts and sometimes may interchange with each other.

When developers introduce WebSocket communication in an application, they often ask the question—what should I do next to deliver some important messages to a user who is currently not actively using the application? WebSockets are great when the app is in the foreground, but when the app goes to the background, the recommended approach is to close the WebSocket connection. This is important to save battery, and operating systems force the closing of connections after some time anyway.

The delivery of important app data is then possible over push notifications. See a good overview of them on web.dev.

Previously, Centrifugo positioned itself solely as a transport layer for real-time messages. In our FAQ, we emphasized this fact and suggested using separate software products to send push notifications.

Now, with Centrifugo PRO, we provide this functionality to our customers. We have extended our server API with methods to manage and send push notifications. I promised to tell you why we believe our implementation is super cool. Let’s dive into the details.

Push notifications API like no one provides

Push notifications are super handy, but there’s a bit to do to get them working right. Let's break it down!

On the user's side (frontend)

  • Request permission from the user to receive push notifications.
  • Integrate with the platform-specific notification service (e.g., Apple Push Notification Service for iOS, Firebase Cloud Messaging for Android) to obtain the device token.
  • Send the device token to the server for storage and future use.
  • Integrate with the platform-specific notification handler to listen for incoming push notifications
  • Handle incoming push notifications: display the notification content to the user, either as a banner, alert, or in-app message, depending on the user's preferences and the type of notification. Handle user actions on the notification, such as opening the app, dismissing the notification, or taking a specific action related to the notification content.

On the server (backend)

  • Store device tokens in a database when received from the client side
  • Regularly clean up the database to remove stale or invalid device tokens. and handle scenarios where a device token becomes invalid or is revoked by the user, ensuring that no further notifications are sent to that device.
  • Integrate with platform-specific notification services (e.g., APNS, FCM) to send notifications to devices. Handle errors or failures in sending notifications and implement retry mechanisms if necessary.
  • Track the delivery status of each push notification sent out. Monitor the open rates, click-through rates, and other relevant metrics for the notifications.
  • Use analytics to understand user behavior in response to notifications and refine the notification strategy based on insights gained.

We believe that we were able to achieve a unique combination of design decisions which allows us to provide push notification support like no one else provides. Let’s dive into what makes our approach special!

Frontend decisions

When providing the push notification feature, other solutions like Pusher or Ably also offer their own SDKs for managing notifications on the client side.

What we've learned, though, during the Centrifugo life cycle, is that creating and maintaining client SDKs for various environments (iOS, Android, Web, Flutter) is one of the hardest parts of the Centrifugo project.

So the decision here was simple and natural: Centrifugo PRO does not introduce any client SDKs for push notifications on the client side.

When integrating with Centrifugo, you can simply use the native SDKs provided by each platform. We bypass the complexities of SDK development and concentrate on server-side improvements. With this decision, we are not introducing any limitations to the client side.

You get:

  • Wealthy documentation and community support. Platforms like APNs provide comprehensive documentation, tutorials, and best practices, making the integration process smoother.
  • Stability and reliability: native SDKs are rigorously tested and frequently updated by the platform providers. This ensures that they are stable, reliable, and free from critical bugs.
  • Access to the latest features. As platform providers roll out new features or enhancements, native SDKs are usually the first to get updated. This ensures that your application can leverage the latest functionalities without waiting for SDKs to catch up.

This approach was not possible with our real-time SDKs, as WebSocket communication is very low-level, and Centrifugo’s main goal was to provide some high-level features on top of it. However, with push notifications, proceeding without a custom SDK seems like a choice beneficial for everyone.

Server implementation

The main work we did was on the server side. Let's go through the entire workflow of push notification delivery and describe what Centrifugo PRO provides for each step.

How we keep tokens

Let's suppose you got the permission from the user and received the device push token. At this point you must save it to database for sending notifications later using this token. Centrifugo PRO provides API called device_register to do exactly this.

At this point, we use PostgreSQL for storing tokens – which is a very popular SQL database. Probably we will add more storage backend options in the future.

When calling Centrifugo device_register API you can provide user ID, list of topics to subscribe, platform from which the user came from (ios, android, web), also push notifications provider. To deliver push notifications to devices Centrifugo PRO integrates with the following push notification providers:

Push

So we basically cover all the most popular platforms out of the box.

After registering the device token, Centrifugo PRO returns a device_id to you. This device ID must be stored on the client device. As long as the frontend has this device_id, it can update the device's push token information from time to time to keep it current (by just calling device_register again, but with device_id attached).

After saving the token, your backend can start sending push notifications to devices.

How we send notifications

To send push notifications we provide another API called send_push_notification. You need to provide some filter in the API request to tell Centrifugo who you want to send notification. You also need to provide push notification payload. For example, using Centrifugo HTTP API:

curl -X POST http://localhost:8000/api/send_push_notification \
-H "Authorization: apikey <KEY>" \
-d @- <<'EOF'

{
"recipient": {
"filter": {
"topics": ["test"]
}
},
"notification": {
"fcm": {
"message": {
"notification": {"title": "Hello", "body": "How are you?"}
}
}
}
}
EOF

Here is another important decision we made: Centrifugo PRO allows you to specify raw JSON objects for each provider we support. In other words, we do not wrap the push notifications API for FCM, APNS, HMS - we give you a way to construct the entire push notification message.

This means the Centrifugo push API supports all the fields of push notification payloads out-of-the-box, for all push providers. You can simply use the documentation of FCM, APNs, and send the constructed requests to Centrifugo. There is no need for us to update Centrifugo PRO in any way to support new fields added by providers to push APIs.

When you send a push notification with a filter and push payload for each provider you want, it's queued by Centrifugo. We use Redis Streams for queuing and optionally a queue based on PostgreSQL (less efficient, but still robust enough).

The fact that the notification is being queued means a very fast response time – so you can integrate with Centrifugo from within the hot paths of your application backend. You may additionally provide a push expiration time and a unique push identifier. If you have not provided a unique identifier, Centrifugo generates one for you and returns it in the response. The unique identifier may later be used to track push status in Centrifugo PRO's push notification analytics.

We then have efficient workers which process the queue with minimal latency and send push notifications using batch requests for each provider - i.e., we do this in the most effective way possible. We conducted a benchmark of our worker system with FCM – and we can easily send several million pushes per minute.

Another decision we made - Centrifugo PRO supports sending push notifications to a raw list of tokens. This makes it possible for our customers to use their own token storage. For example, such storage could already exist before you started using Centrifugo, or you might need a different storage/schema. In such cases, you can use Centrifugo just as an effective push sender server.

Finally, Centrifugo PRO supports sending delayed push notification - to queue push for a later delivery, so for example you can send notification based on user time zone and let Centrifugo PRO send it when needed. Or you may send slightly delayed push notification together with real-time message and if client provided an ack to real-time message - cancel push notification.

Secure unified topics

FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that device can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM and HMS topics by introducing an additional API to manage device subscriptions to topics.

Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs.

Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. See user_topic_list and user_topic_update. As soon as user registers a device – it will be automatically subscribed to its own topics pre-created over the API. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched.

This design solves one of the issues with push notifications (with FCM in particular) – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call - Centrifugo PRO API gives you freedom here.

Push analytics

Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Push providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API. This API allows updating ClickHouse table and add status for each push sent:

  • delivered
  • or interacted

It's then possible to make queries to ClickHouse and build various analytical reports. Or use ClickHouse for real-time graphs - for example, from Grafana.

Push notifications UI

Finally, Centrifugo PRO provides a simple web UI for inspecting registered devices. It can simplify development, provide a way to look at live data, and send simple push notification alerts to users or topics.

Conclusion

We really believe in our push notifications and will be working hard to make them even better. The API we already have serves well to cover common push notification delivery use cases, but we won't stop here. Some areas for improvements are: functionality of built-in push notifications web UI, extending push analytics by providing user friendly UI for the insights about push delivery and engagement. The good thing is that we already have a ground for making this.

Take a look at the documentation of Centrifugo PRO push notification API for more formal details and some things not mentioned here. Probably at the time you are reading this we already added something great to the API.

Even though Centrifugo PRO is pretty new, it already has a lot of helpful features, and we have plans to add even more. You can see what’s coming up next on our Centrifugo PRO planned features board. We're excited to share more blog posts like this one in the future.

- + \ No newline at end of file diff --git a/blog/archive.html b/blog/archive.html index c8af44d1d..c384f0e00 100644 --- a/blog/archive.html +++ b/blog/archive.html @@ -16,13 +16,13 @@ - + - + \ No newline at end of file diff --git a/blog/tags.html b/blog/tags.html index adbb722a1..c5ed63df7 100644 --- a/blog/tags.html +++ b/blog/tags.html @@ -16,13 +16,13 @@ - + - + \ No newline at end of file diff --git a/blog/tags/authentication.html b/blog/tags/authentication.html index 20fc3bbc6..4c4ba3d2d 100644 --- a/blog/tags/authentication.html +++ b/blog/tags/authentication.html @@ -16,13 +16,13 @@ - +

One post tagged with "authentication"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/benthos.html b/blog/tags/benthos.html index 292dded06..04dea4d2f 100644 --- a/blog/tags/benthos.html +++ b/blog/tags/benthos.html @@ -16,13 +16,13 @@ - +

One post tagged with "benthos"

View All Tags
August 19, 2023 by Alexander Emelin
In this post, we'll demonstrate how to asynchronously stream messages into Centrifugo channels from external data providers using Benthos tool. We also highlight some pitfalls which become more important in asynchronous publishing scenario.
- + \ No newline at end of file diff --git a/blog/tags/centrifuge.html b/blog/tags/centrifuge.html index 2ab33f130..ca9b5013d 100644 --- a/blog/tags/centrifuge.html +++ b/blog/tags/centrifuge.html @@ -16,13 +16,13 @@ - +

2 posts tagged with "centrifuge"

View All Tags
February 10, 2020 by Centrifugal team
Describing a test stand in Kubernetes where we connect one million websocket connections to a server, using Redis to scale nodes, and providing insights about hardware resources required to achieve 500k messages per second
- + \ No newline at end of file diff --git a/blog/tags/centrifugo.html b/blog/tags/centrifugo.html index 58c9c4ca9..990db8070 100644 --- a/blog/tags/centrifugo.html +++ b/blog/tags/centrifugo.html @@ -16,13 +16,13 @@ - +

12 posts tagged with "centrifugo"

View All Tags
October 29, 2023 by Alexander Emelin
We start talking more about recently launched Centrifugo PRO. In this post, we share details about Centrifugo PRO push notification API implementation - how it works and what makes it special and practical.
August 29, 2023 by Centrifugal + RabbitX
In this post, the engineering team of RabbitX platform shares details about the usage of Centrifugo in their product.
August 19, 2023 by Alexander Emelin
In this post, we'll demonstrate how to asynchronously stream messages into Centrifugo channels from external data providers using Benthos tool. We also highlight some pitfalls which become more important in asynchronous publishing scenario.
June 29, 2023 by Centrifugal team
We are excited to announce a new version of Centrifugo. It's an evolutionary step which makes Centrifugo cleaner and more intuitive to use.
July 19, 2022 by Centrifugal team
Centrifugo v4 provides an optimized client protocol, modern WebSocket emulation, improved channel security, redesigned client SDK behavior, experimental HTTP/3 and WebTransport support.
October 18, 2021 by Alexander Emelin
In this tutorial we are integrating Centrifugo with NodeJS. We are using Centrifugo connect proxy feature to authenticate connections over standard Express.js session middleware.
August 31, 2021 by Centrifugal team
Centrifugo v3 released with lots of exciting improvements
- + \ No newline at end of file diff --git a/blog/tags/django.html b/blog/tags/django.html index dc66f3784..fe9645d79 100644 --- a/blog/tags/django.html +++ b/blog/tags/django.html @@ -16,13 +16,13 @@ - +

One post tagged with "django"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/go.html b/blog/tags/go.html index c2a27c4f0..b81afaaad 100644 --- a/blog/tags/go.html +++ b/blog/tags/go.html @@ -16,13 +16,13 @@ - +

5 posts tagged with "go"

View All Tags
November 12, 2020 by Alexander Emelin
The post describes techniques to write scalable WebSocket servers within Go ecosystem and beyond it
February 10, 2020 by Centrifugal team
Describing a test stand in Kubernetes where we connect one million websocket connections to a server, using Redis to scale nodes, and providing insights about hardware resources required to achieve 500k messages per second
- + \ No newline at end of file diff --git a/blog/tags/interview.html b/blog/tags/interview.html index 08cf48cfa..903e9f157 100644 --- a/blog/tags/interview.html +++ b/blog/tags/interview.html @@ -16,13 +16,13 @@ - +

One post tagged with "interview"

View All Tags
August 29, 2023 by Centrifugal + RabbitX
In this post, the engineering team of RabbitX platform shares details about the usage of Centrifugo in their product.
- + \ No newline at end of file diff --git a/blog/tags/keycloak.html b/blog/tags/keycloak.html index 274948ee4..77bdc64c2 100644 --- a/blog/tags/keycloak.html +++ b/blog/tags/keycloak.html @@ -16,13 +16,13 @@ - +

One post tagged with "keycloak"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/laravel.html b/blog/tags/laravel.html index 555d47d30..bf944ec47 100644 --- a/blog/tags/laravel.html +++ b/blog/tags/laravel.html @@ -16,13 +16,13 @@ - +

One post tagged with "laravel"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/php.html b/blog/tags/php.html index 3a19a76e1..92ce365ae 100644 --- a/blog/tags/php.html +++ b/blog/tags/php.html @@ -16,13 +16,13 @@ - +

One post tagged with "php"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/pro.html b/blog/tags/pro.html index 0f98ee09f..ce701c376 100644 --- a/blog/tags/pro.html +++ b/blog/tags/pro.html @@ -16,13 +16,13 @@ - +

One post tagged with "pro"

View All Tags
October 29, 2023 by Alexander Emelin
We start talking more about recently launched Centrifugo PRO. In this post, we share details about Centrifugo PRO push notification API implementation - how it works and what makes it special and practical.
- + \ No newline at end of file diff --git a/blog/tags/proxy.html b/blog/tags/proxy.html index f11b5a7dd..a8ba52493 100644 --- a/blog/tags/proxy.html +++ b/blog/tags/proxy.html @@ -16,13 +16,13 @@ - +

One post tagged with "proxy"

View All Tags
October 18, 2021 by Alexander Emelin
In this tutorial we are integrating Centrifugo with NodeJS. We are using Centrifugo connect proxy feature to authenticate connections over standard Express.js session middleware.
- + \ No newline at end of file diff --git a/blog/tags/push-notifications.html b/blog/tags/push-notifications.html index b262e9a8e..dea47428f 100644 --- a/blog/tags/push-notifications.html +++ b/blog/tags/push-notifications.html @@ -16,13 +16,13 @@ - +

One post tagged with "push notifications"

View All Tags
October 29, 2023 by Alexander Emelin
We start talking more about recently launched Centrifugo PRO. In this post, we share details about Centrifugo PRO push notification API implementation - how it works and what makes it special and practical.
- + \ No newline at end of file diff --git a/blog/tags/quic.html b/blog/tags/quic.html index 1e69c0cb0..d1573cdec 100644 --- a/blog/tags/quic.html +++ b/blog/tags/quic.html @@ -16,13 +16,13 @@ - +

One post tagged with "quic"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/redis.html b/blog/tags/redis.html index 0078a52f4..e90460403 100644 --- a/blog/tags/redis.html +++ b/blog/tags/redis.html @@ -16,13 +16,13 @@ - +

One post tagged with "redis"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/release.html b/blog/tags/release.html index f965aa7cb..07f595891 100644 --- a/blog/tags/release.html +++ b/blog/tags/release.html @@ -16,13 +16,13 @@ - +

3 posts tagged with "release"

View All Tags
June 29, 2023 by Centrifugal team
We are excited to announce a new version of Centrifugo. It's an evolutionary step which makes Centrifugo cleaner and more intuitive to use.
July 19, 2022 by Centrifugal team
Centrifugo v4 provides an optimized client protocol, modern WebSocket emulation, improved channel security, redesigned client SDK behavior, experimental HTTP/3 and WebTransport support.
August 31, 2021 by Centrifugal team
Centrifugo v3 released with lots of exciting improvements
- + \ No newline at end of file diff --git a/blog/tags/sso.html b/blog/tags/sso.html index 859a30313..f550800f8 100644 --- a/blog/tags/sso.html +++ b/blog/tags/sso.html @@ -16,13 +16,13 @@ - +

One post tagged with "sso"

View All Tags
- + \ No newline at end of file diff --git a/blog/tags/tutorial.html b/blog/tags/tutorial.html index 594652af8..9cf9dfc31 100644 --- a/blog/tags/tutorial.html +++ b/blog/tags/tutorial.html @@ -16,13 +16,13 @@ - +

5 posts tagged with "tutorial"

View All Tags
August 19, 2023 by Alexander Emelin
In this post, we'll demonstrate how to asynchronously stream messages into Centrifugo channels from external data providers using Benthos tool. We also highlight some pitfalls which become more important in asynchronous publishing scenario.
October 18, 2021 by Alexander Emelin
In this tutorial we are integrating Centrifugo with NodeJS. We are using Centrifugo connect proxy feature to authenticate connections over standard Express.js session middleware.
- + \ No newline at end of file diff --git a/blog/tags/usecase.html b/blog/tags/usecase.html index 6be2b3393..bc37e3da9 100644 --- a/blog/tags/usecase.html +++ b/blog/tags/usecase.html @@ -16,13 +16,13 @@ - +

One post tagged with "usecase"

View All Tags
August 29, 2023 by Centrifugal + RabbitX
In this post, the engineering team of RabbitX platform shares details about the usage of Centrifugo in their product.
- + \ No newline at end of file diff --git a/blog/tags/websocket.html b/blog/tags/websocket.html index 3966814d5..827d54b90 100644 --- a/blog/tags/websocket.html +++ b/blog/tags/websocket.html @@ -16,13 +16,13 @@ - +

One post tagged with "websocket"

View All Tags
November 12, 2020 by Alexander Emelin
The post describes techniques to write scalable WebSocket servers within Go ecosystem and beyond it
- + \ No newline at end of file diff --git a/blog/tags/webtransport.html b/blog/tags/webtransport.html index 86596a2ad..098c5ecc4 100644 --- a/blog/tags/webtransport.html +++ b/blog/tags/webtransport.html @@ -16,13 +16,13 @@ - +

One post tagged with "webtransport"

View All Tags
- + \ No newline at end of file diff --git a/components/Highlight.html b/components/Highlight.html index c011a7b84..0a88d4b26 100644 --- a/components/Highlight.html +++ b/components/Highlight.html @@ -16,13 +16,13 @@ - +

- + \ No newline at end of file diff --git a/components/logo.html b/components/logo.html index 0cfd6b2df..cded6d0a3 100644 --- a/components/logo.html +++ b/components/logo.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/components/logos/Badoo.html b/components/logos/Badoo.html index 8ba757330..3317ebe31 100644 --- a/components/logos/Badoo.html +++ b/components/logos/Badoo.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/components/logos/Grafana.html b/components/logos/Grafana.html index ace4325e7..16e232303 100644 --- a/components/logos/Grafana.html +++ b/components/logos/Grafana.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/components/logos/ManyChat.html b/components/logos/ManyChat.html index 251b93da5..35441efb8 100644 --- a/components/logos/ManyChat.html +++ b/components/logos/ManyChat.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/components/logos/OpenWeb.html b/components/logos/OpenWeb.html index 6b1d3a782..02c3138fb 100644 --- a/components/logos/OpenWeb.html +++ b/components/logos/OpenWeb.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/docs/3/attributions.html b/docs/3/attributions.html index db9b098bd..198a248cb 100644 --- a/docs/3/attributions.html +++ b/docs/3/attributions.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/docs/3/ecosystem/centrifuge.html b/docs/3/ecosystem/centrifuge.html index 1e1d3eeb6..f9fe967e0 100644 --- a/docs/3/ecosystem/centrifuge.html +++ b/docs/3/ecosystem/centrifuge.html @@ -16,13 +16,13 @@ - +

Centrifuge library

Centrifugo is a server built on top of Centrifuge library for Go language.

Due to its standalone language-agnostic nature Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a more tight integration of real-time server with application business logic you may consider using Centrifuge library to build something similar to Centrifugo but with customized behavior.

Library README has detailed description, link to examples and introduction post.

Many Centrifugo features should be re-implemented when using Centrifuge - like API layer, admin web UI, proxy etc (if you need those of course). And you need to write backend in Go language. But core functionality like a client-server protocol (all Centrifugo client connectors work with Centrifuge library based server) and Redis engine to scale come out of the box.

tip

Many things said in Centrifugo doc can be considered as extra documentation for Centrifuge library (for example part about infrastructure tuning or transport description). But not all of them.

- + \ No newline at end of file diff --git a/docs/3/ecosystem/integrations.html b/docs/3/ecosystem/integrations.html index 0393c1c1a..f0da7c111 100644 --- a/docs/3/ecosystem/integrations.html +++ b/docs/3/ecosystem/integrations.html @@ -16,13 +16,13 @@ - +

Framework integrations

tip

In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. But there are some community-driven projects that provide integration for more native experience or even some additional functionality on top of Centrifugo.

- + \ No newline at end of file diff --git a/docs/3/faq.html b/docs/3/faq.html index f57c2800d..97ba31fea 100644 --- a/docs/3/faq.html +++ b/docs/3/faq.html @@ -16,13 +16,13 @@ - +

Frequently Asked Questions

Answers to popular questions here.

How many connections can one Centrifugo instance handle?

This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here.

Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case.

You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.

Memory usage per connection?

Depending on transport used and features enabled the amount of RAM required per each connection can vary.

For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections.

For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.

Can Centrifugo scale horizontally?

Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker.

See engines and scalability considerations.

Message delivery model

See design overview

Message order guarantees

See design overview.

Should I create channels explicitly?

No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel.

When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).

What about best practices with the number of channels?

Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application.

But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc.

One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.

Any way to exclude message publisher from receiving a message from a channel?

Currently, no.

We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this:

  • Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID)
  • Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history

Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates.

Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point.

Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel.

In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.

Can I have both binary and JSON clients in one channel?

No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere.

Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end).

To summarize:

  • if you are using binary Protobuf clients and binary payloads everywhere – you are fine.
  • if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine.
  • if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.

Online presence for chat apps - online status of your contacts

While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel.

You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.

Centrifugo stops accepting new connections, why?

The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.

Can I use Centrifugo without reverse-proxy like Nginx before it?

Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.

Does Centrifugo work with HTTP/2?

Yes, Centrifugo works with HTTP/2.

You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable:

GODEBUG="http2server=0" centrifugo -c config.json

Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.

Is there a way to use a single connection to Centrifugo from different browser tabs?

If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.

What if I need to send push notifications to mobile or web applications?

Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via GCM, or browsers over Web Push API. This is a goal for another software.

But the reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.

How can I know a message is delivered to a client?

You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.

Can I publish new messages over a WebSocket connection from a client?

It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend.

We suggest using one of the available approaches:

  • When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API.
  • Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network.
  • Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself).

Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.

How to create a secure channel for two users only (private chat case)?

There are several ways to achieve it:

  • use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter
  • next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers
  • you can use subscribe proxy feature to validate subscriptions, see chapter about proxy
  • finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations

What's the best way to organize channel configuration?

In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled.

For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients.

The same relates to other channel options.

Does Centrifugo support webhooks?

Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there.

Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.

Why Centrifugo does not have disconnect hooks?

Centrifugo does not support disconnect hooks at this point.

First of all, there is no guarantee that the disconnect process will have a time to execute on the client-side (as the client can just switch off its device or simply lose internet connection). This means that a server may notice a connection loss with some delay (thanks to PING/PONG).

Also, Centrifugo node can be unexpectedly killed. So there is a chance that disconnect event won't have a chance to be emitted to the backend.

One more reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. To reduce the load during connect process Centrifugo has JWT authentication. Even if disconnect events were queued/rate-limited there could be situations when your app processes disconnect hook while user already reconnected and connect event processed. This is a racy situation which you will need to handle somehow (possibly based on unique client ID attached to each connection).

If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend from the client-side and update user status somewhere on the backend (use Redis maybe). This is a pretty robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo refresh proxy for the task of periodic backend pinging. In this case you will notice that user (or particular client) left app with some delay – this may be a acceptable trade-off in many cases.

Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together.

Is it possible to listen to join/leave events on the app backend side?

No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.

How scalable is the online presence and join/leave features?

Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel).

There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers.

You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs.

The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.

I have not found an answer to my question here:

Ask in our community rooms:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

- + \ No newline at end of file diff --git a/docs/3/flow_diagrams.html b/docs/3/flow_diagrams.html index d50dbf261..ed06d565a 100644 --- a/docs/3/flow_diagrams.html +++ b/docs/3/flow_diagrams.html @@ -16,13 +16,13 @@ - +

flow_diagrams

For swimlines.io:

Client <- App Backend: JWT

note:
The backend generates JWT for a user and passes it to the client side.

Client -> Centrifugo: Client connects to Centrifugo with JWT

...: {fas-spinner} Persistent connection established

Client -> Centrifugo: Client issues channel subscribe requests

Centrifugo -->> Client: Client receives real-time updates from channels
Client -> Centrifugo: Connect request

note:
Client connects to Centrifugo without JWT.

Centrifugo -> App backend: Sends request further (via HTTP or GRPC)

note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.

App backend -> Centrifugo: Connect reply

Centrifugo -> Client: Connect Reply

...: {fas-spinner} Persistent connection established
Client -> App Backend: Publish request

note:
Client sends data to publish to the application backend.

Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.

App Backend -> Centrifugo: Publish over Centrifugo API

Centrifugo -->> Client: {far-bolt fa-lg} Real-time notification

note: Centrifugo delivers real-time message to active channel subscribers.
Client -> App Backend: Publish request

note:
Client sends data to publish to the application backend.

Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.

App Backend -> Centrifugo: Publish over Centrifugo API

Centrifugo -->> Client: {far-bolt fa-lg} Real-time notification

note: Centrifugo delivers real-time message to active channel subscribers.
- + \ No newline at end of file diff --git a/docs/3/getting-started/client_api.html b/docs/3/getting-started/client_api.html index 2e960881d..c1f9f629d 100644 --- a/docs/3/getting-started/client_api.html +++ b/docs/3/getting-started/client_api.html @@ -16,13 +16,13 @@ - +

Client API showcase

This chapter showcases Centrifugo bidirectional client API capabilities – i.e. real-time messaging primitives available on a front-end (can be a browser or a mobile device).

tip

It's also possible to avoid using the client library at all with unidirectional transports.

This is a formal description – we use Javascript client centrifuge-js for examples here. Refer to each specific client implementation for concrete method names and possibilities. See full list of Centrifugo client SDK. This description does not cover all protocol possibilities – just the most important to start with.

If you are looking for detailed information about client-server protocol internals then client protocol description chapter can help.

Connecting to a server

Each Centrifugo client allows connecting to a server.

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.connect();

In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to:

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.setToken('<USER-JWT>')
centrifuge.connect();

See authentication chapter for more information on how to generate connection JWT.

If you are using connect proxy then you may go without setting JWT.

Disconnecting from a server

After connecting you can disconnect from a server at any moment.

centrifuge.disconnect();

Reconnecting to a server

Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.

Connection lifecycle events

All client implementations allow setting handlers on connect and disconnect events.

For example:

centrifuge.on('connect', function(connectCtx){
console.log('connected', connectCtx)
});

centrifuge.on('disconnect', function(disconnectCtx){
console.log('disconnected', disconnectCtx)
});

Subscribe to a channel

Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel.

centrifuge.subscribe('channel', function(messageCtx) {
console.log(messageCtx);
})

Client can subscribe to different channels. Subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events.

In idiomatic case messages published to channels from application backend over Centrifugo server API. Though it's not always true.

Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel properly configured. Client keeps last seen Publication offset and restores missed publications since known offset upon reconnecting. If recovery failed then client implementation provides a flag inside subscribe event to let the application know that some publications were missed – so you may need to load state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery in a dedicated chapter.

Server-side subscriptions

To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance:

centrifuge.on('publish', function(messageCtx) {
console.log(messageCtx);
});

It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.

Send RPC

A client can send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the WebSocket or SockJS connection. RPC is only available when RPC proxy configured.

const rpcRequest = {'key': 'value'};
const data = await centrifuge.namedRPC('example_method', rpcRequest);

Call channel history

Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel:

Get stream current top position:

const resp = await subscription.history();
console.log(resp.offset);
console.log(resp.epoch);

Get up to 10 publications from history since known stream position:

const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});
console.log(resp.publications);

Get up to 10 publications from history since current stream beginning:

const resp = await subscription.history({limit: 10});
console.log(resp.publications);

Get up to 10 publications from history since current stream end in reversed order (last to first):

const resp = await subscription.history({limit: 10, reverse: true});
console.log(resp.publications);

Presence and presence stats

Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured):

For presence (full information about active subscribers in channel):

const resp = await subscription.presence();
// resp contains presence information - a map client IDs as keys
// and client information as values.

For presence stats (just a number of clients and unique users in a channel):

const resp = await subscription.presenceStats();
// resp contains a number of clients and a number of unique users.
- + \ No newline at end of file diff --git a/docs/3/getting-started/design.html b/docs/3/getting-started/design.html index 124f8c5e8..7737b9127 100644 --- a/docs/3/getting-started/design.html +++ b/docs/3/getting-started/design.html @@ -16,13 +16,13 @@ - +

Design overview

Let's discuss some architectural and design topics about Centrifugo.

Idiomatic usage

Originally Centrifugo was built with the unidirectional flow as the main approach. Though Centrifugo itself used a bidirectional protocol between a client and a server to allow client dynamically create subscriptions, Centrifugo did not allow using it for sending data from client to server.

With this approach publications travel only from server to a client. All requests that generate new data first go to the application backend (for example over AJAX call of backend API). The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event from a backend side to Centrifugo API.

This is a pretty natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is decoupled from the application in this case.

diagram_unidirectional_publish

During Centrifugo v2 life cycle this paradigm evolved a bit. It's now possible to send RPC requests from client to Centrifugo and the request will be then proxied to the application backend. Also, connection attempts and publications to channels can now be proxied. So bidirectional connection between client and Centrifugo is now available for utilizing by developers in both directions. For example, here is how publish diagram could look like when using publish request proxy feature:

So at the moment, the number of possible integration ways increased.

Message history considerations

Idiomatic Centrifugo usage requires having the main application database from which initial and actual state can be loaded at any point in time.

While Centrifugo has channel history, it has been mostly designed to reduce the load on the main application database when all users reconnect at once (in case of load balancer configuration reload, Centrifugo restart, temporary network problems, etc). This allows to radically reduce the load on the application main database during reconnect storm. Since such disconnects are usually pretty short in time having a reasonably small number of messages cached in history is sufficient.

The addition of history iteration API shifts possible use cases a bit. Calling history chunk by chunk allows keeping larger number of publications per channel. But depending on Engine used and configuration of the underlying storage history stream persistence characteristics can vary. For example, with Memory Engine history will be lost upon Centrifugo restart. With Redis or Tarantool engines history will survive Centrifugo restarts but depending on a storage configuration it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis RDB and AOF, configure replication for storage high-availability, use Redis Cluster or maybe synchronous replication with Tarantool.

Centrifugo provides ways to distinguish whether the missed messages can't be restored from Centrifugo history upon recovery so a client should restore state from the main application database. So Centrifugo message history can be used as a complementary way to restore messages and thus reduce a load on the main application database most of the time.

Message delivery model

By default, the message delivery model of Centrifugo is at most once. With history and the position/recovery features enabled it's possible to achieve at least once guarantee within history retention time and size. After abnormal disconnect clients have an option to recover missed messages from the publication stream cache that Centrifugo maintains.

Without the positioning or recovery features enabled a message sent to Centrifugo can be theoretically lost while moving towards clients. Centrifugo tries to do its best to prevent message loss on a way to online clients, but the application should tolerate a loss.

As noted Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. Also, it compensates at most once delivery of broker (Redis, Tarantool) PUB/SUB by using additional publication offset checks and periodic offset synchronization.

At this moment Centrifugo message recovery is designed for a short-term disconnect period (think no more than one hour for a typical chat application, but this can vary). After this period (which can be configured per channel basis) Centrifugo removes messages from the channel history cache. In this case, Centrifugo may tell the client that some messages can not be recovered, so your application state should be loaded from the main database.

Message order guarantees

Message order in channels is guaranteed to be the same while you publish messages into channel one after another or publish them in one request. If you do parallel publications into the same channel then Centrifugo can't guarantee message order since those may be processed concurrently by Centrifugo.

Graceful degradation

It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely only on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in AJAX call response and render it. This way user that posts a comment will think that everything works just fine. Be careful to not draw comments twice in this case - think about idempotent identifiers for your entities.

Online presence considerations

Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to the network failures and unexpected shut down of Centrifugo node, there are chances that clients can be presented in a presence up to one minute more (until presence entry expiration).

Also, channel presence does not scale well for channels with lots of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows the response size becomes larger. In some cases, presence_stats API call can be sufficient to avoid receiving the entire presence state.

Scalability considerations

Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with Nats broker. See engines.

All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications.

But, if you approach broker resource limits (CPU or memory) then it's possible:

  • Use Centrifugo consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool)
  • Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters)
  • Nats broker should scale well itself in cluster setup

All brokers can be set up in highly available way so there won't be a single point of failure.

All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact and the fact that Centrifugo provides hooks for the application to understand history loss makes the process of resharding mostly automatic. As soon as you need to add additional broker shard (when using client-side sharding) you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently part of the data will stay on the same broker nodes. Applications should handle cases that channel data moved to another shard and restore a state from the main application database when needed.

- + \ No newline at end of file diff --git a/docs/3/getting-started/highlights.html b/docs/3/getting-started/highlights.html index 8d50a05b2..5dd4f22dc 100644 --- a/docs/3/getting-started/highlights.html +++ b/docs/3/getting-started/highlights.html @@ -16,13 +16,13 @@ - +

Main highlights

At this point you know how to build the simplest real-time app with Centrifugo. Beyond the core PUB/SUB functionality Centrifugo provides more features and primitives to build scalable real-time applications. Let's summarize main Centrifugo ✨highlights✨ here. Every point is then extended throughout the documentation.

Simple integration

Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.). It works as a standalone service with well-defined communication contracts. It fits very well in both monolithic and microservice architecture. Application developers should not change backend philosophy at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.

Great performance

Centrifugo is pretty fast. It's written in Go language, uses fast and battle-tested open-source libraries internally, has some internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTT with broker, connection hub sharding to avoid contention, JSON and Protobuf encoding speedups over code generation and other.

See a Million WebSocket with Centrifugo post in our blog to see some real-world numbers.

Built-in scalability

Centrifugo scales to many machines with a help of PUB/SUB brokers. The main PUB/SUB engine Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck also. There are other options to scale: KeyDB, Nats, Tarantool.

Strict client protocol

Centrifugo supports JSON and binary Protobuf protocol for client-server communication. The bidirectional protocol is defined by strict schema and several ready-to-use connectors wrap this protocol, handle asynchronous message passing, timeouts, reconnect, and various Centrifugo client API features.

Variety of real-time transports

The main transport in Centrifugo is WebSocket. It's a bidirectional transport on top of TCP with low overhead. For browsers that do not support WebSocket Centrifugo provides SockJS support.

Centrifugo v3 also introduced support for unidirectional transports for real-time updates: like SSE (EventSource), HTTP streaming, GRPC unidirectional stream. Using unidirectional transport is sufficient for many real-time applications and does not require using custom client connectors – just native APIs or GRPC-generated code.

Flexible authentication

Centrifugo can authenticate connections using JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon connection attempt. It's possible to proxy original request headers or request metadata (in the case of GRPC connection). It supports the JWK specification.

Connection management

Connections can expire, developers can choose a way to handle connection refresh – using client-side refresh workflow, or server-side call from Centrifugo to the application backend.

Channel (room) concept

Centrifugo is a PUB/SUB server – users subscribe to channels to receive real-time updates. Message sent to a channel will be delivered to all active subscribers.

There are several different types of channels to deal with permissions.

Different types of subscriptions

Centrifugo is unique in terms of the fact that it supports both client-side and server-side channel subscriptions.

RPC over bidirectional connection

You can fully utilize bidirectional persistent connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing external traffic and, in most cases, provides better latency characteristics.

Online presence information

It's possible to turn on an online presence feature for channels so you will have information about active channel subscribers. Channel join and leave events (when a user subscribes/unsubscribes) can also be sent.

Message history in channels

Optionally Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With a channel history, Centrifugo can help to survive the mass reconnect scenario. Clients can automatically recover missed messages from a cache – thus reducing the load on your primary database. It's also possible to manually iterate over a stream from a client or a server-side.

Embedded admin web UI

Built-in administrative web UI allows publishing messages to channels, looking at Centrifugo cluster state, monitoring stats, etc.

Cross-platform

Centrifugo works on Linux, macOS, and Windows.

Ready to deploy

Centrifugo supports various deploy ways: in Docker, using prepared RPM or DEB packages, via Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, has an official Grafana dashboard for Prometheus data source.

Open-source

Centrifugo stands on top of open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All client connectors are also MIT-licensed.

Pro features

Centrifugo PRO extends Centrifugo with several unique features which can give interesting advantages for business adopters.

With Centrifugo PRO it's possible to trace specific user or specific channel events in real-time. Centrifugo PRO integrates with ClickHouse for real-time connection analytics. This all may help with understanding client behavior, inspect and analyze an application on a very granular level.

Centrifugo PRO offers even more extensions that tend to be useful in practice. This includes user active status and throttling features. Active status is useful to build messenger-like applications where you want to show online indicators of users based on last activity time, throttling can help you limit the number of operations each user may execute on a Centrifugo cluster.

For additional details, refer to the Centrifugo PRO documentation.

- + \ No newline at end of file diff --git a/docs/3/getting-started/installation.html b/docs/3/getting-started/installation.html index 6fcd8d269..592f2369e 100644 --- a/docs/3/getting-started/installation.html +++ b/docs/3/getting-started/installation.html @@ -16,13 +16,13 @@ - +

Install Centrifugo

Centrifugo server is written in Go language. It's an open-source software, the source code is available on Github.

Install from the binary release

For a local development the simplest way to get Centrifugo is from binary release (i.e. single all-contained executable file).

Binary releases available on Github. Download latest release for your operating system, unpack it and you are done. Centrifugo is pre-built for:

  • Linux 64-bit (linux_amd64)
  • Linux 32-bit (linux_386)
  • Linux ARM 64-bit (linux_arm64)
  • MacOS (darwin_amd64)
  • MacOS on Apple Silicon (darwin_arm64)
  • Windows (windows_amd64)
  • FreeBSD (freebsd_amd64)
  • ARM v6 (linux_armv6)

Archives contain a single statically compiled binary centrifugo file that is ready to run:

./centrifugo -h

See the version of Centrifugo:

./centrifugo version

Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo then there is genconfig command which generates a minimal configuration file to get started:

./centrifugo genconfig

It creates a configuration file config.json with some auto-generated option values in a current directory (by default).

tip

It's possible to generate file in YAML or TOML format, i.e. ./centrifugo genconfig -c config.toml

Having a configuration file you can finally run Centrifugo instance:

./centrifugo --config=config.json

We will talk about a configuration in detail in the next sections.

You can also put or symlink centrifugo into your bin OS directory and run it from anywhere:

centrifugo --config=config.json

Docker image

Centrifugo server has a docker image available on Docker Hub.

docker pull centrifugo/centrifugo

Run:

docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json

Note that docker allows setting nofile limits in command-line arguments which is pretty important to handle lots of simultaneous persistent connections and not run out of open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter.

caution

Pin to the exact Docker Image tag in production, for example: centrifugo/centrifugo:v3.0.0, this will help to avoid unexpected problems during re-deploy process.

Docker-compose example

Create configuration file config.json:

{
"token_hmac_secret_key": "my_secret",
"api_key": "my_api_key",
"admin_password": "password",
"admin_secret": "secret",
"admin": true
}

Create docker-compose.yml:

centrifugo:
container_name: centrifugo
image: centrifugo/centrifugo:v3
volumes:
- ./config.json:/centrifugo/config.json
command: centrifugo -c config.json
ports:
- 8000:8000
ulimits:
nofile:
soft: 65535
hard: 65535

Run with:

docker-compose up

Kubernetes Helm chart

See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.

RPM and DEB packages for Linux

Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io.

At moment, we support versions of the following distributions:

  • 64-bit Debian 9 Stretch
  • 64-bit Debian 10 Buster
  • 64-bit Debian 11 Bullseye
  • 64-bit Ubuntu 16.04 Xenial
  • 64-bit Ubuntu 18.04 Bionic
  • 64-bit Ubuntu 20.04 Focal Fossa
  • 64-bit Centos 7
  • 64-bit Centos 8

See full list of available packages and installation instructions.

Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.

With brew on macOS

If you are developing on macOS then you can install Centrifugo over brew:

brew tap centrifugal/centrifugo
brew install centrifugo

Build from source

You need Go language installed:

git clone https://github.com/centrifugal/centrifugo.git
cd centrifugo
go build
./centrifugo
- + \ No newline at end of file diff --git a/docs/3/getting-started/integration.html b/docs/3/getting-started/integration.html index d4ac89fdb..e94d1e500 100644 --- a/docs/3/getting-started/integration.html +++ b/docs/3/getting-started/integration.html @@ -16,13 +16,13 @@ - +

Integration guide

This chapter aims to help you get started with Centrifugo. We will look at a step-by-step workflow of integrating your application with Centrifugo providing links to relevant parts of this documentation.

As Centrifugo is language-agnostic and can be used together with any language/framework we won't be specific here about any backend or frontend technology your application can be built with. Only abstract steps which you can extrapolate to your application stack.

Let's look again at a simplified scheme:

Centrifugo scheme

There are three parts involved in the idiomatic Centrifugo usage scenario: your clients (frontend application), your application backend, and Centrifugo. It's possible to use Centrifugo without any application backend involved but here we won't consider this use case.

Here let's suppose you already have 2 of 3 elements: clients and backend. Now you want to add Centrifugo to receive real-time events on the client-side.

0. Install

First, you need to do is download/install Centrifugo server. See install chapter for details.

1. Configure Centrifugo

Create basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and chapter about server API for API description. The simplest way to do this automatically is by using genconfig command:

./centrifugo genconfig

– which will generate config.json file for you with all required fields.

Properly configure allowed_origins option.

2. Configure your backend

In the configuration file of your application backend register several variables: Centrifugo secret and Centrifugo API key you set on a previous step and Centrifugo API address. By default, the API address is http://localhost:8000/api. You must never reveal token secret and API key to your users.

3. Connect to Centrifugo

Now your users can start connecting to Centrifugo. You should get a client library (see list of available client SDK) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here. Every client should provide a connection token (JWT) on connect. You must generate this token on your backend side using Centrifugo secret key you set to backend configuration (note that in the case of RSA tokens you are generating JWT with a private key). See how to generate this JWT in special chapter. You pass this token from the backend to your frontend app (pass it in template context or use separate request from client-side to get user-specific JWT from backend side). And use this token when connecting to Centrifugo (for example browser client has a special method setToken).

There is also a way to authenticate connections without using JWT - see chapter about proxying to backend.

You are connecting to Centrifugo using one of the available transports. At this moment you can choose from:

  • WebSocket, with JSON or binary protobuf protocol. See more info in a chapter about WebSocket transport
  • SockJS (only supports JSON protocol). See more info about SockJS transport

4. Subscribe to channels

After connecting to Centrifugo subscribe clients to channels they are interested in. See more about channels in special chapter. All client libraries provide a way to handle messages coming to a client from a channel after subscribing to it.

There is also a way to subscribe connection to a list of channels on the server side at the moment of connection establishment. See chapter about server-side subscriptions.

5. Publish to channel

So everything should work now – as soon as a user opens some page of your application it must successfully connect to Centrifugo and subscribe to a channel (or channels). Now let's imagine you want to send a real-time message to users subscribed on a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, the administrator just created a new post, the user pressed the like button, etc. Anyway, this is an event your backend just got, and you want to immediately share it with interested users. You can do this using Centrifugo HTTP API. To simplify your life we have several API libraries for different languages. You can publish messages into a channel using one of those libraries or you can simply follow API description to construct API requests yourself - this is very simple. Also Centrifugo supports GRPC API. As soon as you published a message to the channel it must be delivered to your client.

6. Deploy to production

To put this all into production you need to deploy Centrifugo on your production server. To help you with this we have many things like Docker image, rpm and deb packages, Nginx configuration. See Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.

7. Monitor Centrifugo

Don't forget to monitor your production Centrifugo setup.

8. Scale Centrifugo

As soon as you are close to machine resource limits you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using Redis engine.

9. Read FAQ

That's all for basics. The documentation actually covers lots of other concepts Centrifugo server has: scalability, private channels, admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ.

- + \ No newline at end of file diff --git a/docs/3/getting-started/introduction.html b/docs/3/getting-started/introduction.html index d28380b7e..ebbd96b97 100644 --- a/docs/3/getting-started/introduction.html +++ b/docs/3/getting-started/introduction.html @@ -16,13 +16,13 @@ - +

Centrifugo introduction

Centrifugo is an open-source scalable real-time messaging server in a language-agnostic way.

Real-time?

By real-time, we indicate a soft real-time. Due to network latencies, garbage collection cycles, and so on, the delay of a delivered message can be up to several hundred milliseconds or higher.

It can be a missing piece in your application architecture to send real-time updates to users. Think chats, live comments, multiplayer games, streaming metrics – you'll be able to build amazing web and mobile real-time apps with a help of Centrifugo.

Centrifugo works in conjunction with applications written in any programming language – both on the backend and frontend sides. It runs as a standalone service hosted on your hardware and fits well to both monolithic and microservice architectures.

Centrifugo is fast and scales well to support millions of concurrent client connections. It provides several real-time transports to choose from and a set of features to simplify building real-time applications.

Motivation

Centrifugo was born to help applications with a server-side written in a language or a framework without built-in concurrency support. In this case, dealing with persistent connections is a real headache that usually can only be resolved by introducing a shift in the technology stack and spending enough time to create a production-ready solution.

For example, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others have poor and not performant support of working with many persistent connections for the real-time messaging task.

In this case, Centrifugo is a very straightforward and non-obtrusive way to introduce real-time updates and handle lots of persistent connections without radical changes in application backend architecture. Developers could proceed writing a backend with a favorite language or favorite framework, keep existing architecture – and just let Centrifugo deal with persistent connections.

At the moment, Centrifugo provides some advanced and unique features that can simplify a developer's life and save months of development, even if the application backend is built with the asynchronous concurrent language. One example is that Centrifugo can scale out-of-the-box to many machines with several supported brokers. And there are more things to mention – see detailed highlights further in the docs.

Concepts

As mentioned above, Centrifugo runs as a standalone service that cares about handling persistent connections from application users. Application backend and frontend can be written in any programming language. Clients connect to Centrifugo and subscribe to channels.

As soon as some event happens application backend can publish a message with event payload into a channel using Centrifugo API. The message will be delivered to all clients currently connected and subscribed to a channel.

So Centrifugo is a user-facing PUB/SUB server in a nutshell. Here is a simplified scheme:

Centrifugo scheme

Join community

We have rooms in Telegram and Discord:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

See you there!

- + \ No newline at end of file diff --git a/docs/3/getting-started/migration_v3.html b/docs/3/getting-started/migration_v3.html index 51b43b9db..694194de4 100644 --- a/docs/3/getting-started/migration_v3.html +++ b/docs/3/getting-started/migration_v3.html @@ -16,13 +16,13 @@ - +

Migrating to v3

This chapter aims to help developers migrate from Centrifugo v2 to Centrifugo v3. Migration should mostly affect the backend part only, so you won't need to change the code of your frontend applications at all. In most cases, all you should do is adapt Centrifugo configuration to match v3 changes and redeploy Centrifugo using v3 build instead of v2.

There are a couple of exceptions - read carefully about client-side changes below.

In any case – don't forget to test your application before running in production.

Client-side changes

Client protocol has some backward incompatible changes regarding working with history API and removing deprecated fields.

No unlimited history by default

Call to history API from client-side now does not return all publications from history cache. It returns only information about a stream with zero publications. Clients should explicitly provide a limit when calling history API. Also, the maximum allowed limit can be set by client_history_max_publication_limit option (by default 300).

We provide a boolean flag use_unlimited_history_by_default on configuration file top level to enable previous behavior while you migrate client applications to use explicit limit.

Publication limit for recovery

The maximum number of messages that can be recovered is now limited by client_recovery_max_publication_limit option which is by default 300.

Seq/Gen fields removed

Deprecated seq/gen now removed and Centrifugo uses offset field for a position in a stream. This means that there is no need for v3_use_offset option anymore – it's not used in Centrifugo v3.

Server-side changes

Time interval options are duration

In Centrifugo v3 all time intervals should be configured using duration.

For example "proxy_connect_timeout": 1 should be changed to "proxy_connect_timeout": "1s".

We provide a configuration converter which takes this change into account.

Channel options changes

In Centrifugo v3 history_recover option becomes recover.

Option history_lifetime renamed to history_ttl and it's now a duration.

Option server_side removed, see protected option as a replacement.

We provide a configuration converter which takes these changes into account.

Some command-line flags removed

Configuring over command-line flags is not very convenient for production deployments, Centrifugo v3 reduced the number of command-line flags available – it mostly has flags frequently useful for development now.

Enforced request Origin check

In Centrifugo v3 you should explicitly set a list of allowed origins which are allowed to connect to client transport endpoints.

config.json
{
...
"allowed_origins": ["https://mysite.com"]
}

There is a way to disable origin check, but it's discouraged and insecure in case you are using connect proxy feature.

config.json
{
...
"allowed_origins": ["*"]
}

Updated GRPC API Protobuf package

In Centrifugo v3 we addressed an issue where package name in Protobuf definitions resulted in some inconvenience and attempts to rename it. But it's not possible to rename it since GRPC uses it as part of RPC methods internally. Now GRPC API package looks like this:

package centrifugal.centrifugo.api;

This means you need to regenerate your GRPC code which communicates with Centrifugo using the latest Protobuf definitions. Refer to the GRPC API doc.

Channels API method changed

The response format of channels API call changed in v3. See description in API doc.

The channels method has new additional possibilities like showing the number of connections in a channel and filter channels by pattern.

info

Channels API call still has the same concern as before: this method does not scale well for many active channels in a system and is mostly recommended for administrative/debug purposes.

HTTP proxy changes

When using HTTP proxy you should now set an explicit list of headers you want to proxy. To mimic the behavior of Centrifugo v2 add to your configuration:

config.json
{
"proxy_http_headers": [
"Origin",
"User-Agent",
"Cookie",
"Authorization",
"X-Real-Ip",
"X-Forwarded-For",
"X-Request-Id"
]
}

If you had a list of extra HTTP headers using proxy_extra_http_headers then additionally extend list above with values from proxy_extra_http_headers. Then you can remove proxy_extra_http_headers - it's not used anymore.

Another important change is how Centrifugo proxies binary data over HTTP JSON proxy. Previously proxy mode (whether to use base64 fields or not) could be configured using encoding=binary URL param of connection. With Centrifugo v3 it's only possible to use binary mode by enabling "proxy_binary_encoding": true option. BTW according to our community poll only 2% of Centrifugo users used binary mode in HTTP proxy. If you have problems with new behavior – write about your situation to our community chats – and we will see what's possible.

JWT changes

eto claim of subscription JWT removed. But since Centrifugo v3 introduced an additional expire_at claim it's still possible to implement one-time subscription tokens without enabling subscription expiration workflow by setting "expire_at: 0" in subscription JWT claims.

Redis configuration changes

Redis configuration was a bit messy - especially in the Redis sharding case, in v3 we decided to clean up it a bit. Make it more explicit and reduce the number of possible ways to configure.

Refer to the Redis Engine docs for the new configuration details. The important thing is that there is no separate redis_host and redis_port option anymore – those are replaced with single redis_address option.

Redis streams used by default

Centrifugo v3 will use Redis Stream data structure to keep history instead of lists.

danger

This requires Redis >= 5.0.1 to work. If you still need List data structure or have an old Redis version you can use "redis_use_lists": true to mimic the default behavior of Centrifugo v2.

SockJS disabled by default

Our poll showed that most Centrifugo users do not use SockJS transport. In v3 it's disabled by default. You can enable it by setting "sockjs": true in configuration.

Other configuration changes

Here is a full list of configuration option changes. We provide a best-effort configuration converter.

allowed_origins is now required to be set to authorize requests with Origin header

v3_use_offset removed

redis_streams removed

tls_autocert_force_rsa removed

redis_pubsub_num_workers removed

sockjs_disable removed

secret renamed to token_hmac_secret_key

history_lifetime renamed to history_ttl

history_recover renamed to recover

client_presence_ping_interval renamed to client_presence_update_interval

client_ping_interval renamed to websocket_ping_interval

client_message_write_timeout renamed to websocket_write_timeout

client_request_max_size renamed to websocket_message_size_limit

client_presence_expire_interval renamed to presence_ttl

memory_history_meta_ttl renamed to history_meta_ttl

redis_history_meta_ttl renamed to history_meta_ttl

redis_sequence_ttl renamed to history_meta_ttl

redis_presence_ttl renamed to presence_ttl

presence_ttl should be converted to duration

websocket_write_timeout should be converted to duration

websocket_ping_interval should be converted to duration

client_presence_update_interval should be converted to duration

history_ttl should be converted to duration

history_meta_ttl should be converted to duration

nats_dial_timeout should be converted to duration

nats_write_timeout should be converted to duration

graphite_interval should be converted to duration

shutdown_timeout should be converted to duration

shutdown_termination_delay should be converted to duration

proxy_connect_timeout should be converted to duration

proxy_refresh_timeout should be converted to duration

proxy_rpc_timeout should be converted to duration

proxy_subscribe_timeout should be converted to duration

proxy_publish_timeout should be converted to duration

client_expired_close_delay should be converted to duration

client_expired_sub_close_delay should be converted to duration

client_stale_close_delay should be converted to duration

client_channel_position_check_delay should be converted to duration

node_info_metrics_aggregate_interval should be converted to duration

websocket_ping_interval should be converted to duration

websocket_write_timeout should be converted to duration

sockjs_heartbeat_delay should be converted to duration

redis_idle_timeout should be converted to duration

redis_connect_timeout should be converted to duration

redis_read_timeout should be converted to duration

redis_write_timeout should be converted to duration

redis_cluster_addrs renamed to redis_cluster_address

redis_sentinels renamed to redis_sentinel_address

redis_master_name renamed to redis_sentinel_master_name

v2 to v3 config converter

Here is a converter between Centrifugo v2 and v3 JSON configuration. It can help to translate most of the things automatically for you.

If you are using Centrifugo with TOML format then you can use online converter as initial step. Or yaml-to-json and json-to-yaml for YAML.

tip

It's fully client-side: your data won't be sent anywhere.

danger

Unfortunately, we can't migrate environment variables and command-line flags automatically - so if you are using env vars or command-line flags to configure Centrifugo you still need to migrate manually. Also, be aware: this converter tool is the best effort only – we can not guarantee it solves all corner cases, especially in Redis configuration. You may still need to fix some things manually, for example - properly fill allowed_origins.

Here will be configuration for v3
Here will be log of changes made in your config
- + \ No newline at end of file diff --git a/docs/3/getting-started/quickstart.html b/docs/3/getting-started/quickstart.html index a3ddea6fe..0bd343dfc 100644 --- a/docs/3/getting-started/quickstart.html +++ b/docs/3/getting-started/quickstart.html @@ -16,13 +16,13 @@ - +

Quickstart tutorial ⏱️

Here we will build a very simple browser application with Centrifugo. It works in a way that users connect to Centrifugo over WebSocket, subscribe to a channel, and start receiving all messages published to that channel. In our case, we will send a counter value to all channel subscribers to update it in all open browser tabs in real-time.

First you need to install Centrifugo. Below in this example, we will use a binary file release for simplicity. Once you have Centrifugo available on your machine you can generate minimal required configuration file with the following command:

./centrifugo genconfig

This helper command will generate config.json file in the working directory with content like this:

config.json
{
"token_hmac_secret_key": "46b38493-147e-4e3f-86e0-dc5ec54f5133",
"admin_password": "ad0dff75-3131-4a02-8d64-9279b4f1c57b",
"admin_secret": "583bc4b7-0fa5-4c4a-8566-16d3ce4ad401",
"api_key": "aaaf202f-b5f8-4b34-bf88-f6c03a1ecda6",
"allowed_origins": []
}

Now we can start a server. Let's start it with a built-in admin web interface:

./centrifugo --config=config.json --admin

We could also enable the admin web interface by not using --admin flag but by adding "admin": true option to the JSON configuration file:

config.json
{
"token_hmac_secret_key": "46b38493-147e-4e3f-86e0-dc5ec54f5133",
"admin": true,
"admin_password": "ad0dff75-3131-4a02-8d64-9279b4f1c57b",
"admin_secret": "583bc4b7-0fa5-4c4a-8566-16d3ce4ad401",
"api_key": "aaaf202f-b5f8-4b34-bf88-f6c03a1ecda6",
"allowed_origins": []
}

And then running only with a path to a configuration file:

./centrifugo --config=config.json

Now open http://localhost:8000. You should see Centrifugo admin web panel. Enter admin_password value from the configuration file to log in.

Admin web panel

Inside the admin panel, you should see that one Centrifugo node is running, and it does not have connected clients.

Now let's create index.html file with our simple app:

index.html
<html>
<head>
<title>Centrifugo quick start</title>
</head>
<body>
<div id="counter">-</div>
<script src="https://cdn.jsdelivr.net/gh/centrifugal/centrifuge-js@2.8.4/dist/centrifuge.min.js"></script>
<script type="text/javascript">
const container = document.getElementById('counter')
const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket");
centrifuge.setToken("<TOKEN>");

centrifuge.on('connect', function(ctx) {
console.log("connected", ctx);
});

centrifuge.on('disconnect', function(ctx) {
console.log("disconnected", ctx);
});

centrifuge.subscribe("channel", function(ctx) {
container.innerHTML = ctx.data.value;
document.title = ctx.data.value;
});

centrifuge.connect();
</script>
</body>
</html>

Note that we are using centrifuge-js 2.8.4 in this example, you better use its latest version at the moment of reading this tutorial.

In index.html above we created an instance of a client (called Centrifuge) passing Centrifugo default WebSocket endpoint address to it, then we subscribed to a channel called channel and provided a callback function to process incoming real-time messages. Then we called .connect() method to start a WebSocket connection.

Now you need to serve this file with an HTTP server. In a real-world Javascript application, you will serve your HTML files with a web server of your choice – but for this simple example we can use a simple built-in Centrifugo static file server:

./centrifugo serve --port 3000

Alternatively, if you have Python 3 installed:

python3 -m http.server 3000

These commands start a simple static file web server that serves the current directory on port 3000. Make sure you still have Centrifugo server running. Open http://localhost:3000/.

Now if you look at browser developer tools or in Centrifugo logs you will notice that a connection can not be successfully established:

2021-09-01 10:17:33 [INF] request Origin is not authorized due to empty allowed_origins origin=http://localhost:3000

That's because we have not set allowed_origins in the configuration. Modify allowed_origins like this:

config.json
{
...
"allowed_origins": [
"http://localhost:3000"
]
}

Allowed origins is a security option for request originating from web browsers – see more details in server configuration docs. Restart Centrifugo after modifying allowed_origins in a configuration file.

Now if you reload a browser window with an application you should see new information logs in server output:

2021-02-26 17:47:47 [INF] invalid connection token error="jwt: token format is not valid" client=45a1b8f4-d6dc-4679-9927-93e41c14ad93
2021-02-26 17:47:47 [INF] disconnect after handling command client=45a1b8f4-d6dc-4679-9927-93e41c14ad93 command="id:1 params:\"{\\\"token\\\":\\\"<TOKEN>\\\"}\" " reason="invalid token" user=

We still can not connect. That's because the client should provide a valid JWT (JSON Web Token) to authenticate itself. This token must be generated on your backend and passed to a client-side (over template variables or using separate AJAX call – whatever way you prefer). Since in our simple example we don't have an application backend we can quickly generate an example token for a user using centrifugo sub-command gentoken. Like this:

./centrifugo gentoken -u 123722

– where -u flag sets user ID. The output should be like this:

HMAC SHA-256 JWT for user 123722 with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE1OTAxODYzMTZ9.YMJVJsQbK_p1fYFWkcoKBYr718AeavAk3MAYvxcMk0M

– you will have another token value since this one is based on randomly generated token_hmac_secret_key from the configuration file we created at the beginning of this tutorial. See authentication docs for information about proper token generation in real app.

Now we can copy generated HMAC SHA-256 JWT and paste it into centrifuge.setToken call instead of <TOKEN> placeholder in index.html file. I.e.:

centrifuge.setToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE1OTAxODYzMTZ9.YMJVJsQbK_p1fYFWkcoKBYr718AeavAk3MAYvxcMk0M");

That's it! If you reload your browser tab – the connection will be successfully established and the client will subscribe to a channel.

Open developer tools and look at WebSocket frames panel, you should see sth like this:

Connected

OK, the last thing we need to do here is to publish a new counter value to a channel and make sure our app works properly.

We can do this over Centrifugo API sending an HTTP request to default API endpoint http://localhost:8000/api, but let's do this over the admin web panel first.

Open Centrifugo admin web panel in another browser tab (http://localhost:8000/) and go to Actions section. Select publish action, insert channel name that you want to publish to – in our case this is a string channel and insert into data area JSON like this:

{
"value": 1
}

Admin publish

Click Submit button and check out the application browser tab – counter value must be immediately received and displayed.

Open several browser tabs with our app and make sure all tabs receive a message as soon as you publish it.

Message received

BTW, let's also look at how you can publish data to channel over Centrifugo API from a terminal using curl tool:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey aaaf202f-b5f8-4b34-bf88-f6c03a1ecda6" \
--request POST \
--data '{"method": "publish", "params": {"channel": "channel", "data": {"value": 2}}}' \
http://localhost:8000/api

– where for Authorization header we set api_key value from Centrifugo config file generated above.

We did it! We built the simplest browser real-time app with Centrifugo and its Javascript client. It does not have a backend, it's not very useful, to be honest, but it should give you an insight on how to start working with Centrifugo server. Read more about Centrifugo server in the next documentations chapters – it can do much much more than we just showed here. Integration guide describes a process of idiomatic Centrifugo integration with your application backend.

More examples

Several more examples are located on Github – check out this repo.

Also, check out our blog with several tutorials.

- + \ No newline at end of file diff --git a/docs/3/pro/analytics.html b/docs/3/pro/analytics.html index c3ab12c56..5af5b2842 100644 --- a/docs/3/pro/analytics.html +++ b/docs/3/pro/analytics.html @@ -16,13 +16,13 @@ - +

Analytics with ClickHouse

This feature allows exporting information about connections, subscriptions and client operations to ClickHouse thus providing an integration with a real-time (with seconds delay) analytics storage. ClickHouse is super fast and simple to operate with, and it allows effective data keeping for a window of time.

This unlocks a great observability and possibility to perform various analytics queries for better user behavior understanding, check application correctness, building trends, reports and so on.

Configuration

To enable integration with ClickHouse add the following section to a configuration file:

config.json
{
...
"clickhouse_analytics": {
"enabled": true,
"clickhouse_dsn": [
"tcp://127.0.0.1:9000",
"tcp://127.0.0.1:9001",
"tcp://127.0.0.1:9002",
"tcp://127.0.0.1:9003"
],
"clickhouse_database": "centrifugo",
"clickhouse_cluster": "centrifugo_cluster",
"export_connections": true,
"export_operations": true,
"export_http_headers": [
"User-Agent",
"Origin",
"X-Real-Ip",
]
}
}

All ClickHouse analytics options scoped to clickhouse_analytics section of configuration.

Toggle this feature using enabled boolean option.

tip

While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo.

Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option.

You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database.

export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available.

export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available.

export_http_headers is a list of HTTP headers to export for connection information.

export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport.

skip_schema_initialization (new in Centrifugo PRO v3.1.1) - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process.

skip_ping_on_start (new in Centrifugo PRO v3.1.1) - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.

Connections table

SHOW CREATE TABLE centrifugo.connections;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.connections
(
`client` FixedString(36),
`user` String,
`name` String,
`version` String,
`transport` String,
`channels` Array(String),
`headers.key` Array(String),
`headers.value` Array(String),
`metadata.key` Array(String),
`metadata.value` Array(String),
`time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.connections_distributed;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.connections_distributed
(
`client` FixedString(36),
`user` String,
`name` String,
`version` String,
`transport` String,
`channels` Array(String),
`headers.key` Array(String),
`headers.value` Array(String),
`metadata.key` Array(String),
`metadata.value` Array(String),
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client))
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

Operations table

SHOW CREATE TABLE centrifugo.operations;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations
(
`client` FixedString(36),
`user` String,
`op` String,
`channel` String,
`method` String,
`error` UInt32,
`disconnect` UInt32,
`duration` UInt64,
`time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.operations_distributed;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations_distributed
(
`client` FixedString(36),
`user` String,
`op` String,
`channel` String,
`method` String,
`error` UInt32,
`disconnect` UInt32,
`duration` UInt64,
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client))
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Query examples

Show unique users which were connected:

SELECT DISTINCT user
FROM centrifugo.connections_distributed;

┌─user─────┐
│ user_1 │
│ user_2 │
│ user_3 │
│ user_4 │
│ user_5 │
└──────────┘

Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111):

SELECT COUNT(*)
FROM centrifugo.operations_distributed
WHERE (error = 111) AND (op = 'publish');

┌─count()─┐
4502
└─────────┘

The same for a specific user:

SELECT COUNT(*)
FROM centrifugo.operations_distributed
WHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');

┌─count()─┐
1214
└─────────┘

Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since connections table contain periodic snapshot entries, clients could subscribe/unsubscribe in between snapshots – this is reflected in operations table):

SELECT COUNT(Distinct(user))
FROM centrifugo.connections_distributed
WHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));

┌─uniqExact(user)─┐
101
└─────────────────┘

Show top 10 users which called publish operation during last one minute:

SELECT
COUNT(op) AS num_ops,
user
FROM centrifugo.operations_distributed
WHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))
GROUP BY user
ORDER BY num_ops DESC
LIMIT 10;

┌─num_ops─┬─user─────┐
56 │ user_200 │
11 │ user_75 │
6 │ user_87 │
6 │ user_65 │
6 │ user_39 │
5 │ user_28 │
5 │ user_63 │
5 │ user_89 │
3 │ user_32 │
3 │ user_52 │
└─────────┴──────────┘

Development

The recommended way to run ClickHouse in production is with cluster. But during development you may want to run Centrifugo with single instance ClickHouse.

To do this set only one ClickHouse dsn and do not set cluster name:

config.json
{
...
"clickhouse_analytics": {
"enabled": true,
"clickhouse_dsn": [
"tcp://127.0.0.1:9000"
],
"clickhouse_database": "centrifugo",
"clickhouse_cluster": "",
"export_connections": true,
"export_operations": true,
"export_http_headers": [
"Origin",
"User-Agent"
]
}
}

Run ClickHouse locally:

docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click yandex/clickhouse-server

Run ClickHouse client:

docker run -it --rm --link click:clickhouse-server yandex/clickhouse-client --host clickhouse-server

Issue queries:

:) SELECT * FROM centrifugo.operations

┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │
└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘

How export works

When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 5 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are not configurable at the moment but just reach us out if you need to tune these values.

Several metrics are exposed to monitor export process health:

  • centrifugo_clickhouse_analytics_flush_duration_seconds summary
  • centrifugo_clickhouse_analytics_batch_size summary
  • centrifugo_clickhouse_analytics_drop_count counter
- + \ No newline at end of file diff --git a/docs/3/pro/db_namespaces.html b/docs/3/pro/db_namespaces.html index 7bddb7232..1c0a0bd74 100644 --- a/docs/3/pro/db_namespaces.html +++ b/docs/3/pro/db_namespaces.html @@ -16,13 +16,13 @@ - +

Database-driven namespace configuration

Centrifugo PRO supports database-driven namespace configuration. This means that instead of configuring namespaces in a configuration file you will be able to configure them in admin web UI. It's also possible to select a namespace for automatic personal channel subscription.

Namespaces

How it works

As soon as you point Centrifugo PRO to an admin storage and enable storage namespace management, Centrifugo will load namespaces from database table on start. Changes made in web UI will then propagate to all running Centrifugo nodes in up to 30 seconds.

info

Centrifugo nodes cache namespace configuration in memory so if Centrifugo temporarily lost connection to a database it will continue working with previous namespace configuration until connection problems will be resolved.

Configuration

By default namespace database management is off – i.e. namespaces loaded on Centrifugo start from a configuration file (or environment variable).

To enable namespace management through database add the following into configuration file:

config.json
{
...
"admin_storage": {
"enabled": true,
"storage_type": "sqlite",
"storage_dsn": "/path/to/centrifugo.db",
"manage_namespaces": true
}
}

Centrifugo PRO supports several SQL database backends to keep namespace information:

  • SQLite (storage_type: sqlite)
  • PostgreSQL (storage_type: postgresql)
  • MySQL (storage_type: mysql)

Each storage type has its own storage_dsn format. For SQLite it's just a path to a db file.

PostgreSQL dsn format described here. Example:

config.json
{
...
"admin_storage": {
"enabled": true,
"storage_type": "postgresql",
"storage_dsn": "host=localhost user=postgres password=mysecretpassword dbname=centrifugo port=5432 sslmode=disable",
"manage_namespaces": true
}
}

MySQL dsn format described here. Example:

config.json
{
...
"admin_storage": {
"enabled": true,
"storage_type": "mysql",
"storage_dsn": "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local",
"manage_namespaces": true
}
}
- + \ No newline at end of file diff --git a/docs/3/pro/install_and_run.html b/docs/3/pro/install_and_run.html index 36ac8ea1e..de7df9dd7 100644 --- a/docs/3/pro/install_and_run.html +++ b/docs/3/pro/install_and_run.html @@ -16,13 +16,13 @@ - +

Install and run PRO version

caution

Centrifugo PRO is distributed under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept license terms.

Binary release

Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).

Docker image

Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro:

docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v3.2.2 centrifugo -c config.json

Kubernetes

You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag:

values.yaml
...
image:
registry: docker.io
repository: centrifugo/centrifugo-pro
tag: v3.2.2

Debian and Ubuntu

DEB package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v3.2.2/centrifugo-pro_3.2.2-0_amd64.deb
sudo dpkg -i centrifugo-pro_3.2.2-0_amd64.deb

Centos

RPM package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v3.2.2/centrifugo-pro-3.2.2-0.x86_64.rpm
sudo yum install centrifugo-pro-3.2.2-0.x86_64.rpm

Setting PRO license key

Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits.

Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this:

config.json
{
...
"license": "<YOUR_LICENSE_KEY>"
}
tip

If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.

- + \ No newline at end of file diff --git a/docs/3/pro/overview.html b/docs/3/pro/overview.html index 9a11b5735..ac46e343a 100644 --- a/docs/3/pro/overview.html +++ b/docs/3/pro/overview.html @@ -16,13 +16,13 @@ - +

Centrifugo PRO overview

Centrifugo PRO is an extended version of Centrifugo with a set of additional features. These features can provide your business with unique benefits – drastically save development time, reduce resource usage on a server, protect your backend from misusing, and put the system observability to the next level.

Features

Centrifugo PRO includes the following features:

info

PRO features can change with time. We reserve a right to move features from PRO to OSS version if there is a clear signal that this is required to do for the Centrifugo ecosystem.

Sandbox mode

You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example:

  • Centrifugo handles up to 50 concurrent connections
  • up to 2 server nodes supported
  • up to 20 API requests per second allowed

This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future.

caution

Centrifugo PRO is distributed under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept license terms.

Pricing

To run without limits Centrifugo PRO requires a license key. At this point we are not issuing license keys for Centrifugo PRO as we are in the process of defining the pricing strategy for it. Please contact us over centrifugal.dev@gmail.com, we can add you to the list of interested customers and will appreciate if you share which features you are mostly interested in.

- + \ No newline at end of file diff --git a/docs/3/pro/performance.html b/docs/3/pro/performance.html index ed76d7b63..f66a8a370 100644 --- a/docs/3/pro/performance.html +++ b/docs/3/pro/performance.html @@ -16,13 +16,13 @@ - +

Faster performance

Centrifugo PRO has performance improvements for several server parts. These improvements can help to reduce tail end-to-end latencies in application, increase server throughput and/or reduce CPU usage on server machines.

Faster HTTP API

Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API.

The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.

Faster GRPC API

Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster HTTP proxy

Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster GRPC proxy

Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster JWT decoding

Centrifugo PRO has an optimized decoding of JWT claims.

Faster GRPC unidirectional stream

Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.

Examples

Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.

Publish HTTP API

In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.

History HTTP API

In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.

- + \ No newline at end of file diff --git a/docs/3/pro/process_stats.html b/docs/3/pro/process_stats.html index 058ad38c3..d07f8763a 100644 --- a/docs/3/pro/process_stats.html +++ b/docs/3/pro/process_stats.html @@ -16,13 +16,13 @@ - +

CPU and RSS stats

A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI.

Here is how this looks like:

Process stats

The information updated in near real-time (with several seconds delay). It's also available as part of info API.

- + \ No newline at end of file diff --git a/docs/3/pro/singleflight.html b/docs/3/pro/singleflight.html index d9bbe5b09..635b0bbe5 100644 --- a/docs/3/pro/singleflight.html +++ b/docs/3/pro/singleflight.html @@ -16,13 +16,13 @@ - +

Singleflight

Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request.

This option can radically reduce a load on a broker in the following situations:

  • Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature)
  • Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position
  • Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously

Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine.

To enable:

config.json
{
...
"use_singleflight": true
}

Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.

- + \ No newline at end of file diff --git a/docs/3/pro/throttling.html b/docs/3/pro/throttling.html index 892fee823..144a63f83 100644 --- a/docs/3/pro/throttling.html +++ b/docs/3/pro/throttling.html @@ -16,13 +16,13 @@ - +

Operation throttling

The throttling feature allows limiting the number of operations each user can issue during a configured time interval. This is useful to protect the system from misusing or protect it from a bug in the application frontend code.

Redis throttling

At this moment Centrifugo PRO provides throttling over Redis. It's only possible to throttle by the user ID. Requests from anonymous users can't be throttled. Throttling with Redis uses token bucket algorithm internally.

Here is a list of operations that can be throttled:

  • connect
  • subscribe
  • publish
  • history
  • presence
  • presence_stats
  • refresh
  • sub_refresh
  • rpc (with method resolution)

An example configuration:

config.json
{
...
"redis_throttling": {
"enabled": false,
"redis_address": "localhost:6379",
"buckets": {
"publish": {
"enabled": true,
"interval": "1s",
"rate": 1,
"capacity": 1
},
"rpc": {
"enabled": true,
"interval": "1s",
"rate": 10,
"capacity": 1,
"method_override": [
{
"method": "updateActiveStatus",
"interval": "20s",
"rate": 1,
"capacity": 1
}
]
}
}
}
}

This configuration enables throttling and throttles publish attempts in a way that only 1 publication is possible in 1 second from the same user.

Redis configuration for throttling feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for throttling feature too.

It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this:

config.json
{
...
"engine": "redis",
"redis_address": "localhost:6379",
"redis_throttling": {
"enabled": false,
"use_redis_from_engine": true,
"buckets": {
"publish": {
"enabled": true,
"interval": "1s",
"rate": 1,
"capacity": 1
}
}
}
}

In this case throttling will simply connect to Redis instances configured for an Engine.

- + \ No newline at end of file diff --git a/docs/3/pro/token_revocation.html b/docs/3/pro/token_revocation.html index c3b9977bc..4b421c537 100644 --- a/docs/3/pro/token_revocation.html +++ b/docs/3/pro/token_revocation.html @@ -16,13 +16,13 @@ - +

Token revocation API

One more protective instrument in Centrifugo PRO is API to manage token revocations.

At the moment Centrifugo provides two ways to revoke tokens:

  1. Revoke token by ID: based on jti claim in the case of JWT.
  2. Revoke all user's tokens issued before certain time: based on iat in the case of JWT.

When token is revoked client with such token will be disconnected from Centrifugo shortly. And attempt to connect with a revoked token won't succeed.

How it works

By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart.

But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts.

Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.

Configure

Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory.

There are two types of persistent engines supported at the moment:

  1. redis
  2. database

Redis persistence engine

Revocation data can be kept in Redis. To enable this configuration should be:

{
...
"token_revoke": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
},
"user_tokens_invalidate": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
}
}
danger

Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start.

caution

One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.

Database persistence engine

Revocation data can be kept in the relational database. Only PostgreSQL is supported.

To enable this configuration should be like:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"token_revoke": {
"persistence_engine": "database"
},
"user_tokens_invalidate": {
"persistence_engine": "database"
}
}

Revoke token API

Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT).

caution

This API assumes that JWTs you are using contain "jti" claim which is a unique token ID (according to RFC).

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "revoke_token", "params": {"uid": "xxx-xxx-xxx", "expire_at": 1635845122}}' \
http://localhost:8000/api

Revoke token params

Parameter nameParameter typeRequiredDescription
uidstringyesToken unique ID (JTI claim in case of JWT)
expire_atintnoUnix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache).

Revoke token result

Empty object at the moment.

Invalidate user tokens API

Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application.

caution

This API assumes that JWTs you are using contain "iat" claim which is a time token was issued at (according to RFC).

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "invalidate_user_tokens", "params": {"user": "test", "issued_before": 1635845022, "expire_at": 1635845122}}' \
http://localhost:8000/api

Invalidate user tokens params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to invalidate tokens for
issued_beforeintyesAll tokens issued at before this time will be considered revoked (in case of JWT this requires iat to be properly set in JWT)
expire_atintnoUnix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache).

Invalidate user tokens result

Empty object at the moment.

- + \ No newline at end of file diff --git a/docs/3/pro/tracing.html b/docs/3/pro/tracing.html index 208ef74b1..bb693afeb 100644 --- a/docs/3/pro/tracing.html +++ b/docs/3/pro/tracing.html @@ -16,13 +16,13 @@ - +

User and channel tracing

That's a unique thing. The tracing feature of Centrifugo PRO allows attaching to any channel to see all messages flying towards subscribers or attach to a specific user ID to see all user-related events in real-time.

It's possible to attach to trace streams using Centrifugo admin UI panel or simply from terminal using CURL and admin token.

This can be super-useful for debugging issues, investigating application behavior, understanding that the application works as expected.

caution

The tracing feature works fine for JSON messages. For binary payloads, there are some limitations currently.

Save to a file

It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing.

curl -X POST http://localhost:8000/admin/trace -H "Authorization: token <ADMIN_AUTH_TOKEN>" -d '{"type": "user", "entity": "56"}' -o trace.txt

Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future.

- + \ No newline at end of file diff --git a/docs/3/pro/user_block.html b/docs/3/pro/user_block.html index 9bf4b69db..33b20fe32 100644 --- a/docs/3/pro/user_block.html +++ b/docs/3/pro/user_block.html @@ -16,13 +16,13 @@ - +

User blocking API

In addition to detailed observability properties Centrifugo PRO provides instruments for performing protective actions. One of such instruments is user blocking API which allows blocking a specific user in Centrifugo.

When user is blocked it will be disconnected from Centrifugo immediately and also on the next connect attempt right after JWT decoded (so that Centrifugo got a user ID) or after result from connect proxy received. In case of using connect proxy you can actually disconnect user yourself by implementing blocking check on the application backend side – but possibility to block user in Centrifugo can still be helpful.

How it works

By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart.

But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts.

Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.

Configure

User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine.

There are two types of persistent engines supported at the moment:

  1. redis
  2. database

Redis persistence engine

Blocking data can be kept in Redis. To enable this configuration should be:

{
...
"user_block": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
}
}
danger

Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start.

caution

One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.

Database persistence engine

Blocking data can be kept in the relational database. Only PostgreSQL is supported.

To enable this configuration should be like:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"user_block": {
"persistence_engine": "database"
}
}

Block user API

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "block_user", "params": {"user": "2695", "expire_at": 1635845122}}' \
http://localhost:8000/api

Block user params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to block
expire_atintnoUnix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache).

Block user result

Empty object at the moment.

Unblock user API

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "unblock_user", "params": {"user": "2695"}}' \
http://localhost:8000/api

Unblock user params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to unblock

Unblock user result

Empty object at the moment.

- + \ No newline at end of file diff --git a/docs/3/pro/user_connections.html b/docs/3/pro/user_connections.html index 18fb73014..75a6783f6 100644 --- a/docs/3/pro/user_connections.html +++ b/docs/3/pro/user_connections.html @@ -16,13 +16,13 @@ - +

User connections API

Centrifugo PRO provides an additional API call user_connections. It allows getting all active sessions of the user (by user ID) without turning on presence feature for channels at all. It's also possible to attach any JSON payload to a connection which will be then visible in the result of user_connections call. The important thing is that this additional meta information won't be exposed to a client-side (unlike connection info for example).

This feature can be useful to manage active user sessions – for example in a messenger application. Users can look at a list of own current sessions and close some of them (possible with Centrifugo disconnect server API).

Below is a feature showcase using admin web UI, but this call is available over HTTP or GRPC server API.

Let's look at example. Generate a JWT for user 42:

centrifugo genconfig
centrifugo gentoken -u 42
HMAC SHA-256 JWT for user 42 with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y

Run Centrifugo with uni_http_stream transport enabled (it will allow us to connect from console):

CENTRIFUGO_UNI_HTTP_STREAM=1 centrifugo -c config.json

Create new terminal window and run:

curl -X POST http://localhost:8000/connection/uni_http_stream --data '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y", "name": "terminal"}'

In another terminal create one more connection:

curl -X POST http://localhost:8000/connection/uni_http_stream --data '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y", "name": "terminal"}'

Now let's call user_connections over HTTP API:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "user_connections", "params": {"user": "42"}}' \
http://localhost:8000/api

The result:

{
"result": {
"connections": {
"db8bc772-2654-4283-851a-f29b888ace74": {
"app_name": "terminal",
"transport": "uni_http_stream",
"protocol": "json"
},
"4bc3ca70-ecc5-439d-af14-a78ae18e31c7": {
"app_name": "terminal",
"transport": "uni_http_stream",
"protocol": "json"
}
}
}
}

Here we can see that user has 2 connections from terminal app.

Each connection can be annotated with meta JSON information which is set during connection establishment (over meta claim of JWT or by returning meta in connect proxy result).

User connections params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID

User connections result

Field nameField typeOptionalDescription
connectionsmap of string to UserConnectionInfonoactive user connections map where key is client ID and value is UserConnectionInfo

UserConnectionInfo

Field nameField typeOptionalDescription
app_namestringyesclient app name (if provided by client)
app_versionstringyesclient app version (if provided by client)
transportstringnoclient connection transport
protocolstringnoclient connection protocol (json or protobuf)
token_uidstringyesclient JWT unique ID (if set)
token_issued_atintyesclient JWT issued at time as Unix seconds (if set)
metaJSONyesmeta information attached to a connection
- + \ No newline at end of file diff --git a/docs/3/pro/user_status.html b/docs/3/pro/user_status.html index a5799f034..f85117bf1 100644 --- a/docs/3/pro/user_status.html +++ b/docs/3/pro/user_status.html @@ -16,13 +16,13 @@ - +

User status

Centrifugo OSS provides a presence feature for channels. It works well (for channels with reasonably small number of active subscribers though), but sometimes you may need a bit different functionality.

What if you want to get a specific user status based on its recent activity in application? You can create a personal channel with a presence enabled for each user. It will show that user has an active connection with a server. But this won't show whether user did some actions in an application recently or just left it open while not actually using it.

User status feature of Centrifugo PRO allows calling a special RPC method from a client side when a user makes a useful action in an application (clicks on buttons, uses a mouse – whatever means that user really uses application at the moment). This call sets a time of last user activity in Redis, and this information can then be queried over Centrifugo PRO server API.

The feature can be useful for chat applications when you need to get online/activity status for a list of buddies (Centrifugo supports batch requests to user status information – i.e. ask for many users in one call).

Client-side status update RPC

Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript:

await centrifuge.namedRPC('update_user_status', {});
note

Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example.

This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.

update_user_status server API

It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports):

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "update_user_status", "params": {"users": ["42"]}}' \
http://localhost:8000/api

Update user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to update status for

Update user status result

Empty object at the moment.

get_user_status server API

Now on a backend side you have access to a bulk API to effectively get status of particular users.

Call RPC method of server API (over HTTP or GRPC):

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "get_user_status", "params": {"users": ["42"]}}' \
http://localhost:8000/api

You should get a response like this:

{
"result":{
"statuses":[
{
"user":"42",
"active":1627107289,
"online":1627107289
}
]
}
}

In case information about last status update time not available the response will be like this:

{
"result":{
"statuses":[
{
"user":"42"
}
]
}
}

I.e. status object will present in a response but active field won't be set for status object.

Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time).

Get user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to get status for

Get user status result

Field nameField typeOptionalDescription
statusesarray of UserStatusnoStatuses for each user in params (same order)

UserStatus

Field nameField typeOptionalDescription
userstringnoUser ID
activeintegeryesLast active time (Unix seconds)
onlineintegeryesLast online time (Unix seconds)

delete_user_status server API

If you need to clear user status information for some reason there is a delete_user_status server API call:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "delete_user_status", "params": {"users": ["42"]}}' \
http://localhost:8000/api

Delete user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to delete status for

Delete user status result

Empty object at the moment.

Configuration

To enable Redis active status feature:

config.json
{
...
"redis_active_status": {
"enabled": true,
"redis_address": "127.0.0.1:6379"
}
}

Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too.

It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this:

config.json
{
...
"engine": "redis",
"redis_address": "localhost:6379",
"redis_active_status": {
"enabled": true,
"use_redis_from_engine": true,
}
}

In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine.

expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day:

config.json
{
...
"redis_active_status": {
...
"expire_interval": "24h"
}
}
- + \ No newline at end of file diff --git a/docs/3/server/admin_web.html b/docs/3/server/admin_web.html index 0cea5f031..65a9cb38d 100644 --- a/docs/3/server/admin_web.html +++ b/docs/3/server/admin_web.html @@ -16,13 +16,13 @@ - +

Admin web UI

Centrifugo comes with builtin admin web interface. It can:

  • Show general information and statistics from server nodes - number of connections, unique users, number of subscriptions, unique channels etc.
  • Call publish, broadcast, subscribe, unsubscribe, disconnect, history, history_remove, presence, presence_stats, info, channels and some additional Centrifugo PRO server API commands.
  • Configure namespaces over a database (Centrifugo PRO feature)
  • Trace connections in real-time (Centrifugo PRO feature)

To enable admin web interface run Centrifugo with admin option enabled and provide some security options in configuration file:

config.json
{
...
"admin": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>"
}

Options

  • admin (boolean, default: false) – enables/disables admin web UI
  • admin_password (string, default: "") – this is a password to log into admin web interface
  • admin_secret (string, default: "") - this is a secret key for authentication token set on successful login.

Make both admin_password and admin_secret strong and keep them in secret.

After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface.

tip

Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.

Admin web panel

Using custom web interface

If you want to use custom web interface you can specify path to web interface directory dist:

config.json
{
...,
"admin": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>",
"admin_web_path": "<PATH_TO_WEB_DIST>"
}

This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.

Admin insecure mode

There is also an option to run Centrifugo in insecure admin mode - in this case you don't need to set admin_password and admin_secret in config – in web interface you will be logged in automatically without any password. Note that this is only an option for production if you protected admin web interface with firewall rules. Otherwise anyone in internet will have full access to admin functionality described above. To enable insecure admin mode:

config.json
{
...,
"admin": true,
"admin_insecure": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>"
}
- + \ No newline at end of file diff --git a/docs/3/server/authentication.html b/docs/3/server/authentication.html index 1a72a0076..7d9c10296 100644 --- a/docs/3/server/authentication.html +++ b/docs/3/server/authentication.html @@ -16,13 +16,13 @@ - +

Client authentication

To authenticate incoming connection (client) Centrifugo can use JSON Web Token (JWT) passed from the client-side. This way Centrifugo may know the ID of user in your application, also application can pass additional data to Centrifugo inside JWT claims. This chapter describes this authentication mechanism.

tip

If you prefer to avoid using JWT then look at the proxy feature. It allows proxying connection requests from Centrifugo to your application backend for authentication details.

Upon connecting to Centrifugo client should provide a connection JWT with several predefined credential claims. If you've never heard about JWT before - refer to jwt.io page.

At the moment Centrifugo supports HMAC, RSA and ECDSA JWT algorithms - i.e. HS256, HS384, HS512, RSA256, RSA384, RSA512, EC256, EC384, EC512.

We will use Javascript Centrifugo client here for example snippets for client-side and PyJWT Python library to generate a connection token on the backend side.

To add HMAC secret key to Centrifugo add token_hmac_secret_key to configuration file:

config.json
{
...
"token_hmac_secret_key": "<YOUR-SECRET-STRING-HERE>"
}

To add RSA public key (must be PEM encoded string) add token_rsa_public_key option, ex:

config.json
{
...
"token_rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZ..."
}

To add ECDSA public key (must be PEM encoded string) add token_ecdsa_public_key option, ex:

config.json
{
...
"token_ecdsa_public_key": "-----BEGIN PUBLIC KEY-----\nxyz23adf..."
}

Claims

Centrifugo uses the following claims in a JWT: sub, exp, iat, jti, info, b64info, channels, subs.

sub

This is a standard JWT claim which must contain an ID of the current application user (as string).

If a user is not currently authenticated in an application, but you want to let him connect to Centrifugo anyway – you can use an empty string as a user ID in sub claim. This is called anonymous access. In this case, the anonymous option must be enabled in Centrifugo configuration for channels that the client will subscribe to.

exp

This is a UNIX timestamp seconds when the token will expire. This is a standard JWT claim - all JWT libraries for different languages provide an API to set it.

If exp claim is not provided then Centrifugo won't expire connection. When provided special algorithm will find connections with exp in the past and activate the connection refresh mechanism. Refresh mechanism allows connection to survive and be prolonged. In case of refresh failure, the client connection will be eventually closed by Centrifugo and won't be accepted until new valid and actual credentials are provided in the connection token.

You can use the connection expiration mechanism in cases when you don't want users of your app to be subscribed on channels after being banned/deactivated in the application. Or to protect your users from token leakage (providing a reasonably short time of expiration).

Choose exp value wisely, you don't need small values because the refresh mechanism will hit your application often with refresh requests. But setting this value too large can lead to slow user connection deactivation. This is a trade-off.

Read more about connection expiration below.

iat

This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

jti

This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

aud

Handled since Centrifugo v3.2.0

By default, Centrifugo does not check JWT audience (rfc7519 aud claim).

But you can force this check by setting token_audience string option:

config.json
{
"token_audience": "centrifugo"
}
caution

Setting token_audience will also affect subscription tokens (used for private channels).

iss

Handled since Centrifugo v3.2.0

By default, Centrifugo does not check JWT issuer (rfc7519 iss claim).

But you can force this check by setting token_issuer string option:

config.json
{
"token_issuer": "my_app"
}
caution

Setting token_issuer will also affect subscription tokens (used for private channels).

info

This claim is optional - this is additional information about client connection that can be provided for Centrifugo. This information will be included in presence information, join/leave events, and channel publication if it was published from a client-side.

b64info

If you are using binary Protobuf protocol you may want info to be custom bytes. Use this field in this case.

This field contains a base64 representation of your bytes. After receiving Centrifugo will decode base64 back to bytes and will embed the result into various places described above.

channels

An optional array of strings with server-side channels to subscribe a client to. See more details about server-side subscriptions.

caution

By providing a list of channels in JWT with channels claim you are not making them automatically unaccessible by other users. Other users can still call a client-side .subscribe() method and subscribe to these channels if channel permissions allow doing this. If you need to protect channels from being subscribed by other connections then you can use private channels inside this channels array (i.e. starting with $) or turn on protected option for channels namespaces.

subs

An optional map of channels with options. This is like a channels claim but allows more control over server-side subscription since every channel can be annotated with info, data, and so on using options.

tip

This claim is called subs as a shortcut from subscriptions. The claim sub described above is a standart JWT claim to provide a user ID (it's a shortcut from subject). While claims have similar names they have different purpose in a connection JWT.

caution

By providing a map of channels in JWT with subs claim you are not making channels automatically unaccessible by other users. Other users can still call a client-side .subscribe() method and subscribe to these channels if channel permissions allow doing this. If you need to protect channels from being subscribed by other connections then you can use private channels inside this subs map (i.e. starting with $) or turn on protected option for channels namespaces.

Example:

{
...
"subs": {
"channel1": {
"data": {"welcome": "welcome to channel1"}
},
"channel2": {
"data": {"welcome": "welcome to channel2"}
}
}
}

Subscribe options:

FieldTypeOptionalDescription
infoJSON objectyesCustom channel info
b64infostringyesCustom channel info in Base64 - to pass binary channel info
dataJSON objectyesCustom JSON data to return in subscription context inside Connect reply
b64datastringyesSame as data but in Base64 to send binary data
overrideOverride objectyesAllows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
positionBoolValueyesOverride position
recoverBoolValueyesOverride recover

BoolValue is an object like this:

{
"value": true/false
}

meta

Meta is an additional JSON object (ex. {"key": "value"}) that will be attached to a connection. Unlike info it's never exposed to clients and only accessible on a backend side. It will be included in proxy calls from Centrifugo to the application backend. Also, there is a get_user_connections API method in Centrifugo PRO that returns this data in the user connection object.

expire_at

By default, Centrifugo looks on exp claim to configure connection expiration. In most cases this is fine, but there could be situations where you wish to decouple token expiration check with connection expiration time. As soon as the expire_at claim is provided (set) in JWT Centrifugo relies on it for setting connection expiration time (JWT expiration still checked over exp though).

expire_at is a UNIX timestamp seconds when the connection should expire.

  • Set it to the future time for expiring connection at some point
  • Set it to 0 to disable connection expiration (but still check token exp claim).

Connection expiration

As said above exp claim in a connection token allows expiring client connection at some point in time. Let's look in detail at what happens when Centrifugo detects that the connection is going to expire.

First, you should do is enable client expiration mechanism in Centrifugo providing a connection JWT with expiration:

import jwt
import time

token = jwt.encode({"sub": "42", "exp": int(time.time()) + 10*60}, "secret").decode()

print(token)

Let's suppose that you set exp field to timestamp that will expire in 10 minutes and the client connected to Centrifugo with this token. During 10 minutes the connection will be kept by Centrifugo. When this time passed Centrifugo gives the connection some time (configured, 25 seconds by default) to refresh its credentials and provide a new valid token with new exp.

When a client first connects to Centrifugo it receives the ttl value in connect reply. That ttl value contains the number of seconds after which the client must send the refresh command with new credentials to Centrifugo. Centrifugo clients must handle this ttl field and automatically start the refresh process.

For example, a Javascript browser client will send an AJAX POST request to your application when it's time to refresh credentials. By default, this request goes to /centrifuge/refresh URL endpoint. In response your server must return JSON with a new connection JWT:

{
"token": token
}

So you must just return the same connection JWT for your user when rendering the page initially. But with actual valid exp. Javascript client will then send them to Centrifugo server and connection will be refreshed for a time you set in exp.

In this case, you know which user wants to refresh its connection because this is just a general request to your app - so your session mechanism will tell you about the user.

If you don't want to refresh the connection for this user - just return 403 Forbidden on refresh request to your application backend.

Javascript client also has options to hook into a refresh mechanism to implement your custom way of refreshing. Other Centrifugo clients also should have hooks to refresh credentials but depending on client API for this can be different - see specific client docs.

Examples

Let's look at how to generate connection HS256 JWT in Python:

Simplest token

import jwt

token = jwt.encode({"sub": "42"}, "secret").decode()

print(token)

Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users.

Then you can pass this token to your client side and use it when connecting to Centrifugo:

Using centrifuge-js v2.x
var centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket");
centrifuge.setToken(token);
centrifuge.connect();

Token with expiration

HS256 token that will be valid for 5 minutes:

import jwt
import time

claims = {"sub": "42", "exp": int(time.time()) + 5*60}
token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Token with additional connection info

Let's attach user name:

import jwt

claims = {"sub": "42", "info": {"name": "Alexander Emelin"}}
token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Investigating problems with JWT

You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.

JSON Web Key support

Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT).

A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address).

As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels.

At the moment Centrifugo caches keys loaded from an endpoint for one hour.

Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment).

Only RSA algorithm is supported.

JWKS support enabled both connection and private channel subscription tokens.

- + \ No newline at end of file diff --git a/docs/3/server/channels.html b/docs/3/server/channels.html index e997f48c0..961de2612 100644 --- a/docs/3/server/channels.html +++ b/docs/3/server/channels.html @@ -16,13 +16,13 @@ - +

Channels

Channel is a route for publications. Clients can be subscribed to a channel to receive real-time messages published to a channel – new publications and join/leave events (if enabled for a channel namespace). A channel subscriber can also ask for a channel presence or channel history information (if enabled for a channel namespace).

Channel is just a string - news, comments, personal_feed are valid channel names. Though this string has some predefined rules as we will see below. You can define different channel behavior using a set of available channel options.

Channels are ephemeral – you don't need to create them explicitly. Channels created automatically by Centrifugo as soon as the first client subscribes to a channel. As soon as the last subscriber leaves a channel - it's automatically cleaned up.

Channel can belong to a channel namespace. Channel namespacing is a mechanism to define different behavior for different channels in Centrifugo. Using namespaces is a recommended way to manage channels – to turn on only those channel options which are required for a specific real-time feature you are implementing on top of Centrifugo.

caution

When using channel namespaces make sure you defined a namespace in configuration. Subscription attempts to a channel within a non-defined namespace will result into 102: unknown channel errors.

Channel name rules

Only ASCII symbols must be used in channel string.

Channel name length limited by 255 characters by default (can be changed via configuration file option channel_max_length).

Several symbols in channel names reserved for Centrifugo internal needs:

  • : – for namespace channel boundary (see below)
  • $ – for private channel prefix (see below)
  • # – for user channel boundary (see below)
  • * – for the future Centrifugo needs
  • & – for the future Centrifugo needs
  • / – for the future Centrifugo needs

namespace boundary (:)

: – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about available channel options and more about namespaces below.

If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public.

info

A namespace is part of the channel name. If a user subscribed to a channel with namespace, like public:chat – then you need to publish messages into public:chat channel to be delivered to the user. We often see some confusion from developers trying to publish messages into chat and thinking that namespace is somehow stripped upon subscription. It's not true.

private channel prefix ($)

If the channel starts with $ then it is considered private. The subscription on a private channel must be properly signed by your backend.

Use private channels if you pass sensitive data inside the channel and want to control access permissions on your backend.

For example $secrets is a private channel, $public:chat - is a private channel that belongs to namespace public.

Subscription request to private channels requires additional JWT from your application backend. Read detailed chapter about private channels.

If you need a personal channel for a single user (or maybe a channel for a short and stable set of users) then consider using a user-limited channel (see below) as a simpler alternative that does not require an additional subscription token from your backend.

Also, consider using server-side subscriptions or subscribe proxy feature of Centrifugo to model channels with restrictive access.

user channel boundary (#)

# – is a user channel boundary. This is a separator to create personal channels for users (we call this user-limited channels) without the need to provide a subscription token.

For example, if the channel is news#42 then the only user with ID 42 can subscribe to this channel (Centrifugo knows user ID because clients provide it in connection credentials with connection JWT).

If you want to create a user-limited channel in namespace personal then you can use a name like personal:user#42 for example.

tip

Channel like $personal:user#42 - i.e. channel with both private prefix $ and user channel boundary # does not have a lot of sense, most probably you can just use personal:user#42 as the ID of the user protected by authentication JWT or set by application backend when the connect proxy feature is used.

Moreover, you can provide several user IDs in channel name separated by a comma: dialog#42,43 – in this case only the user with ID 42 and user with ID 43 will be able to subscribe on this channel.

This is useful for channels with a static list of allowed users, for example for single user personal messages channel, for dialog channel between certainly defined users. As soon as you need to manage access to a channel dynamically for many users this channel type does not suit well.

Channel options

Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.

publish

publish (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side).

Keep in mind that your application will never receive such messages. In an idiomatic use case, all messages must be published to Centrifugo by an application backend using Centrifugo API (HTTP or GRPC). Or using publish proxy. Since in those cases your application has a chance to validate a message, save it into a database, and only after that broadcast to all subscribers.

But the publish option still can be useful to send something without backend-side validation and saving it into a database. This option can also be handy for demos and quick prototyping real-time app ideas.

subscribe_to_publish

subscribe_to_publish (boolean, default false) - when the publish option is enabled client can publish into a channel without being subscribed to it. This option enables an automatic check that the client subscribed to a channel before allowing a client to publish.

anonymous

anonymous (boolean, default false) – this option enables anonymous user access (i.e. for a user with an empty user ID). In most situations, your application works with authenticated users so every user has its unique user ID (set inside JWT sub claim or provided by backend when using connect proxy). But if you provide real-time features for public access you may need unauthenticated access to some channels. Turn on this option and use an empty string as a user ID. See also related global option client_anonymous which allows anonymous users to connect without JWT.

presence

presence (boolean, default false) – enable/disable online presence information for channels. Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels.

Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk).

presence_disable_for_client

presence_disable_for_client (boolean, default false) – allows making presence calls available only for a server-side API. By default, presence information is available for both client and server-side APIs.

join_leave

join_leave (boolean, default false) – enable/disable sending join(leave) messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info.

caution

Keep in mind that join/leave messages can generate a big number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature or to scale Centrifugo.

history_size

history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted.

Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below).

caution

Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk).

history_ttl

history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision).

As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels.

By default history TTL duration is zero – this means that channel history is disabled.

Again – to turn on history you should wisely configure both history_size and history_ttl options.

For example for top-level channels (which do not belong to a namespace):

config.json
{
...
"history_size": 10,
"history_ttl": "60s"
}

position

position (boolean, default false) – when the position feature is on Centrifugo tries to compensate at most once delivery of PUB/SUB messages checking client position inside a stream.

If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API.

position option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream).

recover

recover (boolean, default false) – when enabled Centrifugo will try to recover missed publications after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the position option described above.

recover option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages).

Also, note that not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.

history_disable_for_client

history_disable_for_client (boolean, default false) – allows making history available only for a server-side API. By default false – i.e. history calls are available for both client and server-side APIs.

note

History recovery mechanism if enabled will continue to work for clients anyway even if history_disable_for_client is on.

protected

protected (boolean, default false) – when on will prevent a client to subscribe to arbitrary channels in a namespace. In this case, Centrifugo will only allow a client to subscribe on user-limited channels, on channels returned by the proxy response, or channels listed inside JWT. Client-side subscriptions to arbitrary channels will be rejected with PermissionDenied error. Server-side channels belonging to the protected namespace passed by the client itself during connect will be ignored.

proxy_subscribe

proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter

proxy_publish

proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter

subscribe_proxy_name

subscribe_proxy_name (string, default "") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.

publish_proxy_name

publish_proxy_name (string, default "") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.

Channel options config example

Let's look at how to set some of these options in a config:

config.json
{
"token_hmac_secret_key": "my-secret-key",
"api_key": "secret-api-key",
"anonymous": true,
"publish": true,
"subscribe_to_publish": true,
"presence": true,
"join_leave": true,
"history_size": 10,
"history_ttl": "300s",
"recover": true
}

Here we set channel options on config top-level – these options will affect channels without namespace. Below we describe namespaces and how to define channel options for a namespace.

Channel namespaces

It's possible to configure a list of channel namespaces. Namespaces are optional but very useful.

A namespace allows setting custom options for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application.

Namespace has a name, and the same channel options (with the same defaults) as described above.

  • name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$).

If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator:

public:messages

gossips:messages

Where public and gossips are namespace names. Centrifugo will look for : symbol in the channel name, will extract the namespace name, and will apply namespace options whenever required.

All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured:

config.json
{
"token_hmac_secret_key": "very-long-secret-key",
"api_key": "secret-api-key",
"anonymous": true,
"publish": true,
"presence": true,
"join_leave": true,
"history_size": 10,
"history_ttl": "30s",
"namespaces": [
{
"name": "public",
"publish": true,
"anonymous": true,
"history_size": 10,
"history_ttl": "300s",
"recover": true
},
{
"name": "gossips",
"presence": true,
"join_leave": true
}
]
}
  • Channel news will use globally defined channel options.
  • Channel public:news will use public namespace options.
  • Channel gossips:news will use gossips namespace options.
  • Channel xxx:hello will result into subscription error since there is no xxx namespace defined in configuration above.

Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2.

note

There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly.

- + \ No newline at end of file diff --git a/docs/3/server/codes.html b/docs/3/server/codes.html index e4d836e38..37160d5af 100644 --- a/docs/3/server/codes.html +++ b/docs/3/server/codes.html @@ -16,7 +16,7 @@ - + @@ -67,7 +67,7 @@ Message: "bad request".

Error Bad Request says that Centrifugo can not parse received data because it is malformed or there are required fields missing.

Not Available

Code: 108, Message: "not available".

Error Not Available means that resource is not enabled.

Unrecoverable Position

Code: 112, Message: "unrecoverable position".

ErrorUnrecoverablePosition means that stream does not contain required range of publications to fulfill a history query.

This can happen due to wrong epoch.

- + \ No newline at end of file diff --git a/docs/3/server/configuration.html b/docs/3/server/configuration.html index e758c944e..76f3c6c76 100644 --- a/docs/3/server/configuration.html +++ b/docs/3/server/configuration.html @@ -16,13 +16,13 @@ - +

Configure Centrifugo

Let's look at how Centrifugo can be configured.

There are more options

This chapter describes configuration principles and some important configuration options. There are some options not mentioned here but described later in each feature documentation.

Configuration sources

Centrifugo can be configured in several ways.

Command-line flags

Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer environment or configuration file sources.

Command-line options have the highest priority when set than other ways to configure Centrifugo.

OS environment variables

All Centrifugo options can be set over env in the format CENTRIFUGO_<OPTION_NAME> (i.e. option name with CENTRIFUGO_ prefix, all in uppercase).

Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags.

Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use "true" value for an environment variable (or simply "1"). To set false use "false" or "0". Example:

export CENTRIFUGO_PROMETHEUS="1"

Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example:

export CENTRIFUGO_ALLOWED_ORIGINS="https://mysite1.example.com https://mysite2.example.com"

For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name.

Empty environment variables are considered unset (!) and will fall back to the next configuration source.

Configuration file

Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable prevails over the same option in config file).

A simple way to start with Centrifugo is to run:

centrifugo genconfig

This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo:

centrifugo -c config.json

Config file formats

Centrifugo supports three configuration file formats: JSON, YAML, or TOML.

JSON config format

Here is an example of Centrifugo JSON configuration file:

config.json
{
"allowed_origins": ["http://localhost:3000"],
"token_hmac_secret_key": "<YOUR-SECRET-STRING-HERE>",
"api_key": "<YOUR-API-KEY-HERE>"
}

token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT.

api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients.

allowed_origins option described below.

TOML config format

Centrifugo also supports TOML format for configuration file:

centrifugo --config=config.toml

Where config.toml contains:

config.toml
allowed_origins: [ "http://localhost:3000" ]
token_hmac_secret_key = "<YOUR-SECRET-STRING-HERE>"
api_key = "<YOUR-API-KEY-HERE>"
log_level = "debug"

In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.

YAML config format

YAML format is also supported:

config.yaml
allowed_origins:
- "http://localhost:3000"
token_hmac_secret_key: "<YOUR-SECRET-STRING-HERE>"
api_key: "<YOUR-API-KEY-HERE>"
log_level: debug

With YAML remember to use spaces, not tabs when writing a configuration file.

Important options

Let's describe some important options you can configure when running Centrifugo.

allowed_origins

This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins.

As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array.

Connection requests without Origin header set are passing through without any checks (i.e. always allowed).

For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way:

"allowed_origins": [
"http://localhost:3000"
]

When connecting from https://example.com:

"allowed_origins": [
"https://example.com"
]

Origin pattern can contain wildcard symbol * to match subdomains:

"allowed_origins": [
"https://*.example.com"
]

– in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check.

It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature):

"allowed_origins": [
"*"
]

address

Bind your Centrifugo to a specific interface address (string, by default "" - listen on all available interfaces).

port

Port to bind Centrifugo to (string, by default "8000").

engine

Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.

Advanced options

These options allow tweaking server behavior, in most cases default values are good to start with.

client_channel_limit

Default: 128

Sets the maximum number of different channel subscriptions a single client can have.

tip

When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.

channel_max_length

Default: 255

Sets the maximum length of the channel name.

client_user_connection_limit

Default: 0

The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited.

The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).

client_queue_max_size

Default: 1048576

Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.

client_anonymous

Default: false

Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID) and only can subscribe to channels with anonymous option enabled.

client_concurrency

Default: 0

client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently.

By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).

gomaxprocs

Default: 0

By default, Centrifugo runs on all available CPU cores. To limit the number of cores Centrifugo can utilize in one moment use this option.

Endpoint configuration.

After Centrifugo started there are several endpoints available.

Default endpoints.

Bidirectional WebSocket default endpoint:

ws://localhost:8000/connection/websocket

Bidirectional SockJS default endpoint (disabled by default):

http://localhost:8000/connection/sockjs

Unidirectional EventSource endpoint (disabled by default):

http://localhost:8000/connection/uni_sse

Unidirectional HTTP streaming endpoint (disabled by default):

http://localhost:8000/connection/uni_http_stream

Unidirectional WebSocket endpoint (disabled by default):

http://localhost:8000/connection/uni_websocket

Unidirectional EventSource (SSE) endpoint (disabled by default):

http://localhost:8000/connection/uni_sse

Server HTTP API endpoint:

http://localhost:8000/api

By default, all endpoints work on port 8000. This can be changed with port option:

{
"port": 9000
}

In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.

Admin endpoints.

Admin web UI endpoint works on root path by default, i.e. http://localhost:8000.

For more details about admin web UI, refer to the Admin web UI documentation.

Debug endpoints.

Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config:

{
...
"debug": true
}

And endpoint:

http://localhost:8000/debug/pprof/

– will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.

Health check endpoint

Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag:

centrifugo -c config.json --health

Custom internal ports

We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal:

  • API endpoint (/api) - for HTTP API requests
  • Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface
  • Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format
  • Health check endpoint (/health) - used to do health checks
  • Debug endpoints (/debug/pprof) - used to inspect internal server state

It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration.

Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work.

To run internal endpoints on custom port use internal_port option:

{
...
"internal_port": 9000
}

So admin web interface will work on address:

http://localhost:9000

Also, debug page will be available on a new custom port too:

http://localhost:9000/debug/pprof/

The same for API and Prometheus endpoints.

Disable default endpoints

To disable websocket endpoint set websocket_disable boolean option to true.

To disable API endpoint set api_disable boolean option to true.

Customize handler endpoints

It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options:

  • admin_handler_prefix (default "") - to control Admin panel URL prefix
  • websocket_handler_prefix (default "/connection/websocket") - to control WebSocket URL prefix
  • sockjs_handler_prefix (default "/connection/sockjs") - to control SockJS URL prefix
  • uni_sse_handler_prefix (default "/connection/uni_sse") - to control unidirectional Eventsource URL prefix
  • uni_http_stream_handler_prefix (default "/connection/uni_http_stream") - to control unidirectional HTTP streaming URL prefix
  • uni_websocket_handler_prefix (default "/connection/uni_websocket") - to control unidirectional WebSocket URL prefix
  • api_handler_prefix (default "/api") - to control HTTP API URL prefix
  • prometheus_handler_prefix (default "/metrics") - to control Prometheus URL prefix
  • health_handler_prefix (default "/health") - to control health check URL prefix

Signal handling

It's possible to send HUP signal to Centrifugo to reload a configuration:

kill -HUP <PID>

Though at moment this will only reload token secrets and channel options (top-level and namespaces).

Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.

Insecure modes

Insecure client connection

The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. This mode can be useful for demo projects based on Centrifugo, local projects, or real-time application prototyping. Don't use it in production.

Insecure API mode

This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules.

The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.

Insecure admin mode

This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).

Setting time duration options

Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use "5s".

The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option).

Valid time units are "ms" (milliseconds), "s" (seconds), "m" (minutes), "h" (hours).

Some examples:

"1000ms" // 1000 milliseconds
"1s" // 1 second
"12h" // 12 hours
"720h" // 30 days

Setting namespaces over env

While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special:

CENTRIFUGO_NAMESPACES='[{"name": "ns1"}, {"name": "ns2"}]' ./centrifugo

I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.

Anonymous usage stats

Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage.

Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal.

To disable sending usage stats set usage_stats_disable option:

config.json
{
"usage_stats_disable": true
}
- + \ No newline at end of file diff --git a/docs/3/server/console_commands.html b/docs/3/server/console_commands.html index 296a6531d..c15fab716 100644 --- a/docs/3/server/console_commands.html +++ b/docs/3/server/console_commands.html @@ -16,13 +16,13 @@ - +

Console commands

Here is a list of console commands that come with Centrifugo executable.

version command

To show Centrifugo version and exit run:

centrifugo version

checkconfig command

Centrifugo has special command to check configuration file checkconfig:

centrifugo checkconfig --config=config.json

If any errors found during validation – program will exit with error message and exit code 1.

genconfig command

Another command is genconfig:

centrifugo genconfig -c config.json

It will automatically generate the minimal required configuration file.

If any errors happen – program will exit with error message and exit code 1.

genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file:

centrifugo genconfig -c config.toml

gentoken command

Another command is gentoken:

centrifugo gentoken -c config.json -u 28282

It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week).

You can change token TTL with -t flag (number of seconds):

centrifugo gentoken -c config.json -u 28282 -t 3600

This way generated token will be valid for 1 hour.

If any errors happen – program will exit with error message and exit code 1.

checktoken command

One more command is checktoken:

centrifugo checktoken -c config.json <TOKEN>

It will validate your connection JWT, so you can test it before using while developing application.

If any errors happen or validation failed – program will exit with error message and exit code 1.

- + \ No newline at end of file diff --git a/docs/3/server/engines.html b/docs/3/server/engines.html index 78dffdd90..dd332248e 100644 --- a/docs/3/server/engines.html +++ b/docs/3/server/engines.html @@ -16,7 +16,7 @@ - + @@ -25,7 +25,7 @@ the same port number (8000 or whatever you want) for all instances.

And finally, let's start the third instance:

centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379

Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes.

To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation.

tip

In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers.

Here is a live example where we locally start two Centrifugo nodes both connected to local Redis:

Redis Sentinel for high availability

Centrifugo supports the official way to add high availability to Redis - Redis Sentinel.

For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name:

  • redis_sentinel_address (string, default "") - comma separated list of Sentinel addresses for HA. At least one known server required.
  • redis_sentinel_master_name (string, default "") - name of Redis master Sentinel monitors

Also:

  • redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1
  • redis_sentinel_user (available since v3.2.0) - optional string user (used only in Redis ACL-based auth).

So you can start Centrifugo which will use Sentinels to discover Redis master instances like this:

centrifugo --config=config.json

Where config.json:

config.json
{
...
"engine": "redis",
"redis_sentinel_address": "127.0.0.1:26379",
"redis_sentinel_master_name": "mymaster"
}

Sentinel configuration files can look like this:

port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 60000

You can find how to properly set up Sentinels in official documentation.

Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.

Haproxy instead of Sentinel configuration

Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case.

For example, you can use something like this in Haproxy config:

listen redis
server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2
server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup
bind *:16379
mode tcp
option tcpka
option tcplog
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
balance roundrobin

And then just point Centrifugo to this Haproxy:

centrifugo --config=config.json --engine=redis --redis_address="localhost:16379"

Redis sharding

Centrifugo has built-in Redis sharding support.

This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale.

At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples.

To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this:

config.json
{
...
"engine": "redis",
"redis_address": [
"127.0.0.1:6379",
"127.0.0.1:6380",
]
}

To start Centrifugo with Redis instances on different hosts:

config.json
{
...
"engine": "redis",
"redis_address": [
"192.168.1.34:6379",
"192.168.1.35:6379",
]
}

If you also need to customize AUTH password, Redis DB number then you can use an extended address notation.

note

Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers.

When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).

Redis cluster

Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example:

{
...
"redis_cluster_address": [
"localhost:30001,localhost:30002,localhost:30003"
]
}

You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start.

To set the same over environment variable:

CENTRIFUGO_REDIS_CLUSTER_ADDRESS="localhost:30001" CENTRIFUGO_ENGINE=redis ./centrifugo

If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array:

{
...
"redis_cluster_address": [
"localhost:30001,localhost:30002,localhost:30003",
"localhost:30101,localhost:30102,localhost:30103"
]
}

Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump).

To reproduce the same over environment variable use space to separate different clusters:

CENTRIFUGO_REDIS_CLUSTER_ADDRESS="localhost:30001,localhost:30002 localhost:30101,localhost:30102" CENTRIFUGO_ENGINE=redis ./centrifugo

KeyDB Engine

EXPERIMENTAL

Centrifugo Redis engine seamlessly works with KeyDB. KeyDB server is compatible with Redis and provides several additional features beyond.

caution

We can't give any promises about compatibility with KeyDB in the future Centrifugo releases - while KeyDB is fully compatible with Redis things should work just fine. That's why we consider this as EXPERIMENTAL feature.

Use KeyDB instead of Redis only if you are sure you need it. Nothing stops you from running several Redis instances per each core you have, configure sharding, and obtain even better performance than KeyDB can provide (due to lack of synchronization between threads in Redis).

To run Centrifugo with KeyDB all you need to do is use redis engine but run the KeyDB server instead of Redis.

Tarantool engine

EXPERIMENTAL

Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics.

caution

EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves.

There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge.

info

To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together.

There are several supported Tarantool topologies to which Centrifugo can connect:

  • One standalone Tarantool instance
  • Many standalone Tarantool instances and consistently shard data between them
  • Tarantool running in Cartridge
  • Tarantool with replica and automatic failover in Cartridge
  • Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them
  • Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7)

After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes):

config.json
{
...
"engine": "tarantool",
"tarantool_address": "127.0.0.1:3301"
}

See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework.

See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.

Tarantool engine options

tarantool_address

String or array of strings. Default tcp://127.0.0.1:3301.

Connection address to Tarantool.

tarantool_mode

String, default standalone

A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication).

All modes support client-side consistent sharding (similar to what Redis engine provides).

tarantool_user

String, default "". Allows setting a user.

tarantool_password

String, default "". Allows setting a password.

history_meta_ttl

Duration, default 0s.

Same option as for Memory engine and Redis engine also applies to Tarantool case.

Nats broker

It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below.

Limitations:

  • Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available.
  • Nats wildcard channel subscriptions with symbols * and > not supported.

First start Nats server:

$ nats-server
[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7
[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set]
[3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222
[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4
[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready

Then start Centrifugo with broker option:

centrifugo --broker=nats --config=config.json

And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine):

centrifugo --broker=nats --config=config.json --port=8001

Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.

Options

nats_url

String, default nats://127.0.0.1:4222.

Connection url in format nats://derek:pass@localhost:4222.

nats_prefix

String, default centrifugo.

Prefix for channels used by Centrifugo inside Nats.

nats_dial_timeout

Duration, default 1s.

Timeout for dialing with Nats.

nats_write_timeout

Duration, default 1s.

Write (and flush) timeout for a connection to Nats.

- + \ No newline at end of file diff --git a/docs/3/server/history_and_recovery.html b/docs/3/server/history_and_recovery.html index 929b39510..e3025c28c 100644 --- a/docs/3/server/history_and_recovery.html +++ b/docs/3/server/history_and_recovery.html @@ -16,13 +16,13 @@ - +

History and recovery

Centrifugo engines can maintain publication history for channels with configured history size and TTL.

History design

History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero.

Centrifugo is not designed to keep publications streams forever. Streams are ephemeral and can expire or can be lost at any moment. But Centrifugo provides a way for an application or a client to understand that stream history lost. In this case, the main application database should be the source of truth and state recovery.

When history is on every publication is published into a channel saved into history. Depending on the engine used history stream implementation can differ. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside spaces.

Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of truth anymore.

tip

History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.

History iteration API

History iteration based on three fields:

  • limit
  • since
  • reverse

Combining these fields you can iterate over a stream in both directions.

Get current stream top offset and epoch:

history(limit: 0, since: null, reverse: false)

Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default):

history(limit: -1, since: null, reverse: false)

Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default):

history(limit: -1, since: null, reverse: true)

Get history from the current beginning (up to 10):

history(limit: 10, since: null, reverse: false)

Get history from the current end in reversed direction (up to 10):

history(limit: 10, since: null, reverse: true) 

Get up to 10 publications since known stream position (described by offset and epoch values):

history(limit: 10, since: {offset: 0, epoch: "epoch"}, reverse: false)

Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest):

history(limit: 10, since: {offset: 11, epoch: "epoch"}, reverse: true)

Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun):

// Iterate by 10.
limit := 10
// Paginate in reversed order first, then invert it.
reverse := true
// Start with nil StreamPosition, then fill it with value while paginating.
var sp *gocent.StreamPosition

for {
historyResult, err = c.History(
ctx,
channel,
gocent.WithLimit(limit),
gocent.WithReverse(reverse),
gocent.WithSince(sp),
)
if err != nil {
log.Fatalf("Error calling history: %v", err)
}
for _, pub := range historyResult.Publications {
log.Println(pub.Offset, "=>", string(pub.Data))
sp = &gocent.StreamPosition{
Offset: pub.Offset,
Epoch: historyResult.Epoch,
}
}
if len(historyResult.Publications) < limit {
// Got all pubs, invert pagination direction.
reverse = !reverse
log.Println("end of stream reached, change iteration direction")
}
}

Automatic message recovery

One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while.

In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnecting at the same time).

danger

Message recovery protocol feature designed to be used together with reasonably small Publication stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time.

To enable recovery mechanism for channels set the recover boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on.

When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not.

The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default.

Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client libraries (for bidirectional transport), but it's good to know how recovery works under the hood.

The recovery feature heavily relies on offset and epoch values described above.

epoch handles cases when history storage has been restarted while the client was in a disconnected state so publication numeration in a channel started from scratch. For example at the moment Memory engine does not persist publication sequences on disk so every restart will start numeration from scratch. After each restart a new epoch field is generated, and we can understand in the recovery process that the client could miss messages thus returning the correct recovered flag in a subscribe Reply. This also applies to the Redis engine – if you do not use AOF with fsync then sequences can be lost after Redis restart. When using the Redis engine you need to use a fully in-memory model strategy or AOF with fsync to guarantee the reliability of the recovered flag sent by Centrifugo.

When a server receives subscribe command with the boolean flag recover set to true and offset, epoch set to values last seen by a client (see SubscribeRequest type in protocol definitions) it will try to find all missed publications from history cache. Recovered publications will be passed to the client in a subscribe Reply in the correct order, so your publication handler will be automatically called to process each missed message.

You can also manually implement your recovery algorithm on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client reconnects completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.

- + \ No newline at end of file diff --git a/docs/3/server/infra_tuning.html b/docs/3/server/infra_tuning.html index e9fb8ccd6..3026cebc0 100644 --- a/docs/3/server/infra_tuning.html +++ b/docs/3/server/infra_tuning.html @@ -16,14 +16,14 @@ - +

Infrastructure tuning

As Centrifugo deals with lots of persistent connections your operating system and server infrastructure must be ready for it.

Open files limit

You should increase a max number of open files Centrifugo process can open if you want to handle more connections.

To get the current open files limit run:

ulimit -n

On Linux you can check limits for a running process using:

cat /proc/<PID>/limits

The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default.

See this document to get more info on how to increase this number.

If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536.

You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).

Ephemeral port exhaustion

Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing.

The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple:

source ip | source port | destination ip | destination port

On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default).

In order to eliminate a problem you can:

  • Increase the ephemeral port range by tuning ip_local_port_range option
  • Deploy more Centrifugo server instances to load balance across
  • Deploy more load balancer instances
  • Use virtual network interfaces

See a post in Pusher blog about this problem and more detailed solution steps.

Sockets in TIME_WAIT state

On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state.

Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side.

Look how many socket descriptors in TIME_WAIT state.

netstat -an |grep TIME_WAIT | grep <CENTRIFUGO_PID> | wc -l

Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html

The advices here are similar to ephemeral port exhaustion problem:

  • Increase the ephemeral port range by tuning ip_local_port_range option
  • Deploy more Centrifugo server instances to load balance across
  • Deploy more load balancer instances
  • Use virtual network interfaces

Proxy max connections

Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established.

Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.

Conntrack table

More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.

- + \ No newline at end of file diff --git a/docs/3/server/load_balancing.html b/docs/3/server/load_balancing.html index d127707dc..b7688e4b6 100644 --- a/docs/3/server/load_balancing.html +++ b/docs/3/server/load_balancing.html @@ -16,7 +16,7 @@ - + @@ -28,7 +28,7 @@ can be such a balancer too.

In this section we will look at Nginx configuration to deploy Centrifugo.

Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections.

There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).

Separate domain for Centrifugo

upstream centrifugo {
# uncomment ip_hash if using SockJS transport with many upstream servers.
#ip_hash;
server 127.0.0.1:8000;
}

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

#server {
# listen 80;
# server_name centrifugo.example.com;
# rewrite ^(.*) https://$server_name$1 permanent;
#}

server {

server_name centrifugo.example.com;

listen 80;

#listen 443 ssl;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_certificate /etc/nginx/ssl/server.crt;
#ssl_certificate_key /etc/nginx/ssl/server.key;

include /etc/nginx/mime.types;
default_type application/octet-stream;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1000;
gzip_proxied any;

# Only retry if there was a communication error, not a timeout
# on the Centrifugo server (to avoid propagating "queries of death"
# to all frontends)
proxy_next_upstream error;

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;

location /connection {
proxy_pass http://centrifugo;
proxy_buffering off;
keepalive_timeout 65;
proxy_read_timeout 60s;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}

location / {
proxy_pass http://centrifugo;
}

error_page 500 502 503 504 /50x.html;

location = /50x.html {
root /usr/share/nginx/html;
}
}

Embed to a location of web site

upstream centrifugo {
# uncomment ip_hash if using SockJS transport with many upstream servers.
#ip_hash;
server 127.0.0.1:8000;
}

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {

# ... your web site Nginx config

location /centrifugo/ {
rewrite ^/centrifugo/(.*) /$1 break;
proxy_pass http://centrifugo;
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
}

location /centrifugo/connection {
rewrite ^/centrifugo(.*) $1 break;
proxy_pass http://centrifugo;
proxy_buffering off;
keepalive_timeout 65;
proxy_read_timeout 60s;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}

worker_connections

You may also need to update worker_connections option of Nginx:

events {
worker_connections 65535;
}
- + \ No newline at end of file diff --git a/docs/3/server/monitoring.html b/docs/3/server/monitoring.html index 5c29eed3c..080e02031 100644 --- a/docs/3/server/monitoring.html +++ b/docs/3/server/monitoring.html @@ -16,13 +16,13 @@ - +

Monitoring

Centrifugo supports reporting metrics in Prometheus format and can automatically export metrics to Graphite.

Prometheus

To enable Prometheus endpoint start Centrifugo with prometheus option on:

config.json
{
...
"prometheus": true
}

This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.

Graphite

To enable automatic export to Graphite (via TCP):

config.json
{
...
"graphite": true,
"graphite_host": "localhost",
"graphite_port": 2003
}

By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection.

If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).

Grafana dashboard

Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.

- + \ No newline at end of file diff --git a/docs/3/server/private_channels.html b/docs/3/server/private_channels.html index 6704f8e6d..3a1618c94 100644 --- a/docs/3/server/private_channels.html +++ b/docs/3/server/private_channels.html @@ -16,13 +16,13 @@ - +

Private channels

In the channels chapter we mentioned private channels. This chapter has more information about the private channel mechanism in Centrifugo.

All channels starting with $ are considered private. Your backend should additionally provide a token for every subscription request to a private channel. This way you can control subscription permissions and only allow certain users to subscribe to a channel.

The way how this token is obtained varies depending on a client connector implementation.

For example, in Javascript client, AJAX POST request is automatically sent to /centrifuge/subscribe endpoint on every private channel subscription attempt. Other client libraries provide a hook for your custom code that will obtain a private channel subscription token from the application backend (so you need manually implement HTTP call to your backend for example).

Private channel subscription token is also JWT (like connection JWT described in authentication chapter). But it has its specific claims.

tip

Connection token and private channel subscription token are both JWT and both can be generated with any JWT library.

note

Even when authorizing a subscription to a private channel with a private subscription JWT you should set a proper connection JWT for a client as it provides user authentication details to Centrifugo.

tip

When you need to use namespace for a private channel then the name of a namespace should be written after a $ symbol, i.e. if you have a namespace name chat – then the private channel which belongs to that namespace must be written as sth like $chat:stream.

Supported JWT algorithms for private subscription tokens match algorithms to create connection JWT. The same HMAC secret key, RSA, and ECDSA public keys set for authentication tokens are re-used to check subscription JWT.

Claims

Private channel subscription token claims are: client, channel, info, b64info, exp and expire_at. What do they mean? Let's describe it in detail.

client

Required. Client ID which wants to subscribe on a channel (string).

note

Centrifugo server generates a unique client ID for each incoming connection. This client ID regenerated for a client on every reconnect. You must use this client ID for a private channel subscription token. If you are using centrifuge-js library then Client ID and channels that the user wants to subscribe will be automatically added to AJAX POST request to application backend. In other cases refer to specific client documentation (in most cases you will have client ID and channel in private subscription event context).

channel

Required. Channel that client tries to subscribe to (string).

info

Optional. Additional information for connection inside this channel (valid JSON).

b64info

Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.

exp

Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time.

At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs.

Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.

expire_at

Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though).

expire_at is a UNIX timestamp seconds when the subscription should expire.

  • Set it to the future time for expiring subscription at some point
  • Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.

aud

Handled since Centrifugo v3.2.0

By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.

iss

Handled since Centrifugo v3.2.0

By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.

Example

So to generate a subscription token you can use something like this in Python (assuming client ID is xxxx-xxx-xxx-xxxx and the private channel is $gossips):

import jwt

token = jwt.encode({
"client": "xxxx-xxx-xxx-xxxx",
"channel": "$gossips"
}, "secret", algorithm="HS256").decode()

print(token)

Where "secret" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).

- + \ No newline at end of file diff --git a/docs/3/server/proxy.html b/docs/3/server/proxy.html index 7bad144b8..f4221400f 100644 --- a/docs/3/server/proxy.html +++ b/docs/3/server/proxy.html @@ -16,13 +16,13 @@ - +

Proxy to backend

It's possible to proxy some client connection events from Centrifugo to the application backend and react to them in a custom way. For example, it's possible to authenticate connection via request from Centrifugo to application backend, refresh client sessions and answer to RPC calls sent by a client over bidirectional connection.

The list of events that can be proxied:

  • Connect – called when a client connects to Centrifugo, so it's possible to authenticate user, return custom data to a client, subscribe connection to several channels, attach meta information to the connection, and so on. Works for bidirectional and unidirectional transports.
  • Refresh - called when a client session is going to expire, so it's possible to prolong it or just let it expire. Can also be used just as a periodical connection liveness callback from Centrifugo to app backend. Works for bidirectional and unidirectional transports.
  • Subscribe - called when clients try to subscribe on a channel, so it's possible to check permissions and return custom initial subscription data. Works for bidirectional transports only.
  • Publish - called when a client tries to publish into a channel, so it's possible to check permissions and optionally modify publication data. Works for bidirectional transports only.
  • RPC - called when a client sends RPC, you can do whatever logic you need based on a client-provided RPC method and params. Works for bidirectional transports only.

At the moment Centrifugo can proxy these events over two protocols:

  • HTTP (JSON payloads)
  • GRPC (Protobuf messages)

HTTP proxy

HTTP proxy in Centrifugo converts client connection events into HTTP call to the application backend.

HTTP request structure

All proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).

Proxy HTTP headers

The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example:

config.json
{
...
"proxy_http_headers": [
"Origin",
"User-Agent",
"Cookie",
"Authorization",
"X-Real-Ip",
"X-Forwarded-For",
"X-Request-Id"
]
}

Alternatively, you can set a list of headers via an environment variable (space separated):

export CENTRIFUGO_PROXY_HTTP_HEADERS="Cookie User-Agent X-B3-TraceId X-B3-SpanId" ./centrifugo
note

Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend.

Proxy GRPC metadata

When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied.

See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.

Connect proxy

With the following options in the configuration file:

{
...
"proxy_connect_endpoint": "http://localhost:3000/centrifugo/connect",
"proxy_connect_timeout": "1s"
}

– connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request.

danger

Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections.

Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers.

tip

If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server.

This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired).

Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json"
}

Expected response example:

{"result": {"user": "56"}}

This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result.

Several app examples which use connect proxy can be found in our blog:

Connect request fields

This is what sent from Centrifugo to application backend in case of connect proxy request.

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
namestringyesoptional name of the client (this field will only be set if provided by a client on connect)
versionstringyesoptional version of the client (this field will only be set if provided by a client on connect)
dataJSONyesoptional data from client (this field will only be set if provided by a client on connect)
b64datastringyesoptional data from the client in base64 format (if the binary proxy mode is used)
channelsArray of stringsyeslist of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result

Connect result fields

This is what application returns to Centrifugo inside result field in case of connect proxy request.

FieldTypeOptionalDescription
userstringnouser ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests
expire_atintegeryesa timestamp when connection must be considered expired. If not set or set to 0 connection won't expire at all
infoJSONyesa connection info JSON
b64infostringyesbinary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages
dataJSONyesa custom data to send to the client in connect command response.
b64datastringyesa custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client
channelsarray of stringsyesallows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions
subsmap of SubscribeOptionsyesmap of channels with options to subscribe connection to. See more details about server-side subscriptions
metaJSON object (ex. {"key": "value"})yesa custom data to attach to connection (this won't be exposed to client-side)

Options

proxy_connect_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

Example

Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return "56" as user ID):

class CentrifugoConnectHandler(tornado.web.RequestHandler):

def check_xsrf_cookie(self):
pass

def post(self):
self.set_header('Content-Type', 'application/json; charset="utf-8"')
data = json.dumps({
'result': {
'user': '56'
}
})
self.write(data)


def main():
options.parse_command_line()
app = tornado.web.Application([
(r'/centrifugo/connect', CentrifugoConnectHandler),
])
app.listen(3000)
tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
main()

This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side.

We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea.

Refresh proxy

With the following options in the configuration file:

{
...
"proxy_refresh_endpoint": "http://localhost:3000/centrifugo/refresh",
"proxy_refresh_timeout": "1s"
}

– Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side.

The payload sent to app backend in refresh request (when the connection is going to expire):

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56"
}

Expected response example:

{"result": {"expire_at": 1565436268}}

Refresh request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc.)
protocolstringnoprotocol type used by client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Refresh result fields

FieldTypeOptionalDescription
expiredboolyesa flag to mark the connection as expired - the client will be disconnected
expire_atintegeryesa timestamp in the future when connection must be considered expired
infoJSONyesa connection info JSON
b64infostringyesbinary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages

Options

proxy_refresh_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

RPC proxy

With the following option in the configuration file:

{
...
"proxy_rpc_endpoint": "http://localhost:3000/centrifugo/connect",
"proxy_rpc_timeout": "1s"
}

RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket (or SockJS) connection in a bidirectional way.

Payload example sent to app backend in RPC request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"method": "getCurrentPrice",
"data":{"params": {"object_id": 12}}
}

Expected response example:

{"result": {"data": {"answer": "2019"}}}

RPC request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket or sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
methodstringyesan RPC method string, if the client does not use named RPC call then method will be omitted
dataJSONyesRPC custom data sent by client
b64datastringyeswill be set instead of data field for binary proxy mode
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

RPC result fields

FieldTypeOptionalDescription
dataJSONyesRPC response - any valid JSON is supported
b64datastringyescan be set instead of data for binary response encoded in base64 format

Options

proxy_rpc_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

See below on how to return a custom error.

Subscribe proxy

With the following option in the configuration file:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s"
}

– subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel.

tip

Subscribe proxy does not proxy private and user-limited channels at the moment. That's because those are already providing a level of security (user-limited channels check current user ID, private channels require subscription token). In some cases you may use subscribe proxy as a replacement for private channels actually: if you prefer to check permissions using the proxy to backend mechanism – just stop using $ prefixes in channels, properly configure subscribe proxy and validate subscriptions upon proxy from Centrifugo to your backend (issued each time user tries to subscribe on a channel for which subscribe proxy enabled).

Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace.

So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s",
"proxy_subscribe": true
}

Or for channels in namespace sun:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s",
"namespaces": [{
"name": "sun",
"proxy_subscribe": true
}]
}

Payload example sent to app backend in subscribe request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "chat:index"
}

Expected response example if subscription is allowed:

{"result": {}}

Subscribe request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket or sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnoa string channel client wants to subscribe to
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)
dataJSONyescustom data from client sent with subscription request (this field will only be set if provided by a client on subscribe). Available since Centrifugo v3.1.1
b64datastringyesoptional subscription data from the client in base64 format (if the binary proxy mode is used). Available since Centrifugo v3.1.1

Subscribe result fields

FieldTypeOptionalDescription
infoJSONyesa channel info JSON
b64infostringyesa binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using
dataJSONyesa custom data to send to the client in subscribe command reply.
b64datastringyesa custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client
overrideOverride objectyesAllows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
positionBoolValueyesOverride position
recoverBoolValueyesOverride recover

BoolValue is an object like this:

{
"value": true/false
}

See below on how to return an error in case you don't want to allow subscribing.

Options

proxy_subscribe_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

Publish proxy

With the following option in the configuration file:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s"
}

– publish calls sent by a client will be proxied to proxy_publish_endpoint.

This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side.

Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too.

So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s",
"publish": true,
"proxy_publish": true
}

Or for channels in namespace sun:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s",
"namespaces": [{
"name": "sun",
"publish": true,
"proxy_publish": true
}]
}

Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace).

Payload example sent to app backend in a publish request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "chat:index",
"data":{"input":"hello"}
}

Expected response example if publish is allowed:

{"result": {}}

Publish request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnoa string channel client wants to publish to
dataJSONyesdata sent by client
b64datastringyeswill be set instead of data field for binary proxy mode
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Publish result fields

FieldTypeOptionalDescription
dataJSONyesan optional JSON data to send into a channel instead of original data sent by a client
b64datastringyesa binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing
skip_historyboolyeswhen set to true Centrifugo won't save publication to the channel history

See below on how to return an error in case you don't want to allow publishing.

Options

proxy_publish_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.

Return custom error

Application backend can return JSON object that contains an error to return it to the client:

{
"error": {
"code": 1000,
"message": "custom error"
}
}

Application should use error codes >= 1000, error codes in the range 0-999 are reserved by Centrifugo internal protocol. Error code field is uint32 internally.

note

Returning custom error does not apply to response on refresh request as there is no sense in returning an error (will not reach client anyway).

Return custom disconnect

Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way:

{
"disconnect": {
"code": 4000,
"reconnect": false,
"reason": "custom disconnect"
}
}

Application must use numbers in the range 4000-4999 for custom disconnect codes. Code is uint32 internally. Numbers below 4000 are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 symbols.

note

Returning custom disconnect does not apply to response on refresh request as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason.

GRPC proxy

Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server.

GRPC service definitions can be found in the Centrifugo repository: proxy.proto.

tip

GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions.

Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept).

All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy:

config.json
{
...
"proxy_connect_endpoint": "grpc://localhost:12000",
"proxy_connect_timeout": "1s"
}

Refresh proxy:

config.json
{
...
"proxy_refresh_endpoint": "grpc://localhost:12000",
"proxy_refresh_timeout": "1s"
}

Or for RPC proxy:

config.json
{
...
"proxy_rpc_endpoint": "grpc://localhost:12000",
"proxy_rpc_timeout": "1s"
}

For publish proxy in namespace chat:

config.json
{
...
"proxy_publish_endpoint": "grpc://localhost:12000",
"proxy_publish_timeout": "1s"
"namespaces": [
{
"name": "chat",
"publish": true,
"proxy_publish": true
}
]
}

Use subscribe proxy for all channels without namespaces:

config.json
{
...
"proxy_subscribe_endpoint": "grpc://localhost:12000",
"proxy_subscribe_timeout": "1s",
"proxy_subscribe": true
}

So the same as for HTTP, just the different endpoint scheme.

GRPC proxy options

Some additional options exist to control GRPC proxy behavior.

proxy_grpc_cert_file

String, default: "".

Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used.

proxy_grpc_credentials_key

String, default "" (i.e. not used).

Add custom key to per-RPC credentials.

proxy_grpc_credentials_value

String, default "" (i.e. not used).

A custom value for proxy_grpc_credentials_key.

GRPC proxy example

We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.:

  1. Copy proxy Protobuf definitions
  2. Generate GRPC code
  3. Run backend service with you custom business logic
  4. Point Centrifugo to it.

Header proxy rules

Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios:

Client protocol typeProxy typeClient headersClient metadata
HTTPHTTPIn proxy request headersN/A
GRPCGRPCN/AIn proxy request metadata
HTTPGRPCIn proxy request metadataN/A
GRPCHTTPN/AIn proxy request headers

Binary mode

As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding.

Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this.

Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side).

But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON.

In this case it's possible to turn Centrifugo proxy into binary mode by using:

config.json
{
...
"proxy_binary_encoding": true
}

Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues.

While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine.

Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.

Granular proxy mode

New in Centrifugo v3.1.0.

By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture).

Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.

Enable granular proxy mode

Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below.

config.json
{
...
"granular_proxy_mode": true
}

Defining a list of proxies

When using granular proxy mode on configuration top level you can define "proxies" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint.

Here is an example:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [
{
"name": "connect",
"endpoint": "http://localhost:3000/centrifugo/connect",
"timeout": "500ms",
"http_headers": ["Cookie"]
},
{
"name": "refresh",
"endpoint": "http://localhost:3000/centrifugo/refresh",
"timeout": "500ms"
},
{
"name": "subscribe1",
"endpoint": "http://localhost:3001/centrifugo/subscribe"
},
{
"name": "publish1",
"endpoint": "http://localhost:3001/centrifugo/publish"
},
{
"name": "rpc1",
"endpoint": "http://localhost:3001/centrifugo/rpc"
},
{
"name": "subscribe2",
"endpoint": "http://localhost:3002/centrifugo/subscribe"
},
{
"name": "publish2",
"endpoint": "grpc://localhost:3002"
}
{
"name": "rpc2",
"endpoint": "grpc://localhost:3002"
}
]
}

Let's look at all fields for a proxy object which is possible to set for each proxy inside "proxies" array.

Field nameField typeRequiredDescription
namestringyesUnique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$
endpointstringyesHTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC.
timeoutduration (string)noProxy request timeout, default "1s"
http_headersarray of stringsnoList of headers to proxy, by default no headers
grpc_metadataarray of stringsnoList of GRPC metadata keys to proxy, by default no metadata keys
binary_encodingboolnoUse base64 for payloads
include_connection_metaboolnoInclude meta information (attached on connect)
grpc_cert_filestringnoPath to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used.
grpc_credentials_keystringnoAdd custom key to per-RPC credentials.
grpc_credentials_valuestringnoA custom value for grpc_credentials_key.

Granular connect and refresh

As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event.

To enable connect proxy:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"connect_proxy_name": "connect"
}

We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea.

Let's also add refresh proxy:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"connect_proxy_name": "connect",
"refresh_proxy_name": "refresh"
}

Granular subscribe and publish

Subscribe and publish proxy work per-namespace. This means that subscribe_proxy_name and publish_proxy_name are just a channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object.

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"namespaces": [
{
"name": "ns1",
"subscribe_proxy_name": "subscribe1",
"publish": true,
"publish_proxy_name": "publish1"
},
{
"name": "ns2",
"subscribe_proxy_name": "subscribe2",
"publish": true,
"publish_proxy_name": "publish2"
}
]
}

If namespace does not have "subscribe_proxy_name" or "subscribe_proxy_name" is empty then no subscribe proxy will be used for a namespace.

If namespace does not have "publish_proxy_name" or "publish_proxy_name" is empty then no publish proxy will be used for a namespace.

tip

You can define subscribe_proxy_name and publish_proxy_name on configuration top level – and in this case publish and subscribe requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.

Granular RPC

Analogous to channel namespaces it's possible to configure rpc namespaces:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"namespaces": [...],
"rpc_namespaces": [
{
"name": "rpc_ns1",
"rpc_proxy_name": "rpc1",
},
{
"name": "rpc_ns2",
"rpc_proxy_name": "rpc2"
}
]
}

The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces).

Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start.

tip

The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.

- + \ No newline at end of file diff --git a/docs/3/server/server_api.html b/docs/3/server/server_api.html index 8f1dd69c8..1f6c0d839 100644 --- a/docs/3/server/server_api.html +++ b/docs/3/server/server_api.html @@ -16,13 +16,13 @@ - +

Server API

Server API is a way to send various commands to Centrifugo. For example, server API allows publishing messages to channels, get server statistics, etc. There are two kinds of API available at the moment:

  • HTTP API
  • GRPC API

HTTP API

Server HTTP API works on /api endpoint (by default). It has a simple request format: this is an HTTP POST request with application/json Content-Type and with JSON command body.

Here we will look at available methods and parameters

tip

In some cases, you can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests.

HTTP API authorization

HTTP API protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like:

config.json
{
...
"api_key": "<YOUR API KEY>"
}

This API key must be set in the request Authorization header in this way:

Authorization: apikey <KEY>

It's also possible to pass API key over URL query param. This solves some edge cases where it's not possible to use the Authorization header. Simply add ?api_key=<YOUR API KEY> query param to the API endpoint. Keep in mind that passing the API key in the Authorization header is a recommended way.

It's possible to disable API key check on Centrifugo side using the api_insecure configuration option. Be sure to protect the API endpoint by firewall rules, in this case, to prevent anyone on the internet to send commands over your unprotected Centrifugo API endpoint. API key auth is not very safe for man-in-the-middle so we also recommended running Centrifugo with TLS.

A command is a JSON object with two properties: method and params.

  • method is the name of the API command you want to call.
  • params is an object with command arguments. Each method can have its own params

Before looking at all available commands here is a CURL that calls info command:

curl --header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "info", "params": {}}' \
http://localhost:8000/api

Here is a live example:

Now let's investigate each API method in detail.

publish

Publish command allows publishing data into a channel. Most probably this is a command you'll use most.

It looks like this:

{
"method": "publish",
"params": {
"channel": "chat",
"data": {
"text": "hello"
}
}
}

Let's apply all information said above and send publish command to Centrifugo. We will send a request using the requests library for Python.

import json
import requests

command = {
"method": "publish",
"params": {
"channel": "docs",
"data": {
"content": "1"
}
}
}

api_key = "YOUR_API_KEY"
data = json.dumps(command)
headers = {'Content-type': 'application/json', 'Authorization': 'apikey ' + api_key}
resp = requests.post("https://centrifuge.example.com/api", data=data, headers=headers)
print(resp.json())

The same using httpie console tool:

echo '{"method": "publish", "params": {"channel": "chat", "data": {"text": "hello"}}}' | http "localhost:8000/api" Authorization:"apikey <YOUR_API_KEY>" -vvv
POST /api HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Authorization: apikey KEY
Connection: keep-alive
Content-Length: 80
Content-Type: application/json
Host: localhost:8000
User-Agent: HTTPie/0.9.8

{
"method": "publish",
"params": {
"channel": "chat",
"data": {
"text": "hello"
}
}
}

HTTP/1.1 200 OK
Content-Length: 3
Content-Type: application/json
Date: Thu, 17 May 2018 22:01:42 GMT

{
"result": {}
}

In case of error response object can contain error field (here we artificially publishing to a channel with unknown namespace):

echo '{"method": "publish", "params": {"channel": "unknown:chat", "data": {"text": "hello"}}}' | http "localhost:8000/api" Authorization:"apikey <YOUR_API_KEY>"
HTTP/1.1 200 OK
Content-Length: 55
Content-Type: application/json
Date: Thu, 17 May 2018 22:03:09 GMT

{
"error": {
"code": 102,
"message": "namespace not found"
}
}

error object contains error code and message - this is also the same for other commands described below.

Publish params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to publish
dataany JSONyesCustom JSON data to publish into a channel
skip_historyboolnoSkip adding publication to history for this request
tagsmap[string]stringnoPublication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients (available since v3.2.0)

Publish result

Field nameField typeOptionalDescription
offsetintegeryesOffset of publication in history stream
epochstringyesEpoch of current stream

broadcast

Similar to publish but allows to send the same data into many channels.

{
"method": "broadcast",
"params": {
"channels": ["CHANNEL_1", "CHANNEL_2"],
"data": {
"text": "hello"
}
}
}

Broadcast params

Parameter nameParameter typeRequiredDescription
channelsArray of stringsyesList of channels to publish data to
dataany JSONyesCustom JSON data to publish into each channel
skip_historyboolnoSkip adding publications to channels' history for this request
tagsmap[string]stringnoPublication tags (available since v3.2.0) - map with arbitrary string keys and values which is attached to publication and will be delivered to clients

Broadcast result

Field nameField typeOptionalDescription
responsesArray of publish responsesnoResponses for each individual publish (with possible error and publish result)

subscribe

subscribe allows subscribing user to a channel.

Subscribe params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to subscribe
channelstringyesName of channel to subscribe user to
infoany JSONnoAttach custom data to subscription (will be used in presence and join/leave messages)
b64infostringnoinfo in base64 for binary mode (will be decoded by Centrifugo)
clientstringnoSpecific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs)
sessionstringnoSpecific client session to subscribe (user still required to be set). Available since Centrifugo v3.2.0
dataany JSONnoCustom subscription data (will be sent to client in Subscribe push)
b64datastringnoSame as data but in base64 format (will be decoded by Centrifugo)
recover_sinceStreamPosition objectnoStream position to recover from
overrideOverride objectnoAllows dynamically override some channel options defined in Centrifugo configuration (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
positionBoolValueyesOverride position
recoverBoolValueyesOverride recover

BoolValue is an object like this:

{
"value": true/false
}

Subscribe result

Empty object at the moment.

unsubscribe

unsubscribe allows unsubscribing user from a channel.

{
"method": "unsubscribe",
"params": {
"channel": "CHANNEL NAME",
"user": "USER ID"
}
}

Unsubscribe params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to unsubscribe
channelstringyesName of channel to unsubscribe user to
clientstringnoSpecific client ID to unsubscribe (user still required to be set)
sessionstringnoSpecific client session to disconnect (user still required to be set). Available since Centrifugo v3.2.0

Unsubscribe result

Empty object at the moment.

disconnect

disconnect allows disconnecting a user by ID.

{
"method": "disconnect",
"params": {
"user": "USER ID"
}
}

Disconnect params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to disconnect
clientstringnoSpecific client ID to disconnect (user still required to be set)
sessionstringnoSpecific client session to disconnect (user still required to be set). Available since Centrifugo v3.2.0
whitelistArray of stringsnoArray of client IDs to keep
disconnectDisconnect objectnoProvide custom disconnect object, see below

Disconnect object

Field nameField typeRequiredDescription
codeintyesDisconnect code
reasonstringyesDisconnect reason
reconnectboolnoReconnect advice

Disconnect result

Empty object at the moment.

refresh

refresh allows refreshing user connection (mostly useful when unidirectional transports are used).

Refresh params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to refresh
clientstringnoClient ID to refresh (user still required to be set)
sessionstringnoSpecific client session to refresh (user still required to be set). Available since Centrifugo v3.2.0
expiredboolnoMark connection as expired and close with Disconnect Expired reason
expire_atintnoUnix time (in seconds) in the future when the connection will expire

Refresh result

Empty object at the moment.

presence

presence allows getting channel online presence information (all clients currently subscribed on this channel).

tip

Presence in channels is not enabled by default. See how to enable it over channel options.

{
"method": "presence",
"params": {
"channel": "chat"
}
}

Example:

fz@centrifugo: echo '{"method": "presence", "params": {"channel": "chat"}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 127
Content-Type: application/json
Date: Thu, 17 May 2018 22:13:17 GMT

{
"result": {
"presence": {
"c54313b2-0442-499a-a70c-051f8588020f": {
"client": "c54313b2-0442-499a-a70c-051f8588020f",
"user": "42"
},
"adad13b1-0442-499a-a70c-051f858802da": {
"client": "adad13b1-0442-499a-a70c-051f858802da",
"user": "42"
}
}
}
}

Presence params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to call presence from

Presence result

Field nameField typeOptionalDescription
presenceMap of client ID (string) to ClientInfo objectnoOffset of publication in history stream

ClientInfo

Field nameField typeOptionalDescription
clientstringnoClient ID
userstringnoUser ID
conn_infoJSONyesOptional connection info
chan_infoJSONyesOptional channel info

presence_stats

presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID).

{
"method": "presence_stats",
"params": {
"channel": "chat"
}
}

Example:

echo '{"method": "presence_stats", "params": {"channel": "public:chat"}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 43
Content-Type: application/json
Date: Thu, 17 May 2018 22:09:44 GMT

{
"result": {
"num_clients": 0,
"num_users": 0
}
}

Presence stats params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to call presence from

Presence stats result

Field nameField typeOptionalDescription
num_clientsintegernoTotal number of clients in channel
num_usersintegernoTotal number of unique users in channel

history

history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter.

tip

History in channels is not enabled by default. See how to enable it over channel options.

{
"method": "history",
"params": {
"channel": "chat",
"limit": 2
}
}

Example:

echo '{"method": "history", "params": {"channel": "chat", "limit": 2}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 129
Content-Type: application/json
Date: Wed, 21 Jul 2021 05:30:48 GMT

{
"result": {
"epoch": "qFhv",
"offset": 4,
"publications": [
{
"data": {
"text": "hello"
},
"offset": 2
},
{
"data": {
"text": "hello"
},
"offset": 3
}
]
}
}

History params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to call history from
limitintnoLimit number of returned publications, if not set in request then only current stream position information will present in result (without any publications)
sinceStreamPosition objectnoTo return publications after this position
reverseboolnoIterate in reversed order (from latest to earliest)

StreamPosition

Field nameField typeRequiredDescription
offsetintegeryesOffset in a stream
epochstringyesStream epoch

History result

Field nameField typeOptionalDescription
publicationsArray of publication objectsyesList of publications in channel
offsetintegeryesTop offset in history stream
epochstringyesEpoch of current stream

history_remove

history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state.

{
"method": "history_remove",
"params": {
"channel": "chat"
}
}

Example:

echo '{"method": "history_remove", "params": {"channel": "chat"}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 43
Content-Type: application/json
Date: Thu, 17 May 2018 22:09:44 GMT

{
"result": {}
}

History remove params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to remove history

History remove result

Empty object at the moment.

channels

channels return active channels (with one or more active subscribers in it).

{
"method": "channels",
"params": {}
}

Channels params

Parameter nameParameter typeRequiredDescription
patternstringnoPattern to filter channels

Channels result

Field nameField typeOptionalDescription
channelsMap of string to ChannelInfonoMap where key is channel and value is ChannelInfo (see below)

ChannelInfo

Field nameField typeOptionalDescription
num_clientsintegernoTotal number of connections currently subscribed to a channel
caution

Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels RPC extension will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.

info

info method allows getting information about running Centrifugo nodes.

Example:

echo '{"method": "info", "params": {}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 184
Content-Type: application/json
Date: Thu, 17 May 2018 22:07:58 GMT

{
"result": {
"nodes": [
{
"name": "Alexanders-MacBook-Pro.local_8000",
"num_channels": 0,
"num_clients": 0,
"num_users": 0,
"uid": "f844a2ed-5edf-4815-b83c-271974003db9",
"uptime": 0,
"version": ""
}
]
}
}

Info params

Empty object at the moment.

Info result

Field nameField typeOptionalDescription
nodesArray of Node objectsnoInformation about all nodes in a cluster

Command pipelining

It's possible to combine several commands into one request to Centrifugo. To do this use JSON streaming format. This can improve server throughput and reduce traffic traveling around.

HTTP API libraries

Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header.

But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually:

Also, there are API libraries created by community:

tip

Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.

GRPC API

Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC.

GRPC API is also useful if you want to publish binary data to Centrifugo channels.

tip

GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description.

You can enable GRPC API in Centrifugo using grpc_api option:

config.json
{
...
"grpc_api": true
}

By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option.

Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file.

Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.

GRPC example for Python

For example for Python you need to run sth like this according to GRPC docs:

pip install grpcio-tools
python -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto

As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo:

import grpc
import api_pb2_grpc as api_grpc
import api_pb2 as api_pb

channel = grpc.insecure_channel('localhost:10000')
stub = api_grpc.CentrifugoApiStub(channel)

try:
resp = stub.Info(api_pb.InfoRequest())
except grpc.RpcError as err:
# GRPC level error.
print(err.code(), err.details())
else:
if resp.error.code:
# Centrifugo server level error.
print(resp.error.code, resp.error.message)
else:
print(resp.result)

Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.

GRPC example for Go

Here is a simple example of how to run Centrifugo with the GRPC Go client.

You need protoc, protoc-gen-go and protoc-gen-go-grpc installed.

First start Centrifugo itself with GRPC API enabled:

CENTRIFUGO_GRPC_API=1 centrifugo --config config.json

In another terminal tab:

mkdir centrifugo_grpc_example
cd centrifugo_grpc_example/
touch main.go
go mod init centrifugo_example
mkdir apiproto
cd apiproto
wget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto

Run protoc to generate code:

protoc -I ./ api.proto --go_out=. --go-grpc_out=.

Put the following code to main.go file (created on the last step above):

package main

import (
"context"
"log"
"time"

"centrifugo_example/apiproto"

"google.golang.org/grpc"
)

func main() {
conn, err := grpc.Dial("localhost:10000", grpc.WithInsecure())
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
client := apiproto.NewCentrifugoApiClient(conn)
for {
resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{
Channel: "chat:index",
Data: []byte(`{"input": "hello from GRPC"}`),
})
if err != nil {
log.Printf("Transport level error: %v", err)
} else {
if resp.GetError() != nil {
respError := resp.GetError()
log.Printf("Error %d (%s)", respError.Code, respError.Message)
} else {
log.Println("Successfully published")
}
}
time.Sleep(time.Second)
}
}

Then run:

go run main.go

The program starts and periodically publishes the same payload into chat:index channel.

GRPC API key authorization

You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey <KEY>. For example in Go language:

package main

import (
"context"
"log"
"time"

"centrifugo_example/apiproto"

"google.golang.org/grpc"
)

type keyAuth struct {
key string
}

func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": "apikey " + t.key,
}, nil
}

func (t keyAuth) RequireTransportSecurity() bool {
return false
}

func main() {
conn, err := grpc.Dial("localhost:10000", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{"xxx"}))
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
client := apiproto.NewCentrifugoClient(conn)
for {
resp, err := client.Publish(context.Background(), &PublishRequest{
Channel: "chat:index",
Data: []byte(`{"input": "hello from GRPC"}`),
})
if err != nil {
log.Printf("Transport level error: %v", err)
} else {
if resp.GetError() != nil {
respError := resp.GetError()
log.Printf("Error %d (%s)", respError.Code, respError.Message)
} else {
log.Println("Successfully published")
}
}
time.Sleep(time.Second)
}
}

For other languages refer to GRPC docs.

- + \ No newline at end of file diff --git a/docs/3/server/server_subs.html b/docs/3/server/server_subs.html index 45d9b312e..47b44fe49 100644 --- a/docs/3/server/server_subs.html +++ b/docs/3/server/server_subs.html @@ -16,13 +16,13 @@ - +

Server-side subscriptions

Centrifugo clients can initiate a subscription to a channel by calling the Subscribe method of client protocol. In most cases, this is the most flexible approach since a client-side usually knows which channels it needs to consume at a concrete moment. But in some situations, all you need is to subscribe your connections to several channels on a server-side at the moment of connection establishment. So client effectively starts receiving publications from those channels without calling the Subscribe method at all.

You can set a list of channels for a connection in two ways at the moment:

  • over connection JWT using channels claim, which is an array of strings
  • over connect proxy returning channels field in result (also an array of strings)
  • dynamically over server subscribe API

On the client-side, you need to listen for publications from server-side channels using a top-level client event handler. For example with centrifuge-js:

var centrifuge = new Centrifuge(address);

centrifuge.on('publish', function(ctx) {
const channel = ctx.channel;
const payload = JSON.stringify(ctx.data);
console.log('Publication from server-side channel', channel, payload);
});

centrifuge.connect();

I.e. listen for publications without any usage of subscription objects. You can look at channel publication relates to using field in callback context as shown in the example.

tip

The same best practices of working with channels and client-side subscriptions equally applicable to server-side subscription.

Dynamic server-side subscriptions

See subscribe and unsubscribe server API

Automatic personal channel subscription

It's possible to automatically subscribe a user to a personal server-side channel.

To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel.

For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above.

As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default "") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.

Maintain single user connection

Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes.

user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect).

This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel.

Example config:

{
"user_subscribe_to_personal": true,
"user_personal_single_connection": true,
"user_personal_channel_namespace": "personal",
"namespaces": [
{
"name": "personal",
"presence": true
}
]
}
note

Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.

- + \ No newline at end of file diff --git a/docs/3/server/tls.html b/docs/3/server/tls.html index 04b97d050..182fcdee2 100644 --- a/docs/3/server/tls.html +++ b/docs/3/server/tls.html @@ -16,7 +16,7 @@ - + @@ -33,7 +33,7 @@ browsers such as Chrome 49 on Windows XP and IE8 on XP:

  • tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys.
  • tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment

grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints.

uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.

TLS for GRPC API

You can provide custom certificate files to configure TLS for GRPC API server.

  • grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file
  • grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server
  • grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server

TLS for GRPC unidirectional stream

Starting from Centrifugo v3.0.0 you can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint.

  • uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file
  • uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server
  • uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server
- + \ No newline at end of file diff --git a/docs/3/transports/client_protocol.html b/docs/3/transports/client_protocol.html index 0ab5b8f22..ba7a7dd27 100644 --- a/docs/3/transports/client_protocol.html +++ b/docs/3/transports/client_protocol.html @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@

Client protocol

This chapter describes internal bidirectional client-server protocol in details to help developers build new client libraries or understand how existing client connectors work.

Note that you can always look at existing client implementations in case of any questions. Not all clients support all available server features though.

Client implementation feature matrix

First we will look at list of features bidirectional client library should support. If you are an author of client library you can use this list as a checklist.

Our current client feature matrix looks like this:

  • connect to server (both Centrifugo and Centrifuge-based) using JSON protocol format
  • connect to server (both Centrifugo and Centrifuge-based) using Protobuf protocol format
  • connect with token (JWT in Centrifugo case, any string token in Centrifuge library case)
  • connect to server with custom headers (not available in a browser)
  • automatic reconnect in case of connection problems (server restart, unavailable network)
  • an exponential backoff for reconnect process
  • possibility to set handlers for connect and disconnect events
  • extract and expose disconnect code and reason
  • subscribe to a channel and provide a way to handle asynchronous Publications coming from it
  • handle Join and Leave messages from a channel
  • handle Unsubscribe notifications
  • provide publish method of Subscription object
  • provide unsubscribe method of Subscription
  • provide presence method of Subscription
  • provide presence stats method of Subscription
  • provide history method of Subscription
  • provide publish method on top level
  • provide unsubscribe method on top level
  • provide presence method on top level
  • provide presence stats method on top level
  • provide history method on top level
  • send asynchronous messages to server
  • handle asynchronous messages from server
  • send RPC requests to server
  • publish to channel without being subscribed
  • subscribe to private (token-protected) channels with a token
  • implement client-side connection token refresh mechanism
  • implement private channel subscription token refresh mechanism
  • client protocol level ping/pong to find a broken connection
  • automatic reconnect in case of connect or subscribe command timeouts
  • handle connection expired error
  • handle subscription expired error
  • server-side subscriptions
  • message recovery mechanism for client-side subscriptions
  • message recovery mechanism for server-side subscriptions

This document describes protocol specifics for Websocket transport which supports binary and text formats to transfer data. As Centrifugo and Centrifuge library for Go have various types of messages it serializes protocol messages using JSON or Protobuf formats.

info

SockJS works almost the same way as JSON websocket described here but has its own extra framing on top of Centrifuge protocol messages. SockJS can only work with JSON - it's not possible to transfer binary data over it.

Top level framing

Centrifuge protocol defined in Protobuf schema. That schema is a source of the truth. Below we describe messages from that schema.

In bidirectional case client sends Command to server and server sends Reply to client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages.

One request from client to server and one response from server to client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data.

When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \n:

{"id": 1, "method": 1, "params": {"channel": "ch1"}}
{"id": 2, "method": 1, "params": {"channel": "ch2"}}

For example here is how we do this in Javascript client when JSON format used:

function encodeCommands(commands) {
const encodedCommands = [];
for (const i in commands) {
if (commands.hasOwnProperty(i)) {
encodedCommands.push(JSON.stringify(commands[i]));
}
}
return encodedCommands.join('\n');
}
info

This doc will use JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo.

When Protobuf format used then many Command can be sent from client to server in length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example.

The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there.

For example here is how we read server response and extracting individual replies in Javascript client when JSON format used:

function decodeReplies(data) {
const replies = [];
const encodedReplies = data.split('\n');
for (const i in encodedReplies) {
if (encodedReplies.hasOwnProperty(i)) {
if (!encodedReplies[i]) {
continue;
}
const reply = JSON.parse(encodedReplies[i]);
replies.push(reply);
}
}
return replies;
}

For Protobuf case see existing client implementations for decoding example.

As you can see each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous protocol where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before.

So you can expect something like this in response after sending commands to server:

{"id": 1, "result": {}}
{"id": 2, "result": {}}

Besides id Reply from server to client have two important fields: result and error.

result contains useful payload object which is different for various Reply messages.

error contains error description if Command processing resulted in some error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can parse result object appropriately.

error looks like this in JSON case:

{
"code": 100,
"message": "internal server error"
}

We will talk more about error handling below.

The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to client in any moment - not as reaction to issued Command but as message from server to client in arbitrary time. For example this can be a message published into channel.

Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow. Communication with Centrifuge/Centrifugo server starts with issuing connect command.

Connect

First of all client must dial with a server and then send connect Command to it.

Default Websocket endpoint in Centrifugo is:

ws://centrifugo.example.com/connection/websocket

In case of using TLS:

wss://centrifugo.example.com/connection/websocket

After a successful dial to WebSocket endpoint client must send connect command to server to authorize itself.

connect command looks like:

{
"id": 1,
"method": 0,
"params": {
"token": "JWT",
"data": {}
}
}

All methods defined in Protobuf schema:

message Command {
uint32 id = 1;
enum MethodType {
CONNECT = 0;
SUBSCRIBE = 1;
UNSUBSCRIBE = 2;
PUBLISH = 3;
PRESENCE = 4;
PRESENCE_STATS = 5;
HISTORY = 6;
PING = 7;
SEND = 8;
RPC = 9;
REFRESH = 10;
SUB_REFRESH = 11;
}
MethodType method = 2;
bytes params = 3;
}

So here we are using a enum value for CONNECT (0).

Params fields:

  • optional string token - connection token. Can be omitted if token-based auth not used.
  • data - can contain custom connect data, for example it can contain client settings.

In response to connect command server sends a connect reply. It looks this way:

{
"id": 1,
"result":{
"client": "421bf374-dd01-4f82-9def-8c31697e956f",
"version": "2.0.0"
}
}

result has some fields:

  • string client - unique client connection ID server issued to this connection
  • string version - server version
  • optional bool expires - whether a server will expire connection at some point
  • optional int32 ttl - time in seconds until connection expires

Subscribe

As soon as client successfully connected and got unique connection ID it is ready to subscribe on channels. To do this it must send subscribe command to server:

{
"id": 2,
"method": 1,
"params": {
"channel": "ch1"
}
}

Fields that can be set in params are:

  • string channel - channel to subscribe

In response to subscribe a client receives reply like:

{
"id": 2,
"result": {}
}

result can have the following fields that relate to subscription expiration:

  • optional bool expires - indicates whether subscription expires or not.
  • optional uint32 ttl - number of seconds until subscription expire.

Also several fields that relate to message recovery:

  • optional bool recoverable - means that messages can be recovered in this subscription.
  • optional uint64 offset - current publication offset inside channel
  • optional string epoch - current epoch inside channel
  • optional array publications - this is an array of missed publications in channel. When received client must call general publication event handler for each message in this array.
  • optional bool recovered - this flag set to true when server thinks that all missed publications successfully recovered and send in subscribe reply (in publications array) and false otherwise.

See more about meaning of recovery related fields in special doc chapter.

After a client received a successful reply on subscribe command it will receive asynchronous reply messages published to this channel. Messages can be of several types:

  • Publication message
  • Join message
  • Leave message
  • Unsubscribe message

See more about asynchronous messages below.

Unsubscribe

When client wants to unsubscribe from a channel and therefore stop receiving asynchronous subscription messages from connection related to channel it must call unsubscribe command:

{
"id": 3,
"method": 2,
"params": {
"channel": "ch1"
}
}

Actually server response does not mean a lot for a client - it must immediately remove channel subscription from internal implementation data structures and ignore all messages related to channel.

Refresh

It's possible to turn on client connection expiration mechanism on a server. While enabled server will keep track of connections whose time of life is close to the end (connection lifetime set on connection authentication phase). In this case connection will be closed. Client can prevent closing connection refreshing its connection credentials. To do this it must send refresh command to server. refresh command is similar to connect:

{
"id": 4,
"method": 10,
"params": {
"token": "<refreshed token>"
}
}

The tip whether a connection must be refreshed by a client comes in reply to connect command shown above - fields expires and ttl.

When client connection expire mechanism is on the value of field expires in connect reply is true. In this case client implementation should look at ttl value which is seconds left until connection will be considered expired. Client must send refresh command after this ttl seconds. Server gives client a configured window to refresh token after ttl passed and then closes connection if client have not updated its token.

When connecting with already expired token an error will be returned (with code 109). In this case client should refresh its token and reconnect with exponential backoff.

RPC-like calls: publish, history, presence

The mechanics of these calls is simple - client sends command and expects response from server.

publish command allows to publish a message into a channel from a client.

tip

To publish from client publish option in Centrifugo configuration must be set to true

history allows asking a server for channel history if enabled.

presence allows asking a server for channel presence information if enabled.

presence_stats allows asking for short presence info (num clients and unique users in a channel).

Asynchronous server-to-client messages

There are several types of asynchronous messages that can come from a server to a client. All of them relate to the current client subscriptions.

The most important message is Publication:

{
"result":{
"channel":"ch1",
"data":{
"data":{"input":"1"},
"info":{
"user":"2694",
"client":"5c48510e-cf49-4fa8-a9b2-490b22231e74",
"conn_info":{"name":"Alexander"},
"chan_info":{}
}
}
}
}

Publication is a message published into channel. Note that there is no id field in this message - this symptom allows to distinguish it from Reply to Command.

Next message is Join message:

{
"result":{
"type":1,
"channel":"ch1",
"data":{
"info":{
"user":"2694",
"client":"5c48510e-cf49-4fa8-a9b2-490b22231e74",
"conn_info":{"name":"Alexander"},
"chan_info":{}
}
}
}
}

Join messages sent when someone joined (subscribed on) channel.

tip

To enable Join and Leave messages join_leave option must be enabled in Centrifugo for a channel namespace.

Leave messages sent when someone left (unsubscribed from) channel.

{
"result":{
"type":2,
"channel":"ch1",
"data":{
"info":{
"user":"2694",
"client":"5c48510e-cf49-4fa8-a9b2-490b22231e74",
"conn_info":{"name":"Alexander"},
"chan_info":{}
}
}
}
}

Finally Unsubscribe message that means that server unsubscribed current client from a channel:

{
"result":{
"type":3,
"channel":"ch1",
"data":{}
}
}

It's possible to distinguish between different types of asynchronous messages looking at type field (for Publication this field not set or 0).

Ping Pong

To maintain connection alive and detect broken connections client must periodically send ping commands to server and expect replies to it. Ping command looks like:

{
"id":32,
"method":"ping"
}

Server just echoes this command back. When client does not receive ping reply for some time it must consider connection broken and try to reconnect. Recommended ping interval is 25 seconds, recommended period to wait for pong is 1-5 seconds. Though those numbers can vary.

Handle disconnects

Client should handle disconnect advices from server. In websocket case disconnect advice is sent in reason field of CLOSE Websocket frame. Reason contains string which is disconnect object encoded into JSON (even in case of Protobuf scenario). That objects looks like:

{
"reason": "shutdown",
"reconnect": true
}

It contains string reason of connection closing and advice to reconnect or not. Client should take this reconnect advice into account.

In case of network problems and random disconnect from server without well known reason client should always try to reconnect with exponential intervals.

Handle errors

This section contains advices to error handling in client implementations.

Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems.

Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again.

Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side.

The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance.

Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).

Client implementation advices

Here are some advices about client public API. Examples here are in Javascript language. This is just an attempt to help in developing a client - but rules here is not obligatorily the best way to implement client.

Create client instance:

const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {});

Set connection token (in case of using Centrifugo):

centrifuge.setToken("XXX")

Connect to server:

centrifuge.connect();

2 event handlers can be set to centrifuge object: connect and disconnect

centrifuge.on('connect', function(context) {
console.log(context);
});

centrifuge.on('disconnect', function(context) {
console.log(context);
});

Client created in disconnected state with reconnect attribute set to true and reconnecting flag set to false . After connect() called state goes to connecting. It's only possible to connect from disconnected state. Every time connect() called reconnect flag of client must be set to true. After each failed connect attempt state must be set to disconnected, disconnect event must be emitted (only if reconnecting flag is false), and then reconnecting flag must be set to true (if client should continue reconnecting) to not emit disconnect event again after next in a row connect attempt failure. In case of failure next connection attempt must be scheduled automatically with backoff strategy. On successful connect reconnecting flag must be set to false, backoff retry must be reset and connect event must be emitted. When connection lost then the same set of actions as when connect failed must be performed.

Client must allow to subscribe on channels:

var subscription = centrifuge.subscribe("channel", eventHandlers);

Subscription object created and control immediately returned to caller - subscribing must be performed asynchronously. This is required because client can automatically reconnect later so event-based model better suites for subscriptions.

Subscription should support several event handlers:

  • handler for publication received from channel
  • join message handler
  • leave message handler
  • error handler
  • subscribe success event handler
  • unsubscribe event handler

Every time client connects to server it must restore all subscriptions.

Every time client disconnects from server it must call unsubscribe handlers for all active subscriptions and then emit disconnect event.

Client must periodically (once in 25 secs, configurable) send ping messages to server. If pong has not beed received in 5 secs (configurable) then client must disconnect from server and try to reconnect with backoff strategy.

Client can automatically batch several requests into one frame to server and also must be able to handle several replies received from server in one frame.

Server side subscriptions (SSS)

It's also possible to subscribe connection to channels on server side. In this case we call this server-side subscription. Client should only handle asynchronous messages coming from a server without need to create subscriptions on client side.

  • SSS should be kept separate from client-side subs
  • SSS requires new event handlers on top-level of Client - Subscribe, Publish, Join, Leave, Unsubscribe, event handlers will be called with event context similar to client-side subs case but with channel field
  • Connect Reply contains SSS set by a server on connect, on reconnect client has a chance to recover missed Publications
  • Server side subscription can happen at any moment - Sub Push will be sent to client

Message recovery

Client should automatically recover messages after being disconnected due to network problems and set appropriate fields in subscribe event context. Two important fields in onSubscribeSuccess event context are isRecovered and isResubscribe. First field let user know what server thinks about subscription state - were all messages recovered or not. The second field must only be true if resubscribe was caused by temporary network connection lost. If user initiated resubscribe himself (calling unsubscribe method and then subscribe method) then recover workflow should not be used and isResubscribe must be false.

Disconnect code and reason

In case of Websocket it is sent by server in CLOSE Websocket frame. This is a string containing JSON object with fields: reason (string) and reconnect (bool). Client should give users access to these fields in disconnect event and automatically follow reconnect advice.

Additional notes

Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in reply to a subscribe command.

- + \ No newline at end of file diff --git a/docs/3/transports/client_sdk.html b/docs/3/transports/client_sdk.html index 4406b1847..4eb1e7c71 100644 --- a/docs/3/transports/client_sdk.html +++ b/docs/3/transports/client_sdk.html @@ -16,13 +16,13 @@ - +

Client real-time SDKs

The following SDKs allow connecting to Centrifugo from the application frontend:

No need in clients for unidirectional approach

Client libraries listed here speak Centrifugo bidirectional protocol (WebSocket). If you aim to use unidirectional approach you don't need client connectors – just use standard APIs. See the difference here.

See a description of client protocol if you want to write a custom client bidirectional connector.

- + \ No newline at end of file diff --git a/docs/3/transports/overview.html b/docs/3/transports/overview.html index 312144c73..3d55c77f2 100644 --- a/docs/3/transports/overview.html +++ b/docs/3/transports/overview.html @@ -16,13 +16,13 @@ - +

Real-time transports

Centrifugo supports a variety of transports to deliver real-time messages to clients.

Every transport is a persistent connection

Here we describe supported transports between your application frontend and Centrifugo itself. Every Centrifugo transport is a persistent connection so the server can push data towards clients at any moment.

The important distinction here is that all supported transports belong to one of two possible groups:

  • Bidirectional
  • Unidirectional

Bidirectional

Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus.

Bidirectional transports come with a cost that developers need to use a special client connector library which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state and request queueing/timeouts/errors.

Centrifugo has client SDKs for bidirectional communication for popular environments.

Unidirectional

Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time.

The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource, HTTP streaming), or GRPC generated code to receive real-time updates from Centrifugo – thus avoiding dependency to a client connector that abstracts bidirectional communication.

The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo).

Unidirectional message types

In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as:

message Push {
enum PushType {
PUBLICATION = 0;
JOIN = 1;
LEAVE = 2;
UNSUBSCRIBE = 3;
MESSAGE = 4;
SUBSCRIBE = 5;
CONNECT = 6;
DISCONNECT = 7;
REFRESH = 8;
}
PushType type = 1;
string channel = 2;
bytes data = 3;
}

So unidirectional connection will receive various pushes. All you need to do is look at Push type and process it or skip it. In most cases you will be most interested in CONNECT and PUBLICATION types.

tip

In case of unidirectional WebSocket, EventSource and HTTP-streaming which currently work only with JSON data field of Push will come as an embedded JSON instead of bytes (again – the same mechanism as for Centrifugo bidirectional JSON protocol).

Just try using any unidirectional transport and you will quickly get the idea.

- + \ No newline at end of file diff --git a/docs/3/transports/sockjs.html b/docs/3/transports/sockjs.html index b0653e950..3528eec46 100644 --- a/docs/3/transports/sockjs.html +++ b/docs/3/transports/sockjs.html @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@

SockJS

SockJS is a polyfill browser library which provides HTTP-based fallback transports in case when it's not possible to establish Websocket connection. This can happen in old client browsers or because of some proxy behind client and server that cuts of Websocket traffic. You can find more information on SockJS project Github page.

If you have a requirement to work everywhere SockJS is the solution. SockJS will automatically choose best fallback transport if Websocket connection failed for some reason. Some of the fallback transports are:

  • EventSource (SSE)
  • XHR-streaming
  • Long-polling
  • And more (see SockJS docs)

SockJS connection endpoint in Centrifugo is:

/connection/sockjs

SockJS caveats

caution

There are several important caveats to know when using SockJS – see below.

Sticky sessions

First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend.

For example, with Nginx sticky support can be enabled with ip_hash directive for upstream:

upstream centrifugo {
ip_hash;
server 127.0.0.1:8000;
server 127.0.0.2:8000;
}

With this configuration Nginx will proxy connections with the same ip address to the same upstream backend.

But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair.

So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.

Browser only

SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.

JSON only

One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.

Options

sockjs

Boolean, default: false.

Enables SockJS transport.

sockjs_url

Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js

Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.

- + \ No newline at end of file diff --git a/docs/3/transports/uni_grpc.html b/docs/3/transports/uni_grpc.html index 0c7bac961..cf7187798 100644 --- a/docs/3/transports/uni_grpc.html +++ b/docs/3/transports/uni_grpc.html @@ -16,13 +16,13 @@ - +

Unidirectional GRPC

It's possible to connect to GRPC unidirectional stream to consume real-time messages from Centrifugo. In this case you need to generate GRPC code for your language on client-side.

Protobuf definitions can be found here.

GRPC server will start on port 11000 (default).

Supported data formats

JSON and binary.

Options

uni_grpc

Boolean, default: false.

Enables unidirectional GRPC endpoint.

config.json
{
...
"uni_grpc": true
}

uni_grpc_port

String, default "11000".

Port to listen on.

uni_grpc_address

String, default "" (listen on all interfaces)

Address to bind uni GRPC to.

uni_grpc_max_receive_message_size

Default: 65536 (64KB)

Maximum allowed size of a first connect message received from GRPC connection in bytes.

uni_grpc_tls

Boolean, default: false

Enable custom TLS for unidirectional GRPC server.

uni_grpc_tls_cert

String, default: "".

Path to cert file.

uni_grpc_tls_key

String, default: "".

Path to key file.

Example

A basic example can be found here. It uses Go language, but for other languages approach is mostly the same:

  1. Copy Protobuf definitions
  2. Generate GRPC client code
  3. Use generated code to connect to Centrifugo
  4. Process Push messages, drop unknown Push types, handle those necessary for the application.
- + \ No newline at end of file diff --git a/docs/3/transports/uni_http_stream.html b/docs/3/transports/uni_http_stream.html index 2a22ee836..b9423b14a 100644 --- a/docs/3/transports/uni_http_stream.html +++ b/docs/3/transports/uni_http_stream.html @@ -16,13 +16,13 @@ - +

Unidirectional HTTP streaming

Default unidirectional HTTP streaming connection endpoint in Centrifugo is:

/connection/uni_http_stream

This is basically a long-lived HTTP connection. You can consume it from a browser using fetch API.

Streaming endpoint accepts HTTP POST requests and sends JSON messages to a connection. These JSON messages can have different meaning according to Centrifuge protocol Protobuf definitions. But in most cases you will be interested in Publication push types.

Connect command

It's possible to pass initial connect command by posting a JSON body to a streaming endpoint.

Refer to the full Connect command description – it's the same as for unidirectional WebSocket.

Supported data formats

JSON

Pings

Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).

Options

uni_http_stream

Boolean, default: false.

Enables unidirectional HTTP streaming endpoint.

config.json
{
...
"uni_http_stream": true
}

uni_http_stream_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes.

Connecting using CURL

Let's look how simple it is to connect to Centrifugo using HTTP streaming.

We will start from scratch, generate new configuration file:

centrifugo genconfig

Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect:

config.json
{
...
"uni_http_stream": true,
"user_subscribe_to_personal": true
}

Run Centrifugo:

centrifugo -c config.json

In separate terminal window create token for a user:

❯ go run main.go gentoken -u user12
HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM

Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request:

curl -X POST http://localhost:8000/connection/uni_http_stream \
-d '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM"}'

Open one more terminal window and publish message to a personal user channel:

curl -X POST http://localhost:8000/api \
-d '{"method": "publish", "params": {"channel": "#user12", "data": {"input": "hello"}}}' \
-H "Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc"

You should see this message received in a terminal window with established connection to HTTP streaming endpoint:

curl -X POST http://localhost:8000/connection/uni_http_stream \
-d '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM"}'
{"type":6,"data":{"client":"cf5dc239-83ac-4d0f-b9ed-9733d7f7b61b","version":"dev","subs":{"#user12":{}}}}
null
null
null
null
null
{"channel":"#user12","data":{"data":{"input": "hello"}}}

null messages are pings from a server.

That's all, happy streaming!

Browser example

A basic browser example can be found here.

- + \ No newline at end of file diff --git a/docs/3/transports/uni_sse.html b/docs/3/transports/uni_sse.html index 6ec0fc51a..e27577d29 100644 --- a/docs/3/transports/uni_sse.html +++ b/docs/3/transports/uni_sse.html @@ -16,13 +16,13 @@ - +

Unidirectional SSE (EventSource)

Default unidirectional SSE (EventSource) connection endpoint in Centrifugo is:

/connection/uni_sse
info

Only parts of EventSource spec that make sense for Centrifugo are implemented. For example Last-Event-Id header not supported (since one connection can be subscribed to many channels) and multiline strings (since we are passing JSON-encoded objects to the client) etc.

Connect command

Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example:

const url = new URL('http://localhost:8000/connection/uni_sse');
url.searchParams.append("cf_connect", JSON.stringify({
'token': '<JWT>'
}));

const eventSource = new EventSource(url);

Refer to the full Connect command description – it's the same as for unidirectional WebSocket.

The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases.

tip

Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.

Supported data formats

JSON

Pings

Centrifugo sends SSE data like this as pings:

event: ping
data:

I.e. with event name ping and empty data. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).

Options

uni_sse

Boolean, default: false.

Enables unidirectional SSE (EventSource) endpoint.

config.json
{
...
"uni_sse": true
}

uni_sse_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes (when using HTTP POST requests to connect).

Browser example

A basic browser example can be found here.

- + \ No newline at end of file diff --git a/docs/3/transports/uni_websocket.html b/docs/3/transports/uni_websocket.html index 972ea78ed..d1046a160 100644 --- a/docs/3/transports/uni_websocket.html +++ b/docs/3/transports/uni_websocket.html @@ -16,13 +16,13 @@ - +

Unidirectional WebSocket

Default unidirectional WebSocket connection endpoint in Centrifugo is:

/connection/uni_websocket

While WebSocket is bidirectional transport in its nature Centrifugo provides its unidirectional version too to give developers more choice in transports when using unidirectional approach.

Connect command

It's possible to send connect command as first WebSocket message (as JSON).

Field nameField typeRequiredDescription
tokenstringnoConnection JWT, not required when using the connect proxy feature.
dataany JSONnoCustom JSON connection data
namestringnoApplication name
versionstringnoApplication version
subsmap of channel to SubscribeRequestnoPass an information about desired subscriptions to a server

SubscribeRequest

Field nameField typeRequiredDescription
recoverbooleannoWhether a client wants to recover from a certain position
offsetintegernoKnown stream position offset when recover is used
epochstringnoKnown stream position epoch when recover is used

Supported data formats

JSON

Pings

Centrifugo uses empty messages (frame with no payload at all) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).

Options

uni_websocket

Boolean, default: false.

Enables unidirectional WebSocket endpoint.

config.json
{
...
"uni_websocket": true
}

uni_websocket_message_size_limit

Default: 65536 (64KB)

Maximum allowed size of a first connect message received from WebSocket connection in bytes.

Example

Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal.

First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example:

config.json
{
"token_hmac_secret_key": "secret",
"uni_websocket":true,
"user_subscribe_to_personal": true
}

Run Centrifugo:

./centrifugo -c config.json

In another terminal:

❯ ./centrifugo -c config.json -u test_user
HMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw

Install wscat and run:

wscat -c "ws://localhost:8000/connection/uni_websocket"

This will establish a connection with a server and you then can send connect command to a server:

❯ wscat -c "ws://localhost:8000/connection/uni_websocket"
Connected (press CTRL+C to quit)
> {"token": "eyJh..5lz1Iw", "subs": {"abc": {}}}
< {"type":6,"data":{"client":"8ceaa299-4c7b-4254-9d65-c61b6883833a","version":"3.0.0","subs":{"#test_user":{"recoverable":true,"epoch":"StoH","positioned":true},"abc":{"recoverable":true,"epoch":"nNgd","positioned":true},"expires":true,"ttl":604653}}

The connection will receive pings (empty messages) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.

- + \ No newline at end of file diff --git a/docs/3/transports/websocket.html b/docs/3/transports/websocket.html index 21cfa1ec8..7224aa96a 100644 --- a/docs/3/transports/websocket.html +++ b/docs/3/transports/websocket.html @@ -16,13 +16,13 @@ - +

WebSocket

Websocket is the main transport in Centrifugo. It's a very efficient low-overhead protocol on top of TCP.

The biggest advantage is that Websocket works out of the box in all modern browsers and almost all programming languages have Websocket implementations. This makes Websocket a pretty universal transport that can even be used to connect to Centrifugo from web apps and mobile apps and other environments.

Default WebSocket connection endpoint in Centrifugo is:

/connection/websocket

By default WebSocket connection uses JSON protocol internally.

Options

websocket_message_size_limit

Default: 65536 (64KB)

Maximum allowed size of a message received from WebSocket connection in bytes.

websocket_read_buffer_size

In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size).

config.json
{
...
"websocket_read_buffer_size": 512
}

websocket_write_buffer_size

In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused):

config.json
{
...
"websocket_write_buffer_size": 512
}

websocket_use_write_buffer_pool

If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.

websocket_compression

An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire.

We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally.

caution

Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable.

To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature.

Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client.

It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.

Protobuf binary protocol

In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if:

  • you want less traffic on wire as Protobuf is very compact
  • you want maximum performance on server-side as Protobuf encoding/decoding is very efficient
  • you can sacrifice human-readable JSON for your application

Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like:

wss://centrifugo.example.com/connection/websocket?format=protobuf

After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes.

tip

You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.

- + \ No newline at end of file diff --git a/docs/4/attributions.html b/docs/4/attributions.html index 3218955ac..ba5640d63 100644 --- a/docs/4/attributions.html +++ b/docs/4/attributions.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/docs/4/faq.html b/docs/4/faq.html index 80c733ab1..eca5e00b4 100644 --- a/docs/4/faq.html +++ b/docs/4/faq.html @@ -16,13 +16,13 @@ - +

Frequently Asked Questions

Answers to popular questions here.

How many connections can one Centrifugo instance handle?

This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here.

Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case.

You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.

Memory usage per connection?

Depending on transport used and features enabled the amount of RAM required per each connection can vary.

For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections.

For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.

Can Centrifugo scale horizontally?

Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker.

See engines and scalability considerations.

Message delivery model

See design overview

Message order guarantees

See design overview.

Should I create channels explicitly?

No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel.

When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).

What about best practices with the number of channels?

Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application.

But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc.

One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.

Any way to exclude message publisher from receiving a message from a channel?

Currently, no.

We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this:

  • Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID)
  • Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history

Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates.

Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point.

Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel.

In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.

Can I have both binary and JSON clients in one channel?

No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere.

Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end).

To summarize:

  • if you are using binary Protobuf clients and binary payloads everywhere – you are fine.
  • if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine.
  • if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.

Online presence for chat apps - online status of your contacts

While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel.

You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.

Centrifugo stops accepting new connections, why?

The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.

Can I use Centrifugo without reverse-proxy like Nginx before it?

Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.

Does Centrifugo work with HTTP/2?

Yes, Centrifugo works with HTTP/2. This is provided by built-in Go http server implementation.

You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable:

GODEBUG="http2server=0" centrifugo -c config.json

Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.

Does Centrifugo work with HTTP/3?

Centrifugo v4 added an experimental HTTP/3 support. As soon as you enabled TLS and provided "http3": true option all endpoints on external port will be served by HTTP/3 server based on github.com/quic-go/quic-go implementation. This (among other benefits which HTTP/3 can provide) is a step towards a proper WebTransport support. For now we support WebTransport experimentally.

It's worth noting that WebSocket transport does not work over HTTP/3, it still starts with HTTP/1.1 Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and Eventsource should work just fine with HTTP/3.

HTTP/3 does not work with ACME autocert TLS at the moment - i.e. you need to explicitly provide paths to cert and key files as described here.

Is there a way to use a single connection to Centrifugo from different browser tabs?

If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.

What if I need to send push notifications to mobile or web applications?

Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via FCM, or browsers over Web Push API.

We are preparing our own push notifications API as part of Centrifugo PRO version. This is under construction though.

The reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.

How can I know a message is delivered to a client?

You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.

Can I publish new messages over a WebSocket connection from a client?

It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend.

We suggest using one of the available approaches:

  • When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API.
  • Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network.
  • Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself).

Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.

How to create a secure channel for two users only (private chat case)?

There are several ways to achieve it:

  • use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter
  • next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers
  • you can use subscribe proxy feature to validate subscriptions, see chapter about proxy
  • finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations

What's the best way to organize channel configuration?

In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled.

For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients.

The same relates to other channel options.

Does Centrifugo support webhooks?

Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there.

Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.

Why Centrifugo does not have disconnect hooks?

Centrifugo does not support disconnect hooks at this point.

First of all, there is no guarantee that the disconnect process will have a time to execute on the client-side (as the client can just switch off its device or simply lose internet connection). This means that a server may notice a connection loss with some delay (thanks to PING/PONG mechanism).

Centrifugo node can be unexpectedly killed. So there is a chance that disconnect event won't have a chance to be emitted to the backend.

One more reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. To reduce the load during connect process Centrifugo has JWT authentication. Even if disconnect events were queued/rate-limited there could be situations when your app processes disconnect hook while user already reconnected and connect event processed. This is a racy situation which you will need to handle somehow (possibly based on unique client ID attached to each connection).

If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend from the client-side and update user status somewhere on the backend (use Redis maybe). This is a pretty robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo refresh proxy for the task of periodic backend pinging. In this case you will notice that user (or particular client) left app with some delay – this may be a acceptable trade-off in many cases.

Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together.

Is it possible to listen to join/leave events on the app backend side?

No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.

How scalable is the online presence and join/leave features?

Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel).

There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers.

You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs.

The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.

How to send initial data to channel subscriber?

Sometimes you need to send some initial state towards channel subscriber. Centrifugo provides a way to attach any data to a successful subscribe reply when using subscribe proxy feature. See data and b64data fields. This data will be part of subscribed event context. And of course, you can always simply send request to get initial data from the application backend before or after subscribing to a channel without Centrifugo connection involved (i.e. using sth like general AJAX/HTTP call or passing data to the template when rendering an application page).

Does Centrifugo support multitenancy?

If you want to use Centrifugo with different projects the recommended approach is to have different Centrifugo installations for each project. Multitenancy is better to solve on infrastructure level in case of Centrifugo.

It's possible to share one Redis setup though by setting unique redis_prefix. But we recommend having completely isolated setups.

I have not found an answer to my question here:

Ask in our community rooms:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

- + \ No newline at end of file diff --git a/docs/4/flow_diagrams.html b/docs/4/flow_diagrams.html index d11fa1c3e..c5a67a29c 100644 --- a/docs/4/flow_diagrams.html +++ b/docs/4/flow_diagrams.html @@ -16,13 +16,13 @@ - +

flow_diagrams

For swimlanes.io:

Client <- App Backend: JWT

note:
The backend generates JWT for a user and passes it to the client side.

Client -> Centrifugo: Client connects to Centrifugo with JWT

...: {fas-spinner} Persistent connection established

Client -> Centrifugo: Client issues channel subscribe requests

Centrifugo -->> Client: Client receives real-time updates from channels
Client -> Centrifugo: Connect request

note:
Client connects to Centrifugo without JWT.

Centrifugo -> App backend: Sends request further (via HTTP or GRPC)

note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.

App backend -> Centrifugo: Connect reply

Centrifugo -> Client: Connect Reply

...: {fas-spinner} Persistent connection established
Client -> App Backend: Publish request

note:
Client sends data to publish to the application backend.

Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.

App Backend -> Centrifugo: Publish over Centrifugo API

Centrifugo -->> Client: {far-bolt fa-lg} Real-time notification

note: Centrifugo delivers real-time message to active channel subscribers.
Client -> App Backend: Publish request

note:
Client sends data to publish to the application backend.

Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.

App Backend -> Centrifugo: Publish over Centrifugo API

Centrifugo -->> Client: {far-bolt fa-lg} Real-time notification

note: Centrifugo delivers real-time message to active channel subscribers.
- + \ No newline at end of file diff --git a/docs/4/getting-started/client_api.html b/docs/4/getting-started/client_api.html index 8932d42d4..366340b4d 100644 --- a/docs/4/getting-started/client_api.html +++ b/docs/4/getting-started/client_api.html @@ -16,13 +16,13 @@ - +

Client API showcase

This chapter showcases Centrifugo bidirectional client API capabilities – i.e. real-time messaging primitives available on a front-end (can be a browser or a mobile device).

tip

It's also possible to avoid using the client library at all with unidirectional transports.

This is a formal description – we use Javascript client centrifuge-js for examples here. Refer to each specific client implementation for concrete method names and possibilities. See full list of Centrifugo client SDKs. This description does not cover all protocol possibilities – just the most important to start with.

If you are looking for detailed information about client-server protocol internals then client protocol description chapter can help.

Connecting to a server

Each Centrifugo client allows connecting to a server.

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.connect();

In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to:

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.setToken('<USER-JWT>')
centrifuge.connect();

See authentication chapter for more information on how to generate connection JWT.

If you are using connect proxy then you may go without setting JWT.

Disconnecting from a server

After connecting you can disconnect from a server at any moment.

centrifuge.disconnect();

Reconnecting to a server

Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.

Connection lifecycle events

All client implementations allow setting handlers on connect and disconnect events.

For example:

centrifuge.on('connect', function(connectCtx){
console.log('connected', connectCtx)
});

centrifuge.on('disconnect', function(disconnectCtx){
console.log('disconnected', disconnectCtx)
});

Subscribe to a channel

Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel.

centrifuge.subscribe('channel', function(messageCtx) {
console.log(messageCtx);
})

Client can subscribe to different channels. Subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events.

In idiomatic case messages published to channels from application backend over Centrifugo server API. Though it's not always true.

Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel properly configured. Client keeps last seen Publication offset and restores missed publications since known offset upon reconnecting. If recovery failed then client implementation provides a flag inside subscribe event to let the application know that some publications were missed – so you may need to load state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery in a dedicated chapter.

Server-side subscriptions

To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance:

centrifuge.on('publish', function(messageCtx) {
console.log(messageCtx);
});

It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.

Send RPC

A client can send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the WebSocket or SockJS connection. RPC is only available when RPC proxy configured.

const rpcRequest = {'key': 'value'};
const data = await centrifuge.namedRPC('example_method', rpcRequest);

Call channel history

Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel:

Get stream current top position:

const resp = await subscription.history();
console.log(resp.offset);
console.log(resp.epoch);

Get up to 10 publications from history since known stream position:

const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});
console.log(resp.publications);

Get up to 10 publications from history since current stream beginning:

const resp = await subscription.history({limit: 10});
console.log(resp.publications);

Get up to 10 publications from history since current stream end in reversed order (last to first):

const resp = await subscription.history({limit: 10, reverse: true});
console.log(resp.publications);

Presence and presence stats

Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured):

For presence (full information about active subscribers in channel):

const resp = await subscription.presence();
// resp contains presence information - a map client IDs as keys
// and client information as values.

For presence stats (just a number of clients and unique users in a channel):

const resp = await subscription.presenceStats();
// resp contains a number of clients and a number of unique users.
- + \ No newline at end of file diff --git a/docs/4/getting-started/community.html b/docs/4/getting-started/community.html index ceca73f00..6ab4d7885 100644 --- a/docs/4/getting-started/community.html +++ b/docs/4/getting-started/community.html @@ -16,13 +16,13 @@ - +

Join community

If you find Centrifugo interesting – please welcome to our community rooms in Telegram (the most active) and Discord:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

We are trying to create a respectful and curious community. In those rooms you may find answers to questions not fully covered by the documentation, share your thoughts and ideas on the real-time messaging topics and Centrifugo in particular.

We also have Twitter account and Youtube channel.

See you there!

- + \ No newline at end of file diff --git a/docs/4/getting-started/design.html b/docs/4/getting-started/design.html index 51a8fcedc..dd3db58d7 100644 --- a/docs/4/getting-started/design.html +++ b/docs/4/getting-started/design.html @@ -16,13 +16,13 @@ - +

Design overview

Let's discuss some architectural and design topics about Centrifugo.

Idiomatic usage

Originally Centrifugo was built with the unidirectional flow as the main approach. Though Centrifugo itself used a bidirectional protocol between a client and a server to allow client dynamically create subscriptions, Centrifugo did not allow using it for sending data from client to server.

With this approach publications travel only from server to a client. All requests that generate new data first go to the application backend (for example over AJAX call of backend API). The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event from a backend side to Centrifugo API.

This is a pretty natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is decoupled from the application in this case.

diagram_unidirectional_publish

During Centrifugo v2 life cycle this paradigm evolved a bit. It's now possible to send RPC requests from client to Centrifugo and the request will be then proxied to the application backend. Also, connection attempts and publications to channels can now be proxied. So bidirectional connection between client and Centrifugo is now available for utilizing by developers in both directions. For example, here is how publish diagram could look like when using publish request proxy feature:

So at the moment, the number of possible integration ways increased.

Message history considerations

Idiomatic Centrifugo usage requires having the main application database from which initial and actual state can be loaded at any point in time.

While Centrifugo has channel history, it has been mostly designed to reduce the load on the main application database when all users reconnect at once (in case of load balancer configuration reload, Centrifugo restart, temporary network problems, etc). This allows to radically reduce the load on the application main database during reconnect storm. Since such disconnects are usually pretty short in time having a reasonably small number of messages cached in history is sufficient.

The addition of history iteration API shifts possible use cases a bit. Manually calling history chunk by chunk allows keeping larger number of publications per channel.

Depending on Engine used and configuration of the underlying storage history stream persistence characteristics can vary. For example, with Memory Engine history will be lost upon Centrifugo restart. With Redis or Tarantool engines history will survive Centrifugo restarts but depending on a storage configuration it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis RDB and AOF, configure replication for storage high-availability, use Redis Cluster or maybe synchronous replication with Tarantool.

Centrifugo provides ways to distinguish whether the missed messages can't be restored from Centrifugo history upon recovery so a client should restore state from the main application database. So Centrifugo message history can be used as a complementary way to restore messages and thus reduce a load on the main application database most of the time.

Message delivery model

By default, the message delivery model of Centrifugo is at most once. With history and the positioning/recovery features enabled it's possible to achieve at least once guarantee within history retention time and size. After abnormal disconnect clients have an option to recover missed messages from the publication channel stream history that Centrifugo maintains.

Without the positioning or recovery features enabled a message sent to Centrifugo can be theoretically lost while moving towards clients. Centrifugo tries to do its best to prevent message loss on a way to online clients, but the application should tolerate a loss.

As noted Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. Also, it compensates at most once delivery of broker PUB/SUB system (Redis, Tarantool) by using additional publication offset checks and periodic offset synchronization. So publication loss missed in PUB/SUB layer will be detected eventually and client may catch up the state loading it from history.

At this moment Centrifugo message recovery is designed for a short-term disconnect period (think no more than one hour for a typical chat application, but this can vary). After this period (which can be configured per channel basis) Centrifugo removes messages from the channel history cache. In this case, Centrifugo may tell the client that some messages can not be recovered, so your application state should be loaded from the main database.

Message order guarantees

Message order in channels is guaranteed to be the same while you publish messages into channel one after another or publish them in one request. If you do parallel publications into the same channel then Centrifugo can't guarantee message order since those may be processed concurrently by Centrifugo.

Graceful degradation

It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely only on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in AJAX call response and render it. This way user that posts a comment will think that everything works just fine. Be careful to not draw comments twice in this case - think about idempotent identifiers for your entities.

Online presence considerations

Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to the network failures and unexpected shut down of Centrifugo node, there are chances that clients can be presented in a presence up to one minute more (until presence entry expiration).

Also, channel presence does not scale well for channels with lots of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows the response size becomes larger. In some cases, presence_stats API call can be sufficient to avoid receiving the entire presence state.

Scalability considerations

Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with Nats broker. See engines.

All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications.

But, if you approach broker resource limits (CPU or memory) then it's possible:

  • Use Centrifugo consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool)
  • Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters)
  • Nats broker should scale well itself in cluster setup

All brokers can be set up in highly available way so there won't be a single point of failure.

All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact and the fact that Centrifugo provides hooks for the application to understand history loss makes the process of resharding mostly automatic. As soon as you need to add additional broker shard (when using client-side sharding) you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently part of the data will stay on the same broker nodes. Applications should handle cases that channel data moved to another shard and restore a state from the main application database when needed.

- + \ No newline at end of file diff --git a/docs/4/getting-started/ecosystem.html b/docs/4/getting-started/ecosystem.html index 3b431c70f..5f6082184 100644 --- a/docs/4/getting-started/ecosystem.html +++ b/docs/4/getting-started/ecosystem.html @@ -16,13 +16,13 @@ - +

Ecosystem notes

Some additional notes about our ecosystem which may help you develop with our tech.

Centrifuge library for Go

Centrifugo is built on top of Centrifuge library for Go language.

Due to its standalone language-agnostic nature Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a more tight integration of real-time server with application business logic you may consider using Centrifuge library to build something similar to Centrifugo but with customized behavior. Centrifuge library can be considered as Socket.IO analogue in Go language ecosystem.

Library README has detailed description, link to examples and introduction post.

Many Centrifugo features should be re-implemented when using Centrifuge - like API layer, admin web UI, proxy etc (if you need those of course). And you need to write in Go language. But the core functionality like a client-server protocol (all Centrifugo client SDKs work with Centrifuge library based server) and Redis engine to scale come out of the box – in most cases this is enough to start building an app.

tip

Many things said in Centrifugo doc can be considered as an extra documentation for Centrifuge library (for example part about infrastructure tuning or transport description). But not all of them.

Framework integrations

There are some community-driven projects that provide integration with frameworks for more native experience.

tip

In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. Integrating directly may allow using all Centrifugo features without limitations which can be introduced by third-party wrapper.

- + \ No newline at end of file diff --git a/docs/4/getting-started/highlights.html b/docs/4/getting-started/highlights.html index 81d6366aa..655cf509a 100644 --- a/docs/4/getting-started/highlights.html +++ b/docs/4/getting-started/highlights.html @@ -16,13 +16,13 @@ - +

Main highlights

At this point you know how to build the simplest real-time app with Centrifugo. Beyond the core PUB/SUB functionality Centrifugo provides more features and primitives to build scalable real-time applications. Let's summarize main Centrifugo ✨highlights✨ here. Every point is then extended throughout the documentation.

Simple integration

Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.).

It works as a standalone service with well-defined communication contracts. It nicely fits both monolithic and microservice architectures. Application developers should not change backend philosophy and technology stack at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.

Great performance

Centrifugo is fast. It's written in Go language, built on top of fast and battle-tested open-source libraries, has some smart internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTT with broker, connection hub sharding to avoid lock contention, JSON and Protobuf encoding speedups over code generation and other.

See a Million WebSocket with Centrifugo post in our blog to see some real-world numbers.

Built-in scalability

Centrifugo scales well to many machines with a help of PUB/SUB brokers. So as soon as you have more client connections in the application – you can spread them over different Centrifugo nodes which will be connected together into a cluster.

The main PUB/SUB engine Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck also.

There are other options to scale: KeyDB, Nats, Tarantool. See docs.

Strict client protocol

Centrifugo supports JSON and binary Protobuf protocol for client-server communication. The bidirectional protocol is defined by a strict schema and several ready-to-use SDKs wrap this protocol, handle asynchronous message passing, timeouts, reconnects, and various Centrifugo client API features. See the detailed information about client real-time transports in a dedicated section.

Variety of real-time transports

The main transport in Centrifugo is WebSocket. For browsers that do not support WebSocket Centrifugo provides its own bidirectional WebSocket emulation layer based on HTTP-streaming and SSE (EventSource), and also supports SockJS as an older but battle-tested WebSocket polyfill option, and WebTransport in experimental form.

Centrifugo also supports unidirectional transports for real-time updates: like SSE (EventSource), HTTP streaming, GRPC unidirectional stream. Using unidirectional transport is sufficient for many real-time applications and does not require using our client SDKs – just native standards or GRPC-generated code.

See the detailed information about client real-time transports in a dedicated section.

Flexible authentication

Centrifugo can authenticate connections using JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon client connection to Centrifugo. It's possible to proxy original request headers or request metadata (in the case of GRPC connection).

It supports the JWK specification.

Connection management

Connections can expire, developers can choose a way to handle connection refresh – using a client-side refresh workflow, or a server-side call from Centrifugo to the application backend.

Channel (room) concept

Centrifugo is a PUB/SUB server – users subscribe on channels to receive real-time updates. Message sent to a channel is delivered to all online channel subscribers.

Different types of subscriptions

Centrifugo supports client-side (initiated by a client) and server-side (forced by a server) channel subscriptions.

RPC over bidirectional connection

You can fully utilize bidirectional connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing external traffic and, in most cases, provides better latency characteristics.

Online presence information

Online presence feature for channels provides information about active channel subscribers. Also, channel join and leave events (when someone subscribes/unsubscribes) can be received on the client side.

Message history in channels

Optionally Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With a channel history, Centrifugo can help to survive the mass reconnect scenario – clients can automatically catch up missed state from a fast cache thus reducing the load on your primary database. It's also possible to manually iterate over a stream from a client or a server-side.

Embedded admin web UI

Built-in admin UI allows publishing messages to channels, look at Centrifugo cluster information, and more.

Cross-platform

Centrifugo works on Linux, macOS, and Windows.

Ready to deploy

Centrifugo supports various deploy ways: in Docker, using prepared RPM or DEB packages, via Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, has an official Grafana dashboard for Prometheus data source.

Open-source

Centrifugo stands on top of open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All our official client SDKs and API libraries are MIT-licensed.

Pro features

Centrifugo PRO extends Centrifugo with several unique features which can give interesting advantages for business adopters. For additional details, refer to the Centrifugo PRO documentation.

- + \ No newline at end of file diff --git a/docs/4/getting-started/installation.html b/docs/4/getting-started/installation.html index 5777adf95..6960c3b64 100644 --- a/docs/4/getting-started/installation.html +++ b/docs/4/getting-started/installation.html @@ -16,13 +16,13 @@ - +

Install Centrifugo

Centrifugo server is written in Go language. It's an open-source software, the source code is available on Github.

Install from the binary release

For a local development you can download prebuilt Centrifugo binary release (i.e. single all-contained executable file) for your system.

Binary releases available on Github. Download latest release for your operating system, unpack it and you are done. Centrifugo is pre-built for:

  • Linux 64-bit (linux_amd64)
  • Linux 32-bit (linux_386)
  • Linux ARM 64-bit (linux_arm64)
  • MacOS (darwin_amd64)
  • MacOS on Apple Silicon (darwin_arm64)
  • Windows (windows_amd64)
  • FreeBSD (freebsd_amd64)
  • ARM v6 (linux_armv6)

Archives contain a single statically compiled binary centrifugo file that is ready to run:

./centrifugo

If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack centrifugo binary to your current working directory:

curl -sSLf https://centrifugal.dev/install.sh | sh

See the version of Centrifugo:

./centrifugo version

Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo then there is genconfig command which generates a minimal configuration file to get started:

./centrifugo genconfig

It creates a configuration file config.json with some auto-generated option values in a current directory (by default).

tip

It's possible to generate file in YAML or TOML format, i.e. ./centrifugo genconfig -c config.toml

Having a configuration file you can finally run Centrifugo instance:

./centrifugo --config=config.json

We will talk about a configuration in detail in the next sections.

You can also put or symlink centrifugo into your bin OS directory and run it from anywhere:

centrifugo --config=config.json

Docker image

Centrifugo server has a docker image available on Docker Hub.

docker pull centrifugo/centrifugo

Run:

docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json

Note that docker allows setting nofile limits in command-line arguments which is pretty important to handle lots of simultaneous persistent connections and not run out of open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter.

caution

Pin to the exact Docker Image tag in production, for example: centrifugo/centrifugo:v4.0.0, this will help to avoid unexpected problems during re-deploy process.

Docker-compose example

Create configuration file config.json:

{
"token_hmac_secret_key": "my_secret",
"api_key": "my_api_key",
"admin_password": "password",
"admin_secret": "secret",
"admin": true
}

Create docker-compose.yml:

version: "3.9"
services:
centrifugo:
container_name: centrifugo
image: centrifugo/centrifugo:v4
volumes:
- ./config.json:/centrifugo/config.json
command: centrifugo -c config.json
ports:
- 8000:8000
ulimits:
nofile:
soft: 65535
hard: 65535

Run with:

docker-compose up

Kubernetes Helm chart

See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.

RPM and DEB packages for Linux

Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io.

At moment, we support versions of the following distributions:

  • 64-bit Debian 8 Jessie
  • 64-bit Debian 9 Stretch
  • 64-bit Debian 10 Buster
  • 64-bit Debian 11 Bullseye
  • 64-bit Ubuntu 16.04 Xenial
  • 64-bit Ubuntu 18.04 Bionic
  • 64-bit Ubuntu 20.04 Focal Fossa
  • 64-bit Centos 7
  • 64-bit Centos 8

See full list of available packages and installation instructions.

Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.

With brew on macOS

If you are developing on macOS then you can install Centrifugo over brew:

brew tap centrifugal/centrifugo
brew install centrifugo

Build from source

You need Go language installed:

git clone https://github.com/centrifugal/centrifugo.git
cd centrifugo
go build
./centrifugo
- + \ No newline at end of file diff --git a/docs/4/getting-started/integration.html b/docs/4/getting-started/integration.html index ebc3feee1..13ead1db7 100644 --- a/docs/4/getting-started/integration.html +++ b/docs/4/getting-started/integration.html @@ -16,13 +16,13 @@ - +

Integration guide

This chapter aims to help you get started with Centrifugo. We will look at a step-by-step workflow of integrating your application with Centrifugo providing links to relevant parts of this documentation.

As Centrifugo is language-agnostic and can be used together with any language/framework we won't be specific here about any backend or frontend technology your application can be built with. Only abstract steps which you can extrapolate to your application stack.

Let's look at a simplified scheme:

Centrifugo scheme

There are three parts involved in the idiomatic Centrifugo usage scenario:

  1. Your application backend
  2. Centrifugo
  3. Your clients (frontend application)

It's possible to use Centrifugo without any application backend involved but here we won't consider this use case.

Here let's suppose you already have 2 of 3 elements: clients and the backend. Now you want to add Centrifugo to receive real-time events on the client-side.

0. Install

First, you need to do is download/install Centrifugo server. See install chapter for details.

1. Configure Centrifugo

Create basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and chapter about server API for API description. The simplest way to do this automatically is by using genconfig command:

./centrifugo genconfig

– which will generate config.json file for you with a minimal set of fields to start from.

Properly configure allowed_origins option.

2. Configure your backend

In the configuration file of your application backend register several variables: Centrifugo token secret (if you decided to stick with JWT authentication) and Centrifugo API key you set on a previous step, also Centrifugo API endpoint address. By default, the API address is http://localhost:8000/api. You must never reveal token secret and API key to your users.

3. Connect to Centrifugo

Now your users can start connecting to Centrifugo. You should get a client library (see list of available client SDKs) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here.

Every client should provide a connection token (JWT) on connect. You must generate this token on your backend side using Centrifugo secret key you set to backend configuration (note that in the case of RSA tokens you are generating JWT with a private key). See how to generate this JWT in special chapter.

You pass this token from the backend to your frontend app (pass it in template context or use separate request from client-side to get user-specific JWT from backend side). And use this token when connecting to Centrifugo (for example browser client has a special method setToken).

There is also a way to authenticate connections without using JWT - see chapter about proxying to backend.

You are connecting to Centrifugo using one of the available transports.

4. Subscribe to channels

After connecting to Centrifugo subscribe clients to channels they are interested in. See more about channels in special chapter. All bidirectional client SDKs provide a way to handle messages coming to a client from a channel after subscribing to it. Learn more about client SDK possibilities from client SDK API spec.

There is also a way to subscribe connection to a list of channels on the server side at the moment of connection establishment. See chapter about server-side subscriptions.

5. Publish to channel

Everything should work now – as soon as a user opens some page of your application it must successfully connect to Centrifugo and subscribe to a channel (or channels).

Now let's imagine you want to send a real-time message to users subscribed on a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, the administrator just created a new post, the user pressed the "like" button, etc. Anyway, this is an event your backend just got, and you want to immediately send it to interested users.

You can do this using Centrifugo HTTP API. To simplify your life we have several API libraries for different languages. You can publish messages into a channel using one of those libraries or you can simply follow API description to construct API requests yourself - this is very simple. Also Centrifugo supports GRPC API. As soon as you published a message to the channel it must be delivered to your online client subscribed to that channel.

6. Deploy to production

To put this all into production you need to deploy Centrifugo on your production server. To help you with this we have many things like Docker image, rpm and deb packages, Nginx configuration. See Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.

7. Monitor Centrifugo

Don't forget to configure metrics monitoring your production Centrifugo setup. This may help you to understand what's going on with Centrifugo setup, understand number of connections, operation count and latency distributions, etc.

8. Scale Centrifugo

As soon as you are close to machine resource limits you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using Redis engine, or with KeyDB, or with Tarantool, or with Nats broker. Engines and scalability chapter describes available options in detail.

9. Read FAQ

That's all for basics. The documentation actually covers lots of other concepts Centrifugo server has: scalability, private channels, admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ – it contains lot of useful information.

- + \ No newline at end of file diff --git a/docs/4/getting-started/introduction.html b/docs/4/getting-started/introduction.html index d30f29a12..adb9897f6 100644 --- a/docs/4/getting-started/introduction.html +++ b/docs/4/getting-started/introduction.html @@ -16,13 +16,13 @@ - +

Centrifugo introduction

Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, WebTransport, GRPC, SockJS). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.

Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.

Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.

Real-time?

By real-time, we indicate a soft real-time. Due to network latencies, garbage collection cycles, and so on, the delay of a delivered message can be up to several hundred milliseconds or higher.

Background

Centrifugo was born a decade ago to help applications with a server-side written in a language or a framework without built-in concurrency support. In this case, dealing with persistent connections is a real headache that usually can only be resolved by introducing a shift in the technology stack and spending time to create a production-ready solution.

For example, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others have poor or not really performant support of working with many persistent connections for the real-time messaging tasks.

In this case, Centrifugo is a straightforward and non-obtrusive way to introduce real-time updates and handle lots of persistent connections without radical changes in the application backend architecture. Developers could proceed writing the application backend with a favorite language or favorite framework, keep existing architecture – and just let Centrifugo deal with persistent connections and be a real-time messaging transport layer.

These days Centrifugo provides some advanced and unique features that can simplify a developer's life and save months of development. Even if the application backend is built with the asynchronous concurrent language. One example is that Centrifugo has built-in support for scalability to many machines to handle more connections and still making sure channel subscribers on different Centrifugo nodes receive all the publications.

Centrifugo fits well modern architectures and may be a universal real-time component regardless of the application technology stack. There are more things to mention, the documentation uncovers them step by step.

- + \ No newline at end of file diff --git a/docs/4/getting-started/migration_v4.html b/docs/4/getting-started/migration_v4.html index 065387457..d45185c4c 100644 --- a/docs/4/getting-started/migration_v4.html +++ b/docs/4/getting-started/migration_v4.html @@ -16,13 +16,13 @@ - +

Migrating to v4

Centrifugo v4 development was concentrated around two main things:

  • adopt a new generation of client protocol
  • make namespaces secure by default

These goals dictate most of backwards compatibility changes in v4.

tip

What we would like to emphasize is that even there are many backwards incompatible changes it should be possible to migrate to Centrifugo v4 server without changing your client-side code at all. And then gradually upgrade the client-side. Below we are giving all the tips to achieve this.

Client SDK migration

New generation of client protocol requires using the latest versions of client SDKs. During the next several days we will release the following SDK versions which are compatible with Centrifugo v4:

  • centrifuge-js >= v3.0.0
  • centrifuge-go >= v0.9.0
  • centrifuge-dart >= v0.9.0
  • centrifuge-swift >= v0.5.0
  • centrifuge-java >= v0.2.0

New client SDKs support only new client protocol – you can not connect to Centrifugo v3 with them.

If you have a production system where you want to upgrade Centrifugo from v3 to v4 then the plan is:

danger

If you are using private channels (starting with $) or user-limited channels (containing #) then carefully read about subscription token migration and user-limited channels migration below.

  1. Upgrade Centrifugo and its configuration to adopt changes in v4.
  2. In Centrifugo v4 config turn on use_client_protocol_v1_by_default.
  3. Run Centrifugo v4 – all current clients should continue working with it.
  4. Then on the client-side uprade client SDK version to the one which works with Centrifugo v4, adopt changes in SDK API dictated by our new client SDK API spec. Important thing – add ?cf_protocol_version=v2 URL param to the connection endpoint to tell Centrifugo that modern generation of protocol is being used by the connection (otherwise, it assumes old protocol since we have use_client_protocol_v1_by_default option enabled).
  5. As soon as all your clients migrated to use new protocol generation you can remove use_client_protocol_v1_by_default option from the server configuration.
  6. After that you can remove ?cf_protocol_version=v2 from connection endpoint on the client-side.
tip

If you are using mobile client SDKs then most probably some time must pass while clients update their apps to use an updated Centrifugo SDK version.

tip

Starting from Centrifugo v4.1.1 it's possible to completely turn off client protocol v1 by setting disable_client_protocol_v1 boolean option to true.

Unidirectional transport migration

Client protocol framing also changed in unidirectional transports. The good news is that Centrifugo v4 still supports previous format for unidirectional transports.

When you are enabling use_client_protocol_v1_by_default option described above you also make unidirectional transports to work over old protocol format. So your existing clients will continue working just fine with Centrifugo v4. Then the same steps to migrate described above can be applied to unidirectional transport case. The only difference that in unidirectional approach you are not using Centrifugo SDKs.

SockJS migration

SockJS is now DEPRECATED in Centrifugo. Centrifugo v4 may be the last release which supports it. We now offer our own bidirectional emulation layer on top of HTTP-streaming and EventSource. See additional information in Centrifugo v4 introduction post.

Channel ASCII enforced

Centrifugo v2 and v3 docs mentioned the fact that channels must contain only ASCII characters. But it was not actually enforced by a server. Now Centrifugo is more strict. If a channel has non-ASCII characters then the 107: bad request error will be returned to the client. Please reach us out if this behavior is not suitable for your use case – we can discuss the use case and think on a proper solution together.

Subscription token migration

Subscription token now requires sub claim (current user ID) to be set.

In most cases the only change which is required to smoothly migrate to v4 without breaking things is to add a boolean option "skip_user_check_in_subscription_token": true to a Centrifugo v4 configuration. This skips the check of sub claim to contain the current user ID set to a connection during authentication.

After that start adding sub claim (with current user ID) to subscription tokens. As soon as all subscription tokens in your system contain user ID in sub claim you can remove the skip_user_check_in_subscription_token from a server configuration.

One more important note is that client claim in subscription token in Centrifugo v4 only supported for backwards compatibility. It must not be included into new subscription tokens.

It's worth mentioning that Centrifugo v4 does not allow subscribing on channels starting with $ without token even if namespace marked as available for subscribing using sth like allow_subscribe_for_client option. This is done to prevent potential security risk during v3 -> v4 migration when client previously not available to subscribe to channels starting with $ in any case may get permissions to do so.

User-limited channel migration

User-limited channel support should now be allowed over a separate channel namespace option allow_user_limited_channels. See below the namespace option converter which takes this change into account.

Namespace configuration migration

In Centrifugo v4 namespace configuration options have been changed. Centrifugo now has secure by default namespaces. First thing to do is to read the new docs about channels and namespaces.

Then you can use the following converter which will transform your old namespace configuration to a new one. This converter tries to keep backwards compatibility – i.e. it should be possible to deploy Centrifugo with namespace configuration from converter output and have the same behaviour as before regarding channel permissions. We believe that new option names should provide a more readable configuration and may help to reveal some potential security improvements in your namespace configuration – i.e. making it more strict and protective.

caution

Do not blindly deploy things to production – test your system first, go through the possible usage scenarios and/or test cases.

tip

It's fully client-side: your data won't be sent anywhere.

Here will be configuration for v4
Here will be log of changes made in your config

Proxy disconnect code changes

reconnect flag from custom disconnect code is removed. Reconnect advice is now determined by disconnect code value. This allowed us avoiding using JSON in WebSocket CLOSE frame reason. See proxy docs docs for more details.

Other configuration option changes

Several other non-namespace related options have been renamed or removed:

  • client_anonymous option renamed to allow_anonymous_connect_without_token – new name better describes the purpose of this option which was previously not clear. Converter above takes this into account.
  • use_unlimited_history_by_default option was removed. It was used to help migrating from Centrifugo v2 to v3.

Server API changes

The only breaking change is that user_connections API method (which is available in Centrifugo PRO only) was renamed to connections. The method is more generic now with a broader possibilities – so previous name does not match the current behavior.

- + \ No newline at end of file diff --git a/docs/4/getting-started/quickstart.html b/docs/4/getting-started/quickstart.html index 8438df231..8c91f3046 100644 --- a/docs/4/getting-started/quickstart.html +++ b/docs/4/getting-started/quickstart.html @@ -16,13 +16,13 @@ - +

Quickstart tutorial ⏱️

In this tutorial we will build a very simple browser application with Centrifugo. Users will connect to Centrifugo over WebSocket, subscribe to a channel, and start receiving all channel publications (messages published to that channel). In our case, we will send a counter value to all channel subscribers to update counter widget in all open browser tabs in real-time.

First you need to install Centrifugo. In this example, we are using a binary file release which is fine for development. Once you have Centrifugo binary available on your machine you can generate minimal required configuration file with the following command:

./centrifugo genconfig

This helper command will generate config.json file in the working directory with a content like this:

config.json
{
"token_hmac_secret_key": "bbe7d157-a253-4094-9759-06a8236543f9",
"admin_password": "d0683813-0916-4c49-979f-0e08a686b727",
"admin_secret": "4e9eafcf-0120-4ddd-b668-8dc40072c78e",
"api_key": "d7627bb6-2292-4911-82e1-615c0ed3eebb",
"allowed_origins": []
}

Now we can start a server. Let's start Centrifugo with a built-in admin web interface:

./centrifugo --config=config.json --admin

We could also enable the admin web interface by not using --admin flag but by adding "admin": true option to the JSON configuration file:

config.json
{
"token_hmac_secret_key": "bbe7d157-a253-4094-9759-06a8236543f9",
"admin": true,
"admin_password": "d0683813-0916-4c49-979f-0e08a686b727",
"admin_secret": "4e9eafcf-0120-4ddd-b668-8dc40072c78e",
"api_key": "d7627bb6-2292-4911-82e1-615c0ed3eebb",
"allowed_origins": []
}

And then running Centrifugo only with a path to a configuration file:

./centrifugo --config=config.json

Now open http://localhost:8000. You should see Centrifugo admin web panel. Enter admin_password value from the configuration file to log in (in our case it's d0683813-0916-4c49-979f-0e08a686b727, but you will have a different value).

Admin web panel

Inside the admin panel, you should see that one Centrifugo node is running, and it does not have connected clients:

Admin web panel

Now let's create index.html file with our simple app:

index.html
<html>

<head>
<title>Centrifugo quick start</title>
</head>

<body>
<div id="counter">-</div>
<script src="https://unpkg.com/centrifuge@3.1.0/dist/centrifuge.js"></script>
<script type="text/javascript">
const container = document.getElementById('counter');

const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: "<TOKEN>"
});

centrifuge.on('connecting', function (ctx) {
console.log(`connecting: ${ctx.code}, ${ctx.reason}`);
}).on('connected', function (ctx) {
console.log(`connected over ${ctx.transport}`);
}).on('disconnected', function (ctx) {
console.log(`disconnected: ${ctx.code}, ${ctx.reason}`);
}).connect();

const sub = centrifuge.newSubscription("channel");

sub.on('publication', function (ctx) {
container.innerHTML = ctx.data.value;
document.title = ctx.data.value;
}).on('subscribing', function (ctx) {
console.log(`subscribing: ${ctx.code}, ${ctx.reason}`);
}).on('subscribed', function (ctx) {
console.log('subscribed', ctx);
}).on('unsubscribed', function (ctx) {
console.log(`unsubscribed: ${ctx.code}, ${ctx.reason}`);
}).subscribe();
</script>
</body>

</html>

Note that we are using centrifuge-js 3.1.0 in this example, getting it from CDN, you better use its latest version at the moment of reading this tutorial. In real Javascript app you most probably will load centrifuge from NPM.

In index.html above we created an instance of a Centrifuge client passing Centrifugo server default WebSocket endpoint address to it, then we subscribed to a channel called channel and provided a callback function to process incoming real-time messages (publications). Upon receiving a new publication we update page HTML and setting counter value to page title. We call .subscribe() to initialte subscription and .connect() method of Client to start a WebSocket connection. We also handle Client state transitions (disconnected, connecting, connected) and Subscription state transitions (unsubscribed, subscribing, subscribed) – see detailed description in client SDK spec.

Now you need to serve this file with an HTTP server. In a real-world Javascript application, you will serve your HTML files with a web server of your choice – but for this simple example we can use a simple built-in Centrifugo static file server:

./centrifugo serve --port 3000

Alternatively, if you have Python 3 installed:

python3 -m http.server 3000

These commands start a simple static file web server that serves the current directory on port 3000. Make sure you still have Centrifugo server running. Open http://localhost:3000/.

Now if you look at browser developer tools or in Centrifugo logs you will notice that a connection can not be successfully established:

2021-09-01 10:17:33 [INF] request Origin is not authorized due to empty allowed_origins origin=http://localhost:3000

That's because we have not set allowed_origins in the configuration. Modify allowed_origins like this:

config.json
{
...
"allowed_origins": ["http://localhost:3000"]
}

Allowed origins is a security option for request originating from web browsers – see more details in server configuration docs. Restart Centrifugo after modifying allowed_origins in a configuration file.

Now if you reload a browser window with an application you should see new information logs in server output:

2022-06-10 09:44:21 [INF] invalid connection token error="invalid token: token format is not valid" client=a65a8463-6a36-421d-814a-0083c8836529
2022-06-10 09:44:21 [INF] disconnect after handling command client=a65a8463-6a36-421d-814a-0083c8836529 command="id:1 connect:{token:\"<TOKEN>\" name:\"js\"}" reason="invalid token" user=

We still can not connect. That's because the client should provide a valid JWT (JSON Web Token) to authenticate itself. This token must be generated on your backend and passed to a client-side (over template variables or using separate AJAX call – whatever way you prefer). Since in our simple example we don't have an application backend we can quickly generate an example token for a user using centrifugo sub-command gentoken. Like this:

./centrifugo gentoken -u 123722

– where -u flag sets user ID. The output should be like this:

HMAC SHA-256 JWT for user "123722" with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw

– you will have another token value since this one is based on randomly generated token_hmac_secret_key from the configuration file we created at the beginning of this tutorial. See token authentication docs for information about proper token generation in a real application.

Now we can copy generated HMAC SHA-256 JWT and paste it into Centrifugo constructor instead of <TOKEN> placeholder in index.html file. I.e.:

const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw"
});

If you reload your browser tab – the connection will be successfully established, but the client still can not subscribe to a channel:

2022-06-10 09:45:49 [INF] client command error error="permission denied" client=88116489-350f-447f-9ff3-ab61c9341efe code=103 command="id:2  subscribe:{channel:\"channel\"}" reply="id:2  error:{code:103  message:\"permission denied\"}" user=123722

We need to give client a permission to subscribe on the channel channel. There are several ways to do this. For example, client can provide subscription JWT for a channel. But here we will use an option to allow all authenticated clients subscribe to any channel.

To do this let's extend a server configuration with allow_subscribe_for_client option:

config.json
{
"token_hmac_secret_key": "bbe7d157-a253-4094-9759-06a8236543f9",
"admin": true,
"admin_password": "d0683813-0916-4c49-979f-0e08a686b727",
"admin_secret": "4e9eafcf-0120-4ddd-b668-8dc40072c78e",
"api_key": "d7627bb6-2292-4911-82e1-615c0ed3eebb",
"allowed_origins": ["http://localhost:3000"],
"allow_subscribe_for_client": true
}
tip

A good practice with Centrifugo is configuring channel namespaces for different types of real-time features you have in the application. By defining namespaces you can achieve a granular control over channel behavior and permissions.

Restart Centrifugo – and after doing this everything should start working. Client can successfully connect and successfully subscribe to a channel now.

Open developer tools and look at WebSocket frames panel, you should see sth like this:

Connected

Note, that in this example we generated connection JWT – but it has expiration time, so after some time Centrifugo stops accepting those tokens. In real-life you need to add a token refresh function to a client to rotate tokens. See out client API SDK spec.

OK, the last thing we need to do here is to publish a new counter value to a channel and make sure our app works properly.

We can do this over Centrifugo API sending an HTTP request to default API endpoint http://localhost:8000/api, but let's do this over the admin web panel first.

Open Centrifugo admin web panel in another browser tab (http://localhost:8000/) and go to Actions section. Select publish action, insert channel name that you want to publish to – in our case this is a string channel and insert into data area JSON like this:

{
"value": 1
}

Admin publish

Click PUBLISH button and check out the application browser tab – counter value must be immediately received and displayed.

Open several browser tabs with our app and make sure all tabs receive a message as soon as you publish it.

Message received

BTW, let's also look at how you can publish data to a channel over Centrifugo server API from a terminal using curl tool:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey d7627bb6-2292-4911-82e1-615c0ed3eebb" \
--request POST \
--data '{"method": "publish", "params": {"channel": "channel", "data": {"value": 2}}}' \
http://localhost:8000/api

– where for Authorization header we set api_key value from Centrifugo config file generated above.

We did it! We built the simplest browser real-time app with Centrifugo and its Javascript client. It does not have a backend, it's not very useful, to be honest, but it should give you an insight on how to start working with Centrifugo server. Read more about Centrifugo server in the next documentations chapters – it can do much much more than we just showed here. Integration guide describes a process of idiomatic Centrifugo integration with your application backend.

- + \ No newline at end of file diff --git a/docs/4/pro/analytics.html b/docs/4/pro/analytics.html index 1b4c52ff4..177c29e56 100644 --- a/docs/4/pro/analytics.html +++ b/docs/4/pro/analytics.html @@ -16,13 +16,13 @@ - +

Analytics with ClickHouse

This feature allows exporting information about channel publications, client connections, channel subscriptions, client operations and push notifications to ClickHouse thus providing an integration with a real-time (with seconds delay) analytics storage. ClickHouse is super fast for analytical queries, simple to operate with and it allows effective data keeping for a window of time. Also, it's relatively simple to create a high performance ClickHouse cluster.

clickhouse

This unlocks a great observability and a way to perform various analytics queries for better connection behavior understanding, check application correctness, building trends, reports, and so on.

As soon as you start using integration with ClickHouse some of mentioned possibilities may be easily accessed with Centrifugo PRO web UI and it's analytics page:

Admin analytics

Configuration

To enable integration with ClickHouse add the following section to a configuration file:

config.json
{
...
"clickhouse_analytics": {
"enabled": true,
"clickhouse_dsn": [
"tcp://127.0.0.1:9000",
"tcp://127.0.0.1:9001",
"tcp://127.0.0.1:9002",
"tcp://127.0.0.1:9003"
],
"clickhouse_database": "centrifugo",
"clickhouse_cluster": "centrifugo_cluster",
"export_connections": true,
"export_subscriptions": true,
"export_operations": true,
"export_publications": true,
"export_notifications": true,
"export_http_headers": [
"User-Agent",
"Origin",
"X-Real-Ip"
]
}
}

All ClickHouse analytics options scoped to clickhouse_analytics section of configuration.

Toggle this feature using enabled boolean option.

tip

While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo.

Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option.

You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database.

export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available.

export_subscriptions tells Centrifugo to export subscription information snapshots. Information about subscription will be exported once a subscription established and then periodically while connection alive. See below on table structure to see which fields are available.

export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available.

export_publications tells Centrifugo to export publications for channels to a separate ClickHouse table.

export_notifications tells Centrifugo to export push notifications to a separate ClickHouse table.

export_http_headers is a list of HTTP headers to export for connection information.

export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport.

skip_schema_initialization - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process.

skip_ping_on_start - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.

Connections table

SHOW CREATE TABLE centrifugo.connections;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.connections
(
`client` String,
`user` String,
`name` String,
`version` String,
`transport` String,
`headers` Map(String, Array(String)),
`metadata` Map(String, Array(String)),
`time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.connections_distributed;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.connections_distributed
(
`client` String,
`user` String,
`name` String,
`version` String,
`transport` String,
`headers` Map(String, Array(String)),
`metadata` Map(String, Array(String)),
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client))
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

Subscriptions table

SHOW CREATE TABLE centrifugo.subscriptions

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.subscriptions
(
`client` String,
`user` String,
`channels` Array(String),
`time` DateTime
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.subscriptions_distributed;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.subscriptions_distributed
(
`client` String,
`user` String,
`channels` Array(String),
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'subscriptions', murmurHash3_64(client))
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

Operations table

SHOW CREATE TABLE centrifugo.operations;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations
(
`client` String,
`user` String,
`op` String,
`channel` String,
`method` String,
`error` UInt32,
`disconnect` UInt32,
`duration` UInt64,
`time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.operations_distributed;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations_distributed
(
`client` String,
`user` String,
`op` String,
`channel` String,
`method` String,
`error` UInt32,
`disconnect` UInt32,
`duration` UInt64,
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client))
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Publications table

SHOW CREATE TABLE centrifugo.publications

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.publications
(
`channel` String,
`source` String,
`size` UInt64,
`client` String,
`user` String,
`time` DateTime
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.publications_distributed;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations_distributed
(
`channel` String,
`source` String,
`size` UInt64,
`client` String,
`user` String,
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'publications', murmurHash3_64(channel))
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Notifications table

🚧 This PRO feature is under construction together with push notification API.

SHOW CREATE TABLE centrifugo.notifications

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.notifications
(
`uid` String,
`provider` String,
`type` String,
`recipient` String,
`device_id` String,
`platform` String,
`user` String,
`msg_id` String,
`status` String,
`error_message` String,
`error_code` String,
`time` DateTime
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.notifications_distributed;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations_distributed
(
`uid` String,
`provider` String,
`type` String,
`recipient` String,
`device_id` String,
`platform` String,
`user` String,
`msg_id` String,
`status` String,
`error_message` String,
`error_code` String,
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'notifications', murmurHash3_64(uid))
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Query examples

Show unique users which were connected:

SELECT DISTINCT user
FROM centrifugo.connections_distributed;

┌─user─────┐
│ user_1 │
│ user_2 │
│ user_3 │
│ user_4 │
│ user_5 │
└──────────┘

Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111):

SELECT COUNT(*)
FROM centrifugo.operations_distributed
WHERE (error = 111) AND (op = 'publish');

┌─count()─┐
4502
└─────────┘

The same for a specific user:

SELECT COUNT(*)
FROM centrifugo.operations_distributed
WHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');

┌─count()─┐
1214
└─────────┘

Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since subscriptions table contain periodic snapshot entries, clients could unsubscribe in between snapshots – this is reflected in operations table):

SELECT COUNT(Distinct(user))
FROM centrifugo.subscriptions_distributed
WHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));

┌─uniqExact(user)─┐
101
└─────────────────┘

Show top 10 users which called publish operation during last one minute:

SELECT
COUNT(op) AS num_ops,
user
FROM centrifugo.operations_distributed
WHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))
GROUP BY user
ORDER BY num_ops DESC
LIMIT 10;

┌─num_ops─┬─user─────┐
56 │ user_200 │
11 │ user_75 │
6 │ user_87 │
6 │ user_65 │
6 │ user_39 │
5 │ user_28 │
5 │ user_63 │
5 │ user_89 │
3 │ user_32 │
3 │ user_52 │
└─────────┴──────────┘

Show total number of push notifications to iOS devices sent during last 24 hours:

SELECT COUNT(*)
FROM centrifugo.notifications
WHERE (time > (now() - toIntervalHour(24))) AND (platform = 'ios')

┌─count()─┐
31200
└─────────┘

Development

The recommended way to run ClickHouse in production is with cluster. See an example of such cluster configuration made with Docker Compose.

But during development you may want to run Centrifugo with single instance ClickHouse.

To do this set only one ClickHouse dsn and do not set cluster name:

config.json
{
...
"clickhouse_analytics": {
"enabled": true,
"clickhouse_dsn": [
"tcp://127.0.0.1:9000"
],
"clickhouse_database": "centrifugo",
"clickhouse_cluster": "",
"export_connections": true,
"export_subscriptions": true,
"export_publications": true,
"export_operations": true,
"export_http_headers": [
"Origin",
"User-Agent"
]
}
}

Run ClickHouse locally:

docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click clickhouse/clickhouse-server

Run ClickHouse client:

docker run -it --rm --link click:clickhouse-server --entrypoint clickhouse-client clickhouse/clickhouse-server --host clickhouse-server

Issue queries:

:) SELECT * FROM centrifugo.operations

┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │
└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘

How export works

When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 10 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are configurable. Centrifugo PRO uses very efficient code for writing data to ClickHouse, so analytics feature should only add a little overhead for Centrifugo node.

Several metrics are exposed to monitor export process health:

  • centrifugo_clickhouse_analytics_flush_duration_seconds summary
  • centrifugo_clickhouse_analytics_batch_size summary
  • centrifugo_clickhouse_analytics_drop_count counter
- + \ No newline at end of file diff --git a/docs/4/pro/capabilities.html b/docs/4/pro/capabilities.html index 046d8a501..d1d3757cc 100644 --- a/docs/4/pro/capabilities.html +++ b/docs/4/pro/capabilities.html @@ -16,13 +16,13 @@ - +

Channel capabilities

At this point you know that Centrifugo allows configuring channel permissions on a per-namespace level. When creating a new real-time feature it's recommended to create a new namespace for it and configure permissions. To achieve a better channel permission control inside a namespace Centrifugo PRO provides possibility to set capabilities on individual connection basis, or individual channel subscription basis.

Let's start by looking at connection-wide capabilities first.

Connection capabilities

Connection capabilities can be set:

  • in connection JWT (in caps claim)
  • in connect proxy result (caps field)

For example, here we are issuing permissions to subscribe on channel news and channel user_42 to a client:

{
"caps": [
{
"channels": ["news", "user_42"],
"allow": ["sub"]
}
]
}

Known capabilities:

  • sub - subscribe to a channel to receive publications from it
  • pub - publish into a channel (your backend won't be able to process the publication in this case)
  • prs - call presence and presence stats API, also consume join/leave events upon subscribing
  • hst - call history API, also make Subscription positioned or recoverable upon subscribing

Caps processing behavior

Centrifugo processes caps objects till it finds a match to a channel. At this point it applies permissions in the matched object and stops processing remaining caps. If no match found – then 103 permission denied returned to a client (of course if namespace does not have other permission-related options enabled). Let's consider example like this:

WRONG!
{
"caps": [
{
"channels": ["news"],
"allow": ["pub"]
},
{
"channels": ["news"],
"allow": ["sub"]
},
]
}

Here we have two entries for channel news, but when client subscribes on news only the first entry will be taken into considiration by Centrifugo – so Subscription attempt will be rejected (since first cap object does not have sub capability). In real life you don't really want to have cap objects with identical channels – but below we will introduce wildcard matching where understanding how caps processed becomes important.

Another example:

WRONG!
{
"caps": [
{
"channels": ["news", "user_42"],
"allow": ["sub"]
},
{
"channels": ["user_42"],
"allow": ["pub", "hst", "prs"]
},
]
}

One could expect that client will have ["sub", "pub", "hst", "prs"] capabilities for a channel user_42. But it's not true since Centrifugo processes caps objects and channels inside caps object in order – it finds a match to user_42 in first caps object, it contains only "sub" capability, processing stops. So user can subscribe to a channel, but can not publish, can not call history and presence APIs even though those capabilities are mentioned in caps object. The correct way to give all caps to the channel user_42 would be to split channels into different caps objects:

CORRECT
{
"caps": [
{
"channels": ["news"],
"allow": ["sub"]
},
{
"channels": ["user_42"],
"allow": ["sub", "pub", "hst", "prs"]
},
]
}

The processing behaves like this to avoid potential problems with possibly conflicting matches (mostly when using wildcard and regex matching – see below) and still allow overriding capabilities for specific channels.

Expiration considirations

  • In JWT auth case – capabilities in JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific.
  • In connect proxy case – capabilities will work until client connection close (disconnect) or connection refresh triggered (with refresh proxy you can provide an updated set of capabilities).

Revoking connection caps

If at some point you need to revoke some capability from a client:

  • Simplest way is to wait for a connection expiration, then upon refresh:
    • if using proxy – provide new caps in refresh proxy result, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities).
    • if JWT auth - provide new caps in connection token, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities).
  • In case of using connect proxy – you can disconnect a user (or client) with a reconnect code. New capabilities will be asked upon reconnection.
  • In case of using token auth – revoke token (Centrifugo PRO feature) and disconnect user (or client) with reconnect code. Upon reconnection user will receive an error that token revoked and will try to load a new one.

Example: wildcard match

It's possible to use wildcards in channel resource names. For example, let's give a permission to subscribe on all channels in news namespace.

{
"caps": [
{
"channels": ["news:*"],
"match": "wildcard",
"allow": ["sub"]
}
]
}
note

Match type is used for all channels in caps object. If you need different matching behavior for different channels then split them on different caps objects.

Example: regex match

Or regex:

{
"caps": [
{
"channels": ["^posts_[\d]+$"],
"match": "regex",
"allow": ["sub"]
}
]
}

Example: different types of match

Of course it's possible to combine different types of match inside one caps array:

{
"caps": [
{
"channels": ["^posts_[\d]+$"],
"match": "regex",
"allow": ["sub"]
}
{
"channels": ["user_42"],
"allow": ["sub"]
}
]
}

Example: full access to all channels

Let's look how to allow all permissions to a client:

{
"caps": [
{
"channels": ["*"],
"match": "wildcard",
"allow": ["sub", "pub", "hst", "prs"]
}
]
}
Full access warn

Should we mention that giving full access to a client is something to wisely consider? 🤔

Subscription capabilities

Subscription capabilities can be set:

  • in subscription JWT (in allow claim)
  • in subscribe proxy result (allow field)

Subscription token already belongs to a channel (it has a channel claim). So users with a valid subscription token can subscribe to a channel. But it's possible to additionally grant channel permissions to a user for publishing and calling presence and history using allow claim:

{
"allow": ["pub", "hst", "prs"]
}

Putting sub permission to the Subscription token does not make much sense – Centrifugo only expects valid token for a subscription permission check.

Expiration considirations

  • In JWT auth case – capabilities in subscription JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific.
  • In subscribe proxy case – capabilities will work until client unsubscribe (or connection close).

Revoking subscription permissions

If at some point you need to revoke some capability from a client:

  • Simplest way is to wait for a subscription expiration, then upon refresh:
    • provide new caps in subscription token, Centrifugo will update channel caps.
  • In case of using subscribe proxy – you can unsubscribe a user (or client) with a resubscribe code. Or disconnect with reconnect code. New capabilities will be set up upon resubscription/reconnection.
  • In case of using JWT auth – revoke token (Centrifugo PRO feature) and unsubscribe/disconnect user (or client) with resubscribe/reconnect code. Upon resubscription/reconnection user will receive an error that token revoked and will try to load a new one.
- + \ No newline at end of file diff --git a/docs/4/pro/cel_expressions.html b/docs/4/pro/cel_expressions.html index e78121735..e5debeebf 100644 --- a/docs/4/pro/cel_expressions.html +++ b/docs/4/pro/cel_expressions.html @@ -16,13 +16,13 @@ - +

CEL expressions

This PRO feature is under active development, some changes expected here 🚧

Centrifugo PRO supports CEL expressions (Common Expression Language) for checking channel operation permissions.

CEL expressions provide a developer-friendly, fast and secure way to evaluate some conditions predefined in the configuration. They are used in some Google services (ex. Firebase), in Envoy RBAC configuration, etc.

For Centrifugo this is a flexible mechanism which can help to avoid using subscription tokens or using subscribe proxy in some cases. This means you can avoid sending an additional HTTP request to the backend for a channel subscription attempt. As the result less resources may be used and smaller latencies may be achieved in the system. This is a way to introduce efficient channel permission mechanics when Centrifugo built-in rules are not enough.

Some good links which may help you dive into CEL expressions are:

Below we will explore some basic expressions and show how they can be used in Centrifugo.

subscribe_cel

We suppose that the main operation for which developers may use CEL expressions in Centrifugo is a subscribe operation. Let's look at it in detail.

It's possible to configure subscribe_cel for a channel namespace (subscribe_cel is just an additional namespace channel option, with same rules applied). This expression should be a valid CEL expression.

config.json
{
"namespaces": [
{
"name": "admin",
"subscribe_cel": "'admin' in meta.roles"
}
]
}

In the example we are using custom meta information (must be an object) attached to the connection. As mentioned before in the doc this meta may be attached to the connection:

An expression is evaluated for every subscription attempt to a channel in a namespace. So if meta attached to the connection is sth like this:

{
"roles": ["admin"]
}

– then for every channel in the admin namespace defined above expression will be evaluated to True and subscription will be accepted by Centrifugo.

tip

meta must be JSON object (any {}) for CEL expressions to work.

Expression variables

Inside the expression developers can use some variables which are injected by Centrifugo to the CEL runtime.

Information about current user ID, meta information attached to the connection, all the variables defined in matched channel pattern will be available for CEL expression evaluation.

Say client with user ID 123 subscribes to a channel /users/4 which matched the channel pattern /users/:user:

VariableTypeExampleDescription
subscribedboolfalseWhether client is subscribed to channel, always false for subscribe operation
userstring"123"Current authenticated user ID (known from from JWT or connect proxy result)
metamap[string]any{"roles": ["admin"]}Meta information attached to the connection by the apllication backend (in JWT or over connect proxy result)
channelstring"/users/4"Channel client tries to subscribe
varsmap[string]string{"user": "4"}Extracted variables from the matched channel pattern. It's empty in case of using channels without variables.

In this case, to allow admin to subscribe on any user's channel or allow non-admin user to subscribe only on its own channel, you may construct an expression like this:

{
...
"subscribe_cel": "vars.user == user or 'admin' in meta.roles"
}

Let's look at one more example. Say client with user ID 123 subscribes to a channel /example.com/users/4 which matched the channel pattern /:tenant/users/:user. The permission check may be transformed into sth like this (assuming meta information has information about current connection tenant):

{
"namespaces": [
{
"name": "/:tenant/users/:user",
"subscribe_cel": "vars.tenant == meta.tenant && (vars.user == user or 'admin' in meta.roles)"
}
]
}

publish_cel

CEL expression to check permissions to publish into a channel. Same expression variables are available.

history_cel

CEL expression to check permissions for channel history. Same expression variables are available.

presence_cel

CEL expression to check permissions for channel presence. Same expression variables are available.

- + \ No newline at end of file diff --git a/docs/4/pro/channel_patterns.html b/docs/4/pro/channel_patterns.html index ce4f4d39a..a55861010 100644 --- a/docs/4/pro/channel_patterns.html +++ b/docs/4/pro/channel_patterns.html @@ -16,13 +16,13 @@ - +

Channel patterns

This PRO feature is under active development, some changes expected here 🚧

Centrifugo PRO enhances a way to configure channels with Channel Patterns feature. This opens a road for building channel model similar to what developers got used to when writing HTTP servers and configuring routes for HTTP request processing.

Configuration

Let's look at the example:

{
// rest of the config ...
"channel_patterns": true, // required to turn on the feature.
"namespaces": [
{
"name": "/users/:name"
// namespace options may go here ...
},
{
"name": "/events/:project/:type"
// namespace options may go here ...
}
]
}

As soon as namespace name starts with / - it's considered a channel pattern. Just like an HTTP path it consists of segments delimited by /. The : symbol in the segment beginning defines a variable part – more information below.

In this case a channel to be used must be sth like /users/mario - i.e. start with / and match one of the patterns defined in the configuration. So this channel pattern matching mechanics behaves mostly like HTTP route matching in many frameworks.

Given the configuration example above:

  • if channel is /users/mario, then the namespace with the name /users/:name will match and we apply all the options defined for it to the channel.
  • if channel is /events/42/news, then the namespace with the name /events/:project/:type will match.
  • if channel is /events/42, then no namespace will match and the unknown channel error will be returned.
Basic example demonstrating use of pattern channels in JS
const client := new Centrifuge("ws://...", {});
const sub = client.newSubscription('/users/mario');
sub.subscribe();
client.connect();

Implementation details

Some implementation restrictions and details to know about:

  • When using channel patterns feature : symbol in a namespace name defines a variable part. It's not related to a namespace separator anymore – the entire channel is matched over the channel pattern. Similar to the HTTP routes semantics. So namespace separator is not needed at all when using channel patterns.
  • Centrifugo only allows explicit channel pattern matching which do not result into channel pattern conflicts in runtime, this is checked during configuration validation on server start. Explicitly defined static patterns (without variables) have precedence over patterns with variables.
  • There is no analogue of top-level namespace (like we have for standard namespace configuration) for channels starting with /. If a channel does not match any explicitly defined pattern then Centrifugo returns the 102: unknown channel error.
  • If you define channel_regex inside channel pattern options – then regex matches over the entire channel (since variable parts are located in the namespace name in this case).
  • Channel pattern must only contain ASCII characters.
  • Duplicate variable names are not allowed inside an individual pattern, i.e. defining /users/:user/:user will result into validation error on start.

Variables

: in the channel pattern name helps to define a variable to match against. Named parameters only match a single segment of the channel:

Channel pattern "/users/:name":

/users/mary ✅ match
/users/john ✅ match
/users/mary/info ❌ no match
/users ❌ no match

Another example for channel pattern /news/:type/:subtype, i.e. with multiple variables:

Channel pattern "/news/:type/:subtype":

/news/sport/football ✅ match
/news/sport/volleyball ✅ match
/news/sport ❌ no match
/news ❌ no match

Channel patterns support mid-segment variables, so the following is possible:

Channel pattern "/personal/user_:user":

/personal/user_mary ✅ match
/personal/user_john ✅ match
/personal/user_ ❌ no match

Using varibles

Additional benefits of using channel patterns may be achieved together with Centrifugo PRO CEL expressions. Channel pattern variables are available inside CEL expressions for evaluation in a custom way.

- + \ No newline at end of file diff --git a/docs/4/pro/client_message_batching.html b/docs/4/pro/client_message_batching.html index aad74048f..a6552cf00 100644 --- a/docs/4/pro/client_message_batching.html +++ b/docs/4/pro/client_message_batching.html @@ -16,13 +16,13 @@ - +

Message batching control

This PRO feature is under active development, some changes expected here 🚧

Centrifugo PRO provides advanced options to tweak connection message write behaviour.

By default, Centrifugo tries to write messages to clients as fast as possible. Centrifugo also does best effort combining different protocol messages into one transport frame (to reduce system calls and thus reduce CPU usage) without sacrificing delivery latency.

But still in this model if you have a lot of messages sent to each individual connection, you may have a lot of write system calls. These system calls have an huge impact on the server CPU utilization. Sometimes you want to trade-off delivery latency in favour of lower CPU consumption by Centrifugo node. It's possible to do by telling Centrifugo to slow down message delivery and collect messages to larger batches before sending them towards individual client. To achieve that Centrifugo PRO exposes additional configuration options.

tip

Note, this is only useful when you have lots of messages per client. This specific feature won't be helpful with a case when the message is broadcasted towards many different connections as the feature described here only batches message writing it terms of a single socket.

client_write_delay

The client_write_delay is a duration option, it is a time Centrifugo will try to collect messages inside each connection message write loop before sending them towards the connection.

Enabling client_write_delay may reduce CPU usage of both server and client in case of high message rate inside individual connections. The reduction happens due to the lesser number of system calls to execute. Enabling client_write_delay limits the maximum throughput of messages towards the connection which may be achieved. For example, if client_write_delay is 100ms then the max throughput per second will be (1000 / 100) * client_max_messages_in_frame (16 by default), i.e. 160 messages per second. Though this should be more than enough for target Centrifugo use cases (frontend apps).

Example:

config.json
{
// Rest of config here ...
"client_write_delay": "100ms"
}

client_reply_without_queue

The client_reply_without_queue is a boolean option to not use client queue for replies to commands. When true replies are written to the transport without going through the connection message queue.

client_max_messages_in_frame

The client_max_messages_in_frame is an integer option which controls the maximum number of messages which may be joined by Centrifugo into one transport frame. By default, 16. Use -1 for unlimited number.

- + \ No newline at end of file diff --git a/docs/4/pro/connections.html b/docs/4/pro/connections.html index 28dc501fd..184330979 100644 --- a/docs/4/pro/connections.html +++ b/docs/4/pro/connections.html @@ -16,13 +16,13 @@ - +

Connections API

Centrifugo PRO offers an extra API call, connections, which enables retrieval of all active sessions (based on user ID or expression) without the need to activate the presence feature for channels. Furthermore, developers can attach any desired JSON payload to a connection that will then be visible in the result of the connections call. It's worth noting that this additional meta-information remains hidden from the client-side, unlike the info associated with the connection.

This feature serves a valuable purpose in managing active user sessions, particularly for messenger applications. Users can review their current sessions and terminate some of them using the Centrifugo disconnect server API.

Moreover, this feature can help developers investigate issues by providing insights into the system's state.

Example

Let's look at the quick example. First, generate a JWT for user 42:

$ centrifugo genconfig

Generate token for some user to be used in the example connections:

$ centrifugo gentoken -u 42
HMAC SHA-256 JWT for user 42 with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y

Run Centrifugo with uni_http_stream transport enabled (it will allow us connecting from the terminal with curl):

CENTRIFUGO_UNI_HTTP_STREAM=1 centrifugo -c config.json

Create new terminal window and run:

curl -X POST http://localhost:8000/connection/uni_http_stream --data '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y", "name": "terminal"}'

In another terminal create one more connection:

curl -X POST http://localhost:8000/connection/uni_http_stream --data '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y", "name": "terminal"}'

Now let's call connections over HTTP API:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "connections", "params": {"user": "42"}}' \
http://localhost:8000/api

The result:

{
"result": {
"connections": {
"db8bc772-2654-4283-851a-f29b888ace74": {
"app_name": "terminal",
"transport": "uni_http_stream",
"protocol": "json"
},
"4bc3ca70-ecc5-439d-af14-a78ae18e31c7": {
"app_name": "terminal",
"transport": "uni_http_stream",
"protocol": "json"
}
}
}
}

Here we can see that user has 2 connections from terminal app.

Each connection can be annotated with meta JSON information which is set during connection establishment (over meta claim of JWT or by returning meta in the connect proxy result).

connections

Returns information about active connections according to the request.

connections params

Parameter nameParameter typeRequiredDescription
userstringnofast filter by User ID
expressionstringnoCEL expression to filter users

connections result

Field nameField typeOptionalDescription
connectionsmap[string]ConnectionInfonoactive user connections map where key is client ID and value is ConnectionInfo

ConnectionInfo

Field nameField typeOptionalDescription
app_namestringyesclient app name (if provided by client)
app_versionstringyesclient app version (if provided by client)
transportstringnoclient connection transport
protocolstringnoclient connection protocol (json or protobuf)
userstringyesclient user ID
stateConnectionStateyesconnection state

ConnectionState object

Field nameField typeOptionalDescription
channelsmap[string]ChannelContextyesChannels client subscribed to
connection_tokenConnectionTokenInfoyesinformation about connection token
subscription_tokensmap<string, SubscriptionTokenInfo>yesinformation about channel tokens used to subscribe
metaJSON objectyesmeta information attached to a connection

ChannelContext object

Field nameField typeOptionalDescription
sourceintyesThe source of channel subscription

ConnectionTokenInfo object

Field nameField typeOptionalDescription
uidstringyesunique token ID (jti)
issued_atintyestime (Unix seconds) when token was issued

SubscriptionTokenInfo object

Field nameField typeOptionalDescription
uidstringyesunique token ID (jti)
issued_atintyestime (Unix seconds) when token was issued
- + \ No newline at end of file diff --git a/docs/4/pro/install_and_run.html b/docs/4/pro/install_and_run.html index 6fbda428c..06edcf425 100644 --- a/docs/4/pro/install_and_run.html +++ b/docs/4/pro/install_and_run.html @@ -16,13 +16,13 @@ - +

Install and run PRO version

Centrifugo PRO license agreement

Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.

Binary release

Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).

Docker image

Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro:

docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v4.0.0-beta.10 centrifugo -c config.json

Kubernetes

You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag:

values.yaml
...
image:
registry: docker.io
repository: centrifugo/centrifugo-pro
tag: v4.0.0-beta.10

Debian and Ubuntu

DEB package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v4.0.0-beta.10/centrifugo-pro_4.0.0-beta.10_amd64.deb
sudo dpkg -i centrifugo-pro_4.0.0-beta.10_amd64.deb

Centos

RPM package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v4.0.0-beta.10/centrifugo-pro-4.0.0-beta.10.x86_64.rpm
sudo yum install centrifugo-pro-4.0.0-beta.10.x86_64.rpm

Setting PRO license key

Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits.

Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this:

config.json
{
...
"license": "<YOUR_LICENSE_KEY>"
}
tip

If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.

- + \ No newline at end of file diff --git a/docs/4/pro/overview.html b/docs/4/pro/overview.html index f05271531..5e31d1c15 100644 --- a/docs/4/pro/overview.html +++ b/docs/4/pro/overview.html @@ -16,13 +16,13 @@ - +

Centrifugo PRO overview

Centrifugo PRO is the enhanced version of Centrifugo provided by Centrifugal Labs LTD under commercial license. It's packed with a set of unique powerful features that offer exceptional benefits to your business. It provides granular channel permission control, lower CPU utilization on Centrifugo nodes, backend protection from misusing, next level system observability, additional APIs, and more.

All the features of Centrifugo PRO come with a decent scalable performance. Some reuse Centrifugo super fast Redis communication capabilities. ClickHouse analytics built on top of efficient approach with the minimal overhead. We've put a lot of love into all of the extra powers of Centrifugo to make sure they are practical and ready for production workloads.

Features

Centrifugo PRO is packed with the following features:

info

PRO features can change with time. We reserve a right to move features from PRO to OSS version if there is a clear signal that this is required to do for the ecosystem.

Try for free in sandbox mode

You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example:

  • Centrifugo handles up to 20 concurrent connections
  • up to 2 server nodes supported
  • up to 10 API requests per second allowed

This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future.

Centrifugo PRO license agreement

Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.

Pricing

To run without limits Centrifugo PRO requires a license key.

At this point we are not issuing license keys for Centrifugo PRO as we are in the process of defining pricing strategy and distribution model for it. Please contact us over centrifugal.dev@gmail.com – so we can add you to the list of interested customers. Will appreciate if you share which PRO features you are mostly interested in.

- + \ No newline at end of file diff --git a/docs/4/pro/performance.html b/docs/4/pro/performance.html index 0ca9c3e3d..67085cbc3 100644 --- a/docs/4/pro/performance.html +++ b/docs/4/pro/performance.html @@ -16,13 +16,13 @@ - +

Faster performance

Centrifugo PRO has performance improvements for several server parts. These improvements can help to reduce tail end-to-end latencies in the application, increase server throughput and/or reduce CPU usage on server machines. Our open-source version has a decent performance by itself, with PRO improvements Cenrifugo steps even further.

Faster HTTP API

Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API.

The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.

Faster GRPC API

Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster HTTP proxy

Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster GRPC proxy

Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster JWT decoding

Centrifugo PRO has an optimized decoding of JWT claims.

Faster GRPC unidirectional stream

Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.

Examples

Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.

Publish HTTP API

In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.

History HTTP API

In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.

- + \ No newline at end of file diff --git a/docs/4/pro/process_stats.html b/docs/4/pro/process_stats.html index 9235e578e..d7de7de71 100644 --- a/docs/4/pro/process_stats.html +++ b/docs/4/pro/process_stats.html @@ -16,13 +16,13 @@ - +

CPU and RSS stats

A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI.

Here is how this looks like:

Process stats

The information updated in near real-time (with several seconds delay). It's also available as part of info API.

- + \ No newline at end of file diff --git a/docs/4/pro/push_notifications.html b/docs/4/pro/push_notifications.html index 7c27d7d1e..0aba46986 100644 --- a/docs/4/pro/push_notifications.html +++ b/docs/4/pro/push_notifications.html @@ -16,13 +16,13 @@ - +

Push notification API

This PRO feature is under active development, some changes expected here 🚧

Centrifugo excels in delivering real-time in-app messages to online users. Sometimes though you need a way to engage offline users to come back to your app. Or trigger some update in the app while it's running in the background. That's where push notifications may be used. Push notifications delivered over battery-efficient platform-dependent transport.

With Centrifugo PRO push notifications may be delivered to all popular application platforms:

  • Android devices
  • iOS devices
  • Web browsers which support Web Push API (Chrome, Firefox, see this matrix)

Centrifugo PRO provides API to manage user device tokens, device topic subscriptions and API to send push notifications towards registered devices and group of devices (subscribed to a topic).

Push

To deliver push notifications to devices Centrifugo PRO integrates with the following providers:

Centrifugo PRO provides a comprehensive solution for sending push notifications by incorporating frontend SDKs from FCM, HMS, and Apple SDKs.

While these push notification providers handle the frontend and transport aspects of notification delivery, device token management and efficient push notification broadcasting still need to be addressed by the application backend. Centrifugo PRO offers an API for storing tokens in a PostgreSQL database and managing device subscriptions to topics in a secure, unified manner.

To facilitate efficient push notification broadcasting towards devices, Centrifugo PRO includes worker queues based on Redis streams.

Integration with FCM means that you can use existing Firebase messaging SDKs to extract push notification token for a device on different platforms (iOS, Android, Flutter, web browser) and setting up push notification listeners. The same for HMS and APNs - just use existing native SDKs and best practices on the frontend. Only a couple of additional steps required to integrate frontend with Centrifugo PRO device token and device topic storage. After doing that you will be able to send push notification towards single device, or towards group of devices subscribed to a topic. For example, with a simple Centrifugo API call like this:

curl -X POST http://localhost:8000/api \
-H "Authorization: apikey <KEY>" \
-d @- <<'EOF'

{
"method": "send_push_notification",
"params": {
"recipient": {"topics": ["test"]},
"notification": {
"fcm": {
"message": {
"notification": {"title": "Hello", "body": "How are you?"}
}
}
}
}
}
EOF

Motivation and design choices

We tried to be practical with our Push Notification API, let's look at its design choices and implementation properties we were able to achieve.

Storage for tokens

To start delivering push notifications in the application, developers usually need to integrate with providers such as FCM, HMS, and APNs. This integration typically requires the storage of device tokens in the application database and the implementation of sending push messages to provider push services.

Centrifugo PRO simplifies the process by providing a backend for device token storage, following best practices in token management. It reacts to errors and periodically removes stale devices/tokens to maintain a working set of device tokens based on provider recommendations.

Efficient queuing

Additionally, Centrifugo PRO provides an efficient, scalable queuing mechanism for sending push notifications. Developers can send notifications from the app backend to Centrifugo API with minimal latency and let Centrifugo process sending to FCM, HMS, APNs concurrently using built-in workers. In our tests, we achieved hundreds of thousands of pushes in tens of seconds.

Unified secure topics

FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that client can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM, HMS topics by introducing an additional API to manage device subscriptions to topics.

tip

In some cases you may have real-time channels and device subscription topics with matching names – to send messages to both online and offline users. Though it's up to you.

Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs.

Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. Then – as soon as user registers a device – it will be automatically subscribed to its own topics. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with FCM – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call.

Non-obtrusive proxying

Unlike other solutions that combine different provider push sending APIs into a unified API, Centrifugo PRO provides a non-obtrusive proxy for all the mentioned providers. Developers can send notification payloads in a format defined by each provider.

It's also possible to send notifications into native FCM, HMS topics or send to raw FCM, HMS, APNs tokens using Centrifugo PRO's push API, allowing them to combine native provider primitives with those added by Centrifugo (i.e., sending to a list of device IDs or to a list of topics).

Builtin analytics

Furthermore, Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API.

Steps to integrate

  1. Add provider SDK on the frontend side, follow provider instructions for your platform to obtain a push token for a device. For example, for FCM see instructions for iOS, Android, Flutter, Web Browser). The same for HMS or APNs – frontend part should be handled by their native SDKs.
  2. Call Centrifugo PRO backend API with the obtained token. From the application backend call Centrifugo device_register API to register the device in Centrifugo PRO storage. Optionally provide list of topics to subscribe device to.
  3. Centrifugo returns a registered device object. Pass a generated device ID to the frontend and save it on the frontend together with a token received from FCM.
  4. Call Centrifugo send_push_notification API whenever it's time to deliver a push notification.

At any moment you can inspect device storage by calling device_list API.

Once user logs out from the app, you can detach user ID from device by using device_update or remove device with device_remove API.

Configuration

In Centrifugo PRO you can configure one push provider or use all of them – this choice is up to you.

FCM

As mentioned above Centrifigo uses PostgreSQL for token storage. To enable push notifications make sure database section defined in the configration and fcm is in the push_notifications.enabled_providers list. Centrifugo PRO uses Redis for queuing push notification requests, so Redis address should be configured also. Finally, to integrate with FCM a path to the credentials file must be provided (see how to create one in this instruction). So the full configuration to start sending push notifications over FCM may look like this:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"redis_address": "localhost:6379",
"enabled_providers": ["fcm"],
"fcm_credentials_file_path": "/path/to/service/account/credentials.json"
}
}
tip

Actually, PostgreSQL database configuration is optional here – you can use push notifications API without it. In this case you will be able to send notifications to FCM, HMS, APNs raw tokens, FCM and HMS native topics and conditions. I.e. using Centrifugo as an efficient proxy for push notifications (for example if you already keep tokens in your database). But sending to device ids and topics, and token/topic management APIs won't be available for usage.

HMS

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"redis_address": "localhost:6379",
"enabled_providers": ["hms"],
"hms_app_id": "<your_app_id>",
"hms_app_secret": "<your_app_secret>",
}
}
tip

See example how to get app id and app secret here.

APNs

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"redis_address": "localhost:6379",
"enabled_providers": ["apns"],
"apns_endpoint": "development",
"apns_bundle_id": "com.example.your_app",
"apns_auth": "token",
"apns_token_auth_key_path": "/path/to/auth/key/file.p8",
"apns_token_key_id": "<your_key_id>",
"apns_token_team_id": "your_team_id",
}
}

We also support auth over p12 certificates with the following options:

  • push_notifications.apns_cert_p12_path
  • push_notifications.apns_cert_p12_b64
  • push_notifications.apns_cert_p12_password

Other options

push_notifications.max_inactive_device_days

This option configures the number of days to keep device without updates. By default Centrifugo does not remove inactive devices.

Use PostgreSQL as queue

Coming soon 🚧

Centrifugo PRO utilizes Redis Streams as the default queue engine for push notifications. However, it also offers the option to employ PostgreSQL for queuing. It's as simple as:

config.json
{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"queue_engine": "database",
// rest of the options...
}
}
tip

Queue based on Redis streams is faster, so if you start with PostgreSQL based queue – you have an option to switch to a more performant implementation later. Though active push notifications will be lost during a switch.

API description

device_register

Registers or updates device information.

device_register request

FieldTypeRequiredDescription
idstringNoID of the device being registered (provide it when updating).
providerstringYesProvider of the device token (valid choices: fcm, hms, apns).
tokenstringYesPush notification token for the device.
platformstringYesPlatform of the device (valid choices: ios, android, web).
userstringNoUser associated with the device.
topicsarray of stringsNoDevice topic subscriptions. This should be a full list which replaces all the topics previously accociated with the device. User topics managed by UserTopic model will be automatically attached.
tagsmap<string, string>NoAdditional tags for the device (indexed key-value data).
metamap<string, string>NoAdditional metadata for the device (not indexed).

device_register result

Field NameTypeRequiredDescription
idstringYesThe device ID that was registered/updated.

device_update

Call this method to update device. For example, when user logs out the app and you need to detach user ID from the device.

device_update request

FieldTypeRequiredDescription
idsrepeated stringNoDevice ids to filter
usersrepeated stringNoDevice users filter
provider_tokensrepeated DeviceProviderTokensNoProvider tokens filter
user_updateDeviceUserUpdateNoOptional user update object
meta_updateDeviceMetaUpdateNoOptional device meta update object
tags_updateDeviceTagsUpdateNoOptional device tags update object
topics_updateDeviceChannelsUpdateNoOptional topics update object

DeviceUserUpdate:

FieldTypeRequiredDescription
userstringYesUser to set

DeviceMetaUpdate:

FieldTypeRequiredDescription
metamap<string, string>YesMeta to set

DeviceTagsUpdate:

FieldTypeRequiredDescription
tagsmap<string, string>YesTags to set

DeviceChannelsUpdate:

FieldTypeRequiredDescription
topicsrepeated stringYesChannels to set

device_update result

Empty object.

device_remove

Removes device from storage. This may be also called when user logs out the app and you don't need its device token after that.

device_remove request

Field NameTypeRequiredDescription
idsrepeated stringNoA list of device IDs to be removed
usersrepeated stringNoA list of device user IDs to filter devices to remove
provider_tokensProviderTokensNoProvider tokens to remove

device_remove result

Empty object.

device_list

Returns a paginated list of registered devices according to request filter conditions.

device_list request

FieldTypeRequiredDescription
idsrepeated stringNoList of device IDs to filter results.
providersrepeated stringNoList of device token providers to filter results.
provider_tokensrepeated ProviderTokensNoProvider tokens to filter results.
platformsrepeated stringNoList of device platforms to filter results.
usersrepeated stringNoList of device users to filter results.
sincestringNoCursor for pagination (last device id in previous batch, empty for first page).
limitint32NoMaximum number of devices to retrieve.
include_topicsboolNoFlag indicating whether to include topics information for each device.
include_tagsboolNoFlag indicating whether to include tags information for each device.
include_metaboolNoFlag indicating whether to include meta information for each device.

device_list result

Field NameTypeRequiredDescription
itemsrepeated DeviceYesA list of devices
has_moreboolYesA flag indicating whether there are more devices available

Device:

Field NameTypeDescription
idstringThe device's ID.
providerstringThe device's token provider.
tokenstringThe device's token.
platformstringThe device's platform.
userstringThe user associated with the device.
topicsarray of stringsOnly included if include_topics was true
tagsmap<string, string>Only included if include_tags was true
metamap<string, string>Only included if include_meta was true

device_topic_update

Manage mapping of device to topics.

device_topic_update request

FieldTypeRequiredDescription
device_idstringYesDevice ID.
opstringYesadd or remove or set
topicsrepeated stringNoList of topics.

device_topic_update result

Empty object.

device_topic_list

List device to topic mapping.

device_topic_list request

FieldTypeRequiredDescription
device_idsrepeated stringNoList of device IDs to filter results.
device_providersrepeated stringNoList of device token providers to filter results.
device_provider_tokensrepeated ProviderTokensNoProvider tokens to filter results.
device_platformsrepeated stringNoList of device platforms to filter results.
device_usersrepeated stringNoList of device users to filter results.
topicsrepeated stringNoList of topics to filter results.
topic_prefixstringNoChannel prefix to filter results.
sincestringNoCursor for pagination (last device id in previous batch, empty for first page).
limitint32NoMaximum number of devices to retrieve.
include_deviceboolNoFlag indicating whether to include Device information for each object.

device_topic_list result

Field NameTypeRequiredDescription
itemsrepeated DeviceChannelYesA list of DeviceChannel objects
has_moreboolYesA flag indicating whether there are more devices available

DeviceChannel:

FieldTypeRequiredDescription
idstringYesID of DeviceChannel
device_idstringYesDevice ID
topicstringYesChannel

user_topic_update

Manage mapping of topics with users. These user topics will be automatically attached to user devices upon registering. And removed from device upon deattaching user.

user_topic_update request

FieldTypeRequiredDescription
userstringYesUser ID.
opstringYesadd or remove or set
topicsrepeated stringNoList of topics.

user_topic_update result

Empty object.

user_topic_list

List user to topic mapping.

user_topic_list request

FieldTypeRequiredDescription
usersrepeated stringNoList of users to filter results.
topicsrepeated stringNoList of topics to filter results.
topic_prefixstringNoChannel prefix to filter results.
sincestringNoCursor for pagination (last id in previous batch, empty for first page).
limitint32NoMaximum number of UserTopic objects to retrieve.

user_topic_list result

Field NameTypeDescription
itemsrepeated UserTopicA list of UserTopic objects
has_moreboolA flag indicating whether there are more devices available

UserTopic:

FieldTypeRequiredDescription
idstringYesID of UserTopic
userstringYesUser ID
topicstringYesChannel

send_push_notification

Send push notification to specific device_ids, or to topics, or native provider identifiers like fcm_tokens, or to fcm_topic. Request will be queued by Centrifugo, consumed by Centrifugo built-in workers and sent to the provider API.

send_push_notification request

Field nameTypeRequiredDescription
recipientPushRecipientYesRecipient of push notification
notificationPushNotificationYesPush notification to send

PushRecipient (you must set only one of the following fields):

FieldTypeRequiredDescription
device_idsrepeated stringNoSend to a list of device IDs (managed by Centrifugo)
topicsrepeated stringNoSend to topics (managed by Centrifugo)
fcm_tokensrepeated stringNoSend to a list of FCM native tokens
fcm_topicstringNoSend to a FCM native topic
fcm_conditionstringNoSend to a FCM native condition
hms_tokensrepeated stringNoSend to a list of HMS native tokens
hms_topicstringNoSend to a HMS native topic
hms_conditionstringNoSend to a HMS native condition
apns_tokensrepeated stringNoSend to a list of APNs native tokens

PushNotification:

FieldTypeRequiredDescription
uidstringNoUnique send id, used for Centrifugo builtin analytics
expire_atint64NoUnix timestamp when Centrifugo stops attempting to send this notification (this does not relate to notification TTL fields)
fcmFcmPushNotificationNoNotification for FCM
hmsHmsPushNotificationNoNotification for HMS
apnsApnsPushNotificationNoNotification for APNs

FcmPushNotification:

FieldTypeRequiredDescription
messageJSON objectYesFCM Message described in FCM docs.

HmsPushNotification:

FieldTypeRequiredDescription
messageJSON objectYesHMS Message described in HMS Push Kit docs.

ApnsPushNotification:

FieldTypeRequiredDescription
headersmap<string, string>NoAPNs headers
payloadJSON objectYesAPNs payload

send_push_notification result

Field NameTypeDescription
uidstringUnique send id, matches uid in request if it was provided

update_push_status

This API call is experimental, some changes may happen here.

Centrifugo PRO also allows tracking status of push notification delivery and interaction. It's possible to use update_push_status API to save the updated status of push notification to the notifications analytics table. Then it's possible to build insights into push notification effectiveness by querying the table.

The update_push_status API supposes that you are using uid field with each notification sent and you are using Centrifugo PRO generated device IDs (as described in steps to integrate).

This is a part of server API at the moment, so you need to proxy requests to this endpoint over your backend. We can consider making this API suitable for requests from the client side – please reach out if your use case requires it.

update_push_status request

FieldTypeRequiredDescription
uidstringYesuid (unique send id) from send_push_notification
statusstringYesStatus of push notification - delivered or interacted
device_idstringYesDevice ID
msg_idstringNoMessage ID

update_push_status result

Empty object.

Metrics

Several metrics are available to monitor the state of Centrifugo push worker system:

  • centrifugo_push_notification_count - counter, shows total count of push notifications sent to providers (splitted by provider, recipient type, platform, success, error code).
  • centrifugo_push_queue_consuming_lag - gauge, shows the lag of queues, should be close to zero most of the time. Splitted by provider and name of queue.
  • centrifugo_push_consuming_inflight_jobs - gauge, shows immediate number of workers proceccing pushes. Splitted by provider and name of queue.
  • centrifugo_push_job_duration_seconds - summary, provides insights about worker job duration timings. Splitted by provider and recipient type.

Further reading and tutorials

Coming soon.

- + \ No newline at end of file diff --git a/docs/4/pro/singleflight.html b/docs/4/pro/singleflight.html index 7d9c70274..7f20fb36e 100644 --- a/docs/4/pro/singleflight.html +++ b/docs/4/pro/singleflight.html @@ -16,13 +16,13 @@ - +

Singleflight

Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. It will do this by using in-memory component called singleflight.

Singleflight

tip

While it can seem similar, singleflight is not a cache. It only combines identical parallel requests into one. If requests come one after another – they will be sent separately to the broker or presence storage.

This option can radically reduce a load on a broker in the following situations:

  • Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature)
  • Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position
  • Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously

Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine.

To enable:

config.json
{
...
"use_singleflight": true
}

Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.

- + \ No newline at end of file diff --git a/docs/4/pro/throttling.html b/docs/4/pro/throttling.html index 77205d90f..78106fc1d 100644 --- a/docs/4/pro/throttling.html +++ b/docs/4/pro/throttling.html @@ -16,13 +16,13 @@ - +

Operation throttling

The throttling feature allows limiting the number of operations each connection or user can issue during a configured time interval. This is useful to protect the system from misusing, detecting and disconnecting abusive or broken (due to the bug in the frontend application) clients which add unwanted load on a server.

With throttling properly configured you can protect your Centrifugo installation to some degree without sophisticated third-party solution. Centrifugo PRO protection works best in combination with protection on infrastructure level though.

Throttling

In-memory per connection throttling

In-memory throttling is an efficient way to limit number of operations allowed on a per-connection basis – i.e. inside each individual real-time connection. Our throttling implementation uses token bucket algorithm internally.

The list of operations which can be throttled on a per-connection level is:

  • subscribe
  • publish
  • history
  • presence
  • presence_stats
  • refresh
  • sub_refresh
  • rpc (with optional method resolution)

In addition, Centrifugo allows defining two special buckets containers:

  • total – define it to limit the total number of commands per interval (all commands sent from client count), these buckets will always be checked if defined, every command from the client always consumes token from total buckets
  • default - define it if you don't want to configure some command buckets explicitly, default buckets will be used in case command buckets is not configured explicitly.
config.json
{
...
"client_command_throttling": {
"enabled": true,

"default": {
"buckets": [
{
"interval": "1s",
"rate": 60
},
]
},
"total": {
"buckets": [
{
"interval": "1s",
"rate": 20
},
{
"interval": "60s",
"rate": 50
},
]
},
"publish": {
"buckets": [
{
"interval": "1s",
"rate": 1
},
]
},
"rpc": {
"buckets": [
{
"interval": "1s",
"rate": 10
}
],
"method_override": [
{
"method": "updateActiveStatus",
"buckets": [
{
"interval": "20s",
"rate": 1
}
]
}
]
}
}
}
tip

Centrifugo real-time SDKs written in a way that if client receives an error during connect – it will try to reconnect to a server with backoff algorithm. The same for subscribing to channels (i.e. error from subscribe command) – subscription request will be retried with a backoff. Refresh and subscription refresh will be also retried automatically by SDK upon errors after in several seconds. Retries of other commands should be handled manually from the client side if needed – though usually you should choose throttling limits in a way that normal users of your app never hit the limits.

In-memory per user throttling

Another type of throttling in Centrifugo PRO is a per user ID in-memory throttling. Like per client throttling this one is also very efficient since also uses in-memory token buckets. The difference is that instead of throttling per individual client this type of throttling takes user ID into account.

This type of throttling only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be throttled with it.

The list of operations which can be throttled is similar to the in-memory throttling described above. But with additional connect method:

  • total
  • default
  • connect
  • subscribe
  • publish
  • history
  • presence
  • presence_stats
  • refresh
  • sub_refresh
  • rpc (with optional method resolution)

The configuration is very similar:

config.json
{
...
"user_command_throttling": {
"enabled": true,

"default": {
"buckets": [
{
"interval": "1s",
"rate": 60
},
]
},
"publish": {
"buckets": [
{
"interval": "1s",
"rate": 1
}
]
},
"rpc": {
"buckets": [
{
"interval": "1s",
"rate": 10
}
],
"method_override": [
{
"method": "updateActiveStatus",
"buckets": [
{
"interval": "20s",
"rate": 1
}
]
}
]
}
}
}

Redis per user throttling

The next type of throttling in Centrifugo PRO is a distributed per user ID throttling with Redis as a bucket state storage. In this case limits are global for the entire Centrifugo cluster. If one user executed two commands on different Centrifugo nodes, Centrifugo consumes two tokens from the same bucket kept in Redis. Since this throttling goes to Redis to check limits, it adds some latency to a command processing. Our implementation tries to provide good throughput characteristics though – in our tests single Redis instance can handle more than 100k limit check requests per second. And it's possible to scale Redis in the same ways as for Centrifugo Redis Engine.

This type of throttling only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be throttled with it. The implementation also uses token bucket algorithm internally.

The list of operations which can be throttled is similar to the in-memory user command throttling described above. But without special bucket total:

  • default
  • connect
  • subscribe
  • publish
  • history
  • presence
  • presence_stats
  • refresh
  • sub_refresh
  • rpc (with optional method resolution)

The configuration is very similar:

config.json
{
...
"redis_user_command_throttling": {
"enabled": true,
"redis_address": "localhost:6379",

"default": {
"buckets": [
{
"interval": "1s",
"rate": 60
},
]
},
"publish": {
"buckets": [
{
"interval": "1s",
"rate": 1
}
]
},
"rpc": {
"buckets": [
{
"interval": "1s",
"rate": 10
}
],
"method_override": [
{
"method": "updateActiveStatus",
"buckets": [
{
"interval": "20s",
"rate": 1
}
]
}
]
}
}
}

Redis configuration for throttling feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for throttling feature too.

It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis configuration declaration, like this:

config.json
{
...
"engine": "redis",
"redis_address": "localhost:6379",
"redis_user_command_throttling": {
"enabled": true,
"use_redis_from_engine": true,
...
}
}

In this case throttling will simply connect to Redis instances configured for an Engine.

Disconnecting abusive or misbehaving connections

Above we showed how you can define throttling strategies to protect server resources and prevent execution of many commands inside the connection and from certain user.

But there are scenarios when abusive or broken connections may generate a significant load on the server just by calling commands and getting error responses due to throttling or due to other reasons (like malformed command). Centrifugo PRO provides a way to configure error limits per connection to deal with this case.

Error limits are configured as in-memory buckets operating on a per-connection level. When these buckets are full due to lots of errors for an individual connection Centrifugo disconnects the client (with advice to not reconnect, so our SDKs may follow it). This way it's possible to get rid of the connection and rely on HTTP infrastracture tools to deal with client reconnections. Since WebSocket or other our transports (except unidirectional GRPC, but it's usually not available to the public port) are HTTP-based (or start with HTTP request in WebSocket Upgrade case) – developers can use Nginx limit_req_zone directive, Cloudflare rules, iptables, and so on, to protect Centrifugo from unwanted connections.

tip

Centrifugo PRO does not count internal errors for the error limit buckets – as internal errors is usually not a client's fault.

The configuration on error limits per connection may look like this:

config.json
{
...
"client_error_limits": {
"enabled": true,
"total": {
"buckets" : [
{
"interval": "5s",
"rate": 20
}
]
}
}
}
- + \ No newline at end of file diff --git a/docs/4/pro/token_revocation.html b/docs/4/pro/token_revocation.html index 05bead06c..9eabb3e34 100644 --- a/docs/4/pro/token_revocation.html +++ b/docs/4/pro/token_revocation.html @@ -16,13 +16,13 @@ - +

Token revocation API

One more protective instrument in Centrifugo PRO is API to manage token revocations.

At the moment Centrifugo provides two ways to revoke tokens:

  1. Revoke token by ID: based on jti claim in the case of JWT.
  2. Revoke all user's tokens issued before certain time: based on iat in the case of JWT.

When token is revoked client with such token will be disconnected from Centrifugo shortly. And attempt to connect with a revoked token won't succeed.

How it works

By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart.

But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts.

Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.

Configure

Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory.

There are two types of persistent engines supported at the moment:

  1. redis
  2. database

Redis persistence engine

Revocation data can be kept in Redis. To enable this configuration should be:

{
...
"token_revoke": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
},
"user_tokens_invalidate": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
}
}
danger

Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start.

caution

One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.

Database persistence engine

Revocation data can be kept in the relational database. Only PostgreSQL is supported.

To enable this configuration should be like:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"token_revoke": {
"persistence_engine": "database"
},
"user_tokens_invalidate": {
"persistence_engine": "database"
}
}

Revoke token API

revoke_token

Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT).

caution

This API assumes that JWTs you are using contain "jti" claim which is a unique token ID (according to RFC).

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "revoke_token", "params": {"uid": "xxx-xxx-xxx", "expire_at": 1635845122}}' \
http://localhost:8000/api

revoke_token params

Parameter nameParameter typeRequiredDescription
uidstringyesToken unique ID (JTI claim in case of JWT)
expire_atintnoUnix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache).

revoke_token result

Empty object at the moment.

Invalidate user tokens API

invalidate_user_tokens

Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application.

caution

This API assumes that JWTs you are using contain "iat" claim which is a time token was issued at (according to RFC).

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "invalidate_user_tokens", "params": {"user": "test", "issued_before": 1635845022, "expire_at": 1635845122}}' \
http://localhost:8000/api

invalidate_user_tokens params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to invalidate tokens for
issued_beforeintnoAll tokens issued at before this Unix time will be considered revoked (in case of JWT this requires iat to be properly set in JWT), if not provided server uses current time
expire_atintnoUnix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache).

invalidate_user_tokens result

Empty object.

- + \ No newline at end of file diff --git a/docs/4/pro/tracing.html b/docs/4/pro/tracing.html index 07e0db344..385bf02df 100644 --- a/docs/4/pro/tracing.html +++ b/docs/4/pro/tracing.html @@ -16,13 +16,13 @@ - +

User and channel tracing

That's a unique thing. The tracing feature of Centrifugo PRO allows attaching to any channel to see all messages flying towards subscribers or attach to a specific user ID to see all user-related events in real-time.

tracing

It's possible to attach to trace streams using Centrifugo admin UI panel or simply from terminal using CURL and admin token.

This can be super-useful for debugging issues, investigating application behavior, understanding that the application works as expected.

Save to a file

It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing.

curl -X POST http://localhost:8000/admin/trace -H "Authorization: token <ADMIN_AUTH_TOKEN>" -d '{"type": "user", "entity": "56"}' -o trace.txt

Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future as PRO version evolves.

- + \ No newline at end of file diff --git a/docs/4/pro/user_block.html b/docs/4/pro/user_block.html index f25a5d1a9..701ccc873 100644 --- a/docs/4/pro/user_block.html +++ b/docs/4/pro/user_block.html @@ -16,13 +16,13 @@ - +

User blocking API

One additional instrument for making protective actions in Centrifugo PRO is user blocking API which allows blocking a specific user on Centrifugo level.

When user is blocked it will be disconnected from Centrifugo immediately and also on the next connect attempt right after JWT decoded (so that Centrifugo got a user ID) or after result from connect proxy received. In case of using connect proxy you can actually disconnect user yourself by implementing blocking check on the application backend side – but possibility to block user in Centrifugo can still be helpful.

How it works

By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart.

But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts.

Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.

Configure

User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine.

There are two types of persistent engines supported at the moment:

  1. redis
  2. database

Redis persistence engine

Blocking data can be kept in Redis. To enable this configuration should be:

{
...
"user_block": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
}
}
danger

Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start.

caution

One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.

Database persistence engine

Blocking data can be kept in the relational database. Only PostgreSQL is supported.

To enable this configuration should be like:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"user_block": {
"persistence_engine": "database"
}
}
tip

To quickly start local PostgreSQL database:

docker run -it --rm -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:15

Block API

block_user

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "block_user", "params": {"user": "2695", "expire_at": 1635845122}}' \
http://localhost:8000/api

block_user params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to block
expire_atintnoUnix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache).

block_user result

Empty object at the moment.

unblock_user

Example:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "unblock_user", "params": {"user": "2695"}}' \
http://localhost:8000/api

unblock_user params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to unblock

unblock_user result

Empty object at the moment.

- + \ No newline at end of file diff --git a/docs/4/pro/user_status.html b/docs/4/pro/user_status.html index acdf471c2..2d9bbb941 100644 --- a/docs/4/pro/user_status.html +++ b/docs/4/pro/user_status.html @@ -16,13 +16,13 @@ - +

User status API

Centrifugo OSS provides a presence feature for channels. It works well (for channels with reasonably small number of active subscribers though), but sometimes you may need a bit different functionality.

What if you want to get a specific user status based on its recent activity in application? You can create a personal channel with a presence enabled for each user. It will show that user has an active connection with a server. But this won't show whether user did some actions in an application recently or just left it open while not actually using it.

user status

User status feature of Centrifugo PRO allows calling a special RPC method from a client side when a user makes a useful action in an application (clicks on buttons, uses a mouse – whatever means that user really uses application at the moment). This call sets a time of last user activity in Redis, and this information can then be queried over Centrifugo PRO server API.

The feature can be useful for chat applications when you need to get online/activity status for a list of buddies (Centrifugo supports batch requests to user status information – i.e. ask for many users in one call).

Client-side status update RPC

Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript:

await centrifuge.rpc('update_user_status', {});
note

Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example.

This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.

update_user_status server API

It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports):

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "update_user_status", "params": {"users": ["42"]}}' \
http://localhost:8000/api

Update user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to update status for

Update user status result

Empty object at the moment.

get_user_status server API

Now on a backend side you have access to a bulk API to effectively get status of particular users.

Call RPC method of server API (over HTTP or GRPC):

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "get_user_status", "params": {"users": ["42"]}}' \
http://localhost:8000/api

You should get a response like this:

{
"result":{
"statuses":[
{
"user":"42",
"active":1627107289,
"online":1627107289
}
]
}
}

In case information about last status update time not available the response will be like this:

{
"result":{
"statuses":[
{
"user":"42"
}
]
}
}

I.e. status object will present in a response but active field won't be set for status object.

Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time).

Get user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to get status for

Get user status result

Field nameField typeOptionalDescription
statusesarray of UserStatusnoStatuses for each user in params (same order)

UserStatus

Field nameField typeOptionalDescription
userstringnoUser ID
activeintegeryesLast active time (Unix seconds)
onlineintegeryesLast online time (Unix seconds)

delete_user_status server API

If you need to clear user status information for some reason there is a delete_user_status server API call:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "delete_user_status", "params": {"users": ["42"]}}' \
http://localhost:8000/api

Delete user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to delete status for

Delete user status result

Empty object at the moment.

Configuration

To enable Redis user status feature:

config.json
{
...
"user_status": {
"enabled": true,
"redis_address": "127.0.0.1:6379"
}
}

Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too.

It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this:

config.json
{
...
"engine": "redis",
"redis_address": "localhost:6379",
"user_status": {
"enabled": true,
"use_redis_from_engine": true,
}
}

In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine.

expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day:

config.json
{
...
"user_status": {
...
"expire_interval": "24h"
}
}
- + \ No newline at end of file diff --git a/docs/4/server/admin_web.html b/docs/4/server/admin_web.html index f71928d0e..2eb5ecd34 100644 --- a/docs/4/server/admin_web.html +++ b/docs/4/server/admin_web.html @@ -16,13 +16,13 @@ - +

Admin web UI

Centrifugo comes with a built-in admin web interface. It can:

  • Show general information and statistics from server nodes - number of connections, unique users, number of subscriptions, unique channels etc.
  • Call publish, broadcast, subscribe, unsubscribe, disconnect, history, history_remove, presence, presence_stats, info, channels and several additional Centrifugo PRO server API commands.
  • Trace connections in real-time (Centrifugo PRO feature)

To enable admin web interface run Centrifugo with admin option enabled and provide some security options in configuration file:

config.json
{
...
"admin": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>"
}

Options

  • admin (boolean, default: false) – enables/disables admin web UI
  • admin_password (string, default: "") – this is a password to log into admin web interface
  • admin_secret (string, default: "") - this is a secret key for authentication token set on successful login.

Make both admin_password and admin_secret strong and keep them in secret.

After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface.

tip

Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.

Admin web panel

Using custom web interface

If you want to use custom web interface you can specify path to web interface directory dist:

config.json
{
...,
"admin": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>",
"admin_web_path": "<PATH_TO_WEB_DIST>"
}

This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.

Admin insecure mode

There is also an option to run Centrifugo in insecure admin mode - in this case you don't need to set admin_password and admin_secret in config – in web interface you will be logged in automatically without any password. Note that this is only an option for production if you protected admin web interface with firewall rules. Otherwise anyone in internet will have full access to admin functionality described above. To enable insecure admin mode:

config.json
{
...,
"admin": true,
"admin_insecure": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>"
}
- + \ No newline at end of file diff --git a/docs/4/server/authentication.html b/docs/4/server/authentication.html index c72e9e392..6449cc9cf 100644 --- a/docs/4/server/authentication.html +++ b/docs/4/server/authentication.html @@ -16,13 +16,13 @@ - +

Client JWT authentication

To authenticate incoming connection (client) Centrifugo can use JSON Web Token (JWT) passed from the client-side. This way Centrifugo may know the ID of user in your application, also application can pass additional data to Centrifugo inside JWT claims. This chapter describes this authentication mechanism.

tip

If you prefer to avoid using JWT then look at the proxy feature. It allows proxying connection requests from Centrifugo to your application backend endpoint for authentication details.

tip

Using JWT auth can be nice in terms of massive reconnect scenario. Since authentication information is encoded directly in the token this may help to drastically reduce load on your application session backend. See in our blog post.

Upon connecting to Centrifugo client should provide a connection JWT with several predefined credential claims. Here is a diagram:

At the moment Centrifugo supports HMAC, RSA and ECDSA JWT algorithms - i.e. HS256, HS384, HS512, RSA256, RSA384, RSA512, EC256, EC384, EC512.

We will use Javascript Centrifugo client here for example snippets for client-side and PyJWT Python library to generate a connection token on the backend side.

To add HMAC secret key to Centrifugo add token_hmac_secret_key to configuration file:

config.json
{
...
"token_hmac_secret_key": "<YOUR-SECRET-STRING-HERE>"
}

To add RSA public key (must be PEM encoded string) add token_rsa_public_key option, ex:

config.json
{
...
"token_rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZ..."
}

To add ECDSA public key (must be PEM encoded string) add token_ecdsa_public_key option, ex:

config.json
{
...
"token_ecdsa_public_key": "-----BEGIN PUBLIC KEY-----\nxyz23adf..."
}

Connection JWT claims

For connection JWT Centrifugo uses the some standart claims defined in rfc7519, also some custom Centrifugo-specific.

sub

This is a standard JWT claim which must contain an ID of the current application user (as string).

If a user is not currently authenticated in an application, but you want to let him connect to Centrifugo anyway – you can use an empty string as a user ID in sub claim. This is called anonymous access. In this case, you may need to enable corresponding channel namespace options which enable access to protocol features for anonymous users.

exp

This is a UNIX timestamp seconds when the token will expire. This is a standard JWT claim - all JWT libraries for different languages provide an API to set it.

If exp claim is not provided then Centrifugo won't expire connection. When provided special algorithm will find connections with exp in the past and activate the connection refresh mechanism. Refresh mechanism allows connection to survive and be prolonged. In case of refresh failure, the client connection will be eventually closed by Centrifugo and won't be accepted until new valid and actual credentials are provided in the connection token.

You can use the connection expiration mechanism in cases when you don't want users of your app to be subscribed on channels after being banned/deactivated in the application. Or to protect your users from token leakage (providing a reasonably short time of expiration).

Choose exp value wisely, you don't need small values because the refresh mechanism will hit your application often with refresh requests. But setting this value too large can lead to slow user connection deactivation. This is a trade-off.

Read more about connection expiration below.

iat

This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

jti

This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

aud

By default, Centrifugo does not check JWT audience (rfc7519 aud claim).

But you can force this check by setting token_audience string option:

config.json
{
"token_audience": "centrifugo"
}
caution

Setting token_audience will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.

iss

By default, Centrifugo does not check JWT issuer (rfc7519 iss claim).

But you can force this check by setting token_issuer string option:

config.json
{
"token_issuer": "my_app"
}
caution

Setting token_issuer will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.

info

This claim is optional - this is additional information about client connection that can be provided for Centrifugo. This information will be included in presence information, join/leave events, and channel publication if it was published from a client-side.

b64info

If you are using binary Protobuf protocol you may want info to be custom bytes. Use this field in this case.

This field contains a base64 representation of your bytes. After receiving Centrifugo will decode base64 back to bytes and will embed the result into various places described above.

channels

An optional array of strings with server-side channels to subscribe a client to. See more details about server-side subscriptions.

subs

An optional map of channels with options. This is like a channels claim but allows more control over server-side subscription since every channel can be annotated with info, data, and so on using options.

tip

This claim is called subs as a shortcut from subscriptions. The claim sub described above is a standart JWT claim to provide a user ID (it's a shortcut from subject). While claims have similar names they have different purpose in a connection JWT.

Example:

{
...
"subs": {
"channel1": {
"data": {"welcome": "welcome to channel1"}
},
"channel2": {
"data": {"welcome": "welcome to channel2"}
}
}
}

Subscribe options:

FieldTypeOptionalDescription
infoJSON objectyesCustom channel info
b64infostringyesCustom channel info in Base64 - to pass binary channel info
dataJSON objectyesCustom JSON data to return in subscription context inside Connect reply
b64datastringyesSame as data but in Base64 to send binary data
overrideOverride objectyesAllows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
positionBoolValueyesOverride position
recoverBoolValueyesOverride recover

BoolValue is an object like this:

{
"value": true/false
}

meta

Meta is an additional JSON object (ex. {"key": "value"}) that will be attached to a connection. Unlike info it's never exposed to clients inside presence and join/leave payloads and only accessible on a backend side. It may be included in proxy calls from Centrifugo to the application backend (see proxy_include_connection_meta option). Also, there is a connections API method in Centrifugo PRO that returns this data in the connection description object.

expire_at

By default, Centrifugo looks on exp claim to configure connection expiration. In most cases this is fine, but there could be situations where you wish to decouple token expiration check with connection expiration time. As soon as the expire_at claim is provided (set) in JWT Centrifugo relies on it for setting connection expiration time (JWT expiration still checked over exp though).

expire_at is a UNIX timestamp seconds when the connection should expire.

  • Set it to the future time for expiring connection at some point
  • Set it to 0 to disable connection expiration (but still check token exp claim).

Connection expiration

As said above exp claim in a connection token allows expiring client connection at some point in time. Let's look in detail at what happens when Centrifugo detects that the connection is going to expire.

First, you should do is enable client expiration mechanism in Centrifugo providing a connection JWT with expiration:

import jwt
import time

token = jwt.encode({"sub": "42", "exp": int(time.time()) + 10*60}, "secret").decode()

print(token)

Let's suppose that you set exp field to timestamp that will expire in 10 minutes and the client connected to Centrifugo with this token. During 10 minutes the connection will be kept by Centrifugo. When this time passed Centrifugo gives the connection some time (configured, 25 seconds by default) to refresh its credentials and provide a new valid token with new exp.

When a client first connects to Centrifugo it receives the ttl value in connect reply. That ttl value contains the number of seconds after which the client must send the refresh command with new credentials to Centrifugo. Centrifugo clients must handle this ttl field and automatically start the refresh process.

For example, a Javascript browser client will send an AJAX POST request to your application when it's time to refresh credentials. By default, this request goes to /centrifuge/refresh URL endpoint. In response your server must return JSON with a new connection JWT:

{
"token": token
}

So you must just return the same connection JWT for your user when rendering the page initially. But with actual valid exp. Javascript client will then send them to Centrifugo server and connection will be refreshed for a time you set in exp.

In this case, you know which user wants to refresh its connection because this is just a general request to your app - so your session mechanism will tell you about the user.

If you don't want to refresh the connection for this user - just return 403 Forbidden on refresh request to your application backend.

Javascript client also has options to hook into a refresh mechanism to implement your custom way of refreshing. Other Centrifugo clients also should have hooks to refresh credentials but depending on client API for this can be different - see specific client docs.

Examples

Let's look at how to generate connection HS256 JWT in Python:

Simplest token

import jwt

token = jwt.encode({"sub": "42"}, "secret").decode()

print(token)

Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users.

Then you can pass this token to your client side and use it when connecting to Centrifugo:

Using centrifuge-js v3
var centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: token
});
centrifuge.connect();

See more details about working with connection tokens and handling token expiration on the client-side in the real-time SDK API spec.

Token with expiration

HS256 token that will be valid for 5 minutes:

import jwt
import time

claims = {"sub": "42", "exp": int(time.time()) + 5*60}
token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Token with additional connection info

Let's attach user name:

import jwt

claims = {"sub": "42", "info": {"name": "Alexander Emelin"}}
token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Investigating problems with JWT

You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.

JSON Web Key support

Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT).

A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address).

As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels.

tip

Read a tutorial in our blog about using Centrifugo with Keycloak SSO. In that case connection tokens are verified using public key loaded from the JWKS endpoint of Keycloak.

At the moment Centrifugo caches keys loaded from an endpoint for one hour.

Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment).

Only RSA algorithm is supported.

Once enabled JWKS used for both connection and channel subscription tokens.

Dynamic JWKs endpoint

Available since Centrifugo v4.1.3

It's possible to extract variables from iss and aud JWT claims using Go regexp named groups, then use variables extracted during iss or aud matching to construct a JWKS endpoint dynamically upon token validation. In this case JWKS endpoint may be set in config as template.

To achieve this Centrifugo provides two additional options:

  • token_issuer_regex - match JWT issuer (iss claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction.
  • token_audience_regex - match JWT audience (aud claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction.

Let's look at the example:

{
"token_issuer_regex": "https://example.com/auth/realms/(?P<realm>[A-z]+)",
"token_jwks_public_endpoint": "https://keycloak:443/{{realm}}/protocol/openid-connect/certs",
}

To use variable in token_jwks_public_endpoint it must be wrapped in {{ }}.

When using token_issuer_regex and token_audience_regex make sure token_issuer and token_audience not used in the config - otherwise and error will be returned on Centrifugo start.

caution

Setting token_issuer_regex and token_audience_regex will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.

- + \ No newline at end of file diff --git a/docs/4/server/channel_permissions.html b/docs/4/server/channel_permissions.html index 3c496469c..4d0df4c45 100644 --- a/docs/4/server/channel_permissions.html +++ b/docs/4/server/channel_permissions.html @@ -16,13 +16,13 @@ - +

Channel permission model

When using Centrifugo server API you don't need to think about channel permissions at all – everything is allowed. In server API case, request to Centrifugo must be issued by your application backend – so you have all the power to check any required permissions before issuing API request to Centrifugo.

The situation is different when we are talking about client real-time API.

In order to configure which client (i.e. connection established using one of supported bidirectional real-time transports) can subscribe to channels and call publish, history and presence real-time APIs Centrifugo provides several ways to configure the desired behavior.

Let's start from looking at Centrifugo subscribe permission model.

Subscribe permission model

By default, client's attempt to subscribe on a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel subscribe permissions:

Below, we are describing those in detail.

Provide subscription token

A client can provide a subscription token in subscribe request. See the format of the token.

If client provides a valid token then subscription will be accepted. In Centrifugo PRO subscription token can additionally grant publish, history and presence permissions to a client.

caution

For namespaces with allow_subscribe_for_client option ON Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without token. This limitation exists to help users migrate to Centrifugo v4 without security risks.

Configure subscribe proxy

If client subscribes on a namespace with configured subscribe proxy then depending on proxy response subscription will be accepted or not.

If a namespace has configured subscribe proxy, but user came with a token – then subscribe proxy is not used, we are relying on token in this case. If a namespace has subscribe proxy, but user subscribes on a user-limited channel – then subscribe proxy is not used also.

Use user-limited channels

If client subscribes on a user-limited channel and there is a user ID match then subscription will be accepted.

caution

User-limited channels must be enabled in a namespace using allow_user_limited_channels option.

Use allow_subscribe_for_client namespace option

allow_subscribe_for_client allows all authenticated non-anonymous connections to subscribe on all channels in a namespace.

caution

Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.

To additionally allow subscribing to anonymous connections take a look at allow_subscribe_for_anonymous option.

Subscribe capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow user subscribe to channels.

Subscribe capabilities in connect proxy

Centrifugo PRO only

Connect proxy can return capability object to allow user subscribe to channels.

Publish permission model

tip

In idiomatic Centrifugo use case data should be published to channels from the application backend (over server API). In this case backend can validate data, save it into persistent storage before publishing in real-time towards connections. When publishing from the client-side backend does not receive publication data at all – it just goes through Centrifugo (except using publish proxy). There are cases when direct publications from the client-side are desired (like typing indicators in chat applications) though.

By default, client's attempt to publish data into a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel publish permissions:

Use allow_publish_for_client namespace option

allow_publish_for_client allows publications to channels of a namespace for all client connections.

Use allow_publish_for_subscriber namespace option

allow_publish_for_subscriber allows publications to channels of a namespace for all connections subscribed on a channel they want to publish data into.

Configure publish proxy

If client publishes to a namespace with configured publish proxy then depending on proxy response publication will be accepted or not.

Configured publish proxy always used??? (what if user has permission in token or allow_publish_for_client?)

Publish capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow client to publish to channels.

Publish capability in subscription token

Centrifugo PRO only

Connection token can contain a capability object to allow client to publish to a channel.

Publish capabilities in connect proxy

Centrifugo PRO only

Connect proxy can return capability object to allow client publish to certain channels.

Publish capability in subscribe proxy

Centrifugo PRO only

Subscribe proxy can return capability object to allow subscriber publish to channel.

History permission model

By default, client's attempt to call history from a channel (with history retention configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel history permissions.

Use allow_history_for_subscriber namespace option

allow_history_for_subscriber allows history requests to all channels in a namespace for all client connections subscribed on a channel they want to call history for.

Use allow_history_for_client namespace option

allow_history_for_client allows history requests to all channels in a namespace for all client connections.

History capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow user call history for channels.

History capabilities in subscription token

Centrifugo PRO only

Connection token can contain a capability object to allow user call history from a channel.

History capabilities in connect proxy

This is a Centrifugo PRO feature.

Connect proxy can return capability object to allow client call history from certain channels.

History capability in subscribe proxy response

Centrifugo PRO only

Subscribe proxy can return capability object to allow subscriber call history from channel.

Presence permission model

By default, client's attempt to call presence from a channel (with channel presence configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel presence permissions.

Presence capability in subscribe proxy response

Subscribe proxy can return capability object to allow subscriber call presence from channel.

Use allow_presence_for_subscriber namespace option

allow_presence_for_subscriber allows presence requests to all channels in a namespace for all client connections subscribed on a channel they want to call presence for.

Use allow_presence_for_client namespace option

allow_presence_for_client allows presence requests to all channels in a namespace for all client connections.

Presence capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow user call presence for channels.

Presence capabilities in subscription token

Centrifugo PRO only

Connection token can contain a capability object to allow user call presence of a channel.

Presence capabilities in connect proxy

Centrifugo PRO only

Connect proxy can return capability object to allow client call presence from certain channels.

Positioning permission model

Server can whether turn on positioning for all channels in a namespace using "force_positioning": true option or client can create positioned subscriptions (but in this case client must have access to history capability).

Recovery permission model

Server can whether turn on automatic recovery for all channels in a namespace using "force_recovery": true option or client can create recoverable subscriptions (but in this case client must have access to history capability).

Join/Leave permission model

Server can whether force sending join/leave messages to all subscribers for all channels in a namespace using "force_push_join_leave": true option or client can ask server to include join/leave messages upon subscribing (but in this case client must have access to presence capability).

- + \ No newline at end of file diff --git a/docs/4/server/channel_token_auth.html b/docs/4/server/channel_token_auth.html index 671ad85e9..aeb59fc6d 100644 --- a/docs/4/server/channel_token_auth.html +++ b/docs/4/server/channel_token_auth.html @@ -16,13 +16,13 @@ - +

Channel JWT authorization

In the chapter about channel permissions we mentioned that to subscribe on a channel client can provide subscription token. This chapter has more information about the subscription token mechanism in Centrifugo.

Subscription token is also JWT. Very similar to connection token, but with specific custom claims.

Valid subscription token passed to Centrifugo in subscribe request will tell Centrifugo that subscription must be accepted.

The way how this token is obtained on the frontend side varies depending on a client SDK implementation.

tip

Connection token and subscription token are both JWT and both can be generated with any JWT library.

tip

Even when authorizing a subscription to a channel with a subscription JWT you should still set a proper connection JWT for a client as it provides user authentication details to Centrifugo.

tip

Just like connection JWT using subscription JWT with a reasonable expiration time may help you have a good level of security in channels and still survive massive reconnect scenario – when many clients resubscribe alltogether.

Supported JWT algorithms for private subscription tokens match algorithms to create connection JWT. The same HMAC secret key, RSA, and ECDSA public keys set for authentication tokens are re-used to check subscription JWT.

Subscription JWT claims

For subscription JWT Centrifugo uses some standard claims defined in rfc7519, also some custom Centrifugo-specific.

sub

This is a standard JWT claim which must contain an ID of the current application user (as string).

The value must match a user in connection JWT – since it's the same real-time connection. The missing claim will mean that token issued for anonymous user (i.e. with empty user ID).

channel

Required. Channel that client tries to subscribe to with this token (string).

info

Optional. Additional information for connection inside this channel (valid JSON).

b64info

Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.

exp

Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time.

At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs.

Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.

expire_at

Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though).

expire_at is a UNIX timestamp seconds when the subscription should expire.

  • Set it to the future time for expiring subscription at some point
  • Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.

aud

By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.

iss

By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.

iat

This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

jti

This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

override

One more claim is override. This is an object which allows overriding channel options for the particular channel subscriber which comes with subscription token.

FieldTypeOptionalDescription
presenceBoolValueyesoverride presence channel option
join_leaveBoolValueyesoverride join_leave channel option
force_push_join_leaveBoolValueyesoverride force_push_join_leave channel option
force_recoveryBoolValueyesoverride force_recovery channel option
force_positioningBoolValueyesoverride force_positioning channel option

BoolValue is an object like this:

{
"value": true/false
}

So for example, you want to turn off emitting a presence information for a particular subscriber in a channel:

{
...
"override": {
"presence": {
"value": false
}
}
}

Example

So to generate a subscription token you can use something like this in Python (assuming user ID is 42 and the channel is gossips):

import jwt

token = jwt.encode({
"sub": "42",
"channel": "$gossips"
}, "secret", algorithm="HS256").decode()

print(token)

Where "secret" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).

With gensubtoken cli command

During development you can quickly generate valid subscription token using Centrifugo gensubtoken cli command.

./centrifugo gensubtoken -u 123722 -s channel

You should see an output like this:

HMAC SHA-256 JWT for user "123722" and channel "channel" with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDg0MzgsImNoYW5uZWwiOiJjaGFubmVsIn0.JyRI3ovNV-abV8VxCmZCD556o2F2mNL1UoU58gNR-uI

But in real app subscription JWT must be generated by your application backend.

- + \ No newline at end of file diff --git a/docs/4/server/channels.html b/docs/4/server/channels.html index 412a0db37..6377bc0f8 100644 --- a/docs/4/server/channels.html +++ b/docs/4/server/channels.html @@ -16,13 +16,13 @@ - +

Channels and namespaces

Upon connecting to a server clients can subscribe to channels. Channel is one of the core concepts of Centrifugo. Most of the time when integrating Centrifugo you will work with channels and decide what is the best channel configuration for your application.

What is channel

Centrifugo is a PUB/SUB system - it has publishers and subscribers. Channel is a route for publications. Clients can subscribe to a channel to receive all real-time messages published to a channel. A channel subscriber can also ask for a channel online presence or channel history information.

pub_sub

Channel is just a string - news, comments, personal_feed are valid channel names. Though this string has some predefined rules as we will see below. You can define different channel behavior using a set of available channel options.

Channels are ephemeral – you don't need to create them explicitly. Channels created automatically by Centrifugo as soon as the first client subscribes to a channel. As soon as the last subscriber leaves a channel - it's automatically cleaned up.

Channel can belong to a channel namespace. Channel namespacing is a mechanism to define different behavior for different channels in Centrifugo. Using namespaces is a recommended way to manage channels – to turn on only those channel options which are required for a specific real-time feature you are implementing on top of Centrifugo.

caution

When using channel namespaces make sure you defined a namespace in configuration. Subscription attempts to a channel within a non-defined namespace will result into 102: unknown channel errors.

Channel name rules

Only ASCII symbols must be used in a channel string.

Channel name length limited by 255 characters by default (controlled by configuration option channel_max_length).

Several symbols in channel names reserved for Centrifugo internal needs:

  • : – for namespace channel boundary (see below)
  • # – for user channel boundary (see below)
  • $ – for private channel prefix (see below)
  • * – for the future Centrifugo needs
  • & – for the future Centrifugo needs
  • / – for the future Centrifugo needs

namespace boundary (:)

: – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about about namespaces and channel options below.

If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public.

info

A namespace is part of the channel name. If a user subscribed to a channel with namespace, like public:chat – then you need to publish messages into public:chat channel to be delivered to the user. We often see some confusion from developers trying to publish messages into chat and thinking that namespace is somehow stripped upon subscription. It's not true.

user channel boundary (#)

# – is a user channel boundary. This is a separator to create personal channels for users (we call this user-limited channels) without the need to provide a subscription token.

For example, if the channel is news#42 then the only user with ID 42 can subscribe to this channel (Centrifugo knows user ID because clients provide it in connection credentials with connection JWT).

If you want to create a user-limited channel in namespace personal then you can use a name like personal:user#42 for example.

Moreover, you can provide several user IDs in channel name separated by a comma: dialog#42,43 – in this case only the user with ID 42 and user with ID 43 will be able to subscribe on this channel.

This is useful for channels with a static list of allowed users, for example for single user personal messages channel, for dialog channel between certainly defined users. As soon as you need to manage access to a channel dynamically for many users this channel type does not suit well.

tip

User-limited channels must be enabled for a channel namespace using allow_user_limited_channels option. See below more information about channel options and channel namespaces.

private channel prefix ($)

Centrifugo v4 has this option to achieve compatibility with previous Centrifugo versions. Previously (in Centrifugo v1, v2 and v3) only channels starting with $ could be subscribed with a subscription JWT. In Centrifugo v4 that's not the case anymore – clients can subscribe to any channel with a subscription token (if the token is valid – then subscription to a channel is accepted).

But for namespaces with allow_subscribe_for_client option enabled Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without a subscription token. This limitation exists to help users migrate to Centrifugo v4 without security risks.

Channel is just a string

Keep in mind that a channel is uniquely identified by its string representation. Do not expect that channels $news and news are the same. They are different because strings are not equal. So if a user subscribed to $news then user won't receive messages published to news.

Channels dialog#42,43 and dialog#43,42 are two different channels too. Centrifugo only applies permission checks when a user subscribes to a channel. So if user-limited channels are enabled then the user with ID 42 will be able to subscribe on both dialog#42,43 and dialog#43,42. But Centrifugo does no magic regarding channel strings when keeping channel->to->subscribers map. So if the user subscribed on dialog#42,43 you must publish messages to exactly that channel: dialog#42,43.

The same applies to channels with namespaces. Do not expect that channels chat:index and index are the same – they are different, moreover, belong to different namespaces. We'll look at the concept of channel namespaces in Centrifugo shortly.

Channel namespaces

It's possible to configure a list of channel namespaces. Namespaces are optional but very useful.

A namespace allows setting custom options for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application.

Namespace has a name, and the same channel options (with the same defaults) as described above.

  • name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$).

If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator:

public:messages

gossips:messages

Where public and gossips are namespace names. Centrifugo looks for : symbol in the channel name, if found – extracts the namespace name, and applies namespace options while processing protocol commands from a client.

All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured:

config.json
{
"token_hmac_secret_key": "very-long-secret-key",
"api_key": "secret-api-key",

"presence": true,
"history_size": 10,
"history_ttl": "30s",

"namespaces": [
{
"name": "facts",
"history_size": 10,
"history_ttl": "300s"
},
{
"name": "gossips"
}
]
}
  • Channel news will use globally defined channel options.
  • Channel facts:sport will use facts namespace options.
  • Channel gossips:sport will use gossips namespace options.
  • Channel xxx:hello will result into subscription error since there is no xxx namespace defined in the configuration above.

Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2.

note

There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly.

There are many options which can be set for channel namespace (on top-level and to named one) to modify behavior of channels belonging to a namespace. Below we describe all these options.

Channel options

Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.

presence

presence (boolean, default false) – enable/disable online presence information for channels in a namespace.

Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels.

Let's say you have a channel chat:index and 2 users (with ID 2694 and 56) subscribed to it. And user 2694 has 2 connections to Centrifugo in different browser tabs. In presence data you may see sth like this:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "presence", "params": {"channel": "chat:index"}}' \
http://localhost:8000/api
{
"result": {
"presence": {
"66fdf8d1-06f0-4375-9fac-db959d6ee8d6": {
"user": "2694",
"client": "66fdf8d1-06f0-4375-9fac-db959d6ee8d6",
"conn_info": {"name": "Alex"}
},
"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c": {
"user": "2694",
"client": "d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c",
"conn_info": {"name": "Alex"}
}
"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c": {
"user": "56",
"client": "g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c",
"conn_info": {"name": "Alice"}
}
}
}
}

To call presence API from the client connection side client must have permission to do so. See presence permission model.

caution

Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or in a broker memory/disk). So only use it for channels where presence is required.

See more details about online presence design.

join_leave

join_leave (boolean, default false) – enable/disable sending join and leave messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info (similar to entry inside presence information).

Enabling join_leave means that Join/Leave messages will start being emitted, but by default they are not delivered to clients subscribed to a channel. You need to force this using namespace option force_push_join_leave or explicitly provide intent from a client-side (in this case client must have permission to call presence API).

caution

Keep in mind that join/leave messages can generate a huge number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature. It's hard to say what is "large" for you though – just estimate the load based on the fact that each subscribe/unsubscribe event in a channel with N subscribers will result into N messages broadcasted to all. If all clients reconnect at the same time the amount of generated messages is N^2.

Join/leave messages distributed only with at most once delivery guarantee.

force_push_join_leave

Boolean, default false.

When on all clients will receive join/leave events for a channel in a namespace automatically – without explicit intent to consume join/leave messages from the client side.

If pushing join/leave is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel presence (by having an explicit capability or if allowed on a namespace level).

history_size

history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted.

Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below).

caution

Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk). So only use history for channels where it's required.

history_ttl

history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision).

As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels.

By default history TTL duration is zero – this means that channel history is disabled.

Again – to turn on history you should wisely configure both history_size and history_ttl options.

For example for top-level channels (which do not belong to a namespace):

config.json
{
...
"history_size": 10,
"history_ttl": "60s"
}

Let's look at example. You enabled history for a namespace chat and sent two messages in channel chat:index. Then history will contain sth like this:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "history", "params": {"channel": "chat:index", "limit": 100}}' \
http://localhost:8000/api
{
"result": {
"publications": [
{
"data": {
"input": "1"
},
"offset": 1
},
{
"data": {
"input": "2"
},
"offset": 2
}
],
"epoch": "gWuY",
"offset": 2
}
}

To call history API from the client connection side client must have permission to do so. See history permission model.

See additional information about offsets and epoch in History and recovery chapter.

tip

History persistence properties are dictated by Centrifugo engine used. For example, when using memory engine history is only kept till Centrifugo node restart. In Redis engine case persistence is determined by a Redis server persistence configuration (same for KeyDB and Tarantool).

force_positioning

force_positioning (boolean, default false) – when the force_positioning option is on Centrifugo forces all subscriptions in a namespace to be positioned. I.e. Centrifugo will try to compensate at most once delivery of PUB/SUB broker checking client position inside a stream.

If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API.

force_positioning option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream).

If positioning is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).

force_recovery

force_recovery (boolean, default false) – when the position option is on Centrifugo forces all subscriptions in a namespace to be recoverable. When enabled Centrifugo will try to recover missed publications in channels after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the force_positioning option described above.

force_recovery option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages).

If recovery is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).

tip

Not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.

allow_subscribe_for_client

allow_subscribe_for_client (boolean, default false) – when on all non-anonymous clients will be able to subscribe to any channel in a namespace. To additionally allow anonymous users to subscribe turn on allow_subscribe_for_anonymous (see below).

caution

Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.

allow_subscribe_for_anonymous

allow_subscribe_for_anonymous (boolean, default false) – turn on if anonymous clients (with empty user ID) should be able to subscribe on channels in a namespace.

allow_publish_for_subscriber

allow_publish_for_subscriber (boolean, default false) - when the allow_publish_for_subscriber option is enabled client can publish into a channel in namespace directly from the client side over real-time connection but only if client subscribed to that channel.

danger

Keep in mind that in this case subscriber can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).

allow_publish_for_subscriber (or allow_publish_for_client mentioned below) option still can be useful to send something without backend-side validation and saving it into a database – for example, this option may be handy for demos and quick prototyping real-time app ideas.

allow_publish_for_client

allow_publish_for_client (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). It's like allow_publish_for_subscriber – but client should not be a channel subscriber to publish.

danger

Keep in mind that in this case client can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).

allow_publish_for_anonymous

allow_publish_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to publish into channels in a namespace.

allow_history_for_subscriber

allow_history_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call history API from that channel.

allow_history_for_client

allow_history_for_client (boolean, default false) – allows all clients to call history information in a namespace.

allow_history_for_anonymous

allow_history_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call history from channels in a namespace.

allow_presence_for_subscriber

allow_presence_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call presence information from that channel.

allow_presence_for_client

allow_presence_for_client (boolean, default false) – allows all clients to call presence information in a namespace.

allow_presence_for_anonymous

allow_presence_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call presence from channels in a namespace.

allow_user_limited_channels

allow_user_limited_channels (boolean, default false) - allows using user-limited channels in a namespace for checking subscribe permission.

note

If client subscribes to a user-limited channel while this option is off then server rejects subscription with 103: permission denied error.

channel_regex

channel_regex (string, default "") – is an option to set a regular expression for channels allowed in the namespace. By default Centrifugo does not limit channel name variations. For example, if you have a namespace chat, then channel names inside this namespace are not really limited, it can be chat:index, chat:1, chat:2, chat:zzz and so on. But if you want to be strict and know possible channel patterns you can use channel_regex option. This is especially useful in namespaces where all clients can subscribe to channels.

For example, let's only allow digits after chat: for channel names in a chat namespace:

{
"namespaces": [
{
"name": "chat",
"allow_subscribe_for_client": true,
"channel_regex": "^[\d+]$"
}
]
}
danger

Note, that we are skipping chat: part in regex. Since namespace prefix is the same for all channels in a namespace we only match the rest (after the prefix) of channel name.

Channel regex only checked for client-side subscriptions, if you are using server-side subscriptions Centrifugo won't check the regex.

Centrifugo uses Go language regexp package for regular expressions.

proxy_subscribe

proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter

proxy_publish

proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter

proxy_sub_refresh

proxy_sub_refresh (boolean, default false) – turns on sub refresh proxy, more info in proxy chapter

subscribe_proxy_name

subscribe_proxy_name (string, default "") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.

publish_proxy_name

publish_proxy_name (string, default "") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.

sub_refresh_proxy_name

sub_refresh_proxy_name (string, default "") – turns on sub refresh proxy when granular proxy mode is used. Note that proxy_sub_refresh option defined above is ignored in granular proxy mode.

Channel config examples

Let's look at how to set some of these options in a config. In this example we turning on presence, history features, forcing publication recovery. Also allowing all client connections (including anonymous users) to subscribe to channels and call publish, history, presence APIs if subscribed.

config.json
{
"token_hmac_secret_key": "my-secret-key",
"api_key": "secret-api-key",
"presence": true,
"history_size": 10,
"history_ttl": "300s",
"force_recovery": true,
"allow_subscribe_for_client": true,
"allow_subscribe_for_anonymous": true,
"allow_publish_for_subscriber": true,
"allow_publish_for_anonymous": true,
"allow_history_for_subscriber": true,
"allow_history_for_anonymous": true,
"allow_presence_for_subscriber": true,
"allow_presence_for_anonymous": true
}

Here we set channel options on config top-level – these options will affect channels without namespace. In many cases defining namespaces is a recommended approach so you can manage options for every real-time feature separately. With namespaces the above config may transform to:

config.json
{
"token_hmac_secret_key": "my-secret-key",
"api_key": "secret-api-key",
"namespaces": [
{
"name": "feed",
"presence": true,
"history_size": 10,
"history_ttl": "300s",
"force_recovery": true,
"allow_subscribe_for_client": true,
"allow_subscribe_for_anonymous": true,
"allow_publish_for_subscriber": true,
"allow_publish_for_anonymous": true,
"allow_history_for_subscriber": true,
"allow_history_for_anonymous": true,
"allow_presence_for_subscriber": true,
"allow_presence_for_anonymous": true
}
]
}

In this case channels should be prefixed with feed: to follow the behavior configured for a feed namespace.

- + \ No newline at end of file diff --git a/docs/4/server/codes.html b/docs/4/server/codes.html index 5d13d8bb9..e93c94c40 100644 --- a/docs/4/server/codes.html +++ b/docs/4/server/codes.html @@ -16,13 +16,13 @@ - +

Error and disconnect codes

This chapter describes error and disconnect codes Centrifugo uses in a client protocol, also error codes which a server API can return in response.

Client error codes

Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client.

Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).

Internal

Code:    100
Message: "internal server error"
Temporary: true

Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.

Unauthorized

Code:    101
Message: "unauthorized"

Error Unauthorized says that request is unauthorized.

Unknown Channel

Code:    102
Message: "unknown channel"

Error Unknown Channel means that channel name does not exist.

Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.

Permission Denied

Code:    103
Message: "permission denied"

Error Permission Denied means that access to resource not allowed.

Method Not Found

Code:    104
Message: "method not found"

Error Method Not Found means that method sent in command does not exist.

Already Subscribed

Code:    105
Message: "already subscribed"

Error Already Subscribed returned when client wants to subscribe on channel it already subscribed to.

Limit Exceeded

Code:    106
Message: "limit exceeded"

Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.

Bad Request

Code:    107
Message: "bad request"

Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.

Not Available

Code:    108
Message: "not available"

Error Not Available means that resource is not enabled.

For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.

Token Expired

Code:    109
Message: "token expired"

Error Token Expired indicates that connection token expired. Our SDKs handle it in a special way by updating token.

Expired

Code:    110
Message: "expired"

Error Expired indicates that connection expired (no token involved).

Too Many Requests

Code:    111
Message: "too many requests"
Temporary: true

Error Too Many Requests means that server rejected request due to rate limiting strategies.

Unrecoverable Position

Code:    112
Message: "unrecoverable position"

Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query.

This can happen due to wrong epoch passed.

Client disconnect codes

Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes).

note

We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. We expect these codes are mostly useful for logs and metrics.

DisconnectConnectionClosed

Code: 3000
Reason: "connection closed"

DisconnectConnectionClosed is a special Disconnect object used when client connection was closed without any advice from a server side. This can be a clean disconnect, or temporary disconnect of the client due to internet connection loss. Server can not distinguish the actual reason of disconnect.

Non-terminal disconnect codes

Client will reconnect after receiving such codes.

Shutdown

Code:      3001
Reason: "shutdown"

Disconnect Shutdown may be sent when node is going to shut down.

DisconnectServerError

Code:   3004
Reason: "internal server error"

DisconnectServerError issued when internal error occurred on server.

DisconnectExpired

Code:   3005
Reason: "connection expired"

DisconnectSubExpired

Code:   3006
Reason: "subscription expired"

DisconnectSubExpired issued when client subscription expired.

DisconnectSlow

Code:   3008
Reason: "slow"

DisconnectSlow issued when client can't read messages fast enough.

DisconnectWriteError

Code:   3009
Reason: "write error"

DisconnectWriteError issued when an error occurred while writing to client connection.

DisconnectInsufficientState

Code:   3010
Reason: "insufficient state"

DisconnectInsufficientState issued when server detects wrong client position in channel Publication stream. Disconnect allows clien to restore missed publications on reconnect.

DisconnectForceReconnect

Code:   3011
Reason: "force reconnect"

DisconnectForceReconnect issued when server disconnects connection for some reason and whants it to reconnect.

DisconnectNoPong

Code:   3012
Reason: "no pong"

DisconnectNoPong may be issued when server disconnects bidirectional connection due to no pong received to application-level server-to-client pings in a configured time.

DisconnectTooManyRequests

Code:   3013
Reason: "too many requests"

DisconnectTooManyRequests may be issued when client sends too many commands to a server.

Terminal disconnect codes

Client won't reconnect upon receiving such code.

DisconnectInvalidToken

Code:   3500
Reason: "invalid token"

DisconnectInvalidToken issued when client came with invalid token.

DisconnectBadRequest

Code:   3501
Reason: "bad request"

DisconnectBadRequest issued when client uses malformed protocol frames.

DisconnectStale

Code:   3502
Reason: "stale"

DisconnectStale issued to close connection that did not become authenticated in configured interval after dialing.

DisconnectForceNoReconnect

Code:   3503
Reason: "force disconnect"

DisconnectForceNoReconnect issued when server disconnects connection and asks it to not reconnect again.

DisconnectConnectionLimit

Code:   3504
Reason: "connection limit"

DisconnectConnectionLimit can be issued when client connection exceeds a configured connection limit (per user ID or due to other rule).

DisconnectChannelLimit

Code:   3505
Reason: "channel limit"

DisconnectChannelLimit can be issued when client connection exceeds a configured channel limit.

DisconnectInappropriateProtocol

Code:   3506
Reason: "inappropriate protocol"

DisconnectInappropriateProtocol can be issued when client connection format can not handle incoming data. For example, this happens when JSON-based clients receive binary data in a channel. This is usually an indicator of programmer error, JSON clients can not handle binary.

DisconnectPermissionDenied

Code:   3507
Reason: "permission denied"

DisconnectPermissionDenied may be issued when client attempts accessing a server without enough permissions.

DisconnectNotAvailable

Code:   3508
Reason: "not available"

DisconnectNotAvailable may be issued when ErrorNotAvailable does not fit message type, for example we issue DisconnectNotAvailable when client sends asynchronous message without MessageHandler set on server side.

DisconnectTooManyErrors

Code:   3509
Reason: "too many errors"

DisconnectTooManyErrors may be issued when client generates too many errors.

- + \ No newline at end of file diff --git a/docs/4/server/configuration.html b/docs/4/server/configuration.html index fe86b92bf..69e4b697a 100644 --- a/docs/4/server/configuration.html +++ b/docs/4/server/configuration.html @@ -16,13 +16,13 @@ - +

Configure Centrifugo

Let's look at how Centrifugo can be configured.

There are more options

This chapter describes configuration principles and some important configuration options. There are some options not mentioned here but described later in each feature documentation.

Configuration sources

Centrifugo can be configured in several ways.

Command-line flags

Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer environment or configuration file sources.

Command-line options have the highest priority when set than other ways to configure Centrifugo.

OS environment variables

All Centrifugo options can be set over env in the format CENTRIFUGO_<OPTION_NAME> (i.e. option name with CENTRIFUGO_ prefix, all in uppercase).

Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags.

Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use "true" value for an environment variable (or simply "1"). To set false use "false" or "0". Example:

export CENTRIFUGO_PROMETHEUS="1"

Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example:

export CENTRIFUGO_ALLOWED_ORIGINS="https://mysite1.example.com https://mysite2.example.com"

For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name.

Empty environment variables are considered unset (!) and will fall back to the next configuration source.

Configuration file

Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable prevails over the same option in config file).

A simple way to start with Centrifugo is to run:

centrifugo genconfig

This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo:

centrifugo -c config.json

Config file formats

Centrifugo supports three configuration file formats: JSON, YAML, or TOML.

JSON config format

Here is an example of Centrifugo JSON configuration file:

config.json
{
"allowed_origins": ["http://localhost:3000"],
"token_hmac_secret_key": "<YOUR-SECRET-STRING-HERE>",
"api_key": "<YOUR-API-KEY-HERE>"
}

token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT.

api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients.

allowed_origins option described below.

TOML config format

Centrifugo also supports TOML format for configuration file:

centrifugo --config=config.toml

Where config.toml contains:

config.toml
allowed_origins: [ "http://localhost:3000" ]
token_hmac_secret_key = "<YOUR-SECRET-STRING-HERE>"
api_key = "<YOUR-API-KEY-HERE>"
log_level = "debug"

In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.

YAML config format

YAML format is also supported:

config.yaml
allowed_origins:
- "http://localhost:3000"
token_hmac_secret_key: "<YOUR-SECRET-STRING-HERE>"
api_key: "<YOUR-API-KEY-HERE>"
log_level: debug

With YAML remember to use spaces, not tabs when writing a configuration file.

Important options

Let's describe some important options you can configure when running Centrifugo.

allowed_origins

This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins.

As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array.

Connection requests without Origin header set are passing through without any checks (i.e. always allowed).

For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way:

"allowed_origins": [
"http://localhost:3000"
]

When connecting from https://example.com:

"allowed_origins": [
"https://example.com"
]

Origin pattern can contain wildcard symbol * to match subdomains:

"allowed_origins": [
"https://*.example.com"
]

– in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check.

It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature):

"allowed_origins": [
"*"
]

address

Bind your Centrifugo to a specific interface address (string, by default "" - listen on all available interfaces).

port

Port to bind Centrifugo to (string, by default "8000").

engine

Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.

Advanced options

These options allow tweaking server behavior, in most cases default values are good to start with.

client_channel_limit

Default: 128

Sets the maximum number of different channel subscriptions a single client can have.

tip

When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.

channel_max_length

Default: 255

Sets the maximum length of the channel name.

client_user_connection_limit

Default: 0

The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited.

The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).

client_connection_limit

Added in Centrifugo v4.0.1

Default: 0

When set to a value > 0 client_connection_limit limits the max number of connections single Centrifugo node can handle. It acts on HTTP middleware level and stops processing request if the condition met. It logs a warning into logs in this case and increments centrifugo_node_client_connection_limit Prometheus counter. Client SDKs will attempt reconnecting.

Some motivation behind this option may be found in this issue.

Note, that at this point client_connection_limit does not affect connections coming over GRPC unidirectional transport.

client_connection_rate_limit

Added in Centrifugo v4.1.1

Default: 0

client_connection_rate_limit sets the maximum number of HTTP requests to establish a new real-time connection a single Centrifugo node will accept per second (on real-time transport endpoints). All requests outside the limit will get 503 Service Unavailable code in response. Our SDKs handle this with backoff reconnection.

By default, no limit is used.

Note, that at this point client_connection_rate_limit does not affect connections coming over GRPC unidirectional transport.

client_queue_max_size

Default: 1048576

Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.

client_concurrency

Default: 0

client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently.

By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).

client_stale_close_delay

Duration, default: 10s

This option allows tuning the maximum time Centrifugo will wait for the connect frame (which contains authentication information) from the client after establishing connection. Default value should be reasonable for most use cases.

allow_anonymous_connect_without_token

Default: false

Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID). Access to channel operations should be explicitly enabled for anonymous connections.

disallow_anonymous_connection_tokens

Added in Centrifugo v4.1.1

Default: false

When the option is set Centrifugo won't accept connections from anonymous users even if they provided a valid JWT. I.e. if token is valid, but sub claim is empty – then Centrifugo closes connection with advice to not reconnect again.

gomaxprocs

Default: 0

By default, Centrifugo runs on all available CPU cores (also Centrifugo can look at cgroup limits when rnning in Docker/Kubernetes). To limit the number of cores Centrifugo can utilize in one moment use this option.

Endpoint configuration.

After Centrifugo started there are several endpoints available.

Default endpoints.

Bidirectional WebSocket default endpoint:

ws://localhost:8000/connection/websocket

Bidirectional emulation with HTTP-streaming (disabled by default):

ws://localhost:8000/connection/http_stream

Bidirectional emulation with SSE (EventSource) (disabled by default):

ws://localhost:8000/connection/sse

Bidirectional SockJS default endpoint (disabled by default):

http://localhost:8000/connection/sockjs

Unidirectional EventSource endpoint (disabled by default):

http://localhost:8000/connection/uni_sse

Unidirectional HTTP streaming endpoint (disabled by default):

http://localhost:8000/connection/uni_http_stream

Unidirectional WebSocket endpoint (disabled by default):

http://localhost:8000/connection/uni_websocket

Unidirectional SSE (EventSource) endpoint (disabled by default):

http://localhost:8000/connection/uni_sse

Server HTTP API endpoint:

http://localhost:8000/api

By default, all endpoints work on port 8000. This can be changed with port option:

{
"port": 9000
}

In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.

Admin endpoints.

Admin web UI endpoint works on root path by default, i.e. http://localhost:8000.

For more details about admin web UI, refer to the Admin web UI documentation.

Debug endpoints.

Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config:

{
...
"debug": true
}

And endpoint:

http://localhost:8000/debug/pprof/

– will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.

Health check endpoint

Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag:

centrifugo -c config.json --health

Custom internal ports

We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal:

  • API endpoint (/api) - for HTTP API requests
  • Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface
  • Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format
  • Health check endpoint (/health) - used to do health checks
  • Debug endpoints (/debug/pprof) - used to inspect internal server state

It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration.

Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work.

To run internal endpoints on custom port use internal_port option:

{
...
"internal_port": 9000
}

So admin web interface will work on address:

http://localhost:9000

Also, debug page will be available on a new custom port too:

http://localhost:9000/debug/pprof/

The same for API and Prometheus endpoints.

Disable default endpoints

To disable websocket endpoint set websocket_disable boolean option to true.

To disable API endpoint set api_disable boolean option to true.

Customize handler endpoints

It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options:

  • admin_handler_prefix (default "") - to control Admin panel URL prefix
  • websocket_handler_prefix (default "/connection/websocket") - to control WebSocket URL prefix
  • http_stream_handler_prefix (default "/connection/http_stream") - to control HTTP-streaming URL prefix
  • sse_handler_prefix (default "/connection/sse") - to control SSE/EventSource URL prefix
  • sockjs_handler_prefix (default "/connection/sockjs") - to control SockJS URL prefix
  • uni_sse_handler_prefix (default "/connection/uni_sse") - to control unidirectional Eventsource URL prefix
  • uni_http_stream_handler_prefix (default "/connection/uni_http_stream") - to control unidirectional HTTP streaming URL prefix
  • uni_websocket_handler_prefix (default "/connection/uni_websocket") - to control unidirectional WebSocket URL prefix
  • api_handler_prefix (default "/api") - to control HTTP API URL prefix
  • prometheus_handler_prefix (default "/metrics") - to control Prometheus URL prefix
  • health_handler_prefix (default "/health") - to control health check URL prefix

Signal handling

It's possible to send HUP signal to Centrifugo to reload a configuration:

kill -HUP <PID>

Though at moment this will only reload token secrets and channel options (top-level and namespaces).

Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.

Insecure modes

Insecure client connection

The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. It also disables permission checks on client API level - for presence and history calls. This mode can be useful for demo projects based on Centrifugo, integration tests, local projects, or real-time application prototyping. Don't use it in production until you 100% know what you are doing.

Insecure API mode

This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules.

The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.

Insecure admin mode

This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).

Setting time duration options

Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use "5s".

The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option).

Valid time units are "ms" (milliseconds), "s" (seconds), "m" (minutes), "h" (hours).

Some examples:

"1000ms" // 1000 milliseconds
"1s" // 1 second
"12h" // 12 hours
"720h" // 30 days

Setting namespaces over env

While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special:

CENTRIFUGO_NAMESPACES='[{"name": "ns1"}, {"name": "ns2"}]' ./centrifugo

I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.

Anonymous usage stats

Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage.

Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal.

To disable sending usage stats set usage_stats_disable option:

config.json
{
"usage_stats_disable": true
}
- + \ No newline at end of file diff --git a/docs/4/server/console_commands.html b/docs/4/server/console_commands.html index c036599ae..44bf99ff6 100644 --- a/docs/4/server/console_commands.html +++ b/docs/4/server/console_commands.html @@ -16,13 +16,13 @@ - +

Helper CLI commands

Here is a list of helpful command-line commands that come with Centrifugo executable.

version

To show Centrifugo version and exit run:

centrifugo version

genconfig

Another command is genconfig:

centrifugo genconfig -c config.json

It will automatically generate the minimal required configuration file. This is mostly useful for development.

If any errors happen – program will exit with error message and exit code 1.

genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file:

centrifugo genconfig -c config.toml

checkconfig

Centrifugo has special command to check configuration file checkconfig:

centrifugo checkconfig --config=config.json

If any errors found during validation – program will exit with error message and exit code 1.

gentoken

Another command is gentoken:

centrifugo gentoken -c config.json -u 28282

It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week).

You can change token TTL with -t flag (number of seconds):

centrifugo gentoken -c config.json -u 28282 -t 3600

This way generated token will be valid for 1 hour.

If any errors happen – program will exit with error message and exit code 1.

This command is mostly useful for development.

gensubtoken

Another command is gensubtoken:

centrifugo gensubtoken -c config.json -u 28282 -s channel

It will automatically generate HMAC SHA-256 based subscription token for channel channel and user with ID 28282 (which expires in 1 week).

You can change token TTL with -t flag (number of seconds):

centrifugo gentoken -c config.json -u 28282 -s channel -t 3600

This way generated token will be valid for 1 hour.

If any errors happen – program will exit with error message and exit code 1.

This command is mostly useful for development.

checktoken

One more command is checktoken:

centrifugo checktoken -c config.json <TOKEN>

It will validate your connection JWT, so you can test it before using while developing application.

If any errors happen or validation failed – program will exit with error message and exit code 1.

This is mostly useful for development.

checksubtoken

One more command is checksubtoken:

centrifugo checksubtoken -c config.json <TOKEN>

It will validate your subscription JWT, so you can test it before using while developing application.

If any errors happen or validation failed – program will exit with error message and exit code 1.

This is mostly useful for development.

- + \ No newline at end of file diff --git a/docs/4/server/engines.html b/docs/4/server/engines.html index e0a2d4d62..751e98084 100644 --- a/docs/4/server/engines.html +++ b/docs/4/server/engines.html @@ -16,7 +16,7 @@ - + @@ -25,7 +25,7 @@ the same port number (8000 or whatever you want) for all instances.

And finally, let's start the third instance:

centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379

Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes.

To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation.

tip

In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers.

Here is a live example where we locally start two Centrifugo nodes both connected to local Redis:

Redis Sentinel for high availability

Centrifugo supports the official way to add high availability to Redis - Redis Sentinel.

For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name:

  • redis_sentinel_address (string, default "") - comma separated list of Sentinel addresses for HA. At least one known server required.
  • redis_sentinel_master_name (string, default "") - name of Redis master Sentinel monitors

Also:

  • redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1
  • redis_sentinel_user - optional string user (used only in Redis ACL-based auth).

So you can start Centrifugo which will use Sentinels to discover Redis master instances like this:

centrifugo --config=config.json

Where config.json:

config.json
{
...
"engine": "redis",
"redis_sentinel_address": "127.0.0.1:26379",
"redis_sentinel_master_name": "mymaster"
}

Sentinel configuration file may look like this (for 3-node Sentinel setup with quorum 2):

port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 60000

You can find how to properly set up Sentinels in official documentation.

Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.

Redis Sentinel TLS

To configure TLS for Redis Sentinel use the following options.

redis_sentinel_tls

Boolean, default false - enable Redis TLS connection.

redis_sentinel_tls_insecure_skip_verify

Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_sentinel_tls_skip_verify – but it will be removed in v5.

redis_sentinel_tls_cert

Added in Centrifugo v4.1.0

String, default "" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_sentinel_tls_cert_pem option.

redis_sentinel_tls_key

Added in Centrifugo v4.1.0

String, default "" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_sentinel_tls_key_pem option.

redis_sentinel_tls_root_ca

Added in Centrifugo v4.1.0

String, default "" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_sentinel_tls_root_ca_pem option.

redis_sentinel_tls_server_name

Added in Centrifugo v4.1.0

String, default "" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.

Haproxy instead of Sentinel configuration

Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case.

For example, you can use something like this in Haproxy config:

listen redis
server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2
server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup
bind *:16379
mode tcp
option tcpka
option tcplog
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
balance roundrobin

And then just point Centrifugo to this Haproxy:

centrifugo --config=config.json --engine=redis --redis_address="localhost:16379"

Redis sharding

Centrifugo has built-in Redis sharding support.

This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale.

At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples.

To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this:

config.json
{
...
"engine": "redis",
"redis_address": [
"127.0.0.1:6379",
"127.0.0.1:6380",
]
}

To start Centrifugo with Redis instances on different hosts:

config.json
{
...
"engine": "redis",
"redis_address": [
"192.168.1.34:6379",
"192.168.1.35:6379",
]
}

If you also need to customize AUTH password, Redis DB number then you can use an extended address notation.

note

Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers.

When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).

Redis cluster

Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example:

{
...
"redis_cluster_address": [
"localhost:30001,localhost:30002,localhost:30003"
]
}

You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start.

To set the same over environment variable:

CENTRIFUGO_REDIS_CLUSTER_ADDRESS="localhost:30001" CENTRIFUGO_ENGINE=redis ./centrifugo

If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array:

{
...
"redis_cluster_address": [
"localhost:30001,localhost:30002,localhost:30003",
"localhost:30101,localhost:30102,localhost:30103"
]
}

Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump).

To reproduce the same over environment variable use space to separate different clusters:

CENTRIFUGO_REDIS_CLUSTER_ADDRESS="localhost:30001,localhost:30002 localhost:30101,localhost:30102" CENTRIFUGO_ENGINE=redis ./centrifugo

KeyDB Engine

EXPERIMENTAL

Centrifugo Redis engine seamlessly works with KeyDB. KeyDB server is compatible with Redis and provides several additional features beyond.

caution

We can't give any promises about compatibility with KeyDB in the future Centrifugo releases - while KeyDB is fully compatible with Redis things should work just fine. That's why we consider this as EXPERIMENTAL feature.

Use KeyDB instead of Redis only if you are sure you need it. Nothing stops you from running several Redis instances per each core you have, configure sharding, and obtain even better performance than KeyDB can provide (due to lack of synchronization between threads in Redis).

To run Centrifugo with KeyDB all you need to do is use redis engine but run the KeyDB server instead of Redis.

Other Redis compatible

Other storages which are compatible with Centrifugo may work, but we did not make enough testing with them. Some of them still evolving and do not fully support Redis protocol. So if you want to use these storages with Centrifugo – please read carefully the notes below:

  • AWS Elasticache – it was reported to work, but we suggest you testing the setup including failover tests and work under load.
  • DragonflyDB - it's mostly compatible, the only problem with DragonflyDB v1.0.0 we observed is failing test regarding history iteration in reversed order (not very common). We have not tested a Redis Cluster emulation mode provided by DragonflyDB yet. We suggest you testing the setup including failover tests and work under load.

Tarantool engine

EXPERIMENTAL

Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics.

caution

EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves.

There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge.

info

To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together.

There are several supported Tarantool topologies to which Centrifugo can connect:

  • One standalone Tarantool instance
  • Many standalone Tarantool instances and consistently shard data between them
  • Tarantool running in Cartridge
  • Tarantool with replica and automatic failover in Cartridge
  • Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them
  • Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7)

After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes):

config.json
{
...
"engine": "tarantool",
"tarantool_address": "127.0.0.1:3301"
}

See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework.

See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.

Tarantool engine options

tarantool_address

String or array of strings. Default tcp://127.0.0.1:3301.

Connection address to Tarantool.

tarantool_mode

String, default standalone

A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication).

All modes support client-side consistent sharding (similar to what Redis engine provides).

tarantool_user

String, default "". Allows setting a user.

tarantool_password

String, default "". Allows setting a password.

history_meta_ttl

Duration, default 2160h.

Same option as for Memory engine and Redis engine also applies to Tarantool case.

Nats broker

It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below.

Limitations:

  • Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available.
  • Nats wildcard channel subscriptions with symbols * and > not supported.

First start Nats server:

$ nats-server
[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7
[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set]
[3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222
[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4
[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready

Then start Centrifugo with broker option:

centrifugo --broker=nats --config=config.json

And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine):

centrifugo --broker=nats --config=config.json --port=8001

Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.

Options

nats_url

String, default nats://127.0.0.1:4222.

Connection url in format nats://derek:pass@localhost:4222.

nats_prefix

String, default centrifugo.

Prefix for channels used by Centrifugo inside Nats.

nats_dial_timeout

Duration, default 1s.

Timeout for dialing with Nats.

nats_write_timeout

Duration, default 1s.

Write (and flush) timeout for a connection to Nats.

- + \ No newline at end of file diff --git a/docs/4/server/history_and_recovery.html b/docs/4/server/history_and_recovery.html index f44591e49..8fd75a13f 100644 --- a/docs/4/server/history_and_recovery.html +++ b/docs/4/server/history_and_recovery.html @@ -16,13 +16,13 @@ - +

History and recovery

Centrifugo engines can maintain publication history for channels with configured history size and TTL.

History design

History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero.

Centrifugo is designed with an idea that history streams are ephemeral (can be created on the fly without explicit create call from Centrifugo users) and can expire or can be lost at any moment. Centrifugo provides a way for a client to understand that channel history lost. In this case, the main application database should be the source of truth and state recovery.

When history is on every message published into a channel is saved into a history stream. The persistence properties of history are dictated by a Centrifugo engine used. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside Tarantool spaces.

Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of state recovery anymore.

tip

History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.

History iteration API

History iteration based on three fields:

  • limit
  • since
  • reverse

Combining these fields you can iterate over a stream in both directions.

Get current stream top offset and epoch:

history(limit: 0, since: null, reverse: false)

Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default):

history(limit: -1, since: null, reverse: false)

Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default):

history(limit: -1, since: null, reverse: true)

Get history from the current beginning (up to 10):

history(limit: 10, since: null, reverse: false)

Get history from the current end in reversed direction (up to 10):

history(limit: 10, since: null, reverse: true) 

Get up to 10 publications since known stream position (described by offset and epoch values):

history(limit: 10, since: {offset: 0, epoch: "epoch"}, reverse: false)

Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest):

history(limit: 10, since: {offset: 11, epoch: "epoch"}, reverse: true)

Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun):

// Iterate by 10.
limit := 10
// Paginate in reversed order first, then invert it.
reverse := true
// Start with nil StreamPosition, then fill it with value while paginating.
var sp *gocent.StreamPosition

for {
historyResult, err = c.History(
ctx,
channel,
gocent.WithLimit(limit),
gocent.WithReverse(reverse),
gocent.WithSince(sp),
)
if err != nil {
log.Fatalf("Error calling history: %v", err)
}
for _, pub := range historyResult.Publications {
log.Println(pub.Offset, "=>", string(pub.Data))
sp = &gocent.StreamPosition{
Offset: pub.Offset,
Epoch: historyResult.Epoch,
}
}
if len(historyResult.Publications) < limit {
// Got all pubs, invert pagination direction.
reverse = !reverse
log.Println("end of stream reached, change iteration direction")
}
}

Automatic message recovery

One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while.

In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnect at the same time).

danger

Message recovery protocol feature designed to be used together with reasonably small history stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time.

To enable recovery mechanism for channels set the force_recovery boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. It's also possible to ask for enabling recovery from the client-side when configuring Subscription object – in this case client must have a permission to call history API.

When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not.

The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default.

Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client SDKs (for bidirectional transport).

The recovery process works this way:

  1. Let's suppose client subscribes on a channel with recovery on.
  2. Client receives subscribe reply from Centrifugo with two values: stream epoch and stream top offset, those values are saved by an SDK implementation.
  3. Every received publication has incremental offset, client SDK increments locally saved offset on each publication from a channel.
  4. Let's say at this point client disconnected for a while.
  5. Upon resubscribing to a channel SDK provides last seen epoch anf offset to Centrifugo.
  6. Centrifugo tries to load all the missed publications starting from the stream position provided by a client.
  7. If Centrifugo decides it can successfully recover client's state – then all missed publications returned in subscribe reply and client receives recovered: true in subscribed event context, and publication event handler of Subscription is called for every missed publication. Otherwise no publications returned and recovered flag of subscribed event context is set to false.

epoch is useful for cases when history storage is temporary and can lose the history stream entirely. In this case comparing epoch values gives Centrifugo a tip that while publications exist and theoretically have same offsets as before - the stream is actually different, so it's impossible to use it for the recovery process.

To summarize, here is a list of possible scenarios when Centrifugo can't recover client's state for a channel and provides recovered: false flag in subscribed event context:

  • number of missed publications exceeds client_recovery_max_publication_limit option
  • number of missed publications exceeds history_size namespace option
  • client was away for a long time and history stream expired according to history_ttl namespace option
  • storage used by Centrifugo engine lost the stream (restart, number of shards changed, cleared by the administrator, etc.)

Having said this all, Centrifugo recovery is nice to keep the continuity of the connection and subscription. This speed-ups resubscribe in many cases and helps the backend to survive mass reconnect scenario since you avoid lots of requests for state loading. For setups with millions of connections this can be a life-saver. But we recommend applications to always have a way to load the state from the main application database. For example, on app reload.

You can also manually implement your own recovery logic on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client resubscribe completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.

- + \ No newline at end of file diff --git a/docs/4/server/infra_tuning.html b/docs/4/server/infra_tuning.html index 812005a0e..08c4234f8 100644 --- a/docs/4/server/infra_tuning.html +++ b/docs/4/server/infra_tuning.html @@ -16,14 +16,14 @@ - +

Infrastructure tuning

As Centrifugo deals with lots of persistent connections your operating system and server infrastructure must be ready for it.

Open files limit

You should increase a max number of open files Centrifugo process can open if you want to handle more connections.

To get the current open files limit run:

ulimit -n

On Linux you can check limits for a running process using:

cat /proc/<PID>/limits

The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default.

See this document to get more info on how to increase this number.

If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536.

You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).

Ephemeral port exhaustion

Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing.

The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple:

source ip | source port | destination ip | destination port

On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default).

In order to eliminate a problem you can:

  • Increase the ephemeral port range by tuning ip_local_port_range option
  • Deploy more Centrifugo server instances to load balance across
  • Deploy more load balancer instances
  • Use virtual network interfaces

See a post in Pusher blog about this problem and more detailed solution steps.

Sockets in TIME_WAIT state

On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state.

Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side.

Look how many socket descriptors in TIME_WAIT state.

netstat -an |grep TIME_WAIT | grep <CENTRIFUGO_PID> | wc -l

Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html

The advices here are similar to ephemeral port exhaustion problem:

  • Increase the ephemeral port range by tuning ip_local_port_range option
  • Deploy more Centrifugo server instances to load balance across
  • Deploy more load balancer instances
  • Use virtual network interfaces

Proxy max connections

Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established.

Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.

Conntrack table

More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.

Additional server protection

You should also consider adding additional protection to your Centrifugo endpoints. Centrifugo itself provides several options (described in configuration section) regarding server protection from the malicious behavior. Though an additional layer of DDOS protection on network or infrastructure level is highly recommended. For example, you may want to limit the number of connections coming from particular IP address.

Here we list some possible ways you can use to protect your Centrifugo installation:

The list is not exhaustive of course.

- + \ No newline at end of file diff --git a/docs/4/server/load_balancing.html b/docs/4/server/load_balancing.html index a4e36d3f5..245fd5e29 100644 --- a/docs/4/server/load_balancing.html +++ b/docs/4/server/load_balancing.html @@ -16,7 +16,7 @@ - + @@ -28,7 +28,7 @@ can be such a balancer too.

In this section we will look at Nginx configuration to deploy Centrifugo.

Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections.

There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).

Separate domain for Centrifugo

upstream centrifugo {
# uncomment ip_hash if using SockJS transport with many upstream servers.
#ip_hash;
server 127.0.0.1:8000;
}

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

#server {
# listen 80;
# server_name centrifugo.example.com;
# rewrite ^(.*) https://$server_name$1 permanent;
#}

server {

server_name centrifugo.example.com;

listen 80;

#listen 443 ssl;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_certificate /etc/nginx/ssl/server.crt;
#ssl_certificate_key /etc/nginx/ssl/server.key;

include /etc/nginx/mime.types;
default_type application/octet-stream;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1000;
gzip_proxied any;

# Only retry if there was a communication error, not a timeout
# on the Centrifugo server (to avoid propagating "queries of death"
# to all frontends)
proxy_next_upstream error;

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;

location /connection {
proxy_pass http://centrifugo;
proxy_buffering off;
keepalive_timeout 65;
proxy_read_timeout 60s;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}

location / {
proxy_pass http://centrifugo;
}

error_page 500 502 503 504 /50x.html;

location = /50x.html {
root /usr/share/nginx/html;
}
}

Embed to a location of web site

upstream centrifugo {
# uncomment ip_hash if using SockJS transport with many upstream servers.
#ip_hash;
server 127.0.0.1:8000;
}

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {

# ... your web site Nginx config

location /centrifugo/ {
rewrite ^/centrifugo/(.*) /$1 break;
proxy_pass http://centrifugo;
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
}

location /centrifugo/connection {
rewrite ^/centrifugo(.*) $1 break;
proxy_pass http://centrifugo;
proxy_buffering off;
keepalive_timeout 65;
proxy_read_timeout 60s;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}

worker_connections

You may also need to update worker_connections option of Nginx:

events {
worker_connections 65535;
}
- + \ No newline at end of file diff --git a/docs/4/server/monitoring.html b/docs/4/server/monitoring.html index a86f2156b..f30cd073b 100644 --- a/docs/4/server/monitoring.html +++ b/docs/4/server/monitoring.html @@ -16,13 +16,13 @@ - +

Metrics monitoring

Centrifugo supports reporting metrics in Prometheus format and can automatically export metrics to Graphite.

Prometheus

To enable Prometheus endpoint start Centrifugo with prometheus option on:

config.json
{
...
"prometheus": true
}

This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.

Graphite

To enable automatic export to Graphite (via TCP):

config.json
{
...
"graphite": true,
"graphite_host": "localhost",
"graphite_port": 2003
}

By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection.

If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).

Grafana dashboard

Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.

- + \ No newline at end of file diff --git a/docs/4/server/presence.html b/docs/4/server/presence.html index ce785e835..00ac91a39 100644 --- a/docs/4/server/presence.html +++ b/docs/4/server/presence.html @@ -16,13 +16,13 @@ - +

Online presence

The online presence feature of Centrifugo is a powerful tool that allows you to monitor and manage active users in real-time on a specific channel. It provides live data about which users are currently connected to your application.

Overview

Online presence provides an instantaneous snapshot of users currently connected to a specific channel. This information includes the user's unique ID and the connection timestamp.

Enabling Online Presence

To enable Online Presence, you need to set the presence option to true for the specific channel in your server configuration.

{
"namespaces": [{
"namespace": "public",
"presence": true
}]
}

After enabling this you can query presence information over server HTTP/GRPC presence call:

curl --header "Content-Type: application/json" \
--header "Authorization: apikey YOUR_API_KEY" \
--request POST \
--data '{"method": "presence", "params": {"channel": "public:test"}}' \
http://localhost:8000/api

See description of presence API.

Retrieving presence on the client side

Once presence enabled, you can retrieve the presence information on the client side too by calling the presence method on the channel.

To do this you need to give the client permission to call presence. Once done, presence may be retrieved from the subscription:

const presenceData = await subscription.presence(channel);

It's also available on the top-level of the client (for example, if you need to call presence for server-side subscription):

const presenceData = await client.presence(channel);

If the permission check has passed successfully – both methods will return an object containing information about currently subscribed clients.

Join and leave events

Online Presence feature also allows real-time tracking of users joining or leaving a channel by subscribing to join and leave events:

subscription.on('join', function(joinCtx) {
console.log('client joined:', joinCtx);
});

subscription.on('leave', function(leaveCtx) {
console.log('client left:', leaveCtx);
});

And the same on client top-level for the needs of server-side subscriptions (analogous to the presence call described above).

These events provide real-time updates and can be used to keep track of user activity and manage live interactions.

Implementation notes

The Online Presence feature might increase the load on your Centrifugo server, since Centrifugo need to maintain an addition data structure. Therefore, it is recommended to use this feature judiciously based on your server's capability and the necessity of real-time presence data in your application.

Always make sure to secure the presence data, as it could expose sensitive information about user activity in your application. Ensure appropriate security measures are in place.

Join and leave events delivered with at most once guarantee.

See more about presence design in design overview chapter.

Also check out FAQ.

Conclusion

The Online Presence feature of Centrifugo is a highly useful tool for real-time applications. It provides instant and live data about user activity, which can be critical for interactive features like chats, collaborative tools, multiplayer games, or live tracking systems. Make sure to configure and use this feature appropriately to get the most out of your real-time application.

- + \ No newline at end of file diff --git a/docs/4/server/proxy.html b/docs/4/server/proxy.html index dd5d622c3..6716a726c 100644 --- a/docs/4/server/proxy.html +++ b/docs/4/server/proxy.html @@ -16,13 +16,13 @@ - +

Proxy events to the backend

It's possible to proxy some client connection events from Centrifugo to the application backend and react to them in a custom way. For example, it's possible to authenticate connection via request from Centrifugo to application backend, refresh client sessions and answer to RPC calls sent by a client over bidirectional connection. Also, you may control subscription and publication permissions using these hooks.

The list of events that can be proxied:

  • connect – called when a client connects to Centrifugo, so it's possible to authenticate user, return custom data to a client, subscribe connection to several channels, attach meta information to the connection, and so on. Works for bidirectional and unidirectional transports.
  • refresh - called when a client session is going to expire, so it's possible to prolong it or just let it expire. Can also be used just as a periodical connection liveness callback from Centrifugo to app backend. Works for bidirectional and unidirectional transports.
  • subscribe - called when clients try to subscribe on a channel, so it's possible to check permissions and return custom initial subscription data. Works for bidirectional transports only.
  • publish - called when a client tries to publish into a channel, so it's possible to check permissions and optionally modify publication data. Works for bidirectional transports only.
  • sub_refresh - called when a client subscription is going to expire, so it's possible to prolong it or just let it expire. Can also be used just as a periodical subscription liveness callback from Centrifugo to app backend. Works for bidirectional and unidirectional transports.
  • rpc - called when a client sends RPC, you can do whatever logic you need based on a client-provided RPC method and params. Works for bidirectional transports only.

At the moment Centrifugo can proxy these events over two protocols:

  • HTTP (JSON payloads)
  • GRPC (Protobuf messages)

HTTP proxy

HTTP proxy in Centrifugo converts client connection events into HTTP calls to the application backend.

HTTP request structure

All proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).

Proxy HTTP headers

The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example:

config.json
{
...
"proxy_http_headers": [
"Origin",
"User-Agent",
"Cookie",
"Authorization",
"X-Real-Ip",
"X-Forwarded-For",
"X-Request-Id"
]
}

Alternatively, you can set a list of headers via an environment variable (space separated):

export CENTRIFUGO_PROXY_HTTP_HEADERS="Cookie User-Agent X-B3-TraceId X-B3-SpanId" ./centrifugo
note

Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend.

Proxy GRPC metadata

When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied.

See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.

Connect proxy

With the following options in the configuration file:

{
...
"proxy_connect_endpoint": "http://localhost:3000/centrifugo/connect",
"proxy_connect_timeout": "1s"
}

– connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request.

danger

Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections.

Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. In many cases your existing session mechanism will provide user authentication details to the connect proxy handler.

tip

If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server.

This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired).

Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json"
}

Expected response example:

{"result": {"user": "56"}}

This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result.

Several app examples which use connect proxy can be found in our blog:

Connect request fields

This is what sent from Centrifugo to application backend in case of connect proxy request.

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
namestringyesoptional name of the client (this field will only be set if provided by a client on connect)
versionstringyesoptional version of the client (this field will only be set if provided by a client on connect)
dataJSONyesoptional data from client (this field will only be set if provided by a client on connect)
b64datastringyesoptional data from the client in base64 format (if the binary proxy mode is used)
channelsArray of stringsyeslist of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result

Connect result fields

This is what application returns to Centrifugo inside result field in case of connect proxy request.

FieldTypeOptionalDescription
userstringnouser ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests
expire_atintegeryesa timestamp when connection must be considered expired. If not set or set to 0 connection won't expire at all
infoJSONyesa connection info JSON
b64infostringyesbinary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages
dataJSONyesa custom data to send to the client in connect command response.
b64datastringyesa custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client
channelsarray of stringsyesallows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions
subsmap of SubscribeOptionsyesmap of channels with options to subscribe connection to. See more details about server-side subscriptions
metaJSON object (ex. {"key": "value"})yesa custom data to attach to connection (this won't be exposed to client-side)

Options

proxy_connect_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

Example

Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return "56" as user ID):

class CentrifugoConnectHandler(tornado.web.RequestHandler):

def check_xsrf_cookie(self):
pass

def post(self):
self.set_header('Content-Type', 'application/json; charset="utf-8"')
data = json.dumps({
'result': {
'user': '56'
}
})
self.write(data)


def main():
options.parse_command_line()
app = tornado.web.Application([
(r'/centrifugo/connect', CentrifugoConnectHandler),
])
app.listen(3000)
tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
main()

This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side.

We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea.

What if connection is unauthenticated/unauthorized to connect?

In this case return a disconnect object as a response. See Return custom disconnect section. Depending on whether you want connection to reconnect or not (usually not) you can select the appropriate disconnect code. Sth like this in response:

{
"disconnect": {
"code": 4501,
"reason": "unauthorized"
}
}

– may be sufficient enough. Choosing codes and reason is up to the developer, but follow the rules described in Return custom disconnect section.

Refresh proxy

With the following options in the configuration file:

{
...
"proxy_refresh_endpoint": "http://localhost:3000/centrifugo/refresh",
"proxy_refresh_timeout": "1s"
}

– Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side.

The payload sent to app backend in refresh request (when the connection is going to expire):

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56"
}

Expected response example:

{"result": {"expire_at": 1565436268}}

Refresh request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc.)
protocolstringnoprotocol type used by client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Refresh result fields

FieldTypeOptionalDescription
expiredboolyesa flag to mark the connection as expired - the client will be disconnected
expire_atintegeryesa timestamp in the future when connection must be considered expired
infoJSONyesa connection info JSON
b64infostringyesbinary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages

Options

proxy_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

RPC proxy

With the following option in the configuration file:

{
...
"proxy_rpc_endpoint": "http://localhost:3000/centrifugo/connect",
"proxy_rpc_timeout": "1s"
}

RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket (or SockJS) connection in a bidirectional way.

Payload example sent to app backend in RPC request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"method": "getCurrentPrice",
"data":{"params": {"object_id": 12}}
}

Expected response example:

{"result": {"data": {"answer": "2019"}}}

RPC request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket or sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
methodstringyesan RPC method string, if the client does not use named RPC call then method will be omitted
dataJSONyesRPC custom data sent by client
b64datastringyeswill be set instead of data field for binary proxy mode
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

RPC result fields

FieldTypeOptionalDescription
dataJSONyesRPC response - any valid JSON is supported
b64datastringyescan be set instead of data for binary response encoded in base64 format

Options

proxy_rpc_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

See below on how to return a custom error.

Subscribe proxy

With the following option in the configuration file:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s"
}

– subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel.

tip

Subscribe proxy does not proxy private and user-limited channels at the moment. That's because those are already providing a level of security (user-limited channels check current user ID, private channels require subscription token). In some cases you may use subscribe proxy as a replacement for private channels actually: if you prefer to check permissions using the proxy to backend mechanism – just stop using $ prefixes in channels, properly configure subscribe proxy and validate subscriptions upon proxy from Centrifugo to your backend (issued each time user tries to subscribe on a channel for which subscribe proxy enabled).

Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace.

So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s",
"proxy_subscribe": true
}

Or for channels in namespace sun:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s",
"namespaces": [{
"name": "sun",
"proxy_subscribe": true
}]
}

Payload example sent to app backend in subscribe request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "chat:index"
}

Expected response example if subscription is allowed:

{"result": {}}

Subscribe request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket or sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnoa string channel client wants to subscribe to
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)
dataJSONyescustom data from client sent with subscription request (this field will only be set if provided by a client on subscribe).
b64datastringyesoptional subscription data from the client in base64 format (if the binary proxy mode is used).

Subscribe result fields

FieldTypeOptionalDescription
infoJSONyesa channel info JSON
b64infostringyesa binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using
dataJSONyesa custom data to send to the client in subscribe command reply.
b64datastringyesa custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client
overrideOverride objectyesAllows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
force_push_join_leaveBoolValueyesOverride force_push_join_leave
force_positioningBoolValueyesOverride force_positioning
force_recoveryBoolValueyesOverride force_recovery

BoolValue is an object like this:

{
"value": true/false
}

See below on how to return an error in case you don't want to allow subscribing.

Options

proxy_subscribe_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

What if connection is not allowed to subscribe?

In this case you can return error object as a subscribe handler response. See return custom error section.

In general, frontend applications should not try to subscribe to channels for which access is not allowed. But these situations can happen or malicious user can try to subscribe to a channel. In most scenarios returning:

{
"error": {
"code": 403,
"message": "permission denied"
}
}

– is sufficient enough. Error code may be not 403 actually, no real reason to force HTTP semantics here - so it's up to Centrifugo user to decide. Just keep it in range [400, 1999] as described here.

If case of returning response above, on client side unsubscribed event of Subscription object will be called with error code 403. Subscription won't resubscribe automatically after that.

Publish proxy

With the following option in the configuration file:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s"
}

– publish calls sent by a client will be proxied to proxy_publish_endpoint.

This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side.

Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too.

So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s",
"publish": true,
"proxy_publish": true
}

Or for channels in namespace sun:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s",
"namespaces": [{
"name": "sun",
"publish": true,
"proxy_publish": true
}]
}

Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace).

Payload example sent to app backend in a publish request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "chat:index",
"data":{"input":"hello"}
}

Expected response example if publish is allowed:

{"result": {}}

Publish request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnoa string channel client wants to publish to
dataJSONyesdata sent by client
b64datastringyeswill be set instead of data field for binary proxy mode
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Publish result fields

FieldTypeOptionalDescription
dataJSONyesan optional JSON data to send into a channel instead of original data sent by a client
b64datastringyesa binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing
skip_historyboolyeswhen set to true Centrifugo won't save publication to the channel history

See below on how to return an error in case you don't want to allow publishing.

Options

proxy_publish_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

Sub refresh proxy

Added in Centrifugo v4.1.1

With the following options in the configuration file:

{
...
"proxy_sub_refresh_endpoint": "http://localhost:3000/centrifugo/sub_refresh",
"proxy_sub_refresh_timeout": "1s"
}

– Centrifugo will call proxy_sub_refresh_endpoint when it's time to refresh the subscription. Centrifugo itself will ask your backend about subscription validity instead of subscription refresh workflow on the client-side.

Like subscribe and publish proxy types, sub refresh proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_sub_refresh that enables sub refresh proxy for channels in the namespace. Only subscriptions which have expiration time will be validated over sub refresh proxy endpoint.

Sub refresh proxy may be used as a periodical Subscription liveness callback from Centrifugo to app backend.

caution

In the current implementation the delay of Subscription refresh requests from Centrifugo to application backend may be up to one minute (was implemented this way from a simplicity and efficiency perspective). We assume this should be enough for many scenarios. But this may be improved if needed. Please reach us out with a detailed description of your use case where you want more accurate requests to refresh subscriptions.

So to enable sub refresh proxy for channels without namespace define proxy_sub_refresh on a top configuration level:

{
...
"proxy_sub_refresh_endpoint": "http://localhost:3000/centrifugo/sub_refresh",
"proxy_sub_refresh": true
}

Or for channels in namespace sun:

{
...
"proxy_sub_refresh_endpoint": "http://localhost:3000/centrifugo/publish",
"namespaces": [{
"name": "sun",
"proxy_sub_refresh": true
}]
}

The payload sent to app backend in sub refresh request (when the subscription is going to expire):

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "channel"
}

Expected response example:

{"result": {"expire_at": 1565436268}}

Sub refresh request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc.)
protocolstringnoprotocol type used by client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnochannel for which Subscription is going to expire
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Sub refresh result fields

FieldTypeOptionalDescription
expiredboolyesa flag to mark the connection as expired - the client will be disconnected
expire_atintegeryesa timestamp in the future when connection must be considered expired
infoJSONyesa channel info JSON
b64infostringyesbinary channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages

Options

proxy_sub_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

Return custom error

Application backend can return JSON object that contains an error to return it to the client:

{
"error": {
"code": 1000,
"message": "custom error"
}
}

Applications must use error codes in range [400, 1999]. Error code field is uint32 internally.

note

Returning custom error does not apply to response for refresh and sub refresh proxy requests as there is no sense in returning an error (will not reach client anyway).

Return custom disconnect

Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way:

{
"disconnect": {
"code": 4500,
"reason": "disconnect reason"
}
}

Application must use numbers in the range 4000-4999 for custom disconnect codes:

  • codes in range [4000, 4499] give client an advice to reconnect
  • codes in range [4500, 4999] are terminal codes – client won't reconnect upon receiving it.

Code is uint32 internally. Numbers outside of 4000-4999 range are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 ASCII symbols (i.e. 32 bytes max).

note

Returning custom disconnect does not apply to response for refresh and sub refresh proxy requests as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason.

GRPC proxy

Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server.

GRPC service definitions can be found in the Centrifugo repository: proxy.proto.

tip

GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions.

Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept).

All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy:

config.json
{
...
"proxy_connect_endpoint": "grpc://localhost:12000",
"proxy_connect_timeout": "1s"
}

Refresh proxy:

config.json
{
...
"proxy_refresh_endpoint": "grpc://localhost:12000",
"proxy_refresh_timeout": "1s"
}

Or for RPC proxy:

config.json
{
...
"proxy_rpc_endpoint": "grpc://localhost:12000",
"proxy_rpc_timeout": "1s"
}

For publish proxy in namespace chat:

config.json
{
...
"proxy_publish_endpoint": "grpc://localhost:12000",
"proxy_publish_timeout": "1s"
"namespaces": [
{
"name": "chat",
"publish": true,
"proxy_publish": true
}
]
}

Use subscribe proxy for all channels without namespaces:

config.json
{
...
"proxy_subscribe_endpoint": "grpc://localhost:12000",
"proxy_subscribe_timeout": "1s",
"proxy_subscribe": true
}

So the same as for HTTP, just the different endpoint scheme.

GRPC proxy options

Some additional options exist to control GRPC proxy behavior.

proxy_grpc_cert_file

String, default: "".

Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used.

proxy_grpc_credentials_key

String, default "" (i.e. not used).

Add custom key to per-RPC credentials.

proxy_grpc_credentials_value

String, default "" (i.e. not used).

A custom value for proxy_grpc_credentials_key.

GRPC proxy example

We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.:

  1. Copy proxy Protobuf definitions
  2. Generate GRPC code
  3. Run backend service with you custom business logic
  4. Point Centrifugo to it.

Header proxy rules

Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios:

Client protocol typeProxy typeClient headersClient metadata
HTTPHTTPIn proxy request headersN/A
GRPCGRPCN/AIn proxy request metadata
HTTPGRPCIn proxy request metadataN/A
GRPCHTTPN/AIn proxy request headers

Binary mode

As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding.

Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this.

Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side).

But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON.

In this case it's possible to turn Centrifugo proxy into binary mode by using:

config.json
{
...
"proxy_binary_encoding": true
}

Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues.

While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine.

Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.

Granular proxy mode

By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture).

Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.

Enable granular proxy mode

Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below.

config.json
{
...
"granular_proxy_mode": true
}

Defining a list of proxies

When using granular proxy mode on configuration top level you can define "proxies" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint.

Here is an example:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [
{
"name": "connect",
"endpoint": "http://localhost:3000/centrifugo/connect",
"timeout": "500ms",
"http_headers": ["Cookie"]
},
{
"name": "refresh",
"endpoint": "http://localhost:3000/centrifugo/refresh",
"timeout": "500ms"
},
{
"name": "subscribe1",
"endpoint": "http://localhost:3001/centrifugo/subscribe"
},
{
"name": "publish1",
"endpoint": "http://localhost:3001/centrifugo/publish"
},
{
"name": "rpc1",
"endpoint": "http://localhost:3001/centrifugo/rpc"
},
{
"name": "subscribe2",
"endpoint": "http://localhost:3002/centrifugo/subscribe"
},
{
"name": "publish2",
"endpoint": "grpc://localhost:3002"
}
{
"name": "rpc2",
"endpoint": "grpc://localhost:3002"
}
]
}

Let's look at all fields for a proxy object which is possible to set for each proxy inside "proxies" array.

Field nameField typeRequiredDescription
namestringyesUnique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$
endpointstringyesHTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC.
timeoutduration (string)noProxy request timeout, default "1s"
http_headersarray of stringsnoList of headers to proxy, by default no headers
grpc_metadataarray of stringsnoList of GRPC metadata keys to proxy, by default no metadata keys
binary_encodingboolnoUse base64 for payloads
include_connection_metaboolnoInclude meta information (attached on connect)
grpc_cert_filestringnoPath to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used.
grpc_credentials_keystringnoAdd custom key to per-RPC credentials.
grpc_credentials_valuestringnoA custom value for grpc_credentials_key.

Granular connect and refresh

As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event.

To enable connect proxy:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"connect_proxy_name": "connect"
}

We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea.

Let's also add refresh proxy:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"connect_proxy_name": "connect",
"refresh_proxy_name": "refresh"
}

Granular subscribe, publish, sub refresh

Subscribe, publish and sub refresh proxies work per-namespace. This means that subscribe_proxy_name, publish_proxy_name and sub_refresh_proxy_name are just channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object.

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"namespaces": [
{
"name": "ns1",
"subscribe_proxy_name": "subscribe1",
"publish": true,
"publish_proxy_name": "publish1"
},
{
"name": "ns2",
"subscribe_proxy_name": "subscribe2",
"publish": true,
"publish_proxy_name": "publish2"
}
]
}

If namespace does not have "subscribe_proxy_name" or "subscribe_proxy_name" is empty then no subscribe proxy will be used for a namespace.

If namespace does not have "publish_proxy_name" or "publish_proxy_name" is empty then no publish proxy will be used for a namespace.

If namespace does not have "sub_refresh_proxy_name" or "sub_refresh_proxy_name" is empty then no sub refresh proxy will be used for a namespace.

tip

You can define subscribe_proxy_name, publish_proxy_name, sub_refresh_proxy_name on configuration top level – and in this case publish, subscribe and sub refresh requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.

Granular RPC

Analogous to channel namespaces it's possible to configure rpc namespaces:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"namespaces": [...],
"rpc_namespaces": [
{
"name": "rpc_ns1",
"rpc_proxy_name": "rpc1",
},
{
"name": "rpc_ns2",
"rpc_proxy_name": "rpc2"
}
]
}

The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces).

Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start.

tip

The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.

- + \ No newline at end of file diff --git a/docs/4/server/server_api.html b/docs/4/server/server_api.html index c39ea8a61..214ff3873 100644 --- a/docs/4/server/server_api.html +++ b/docs/4/server/server_api.html @@ -16,13 +16,13 @@ - +

Server API walkthrough

Server API provides a way to send various commands to Centrifugo. For example, server API allows publishing messages to channels, get server statistics, etc. There are two kinds of API available at the moment:

  • HTTP API
  • GRPC API

HTTP API

Server HTTP API works on /api endpoint (by default). It has a simple request format: this is an HTTP POST request with application/json Content-Type and with JSON command body.

Here we will look at available command methods and parameters.

tip

In some cases, you can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests.

HTTP API authorization

HTTP API is protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like:

config.json
{
...
"api_key": "<YOUR API KEY>"
}

This API key must be set in the request Authorization header in this way:

Authorization: apikey <KEY>

It's also possible to pass API key over URL query param. This solves some edge cases where it's not possible to use the Authorization header. Simply add ?api_key=<YOUR API KEY> query param to the API endpoint. Keep in mind that passing the API key in the Authorization header is a recommended way.

It's possible to disable API key check on Centrifugo side using the api_insecure configuration option. Be sure to protect the API endpoint by firewall rules in this case – to prevent anyone on the internet to send commands over your unprotected Centrifugo API endpoint. API key auth is not very safe for man-in-the-middle so we also recommended running Centrifugo with TLS.

A command is a JSON object with two properties: method and params.

  • method is the name of the API command you want to call.
  • params is an object with command arguments. Each method can have its own params

Before looking at all available commands here is a CURL that calls info command:

curl --header "Authorization: apikey <API_KEY>" \
--request POST \
--data '{"method": "info", "params": {}}' \
http://localhost:8000/api

Here is a live example:

Now let's investigate each API method in detail.

publish

Publish command allows publishing data into a channel (we call this message publication in Centrifugo). Most probably this is a command you'll use most of the time.

It looks like this:

{
"method": "publish",
"params": {
"channel": "chat",
"data": {
"text": "hello"
}
}
}

Let's apply all information said above and send publish command to Centrifugo. We will send a request using the requests library for Python.

import json
import requests

command = {
"method": "publish",
"params": {
"channel": "docs",
"data": {
"content": "1"
}
}
}

api_key = "YOUR_API_KEY"
data = json.dumps(command)
headers = {'Content-type': 'application/json', 'Authorization': 'apikey ' + api_key}
resp = requests.post("https://centrifuge.example.com/api", data=data, headers=headers)
print(resp.json())

The same using httpie console tool:

echo '{"method": "publish", "params": {"channel": "chat", "data": {"text": "hello"}}}' | http "localhost:8000/api" Authorization:"apikey <YOUR_API_KEY>" -vvv
POST /api HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Authorization: apikey KEY
Connection: keep-alive
Content-Length: 80
Content-Type: application/json
Host: localhost:8000
User-Agent: HTTPie/0.9.8

{
"method": "publish",
"params": {
"channel": "chat",
"data": {
"text": "hello"
}
}
}

HTTP/1.1 200 OK
Content-Length: 3
Content-Type: application/json
Date: Thu, 17 May 2018 22:01:42 GMT

{
"result": {}
}

In case of error response object can contain error field. For example, let's publish to a channel with unknown namespace:

echo '{"method": "publish", "params": {"channel": "unknown:chat", "data": {"text": "hello"}}}' | http "localhost:8000/api" Authorization:"apikey <YOUR_API_KEY>"
HTTP/1.1 200 OK
Content-Length: 55
Content-Type: application/json
Date: Thu, 17 May 2018 22:03:09 GMT

{
"error": {
"code": 102,
"message": "namespace not found"
}
}

error object contains error code and message - this is also the same for other commands described below.

Publish params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to publish
dataany JSONyesCustom JSON data to publish into a channel
skip_historyboolnoSkip adding publication to history for this request
tagsmap[string]stringnoPublication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients
b64datastringnoCustom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field.

Publish result

Field nameField typeOptionalDescription
offsetintegeryesOffset of publication in history stream
epochstringyesEpoch of current stream

broadcast

Similar to publish but allows to send the same data into many channels.

{
"method": "broadcast",
"params": {
"channels": ["CHANNEL_1", "CHANNEL_2"],
"data": {
"text": "hello"
}
}
}

Broadcast params

Parameter nameParameter typeRequiredDescription
channelsArray of stringsyesList of channels to publish data to
dataany JSONyesCustom JSON data to publish into each channel
skip_historyboolnoSkip adding publications to channels' history for this request
tagsmap[string]stringnoPublication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients
b64datastringnoCustom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field.

Broadcast result

Field nameField typeOptionalDescription
responsesArray of publish responsesnoResponses for each individual publish (with possible error and publish result)

subscribe

subscribe allows subscribing user to a channel.

Subscribe params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to subscribe
channelstringyesName of channel to subscribe user to
infoany JSONnoAttach custom data to subscription (will be used in presence and join/leave messages)
b64infostringnoinfo in base64 for binary mode (will be decoded by Centrifugo)
clientstringnoSpecific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs)
sessionstringnoSpecific client session to subscribe (user still required to be set)
dataany JSONnoCustom subscription data (will be sent to client in Subscribe push)
b64datastringnoSame as data but in base64 format (will be decoded by Centrifugo)
recover_sinceStreamPosition objectnoStream position to recover from
overrideOverride objectnoAllows dynamically override some channel options defined in Centrifugo configuration (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
force_push_join_leaveBoolValueyesOverride force_push_join_leave
force_positioningBoolValueyesOverride force_positioning
force_recoveryBoolValueyesOverride force_recovery

BoolValue is an object like this:

{
"value": true/false
}

Subscribe result

Empty object at the moment.

unsubscribe

unsubscribe allows unsubscribing user from a channel.

{
"method": "unsubscribe",
"params": {
"channel": "CHANNEL NAME",
"user": "USER ID"
}
}

Unsubscribe params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to unsubscribe
channelstringyesName of channel to unsubscribe user to
clientstringnoSpecific client ID to unsubscribe (user still required to be set)
sessionstringnoSpecific client session to disconnect (user still required to be set).

Unsubscribe result

Empty object at the moment.

disconnect

disconnect allows disconnecting a user by ID.

{
"method": "disconnect",
"params": {
"user": "USER ID"
}
}

Disconnect params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to disconnect
clientstringnoSpecific client ID to disconnect (user still required to be set)
sessionstringnoSpecific client session to disconnect (user still required to be set).
whitelistArray of stringsnoArray of client IDs to keep
disconnectDisconnect objectnoProvide custom disconnect object, see below

Disconnect object

Field nameField typeRequiredDescription
codeintyesDisconnect code
reasonstringyesDisconnect reason

Disconnect result

Empty object at the moment.

refresh

refresh allows refreshing user connection (mostly useful when unidirectional transports are used).

Refresh params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to refresh
clientstringnoClient ID to refresh (user still required to be set)
sessionstringnoSpecific client session to refresh (user still required to be set).
expiredboolnoMark connection as expired and close with Disconnect Expired reason
expire_atintnoUnix time (in seconds) in the future when the connection will expire

Refresh result

Empty object at the moment.

presence

presence allows getting channel online presence information (all clients currently subscribed on this channel).

tip

Presence in channels is not enabled by default. See how to enable it over channel options. Also check out dedicated chapter about it.

{
"method": "presence",
"params": {
"channel": "chat"
}
}

Example:

fz@centrifugo: echo '{"method": "presence", "params": {"channel": "chat"}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 127
Content-Type: application/json
Date: Thu, 17 May 2018 22:13:17 GMT

{
"result": {
"presence": {
"c54313b2-0442-499a-a70c-051f8588020f": {
"client": "c54313b2-0442-499a-a70c-051f8588020f",
"user": "42"
},
"adad13b1-0442-499a-a70c-051f858802da": {
"client": "adad13b1-0442-499a-a70c-051f858802da",
"user": "42"
}
}
}
}

Presence params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to call presence from

Presence result

Field nameField typeOptionalDescription
presenceMap of client ID (string) to ClientInfo objectnoOffset of publication in history stream

ClientInfo

Field nameField typeOptionalDescription
clientstringnoClient ID
userstringnoUser ID
conn_infoJSONyesOptional connection info
chan_infoJSONyesOptional channel info

presence_stats

presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID).

{
"method": "presence_stats",
"params": {
"channel": "chat"
}
}

Example:

echo '{"method": "presence_stats", "params": {"channel": "public:chat"}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 43
Content-Type: application/json
Date: Thu, 17 May 2018 22:09:44 GMT

{
"result": {
"num_clients": 0,
"num_users": 0
}
}

Presence stats params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to call presence from

Presence stats result

Field nameField typeOptionalDescription
num_clientsintegernoTotal number of clients in channel
num_usersintegernoTotal number of unique users in channel

history

history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter.

tip

History in channels is not enabled by default. See how to enable it over channel options.

{
"method": "history",
"params": {
"channel": "chat",
"limit": 2
}
}

Example:

echo '{"method": "history", "params": {"channel": "chat", "limit": 2}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 129
Content-Type: application/json
Date: Wed, 21 Jul 2021 05:30:48 GMT

{
"result": {
"epoch": "qFhv",
"offset": 4,
"publications": [
{
"data": {
"text": "hello"
},
"offset": 2
},
{
"data": {
"text": "hello"
},
"offset": 3
}
]
}
}

History params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to call history from
limitintnoLimit number of returned publications, if not set in request then only current stream position information will present in result (without any publications)
sinceStreamPosition objectnoTo return publications after this position
reverseboolnoIterate in reversed order (from latest to earliest)

StreamPosition

Field nameField typeRequiredDescription
offsetintegeryesOffset in a stream
epochstringyesStream epoch

History result

Field nameField typeOptionalDescription
publicationsArray of publication objectsyesList of publications in channel
offsetintegeryesTop offset in history stream
epochstringyesEpoch of current stream

history_remove

history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state.

{
"method": "history_remove",
"params": {
"channel": "chat"
}
}

Example:

echo '{"method": "history_remove", "params": {"channel": "chat"}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 43
Content-Type: application/json
Date: Thu, 17 May 2018 22:09:44 GMT

{
"result": {}
}

History remove params

Parameter nameParameter typeRequiredDescription
channelstringyesName of channel to remove history

History remove result

Empty object at the moment.

channels

channels return active channels (with one or more active subscribers in it).

{
"method": "channels",
"params": {}
}

Channels params

Parameter nameParameter typeRequiredDescription
patternstringnoPattern to filter channels, we are using gobwas/glob library for matching

Channels result

Field nameField typeOptionalDescription
channelsMap of string to ChannelInfonoMap where key is channel and value is ChannelInfo (see below)

ChannelInfo

Field nameField typeOptionalDescription
num_clientsintegernoTotal number of connections currently subscribed to a channel
caution

Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels API call will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.

info

info method allows getting information about running Centrifugo nodes.

Example:

echo '{"method": "info", "params": {}}' | http "localhost:8000/api" Authorization:"apikey KEY"
HTTP/1.1 200 OK
Content-Length: 184
Content-Type: application/json
Date: Thu, 17 May 2018 22:07:58 GMT

{
"result": {
"nodes": [
{
"name": "Alexanders-MacBook-Pro.local_8000",
"num_channels": 0,
"num_clients": 0,
"num_users": 0,
"uid": "f844a2ed-5edf-4815-b83c-271974003db9",
"uptime": 0,
"version": ""
}
]
}
}

Info params

Empty object at the moment.

Info result

Field nameField typeOptionalDescription
nodesArray of Node objectsnoInformation about all nodes in a cluster

Command pipelining

It's possible to combine several commands into one request to Centrifugo. To do this use JSON streaming format. This can improve server throughput and reduce traffic traveling around.

Example:

curl --header "Authorization: apikey <API_KEY>" \
--request POST \
--data $'{"method": "publish", "params": {"channel": "test1", "data": {"test": 1}}}\n{"method": "publish", "params": {"channel": "test2", "data": {"test": 2}}}' \
http://localhost:8000/api
{"result":{}}
{"result":{}}

Note that with CURL we had to use $ to properly send new line \n character in data.

HTTP API libraries

Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header.

But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually:

Also, there are API libraries created by community:

tip

Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.

GRPC API

Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC.

GRPC API is also useful if you want to publish binary data to Centrifugo channels.

tip

GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description.

You can enable GRPC API in Centrifugo using grpc_api option:

config.json
{
...
"grpc_api": true
}

By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option.

Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file.

Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.

GRPC example for Python

For example for Python you need to run sth like this according to GRPC docs:

pip install grpcio-tools
python -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto

As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo:

import grpc
import api_pb2_grpc as api_grpc
import api_pb2 as api_pb

channel = grpc.insecure_channel('localhost:10000')
stub = api_grpc.CentrifugoApiStub(channel)

try:
resp = stub.Info(api_pb.InfoRequest())
except grpc.RpcError as err:
# GRPC level error.
print(err.code(), err.details())
else:
if resp.error.code:
# Centrifugo server level error.
print(resp.error.code, resp.error.message)
else:
print(resp.result)

Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.

GRPC example for Go

Here is a simple example of how to run Centrifugo with the GRPC Go client.

You need protoc, protoc-gen-go and protoc-gen-go-grpc installed.

First start Centrifugo itself with GRPC API enabled:

CENTRIFUGO_GRPC_API=1 centrifugo --config config.json

In another terminal tab:

mkdir centrifugo_grpc_example
cd centrifugo_grpc_example/
touch main.go
go mod init centrifugo_example
mkdir apiproto
cd apiproto
wget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto

Run protoc to generate code:

protoc -I ./ api.proto --go_out=. --go-grpc_out=.

Put the following code to main.go file (created on the last step above):

package main

import (
"context"
"log"
"time"

"centrifugo_example/apiproto"

"google.golang.org/grpc"
)

func main() {
conn, err := grpc.Dial("localhost:10000", grpc.WithInsecure())
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
client := apiproto.NewCentrifugoApiClient(conn)
for {
resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{
Channel: "chat:index",
Data: []byte(`{"input": "hello from GRPC"}`),
})
if err != nil {
log.Printf("Transport level error: %v", err)
} else {
if resp.GetError() != nil {
respError := resp.GetError()
log.Printf("Error %d (%s)", respError.Code, respError.Message)
} else {
log.Println("Successfully published")
}
}
time.Sleep(time.Second)
}
}

Then run:

go run main.go

The program starts and periodically publishes the same payload into chat:index channel.

GRPC API key authorization

You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey <KEY>. For example in Go language:

package main

import (
"context"
"log"
"time"

"centrifugo_example/apiproto"

"google.golang.org/grpc"
)

type keyAuth struct {
key string
}

func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": "apikey " + t.key,
}, nil
}

func (t keyAuth) RequireTransportSecurity() bool {
return false
}

func main() {
conn, err := grpc.Dial("localhost:10000", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{"xxx"}))
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
client := apiproto.NewCentrifugoClient(conn)
for {
resp, err := client.Publish(context.Background(), &PublishRequest{
Channel: "chat:index",
Data: []byte(`{"input": "hello from GRPC"}`),
})
if err != nil {
log.Printf("Transport level error: %v", err)
} else {
if resp.GetError() != nil {
respError := resp.GetError()
log.Printf("Error %d (%s)", respError.Code, respError.Message)
} else {
log.Println("Successfully published")
}
}
time.Sleep(time.Second)
}
}

For other languages refer to GRPC docs.

- + \ No newline at end of file diff --git a/docs/4/server/server_subs.html b/docs/4/server/server_subs.html index 16680c3a9..06051af90 100644 --- a/docs/4/server/server_subs.html +++ b/docs/4/server/server_subs.html @@ -16,13 +16,13 @@ - +

Server-side subscriptions

Centrifugo clients can initiate a subscription to a channel by calling the subscribe method of client API. In most cases, client-side subscriptions is a more flexible and recommended approach since a frontend usually knows which channels it needs to consume at a concrete moment.

But in some situations, all you need is to subscribe your connections to several channels on a server-side at the moment of connection establishment. So client effectively starts receiving publications from those channels without calling the subscribe API at all.

You can set a list of channels for a connection in two ways at the moment:

  • over connection JWT using channels claim, which is an array of strings
  • over connect proxy returning channels field in result (also an array of strings)
  • dynamically over server subscribe API

On the client-side, you need to listen for publications from server-side channels using a top-level client event handler. For example with centrifuge-js:

const centrifuge = new Centrifuge(address);

centrifuge.on('publication', function(ctx) {
const channel = ctx.channel;
const payload = JSON.stringify(ctx.data);
console.log('Publication from server-side channel', channel, payload);
});

centrifuge.connect();

I.e. listen for publications without any usage of subscription objects. You can look at channel publication relates to using field in callback context as shown in the example.

tip

The same best practices of working with channels and client-side subscriptions equally applicable to server-side subscription.

Dynamic server-side subscriptions

See subscribe and unsubscribe server API

Automatic personal channel subscription

It's possible to automatically subscribe a user to a personal server-side channel.

To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel.

For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above.

As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default "") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.

Maintain single user connection

Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes.

user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect).

This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel.

Example config:

config.json
{
"user_subscribe_to_personal": true,
"user_personal_single_connection": true,
"user_personal_channel_namespace": "personal",
"namespaces": [
{
"name": "personal",
"presence": true
}
]
}
note

Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.

- + \ No newline at end of file diff --git a/docs/4/server/tls.html b/docs/4/server/tls.html index b8acfa869..d8583851a 100644 --- a/docs/4/server/tls.html +++ b/docs/4/server/tls.html @@ -16,7 +16,7 @@ - + @@ -33,7 +33,7 @@ browsers such as Chrome 49 on Windows XP and IE8 on XP:

  • tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys.
  • tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment

grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints.

uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.

TLS for GRPC API

You can provide custom certificate files to configure TLS for GRPC API server.

  • grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file
  • grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server
  • grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server

TLS for GRPC unidirectional stream

You can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint.

  • uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file
  • uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server
  • uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server
- + \ No newline at end of file diff --git a/docs/4/transports/client_api.html b/docs/4/transports/client_api.html index b6914ebad..9e792e54b 100644 --- a/docs/4/transports/client_api.html +++ b/docs/4/transports/client_api.html @@ -16,13 +16,13 @@ - +

Client SDK API

Centrifugo has several client SDKs to establish a real-time connection with a server. Centrifugo SDKs use WebSocket as the main data transport and send/receive messages encoded according to our bidirectional protocol. That protocol is built on top of the Protobuf schema (both JSON and binary Protobuf formats are supported). It provides asynchronous communication, sending RPC, multiplexing subscriptions to channels, etc. Client SDK wraps the protocol and exposes a set of APIs to developers.

tip

For Centrifugo v4 we introduced a new generation of SDKs for Javascript, Dart, Go, Swift, and Java – all based on updated client protocol and new client API iteration.

This chapter describes the core concepts of client SDKs API. If you want to find out lower-level client protocol framing details then look at client protocol document.

Most examples here are written using our Javascript client (centrifuge-js), but all other Centrifugo connectors now have very similar semantics and APIs very close to each other.

Client connection states

Client connection has 4 states:

  • disconnected
  • connecting
  • connected
  • closed
note

closed state is only implemented by SDKs where it makes sense (need to clean up allocated resources when app gracefully shuts down – for example in Java SDK we close thread executors used internally).

When a new Client is created it has a disconnected state. To connect to a server connect() method must be called. After calling connect Client moves to the connecting state. If a Client can't connect to a server it attempts to create a connection with an exponential backoff algorithm (with full jitter). If a connection to a server is successful then the state becomes connected.

If a connection is lost (due to a missing network for example, or due to reconnect advice received from a server, or due to some client-side error that can't be recovered without reconnecting) Client goes to the connecting state again. In this state Client tries to reconnect (again, with an exponential backoff algorithm).

The Client's state can become disconnected. This happens when Client's disconnect() method was called by a developer. Also, this can happen due to server advice from a server, or due to a terminal problem that happened on the client-side.

Here is a program where we create a Client instance, set callbacks to listen to state updates and establish a connection with a server:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

client.on('connecting', function(ctx) {
console.log('connecting', ctx);
});

client.on('connected', function(ctx) {
console.log('connected', ctx);
});

client.on('disconnected', function(ctx) {
console.log('disconnected', ctx);
});

client.connect();

In case of successful connection Client states will transition like this:

disconnected (initial) -> connecting (on('connecting') called) -> connected (on('connected') called).

In case of already connected Client temporary lost a connection with a server and then successfully reconnected:

connected -> connecting (on('connecting') called) -> connected (on('connected') called).

In case of already connected Client temporary lost a connection with a server, but got a terminal error upon reconnection:

connected -> connecting (on('connecting') called) -> disconnected (on('disconnected') called).

In case of already connected Client came across terminal condition (for example, if during a connection token refresh application found that user has no permission to connect anymore):

connected -> disconnected (on('disconnected') called).

Both connecting and disconnected events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why the Client went to the connecting state or to the disconnected state.

This diagram demonstrates possible Client state transitions:

Centrifugo scheme

You can also listen for all errors happening internally (which are not reflected by state changes, for example, transport errors happening on initial connect, transport during reconnect, connection token refresh related errors, etc) while the client works by using error event:

client.on('error', function(ctx) {
console.log('client error', ctx);
});

If you want to disconnect from a server call .disconnect() method:

client.disconnect();

In this case on('disconnected') will be called. You can call connect() again when you need to establish a connection.

closed state implemented in SDKs where resources like internal queues, thread executors, etc must be cleaned up when the Client is not needed anymore. All subscriptions should automatically go to the unsubscribed state upon closing. The client is not usable after going to a closed state.

Client common options

There are several common options available when creating Client instance.

  • option to set connection token and callback to get connection token upon expiration (see below mode details)
  • option to set connect data
  • option to configure operation timeout
  • tweaks for reconnect backoff algorithm (min delay, max delay)
  • configure max delay of server pings (to detect broken connection)
  • configure headers to send in WebSocket upgrade request (except centrifuge-js)
  • configure client name and version for analytics purpose

Client methods

  • connect() – connect to a server
  • disconnect() - disconnect from a server
  • close() - close Client if not needed anymore
  • send(data) - send asynchronous message to a server
  • rpc(method, data) - send arbitrary RPC and wait for response

Client connection token

All SDKs support connecting to Centrifugo with JWT. Initial connection token can be set in Client option upon initialization. Example:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {
token: 'JWT-GENERATED-ON-BACKEND-SIDE'
});

If the token sets connection expiration then the client SDK will keep the token refreshed. It does this by calling a special callback function. This callback must return a new token. If a new token with updated connection expiration is returned from callback then it's sent to Centrifugo. If your callback returns an empty string – this means the user has no permission to connect to Centrifugo and the Client will move to a disconnected state. In case of error returned by your callback SDK will retry the operation after some jittered time.

An example:

function getToken(url, ctx) {
return new Promise((resolve, reject) => {
fetch(url, {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify(ctx)
})
.then(res => {
if (!res.ok) {
throw new Error(`Unexpected status code ${res.status}`);
}
return res.json();
})
.then(data => {
resolve(data.token);
})
.catch(err => {
reject(err);
});
});
}

const client = new Centrifuge(
'ws://localhost:8000/connection/websocket',
{
token: 'JWT-GENERATED-ON-BACKEND-SIDE',
getToken: function (ctx) {
return getToken('/centrifuge/connection_token', ctx);
}
}
);
tip

If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authentication. In this case SDK should attempt to get a connection token before establishing an initial connection.

Connection PING/PONG

PINGs sent by a server, a client should answer with PONGs upon receiving PING. If a client does not receive PING from a server for a long time (ping interval + configured delay) – the connection is considered broken and will be re-established.

Subscription states

Client allows subscribing on channels. This can be done by creating Subscription object.

const sub = centrifuge.newSubscription(channel);
sub.subscribe();

When anewSubscription method is called Client allocates a new Subscription instance and saves it in the internal subscription registry. Having a registry of allocated subscriptions allows SDK to manage resubscribes upon reconnecting to a server. Centrifugo connectors do not allow creating two subscriptions to the same channel – in this case, newSubscription can throw an exception.

Subscription has 3 states:

  • unsubscribed
  • subscribing
  • subscribed

When a new Subscription is created it has an unsubscribed state.

To initiate the actual process of subscribing to a channel subscribe() method of Subscription instance should be called. After calling subscribe() Subscription moves to subscribing state.

If subscription to a channel is not successful then depending on error type subscription can automatically resubscribe (with exponential backoff) or go to an unsubscribed state (upon non-temporary error). If subscription to a channel is successful then the state becomes subscribed.

const sub = client.newSubscription(channel);

sub.on('subscribing', function(ctx) {
console.log('subscribing');
});

sub.on('subscribed', function(ctx) {
console.log('subscribed');
});

sub.on('unsubscribed', function(ctx) {
console.log('unsubscribed');
});

sub.subscribe();

Subscriptions also go to subscribing state when Client connection (i.e. transport) becomes unavailable. Upon connection re-establishement all subscriptions which are not in unsubscribed state will resubscribe automatically.

In case of successful subscription states will transition like this:

unsubscribed (initial) -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called).

In case of connected and subscribed Client temporary lost a connection with a server and then succesfully reconnected and resubscribed:

subscribed -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called).

Both subscribing and unsubscribed events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why Subscription went to subscribing state or to unsubscribed state.

This diagram demonstrates possible Subscription state transitions:

Centrifugo scheme

You can listen for all errors happening internally in Subscription (which are not reflected by state changes, for example, temporary subscribe errors, subscription token related errors, etc) by using error event:

sub.on('error', function(ctx) {
console.log("subscription error", ctx);
});

If you want to unsubscribe from a channel call .unsubscribe() method:

sub.unsubscribe();

In this case on('unsubscribed') will be called. Subscription still kept in Client's registry, but no resubscription attempts will be made. You can call subscribe() again when you need Subscription again. Or you can remove Subscription from Client's registry (see below).

Subscription management

The client SDK provides several methods to manage the internal registry of client-side subscriptions.

newSubscription(channel, options) allocates a new Subscription in the registry or throws an exception if the Subscription is already there. We will discuss common Subscription options below.

getSubscription(channel) returns the existing Subscription by a channel from the registry (or null if it does not exist).

removeSubscription(sub) removes Subscription from Client's registry. Subscription is automatically unsubscribed before being removed. Use this to free resources if you don't need a Subscription to a channel anymore.

subscriptions() returns all registered subscriptions, so you can iterate over all and do some action if required (for example, you want to unsubscribe/remove all subscriptions).

Listen to channel publications

Of course the main point of having Subscriptions is the ability to listen for publications (i.e. messages published to a channel).

sub.on('publication', function(ctx) {
console.log("received publication", ctx);
});

Publication context has several fields:

  • data - publication payload, this can be JSON or binary data
  • offset - optional offset inside history stream, this is an incremental number
  • tags - optional tags, this is a map with string keys and string values
  • info - optional information about client connection who published this (only exists if publication comes from client-side publish() API).

So minimal code where we connect to a server and listen for messages published into example channel may look like:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

const sub = client.newSubscription('example').on('publication', function(ctx) {
console.log("received publication from a channel", ctx.data);
});

sub.subscribe();

client.connect();

Note, that we can call subscribe() before making a connection to a server – and this will work just fine, subscription goes to subscribing state and will be subscribed upon succesfull connection. And of course, it's possible to call .subscribe() after .connect().

Subscription recovery state

Subscriptions to channels with recovery option enabled maintain stream position information internally. On every publication received this information updated and used to recover missed publications upon resubscribe (caused by reconnect for example).

When you call unsubscribe() Subscription position state is not cleared. So it's possible to call subscribe() later and catch up a state.

The recovery process result – i.e. whether all missed publications recovered or not – can be found in on('subscribed') event context. Centrifuge protocol provides two fields:

  • wasRecovering - boolean flag that tells whether recovery was used during subscription process resulted into subscribed state. Can be useful if you want to distinguish first subscribe attempt (when subscription does not have any position information yet)
  • recovered - boolean flag that tells whether Centrifugo thinks that all missed publications can be successfully recovered and there is no need to load state from the main application database. It's always false when wasRecovering is false.

Subscription common options

There are several common options available when creating Subscription instance.

  • option to set subscription token and callback to get subscription token upon expiration (see below more details)
  • option to set subscription data (attached to every subscribe/resubscribe request)
  • options to tweak resubscribe backoff algorithm
  • option to start Subscription since known Stream Position (i.e. attempt recovery on first subscribe)
  • option to ask server to make subscription positioned (if not forced by a server)
  • option to ask server to make subscription recoverable (if not forced by a server)
  • option to ask server to push Join/Leave messages (if not forced by a server)

Subscription methods

  • subscribe() – start subscribing to a channel
  • unsubscribe() - unsubscribe from a channel
  • publish(data) - publish data to Subscription channel
  • history(options) - request Subscription channel history
  • presence() - request Subscription channel online presence information
  • presenceStats() - request Subscription channel online presence stats information (number of client connections and unique users in a channel).

Subscription token

All SDKs support subscribing to Centrifugo channels with JWT. Channel subscription token can be set as a Subscription option upon initialization. Example:

const sub = centrifuge.newSubscription(channel, {
token: 'JWT-GENERATED-ON-BACKEND-SIDE'
});
sub.subscribe();

If token sets subscription expiration client SDK will keep token refreshed. It does this by calling special callback function. This callback must return a new token. If new token with updated subscription expiration returned from a calbback then it's sent to Centrifugo. If your callback returns an empty string – this means user has no permission to subscribe to a channel anymore and subscription will be unsubscribed. In case of error returned by your callback SDK will retry operation after some jittered time.

An example:

function getToken(url, ctx) {
return new Promise((resolve, reject) => {
fetch(url, {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify(ctx)
})
.then(res => {
if (!res.ok) {
throw new Error(`Unexpected status code ${res.status}`);
}
return res.json();
})
.then(data => {
resolve(data.token);
})
.catch(err => {
reject(err);
});
});
}

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

const sub = centrifuge.newSubscription(channel, {
token: 'JWT-GENERATED-ON-BACKEND-SIDE',
getToken: function (ctx) {
// ctx has channel in the Subscription token case.
return getToken('/centrifuge/subscription_token', ctx);
},
});
sub.subscribe();
tip

If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authorization for a channel subscription. In this case SDK should attempt to get a subscription token before initial subscribe.

Server-side subscriptions

We encourage using client-side subscriptions where possible as they provide a better control and isolation from connection. But in some cases you may want to use server-side subscriptions (i.e. subscriptions created by server upon connection establishment).

Technically, client SDK keeps server-side subscriptions in internal registry (similar to client-side subscriptions but without possibility to control them).

To listen for server-side subscription events use callbacks as shown in example below:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

client.on('subscribed', function(ctx) {
// Called when subscribed to a server-side channel upon Client moving to
// connected state or during connection lifetime if server sends Subscribe
// push message.
console.log('subscribed to server-side channel', ctx.channel);
});

client.on('subscribing', function(ctx) {
// Called when existing connection lost (Client reconnects) or Client
// explicitly disconnected. Client continue keeping server-side subscription
// registry with stream position information where applicable.
console.log('subscribing to server-side channel', ctx.channel);
});

client.on('unsubscribed', function(ctx) {
// Called when server sent unsubscribe push or server-side subscription
// previously existed in SDK registry disappeared upon Client reconnect.
console.log('unsubscribed from server-side channel', ctx.channel);
});

client.on('publication', function(ctx) {
// Called when server sends Publication over server-side subscription.
console.log('publication receive from server-side channel', ctx.channel, ctx.data);
});

client.connect();

Server-side subscription events mostly mimic events of client-side subscriptions. But again – they do not provide control to the client and managed entirely by a server side.

Additionally, Client has several top-level methods to call with server-side subscription related operations:

  • publish(channel, data)
  • history(channel, options)
  • presence(channel)
  • presenceStats(channel)

Error codes

Server can return error codes in range 100-1999. Error codes in interval 0-399 reserved by Centrifuge/Centrifugo server. Codes in range [400, 1999] may be returned by application code built on top of Centrifuge/Centrifugo.

Server errors contain a temporary boolean flag which works as a signal that error may be fixed by a later retry.

Errors with codes 0-100 can be used by client-side implementation. Client-side errors may not have code attached at all since in many languages error can be distinguished by its type.

Unsubscribe codes

Server may return unsubscribe codes. Server unsubscribe codes must be in range [2000, 2999].

Unsubscribe codes >= 2500 coming from server to client result into automatic resubscribe attempt (i.e. client goes to subscribing state). Codes < 2500 result into going to unsubscribed state.

Client implementation can use codes <2000 for client-side specific unsubscribe reasons.

Disconnect codes

Server may send custom disconnect codes to a client. Custom disconnect codes must be in range [3000, 4999].

Client automatically reconnects upon receiving code in range 3000-3499, 4000-4499 (i.e. Client goes to connecting state). Other codes result into going to disconnected state.

Client implementation can use codes <3000 for client-side specific disconnect reasons.

RPC

An SDK provides a way to send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the real-time connection. RPC is only available when RPC proxy configured (so Centrifugo proxies the RPC to your application backend).

const rpcRequest = {'key': 'value'};
const data = await centrifuge.namedRPC('example_method', rpcRequest);

Channel history API

SDK provides a method to call publication history inside a channel (only for channels where history is enabled) to get last publications in a channel.

Get stream current top position:

const resp = await subscription.history();
console.log(resp.offset);
console.log(resp.epoch);

Get up to 10 publications from history since known stream position:

const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});
console.log(resp.publications);

Get up to 10 publications from history since current stream beginning:

const resp = await subscription.history({limit: 10});
console.log(resp.publications);

Get up to 10 publications from history since current stream end in reversed order (last to first):

const resp = await subscription.history({limit: 10, reverse: true});
console.log(resp.publications);

Presence and presence stats API

Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured):

For presence (full information about active subscribers in channel):

const resp = await subscription.presence();
// resp contains presence information - a map client IDs as keys
// and client information as values.

For presence stats (just a number of clients and unique users in a channel):

const resp = await subscription.presenceStats();
// resp contains a number of clients and a number of unique users.

SDK common best practices

  • Callbacks must be fast. Avoid blocking operations inside event handlers. Callbacks caused by protocol messages received from a server are called synchronously and connection read loop is blocked while such callbacks are being executed. Consider doing heavy work asynchronously.
  • Do not blindly rely on the current Client or Subscription state when making client API calls – state can change at any moment, so don't forget to handle errors.
  • Disconnect from a server when a mobile application goes to the background since a mobile OS can kill the connection at some point without any callbacks called.
- + \ No newline at end of file diff --git a/docs/4/transports/client_protocol.html b/docs/4/transports/client_protocol.html index 3ba3cdac5..595e8fa3f 100644 --- a/docs/4/transports/client_protocol.html +++ b/docs/4/transports/client_protocol.html @@ -16,13 +16,13 @@ - +

Client protocol

This chapter describes the core concepts of Centrifugo bidirectional client protocol – concentrating on framing level. If you want to find out details about exposed client API then look at client API document.

We need our own protocol on top of real-time transport due to several reasons:

  • We want request-response semantics (while our main transport – WebSocket – does not provide this out of the box)
  • Multiplex many subscriptions over a single physical connection
  • Efficient ping-pong support
  • Handle server disconnect advices
tip

In case of questions on how client protocol works/structured you can always look at existing client SDKs.

Protobuf schema

Centrifugo is built on top of Centrifuge library for Go. Centrifuge library uses its own framing for wrapping Centrifuge-specific messages – synchronous commands from a client to a server (which expect replies from a server) and asynchronous pushes.

Centrifuge client protocol is defined by a Protobuf schema. This is the source of truth.

tip

At the moment Protobuf schema contains some fields which are only used in client protocol v1. This is for backwards compatibility – server supports clients connecting over both client protocol v2 and client protocol v1. Client protocol v1 is considered deprecated and will be removed at some point in the future (giving enough time to our users to migrate).

Command-Reply

In bidirectional case client sends Command to a server and server sends Reply to a client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages.

Each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous transport where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before on SDK level.

In JSON case client can send command like this:

{"id": 1, "subscribe": {"channel": "example"}}

And client can expect something like this in response:

{"id": 1, "subscribe": {}}

Reply for different commands has corresponding field with command result ("subscribe" in example above).

Reply can also contain error if Command processing resulted into an error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can handle result object appropriately.

error looks like this in JSON case:

{
"code": 100,
"message": "internal server error",
"temporary": true
}

I.e. reply with error may look like this:

{"id": 1, "error": {"code": 100, "message": "internal server error"}}

We will talk more about error handling below.

Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow.

Current commands:

  • connect – sent to authenticate connection, sth like hello from a client which can carry authentication token and arbitrary data.
  • subscribe – sent to subscribe to a channel
  • unsubscribe - sent to unsubscribe from a channel
  • publish - sent to publish data into a channel
  • presence - sent to request presence information from a channel
  • presence_stats - sent to request presence stats information from a channel
  • history - sent to request history information for a channel
  • send - sent to send async message to a server (this command is a bit special since it must not contain id - as we don't wait for any response from a server in this case).
  • rpc - sent to send RPC to a channel (execute arbitrary logic and wait for response)
  • refresh - sent to refresh connection token
  • sub_refresh - sent to refresh channel subscription token

Asynchronous pushes

The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to a client at any moment - not as reaction to issued Command but as a message from a server to a client at arbitrary time. For example, this can be a message published into channel.

There are several types of asynchronous messages that can come from a server to a client.

  • pub is a message published into channel
  • join messages sent when someone joined (subscribed on) channel.
  • leave messages sent when someone left (unsubscribed from) channel.
  • unsubscribe message sent when a server unsubscribed current client from a channel:
  • subscribe may be sent when a server subscribes client to a channel.
  • disconnect may be sent be a server before closing connection and contains disconnect code/reason
  • message may be sent when server sends asynchronous message to a client
  • connect push can be sent in unidirectional transport case
  • refresh may be sent when a server refreshes client credentials (useful in unidirectional transports)

Top level batching

To reduce number of system calls one request from a client to a server and one response from a server to a client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data.

When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \n:

{"id": 1, "subscribe": {"channel": "ch1"}}
{"id": 2, "subscribe": {"channel": "ch2"}}

Here is an example how we do this in Javascript client when JSON format used:

function encodeCommands(commands) {
const encodedCommands = [];
for (const i in commands) {
if (commands.hasOwnProperty(i)) {
encodedCommands.push(JSON.stringify(commands[i]));
}
}
return encodedCommands.join('\n');
}
info

This doc uses JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo.

When Protobuf format used then many Command can be sent from a client to a server in a length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example.

The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there.

tip

Server can even send reply to a command and asynchronous message batched together in a one frame.

For example here is how we read server response and extracting individual replies in Javascript client when JSON format used:

function decodeReplies(data) {
const replies = [];
const encodedReplies = data.split('\n');
for (const i in encodedReplies) {
if (encodedReplies.hasOwnProperty(i)) {
if (!encodedReplies[i]) {
continue;
}
const reply = JSON.parse(encodedReplies[i]);
replies.push(reply);
}
}
return replies;
}

For Protobuf case see existing client implementations for decoding example.

Ping Pong

To maintain connection alive and detect broken connections server periodically sends empty commands to clients and expects empty replies from them.

When client does not receive ping from a server for some time it can consider connection broken and try to reconnect. Usually a server sends pings every 25 seconds.

Handle disconnects

Client should handle disconnect advices from server. In websocket case disconnect advice is sent in CLOSE Websocket frame. Disconnect advice contains uint32 code and human-readable string reason.

Handle errors

This section contains advices to error handling in client implementations.

Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems.

Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again.

Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side.

The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance.

Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).

Additional notes

Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in a reply to a subscribe command.

- + \ No newline at end of file diff --git a/docs/4/transports/client_sdk.html b/docs/4/transports/client_sdk.html index 075a754d6..04b499e2e 100644 --- a/docs/4/transports/client_sdk.html +++ b/docs/4/transports/client_sdk.html @@ -16,13 +16,13 @@ - +

Client real-time SDKs

In the previous chapter we investigated common principles of Centrifugo client SDK API. Here we will provide a list of available bidirectional connectors you can use to communicate with Centrifugo.

No need in clients for unidirectional approach

Client libraries listed here speak Centrifugo bidirectional protocol (WebSocket). If you aim to use unidirectional approach you don't need client connectors – just use standard APIs. See the difference here.

List of client SDKs

See a description of client protocol if you want to write a custom bidirectional connector or eager to learn how Centrifugo protocol internals are structured.

Protobuf and JSON formats in SDKs

Centrifugo real-time SDKs work using two possible serialization formats: JSON and Protobuf. The entire bidirectional client protocol is described by the Protobuf schema. But those Protobuf messages may be also encoded as JSON objects (in JSON representation bytes fields in the Protobuf schema is replaced by the embedded JSON object in Centrifugo case).

Our Javascript SDK - centrifuge-js - uses JSON serialization for protocol frames by default. This makes communication with Centrifugo server convenient as we are exchanging human-readable JSON frames between client and server. And it makes it possible to use centrifuge-js without extra dependency to protobuf.js library. It's possible to switch to Protobuf protocol with centrifuge-js SDK though, in case you want more compact Centrifuge protocol representation, faster decode/encode speeds on Centrifugo server side, or payloads you need to pass are custom binary. See more details on how to use centrifuge-js with Protobuf serialization in README.

centrifuge-go real-time SDK for Go language also supports both JSON and Protobuf formats when communicating with Centrifugo server.

Other SDKs, like centrifuge-dart, centrifuge-swift, centrifuge-java work using only Protobuf serialization for Centrifuge protocol internally. So they utilize the fastest and the most compact wire representation by default. Note, that while internally in those SDKs the serialization format is Protobuf, you can still send JSON towards these clients as JSON objects may be encoded as UTF-8 bytes. So these SDKs may work with both custom binary and JSON payloads.

There are some important notes about JSON and Protobuf interoperability mentioned in our FAQ.

SDK feature matrix

Below you can find an information regarding support of different features in our official client SDKs

Client featurejsdartswiftgojava
connect to a server
setting client options
automatic reconnect with backoff algorithm
client state changes
command-reply
command timeouts
async pushes
ping-pong
connection token refresh
handle disconnect advice from server
server-side subscriptions
batching API
bidirectional WebSocket emulation
Client featurejsdartswiftgojava
subscrbe to a channel
setting subscription options
automatic resubscribe with backoff algorithm
subscription state changes
subscription command-reply
subscription async pushes
subscription token refresh
handle unsubscribe advice from server
manage subscription registry
optimistic subscriptions
- + \ No newline at end of file diff --git a/docs/4/transports/http_stream.html b/docs/4/transports/http_stream.html index dbc97437d..e9eea5669 100644 --- a/docs/4/transports/http_stream.html +++ b/docs/4/transports/http_stream.html @@ -16,13 +16,13 @@ - +

HTTP streaming, with bidirectional emulation

HTTP streaming connection endpoint in Centrifugo is:

/connection/http_stream
info

This transport is only implemented by our Javascript SDK at this point – as it mostly makes sense as a fallback for WebSocket to have real-time connection in an environment where WebSocket is unavailable. These days those envs are mostly corporate networks which can block WebSocket traffic (even TLS-based).

Here is an example how to use JavaScript SDK with WebSocket as the main transport and HTTP-streaming transport fallback:

Use HTTP-streaming with bidirectional emulation as a fallback for WebSocket in JS SDK
const transports = [
{
transport: 'websocket',
endpoint: 'ws://localhost:8000/connection/websocket'
},
{
transport: 'http_stream',
endpoint: 'http://localhost:8000/connection/http_stream'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()
danger

Make sure allowed_origins are properly configured.

Options

http_stream

Boolean, default: false.

Enables HTTP streaming endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients.

config.json
{
...
"http_stream": true
}

http_stream_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes.

- + \ No newline at end of file diff --git a/docs/4/transports/overview.html b/docs/4/transports/overview.html index 97cbbfb84..05e12eb8c 100644 --- a/docs/4/transports/overview.html +++ b/docs/4/transports/overview.html @@ -16,13 +16,13 @@ - +

Real-time transports

Centrifugo supports a variety of transports to deliver real-time messages to clients.

Every transport is a persistent connection

Here we describe supported transports between your application frontend and Centrifugo itself. Every Centrifugo transport is a persistent connection so the server can push data towards clients at any moment.

The important distinction here is that all supported transports belong to one of two possible groups:

  • Bidirectional
  • Unidirectional

Bidirectional

Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus.

Bidirectional transports come with a cost that developers need to use a special client connector library (SDK) which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state, handle request queueing/timeouts/errors, etc.

Centrifugo has several official client SDKs for popular environments. All of them work over WebSocket transport. Our Javascript SDK also offers bidirectional fallbacks over HTTP-Streaming, Server-Sent Events (SSE) or SockJS.

Unidirectional

Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time.

The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource/SSE, HTTP-streaming), or GRPC generated code to receive real-time updates from Centrifugo. Thus avoiding dependency to a client connector that abstracts bidirectional communication.

The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo).

Unidirectional message types

In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as:

message Push {
string channel = 2;

Publication pub = 4;
Join join = 5;
Leave leave = 6;
Unsubscribe unsubscribe = 7;
Message message = 8;
Subscribe subscribe = 9;
Connect connect = 10;
Disconnect disconnect = 11;
Refresh refresh = 12;
}
tip

Some numbers in Protobuf definitions skipped for backwards compatibility with previous client protocol version.

So unidirectional connection will receive various pushes. Every push contains one of the following objects:

  • Publication
  • Join
  • Leave
  • Unsubscribe
  • Message
  • Subscribe
  • Connect
  • Disconnect
  • Refresh

Some pushes belong to a channel which may be set on Push top level.

All you need to do is look at Push, process messages you are interested in and ignore others. In most cases you will be most interested in pushes which contain Connect or Publication messages.

For example, according to protocol schema Publication message type looks like this:

message Publication {
bytes data = 4;
ClientInfo info = 5;
uint64 offset = 6;
map<string, string> tags = 7;
}
tip

In JSON protocol case Centrifugo replaces bytes type with embedded JSON.

Just try using any unidirectional transport and you will quickly get the idea.

PING/PONG behavior

Centrifugo server periodically sends pings to clients and expects pong from clients that works over bidirectional transports. Sending ping and receiving pong allows to find broken connections faster. Centrifugo sends pings on the Centrifugo client protocol level, thus it's possible for clients to handle ping messages on the client side to make sure connection is not broken (our bidirectional SDKs do this automatically).

By default Centrifugo sends pings every 25 seconds. This may be changed using ping_interval option (duration, default "25s").

Centrifugo expects pong message from bidirectional client SDK after sending ping to it. By default, it waits no more than 8 seconds before closing a connection. This may be changed using pong_timeout option (duration, default "8s").

In most cases default ping/pong intervals are fine so you don't really need to tweak them. Reducing timeouts may help you to find non-gracefully closed connections faster, but will increase network traffic and CPU resource usage since ping/pongs are sent faster.

caution

ping_interval must be greater than pong_timeout in the current implementation.

Here is a scheme how ping/pong works in bidirectional and unidirectional client scenarios:

- + \ No newline at end of file diff --git a/docs/4/transports/sockjs.html b/docs/4/transports/sockjs.html index 579da85e9..c00f97e4e 100644 --- a/docs/4/transports/sockjs.html +++ b/docs/4/transports/sockjs.html @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@

SockJS

SockJS is a polyfill browser library which provides HTTP-based fallback transports in case when it's not possible to establish Websocket connection. This can happen in old client browsers or because of some proxy behind client and server that cuts of Websocket traffic. You can find more information on SockJS project Github page.

caution

SockJS transport is DEPRECATED. It may be removed in the next major releases. Consider using our own WebSocket emulation layer based on HTTP-streaming and EventSource instead. If you know a use case where SockJS is still prefferred – reach us out to discuss this use case.

info

This transport is only implemented by our Javascript SDK at this point – as it mostly makes sense as a fallback for WebSocket to have real-time connection in an environment where WebSocket is unavailable. These days those envs are mostly corporate networks which can block WebSocket traffic (even TLS-based).

If you have a requirement to work everywhere SockJS is the solution. SockJS will automatically choose best fallback transport if Websocket connection failed for some reason. Some of the fallback transports are:

  • EventSource (SSE)
  • XHR-streaming
  • Long-polling
  • And more (see SockJS docs)

SockJS connection endpoint in Centrifugo is:

/connection/sockjs

SockJS caveats

caution

There are several important caveats to know when using SockJS – see below.

Sticky sessions

First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend.

For example, with Nginx sticky support can be enabled with ip_hash directive for upstream:

upstream centrifugo {
ip_hash;
server 127.0.0.1:8000;
server 127.0.0.2:8000;
}

With this configuration Nginx will proxy connections with the same ip address to the same upstream backend.

But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair.

So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.

Browser only

SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.

JSON only

One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.

Options

sockjs

Boolean, default: false.

Enables SockJS transport.

sockjs_url

Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js

Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.

- + \ No newline at end of file diff --git a/docs/4/transports/sse.html b/docs/4/transports/sse.html index 3083cb746..277951cbc 100644 --- a/docs/4/transports/sse.html +++ b/docs/4/transports/sse.html @@ -16,13 +16,13 @@ - +

SSE (EventSource), with bidirectional emulation

SSE (EventSource) connection endpoint in Centrifugo is:

/connection/sse
info

This transport is only implemented by our Javascript SDK at this point – as it mostly makes sense as a fallback for WebSocket to have real-time connection in an environment where WebSocket is unavailable. These days those envs are mostly corporate networks which can block WebSocket traffic (even TLS-based).

Here is an example how to use JavaScript SDK with WebSocket as the main transport and SSE transport fallback:

Use SSE with bidirectional emulation as a fallback for WebSocket in JS SDK
const transports = [
{
transport: 'websocket',
endpoint: 'ws://localhost:8000/connection/websocket'
},
{
transport: 'sse',
endpoint: 'http://localhost:8000/connection/sse'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()
danger

Make sure allowed_origins are properly configured.

Options

sse

Boolean, default: false.

Enables SSE (EventSource) endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients.

config.json
{
...
"sse": true
}

sse_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).

- + \ No newline at end of file diff --git a/docs/4/transports/uni_grpc.html b/docs/4/transports/uni_grpc.html index 322c7b6b7..c1232e927 100644 --- a/docs/4/transports/uni_grpc.html +++ b/docs/4/transports/uni_grpc.html @@ -16,13 +16,13 @@ - +

Unidirectional GRPC

It's possible to connect to GRPC unidirectional stream to consume real-time messages from Centrifugo. In this case you need to generate GRPC code for your language on client-side.

Protobuf definitions can be found here.

GRPC server will start on port 11000 (default).

Supported data formats

JSON and binary.

Options

uni_grpc

Boolean, default: false.

Enables unidirectional GRPC endpoint.

config.json
{
...
"uni_grpc": true
}

uni_grpc_port

String, default "11000".

Port to listen on.

uni_grpc_address

String, default "" (listen on all interfaces)

Address to bind uni GRPC to.

uni_grpc_max_receive_message_size

Default: 65536 (64KB)

Maximum allowed size of a first connect message received from GRPC connection in bytes.

uni_grpc_tls

Boolean, default: false

Enable custom TLS for unidirectional GRPC server.

uni_grpc_tls_cert

String, default: "".

Path to cert file.

uni_grpc_tls_key

String, default: "".

Path to key file.

Example

A basic example will come soon as we update docs for v4. In general, algorithm is like this:

  1. Copy Protobuf definitions
  2. Generate GRPC client code
  3. Use generated code to connect to Centrifugo
  4. Process Push messages, drop unknown Push types, handle those necessary for the application.
- + \ No newline at end of file diff --git a/docs/4/transports/uni_http_stream.html b/docs/4/transports/uni_http_stream.html index f46011627..8f57aa1f5 100644 --- a/docs/4/transports/uni_http_stream.html +++ b/docs/4/transports/uni_http_stream.html @@ -16,13 +16,13 @@ - +

Unidirectional HTTP streaming

Default unidirectional HTTP streaming connection endpoint in Centrifugo is:

/connection/uni_http_stream

This is basically a long-lived HTTP connection. You can consume it from a browser using fetch API.

Streaming endpoint accepts HTTP POST requests and sends JSON messages to a connection. These JSON messages can have different meaning according to Centrifuge protocol Protobuf definitions. But in most cases you will be interested in Publication push types.

Connect command

It's possible to pass initial connect command by posting a JSON body to a streaming endpoint.

Refer to the full Connect command description – it's the same as for unidirectional WebSocket.

Supported data formats

JSON

Pings

Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).

Options

uni_http_stream

Boolean, default: false.

Enables unidirectional HTTP streaming endpoint.

config.json
{
...
"uni_http_stream": true
}

uni_http_stream_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes.

Connecting using CURL

Let's look how simple it is to connect to Centrifugo using HTTP streaming.

We will start from scratch, generate new configuration file:

centrifugo genconfig

Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect:

config.json
{
...
"uni_http_stream": true,
"user_subscribe_to_personal": true
}

Run Centrifugo:

centrifugo -c config.json

In separate terminal window create token for a user:

❯ go run main.go gentoken -u user12
HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM

Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request:

curl -X POST http://localhost:8000/connection/uni_http_stream \
-d '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM"}'

Open one more terminal window and publish message to a personal user channel:

curl -X POST http://localhost:8000/api \
-d '{"method": "publish", "params": {"channel": "#user12", "data": {"input": "hello"}}}' \
-H "Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc"

You should see this messages coming from server.

{} messages are pings from a server.

That's all, happy streaming!

Browser example

A basic browser will come soon as we update docs for v4.

- + \ No newline at end of file diff --git a/docs/4/transports/uni_sse.html b/docs/4/transports/uni_sse.html index b1e40e7eb..27766bca5 100644 --- a/docs/4/transports/uni_sse.html +++ b/docs/4/transports/uni_sse.html @@ -16,13 +16,13 @@ - +

Unidirectional SSE (EventSource)

Default unidirectional SSE (EventSource) connection endpoint in Centrifugo is:

/connection/uni_sse
info

Only parts of EventSource spec that make sense for Centrifugo are implemented. For example Last-Event-Id header not supported (since one connection can be subscribed to many channels) and multiline strings (since we are passing JSON-encoded objects to the client) etc.

Connect command

Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example:

const url = new URL('http://localhost:8000/connection/uni_sse');
url.searchParams.append("cf_connect", JSON.stringify({
'token': '<JWT>'
}));

const eventSource = new EventSource(url);

Refer to the full Connect command description – it's the same as for unidirectional WebSocket.

The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases.

tip

Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.

Supported data formats

JSON

Options

uni_sse

Boolean, default: false.

Enables unidirectional SSE (EventSource) endpoint.

config.json
{
...
"uni_sse": true
}

uni_sse_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).

Browser example

A basic browser will come soon as we update docs for v4.

- + \ No newline at end of file diff --git a/docs/4/transports/uni_websocket.html b/docs/4/transports/uni_websocket.html index b8aa4884b..a7290aec5 100644 --- a/docs/4/transports/uni_websocket.html +++ b/docs/4/transports/uni_websocket.html @@ -16,13 +16,13 @@ - +

Unidirectional WebSocket

Default unidirectional WebSocket connection endpoint in Centrifugo is:

/connection/uni_websocket

While WebSocket is bidirectional transport in its nature Centrifugo provides its unidirectional version too to give developers more choice in transports when using unidirectional approach.

Connect command

It's possible to send connect command as first WebSocket message (as JSON).

Field nameField typeRequiredDescription
tokenstringnoConnection JWT, not required when using the connect proxy feature.
dataany JSONnoCustom JSON connection data
namestringnoApplication name
versionstringnoApplication version
subsmap of channel to SubscribeRequestnoPass an information about desired subscriptions to a server

SubscribeRequest

Field nameField typeRequiredDescription
recoverbooleannoWhether a client wants to recover from a certain position
offsetintegernoKnown stream position offset when recover is used
epochstringnoKnown stream position epoch when recover is used

Supported data formats

JSON

Pings

Centrifugo uses empty commands ({} in JSON case) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).

Options

uni_websocket

Boolean, default: false.

Enables unidirectional WebSocket endpoint.

config.json
{
...
"uni_websocket": true
}

uni_websocket_message_size_limit

Default: 65536 (64KB)

Maximum allowed size of a first connect message received from WebSocket connection in bytes.

Example

Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal.

First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example:

config.json
{
"token_hmac_secret_key": "secret",
"uni_websocket":true,
"user_subscribe_to_personal": true
}

Run Centrifugo:

./centrifugo -c config.json

In another terminal:

❯ ./centrifugo gentoken -c config.json -u test_user
HMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw

Install wscat and run:

wscat -c "ws://localhost:8000/connection/uni_websocket"

This will establish a connection with a server and you then can send connect command to a server:

❯ wscat -c "ws://localhost:8000/connection/uni_websocket"

Connected (press CTRL+C to quit)
> {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2NTY1MDMwNDV9.3UYL-UCUBp27TybeBK7Z0OenwdsKwCMRe46fuEjJnzI", "subs": {"abc": {}}}
< {"connect":{"client":"bfd28799-b958-4791-b9e9-b011eaef68c1","version":"0.0.0","subs":{"#test_user":{}},"expires":true,"ttl":604407,"ping":25,"session":"57b1287b-44ec-45c8-93fc-696c5294af25"}}

The connection will receive server pings (empty commands {}) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.

- + \ No newline at end of file diff --git a/docs/4/transports/websocket.html b/docs/4/transports/websocket.html index 9ab5940ed..9a32c6851 100644 --- a/docs/4/transports/websocket.html +++ b/docs/4/transports/websocket.html @@ -16,13 +16,13 @@ - +

WebSocket

Websocket is the main transport in Centrifugo. It's a very efficient low-overhead protocol on top of TCP.

The biggest advantage is that Websocket works out of the box in all modern browsers and almost all programming languages have Websocket implementations. This makes Websocket a universal transport that can be used to connect to Centrifugo from almost everywhere.

Default WebSocket connection endpoint in Centrifugo is:

/connection/websocket

So to connect:

Connect to local Centrifugo with JavaScript SDK
const client = new Centrifuge('ws://localhost:8000/connection/websocket', {
// token: ?,
// getToken: ?
});

client.connect();

Options

websocket_message_size_limit

Default: 65536 (64KB)

Maximum allowed size of a message received from WebSocket connection in bytes.

websocket_read_buffer_size

In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size).

config.json
{
...
"websocket_read_buffer_size": 512
}

websocket_write_buffer_size

In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused):

config.json
{
...
"websocket_write_buffer_size": 512
}

websocket_use_write_buffer_pool

If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.

websocket_compression

An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire.

We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally.

caution

Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable.

To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature.

Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client.

It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.

Protobuf binary protocol

In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if:

  • you want less traffic on wire as Protobuf is very compact
  • you want maximum performance on server-side as Protobuf encoding/decoding is very efficient
  • you can sacrifice human-readable JSON for your application

Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like:

wss://centrifugo.example.com/connection/websocket?format=protobuf

After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes.

tip

You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.

- + \ No newline at end of file diff --git a/docs/4/transports/webtransport.html b/docs/4/transports/webtransport.html index 9deead7f1..cee429eb5 100644 --- a/docs/4/transports/webtransport.html +++ b/docs/4/transports/webtransport.html @@ -16,13 +16,13 @@ - +

WebTransport

WebTransport is an API offering low-latency, bidirectional, client-server messaging on top of HTTP/3. See Using WebTransport article that gives a good overview of it.

danger

WebTransport support in Centrifugo is EXPERIMENTAL and not recommended for production usage. WebTransport IETF specification is not finished yet and may have breaking changes.

To use WebTransport you first need to run HTTP/3 experimental server and enable webtransport endpoint:

config.json
{
"http3": true,
"tls": true,
"tls_cert": "path/to/crt",
"tls_key": "path/to/key",
"webtransport": true
}

In HTTP/3 and WebTransport case TLS is required.

tip

At the time of writing only Chrome (since v97) supports WebTransport API. If you are experimenting with self-signed certificates you may need to run Chrome with flags to force HTTP/3 on origin and ignore certificate errors:

/path/to/your/Chrome --origin-to-force-quic-on=localhost:8000 --ignore-certificate-errors-spki-list=TSZTiMjLG+DNjESXdJh3f+S8C+RhsFCav7T24VNuCPQ=

Where the value of --ignore-certificate-errors-spki-list is a certificate fingerprint obtained this way:

openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

With not self-signed certs things should work just fine in Chrome.

Here is a video tutorial that shows this in action:

After starting Centrifugo with HTTP/3 and WebTransport endpoint you can connect to that endpoint (by default – /connection/webtransport) using centrifuge-js. For example, let's enable WebTransport and will use WebSocket as a fallback option:

const transports = [
{
transport: 'webtransport',
endpoint: 'https://localhost:8000/connection/webtransport'
},
{
transport: 'websocket',
endpoint: 'wss://localhost:8000/connection/websocket'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()

Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in WebTransport case non-TLS http:// scheme is simply not supported by the specification.

tip

Make sure you run Centrifugo without load balancer or reverse proxy in front, or make sure your proxy can proxy HTTP/3 traffic to Centrifugo.

In Centrifugo case, we utilize a single bidirectional stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional.

Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification it's an experimental feature. And it's not recommended for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket.

- + \ No newline at end of file diff --git a/docs/attributions.html b/docs/attributions.html index cece18e89..9d567fbbe 100644 --- a/docs/attributions.html +++ b/docs/attributions.html @@ -16,13 +16,13 @@ - +
- + \ No newline at end of file diff --git a/docs/faq.html b/docs/faq.html index 3216f3362..6987ab648 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -16,13 +16,13 @@ - +

Frequently Asked Questions

Answers to popular questions here.

How many connections can one Centrifugo instance handle?

This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here.

Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case.

You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.

Memory usage per connection?

Depending on transport used and features enabled the amount of RAM required per each connection can vary.

For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections.

For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.

Can Centrifugo scale horizontally?

Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker.

See engines and scalability considerations.

Message delivery model

See design overview

Message order guarantees

See design overview.

Should I create channels explicitly?

No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel.

When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).

What about best practices with the number of channels?

Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application.

But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc.

One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.

Any way to exclude message publisher from receiving a message from a channel?

Currently, no.

We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this:

  • Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID)
  • Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history

Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates.

Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point.

Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel.

In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.

Can I have both binary and JSON clients in one channel?

No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere.

Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end).

To summarize:

  • if you are using binary Protobuf clients and binary payloads everywhere – you are fine.
  • if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine.
  • if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.

Online presence for chat apps - online status of your contacts

While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel.

You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.

Centrifugo stops accepting new connections, why?

The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.

Can I use Centrifugo without reverse-proxy like Nginx before it?

Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.

Does Centrifugo work with HTTP/2?

Yes, Centrifugo works with HTTP/2. This is provided by built-in Go http server implementation.

You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable:

GODEBUG="http2server=0" centrifugo -c config.json

Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.

Does Centrifugo work with HTTP/3?

Centrifugo v4 added an experimental HTTP/3 support. As soon as you enabled TLS and provided "http3": true option all endpoints on external port will be served by HTTP/3 server based on github.com/quic-go/quic-go implementation. This (among other benefits which HTTP/3 can provide) is a step towards a proper WebTransport support. For now we support WebTransport experimentally.

It's worth noting that WebSocket transport does not work over HTTP/3, it still starts with HTTP/1.1 Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and Eventsource should work just fine with HTTP/3.

HTTP/3 does not work with ACME autocert TLS at the moment - i.e. you need to explicitly provide paths to cert and key files as described here.

Is there a way to use a single connection to Centrifugo from different browser tabs?

If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.

What if I need to send push notifications to mobile or web applications?

Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via FCM, or browsers over Web Push API.

We are preparing our own push notifications API as part of Centrifugo PRO version. This is under construction though.

The reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.

How can I know a message is delivered to a client?

You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.

Can I publish new messages over a WebSocket connection from a client?

It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend.

We suggest using one of the available approaches:

  • When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API.
  • Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network.
  • Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself).

Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.

How to create a secure channel for two users only (private chat case)?

There are several ways to achieve it:

  • use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter
  • next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers
  • you can use subscribe proxy feature to validate subscriptions, see chapter about proxy
  • finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations

What's the best way to organize channel configuration?

In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled.

For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients.

The same relates to other channel options.

Does Centrifugo support webhooks?

Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there.

Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.

Why Centrifugo does not have disconnect hooks?

UPDATE

Centrifugo PRO now solves the pitfalls mentioned here with its Channel State Events feature.

Centrifugo does not support disconnect hooks at this point. We understand that this may be useful for some use cases but there are some pitfalls which prevent us adding such hooks to Centrifugo.

Let's consider a case when Centrifugo node is unexpectedly killed. In this case there is no chance for Centrifugo to emit disconnect events for connections on that node. While this may be rare thing in practice – it may lead to inconsistent state in you app if you'd rely on disconnect hooks.

Another reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. Even if disconnect events were queued, rate-limited, or suppressed for quickly reconnected clients there could be situations when your app processes disconnect hook after user already reconnected. This is a racy situation which also can lead to the inconsistency if not properly addressed.

Is there a workaround though? If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend while client connection is active and update status somewhere on the backend (possibly using Redis for this). Then periodically do clealup logic for connections/users not updated for a configured interval. This is a robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo connect proxy + refresh proxy for getting notified about initial connection and get periodic refresh requests while connection is alive.

The trade-off of the described workaround scenario is that you will notice disconnection only with some delay – this may be a acceptable in many cases though.

Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together.

All the pitfalls and workarounds here may be also applied to unsubscribe event hooks.

Is it possible to listen to join/leave events on the app backend side?

No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.

How scalable is the online presence and join/leave features?

Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel).

There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers.

You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs.

The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.

How to send initial data to channel subscriber?

Sometimes you need to send some initial state towards channel subscriber. Centrifugo provides a way to attach any data to a successful subscribe reply when using subscribe proxy feature. See data and b64data fields. This data will be part of subscribed event context. And of course, you can always simply send request to get initial data from the application backend before or after subscribing to a channel without Centrifugo connection involved (i.e. using sth like general AJAX/HTTP call or passing data to the template when rendering an application page).

Does Centrifugo support multitenancy?

If you want to use Centrifugo with different projects the recommended approach is to have different Centrifugo installations for each project. Multitenancy is better to solve on infrastructure level in case of Centrifugo.

It's possible to share one Redis setup though by setting unique redis_prefix. But we recommend having completely isolated setups.

I have not found an answer to my question here

Ask in our community rooms:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

- + \ No newline at end of file diff --git a/docs/flow_diagrams.html b/docs/flow_diagrams.html index f78eedc97..b9e76f319 100644 --- a/docs/flow_diagrams.html +++ b/docs/flow_diagrams.html @@ -16,13 +16,13 @@ - +

flow_diagrams

For swimlanes.io:

Client <- App Backend: JWT

note:
The backend generates JWT for a user and passes it to the client side.

Client -> Centrifugo: Client connects to Centrifugo with JWT

...: {fas-spinner} Persistent connection established

Client -> Centrifugo: Client issues channel subscribe requests

Centrifugo -->> Client: Client receives real-time updates from channels
Client -> Centrifugo: Connect request

note:
Client connects to Centrifugo without JWT.

Centrifugo -> App backend: Sends request further (via HTTP or GRPC)

note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.

App backend -> Centrifugo: Connect reply

Centrifugo -> Client: Connect Reply

...: {fas-spinner} Persistent connection established
Client -> App Backend: Publish request

note:
Client sends data to publish to the application backend.

Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.

App Backend -> Centrifugo: Publish over Centrifugo API

Centrifugo -->> Client: {far-bolt fa-lg} Real-time notification

note: Centrifugo delivers real-time message to active channel subscribers.
Client -> App Backend: Publish request

note:
Client sends data to publish to the application backend.

Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.

App Backend -> Centrifugo: Publish over Centrifugo API

Centrifugo -->> Client: {far-bolt fa-lg} Real-time notification

note: Centrifugo delivers real-time message to active channel subscribers.
- + \ No newline at end of file diff --git a/docs/getting-started/client_api.html b/docs/getting-started/client_api.html index 57a5f78f6..d43143248 100644 --- a/docs/getting-started/client_api.html +++ b/docs/getting-started/client_api.html @@ -16,13 +16,13 @@ - +

Client API showcase

This chapter showcases the capabilities of Centrifugo's bidirectional client API – i.e., the real-time messaging primitives available on the front end (which can be a browser or a mobile device).

tip

It is also possible to avoid using the client library at all with unidirectional transports (../transports/overview.md).

This is a formal description – we use the JavaScript client centrifuge-js for examples here. Refer to each specific client implementation for concrete method names and possibilities. See the full list of Centrifugo client SDKs. This description does not cover all protocol possibilities – just the most important ones to start with.

If you are looking for detailed information about the client-server protocol internals, then the client protocol description chapter can help.

Connecting to a server

Each Centrifugo client allows connecting to a server.

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.connect();

In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to:

const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.setToken('<USER-JWT>')
centrifuge.connect();

See authentication chapter for more information on how to generate connection JWT.

If you are using connect proxy then you may go without setting JWT.

Disconnecting from a server

After connecting you can disconnect from a server at any moment.

centrifuge.disconnect();

Reconnecting to a server

Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.

Connection lifecycle events

All client implementations allow setting handlers on connect and disconnect events.

For example:

centrifuge.on('connect', function(connectCtx){
console.log('connected', connectCtx)
});

centrifuge.on('disconnect', function(disconnectCtx){
console.log('disconnected', disconnectCtx)
});

Subscribe to a channel

Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel.

centrifuge.subscribe('channel', function(messageCtx) {
console.log(messageCtx);
})

Clients can subscribe to different channels. The subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events.

In the idiomatic case, messages are published to channels from the application backend over the Centrifugo server API. However, this is not always the case.

Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel is properly configured. The client keeps the last seen Publication offset and restores missed publications since the known offset upon reconnecting. If recovery fails, then the client implementation provides a flag inside the subscribe event to let the application know that some publications were missed – so you may need to load the state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery can be found in a dedicated chapter.

Server-side subscriptions

To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance:

centrifuge.on('publish', function(messageCtx) {
console.log(messageCtx);
});

It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.

Send RPC

A client can send an RPC to the server. RPC is a call that is not related to channels at all. It's just a way to call a server method from the client side over the WebSocket or SockJS connection. RPC is only available when an RPC proxy is configured.

const rpcRequest = {'key': 'value'};
const data = await centrifuge.namedRPC('example_method', rpcRequest);

Call channel history

Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel:

Get stream current top position:

const resp = await subscription.history();
console.log(resp.offset);
console.log(resp.epoch);

Get up to 10 publications from history since known stream position:

const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});
console.log(resp.publications);

Get up to 10 publications from history since current stream beginning:

const resp = await subscription.history({limit: 10});
console.log(resp.publications);

Get up to 10 publications from history since current stream end in reversed order (last to first):

const resp = await subscription.history({limit: 10, reverse: true});
console.log(resp.publications);

Presence and presence stats

Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured):

For presence (full information about active subscribers in channel):

const resp = await subscription.presence();
// resp contains presence information - a map client IDs as keys
// and client information as values.

For presence stats (just a number of clients and unique users in a channel):

const resp = await subscription.presenceStats();
// resp contains a number of clients and a number of unique users.
- + \ No newline at end of file diff --git a/docs/getting-started/community.html b/docs/getting-started/community.html index c1dba362c..a5dff46e6 100644 --- a/docs/getting-started/community.html +++ b/docs/getting-started/community.html @@ -16,13 +16,13 @@ - +

Join community

If you find Centrifugo interesting, you are welcome to join our community rooms on Telegram (the most active) and Discord:

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ  Join the chat at https://discord.gg/tYgADKx

We strive to create a respectful and curious community. In these rooms, you may find answers to questions not fully covered by the documentation, share your thoughts and ideas on real-time messaging topics and Centrifugo in particular.

We also have a Twitter account and a YouTube channel.

See you there!

- + \ No newline at end of file diff --git a/docs/getting-started/comparisons.html b/docs/getting-started/comparisons.html index a1a2b9101..8179016f5 100644 --- a/docs/getting-started/comparisons.html +++ b/docs/getting-started/comparisons.html @@ -16,13 +16,13 @@ - +

Comparing with others

Let's compare Centrifugo with various systems. These comparisons arose from popular questions raised in our communities. Here we are emphasizing things that make Centrifugo special.

Centrifugo vs Socket.io

Socket.io is a library; you need to write your own server on top of it. Centrifugo is a ready-to-use standalone server.

Because of this, Socket.io may give you more flexibility, but you are mostly limited to writing code in JavaScript on the backend to get the most out of it.

Since Centrifugo is a standalone server, it's a universal, language-agnostic element that integrates well with a backend written in any language. But because it's universal, it comes with integration rules, its own mechanics, and limitations of such a design.

Centrifugo vs Pusher, Ably, Pubnub

The main difference is that Centrifugo is a self-hosted solution, while the mentioned technologies are all cloud SaaS platforms.

So, when using Centrifugo, you need to configure and run it on your own; you need to be skilled in engineering. The benefit is obvious – it's much cheaper once the integration is done. And all the data stays within your organization.

With cloud services, all the hard work of setting up an infrastructure for a WebSocket server and its maintenance is done for you. But it's more expensive, and the data flows through an external network.

Centrifugo vs Redis

A popular question from newcomers – does Centrifugo provide the same as Redis PUB/SUB? The answer is that Centrifugo and Redis can't be directly compared at all. Centrifugo uses Redis internally for PUB/SUB scalability, keeping channel message history, and online presence.

You can build a system similar to Centrifugo on top of Redis – but you would need to write a lot of code, i.e., replicate everything that Centrifugo provides out of the box – like real-time client SDKs, client protocol, re-implement all the transport endpoints, write efficient Redis integration, etc.

Centrifugo vs Kafka

At first glance, Centrifugo provides concepts similar to Apache Kafka - it has channels which seem similar to Kafka's topics, channel history with time and size retention policies. So sometimes people ask whether Centrifugo may be used as a lightweight Kafka replacement.

But Centrifugo and Kafka were designed for different purposes.

Centrifugo is a real-time messaging system with push semantics. It provides a lightweight PUB/SUB with ephemeral in-memory channels, designed to be exposed to frontend real-time apps. It has limited guarantees about message persistence in channel history, though it provides client hooks to be notified about message loss and recover the state from the main application database.

Kafka is a persistent disk-based message bus that you can't easily expose to frontend users. It won't work well with millions of topics where users connect and disconnect constantly – this would cause constant repartitioning and eventually require much more resources. Kafka fits well for service-to-service communication where topics may be pre-created and under control; Centrifugo fits well for frontend-to-backend real-time communication.

Centrifugo vs Nats

This is also a popular comparison request. Especially since both Centrifugo and Nats are written in the Go language, and Nats also supports connections from the application client side over the WebSocket protocol.

Nats is a very powerful messaging system, and it also has a built-in Jetstream system to provide at least once delivery guarantees. It has a larger community, more SDKs for various languages, incredible performance.

How Centrifugo is special:

  • Centrifugo actually uses Nats as one of the options for a PUB/SUB broker, i.e., as a PUB/SUB scalability backend.
  • Centrifugo was originally designed to be exposed to application frontend clients, providing various convenient authentication and channel authorization mechanisms for client-side integration.
  • It supports more transports, including WebSocket fallbacks.
  • It provides many unique features that are out of scope for Nats – like online presence, individual GRPC subscription streams, or some of Centrifugo PRO features like push notifications support, real-time analytics with ClickHouse, etc.
  • Topics in Jetstream still should be pre-created, while Centrifugo has ephemeral channels – created on the fly, even when using a channel history cache.
  • Centrifugo provides client connection event proxy features - it's possible to delegate authentication, channel authorization to the application backend, and provides channel state events (when a channel is occupied or vacated) in the PRO version.

Nats is great, and we are constantly looking for tighter integration with Nats. But both systems have unique sets of features and may be better or worse for various tasks.

- + \ No newline at end of file diff --git a/docs/getting-started/design.html b/docs/getting-started/design.html index f19df3638..3c2934502 100644 --- a/docs/getting-started/design.html +++ b/docs/getting-started/design.html @@ -16,13 +16,13 @@ - +

Design overview

Let's discuss some architectural and design topics about Centrifugo.

Idiomatic usage

Centrifugo is a standalone server that abstracts away the complexity of working with many persistent connections and efficiently broadcasting messages from the application backend. The fact that Centrifugo acts as a separate service dictates some idiomatic patterns for how to integrate with Centrifugo for real-time message delivery.

Usually, you want to deliver content created by a user in your app to other users in real time. Each user may have several real-time connections with Centrifugo. For example, a user opened several browser tabs, with each tab creating a separate connection. Or a user has two mobile devices and created a separate connection to your app from each of them. We call a connection a client in Centrifugo. Thus, the words connection and client are synonyms for us.

All requests from users that generate new data should first go to the application backend – i.e., calling the app backend API from the client side. The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event to a channel using Centrifugo server API. This event is then efficiently broadcast by Centrifugo to all active channel subscribers.

The following diagram shows the process (assuming the client that generates new content is also a channel subscriber and thus also receives the real-time message):

diagram_unidirectional_publish

This is usually a natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is fully decoupled from the application in this case.

Centrifugo serves the role of a real-time transport layer in this case, and you may design the app with graceful degradation in mind – so that removing Centrifugo won't be a fatal problem for the application – it will continue working, just without the real-time features.

If the original source of events is your app backend (without any user involvement) – then the above diagram simplifies to:

diagram_unidirectional_publish

So the backend publishes data to channels and if there are active subscribers – events are delivered. If there are no active subscribers then events are dropped by Centrifugo (or, in case of using history features in channels, events may be temporarily kept in the Centrifugo history stream).

It's also possible to utilize Centrifugo's bidirectional connection for sending requests to the backend. To achieve this, Centrifugo provides event proxy features. It's possible to send RPC (with a custom request-response) requests from the client to Centrifugo and the request will then be proxied to the application backend (see RPC proxy). Moreover, the proxy provides a way to utilize the bidirectional connection for publishing into channels (using publish proxy). But again – in most real scenarios, your backend must validate the publication attempt, so the scheme will look like this:

client generates content

Message history considerations

Idiomatic Centrifugo usage requires having the main application database from which the initial and actual state can be loaded at any point in time.

While Centrifugo has channel history, it has been mostly designed to be a hot cache to reduce the load on the main application database when all users reconnect at once (in case of a load balancer configuration reload, Centrifugo restart, temporary network problems, etc.). This allows for a radical reduction in the load on the application's main database during a reconnect storm. Since such disconnects are usually pretty short in time, having a reasonably small number of messages cached in history is sufficient.

The addition of the history iteration API shifts possible use cases a bit. Manually calling history chunk by chunk allows for keeping a larger number of publications per channel.

Depending on the Engine used and the configuration of the underlying storage, history stream persistence characteristics can vary. For example, with the Memory Engine, history will be lost upon Centrifugo restart. With Redis or Tarantool engines, history will survive Centrifugo restarts, but depending on the storage configuration, it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis AOF with fsync for maximum durability, or configure replication for high availability, use Redis Cluster, or maybe synchronous replication with Tarantool.

When using history with automatic recovery, Centrifugo provides clients with a flag to distinguish whether the missed messages were all successfully restored from Centrifugo history upon recovery or not. If not – the client may restore the state from the main application database. Centrifugo message history can be used as a complementary way to restore messages and thus reduce the load on the main application database most of the time.

Message delivery model

By default, the message delivery model of Centrifugo is 'at most once'. With history and the positioning/recovery features enabled, it's possible to achieve 'at least once' guarantee within the history retention time and size. After an abnormal disconnect, clients have an option to recover missed messages from the publication channel stream history that Centrifugo maintains.

Without the positioning or recovery features enabled, a message sent to Centrifugo could theoretically be lost while moving towards clients. Centrifugo makes its best effort only to prevent message loss on the way to online clients, but the application should tolerate the loss.

As noted, Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. It also compensates for the 'at most once' delivery of a broker PUB/SUB system (Redis, Tarantool) by using additional publication offset checks and periodic offset synchronization. So publication loss missed in the PUB/SUB layer will be detected eventually, and the client may catch up on the state by loading it from history.

Message order guarantees

Message order in channels is guaranteed to be the same when you publish messages into a channel one after another or publish them in one request. If you do parallel publications into the same channel, then Centrifugo can't guarantee message order since those are processed in parallel.

Graceful degradation

It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely solely on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in the AJAX call response and render it. This way, the user that posts a comment will think that everything works just fine. Be careful not to draw comments twice in this case because you may also receive the same data from a channel - think about idempotent identifiers for your entities.

Online presence considerations

Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to network failures and the unexpected shutdown of a Centrifugo node, there are chances that clients can be present in a presence for up to one minute more (until the presence entry expires).

Also, channel presence does not scale well for channels with a lot of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows, the response size becomes larger. In some cases, the presence_stats API call can be sufficient to avoid receiving the entire presence state.

Scalability considerations

Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with the Nats broker. See engines.

All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications.

But if you approach broker resource limits (CPU or memory), then it's possible:

  • Use Centrifugo's consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool).
  • Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters).
  • Nats broker should scale well itself in a cluster setup.

All brokers can be set up in a highly available way so there won't be a single point of failure.

All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact, and the fact that Centrifugo provides hooks for the application to understand history loss, makes the process of resharding mostly automatic. As soon as you need to add an additional broker shard (when using client-side sharding), you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently, part of the data will stay on the same broker nodes. Applications should handle cases where channel data moved to another shard and restore the state from the main application database when needed (i.e., when the recovered flag provided by the SDK is false).

- + \ No newline at end of file diff --git a/docs/getting-started/ecosystem.html b/docs/getting-started/ecosystem.html index 02f003040..ea6c9425a 100644 --- a/docs/getting-started/ecosystem.html +++ b/docs/getting-started/ecosystem.html @@ -16,13 +16,13 @@ - +

Ecosystem notes

Some additional notes about our ecosystem which may help you develop with our tech.

Centrifuge library for Go

Centrifugo is built on top of the Centrifuge library for the Go language.

Due to its standalone language-agnostic nature, Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a tighter integration of a real-time server with application business logic, you may consider using the Centrifuge library to build something similar to Centrifugo but with customized behavior. The Centrifuge library can be considered as an analogue of Socket.IO in the Go language ecosystem.

The library's README has a detailed description, a link to examples, and an introduction post.

Many Centrifugo features should be re-implemented when using Centrifuge - like the API layer, admin web UI, proxy, etc. (if you need those of course). And you need to write in the Go language. But the core functionality like a client-server protocol (all Centrifugo client SDKs work with a Centrifuge library-based server) and Redis engine to scale come out of the box – in most cases, this is enough to start building an app.

tip

Many things said in the Centrifugo doc can be considered as extra documentation for the Centrifuge library (for example, parts about infrastructure tuning or transport description). But not all of them.

Framework integrations

There are some community-driven projects that provide integration with frameworks for more native experience.

tip

In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. Integrating directly may allow using all Centrifugo features without limitations which can be introduced by third-party wrapper.

- + \ No newline at end of file diff --git a/docs/getting-started/highlights.html b/docs/getting-started/highlights.html index 209c819cd..6c29af3c2 100644 --- a/docs/getting-started/highlights.html +++ b/docs/getting-started/highlights.html @@ -16,13 +16,13 @@ - +

Main highlights

At this point, you know how to build the simplest real-time app with Centrifugo. Beyond the core PUB/SUB functionality, Centrifugo provides more features and primitives to build scalable real-time applications. Let's summarize the main Centrifugo ✨highlights✨ here. Every point is then extended throughout the documentation.

Simple integration

Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.).

It works as a standalone service with well-defined communication contracts. It nicely fits both monolithic and microservice architectures. Application developers should not change the backend philosophy and technology stack at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.

Great performance

Centrifugo is fast. It's written in the Go language, built on top of fast and battle-tested open-source libraries, has some smart internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTTs with the broker, connection hub sharding to avoid lock contention, JSON and Protobuf encoding speedups through code generation, and others.

See the Million WebSocket with Centrifugo post on our blog to see some real-world numbers.

Built-in scalability

Centrifugo scales well to many machines with the help of PUB/SUB brokers. So as soon as you have more client connections in the application – you can spread them over different Centrifugo nodes which will be connected together into a cluster.

The main PUB/SUB engine that Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck either.

There are other options to scale: KeyDB, Nats, Tarantool. See docs about available engines.

Strict client protocol

Centrifugo supports JSON and binary Protobuf protocols for client-server communication. The bidirectional protocol is defined by a strict schema and several ready-to-use SDKs wrap this protocol, handle asynchronous message passing, timeouts, reconnects, and various Centrifugo client API features. See detailed information about client real-time transports in a dedicated section.

Variety of real-time transports

The main transport in Centrifugo is WebSocket. For browsers that do not support WebSocket, Centrifugo provides its own bidirectional WebSocket emulation layer based on HTTP-streaming (using Fetch and Readable streams browser APIs) and SSE (EventSource). It also supports SockJS as an older but battle-tested WebSocket polyfill option. Additionally, WebTransport is supported in an experimental form.

In addition to bidirectional transports, Centrifugo also supports a unidirectional approach for real-time updates: using SSE (EventSource), HTTP-streaming, and GRPC unidirectional stream. Utilizing a unidirectional transport is sufficient for many real-time applications and does not require using our client SDKs – just native standards or GRPC-generated code.

See detailed information about client real-time transports in a dedicated section.

Flexible authentication

Centrifugo can authenticate connections by checking JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon a client connection to Centrifugo. It's possible to proxy original request headers or request metadata (in the case of a GRPC connection).

It supports the JWK specification.

Connection management

Connections can expire; developers can choose a way to handle connection refresh – using a client-side refresh workflow or a server-side call from Centrifugo to the application backend. Centrifugo provides APIs to disconnect users, unsubscribe users from channels, and inspect active channels.

Channel (room) concept

Centrifugo is a PUB/SUB server – users subscribe to channels to receive real-time updates. A message sent to a channel is delivered to all online channel subscribers.

Different types of subscriptions

Centrifugo supports client-side (initiated by the client) and server-side (forced by the server) channel subscriptions.

RPC over bidirectional connection

You can fully utilize bidirectional connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing incoming traffic.

Online presence information

The online presence feature for channels provides information about active channel subscribers. Also, channel join and leave events (when someone subscribes/unsubscribes) can be received on the client side.

Message history in channels

Optionally, Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With channel history, Centrifugo can help to survive the mass reconnect scenario – clients can automatically catch up on missed state from a fast cache thus reducing the load on your primary database. It's also possible to manually iterate over a history stream from the client or from the application backend side.

Embedded admin web UI

The built-in admin UI allows publishing messages to channels, looking at Centrifugo cluster information, and more.

Cross-platform

Centrifugo works on Linux, macOS, and Windows.

Ready to deploy

Centrifugo supports various deployment methods: in Docker, using prepared RPM or DEB packages, via a Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, and has an official Grafana dashboard for the Prometheus data source.

Open-source

Centrifugo stands on top of the open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All our official client SDKs and API libraries are MIT-licensed.

PRO features

Centrifugo PRO extends Centrifugo with several unique features which can provide interesting advantages for business adopters. For additional details, refer to the Centrifugo PRO documentation.

- + \ No newline at end of file diff --git a/docs/getting-started/installation.html b/docs/getting-started/installation.html index da74cfdf0..a1cafbb38 100644 --- a/docs/getting-started/installation.html +++ b/docs/getting-started/installation.html @@ -16,13 +16,13 @@ - +

Install Centrifugo

Centrifugo server is written in the Go language. It's open-source software, and the source code is available on Github.

Install from the binary release

For local development, you can download the prebuilt Centrifugo binary release (i.e., a single all-contained executable file) for your system.

Binary releases are available on Github. Download the latest release for your operating system, unpack it, and you are done. Centrifugo is pre-built for:

  • Linux 64-bit (linux_amd64)
  • Linux 32-bit (linux_386)
  • Linux ARM 64-bit (linux_arm64)
  • MacOS (darwin_amd64)
  • MacOS on Apple Silicon (darwin_arm64)
  • Windows (windows_amd64)
  • FreeBSD (freebsd_amd64)
  • ARM v6 (linux_armv6)

Archives contain a single statically compiled binary centrifugo file that is ready to run.

./centrifugo

If you are unsure which distribution you need, then on Linux or MacOS you can use the following command to download and unpack the centrifugo binary to your current working directory:

curl -sSLf https://centrifugal.dev/install.sh | sh

See the version of Centrifugo:

./centrifugo version

Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo, then there is a genconfig command which generates a minimal configuration file to get started:

./centrifugo genconfig

This creates a configuration file config.json with some auto-generated option values in the current directory (by default).

tip

It's possible to generate a file in YAML or TOML format, for example: ./centrifugo genconfig -c config.toml

With a configuration file, you can finally run a Centrifugo instance:

./centrifugo --config=config.json

We will talk about configuration in detail in the next sections.

You can also put or symlink centrifugo into your bin OS directory and run it from anywhere:

centrifugo --config=config.json

Docker image

Centrifugo server has a docker image available on Docker Hub.

docker pull centrifugo/centrifugo

Run:

docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo:v5 centrifugo -c config.json

Note that docker allows setting nofile limits in command-line arguments, which is quite important to handle many simultaneous persistent connections and not run out of the open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter.

caution

Pin to the exact Docker Image tag in production, for instance: centrifugo/centrifugo:v5.0.0. This will help to avoid unexpected problems during redeployment process.

Docker-compose example

Create configuration file config.json:

{
"token_hmac_secret_key": "my_secret",
"api_key": "my_api_key",
"admin_password": "password",
"admin_secret": "secret",
"admin": true
}

Create docker-compose.yml:

version: "3.9"
services:
centrifugo:
container_name: centrifugo
image: centrifugo/centrifugo:v5
volumes:
- ./config.json:/centrifugo/config.json
command: centrifugo -c config.json
ports:
- 8000:8000
ulimits:
nofile:
soft: 65535
hard: 65535

Run with:

docker-compose up

Kubernetes Helm chart

See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.

RPM and DEB packages for Linux

Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io.

At moment, we support versions of the following distributions:

  • 64-bit Debian 10 Buster
  • 64-bit Debian 11 Bullseye
  • 64-bit Ubuntu 18.04 Bionic
  • 64-bit Ubuntu 20.04 Focal Fossa
  • 64-bit Ubuntu 20.04 Jammy
  • 64-bit Centos 7

See full list of available packages and installation instructions.

Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.

With brew on macOS

If you are developing on macOS then you can install Centrifugo over brew:

brew tap centrifugal/centrifugo
brew install centrifugo

Build from source

You need Go language installed. Then:

git clone https://github.com/centrifugal/centrifugo.git
cd centrifugo
go build
./centrifugo
- + \ No newline at end of file diff --git a/docs/getting-started/integration.html b/docs/getting-started/integration.html index 1cfe31252..5bef3fe17 100644 --- a/docs/getting-started/integration.html +++ b/docs/getting-started/integration.html @@ -16,13 +16,13 @@ - +

Integration guide

This chapter aims to help you get started with Centrifugo. We will look at a step-by-step workflow of integrating your application with Centrifugo, providing links to relevant parts of this documentation.

As Centrifugo is language-agnostic and can be used together with any language/framework, we won't be specific here about any backend or frontend technology your application can be built with. Only abstract steps, which you can extrapolate to your application stack, are presented.

Let's look at a simplified scheme:

Centrifugo scheme

There are three parts involved in the idiomatic Centrifugo usage scenario:

  1. Your application backend
  2. Centrifugo
  3. Your clients (frontend application)

It's possible to use Centrifugo without any application backend involved, but here we won't consider this use case.

Here, let's suppose you already have 2 of the 3 elements: clients and the backend. Now you want to add Centrifugo to receive real-time events on the client-side.

0. Install

The first thing you need to do is download/install the Centrifugo server. See the install chapter for details.

1. Configure Centrifugo

Create a basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set, and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and the chapter about server API for the API description. The simplest way to do this automatically is by using the genconfig command:

./centrifugo genconfig

– which will generate config.json file for you with a minimal set of fields to start from.

Properly configure allowed_origins option.

2. Configure your backend

In the configuration file of your application backend, register several variables: the Centrifugo token secret (if you decided to stick with JWT authentication) and the Centrifugo API key you set in the previous step, as well as the Centrifugo API endpoint address. By default, the API address is http://localhost:8000/api. You must never reveal the token secret and API key to your users.

3. Connect to Centrifugo

Now your users can start connecting to Centrifugo. You should get a client library (see the list of available client SDKs) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here.

Every client should provide a connection token (JWT) upon connecting. You must generate this token on your backend side using the Centrifugo secret key you set in the backend configuration (note that in the case of RSA tokens, you are generating JWT with a private key). See how to generate this JWT in the special chapter.

You pass this token from the backend to your frontend app (pass it in the template context or use a separate request from the client-side to get a user-specific JWT from the backend side). And use this token when connecting to Centrifugo (for example, the browser client has a special method setToken).

There is also a way to authenticate connections without using JWT - see the chapter about proxying to the backend.

You connect to Centrifugo using one of the available transports.

4. Subscribe to channels

After connecting to Centrifugo, subscribe clients to channels they are interested in. See more about channels in the special chapter. All bidirectional client SDKs provide a way to handle messages coming to a client from a channel after subscribing to it. Learn more about client SDK possibilities from the client SDK API spec.

There is also a way to subscribe a connection to a list of channels on the server side at the moment of connection establishment. See the chapter about server-side subscriptions.

5. Publish to a channel

Everything should work now – as soon as a user opens some page of your application, they must successfully connect to Centrifugo and subscribe to a channel (or channels).

Now let's imagine you want to send a real-time message to users subscribed to a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, an administrator just created a new post, a user pressed the "like" button, etc. Anyway, this is an event your backend has just received, and you want to immediately send it to interested users.

You can do this using the Centrifugo HTTP API. To simplify your life, we have several API libraries for different languages. You can publish messages into a channel using one of those libraries, or you can simply follow the API description to construct API requests yourself - this is very simple. Centrifugo also supports a GRPC API. As soon as you have published a message to the channel, it must be delivered to your online clients subscribed to that channel.

6. Deploy to production

To put all this into production, you need to deploy Centrifugo on your production server. To help you with this, we have many things like a Docker image, rpm and deb packages, Nginx configuration. See the Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.

7. Monitor Centrifugo

Don't forget to configure metrics monitoring for your production Centrifugo setup. This may help you to understand what's going on with your Centrifugo setup, understand the number of connections, operation count, and latency distributions, etc.

8. Scale Centrifugo

As soon as you are close to machine resource limits, you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using the Redis engine, or with KeyDB, or with Tarantool, or with a Nats broker. The Engines and scalability chapter describes available options in detail.

9. Read FAQ

That's all for the basics. The documentation actually covers a lot of other concepts Centrifugo server has: scalability, private channels, the admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ – it contains a lot of useful information.

- + \ No newline at end of file diff --git a/docs/getting-started/introduction.html b/docs/getting-started/introduction.html index 9d5889d71..4b201cbcc 100644 --- a/docs/getting-started/introduction.html +++ b/docs/getting-started/introduction.html @@ -16,13 +16,13 @@ - +

Centrifugo introduction

Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, WebTransport, GRPC, SockJS). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.

Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc., in combination with any backend. It is well suited for modern architectures and allows decoupling of business logic from the real-time transport layer.

Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.

Real-time?

By real-time, we mean a soft real-time. Due to network latencies, garbage collection cycles, etc., the delay of a delivered message can be up to several hundred milliseconds or higher.

Background

Centrifugo was born more than a decade ago to help applications whose server-side code was written in a language or framework lacking built-in concurrency support. In such cases, managing persistent connections can be a real headache, usually resolvable only by altering the technology stack and investing time in developing a production-ready solution.

For instance, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others offer limited or suboptimal support for handling numerous persistent connections for real-time messaging tasks.

Here, Centrifugo provides a straightforward and non-obtrusive way to introduce real-time updates and manage many persistent connections without radical changes in the application backend architecture. Developers can continue to work on the application's backend using their preferred language or framework, and keep the existing architecture. Just let Centrifugo deal with persistent connections and be the real-time messaging transport layer.

These days, Centrifugo offers advanced and unique features that can significantly simplify a developer's workload and save months (if not years) of development time, even if the application's backend is built with an asynchronous concurrent language or framework. One example is Centrifugo's built-in support for scaling across numerous machines to accommodate more connections while ensuring that channel subscribers on different Centrifugo nodes receive all publications. Or the fact that Centrifugo has a bunch of real-time SDKs which provide subscription multiplexing over a WebSocket connection, robust reconnect logic, built-in ping-pong, etc. And there are more things to mention: the documentation uncovers features step by step.

Centrifugo fits well with modern architectures and can serve as a universal real-time component, regardless of the application's technology stack. It stands as a viable self-hosted alternative to cloud solutions like Pusher, Ably, or PubNub.

- + \ No newline at end of file diff --git a/docs/getting-started/migration_v4.html b/docs/getting-started/migration_v4.html index d37738e37..6d2a2068d 100644 --- a/docs/getting-started/migration_v4.html +++ b/docs/getting-started/migration_v4.html @@ -16,13 +16,13 @@ - +

Migrating to v4

Centrifugo v4 development was concentrated around two main things:

  • adopt a new generation of client protocol
  • make namespaces secure by default

These goals dictate most of backwards compatibility changes in v4.

tip

What we would like to emphasize is that even there are many backwards incompatible changes it should be possible to migrate to Centrifugo v4 server without changing your client-side code at all. And then gradually upgrade the client-side. Below we are giving all the tips to achieve this.

Client SDK migration

New generation of client protocol requires using the latest versions of client SDKs. During the next several days we will release the following SDK versions which are compatible with Centrifugo v4:

  • centrifuge-js >= v3.0.0
  • centrifuge-go >= v0.9.0
  • centrifuge-dart >= v0.9.0
  • centrifuge-swift >= v0.5.0
  • centrifuge-java >= v0.2.0

New client SDKs support only new client protocol – you can not connect to Centrifugo v3 with them.

If you have a production system where you want to upgrade Centrifugo from v3 to v4 then the plan is:

danger

If you are using private channels (starting with $) or user-limited channels (containing #) then carefully read about subscription token migration and user-limited channels migration below.

  1. Upgrade Centrifugo and its configuration to adopt changes in v4.
  2. In Centrifugo v4 config turn on use_client_protocol_v1_by_default.
  3. Run Centrifugo v4 – all current clients should continue working with it.
  4. Then on the client-side uprade client SDK version to the one which works with Centrifugo v4, adopt changes in SDK API dictated by our new client SDK API spec. Important thing – add ?cf_protocol_version=v2 URL param to the connection endpoint to tell Centrifugo that modern generation of protocol is being used by the connection (otherwise, it assumes old protocol since we have use_client_protocol_v1_by_default option enabled).
  5. As soon as all your clients migrated to use new protocol generation you can remove use_client_protocol_v1_by_default option from the server configuration.
  6. After that you can remove ?cf_protocol_version=v2 from connection endpoint on the client-side.
tip

If you are using mobile client SDKs then most probably some time must pass while clients update their apps to use an updated Centrifugo SDK version.

tip

Starting from Centrifugo v4.1.1 it's possible to completely turn off client protocol v1 by setting disable_client_protocol_v1 boolean option to true.

Unidirectional transport migration

Client protocol framing also changed in unidirectional transports. The good news is that Centrifugo v4 still supports previous format for unidirectional transports.

When you are enabling use_client_protocol_v1_by_default option described above you also make unidirectional transports to work over old protocol format. So your existing clients will continue working just fine with Centrifugo v4. Then the same steps to migrate described above can be applied to unidirectional transport case. The only difference that in unidirectional approach you are not using Centrifugo SDKs.

SockJS migration

SockJS is now DEPRECATED in Centrifugo. Centrifugo v4 may be the last release which supports it. We now offer our own bidirectional emulation layer on top of HTTP-streaming and EventSource. See additional information in Centrifugo v4 introduction post.

Channel ASCII enforced

Centrifugo v2 and v3 docs mentioned the fact that channels must contain only ASCII characters. But it was not actually enforced by a server. Now Centrifugo is more strict. If a channel has non-ASCII characters then the 107: bad request error will be returned to the client. Please reach us out if this behavior is not suitable for your use case – we can discuss the use case and think on a proper solution together.

Subscription token migration

Subscription token now requires sub claim (current user ID) to be set.

In most cases the only change which is required to smoothly migrate to v4 without breaking things is to add a boolean option "skip_user_check_in_subscription_token": true to a Centrifugo v4 configuration. This skips the check of sub claim to contain the current user ID set to a connection during authentication.

After that start adding sub claim (with current user ID) to subscription tokens. As soon as all subscription tokens in your system contain user ID in sub claim you can remove the skip_user_check_in_subscription_token from a server configuration.

One more important note is that client claim in subscription token in Centrifugo v4 only supported for backwards compatibility. It must not be included into new subscription tokens.

It's worth mentioning that Centrifugo v4 does not allow subscribing on channels starting with $ without token even if namespace marked as available for subscribing using sth like allow_subscribe_for_client option. This is done to prevent potential security risk during v3 -> v4 migration when client previously not available to subscribe to channels starting with $ in any case may get permissions to do so.

User-limited channel migration

User-limited channel support should now be allowed over a separate channel namespace option allow_user_limited_channels. See below the namespace option converter which takes this change into account.

Namespace configuration migration

In Centrifugo v4 namespace configuration options have been changed. Centrifugo now has secure by default namespaces. First thing to do is to read the new docs about channels and namespaces.

Then you can use the following converter which will transform your old namespace configuration to a new one. This converter tries to keep backwards compatibility – i.e. it should be possible to deploy Centrifugo with namespace configuration from converter output and have the same behaviour as before regarding channel permissions. We believe that new option names should provide a more readable configuration and may help to reveal some potential security improvements in your namespace configuration – i.e. making it more strict and protective.

caution

Do not blindly deploy things to production – test your system first, go through the possible usage scenarios and/or test cases.

tip

It's fully client-side: your data won't be sent anywhere.

Here will be configuration for v4
Here will be log of changes made in your config

Proxy disconnect code changes

reconnect flag from custom disconnect code is removed. Reconnect advice is now determined by disconnect code value. This allowed us avoiding using JSON in WebSocket CLOSE frame reason. See proxy docs docs for more details.

Other configuration option changes

Several other non-namespace related options have been renamed or removed:

  • client_anonymous option renamed to allow_anonymous_connect_without_token – new name better describes the purpose of this option which was previously not clear. Converter above takes this into account.
  • use_unlimited_history_by_default option was removed. It was used to help migrating from Centrifugo v2 to v3.

Server API changes

The only breaking change is that user_connections API method (which is available in Centrifugo PRO only) was renamed to connections. The method is more generic now with a broader possibilities – so previous name does not match the current behavior.

- + \ No newline at end of file diff --git a/docs/getting-started/migration_v5.html b/docs/getting-started/migration_v5.html index 8242c78de..7d4687d1c 100644 --- a/docs/getting-started/migration_v5.html +++ b/docs/getting-started/migration_v5.html @@ -16,13 +16,13 @@ - +

Migrating to v5

Centrifugo v5 migration from v4 should be smooth for most of the use cases.

Client SDK token behaviour adjustments

In Centrifugo v5 client SDK spec was adjusted in regards how SDKs work with tokens.

Returning an empty string from getToken function (for Javascript, and the same for analogous functions in other SDKs) is a valid scenario which won't result into disconnect on the client side. It's still possible to disconnect client by returning a special error from getToken function. We updated all our SDKs to inherit this behaviour. Specifically, here is a list of SDK versions which work according to adjusted spec:

  • centrifuge-js >= v4.0.0
  • centrifuge-go >= v0.10.0
  • centrifuge-dart >= v0.10.0
  • centrifuge-swift >= v0.6.0
  • centrifuge-java >= v0.3.0

Nothing prevents you from updating Centrifugo v4 to v5 first and then migrate to new client versions or doing vice versa. This change is client-side only. But we bind it to major server release to make it more notable – as it changes the core SDK behavior.

Node communication format changed

Avoid running Centrifugo v5 in the same cluster with Centrifugo v4 nodes – v4 and v5 have backwards incompatible node communication protocols.

Old HTTP API format is DEPRECATED

Prefer using new HTTP API format instead of old one where possible. The old format still works and enabled by default. But we are planning to migrate our API libraries to the new format eventually – and then remove the old format. If you are using one of our HTTP API libs - at some point a new version will be released which will seamlessly migrate you to the modern HTTP API format.

If you are using hand-written requests – then some refactoring is required. It should be rather straighforward:

Don't forget that you can now generate HTTP clients from OpenAPI spec we now maintain for the new HTTP API format.

Other changes

  • skip_user_check_in_subscription_token removed
  • reconnect flag removed from disconnect API call and proxy structures for custom disconnection
  • use_client_protocol_v1_by_default option removed
  • disable_client_protocol_v1 option removed
  • redis_tls_skip_verify option removed
  • presence_ttl renamed to global_presence_ttl
  • sockjs_heartbeat_delay option removed
  • uni_websocket_ping_interval option removed
  • websocket_ping_interval option removed

Shutting down Centrifugo v2 doc site

With Centrifugo v5 release we are shutting down Centrifugo v2 documentation site - this means https://centrifugal.github.io/centrifugo/ won't be available anymore. Documents may still be found on Github. Documentation for v3, v4 and v5 is hosted here.

- + \ No newline at end of file diff --git a/docs/getting-started/quickstart.html b/docs/getting-started/quickstart.html index 7d8f49680..dc2142fbd 100644 --- a/docs/getting-started/quickstart.html +++ b/docs/getting-started/quickstart.html @@ -16,13 +16,13 @@ - +

Quickstart tutorial ⏱️

In this tutorial, we will build a very simple browser application with Centrifugo. Users will connect to Centrifugo over WebSocket, subscribe to a channel, and start receiving all channel publications (messages published to that channel). In our case, we will send a counter value to all channel subscribers to update the counter widget in all open browser tabs in real-time.

First, you need to install Centrifugo. In this example, we are using a binary file release which is fine for development. Once you have the Centrifugo binary available on your machine, you can generate the minimal required configuration file with the following command:

./centrifugo genconfig

This helper command will generate config.json file in the working directory with a content like this:

config.json
{
"token_hmac_secret_key": "bbe7d157-a253-4094-9759-06a8236543f9",
"admin_password": "d0683813-0916-4c49-979f-0e08a686b727",
"admin_secret": "4e9eafcf-0120-4ddd-b668-8dc40072c78e",
"api_key": "d7627bb6-2292-4911-82e1-615c0ed3eebb",
"allowed_origins": []
}

Now we can start a server. Let's start Centrifugo with a built-in admin web interface:

./centrifugo --config=config.json --admin

We could also enable the admin web interface by not using --admin flag but by adding "admin": true option to the JSON configuration file:

config.json
{
"token_hmac_secret_key": "bbe7d157-a253-4094-9759-06a8236543f9",
"admin": true,
"admin_password": "d0683813-0916-4c49-979f-0e08a686b727",
"admin_secret": "4e9eafcf-0120-4ddd-b668-8dc40072c78e",
"api_key": "d7627bb6-2292-4911-82e1-615c0ed3eebb",
"allowed_origins": []
}

And then running Centrifugo only with a path to a configuration file:

./centrifugo --config=config.json

Now open http://localhost:8000. You should see Centrifugo admin web panel. Enter admin_password value from the configuration file to log in (in our case it's d0683813-0916-4c49-979f-0e08a686b727, but you will have a different value).

Admin web panel

Inside the admin panel, you should see that one Centrifugo node is running, and it does not have connected clients:

Admin web panel

Now let's create index.html file with our simple app:

index.html
<html>

<head>
<title>Centrifugo quick start</title>
</head>

<body>
<div id="counter">-</div>
<script src="https://unpkg.com/centrifuge@5.0.1/dist/centrifuge.js"></script>
<script type="text/javascript">
const container = document.getElementById('counter');

const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: "<TOKEN>"
});

centrifuge.on('connecting', function (ctx) {
console.log(`connecting: ${ctx.code}, ${ctx.reason}`);
}).on('connected', function (ctx) {
console.log(`connected over ${ctx.transport}`);
}).on('disconnected', function (ctx) {
console.log(`disconnected: ${ctx.code}, ${ctx.reason}`);
}).connect();

const sub = centrifuge.newSubscription("channel");

sub.on('publication', function (ctx) {
container.innerHTML = ctx.data.value;
document.title = ctx.data.value;
}).on('subscribing', function (ctx) {
console.log(`subscribing: ${ctx.code}, ${ctx.reason}`);
}).on('subscribed', function (ctx) {
console.log('subscribed', ctx);
}).on('unsubscribed', function (ctx) {
console.log(`unsubscribed: ${ctx.code}, ${ctx.reason}`);
}).subscribe();
</script>
</body>

</html>

Note that we are using centrifuge-js 5.0.1 in this example, getting it from a CDN. You should use its latest version at the moment of reading this tutorial. In a real Javascript app, you would most likely load centrifuge from NPM.

In the index.html above, we created an instance of a Centrifuge client by passing the Centrifugo server's default WebSocket endpoint address to it. Then we subscribed to a channel called channel and provided a callback function to process incoming real-time messages (publications). Upon receiving a new publication, we update the page's HTML by setting the counter value to the page title. We call .subscribe() to initiate the subscription and the .connect() method of the Client to start a WebSocket connection. We also handle Client state transitions (disconnected, connecting, connected) and Subscription state transitions (unsubscribed, subscribing, subscribed) – see a detailed description in client SDK spec.

Now you need to serve this file with an HTTP server. In a real-world Javascript application, you would serve your HTML files with a web server of your choice – but for this simple example, we can use a simple built-in Centrifugo static file server:

./centrifugo serve --port 3000

Alternatively, if you have Python 3 installed:

python3 -m http.server 3000

These commands start a simple static file web server that serves the current directory on port 3000. Make sure you still have Centrifugo server running. Open http://localhost:3000/.

Now if you look at browser developer tools or in Centrifugo logs you will notice that a connection can not be successfully established:

2021-09-01 10:17:33 [INF] request Origin is not authorized due to empty allowed_origins origin=http://localhost:3000

That's because we have not set allowed_origins in the configuration. Modify allowed_origins like this:

config.json
{
...
"allowed_origins": ["http://localhost:3000"]
}

Allowed origins is a security option for request originating from web browsers – see more details in server configuration docs. Restart Centrifugo after modifying allowed_origins in a configuration file.

Now if you reload a browser window with an application you should see new information logs in server output:

2022-06-10 09:44:21 [INF] invalid connection token error="invalid token: token format is not valid" client=a65a8463-6a36-421d-814a-0083c8836529
2022-06-10 09:44:21 [INF] disconnect after handling command client=a65a8463-6a36-421d-814a-0083c8836529 command="id:1 connect:{token:\"<TOKEN>\" name:\"js\"}" reason="invalid token" user=

We still cannot connect. That's because the client must provide a valid JWT (JSON Web Token) to authenticate itself. This token must be generated on your backend and passed to the client-side (over template variables or using a separate AJAX call – whichever way you prefer). Since in our simple example we don't have an application backend, we can quickly generate an example token for a user using the centrifugo sub-command gentoken, like this:

./centrifugo gentoken -u 123722

– where -u flag sets user ID. The output should be like this:

HMAC SHA-256 JWT for user "123722" with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw

– you will have a different token value since this one is based on the randomly generated token_hmac_secret_key from the configuration file we created at the beginning of this tutorial. See the token authentication docs for information about proper token generation in a real application.

Now we can copy generated HMAC SHA-256 JWT and paste it into Centrifugo constructor instead of <TOKEN> placeholder in index.html file. I.e.:

const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw"
});

If you reload your browser tab – the connection will be successfully established, but the client still cannot subscribe to a channel:

2022-06-10 09:45:49 [INF] client command error error="permission denied" client=88116489-350f-447f-9ff3-ab61c9341efe code=103 command="id:2  subscribe:{channel:\"channel\"}" reply="id:2  error:{code:103  message:\"permission denied\"}" user=123722

We need to give the client permission to subscribe to the channel channel. There are several ways to do this. For example, the client can provide a subscription JWT for a channel. But here we will use an option to allow all authenticated clients to subscribe to any channel.

To do this let's extend the server configuration with the allow_subscribe_for_client option:

config.json
{
"token_hmac_secret_key": "bbe7d157-a253-4094-9759-06a8236543f9",
"admin": true,
"admin_password": "d0683813-0916-4c49-979f-0e08a686b727",
"admin_secret": "4e9eafcf-0120-4ddd-b668-8dc40072c78e",
"api_key": "d7627bb6-2292-4911-82e1-615c0ed3eebb",
"allowed_origins": ["http://localhost:3000"],
"allow_subscribe_for_client": true
}
tip

A good practice with Centrifugo is to configure channel namespaces for the different types of real-time features you have in the application. By defining namespaces, you can achieve granular control over channel behavior and permissions.

Restart Centrifugo – and after doing this, everything should start working. The client can now successfully connect and subscribe to a channel.

Open the developer tools and look at the WebSocket frames panel; you should see something like this:

Connected

Note that in this example, we generated a connection JWT – but it has an expiration time, so after some time, Centrifugo will stop accepting those tokens. In real life, you need to add a token refresh function to the client to rotate tokens. See our client API SDK spec.

OK, the last thing we need to do here is to publish a new counter value to a channel and make sure our app works properly.

We can do this over the Centrifugo API by sending an HTTP request to the default API endpoint http://localhost:8000/api, but let's do this over the admin web panel first.

Open the Centrifugo admin web panel in another browser tab (http://localhost:8000/) and go to the Actions section. Select the publish action, insert the channel name that you want to publish to – in our case, this is the string channel – and insert into the data area JSON like this:

{
"value": 1
}

Admin publish

Click PUBLISH button and check out the application browser tab – counter value must be immediately received and displayed.

Open several browser tabs with our app and make sure all tabs receive a message as soon as you publish it.

Message received

BTW, let's also look at how you can publish data to a channel over Centrifugo server API from a terminal using curl tool:

curl --header "Content-Type: application/json" \
--header "X-API-Key: d7627bb6-2292-4911-82e1-615c0ed3eebb" \
--request POST \
--data '{"channel": "channel", "data": {"value": 2}}' \
http://localhost:8000/api/publish

– where for Authorization header we set api_key value from Centrifugo config file generated above.

We did it! We built the simplest browser real-time app with Centrifugo and its JavaScript client. It doesn't have a backend; it's not very useful, to be honest, but it should give you an insight into how to start working with the Centrifugo server. Read more about the Centrifugo server in the next documentation chapters – it can do much more than we just showed here. The Integration guide describes the process of idiomatic Centrifugo integration with your application backend.

- + \ No newline at end of file diff --git a/docs/pro/analytics.html b/docs/pro/analytics.html index 892ed24cf..5c82ddee1 100644 --- a/docs/pro/analytics.html +++ b/docs/pro/analytics.html @@ -16,13 +16,13 @@ - +

Analytics with ClickHouse

This feature allows exporting information about channel publications, client connections, channel subscriptions, client operations and push notifications to ClickHouse thus providing an integration with a real-time (with seconds delay) analytics storage. ClickHouse is super fast for analytical queries, simple to operate with and it allows effective data keeping for a window of time. Also, it's relatively simple to create a high performance ClickHouse cluster.

clickhouse

This unlocks a great observability and a way to perform various analytics queries for better connection behavior understanding, check application correctness, building trends, reports, and so on.

As soon as you start using integration with ClickHouse some of mentioned possibilities may be easily accessed with Centrifugo PRO web UI and it's analytics page:

Admin analytics

Configuration

To enable integration with ClickHouse add the following section to a configuration file:

config.json
{
...
"clickhouse_analytics": {
"enabled": true,
"clickhouse_dsn": [
"tcp://127.0.0.1:9000",
"tcp://127.0.0.1:9001",
"tcp://127.0.0.1:9002",
"tcp://127.0.0.1:9003"
],
"clickhouse_database": "centrifugo",
"clickhouse_cluster": "centrifugo_cluster",
"export_connections": true,
"export_subscriptions": true,
"export_operations": true,
"export_publications": true,
"export_notifications": true,
"export_http_headers": [
"User-Agent",
"Origin",
"X-Real-Ip"
]
}
}

All ClickHouse analytics options scoped to clickhouse_analytics section of configuration.

Toggle this feature using enabled boolean option.

tip

While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo.

Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option.

You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database.

export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available.

export_subscriptions tells Centrifugo to export subscription information snapshots. Information about subscription will be exported once a subscription established and then periodically while connection alive. See below on table structure to see which fields are available.

export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available.

export_publications tells Centrifugo to export publications for channels to a separate ClickHouse table.

export_notifications tells Centrifugo to export push notifications to a separate ClickHouse table.

export_http_headers is a list of HTTP headers to export for connection information.

export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport.

skip_schema_initialization - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process.

skip_ping_on_start - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.

Connections table

SHOW CREATE TABLE centrifugo.connections;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.connections
(
`client` String,
`user` String,
`name` String,
`version` String,
`transport` String,
`headers` Map(String, Array(String)),
`metadata` Map(String, Array(String)),
`time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.connections_distributed;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.connections_distributed
(
`client` String,
`user` String,
`name` String,
`version` String,
`transport` String,
`headers` Map(String, Array(String)),
`metadata` Map(String, Array(String)),
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client))
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

Subscriptions table

SHOW CREATE TABLE centrifugo.subscriptions

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.subscriptions
(
`client` String,
`user` String,
`channels` Array(String),
`time` DateTime
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.subscriptions_distributed;

┌─statement───────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.subscriptions_distributed
(
`client` String,
`user` String,
`channels` Array(String),
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'subscriptions', murmurHash3_64(client))
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

Operations table

SHOW CREATE TABLE centrifugo.operations;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations
(
`client` String,
`user` String,
`op` String,
`channel` String,
`method` String,
`error` UInt32,
`disconnect` UInt32,
`duration` UInt64,
`time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.operations_distributed;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations_distributed
(
`client` String,
`user` String,
`op` String,
`channel` String,
`method` String,
`error` UInt32,
`disconnect` UInt32,
`duration` UInt64,
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client))
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Publications table

SHOW CREATE TABLE centrifugo.publications

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.publications
(
`channel` String,
`source` String,
`size` UInt64,
`client` String,
`user` String,
`time` DateTime
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.publications_distributed;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations_distributed
(
`channel` String,
`source` String,
`size` UInt64,
`client` String,
`user` String,
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'publications', murmurHash3_64(channel))
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Notifications table

🚧 This PRO feature is under construction together with push notification API.

SHOW CREATE TABLE centrifugo.notifications

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.notifications
(
`uid` String,
`provider` String,
`type` String,
`recipient` String,
`device_id` String,
`platform` String,
`user` String,
`msg_id` String,
`status` String,
`error_message` String,
`error_code` String,
`time` DateTime
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(time)
ORDER BY time
TTL time + toIntervalDay(1)
SETTINGS index_granularity = 8192
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

And distributed one:

SHOW CREATE TABLE centrifugo.notifications_distributed;

┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
CREATE TABLE centrifugo.operations_distributed
(
`uid` String,
`provider` String,
`type` String,
`recipient` String,
`device_id` String,
`platform` String,
`user` String,
`msg_id` String,
`status` String,
`error_message` String,
`error_code` String,
`time` DateTime
)
ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'notifications', murmurHash3_64(uid))
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Query examples

Show unique users which were connected:

SELECT DISTINCT user
FROM centrifugo.connections_distributed;

┌─user─────┐
│ user_1 │
│ user_2 │
│ user_3 │
│ user_4 │
│ user_5 │
└──────────┘

Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111):

SELECT COUNT(*)
FROM centrifugo.operations_distributed
WHERE (error = 111) AND (op = 'publish');

┌─count()─┐
4502
└─────────┘

The same for a specific user:

SELECT COUNT(*)
FROM centrifugo.operations_distributed
WHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');

┌─count()─┐
1214
└─────────┘

Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since subscriptions table contain periodic snapshot entries, clients could unsubscribe in between snapshots – this is reflected in operations table):

SELECT COUNT(Distinct(user))
FROM centrifugo.subscriptions_distributed
WHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));

┌─uniqExact(user)─┐
101
└─────────────────┘

Show top 10 users which called publish operation during last one minute:

SELECT
COUNT(op) AS num_ops,
user
FROM centrifugo.operations_distributed
WHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))
GROUP BY user
ORDER BY num_ops DESC
LIMIT 10;

┌─num_ops─┬─user─────┐
56 │ user_200 │
11 │ user_75 │
6 │ user_87 │
6 │ user_65 │
6 │ user_39 │
5 │ user_28 │
5 │ user_63 │
5 │ user_89 │
3 │ user_32 │
3 │ user_52 │
└─────────┴──────────┘

Show total number of push notifications to iOS devices sent during last 24 hours:

SELECT COUNT(*)
FROM centrifugo.notifications
WHERE (time > (now() - toIntervalHour(24))) AND (platform = 'ios')

┌─count()─┐
31200
└─────────┘

Development

The recommended way to run ClickHouse in production is with cluster. See an example of such cluster configuration made with Docker Compose.

But during development you may want to run Centrifugo with single instance ClickHouse.

To do this set only one ClickHouse dsn and do not set cluster name:

config.json
{
...
"clickhouse_analytics": {
"enabled": true,
"clickhouse_dsn": [
"tcp://127.0.0.1:9000"
],
"clickhouse_database": "centrifugo",
"clickhouse_cluster": "",
"export_connections": true,
"export_subscriptions": true,
"export_publications": true,
"export_operations": true,
"export_http_headers": [
"Origin",
"User-Agent"
]
}
}

Run ClickHouse locally:

docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click clickhouse/clickhouse-server

Run ClickHouse client:

docker run -it --rm --link click:clickhouse-server --entrypoint clickhouse-client clickhouse/clickhouse-server --host clickhouse-server

Issue queries:

:) SELECT * FROM centrifugo.operations

┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 │
│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │
└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘

How export works

When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 10 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are configurable. Centrifugo PRO uses very efficient code for writing data to ClickHouse, so analytics feature should only add a little overhead for Centrifugo node.

Exposed metrics

Several metrics are exposed to monitor export process health:

centrifugo_clickhouse_analytics_drop_count

  • Type: Counter
  • Labels: type
  • Description: Total count of drops.
  • Usage: Useful for tracking the number of data drops in ClickHouse analytics, helping identify potential issues with data processing.

centrifugo_clickhouse_analytics_flush_duration_seconds

  • Type: Summary
  • Labels: type, retries, result
  • Description: Duration of ClickHouse data flush in seconds.
  • Usage: Helps in monitoring the performance of data flush operations in ClickHouse, aiding in performance tuning and issue resolution.

centrifugo_clickhouse_analytics_batch_size

  • Type: Summary
  • Labels: type
  • Description: Distribution of batch sizes for ClickHouse flush.
  • Usage: Useful for understanding the size of data batches being flushed to ClickHouse, helping optimize performance.
- + \ No newline at end of file diff --git a/docs/pro/capabilities.html b/docs/pro/capabilities.html index ac7c9ffe8..adae5afa7 100644 --- a/docs/pro/capabilities.html +++ b/docs/pro/capabilities.html @@ -16,13 +16,13 @@ - +

Channel capabilities

At this point you know that Centrifugo allows configuring channel permissions on a per-namespace level. When creating a new real-time feature it's recommended to create a new namespace for it and configure permissions. To achieve a better channel permission control inside a namespace Centrifugo PRO provides possibility to set capabilities on individual connection basis, or individual channel subscription basis.

Let's start by looking at connection-wide capabilities first.

Connection capabilities

Connection capabilities can be set:

  • in connection JWT (in caps claim)
  • in connect proxy result (caps field)

For example, here we are issuing permissions to subscribe on channel news and channel user_42 to a client:

{
"caps": [
{
"channels": ["news", "user_42"],
"allow": ["sub"]
}
]
}

Known capabilities:

  • sub - subscribe to a channel to receive publications from it
  • pub - publish into a channel (your backend won't be able to process the publication in this case)
  • prs - call presence and presence stats API, also consume join/leave events upon subscribing
  • hst - call history API, also make Subscription positioned or recoverable upon subscribing

Caps processing behavior

Centrifugo processes caps objects till it finds a match to a channel. At this point it applies permissions in the matched object and stops processing remaining caps. If no match found – then 103 permission denied returned to a client (of course if namespace does not have other permission-related options enabled). Let's consider example like this:

WRONG!
{
"caps": [
{
"channels": ["news"],
"allow": ["pub"]
},
{
"channels": ["news"],
"allow": ["sub"]
},
]
}

Here we have two entries for channel news, but when client subscribes on news only the first entry will be taken into considiration by Centrifugo – so Subscription attempt will be rejected (since first cap object does not have sub capability). In real life you don't really want to have cap objects with identical channels – but below we will introduce wildcard matching where understanding how caps processed becomes important.

Another example:

WRONG!
{
"caps": [
{
"channels": ["news", "user_42"],
"allow": ["sub"]
},
{
"channels": ["user_42"],
"allow": ["pub", "hst", "prs"]
},
]
}

One could expect that client will have ["sub", "pub", "hst", "prs"] capabilities for a channel user_42. But it's not true since Centrifugo processes caps objects and channels inside caps object in order – it finds a match to user_42 in first caps object, it contains only "sub" capability, processing stops. So user can subscribe to a channel, but can not publish, can not call history and presence APIs even though those capabilities are mentioned in caps object. The correct way to give all caps to the channel user_42 would be to split channels into different caps objects:

CORRECT
{
"caps": [
{
"channels": ["news"],
"allow": ["sub"]
},
{
"channels": ["user_42"],
"allow": ["sub", "pub", "hst", "prs"]
},
]
}

The processing behaves like this to avoid potential problems with possibly conflicting matches (mostly when using wildcard and regex matching – see below) and still allow overriding capabilities for specific channels.

Expiration considirations

  • In JWT auth case – capabilities in JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific.
  • In connect proxy case – capabilities will work until client connection close (disconnect) or connection refresh triggered (with refresh proxy you can provide an updated set of capabilities).

Revoking connection caps

If at some point you need to revoke some capability from a client:

  • Simplest way is to wait for a connection expiration, then upon refresh:
    • if using proxy – provide new caps in refresh proxy result, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities).
    • if JWT auth - provide new caps in connection token, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities).
  • In case of using connect proxy – you can disconnect a user (or client) with a reconnect code. New capabilities will be asked upon reconnection.
  • In case of using token auth – revoke token (Centrifugo PRO feature) and disconnect user (or client) with reconnect code. Upon reconnection user will receive an error that token revoked and will try to load a new one.

Example: wildcard match

It's possible to use wildcards in channel resource names. For example, let's give a permission to subscribe on all channels in news namespace.

{
"caps": [
{
"channels": ["news:*"],
"match": "wildcard",
"allow": ["sub"]
}
]
}
note

Match type is used for all channels in caps object. If you need different matching behavior for different channels then split them on different caps objects.

Example: regex match

Or regex:

{
"caps": [
{
"channels": ["^posts_[\d]+$"],
"match": "regex",
"allow": ["sub"]
}
]
}

Example: different types of match

Of course it's possible to combine different types of match inside one caps array:

{
"caps": [
{
"channels": ["^posts_[\d]+$"],
"match": "regex",
"allow": ["sub"]
}
{
"channels": ["user_42"],
"allow": ["sub"]
}
]
}

Example: full access to all channels

Let's look how to allow all permissions to a client:

{
"caps": [
{
"channels": ["*"],
"match": "wildcard",
"allow": ["sub", "pub", "hst", "prs"]
}
]
}
Full access warn

Should we mention that giving full access to a client is something to wisely consider? 🤔

Subscription capabilities

Subscription capabilities can be set:

  • in subscription JWT (in allow claim)
  • in subscribe proxy result (allow field)

Subscription token already belongs to a channel (it has a channel claim). So users with a valid subscription token can subscribe to a channel. But it's possible to additionally grant channel permissions to a user for publishing and calling presence and history using allow claim:

{
"allow": ["pub", "hst", "prs"]
}

Putting sub permission to the Subscription token does not make much sense – Centrifugo only expects valid token for a subscription permission check.

Expiration considirations

  • In JWT auth case – capabilities in subscription JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific.
  • In subscribe proxy case – capabilities will work until client unsubscribe (or connection close).

Revoking subscription permissions

If at some point you need to revoke some capability from a client:

  • Simplest way is to wait for a subscription expiration, then upon refresh:
    • provide new caps in subscription token, Centrifugo will update channel caps.
  • In case of using subscribe proxy – you can unsubscribe a user (or client) with a resubscribe code. Or disconnect with reconnect code. New capabilities will be set up upon resubscription/reconnection.
  • In case of using JWT auth – revoke token (Centrifugo PRO feature) and unsubscribe/disconnect user (or client) with resubscribe/reconnect code. Upon resubscription/reconnection user will receive an error that token revoked and will try to load a new one.
- + \ No newline at end of file diff --git a/docs/pro/cel_expressions.html b/docs/pro/cel_expressions.html index 94f4c2ef2..54f95bebf 100644 --- a/docs/pro/cel_expressions.html +++ b/docs/pro/cel_expressions.html @@ -16,13 +16,13 @@ - +

CEL expressions

Centrifugo PRO supports CEL expressions (Common Expression Language) for checking channel operation permissions.

CEL expressions provide a developer-friendly, fast and secure way to evaluate some conditions predefined in the configuration. They are used in some Google services (ex. Firebase), in Envoy RBAC configuration, etc.

For Centrifugo this is a flexible mechanism which can help to avoid using subscription tokens or using subscribe proxy in some cases. This means you can avoid sending an additional HTTP request to the backend for a channel subscription attempt. As the result less resources may be used and smaller latencies may be achieved in the system. This is a way to introduce efficient channel permission mechanics when Centrifugo built-in rules are not enough.

Some good links which may help you dive into CEL expressions are:

Below we will explore some basic expressions and show how they can be used in Centrifugo.

subscribe_cel

We suppose that the main operation for which developers may use CEL expressions in Centrifugo is a subscribe operation. Let's look at it in detail.

It's possible to configure subscribe_cel for a channel namespace (subscribe_cel is just an additional namespace channel option, with same rules applied). This expression should be a valid CEL expression.

config.json
{
"namespaces": [
{
"name": "admin",
"subscribe_cel": "'admin' in meta.roles"
}
]
}

In the example we are using custom meta information (must be an object) attached to the connection. As mentioned before in the doc this meta may be attached to the connection:

An expression is evaluated for every subscription attempt to a channel in a namespace. So if meta attached to the connection is sth like this:

{
"roles": ["admin"]
}

– then for every channel in the admin namespace defined above expression will be evaluated to True and subscription will be accepted by Centrifugo.

tip

meta must be JSON object (any {}) for CEL expressions to work.

Expression variables

Inside the expression developers can use some variables which are injected by Centrifugo to the CEL runtime.

Information about current user ID, meta information attached to the connection, all the variables defined in matched channel pattern will be available for CEL expression evaluation.

Say client with user ID 123 subscribes to a channel /users/4 which matched the channel pattern /users/:user:

VariableTypeExampleDescription
subscribedboolfalseWhether client is subscribed to channel, always false for subscribe operation
userstring"123"Current authenticated user ID (known from from JWT or connect proxy result)
metamap[string]any{"roles": ["admin"]}Meta information attached to the connection by the apllication backend (in JWT or over connect proxy result)
channelstring"/users/4"Channel client tries to subscribe
varsmap[string]string{"user": "4"}Extracted variables from the matched channel pattern. It's empty in case of using channels without variables.

In this case, to allow admin to subscribe on any user's channel or allow non-admin user to subscribe only on its own channel, you may construct an expression like this:

{
...
"subscribe_cel": "vars.user == user or 'admin' in meta.roles"
}

Let's look at one more example. Say client with user ID 123 subscribes to a channel /example.com/users/4 which matched the channel pattern /:tenant/users/:user. The permission check may be transformed into sth like this (assuming meta information has information about current connection tenant):

{
"namespaces": [
{
"name": "/:tenant/users/:user",
"subscribe_cel": "vars.tenant == meta.tenant && (vars.user == user or 'admin' in meta.roles)"
}
]
}

publish_cel

CEL expression to check permissions to publish into a channel. Same expression variables are available.

history_cel

CEL expression to check permissions for channel history. Same expression variables are available.

presence_cel

CEL expression to check permissions for channel presence. Same expression variables are available.

- + \ No newline at end of file diff --git a/docs/pro/channel_patterns.html b/docs/pro/channel_patterns.html index e8a9e5072..5ad19b274 100644 --- a/docs/pro/channel_patterns.html +++ b/docs/pro/channel_patterns.html @@ -16,13 +16,13 @@ - +

Channel patterns

Centrifugo PRO enhances a way to configure channels with Channel Patterns feature. This opens a road for building channel model similar to what developers got used to when writing HTTP servers and configuring routes for HTTP request processing.

Configuration

Let's look at the example:

{
// rest of the config ...
"channel_patterns": true, // required to turn on the feature.
"namespaces": [
{
"name": "/users/:name"
// namespace options may go here ...
},
{
"name": "/events/:project/:type"
// namespace options may go here ...
}
]
}

As soon as namespace name starts with / - it's considered a channel pattern. Just like an HTTP path it consists of segments delimited by /. The : symbol in the segment beginning defines a variable part – more information below.

In this case a channel to be used must be sth like /users/mario - i.e. start with / and match one of the patterns defined in the configuration. So this channel pattern matching mechanics behaves mostly like HTTP route matching in many frameworks.

Given the configuration example above:

  • if channel is /users/mario, then the namespace with the name /users/:name will match and we apply all the options defined for it to the channel.
  • if channel is /events/42/news, then the namespace with the name /events/:project/:type will match.
  • if channel is /events/42, then no namespace will match and the unknown channel error will be returned.
Basic example demonstrating use of pattern channels in JS
const client := new Centrifuge("ws://...", {});
const sub = client.newSubscription('/users/mario');
sub.subscribe();
client.connect();

Implementation details

Some implementation restrictions and details to know about:

  • When using channel patterns feature : symbol in a namespace name defines a variable part. It's not related to a namespace separator anymore – the entire channel is matched over the channel pattern. Similar to the HTTP routes semantics. So namespace separator is not needed at all when using channel patterns.
  • Centrifugo only allows explicit channel pattern matching which do not result into channel pattern conflicts in runtime, this is checked during configuration validation on server start. Explicitly defined static patterns (without variables) have precedence over patterns with variables.
  • There is no analogue of top-level namespace (like we have for standard namespace configuration) for channels starting with /. If a channel does not match any explicitly defined pattern then Centrifugo returns the 102: unknown channel error.
  • If you define channel_regex inside channel pattern options – then regex matches over the entire channel (since variable parts are located in the namespace name in this case).
  • Channel pattern must only contain ASCII characters.
  • Duplicate variable names are not allowed inside an individual pattern, i.e. defining /users/:user/:user will result into validation error on start.

Variables

: in the channel pattern name helps to define a variable to match against. Named parameters only match a single segment of the channel:

Channel pattern "/users/:name":

/users/mary ✅ match
/users/john ✅ match
/users/mary/info ❌ no match
/users ❌ no match

Another example for channel pattern /news/:type/:subtype, i.e. with multiple variables:

Channel pattern "/news/:type/:subtype":

/news/sport/football ✅ match
/news/sport/volleyball ✅ match
/news/sport ❌ no match
/news ❌ no match

Channel patterns support mid-segment variables, so the following is possible:

Channel pattern "/personal/user_:user":

/personal/user_mary ✅ match
/personal/user_john ✅ match
/personal/user_ ❌ no match

Using varibles

Additional benefits of using channel patterns may be achieved together with Centrifugo PRO CEL expressions. Channel pattern variables are available inside CEL expressions for evaluation in a custom way.

- + \ No newline at end of file diff --git a/docs/pro/channel_state_events.html b/docs/pro/channel_state_events.html index d3b3b05d7..bc589352a 100644 --- a/docs/pro/channel_state_events.html +++ b/docs/pro/channel_state_events.html @@ -16,13 +16,13 @@ - +

Channel state events

Centrifugo PRO has a feature to enable channel state event webhooks to be sent to your configured backend endpoint:

  • channel occupied event - called whenever first subscriber occipies the channel
  • channel vacated event - called whenever last subscriber leaves the channel
Preview state

This feature is in the preview state now. We still need some time before it will be ready for usage in production. But starting from Centrifugo PRO v5.1.1 the feature is avalable for evaluation.

To enable the feature you must use redis engine. Also, only channels with presence enabled and channel_state_events explicitly configured may deliver channel state notifications. When enabling channel state proxy Centrifugo PRO starts using another approach to create Redis keys for presence for namespaces where channel state events enabled, this is an important implementation detail.

So the minimal config can look like this (occupied and vacated events for channels in chat namespace will be sent to proxy_channel_state_endpoint):

config.json
{
...
"engine": "redis",
"proxy_channel_state_endpoint": "http://localhost:3000/centrifugo/channel_events",
"namespaces": [
{
"name": "chat",
"presence": true,
"channel_state_events": ["occupied", "vacated"]
}
]
}

The proxy endpoint is an extension of Centrifugo OSS proxy and proto definitions may be found in the same proxy.proto file - see NotifyChannelState rpc. Example of the payload your backend request handler will receive:

{
"events": [
{"channel": "chat:index", "type": "occupied", "time_ms": 1697206286533},
]
}

Payload may contain a batch of events, that's why events is an array – this is important for achieving a high event throughput. Your backend must be fast enough to keep up with the events rate and volume, otherwise event queues will grow and eventually new events will be dropped by Centrifugo PRO.

Respond with empty object without error set to let Centrifugo PRO know that events were processed successfully.

If last channel client resubscribes to a channel fast (during 5 secs) – then Centrifugo PRO won't send vacated events. If client does not resubscribe during 5 secs - event will be sent. So vacated events always delivered with a delay. This is implemented in such way to avoid unnecessary webhooks for quick reconnect scenarios.

Centrifugo PRO does the best effort delivering channel state events, making retries when the backend endpoint is unavailable (with exponential backoff), also survives cases when Centrifugo node dies unexpectedly. But there are rare scenarios, when notifications may be lost – like when data lost in Redis. For such cases we recommend syncing the state periodically looking at channel presence information using server API – this depends on the use case though.

- + \ No newline at end of file diff --git a/docs/pro/client_message_batching.html b/docs/pro/client_message_batching.html index 88e5f8892..8fb69f048 100644 --- a/docs/pro/client_message_batching.html +++ b/docs/pro/client_message_batching.html @@ -16,13 +16,13 @@ - +

Message batching control

Centrifugo PRO provides advanced options to tweak connection message write behaviour.

By default, Centrifugo tries to write messages to clients as fast as possible. Centrifugo also does best effort combining different protocol messages into one transport frame (to reduce system calls and thus reduce CPU usage) without sacrificing delivery latency.

But still in this model if you have a lot of messages sent to each individual connection, you may have a lot of write system calls. These system calls have an huge impact on the server CPU utilization. Sometimes you want to trade-off delivery latency in favour of lower CPU consumption by Centrifugo node. It's possible to do by telling Centrifugo to slow down message delivery and collect messages to larger batches before sending them towards individual client. To achieve that Centrifugo PRO exposes additional configuration options.

tip

Note, this is only useful when you have lots of messages per client. This specific feature won't be helpful with a case when the message is broadcasted towards many different connections as the feature described here only batches message writing it terms of a single socket.

client_write_delay

The client_write_delay is a duration option, it is a time Centrifugo will try to collect messages inside each connection message write loop before sending them towards the connection.

Enabling client_write_delay may reduce CPU usage of both server and client in case of high message rate inside individual connections. The reduction happens due to the lesser number of system calls to execute. Enabling client_write_delay limits the maximum throughput of messages towards the connection which may be achieved. For example, if client_write_delay is 100ms then the max throughput per second will be (1000 / 100) * client_max_messages_in_frame (16 by default), i.e. 160 messages per second. Though this should be more than enough for target Centrifugo use cases (frontend apps).

Example:

config.json
{
// Rest of config here ...
"client_write_delay": "100ms"
}

client_reply_without_queue

The client_reply_without_queue is a boolean option to not use client queue for replies to commands. When true replies are written to the transport without going through the connection message queue.

client_max_messages_in_frame

The client_max_messages_in_frame is an integer option which controls the maximum number of messages which may be joined by Centrifugo into one transport frame. By default, 16. Use -1 for unlimited number.

- + \ No newline at end of file diff --git a/docs/pro/connections.html b/docs/pro/connections.html index b4f2a82fd..e9cd07d17 100644 --- a/docs/pro/connections.html +++ b/docs/pro/connections.html @@ -16,13 +16,13 @@ - +

Connections API

Centrifugo PRO offers an extra API call, connections, which enables retrieval of all active sessions (based on user ID or expression) without the need to activate the presence feature for channels. Furthermore, developers can attach any desired JSON payload to a connection that will then be visible in the result of the connections call. It's worth noting that this additional meta-information remains hidden from the client-side, unlike the info associated with the connection.

This feature serves a valuable purpose in managing active user sessions, particularly for messenger applications. Users can review their current sessions and terminate some of them using the Centrifugo disconnect server API.

Moreover, this feature can help developers investigate issues by providing insights into the system's state.

Example

Let's look at the quick example. First, generate a JWT for user 42:

$ centrifugo genconfig

Generate token for some user to be used in the example connections:

$ centrifugo gentoken -u 42
HMAC SHA-256 JWT for user 42 with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y

Run Centrifugo with uni_http_stream transport enabled (it will allow us connecting from the terminal with curl):

CENTRIFUGO_UNI_HTTP_STREAM=1 centrifugo -c config.json

Create new terminal window and run:

curl -X POST http://localhost:8000/connection/uni_http_stream --data '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y", "name": "terminal"}'

In another terminal create one more connection:

curl -X POST http://localhost:8000/connection/uni_http_stream --data '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y", "name": "terminal"}'

Now let's call connections over HTTP API:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"user": "42"}' \
http://localhost:8000/api/connections

The result:

{
"result": {
"connections": {
"db8bc772-2654-4283-851a-f29b888ace74": {
"app_name": "terminal",
"transport": "uni_http_stream",
"protocol": "json"
},
"4bc3ca70-ecc5-439d-af14-a78ae18e31c7": {
"app_name": "terminal",
"transport": "uni_http_stream",
"protocol": "json"
}
}
}
}

Here we can see that user has 2 connections from terminal app.

Each connection can be annotated with meta JSON information which is set during connection establishment (over meta claim of JWT or by returning meta in the connect proxy result).

connections

Returns information about active connections according to the request.

connections params

Parameter nameParameter typeRequiredDescription
userstringnofast filter by User ID
expressionstringnoCEL expression to filter users

connections result

Field nameField typeOptionalDescription
connectionsmap[string]ConnectionInfonoactive user connections map where key is client ID and value is ConnectionInfo

ConnectionInfo

Field nameField typeOptionalDescription
app_namestringyesclient app name (if provided by client)
app_versionstringyesclient app version (if provided by client)
transportstringnoclient connection transport
protocolstringnoclient connection protocol (json or protobuf)
userstringyesclient user ID
stateConnectionStateyesconnection state

ConnectionState object

Field nameField typeOptionalDescription
channelsmap[string]ChannelContextyesChannels client subscribed to
connection_tokenConnectionTokenInfoyesinformation about connection token
subscription_tokensmap<string, SubscriptionTokenInfo>yesinformation about channel tokens used to subscribe
metaJSON objectyesmeta information attached to a connection

ChannelContext object

Field nameField typeOptionalDescription
sourceintyesThe source of channel subscription

ConnectionTokenInfo object

Field nameField typeOptionalDescription
uidstringyesunique token ID (jti)
issued_atintyestime (Unix seconds) when token was issued

SubscriptionTokenInfo object

Field nameField typeOptionalDescription
uidstringyesunique token ID (jti)
issued_atintyestime (Unix seconds) when token was issued
- + \ No newline at end of file diff --git a/docs/pro/distributed_rate_limit.html b/docs/pro/distributed_rate_limit.html index eac26ae10..4ba993b40 100644 --- a/docs/pro/distributed_rate_limit.html +++ b/docs/pro/distributed_rate_limit.html @@ -16,13 +16,13 @@ - +

Distributed rate limit API

In addition to connection operation rate limiting features Centrifugo PRO provides a generic high precision rate limiting API. It may be used for custom quota managing tasks not even related to real-time connections. Its distributed nature allows managing quotas across different instances of your application backend.

The original reason why we decided to ship this as part of our PRO version APIs was the desire to simplify our PRO users the implementation of per-user push notification limits when using Push Notification API. But you are free to use the API for other custom needs as well.

Overview

Centrifugo distributed rate limiting is a high performance zero-configuration Redis-based token bucket with milliseconds precision. Zero configuration in this case means that you don't have to preconfigure buckets in Centrifugo – bucket configuration is a part of request to check allowed limits.

curl -X POST http://localhost:8000/api/rate_limit \
-H "Authorization: apikey <KEY>" \
-d @- <<'EOF'

{
"key": "rate_limit_test",
"interval": 60000,
"rate": 10
}
EOF

Example result:

{
"result": {
"allowed": true,
"tokens_left": 9
}
}

Or, when no tokens left in a bucket:

{
"result": {
"allowed": false,
"tokens_left": 0,
"allowed_in": 5208,
"server_time": 1694627573210,
}
}

In your app code call rate_limit API of Centrifugo PRO every time some action is executed and check allowed flag to allow or discard the action.

Centrifugo PRO also returns allowed_in and server_time fields to help understanding when action will be allowed. These two fields are only appended when tokens_left are less than requested score. allowed_in + server_time will provide you a timestamp in the future (in milliseconds) when action is possible to be executed. So you can delay next action execution till that time if possible.

Configuration

To enable distributed rate limiter:

config.json
{
...
"distributed_rate_limit": {
"enabled": true,
"redis_address": "localhost:6379"
}
}

Note, that just like most of other features in Centrifugo it's possible to configure Redis shards here or use Redis Cluster.

API description

Now let's look at API description.

rate_limit request

FieldTypeRequiredDescription
keystringYesKey for a bucket - you can construct keys whatever way you like
intervalintegerYesInterval in milliseconds
rateintegerYesAllowed rate per provided interval
scoreintegerNoScore for the current action, if not provided the default score 1 is used

rate_limit result

Field NameTypeRequiredDescription
allowedboolYesWhether desired action is allowed at this point in time
tokens_leftintegerYesHow many tokens left in a bucket
allowed_inintegerNoMilliseconds till desired score will be allowed again
server_timeintegerNoServer time as Unix epoch in milliseconds used to calculate result
- + \ No newline at end of file diff --git a/docs/pro/install_and_run.html b/docs/pro/install_and_run.html index 1090324a2..fbdaf6da4 100644 --- a/docs/pro/install_and_run.html +++ b/docs/pro/install_and_run.html @@ -16,13 +16,13 @@ - +
-

Install and run PRO version

Centrifugo PRO license agreement

Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.

Binary release

Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).

If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack centrifugo binary to your current working directory:

curl -sSLf https://centrifugal.dev/install_pro.sh | sh

Docker image

Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro:

docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v5.1.1 centrifugo -c config.json

Kubernetes

You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag:

values.yaml
...
image:
registry: docker.io
repository: centrifugo/centrifugo-pro
tag: v5.1.1

Debian and Ubuntu

DEB package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.1/centrifugo-pro_5.1.1_amd64.deb
sudo dpkg -i centrifugo-pro_5.1.1_amd64.deb

Centos

RPM package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.1/centrifugo-pro-5.1.1.x86_64.rpm
sudo yum install centrifugo-pro-5.1.1.x86_64.rpm

Setting PRO license key

Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits.

Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this:

config.json
{
...
"license": "<YOUR_LICENSE_KEY>"
}
tip

If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.

- +

Install and run PRO version

Centrifugo PRO license agreement

Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.

Binary release

Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).

If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack centrifugo binary to your current working directory:

curl -sSLf https://centrifugal.dev/install_pro.sh | sh

Docker image

Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro:

docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v5.1.2 centrifugo -c config.json

Kubernetes

You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag:

values.yaml
...
image:
registry: docker.io
repository: centrifugo/centrifugo-pro
tag: v5.1.2

Debian and Ubuntu

DEB package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.2/centrifugo-pro_5.1.2_amd64.deb
sudo dpkg -i centrifugo-pro_5.1.2_amd64.deb

Centos

RPM package available in release assets.

wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.2/centrifugo-pro-5.1.2.x86_64.rpm
sudo yum install centrifugo-pro-5.1.2.x86_64.rpm

Setting PRO license key

Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits.

Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this:

config.json
{
...
"license": "<YOUR_LICENSE_KEY>"
}
tip

If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.

+ \ No newline at end of file diff --git a/docs/pro/observability_enhancements.html b/docs/pro/observability_enhancements.html index 45ac9a42a..e08e1f7b5 100644 --- a/docs/pro/observability_enhancements.html +++ b/docs/pro/observability_enhancements.html @@ -16,13 +16,13 @@ - +

Observability enhancements

Centrifugo PRO has some enhancements to exposed metrics. At this moment it provides channel namespace resolution to the following metrics:

  • centrifugo_transport_messages_sent
  • centrifugo_transport_messages_sent_size
  • centrifugo_transport_messages_received
  • centrifugo_transport_messages_received_size

Since channel namespace resolution may add some overhead (though negligible in most cases), Centrifugo PRO requires it to be explicitly enabled using two boolean config options (available since Centrifugo PRO v5.1.1):

config.json
{
...
"channel_namespace_for_transport_messages_sent": true,
"channel_namespace_for_transport_messages_received": true
}
  • First option channel_namespace_for_transport_messages_sent enables channel namespace label for:
    • centrifugo_transport_messages_sent
    • centrifugo_transport_messages_sent_size
  • Second option channel_namespace_for_transport_messages_received enables for:
    • centrifugo_transport_messages_received
    • centrifugo_transport_messages_received_size.
- + \ No newline at end of file diff --git a/docs/pro/overview.html b/docs/pro/overview.html index 17a513d11..6b803186a 100644 --- a/docs/pro/overview.html +++ b/docs/pro/overview.html @@ -16,13 +16,13 @@ - +

Centrifugo PRO

Centrifugo PRO is the enhanced version of Centrifugo provided by Centrifugal Labs LTD under commercial license. It's packed with a set of unique features that offer exceptional benefits to your business. It provides granular channel permission control, lower CPU utilization on Centrifugo nodes, backend protection from misusing, next level system observability, additional APIs (like push notifications), and more.

All the features of Centrifugo PRO come with a decent scalable performance. Some reuse Centrifugo super fast Redis communication capabilities. ClickHouse analytics built on top of efficient approach with the minimal overhead. We've put a lot of love into all of the extra powers of Centrifugo to make sure they are practical and ready for production workloads.

Features

Centrifugo PRO is packed with the following features:

Also, explore our Centrifugo PRO planned features board for a concise overview of upcoming features which are currently in progress and enhancements planned for a future.

Pricing

Centrifugo PRO requires a license key to run.

The price for a license key is 3,499 EUR. A license key has an expiration date — six months since the purchase. Once your key expires, Centrifugo PRO will continue to work, but you will lose access to updates for versions released after the expiration date of your key. This model is like subscription, yet provides the flexibility to delay subsequent payments until an upgrade is required.

Our services are exclusively available to corporate and business clients at this time. To purchase the license key, please reach out to us at sales@centrifugal.dev. We kindly ask that your email includes specific information about your company, such as its name and address. We would also value a brief description of how you plan to utilize Centrifugo PRO.

The purchase is just an online payment by card (or other available method) in Centrifugal Labs store on Lemon Squeezy platform. We will share a payment link in response to your request – and everything else is automated.

Try for free in sandbox mode

You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example:

  • Centrifugo handles up to 20 concurrent connections
  • up to 2 server nodes supported
  • up to 5 API requests per second allowed

This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future.

Centrifugo PRO license agreement

Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.

- + \ No newline at end of file diff --git a/docs/pro/performance.html b/docs/pro/performance.html index c0ff8240d..57c3036f4 100644 --- a/docs/pro/performance.html +++ b/docs/pro/performance.html @@ -16,13 +16,13 @@ - +

Faster performance

Centrifugo PRO has performance improvements for several server parts. These improvements can help to reduce tail end-to-end latencies in the application, increase server throughput and/or reduce CPU usage on server machines. Our open-source version has a decent performance by itself, with PRO improvements Cenrifugo steps even further.

Faster HTTP API

Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API.

The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.

Faster GRPC API

Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster HTTP proxy

Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster GRPC proxy

Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.

Faster JWT decoding

Centrifugo PRO has an optimized decoding of JWT claims.

Faster GRPC unidirectional stream

Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.

Examples

Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.

Publish HTTP API

In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.

History HTTP API

In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.

- + \ No newline at end of file diff --git a/docs/pro/process_stats.html b/docs/pro/process_stats.html index 88c7aec83..c7524bcf3 100644 --- a/docs/pro/process_stats.html +++ b/docs/pro/process_stats.html @@ -16,13 +16,13 @@ - + - + \ No newline at end of file diff --git a/docs/pro/push_notifications.html b/docs/pro/push_notifications.html index 19a8ed900..3b854dd7b 100644 --- a/docs/pro/push_notifications.html +++ b/docs/pro/push_notifications.html @@ -16,13 +16,13 @@ - +

Push notification API

Centrifugo excels in delivering real-time in-app messages to online users. Sometimes though you need a way to engage offline users to come back to your app. Or trigger some update in the app while it's running in the background. That's where push notifications may be used. Push notifications delivered over battery-efficient platform-dependent transport.

With Centrifugo PRO push notifications may be delivered to all popular application platforms:

  • Android devices
  • iOS devices
  • Web browsers which support Web Push API (Chrome, Firefox, see this matrix)

Centrifugo PRO provides API to manage user device tokens, device topic subscriptions and API to send push notifications towards registered devices and group of devices (subscribed to a topic).

Push

To deliver push notifications to devices Centrifugo PRO integrates with the following providers:

FCM, HMS, APNs handle the frontend and transport aspects of notification delivery. Device token storage, management and efficient push notification broadcasting is managed by Centrifugo PRO. Tokens are stored in a PostgreSQL database. To facilitate efficient push notification broadcasting towards devices, Centrifugo PRO includes worker queues based on Redis streams (and also provides and option to use PostgreSQL-based queue).

Integration with FCM means that you can use existing Firebase messaging SDKs to extract push notification token for a device on different platforms (iOS, Android, Flutter, web browser) and setting up push notification listeners. The same for HMS and APNs - just use existing native SDKs and best practices on the frontend. Only a couple of additional steps required to integrate frontend with Centrifugo PRO device token and device topic storage. After doing that you will be able to send push notification towards single device, or towards group of devices subscribed to a topic. For example, with a simple Centrifugo API call like this:

curl -X POST http://localhost:8000/api/send_push_notification \
-H "Authorization: apikey <KEY>" \
-d @- <<'EOF'

{
"recipient": {
"filter": {
"topics": ["test"]
}
},
"notification": {
"fcm": {
"message": {
"notification": {"title": "Hello", "body": "How are you?"}
}
}
}
}
EOF

In addition, Centrifugo PRO includes a helpful web UI for inspecting registered devices and sending push notifications:

Motivation and design choices

We tried to be practical with our Push Notification API, let's look at its design choices and implementation properties we were able to achieve.

Storage for tokens

To start delivering push notifications in the application, developers usually need to integrate with providers such as FCM, HMS, and APNs. This integration typically requires the storage of device tokens in the application database and the implementation of sending push messages to provider push services.

Centrifugo PRO simplifies the process by providing a backend for device token storage, following best practices in token management. It reacts to errors and periodically removes stale devices/tokens to maintain a working set of device tokens based on provider recommendations.

Efficient queuing

Additionally, Centrifugo PRO provides an efficient, scalable queuing mechanism for sending push notifications. Developers can send notifications from the app backend to Centrifugo API with minimal latency and let Centrifugo process sending to FCM, HMS, APNs concurrently using built-in workers. In our tests, we achieved several millions pushes per minute.

Centrifugo PRO also supports delayed push notifications feature – to queue push for a later delivery, so for example you can send notification based on user time zone and let Centrifugo PRO send it when needed.

Unified secure topics

FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that client can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM, HMS topics by introducing an additional API to manage device subscriptions to topics.

tip

In some cases you may have real-time channels and device subscription topics with matching names – to send messages to both online and offline users. Though it's up to you.

Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs.

Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. Then – as soon as user registers a device – it will be automatically subscribed to its own topics. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with FCM – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call.

Non-obtrusive proxying

Unlike other solutions that combine different provider push sending APIs into a unified API, Centrifugo PRO provides a non-obtrusive proxy for all the mentioned providers. Developers can send notification payloads in a format defined by each provider.

It's also possible to send notifications into native FCM, HMS topics or send to raw FCM, HMS, APNs tokens using Centrifugo PRO's push API, allowing them to combine native provider primitives with those added by Centrifugo (i.e., sending to a list of device IDs or to a list of topics).

Builtin analytics

Furthermore, Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API.

Steps to integrate

  1. Add provider SDK on the frontend side, follow provider instructions for your platform to obtain a push token for a device. For example, for FCM see instructions for iOS, Android, Flutter, Web Browser). The same for HMS or APNs – frontend part should be handled by their native SDKs.
  2. Call Centrifugo PRO backend API with the obtained token. From the application backend call Centrifugo device_register API to register the device in Centrifugo PRO storage. Optionally provide list of topics to subscribe device to.
  3. Centrifugo returns a registered device object. Pass a generated device ID to the frontend and save it on the frontend together with a token received from FCM.
  4. Call Centrifugo send_push_notification API whenever it's time to deliver a push notification.

At any moment you can inspect device storage by calling device_list API.

Once user logs out from the app, you can detach user ID from device by using device_update or remove device with device_remove API.

Configuration

In Centrifugo PRO you can configure one push provider or use all of them – this choice is up to you.

FCM

As mentioned above Centrifigo uses PostgreSQL for token storage. To enable push notifications make sure database section defined in the configration and fcm is in the push_notifications.enabled_providers list. Centrifugo PRO uses Redis for queuing push notification requests, so Redis address should be configured also. Finally, to integrate with FCM a path to the credentials file must be provided (see how to create one in this instruction). So the full configuration to start sending push notifications over FCM may look like this:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"redis_address": "localhost:6379",
"enabled_providers": ["fcm"],
"fcm_credentials_file_path": "/path/to/service/account/credentials.json"
}
}
tip

Actually, PostgreSQL database configuration is optional here – you can use push notifications API without it. In this case you will be able to send notifications to FCM, HMS, APNs raw tokens, FCM and HMS native topics and conditions. I.e. using Centrifugo as an efficient proxy for push notifications (for example if you already keep tokens in your database). But sending to device ids and topics, and token/topic management APIs won't be available for usage.

HMS

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"redis_address": "localhost:6379",
"enabled_providers": ["hms"],
"hms_app_id": "<your_app_id>",
"hms_app_secret": "<your_app_secret>",
}
}
tip

See example how to get app id and app secret here.

APNs

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"redis_address": "localhost:6379",
"enabled_providers": ["apns"],
"apns_endpoint": "development",
"apns_bundle_id": "com.example.your_app",
"apns_auth": "token",
"apns_token_auth_key_path": "/path/to/auth/key/file.p8",
"apns_token_key_id": "<your_key_id>",
"apns_token_team_id": "your_team_id",
}
}

We also support auth over p12 certificates with the following options:

  • push_notifications.apns_cert_p12_path
  • push_notifications.apns_cert_p12_b64
  • push_notifications.apns_cert_p12_password

Other options

push_notifications.max_inactive_device_days

This integer option configures the number of days to keep device without updates. By default Centrifugo does not remove inactive devices.

push_notifications.enable_redis_delayed_scheduler

Boolean option which enables Redis scheduler to process delayed push notifications. It's off by default since produces additional requests to Redis. When using PostgreSQL as push notifications queue engine you don't need to enable sheduler explicitly.

push_notifications.dry_run

Boolean option, when true Centrifugo PRO does not send push notifications to FCM, APNs, HMS providers but instead just print logs. Useful for development.

push_notifications.dry_run_latency

Duration. When set together with push_notifications.dry_run every dry-run request will cause some delay in workers emulating real-world latency. Useful for development.

Use PostgreSQL as queue

Centrifugo PRO utilizes Redis Streams as the default queue engine for push notifications. However, it also offers the option to employ PostgreSQL for queuing. It's as simple as:

config.json
{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"push_notifications": {
"queue_engine": "database",
// rest of the options...
}
}
tip

Queue based on Redis streams is generally more efficient, so if you start with PostgreSQL based queue – you have an option to switch to a more performant implementation later. Though in-flight and currently queued push notifications will be lost during a switch.

API description

device_register

Registers or updates device information.

device_register request

FieldTypeRequiredDescription
idstringNoID of the device being registered (provide it when updating).
providerstringYesProvider of the device token (valid choices: fcm, hms, apns).
tokenstringYesPush notification token for the device.
platformstringYesPlatform of the device (valid choices: ios, android, web).
userstringNoUser associated with the device.
topicsarray of stringsNoDevice topic subscriptions. This should be a full list which replaces all the topics previously accociated with the device. User topics managed by UserTopic model will be automatically attached.
metamap<string, string>NoAdditional custom metadata for the device

device_register result

Field NameTypeRequiredDescription
idstringYesThe device ID that was registered/updated.

device_update

Call this method to update device. For example, when user logs out the app and you need to detach user ID from the device.

device_update request

FieldTypeRequiredDescription
idsrepeated stringNoDevice ids to filter
usersrepeated stringNoDevice users filter
user_updateDeviceUserUpdateNoOptional user update object
meta_updateDeviceMetaUpdateNoOptional device meta update object
topics_updateDeviceTopicsUpdateNoOptional topics update object

DeviceUserUpdate:

FieldTypeRequiredDescription
userstringYesUser to set

DeviceMetaUpdate:

FieldTypeRequiredDescription
metamap<string, string>YesMeta to set

DeviceTopicsUpdate:

FieldTypeRequiredDescription
opstringYesOperation to make: add, remove or set
topicsrepeated stringYesTopics for the operation

device_update result

Empty object.

device_remove

Removes device from storage. This may be also called when user logs out the app and you don't need its device token after that.

device_remove request

Field NameTypeRequiredDescription
idsrepeated stringNoA list of device IDs to be removed
usersrepeated stringNoA list of device user IDs to filter devices to remove

device_remove result

Empty object.

device_list

Returns a paginated list of registered devices according to request filter conditions.

device_list request

FieldTypeRequiredDescription
filterDeviceFilterYesHow to filter results
cursorstringNoCursor for pagination (last device id in previous batch, empty for first page).
limitint32NoMaximum number of devices to retrieve.
include_total_countboolNoFlag indicating whether to include total count for the current filter.
include_topicsboolNoFlag indicating whether to include topics information for each device.
include_metaboolNoFlag indicating whether to include meta information for each device.

DeviceFilter:

FieldTypeRequiredDescription
idsrepeated stringNoList of device IDs to filter results.
providersrepeated stringNoList of device token providers to filter results.
platformsrepeated stringNoList of device platforms to filter results.
usersrepeated stringNoList of device users to filter results.
topicsrepeated stringNoList of topics to filter results.

device_list result

Field NameTypeRequiredDescription
itemsrepeated DeviceYesA list of devices
next_cursorstringNoCursor string for retreiving the next page, if not set - then no next page exists
total_countintegerNoTotal count value (if include_total_count used)

Device:

Field NameTypeRequiredDescription
idstringYesThe device's ID.
providerstringYesThe device's token provider.
tokenstringYesThe device's token.
platformstringYesThe device's platform.
userstringNoThe user associated with the device.
topicsarray of stringsNoOnly included if include_topics was true
metamap<string, string>NoOnly included if include_meta was true

device_topic_update

Manage mapping of device to topics.

device_topic_update request

FieldTypeRequiredDescription
device_idstringYesDevice ID.
opstringYesadd or remove or set
topicsrepeated stringNoList of topics.

device_topic_update result

Empty object.

device_topic_list

List device to topic mapping.

device_topic_list request

FieldTypeRequiredDescription
filterDeviceTopicFilterNoList of device IDs to filter results.
cursorstringNoCursor for pagination (last device id in previous batch, empty for first page).
limitint32NoMaximum number of devices to retrieve.
include_deviceboolNoFlag indicating whether to include Device information for each object.
include_total_countboolNoFlag indicating whether to include total count info to response.

DeviceTopicFilter:

FieldTypeRequiredDescription
device_idsrepeated stringNoList of device IDs to filter results.
device_providersrepeated stringNoList of device token providers to filter results.
device_platformsrepeated stringNoList of device platforms to filter results.
device_usersrepeated stringNoList of device users to filter results.
topicsrepeated stringNoList of topics to filter results.
topic_prefixstringNoTopic prefix to filter results.

device_topic_list result

Field NameTypeRequiredDescription
itemsrepeated DeviceTopicYesA list of DeviceChannel objects
next_cursorstringNoCursor string for retreiving the next page, if not set - then no next page exists
total_countintegerNoTotal count value (if include_total_count used)

DeviceTopic:

FieldTypeRequiredDescription
idstringYesID of DeviceTopic object
device_idstringYesDevice ID
topicstringYesTopic

user_topic_update

Manage mapping of topics with users. These user topics will be automatically attached to user devices upon registering. And removed from device upon deattaching user.

user_topic_update request

FieldTypeRequiredDescription
userstringYesUser ID.
opstringYesadd or remove or set
topicsrepeated stringNoList of topics.

user_topic_update result

Empty object.

user_topic_list

List user to topic mapping.

user_topic_list request

FieldTypeRequiredDescription
flterUserTopicFilterNoFilter object.
cursorstringNoCursor for pagination (last id in previous batch, empty for first page).
limitint32NoMaximum number of UserTopic objects to retrieve.
include_total_countboolNoFlag indicating whether to include total count info to response.

UserTopicFilter:

FieldTypeRequiredDescription
usersrepeated stringNoList of users to filter results.
topicsrepeated stringNoList of topics to filter results.
topic_prefixstringNoChannel prefix to filter results.

user_topic_list result

Field NameTypeDescription
itemsrepeated UserTopicA list of UserTopic objects
next_cursorstringNo
total_countintegerNo

UserTopic:

FieldTypeRequiredDescription
idstringYesID of UserTopic
userstringYesUser ID
topicstringYesTopic

send_push_notification

Send push notification to specific device_ids, or to topics, or native provider identifiers like fcm_tokens, or to fcm_topic. Request will be queued by Centrifugo, consumed by Centrifugo built-in workers and sent to the provider API.

send_push_notification request

Field nameTypeRequiredDescription
recipientPushRecipientYesRecipient of push notification
notificationPushNotificationYesPush notification to send
uidstringNoUnique send id, used for Centrifugo builtin analytics or to cancel delayed push. We recommend using UUID v4 for it
send_atint64NoOptional Unix time in the future (in seconds) when to send push notification, push will be queued until that time.

PushRecipient (you must set only one of the following fields):

FieldTypeRequiredDescription
filterDeviceFilterNoSend to device IDs based on Centrifugo device storage filter
fcm_tokensrepeated stringNoSend to a list of FCM native tokens
fcm_topicstringNoSend to a FCM native topic
fcm_conditionstringNoSend to a FCM native condition
hms_tokensrepeated stringNoSend to a list of HMS native tokens
hms_topicstringNoSend to a HMS native topic
hms_conditionstringNoSend to a HMS native condition
apns_tokensrepeated stringNoSend to a list of APNs native tokens

PushNotification:

FieldTypeRequiredDescription
expire_atint64NoUnix timestamp when Centrifugo stops attempting to send this notification. Note, it's Centrifugo specific and does not relate to notification TTL fields. We generally recommend to always set this to a reasonable value to protect your app from old push notifications sending
fcmFcmPushNotificationNoNotification for FCM
hmsHmsPushNotificationNoNotification for HMS
apnsApnsPushNotificationNoNotification for APNs

FcmPushNotification:

FieldTypeRequiredDescription
messageJSON objectYesFCM Message described in FCM docs.

HmsPushNotification:

FieldTypeRequiredDescription
messageJSON objectYesHMS Message described in HMS Push Kit docs.

ApnsPushNotification:

FieldTypeRequiredDescription
headersmap<string, string>NoAPNs headers
payloadJSON objectYesAPNs payload

send_push_notification result

Field NameTypeDescription
uidstringUnique send id, matches uid in request if it was provided

cancel_push

Cancel delayed push notification (which was sent with custom send_at value).

update_push_status request

FieldTypeRequiredDescription
uidstringYesuid of push notification to cancel

update_push_status result

Empty object.

update_push_status

This API call is experimental, some changes may happen here.

Centrifugo PRO also allows tracking status of push notification delivery and interaction. It's possible to use update_push_status API to save the updated status of push notification to the notifications analytics table. Then it's possible to build insights into push notification effectiveness by querying the table.

The update_push_status API supposes that you are using uid field with each notification sent and you are using Centrifugo PRO generated device IDs (as described in steps to integrate).

This is a part of server API at the moment, so you need to proxy requests to this endpoint over your backend. We can consider making this API suitable for requests from the client side – please reach out if your use case requires it.

update_push_status request

FieldTypeRequiredDescription
uidstringYesuid (unique send id) from send_push_notification
statusstringYesStatus of push notification - delivered or interacted
device_idstringYesDevice ID
msg_idstringNoMessage ID

update_push_status result

Empty object.

Exposed metrics

Several metrics are available to monitor the state of Centrifugo push worker system:

centrifugo_push_notification_count

  • Type: Counter
  • Labels: provider, recipient_type, platform, success, err_code
  • Description: Total count of push notifications.
  • Usage: Helps in tracking the number and success rate of push notifications sent, providing insights for optimization and troubleshooting.

centrifugo_push_queue_consuming_lag

  • Type: Gauge
  • Labels: provider, queue
  • Description: Queue consuming lag in seconds.
  • Usage: Useful for monitoring the delay in processing jobs from the queue, helping identify potential bottlenecks and ensuring timely processing.

centrifugo_push_consuming_inflight_jobs

  • Type: Gauge
  • Labels: provider, queue
  • Description: Number of inflight jobs being consumed.
  • Usage: Helps in tracking the load on the job processing system, ensuring that resources are being utilized efficiently.

centrifugo_push_job_duration_seconds

  • Type: Summary
  • Labels: provider, recipient_type
  • Description: Duration of push processing job in seconds.
  • Usage: Useful for monitoring the performance of job processing, helping in performance tuning and issue resolution.

Further reading and tutorials

Coming soon.

- + \ No newline at end of file diff --git a/docs/pro/rate_limiting.html b/docs/pro/rate_limiting.html index 76b979959..0214b896f 100644 --- a/docs/pro/rate_limiting.html +++ b/docs/pro/rate_limiting.html @@ -16,13 +16,13 @@ - +

Operation rate limits

The rate limit feature allows limiting the number of operations each connection or user can issue during a configured time interval. This is useful to protect the system from misusing, detecting and disconnecting abusive or broken (due to the bug in the frontend application) clients which add unwanted load on a server.

With rate limit properly configured you can protect your Centrifugo installation to some degree without sophisticated third-party solution. Centrifugo PRO protection works best in combination with protection on infrastructure level though.

Throttling

In-memory per connection rate limit

In-memory rate limit is an efficient way to limit number of operations allowed on a per-connection basis – i.e. inside each individual real-time connection. Our rate limit implementation uses token bucket algorithm internally.

The list of operations which can be rate limited on a per-connection level is:

  • subscribe
  • publish
  • history
  • presence
  • presence_stats
  • refresh
  • sub_refresh
  • rpc (with optional method resolution)

In addition, Centrifugo allows defining two special buckets containers:

  • total – define it to limit the total number of commands per interval (all commands sent from client count), these buckets will always be checked if defined, every command from the client always consumes token from total buckets
  • default - define it if you don't want to configure some command buckets explicitly, default buckets will be used in case command buckets is not configured explicitly.
config.json
{
...
"client_command_rate_limit": {
"enabled": true,

"default": {
"buckets": [
{
"interval": "1s",
"rate": 60
},
]
},
"total": {
"buckets": [
{
"interval": "1s",
"rate": 20
},
{
"interval": "60s",
"rate": 50
},
]
},
"publish": {
"buckets": [
{
"interval": "1s",
"rate": 1
},
]
},
"rpc": {
"buckets": [
{
"interval": "1s",
"rate": 10
}
],
"method_override": [
{
"method": "update_user_status",
"buckets": [
{
"interval": "20s",
"rate": 1
}
]
}
]
}
}
}
tip

Centrifugo real-time SDKs written in a way that if client receives an error during connect – it will try to reconnect to a server with backoff algorithm. The same for subscribing to channels (i.e. error from subscribe command) – subscription request will be retried with a backoff. Refresh and subscription refresh will be also retried automatically by SDK upon errors after in several seconds. Retries of other commands should be handled manually from the client side if needed – though usually you should choose rate limit limits in a way that normal users of your app never hit the limits.

In-memory per user rate limit

Another type of rate limit in Centrifugo PRO is a per user ID in-memory rate limit. Like per client rate limit this one is also very efficient since also uses in-memory token buckets. The difference is that instead of rate limit per individual client this type of rate limit takes user ID into account.

This type of rate limit only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be rate limited with it.

The list of operations which can be rate limited is similar to the in-memory rate limit described above. But with additional connect method:

  • total
  • default
  • connect
  • subscribe
  • publish
  • history
  • presence
  • presence_stats
  • refresh
  • sub_refresh
  • rpc (with optional method resolution)

The configuration is very similar:

config.json
{
...
"user_command_rate_limit": {
"enabled": true,

"default": {
"buckets": [
{
"interval": "1s",
"rate": 60
},
]
},
"publish": {
"buckets": [
{
"interval": "1s",
"rate": 1
}
]
},
"rpc": {
"buckets": [
{
"interval": "1s",
"rate": 10
}
],
"method_override": [
{
"method": "update_user_status",
"buckets": [
{
"interval": "20s",
"rate": 1
}
]
}
]
}
}
}

Redis per user rate limit

The next type of rate limit in Centrifugo PRO is a distributed per user ID rate limit with Redis as a bucket state storage. In this case limits are global for the entire Centrifugo cluster. If one user executed two commands on different Centrifugo nodes, Centrifugo consumes two tokens from the same bucket kept in Redis. Since this rate limit goes to Redis to check limits, it adds some latency to a command processing. Our implementation tries to provide good throughput characteristics though – in our tests single Redis instance can handle more than 100k limit check requests per second. And it's possible to scale Redis in the same ways as for Centrifugo Redis Engine.

This type of rate limit only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be rate limited with it. The implementation also uses token bucket algorithm internally.

The list of operations which can be rate limited is similar to the in-memory user command rate limit described above. But without special bucket total:

  • default
  • connect
  • subscribe
  • publish
  • history
  • presence
  • presence_stats
  • refresh
  • sub_refresh
  • rpc (with optional method resolution)

The configuration is very similar:

config.json
{
...
"redis_user_command_rate_limit": {
"enabled": true,
"redis_address": "localhost:6379",

"default": {
"buckets": [
{
"interval": "1s",
"rate": 60
},
]
},
"publish": {
"buckets": [
{
"interval": "1s",
"rate": 1
}
]
},
"rpc": {
"buckets": [
{
"interval": "1s",
"rate": 10
}
],
"method_override": [
{
"method": "update_user_status",
"buckets": [
{
"interval": "20s",
"rate": 1
}
]
}
]
}
}
}

Redis configuration for rate limit feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for rate limit feature too.

It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom rate limit Redis configuration declaration, like this:

config.json
{
...
"engine": "redis",
"redis_address": "localhost:6379",
"redis_user_command_rate_limit": {
"enabled": true,
"use_redis_from_engine": true,
...
}
}

In this case rate limit will simply connect to Redis instances configured for an Engine.

Disconnecting abusive or misbehaving connections

Above we showed how you can define rate limit strategies to protect server resources and prevent execution of many commands inside the connection and from certain user.

But there are scenarios when abusive or broken connections may generate a significant load on the server just by calling commands and getting error responses due to rate limit or due to other reasons (like malformed command). Centrifugo PRO provides a way to configure error limits per connection to deal with this case.

Error limits are configured as in-memory buckets operating on a per-connection level. When these buckets are full due to lots of errors for an individual connection Centrifugo disconnects the client (with advice to not reconnect, so our SDKs may follow it). This way it's possible to get rid of the connection and rely on HTTP infrastracture tools to deal with client reconnections. Since WebSocket or other our transports (except unidirectional GRPC, but it's usually not available to the public port) are HTTP-based (or start with HTTP request in WebSocket Upgrade case) – developers can use Nginx limit_req_zone directive, Cloudflare rules, iptables, and so on, to protect Centrifugo from unwanted connections.

tip

Centrifugo PRO does not count internal errors for the error limit buckets – as internal errors is usually not a client's fault.

The configuration on error limits per connection may look like this:

config.json
{
...
"client_error_limits": {
"enabled": true,
"total": {
"buckets" : [
{
"interval": "5s",
"rate": 20
}
]
}
}
}
- + \ No newline at end of file diff --git a/docs/pro/singleflight.html b/docs/pro/singleflight.html index 06164ece1..d8d74f27d 100644 --- a/docs/pro/singleflight.html +++ b/docs/pro/singleflight.html @@ -16,13 +16,13 @@ - +

Singleflight

Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. It will do this by using in-memory component called singleflight.

Singleflight

tip

While it can seem similar, singleflight is not a cache. It only combines identical parallel requests into one. If requests come one after another – they will be sent separately to the broker or presence storage.

This option can radically reduce a load on a broker in the following situations:

  • Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature)
  • Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position
  • Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously

Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine.

To enable:

config.json
{
...
"use_singleflight": true
}

Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.

- + \ No newline at end of file diff --git a/docs/pro/token_revocation.html b/docs/pro/token_revocation.html index 0d6239c50..c6a095c62 100644 --- a/docs/pro/token_revocation.html +++ b/docs/pro/token_revocation.html @@ -16,13 +16,13 @@ - +

Token revocation API

One more protective instrument in Centrifugo PRO is API to manage token revocations.

At the moment Centrifugo provides two ways to revoke tokens:

  1. Revoke token by ID: based on jti claim in the case of JWT.
  2. Revoke all user's tokens issued before certain time: based on iat in the case of JWT.

When token is revoked client with such token will be disconnected from Centrifugo shortly. And attempt to connect with a revoked token won't succeed.

How it works

By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart.

But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts.

Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.

Configure

Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory.

There are two types of persistent engines supported at the moment:

  1. redis
  2. database

Redis persistence engine

Revocation data can be kept in Redis. To enable this configuration should be:

{
...
"token_revoke": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
},
"user_tokens_invalidate": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
}
}
danger

Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start.

caution

One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.

Database persistence engine

Revocation data can be kept in the relational database. Only PostgreSQL is supported.

To enable this configuration should be like:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"token_revoke": {
"persistence_engine": "database"
},
"user_tokens_invalidate": {
"persistence_engine": "database"
}
}

Revoke token API

revoke_token

Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT).

caution

This API assumes that JWTs you are using contain "jti" claim which is a unique token ID (according to RFC).

Example:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"uid": "xxx-xxx-xxx", "expire_at": 1635845122}' \
http://localhost:8000/api/revoke_token

revoke_token params

Parameter nameParameter typeRequiredDescription
uidstringyesToken unique ID (JTI claim in case of JWT)
expire_atintnoUnix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache).

revoke_token result

Empty object at the moment.

Invalidate user tokens API

invalidate_user_tokens

Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application.

caution

This API assumes that JWTs you are using contain "iat" claim which is a time token was issued at (according to RFC).

Example:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"user": "test", "issued_before": 1635845022, "expire_at": 1635845122}' \
http://localhost:8000/api/invalidate_user_tokens

invalidate_user_tokens params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to invalidate tokens for
issued_beforeintnoAll tokens issued at before this Unix time will be considered revoked (in case of JWT this requires iat to be properly set in JWT), if not provided server uses current time
expire_atintnoUnix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache).

invalidate_user_tokens result

Empty object.

- + \ No newline at end of file diff --git a/docs/pro/tracing.html b/docs/pro/tracing.html index 89a34c86d..55a200acd 100644 --- a/docs/pro/tracing.html +++ b/docs/pro/tracing.html @@ -16,13 +16,13 @@ - +

User and channel tracing

That's a unique thing. The tracing feature of Centrifugo PRO allows attaching to any channel to see all messages flying towards subscribers or attach to a specific user ID to see all user-related events in real-time.

tracing

It's possible to attach to trace streams using Centrifugo admin UI panel or simply from terminal using CURL and admin token.

This can be super-useful for debugging issues, investigating application behavior, understanding that the application works as expected.

Save to a file

It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing.

curl -X POST http://localhost:8000/admin/trace -H "Authorization: token <ADMIN_AUTH_TOKEN>" -d '{"type": "user", "entity": "56"}' -o trace.txt

Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future as PRO version evolves.

- + \ No newline at end of file diff --git a/docs/pro/user_block.html b/docs/pro/user_block.html index 1ade7ad00..e2fe4e18b 100644 --- a/docs/pro/user_block.html +++ b/docs/pro/user_block.html @@ -16,13 +16,13 @@ - +

User blocking API

One additional instrument for making protective actions in Centrifugo PRO is user blocking API which allows blocking a specific user on Centrifugo level.

When user is blocked it will be disconnected from Centrifugo immediately and also on the next connect attempt right after JWT decoded (so that Centrifugo got a user ID) or after result from connect proxy received. In case of using connect proxy you can actually disconnect user yourself by implementing blocking check on the application backend side – but possibility to block user in Centrifugo can still be helpful.

How it works

By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart.

But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts.

Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.

Configure

User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine.

There are two types of persistent engines supported at the moment:

  1. redis
  2. database

Redis persistence engine

Blocking data can be kept in Redis. To enable this configuration should be:

{
...
"user_block": {
"persistence_engine": "redis",
"redis_address": "localhost:6379"
}
}
danger

Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start.

caution

One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.

Database persistence engine

Blocking data can be kept in the relational database. Only PostgreSQL is supported.

To enable this configuration should be like:

{
...
"database": {
"dsn": "postgresql://postgres:pass@127.0.0.1:5432/postgres"
},
"user_block": {
"persistence_engine": "database"
}
}
tip

To quickly start local PostgreSQL database:

docker run -it --rm -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:15

Block API

block_user

Example:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"user": "2695", "expire_at": 1635845122}' \
http://localhost:8000/api/block_user

block_user params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to block
expire_atintnoUnix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache).

block_user result

Empty object at the moment.

unblock_user

Example:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"user": "2695"}' \
http://localhost:8000/api/unblock_user

unblock_user params

Parameter nameParameter typeRequiredDescription
userstringyesUser ID to unblock

unblock_user result

Empty object at the moment.

- + \ No newline at end of file diff --git a/docs/pro/user_status.html b/docs/pro/user_status.html index 315b090dd..40f2b05fa 100644 --- a/docs/pro/user_status.html +++ b/docs/pro/user_status.html @@ -16,13 +16,13 @@ - +

User status API

Centrifugo OSS provides a presence feature for channels. It works well (for channels with reasonably small number of active subscribers though), but sometimes you may need a bit different functionality.

What if you want to get a specific user status based on its recent activity in application? You can create a personal channel with a presence enabled for each user. It will show that user has an active connection with a server. But this won't show whether user did some actions in an application recently or just left it open while not actually using it.

user status

User status feature of Centrifugo PRO allows calling a special RPC method from a client side when a user makes a useful action in an application (clicks on buttons, uses a mouse – whatever means that user really uses application at the moment). This call sets a time of last user activity in Redis, and this information can then be queried over Centrifugo PRO server API.

The feature can be useful for chat applications when you need to get online/activity status for a list of buddies (Centrifugo supports batch requests to user status information – i.e. ask for many users in one call).

Client-side status update RPC

Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript:

await centrifuge.rpc('update_user_status', {});
note

Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example.

This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.

update_user_status server API

It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports):

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"users": ["42"]}' \
http://localhost:8000/api/update_user_status

Update user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to update status for

Update user status result

Empty object at the moment.

get_user_status server API

Now on a backend side you have access to a bulk API to effectively get status of particular users.

Call RPC method of server API (over HTTP or GRPC):

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"users": ["42"]}' \
http://localhost:8000/api/get_user_status

You should get a response like this:

{
"result":{
"statuses":[
{
"user":"42",
"active":1627107289,
"online":1627107289
}
]
}
}

In case information about last status update time not available the response will be like this:

{
"result":{
"statuses":[
{
"user":"42"
}
]
}
}

I.e. status object will present in a response but active field won't be set for status object.

Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time).

Get user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to get status for

Get user status result

Field nameField typeOptionalDescription
statusesarray of UserStatusnoStatuses for each user in params (same order)

UserStatus

Field nameField typeOptionalDescription
userstringnoUser ID
activeintegeryesLast active time (Unix seconds)
onlineintegeryesLast online time (Unix seconds)

delete_user_status server API

If you need to clear user status information for some reason there is a delete_user_status server API call:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"users": ["42"]}' \
http://localhost:8000/api/delete_user_status

Delete user status params

Parameter nameParameter typeRequiredDescription
usersarray of stringsyesList of users to delete status for

Delete user status result

Empty object at the moment.

Configuration

To enable Redis user status feature:

config.json
{
...
"user_status": {
"enabled": true,
"redis_address": "127.0.0.1:6379"
}
}

Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too.

It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this:

config.json
{
...
"engine": "redis",
"redis_address": "localhost:6379",
"user_status": {
"enabled": true,
"use_redis_from_engine": true,
}
}

In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine.

expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day:

config.json
{
...
"user_status": {
...
"expire_interval": "24h"
}
}
- + \ No newline at end of file diff --git a/docs/server/admin_web.html b/docs/server/admin_web.html index 6332f3638..50723616a 100644 --- a/docs/server/admin_web.html +++ b/docs/server/admin_web.html @@ -16,13 +16,13 @@ - +

Admin web UI

Centrifugo comes with a built-in administrative web interface. It enables users to:

  • Display general information and statistics from server nodes - including the number of connections, unique users, subscriptions, and unique channels, among others
  • Execute commands such as publish, broadcast, subscribe, unsubscribe, disconnect, history, history_remove, presence, presence_stats, info, channels, along with several additional Centrifugo PRO server API commands.
  • Trace connections in real-time (a feature Centrifugo PRO).
  • View analytics widgets (a feature of Centrifugo PRO).
  • Visualize registered devices for push notifications (a feature of Centrifugo PRO).

To activate the administrative web interface, run Centrifugo with the admin option enabled and configure security settings in the configuration file:

config.json
{
...
"admin": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>"
}

Options

  • admin (boolean, default: false) – enables/disables admin web UI
  • admin_password (string, default: "") – this is a password to log into admin web interface
  • admin_secret (string, default: "") - this is a secret key for authentication token set on successful login.

Make both admin_password and admin_secret strong and keep them in secret.

After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface.

tip

Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.

Admin web panel

Using custom web interface

If you want to use custom web interface you can specify path to web interface directory dist:

config.json
{
...,
"admin": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>",
"admin_web_path": "<PATH_TO_WEB_DIST>"
}

This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.

Admin insecure mode

There is also an option to run Centrifugo in insecure admin mode. In this mode, it's unnecessary to set admin_password and admin_secret in the configuration – you will be automatically logged into the web interface without any password. Note that this mode should only be considered for production if you have protected the admin web interface with firewall rules. Without such protection, anyone on the internet would have full access to the admin functionalities described above. To enable insecure admin mode:

config.json
{
...,
"admin": true,
"admin_insecure": true,
"admin_password": "<PASSWORD>",
"admin_secret": "<SECRET>"
}
- + \ No newline at end of file diff --git a/docs/server/authentication.html b/docs/server/authentication.html index 4b8a98cac..e08d014a5 100644 --- a/docs/server/authentication.html +++ b/docs/server/authentication.html @@ -16,13 +16,13 @@ - +

Client JWT authentication

To authenticate an incoming connection (client), Centrifugo can use a JSON Web Token (JWT) provided by your application backend to the client-side. This allows Centrifugo to identify the user ID within your application in a secure way. Also, the application can pass additional data to Centrifugo inside JWT claims. This chapter explains this authentication mechanism.

tip

If you prefer not to use JWTs, consider the proxy feature. It enables the proxying of connection requests from Centrifugo to your application's backend endpoint for authentication.

tip

Using JWT for authentication can be beneficial in scenarios involving massive reconnects. As the authentication information is encoded in the token, this can significantly reduce the load on your application's session backend. For more details, refer to our blog post.

Upon connection, the client should supply a connection JWT containing several predefined credential claims. Below is a diagram illustrating this:

For more information about handling connection tokens on the client side, see the client SDK specification.

Currently, Centrifugo supports HMAC, RSA, and ECDSA JWT algorithms - specifically HS256, HS384, HS512, RSA256, RSA384, RSA512, EC256, EC384, and EC512.

Here, we will demonstrate example snippets using the Javascript Centrifugo client for the client-side and the PyJWT Python library to generate a connection token on the backend side.

To add an HMAC secret key to Centrifugo, insert token_hmac_secret_key into the configuration file:

config.json
{
...
"token_hmac_secret_key": "<YOUR-SECRET-STRING-HERE>"
}

To add RSA public key (must be PEM encoded string) add token_rsa_public_key option, ex:

config.json
{
...
"token_rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZ..."
}

To add ECDSA public key (must be PEM encoded string) add token_ecdsa_public_key option, ex:

config.json
{
...
"token_ecdsa_public_key": "-----BEGIN PUBLIC KEY-----\nxyz23adf..."
}

Connection JWT Claims

For connection JWT, Centrifugo uses some standard claims defined in RFC 7519, as well as custom Centrifugo-specific claims.

sub

This standard JWT claim must contain the ID of the current application user (as a string).

If a user is not authenticated in the application but you wish to allow them to connect to Centrifugo, an empty string can be used as the user ID in the sub claim. This facilitates anonymous access. In such cases, you might need to enable the corresponding channel namespace options that allow protocol features for anonymous users.

exp

This claim specifies the UNIX timestamp (in seconds) when the token will expire. It is a standard JWT claim - all JWT libraries across different programming languages provide an API to set it.

If the exp claim is not included, Centrifugo will not expire the connection. When included, a special algorithm will identify connections with an exp in the past and initiate the connection refresh mechanism. The refresh mechanism allows a connection to be extended. If the refresh fails, Centrifugo will eventually close the client connection, which will not be accepted again until new valid and current credentials are provided in the connection token.

The connection expiration mechanism can be utilized in scenarios where you do not want users to remain subscribed to channels after being banned or deactivated in the application. It also serves to protect users from token leakage by setting a reasonably short expiration time.

Choose the exp value judiciously; too short a value can lead to frequent application hits with refresh requests, whereas too long a value can result in delayed user connection deactivation. It's a matter of balance.

Further details on connection expiration can be found below.

iat

This represents the UNIX time when the token was issued (in seconds). Refer to the definition in RFC 7519. This claim is optional but can be advantageous in conjunction with Centrifugo PRO's token revocation features.

jti

This is a unique identifier for the token. Refer to the definition in RFC 7519. This claim is optional but can be beneficial in conjunction with Centrifugo PRO's token revocation features.

aud

By default, Centrifugo does not check JWT audience (rfc7519 aud claim).

But you can force this check by setting token_audience string option:

config.json
{
"token_audience": "centrifugo"
}
caution

Setting token_audience will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.

iss

By default, Centrifugo does not check JWT issuer (rfc7519 iss claim).

But you can force this check by setting token_issuer string option:

config.json
{
"token_issuer": "my_app"
}
caution

Setting token_issuer will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.

info

This optional claim provides additional information about the client's connection for Centrifugo. This information will be included in presence data, join/leave events, and client-side channel publications.

b64info

For those utilizing the binary Protobuf protocol and requiring the info to be custom bytes, this field should be used.

It contains a base64 encoded representation of your bytes. Centrifugo will decode the base64 back into bytes upon receipt and incorporate the result into the various places described above.

channels

This is an optional array of strings identifying the server-side channels to which the client will be subscribed. Further details can be found in the documentation on server-side subscriptions.

tip

It's important to note that the channels claim is sometimes misinterpreted by users as a list of channel permissions. It does not serve that purpose. Instead, using this claim causes the client to be automatically subscribed to the specified channels upon connection, making it unnecessary to invoke the subscribe API from the client side. More information can be found in the server-side subscriptions documentation.

subs

This optional claim is a map of channels with options, providing a more detailed approach to server-side subscriptions compared to the channels claim, as it allows for the annotation of each channel with additional information and data through options.

tip

The term subs is shorthand for subscriptions. It should not be confused with the sub claim mentioned earlier, which is a standard JWT claim used to provide a user ID (short for subject). Despite their similar names, these claims serve distinct purposes within a connection JWT.

Example:

{
...
"subs": {
"channel1": {
"data": {"welcome": "welcome to channel1"}
},
"channel2": {
"data": {"welcome": "welcome to channel2"}
}
}
}

Subscribe options:

FieldTypeOptionalDescription
infoJSON objectyesCustom channel info
b64infostringyesCustom channel info in Base64 - to pass binary channel info
dataJSON objectyesCustom JSON data to return in subscription context inside Connect reply
b64datastringyesSame as data but in Base64 to send binary data
overrideOverride objectyesAllows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
positionBoolValueyesOverride position
recoverBoolValueyesOverride recover

BoolValue is an object like this:

{
"value": true/false
}

meta

meta is an additional JSON object (e.g., {"key": "value"}) that is attached to a connection. It differs from info as it is never disclosed to clients within presence and join/leave events; it is only accessible on the server side. It can be included in proxy calls from Centrifugo to the application backend (refer to the proxy_include_connection_meta option). In Centrifugo PRO, there is a connections API method that returns this metadata within the connection description object.

expire_at

Although Centrifugo typically uses the exp claim to manage connection expiration, there may be scenarios where you want to separate the token expiration check from the connection expiration time. When the expire_at claim is included in the JWT, Centrifugo uses it to determine the connection expiration time, while the JWT expiration is still verified using the exp claim.

expire_at is a UNIX timestamp indicating when the connection should expire.

  • To expire the connection at a specific future time, set it to that time.
  • To prevent connection expiration, set it to 0 (token exp claim will still be checked).

Connection expiration

As mentioned, the exp claim in a connection token is designed to expire the client connection at some point in time. Here's a detailed look at the process when Centrifugo identifies that the connection is going to expire.

First, activate the client expiration mechanism in Centrifugo by providing a connection JWT with an exp claim:

import jwt
import time

token = jwt.encode({"sub": "42", "exp": int(time.time()) + 10*60}, "secret", algorithm="HS256")

print(token)

Assuming the exp claim is set to expire in 10 minutes, the client connects to Centrifugo with this token. Centrifugo will maintain the connection for the specified duration. Once the time elapses, Centrifugo allows a grace period (default is 25 seconds) for the client to refresh its credentials with a new valid token containing an updated exp.

Upon initial connection, the client receives a ttl value in the connect response, indicating the seconds remaining before it must initiate a refresh command with new credentials. Centrifugo SDKs handle this ttl internally and automatically begin the refresh process.

SDKs provide mechanisms to hook into this process and provide a function to get new token. It's up to developer to decide how to load new token from the backend – in web browser this is usually a simple fetch request and response may look like this:

{
"token": token
}

You should provide the same connection JWT you issued when the page was initially rendered, but with an updated and valid exp. Our SDKs will then send this token to the Centrifugo server, and the connection will be extended for the period set in the new exp.

When you load new token from your app backend user authentication must be facilitated by your app's session mechanism. So you know for whom you are are going to generate an updated token.

Examples

Let's look at how to generate connection HS256 JWT in Python:

Simplest token

import jwt

token = jwt.encode({"sub": "42"}, "secret").decode()

print(token)

Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users.

Then you can pass this token to your client side and use it when connecting to Centrifugo:

Using centrifuge-js v3
var centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: token
});
centrifuge.connect();

See more details about working with connection tokens and handling token expiration on the client-side in the real-time SDK API spec.

Token with expiration

HS256 token that will be valid for 5 minutes:

import jwt
import time

claims = {"sub": "42", "exp": int(time.time()) + 5*60}
token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Token with additional connection info

Let's attach user name:

import jwt

claims = {"sub": "42", "info": {"name": "Alexander Emelin"}}
token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Investigating problems with JWT

You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.

JSON Web Key support

Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT).

A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address).

As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels.

tip

Read a tutorial in our blog about using Centrifugo with Keycloak SSO. In that case connection tokens are verified using public key loaded from the JWKS endpoint of Keycloak.

At the moment Centrifugo caches keys loaded from an endpoint for one hour.

Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment).

Centrifugo supports the following key types (kty) for JWKs tokens:

  • RSA
  • EC (since Centrifugo v5.1.0)

Once enabled JWKS used for both connection and channel subscription tokens.

Dynamic JWKs endpoint

It's possible to extract variables from iss and aud JWT claims using Go regexp named groups, then use variables extracted during iss or aud matching to construct a JWKS endpoint dynamically upon token validation. In this case JWKS endpoint may be set in config as template.

To achieve this Centrifugo provides two additional options:

  • token_issuer_regex - match JWT issuer (iss claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction.
  • token_audience_regex - match JWT audience (aud claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction.

Let's look at the example:

{
"token_issuer_regex": "https://example.com/auth/realms/(?P<realm>[A-z]+)",
"token_jwks_public_endpoint": "https://keycloak:443/{{realm}}/protocol/openid-connect/certs",
}

To use variable in token_jwks_public_endpoint it must be wrapped in {{ }}.

When using token_issuer_regex and token_audience_regex make sure token_issuer and token_audience not used in the config - otherwise and error will be returned on Centrifugo start.

caution

Setting token_issuer_regex and token_audience_regex will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.

- + \ No newline at end of file diff --git a/docs/server/channel_permissions.html b/docs/server/channel_permissions.html index 922305f9e..5d899ff26 100644 --- a/docs/server/channel_permissions.html +++ b/docs/server/channel_permissions.html @@ -16,13 +16,13 @@ - +

Channel permission model

When using Centrifugo server API you don't need to think about channel permissions at all – everything is allowed. In server API case, request to Centrifugo must be issued by your application backend – so you have all the power to check any required permissions before issuing API request to Centrifugo.

The situation is different when we are talking about client real-time API.

In order to configure which client (i.e. connection established using one of supported bidirectional real-time transports) can subscribe to channels and call publish, history and presence real-time APIs Centrifugo provides several ways to configure the desired behavior.

Let's start from looking at Centrifugo subscribe permission model.

Subscribe permission model

By default, client's attempt to subscribe on a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel subscribe permissions:

Below, we are describing those in detail.

Provide subscription token

A client can provide a subscription token in subscribe request. See the format of the token.

If client provides a valid token then subscription will be accepted. In Centrifugo PRO subscription token can additionally grant publish, history and presence permissions to a client.

caution

For namespaces with allow_subscribe_for_client option ON Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without token. This limitation exists to help users migrate to Centrifugo v4 without security risks.

Configure subscribe proxy

If client subscribes on a namespace with configured subscribe proxy then depending on proxy response subscription will be accepted or not.

If a namespace has configured subscribe proxy, but user came with a token – then subscribe proxy is not used, we are relying on token in this case. If a namespace has subscribe proxy, but user subscribes on a user-limited channel – then subscribe proxy is not used also.

Use user-limited channels

If client subscribes on a user-limited channel and there is a user ID match then subscription will be accepted.

caution

User-limited channels must be enabled in a namespace using allow_user_limited_channels option.

Use allow_subscribe_for_client namespace option

allow_subscribe_for_client allows all authenticated non-anonymous connections to subscribe on all channels in a namespace.

caution

Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.

To additionally allow subscribing to anonymous connections take a look at allow_subscribe_for_anonymous option.

Subscribe capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow user subscribe to channels.

Subscribe capabilities in connect proxy

Centrifugo PRO only

Connect proxy can return capability object to allow user subscribe to channels.

Publish permission model

tip

In idiomatic Centrifugo use case data should be published to channels from the application backend (over server API). In this case backend can validate data, save it into persistent storage before publishing in real-time towards connections. When publishing from the client-side backend does not receive publication data at all – it just goes through Centrifugo (except using publish proxy). There are cases when direct publications from the client-side are desired (like typing indicators in chat applications) though.

By default, client's attempt to publish data into a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel publish permissions:

Use allow_publish_for_client namespace option

allow_publish_for_client allows publications to channels of a namespace for all client connections.

Use allow_publish_for_subscriber namespace option

allow_publish_for_subscriber allows publications to channels of a namespace for all connections subscribed on a channel they want to publish data into.

Configure publish proxy

If client publishes to a namespace with configured publish proxy then depending on proxy response publication will be accepted or not.

Configured publish proxy always used??? (what if user has permission in token or allow_publish_for_client?)

Publish capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow client to publish to channels.

Publish capability in subscription token

Centrifugo PRO only

Connection token can contain a capability object to allow client to publish to a channel.

Publish capabilities in connect proxy

Centrifugo PRO only

Connect proxy can return capability object to allow client publish to certain channels.

Publish capability in subscribe proxy

Centrifugo PRO only

Subscribe proxy can return capability object to allow subscriber publish to channel.

History permission model

By default, client's attempt to call history from a channel (with history retention configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel history permissions.

Use allow_history_for_subscriber namespace option

allow_history_for_subscriber allows history requests to all channels in a namespace for all client connections subscribed on a channel they want to call history for.

Use allow_history_for_client namespace option

allow_history_for_client allows history requests to all channels in a namespace for all client connections.

History capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow user call history for channels.

History capabilities in subscription token

Centrifugo PRO only

Connection token can contain a capability object to allow user call history from a channel.

History capabilities in connect proxy

This is a Centrifugo PRO feature.

Connect proxy can return capability object to allow client call history from certain channels.

History capability in subscribe proxy response

Centrifugo PRO only

Subscribe proxy can return capability object to allow subscriber call history from channel.

Presence permission model

By default, client's attempt to call presence from a channel (with channel presence configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel presence permissions.

Presence capability in subscribe proxy response

Subscribe proxy can return capability object to allow subscriber call presence from channel.

Use allow_presence_for_subscriber namespace option

allow_presence_for_subscriber allows presence requests to all channels in a namespace for all client connections subscribed on a channel they want to call presence for.

Use allow_presence_for_client namespace option

allow_presence_for_client allows presence requests to all channels in a namespace for all client connections.

Presence capabilities in connection token

Centrifugo PRO only

Connection token can contain a capability object to allow user call presence for channels.

Presence capabilities in subscription token

Centrifugo PRO only

Connection token can contain a capability object to allow user call presence of a channel.

Presence capabilities in connect proxy

Centrifugo PRO only

Connect proxy can return capability object to allow client call presence from certain channels.

Positioning permission model

Server can whether turn on positioning for all channels in a namespace using "force_positioning": true option or client can create positioned subscriptions (but in this case client must have access to history capability).

Recovery permission model

Server can whether turn on automatic recovery for all channels in a namespace using "force_recovery": true option or client can create recoverable subscriptions (but in this case client must have access to history capability).

Join/Leave permission model

Server can whether force sending join/leave messages to all subscribers for all channels in a namespace using "force_push_join_leave": true option or client can ask server to include join/leave messages upon subscribing (but in this case client must have access to presence capability).

- + \ No newline at end of file diff --git a/docs/server/channel_token_auth.html b/docs/server/channel_token_auth.html index abbbefccb..ab7d82bb4 100644 --- a/docs/server/channel_token_auth.html +++ b/docs/server/channel_token_auth.html @@ -16,13 +16,13 @@ - +

Channel JWT authorization

In the chapter about channel permissions we mentioned that to subscribe on a channel client can provide subscription token. This chapter has more information about the subscription token mechanism in Centrifugo.

Subscription token is also JWT. Very similar to connection token, but with specific custom claims.

Valid subscription token passed to Centrifugo in subscribe request will tell Centrifugo that subscription must be accepted.

See more info about working with subscription tokens on the client side in client SDK spec.

tip

Connection token and subscription token are both JWT and both can be generated with any JWT library.

tip

Even when authorizing a subscription to a channel with a subscription JWT you should still set a proper connection JWT for a client as it provides user authentication details to Centrifugo.

tip

Just like connection JWT using subscription JWT with a reasonable expiration time may help you have a good level of security in channels and still survive massive reconnect scenario – when many clients resubscribe alltogether.

Supported JWT algorithms for private subscription tokens match algorithms to create connection JWT. The same HMAC secret key, RSA, and ECDSA public keys set for authentication tokens are re-used to check subscription JWT.

Subscription JWT claims

For subscription JWT Centrifugo uses some standard claims defined in rfc7519, also some custom Centrifugo-specific.

sub

This is a standard JWT claim which must contain an ID of the current application user (as string).

The value must match a user in connection JWT – since it's the same real-time connection. The missing claim will mean that token issued for anonymous user (i.e. with empty user ID).

channel

Required. Channel that client tries to subscribe to with this token (string).

info

Optional. Additional information for connection inside this channel (valid JSON).

b64info

Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.

exp

Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time.

At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs.

Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.

expire_at

Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though).

expire_at is a UNIX timestamp seconds when the subscription should expire.

  • Set it to the future time for expiring subscription at some point
  • Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.

aud

By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.

iss

By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.

iat

This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

jti

This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.

override

One more claim is override. This is an object which allows overriding channel options for the particular channel subscriber which comes with subscription token.

FieldTypeOptionalDescription
presenceBoolValueyesoverride presence channel option
join_leaveBoolValueyesoverride join_leave channel option
force_push_join_leaveBoolValueyesoverride force_push_join_leave channel option
force_recoveryBoolValueyesoverride force_recovery channel option
force_positioningBoolValueyesoverride force_positioning channel option

BoolValue is an object like this:

{
"value": true/false
}

So for example, you want to turn off emitting a presence information for a particular subscriber in a channel:

{
...
"override": {
"presence": {
"value": false
}
}
}

Example

So to generate a subscription token you can use something like this in Python (assuming user ID is 42 and the channel is gossips):

import jwt
import time

claims = {"sub": "42", "channel": "$gossips", "exp": int(time.time()) + 3600}
token = jwt.encode(claims, "secret", algorithm="HS256").decode()
print(token)

Where "secret" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).

gensubtoken cli command

During development you can quickly generate valid subscription token using Centrifugo gensubtoken cli command.

./centrifugo gensubtoken -u 123722 -s channel

You should see an output like this:

HMAC SHA-256 JWT for user "123722" and channel "channel" with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDg0MzgsImNoYW5uZWwiOiJjaGFubmVsIn0.JyRI3ovNV-abV8VxCmZCD556o2F2mNL1UoU58gNR-uI

But in real app subscription JWT must be generated by your application backend.

Separate subscription token config

When separate_subscription_token_config boolean option is true Centrifugo does not look at general token options at all when verifying subscription tokens and uses config options starting from subscription_token_ prefix instead.

Here is an example how to use JWKS for connection tokens, but have HMAC-based verification for subscription tokens:

config.json
{
"token_jwks_public_endpoint": "https://example.com/openid-connect/certs",
"separate_subscription_token_config": true,
"subscription_token_hmac_secret_key": "separate_secret_which_must_be_strong"
}

All the options which are available for connection token configuration may be re-used for a separate subscription token configuration – just prefix them with subscription_token_ instead of token_.

- + \ No newline at end of file diff --git a/docs/server/channels.html b/docs/server/channels.html index c5c694ad7..e3400b24e 100644 --- a/docs/server/channels.html +++ b/docs/server/channels.html @@ -16,13 +16,13 @@ - +

Channels and namespaces

Upon connecting to a server, clients can subscribe to channels. A channel is one of the core concepts of Centrifugo. Most of the time when integrating Centrifugo, you will work with channels and determine the optimal channel configuration for your application.

What is a channel

Centrifugo operates on a PUB/SUB model - it has publishers and subscribers. A channel acts as a conduit for publications. Clients can subscribe to a channel to receive all the real-time messages published there. Subscribers to a channel may also request information about the channel's online presence or its history.

pub_sub

A channel is simply a string - names like news, comments, personal_feed are examples of valid channel names. However, there are predefined rules for these strings, as we will discuss later. You can define different behaviors for a channel using a range of available channel options.

Channels are ephemeral – there is no need to create them explicitly. Channels are automatically created by Centrifugo as soon as the first client subscribes. Similarly, when the last subscriber leaves, the channel is automatically cleaned up.

A channel can be part of a channel namespace. Channel namespacing is a mechanism to define different behaviors for various channels within Centrifugo. Using namespaces is the recommended approach to manage channels – enabling only those channel options which are necessary for the specific real-time feature you are implementing with Centrifugo.

caution

Ensure you have defined a namespace in the configuration when using channel namespaces. Attempts to subscribe to a channel within an undefined namespace will result in 102: unknown channel errors.

Channel name rules

Only ASCII symbols must be used in a channel string.

Channel name length limited by 255 characters by default (controlled by configuration option channel_max_length).

Several symbols in channel names reserved for Centrifugo internal needs:

  • : – for namespace channel boundary (see below)
  • # – for user channel boundary (see below)
  • $ – for private channel prefix (see below)
  • / – for Channel Patterns in Centrifugo PRO
  • * – for the future Centrifugo needs
  • & – for the future Centrifugo needs

namespace boundary (:)

: – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about about namespaces and channel options below.

If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public.

info

A namespace is a inalienable component of the channel name. If a user is subscribed to a channel with a namespace, such as public:chat, then you must publish messages to the public:chat channel for them to be delivered to the user. There is often confusion among developers who try to publish messages to chat, mistakenly believing that the namespace is stripped upon subscription. This is not the case. You must publish exactly to the same channel string you used for subscribing.

user channel boundary (#)

# symbol serves as the user channel boundary. It acts as a separator to create personal channels for users—referred to as user-limited channels—without requiring a subscription token.

For instance, if the channel is named news#42, then only the user with ID 42 can subscribe to this channel. Centrifugo identifies the user ID from the connection credentials provided in the connection JWT.

To create a user-limited channel within the personal namespace, you might use a name such as personal:user#42.

Furthermore, it's possible to specify multiple user IDs in the channel name, separated by a comma: dialog#42,43. In this case, only users with IDs 42 and 43 are permitted to subscribe to this channel.

This setup is ideal for channels that have a static list of allowed users, such as channels for personal messages to a single user or dialogue channels between specific users. However, for dynamic access management of a channel for numerous users, this type of channel is not appropriate.

tip

User-limited channels must be enabled for a channel namespace using allow_user_limited_channels option. See below more information about channel options and channel namespaces.

private channel prefix ($)

Centrifugo maintains compatibility with its previous versions which had concept of private channels. In earlier versions — specifically Centrifugo v1, v2, and v3—only – only channels beginning with $ required a subscription JWT for subscribing. With Centrifugo v4, this is no longer the case; clients can subscribe to any channel if they have a valid subscription token.

However, for namespaces where the allow_subscribe_for_client option is activated, Centrifugo prohibits subscriptions to channels that start with the private_channel_prefix (which defaults to $) unless a subscription token is provided. This restriction is designed to facilitate a secure migration to Centrifugo v4 or later versions.

Channel is just a string

Bear in mind that a channel is uniquely identified by its string representation. Do not assume that channels $news and news are the same; they differ because their strings are not identical. Thus, if a user is subscribed to $news, they will not receive messages published to news.

The channels dialog#42,43 and dialog#43,42 are considered different as well. Centrifugo only applies permission checks when a user subscribes to a channel. So if user-limited channels are enabled then the user with ID 42 will be able to subscribe on both dialog#42,43 and dialog#43,42. But Centrifugo does no magic regarding channel strings when keeping channel->to->subscribers map. So if the user subscribed on dialog#42,43 you must publish messages to exactly that channel: dialog#42,43.

The same reasoning applies to channels within namespaces. Channels chat:index and index are not the same — they are distinct and, moreover, they belong to different namespaces. The concept of channel namespaces in Centrifugo will be discussed shortly.

Channel namespaces

Centrifugo allows configuring a list of channel namespaces. Namespaces are optional but very useful.

A namespace is a container for options applied for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application.

A namespace acts as a container for options that are applied to channels starting with the namespace name. This provides great control over channel behavior, so you have a flexible way to define different channel options for various real-time features in the application.

Namespace has a name, and can contain all the channel options described below (with the same defaults).

  • name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$).

If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator:

public:messages

gossips:messages

Where public and gossips are namespace names. Centrifugo looks for : symbol in the channel name, if found – extracts the namespace name, and applies namespace options while processing protocol commands from a client.

All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured:

config.json
{
"token_hmac_secret_key": "very-long-secret-key",
"api_key": "secret-api-key",

"presence": true,
"history_size": 10,
"history_ttl": "30s",

"namespaces": [
{
"name": "facts",
"history_size": 10,
"history_ttl": "300s"
},
{
"name": "gossips"
}
]
}
  • Channel news will use globally defined channel options.
  • Channel facts:sport will use facts namespace options.
  • Channel gossips:sport will use gossips namespace options.
  • Channel xxx:hello will result into subscription error since there is no xxx namespace defined in the configuration above.

Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2.

note

There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly.

There are many options which can be set for channel namespace (on top-level and to named one) to modify behavior of channels belonging to a namespace. Below we describe all these options.

Channel options

Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.

presence

presence (boolean, default false) – enable/disable online presence information for channels in a namespace.

Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels.

Let's say you have a channel chat:index and 2 users (with ID 2694 and 56) subscribed to it. And user 2694 has 2 connections to Centrifugo in different browser tabs. In presence data you may see sth like this:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "chat:index"}' \
http://localhost:8000/api/presence
{
"result": {
"presence": {
"66fdf8d1-06f0-4375-9fac-db959d6ee8d6": {
"user": "2694",
"client": "66fdf8d1-06f0-4375-9fac-db959d6ee8d6",
"conn_info": {"name": "Alex"}
},
"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c": {
"user": "2694",
"client": "d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c",
"conn_info": {"name": "Alex"}
}
"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c": {
"user": "56",
"client": "g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c",
"conn_info": {"name": "Alice"}
}
}
}
}

To call presence API from the client connection side client must have permission to do so. See presence permission model.

caution

Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or in a broker memory/disk). So only use it for channels where presence is required.

See more details about online presence design.

join_leave

join_leave (boolean, default false) – enable/disable sending join and leave messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info (similar to entry inside presence information).

Enabling join_leave means that Join/Leave messages will start being emitted, but by default they are not delivered to clients subscribed to a channel. You need to force this using namespace option force_push_join_leave or explicitly provide intent from a client-side (in this case client must have permission to call presence API).

caution

Keep in mind that join/leave messages can generate a huge number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature. It's hard to say what is "large" for you though – just estimate the load based on the fact that each subscribe/unsubscribe event in a channel with N subscribers will result into N messages broadcasted to all. If all clients reconnect at the same time the amount of generated messages is N^2.

Join/leave messages distributed only with at most once delivery guarantee.

force_push_join_leave

Boolean, default false.

When on all clients will receive join/leave events for a channel in a namespace automatically – without explicit intent to consume join/leave messages from the client side.

If pushing join/leave is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel presence (by having an explicit capability or if allowed on a namespace level).

history_size

history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted.

Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below).

caution

Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk). So only use history for channels where it's required.

history_ttl

history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision).

As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels.

By default history TTL duration is zero – this means that channel history is disabled.

Again – to turn on history you should wisely configure both history_size and history_ttl options.

For example for top-level channels (which do not belong to a namespace):

config.json
{
...
"history_size": 10,
"history_ttl": "60s"
}

Let's look at example. You enabled history for a namespace chat and sent two messages in channel chat:index. Then history will contain sth like this:

curl --header "Content-Type: application/json" \
--header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "chat:index", "limit": 100}' \
http://localhost:8000/api/history
{
"result": {
"publications": [
{
"data": {
"input": "1"
},
"offset": 1
},
{
"data": {
"input": "2"
},
"offset": 2
}
],
"epoch": "gWuY",
"offset": 2
}
}

To call history API from the client connection side client must have permission to do so. See history permission model.

See additional information about offsets and epoch in History and recovery chapter.

tip

History persistence properties are dictated by Centrifugo engine used. For example, when using memory engine history is only kept till Centrifugo node restart. In Redis engine case persistence is determined by a Redis server persistence configuration (same for KeyDB and Tarantool).

history_meta_ttl

history_meta_ttl (duration) – sets a time of history stream metadata expiration (with seconds precision).

When using a history in a channel, Centrifugo keeps some metadata for each channel stream. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help to expire metadata faster (or slower if you need it). The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.

If not specified Centrifugo uses a global history_meta_ttl which is 30 days. This should be a good default for most use cases to avoid tweaking history_meta_ttl on a namespace level at all.

force_positioning

force_positioning (boolean, default false) – when the force_positioning option is on Centrifugo forces all subscriptions in a namespace to be positioned. I.e. Centrifugo will try to compensate at most once delivery of PUB/SUB broker checking client position inside a stream.

If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API.

force_positioning option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream).

If positioning is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).

force_recovery

force_recovery (boolean, default false) – when the position option is on Centrifugo forces all subscriptions in a namespace to be recoverable. When enabled Centrifugo will try to recover missed publications in channels after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the force_positioning option described above.

force_recovery option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages).

If recovery is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).

tip

Not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.

allow_subscribe_for_client

allow_subscribe_for_client (boolean, default false) – when on all non-anonymous clients will be able to subscribe to any channel in a namespace. To additionally allow anonymous users to subscribe turn on allow_subscribe_for_anonymous (see below).

caution

Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.

allow_subscribe_for_anonymous

allow_subscribe_for_anonymous (boolean, default false) – turn on if anonymous clients (with empty user ID) should be able to subscribe on channels in a namespace.

allow_publish_for_subscriber

allow_publish_for_subscriber (boolean, default false) - when the allow_publish_for_subscriber option is enabled client can publish into a channel in namespace directly from the client side over real-time connection but only if client subscribed to that channel.

danger

Keep in mind that in this case subscriber can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).

allow_publish_for_subscriber (or allow_publish_for_client mentioned below) option still can be useful to send something without backend-side validation and saving it into a database – for example, this option may be handy for demos and quick prototyping real-time app ideas.

allow_publish_for_client

allow_publish_for_client (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). It's like allow_publish_for_subscriber – but client should not be a channel subscriber to publish.

danger

Keep in mind that in this case client can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).

allow_publish_for_anonymous

allow_publish_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to publish into channels in a namespace.

allow_history_for_subscriber

allow_history_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call history API from that channel.

allow_history_for_client

allow_history_for_client (boolean, default false) – allows all clients to call history information in a namespace.

allow_history_for_anonymous

allow_history_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call history from channels in a namespace.

allow_presence_for_subscriber

allow_presence_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call presence information from that channel.

allow_presence_for_client

allow_presence_for_client (boolean, default false) – allows all clients to call presence information in a namespace.

allow_presence_for_anonymous

allow_presence_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call presence from channels in a namespace.

allow_user_limited_channels

allow_user_limited_channels (boolean, default false) - allows using user-limited channels in a namespace for checking subscribe permission.

note

If client subscribes to a user-limited channel while this option is off then server rejects subscription with 103: permission denied error.

channel_regex

channel_regex (string, default "") – is an option to set a regular expression for channels allowed in the namespace. By default Centrifugo does not limit channel name variations. For example, if you have a namespace chat, then channel names inside this namespace are not really limited, it can be chat:index, chat:1, chat:2, chat:zzz and so on. But if you want to be strict and know possible channel patterns you can use channel_regex option. This is especially useful in namespaces where all clients can subscribe to channels.

For example, let's only allow digits after chat: for channel names in a chat namespace:

{
"namespaces": [
{
"name": "chat",
"allow_subscribe_for_client": true,
"channel_regex": "^[\d+]$"
}
]
}
danger

Note, that we are skipping chat: part in regex. Since namespace prefix is the same for all channels in a namespace we only match the rest (after the prefix) of channel name.

Channel regex only checked for client-side subscriptions, if you are using server-side subscriptions Centrifugo won't check the regex.

Centrifugo uses Go language regexp package for regular expressions.

proxy_subscribe

proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter

proxy_publish

proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter

proxy_sub_refresh

proxy_sub_refresh (boolean, default false) – turns on sub refresh proxy, more info in proxy chapter

proxy_subscribe_stream

proxy_subscribe_stream (boolean, default false) - turns on subscribe stream proxy, see subscription streams

subscribe_proxy_name

subscribe_proxy_name (string, default "") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.

publish_proxy_name

publish_proxy_name (string, default "") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.

sub_refresh_proxy_name

sub_refresh_proxy_name (string, default "") – turns on sub refresh proxy when granular proxy mode is used. Note that proxy_sub_refresh option defined above is ignored in granular proxy mode.

subscribe_stream_proxy_name

subscribe_stream_proxy_name (string, default "") – turns on subscribe stream proxy when granular proxy mode is used. Note that proxy_subscribe_stream option defined above is ignored in granular proxy mode.

Channel config examples

Let's look at how to set some of these options in a config. In this example we turning on presence, history features, forcing publication recovery. Also allowing all client connections (including anonymous users) to subscribe to channels and call publish, history, presence APIs if subscribed.

config.json
{
"token_hmac_secret_key": "my-secret-key",
"api_key": "secret-api-key",
"presence": true,
"history_size": 10,
"history_ttl": "300s",
"force_recovery": true,
"allow_subscribe_for_client": true,
"allow_subscribe_for_anonymous": true,
"allow_publish_for_subscriber": true,
"allow_publish_for_anonymous": true,
"allow_history_for_subscriber": true,
"allow_history_for_anonymous": true,
"allow_presence_for_subscriber": true,
"allow_presence_for_anonymous": true
}

Here we set channel options on config top-level – these options will affect channels without namespace. In many cases defining namespaces is a recommended approach so you can manage options for every real-time feature separately. With namespaces the above config may transform to:

config.json
{
"token_hmac_secret_key": "my-secret-key",
"api_key": "secret-api-key",
"namespaces": [
{
"name": "feed",
"presence": true,
"history_size": 10,
"history_ttl": "300s",
"force_recovery": true,
"allow_subscribe_for_client": true,
"allow_subscribe_for_anonymous": true,
"allow_publish_for_subscriber": true,
"allow_publish_for_anonymous": true,
"allow_history_for_subscriber": true,
"allow_history_for_anonymous": true,
"allow_presence_for_subscriber": true,
"allow_presence_for_anonymous": true
}
]
}

In this case channels should be prefixed with feed: to follow the behavior configured for a feed namespace.

- + \ No newline at end of file diff --git a/docs/server/codes.html b/docs/server/codes.html index 70b2d729f..10ec77edc 100644 --- a/docs/server/codes.html +++ b/docs/server/codes.html @@ -16,13 +16,13 @@ - +

Error and disconnect codes

This chapter describes error and disconnect codes Centrifugo uses in a client protocol, also error codes which a server API can return in response.

Client error codes

Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client.

Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).

Internal

Code:    100
Message: "internal server error"
Temporary: true

Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.

Unauthorized

Code:    101
Message: "unauthorized"

Error Unauthorized says that request is unauthorized.

Unknown Channel

Code:    102
Message: "unknown channel"

Error Unknown Channel means that channel name does not exist.

Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.

Permission Denied

Code:    103
Message: "permission denied"

Error Permission Denied means that access to resource not allowed.

Method Not Found

Code:    104
Message: "method not found"

Error Method Not Found means that method sent in command does not exist.

Already Subscribed

Code:    105
Message: "already subscribed"

Error Already Subscribed returned when a client attempts to subscribe to a channel to which it is already subscribed.

In Centrifugo, a client can only have one subscription to a specific channel.

When using client-side subscriptions, this error may signal a bug in the SDK or the SDK being used in a way that was not planned. This error may also be returned by the server when a client tries to subscribe to a channel but is already subscribed to it using server-side subscriptions.

Limit Exceeded

Code:    106
Message: "limit exceeded"

Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.

Bad Request

Code:    107
Message: "bad request"

Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.

Not Available

Code:    108
Message: "not available"

Error Not Available means that resource is not enabled.

For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.

Token Expired

Code:    109
Message: "token expired"

Error Token Expired indicates that connection token expired. Our SDKs handle it in a special way by updating token.

Expired

Code:    110
Message: "expired"

Error Expired indicates that connection expired (no token involved).

Too Many Requests

Code:    111
Message: "too many requests"
Temporary: true

Error Too Many Requests means that server rejected request due to rate limiting strategies.

Unrecoverable Position

Code:    112
Message: "unrecoverable position"

Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query.

This can happen due to wrong epoch passed.

Client disconnect codes

Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes).

note

We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. We expect these codes are mostly useful for logs and metrics.

DisconnectConnectionClosed

Code: 3000
Reason: "connection closed"

DisconnectConnectionClosed is a special Disconnect object used when client connection was closed without any advice from a server side. This can be a clean disconnect, or temporary disconnect of the client due to internet connection loss. Server can not distinguish the actual reason of disconnect.

Non-terminal disconnect codes

Client will reconnect after receiving such codes.

Shutdown

Code:      3001
Reason: "shutdown"

Disconnect Shutdown may be sent when node is going to shut down.

DisconnectServerError

Code:   3004
Reason: "internal server error"

DisconnectServerError issued when internal error occurred on server.

DisconnectExpired

Code:   3005
Reason: "connection expired"

DisconnectSubExpired

Code:   3006
Reason: "subscription expired"

DisconnectSubExpired issued when client subscription expired.

DisconnectSlow

Code:   3008
Reason: "slow"

DisconnectSlow issued when client can't read messages fast enough.

DisconnectWriteError

Code:   3009
Reason: "write error"

DisconnectWriteError issued when an error occurred while writing to client connection.

DisconnectInsufficientState

Code:   3010
Reason: "insufficient state"

DisconnectInsufficientState issued when server detects wrong client position in channel Publication stream. Disconnect allows clien to restore missed publications on reconnect.

DisconnectForceReconnect

Code:   3011
Reason: "force reconnect"

DisconnectForceReconnect issued when server disconnects connection for some reason and whants it to reconnect.

DisconnectNoPong

Code:   3012
Reason: "no pong"

DisconnectNoPong may be issued when server disconnects bidirectional connection due to no pong received to application-level server-to-client pings in a configured time.

DisconnectTooManyRequests

Code:   3013
Reason: "too many requests"

DisconnectTooManyRequests may be issued when client sends too many commands to a server.

Terminal disconnect codes

Client won't reconnect upon receiving such code.

DisconnectInvalidToken

Code:   3500
Reason: "invalid token"

DisconnectInvalidToken issued when client came with invalid token.

DisconnectBadRequest

Code:   3501
Reason: "bad request"

DisconnectBadRequest issued when client uses malformed protocol frames.

DisconnectStale

Code:   3502
Reason: "stale"

DisconnectStale issued to close connection that did not become authenticated in configured interval after dialing.

DisconnectForceNoReconnect

Code:   3503
Reason: "force disconnect"

DisconnectForceNoReconnect issued when server disconnects connection and asks it to not reconnect again.

DisconnectConnectionLimit

Code:   3504
Reason: "connection limit"

DisconnectConnectionLimit can be issued when client connection exceeds a configured connection limit (per user ID or due to other rule).

DisconnectChannelLimit

Code:   3505
Reason: "channel limit"

DisconnectChannelLimit can be issued when client connection exceeds a configured channel limit.

DisconnectInappropriateProtocol

Code:   3506
Reason: "inappropriate protocol"

DisconnectInappropriateProtocol can be issued when client connection format can not handle incoming data. For example, this happens when JSON-based clients receive binary data in a channel. This is usually an indicator of programmer error, JSON clients can not handle binary.

DisconnectPermissionDenied

Code:   3507
Reason: "permission denied"

DisconnectPermissionDenied may be issued when client attempts accessing a server without enough permissions.

DisconnectNotAvailable

Code:   3508
Reason: "not available"

DisconnectNotAvailable may be issued when ErrorNotAvailable does not fit message type, for example we issue DisconnectNotAvailable when client sends asynchronous message without MessageHandler set on server side.

DisconnectTooManyErrors

Code:   3509
Reason: "too many errors"

DisconnectTooManyErrors may be issued when client generates too many errors.

- + \ No newline at end of file diff --git a/docs/server/configuration.html b/docs/server/configuration.html index 307257fe8..2916dd236 100644 --- a/docs/server/configuration.html +++ b/docs/server/configuration.html @@ -16,13 +16,13 @@ - +

Configure Centrifugo

Let's look at how Centrifugo can be configured.

There are more options

This chapter describes configuration principles and some important configuration options. There are more options not mentioned here but described throughout the doc in the context of each individual server feature.

Configuration sources

Centrifugo can be configured in several ways: using command-line flags (highest priority), environment variables (second priority after flags), configuration file (lowest priority).

Command-line flags

Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer using environment variables or configuration file.

Command-line options have the highest priority when set than other ways to configure Centrifugo.

OS environment variables

All Centrifugo options can be set over env in the format CENTRIFUGO_<OPTION_NAME> (i.e. option name with CENTRIFUGO_ prefix, all in uppercase).

Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags.

Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use "true" value for an environment variable (or simply "1"). To set false use "false" or "0". Example:

export CENTRIFUGO_PROMETHEUS="1"

Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example:

export CENTRIFUGO_ALLOWED_ORIGINS="https://mysite1.example.com https://mysite2.example.com"

For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name.

Empty environment variables are considered unset (!) and will fall back to the next configuration source.

Configuration file

Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable is preferred over the same option in config file).

A simple way to start with Centrifugo is to run:

centrifugo genconfig

This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo:

centrifugo -c config.json

Config file formats

Centrifugo supports three configuration file formats: JSON, YAML, or TOML.

JSON config format

Here is an example of Centrifugo JSON configuration file:

config.json
{
"allowed_origins": ["http://localhost:3000"],
"token_hmac_secret_key": "<YOUR-SECRET-STRING-HERE>",
"api_key": "<YOUR-API-KEY-HERE>"
}

token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT.

api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients.

allowed_origins option described below.

TOML config format

Centrifugo also supports TOML format for configuration file:

centrifugo --config=config.toml

Where config.toml contains:

config.toml
allowed_origins: [ "http://localhost:3000" ]
token_hmac_secret_key = "<YOUR-SECRET-STRING-HERE>"
api_key = "<YOUR-API-KEY-HERE>"
log_level = "debug"

In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.

YAML config format

YAML format is also supported:

config.yaml
allowed_origins:
- "http://localhost:3000"
token_hmac_secret_key: "<YOUR-SECRET-STRING-HERE>"
api_key: "<YOUR-API-KEY-HERE>"
log_level: debug

With YAML remember to use spaces, not tabs when writing a configuration file.

Important options

Let's describe some important options you can configure when running Centrifugo.

allowed_origins

This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins.

As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array.

Connection requests without Origin header set are passing through without any checks (i.e. always allowed).

For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way:

"allowed_origins": [
"http://localhost:3000"
]

When connecting from https://example.com:

"allowed_origins": [
"https://example.com"
]

Origin pattern can contain wildcard symbol * to match subdomains:

"allowed_origins": [
"https://*.example.com"
]

– in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check.

It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature):

"allowed_origins": [
"*"
]

address

Bind your Centrifugo to a specific interface address (string, by default "" - listen on all available interfaces).

port

Port to bind Centrifugo to (string, by default "8000").

engine

Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.

Advanced options

These options allow tweaking server behavior, in most cases default values are good to start with.

client_channel_limit

Default: 128

Sets the maximum number of different channel subscriptions a single client can have.

tip

When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.

channel_max_length

Default: 255

Sets the maximum length of the channel name.

client_user_connection_limit

Default: 0

The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited.

The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).

client_connection_limit

Default: 0

When set to a value > 0 client_connection_limit limits the max number of connections single Centrifugo node can handle. It acts on HTTP middleware level and stops processing request if the condition met. It logs a warning into logs in this case and increments centrifugo_node_client_connection_limit Prometheus counter. Client SDKs will attempt reconnecting.

Some motivation behind this option may be found in this issue.

Note, that at this point client_connection_limit does not affect connections coming over GRPC unidirectional transport.

client_connection_rate_limit

Default: 0

client_connection_rate_limit sets the maximum number of HTTP requests to establish a new real-time connection a single Centrifugo node will accept per second (on real-time transport endpoints). All requests outside the limit will get 503 Service Unavailable code in response. Our SDKs handle this with backoff reconnection.

By default, no limit is used.

Note, that at this point client_connection_rate_limit does not affect connections coming over GRPC unidirectional transport.

client_queue_max_size

Default: 1048576

Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.

client_concurrency

Default: 0

client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently.

By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).

client_stale_close_delay

Duration, default: 10s

This option allows tuning the maximum time Centrifugo will wait for the connect frame (which contains authentication information) from the client after establishing connection. Default value should be reasonable for most use cases.

client_user_id_http_header

String, default: ""

Available since v5.1.1

Usually to authenticate client connections with Centrifugo you need to use JWT authentication or connect proxy. Sometimes though it may be convenient to pass user ID information in incoming HTTP request headers. This is usually the case when application backend infrastructure has some authentication proxy (like Envoy, etc). This proxy may set authenticated user ID to some header and proxy requests further to Centrifugo.

When client_user_id_http_header is set to some non-empty header name Centrifugo will try to extract the authenticated user ID for client connections from that header. This mechanism works for all real-time transports based on HTTP (this also includes WebSocket since it starts with HTTP Upgrade request). Example:

config.json
{
...
"client_user_id_http_header": "X-User-Id"
}

When using this way for user authentication – you can not set connection expiration and additional connection info which is possible to do using other authentication ways mentioned above.

Security warning

When using authentication over proxy ensure your proxy strips the header you are using for auth if it comes from the client or forbids such requests to avoid malicious usage. Only your authentication proxy must set the header with user ID.

allow_anonymous_connect_without_token

Default: false

Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID). Access to channel operations should be explicitly enabled for anonymous connections.

disallow_anonymous_connection_tokens

Default: false

When the option is set Centrifugo won't accept connections from anonymous users even if they provided a valid JWT. I.e. if token is valid, but sub claim is empty – then Centrifugo closes connection with advice to not reconnect again.

gomaxprocs

Default: 0

By default, Centrifugo runs on all available CPU cores (also Centrifugo can look at cgroup limits when rnning in Docker/Kubernetes). To limit the number of cores Centrifugo can utilize in one moment use this option.

Endpoint configuration

After Centrifugo started there are several endpoints available.

Default endpoints

Bidirectional WebSocket default endpoint:

ws://localhost:8000/connection/websocket

Bidirectional emulation with HTTP-streaming (disabled by default):

ws://localhost:8000/connection/http_stream

Bidirectional emulation with SSE (EventSource) (disabled by default):

ws://localhost:8000/connection/sse

Bidirectional SockJS default endpoint (disabled by default):

http://localhost:8000/connection/sockjs

Unidirectional EventSource endpoint (disabled by default):

http://localhost:8000/connection/uni_sse

Unidirectional HTTP streaming endpoint (disabled by default):

http://localhost:8000/connection/uni_http_stream

Unidirectional WebSocket endpoint (disabled by default):

http://localhost:8000/connection/uni_websocket

Unidirectional SSE (EventSource) endpoint (disabled by default):

http://localhost:8000/connection/uni_sse

Server HTTP API endpoint:

http://localhost:8000/api

By default, all endpoints work on port 8000. This can be changed with port option:

{
"port": 9000
}

In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.

Admin endpoints

Admin web UI endpoint works on root path by default, i.e. http://localhost:8000.

For more details about admin web UI, refer to the Admin web UI documentation.

Debug endpoints

Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config:

{
...
"debug": true
}

And endpoint:

http://localhost:8000/debug/pprof/

– will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.

Health check endpoint

Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag:

centrifugo -c config.json --health

Swagger UI for server API

Use swagger boolean option (by default false) to enable Swagger UI for server HTTP API. UI will be available on path /swagger. Also available over command-line flag:

centrifugo -c config.json --swagger

Custom internal ports

We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal:

  • API endpoint (/api) - for HTTP API requests
  • Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface
  • Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format
  • Health check endpoint (/health) - used to do health checks
  • Debug endpoints (/debug/pprof) - used to inspect internal server state
  • Swagger UI endpoint (/swagger) - used for showing embedded Swagger UI for server HTTP API

It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration.

Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work.

To run internal endpoints on custom port use internal_port option:

{
...
"internal_port": 9000
}

So admin web interface will work on address:

http://localhost:9000

Also, debug page will be available on a new custom port too:

http://localhost:9000/debug/pprof/

The same for API and Prometheus endpoints.

Disable default endpoints

To disable websocket endpoint set websocket_disable boolean option to true.

To disable API endpoint set api_disable boolean option to true.

Customize handler endpoints

It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options:

  • admin_handler_prefix (default "") - to control Admin panel URL prefix
  • websocket_handler_prefix (default "/connection/websocket") - to control WebSocket URL prefix
  • http_stream_handler_prefix (default "/connection/http_stream") - to control HTTP-streaming URL prefix
  • sse_handler_prefix (default "/connection/sse") - to control SSE/EventSource URL prefix
  • emulation_handler_prefix (default "/emulation") - to control emulation endpoint prefix
  • sockjs_handler_prefix (default "/connection/sockjs") - to control SockJS URL prefix
  • uni_sse_handler_prefix (default "/connection/uni_sse") - to control unidirectional Eventsource URL prefix
  • uni_http_stream_handler_prefix (default "/connection/uni_http_stream") - to control unidirectional HTTP streaming URL prefix
  • uni_websocket_handler_prefix (default "/connection/uni_websocket") - to control unidirectional WebSocket URL prefix
  • api_handler_prefix (default "/api") - to control HTTP API URL prefix
  • prometheus_handler_prefix (default "/metrics") - to control Prometheus URL prefix
  • health_handler_prefix (default "/health") - to control health check URL prefix

Signal handling

It's possible to send HUP signal to Centrifugo to reload a configuration:

kill -HUP <PID>

Though at moment this will only reload token secrets and channel options (top-level and namespaces).

Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.

Insecure modes

Insecure client connection

The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. It also disables permission checks on client API level - for presence and history calls. This mode can be useful for demo projects based on Centrifugo, integration tests, local projects, or real-time application prototyping. Don't use it in production until you 100% know what you are doing.

Disable client token signature check

Available since Centrifugo v5.0.4

The boolean option client_insecure_skip_token_signature_verify (default false), if enabled – tells Centrifugo to skip JWT signature verification - for both connection and subscription tokens. This is absolutely insecure and must only be used for development and testing purposes. Token claims are parsed as usual - so token should still follow JWT format.

Insecure API mode

This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules.

The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.

Insecure admin mode

This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).

Setting time duration options

Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use "5s".

The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option).

Valid time units are "ms" (milliseconds), "s" (seconds), "m" (minutes), "h" (hours).

Some examples:

"1000ms" // 1000 milliseconds
"1s" // 1 second
"12h" // 12 hours
"720h" // 30 days

Setting namespaces over env

While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special:

CENTRIFUGO_NAMESPACES='[{"name": "ns1"}, {"name": "ns2"}]' ./centrifugo

I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.

Anonymous usage stats

Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage.

Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal.

To disable sending usage stats set usage_stats_disable option:

config.json
{
"usage_stats_disable": true
}
- + \ No newline at end of file diff --git a/docs/server/console_commands.html b/docs/server/console_commands.html index c987e5b58..a2c2e48cd 100644 --- a/docs/server/console_commands.html +++ b/docs/server/console_commands.html @@ -16,13 +16,13 @@ - +

Helper CLI commands

Here is a list of helpful command-line commands that come with Centrifugo executable.

version

To show Centrifugo version and exit run:

centrifugo version

genconfig

Another command is genconfig:

centrifugo genconfig -c config.json

It will automatically generate the minimal required configuration file. This is mostly useful for development.

If any errors happen – program will exit with error message and exit code 1.

genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file:

centrifugo genconfig -c config.toml

checkconfig

Centrifugo has special command to check configuration file checkconfig:

centrifugo checkconfig --config=config.json

If any errors found during validation – program will exit with error message and exit code 1.

gentoken

Another command is gentoken:

centrifugo gentoken -c config.json -u 28282

It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week).

You can change token TTL with -t flag (number of seconds):

centrifugo gentoken -c config.json -u 28282 -t 3600

This way generated token will be valid for 1 hour.

If any errors happen – program will exit with error message and exit code 1.

This command is mostly useful for development.

gensubtoken

Another command is gensubtoken:

centrifugo gensubtoken -c config.json -u 28282 -s channel

It will automatically generate HMAC SHA-256 based subscription token for channel channel and user with ID 28282 (which expires in 1 week).

You can change token TTL with -t flag (number of seconds):

centrifugo gentoken -c config.json -u 28282 -s channel -t 3600

This way generated token will be valid for 1 hour.

If any errors happen – program will exit with error message and exit code 1.

This command is mostly useful for development.

checktoken

One more command is checktoken:

centrifugo checktoken -c config.json <TOKEN>

It will validate your connection JWT, so you can test it before using while developing application.

If any errors happen or validation failed – program will exit with error message and exit code 1.

This is mostly useful for development.

checksubtoken

One more command is checksubtoken:

centrifugo checksubtoken -c config.json <TOKEN>

It will validate your subscription JWT, so you can test it before using while developing application.

If any errors happen or validation failed – program will exit with error message and exit code 1.

This is mostly useful for development.

- + \ No newline at end of file diff --git a/docs/server/engines.html b/docs/server/engines.html index 5ee60f339..e3850ec2a 100644 --- a/docs/server/engines.html +++ b/docs/server/engines.html @@ -16,7 +16,7 @@ - + @@ -25,7 +25,7 @@ the same port number (8000 or whatever you want) for all instances.

And finally, let's start the third instance:

centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379

Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes.

To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation.

tip

In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers.

Here is a live example where we locally start two Centrifugo nodes both connected to local Redis:

Redis Sentinel for high availability

Centrifugo supports the official way to add high availability to Redis - Redis Sentinel.

For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name:

  • redis_sentinel_address (string, default "") - comma separated list of Sentinel addresses for HA. At least one known server required.
  • redis_sentinel_master_name (string, default "") - name of Redis master Sentinel monitors

Also:

  • redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1
  • redis_sentinel_user - optional string user (used only in Redis ACL-based auth).

So you can start Centrifugo which will use Sentinels to discover Redis master instances like this:

centrifugo --config=config.json

Where config.json:

config.json
{
...
"engine": "redis",
"redis_sentinel_address": "127.0.0.1:26379",
"redis_sentinel_master_name": "mymaster"
}

Sentinel configuration file may look like this (for 3-node Sentinel setup with quorum 2):

port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 60000

You can find how to properly set up Sentinels in official documentation.

Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.

Redis Sentinel TLS

To configure TLS for Redis Sentinel use the following options.

redis_sentinel_tls

Boolean, default false - enable Redis TLS connection.

redis_sentinel_tls_insecure_skip_verify

Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_sentinel_tls_skip_verify – but it will be removed in v5.

redis_sentinel_tls_cert

String, default "" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_sentinel_tls_cert_pem option.

redis_sentinel_tls_key

String, default "" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_sentinel_tls_key_pem option.

redis_sentinel_tls_root_ca

String, default "" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_sentinel_tls_root_ca_pem option.

redis_sentinel_tls_server_name

String, default "" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.

Haproxy instead of Sentinel configuration

Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case.

For example, you can use something like this in Haproxy config:

listen redis
server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2
server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup
bind *:16379
mode tcp
option tcpka
option tcplog
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
balance roundrobin

And then just point Centrifugo to this Haproxy:

centrifugo --config=config.json --engine=redis --redis_address="localhost:16379"

Redis sharding

Centrifugo has built-in Redis sharding support.

This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale.

At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples.

To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this:

config.json
{
...
"engine": "redis",
"redis_address": [
"127.0.0.1:6379",
"127.0.0.1:6380",
]
}

To start Centrifugo with Redis instances on different hosts:

config.json
{
...
"engine": "redis",
"redis_address": [
"192.168.1.34:6379",
"192.168.1.35:6379",
]
}

If you also need to customize AUTH password, Redis DB number then you can use an extended address notation.

note

Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers.

When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).

Redis cluster

Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example:

{
...
"redis_cluster_address": [
"localhost:30001,localhost:30002,localhost:30003"
]
}

You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start.

To set the same over environment variable:

CENTRIFUGO_REDIS_CLUSTER_ADDRESS="localhost:30001" CENTRIFUGO_ENGINE=redis ./centrifugo

If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array:

{
...
"redis_cluster_address": [
"localhost:30001,localhost:30002,localhost:30003",
"localhost:30101,localhost:30102,localhost:30103"
]
}

Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump).

To reproduce the same over environment variable use space to separate different clusters:

CENTRIFUGO_REDIS_CLUSTER_ADDRESS="localhost:30001,localhost:30002 localhost:30101,localhost:30102" CENTRIFUGO_ENGINE=redis ./centrifugo

Other Redis compatible

When using Redis engine it's possible to point Centrifugo not only to Redis itself, but also to the other Redis compatible server. Such servers may work just fine if implement Redis protocol and support all the data structures Centrifugo uses and have PUB/SUB implemented.

Some known options:

  • AWS Elasticache – it was reported to work, but we suggest you testing the setup including failover tests and work under load.
  • KeyDB – should work fine with Centrifugo, no known problems at this point regarding Centrifugo compatibility.
  • DragonflyDB - should work fine starting from DragonflyDB 1.3.0 and with redis_force_resp2 Centrifugo option on. We have not tested a Redis Cluster emulation mode provided by DragonflyDB yet. We suggest you testing the setup including failover tests and work under load.

Tarantool engine

EXPERIMENTAL

Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics.

caution

EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves.

There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge.

info

To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together.

There are several supported Tarantool topologies to which Centrifugo can connect:

  • One standalone Tarantool instance
  • Many standalone Tarantool instances and consistently shard data between them
  • Tarantool running in Cartridge
  • Tarantool with replica and automatic failover in Cartridge
  • Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them
  • Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7)

After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes):

config.json
{
...
"engine": "tarantool",
"tarantool_address": "127.0.0.1:3301"
}

See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework.

See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.

Tarantool engine options

tarantool_address

String or array of strings. Default tcp://127.0.0.1:3301.

Connection address to Tarantool.

tarantool_mode

String, default standalone

A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication).

All modes support client-side consistent sharding (similar to what Redis engine provides).

tarantool_user

String, default "". Allows setting a user.

tarantool_password

String, default "". Allows setting a password.

history_meta_ttl

Duration, default 2160h.

Same option as for Memory engine and Redis engine also applies to Tarantool case.

Nats broker

It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below.

Limitations:

  • Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available.
  • Nats wildcard channel subscriptions with symbols * and > not supported.

First start Nats server:

$ nats-server
[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7
[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set]
[3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222
[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4
[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready

Then start Centrifugo with broker option:

centrifugo --broker=nats --config=config.json

And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine):

centrifugo --broker=nats --config=config.json --port=8001

Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.

Options

nats_url

String, default nats://127.0.0.1:4222.

Connection url in format nats://derek:pass@localhost:4222.

nats_prefix

String, default centrifugo.

Prefix for channels used by Centrifugo inside Nats.

nats_dial_timeout

Duration, default 1s.

Timeout for dialing with Nats.

nats_write_timeout

Duration, default 1s.

Write (and flush) timeout for a connection to Nats.

- + \ No newline at end of file diff --git a/docs/server/history_and_recovery.html b/docs/server/history_and_recovery.html index cc8d1f16c..a9759d383 100644 --- a/docs/server/history_and_recovery.html +++ b/docs/server/history_and_recovery.html @@ -16,13 +16,13 @@ - +

History and recovery

Centrifugo engines can maintain publication history for channels with configured history size and TTL.

History design

History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero.

Centrifugo is designed with an idea that history streams are ephemeral (can be created on the fly without explicit create call from Centrifugo users) and can expire or can be lost at any moment. Centrifugo provides a way for a client to understand that channel history lost. In this case, the main application database should be the source of truth and state recovery.

When history is on every message published into a channel is saved into a history stream. The persistence properties of history are dictated by a Centrifugo engine used. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside Tarantool spaces.

Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of state recovery anymore.

tip

History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.

History iteration API

History iteration based on three fields:

  • limit
  • since
  • reverse

Combining these fields you can iterate over a stream in both directions.

Get current stream top offset and epoch:

history(limit: 0, since: null, reverse: false)

Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default):

history(limit: -1, since: null, reverse: false)

Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default):

history(limit: -1, since: null, reverse: true)

Get history from the current beginning (up to 10):

history(limit: 10, since: null, reverse: false)

Get history from the current end in reversed direction (up to 10):

history(limit: 10, since: null, reverse: true) 

Get up to 10 publications since known stream position (described by offset and epoch values):

history(limit: 10, since: {offset: 0, epoch: "epoch"}, reverse: false)

Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest):

history(limit: 10, since: {offset: 11, epoch: "epoch"}, reverse: true)

Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun):

// Iterate by 10.
limit := 10
// Paginate in reversed order first, then invert it.
reverse := true
// Start with nil StreamPosition, then fill it with value while paginating.
var sp *gocent.StreamPosition

for {
historyResult, err = c.History(
ctx,
channel,
gocent.WithLimit(limit),
gocent.WithReverse(reverse),
gocent.WithSince(sp),
)
if err != nil {
log.Fatalf("Error calling history: %v", err)
}
for _, pub := range historyResult.Publications {
log.Println(pub.Offset, "=>", string(pub.Data))
sp = &gocent.StreamPosition{
Offset: pub.Offset,
Epoch: historyResult.Epoch,
}
}
if len(historyResult.Publications) < limit {
// Got all pubs, invert pagination direction.
reverse = !reverse
log.Println("end of stream reached, change iteration direction")
}
}

Automatic message recovery

One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while.

In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnect at the same time).

danger

Message recovery protocol feature designed to be used together with reasonably small history stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time.

To enable recovery mechanism for channels set the force_recovery boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. It's also possible to ask for enabling recovery from the client-side when configuring Subscription object – in this case client must have a permission to call history API.

When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not.

The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default.

Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client SDKs (for bidirectional transport).

The recovery process works this way:

  1. Let's suppose client subscribes on a channel with recovery on.
  2. Client receives subscribe reply from Centrifugo with two values: stream epoch and stream top offset, those values are saved by an SDK implementation.
  3. Every received publication has incremental offset, client SDK increments locally saved offset on each publication from a channel.
  4. Let's say at this point client disconnected for a while.
  5. Upon resubscribing to a channel SDK provides last seen epoch anf offset to Centrifugo.
  6. Centrifugo tries to load all the missed publications starting from the stream position provided by a client.
  7. If Centrifugo decides it can successfully recover client's state – then all missed publications returned in subscribe reply and client receives recovered: true in subscribed event context, and publication event handler of Subscription is called for every missed publication. Otherwise no publications returned and recovered flag of subscribed event context is set to false.

epoch is useful for cases when history storage is temporary and can lose the history stream entirely. In this case comparing epoch values gives Centrifugo a tip that while publications exist and theoretically have same offsets as before - the stream is actually different, so it's impossible to use it for the recovery process.

To summarize, here is a list of possible scenarios when Centrifugo can't recover client's state for a channel and provides recovered: false flag in subscribed event context:

  • number of missed publications exceeds client_recovery_max_publication_limit option
  • number of missed publications exceeds history_size namespace option
  • client was away for a long time and history stream expired according to history_ttl namespace option
  • storage used by Centrifugo engine lost the stream (restart, number of shards changed, cleared by the administrator, etc.)

Having said this all, Centrifugo recovery is nice to keep the continuity of the connection and subscription. This speed-ups resubscribe in many cases and helps the backend to survive mass reconnect scenario since you avoid lots of requests for state loading. For setups with millions of connections this can be a life-saver. But we recommend applications to always have a way to load the state from the main application database. For example, on app reload.

You can also manually implement your own recovery logic on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client resubscribe completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.

- + \ No newline at end of file diff --git a/docs/server/infra_tuning.html b/docs/server/infra_tuning.html index b4ae483de..fe8f8afcf 100644 --- a/docs/server/infra_tuning.html +++ b/docs/server/infra_tuning.html @@ -16,14 +16,14 @@ - +

Infrastructure tuning

As Centrifugo deals with lots of persistent connections your operating system and server infrastructure must be ready for it.

Open files limit

You should increase a max number of open files Centrifugo process can open if you want to handle more connections.

To get the current open files limit run:

ulimit -n

On Linux you can check limits for a running process using:

cat /proc/<PID>/limits

The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default.

See this document to get more info on how to increase this number.

If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536.

You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).

Ephemeral port exhaustion

Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing.

The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple:

source ip | source port | destination ip | destination port

On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default).

In order to eliminate a problem you can:

  • Increase the ephemeral port range by tuning ip_local_port_range option
  • Deploy more Centrifugo server instances to load balance across
  • Deploy more load balancer instances
  • Use virtual network interfaces

See a post in Pusher blog about this problem and more detailed solution steps.

Sockets in TIME_WAIT state

On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state.

Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side.

Look how many socket descriptors in TIME_WAIT state.

netstat -an |grep TIME_WAIT | grep <CENTRIFUGO_PID> | wc -l

Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html

The advices here are similar to ephemeral port exhaustion problem:

  • Increase the ephemeral port range by tuning ip_local_port_range option
  • Deploy more Centrifugo server instances to load balance across
  • Deploy more load balancer instances
  • Use virtual network interfaces

Proxy max connections

Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established.

Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.

Conntrack table

More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.

Additional server protection

You should also consider adding additional protection to your Centrifugo endpoints. Centrifugo itself provides several options (described in configuration section) regarding server protection from the malicious behavior. Though an additional layer of DDOS protection on network or infrastructure level is highly recommended. For example, you may want to limit the number of connections coming from particular IP address.

Here we list some possible ways you can use to protect your Centrifugo installation:

The list is not exhaustive of course.

- + \ No newline at end of file diff --git a/docs/server/load_balancing.html b/docs/server/load_balancing.html index 7b905a1c6..45b9d5b25 100644 --- a/docs/server/load_balancing.html +++ b/docs/server/load_balancing.html @@ -16,7 +16,7 @@ - + @@ -28,7 +28,7 @@ can be such a balancer too.

In this section we will look at Nginx configuration to deploy Centrifugo.

Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections.

There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).

Separate domain for Centrifugo

upstream centrifugo {
# uncomment ip_hash if using SockJS transport with many upstream servers.
#ip_hash;
server 127.0.0.1:8000;
}

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

#server {
# listen 80;
# server_name centrifugo.example.com;
# rewrite ^(.*) https://$server_name$1 permanent;
#}

server {

server_name centrifugo.example.com;

listen 80;

#listen 443 ssl;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_certificate /etc/nginx/ssl/server.crt;
#ssl_certificate_key /etc/nginx/ssl/server.key;

include /etc/nginx/mime.types;
default_type application/octet-stream;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1000;
gzip_proxied any;

# Only retry if there was a communication error, not a timeout
# on the Centrifugo server (to avoid propagating "queries of death"
# to all frontends)
proxy_next_upstream error;

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;

location /connection {
proxy_pass http://centrifugo;
proxy_buffering off;
keepalive_timeout 65;
proxy_read_timeout 60s;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}

location / {
proxy_pass http://centrifugo;
}

error_page 500 502 503 504 /50x.html;

location = /50x.html {
root /usr/share/nginx/html;
}
}

Embed to a location of web site

upstream centrifugo {
# uncomment ip_hash if using SockJS transport with many upstream servers.
#ip_hash;
server 127.0.0.1:8000;
}

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {

# ... your web site Nginx config

location /centrifugo/ {
rewrite ^/centrifugo/(.*) /$1 break;
proxy_pass http://centrifugo;
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
}

location /centrifugo/connection {
rewrite ^/centrifugo(.*) $1 break;
proxy_pass http://centrifugo;
proxy_buffering off;
keepalive_timeout 65;
proxy_read_timeout 60s;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}

worker_connections

You may also need to update worker_connections option of Nginx:

events {
worker_connections 65535;
}
- + \ No newline at end of file diff --git a/docs/server/monitoring.html b/docs/server/monitoring.html index 75dc47c06..fe04a9e2f 100644 --- a/docs/server/monitoring.html +++ b/docs/server/monitoring.html @@ -16,13 +16,13 @@ - +

Metrics monitoring

Centrifugo supports reporting metrics in Prometheus format and can automatically export metrics to Graphite.

Prometheus

To enable Prometheus endpoint start Centrifugo with prometheus option on:

config.json
{
...
"prometheus": true
}

This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.

Graphite

To enable automatic export to Graphite (via TCP):

config.json
{
...
"graphite": true,
"graphite_host": "localhost",
"graphite_port": 2003
}

By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection.

If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).

Grafana dashboard

Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.

- + \ No newline at end of file diff --git a/docs/server/observability.html b/docs/server/observability.html index e21ded4a0..85dcd8074 100644 --- a/docs/server/observability.html +++ b/docs/server/observability.html @@ -16,13 +16,13 @@ - +

Server observability

To provide a better server observability Centrifugo supports reporting metrics in Prometheus format and can automatically export metrics to Graphite.

Metrics

Prometheus metrics

To enable Prometheus endpoint start Centrifugo with prometheus option on:

config.json
{
...
"prometheus": true
}

This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.

Graphite metrics

To enable automatic export to Graphite (via TCP):

config.json
{
...
"graphite": true,
"graphite_host": "localhost",
"graphite_port": 2003
}

By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection.

If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).

Grafana dashboard

Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.

Exposed metrics

Here is a description of various metrics exposed by Centrifugo.

centrifugo_node_messages_sent_count

  • Type: Counter
  • Labels: type
  • Description: Tracks the number of messages sent by a node to the broker.
  • Usage: Use this metric to monitor the outgoing message rate and detect any anomalies or spikes in the data flow.

centrifugo_node_messages_received_count

  • Type: Counter
  • Labels: type
  • Description: Measures the number of messages received from the broker.
  • Usage: Helps in understanding the incoming message rate and ensures the node is receiving data as expected.

centrifugo_node_action_count

  • Type: Counter
  • Labels: action
  • Description: Counts the number of various actions called within the node.
  • Usage: Useful for tracking specific actions' usage and frequency.

centrifugo_node_num_clients

  • Type: Gauge
  • Description: Shows the current number of clients connected to the node.
  • Usage: Monitor the client connections to ensure the node is not reaching its capacity.

centrifugo_node_num_users

  • Type: Gauge
  • Description: Displays the number of unique users connected to the node.
  • Usage: Helps in understanding user engagement and capacity planning.

centrifugo_node_num_subscriptions

  • Type: Gauge
  • Description: Indicates the number of active subscriptions.
  • Usage: Use this to monitor the subscription levels and identify any potential issues or required optimizations.

centrifugo_node_num_nodes

  • Type: Gauge
  • Description: Shows the total number of nodes in the cluster.
  • Usage: Essential for monitoring the size of the cluster and ensuring that all nodes are operational.

centrifugo_node_build_info

  • Type: Gauge
  • Labels: version
  • Description: Provides build information of the node.
  • Usage: Helps in tracking the version of the application running across different environments.

centrifugo_node_num_channels

  • Type: Gauge
  • Description: Counts the number of channels with one or more subscribers.
  • Usage: Useful for monitoring the activity and utilization of channels.

centrifugo_node_survey_duration_seconds

  • Type: Summary
  • Labels: op
  • Description: Captures the duration of surveys conducted by the node.
  • Usage: Helps in performance monitoring and identifying any delays or issues in survey operations.

centrifugo_client_num_reply_errors

  • Type: Counter
  • Labels: method, code
  • Description: Counts the number of errors in replies sent to clients.
  • Usage: Critical for error monitoring and ensuring smooth client interactions.

centrifugo_client_num_server_disconnects

  • Type: Counter
  • Labels: code
  • Description: Tracks the number of server-initiated disconnects.
  • Usage: Use this to monitor the health of client connections and identify potential issues with the server.

centrifugo_client_command_duration_seconds

  • Type: Summary
  • Labels: method
  • Description: Measures the duration of commands executed by clients.
  • Usage: Essential for performance monitoring and ensuring timely responses to client commands.

centrifugo_client_recover

  • Type: Counter
  • Labels: recovered
  • Description: Counts the number of recover operations performed.
  • Usage: Helps in tracking the system's resilience and recovery mechanisms.

centrifugo_client_connection_limit_reached_total

  • Type: Counter
  • Labels: None
  • Description: Number of refused connections due to the node client connection limit.
  • Usage: Useful for monitoring the load on the Centrifugo node and identifying when clients are being refused connections due to reaching the connection limit.

centrifugo_transport_connect_count

  • Type: Counter
  • Labels: transport
  • Description: Measures the number of connections to specific transports.
  • Usage: Use this to monitor the usage of different transports and ensure they are functioning as expected.

centrifugo_transport_messages_sent

  • Type: Counter
  • Labels: transport, frame_type, channel_namespace
  • Description: Tracks the number of messages sent to client connections over specific transports.
  • Usage: Essential for understanding the data flow and performance of different transports.

centrifugo_transport_messages_sent_size

  • Type: Counter
  • Labels: transport, frame_type, channel_namespace
  • Description: Measures the size of messages sent to client connections over specific transports.
  • Usage: Helps in monitoring the network bandwidth usage and optimizing the data transfer.

centrifugo_transport_messages_received

  • Type: Counter
  • Labels: transport, frame_type, channel_namespace
  • Description: Counts the number of messages received from client connections over specific transports.
  • Usage: Important for ensuring that messages are being successfully received and processed.

centrifugo_transport_messages_received_size

  • Type: Counter
  • Labels: transport, frame_type, channel_namespace
  • Description: Measures the size of messages received from client connections over specific transports.
  • Usage: Use this metric to monitor the incoming data size and optimize the application's performance.

centrifugo_proxy_duration_seconds

  • Type: Summary & Histogram
  • Labels: protocol, type
  • Description: Captures the duration of proxy calls.
  • Usage: Critical for understanding the performance of proxy calls and identifying any potential bottlenecks or issues.

centrifugo_proxy_errors

  • Type: Counter
  • Labels: protocol, type
  • Description: Counts the number of errors occurred during proxy calls.
  • Usage: Helps in monitoring the reliability of proxy services and ensuring error-free operations.

centrifugo_granular_proxy_duration_seconds

  • Type: Summary & Histogram
  • Labels: type, name
  • Description: Measures the duration of granular proxy calls.
  • Usage: Use this to get more detailed insights into the performance of granular proxy operations.

centrifugo_granular_proxy_errors

  • Type: Counter
  • Labels: type, name
  • Description: Counts the number of errors in granular proxy calls.
  • Usage: Essential for error tracking and ensuring the stability of granular proxy services.

centrifugo_api_command_duration_seconds

  • Type: Summary
  • Labels: protocol, method
  • Description: Tracks the duration of API commands.
  • Usage: Helps in monitoring the API performance and ensuring timely responses.

centrifugo_api_command_duration_seconds_histogram

  • Type: Histogram
  • Labels: protocol, method
  • Description: Tracks the duration of API commands.
  • Usage: Helps in monitoring the API performance and ensuring timely responses.

Traces

OpenTelemetry

At this point Centrifugo can export traces for HTTP and GRPC server API requests in OpenTelemetry format.

To enable:

{
...
"opentelemetry": true,
"opentelemetry_api": true
}

OpenTelemetry must be explicitly turned on to avoid tracing overhead when it's not needed.

To configure OpenTelemetry export behaviour we are relying on OpenTelemetry environment vars supporting only HTTP export endpoints for now.

So a simple example to run Centrifugo with server API tracing would be running Jaeger with COLLECTOR_OTLP_ENABLED:

docker run --rm -it --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-p 16686:16686 \
-p 4318:4318 \
jaegertracing/all-in-one:latest

Then start Centrifugo:

OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" CENTRIFUGO_OPENTELEMETRY=1 CENTRIFUGO_OPENTELEMETRY_API=1 ./centrifugo

Send some API requests - and open http://localhost:16686 to see traces in Jaeger UI.

By default, Centrifugo exports traces in http/protobuf format. If you want to use GRPC exporter then it's possible to turn it on by setting environment variable OTEL_EXPORTER_OTLP_PROTOCOL to grpc (GRPC exporter format supported since Centrifugo v5.0.3).

Logs

Logging may be configured using log_level option. It may have the following values:

  • none
  • trace
  • debug
  • info (default)
  • warn
  • error

We generally do not recommend anything below info to be used in production.

By default Centrifugo logs to STDOUT. Usually this is what you need when running servers on modern infrastructures. Logging into file may be configured using log_file option.

- + \ No newline at end of file diff --git a/docs/server/presence.html b/docs/server/presence.html index 0c3201dcb..bc254dc89 100644 --- a/docs/server/presence.html +++ b/docs/server/presence.html @@ -16,13 +16,13 @@ - +

Online presence

The online presence feature of Centrifugo is a powerful tool that allows you to monitor and manage active users inside a specific channel. It provides an instantaneous snapshot of users currently subscribed to a specific channel. Additionally, Centrifugo may emit join and leave events when clients subscribe to channel and unsubscribe from it.

Enabling online presence

To enable online presence, you need to set the presence option to true for the specific channel namespace in your Centrifugo configuration.

{
"namespaces": [{
"namespace": "public",
"presence": true
}]
}

After enabling this you can query presence information over server HTTP/GRPC presence call:

curl --header "Content-Type: application/json" \
--header "X-API-Key: YOUR_API_KEY" \
--request POST \
--data '{"channel": "public:test"}' \
http://localhost:8000/api/presence

See description of presence API.

Also, a shorter version of presence which only contains two counters - number of clients and number of unique users in channel - may be called:

curl --header "Content-Type: application/json" \
--header "X-API-Key: YOUR_API_KEY" \
--request POST \
--data '{"channel": "public:test"}' \
http://localhost:8000/api/presence_stats

See description of presence stats API.

Retrieving presence on the client side

Once presence enabled, you can retrieve the presence information on the client side too by calling the presence method on the channel.

To do this you need to give the client permission to call presence. Once done, presence may be retrieved from the subscription:

const resp = await subscription.presence(channel);

It's also available on the top-level of the client (for example, if you need to call presence for server-side subscription):

const resp = await client.presence(channel);

If the permission check has passed successfully – both methods will return an object containing information about currently subscribed clients.

Also, presenceStats method is avalable:

const resp = await subscription.presenceStats(channel);

Join and leave events

It's also possible to enable real-time tracking of users joining or leaving a channel by listening to join and leave events on the client side.

By default, Centrifugo does not send these events and they must be explicitly turned on for channel namespace:

{
"namespaces": [{
"namespace": "public",
"presence": true,
"join_leave": true,
"force_push_join_leave": true
}]
}

Then on the client side:

subscription.on('join', function(joinCtx) {
console.log('client joined:', joinCtx);
});

subscription.on('leave', function(leaveCtx) {
console.log('client left:', leaveCtx);
});

And the same on client top-level for the needs of server-side subscriptions (analogous to the presence call described above).

These events provide real-time updates and can be used to keep track of user activity and manage live interactions.

You can combine join/leave events with presence information and maintain a list of currently active subscribers - for example show the list of online players in the game room updated in real-time.

Implementation notes

The online presence feature might increase the load on your Centrifugo server, since Centrifugo need to maintain an addition data structure. Therefore, it is recommended to use this feature judiciously based on your server's capability and the necessity of real-time presence data in your application.

Always make sure to secure the presence data, as it could expose sensitive information about user activity in your application. Ensure appropriate security measures are in place.

Join and leave events delivered with at most once guarantee.

See more about presence design in design overview chapter.

Also check out FAQ which mentions scalability concerns for presence data and join/leave events.

Conclusion

The online presence feature of Centrifugo is a highly useful tool for real-time applications. It provides instant and live data about user activity, which can be critical for interactive features in chats, collaborative tools, multiplayer games, or live tracking systems. Make sure to configure and use this feature appropriately to get the most out of your real-time application.

- + \ No newline at end of file diff --git a/docs/server/proxy.html b/docs/server/proxy.html index ed809073c..64cb8f5c2 100644 --- a/docs/server/proxy.html +++ b/docs/server/proxy.html @@ -16,13 +16,13 @@ - +

Proxy events to the backend

It's possible to proxy some client connection events from Centrifugo to the application backend and react to them in a custom way. For example, it's possible to authenticate connection via request from Centrifugo to application backend, refresh client sessions and answer to RPC calls sent by a client over bidirectional connection. Also, you may control subscription and publication permissions using these hooks.

The list of events that can be proxied:

  • connect – called when a client connects to Centrifugo, so it's possible to authenticate user, return custom data to a client, subscribe connection to several channels, attach meta information to the connection, and so on. Works for bidirectional and unidirectional transports.
  • refresh - called when a client session is going to expire, so it's possible to prolong it or just let it expire. Can also be used just as a periodical connection liveness callback from Centrifugo to app backend. Works for bidirectional and unidirectional transports.
  • subscribe - called when clients try to subscribe on a channel, so it's possible to check permissions and return custom initial subscription data. Works for bidirectional transports only.
  • sub_refresh - called when a client subscription is going to expire, so it's possible to prolong it or just let it expire. Can also be used just as a periodical subscription liveness callback from Centrifugo to app backend. Works for bidirectional and unidirectional transports.
  • publish - called when a client tries to publish into a channel, so it's possible to check permissions and optionally modify publication data. Works for bidirectional transports only.
  • rpc - called when a client sends RPC, you can do whatever logic you need based on a client-provided RPC method and data. Works for bidirectional transports only.
  • and starting from Centrifugo v5.1.0 we have experimental proxy subscription streams which are a bit special, so we describe them in a special doc chapter.

At the moment Centrifugo can proxy these events over two protocols:

  • HTTP (using JSON-based communication)
  • GRPC (exchanging Protobuf messages)
tip

Centrifugo does not emit unsubscribe and disconnect events at this point. For the reasoning and workarounds check out the answer in Centrifugo FAQ.

HTTP proxy

HTTP proxy in Centrifugo converts client connection events into HTTP calls to the application backend.

HTTP request structure

All HTTP proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).

Proxy HTTP headers

The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example:

config.json
{
...
"proxy_http_headers": [
"Origin",
"User-Agent",
"Cookie",
"Authorization",
"X-Real-Ip",
"X-Forwarded-For",
"X-Request-Id"
]
}

Alternatively, you can set a list of headers via an environment variable (space separated):

export CENTRIFUGO_PROXY_HTTP_HEADERS="Cookie User-Agent X-B3-TraceId X-B3-SpanId" ./centrifugo
note

Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend.

Starting from Centrifugo v5.0.2 it's possible to configure static set of headers to be appended to all HTTP proxy requests:

config.json
{
...
"proxy_static_http_headers": {
"X-Custom-Header": "custom value"
}
}

proxy_static_http_headers is a map with string keys and string values. You may also set it over environment variable using JSON object string:

export CENTRIFUGO_PROXY_STATIC_HTTP_HEADERS='{"X-Custom-Header": "custom value"}'

Static headers may be overriden by the header from client connection request if you proxy the header with the same name inside proxy_http_headers option showed above.

Proxy GRPC metadata

When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied.

See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.

Connect proxy

With the following options in the configuration file:

{
...
"proxy_connect_endpoint": "http://localhost:3000/centrifugo/connect",
"proxy_connect_timeout": "1s"
}

– connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request.

danger

Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections.

Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. In many cases your existing session mechanism will provide user authentication details to the connect proxy handler.

tip

If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server.

This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired).

Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json"
}

Expected response example:

{"result": {"user": "56"}}

This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result.

Several app examples which use connect proxy can be found in our blog:

Connect request fields

This is what sent from Centrifugo to application backend in case of connect proxy request.

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
namestringyesoptional name of the client (this field will only be set if provided by a client on connect)
versionstringyesoptional version of the client (this field will only be set if provided by a client on connect)
dataJSONyesoptional data from client (this field will only be set if provided by a client on connect)
b64datastringyesoptional data from the client in base64 format (if the binary proxy mode is used)
channelsArray of stringsyeslist of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result

Connect result fields

This is what application returns to Centrifugo inside result field in case of connect proxy request.

FieldTypeOptionalDescription
userstringnouser ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests
expire_atintegeryesa timestamp (Unix seconds in the future) when connection must be considered expired. If not set or set to 0 connection won't expire at all
infoJSONyesa connection info JSON
b64infostringyesbinary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages
dataJSONyesa custom data to send to the client in connect command response.
b64datastringyesa custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client
channelsarray of stringsyesallows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions
subsmap of SubscribeOptionsyesmap of channels with options to subscribe connection to. See more details about server-side subscriptions
metaJSON object (ex. {"key": "value"})yesa custom data to attach to connection (this won't be exposed to client-side)

Options

proxy_connect_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

Example

Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return "56" as user ID):

class CentrifugoConnectHandler(tornado.web.RequestHandler):

def check_xsrf_cookie(self):
pass

def post(self):
self.set_header('Content-Type', 'application/json; charset="utf-8"')
data = json.dumps({
'result': {
'user': '56'
}
})
self.write(data)


def main():
options.parse_command_line()
app = tornado.web.Application([
(r'/centrifugo/connect', CentrifugoConnectHandler),
])
app.listen(3000)
tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
main()

This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side.

We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea.

What if connection is unauthenticated/unauthorized to connect?

In this case return a disconnect object as a response. See Return custom disconnect section. Depending on whether you want connection to reconnect or not (usually not) you can select the appropriate disconnect code. Sth like this in response:

{
"disconnect": {
"code": 4501,
"reason": "unauthorized"
}
}

– may be sufficient enough. Choosing codes and reason is up to the developer, but follow the rules described in Return custom disconnect section.

Refresh proxy

With the following options in the configuration file:

{
...
"proxy_refresh_endpoint": "http://localhost:3000/centrifugo/refresh",
"proxy_refresh_timeout": "1s"
}

– Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side.

The payload sent to app backend in refresh request (when the connection is going to expire):

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56"
}

Expected response example:

{"result": {"expire_at": 1565436268}}

Refresh request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc.)
protocolstringnoprotocol type used by client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Refresh result fields

FieldTypeOptionalDescription
expiredboolyesa flag to mark the connection as expired - the client will be disconnected
expire_atintegeryesa timestamp in the future when connection must be considered expired
infoJSONyesa connection info JSON
b64infostringyesbinary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages

Options

proxy_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

RPC proxy

With the following option in the configuration file:

{
...
"proxy_rpc_endpoint": "http://localhost:3000/centrifugo/connect",
"proxy_rpc_timeout": "1s"
}

RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket connection (or any other bidirectional transport Centrifugo supports) in a bidirectional way.

Payload example sent to app backend in RPC request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"method": "getCurrentPrice",
"data":{"params": {"object_id": 12}}
}

Expected response example:

{"result": {"data": {"answer": "2019"}}}

RPC request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket or sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
methodstringyesan RPC method string, if the client does not use named RPC call then method will be omitted
dataJSONyesRPC custom data sent by client
b64datastringyeswill be set instead of data field for binary proxy mode
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

RPC result fields

FieldTypeOptionalDescription
dataJSONyesRPC response - any valid JSON is supported
b64datastringyescan be set instead of data for binary response encoded in base64 format

Options

proxy_rpc_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

See below on how to return a custom error.

Subscribe proxy

With the following option in the configuration file:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s"
}

– subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel.

info

Subscribe proxy does not proxy subscriptions with token and subscriptions to user-limited channels at the moment. That's because those are already providing channel access control. Subscribe proxy assumes that all the permission management happens on the backend side when processing proxy request. So if you need to get subscribe proxy requests for all channels in the system - do not use subscription tokens and user-limited channels.

Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace.

So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s",
"proxy_subscribe": true
}

Or for channels in namespace sun:

{
...
"proxy_subscribe_endpoint": "http://localhost:3000/centrifugo/subscribe",
"proxy_subscribe_timeout": "1s",
"namespaces": [{
"name": "sun",
"proxy_subscribe": true
}]
}

Payload example sent to app backend in subscribe request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "chat:index"
}

Expected response example if subscription is allowed:

{"result": {}}

Subscribe request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket or sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnoa string channel client wants to subscribe to
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)
dataJSONyescustom data from client sent with subscription request (this field will only be set if provided by a client on subscribe).
b64datastringyesoptional subscription data from the client in base64 format (if the binary proxy mode is used).

Subscribe result fields

FieldTypeOptionalDescription
infoJSONyesa channel info JSON
b64infostringyesa binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using
dataJSONyesa custom data to send to the client in subscribe command reply.
b64datastringyesa custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client
overrideOverride objectyesAllows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields)
expire_atintegeryesa timestamp (Unix seconds in the future) when subscription must be considered expired. If not set or set to 0 subscription won't expire at all. Supported since Centrifugo v5.0.4

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
force_push_join_leaveBoolValueyesOverride force_push_join_leave
force_positioningBoolValueyesOverride force_positioning
force_recoveryBoolValueyesOverride force_recovery

BoolValue is an object like this:

{
"value": true/false
}

See below on how to return an error in case you don't want to allow subscribing.

Options

proxy_subscribe_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

What if connection is not allowed to subscribe?

In this case you can return error object as a subscribe handler response. See return custom error section.

In general, frontend applications should not try to subscribe to channels for which access is not allowed. But these situations can happen or malicious user can try to subscribe to a channel. In most scenarios returning:

{
"error": {
"code": 403,
"message": "permission denied"
}
}

– is sufficient enough. Error code may be not 403 actually, no real reason to force HTTP semantics here - so it's up to Centrifugo user to decide. Just keep it in range [400, 1999] as described here.

If case of returning response above, on client side unsubscribed event of Subscription object will be called with error code 403. Subscription won't resubscribe automatically after that.

Publish proxy

With the following option in the configuration file:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s"
}

– publish calls sent by a client will be proxied to proxy_publish_endpoint.

This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side.

Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too.

So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s",
"publish": true,
"proxy_publish": true
}

Or for channels in namespace sun:

{
...
"proxy_publish_endpoint": "http://localhost:3000/centrifugo/publish",
"proxy_publish_timeout": "1s",
"namespaces": [{
"name": "sun",
"publish": true,
"proxy_publish": true
}]
}

Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace).

Payload example sent to app backend in a publish request:

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "chat:index",
"data":{"input":"hello"}
}

Expected response example if publish is allowed:

{"result": {}}

Publish request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs)
protocolstringnoprotocol type used by the client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnoa string channel client wants to publish to
dataJSONyesdata sent by client
b64datastringyeswill be set instead of data field for binary proxy mode
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Publish result fields

FieldTypeOptionalDescription
dataJSONyesan optional JSON data to send into a channel instead of original data sent by a client
b64datastringyesa binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing
skip_historyboolyeswhen set to true Centrifugo won't save publication to the channel history

See below on how to return an error in case you don't want to allow publishing.

Options

proxy_publish_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

Sub refresh proxy

With the following options in the configuration file:

{
...
"proxy_sub_refresh_endpoint": "http://localhost:3000/centrifugo/sub_refresh",
"proxy_sub_refresh_timeout": "1s"
}

– Centrifugo will call proxy_sub_refresh_endpoint when it's time to refresh the subscription. Centrifugo itself will ask your backend about subscription validity instead of subscription refresh workflow on the client-side.

Like subscribe and publish proxy types, sub refresh proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_sub_refresh that enables sub refresh proxy for channels in the namespace. Only subscriptions which have expiration time will be validated over sub refresh proxy endpoint.

Sub refresh proxy may be used as a periodical Subscription liveness callback from Centrifugo to app backend.

caution

In the current implementation the delay of Subscription refresh requests from Centrifugo to application backend may be up to one minute (was implemented this way from a simplicity and efficiency perspective). We assume this should be enough for many scenarios. But this may be improved if needed. Please reach us out with a detailed description of your use case where you want more accurate requests to refresh subscriptions.

So to enable sub refresh proxy for channels without namespace define proxy_sub_refresh on a top configuration level:

{
...
"proxy_sub_refresh_endpoint": "http://localhost:3000/centrifugo/sub_refresh",
"proxy_sub_refresh": true
}

Or for channels in namespace sun:

{
...
"proxy_sub_refresh_endpoint": "http://localhost:3000/centrifugo/publish",
"namespaces": [{
"name": "sun",
"proxy_sub_refresh": true
}]
}

The payload sent to app backend in sub refresh request (when the subscription is going to expire):

{
"client":"9336a229-2400-4ebc-8c50-0a643d22e8a0",
"transport":"websocket",
"protocol": "json",
"encoding":"json",
"user":"56",
"channel": "channel"
}

Expected response example:

{"result": {"expire_at": 1565436268}}

Sub refresh request fields

FieldTypeOptionalDescription
clientstringnounique client ID generated by Centrifugo for each incoming connection
transportstringnotransport name (ex. websocket, sockjs, uni_sse etc.)
protocolstringnoprotocol type used by client (json or protobuf at moment)
encodingstringnoprotocol encoding type used (json or binary at moment)
userstringnoa connection user ID obtained during authentication process
channelstringnochannel for which Subscription is going to expire
metaJSONyesa connection attached meta (off by default, enable with "proxy_include_connection_meta": true)

Sub refresh result fields

FieldTypeOptionalDescription
expiredboolyesa flag to mark the subscription as expired - the client will be disconnected
expire_atintegeryesa timestamp in the future (Unix seconds) when subscription must be considered expired
infoJSONyesa channel info JSON
b64infostringyesbinary channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages

Options

proxy_sub_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.

Return custom error

Application backend can return JSON object that contains an error to return it to the client:

{
"error": {
"code": 1000,
"message": "custom error"
}
}

Applications must use error codes in range [400, 1999]. Error code field is uint32 internally.

note

Returning custom error does not apply to response for refresh and sub refresh proxy requests as there is no sense in returning an error (will not reach client anyway). I.e. custom error is only processed for connect, subscribe, publish and rpc proxy types.

Return custom disconnect

Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way:

{
"disconnect": {
"code": 4500,
"reason": "disconnect reason"
}
}

Application must use numbers in the range 4000-4999 for custom disconnect codes:

  • codes in range [4000, 4499] give client an advice to reconnect
  • codes in range [4500, 4999] are terminal codes – client won't reconnect upon receiving it.

Code is uint32 internally. Numbers outside of 4000-4999 range are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 ASCII symbols (i.e. 32 bytes max).

note

Returning custom disconnect does not apply to response for refresh and sub refresh proxy requests as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason. I.e. custom disconnect is only processed for connect, subscribe, publish and rpc proxy types.

GRPC proxy

Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server.

GRPC service definitions can be found in the Centrifugo repository: proxy.proto.

tip

GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions.

Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept).

All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy:

config.json
{
...
"proxy_connect_endpoint": "grpc://localhost:12000",
"proxy_connect_timeout": "1s"
}

Refresh proxy:

config.json
{
...
"proxy_refresh_endpoint": "grpc://localhost:12000",
"proxy_refresh_timeout": "1s"
}

Or for RPC proxy:

config.json
{
...
"proxy_rpc_endpoint": "grpc://localhost:12000",
"proxy_rpc_timeout": "1s"
}

For publish proxy in namespace chat:

config.json
{
...
"proxy_publish_endpoint": "grpc://localhost:12000",
"proxy_publish_timeout": "1s"
"namespaces": [
{
"name": "chat",
"publish": true,
"proxy_publish": true
}
]
}

Use subscribe proxy for all channels without namespaces:

config.json
{
...
"proxy_subscribe_endpoint": "grpc://localhost:12000",
"proxy_subscribe_timeout": "1s",
"proxy_subscribe": true
}

So the same as for HTTP, just the different endpoint scheme.

GRPC proxy options

Some additional options exist to control GRPC proxy behavior.

proxy_grpc_cert_file

String, default: "".

Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used.

proxy_grpc_credentials_key

String, default "" (i.e. not used).

Add custom key to per-RPC credentials.

proxy_grpc_credentials_value

String, default "" (i.e. not used).

A custom value for proxy_grpc_credentials_key.

proxy_grpc_compression

Bool, default false (i.e. not used).

If true then gzip compression will be used for each GRPC proxy call (available since Centrifugo v5.1.0).

GRPC proxy example

We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.:

  1. Copy proxy Protobuf definitions
  2. Generate GRPC code
  3. Run backend service with you custom business logic
  4. Point Centrifugo to it.

Header proxy rules

Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios:

Client protocol typeProxy typeClient headersClient metadata
HTTPHTTPIn proxy request headersN/A
GRPCGRPCN/AIn proxy request metadata
HTTPGRPCIn proxy request metadataN/A
GRPCHTTPN/AIn proxy request headers

Binary mode

As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding.

Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this.

Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side).

But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON.

In this case it's possible to turn Centrifugo proxy into binary mode by using:

config.json
{
...
"proxy_binary_encoding": true
}

Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues.

While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine.

Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.

Granular proxy mode

By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture).

Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.

Enable granular proxy mode

Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below.

config.json
{
...
"granular_proxy_mode": true
}

Defining a list of proxies

When using granular proxy mode on configuration top level you can define "proxies" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint.

Here is an example:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [
{
"name": "connect",
"endpoint": "http://localhost:3000/centrifugo/connect",
"timeout": "500ms",
"http_headers": ["Cookie"]
},
{
"name": "refresh",
"endpoint": "http://localhost:3000/centrifugo/refresh",
"timeout": "500ms"
},
{
"name": "subscribe1",
"endpoint": "http://localhost:3001/centrifugo/subscribe"
},
{
"name": "publish1",
"endpoint": "http://localhost:3001/centrifugo/publish"
},
{
"name": "rpc1",
"endpoint": "http://localhost:3001/centrifugo/rpc"
},
{
"name": "subscribe2",
"endpoint": "http://localhost:3002/centrifugo/subscribe"
},
{
"name": "publish2",
"endpoint": "grpc://localhost:3002"
}
{
"name": "rpc2",
"endpoint": "grpc://localhost:3002"
}
]
}

Let's look at all fields for a proxy object which is possible to set for each proxy inside "proxies" array.

Field nameField typeRequiredDescription
namestringyesUnique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$
endpointstringyesHTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC.
timeoutduration (string)noProxy request timeout, default "1s"
http_headersarray of stringsnoList of headers to proxy, by default no headers
static_http_headersmap[string]stringnoStatic set of headers to add to HTTP proxy requests. Note these headers only appended to HTTP proxy requests from Centrifugo to backend. Available since Centrifugo v5.0.2
grpc_metadataarray of stringsnoList of GRPC metadata keys to proxy, by default no metadata keys
binary_encodingboolnoUse base64 for payloads
include_connection_metaboolnoInclude meta information (attached on connect)
grpc_cert_filestringnoPath to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used.
grpc_credentials_keystringnoAdd custom key to per-RPC credentials.
grpc_credentials_valuestringnoA custom value for grpc_credentials_key.
grpc_compressionboolnoIf true then gzip compression will be used for each GRPC proxy call (available since Centrifugo v5.1.0)

Granular connect and refresh

As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event.

To enable connect proxy:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"connect_proxy_name": "connect"
}

We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea.

Let's also add refresh proxy:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"connect_proxy_name": "connect",
"refresh_proxy_name": "refresh"
}

Granular subscribe, publish, sub refresh

Subscribe, publish and sub refresh proxies work per-namespace. This means that subscribe_proxy_name, publish_proxy_name and sub_refresh_proxy_name are just channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object.

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"namespaces": [
{
"name": "ns1",
"subscribe_proxy_name": "subscribe1",
"publish": true,
"publish_proxy_name": "publish1"
},
{
"name": "ns2",
"subscribe_proxy_name": "subscribe2",
"publish": true,
"publish_proxy_name": "publish2"
}
]
}

If namespace does not have "subscribe_proxy_name" or "subscribe_proxy_name" is empty then no subscribe proxy will be used for a namespace.

If namespace does not have "publish_proxy_name" or "publish_proxy_name" is empty then no publish proxy will be used for a namespace.

If namespace does not have "sub_refresh_proxy_name" or "sub_refresh_proxy_name" is empty then no sub refresh proxy will be used for a namespace.

tip

You can define subscribe_proxy_name, publish_proxy_name, sub_refresh_proxy_name on configuration top level – and in this case publish, subscribe and sub refresh requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.

Granular RPC

Analogous to channel namespaces it's possible to configure rpc namespaces:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [...],
"namespaces": [...],
"rpc_namespaces": [
{
"name": "rpc_ns1",
"rpc_proxy_name": "rpc1",
},
{
"name": "rpc_ns2",
"rpc_proxy_name": "rpc2"
}
]
}

The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces).

Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start.

tip

The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.

- + \ No newline at end of file diff --git a/docs/server/proxy_streams.html b/docs/server/proxy_streams.html index 6de851287..e1ea8e537 100644 --- a/docs/server/proxy_streams.html +++ b/docs/server/proxy_streams.html @@ -16,13 +16,13 @@ - +

Proxy subscription streams

Experimental

This is an experimental extension of Centrifugo proxy. We appreciate your feedback to make sure it's useful and solves real-world problems before marking it as stable and commit to the API.

Proxy subscription streams (available since Centrifugo v5.1.0) allow pushing data towards client channel subscription directly and individually from your application backend over the unidirectional GRPC stream. Additionally, bidirectional GRPC streams may be utilized to stream data in both directions.

The stream is established between Centrifugo and your application backend as soon as user subscribes to a channel. Subscription streams may be useful if you want to generate individual streams and these streams should only work for a time while client is subscribed to a channel.

In this case Centrifugo plays a role of WebSocket-to-GRPC streaming proxy – keeping numerous real-time connections from your application's clients and establishing GRPC streams to the backend, multiplexing them using a pool of HTTP/2 (transport used by GRPC) connections:

Our bidirectional WebSocket fallbacks (HTTP-streaming and SSE) and experimental WebTransport work with proxy subscription streams too. So it's possible to say that Centrifugo may be also Webtransport-to-GRPC proxy or SSE-to-GRPC proxy.

Subscription streams allow achiving functionality similar to what Websocketd provides but over the network.

Scalability concerns

Using proxy subscription streams increases resource usage on both Centrifugo and app backend sides because it involves more moving parts such as goroutines, additional buffers, connections, etc.

The feature is quite niche. Read carefully the motivation described in this doc. If you don't really need proxy streams – prefer using Centrifugo usual approach by always publishing messages to channels over Centrifugo publish API whenever an event happens. This is efficient and Centrifugo just drops messages in case of no active subscribers in a channel. I.e. follow our idiomatic guidelines.

tip

Use proxy subscription streams only when really needed. Specifically, proxy subscription stream may be very useful to stream data for a limited time upon some user action in the app.

At the same time proxy subscription streams should scale well horizontally with adding more servers. But scaling GRPC is more involved and using GRPC streams results into more resources utilized than with the common Centrifugo approach, so make sure the resource consumption is sufficient for your system by performing load tests with your expected load profile.

The thing is that sometimes proxy streams is the only way to achieve the desired behaviour – at that point they shine even though require more resources and developer effort. Also, not every use case involves tens of thousands of subscriptions/connections to worry about – be realistic about your practical situation.

Motivation and design

Here is a diagram which shows the sequence of events happening when using subscription streams:

Subscription streams generally solve a task of integrating with third-party streaming providers or external process, possibly with custom filtering. They come into play when it's not feasible to continuously stream all data to various channels, and when you need to deallocate resources on the backend side as soon as stream is not needed anymore.

Subscription streams may be also considered as streaming requests – an isolated way to stream something from the backend to the client or from the client to the backend.

Let's describe a real-life use case. Say you have Loki for keeping logs, it provides a streaming API for tailing logs. You decided to stream logs towards your app's clients. When client subscribes to some channel in Centrifugo and the unidirectional stream established between Centrifugo and your backend – you can make sure client has proper permissions for the requested resource and backend then starts tailing Loki logs (or other third-party system, this may be Twitter streaming API, MQTT broker, GraphQL subscription, or streaming query to the real-time database such as RethinkDB). As soon as backend receives log events from Loki it transfers them towards client over Centrifugo.

Client can provide custom data upon subscribing to a channel which makes it possible to pass query filters from the frontend app to the backend. In the example with Loki above this may be a LogQL query.

In case of proxy subscription streams all the client authentication may be delegated to common Centrifugo mechanisms, so when the channel stream is established you know the ID of user (obtained by Centrifugo from JWT auth process or over connect proxy). You can additionally check channel permissions at the moment of stream establishement.

As soon as client unsubscribes from the channel – Centrifugo closes the unidirectional GRPC stream – so your backend will notice that. If client disconnects – stream is closed also.

If for some reason connection between Centrifugo and backend is closed – then Centrifugo will unsubscribe a client with insufficient state reason and a client will soon resubscribe to a channel (managed automatically by our SDKs).

You may wonder – what about the same channel name used for subscribing to such a stream by different connections. Proxy stream is an individual link between a client and a backend – Centrifugo transfers stream data published to the GRPC stream by the backend only to the client connection to whom the stream belongs. I.e. messages sent by the backend to GRPC stream are not broadcasted to other channel subscribers. But if you will use server API for publishing – then message will be broadcasted to all channel subscribers even if they are currently using proxy stream within that channel.

Presence and join/leave features will work as usual for channels with subscription proxy streams. If different connections use the same channel they will be able to use presence (if enabled) to see who else is currently in the channel, and may receive join/leave messages (if enabled).

Channel history for proxy subscription streams

For the case of proxy subscription streams Centrifugo channel history and recovery features do not really make sense. Proxy stream is an individual direct link between client and your backend through Centrifugo which is always re-established from scratch upon re-subscription or connection drops. The benefit of the history and its semantics are not clear in this case and can only bring undesired overhead (because Centrifugo will have to use broker, now messages just go directly towards connections without broker/engine involved at all).

Only for client-side subscriptions

Subscription streams work only with client-side subscriptions (i.e. when client explicitly subscribes to a channel on the application's frontend side). Server-side subscriptions won't initiate a GRPC stream to the backend.

Don't forget that Centrifugo namespace system is very flexible – so you can always combine different approaches using different channel namespaces. You can always use subscription streams only for some channels belonging to a specific namespace.

Unidirectional subscription streams

From the configuration point of view subscription streams may be enabled for channel namespace just as additional type of proxy. The important difference is that only GRPC endpoints may be used - as we are using GRPC streaming RPCs for this functionality.

You can configure subscription streams for channels very similar to how subscribe proxy is configured.

First, configure subscribe stream proxy, pointing it to the backend which implements our proxy stream GRPC service contract:

config.json
{
...
"proxy_subscribe_stream_endpoint": "grpc://localhost:12000",
"proxy_subscribe_stream_timeout": "3s"
}

Only grpc:// endpoints are supported since we are heavily relying on GRPC streaming ecosystem here. In this case proxy_subscribe_stream_timeout defines a time how long Centrifugo waits for a first message from a stream which contains subscription details to transfer to a client.

Then you can enable subscription streams for channels on a namespace level:

config.json
{
...
"proxy_subscribe_stream_endpoint": "grpc://localhost:12000",
"proxy_subscribe_stream_timeout": "3s",
"namespaces": [
{
"name": "streams",
"proxy_subscribe_stream": true
}
]
}
info

You can not use subscribe, publish, sub_refresh proxy configurations together with stream proxy configuration inside one channel namespace.

That's it on Centrifugo side. Now on the app backend you should implement GRPC service according to the following definitions:

service CentrifugoProxy {
...
// SubscribeUnidirectional allows handling unidirectional subscription streams.
rpc SubscribeUnidirectional(SubscribeRequest) returns (stream StreamSubscribeResponse);
...
}

GRPC service definitions can be found in the Centrifugo repository: proxy.proto - same as we described before, probably you already have a service which implements some methods from it. If you don't – just follow GRPC tutorials for your programming language to generate server stubs from our Protobuf schema – and you are ready to describe stream logic.

Here we are looking at unidirectional subscription stream – so the next thing to do is to implement streaming handler on the application backend side which contains stream business logic, i.e. implement SubscribeUnidirectional streaming rpc handler.

tip

You can write GRPC handlers to handle proxy subscription streams in any language with good GRPC support.

A basic example of such handler in Go may look like this (error handling skipped for brevity):

package main

import (
"fmt"
"log"
"math"
"net"
"strconv"
"time"

pb "example/proxyproto"
"google.golang.org/grpc"
)

type streamServer struct {
pb.UnimplementedCentrifugoProxyServer
}

func (s *streamerServer) SubscribeUnidirectional(
req *pb.SubscribeRequest,
stream pb.CentrifugoProxy_SubscribeUnidirectionalServer,
) error {
started := time.Now()
fmt.Println("unidirectional subscribe called with request", req)
defer func() {
fmt.Println("unidirectional subscribe finished, elapsed", time.Since(started))
}()
_ = stream.Send(&pb.StreamSubscribeResponse{
SubscribeResponse: &pb.SubscribeResponse{},
})
// Now publish data to a stream every 1 second.
for {
select {
case <-stream.Context().Done():
return stream.Context().Err()
case <-time.After(1000 * time.Millisecond):
}
pub := &pb.Publication{Data: []byte(`{"input": "` + strconv.Itoa(i) + `"}`)}
_ = stream.Send(&pb.StreamSubscribeResponse{Publication: pub})
}
}

func main() {
lis, _ := net.Listen("tcp", ":12000")
s := grpc.NewServer(grpc.MaxConcurrentStreams(math.MaxUint32))
pb.RegisterCentrifugoProxyServer(s, &streamServer{})
_ = s.Serve(lis)
}
tip

Note we have increased grpc.MaxConcurrentStreams for server to handle more simultaneous streams than allowed by default. Usually default is 100 but can differ in various GRPC server implementations. If you expect more streams then you need a bigger value.

Centrifugo has some rules about messages in streams. Upon stream establishement Centrifugo expects backend to send first message from a stream - this is a StreamSubscribeResponse with SubscribeResponse in it. Centrifugo waits for this message before replying to the client's subscription command. This way we can communicate initial state with a client and make sure streaming is properly established with all permission checks passed. After sending initial message you can send events (publications) as they appear in your system.

Now everything should be ready to test it out from the client side: just subscribe to a channel where stream proxy is on with our SDK – and you will see your stream handler called and data streamed from it to a client. For example, with our Javascript SDK:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {
getToken: getTokenImplementation
});

client.connect();

const sub = client.newSubscription('streams:123e4567-e89b-12d3-a456-426614174000', {
data: {}
}).on('publication', function(ctx) {
console.log("received publication from a channel", ctx.data);
});

sub.subscribe();

Again, while we are still looking for a proper semantics of subscription streams we recommend using unique channel names for all on-demand streams you are establishing.

Bidirectional subscription streams

In addition to unidirectional streams, Centrifugo supports bidirectional streams upon client channel subscription. In this case client gets a possibility to stream any data to the backend utilizing bidirectional communication. Client can send messages to a bidirectional stream by using .publish(data) method of a Subscription object.

In terms of general design bidirectional streams behave similar to unidirectional streams as described above.

When enabling subscription streams, Centrifugo uses unidirectional GRPC streams by default – as those should fit most of the use cases proxy subscription streams were introduced for. To tell Centrifugo use bidirectional streaming add proxy_subscribe_stream_bidirectional flag to the namespace configuration:

config.json
{
...
"proxy_subscribe_stream_endpoint": "grpc://localhost:12000",
"proxy_subscribe_stream_timeout": "3s",
"namespaces": [
{
"name": "streams",
"proxy_subscribe_stream": true,
"proxy_subscribe_stream_bidirectional": true
}
]
}

On the backend you need to implement the following streaming handler:

service CentrifugoProxy {
...
// SubscribeBidirectional allows handling bidirectional subscription streams.
rpc SubscribeBidirectional(stream StreamSubscribeRequest) returns (stream StreamSubscribeResponse);
...
}

The first StreamSubscribeRequest message in stream will contain SubscribeRequest and Centrifugo expects StreamSubscribeResponse with SubscribeResponse from the backend – just like in unidirectional case described above.

An example of such handler in Go language which echoes back all publications from client (error handling skipped for brevity):

func (s *streamerServer) SubscribeBidirectional(
stream pb.CentrifugoProxy_SubscribeBidirectionalServer,
) error {
started := time.Now()
fmt.Println("bidirectional subscribe called")
defer func() {
fmt.Println("bidirectional subscribe finished, elapsed", time.Since(started))
}()
// First message always contains SubscribeRequest.
req, _ := stream.Recv()
fmt.Println("subscribe request received", req.SubscribeRequest)
_ = stream.Send(&pb.StreamSubscribeResponse{
SubscribeResponse: &pb.SubscribeResponse{},
})
// The following messages contain publications from client.
for {
req, _ = stream.Recv()
data := req.Publication.Data
fmt.Println("data from client", string(data))
var cd clientData
pub := &pb.Publication{Data: data}
_ = stream.Send(&pb.StreamSubscribeResponse{Publication: pub})
}
}

Granular proxy mode

Granular proxy mode works with subscription streams in the same manner as for other Centrifugo proxy types.

Here is an example how you can define different subscribe stream proxies for different namespaces:

config.json
{
...
"granular_proxy_mode": true,
"proxies": [
{
"name": "stream_1",
"endpoint": "grpc://localhost:3000",
"timeout": "500ms",
},
{
"name": "stream_2",
"endpoint": "grpc://localhost:3001",
"timeout": "500ms",
}
],
"namespaces": [
{
"name": "ns1",
"subscribe_stream_proxy_name": "stream_1"
},
{
"name": "ns2",
"subscribe_stream_proxy_name": "stream_2"
}
]
}

Full example

Full example which demonstrates proxy subscribe stream backend implemented in Go language may be found in Centrifugo examples repo.

- + \ No newline at end of file diff --git a/docs/server/server_api.html b/docs/server/server_api.html index e89599d9c..340808d2f 100644 --- a/docs/server/server_api.html +++ b/docs/server/server_api.html @@ -16,13 +16,13 @@ - +

Server API walkthrough

Server API provides different methods to interact with Centrifugo. Specifically, in most cases this is an entry point for publications into channels coming from your application backend. There are two kinds of server API available at the moment:

  • HTTP API
  • GRPC API

Both are similar in terms of request/response structures.

HTTP API

Server HTTP API works on /api path prefix (by default). The request format is super-simple: this is an HTTP POST request to a specific method API path with application/json Content-Type, X-API-Key header and with JSON body.

Instead of many words, here is an example how to call publish method:

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "test", "data": {"value": "test_value"}}' \
http://localhost:8000/api/publish
tip

You can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests structures.

Below we look at all aspects of HTTP API in detail, starting with information about authorization.

HTTP API authorization

HTTP API is protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like:

config.json
{
...
"api_key": "<YOUR_API_KEY>"
}

This API key must be set in the request X-API-Key header in this way:

X-API-Key: <YOUR_API_KEY>

It's also possible to pass API key over URL query param. Simply add ?api_key=<YOUR_API_KEY> query param to the API endpoint. Keep in mind that passing the API key in the X-API-Key header is a recommended way as it is considered more secure.

To disable API key check on Centrifugo side you can use api_insecure configuration option. Use it in development only or make sure to protect the API endpoint by proxy or firewall rules in production – to prevent anyone with access to the endpoint to send commands over your unprotected Centrifugo API.

API key auth is not very safe for man-in-the-middle so we also recommended protecting Centrifugo with TLS.

API methods

Server API supports many methods. Let's describe them starting with the most important publish operation.

publish

Publish method allows publishing data into a channel (we call this message publication in Centrifugo). Most probably this is a command you'll use most of the time.

Here is an example of publishing message to Centrifugo:

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "chat", "data": {"text": "hello"}}' \
http://localhost:8000/api/publish

In case of successful publish you will get a response like this:

{
"result": {}
}

As an additional example, let's take a look how to publish to Centrifugo with requests library for Python:

import json
import requests

api_key = "YOUR_API_KEY"
data = json.dumps({
"channel": "docs",
"data": {
"content": "1"
}
})
headers = {'Content-type': 'application/json', 'X-API-Key': api_key}
resp = requests.post("https://centrifuge.example.com/api/publish", data=data, headers=headers)
print(resp.json())

In case of publication error, response object will contain error field. For example, let's publish to an unknown namespace (not defined in Centrifugo configuration):

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "unknown:chat", "data": {"text": "hello"}}' \
http://localhost:8000/api/publish

In response you will also get 200 OK, but payload will contain error field instead of result:

{
"error": {
"code": 102,
"message": "namespace not found"
}
}

error object contains error code and message - this is also the same for other commands described below.

Publish request

Field nameField typeRequiredDescription
channelstringyesName of channel to publish
dataany JSONyesCustom JSON data to publish into a channel
skip_historyboolnoSkip adding publication to history for this request
tagsmap[string]stringnoPublication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients
b64datastringnoCustom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field.

Publish result

Field nameField typeOptionalDescription
offsetintegeryesOffset of publication in history stream
epochstringyesEpoch of current stream

broadcast

broadcast is similar to publish but allows to efficiently send the same data into many channels:

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channels": ["user:1", "user:2"], "data": {"text": "hello"}}' \
http://localhost:8000/api/broadcast

Broadcast request

Field nameField typeRequiredDescription
channelsArray of stringsyesList of channels to publish data to
dataany JSONyesCustom JSON data to publish into each channel
skip_historyboolnoSkip adding publications to channels' history for this request
tagsmap[string]stringnoPublication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients
b64datastringnoCustom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field.

Broadcast result

Field nameField typeOptionalDescription
responsesArray of publish responsesnoResponses for each individual publish (with possible error and publish result)

subscribe

subscribe allows subscribing active user's sessions to a channel. Note, it's mostly for dynamic server-side subscriptions.

Subscribe request

Field nameField typeRequiredDescription
userstringyesUser ID to subscribe
channelstringyesName of channel to subscribe user to
infoany JSONnoAttach custom data to subscription (will be used in presence and join/leave messages)
b64infostringnoinfo in base64 for binary mode (will be decoded by Centrifugo)
clientstringnoSpecific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs)
sessionstringnoSpecific client session to subscribe (user still required to be set)
dataany JSONnoCustom subscription data (will be sent to client in Subscribe push)
b64datastringnoSame as data but in base64 format (will be decoded by Centrifugo)
recover_sinceStreamPosition objectnoStream position to recover from
overrideOverride objectnoAllows dynamically override some channel options defined in Centrifugo configuration (see below available fields)

Override object

FieldTypeOptionalDescription
presenceBoolValueyesOverride presence
join_leaveBoolValueyesOverride join_leave
force_push_join_leaveBoolValueyesOverride force_push_join_leave
force_positioningBoolValueyesOverride force_positioning
force_recoveryBoolValueyesOverride force_recovery

BoolValue is an object like this:

{
"value": true/false
}

Subscribe result

Empty object at the moment.

unsubscribe

unsubscribe allows unsubscribing user from a channel.

Unsubscribe request

Field nameField typeRequiredDescription
userstringyesUser ID to unsubscribe
channelstringyesName of channel to unsubscribe user to
clientstringnoSpecific client ID to unsubscribe (user still required to be set)
sessionstringnoSpecific client session to disconnect (user still required to be set).

Unsubscribe result

Empty object at the moment.

disconnect

disconnect allows disconnecting a user by ID.

Disconnect request

Field nameField typeRequiredDescription
userstringyesUser ID to disconnect
clientstringnoSpecific client ID to disconnect (user still required to be set)
sessionstringnoSpecific client session to disconnect (user still required to be set).
whitelistArray of stringsnoArray of client IDs to keep
disconnectDisconnect objectnoProvide custom disconnect object, see below

Disconnect object

Field nameField typeRequiredDescription
codeintyesDisconnect code
reasonstringyesDisconnect reason

Disconnect result

Empty object at the moment.

refresh

refresh allows refreshing user connection (mostly useful when unidirectional transports are used).

Refresh request

Field nameField typeRequiredDescription
userstringyesUser ID to refresh
clientstringnoClient ID to refresh (user still required to be set)
sessionstringnoSpecific client session to refresh (user still required to be set).
expiredboolnoMark connection as expired and close with Disconnect Expired reason
expire_atintnoUnix time (in seconds) in the future when the connection will expire

Refresh result

Empty object at the moment.

presence

presence allows getting channel online presence information (all clients currently subscribed on this channel).

tip

Presence in channels is not enabled by default. See how to enable it over channel options. Also check out dedicated chapter about it.

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "chat"}' \
http://localhost:8000/api/presence

Example response:

{
"result": {
"presence": {
"c54313b2-0442-499a-a70c-051f8588020f": {
"client": "c54313b2-0442-499a-a70c-051f8588020f",
"user": "42"
},
"adad13b1-0442-499a-a70c-051f858802da": {
"client": "adad13b1-0442-499a-a70c-051f858802da",
"user": "42"
}
}
}
}

Presence request

Field nameField typeRequiredDescription
channelstringyesName of channel to call presence from

Presence result

Field nameField typeOptionalDescription
presenceMap of client ID (string) to ClientInfo objectnoOffset of publication in history stream

ClientInfo

Field nameField typeOptionalDescription
clientstringnoClient ID
userstringnoUser ID
conn_infoJSONyesOptional connection info
chan_infoJSONyesOptional channel info

presence_stats

presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID).

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "chat"}' \
http://localhost:8000/api/presence_stats

Example response:

{
"result": {
"num_clients": 0,
"num_users": 0
}
}

Presence stats request

Field nameField typeRequiredDescription
channelstringyesName of channel to call presence from

Presence stats result

Field nameField typeOptionalDescription
num_clientsintegernoTotal number of clients in channel
num_usersintegernoTotal number of unique users in channel

history

history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter.

tip

History in channels is not enabled by default. See how to enable it over channel options.

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"channel": "chat", "limit": 2}' \
http://localhost:8000/api/history

Example response:

{
"result": {
"epoch": "qFhv",
"offset": 4,
"publications": [
{
"data": {
"text": "hello"
},
"offset": 2
},
{
"data": {
"text": "hello"
},
"offset": 3
}
]
}
}

History request

Field nameField typeRequiredDescription
channelstringyesName of channel to call history from
limitintnoLimit number of returned publications, if not set in request then only current stream position information will present in result (without any publications)
sinceStreamPosition objectnoTo return publications after this position
reverseboolnoIterate in reversed order (from latest to earliest)

StreamPosition

Field nameField typeRequiredDescription
offsetintegeryesOffset in a stream
epochstringyesStream epoch

History result

Field nameField typeOptionalDescription
publicationsArray of publication objectsyesList of publications in channel
offsetintegeryesTop offset in history stream
epochstringyesEpoch of current stream

history_remove

history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state.

History remove request

Field nameField typeRequiredDescription
channelstringyesName of channel to remove history

History remove result

Empty object at the moment.

channels

channels return active channels (with one or more active subscribers in it).

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{}' \
http://localhost:8000/api/channels

Channels request

Field nameField typeRequiredDescription
patternstringnoPattern to filter channels, we are using gobwas/glob library for matching

Channels result

Field nameField typeOptionalDescription
channelsMap of string to ChannelInfonoMap where key is channel and value is ChannelInfo (see below)

ChannelInfo

Field nameField typeOptionalDescription
num_clientsintegernoTotal number of connections currently subscribed to a channel
caution

Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels API call will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.

info

info method allows getting information about running Centrifugo nodes.

Example response:

{
"result": {
"nodes": [
{
"name": "Alexanders-MacBook-Pro.local_8000",
"num_channels": 0,
"num_clients": 0,
"num_users": 0,
"uid": "f844a2ed-5edf-4815-b83c-271974003db9",
"uptime": 0,
"version": ""
}
]
}
}

Info request

Empty object at the moment.

Info result

Field nameField typeOptionalDescription
nodesArray of Node objectsnoInformation about all nodes in a cluster

batch

Batch allows sending many commands in one request. Commands processed sequentially by Centrifugo, users should check individual error in each returned reply. Useful to avoid RTT latency penalty for each command sent, this is an analogue of pipelining.

Example with two publications in one request:

curl --header "X-API-Key: <API_KEY>" \
--request POST \
--data '{"commands": [{"publish": {"channel": "test1", "data": {}}}, {"publish": {"channel": "x:test2", "data": {}}}]}' \
http://localhost:8000/api/batch

Example response:

{
"replies":[
{"publish":{}},
{"error":{"code":102,"message":"unknown channel"}}
]
}

HTTP API libraries

Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header.

But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually:

Also, there are API libraries created by community:

tip

Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.

GRPC API

Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC.

GRPC API is also useful if you want to publish binary data to Centrifugo channels.

tip

GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description.

You can enable GRPC API in Centrifugo using grpc_api option:

config.json
{
...
"grpc_api": true
}

By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option.

Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file.

Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.

GRPC example for Python

For example for Python you need to run sth like this according to GRPC docs:

pip install grpcio-tools
python -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto

As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo:

import grpc
import api_pb2_grpc as api_grpc
import api_pb2 as api_pb

channel = grpc.insecure_channel('localhost:10000')
stub = api_grpc.CentrifugoApiStub(channel)

try:
resp = stub.Info(api_pb.InfoRequest())
except grpc.RpcError as err:
# GRPC level error.
print(err.code(), err.details())
else:
if resp.error.code:
# Centrifugo server level error.
print(resp.error.code, resp.error.message)
else:
print(resp.result)

Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.

GRPC example for Go

Here is a simple example of how to run Centrifugo with the GRPC Go client.

You need protoc, protoc-gen-go and protoc-gen-go-grpc installed.

First start Centrifugo itself with GRPC API enabled:

CENTRIFUGO_GRPC_API=1 centrifugo --config config.json

In another terminal tab:

mkdir centrifugo_grpc_example
cd centrifugo_grpc_example/
touch main.go
go mod init centrifugo_example
mkdir apiproto
cd apiproto
wget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto

Run protoc to generate code:

protoc -I ./ api.proto --go_out=. --go-grpc_out=.

Put the following code to main.go file (created on the last step above):

package main

import (
"context"
"log"
"time"

"centrifugo_example/apiproto"

"google.golang.org/grpc"
)

func main() {
conn, err := grpc.Dial("localhost:10000", grpc.WithInsecure())
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
client := apiproto.NewCentrifugoApiClient(conn)
for {
resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{
Channel: "chat:index",
Data: []byte(`{"input": "hello from GRPC"}`),
})
if err != nil {
log.Printf("Transport level error: %v", err)
} else {
if resp.GetError() != nil {
respError := resp.GetError()
log.Printf("Error %d (%s)", respError.Code, respError.Message)
} else {
log.Println("Successfully published")
}
}
time.Sleep(time.Second)
}
}

Then run:

go run main.go

The program starts and periodically publishes the same payload into chat:index channel.

GRPC API key authorization

You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey <KEY>. For example in Go language:

package main

import (
"context"
"log"
"time"

"centrifugo_example/apiproto"

"google.golang.org/grpc"
)

type keyAuth struct {
key string
}

func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": "apikey " + t.key,
}, nil
}

func (t keyAuth) RequireTransportSecurity() bool {
return false
}

func main() {
conn, err := grpc.Dial("localhost:10000", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{"xxx"}))
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
client := apiproto.NewCentrifugoClient(conn)
for {
resp, err := client.Publish(context.Background(), &PublishRequest{
Channel: "chat:index",
Data: []byte(`{"input": "hello from GRPC"}`),
})
if err != nil {
log.Printf("Transport level error: %v", err)
} else {
if resp.GetError() != nil {
respError := resp.GetError()
log.Printf("Error %d (%s)", respError.Code, respError.Message)
} else {
log.Println("Successfully published")
}
}
time.Sleep(time.Second)
}
}

For other languages refer to GRPC docs.

Transport error mode

By default, Centrifugo server API never returns transport level errors - for example it always returns 200 OK for HTTP API and never returns GRPC transport-level errors. Centrifugo returns its custom errors from API calls inside optional error field of response as we showed above in this doc. This means that API call to Centrifigo API may returns 200 OK, but in the error field you may find Centrifugo-specific 100: internal error.

Since Centrifugo v5.1.0 Centrifigo has an option to use transport-native error codes instead of Centrifugo error field in the response. The main motivation is make API calls friendly to integrate with the network ecosystem - for automatic retries, better logging, etc. In many situations this may be more obvious for humans also.

Let's show an example. Without any special options HTTP request to Centrifigo server API which contains error in response looks like this:

echo '{}' | http POST "http://localhost:8000/api/publish"
HTTP/1.1 200 OK
Content-Length: 46
Content-Type: application/json
Date: Sat, 19 Aug 2023 07:23:40 GMT

{
"error": {
"code": 107,
"message": "bad request"
}
}

Note - it returns 200 OK even though response contains error field. With transport error mode request-response may be transformed into the following:

echo '{}' | http POST "http://localhost:8000/api/publish" "X-Centrifugo-Error-Mode: transport"
HTTP/1.1 400 Bad Request
Content-Length: 36
Content-Type: application/json
Date: Sat, 19 Aug 2023 07:23:59 GMT

{
"code": 107,
"message": "bad request"
}

Transport error mode may be turned on globally:

  • using "api_error_mode": "transport" option for HTTP server API
  • using "grpc_api_error_mode": "transport" option for GRPC server API

Also, this mode may be used on per-request basis:

  • by setting custom header X-Centrifugo-Error-Mode: transport for HTTP (as we just showed in the example)
  • adding custom metadata key x-centrifugo-error-mode: transport for GRPC
caution

Note, that transport error mode does not help a lot with Batch and Broadcast APIs which are quite special because these calls contain many independent operations. For these calls you still need to look at individual error objects in response.

To achieve the goal we have an internal matching of Centrifugo API error codes to HTTP and GRPC error codes.

Centrifugo error code to HTTP code

func MapErrorToHTTPCode(err *Error) int {
switch err.Code {
case ErrorInternal.Code: // 100 -> HTTP 500
return http.StatusInternalServerError
case ErrorUnknownChannel.Code, ErrorNotFound.Code: // 102, 104 -> HTTP 404
return http.StatusNotFound
case ErrorBadRequest.Code, ErrorNotAvailable.Code: // 107, 108 -> HTTP 400
return http.StatusBadRequest
case ErrorUnrecoverablePosition.Code: // 112 -> HTTP 416
return http.StatusRequestedRangeNotSatisfiable
case ErrorConflict.Code: // 113 -> HTTP 409
return http.StatusConflict
default:
// Default to Internal Error for unmapped errors.
// In general should be avoided - all new API errors must be explicitly described here.
return http.StatusInternalServerError // HTTP 500
}
}

Centrifugo error code to GRPC code

func MapErrorToGRPCCode(err *Error) codes.Code {
switch err.Code {
case ErrorInternal.Code: // 100
return codes.Internal
case ErrorUnknownChannel.Code, ErrorNotFound.Code: // 102, 104
return codes.NotFound
case ErrorBadRequest.Code, ErrorNotAvailable.Code: // 107, 108
return codes.InvalidArgument
case ErrorUnrecoverablePosition.Code: // 112
return codes.OutOfRange
case ErrorConflict.Code: // 113
return codes.AlreadyExists
default:
// Default to Internal Error for unmapped errors.
// In general should be avoided - all new API errors must be explicitly described here.
return codes.Internal
}
}
- + \ No newline at end of file diff --git a/docs/server/server_subs.html b/docs/server/server_subs.html index 06cd7f3d9..37c18154a 100644 --- a/docs/server/server_subs.html +++ b/docs/server/server_subs.html @@ -16,13 +16,13 @@ - +

Server-side subscriptions

Centrifugo clients can initiate a subscription to a channel by calling the subscribe method of client API. In most cases, client-side subscriptions is a more flexible and recommended approach since a frontend usually knows which channels it needs to consume at a concrete moment.

But in some situations, all you need is to subscribe your connections to several channels on a server-side at the moment of connection establishment. So client effectively starts receiving publications from those channels without calling the subscribe API at all.

You can set a list of channels for a connection in two ways at the moment:

  • over connection JWT using channels claim, which is an array of strings
  • over connect proxy returning channels field in result (also an array of strings)
  • dynamically over server subscribe API

On the client-side, you need to listen for publications from server-side channels using a top-level client event handler. For example with centrifuge-js:

const centrifuge = new Centrifuge(address);

centrifuge.on('publication', function(ctx) {
const channel = ctx.channel;
const payload = JSON.stringify(ctx.data);
console.log('Publication from server-side channel', channel, payload);
});

centrifuge.connect();

I.e. listen for publications without any usage of subscription objects. You can look at channel publication relates to using field in callback context as shown in the example.

tip

The same best practices of working with channels and client-side subscriptions equally applicable to server-side subscription.

Dynamic server-side subscriptions

See subscribe and unsubscribe server API

Automatic personal channel subscription

It's possible to automatically subscribe a user to a personal server-side channel.

To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel.

For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above.

As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default "") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.

Maintain single user connection

Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes.

user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect).

This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel.

Example config:

config.json
{
"user_subscribe_to_personal": true,
"user_personal_single_connection": true,
"user_personal_channel_namespace": "personal",
"namespaces": [
{
"name": "personal",
"presence": true
}
]
}
note

Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.

- + \ No newline at end of file diff --git a/docs/server/tls.html b/docs/server/tls.html index c33f7dc5d..adecb1aa1 100644 --- a/docs/server/tls.html +++ b/docs/server/tls.html @@ -16,7 +16,7 @@ - + @@ -33,7 +33,7 @@ browsers such as Chrome 49 on Windows XP and IE8 on XP:

  • tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys.
  • tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment

grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints.

uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.

TLS for GRPC API

You can provide custom certificate files to configure TLS for GRPC API server.

  • grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file
  • grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server
  • grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server

TLS for GRPC unidirectional stream

You can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint.

  • uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file
  • uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server
  • uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server
- + \ No newline at end of file diff --git a/docs/transports/client_api.html b/docs/transports/client_api.html index c90dc9c85..c74fdaeb0 100644 --- a/docs/transports/client_api.html +++ b/docs/transports/client_api.html @@ -16,13 +16,13 @@ - +

Client SDK API

Centrifugo has several client SDKs to establish a real-time connection with a server. Centrifugo SDKs use WebSocket as the main data transport and send/receive messages encoded according to our bidirectional protocol. That protocol is built on top of the Protobuf schema (both JSON and binary Protobuf formats are supported). It provides asynchronous communication, sending RPC, multiplexing subscriptions to channels, etc. Client SDK wraps the protocol and exposes a set of APIs to developers.

This chapter describes the core concepts of client SDKs API. All our official real-time SDKs follow this specification. This document describes behaviour visible to SDK user, if you want to find out low-level client protocol framing details – look at client protocol document.

Most examples here are written using our Javascript real-time SDK (centrifuge-js), but all other Centrifugo connectors have very similar semantics and APIs very close to each other.

Client connection states

Client connection has 4 states:

  • disconnected
  • connecting
  • connected
  • closed
note

closed state is only implemented by SDKs where it makes sense (need to clean up allocated resources when app gracefully shuts down – for example in Java SDK we close thread executors used internally).

When a new Client is created it has a disconnected state. To connect to a server connect() method must be called. After calling connect Client moves to the connecting state. If a Client can't connect to a server it attempts to create a connection with an exponential backoff algorithm (with full jitter). If a connection to a server is successful then the state becomes connected.

If a connection is lost (due to a missing network for example, or due to reconnect advice received from a server, or due to some client-side error that can't be recovered without reconnecting) Client goes to the connecting state again. In this state Client tries to reconnect (again, with an exponential backoff algorithm).

The Client's state can become disconnected. This happens when Client's disconnect() method was called by a developer. Also, this can happen due to server advice from a server, or due to a terminal problem that happened on the client-side.

Here is a program where we create a Client instance, set callbacks to listen to state updates and establish a connection with a server:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

client.on('connecting', function(ctx) {
console.log('connecting', ctx);
});

client.on('connected', function(ctx) {
console.log('connected', ctx);
});

client.on('disconnected', function(ctx) {
console.log('disconnected', ctx);
});

client.connect();

In case of successful connection Client states will transition like this:

disconnected (initial) -> connecting (on('connecting') called) -> connected (on('connected') called).

In case of already connected Client temporary lost a connection with a server and then successfully reconnected:

connected -> connecting (on('connecting') called) -> connected (on('connected') called).

In case of already connected Client temporary lost a connection with a server, but got a terminal error upon reconnection:

connected -> connecting (on('connecting') called) -> disconnected (on('disconnected') called).

In case of already connected Client came across terminal condition (for example, if during a connection token refresh application found that user has no permission to connect anymore):

connected -> disconnected (on('disconnected') called).

Both connecting and disconnected events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why the Client went to the connecting state or to the disconnected state.

This diagram demonstrates possible Client state transitions:

Centrifugo scheme

You can also listen for all errors happening internally (which are not reflected by state changes, for example, transport errors happening on initial connect, transport during reconnect, connection token refresh related errors, etc) while the client works by using error event:

client.on('error', function(ctx) {
console.log('client error', ctx);
});

If you want to disconnect from a server call .disconnect() method:

client.disconnect();

In this case on('disconnected') will be called. You can call connect() again when you need to establish a connection.

closed state implemented in SDKs where resources like internal queues, thread executors, etc must be cleaned up when the Client is not needed anymore. All subscriptions should automatically go to the unsubscribed state upon closing. The client is not usable after going to a closed state.

Client common options

There are several common options available when creating Client instance.

  • option to set connection token and callback to get connection token upon expiration (see below mode details)
  • option to set connect data
  • option to configure operation timeout
  • tweaks for reconnect backoff algorithm (min delay, max delay)
  • configure max delay of server pings (to detect broken connection)
  • configure headers to send in WebSocket upgrade request (except centrifuge-js)
  • configure client name and version for analytics purpose

Client methods

  • connect() – connect to a server
  • disconnect() - disconnect from a server
  • close() - close Client if not needed anymore
  • send(data) - send asynchronous message to a server
  • rpc(method, data) - send arbitrary RPC and wait for response

Client connection token

All SDKs support connecting to Centrifugo with JWT. Initial connection token can be set in Client option upon initialization. Example:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {
token: 'JWT-GENERATED-ON-BACKEND-SIDE'
});

If the token sets connection expiration then the client SDK will keep the token refreshed. It does this by calling a special callback function. This callback must return a new token. If a new token with updated connection expiration is returned from callback then it's sent to Centrifugo. In case of error returned by your callback SDK will retry the operation after some jittered time.

An example:

async function getToken() {
if (!loggedIn) {
return ""; // Empty token or pre-generated token for anonymous users.
}
// Fetch your application backend.
const res = await fetch('http://localhost:8000/centrifugo/connection_token');
if (!res.ok) {
if (res.status === 403) {
// Return special error to not proceed with token refreshes,
// client will be disconnected.
throw new Centrifuge.UnauthorizedError();
}
// Any other error thrown will result into token refresh re-attempts.
throw new Error(`Unexpected status code ${res.status}`);
}
const data = await res.json();
return data.token;
}

const client = new Centrifuge(
'ws://localhost:8000/connection/websocket',
{
token: 'JWT-GENERATED-ON-BACKEND-SIDE', // Optional, getToken is enough.
getToken: getToken
}
);
tip

If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authentication. In this case SDK should attempt to get a connection token before establishing an initial connection.

Connection PING/PONG

PINGs sent by a server, a client should answer with PONGs upon receiving PING. If a client does not receive PING from a server for a long time (ping interval + configured delay) – the connection is considered broken and will be re-established.

Subscription states

Client allows subscribing on channels. This can be done by creating Subscription object.

const sub = centrifuge.newSubscription(channel);
sub.subscribe();

When anewSubscription method is called Client allocates a new Subscription instance and saves it in the internal subscription registry. Having a registry of allocated subscriptions allows SDK to manage resubscribes upon reconnecting to a server. Centrifugo connectors do not allow creating two subscriptions to the same channel – in this case, newSubscription can throw an exception.

Subscription has 3 states:

  • unsubscribed
  • subscribing
  • subscribed

When a new Subscription is created it has an unsubscribed state.

To initiate the actual process of subscribing to a channel subscribe() method of Subscription instance should be called. After calling subscribe() Subscription moves to subscribing state.

If subscription to a channel is not successful then depending on error type subscription can automatically resubscribe (with exponential backoff) or go to an unsubscribed state (upon non-temporary error). If subscription to a channel is successful then the state becomes subscribed.

const sub = client.newSubscription(channel);

sub.on('subscribing', function(ctx) {
console.log('subscribing');
});

sub.on('subscribed', function(ctx) {
console.log('subscribed');
});

sub.on('unsubscribed', function(ctx) {
console.log('unsubscribed');
});

sub.subscribe();

Subscriptions also go to subscribing state when Client connection (i.e. transport) becomes unavailable. Upon connection re-establishement all subscriptions which are not in unsubscribed state will resubscribe automatically.

In case of successful subscription states will transition like this:

unsubscribed (initial) -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called).

In case of connected and subscribed Client temporary lost a connection with a server and then succesfully reconnected and resubscribed:

subscribed -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called).

Both subscribing and unsubscribed events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why Subscription went to subscribing state or to unsubscribed state.

This diagram demonstrates possible Subscription state transitions:

Centrifugo scheme

You can listen for all errors happening internally in Subscription (which are not reflected by state changes, for example, temporary subscribe errors, subscription token related errors, etc) by using error event:

sub.on('error', function(ctx) {
console.log("subscription error", ctx);
});

If you want to unsubscribe from a channel call .unsubscribe() method:

sub.unsubscribe();

In this case on('unsubscribed') will be called. Subscription still kept in Client's registry, but no resubscription attempts will be made. You can call subscribe() again when you need Subscription again. Or you can remove Subscription from Client's registry (see below).

Subscription management

The client SDK provides several methods to manage the internal registry of client-side subscriptions.

newSubscription(channel, options) allocates a new Subscription in the registry or throws an exception if the Subscription is already there. We will discuss common Subscription options below.

getSubscription(channel) returns the existing Subscription by a channel from the registry (or null if it does not exist).

removeSubscription(sub) removes Subscription from Client's registry. Subscription is automatically unsubscribed before being removed. Use this to free resources if you don't need a Subscription to a channel anymore.

subscriptions() returns all registered subscriptions, so you can iterate over all and do some action if required (for example, you want to unsubscribe/remove all subscriptions).

Listen to channel publications

Of course the main point of having Subscriptions is the ability to listen for publications (i.e. messages published to a channel).

sub.on('publication', function(ctx) {
console.log("received publication", ctx);
});

Publication context has several fields:

  • data - publication payload, this can be JSON or binary data
  • offset - optional offset inside history stream, this is an incremental number
  • tags - optional tags, this is a map with string keys and string values
  • info - optional information about client connection who published this (only exists if publication comes from client-side publish() API).

So minimal code where we connect to a server and listen for messages published into example channel may look like:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

const sub = client.newSubscription('example').on('publication', function(ctx) {
console.log("received publication from a channel", ctx.data);
});

sub.subscribe();

client.connect();

Note, that we can call subscribe() before making a connection to a server – and this will work just fine, subscription goes to subscribing state and will be subscribed upon succesfull connection. And of course, it's possible to call .subscribe() after .connect().

Subscription recovery state

Subscriptions to channels with recovery option enabled maintain stream position information internally. On every publication received this information updated and used to recover missed publications upon resubscribe (caused by reconnect for example).

When you call unsubscribe() Subscription position state is not cleared. So it's possible to call subscribe() later and catch up a state.

The recovery process result – i.e. whether all missed publications recovered or not – can be found in on('subscribed') event context. Centrifuge protocol provides two fields:

  • wasRecovering - boolean flag that tells whether recovery was used during subscription process resulted into subscribed state. Can be useful if you want to distinguish first subscribe attempt (when subscription does not have any position information yet)
  • recovered - boolean flag that tells whether Centrifugo thinks that all missed publications can be successfully recovered and there is no need to load state from the main application database. It's always false when wasRecovering is false.

Subscription common options

There are several common options available when creating Subscription instance.

  • option to set subscription token and callback to get subscription token upon expiration (see below more details)
  • option to set subscription data (attached to every subscribe/resubscribe request)
  • options to tweak resubscribe backoff algorithm
  • option to start Subscription since known Stream Position (i.e. attempt recovery on first subscribe)
  • option to ask server to make subscription positioned (if not forced by a server)
  • option to ask server to make subscription recoverable (if not forced by a server)
  • option to ask server to push Join/Leave messages (if not forced by a server)

Subscription methods

  • subscribe() – start subscribing to a channel
  • unsubscribe() - unsubscribe from a channel
  • publish(data) - publish data to Subscription channel
  • history(options) - request Subscription channel history
  • presence() - request Subscription channel online presence information
  • presenceStats() - request Subscription channel online presence stats information (number of client connections and unique users in a channel).

Subscription token

All SDKs support subscribing to Centrifugo channels with JWT. Channel subscription token can be set as a Subscription option upon initialization. Example:

const sub = centrifuge.newSubscription(channel, {
token: 'JWT-GENERATED-ON-BACKEND-SIDE'
});
sub.subscribe();

If token sets subscription expiration client SDK will keep token refreshed. It does this by calling special callback function. This callback must return a new token. If new token with updated subscription expiration returned from a calbback then it's sent to Centrifugo. If your callback returns an empty string – this means user has no permission to subscribe to a channel anymore and subscription will be unsubscribed. In case of error returned by your callback SDK will retry operation after some jittered time.

An example:

async function getToken(ctx) {
// Fetch your application backend.
const res = await fetch('http://localhost:8000/centrifugo/subscription_token', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify({
channel: ctx.channel
})
});
if (!res.ok) {
if (res.status === 403) {
// Return special error to not proceed with token refreshes,
// client will be disconnected.
throw new Centrifuge.UnauthorizedError();
}
// Any other error thrown will result into token refresh re-attempts.
throw new Error(`Unexpected status code ${res.status}`);
}
const data = await res.json();
return data.token;
}

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

const sub = centrifuge.newSubscription(channel, {
token: 'JWT-GENERATED-ON-BACKEND-SIDE', // Optional, getToken is enough.
getToken: getToken
});
sub.subscribe();
tip

If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authorization for a channel subscription. In this case SDK should attempt to get a subscription token before initial subscribe.

Server-side subscriptions

We encourage using client-side subscriptions where possible as they provide a better control and isolation from connection. But in some cases you may want to use server-side subscriptions (i.e. subscriptions created by server upon connection establishment).

Technically, client SDK keeps server-side subscriptions in internal registry (similar to client-side subscriptions but without possibility to control them).

To listen for server-side subscription events use callbacks as shown in example below:

const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});

client.on('subscribed', function(ctx) {
// Called when subscribed to a server-side channel upon Client moving to
// connected state or during connection lifetime if server sends Subscribe
// push message.
console.log('subscribed to server-side channel', ctx.channel);
});

client.on('subscribing', function(ctx) {
// Called when existing connection lost (Client reconnects) or Client
// explicitly disconnected. Client continue keeping server-side subscription
// registry with stream position information where applicable.
console.log('subscribing to server-side channel', ctx.channel);
});

client.on('unsubscribed', function(ctx) {
// Called when server sent unsubscribe push or server-side subscription
// previously existed in SDK registry disappeared upon Client reconnect.
console.log('unsubscribed from server-side channel', ctx.channel);
});

client.on('publication', function(ctx) {
// Called when server sends Publication over server-side subscription.
console.log('publication receive from server-side channel', ctx.channel, ctx.data);
});

client.connect();

Server-side subscription events mostly mimic events of client-side subscriptions. But again – they do not provide control to the client and managed entirely by a server side.

Additionally, Client has several top-level methods to call with server-side subscription related operations:

  • publish(channel, data)
  • history(channel, options)
  • presence(channel)
  • presenceStats(channel)

Error codes

Server can return error codes in range 100-1999. Error codes in interval 0-399 reserved by Centrifuge/Centrifugo server. Codes in range [400, 1999] may be returned by application code built on top of Centrifuge/Centrifugo.

Server errors contain a temporary boolean flag which works as a signal that error may be fixed by a later retry.

Errors with codes 0-100 can be used by client-side implementation. Client-side errors may not have code attached at all since in many languages error can be distinguished by its type.

Unsubscribe codes

Server may return unsubscribe codes. Server unsubscribe codes must be in range [2000, 2999].

Unsubscribe codes >= 2500 coming from server to client result into automatic resubscribe attempt (i.e. client goes to subscribing state). Codes < 2500 result into going to unsubscribed state.

Client implementation can use codes <2000 for client-side specific unsubscribe reasons.

Disconnect codes

Server may send custom disconnect codes to a client. Custom disconnect codes must be in range [3000, 4999].

Client automatically reconnects upon receiving code in range 3000-3499, 4000-4499 (i.e. Client goes to connecting state). Other codes result into going to disconnected state.

Client implementation can use codes <3000 for client-side specific disconnect reasons.

RPC

An SDK provides a way to send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the real-time connection. RPC is only available when RPC proxy configured (so Centrifugo proxies the RPC to your application backend).

const rpcRequest = {'key': 'value'};
const data = await centrifuge.namedRPC('example_method', rpcRequest);

Channel history API

SDK provides a method to call publication history inside a channel (only for channels where history is enabled) to get last publications in a channel.

Get stream current top position:

const resp = await subscription.history();
console.log(resp.offset);
console.log(resp.epoch);

Get up to 10 publications from history since known stream position:

const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});
console.log(resp.publications);

Get up to 10 publications from history since current stream beginning:

const resp = await subscription.history({limit: 10});
console.log(resp.publications);

Get up to 10 publications from history since current stream end in reversed order (last to first):

const resp = await subscription.history({limit: 10, reverse: true});
console.log(resp.publications);

Presence and presence stats API

Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured):

For presence (full information about active subscribers in channel):

const resp = await subscription.presence();
// resp contains presence information - a map client IDs as keys
// and client information as values.

For presence stats (just a number of clients and unique users in a channel):

const resp = await subscription.presenceStats();
// resp contains a number of clients and a number of unique users.

SDK common best practices

  • Callbacks must be fast. Avoid blocking operations inside event handlers. Callbacks caused by protocol messages received from a server are called synchronously and connection read loop is blocked while such callbacks are being executed. Consider doing heavy work asynchronously.
  • Do not blindly rely on the current Client or Subscription state when making client API calls – state can change at any moment, so don't forget to handle errors.
  • Disconnect from a server when a mobile application goes to the background since a mobile OS can kill the connection at some point without any callbacks called.
- + \ No newline at end of file diff --git a/docs/transports/client_protocol.html b/docs/transports/client_protocol.html index 763bc2d0d..4de1403b0 100644 --- a/docs/transports/client_protocol.html +++ b/docs/transports/client_protocol.html @@ -16,13 +16,13 @@ - +

Client protocol

This chapter describes the core concepts of Centrifugo bidirectional client protocol – concentrating on framing level. If you want to find out details about exposed client API then look at client API document.

We need our own protocol on top of real-time transport due to several reasons:

  • We want request-response semantics (while our main transport – WebSocket – does not provide this out of the box)
  • Multiplex many subscriptions over a single physical connection
  • Efficient ping-pong support
  • Handle server disconnect advices
tip

In case of questions on how client protocol works/structured you can always look at existing client SDKs.

Protobuf schema

Centrifugo is built on top of Centrifuge library for Go. Centrifuge library uses its own framing for wrapping Centrifuge-specific messages – synchronous commands from a client to a server (which expect replies from a server) and asynchronous pushes.

Centrifuge client protocol is defined by a Protobuf schema. This is the source of truth.

tip

At the moment Protobuf schema contains some fields which are only used in client protocol v1. This is for backwards compatibility – server supports clients connecting over both client protocol v2 and client protocol v1. Client protocol v1 is considered deprecated and will be removed at some point in the future (giving enough time to our users to migrate).

Command-Reply

In bidirectional case client sends Command to a server and server sends Reply to a client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages.

Each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous transport where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before on SDK level.

In JSON case client can send command like this:

{"id": 1, "subscribe": {"channel": "example"}}

And client can expect something like this in response:

{"id": 1, "subscribe": {}}

Reply for different commands has corresponding field with command result ("subscribe" in example above).

Reply can also contain error if Command processing resulted into an error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can handle result object appropriately.

error looks like this in JSON case:

{
"code": 100,
"message": "internal server error",
"temporary": true
}

I.e. reply with error may look like this:

{"id": 1, "error": {"code": 100, "message": "internal server error"}}

We will talk more about error handling below.

Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow.

Current commands:

  • connect – sent to authenticate connection, sth like hello from a client which can carry authentication token and arbitrary data.
  • subscribe – sent to subscribe to a channel
  • unsubscribe - sent to unsubscribe from a channel
  • publish - sent to publish data into a channel
  • presence - sent to request presence information from a channel
  • presence_stats - sent to request presence stats information from a channel
  • history - sent to request history information for a channel
  • send - sent to send async message to a server (this command is a bit special since it must not contain id - as we don't wait for any response from a server in this case).
  • rpc - sent to send RPC to a channel (execute arbitrary logic and wait for response)
  • refresh - sent to refresh connection token
  • sub_refresh - sent to refresh channel subscription token

Asynchronous pushes

The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to a client at any moment - not as reaction to issued Command but as a message from a server to a client at arbitrary time. For example, this can be a message published into channel.

There are several types of asynchronous messages that can come from a server to a client.

  • pub is a message published into channel
  • join messages sent when someone joined (subscribed on) channel.
  • leave messages sent when someone left (unsubscribed from) channel.
  • unsubscribe message sent when a server unsubscribed current client from a channel:
  • subscribe may be sent when a server subscribes client to a channel.
  • disconnect may be sent be a server before closing connection and contains disconnect code/reason
  • message may be sent when server sends asynchronous message to a client
  • connect push can be sent in unidirectional transport case
  • refresh may be sent when a server refreshes client credentials (useful in unidirectional transports)

Top level batching

To reduce number of system calls one request from a client to a server and one response from a server to a client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data.

When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \n:

{"id": 1, "subscribe": {"channel": "ch1"}}
{"id": 2, "subscribe": {"channel": "ch2"}}

Here is an example how we do this in Javascript client when JSON format used:

function encodeCommands(commands) {
const encodedCommands = [];
for (const i in commands) {
if (commands.hasOwnProperty(i)) {
encodedCommands.push(JSON.stringify(commands[i]));
}
}
return encodedCommands.join('\n');
}
info

This doc uses JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo.

When Protobuf format used then many Command can be sent from a client to a server in a length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example.

The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there.

tip

Server can even send reply to a command and asynchronous message batched together in a one frame.

For example here is how we read server response and extracting individual replies in Javascript client when JSON format used:

function decodeReplies(data) {
const replies = [];
const encodedReplies = data.split('\n');
for (const i in encodedReplies) {
if (encodedReplies.hasOwnProperty(i)) {
if (!encodedReplies[i]) {
continue;
}
const reply = JSON.parse(encodedReplies[i]);
replies.push(reply);
}
}
return replies;
}

For Protobuf case see existing client implementations for decoding example.

Ping Pong

To maintain connection alive and detect broken connections server periodically sends empty commands to clients and expects empty replies from them.

When client does not receive ping from a server for some time it can consider connection broken and try to reconnect. Usually a server sends pings every 25 seconds.

Handle disconnects

Client should handle disconnect advices from server. In websocket case disconnect advice is sent in CLOSE Websocket frame. Disconnect advice contains uint32 code and human-readable string reason.

Handle errors

This section contains advices to error handling in client implementations.

Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems.

Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again.

Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side.

The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance.

Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).

Additional notes

Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in a reply to a subscribe command.

- + \ No newline at end of file diff --git a/docs/transports/client_sdk.html b/docs/transports/client_sdk.html index 67200a8bb..ee3b800e5 100644 --- a/docs/transports/client_sdk.html +++ b/docs/transports/client_sdk.html @@ -16,13 +16,13 @@ - +

Client real-time SDKs

In the previous chapter we investigated common principles of Centrifugo client SDK API. Here we will provide a list of available bidirectional connectors you can use to communicate with Centrifugo.

No need in SDK for unidirectional approach

Real-time SDKs listed here speak Centrifugo bidirectional protocol (with WebSocket as main transport). If you aim to use unidirectional approach you don't need client connector dependency – just use standard APIs. See the difference here.

List of client SDKs

Here is a list of SDKs maintained by Centrifugal Labs:

SDKs driven by the community:

See a description of client protocol if you want to write a custom bidirectional connector or eager to learn how Centrifugo protocol internals are structured. In case of any question how protocol works take a look at existing SDK source code or reach out in the community rooms.

Protobuf and JSON formats in SDKs

Centrifugo real-time SDKs work using two possible serialization formats: JSON and Protobuf. The entire bidirectional client protocol is described by the Protobuf schema. But those Protobuf messages may be also encoded as JSON objects (in JSON representation bytes fields in the Protobuf schema is replaced by the embedded JSON object in Centrifugo case).

Our Javascript SDK - centrifuge-js - uses JSON serialization for protocol frames by default. This makes communication with Centrifugo server convenient as we are exchanging human-readable JSON frames between client and server. And it makes it possible to use centrifuge-js without extra dependency to protobuf.js library. It's possible to switch to Protobuf protocol with centrifuge-js SDK though, in case you want more compact Centrifuge protocol representation, faster decode/encode speeds on Centrifugo server side, or payloads you need to pass are custom binary. See more details on how to use centrifuge-js with Protobuf serialization in README.

centrifuge-go real-time SDK for Go language also supports both JSON and Protobuf formats when communicating with Centrifugo server.

Other SDKs, like centrifuge-dart, centrifuge-swift, centrifuge-java work using only Protobuf serialization for Centrifuge protocol internally. So they utilize the fastest and the most compact wire representation by default. Note, that while internally in those SDKs the serialization format is Protobuf, you can still send JSON towards these clients as JSON objects may be encoded as UTF-8 bytes. So these SDKs may work with both custom binary and JSON payloads.

There are some important notes about JSON and Protobuf interoperability mentioned in our FAQ.

SDK feature matrix

Below you can find an information regarding support of different features in our official client SDKs

Client featurejsdartswiftgojava
connect to a server
setting client options
automatic reconnect with backoff algorithm
client state changes
command-reply
command timeouts
async pushes
ping-pong
connection token refresh
handle disconnect advice from server
server-side subscriptions
batching API
bidirectional WebSocket emulation
Client featurejsdartswiftgojava
subscrbe to a channel
setting subscription options
automatic resubscribe with backoff algorithm
subscription state changes
subscription command-reply
subscription async pushes
subscription token refresh
handle unsubscribe advice from server
manage subscription registry
optimistic subscriptions
- + \ No newline at end of file diff --git a/docs/transports/http_stream.html b/docs/transports/http_stream.html index 2636c83f2..c20b75f57 100644 --- a/docs/transports/http_stream.html +++ b/docs/transports/http_stream.html @@ -16,13 +16,13 @@ - +

HTTP streaming, with bidirectional emulation

HTTP streaming is a technique based on using a long-lived HTTP connection between a client and a server with a chunked transfer encoding. Usually it only allows unidirectional flow of messages from server to client but with Centrifugo bidirectional emulation layer it may be used as a full-featured fallback or alternative to WebSocket.

HTTP-streaming connection endpoint in Centrifugo is:

/connection/http_stream
info

This transport is only implemented by our Javascript SDK. Internally it uses modern Fetch and Readable Streams API. HTTP-streaming fully supports binary transfer using our Protobuf protocol.

Here is an example how to use JavaScript SDK with WebSocket as the main transport and HTTP-streaming transport fallback:

Use HTTP-streaming with bidirectional emulation as a fallback for WebSocket in JS SDK
const transports = [
{
transport: 'websocket',
endpoint: 'ws://localhost:8000/connection/websocket'
},
{
transport: 'http_stream',
endpoint: 'http://localhost:8000/connection/http_stream'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()
danger

Make sure allowed_origins are properly configured.

Options

http_stream

Boolean, default: false.

Enables HTTP streaming endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients.

config.json
{
...
"http_stream": true
}

When enabling http_stream you can connect to /connection/http_stream from centrifuge-js. Note that our bidirectional emulation also uses /emulation endpoint of Centrifugo to send requests from client to server. This is required because HTTP streaming is a unidirectional transport in its nature. So we use HTTP call to send data from client to server and proxy this call to the correct Centrifugo node which handles the connection. Thus achieving bidirectional behaviour - see details about Centrifugo bidirectional emulation layer. Make sure /emulation endpoint is available for requests from the client side too. If required, you can also control both HTTP streaming connection url prefix and emulation endpoint prefix, see customizing endpoints.

http_stream_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes.

- + \ No newline at end of file diff --git a/docs/transports/overview.html b/docs/transports/overview.html index 99e9c2a4c..f098dacca 100644 --- a/docs/transports/overview.html +++ b/docs/transports/overview.html @@ -16,13 +16,13 @@ - +

Real-time transports

Centrifugo supports a variety of transports to deliver real-time messages to clients.

Every transport is a persistent connection

Here we describe supported transports between your application frontend and Centrifugo itself. Every Centrifugo transport is a persistent connection so the server can push data towards clients at any moment.

The important distinction here is that all supported transports belong to one of two possible groups:

  • Bidirectional
  • Unidirectional

Bidirectional

Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus and where Centrifugo really shines.

Bidirectional transports come with a cost that developers need to use a special client connector library (SDK) which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state, handle request queueing/timeouts/errors, etc. And of course to multiplex subscriptions to different channels over a single connection.

Centrifugo has several official client SDKs for popular environments. All of them work over WebSocket transport. Our Javascript SDK also offers bidirectional fallbacks over HTTP-Streaming, Server-Sent Events (SSE), SockJS, and has an experimental support for WebTransport.

Unidirectional

Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time.

The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource/SSE, HTTP-streaming), or GRPC generated code to receive real-time updates from Centrifugo. Thus avoiding dependency to a client connector that abstracts bidirectional communication.

The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo).

Learn more about unidirectional protocol and available unidirectional transports.

PING/PONG behavior

Centrifugo server periodically sends pings to clients and expects pong from clients that works over bidirectional transports. Sending ping and receiving pong allows to find broken connections faster. Centrifugo sends pings on the Centrifugo client protocol level, thus it's possible for clients to handle ping messages on the client side to make sure connection is not broken (our bidirectional SDKs do this automatically).

By default Centrifugo sends pings every 25 seconds. This may be changed using ping_interval option (duration, default "25s").

Centrifugo expects pong message from bidirectional client SDK after sending ping to it. By default, it waits no more than 8 seconds before closing a connection. This may be changed using pong_timeout option (duration, default "8s").

In most cases default ping/pong intervals are fine so you don't really need to tweak them. Reducing timeouts may help you to find non-gracefully closed connections faster, but will increase network traffic and CPU resource usage since ping/pongs are sent faster.

caution

ping_interval must be greater than pong_timeout in the current implementation.

Here is a scheme how ping/pong works in bidirectional and unidirectional client scenarios:

- + \ No newline at end of file diff --git a/docs/transports/sockjs.html b/docs/transports/sockjs.html index cea9e9da7..0c9a91760 100644 --- a/docs/transports/sockjs.html +++ b/docs/transports/sockjs.html @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@

SockJS

SockJS is a polyfill browser library which provides HTTP-based fallback transports in case when it's not possible to establish Websocket connection. This can happen in old client browsers or because of some proxy behind client and server that cuts of Websocket traffic. You can find more information on SockJS project Github page.

caution

SockJS transport is DEPRECATED. It may be removed in the next major releases. Consider using our own WebSocket emulation layer based on HTTP-streaming and EventSource instead. If you know a use case where SockJS is still prefferred – reach us out to discuss this use case.

info

This transport is only implemented by our Javascript SDK at this point – as it mostly makes sense as a fallback for WebSocket to have real-time connection in an environment where WebSocket is unavailable. These days those envs are mostly corporate networks which can block WebSocket traffic (even TLS-based).

If you have a requirement to work everywhere SockJS is the solution. SockJS will automatically choose best fallback transport if Websocket connection failed for some reason. Some of the fallback transports are:

  • EventSource (SSE)
  • XHR-streaming
  • Long-polling
  • And more (see SockJS docs)

SockJS connection endpoint in Centrifugo is:

/connection/sockjs

SockJS caveats

caution

There are several important caveats to know when using SockJS – see below.

Sticky sessions

First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend.

For example, with Nginx sticky support can be enabled with ip_hash directive for upstream:

upstream centrifugo {
ip_hash;
server 127.0.0.1:8000;
server 127.0.0.2:8000;
}

With this configuration Nginx will proxy connections with the same ip address to the same upstream backend.

But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair.

So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.

Browser only

SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.

JSON only

One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.

Options

sockjs

Boolean, default: false.

Enables SockJS transport.

sockjs_url

Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js

Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.

- + \ No newline at end of file diff --git a/docs/transports/sse.html b/docs/transports/sse.html index c5b236735..de4f82338 100644 --- a/docs/transports/sse.html +++ b/docs/transports/sse.html @@ -16,13 +16,13 @@ - +

SSE (EventSource), with bidirectional emulation

Server-Sent Events or EventSource is a well-known HTTP-based transport available in all modern browsers and loved by many developers. It's unidirectional in its nature but with Centrifugo bidirectional emulation layer it may be used as a fallback or alternative to WebSocket.

SSE (EventSource) connection endpoint in Centrifugo is:

/connection/sse
info

This transport is only implemented by our Javascript SDK.

Here is an example how to use JavaScript SDK with WebSocket as the main transport and SSE transport fallback:

Use SSE with bidirectional emulation as a fallback for WebSocket in JS SDK
const transports = [
{
transport: 'websocket',
endpoint: 'ws://localhost:8000/connection/websocket'
},
{
transport: 'sse',
endpoint: 'http://localhost:8000/connection/sse'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()
danger

Make sure allowed_origins are properly configured.

Options

sse

Boolean, default: false.

Enables SSE (EventSource) endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients.

config.json
{
...
"sse": true
}

When enabling sse you can connect to /connection/sse from centrifuge-js. Note that our bidirectional emulation also uses /emulation endpoint of Centrifugo to send requests from client to server. This is required because SSE/EventSource is a unidirectional transport in its nature. So we use HTTP call to send data from client to server and proxy this call to the correct Centrifugo node which handles the connection. Thus achieving bidirectional behaviour - see details about Centrifugo bidirectional emulation layer. Make sure /emulation endpoint is available for requests from the client side too. If required, you can also control both SSE connection url prefix and emulation endpoint prefix, see customizing endpoints.

sse_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).

- + \ No newline at end of file diff --git a/docs/transports/uni_client_protocol.html b/docs/transports/uni_client_protocol.html index 9d0c6e50b..3a38d6c88 100644 --- a/docs/transports/uni_client_protocol.html +++ b/docs/transports/uni_client_protocol.html @@ -16,13 +16,13 @@ - +

Unidirectional client protocol

As we mentioned in overview you can avoid using Centrifugo SDKs if you stick with unidirectional approach. In this case though you will need to implement some basic parsing on client side to consume message types sent by Centrifugo into unidirectional connections.

At this point Centrifugo supports unidirectional WebSocket, HTTP streaming, SSE (EventSource), GRPC transports – and all of them inherit the same protocol structure described here.

Unidirectional message types

In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as:

message Push {
string channel = 2;

Publication pub = 4;
Join join = 5;
Leave leave = 6;
Unsubscribe unsubscribe = 7;
Message message = 8;
Subscribe subscribe = 9;
Connect connect = 10;
Disconnect disconnect = 11;
Refresh refresh = 12;
}
tip

Some numbers in Protobuf definitions skipped for backwards compatibility with previous client protocol version.

So unidirectional connection will receive various pushes. Every push contains one of the following objects:

  • Publication
  • Join
  • Leave
  • Unsubscribe
  • Message
  • Subscribe
  • Connect
  • Disconnect
  • Refresh

Some pushes belong to a channel which may be set on Push top level.

All you need to do is look at Push, process messages you are interested in and ignore others. In most cases you will be most interested in pushes which contain Connect or Publication messages.

For example, according to protocol schema Publication message type looks like this:

message Publication {
bytes data = 4;
ClientInfo info = 5;
uint64 offset = 6;
map<string, string> tags = 7;
}
tip

In JSON protocol case Centrifugo replaces bytes type with embedded JSON.

Just try using any unidirectional transport and you will quickly get the idea.

- + \ No newline at end of file diff --git a/docs/transports/uni_grpc.html b/docs/transports/uni_grpc.html index b61cf58e3..d63379ce7 100644 --- a/docs/transports/uni_grpc.html +++ b/docs/transports/uni_grpc.html @@ -16,13 +16,13 @@ - +

Unidirectional GRPC

It's possible to connect to GRPC unidirectional stream to consume real-time messages from Centrifugo. In this case you need to generate GRPC code for your language on client-side.

Protobuf definitions can be found here.

GRPC server will start on port 11000 (default).

Supported data formats

JSON and binary.

Options

uni_grpc

Boolean, default: false.

Enables unidirectional GRPC endpoint.

config.json
{
...
"uni_grpc": true
}

uni_grpc_port

String, default "11000".

Port to listen on.

uni_grpc_address

String, default "" (listen on all interfaces)

Address to bind uni GRPC to.

uni_grpc_max_receive_message_size

Default: 65536 (64KB)

Maximum allowed size of a first connect message received from GRPC connection in bytes.

uni_grpc_tls

Boolean, default: false

Enable custom TLS for unidirectional GRPC server.

uni_grpc_tls_cert

String, default: "".

Path to cert file.

uni_grpc_tls_key

String, default: "".

Path to key file.

Example

We don't have example here yet. In general, the algorithm is like this:

  1. Copy Protobuf definitions
  2. Generate GRPC client code
  3. Use generated code to connect to Centrifugo
  4. Process Push messages, drop unknown Pushes, handle those necessary for the application.
- + \ No newline at end of file diff --git a/docs/transports/uni_http_stream.html b/docs/transports/uni_http_stream.html index 537d6873c..4bac0b957 100644 --- a/docs/transports/uni_http_stream.html +++ b/docs/transports/uni_http_stream.html @@ -16,13 +16,13 @@ - +

Unidirectional HTTP streaming

HTTP streaming is a technique based on using a long-lived HTTP connection between a client and a server with a chunked transfer encoding. These days it's possible to use it from the web browser using modern Fetch and Readable Streams API.

Default unidirectional HTTP streaming connection endpoint in Centrifugo is:

/connection/uni_http_stream

Streaming endpoint accepts HTTP POST requests and sends JSON messages to a connection. These JSON messages can have different meaning according to Centrifuge protocol Protobuf definitions. But in most cases you will be interested in Publication push types.

Connect command

It's possible to pass initial connect command by posting a JSON body to a streaming endpoint.

Refer to the full Connect command description – it's the same as for unidirectional WebSocket.

Supported data formats

JSON

Pings

Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).

Options

uni_http_stream

Boolean, default: false.

Enables unidirectional HTTP streaming endpoint.

config.json
{
...
"uni_http_stream": true
}

uni_http_stream_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes.

Connecting using CURL

Let's look how simple it is to connect to Centrifugo using HTTP streaming.

We will start from scratch, generate new configuration file:

centrifugo genconfig

Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect:

config.json
{
...
"uni_http_stream": true,
"user_subscribe_to_personal": true
}

Run Centrifugo:

centrifugo -c config.json

In separate terminal window create token for a user:

❯ go run main.go gentoken -u user12
HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM

Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request:

curl -X POST http://localhost:8000/connection/uni_http_stream \
-d '{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM"}'

Open one more terminal window and publish message to a personal user channel:

curl -X POST http://localhost:8000/api/publish \
-d '{"channel": "#user12", "data": {"input": "hello"}}' \
-H "Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc"

You should see this messages coming from server.

{} messages are pings from a server.

That's all, happy streaming!

Browser example

A basic browser will come soon as we update docs for v4.

- + \ No newline at end of file diff --git a/docs/transports/uni_sse.html b/docs/transports/uni_sse.html index a71b9a100..d2007e88e 100644 --- a/docs/transports/uni_sse.html +++ b/docs/transports/uni_sse.html @@ -16,13 +16,13 @@ - +

Unidirectional SSE (EventSource)

Server-Sent Events or EventSource is a well-known HTTP-based transport available in all modern browsers and loved by many developers.

Default unidirectional SSE (EventSource) connection endpoint in Centrifugo is:

/connection/uni_sse
info

Only parts of EventSource spec that make sense for Centrifugo are implemented. For example Last-Event-Id header not supported (since one connection can be subscribed to many channels) and multiline strings (since we are passing JSON-encoded objects to the client) etc.

Connect command

Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example:

const url = new URL('http://localhost:8000/connection/uni_sse');
url.searchParams.append("cf_connect", JSON.stringify({
'token': '<JWT>'
}));

const eventSource = new EventSource(url);

Refer to the full Connect command description – it's the same as for unidirectional WebSocket.

The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases.

tip

Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.

Supported data formats

JSON

Options

uni_sse

Boolean, default: false.

Enables unidirectional SSE (EventSource) endpoint.

config.json
{
...
"uni_sse": true
}

uni_sse_max_request_body_size

Default: 65536 (64KB)

Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).

Browser example

Coming soon.

- + \ No newline at end of file diff --git a/docs/transports/uni_websocket.html b/docs/transports/uni_websocket.html index 5e355b061..482b068fd 100644 --- a/docs/transports/uni_websocket.html +++ b/docs/transports/uni_websocket.html @@ -16,13 +16,13 @@ - +

Unidirectional WebSocket

Default unidirectional WebSocket connection endpoint in Centrifugo is:

/connection/uni_websocket

While WebSocket is bidirectional transport in its nature Centrifugo provides its unidirectional version too to give developers more choice in transports when using unidirectional approach.

Connect command

It's possible to send connect command as first WebSocket message (as JSON).

Field nameField typeRequiredDescription
tokenstringnoConnection JWT, not required when using the connect proxy feature.
dataany JSONnoCustom JSON connection data
namestringnoApplication name
versionstringnoApplication version
subsmap of channel to SubscribeRequestnoPass an information about desired subscriptions to a server

SubscribeRequest

Field nameField typeRequiredDescription
recoverbooleannoWhether a client wants to recover from a certain position
offsetintegernoKnown stream position offset when recover is used
epochstringnoKnown stream position epoch when recover is used

Supported data formats

JSON

Pings

Centrifugo uses empty commands ({} in JSON case) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).

Options

uni_websocket

Boolean, default: false.

Enables unidirectional WebSocket endpoint.

config.json
{
...
"uni_websocket": true
}

uni_websocket_message_size_limit

Default: 65536 (64KB)

Maximum allowed size of a first connect message received from WebSocket connection in bytes.

Example

Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal.

First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example:

config.json
{
"token_hmac_secret_key": "secret",
"uni_websocket":true,
"user_subscribe_to_personal": true
}

Run Centrifugo:

./centrifugo -c config.json

In another terminal:

❯ ./centrifugo gentoken -c config.json -u test_user
HMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw

Install wscat and run:

wscat -c "ws://localhost:8000/connection/uni_websocket"

This will establish a connection with a server and you then can send connect command to a server:

❯ wscat -c "ws://localhost:8000/connection/uni_websocket"

Connected (press CTRL+C to quit)
> {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2NTY1MDMwNDV9.3UYL-UCUBp27TybeBK7Z0OenwdsKwCMRe46fuEjJnzI", "subs": {"abc": {}}}
< {"connect":{"client":"bfd28799-b958-4791-b9e9-b011eaef68c1","version":"0.0.0","subs":{"#test_user":{}},"expires":true,"ttl":604407,"ping":25,"session":"57b1287b-44ec-45c8-93fc-696c5294af25"}}

The connection will receive server pings (empty commands {}) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.

- + \ No newline at end of file diff --git a/docs/transports/websocket.html b/docs/transports/websocket.html index 56f565bb1..e12d817f1 100644 --- a/docs/transports/websocket.html +++ b/docs/transports/websocket.html @@ -16,13 +16,13 @@ - +

WebSocket

Websocket is the main transport in Centrifugo. It's a very efficient low-overhead protocol on top of TCP.

The biggest advantage is that Websocket works out of the box in all modern browsers and almost all programming languages have Websocket implementations. This makes Websocket a universal transport that can be used to connect to Centrifugo from almost everywhere.

Default WebSocket connection endpoint in Centrifugo is:

/connection/websocket

So to connect:

Connect to local Centrifugo with JavaScript SDK
const client = new Centrifuge('ws://localhost:8000/connection/websocket', {
// token: ?,
// getToken: ?
});

client.connect();

Options

websocket_message_size_limit

Default: 65536 (64KB)

Maximum allowed size of a message received from WebSocket connection in bytes.

websocket_read_buffer_size

In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size).

config.json
{
...
"websocket_read_buffer_size": 512
}

websocket_write_buffer_size

In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused):

config.json
{
...
"websocket_write_buffer_size": 512
}

websocket_use_write_buffer_pool

If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.

websocket_compression

An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire.

We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally.

caution

Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable.

To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature.

Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client.

It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.

Protobuf binary protocol

In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if:

  • you want less traffic on wire as Protobuf is very compact
  • you want maximum performance on server-side as Protobuf encoding/decoding is very efficient
  • you can sacrifice human-readable JSON for your application

Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like:

wss://centrifugo.example.com/connection/websocket?format=protobuf

After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes.

tip

You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.

Debugging with Postman, wscat, etc

Centrifugo supports a special url parameter for bidirectional websocket which turns on using native WebSocket frame ping-pong mechanism instead of server-to-client application level pings Centrifugo uses by default. This simplifies debugging Centrifugo protocol with tools like Postman, wscat, websocat, etc.

By default, it may be inconvenient due to the fact Centrifugo sends periodic ping message to the client ({} in JSON protocol scenario) and expects pong response back within some time period. Otherwise Centrifugo closes connection. This results in problems with mentioned tools because you had to manually send {} pong message upon ping message. So typical session in wscat could look like this:

❯ wscat --connect ws://localhost:8000/connection/websocket
Connected (press CTRL+C to quit)
> {"id": 1, "connect": {}}
< {"id":1,"connect":{"client":"9ac9de4e-5289-4ad6-9aa7-8447f007083e","version":"0.0.0","ping":25,"pong":true}}
< {}
Disconnected (code: 3012, reason: "no pong")

The parameter is called cf_ws_frame_ping_pong, to use it connect to Centrifugo bidirectional WebSocket endpoint like ws://localhost:8000/connection/websocket?cf_ws_frame_ping_pong=true. Here is an example which demonstrates working with Postman WebSocket where we connect to local Centrifugo and subscribe to two channels test1 and test2:

You can then proceed to Centrifugo admin web UI, publish something to these channels and see publications in Postman.

Note, how we sent several JSON commands in one WebSocket frame to Centrifugo from Postman in the example above - this is possible since Centrifugo protocol supports batches of commands in line-delimited format.

We consider this feature to be used only for debugging, in production prefer using our SDKs without using cf_ws_frame_ping_pong parameter – because app-level ping-pong is more efficient and our SDKs detect broken connections due to it.

- + \ No newline at end of file diff --git a/docs/transports/webtransport.html b/docs/transports/webtransport.html index a4e8ed2d7..b60815766 100644 --- a/docs/transports/webtransport.html +++ b/docs/transports/webtransport.html @@ -16,13 +16,13 @@ - +

WebTransport

WebTransport is an API offering low-latency, bidirectional, client-server messaging on top of HTTP/3 (with QUIC under the hood). See Using WebTransport article that gives a good overview of it.

danger

WebTransport support in Centrifugo is EXPERIMENTAL and not recommended for production usage. WebTransport IETF specification is not finished yet and may have breaking changes.

To use WebTransport you first need to run HTTP/3 experimental server and enable webtransport endpoint:

config.json
{
"http3": true,
"tls": true,
"tls_cert": "path/to/crt",
"tls_key": "path/to/key",
"webtransport": true
}

In HTTP/3 and WebTransport case TLS is required.

tip

At the time of writing only Chrome (since v97) supports WebTransport API. If you are experimenting with self-signed certificates you may need to run Chrome with flags to force HTTP/3 on origin and ignore certificate errors:

/path/to/your/Chrome --origin-to-force-quic-on=localhost:8000 --ignore-certificate-errors-spki-list=TSZTiMjLG+DNjESXdJh3f+S8C+RhsFCav7T24VNuCPQ=

Where the value of --ignore-certificate-errors-spki-list is a certificate fingerprint obtained this way:

openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

With not self-signed certs things should work just fine in Chrome.

Here is a video tutorial that shows this in action:

After starting Centrifugo with HTTP/3 and WebTransport endpoint you can connect to that endpoint (by default – /connection/webtransport) using centrifuge-js. For example, let's enable WebTransport and will use WebSocket as a fallback option:

const transports = [
{
transport: 'webtransport',
endpoint: 'https://localhost:8000/connection/webtransport'
},
{
transport: 'websocket',
endpoint: 'wss://localhost:8000/connection/websocket'
}
];
const centrifuge = new Centrifuge(transports);
centrifuge.connect()

Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in WebTransport case non-TLS http:// scheme is simply not supported by the specification.

Also, Chrome may not automatically close WebTransport sessions upon browser reload, so consider adding:

addEventListener("unload", (event) => { centrifuge.disconnect() });
tip

Make sure you run Centrifugo without load balancer or reverse proxy in front, or make sure your proxy can proxy HTTP/3 traffic to Centrifugo.

In Centrifugo case, we utilize a single bidirectional stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional.

Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification it's an experimental feature. And it's not recommended for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket.

- + \ No newline at end of file diff --git a/index.html b/index.html index e62b6cde1..b9a892c56 100644 --- a/index.html +++ b/index.html @@ -16,13 +16,13 @@ - +
CENTRIFUGO
Scalable real-time messaging server. Set up once and forever.
Integrates with everything

Integrates with everything

Centrifugo is a self-hosted service which handles connections over various transports and provides a simple publishing API. Centrifugo nicely integrates with any application — no changes in the existing app architecture required to introduce real-time updates.

Great performance

Great performance

Centrifugo is written in Go language with some smart optimizations inside. See the description of the test stand with one million WebSocket connections and 30 million delivered messages per minute with hardware comparable to a single modern server machine.

Feature-rich

Feature-rich

Centrifugo provides flexible authentication, various types of subscriptions, hot channel history, online presence, the ability to proxy connection events to the backend, and much more. It comes with official SDK libraries for both web and mobile development.

Out-of-the-box scalability

Out-of-the-box scalability

Built-in Redis, KeyDB, Tarantool engines, or Nats broker make it possible to scale connections across different Centrifugo nodes. So Centrifugo helps you to scale to millions of active connections with reasonable hardware requirements.

Used in production

Used in production

Started a decade ago, Centrifugo (and Centrifuge library for Go it's built on top of) is mature, battle-tested software that has been successfully used in production by many companies around the world: VK, Badoo, ManyChat, OpenWeb, Grafana, and others.

Centrifugo PRO

Centrifugo PRO

Centrifugo PRO has a set of unique features on top of the OSS version: analytics with ClickHouse, real-time user and channel tracing, operation throttling, faster performance, token extensions, additional APIs (for example, push notification API), and more.

What is real-time messaging?

Real-time messaging is used to create interactive applications where events are delivered to online users with minimal delay.

Chats apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. can all be built on top of a real-time messaging system.

Centrifugo is a user facing PUB/SUB server that handles persistent connections over various real-time transports — WebSocket, HTTP-streaming, SSE (Server-Sent Events), SockJS, WebTransport, GRPC.

Looking for a cool demo?

Here is the real-time telemetry streamed from the Assetto Corsa racing simulator to the Grafana dashboard with a help of our WebSocket technologies.

This demonstrates that you can stream 60Hz data towards client connections and thus provide instant visual feedback on the state of the system.

What users ❤️ in Centrifugo

Stability. It just works without restarts, for few years alreadyScalability and integration with RedisIt's simple and powerfulOpen sourceVery simple integrationIt's really well written. Super easy to setup, especially for authClean codeAdmin web UI
- + \ No newline at end of file diff --git a/license.html b/license.html index 9377790ee..fbb9e89bb 100644 --- a/license.html +++ b/license.html @@ -16,13 +16,13 @@ - +

Centrifugal Labs LTD license agreement

PLEASE READ CAREFULLY. This Centrifugal Labs License Agreement (hereinafter referred to as "the Agreement") is a legally binding contract that regulates your use of all Centrifugal Labs software included with this Agreement (referred to as "Centrifugal Labs Software"), which is provided in object code format.

By installing or utilizing any of the Centrifugal Labs Software governed by this Agreement, such as Centrifugo PRO, you are affirming your acceptance of this Agreement's terms and conditions. If you do not consent to these terms and conditions, you are prohibited from installing or using the Centrifugal Labs Software.

Should you be installing or using the Centrifugal Labs Software on behalf of a legal entity, you hereby affirm that you possess the requisite authority to agree to this Agreement's terms and conditions on behalf of that entity.

Posted Date: July 29, 2023

This Agreement is established between Centrifugal Labs LTD and you, or the legal entity you represent or act on behalf of (referred to as "You").

1. OBJECT CODE END USER LICENSES, RESTRICTIONS AND THIRD PARTY OPEN SOURCE SOFTWARE

1.1 Object Code End User License. Subject to the terms and conditions of Section 1.2 of this Agreement, Centrifugal Labs hereby grants to You, AT NO CHARGE and for so long as you are not in breach of any provision of this Agreement, a License to the free features and functions of the Centrifugal Labs Software.

1.2 Reservation of Rights; Restrictions. As between Centrifugal Labs and You, Centrifugal Labs and its licensors own all right, title and interest in and to the Centrifugal Labs Software, and any related documentation or other intellectual property rights, and except as expressly set forth in Sections 1.1 of this Agreement, no other license to the Centrifugal Labs Software is granted to You under this Agreement, by implication, estoppel or otherwise. You agree not to: (i) reverse engineer or decompile, decrypt, disassemble or otherwise reduce any Centrifugal Labs Software provided to You in Object Code, or any portion thereof, to Source Code, except and only to the extent any such restriction is prohibited by applicable law, or otherwise build a competitive product or service, build a product or service using similar ideas, features, functions or graphics, or create any compilations or derivative works thereof, (ii) except as expressly permitted in Section 1.1 above, transfer, sell, rent, lease, distribute, sublicense, loan or otherwise transfer or commercially exploit or make available, Centrifugal Labs Software Object Code, in whole or in part, to any third party; (iii) use Centrifugal Labs Software Object Code for providing time-sharing services, any software-as-a-service, service bureau services or as part of an application services provider or other service offering (collectively, “SaaS Offering”) where obtaining access to the Centrifugal Labs Software or the features and functions of the Centrifugal Labs Software is a primary reason or substantial motivation for users of the SaaS Offering to access and/or use the SaaS Offering (“Prohibited SaaS Offering”); (iv) circumvent the limitations on use of Centrifugal Labs Software provided to You in Object Code format that are imposed or preserved by any License Key, (v) alter or remove any Marks and Notices in the Centrifugal Labs Software, or (vi) violate any of Centrifugal Labs’ posted policies regarding its Marks and Notices. If You have any question as to whether a specific SaaS Offering constitutes a Prohibited SaaS Offering, or are interested in obtaining Centrifugal Labs’s permission to engage in commercial or non-commercial distribution of the Centrifugal Labs Software, please contact at centrifugal.dev@gmail.com

1.3 Third Party Open Source Software. The Centrifugal Labs Software may contain or be provided with third party open source libraries, components, utilities and other open source software (collectively, “Open Source Software”), which Open Source Software may have applicable license terms as identified on a website designated by Centrifugal Labs . Notwithstanding anything to the contrary herein, use of the Open Source Software shall be subject to the license terms and conditions applicable to such Open Source Software, to the extent required by the applicable licensor (which terms shall not restrict the license rights granted to You hereunder, but may contain additional rights). To the extent any condition of this Agreement conflicts with any license to the Open Source Software, the Open Source Software license will govern with respect to such Open Source Software only. Centrifugal Labs may also separately provide you with certain open source software that is licensed by Centrifugal Labs. Your use of such Centrifugal Labs open source software will not be governed by this Agreement, but by the applicable open source license terms.

2. TERMINATION

2.1 Termination. This Agreement will automatically terminate, whether or not You receive notice of such Termination from Centrifugal Labs, if You breach any of its provisions.

2.2 Post Termination. Upon any termination of this Agreement, for any reason, You shall promptly cease the use of the commercial Centrifugal Labs Software in Object Code format. For the avoidance of doubt, termination of this Agreement will not affect Your right to use Centrifugal Labs Software, in either Object Code or Source Code formats, made available under the Apache License version 2.0 .

2.3 Survival. Sections 1.2, 2.2. 2.3, 3, 4, 5, 6 and 7 shall survive any termination or expiration of this Agreement.

3. DISCLAIMER OF WARRANTIES AND LIMITATION OF LIABILITY

3.1 Disclaimer of Warranties. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE CENTRIFUGAL LABS SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, AND CENTRIFUGAL LABS AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR STATUTORY REGARDING OR RELATING TO THE CENTRIFUGAL LABS SOFTWARE. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, CENTRIFUGAL LABS AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AS WELL AS ANY WARRANTIES OF REGULATORY COMPLIANCE, PERFORMANCE, ACCURACY, RELIABILITY, TITLE, AND NON-INFRINGEMENT. FURTHER, CENTRIFUGAL LABS DOES NOT WARRANT THAT THE CENTRIFUGAL LABS SOFTWARE WILL BE ERROR FREE OR UNINTERRUPTED.

3.2 Limitation of Liability. IN NO EVENT SHALL CENTRIFUGAL LABS OR ITS LICENSORS BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES, INCLUDING BUT NOT LIMITED TO, LOSS OF PROFITS, LOSS OF USE LOSS OF DATA, BUSINESS INTERRUPTION, COST OF SUBSTITUTE GOODS OR SERVICES, OR OTHER COMMERCIAL DAMAGES OR LOSSES ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT, HOWEVER CAUSED AND WHETHER IN CONTRACT, TORT OR UNDER ANY OTHER THEORY OF LIABILITY AND WHETHER OR NOT YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

4. MISCELLANEOUS 

This Agreement completely and exclusively states the entire agreement of the parties regarding the subject matter herein, and it supersedes, and its terms govern, all prior proposals, agreements, or other communications between the parties, oral or written, regarding such subject matter. This Agreement may be modified by Centrifugal Labs from time to time, and any such modifications will be effective upon the “Posted Date” set forth at the top of the modified Agreement. If any provision hereof is held unenforceable, this Agreement will continue without said provision and be interpreted to reflect the original intent of the parties. This Agreement and any non-contractual obligation arising out of or in connection with it, is governed exclusively by Cyprus law. All disputes arising out of or in connection with this Agreement, including its existence and validity, shall be resolved by the courts with jurisdiction in Cyprus. The parties hereby irrevocably waive any and all claims and defenses either might otherwise have in any such action or proceeding in any of such courts based upon any alleged lack of personal jurisdiction, improper venue, forum non conveniens or any similar claim or defense. A breach or threatened breach, by You of Section 1 may cause irreparable harm for which damages at law may not provide adequate relief, and therefore Grafana Labs shall be entitled to seek injunctive relief without being required to post a bond. You may not assign this Agreement (including by operation of law in connection with a merger or acquisition), in whole or in part to any third party without the prior written consent of Centrifugal Labs, which may be withheld or granted by Centrifugal Labs in its sole and absolute discretion. Any assignment in violation of the preceding sentence is void. Notices to Centrifugal Labs may also be sent to centrifugal.dev@gmail.com.

5. VERIFICATION

You will maintain accurate records of Your use of the Centrifugal Labs Software sufficient to show compliance with the terms of this Agreement. On reasonable notice, Centrifugal Labs may audit Your use of the Centrifugal Labs Software to confirm Your compliance with the terms of this Agreement, provided such audit does not unreasonably interfere with Your business activities. You will reasonably cooperate with Centrifugal Labs and/or any third party auditor, and will, without prejudice to other rights of Centrifugal Labs, address any non-compliance identified by the audit within thirty (30) days after such audit. Centrifugal Labs may also, at any time on request, require You to furnish Centrifugal Labs with information necessary to verify that Your use of the Centrifugal Labs Software is in compliance with the terms of this Agreement.

6. PROPER AND SECURE USE OF LICENSE KEYS

6.1 Secure Storage: You are responsible for maintaining the secure storage and confidentiality of your license keys for Centrifugal Labs Software. Under no circumstances should you share your license keys publicly or with unauthorized individuals.

6.2 Unauthorized Use: In case of any unauthorized use or suspected breach of the security of a license key, you are obliged to inform Centrifugal Labs immediately. Delay in reporting could lead to you being held responsible for the consequences of unauthorized use.

6.3 Updates and Changes: Changes to license keys, such as updates or renewals, should be managed securely and in accordance with Centrifugal Labs' guidelines and instructions.

6.4 Compliance: You must use the license keys in compliance with the terms and conditions outlined in this Agreement. Any misuse of license keys may result in the immediate termination of this Agreement and the revocation of your rights to use the Centrifugal Labs Software.

6.5 Responsibility: You are entirely responsible for all activities that occur under your license keys. Any harm or liability caused by the misuse of your license keys is solely your responsibility.

6.6 Backup: It is recommended to keep a secure backup of your license keys. Loss of license keys could result in the inability to access and use the Centrifugal Labs Software. Centrifugal Labs will not be responsible for any loss or damage arising from your failure to comply with these requirements.

6.7 Replacement: In case of loss or compromise of a license key, contact Centrifugal Labs immediately for a replacement. Please note that replacement might be subject to a fee.

6.8 Liability: Centrifugal Labs will not be held liable for any damages or losses resulting from the unauthorized or improper use of license keys.

7. DEFINITIONS

7.1 “Affiliate” means, with respect to a party, any entity that controls, is controlled by, or which is under common control with, such party, where “control” means ownership of at least fifty percent (50%) of the outstanding voting shares of the entity, or the contractual right to establish policy for, and manage the operations of, the entity.

7.2 “Centrifugal Labs Software” means all of the Centrifugal Labs LTD software with which this Agreement is included, including but not limited to the free features and functions of the Centrifugal Labs software.

7.3 “License” means a limited, non-exclusive, non-transferable, fully paid up, royalty free, right and license, without the right to grant or authorize sublicenses, solely for Your internal business operations to (i) install and use the applicable Features and Functions of the Centrifugal Labs Software in Object Code, and (ii) permit Contractors and Your Affiliates to use the Centrifugal Labs software as set forth in (i) above, provided that such use by Contractors must be solely for Your benefit and/or the benefit of Your Affiliates, and You shall be responsible for all acts and omissions of such Contractors and Affiliates in connection with their use of the Centrifugal Labs software that are contrary to the terms and conditions of this Agreement.

7.4 “License Key” means a sequence of bytes, including but not limited to a JSON blob, that is used to enable certain features and functions of the Centrifugal Labs Software.

7.5 “Marks and Notices” means all Centrifugal Labs trademarks, trade names, logos and notices, including those present on the documentation as provided by Centrifugal Labs.

7.6 “Object Code” means any form resulting from mechanical transformation or translation of Source Code form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

7.7 “Source Code” means the preferred form of computer software for making modifications, including but not limited to software source code, documentation source, and configuration files.

- + \ No newline at end of file diff --git a/license_exchange_lemon.html b/license_exchange_lemon.html index 495f1917e..ba48b8ba7 100644 --- a/license_exchange_lemon.html +++ b/license_exchange_lemon.html @@ -16,13 +16,13 @@ - +

Thanks for purchasing Centrifugo PRO 🎉

In the email you received from Lemon Squeeze you can find the license key. You need to exchange it to Centrifugo license key using the form below. Please paste the license key from the Lemon Squeeze email to the input below, press Exchange button – and we will exchange the Lemon Squezee license key to a Centrifugo PRO license key.

- + \ No newline at end of file diff --git a/search-index.json b/search-index.json index 27cfd4298..07d386f6c 100644 --- a/search-index.json +++ b/search-index.json @@ -1 +1 @@ -[{"documents":[{"i":1,"t":"Million connections with Centrifugo","u":"/blog/2020/02/10/million-connections-with-centrifugo","b":["Blog"]},{"i":3,"t":"Scaling WebSocket in Go and beyond","u":"/blog/2020/11/12/scaling-websocket","b":["Blog"]},{"i":23,"t":"Centrifuge – real-time messaging with Go","u":"/blog/2021/01/15/centrifuge-intro","b":["Blog"]},{"i":59,"t":"Centrifugo v3 released","u":"/blog/2021/08/31/hello-centrifugo-v3","b":["Blog"]},{"i":91,"t":"Experimenting with QUIC and WebTransport","u":"/blog/2020/10/16/experimenting-with-quic-transport","b":["Blog"]},{"i":117,"t":"Centrifugo integration with Django – building a basic chat application","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","b":["Blog"]},{"i":143,"t":"Building a multi-room chat application with Laravel and Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","b":["Blog"]},{"i":171,"t":"101 ways to subscribe user on a personal channel in Centrifugo","u":"/blog/2022/07/29/101-way-to-subscribe","b":["Blog"]},{"i":193,"t":"Centrifugo v5 released","u":"/blog/2023/06/29/centrifugo-v5-released","b":["Blog"]},{"i":221,"t":"Asynchronous message streaming to Centrifugo with Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","b":["Blog"]},{"i":245,"t":"Using Centrifugo in RabbitX","u":"/blog/2023/08/29/using-centrifugo-in-rabbitx","b":["Blog"]},{"i":247,"t":"Discovering Centrifugo PRO: push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","b":["Blog"]},{"i":271,"t":"","u":"/blog/archive","b":["Blog"]},{"i":272,"t":"Attributions","u":"/docs/3/attributions","b":[]},{"i":276,"t":"Centrifuge library","u":"/docs/3/ecosystem/centrifuge","b":[]},{"i":278,"t":"Centrifugo integration with NodeJS tutorial","u":"/blog/2021/10/18/integrating-with-nodejs","b":["Blog"]},{"i":292,"t":"Setting up Keycloak SSO authentication flow and connecting to Centrifugo WebSocket","u":"/blog/2023/03/31/keycloak-sso-centrifugo","b":["Blog"]},{"i":302,"t":"Framework integrations","u":"/docs/3/ecosystem/integrations","b":[]},{"i":304,"t":"Frequently Asked Questions","u":"/docs/3/faq","b":["FAQ"]},{"i":354,"t":"flow_diagrams","u":"/docs/3/flow_diagrams","b":[]},{"i":356,"t":"Design overview","u":"/docs/3/getting-started/design","b":["Getting Started"]},{"i":372,"t":"Centrifugo v4 released – a little revolution","u":"/blog/2022/07/19/centrifugo-v4-released","b":["Blog"]},{"i":408,"t":"Main highlights","u":"/docs/3/getting-started/highlights","b":["Getting Started"]},{"i":444,"t":"Integration guide","u":"/docs/3/getting-started/integration","b":["Getting Started"]},{"i":466,"t":"Improving Centrifugo Redis Engine throughput and allocation efficiency with Rueidis Go library","u":"/blog/2022/12/20/improving-redis-engine-performance","b":["Blog"]},{"i":486,"t":"Centrifugo introduction","u":"/docs/3/getting-started/introduction","b":["Getting Started"]},{"i":494,"t":"Client API showcase","u":"/docs/3/getting-started/client_api","b":["Getting Started"]},{"i":514,"t":"Database-driven namespace configuration","u":"/docs/3/pro/db_namespaces","b":[]},{"i":520,"t":"Install Centrifugo","u":"/docs/3/getting-started/installation","b":["Getting Started"]},{"i":536,"t":"Migrating to v3","u":"/docs/3/getting-started/migration_v3","b":["Getting Started"]},{"i":573,"t":"Centrifugo PRO overview","u":"/docs/3/pro/overview","b":["Centrifugo PRO"]},{"i":581,"t":"Quickstart tutorial ⏱️","u":"/docs/3/getting-started/quickstart","b":["Getting Started"]},{"i":585,"t":"Install and run PRO version","u":"/docs/3/pro/install_and_run","b":["Centrifugo PRO"]},{"i":599,"t":"Singleflight","u":"/docs/3/pro/singleflight","b":["Centrifugo PRO","PRO features"]},{"i":601,"t":"Analytics with ClickHouse","u":"/docs/3/pro/analytics","b":["Centrifugo PRO","PRO features"]},{"i":615,"t":"Operation throttling","u":"/docs/3/pro/throttling","b":["Centrifugo PRO","PRO features"]},{"i":619,"t":"Faster performance","u":"/docs/3/pro/performance","b":["Centrifugo PRO","PRO features"]},{"i":639,"t":"CPU and RSS stats","u":"/docs/3/pro/process_stats","b":["Centrifugo PRO","PRO features"]},{"i":641,"t":"Token revocation API","u":"/docs/3/pro/token_revocation","b":["Centrifugo PRO","PRO features"]},{"i":655,"t":"User and channel tracing","u":"/docs/3/pro/tracing","b":["Centrifugo PRO","PRO features"]},{"i":659,"t":"User blocking API","u":"/docs/3/pro/user_block","b":["Centrifugo PRO","PRO features"]},{"i":673,"t":"User connections API","u":"/docs/3/pro/user_connections","b":["Centrifugo PRO","PRO features"]},{"i":675,"t":"User status","u":"/docs/3/pro/user_status","b":["Centrifugo PRO","PRO features"]},{"i":687,"t":"Channels","u":"/docs/3/server/channels","b":["Server guide"]},{"i":735,"t":"Client authentication","u":"/docs/3/server/authentication","b":["Server guide"]},{"i":777,"t":"Error and disconnect codes","u":"/docs/3/server/codes","b":["Server guide"]},{"i":851,"t":"Admin web UI","u":"/docs/3/server/admin_web","b":["Server guide"]},{"i":859,"t":"Console commands","u":"/docs/3/server/console_commands","b":["Server guide"]},{"i":871,"t":"Configure Centrifugo","u":"/docs/3/server/configuration","b":["Server guide"]},{"i":946,"t":"Infrastructure tuning","u":"/docs/3/server/infra_tuning","b":["Server guide"]},{"i":958,"t":"History and recovery","u":"/docs/3/server/history_and_recovery","b":["Server guide"]},{"i":966,"t":"Engines, scalability","u":"/docs/3/server/engines","b":["Server guide"]},{"i":996,"t":"Load balancing","u":"/docs/3/server/load_balancing","b":["Server guide"]},{"i":1006,"t":"Monitoring","u":"/docs/3/server/monitoring","b":["Server guide"]},{"i":1014,"t":"Private channels","u":"/docs/3/server/private_channels","b":["Server guide"]},{"i":1036,"t":"Server-side subscriptions","u":"/docs/3/server/server_subs","b":["Server guide"]},{"i":1044,"t":"Configure TLS","u":"/docs/3/server/tls","b":["Server guide"]},{"i":1054,"t":"Proxy to backend","u":"/docs/3/server/proxy","b":["Server guide"]},{"i":1100,"t":"Client real-time SDKs","u":"/docs/3/transports/client_sdk","b":["Real-time transports / SDK","Bidirectional"]},{"i":1102,"t":"Server API","u":"/docs/3/server/server_api","b":["Server guide"]},{"i":1144,"t":"SockJS","u":"/docs/3/transports/sockjs","b":["Real-time transports / SDK","Bidirectional"]},{"i":1159,"t":"Client protocol","u":"/docs/3/transports/client_protocol","b":["Real-time transports / SDK","Bidirectional"]},{"i":1193,"t":"Unidirectional HTTP streaming","u":"/docs/3/transports/uni_http_stream","b":["Real-time transports / SDK","Unidirectional"]},{"i":1210,"t":"Unidirectional SSE (EventSource)","u":"/docs/3/transports/uni_sse","b":["Real-time transports / SDK","Unidirectional"]},{"i":1225,"t":"Unidirectional GRPC","u":"/docs/3/transports/uni_grpc","b":["Real-time transports / SDK","Unidirectional"]},{"i":1246,"t":"Unidirectional WebSocket","u":"/docs/3/transports/uni_websocket","b":["Real-time transports / SDK","Unidirectional"]},{"i":1263,"t":"Real-time transports","u":"/docs/3/transports/overview","b":["Real-time transports / SDK"]},{"i":1271,"t":"WebSocket","u":"/docs/3/transports/websocket","b":["Real-time transports / SDK","Bidirectional"]},{"i":1286,"t":"Attributions","u":"/docs/4/attributions","b":[]},{"i":1290,"t":"Frequently Asked Questions","u":"/docs/4/faq","b":["FAQ"]},{"i":1346,"t":"flow_diagrams","u":"/docs/4/flow_diagrams","b":[]},{"i":1348,"t":"Join community","u":"/docs/4/getting-started/community","b":["Getting Started"]},{"i":1350,"t":"Ecosystem notes","u":"/docs/4/getting-started/ecosystem","b":["Getting Started"]},{"i":1356,"t":"Client API showcase","u":"/docs/4/getting-started/client_api","b":[]},{"i":1376,"t":"Design overview","u":"/docs/4/getting-started/design","b":["Getting Started"]},{"i":1392,"t":"Main highlights","u":"/docs/4/getting-started/highlights","b":["Getting Started"]},{"i":1428,"t":"Install Centrifugo","u":"/docs/4/getting-started/installation","b":["Getting Started"]},{"i":1444,"t":"Integration guide","u":"/docs/4/getting-started/integration","b":["Getting Started"]},{"i":1466,"t":"Centrifugo introduction","u":"/docs/4/getting-started/introduction","b":["Getting Started"]},{"i":1470,"t":"Channel capabilities","u":"/docs/4/pro/capabilities","b":["Centrifugo PRO","PRO features"]},{"i":1494,"t":"Quickstart tutorial ⏱️","u":"/docs/4/getting-started/quickstart","b":["Getting Started"]},{"i":1496,"t":"Migrating to v4","u":"/docs/4/getting-started/migration_v4","b":["Getting Started"]},{"i":1518,"t":"CEL expressions","u":"/docs/4/pro/cel_expressions","b":["Centrifugo PRO","PRO features"]},{"i":1530,"t":"Analytics with ClickHouse","u":"/docs/4/pro/analytics","b":["Centrifugo PRO","PRO features"]},{"i":1550,"t":"Install and run PRO version","u":"/docs/4/pro/install_and_run","b":["Centrifugo PRO"]},{"i":1564,"t":"Channel patterns","u":"/docs/4/pro/channel_patterns","b":["Centrifugo PRO","PRO features"]},{"i":1574,"t":"Connections API","u":"/docs/4/pro/connections","b":["Centrifugo PRO","PRO features"]},{"i":1580,"t":"Centrifugo PRO overview","u":"/docs/4/pro/overview","b":["Centrifugo PRO"]},{"i":1588,"t":"Faster performance","u":"/docs/4/pro/performance","b":["Centrifugo PRO","PRO features"]},{"i":1608,"t":"Message batching control","u":"/docs/4/pro/client_message_batching","b":["Centrifugo PRO","PRO features"]},{"i":1616,"t":"CPU and RSS stats","u":"/docs/4/pro/process_stats","b":["Centrifugo PRO","PRO features"]},{"i":1618,"t":"Singleflight","u":"/docs/4/pro/singleflight","b":["Centrifugo PRO","PRO features"]},{"i":1620,"t":"Push notification API","u":"/docs/4/pro/push_notifications","b":["Centrifugo PRO","PRO features"]},{"i":1673,"t":"Operation throttling","u":"/docs/4/pro/throttling","b":["Centrifugo PRO","PRO features"]},{"i":1683,"t":"User and channel tracing","u":"/docs/4/pro/tracing","b":["Centrifugo PRO","PRO features"]},{"i":1687,"t":"Token revocation API","u":"/docs/4/pro/token_revocation","b":["Centrifugo PRO","PRO features"]},{"i":1703,"t":"User blocking API","u":"/docs/4/pro/user_block","b":["Centrifugo PRO","PRO features"]},{"i":1718,"t":"Admin web UI","u":"/docs/4/server/admin_web","b":["Server guide"]},{"i":1726,"t":"User status API","u":"/docs/4/pro/user_status","b":["Centrifugo PRO","PRO features"]},{"i":1738,"t":"Channel permission model","u":"/docs/4/server/channel_permissions","b":["Server guide"]},{"i":1754,"t":"Client JWT authentication","u":"/docs/4/server/authentication","b":["Server guide"]},{"i":1798,"t":"Channel JWT authorization","u":"/docs/4/server/channel_token_auth","b":["Server guide"]},{"i":1828,"t":"Error and disconnect codes","u":"/docs/4/server/codes","b":["Server guide"]},{"i":1866,"t":"Channels and namespaces","u":"/docs/4/server/channels","b":["Server guide"]},{"i":1938,"t":"Configure Centrifugo","u":"/docs/4/server/configuration","b":["Server guide"]},{"i":2021,"t":"Engines and scalability","u":"/docs/4/server/engines","b":["Server guide"]},{"i":2057,"t":"Helper CLI commands","u":"/docs/4/server/console_commands","b":["Server guide"]},{"i":2073,"t":"Infrastructure tuning","u":"/docs/4/server/infra_tuning","b":["Server guide"]},{"i":2087,"t":"Metrics monitoring","u":"/docs/4/server/monitoring","b":["Server guide"]},{"i":2095,"t":"History and recovery","u":"/docs/4/server/history_and_recovery","b":["Server guide"]},{"i":2103,"t":"Load balancing","u":"/docs/4/server/load_balancing","b":["Server guide"]},{"i":2113,"t":"Online presence","u":"/docs/4/server/presence","b":["Server guide"]},{"i":2127,"t":"Server-side subscriptions","u":"/docs/4/server/server_subs","b":["Server guide"]},{"i":2135,"t":"Proxy events to the backend","u":"/docs/4/server/proxy","b":["Server guide"]},{"i":2183,"t":"Server API walkthrough","u":"/docs/4/server/server_api","b":["Server guide"]},{"i":2225,"t":"Configure TLS","u":"/docs/4/server/tls","b":["Server guide"]},{"i":2235,"t":"Client protocol","u":"/docs/4/transports/client_protocol","b":["Real-time transports / SDK","Bidirectional"]},{"i":2253,"t":"Client real-time SDKs","u":"/docs/4/transports/client_sdk","b":["Real-time transports / SDK","Bidirectional"]},{"i":2265,"t":"HTTP streaming, with bidirectional emulation","u":"/docs/4/transports/http_stream","b":["Real-time transports / SDK","Bidirectional"]},{"i":2272,"t":"SockJS","u":"/docs/4/transports/sockjs","b":["Real-time transports / SDK","Bidirectional"]},{"i":2287,"t":"Real-time transports","u":"/docs/4/transports/overview","b":["Real-time transports / SDK"]},{"i":2297,"t":"SSE (EventSource), with bidirectional emulation","u":"/docs/4/transports/sse","b":["Real-time transports / SDK","Bidirectional"]},{"i":2304,"t":"Unidirectional GRPC","u":"/docs/4/transports/uni_grpc","b":["Real-time transports / SDK","Unidirectional"]},{"i":2325,"t":"Unidirectional HTTP streaming","u":"/docs/4/transports/uni_http_stream","b":["Real-time transports / SDK","Unidirectional"]},{"i":2342,"t":"Client SDK API","u":"/docs/4/transports/client_api","b":["Real-time transports / SDK","Bidirectional"]},{"i":2384,"t":"Unidirectional SSE (EventSource)","u":"/docs/4/transports/uni_sse","b":["Real-time transports / SDK","Unidirectional"]},{"i":2397,"t":"Unidirectional WebSocket","u":"/docs/4/transports/uni_websocket","b":["Real-time transports / SDK","Unidirectional"]},{"i":2414,"t":"Attributions","u":"/docs/attributions","b":[]},{"i":2418,"t":"WebSocket","u":"/docs/4/transports/websocket","b":["Real-time transports / SDK","Bidirectional"]},{"i":2433,"t":"WebTransport","u":"/docs/4/transports/webtransport","b":["Real-time transports / SDK","Bidirectional"]},{"i":2435,"t":"flow_diagrams","u":"/docs/flow_diagrams","b":[]},{"i":2437,"t":"Frequently Asked Questions","u":"/docs/faq","b":["FAQ"]},{"i":2493,"t":"Join community","u":"/docs/getting-started/community","b":["Getting Started"]},{"i":2495,"t":"Comparing with others","u":"/docs/getting-started/comparisons","b":["Getting Started"]},{"i":2507,"t":"Client API showcase","u":"/docs/getting-started/client_api","b":[]},{"i":2527,"t":"Design overview","u":"/docs/getting-started/design","b":["Getting Started"]},{"i":2543,"t":"Ecosystem notes","u":"/docs/getting-started/ecosystem","b":["Getting Started"]},{"i":2549,"t":"Main highlights","u":"/docs/getting-started/highlights","b":["Getting Started"]},{"i":2585,"t":"Integration guide","u":"/docs/getting-started/integration","b":["Getting Started"]},{"i":2607,"t":"Centrifugo introduction","u":"/docs/getting-started/introduction","b":["Getting Started"]},{"i":2611,"t":"Install Centrifugo","u":"/docs/getting-started/installation","b":["Getting Started"]},{"i":2627,"t":"Migrating to v4","u":"/docs/getting-started/migration_v4","b":[]},{"i":2649,"t":"Migrating to v5","u":"/docs/getting-started/migration_v5","b":["Getting Started"]},{"i":2661,"t":"Quickstart tutorial ⏱️","u":"/docs/getting-started/quickstart","b":["Getting Started"]},{"i":2663,"t":"Channel capabilities","u":"/docs/pro/capabilities","b":["Centrifugo PRO","PRO version features"]},{"i":2687,"t":"CEL expressions","u":"/docs/pro/cel_expressions","b":["Centrifugo PRO","PRO version features"]},{"i":2699,"t":"Analytics with ClickHouse","u":"/docs/pro/analytics","b":["Centrifugo PRO","PRO version features"]},{"i":2721,"t":"Channel state events","u":"/docs/pro/channel_state_events","b":["Centrifugo PRO","PRO version features"]},{"i":2723,"t":"Channel patterns","u":"/docs/pro/channel_patterns","b":["Centrifugo PRO","PRO version features"]},{"i":2733,"t":"Message batching control","u":"/docs/pro/client_message_batching","b":["Centrifugo PRO","PRO version features"]},{"i":2741,"t":"Distributed rate limit API","u":"/docs/pro/distributed_rate_limit","b":[]},{"i":2753,"t":"Install and run PRO version","u":"/docs/pro/install_and_run","b":["Centrifugo PRO"]},{"i":2767,"t":"Connections API","u":"/docs/pro/connections","b":["Centrifugo PRO","PRO version features"]},{"i":2773,"t":"Observability enhancements","u":"/docs/pro/observability_enhancements","b":["Centrifugo PRO","PRO version features"]},{"i":2775,"t":"Centrifugo PRO","u":"/docs/pro/overview","b":["Centrifugo PRO"]},{"i":2783,"t":"Faster performance","u":"/docs/pro/performance","b":["Centrifugo PRO","PRO version features"]},{"i":2803,"t":"CPU and RSS stats","u":"/docs/pro/process_stats","b":["Centrifugo PRO","PRO version features"]},{"i":2805,"t":"Singleflight","u":"/docs/pro/singleflight","b":["Centrifugo PRO","PRO version features"]},{"i":2807,"t":"User and channel tracing","u":"/docs/pro/tracing","b":["Centrifugo PRO","PRO version features"]},{"i":2811,"t":"Token revocation API","u":"/docs/pro/token_revocation","b":["Centrifugo PRO","PRO version features"]},{"i":2827,"t":"Operation rate limits","u":"/docs/pro/rate_limiting","b":["Centrifugo PRO","PRO version features"]},{"i":2837,"t":"Push notification API","u":"/docs/pro/push_notifications","b":["Centrifugo PRO","PRO version features"]},{"i":2892,"t":"User blocking API","u":"/docs/pro/user_block","b":["Centrifugo PRO","PRO version features"]},{"i":2907,"t":"Admin web UI","u":"/docs/server/admin_web","b":["Server guide"]},{"i":2915,"t":"User status API","u":"/docs/pro/user_status","b":["Centrifugo PRO","PRO version features"]},{"i":2927,"t":"Channel permission model","u":"/docs/server/channel_permissions","b":["Server guide"]},{"i":2943,"t":"Client JWT authentication","u":"/docs/server/authentication","b":["Server guide"]},{"i":2987,"t":"Channel JWT authorization","u":"/docs/server/channel_token_auth","b":["Server guide"]},{"i":3019,"t":"Error and disconnect codes","u":"/docs/server/codes","b":["Server guide"]},{"i":3057,"t":"Channels and namespaces","u":"/docs/server/channels","b":["Server guide"]},{"i":3135,"t":"Helper CLI commands","u":"/docs/server/console_commands","b":["Server guide"]},{"i":3151,"t":"Configure Centrifugo","u":"/docs/server/configuration","b":["Server guide"]},{"i":3240,"t":"Infrastructure tuning","u":"/docs/server/infra_tuning","b":["Server guide"]},{"i":3254,"t":"History and recovery","u":"/docs/server/history_and_recovery","b":["Server guide"]},{"i":3262,"t":"Engines and scalability","u":"/docs/server/engines","b":["Server guide"]},{"i":3296,"t":"Metrics monitoring","u":"/docs/server/monitoring","b":[]},{"i":3304,"t":"Load balancing","u":"/docs/server/load_balancing","b":["Server guide"]},{"i":3314,"t":"Server observability","u":"/docs/server/observability","b":["Server guide"]},{"i":3330,"t":"Online presence","u":"/docs/server/presence","b":["Server guide"]},{"i":3342,"t":"Proxy subscription streams","u":"/docs/server/proxy_streams","b":["Server guide"]},{"i":3356,"t":"Server-side subscriptions","u":"/docs/server/server_subs","b":["Server guide"]},{"i":3364,"t":"Proxy events to the backend","u":"/docs/server/proxy","b":["Server guide"]},{"i":3412,"t":"Configure TLS","u":"/docs/server/tls","b":["Server guide"]},{"i":3422,"t":"Server API walkthrough","u":"/docs/server/server_api","b":["Server guide"]},{"i":3472,"t":"HTTP streaming, with bidirectional emulation","u":"/docs/transports/http_stream","b":["Real-time transports / SDK","Bidirectional"]},{"i":3479,"t":"Real-time transports","u":"/docs/transports/overview","b":["Real-time transports / SDK"]},{"i":3487,"t":"Client real-time SDKs","u":"/docs/transports/client_sdk","b":["Real-time transports / SDK","Bidirectional"]},{"i":3499,"t":"SockJS","u":"/docs/transports/sockjs","b":["Real-time transports / SDK","Bidirectional"]},{"i":3514,"t":"SSE (EventSource), with bidirectional emulation","u":"/docs/transports/sse","b":["Real-time transports / SDK","Bidirectional"]},{"i":3521,"t":"Unidirectional client protocol","u":"/docs/transports/uni_client_protocol","b":["Real-time transports / SDK","Unidirectional"]},{"i":3525,"t":"Unidirectional GRPC","u":"/docs/transports/uni_grpc","b":["Real-time transports / SDK","Unidirectional"]},{"i":3546,"t":"Client protocol","u":"/docs/transports/client_protocol","b":["Real-time transports / SDK","Bidirectional"]},{"i":3564,"t":"Unidirectional WebSocket","u":"/docs/transports/uni_websocket","b":["Real-time transports / SDK","Unidirectional"]},{"i":3581,"t":"Unidirectional HTTP streaming","u":"/docs/transports/uni_http_stream","b":["Real-time transports / SDK","Unidirectional"]},{"i":3598,"t":"Unidirectional SSE (EventSource)","u":"/docs/transports/uni_sse","b":["Real-time transports / SDK","Unidirectional"]},{"i":3611,"t":"WebTransport","u":"/docs/transports/webtransport","b":["Real-time transports / SDK","Bidirectional"]},{"i":3613,"t":"WebSocket","u":"/docs/transports/websocket","b":["Real-time transports / SDK","Bidirectional"]},{"i":3630,"t":"Client SDK API","u":"/docs/transports/client_api","b":["Real-time transports / SDK","Bidirectional"]}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/1",[0,4.619,1,3.391,2,1.942]],["t/3",[3,4.026,4,2.597,5,3.328,6,4.026]],["t/23",[5,2.649,7,2.869,8,2.243,9,2.149,10,2.149,11,2.484]],["t/59",[2,1.942,12,4.136,13,3.818]],["t/91",[14,4.619,15,4.619,16,3.818]],["t/117",[2,1.118,8,1.862,17,1.862,18,2.661,19,2.383,20,2.661,21,2.383,22,2.383]],["t/143",[2,1.222,19,2.603,21,2.603,22,2.603,23,2.907,24,2.907,25,2.907]],["t/171",[2,1.222,26,2.907,27,2.907,28,2.907,29,1.696,30,2.907,31,1.446]],["t/193",[2,1.942,13,3.818,32,4.136]],["t/221",[2,1.5,11,2.766,33,3.568,34,2.393,35,3.568]],["t/245",[2,1.942,36,4.619,37,4.619]],["t/247",[2,1.347,38,3.204,39,2.149,40,2.649,41,2.649,42,1.4]],["t/271",[]],["t/272",[43,5.411]],["t/276",[7,4.85,44,4.85]],["t/278",[2,1.692,17,2.818,45,4.026,46,3.121]],["t/292",[1,1.801,2,1.031,4,1.582,47,2.453,48,2.453,49,2.453,50,2.453,51,1.901,52,2.453]],["t/302",[17,3.791,53,5.416]],["t/304",[54,3.818,55,3.818,56,3.818]],["t/354",[57,5.411]],["t/356",[58,4.477,59,3.976]],["t/372",[2,1.347,8,2.243,13,2.649,60,2.649,61,3.204,62,3.204]],["t/408",[63,4.477,64,4.477]],["t/444",[17,3.791,65,4.477]],["t/466",[2,0.956,5,1.881,44,2.037,66,2.275,67,2.275,68,1.764,69,2.275,70,2.275,71,2.275,72,2.275]],["t/486",[2,2.277,73,4.477]],["t/494",[42,2.019,74,2.412,75,3.818]],["t/514",[76,4.026,77,4.026,78,3.328,79,2.701]],["t/520",[2,2.277,80,3.791]],["t/536",[12,4.85,81,4.199]],["t/573",[2,1.942,39,3.098,59,3.391]],["t/581",[8,3.233,46,3.581,82,3.818]],["t/585",[39,2.701,80,2.818,83,3.328,84,3.328]],["t/599",[85,5.411]],["t/601",[86,4.477,87,4.477]],["t/615",[88,4.477,89,4.85]],["t/619",[90,4.477,91,4.477]],["t/639",[92,3.818,93,3.818,94,3.818]],["t/641",[42,2.019,95,3.818,96,3.818]],["t/655",[29,2.694,31,2.297,97,3.818]],["t/659",[29,2.694,42,2.019,98,3.818]],["t/673",[1,3.391,29,2.694,42,2.019]],["t/675",[29,3.159,99,4.477]],["t/687",[31,3.256]],["t/735",[51,4.199,74,2.828]],["t/777",[100,3.818,101,3.818,102,3.818]],["t/851",[103,3.818,104,3.818,105,3.818]],["t/859",[106,5.416,107,4.477]],["t/871",[2,2.277,79,3.633]],["t/946",[108,4.477,109,4.477]],["t/958",[110,4.477,111,4.477]],["t/966",[68,4.199,112,4.477]],["t/996",[113,4.477,114,4.477]],["t/1006",[115,5.411]],["t/1014",[31,2.694,116,5.416]],["t/1036",[117,3.098,118,3.818,119,3.581]],["t/1044",[79,3.633,120,4.477]],["t/1054",[121,4.199,122,4.477]],["t/1100",[9,2.701,10,2.701,74,2.103,123,2.956]],["t/1102",[42,2.367,117,3.633]],["t/1144",[124,5.411]],["t/1159",[74,2.828,125,4.199]],["t/1193",[34,3.098,126,2.543,127,3.391]],["t/1210",[126,2.543,128,3.391,129,3.391]],["t/1225",[126,2.981,130,4.477]],["t/1246",[4,3.494,126,2.981]],["t/1263",[9,3.098,10,3.098,131,3.818]],["t/1271",[4,4.223]],["t/1286",[43,5.411]],["t/1290",[54,3.818,55,3.818,56,3.818]],["t/1346",[57,5.411]],["t/1348",[132,4.85,133,4.85]],["t/1350",[134,4.85,135,4.85]],["t/1356",[42,2.019,74,2.412,75,3.818]],["t/1376",[58,4.477,59,3.976]],["t/1392",[63,4.477,64,4.477]],["t/1428",[2,2.277,80,3.791]],["t/1444",[17,3.791,65,4.477]],["t/1466",[2,2.277,73,4.477]],["t/1470",[31,2.694,136,4.85]],["t/1494",[8,3.233,46,3.581,82,3.818]],["t/1496",[60,4.477,81,4.199]],["t/1518",[137,4.85,138,4.85]],["t/1530",[86,4.477,87,4.477]],["t/1550",[39,2.701,80,2.818,83,3.328,84,3.328]],["t/1564",[31,2.694,139,4.85]],["t/1574",[1,3.976,42,2.367]],["t/1580",[2,1.942,39,3.098,59,3.391]],["t/1588",[90,4.477,91,4.477]],["t/1608",[11,3.581,140,4.136,141,4.136]],["t/1616",[92,3.818,93,3.818,94,3.818]],["t/1618",[85,5.411]],["t/1620",[40,3.818,41,3.818,42,2.019]],["t/1673",[88,4.477,89,4.85]],["t/1683",[29,2.694,31,2.297,97,3.818]],["t/1687",[42,2.019,95,3.818,96,3.818]],["t/1703",[29,2.694,42,2.019,98,3.818]],["t/1718",[103,3.818,104,3.818,105,3.818]],["t/1726",[29,2.694,42,2.019,99,3.818]],["t/1738",[31,2.297,142,4.136,143,4.136]],["t/1754",[51,3.581,74,2.412,144,3.581]],["t/1798",[31,2.297,144,3.581,145,4.136]],["t/1828",[100,3.818,101,3.818,102,3.818]],["t/1866",[31,2.694,78,4.477]],["t/1938",[2,2.277,79,3.633]],["t/2021",[68,4.199,112,4.477]],["t/2057",[107,3.818,146,4.136,147,4.136]],["t/2073",[108,4.477,109,4.477]],["t/2087",[115,4.477,148,4.85]],["t/2095",[110,4.477,111,4.477]],["t/2103",[113,4.477,114,4.477]],["t/2113",[149,4.85,150,4.85]],["t/2127",[117,3.098,118,3.818,119,3.581]],["t/2135",[121,3.581,122,3.818,151,3.818]],["t/2183",[42,2.019,117,3.098,152,4.136]],["t/2225",[79,3.633,120,4.477]],["t/2235",[74,2.828,125,4.199]],["t/2253",[9,2.701,10,2.701,74,2.103,123,2.956]],["t/2265",[34,2.701,127,2.956,153,3.121,154,3.121]],["t/2272",[124,5.411]],["t/2287",[9,3.098,10,3.098,131,3.818]],["t/2297",[128,2.956,129,2.956,153,3.121,154,3.121]],["t/2304",[126,2.981,130,4.477]],["t/2325",[34,3.098,126,2.543,127,3.391]],["t/2342",[42,2.019,74,2.412,123,3.391]],["t/2384",[126,2.543,128,3.391,129,3.391]],["t/2397",[4,3.494,126,2.981]],["t/2414",[43,5.411]],["t/2418",[4,4.223]],["t/2433",[16,5.411]],["t/2435",[57,5.411]],["t/2437",[54,3.818,55,3.818,56,3.818]],["t/2493",[132,4.85,133,4.85]],["t/2495",[155,5.416,156,5.416]],["t/2507",[42,2.019,74,2.412,75,3.818]],["t/2527",[58,4.477,59,3.976]],["t/2543",[134,4.85,135,4.85]],["t/2549",[63,4.477,64,4.477]],["t/2585",[17,3.791,65,4.477]],["t/2607",[2,2.277,73,4.477]],["t/2611",[2,2.277,80,3.791]],["t/2627",[60,4.477,81,4.199]],["t/2649",[32,4.85,81,4.199]],["t/2661",[8,3.233,46,3.581,82,3.818]],["t/2663",[31,2.694,136,4.85]],["t/2687",[137,4.85,138,4.85]],["t/2699",[86,4.477,87,4.477]],["t/2721",[31,2.297,151,3.818,157,4.619]],["t/2723",[31,2.694,139,4.85]],["t/2733",[11,3.581,140,4.136,141,4.136]],["t/2741",[42,1.76,158,4.026,159,3.605,160,3.605]],["t/2753",[39,2.701,80,2.818,83,3.328,84,3.328]],["t/2767",[1,3.976,42,2.367]],["t/2773",[161,4.85,162,5.416]],["t/2775",[2,2.277,39,3.633]],["t/2783",[90,4.477,91,4.477]],["t/2803",[92,3.818,93,3.818,94,3.818]],["t/2805",[85,5.411]],["t/2807",[29,2.694,31,2.297,97,3.818]],["t/2811",[42,2.019,95,3.818,96,3.818]],["t/2827",[88,3.818,159,4.136,160,4.136]],["t/2837",[40,3.818,41,3.818,42,2.019]],["t/2892",[29,2.694,42,2.019,98,3.818]],["t/2907",[103,3.818,104,3.818,105,3.818]],["t/2915",[29,2.694,42,2.019,99,3.818]],["t/2927",[31,2.297,142,4.136,143,4.136]],["t/2943",[51,3.581,74,2.412,144,3.581]],["t/2987",[31,2.297,144,3.581,145,4.136]],["t/3019",[100,3.818,101,3.818,102,3.818]],["t/3057",[31,2.694,78,4.477]],["t/3135",[107,3.818,146,4.136,147,4.136]],["t/3151",[2,2.277,79,3.633]],["t/3240",[108,4.477,109,4.477]],["t/3254",[110,4.477,111,4.477]],["t/3262",[68,4.199,112,4.477]],["t/3296",[115,4.477,148,4.85]],["t/3304",[113,4.477,114,4.477]],["t/3314",[117,3.633,161,4.85]],["t/3330",[149,4.85,150,4.85]],["t/3342",[34,3.098,119,3.581,121,3.581]],["t/3356",[117,3.098,118,3.818,119,3.581]],["t/3364",[121,3.581,122,3.818,151,3.818]],["t/3412",[79,3.633,120,4.477]],["t/3422",[42,2.019,117,3.098,152,4.136]],["t/3472",[34,2.701,127,2.956,153,3.121,154,3.121]],["t/3479",[9,3.098,10,3.098,131,3.818]],["t/3487",[9,2.701,10,2.701,74,2.103,123,2.956]],["t/3499",[124,5.411]],["t/3514",[128,2.956,129,2.956,153,3.121,154,3.121]],["t/3521",[74,2.412,125,3.581,126,2.543]],["t/3525",[126,2.981,130,4.477]],["t/3546",[74,2.828,125,4.199]],["t/3564",[4,3.494,126,2.981]],["t/3581",[34,3.098,126,2.543,127,3.391]],["t/3598",[126,2.543,128,3.391,129,3.391]],["t/3611",[16,5.411]],["t/3613",[4,4.223]],["t/3630",[42,2.019,74,2.412,123,3.391]]],"invertedIndex":[["",{"_index":8,"t":{"23":{"position":[[11,1]]},"117":{"position":[[35,1]]},"372":{"position":[[23,1]]},"581":{"position":[[20,2]]},"1494":{"position":[[20,2]]},"2661":{"position":[[20,2]]}}}],["101",{"_index":26,"t":{"171":{"position":[[0,3]]}}}],["admin",{"_index":103,"t":{"851":{"position":[[0,5]]},"1718":{"position":[[0,5]]},"2907":{"position":[[0,5]]}}}],["alloc",{"_index":70,"t":{"466":{"position":[[49,10]]}}}],["analyt",{"_index":86,"t":{"601":{"position":[[0,9]]},"1530":{"position":[[0,9]]},"2699":{"position":[[0,9]]}}}],["api",{"_index":42,"t":{"247":{"position":[[47,3]]},"494":{"position":[[7,3]]},"641":{"position":[[17,3]]},"659":{"position":[[14,3]]},"673":{"position":[[17,3]]},"1102":{"position":[[7,3]]},"1356":{"position":[[7,3]]},"1574":{"position":[[12,3]]},"1620":{"position":[[18,3]]},"1687":{"position":[[17,3]]},"1703":{"position":[[14,3]]},"1726":{"position":[[12,3]]},"2183":{"position":[[7,3]]},"2342":{"position":[[11,3]]},"2507":{"position":[[7,3]]},"2741":{"position":[[23,3]]},"2767":{"position":[[12,3]]},"2811":{"position":[[17,3]]},"2837":{"position":[[18,3]]},"2892":{"position":[[14,3]]},"2915":{"position":[[12,3]]},"3422":{"position":[[7,3]]},"3630":{"position":[[11,3]]}}}],["applic",{"_index":22,"t":{"117":{"position":[[59,11]]},"143":{"position":[[27,11]]}}}],["ask",{"_index":55,"t":{"304":{"position":[[11,5]]},"1290":{"position":[[11,5]]},"2437":{"position":[[11,5]]}}}],["asynchron",{"_index":33,"t":{"221":{"position":[[0,12]]}}}],["attribut",{"_index":43,"t":{"272":{"position":[[0,12]]},"1286":{"position":[[0,12]]},"2414":{"position":[[0,12]]}}}],["authent",{"_index":51,"t":{"292":{"position":[[24,14]]},"735":{"position":[[7,14]]},"1754":{"position":[[11,14]]},"2943":{"position":[[11,14]]}}}],["author",{"_index":145,"t":{"1798":{"position":[[12,13]]},"2987":{"position":[[12,13]]}}}],["backend",{"_index":122,"t":{"1054":{"position":[[9,7]]},"2135":{"position":[[20,7]]},"3364":{"position":[[20,7]]}}}],["balanc",{"_index":114,"t":{"996":{"position":[[5,9]]},"2103":{"position":[[5,9]]},"3304":{"position":[[5,9]]}}}],["basic",{"_index":20,"t":{"117":{"position":[[48,5]]}}}],["batch",{"_index":140,"t":{"1608":{"position":[[8,8]]},"2733":{"position":[[8,8]]}}}],["bentho",{"_index":35,"t":{"221":{"position":[[50,7]]}}}],["beyond",{"_index":6,"t":{"3":{"position":[[28,6]]}}}],["bidirect",{"_index":153,"t":{"2265":{"position":[[21,13]]},"2297":{"position":[[24,13]]},"3472":{"position":[[21,13]]},"3514":{"position":[[24,13]]}}}],["block",{"_index":98,"t":{"659":{"position":[[5,8]]},"1703":{"position":[[5,8]]},"2892":{"position":[[5,8]]}}}],["build",{"_index":19,"t":{"117":{"position":[[37,8]]},"143":{"position":[[0,8]]}}}],["capabl",{"_index":136,"t":{"1470":{"position":[[8,12]]},"2663":{"position":[[8,12]]}}}],["cel",{"_index":137,"t":{"1518":{"position":[[0,3]]},"2687":{"position":[[0,3]]}}}],["centrifug",{"_index":7,"t":{"23":{"position":[[0,10]]},"276":{"position":[[0,10]]}}}],["centrifugo",{"_index":2,"t":{"1":{"position":[[25,10]]},"59":{"position":[[0,10]]},"117":{"position":[[0,10]]},"143":{"position":[[56,10]]},"171":{"position":[[52,10]]},"193":{"position":[[0,10]]},"221":{"position":[[34,10]]},"245":{"position":[[6,10]]},"247":{"position":[[12,10]]},"278":{"position":[[0,10]]},"292":{"position":[[62,10]]},"372":{"position":[[0,10]]},"466":{"position":[[10,10]]},"486":{"position":[[0,10]]},"520":{"position":[[8,10]]},"573":{"position":[[0,10]]},"871":{"position":[[10,10]]},"1428":{"position":[[8,10]]},"1466":{"position":[[0,10]]},"1580":{"position":[[0,10]]},"1938":{"position":[[10,10]]},"2607":{"position":[[0,10]]},"2611":{"position":[[8,10]]},"2775":{"position":[[0,10]]},"3151":{"position":[[10,10]]}}}],["channel",{"_index":31,"t":{"171":{"position":[[41,7]]},"655":{"position":[[9,7]]},"687":{"position":[[0,8]]},"1014":{"position":[[8,8]]},"1470":{"position":[[0,7]]},"1564":{"position":[[0,7]]},"1683":{"position":[[9,7]]},"1738":{"position":[[0,7]]},"1798":{"position":[[0,7]]},"1866":{"position":[[0,8]]},"2663":{"position":[[0,7]]},"2721":{"position":[[0,7]]},"2723":{"position":[[0,7]]},"2807":{"position":[[9,7]]},"2927":{"position":[[0,7]]},"2987":{"position":[[0,7]]},"3057":{"position":[[0,8]]}}}],["chat",{"_index":21,"t":{"117":{"position":[[54,4]]},"143":{"position":[[22,4]]}}}],["cli",{"_index":147,"t":{"2057":{"position":[[7,3]]},"3135":{"position":[[7,3]]}}}],["clickhous",{"_index":87,"t":{"601":{"position":[[15,10]]},"1530":{"position":[[15,10]]},"2699":{"position":[[15,10]]}}}],["client",{"_index":74,"t":{"494":{"position":[[0,6]]},"735":{"position":[[0,6]]},"1100":{"position":[[0,6]]},"1159":{"position":[[0,6]]},"1356":{"position":[[0,6]]},"1754":{"position":[[0,6]]},"2235":{"position":[[0,6]]},"2253":{"position":[[0,6]]},"2342":{"position":[[0,6]]},"2507":{"position":[[0,6]]},"2943":{"position":[[0,6]]},"3487":{"position":[[0,6]]},"3521":{"position":[[15,6]]},"3546":{"position":[[0,6]]},"3630":{"position":[[0,6]]}}}],["code",{"_index":102,"t":{"777":{"position":[[21,5]]},"1828":{"position":[[21,5]]},"3019":{"position":[[21,5]]}}}],["command",{"_index":107,"t":{"859":{"position":[[8,8]]},"2057":{"position":[[11,8]]},"3135":{"position":[[11,8]]}}}],["commun",{"_index":133,"t":{"1348":{"position":[[5,9]]},"2493":{"position":[[5,9]]}}}],["compar",{"_index":155,"t":{"2495":{"position":[[0,9]]}}}],["configur",{"_index":79,"t":{"514":{"position":[[26,13]]},"871":{"position":[[0,9]]},"1044":{"position":[[0,9]]},"1938":{"position":[[0,9]]},"2225":{"position":[[0,9]]},"3151":{"position":[[0,9]]},"3412":{"position":[[0,9]]}}}],["connect",{"_index":1,"t":{"1":{"position":[[8,11]]},"292":{"position":[[48,10]]},"673":{"position":[[5,11]]},"1574":{"position":[[0,11]]},"2767":{"position":[[0,11]]}}}],["consol",{"_index":106,"t":{"859":{"position":[[0,7]]}}}],["control",{"_index":141,"t":{"1608":{"position":[[17,7]]},"2733":{"position":[[17,7]]}}}],["cpu",{"_index":92,"t":{"639":{"position":[[0,3]]},"1616":{"position":[[0,3]]},"2803":{"position":[[0,3]]}}}],["databas",{"_index":76,"t":{"514":{"position":[[0,8]]}}}],["design",{"_index":58,"t":{"356":{"position":[[0,6]]},"1376":{"position":[[0,6]]},"2527":{"position":[[0,6]]}}}],["disconnect",{"_index":101,"t":{"777":{"position":[[10,10]]},"1828":{"position":[[10,10]]},"3019":{"position":[[10,10]]}}}],["discov",{"_index":38,"t":{"247":{"position":[[0,11]]}}}],["distribut",{"_index":158,"t":{"2741":{"position":[[0,11]]}}}],["django",{"_index":18,"t":{"117":{"position":[[28,6]]}}}],["driven",{"_index":77,"t":{"514":{"position":[[9,6]]}}}],["ecosystem",{"_index":134,"t":{"1350":{"position":[[0,9]]},"2543":{"position":[[0,9]]}}}],["effici",{"_index":71,"t":{"466":{"position":[[60,10]]}}}],["emul",{"_index":154,"t":{"2265":{"position":[[35,9]]},"2297":{"position":[[38,9]]},"3472":{"position":[[35,9]]},"3514":{"position":[[38,9]]}}}],["engin",{"_index":68,"t":{"466":{"position":[[27,6]]},"966":{"position":[[0,8]]},"2021":{"position":[[0,7]]},"3262":{"position":[[0,7]]}}}],["enhanc",{"_index":162,"t":{"2773":{"position":[[14,12]]}}}],["error",{"_index":100,"t":{"777":{"position":[[0,5]]},"1828":{"position":[[0,5]]},"3019":{"position":[[0,5]]}}}],["event",{"_index":151,"t":{"2135":{"position":[[6,6]]},"2721":{"position":[[14,6]]},"3364":{"position":[[6,6]]}}}],["eventsourc",{"_index":129,"t":{"1210":{"position":[[19,13]]},"2297":{"position":[[4,14]]},"2384":{"position":[[19,13]]},"3514":{"position":[[4,14]]},"3598":{"position":[[19,13]]}}}],["experi",{"_index":14,"t":{"91":{"position":[[0,13]]}}}],["express",{"_index":138,"t":{"1518":{"position":[[4,11]]},"2687":{"position":[[4,11]]}}}],["faster",{"_index":90,"t":{"619":{"position":[[0,6]]},"1588":{"position":[[0,6]]},"2783":{"position":[[0,6]]}}}],["flow",{"_index":52,"t":{"292":{"position":[[39,4]]}}}],["flow_diagram",{"_index":57,"t":{"354":{"position":[[0,13]]},"1346":{"position":[[0,13]]},"2435":{"position":[[0,13]]}}}],["framework",{"_index":53,"t":{"302":{"position":[[0,9]]}}}],["frequent",{"_index":54,"t":{"304":{"position":[[0,10]]},"1290":{"position":[[0,10]]},"2437":{"position":[[0,10]]}}}],["go",{"_index":5,"t":{"3":{"position":[[21,2]]},"23":{"position":[[38,2]]},"466":{"position":[[84,2]]}}}],["grpc",{"_index":130,"t":{"1225":{"position":[[15,4]]},"2304":{"position":[[15,4]]},"3525":{"position":[[15,4]]}}}],["guid",{"_index":65,"t":{"444":{"position":[[12,5]]},"1444":{"position":[[12,5]]},"2585":{"position":[[12,5]]}}}],["helper",{"_index":146,"t":{"2057":{"position":[[0,6]]},"3135":{"position":[[0,6]]}}}],["highlight",{"_index":64,"t":{"408":{"position":[[5,10]]},"1392":{"position":[[5,10]]},"2549":{"position":[[5,10]]}}}],["histori",{"_index":110,"t":{"958":{"position":[[0,7]]},"2095":{"position":[[0,7]]},"3254":{"position":[[0,7]]}}}],["http",{"_index":127,"t":{"1193":{"position":[[15,4]]},"2265":{"position":[[0,4]]},"2325":{"position":[[15,4]]},"3472":{"position":[[0,4]]},"3581":{"position":[[15,4]]}}}],["improv",{"_index":66,"t":{"466":{"position":[[0,9]]}}}],["infrastructur",{"_index":108,"t":{"946":{"position":[[0,14]]},"2073":{"position":[[0,14]]},"3240":{"position":[[0,14]]}}}],["instal",{"_index":80,"t":{"520":{"position":[[0,7]]},"585":{"position":[[0,7]]},"1428":{"position":[[0,7]]},"1550":{"position":[[0,7]]},"2611":{"position":[[0,7]]},"2753":{"position":[[0,7]]}}}],["integr",{"_index":17,"t":{"117":{"position":[[11,11]]},"278":{"position":[[11,11]]},"302":{"position":[[10,12]]},"444":{"position":[[0,11]]},"1444":{"position":[[0,11]]},"2585":{"position":[[0,11]]}}}],["introduct",{"_index":73,"t":{"486":{"position":[[11,12]]},"1466":{"position":[[11,12]]},"2607":{"position":[[11,12]]}}}],["join",{"_index":132,"t":{"1348":{"position":[[0,4]]},"2493":{"position":[[0,4]]}}}],["jwt",{"_index":144,"t":{"1754":{"position":[[7,3]]},"1798":{"position":[[8,3]]},"2943":{"position":[[7,3]]},"2987":{"position":[[8,3]]}}}],["keycloak",{"_index":49,"t":{"292":{"position":[[11,8]]}}}],["laravel",{"_index":25,"t":{"143":{"position":[[44,7]]}}}],["librari",{"_index":44,"t":{"276":{"position":[[11,7]]},"466":{"position":[[87,7]]}}}],["limit",{"_index":160,"t":{"2741":{"position":[[17,5]]},"2827":{"position":[[15,6]]}}}],["littl",{"_index":61,"t":{"372":{"position":[[27,6]]}}}],["load",{"_index":113,"t":{"996":{"position":[[0,4]]},"2103":{"position":[[0,4]]},"3304":{"position":[[0,4]]}}}],["main",{"_index":63,"t":{"408":{"position":[[0,4]]},"1392":{"position":[[0,4]]},"2549":{"position":[[0,4]]}}}],["messag",{"_index":11,"t":{"23":{"position":[[23,9]]},"221":{"position":[[13,7]]},"1608":{"position":[[0,7]]},"2733":{"position":[[0,7]]}}}],["metric",{"_index":148,"t":{"2087":{"position":[[0,7]]},"3296":{"position":[[0,7]]}}}],["migrat",{"_index":81,"t":{"536":{"position":[[0,9]]},"1496":{"position":[[0,9]]},"2627":{"position":[[0,9]]},"2649":{"position":[[0,9]]}}}],["million",{"_index":0,"t":{"1":{"position":[[0,7]]}}}],["model",{"_index":143,"t":{"1738":{"position":[[19,5]]},"2927":{"position":[[19,5]]}}}],["monitor",{"_index":115,"t":{"1006":{"position":[[0,10]]},"2087":{"position":[[8,10]]},"3296":{"position":[[8,10]]}}}],["multi",{"_index":23,"t":{"143":{"position":[[11,5]]}}}],["namespac",{"_index":78,"t":{"514":{"position":[[16,9]]},"1866":{"position":[[13,10]]},"3057":{"position":[[13,10]]}}}],["nodej",{"_index":45,"t":{"278":{"position":[[28,6]]}}}],["note",{"_index":135,"t":{"1350":{"position":[[10,5]]},"2543":{"position":[[10,5]]}}}],["notif",{"_index":41,"t":{"247":{"position":[[33,13]]},"1620":{"position":[[5,12]]},"2837":{"position":[[5,12]]}}}],["observ",{"_index":161,"t":{"2773":{"position":[[0,13]]},"3314":{"position":[[7,13]]}}}],["onlin",{"_index":149,"t":{"2113":{"position":[[0,6]]},"3330":{"position":[[0,6]]}}}],["oper",{"_index":88,"t":{"615":{"position":[[0,9]]},"1673":{"position":[[0,9]]},"2827":{"position":[[0,9]]}}}],["other",{"_index":156,"t":{"2495":{"position":[[15,6]]}}}],["overview",{"_index":59,"t":{"356":{"position":[[7,8]]},"573":{"position":[[15,8]]},"1376":{"position":[[7,8]]},"1580":{"position":[[15,8]]},"2527":{"position":[[7,8]]}}}],["pattern",{"_index":139,"t":{"1564":{"position":[[8,8]]},"2723":{"position":[[8,8]]}}}],["perform",{"_index":91,"t":{"619":{"position":[[7,11]]},"1588":{"position":[[7,11]]},"2783":{"position":[[7,11]]}}}],["permiss",{"_index":142,"t":{"1738":{"position":[[8,10]]},"2927":{"position":[[8,10]]}}}],["person",{"_index":30,"t":{"171":{"position":[[32,8]]}}}],["presenc",{"_index":150,"t":{"2113":{"position":[[7,8]]},"3330":{"position":[[7,8]]}}}],["privat",{"_index":116,"t":{"1014":{"position":[[0,7]]}}}],["pro",{"_index":39,"t":{"247":{"position":[[23,4]]},"573":{"position":[[11,3]]},"585":{"position":[[16,3]]},"1550":{"position":[[16,3]]},"1580":{"position":[[11,3]]},"2753":{"position":[[16,3]]},"2775":{"position":[[11,3]]}}}],["protocol",{"_index":125,"t":{"1159":{"position":[[7,8]]},"2235":{"position":[[7,8]]},"3521":{"position":[[22,8]]},"3546":{"position":[[7,8]]}}}],["proxi",{"_index":121,"t":{"1054":{"position":[[0,5]]},"2135":{"position":[[0,5]]},"3342":{"position":[[0,5]]},"3364":{"position":[[0,5]]}}}],["push",{"_index":40,"t":{"247":{"position":[[28,4]]},"1620":{"position":[[0,4]]},"2837":{"position":[[0,4]]}}}],["question",{"_index":56,"t":{"304":{"position":[[17,9]]},"1290":{"position":[[17,9]]},"2437":{"position":[[17,9]]}}}],["quic",{"_index":15,"t":{"91":{"position":[[19,4]]}}}],["quickstart",{"_index":82,"t":{"581":{"position":[[0,10]]},"1494":{"position":[[0,10]]},"2661":{"position":[[0,10]]}}}],["rabbitx",{"_index":37,"t":{"245":{"position":[[20,7]]}}}],["rate",{"_index":159,"t":{"2741":{"position":[[12,4]]},"2827":{"position":[[10,4]]}}}],["real",{"_index":9,"t":{"23":{"position":[[13,4]]},"1100":{"position":[[7,4]]},"1263":{"position":[[0,4]]},"2253":{"position":[[7,4]]},"2287":{"position":[[0,4]]},"3479":{"position":[[0,4]]},"3487":{"position":[[7,4]]}}}],["recoveri",{"_index":111,"t":{"958":{"position":[[12,8]]},"2095":{"position":[[12,8]]},"3254":{"position":[[12,8]]}}}],["redi",{"_index":67,"t":{"466":{"position":[[21,5]]}}}],["releas",{"_index":13,"t":{"59":{"position":[[14,8]]},"193":{"position":[[14,8]]},"372":{"position":[[14,8]]}}}],["revoc",{"_index":96,"t":{"641":{"position":[[6,10]]},"1687":{"position":[[6,10]]},"2811":{"position":[[6,10]]}}}],["revolut",{"_index":62,"t":{"372":{"position":[[34,10]]}}}],["room",{"_index":24,"t":{"143":{"position":[[17,4]]}}}],["rss",{"_index":93,"t":{"639":{"position":[[8,3]]},"1616":{"position":[[8,3]]},"2803":{"position":[[8,3]]}}}],["rueidi",{"_index":72,"t":{"466":{"position":[[76,7]]}}}],["run",{"_index":83,"t":{"585":{"position":[[12,3]]},"1550":{"position":[[12,3]]},"2753":{"position":[[12,3]]}}}],["scalabl",{"_index":112,"t":{"966":{"position":[[9,11]]},"2021":{"position":[[12,11]]},"3262":{"position":[[12,11]]}}}],["scale",{"_index":3,"t":{"3":{"position":[[0,7]]}}}],["sdk",{"_index":123,"t":{"1100":{"position":[[17,4]]},"2253":{"position":[[17,4]]},"2342":{"position":[[7,3]]},"3487":{"position":[[17,4]]},"3630":{"position":[[7,3]]}}}],["server",{"_index":117,"t":{"1036":{"position":[[0,6]]},"1102":{"position":[[0,6]]},"2127":{"position":[[0,6]]},"2183":{"position":[[0,6]]},"3314":{"position":[[0,6]]},"3356":{"position":[[0,6]]},"3422":{"position":[[0,6]]}}}],["set",{"_index":47,"t":{"292":{"position":[[0,7]]}}}],["showcas",{"_index":75,"t":{"494":{"position":[[11,8]]},"1356":{"position":[[11,8]]},"2507":{"position":[[11,8]]}}}],["side",{"_index":118,"t":{"1036":{"position":[[7,4]]},"2127":{"position":[[7,4]]},"3356":{"position":[[7,4]]}}}],["singleflight",{"_index":85,"t":{"599":{"position":[[0,12]]},"1618":{"position":[[0,12]]},"2805":{"position":[[0,12]]}}}],["sockj",{"_index":124,"t":{"1144":{"position":[[0,6]]},"2272":{"position":[[0,6]]},"3499":{"position":[[0,6]]}}}],["sse",{"_index":128,"t":{"1210":{"position":[[15,3]]},"2297":{"position":[[0,3]]},"2384":{"position":[[15,3]]},"3514":{"position":[[0,3]]},"3598":{"position":[[15,3]]}}}],["sso",{"_index":50,"t":{"292":{"position":[[20,3]]}}}],["stat",{"_index":94,"t":{"639":{"position":[[12,5]]},"1616":{"position":[[12,5]]},"2803":{"position":[[12,5]]}}}],["state",{"_index":157,"t":{"2721":{"position":[[8,5]]}}}],["statu",{"_index":99,"t":{"675":{"position":[[5,6]]},"1726":{"position":[[5,6]]},"2915":{"position":[[5,6]]}}}],["stream",{"_index":34,"t":{"221":{"position":[[21,9]]},"1193":{"position":[[20,9]]},"2265":{"position":[[5,10]]},"2325":{"position":[[20,9]]},"3342":{"position":[[19,7]]},"3472":{"position":[[5,10]]},"3581":{"position":[[20,9]]}}}],["subscrib",{"_index":28,"t":{"171":{"position":[[12,9]]}}}],["subscript",{"_index":119,"t":{"1036":{"position":[[12,13]]},"2127":{"position":[[12,13]]},"3342":{"position":[[6,12]]},"3356":{"position":[[12,13]]}}}],["throttl",{"_index":89,"t":{"615":{"position":[[10,10]]},"1673":{"position":[[10,10]]}}}],["throughput",{"_index":69,"t":{"466":{"position":[[34,10]]}}}],["time",{"_index":10,"t":{"23":{"position":[[18,4]]},"1100":{"position":[[12,4]]},"1263":{"position":[[5,4]]},"2253":{"position":[[12,4]]},"2287":{"position":[[5,4]]},"3479":{"position":[[5,4]]},"3487":{"position":[[12,4]]}}}],["tl",{"_index":120,"t":{"1044":{"position":[[10,3]]},"2225":{"position":[[10,3]]},"3412":{"position":[[10,3]]}}}],["token",{"_index":95,"t":{"641":{"position":[[0,5]]},"1687":{"position":[[0,5]]},"2811":{"position":[[0,5]]}}}],["trace",{"_index":97,"t":{"655":{"position":[[17,7]]},"1683":{"position":[[17,7]]},"2807":{"position":[[17,7]]}}}],["transport",{"_index":131,"t":{"1263":{"position":[[10,10]]},"2287":{"position":[[10,10]]},"3479":{"position":[[10,10]]}}}],["tune",{"_index":109,"t":{"946":{"position":[[15,6]]},"2073":{"position":[[15,6]]},"3240":{"position":[[15,6]]}}}],["tutori",{"_index":46,"t":{"278":{"position":[[35,8]]},"581":{"position":[[11,8]]},"1494":{"position":[[11,8]]},"2661":{"position":[[11,8]]}}}],["ui",{"_index":105,"t":{"851":{"position":[[10,2]]},"1718":{"position":[[10,2]]},"2907":{"position":[[10,2]]}}}],["unidirect",{"_index":126,"t":{"1193":{"position":[[0,14]]},"1210":{"position":[[0,14]]},"1225":{"position":[[0,14]]},"1246":{"position":[[0,14]]},"2304":{"position":[[0,14]]},"2325":{"position":[[0,14]]},"2384":{"position":[[0,14]]},"2397":{"position":[[0,14]]},"3521":{"position":[[0,14]]},"3525":{"position":[[0,14]]},"3564":{"position":[[0,14]]},"3581":{"position":[[0,14]]},"3598":{"position":[[0,14]]}}}],["up",{"_index":48,"t":{"292":{"position":[[8,2]]}}}],["us",{"_index":36,"t":{"245":{"position":[[0,5]]}}}],["user",{"_index":29,"t":{"171":{"position":[[22,4]]},"655":{"position":[[0,4]]},"659":{"position":[[0,4]]},"673":{"position":[[0,4]]},"675":{"position":[[0,4]]},"1683":{"position":[[0,4]]},"1703":{"position":[[0,4]]},"1726":{"position":[[0,4]]},"2807":{"position":[[0,4]]},"2892":{"position":[[0,4]]},"2915":{"position":[[0,4]]}}}],["v3",{"_index":12,"t":{"59":{"position":[[11,2]]},"536":{"position":[[13,2]]}}}],["v4",{"_index":60,"t":{"372":{"position":[[11,2]]},"1496":{"position":[[13,2]]},"2627":{"position":[[13,2]]}}}],["v5",{"_index":32,"t":{"193":{"position":[[11,2]]},"2649":{"position":[[13,2]]}}}],["version",{"_index":84,"t":{"585":{"position":[[20,7]]},"1550":{"position":[[20,7]]},"2753":{"position":[[20,7]]}}}],["walkthrough",{"_index":152,"t":{"2183":{"position":[[11,11]]},"3422":{"position":[[11,11]]}}}],["way",{"_index":27,"t":{"171":{"position":[[4,4]]}}}],["web",{"_index":104,"t":{"851":{"position":[[6,3]]},"1718":{"position":[[6,3]]},"2907":{"position":[[6,3]]}}}],["websocket",{"_index":4,"t":{"3":{"position":[[8,9]]},"292":{"position":[[73,9]]},"1246":{"position":[[15,9]]},"1271":{"position":[[0,9]]},"2397":{"position":[[15,9]]},"2418":{"position":[[0,9]]},"3564":{"position":[[15,9]]},"3613":{"position":[[0,9]]}}}],["webtransport",{"_index":16,"t":{"91":{"position":[[28,12]]},"2433":{"position":[[0,12]]},"3611":{"position":[[0,12]]}}}]],"pipeline":["stemmer"]}},{"documents":[{"i":5,"t":"WebSocket server tasks","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-server-tasks","p":3},{"i":7,"t":"WebSocket libraries","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-libraries","p":3},{"i":9,"t":"OS tuning","u":"/blog/2020/11/12/scaling-websocket","h":"#os-tuning","p":3},{"i":11,"t":"Sending many messages","u":"/blog/2020/11/12/scaling-websocket","h":"#sending-many-messages","p":3},{"i":13,"t":"WebSocket fallback transport","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-fallback-transport","p":3},{"i":15,"t":"Performance is not scalability","u":"/blog/2020/11/12/scaling-websocket","h":"#performance-is-not-scalability","p":3},{"i":17,"t":"Massive reconnect","u":"/blog/2020/11/12/scaling-websocket","h":"#massive-reconnect","p":3},{"i":19,"t":"Message event stream benefits","u":"/blog/2020/11/12/scaling-websocket","h":"#message-event-stream-benefits","p":3},{"i":21,"t":"Conclusion","u":"/blog/2020/11/12/scaling-websocket","h":"#conclusion","p":3},{"i":25,"t":"How it's all started","u":"/blog/2021/01/15/centrifuge-intro","h":"#how-its-all-started","p":23},{"i":27,"t":"So what is Centrifuge?","u":"/blog/2021/01/15/centrifuge-intro","h":"#so-what-is-centrifuge","p":23},{"i":29,"t":"Centrifuge Node","u":"/blog/2021/01/15/centrifuge-intro","h":"#centrifuge-node","p":23},{"i":31,"t":"Authentication","u":"/blog/2021/01/15/centrifuge-intro","h":"#authentication","p":23},{"i":33,"t":"Channel subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#channel-subscriptions","p":23},{"i":35,"t":"Async message passing","u":"/blog/2021/01/15/centrifuge-intro","h":"#async-message-passing","p":23},{"i":37,"t":"RPC","u":"/blog/2021/01/15/centrifuge-intro","h":"#rpc","p":23},{"i":39,"t":"Server-side subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#server-side-subscriptions","p":23},{"i":41,"t":"Windowed history in channel","u":"/blog/2021/01/15/centrifuge-intro","h":"#windowed-history-in-channel","p":23},{"i":43,"t":"Online presence and presence stats","u":"/blog/2021/01/15/centrifuge-intro","h":"#online-presence-and-presence-stats","p":23},{"i":45,"t":"Scalability aspects","u":"/blog/2021/01/15/centrifuge-intro","h":"#scalability-aspects","p":23},{"i":47,"t":"Order and delivery properties","u":"/blog/2021/01/15/centrifuge-intro","h":"#order-and-delivery-properties","p":23},{"i":49,"t":"Ecosystem","u":"/blog/2021/01/15/centrifuge-intro","h":"#ecosystem","p":23},{"i":51,"t":"Performance","u":"/blog/2021/01/15/centrifuge-intro","h":"#performance","p":23},{"i":53,"t":"Limitations","u":"/blog/2021/01/15/centrifuge-intro","h":"#limitations","p":23},{"i":55,"t":"Examples","u":"/blog/2021/01/15/centrifuge-intro","h":"#examples","p":23},{"i":57,"t":"Conclusion","u":"/blog/2021/01/15/centrifuge-intro","h":"#conclusion","p":23},{"i":61,"t":"Centrifugo v2 flashbacks","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-v2-flashbacks","p":59},{"i":63,"t":"Backwards compatibility","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#backwards-compatibility","p":59},{"i":65,"t":"License change","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#license-change","p":59},{"i":67,"t":"Unidirectional real-time transports","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#unidirectional-real-time-transports","p":59},{"i":69,"t":"History iteration API","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#history-iteration-api","p":59},{"i":71,"t":"Redis Streams by default","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#redis-streams-by-default","p":59},{"i":73,"t":"Tarantool engine","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#tarantool-engine","p":59},{"i":75,"t":"GRPC proxy","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#grpc-proxy","p":59},{"i":77,"t":"Server API improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#server-api-improvements","p":59},{"i":79,"t":"Better clustering","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#better-clustering","p":59},{"i":81,"t":"Client improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#client-improvements","p":59},{"i":83,"t":"New documentation site","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#new-documentation-site","p":59},{"i":85,"t":"Performance improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#performance-improvements","p":59},{"i":87,"t":"Centrifugo PRO","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-pro","p":59},{"i":89,"t":"Conclusion","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#conclusion","p":59},{"i":93,"t":"Overview","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#overview","p":91},{"i":95,"t":"Install Chrome Canary","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#install-chrome-canary","p":91},{"i":97,"t":"Generate self-signed TLS certificates","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#generate-self-signed-tls-certificates","p":91},{"i":99,"t":"Run client example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#run-client-example","p":91},{"i":101,"t":"Writing a QUIC server","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#writing-a-quic-server","p":91},{"i":103,"t":"Server skeleton","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#server-skeleton","p":91},{"i":105,"t":"Accept QUIC connections","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#accept-quic-connections","p":91},{"i":107,"t":"Connection Session handling","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#connection-session-handling","p":91},{"i":109,"t":"Parsing client indication","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#parsing-client-indication","p":91},{"i":111,"t":"Communicating over bidirectional streams","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#communicating-over-bidirectional-streams","p":91},{"i":113,"t":"Full server example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#full-server-example","p":91},{"i":115,"t":"Conclusion","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#conclusion","p":91},{"i":119,"t":"Why integrate Django with Centrifugo","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#why-integrate-django-with-centrifugo","p":117},{"i":121,"t":"Prerequisites","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#prerequisites","p":117},{"i":123,"t":"Creating a project","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-a-project","p":117},{"i":125,"t":"Creating the chat app","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-the-chat-app","p":117},{"i":127,"t":"Add the index view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-index-view","p":117},{"i":129,"t":"Add the room view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-room-view","p":117},{"i":131,"t":"Starting Centrifugo server","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#starting-centrifugo-server","p":117},{"i":133,"t":"Adding Nginx","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#adding-nginx","p":117},{"i":135,"t":"Implementing proxy handlers","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#implementing-proxy-handlers","p":117},{"i":137,"t":"What could be improved","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#what-could-be-improved","p":117},{"i":139,"t":"Tutorial source code with docker-compose","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#tutorial-source-code-with-docker-compose","p":117},{"i":141,"t":"Conclusion","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#conclusion","p":117},{"i":145,"t":"Application overview","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-overview","p":143},{"i":147,"t":"Why integrate Laravel with Centrifugo?","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#why-integrate-laravel-with-centrifugo","p":143},{"i":149,"t":"Setup and start a project","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#setup-and-start-a-project","p":143},{"i":151,"t":"Application structure","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-structure","p":143},{"i":153,"t":"Environment settings","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#environment-settings","p":143},{"i":155,"t":"Database migrations and models","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#database-migrations-and-models","p":143},{"i":157,"t":"Broadcasting","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#broadcasting","p":143},{"i":159,"t":"Interaction with Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#interaction-with-centrifugo","p":143},{"i":161,"t":"Connect proxy controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#connect-proxy-controller","p":143},{"i":163,"t":"Room controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#room-controller","p":143},{"i":165,"t":"Client side","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#client-side","p":143},{"i":167,"t":"Possible improvements","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#possible-improvements","p":143},{"i":169,"t":"Conclusion","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#conclusion","p":143},{"i":173,"t":"Setup","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#setup","p":171},{"i":175,"t":"#1 – user-limited channel","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#1--user-limited-channel","p":171},{"i":177,"t":"#2 - channel token authorization","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#2---channel-token-authorization","p":171},{"i":179,"t":"#3 - subscribe proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#3---subscribe-proxy","p":171},{"i":181,"t":"#4 - server-side channel in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#4---server-side-channel-in-connection-jwt","p":171},{"i":183,"t":"#5 - server-side channel in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#5---server-side-channel-in-connect-proxy","p":171},{"i":185,"t":"#6 - automatic personal channel subscription","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#6---automatic-personal-channel-subscription","p":171},{"i":187,"t":"#7 – capabilities in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#7--capabilities-in-connection-jwt","p":171},{"i":189,"t":"#8 – capabilities in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#8--capabilities-in-connect-proxy","p":171},{"i":191,"t":"Teardown","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#teardown","p":171},{"i":195,"t":"Dropping old client protocol","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#dropping-old-client-protocol","p":193},{"i":197,"t":"Token behaviour adjustments in SDKs","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#token-behaviour-adjustments-in-sdks","p":193},{"i":199,"t":"history_meta_ttl refactoring","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#history_meta_ttl-refactoring","p":193},{"i":201,"t":"Node communication protocol update","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#node-communication-protocol-update","p":193},{"i":203,"t":"New HTTP API format","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#new-http-api-format","p":193},{"i":205,"t":"OpenAPI spec and Swagger UI","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#openapi-spec-and-swagger-ui","p":193},{"i":207,"t":"OpenTelemetry for server API","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#opentelemetry-for-server-api","p":193},{"i":209,"t":"Separate config for subscription token","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#separate-config-for-subscription-token","p":193},{"i":211,"t":"Unknown config keys warnings","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#unknown-config-keys-warnings","p":193},{"i":213,"t":"Simplifying protocol debug with Postman","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#simplifying-protocol-debug-with-postman","p":193},{"i":215,"t":"The future of SockJS","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#the-future-of-sockjs","p":193},{"i":217,"t":"Introducing Centrifugal Labs LTD","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#introducing-centrifugal-labs-ltd","p":193},{"i":219,"t":"Conclusion","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#conclusion","p":193},{"i":223,"t":"Start Centrifugo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#start-centrifugo","p":221},{"i":225,"t":"Install and run Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#install-and-run-benthos","p":221},{"i":227,"t":"Configure Benthos input and output","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#configure-benthos-input-and-output","p":221},{"i":229,"t":"Push messages to Redis queue","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#push-messages-to-redis-queue","p":221},{"i":231,"t":"Demo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#demo","p":221},{"i":233,"t":"Pitfalls of async publishing","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#pitfalls-of-async-publishing","p":221},{"i":235,"t":"Late delivery","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#late-delivery","p":221},{"i":237,"t":"Ordering concerns","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#ordering-concerns","p":221},{"i":239,"t":"Throughput when ordering preserved","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#throughput-when-ordering-preserved","p":221},{"i":241,"t":"Error handling","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#error-handling","p":221},{"i":243,"t":"Conclusion","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#conclusion","p":221},{"i":249,"t":"Centrifugo PRO goals","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#centrifugo-pro-goals","p":247},{"i":251,"t":"Why providing push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#why-providing-push-notifications-api","p":247},{"i":253,"t":"Push notifications API like no one provides","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-api-like-no-one-provides","p":247},{"i":255,"t":"Frontend decisions","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#frontend-decisions","p":247},{"i":257,"t":"Server implementation","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#server-implementation","p":247},{"i":259,"t":"How we keep tokens","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-keep-tokens","p":247},{"i":261,"t":"How we send notifications","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-send-notifications","p":247},{"i":263,"t":"Secure unified topics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#secure-unified-topics","p":247},{"i":265,"t":"Push analytics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-analytics","p":247},{"i":267,"t":"Push notifications UI","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-ui","p":247},{"i":269,"t":"Conclusion","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#conclusion","p":247},{"i":274,"t":"Landing Page Images","u":"/docs/3/attributions","h":"#landing-page-images","p":272},{"i":280,"t":"What we are building","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#what-we-are-building","p":278},{"i":282,"t":"Creating Express.js app","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#creating-expressjs-app","p":278},{"i":284,"t":"Starting Centrifugo","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#starting-centrifugo","p":278},{"i":286,"t":"Adding Nginx","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#adding-nginx","p":278},{"i":288,"t":"Send real-time messages","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#send-real-time-messages","p":278},{"i":290,"t":"Conclusion","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#conclusion","p":278},{"i":294,"t":"TLDR","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#tldr","p":292},{"i":296,"t":"Keycloak","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#keycloak","p":292},{"i":298,"t":"Centrifugo","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#centrifugo","p":292},{"i":300,"t":"React app with Vite","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#react-app-with-vite","p":292},{"i":306,"t":"How many connections can one Centrifugo instance handle?","u":"/docs/3/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":304},{"i":308,"t":"Memory usage per connection?","u":"/docs/3/faq","h":"#memory-usage-per-connection","p":304},{"i":310,"t":"Can Centrifugo scale horizontally?","u":"/docs/3/faq","h":"#can-centrifugo-scale-horizontally","p":304},{"i":312,"t":"Message delivery model","u":"/docs/3/faq","h":"#message-delivery-model","p":304},{"i":314,"t":"Message order guarantees","u":"/docs/3/faq","h":"#message-order-guarantees","p":304},{"i":316,"t":"Should I create channels explicitly?","u":"/docs/3/faq","h":"#should-i-create-channels-explicitly","p":304},{"i":318,"t":"What about best practices with the number of channels?","u":"/docs/3/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":304},{"i":320,"t":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/3/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":304},{"i":322,"t":"Can I have both binary and JSON clients in one channel?","u":"/docs/3/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":304},{"i":324,"t":"Online presence for chat apps - online status of your contacts","u":"/docs/3/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":304},{"i":326,"t":"Centrifugo stops accepting new connections, why?","u":"/docs/3/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":304},{"i":328,"t":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/3/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":304},{"i":330,"t":"Does Centrifugo work with HTTP/2?","u":"/docs/3/faq","h":"#does-centrifugo-work-with-http2","p":304},{"i":332,"t":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/3/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":304},{"i":334,"t":"What if I need to send push notifications to mobile or web applications?","u":"/docs/3/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":304},{"i":336,"t":"How can I know a message is delivered to a client?","u":"/docs/3/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":304},{"i":338,"t":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/3/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":304},{"i":340,"t":"How to create a secure channel for two users only (private chat case)?","u":"/docs/3/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":304},{"i":342,"t":"What's the best way to organize channel configuration?","u":"/docs/3/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":304},{"i":344,"t":"Does Centrifugo support webhooks?","u":"/docs/3/faq","h":"#does-centrifugo-support-webhooks","p":304},{"i":346,"t":"Why Centrifugo does not have disconnect hooks?","u":"/docs/3/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":304},{"i":348,"t":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/3/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":304},{"i":350,"t":"How scalable is the online presence and join/leave features?","u":"/docs/3/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":304},{"i":352,"t":"I have not found an answer to my question here:","u":"/docs/3/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":304},{"i":358,"t":"Idiomatic usage","u":"/docs/3/getting-started/design","h":"#idiomatic-usage","p":356},{"i":360,"t":"Message history considerations","u":"/docs/3/getting-started/design","h":"#message-history-considerations","p":356},{"i":362,"t":"Message delivery model","u":"/docs/3/getting-started/design","h":"#message-delivery-model","p":356},{"i":364,"t":"Message order guarantees","u":"/docs/3/getting-started/design","h":"#message-order-guarantees","p":356},{"i":366,"t":"Graceful degradation","u":"/docs/3/getting-started/design","h":"#graceful-degradation","p":356},{"i":368,"t":"Online presence considerations","u":"/docs/3/getting-started/design","h":"#online-presence-considerations","p":356},{"i":370,"t":"Scalability considerations","u":"/docs/3/getting-started/design","h":"#scalability-considerations","p":356},{"i":374,"t":"Centrifugo v3 flashbacks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#centrifugo-v3-flashbacks","p":372},{"i":376,"t":"Unified client SDK API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#unified-client-sdk-api","p":372},{"i":378,"t":"Modern WebSocket emulation in Javascript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#modern-websocket-emulation-in-javascript","p":372},{"i":380,"t":"No layering in client protocol","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#no-layering-in-client-protocol","p":372},{"i":382,"t":"Redesigned PING-PONG","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#redesigned-ping-pong","p":372},{"i":384,"t":"Secure by default channel namespaces","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#secure-by-default-channel-namespaces","p":372},{"i":386,"t":"Private channel concept revised","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#private-channel-concept-revised","p":372},{"i":388,"t":"Optimistic subscriptions","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#optimistic-subscriptions","p":372},{"i":390,"t":"Channel capabilities","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#channel-capabilities","p":372},{"i":392,"t":"Better connections API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#better-connections-api","p":372},{"i":394,"t":"Javascript client moved to TypeScript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#javascript-client-moved-to-typescript","p":372},{"i":396,"t":"Experimenting with HTTP/3","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-http3","p":372},{"i":398,"t":"Experimenting with WebTransport","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-webtransport","p":372},{"i":400,"t":"Migration guide","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#migration-guide","p":372},{"i":402,"t":"Conclusion","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#conclusion","p":372},{"i":404,"t":"Join community","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#join-community","p":372},{"i":406,"t":"Special thanks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#special-thanks","p":372},{"i":410,"t":"Simple integration","u":"/docs/3/getting-started/highlights","h":"#simple-integration","p":408},{"i":412,"t":"Great performance","u":"/docs/3/getting-started/highlights","h":"#great-performance","p":408},{"i":414,"t":"Built-in scalability","u":"/docs/3/getting-started/highlights","h":"#built-in-scalability","p":408},{"i":416,"t":"Strict client protocol","u":"/docs/3/getting-started/highlights","h":"#strict-client-protocol","p":408},{"i":418,"t":"Variety of real-time transports","u":"/docs/3/getting-started/highlights","h":"#variety-of-real-time-transports","p":408},{"i":420,"t":"Flexible authentication","u":"/docs/3/getting-started/highlights","h":"#flexible-authentication","p":408},{"i":422,"t":"Connection management","u":"/docs/3/getting-started/highlights","h":"#connection-management","p":408},{"i":424,"t":"Channel (room) concept","u":"/docs/3/getting-started/highlights","h":"#channel-room-concept","p":408},{"i":426,"t":"Different types of subscriptions","u":"/docs/3/getting-started/highlights","h":"#different-types-of-subscriptions","p":408},{"i":428,"t":"RPC over bidirectional connection","u":"/docs/3/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":408},{"i":430,"t":"Online presence information","u":"/docs/3/getting-started/highlights","h":"#online-presence-information","p":408},{"i":432,"t":"Message history in channels","u":"/docs/3/getting-started/highlights","h":"#message-history-in-channels","p":408},{"i":434,"t":"Embedded admin web UI","u":"/docs/3/getting-started/highlights","h":"#embedded-admin-web-ui","p":408},{"i":436,"t":"Cross-platform","u":"/docs/3/getting-started/highlights","h":"#cross-platform","p":408},{"i":438,"t":"Ready to deploy","u":"/docs/3/getting-started/highlights","h":"#ready-to-deploy","p":408},{"i":440,"t":"Open-source","u":"/docs/3/getting-started/highlights","h":"#open-source","p":408},{"i":442,"t":"Pro features","u":"/docs/3/getting-started/highlights","h":"#pro-features","p":408},{"i":446,"t":"0. Install","u":"/docs/3/getting-started/integration","h":"#0-install","p":444},{"i":448,"t":"1. Configure Centrifugo","u":"/docs/3/getting-started/integration","h":"#1-configure-centrifugo","p":444},{"i":450,"t":"2. Configure your backend","u":"/docs/3/getting-started/integration","h":"#2-configure-your-backend","p":444},{"i":452,"t":"3. Connect to Centrifugo","u":"/docs/3/getting-started/integration","h":"#3-connect-to-centrifugo","p":444},{"i":454,"t":"4. Subscribe to channels","u":"/docs/3/getting-started/integration","h":"#4-subscribe-to-channels","p":444},{"i":456,"t":"5. Publish to channel","u":"/docs/3/getting-started/integration","h":"#5-publish-to-channel","p":444},{"i":458,"t":"6. Deploy to production","u":"/docs/3/getting-started/integration","h":"#6-deploy-to-production","p":444},{"i":460,"t":"7. Monitor Centrifugo","u":"/docs/3/getting-started/integration","h":"#7-monitor-centrifugo","p":444},{"i":462,"t":"8. Scale Centrifugo","u":"/docs/3/getting-started/integration","h":"#8-scale-centrifugo","p":444},{"i":464,"t":"9. Read FAQ","u":"/docs/3/getting-started/integration","h":"#9-read-faq","p":444},{"i":468,"t":"Broker and PresenceManager","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#broker-and-presencemanager","p":466},{"i":470,"t":"Redigo","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo","p":466},{"i":472,"t":"Redigo with pipelining","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo-with-pipelining","p":466},{"i":474,"t":"Motivation to migrate","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#motivation-to-migrate","p":466},{"i":476,"t":"Go-redis/redis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#go-redisredis","p":466},{"i":478,"t":"Rueidis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#rueidis","p":466},{"i":480,"t":"Switching to Rueidis: reducing CPU usage","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#switching-to-rueidis-reducing-cpu-usage","p":466},{"i":482,"t":"Adding latency","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#adding-latency","p":466},{"i":484,"t":"Conclusion","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#conclusion","p":466},{"i":488,"t":"Motivation","u":"/docs/3/getting-started/introduction","h":"#motivation","p":486},{"i":490,"t":"Concepts","u":"/docs/3/getting-started/introduction","h":"#concepts","p":486},{"i":492,"t":"Join community","u":"/docs/3/getting-started/introduction","h":"#join-community","p":486},{"i":496,"t":"Connecting to a server","u":"/docs/3/getting-started/client_api","h":"#connecting-to-a-server","p":494},{"i":498,"t":"Disconnecting from a server","u":"/docs/3/getting-started/client_api","h":"#disconnecting-from-a-server","p":494},{"i":500,"t":"Reconnecting to a server","u":"/docs/3/getting-started/client_api","h":"#reconnecting-to-a-server","p":494},{"i":502,"t":"Connection lifecycle events","u":"/docs/3/getting-started/client_api","h":"#connection-lifecycle-events","p":494},{"i":504,"t":"Subscribe to a channel","u":"/docs/3/getting-started/client_api","h":"#subscribe-to-a-channel","p":494},{"i":506,"t":"Server-side subscriptions","u":"/docs/3/getting-started/client_api","h":"#server-side-subscriptions","p":494},{"i":508,"t":"Send RPC","u":"/docs/3/getting-started/client_api","h":"#send-rpc","p":494},{"i":510,"t":"Call channel history","u":"/docs/3/getting-started/client_api","h":"#call-channel-history","p":494},{"i":512,"t":"Presence and presence stats","u":"/docs/3/getting-started/client_api","h":"#presence-and-presence-stats","p":494},{"i":516,"t":"How it works","u":"/docs/3/pro/db_namespaces","h":"#how-it-works","p":514},{"i":518,"t":"Configuration","u":"/docs/3/pro/db_namespaces","h":"#configuration","p":514},{"i":522,"t":"Install from the binary release","u":"/docs/3/getting-started/installation","h":"#install-from-the-binary-release","p":520},{"i":524,"t":"Docker image","u":"/docs/3/getting-started/installation","h":"#docker-image","p":520},{"i":526,"t":"Docker-compose example","u":"/docs/3/getting-started/installation","h":"#docker-compose-example","p":520},{"i":528,"t":"Kubernetes Helm chart","u":"/docs/3/getting-started/installation","h":"#kubernetes-helm-chart","p":520},{"i":530,"t":"RPM and DEB packages for Linux","u":"/docs/3/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":520},{"i":532,"t":"With brew on macOS","u":"/docs/3/getting-started/installation","h":"#with-brew-on-macos","p":520},{"i":534,"t":"Build from source","u":"/docs/3/getting-started/installation","h":"#build-from-source","p":520},{"i":538,"t":"Client-side changes","u":"/docs/3/getting-started/migration_v3","h":"#client-side-changes","p":536},{"i":540,"t":"No unlimited history by default","u":"/docs/3/getting-started/migration_v3","h":"#no-unlimited-history-by-default","p":536},{"i":542,"t":"Publication limit for recovery","u":"/docs/3/getting-started/migration_v3","h":"#publication-limit-for-recovery","p":536},{"i":544,"t":"Seq/Gen fields removed","u":"/docs/3/getting-started/migration_v3","h":"#seqgen-fields-removed","p":536},{"i":546,"t":"Server-side changes","u":"/docs/3/getting-started/migration_v3","h":"#server-side-changes","p":536},{"i":547,"t":"Time interval options are duration","u":"/docs/3/getting-started/migration_v3","h":"#time-interval-options-are-duration","p":536},{"i":549,"t":"Channel options changes","u":"/docs/3/getting-started/migration_v3","h":"#channel-options-changes","p":536},{"i":551,"t":"Some command-line flags removed","u":"/docs/3/getting-started/migration_v3","h":"#some-command-line-flags-removed","p":536},{"i":553,"t":"Enforced request Origin check","u":"/docs/3/getting-started/migration_v3","h":"#enforced-request-origin-check","p":536},{"i":555,"t":"Updated GRPC API Protobuf package","u":"/docs/3/getting-started/migration_v3","h":"#updated-grpc-api-protobuf-package","p":536},{"i":557,"t":"Channels API method changed","u":"/docs/3/getting-started/migration_v3","h":"#channels-api-method-changed","p":536},{"i":559,"t":"HTTP proxy changes","u":"/docs/3/getting-started/migration_v3","h":"#http-proxy-changes","p":536},{"i":561,"t":"JWT changes","u":"/docs/3/getting-started/migration_v3","h":"#jwt-changes","p":536},{"i":563,"t":"Redis configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#redis-configuration-changes","p":536},{"i":565,"t":"Redis streams used by default","u":"/docs/3/getting-started/migration_v3","h":"#redis-streams-used-by-default","p":536},{"i":567,"t":"SockJS disabled by default","u":"/docs/3/getting-started/migration_v3","h":"#sockjs-disabled-by-default","p":536},{"i":569,"t":"Other configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#other-configuration-changes","p":536},{"i":571,"t":"v2 to v3 config converter","u":"/docs/3/getting-started/migration_v3","h":"#v2-to-v3-config-converter","p":536},{"i":575,"t":"Features","u":"/docs/3/pro/overview","h":"#features","p":573},{"i":577,"t":"Sandbox mode","u":"/docs/3/pro/overview","h":"#sandbox-mode","p":573},{"i":579,"t":"Pricing","u":"/docs/3/pro/overview","h":"#pricing","p":573},{"i":583,"t":"More examples","u":"/docs/3/getting-started/quickstart","h":"#more-examples","p":581},{"i":587,"t":"Binary release","u":"/docs/3/pro/install_and_run","h":"#binary-release","p":585},{"i":589,"t":"Docker image","u":"/docs/3/pro/install_and_run","h":"#docker-image","p":585},{"i":591,"t":"Kubernetes","u":"/docs/3/pro/install_and_run","h":"#kubernetes","p":585},{"i":593,"t":"Debian and Ubuntu","u":"/docs/3/pro/install_and_run","h":"#debian-and-ubuntu","p":585},{"i":595,"t":"Centos","u":"/docs/3/pro/install_and_run","h":"#centos","p":585},{"i":597,"t":"Setting PRO license key","u":"/docs/3/pro/install_and_run","h":"#setting-pro-license-key","p":585},{"i":603,"t":"Configuration","u":"/docs/3/pro/analytics","h":"#configuration","p":601},{"i":605,"t":"Connections table","u":"/docs/3/pro/analytics","h":"#connections-table","p":601},{"i":607,"t":"Operations table","u":"/docs/3/pro/analytics","h":"#operations-table","p":601},{"i":609,"t":"Query examples","u":"/docs/3/pro/analytics","h":"#query-examples","p":601},{"i":611,"t":"Development","u":"/docs/3/pro/analytics","h":"#development","p":601},{"i":613,"t":"How export works","u":"/docs/3/pro/analytics","h":"#how-export-works","p":601},{"i":617,"t":"Redis throttling","u":"/docs/3/pro/throttling","h":"#redis-throttling","p":615},{"i":621,"t":"Faster HTTP API","u":"/docs/3/pro/performance","h":"#faster-http-api","p":619},{"i":623,"t":"Faster GRPC API","u":"/docs/3/pro/performance","h":"#faster-grpc-api","p":619},{"i":625,"t":"Faster HTTP proxy","u":"/docs/3/pro/performance","h":"#faster-http-proxy","p":619},{"i":627,"t":"Faster GRPC proxy","u":"/docs/3/pro/performance","h":"#faster-grpc-proxy","p":619},{"i":629,"t":"Faster JWT decoding","u":"/docs/3/pro/performance","h":"#faster-jwt-decoding","p":619},{"i":631,"t":"Faster GRPC unidirectional stream","u":"/docs/3/pro/performance","h":"#faster-grpc-unidirectional-stream","p":619},{"i":633,"t":"Examples","u":"/docs/3/pro/performance","h":"#examples","p":619},{"i":635,"t":"Publish HTTP API","u":"/docs/3/pro/performance","h":"#publish-http-api","p":619},{"i":637,"t":"History HTTP API","u":"/docs/3/pro/performance","h":"#history-http-api","p":619},{"i":643,"t":"How it works","u":"/docs/3/pro/token_revocation","h":"#how-it-works","p":641},{"i":645,"t":"Configure","u":"/docs/3/pro/token_revocation","h":"#configure","p":641},{"i":647,"t":"Redis persistence engine","u":"/docs/3/pro/token_revocation","h":"#redis-persistence-engine","p":641},{"i":649,"t":"Database persistence engine","u":"/docs/3/pro/token_revocation","h":"#database-persistence-engine","p":641},{"i":651,"t":"Revoke token API","u":"/docs/3/pro/token_revocation","h":"#revoke-token-api","p":641},{"i":653,"t":"Invalidate user tokens API","u":"/docs/3/pro/token_revocation","h":"#invalidate-user-tokens-api","p":641},{"i":657,"t":"Save to a file","u":"/docs/3/pro/tracing","h":"#save-to-a-file","p":655},{"i":661,"t":"How it works","u":"/docs/3/pro/user_block","h":"#how-it-works","p":659},{"i":663,"t":"Configure","u":"/docs/3/pro/user_block","h":"#configure","p":659},{"i":665,"t":"Redis persistence engine","u":"/docs/3/pro/user_block","h":"#redis-persistence-engine","p":659},{"i":667,"t":"Database persistence engine","u":"/docs/3/pro/user_block","h":"#database-persistence-engine","p":659},{"i":669,"t":"Block user API","u":"/docs/3/pro/user_block","h":"#block-user-api","p":659},{"i":671,"t":"Unblock user API","u":"/docs/3/pro/user_block","h":"#unblock-user-api","p":659},{"i":677,"t":"Client-side status update RPC","u":"/docs/3/pro/user_status","h":"#client-side-status-update-rpc","p":675},{"i":679,"t":"update_user_status server API","u":"/docs/3/pro/user_status","h":"#update_user_status-server-api","p":675},{"i":681,"t":"get_user_status server API","u":"/docs/3/pro/user_status","h":"#get_user_status-server-api","p":675},{"i":683,"t":"delete_user_status server API","u":"/docs/3/pro/user_status","h":"#delete_user_status-server-api","p":675},{"i":685,"t":"Configuration","u":"/docs/3/pro/user_status","h":"#configuration","p":675},{"i":689,"t":"Channel name rules","u":"/docs/3/server/channels","h":"#channel-name-rules","p":687},{"i":691,"t":"namespace boundary (:)","u":"/docs/3/server/channels","h":"#namespace-boundary-","p":687},{"i":693,"t":"private channel prefix ($)","u":"/docs/3/server/channels","h":"#private-channel-prefix-","p":687},{"i":695,"t":"user channel boundary (#)","u":"/docs/3/server/channels","h":"#user-channel-boundary-","p":687},{"i":697,"t":"Channel options","u":"/docs/3/server/channels","h":"#channel-options","p":687},{"i":699,"t":"publish","u":"/docs/3/server/channels","h":"#publish","p":687},{"i":701,"t":"subscribe_to_publish","u":"/docs/3/server/channels","h":"#subscribe_to_publish","p":687},{"i":703,"t":"anonymous","u":"/docs/3/server/channels","h":"#anonymous","p":687},{"i":705,"t":"presence","u":"/docs/3/server/channels","h":"#presence","p":687},{"i":707,"t":"presence_disable_for_client","u":"/docs/3/server/channels","h":"#presence_disable_for_client","p":687},{"i":709,"t":"join_leave","u":"/docs/3/server/channels","h":"#join_leave","p":687},{"i":711,"t":"history_size","u":"/docs/3/server/channels","h":"#history_size","p":687},{"i":713,"t":"history_ttl","u":"/docs/3/server/channels","h":"#history_ttl","p":687},{"i":715,"t":"position","u":"/docs/3/server/channels","h":"#position","p":687},{"i":717,"t":"recover","u":"/docs/3/server/channels","h":"#recover","p":687},{"i":719,"t":"history_disable_for_client","u":"/docs/3/server/channels","h":"#history_disable_for_client","p":687},{"i":721,"t":"protected","u":"/docs/3/server/channels","h":"#protected","p":687},{"i":723,"t":"proxy_subscribe","u":"/docs/3/server/channels","h":"#proxy_subscribe","p":687},{"i":725,"t":"proxy_publish","u":"/docs/3/server/channels","h":"#proxy_publish","p":687},{"i":727,"t":"subscribe_proxy_name","u":"/docs/3/server/channels","h":"#subscribe_proxy_name","p":687},{"i":729,"t":"publish_proxy_name","u":"/docs/3/server/channels","h":"#publish_proxy_name","p":687},{"i":731,"t":"Channel options config example","u":"/docs/3/server/channels","h":"#channel-options-config-example","p":687},{"i":733,"t":"Channel namespaces","u":"/docs/3/server/channels","h":"#channel-namespaces","p":687},{"i":737,"t":"Claims","u":"/docs/3/server/authentication","h":"#claims","p":735},{"i":739,"t":"sub","u":"/docs/3/server/authentication","h":"#sub","p":735},{"i":741,"t":"exp","u":"/docs/3/server/authentication","h":"#exp","p":735},{"i":743,"t":"iat","u":"/docs/3/server/authentication","h":"#iat","p":735},{"i":745,"t":"jti","u":"/docs/3/server/authentication","h":"#jti","p":735},{"i":747,"t":"aud","u":"/docs/3/server/authentication","h":"#aud","p":735},{"i":749,"t":"iss","u":"/docs/3/server/authentication","h":"#iss","p":735},{"i":751,"t":"info","u":"/docs/3/server/authentication","h":"#info","p":735},{"i":753,"t":"b64info","u":"/docs/3/server/authentication","h":"#b64info","p":735},{"i":755,"t":"channels","u":"/docs/3/server/authentication","h":"#channels","p":735},{"i":757,"t":"subs","u":"/docs/3/server/authentication","h":"#subs","p":735},{"i":759,"t":"meta","u":"/docs/3/server/authentication","h":"#meta","p":735},{"i":761,"t":"expire_at","u":"/docs/3/server/authentication","h":"#expire_at","p":735},{"i":763,"t":"Connection expiration","u":"/docs/3/server/authentication","h":"#connection-expiration","p":735},{"i":765,"t":"Examples","u":"/docs/3/server/authentication","h":"#examples","p":735},{"i":767,"t":"Simplest token","u":"/docs/3/server/authentication","h":"#simplest-token","p":735},{"i":769,"t":"Token with expiration","u":"/docs/3/server/authentication","h":"#token-with-expiration","p":735},{"i":771,"t":"Token with additional connection info","u":"/docs/3/server/authentication","h":"#token-with-additional-connection-info","p":735},{"i":773,"t":"Investigating problems with JWT","u":"/docs/3/server/authentication","h":"#investigating-problems-with-jwt","p":735},{"i":775,"t":"JSON Web Key support","u":"/docs/3/server/authentication","h":"#json-web-key-support","p":735},{"i":779,"t":"Client error codes","u":"/docs/3/server/codes","h":"#client-error-codes","p":777},{"i":781,"t":"Internal","u":"/docs/3/server/codes","h":"#internal","p":777},{"i":783,"t":"Unauthorized","u":"/docs/3/server/codes","h":"#unauthorized","p":777},{"i":785,"t":"Unknown Channel","u":"/docs/3/server/codes","h":"#unknown-channel","p":777},{"i":787,"t":"Permission Denied","u":"/docs/3/server/codes","h":"#permission-denied","p":777},{"i":789,"t":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found","p":777},{"i":791,"t":"Already Subscribed","u":"/docs/3/server/codes","h":"#already-subscribed","p":777},{"i":793,"t":"Limit Exceeded","u":"/docs/3/server/codes","h":"#limit-exceeded","p":777},{"i":795,"t":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request","p":777},{"i":797,"t":"Not Available","u":"/docs/3/server/codes","h":"#not-available","p":777},{"i":799,"t":"Token Expired","u":"/docs/3/server/codes","h":"#token-expired","p":777},{"i":801,"t":"Expired","u":"/docs/3/server/codes","h":"#expired","p":777},{"i":803,"t":"Too Many Requests","u":"/docs/3/server/codes","h":"#too-many-requests","p":777},{"i":805,"t":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position","p":777},{"i":807,"t":"Client disconnect codes","u":"/docs/3/server/codes","h":"#client-disconnect-codes","p":777},{"i":809,"t":"Normal","u":"/docs/3/server/codes","h":"#normal","p":777},{"i":811,"t":"Shutdown","u":"/docs/3/server/codes","h":"#shutdown","p":777},{"i":813,"t":"Invalid Token","u":"/docs/3/server/codes","h":"#invalid-token","p":777},{"i":815,"t":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-1","p":777},{"i":817,"t":"Server Error","u":"/docs/3/server/codes","h":"#server-error","p":777},{"i":819,"t":"Expired","u":"/docs/3/server/codes","h":"#expired-1","p":777},{"i":821,"t":"Subscription Expired","u":"/docs/3/server/codes","h":"#subscription-expired","p":777},{"i":823,"t":"Stale","u":"/docs/3/server/codes","h":"#stale","p":777},{"i":825,"t":"Slow","u":"/docs/3/server/codes","h":"#slow","p":777},{"i":827,"t":"Write Error","u":"/docs/3/server/codes","h":"#write-error","p":777},{"i":829,"t":"Insufficient State","u":"/docs/3/server/codes","h":"#insufficient-state","p":777},{"i":831,"t":"Force Reconnect","u":"/docs/3/server/codes","h":"#force-reconnect","p":777},{"i":833,"t":"Force No Reconnect","u":"/docs/3/server/codes","h":"#force-no-reconnect","p":777},{"i":835,"t":"Connection Limit","u":"/docs/3/server/codes","h":"#connection-limit","p":777},{"i":837,"t":"Server API error codes","u":"/docs/3/server/codes","h":"#server-api-error-codes","p":777},{"i":839,"t":"Internal","u":"/docs/3/server/codes","h":"#internal-1","p":777},{"i":841,"t":"Unknown channel","u":"/docs/3/server/codes","h":"#unknown-channel-1","p":777},{"i":843,"t":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found-1","p":777},{"i":845,"t":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-2","p":777},{"i":847,"t":"Not Available","u":"/docs/3/server/codes","h":"#not-available-1","p":777},{"i":849,"t":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position-1","p":777},{"i":853,"t":"Options","u":"/docs/3/server/admin_web","h":"#options","p":851},{"i":855,"t":"Using custom web interface","u":"/docs/3/server/admin_web","h":"#using-custom-web-interface","p":851},{"i":857,"t":"Admin insecure mode","u":"/docs/3/server/admin_web","h":"#admin-insecure-mode","p":851},{"i":861,"t":"version command","u":"/docs/3/server/console_commands","h":"#version-command","p":859},{"i":863,"t":"checkconfig command","u":"/docs/3/server/console_commands","h":"#checkconfig-command","p":859},{"i":865,"t":"genconfig command","u":"/docs/3/server/console_commands","h":"#genconfig-command","p":859},{"i":867,"t":"gentoken command","u":"/docs/3/server/console_commands","h":"#gentoken-command","p":859},{"i":869,"t":"checktoken command","u":"/docs/3/server/console_commands","h":"#checktoken-command","p":859},{"i":873,"t":"Configuration sources","u":"/docs/3/server/configuration","h":"#configuration-sources","p":871},{"i":875,"t":"Command-line flags","u":"/docs/3/server/configuration","h":"#command-line-flags","p":871},{"i":877,"t":"OS environment variables","u":"/docs/3/server/configuration","h":"#os-environment-variables","p":871},{"i":879,"t":"Configuration file","u":"/docs/3/server/configuration","h":"#configuration-file","p":871},{"i":881,"t":"Config file formats","u":"/docs/3/server/configuration","h":"#config-file-formats","p":871},{"i":883,"t":"JSON config format","u":"/docs/3/server/configuration","h":"#json-config-format","p":871},{"i":885,"t":"TOML config format","u":"/docs/3/server/configuration","h":"#toml-config-format","p":871},{"i":887,"t":"YAML config format","u":"/docs/3/server/configuration","h":"#yaml-config-format","p":871},{"i":889,"t":"Important options","u":"/docs/3/server/configuration","h":"#important-options","p":871},{"i":891,"t":"allowed_origins","u":"/docs/3/server/configuration","h":"#allowed_origins","p":871},{"i":893,"t":"address","u":"/docs/3/server/configuration","h":"#address","p":871},{"i":895,"t":"port","u":"/docs/3/server/configuration","h":"#port","p":871},{"i":897,"t":"engine","u":"/docs/3/server/configuration","h":"#engine","p":871},{"i":899,"t":"Advanced options","u":"/docs/3/server/configuration","h":"#advanced-options","p":871},{"i":901,"t":"client_channel_limit","u":"/docs/3/server/configuration","h":"#client_channel_limit","p":871},{"i":903,"t":"channel_max_length","u":"/docs/3/server/configuration","h":"#channel_max_length","p":871},{"i":905,"t":"client_user_connection_limit","u":"/docs/3/server/configuration","h":"#client_user_connection_limit","p":871},{"i":907,"t":"client_queue_max_size","u":"/docs/3/server/configuration","h":"#client_queue_max_size","p":871},{"i":909,"t":"client_anonymous","u":"/docs/3/server/configuration","h":"#client_anonymous","p":871},{"i":911,"t":"client_concurrency","u":"/docs/3/server/configuration","h":"#client_concurrency","p":871},{"i":913,"t":"gomaxprocs","u":"/docs/3/server/configuration","h":"#gomaxprocs","p":871},{"i":915,"t":"Endpoint configuration.","u":"/docs/3/server/configuration","h":"#endpoint-configuration","p":871},{"i":917,"t":"Default endpoints.","u":"/docs/3/server/configuration","h":"#default-endpoints","p":871},{"i":919,"t":"Admin endpoints.","u":"/docs/3/server/configuration","h":"#admin-endpoints","p":871},{"i":921,"t":"Debug endpoints.","u":"/docs/3/server/configuration","h":"#debug-endpoints","p":871},{"i":923,"t":"Health check endpoint","u":"/docs/3/server/configuration","h":"#health-check-endpoint","p":871},{"i":925,"t":"Custom internal ports","u":"/docs/3/server/configuration","h":"#custom-internal-ports","p":871},{"i":927,"t":"Disable default endpoints","u":"/docs/3/server/configuration","h":"#disable-default-endpoints","p":871},{"i":929,"t":"Customize handler endpoints","u":"/docs/3/server/configuration","h":"#customize-handler-endpoints","p":871},{"i":931,"t":"Signal handling","u":"/docs/3/server/configuration","h":"#signal-handling","p":871},{"i":933,"t":"Insecure modes","u":"/docs/3/server/configuration","h":"#insecure-modes","p":871},{"i":934,"t":"Insecure client connection","u":"/docs/3/server/configuration","h":"#insecure-client-connection","p":871},{"i":936,"t":"Insecure API mode","u":"/docs/3/server/configuration","h":"#insecure-api-mode","p":871},{"i":938,"t":"Insecure admin mode","u":"/docs/3/server/configuration","h":"#insecure-admin-mode","p":871},{"i":940,"t":"Setting time duration options","u":"/docs/3/server/configuration","h":"#setting-time-duration-options","p":871},{"i":942,"t":"Setting namespaces over env","u":"/docs/3/server/configuration","h":"#setting-namespaces-over-env","p":871},{"i":944,"t":"Anonymous usage stats","u":"/docs/3/server/configuration","h":"#anonymous-usage-stats","p":871},{"i":948,"t":"Open files limit","u":"/docs/3/server/infra_tuning","h":"#open-files-limit","p":946},{"i":950,"t":"Ephemeral port exhaustion","u":"/docs/3/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":946},{"i":952,"t":"Sockets in TIME_WAIT state","u":"/docs/3/server/infra_tuning","h":"#sockets-in-time_wait-state","p":946},{"i":954,"t":"Proxy max connections","u":"/docs/3/server/infra_tuning","h":"#proxy-max-connections","p":946},{"i":956,"t":"Conntrack table","u":"/docs/3/server/infra_tuning","h":"#conntrack-table","p":946},{"i":960,"t":"History design","u":"/docs/3/server/history_and_recovery","h":"#history-design","p":958},{"i":962,"t":"History iteration API","u":"/docs/3/server/history_and_recovery","h":"#history-iteration-api","p":958},{"i":964,"t":"Automatic message recovery","u":"/docs/3/server/history_and_recovery","h":"#automatic-message-recovery","p":958},{"i":968,"t":"Memory engine","u":"/docs/3/server/engines","h":"#memory-engine","p":966},{"i":970,"t":"Memory engine options","u":"/docs/3/server/engines","h":"#memory-engine-options","p":966},{"i":972,"t":"Redis engine","u":"/docs/3/server/engines","h":"#redis-engine","p":966},{"i":974,"t":"Redis engine options","u":"/docs/3/server/engines","h":"#redis-engine-options","p":966},{"i":976,"t":"Scaling with Redis tutorial","u":"/docs/3/server/engines","h":"#scaling-with-redis-tutorial","p":966},{"i":978,"t":"Redis Sentinel for high availability","u":"/docs/3/server/engines","h":"#redis-sentinel-for-high-availability","p":966},{"i":980,"t":"Haproxy instead of Sentinel configuration","u":"/docs/3/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":966},{"i":982,"t":"Redis sharding","u":"/docs/3/server/engines","h":"#redis-sharding","p":966},{"i":984,"t":"Redis cluster","u":"/docs/3/server/engines","h":"#redis-cluster","p":966},{"i":986,"t":"KeyDB Engine","u":"/docs/3/server/engines","h":"#keydb-engine","p":966},{"i":988,"t":"Tarantool engine","u":"/docs/3/server/engines","h":"#tarantool-engine","p":966},{"i":990,"t":"Tarantool engine options","u":"/docs/3/server/engines","h":"#tarantool-engine-options","p":966},{"i":992,"t":"Nats broker","u":"/docs/3/server/engines","h":"#nats-broker","p":966},{"i":994,"t":"Options","u":"/docs/3/server/engines","h":"#options","p":966},{"i":998,"t":"Nginx configuration","u":"/docs/3/server/load_balancing","h":"#nginx-configuration","p":996},{"i":1000,"t":"Separate domain for Centrifugo","u":"/docs/3/server/load_balancing","h":"#separate-domain-for-centrifugo","p":996},{"i":1002,"t":"Embed to a location of web site","u":"/docs/3/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":996},{"i":1004,"t":"worker_connections","u":"/docs/3/server/load_balancing","h":"#worker_connections","p":996},{"i":1008,"t":"Prometheus","u":"/docs/3/server/monitoring","h":"#prometheus","p":1006},{"i":1010,"t":"Graphite","u":"/docs/3/server/monitoring","h":"#graphite","p":1006},{"i":1012,"t":"Grafana dashboard","u":"/docs/3/server/monitoring","h":"#grafana-dashboard","p":1006},{"i":1016,"t":"Claims","u":"/docs/3/server/private_channels","h":"#claims","p":1014},{"i":1018,"t":"client","u":"/docs/3/server/private_channels","h":"#client","p":1014},{"i":1020,"t":"channel","u":"/docs/3/server/private_channels","h":"#channel","p":1014},{"i":1022,"t":"info","u":"/docs/3/server/private_channels","h":"#info","p":1014},{"i":1024,"t":"b64info","u":"/docs/3/server/private_channels","h":"#b64info","p":1014},{"i":1026,"t":"exp","u":"/docs/3/server/private_channels","h":"#exp","p":1014},{"i":1028,"t":"expire_at","u":"/docs/3/server/private_channels","h":"#expire_at","p":1014},{"i":1030,"t":"aud","u":"/docs/3/server/private_channels","h":"#aud","p":1014},{"i":1032,"t":"iss","u":"/docs/3/server/private_channels","h":"#iss","p":1014},{"i":1034,"t":"Example","u":"/docs/3/server/private_channels","h":"#example","p":1014},{"i":1038,"t":"Dynamic server-side subscriptions","u":"/docs/3/server/server_subs","h":"#dynamic-server-side-subscriptions","p":1036},{"i":1040,"t":"Automatic personal channel subscription","u":"/docs/3/server/server_subs","h":"#automatic-personal-channel-subscription","p":1036},{"i":1042,"t":"Maintain single user connection","u":"/docs/3/server/server_subs","h":"#maintain-single-user-connection","p":1036},{"i":1046,"t":"Using crt and key files","u":"/docs/3/server/tls","h":"#using-crt-and-key-files","p":1044},{"i":1048,"t":"Automatic certificates","u":"/docs/3/server/tls","h":"#automatic-certificates","p":1044},{"i":1050,"t":"TLS for GRPC API","u":"/docs/3/server/tls","h":"#tls-for-grpc-api","p":1044},{"i":1052,"t":"TLS for GRPC unidirectional stream","u":"/docs/3/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":1044},{"i":1056,"t":"HTTP proxy","u":"/docs/3/server/proxy","h":"#http-proxy","p":1054},{"i":1058,"t":"HTTP request structure","u":"/docs/3/server/proxy","h":"#http-request-structure","p":1054},{"i":1060,"t":"Proxy HTTP headers","u":"/docs/3/server/proxy","h":"#proxy-http-headers","p":1054},{"i":1062,"t":"Proxy GRPC metadata","u":"/docs/3/server/proxy","h":"#proxy-grpc-metadata","p":1054},{"i":1064,"t":"Connect proxy","u":"/docs/3/server/proxy","h":"#connect-proxy","p":1054},{"i":1066,"t":"Refresh proxy","u":"/docs/3/server/proxy","h":"#refresh-proxy","p":1054},{"i":1068,"t":"RPC proxy","u":"/docs/3/server/proxy","h":"#rpc-proxy","p":1054},{"i":1070,"t":"Subscribe proxy","u":"/docs/3/server/proxy","h":"#subscribe-proxy","p":1054},{"i":1072,"t":"Publish proxy","u":"/docs/3/server/proxy","h":"#publish-proxy","p":1054},{"i":1074,"t":"Return custom error","u":"/docs/3/server/proxy","h":"#return-custom-error","p":1054},{"i":1076,"t":"Return custom disconnect","u":"/docs/3/server/proxy","h":"#return-custom-disconnect","p":1054},{"i":1078,"t":"GRPC proxy","u":"/docs/3/server/proxy","h":"#grpc-proxy","p":1054},{"i":1080,"t":"GRPC proxy options","u":"/docs/3/server/proxy","h":"#grpc-proxy-options","p":1054},{"i":1082,"t":"GRPC proxy example","u":"/docs/3/server/proxy","h":"#grpc-proxy-example","p":1054},{"i":1084,"t":"Header proxy rules","u":"/docs/3/server/proxy","h":"#header-proxy-rules","p":1054},{"i":1086,"t":"Binary mode","u":"/docs/3/server/proxy","h":"#binary-mode","p":1054},{"i":1088,"t":"Granular proxy mode","u":"/docs/3/server/proxy","h":"#granular-proxy-mode","p":1054},{"i":1090,"t":"Enable granular proxy mode","u":"/docs/3/server/proxy","h":"#enable-granular-proxy-mode","p":1054},{"i":1092,"t":"Defining a list of proxies","u":"/docs/3/server/proxy","h":"#defining-a-list-of-proxies","p":1054},{"i":1094,"t":"Granular connect and refresh","u":"/docs/3/server/proxy","h":"#granular-connect-and-refresh","p":1054},{"i":1096,"t":"Granular subscribe and publish","u":"/docs/3/server/proxy","h":"#granular-subscribe-and-publish","p":1054},{"i":1098,"t":"Granular RPC","u":"/docs/3/server/proxy","h":"#granular-rpc","p":1054},{"i":1104,"t":"HTTP API","u":"/docs/3/server/server_api","h":"#http-api","p":1102},{"i":1106,"t":"HTTP API authorization","u":"/docs/3/server/server_api","h":"#http-api-authorization","p":1102},{"i":1108,"t":"publish","u":"/docs/3/server/server_api","h":"#publish","p":1102},{"i":1110,"t":"broadcast","u":"/docs/3/server/server_api","h":"#broadcast","p":1102},{"i":1112,"t":"subscribe","u":"/docs/3/server/server_api","h":"#subscribe","p":1102},{"i":1114,"t":"unsubscribe","u":"/docs/3/server/server_api","h":"#unsubscribe","p":1102},{"i":1116,"t":"disconnect","u":"/docs/3/server/server_api","h":"#disconnect","p":1102},{"i":1118,"t":"refresh","u":"/docs/3/server/server_api","h":"#refresh","p":1102},{"i":1120,"t":"presence","u":"/docs/3/server/server_api","h":"#presence","p":1102},{"i":1122,"t":"presence_stats","u":"/docs/3/server/server_api","h":"#presence_stats","p":1102},{"i":1124,"t":"history","u":"/docs/3/server/server_api","h":"#history","p":1102},{"i":1126,"t":"history_remove","u":"/docs/3/server/server_api","h":"#history_remove","p":1102},{"i":1128,"t":"channels","u":"/docs/3/server/server_api","h":"#channels","p":1102},{"i":1130,"t":"info","u":"/docs/3/server/server_api","h":"#info","p":1102},{"i":1132,"t":"Command pipelining","u":"/docs/3/server/server_api","h":"#command-pipelining","p":1102},{"i":1134,"t":"HTTP API libraries","u":"/docs/3/server/server_api","h":"#http-api-libraries","p":1102},{"i":1136,"t":"GRPC API","u":"/docs/3/server/server_api","h":"#grpc-api","p":1102},{"i":1138,"t":"GRPC example for Python","u":"/docs/3/server/server_api","h":"#grpc-example-for-python","p":1102},{"i":1140,"t":"GRPC example for Go","u":"/docs/3/server/server_api","h":"#grpc-example-for-go","p":1102},{"i":1142,"t":"GRPC API key authorization","u":"/docs/3/server/server_api","h":"#grpc-api-key-authorization","p":1102},{"i":1146,"t":"SockJS caveats","u":"/docs/3/transports/sockjs","h":"#sockjs-caveats","p":1144},{"i":1148,"t":"Sticky sessions","u":"/docs/3/transports/sockjs","h":"#sticky-sessions","p":1144},{"i":1150,"t":"Browser only","u":"/docs/3/transports/sockjs","h":"#browser-only","p":1144},{"i":1152,"t":"JSON only","u":"/docs/3/transports/sockjs","h":"#json-only","p":1144},{"i":1154,"t":"Options","u":"/docs/3/transports/sockjs","h":"#options","p":1144},{"i":1155,"t":"sockjs","u":"/docs/3/transports/sockjs","h":"#sockjs","p":1144},{"i":1157,"t":"sockjs_url","u":"/docs/3/transports/sockjs","h":"#sockjs_url","p":1144},{"i":1161,"t":"Client implementation feature matrix","u":"/docs/3/transports/client_protocol","h":"#client-implementation-feature-matrix","p":1159},{"i":1163,"t":"Top level framing","u":"/docs/3/transports/client_protocol","h":"#top-level-framing","p":1159},{"i":1165,"t":"Connect","u":"/docs/3/transports/client_protocol","h":"#connect","p":1159},{"i":1167,"t":"Subscribe","u":"/docs/3/transports/client_protocol","h":"#subscribe","p":1159},{"i":1169,"t":"Unsubscribe","u":"/docs/3/transports/client_protocol","h":"#unsubscribe","p":1159},{"i":1171,"t":"Refresh","u":"/docs/3/transports/client_protocol","h":"#refresh","p":1159},{"i":1173,"t":"RPC-like calls: publish, history, presence","u":"/docs/3/transports/client_protocol","h":"#rpc-like-calls-publish-history-presence","p":1159},{"i":1175,"t":"Asynchronous server-to-client messages","u":"/docs/3/transports/client_protocol","h":"#asynchronous-server-to-client-messages","p":1159},{"i":1177,"t":"Ping Pong","u":"/docs/3/transports/client_protocol","h":"#ping-pong","p":1159},{"i":1179,"t":"Handle disconnects","u":"/docs/3/transports/client_protocol","h":"#handle-disconnects","p":1159},{"i":1181,"t":"Handle errors","u":"/docs/3/transports/client_protocol","h":"#handle-errors","p":1159},{"i":1183,"t":"Client implementation advices","u":"/docs/3/transports/client_protocol","h":"#client-implementation-advices","p":1159},{"i":1185,"t":"Server side subscriptions (SSS)","u":"/docs/3/transports/client_protocol","h":"#server-side-subscriptions-sss","p":1159},{"i":1187,"t":"Message recovery","u":"/docs/3/transports/client_protocol","h":"#message-recovery","p":1159},{"i":1189,"t":"Disconnect code and reason","u":"/docs/3/transports/client_protocol","h":"#disconnect-code-and-reason","p":1159},{"i":1191,"t":"Additional notes","u":"/docs/3/transports/client_protocol","h":"#additional-notes","p":1159},{"i":1195,"t":"Connect command","u":"/docs/3/transports/uni_http_stream","h":"#connect-command","p":1193},{"i":1197,"t":"Supported data formats","u":"/docs/3/transports/uni_http_stream","h":"#supported-data-formats","p":1193},{"i":1199,"t":"Pings","u":"/docs/3/transports/uni_http_stream","h":"#pings","p":1193},{"i":1201,"t":"Options","u":"/docs/3/transports/uni_http_stream","h":"#options","p":1193},{"i":1202,"t":"uni_http_stream","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream","p":1193},{"i":1204,"t":"uni_http_stream_max_request_body_size","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":1193},{"i":1206,"t":"Connecting using CURL","u":"/docs/3/transports/uni_http_stream","h":"#connecting-using-curl","p":1193},{"i":1208,"t":"Browser example","u":"/docs/3/transports/uni_http_stream","h":"#browser-example","p":1193},{"i":1212,"t":"Connect command","u":"/docs/3/transports/uni_sse","h":"#connect-command","p":1210},{"i":1214,"t":"Supported data formats","u":"/docs/3/transports/uni_sse","h":"#supported-data-formats","p":1210},{"i":1216,"t":"Pings","u":"/docs/3/transports/uni_sse","h":"#pings","p":1210},{"i":1218,"t":"Options","u":"/docs/3/transports/uni_sse","h":"#options","p":1210},{"i":1219,"t":"uni_sse","u":"/docs/3/transports/uni_sse","h":"#uni_sse","p":1210},{"i":1221,"t":"uni_sse_max_request_body_size","u":"/docs/3/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":1210},{"i":1223,"t":"Browser example","u":"/docs/3/transports/uni_sse","h":"#browser-example","p":1210},{"i":1227,"t":"Supported data formats","u":"/docs/3/transports/uni_grpc","h":"#supported-data-formats","p":1225},{"i":1229,"t":"Options","u":"/docs/3/transports/uni_grpc","h":"#options","p":1225},{"i":1230,"t":"uni_grpc","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc","p":1225},{"i":1232,"t":"uni_grpc_port","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_port","p":1225},{"i":1234,"t":"uni_grpc_address","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_address","p":1225},{"i":1236,"t":"uni_grpc_max_receive_message_size","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":1225},{"i":1238,"t":"uni_grpc_tls","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls","p":1225},{"i":1240,"t":"uni_grpc_tls_cert","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":1225},{"i":1242,"t":"uni_grpc_tls_key","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_key","p":1225},{"i":1244,"t":"Example","u":"/docs/3/transports/uni_grpc","h":"#example","p":1225},{"i":1248,"t":"Connect command","u":"/docs/3/transports/uni_websocket","h":"#connect-command","p":1246},{"i":1250,"t":"SubscribeRequest","u":"/docs/3/transports/uni_websocket","h":"#subscriberequest","p":1246},{"i":1252,"t":"Supported data formats","u":"/docs/3/transports/uni_websocket","h":"#supported-data-formats","p":1246},{"i":1254,"t":"Pings","u":"/docs/3/transports/uni_websocket","h":"#pings","p":1246},{"i":1256,"t":"Options","u":"/docs/3/transports/uni_websocket","h":"#options","p":1246},{"i":1257,"t":"uni_websocket","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket","p":1246},{"i":1259,"t":"uni_websocket_message_size_limit","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":1246},{"i":1261,"t":"Example","u":"/docs/3/transports/uni_websocket","h":"#example","p":1246},{"i":1265,"t":"Bidirectional","u":"/docs/3/transports/overview","h":"#bidirectional","p":1263},{"i":1267,"t":"Unidirectional","u":"/docs/3/transports/overview","h":"#unidirectional","p":1263},{"i":1269,"t":"Unidirectional message types","u":"/docs/3/transports/overview","h":"#unidirectional-message-types","p":1263},{"i":1273,"t":"Options","u":"/docs/3/transports/websocket","h":"#options","p":1271},{"i":1274,"t":"websocket_message_size_limit","u":"/docs/3/transports/websocket","h":"#websocket_message_size_limit","p":1271},{"i":1276,"t":"websocket_read_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_read_buffer_size","p":1271},{"i":1278,"t":"websocket_write_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_write_buffer_size","p":1271},{"i":1280,"t":"websocket_use_write_buffer_pool","u":"/docs/3/transports/websocket","h":"#websocket_use_write_buffer_pool","p":1271},{"i":1282,"t":"websocket_compression","u":"/docs/3/transports/websocket","h":"#websocket_compression","p":1271},{"i":1284,"t":"Protobuf binary protocol","u":"/docs/3/transports/websocket","h":"#protobuf-binary-protocol","p":1271},{"i":1288,"t":"Landing Page Images","u":"/docs/4/attributions","h":"#landing-page-images","p":1286},{"i":1292,"t":"How many connections can one Centrifugo instance handle?","u":"/docs/4/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":1290},{"i":1294,"t":"Memory usage per connection?","u":"/docs/4/faq","h":"#memory-usage-per-connection","p":1290},{"i":1296,"t":"Can Centrifugo scale horizontally?","u":"/docs/4/faq","h":"#can-centrifugo-scale-horizontally","p":1290},{"i":1298,"t":"Message delivery model","u":"/docs/4/faq","h":"#message-delivery-model","p":1290},{"i":1300,"t":"Message order guarantees","u":"/docs/4/faq","h":"#message-order-guarantees","p":1290},{"i":1302,"t":"Should I create channels explicitly?","u":"/docs/4/faq","h":"#should-i-create-channels-explicitly","p":1290},{"i":1304,"t":"What about best practices with the number of channels?","u":"/docs/4/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":1290},{"i":1306,"t":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/4/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":1290},{"i":1308,"t":"Can I have both binary and JSON clients in one channel?","u":"/docs/4/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":1290},{"i":1310,"t":"Online presence for chat apps - online status of your contacts","u":"/docs/4/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":1290},{"i":1312,"t":"Centrifugo stops accepting new connections, why?","u":"/docs/4/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":1290},{"i":1314,"t":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/4/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":1290},{"i":1316,"t":"Does Centrifugo work with HTTP/2?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http2","p":1290},{"i":1318,"t":"Does Centrifugo work with HTTP/3?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http3","p":1290},{"i":1320,"t":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/4/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":1290},{"i":1322,"t":"What if I need to send push notifications to mobile or web applications?","u":"/docs/4/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":1290},{"i":1324,"t":"How can I know a message is delivered to a client?","u":"/docs/4/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":1290},{"i":1326,"t":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/4/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":1290},{"i":1328,"t":"How to create a secure channel for two users only (private chat case)?","u":"/docs/4/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":1290},{"i":1330,"t":"What's the best way to organize channel configuration?","u":"/docs/4/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":1290},{"i":1332,"t":"Does Centrifugo support webhooks?","u":"/docs/4/faq","h":"#does-centrifugo-support-webhooks","p":1290},{"i":1334,"t":"Why Centrifugo does not have disconnect hooks?","u":"/docs/4/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":1290},{"i":1336,"t":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/4/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":1290},{"i":1338,"t":"How scalable is the online presence and join/leave features?","u":"/docs/4/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":1290},{"i":1340,"t":"How to send initial data to channel subscriber?","u":"/docs/4/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":1290},{"i":1342,"t":"Does Centrifugo support multitenancy?","u":"/docs/4/faq","h":"#does-centrifugo-support-multitenancy","p":1290},{"i":1344,"t":"I have not found an answer to my question here:","u":"/docs/4/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":1290},{"i":1352,"t":"Centrifuge library for Go","u":"/docs/4/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":1350},{"i":1354,"t":"Framework integrations","u":"/docs/4/getting-started/ecosystem","h":"#framework-integrations","p":1350},{"i":1358,"t":"Connecting to a server","u":"/docs/4/getting-started/client_api","h":"#connecting-to-a-server","p":1356},{"i":1360,"t":"Disconnecting from a server","u":"/docs/4/getting-started/client_api","h":"#disconnecting-from-a-server","p":1356},{"i":1362,"t":"Reconnecting to a server","u":"/docs/4/getting-started/client_api","h":"#reconnecting-to-a-server","p":1356},{"i":1364,"t":"Connection lifecycle events","u":"/docs/4/getting-started/client_api","h":"#connection-lifecycle-events","p":1356},{"i":1366,"t":"Subscribe to a channel","u":"/docs/4/getting-started/client_api","h":"#subscribe-to-a-channel","p":1356},{"i":1368,"t":"Server-side subscriptions","u":"/docs/4/getting-started/client_api","h":"#server-side-subscriptions","p":1356},{"i":1370,"t":"Send RPC","u":"/docs/4/getting-started/client_api","h":"#send-rpc","p":1356},{"i":1372,"t":"Call channel history","u":"/docs/4/getting-started/client_api","h":"#call-channel-history","p":1356},{"i":1374,"t":"Presence and presence stats","u":"/docs/4/getting-started/client_api","h":"#presence-and-presence-stats","p":1356},{"i":1378,"t":"Idiomatic usage","u":"/docs/4/getting-started/design","h":"#idiomatic-usage","p":1376},{"i":1380,"t":"Message history considerations","u":"/docs/4/getting-started/design","h":"#message-history-considerations","p":1376},{"i":1382,"t":"Message delivery model","u":"/docs/4/getting-started/design","h":"#message-delivery-model","p":1376},{"i":1384,"t":"Message order guarantees","u":"/docs/4/getting-started/design","h":"#message-order-guarantees","p":1376},{"i":1386,"t":"Graceful degradation","u":"/docs/4/getting-started/design","h":"#graceful-degradation","p":1376},{"i":1388,"t":"Online presence considerations","u":"/docs/4/getting-started/design","h":"#online-presence-considerations","p":1376},{"i":1390,"t":"Scalability considerations","u":"/docs/4/getting-started/design","h":"#scalability-considerations","p":1376},{"i":1394,"t":"Simple integration","u":"/docs/4/getting-started/highlights","h":"#simple-integration","p":1392},{"i":1396,"t":"Great performance","u":"/docs/4/getting-started/highlights","h":"#great-performance","p":1392},{"i":1398,"t":"Built-in scalability","u":"/docs/4/getting-started/highlights","h":"#built-in-scalability","p":1392},{"i":1400,"t":"Strict client protocol","u":"/docs/4/getting-started/highlights","h":"#strict-client-protocol","p":1392},{"i":1402,"t":"Variety of real-time transports","u":"/docs/4/getting-started/highlights","h":"#variety-of-real-time-transports","p":1392},{"i":1404,"t":"Flexible authentication","u":"/docs/4/getting-started/highlights","h":"#flexible-authentication","p":1392},{"i":1406,"t":"Connection management","u":"/docs/4/getting-started/highlights","h":"#connection-management","p":1392},{"i":1408,"t":"Channel (room) concept","u":"/docs/4/getting-started/highlights","h":"#channel-room-concept","p":1392},{"i":1410,"t":"Different types of subscriptions","u":"/docs/4/getting-started/highlights","h":"#different-types-of-subscriptions","p":1392},{"i":1412,"t":"RPC over bidirectional connection","u":"/docs/4/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":1392},{"i":1414,"t":"Online presence information","u":"/docs/4/getting-started/highlights","h":"#online-presence-information","p":1392},{"i":1416,"t":"Message history in channels","u":"/docs/4/getting-started/highlights","h":"#message-history-in-channels","p":1392},{"i":1418,"t":"Embedded admin web UI","u":"/docs/4/getting-started/highlights","h":"#embedded-admin-web-ui","p":1392},{"i":1420,"t":"Cross-platform","u":"/docs/4/getting-started/highlights","h":"#cross-platform","p":1392},{"i":1422,"t":"Ready to deploy","u":"/docs/4/getting-started/highlights","h":"#ready-to-deploy","p":1392},{"i":1424,"t":"Open-source","u":"/docs/4/getting-started/highlights","h":"#open-source","p":1392},{"i":1426,"t":"Pro features","u":"/docs/4/getting-started/highlights","h":"#pro-features","p":1392},{"i":1430,"t":"Install from the binary release","u":"/docs/4/getting-started/installation","h":"#install-from-the-binary-release","p":1428},{"i":1432,"t":"Docker image","u":"/docs/4/getting-started/installation","h":"#docker-image","p":1428},{"i":1434,"t":"Docker-compose example","u":"/docs/4/getting-started/installation","h":"#docker-compose-example","p":1428},{"i":1436,"t":"Kubernetes Helm chart","u":"/docs/4/getting-started/installation","h":"#kubernetes-helm-chart","p":1428},{"i":1438,"t":"RPM and DEB packages for Linux","u":"/docs/4/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":1428},{"i":1440,"t":"With brew on macOS","u":"/docs/4/getting-started/installation","h":"#with-brew-on-macos","p":1428},{"i":1442,"t":"Build from source","u":"/docs/4/getting-started/installation","h":"#build-from-source","p":1428},{"i":1446,"t":"0. Install","u":"/docs/4/getting-started/integration","h":"#0-install","p":1444},{"i":1448,"t":"1. Configure Centrifugo","u":"/docs/4/getting-started/integration","h":"#1-configure-centrifugo","p":1444},{"i":1450,"t":"2. Configure your backend","u":"/docs/4/getting-started/integration","h":"#2-configure-your-backend","p":1444},{"i":1452,"t":"3. Connect to Centrifugo","u":"/docs/4/getting-started/integration","h":"#3-connect-to-centrifugo","p":1444},{"i":1454,"t":"4. Subscribe to channels","u":"/docs/4/getting-started/integration","h":"#4-subscribe-to-channels","p":1444},{"i":1456,"t":"5. Publish to channel","u":"/docs/4/getting-started/integration","h":"#5-publish-to-channel","p":1444},{"i":1458,"t":"6. Deploy to production","u":"/docs/4/getting-started/integration","h":"#6-deploy-to-production","p":1444},{"i":1460,"t":"7. Monitor Centrifugo","u":"/docs/4/getting-started/integration","h":"#7-monitor-centrifugo","p":1444},{"i":1462,"t":"8. Scale Centrifugo","u":"/docs/4/getting-started/integration","h":"#8-scale-centrifugo","p":1444},{"i":1464,"t":"9. Read FAQ","u":"/docs/4/getting-started/integration","h":"#9-read-faq","p":1444},{"i":1468,"t":"Background","u":"/docs/4/getting-started/introduction","h":"#background","p":1466},{"i":1472,"t":"Connection capabilities","u":"/docs/4/pro/capabilities","h":"#connection-capabilities","p":1470},{"i":1474,"t":"Caps processing behavior","u":"/docs/4/pro/capabilities","h":"#caps-processing-behavior","p":1470},{"i":1476,"t":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations","p":1470},{"i":1478,"t":"Revoking connection caps","u":"/docs/4/pro/capabilities","h":"#revoking-connection-caps","p":1470},{"i":1480,"t":"Example: wildcard match","u":"/docs/4/pro/capabilities","h":"#example-wildcard-match","p":1470},{"i":1482,"t":"Example: regex match","u":"/docs/4/pro/capabilities","h":"#example-regex-match","p":1470},{"i":1484,"t":"Example: different types of match","u":"/docs/4/pro/capabilities","h":"#example-different-types-of-match","p":1470},{"i":1486,"t":"Example: full access to all channels","u":"/docs/4/pro/capabilities","h":"#example-full-access-to-all-channels","p":1470},{"i":1488,"t":"Subscription capabilities","u":"/docs/4/pro/capabilities","h":"#subscription-capabilities","p":1470},{"i":1490,"t":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations-1","p":1470},{"i":1492,"t":"Revoking subscription permissions","u":"/docs/4/pro/capabilities","h":"#revoking-subscription-permissions","p":1470},{"i":1498,"t":"Client SDK migration","u":"/docs/4/getting-started/migration_v4","h":"#client-sdk-migration","p":1496},{"i":1500,"t":"Unidirectional transport migration","u":"/docs/4/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":1496},{"i":1502,"t":"SockJS migration","u":"/docs/4/getting-started/migration_v4","h":"#sockjs-migration","p":1496},{"i":1504,"t":"Channel ASCII enforced","u":"/docs/4/getting-started/migration_v4","h":"#channel-ascii-enforced","p":1496},{"i":1506,"t":"Subscription token migration","u":"/docs/4/getting-started/migration_v4","h":"#subscription-token-migration","p":1496},{"i":1508,"t":"User-limited channel migration","u":"/docs/4/getting-started/migration_v4","h":"#user-limited-channel-migration","p":1496},{"i":1510,"t":"Namespace configuration migration","u":"/docs/4/getting-started/migration_v4","h":"#namespace-configuration-migration","p":1496},{"i":1512,"t":"Proxy disconnect code changes","u":"/docs/4/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":1496},{"i":1514,"t":"Other configuration option changes","u":"/docs/4/getting-started/migration_v4","h":"#other-configuration-option-changes","p":1496},{"i":1516,"t":"Server API changes","u":"/docs/4/getting-started/migration_v4","h":"#server-api-changes","p":1496},{"i":1520,"t":"subscribe_cel","u":"/docs/4/pro/cel_expressions","h":"#subscribe_cel","p":1518},{"i":1522,"t":"Expression variables","u":"/docs/4/pro/cel_expressions","h":"#expression-variables","p":1518},{"i":1524,"t":"publish_cel","u":"/docs/4/pro/cel_expressions","h":"#publish_cel","p":1518},{"i":1526,"t":"history_cel","u":"/docs/4/pro/cel_expressions","h":"#history_cel","p":1518},{"i":1528,"t":"presence_cel","u":"/docs/4/pro/cel_expressions","h":"#presence_cel","p":1518},{"i":1532,"t":"Configuration","u":"/docs/4/pro/analytics","h":"#configuration","p":1530},{"i":1534,"t":"Connections table","u":"/docs/4/pro/analytics","h":"#connections-table","p":1530},{"i":1536,"t":"Subscriptions table","u":"/docs/4/pro/analytics","h":"#subscriptions-table","p":1530},{"i":1538,"t":"Operations table","u":"/docs/4/pro/analytics","h":"#operations-table","p":1530},{"i":1540,"t":"Publications table","u":"/docs/4/pro/analytics","h":"#publications-table","p":1530},{"i":1542,"t":"Notifications table","u":"/docs/4/pro/analytics","h":"#notifications-table","p":1530},{"i":1544,"t":"Query examples","u":"/docs/4/pro/analytics","h":"#query-examples","p":1530},{"i":1546,"t":"Development","u":"/docs/4/pro/analytics","h":"#development","p":1530},{"i":1548,"t":"How export works","u":"/docs/4/pro/analytics","h":"#how-export-works","p":1530},{"i":1552,"t":"Binary release","u":"/docs/4/pro/install_and_run","h":"#binary-release","p":1550},{"i":1554,"t":"Docker image","u":"/docs/4/pro/install_and_run","h":"#docker-image","p":1550},{"i":1556,"t":"Kubernetes","u":"/docs/4/pro/install_and_run","h":"#kubernetes","p":1550},{"i":1558,"t":"Debian and Ubuntu","u":"/docs/4/pro/install_and_run","h":"#debian-and-ubuntu","p":1550},{"i":1560,"t":"Centos","u":"/docs/4/pro/install_and_run","h":"#centos","p":1550},{"i":1562,"t":"Setting PRO license key","u":"/docs/4/pro/install_and_run","h":"#setting-pro-license-key","p":1550},{"i":1566,"t":"Configuration","u":"/docs/4/pro/channel_patterns","h":"#configuration","p":1564},{"i":1568,"t":"Implementation details","u":"/docs/4/pro/channel_patterns","h":"#implementation-details","p":1564},{"i":1570,"t":"Variables","u":"/docs/4/pro/channel_patterns","h":"#variables","p":1564},{"i":1572,"t":"Using varibles","u":"/docs/4/pro/channel_patterns","h":"#using-varibles","p":1564},{"i":1576,"t":"Example","u":"/docs/4/pro/connections","h":"#example","p":1574},{"i":1578,"t":"connections","u":"/docs/4/pro/connections","h":"#connections","p":1574},{"i":1582,"t":"Features","u":"/docs/4/pro/overview","h":"#features","p":1580},{"i":1584,"t":"Try for free in sandbox mode","u":"/docs/4/pro/overview","h":"#try-for-free-in-sandbox-mode","p":1580},{"i":1586,"t":"Pricing","u":"/docs/4/pro/overview","h":"#pricing","p":1580},{"i":1590,"t":"Faster HTTP API","u":"/docs/4/pro/performance","h":"#faster-http-api","p":1588},{"i":1592,"t":"Faster GRPC API","u":"/docs/4/pro/performance","h":"#faster-grpc-api","p":1588},{"i":1594,"t":"Faster HTTP proxy","u":"/docs/4/pro/performance","h":"#faster-http-proxy","p":1588},{"i":1596,"t":"Faster GRPC proxy","u":"/docs/4/pro/performance","h":"#faster-grpc-proxy","p":1588},{"i":1598,"t":"Faster JWT decoding","u":"/docs/4/pro/performance","h":"#faster-jwt-decoding","p":1588},{"i":1600,"t":"Faster GRPC unidirectional stream","u":"/docs/4/pro/performance","h":"#faster-grpc-unidirectional-stream","p":1588},{"i":1602,"t":"Examples","u":"/docs/4/pro/performance","h":"#examples","p":1588},{"i":1604,"t":"Publish HTTP API","u":"/docs/4/pro/performance","h":"#publish-http-api","p":1588},{"i":1606,"t":"History HTTP API","u":"/docs/4/pro/performance","h":"#history-http-api","p":1588},{"i":1610,"t":"client_write_delay","u":"/docs/4/pro/client_message_batching","h":"#client_write_delay","p":1608},{"i":1612,"t":"client_reply_without_queue","u":"/docs/4/pro/client_message_batching","h":"#client_reply_without_queue","p":1608},{"i":1614,"t":"client_max_messages_in_frame","u":"/docs/4/pro/client_message_batching","h":"#client_max_messages_in_frame","p":1608},{"i":1622,"t":"Motivation and design choices","u":"/docs/4/pro/push_notifications","h":"#motivation-and-design-choices","p":1620},{"i":1624,"t":"Storage for tokens","u":"/docs/4/pro/push_notifications","h":"#storage-for-tokens","p":1620},{"i":1626,"t":"Efficient queuing","u":"/docs/4/pro/push_notifications","h":"#efficient-queuing","p":1620},{"i":1628,"t":"Unified secure topics","u":"/docs/4/pro/push_notifications","h":"#unified-secure-topics","p":1620},{"i":1630,"t":"Non-obtrusive proxying","u":"/docs/4/pro/push_notifications","h":"#non-obtrusive-proxying","p":1620},{"i":1632,"t":"Builtin analytics","u":"/docs/4/pro/push_notifications","h":"#builtin-analytics","p":1620},{"i":1634,"t":"Steps to integrate","u":"/docs/4/pro/push_notifications","h":"#steps-to-integrate","p":1620},{"i":1636,"t":"Configuration","u":"/docs/4/pro/push_notifications","h":"#configuration","p":1620},{"i":1638,"t":"FCM","u":"/docs/4/pro/push_notifications","h":"#fcm","p":1620},{"i":1640,"t":"HMS","u":"/docs/4/pro/push_notifications","h":"#hms","p":1620},{"i":1642,"t":"APNs","u":"/docs/4/pro/push_notifications","h":"#apns","p":1620},{"i":1644,"t":"Other options","u":"/docs/4/pro/push_notifications","h":"#other-options","p":1620},{"i":1646,"t":"Use PostgreSQL as queue","u":"/docs/4/pro/push_notifications","h":"#use-postgresql-as-queue","p":1620},{"i":1648,"t":"API description","u":"/docs/4/pro/push_notifications","h":"#api-description","p":1620},{"i":1649,"t":"device_register","u":"/docs/4/pro/push_notifications","h":"#device_register","p":1620},{"i":1651,"t":"device_update","u":"/docs/4/pro/push_notifications","h":"#device_update","p":1620},{"i":1653,"t":"device_remove","u":"/docs/4/pro/push_notifications","h":"#device_remove","p":1620},{"i":1655,"t":"device_list","u":"/docs/4/pro/push_notifications","h":"#device_list","p":1620},{"i":1657,"t":"device_topic_update","u":"/docs/4/pro/push_notifications","h":"#device_topic_update","p":1620},{"i":1659,"t":"device_topic_list","u":"/docs/4/pro/push_notifications","h":"#device_topic_list","p":1620},{"i":1661,"t":"user_topic_update","u":"/docs/4/pro/push_notifications","h":"#user_topic_update","p":1620},{"i":1663,"t":"user_topic_list","u":"/docs/4/pro/push_notifications","h":"#user_topic_list","p":1620},{"i":1665,"t":"send_push_notification","u":"/docs/4/pro/push_notifications","h":"#send_push_notification","p":1620},{"i":1667,"t":"update_push_status","u":"/docs/4/pro/push_notifications","h":"#update_push_status","p":1620},{"i":1669,"t":"Metrics","u":"/docs/4/pro/push_notifications","h":"#metrics","p":1620},{"i":1671,"t":"Further reading and tutorials","u":"/docs/4/pro/push_notifications","h":"#further-reading-and-tutorials","p":1620},{"i":1675,"t":"In-memory per connection throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-connection-throttling","p":1673},{"i":1677,"t":"In-memory per user throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-user-throttling","p":1673},{"i":1679,"t":"Redis per user throttling","u":"/docs/4/pro/throttling","h":"#redis-per-user-throttling","p":1673},{"i":1681,"t":"Disconnecting abusive or misbehaving connections","u":"/docs/4/pro/throttling","h":"#disconnecting-abusive-or-misbehaving-connections","p":1673},{"i":1685,"t":"Save to a file","u":"/docs/4/pro/tracing","h":"#save-to-a-file","p":1683},{"i":1689,"t":"How it works","u":"/docs/4/pro/token_revocation","h":"#how-it-works","p":1687},{"i":1691,"t":"Configure","u":"/docs/4/pro/token_revocation","h":"#configure","p":1687},{"i":1693,"t":"Redis persistence engine","u":"/docs/4/pro/token_revocation","h":"#redis-persistence-engine","p":1687},{"i":1695,"t":"Database persistence engine","u":"/docs/4/pro/token_revocation","h":"#database-persistence-engine","p":1687},{"i":1697,"t":"Revoke token API","u":"/docs/4/pro/token_revocation","h":"#revoke-token-api","p":1687},{"i":1698,"t":"revoke_token","u":"/docs/4/pro/token_revocation","h":"#revoke_token","p":1687},{"i":1700,"t":"Invalidate user tokens API","u":"/docs/4/pro/token_revocation","h":"#invalidate-user-tokens-api","p":1687},{"i":1701,"t":"invalidate_user_tokens","u":"/docs/4/pro/token_revocation","h":"#invalidate_user_tokens","p":1687},{"i":1705,"t":"How it works","u":"/docs/4/pro/user_block","h":"#how-it-works","p":1703},{"i":1707,"t":"Configure","u":"/docs/4/pro/user_block","h":"#configure","p":1703},{"i":1709,"t":"Redis persistence engine","u":"/docs/4/pro/user_block","h":"#redis-persistence-engine","p":1703},{"i":1711,"t":"Database persistence engine","u":"/docs/4/pro/user_block","h":"#database-persistence-engine","p":1703},{"i":1713,"t":"Block API","u":"/docs/4/pro/user_block","h":"#block--api","p":1703},{"i":1714,"t":"block_user","u":"/docs/4/pro/user_block","h":"#block_user","p":1703},{"i":1716,"t":"unblock_user","u":"/docs/4/pro/user_block","h":"#unblock_user","p":1703},{"i":1720,"t":"Options","u":"/docs/4/server/admin_web","h":"#options","p":1718},{"i":1722,"t":"Using custom web interface","u":"/docs/4/server/admin_web","h":"#using-custom-web-interface","p":1718},{"i":1724,"t":"Admin insecure mode","u":"/docs/4/server/admin_web","h":"#admin-insecure-mode","p":1718},{"i":1728,"t":"Client-side status update RPC","u":"/docs/4/pro/user_status","h":"#client-side-status-update-rpc","p":1726},{"i":1730,"t":"update_user_status server API","u":"/docs/4/pro/user_status","h":"#update_user_status-server-api","p":1726},{"i":1732,"t":"get_user_status server API","u":"/docs/4/pro/user_status","h":"#get_user_status-server-api","p":1726},{"i":1734,"t":"delete_user_status server API","u":"/docs/4/pro/user_status","h":"#delete_user_status-server-api","p":1726},{"i":1736,"t":"Configuration","u":"/docs/4/pro/user_status","h":"#configuration","p":1726},{"i":1740,"t":"Subscribe permission model","u":"/docs/4/server/channel_permissions","h":"#subscribe-permission-model","p":1738},{"i":1742,"t":"Publish permission model","u":"/docs/4/server/channel_permissions","h":"#publish-permission-model","p":1738},{"i":1744,"t":"History permission model","u":"/docs/4/server/channel_permissions","h":"#history-permission-model","p":1738},{"i":1746,"t":"Presence permission model","u":"/docs/4/server/channel_permissions","h":"#presence-permission-model","p":1738},{"i":1748,"t":"Positioning permission model","u":"/docs/4/server/channel_permissions","h":"#positioning-permission-model","p":1738},{"i":1750,"t":"Recovery permission model","u":"/docs/4/server/channel_permissions","h":"#recovery-permission-model","p":1738},{"i":1752,"t":"Join/Leave permission model","u":"/docs/4/server/channel_permissions","h":"#joinleave-permission-model","p":1738},{"i":1756,"t":"Connection JWT claims","u":"/docs/4/server/authentication","h":"#connection-jwt-claims","p":1754},{"i":1758,"t":"sub","u":"/docs/4/server/authentication","h":"#sub","p":1754},{"i":1760,"t":"exp","u":"/docs/4/server/authentication","h":"#exp","p":1754},{"i":1762,"t":"iat","u":"/docs/4/server/authentication","h":"#iat","p":1754},{"i":1764,"t":"jti","u":"/docs/4/server/authentication","h":"#jti","p":1754},{"i":1766,"t":"aud","u":"/docs/4/server/authentication","h":"#aud","p":1754},{"i":1768,"t":"iss","u":"/docs/4/server/authentication","h":"#iss","p":1754},{"i":1770,"t":"info","u":"/docs/4/server/authentication","h":"#info","p":1754},{"i":1772,"t":"b64info","u":"/docs/4/server/authentication","h":"#b64info","p":1754},{"i":1774,"t":"channels","u":"/docs/4/server/authentication","h":"#channels","p":1754},{"i":1776,"t":"subs","u":"/docs/4/server/authentication","h":"#subs","p":1754},{"i":1778,"t":"meta","u":"/docs/4/server/authentication","h":"#meta","p":1754},{"i":1780,"t":"expire_at","u":"/docs/4/server/authentication","h":"#expire_at","p":1754},{"i":1782,"t":"Connection expiration","u":"/docs/4/server/authentication","h":"#connection-expiration","p":1754},{"i":1784,"t":"Examples","u":"/docs/4/server/authentication","h":"#examples","p":1754},{"i":1786,"t":"Simplest token","u":"/docs/4/server/authentication","h":"#simplest-token","p":1754},{"i":1788,"t":"Token with expiration","u":"/docs/4/server/authentication","h":"#token-with-expiration","p":1754},{"i":1790,"t":"Token with additional connection info","u":"/docs/4/server/authentication","h":"#token-with-additional-connection-info","p":1754},{"i":1792,"t":"Investigating problems with JWT","u":"/docs/4/server/authentication","h":"#investigating-problems-with-jwt","p":1754},{"i":1794,"t":"JSON Web Key support","u":"/docs/4/server/authentication","h":"#json-web-key-support","p":1754},{"i":1796,"t":"Dynamic JWKs endpoint","u":"/docs/4/server/authentication","h":"#dynamic-jwks-endpoint","p":1754},{"i":1800,"t":"Subscription JWT claims","u":"/docs/4/server/channel_token_auth","h":"#subscription-jwt-claims","p":1798},{"i":1802,"t":"sub","u":"/docs/4/server/channel_token_auth","h":"#sub","p":1798},{"i":1804,"t":"channel","u":"/docs/4/server/channel_token_auth","h":"#channel","p":1798},{"i":1806,"t":"info","u":"/docs/4/server/channel_token_auth","h":"#info","p":1798},{"i":1808,"t":"b64info","u":"/docs/4/server/channel_token_auth","h":"#b64info","p":1798},{"i":1810,"t":"exp","u":"/docs/4/server/channel_token_auth","h":"#exp","p":1798},{"i":1812,"t":"expire_at","u":"/docs/4/server/channel_token_auth","h":"#expire_at","p":1798},{"i":1814,"t":"aud","u":"/docs/4/server/channel_token_auth","h":"#aud","p":1798},{"i":1816,"t":"iss","u":"/docs/4/server/channel_token_auth","h":"#iss","p":1798},{"i":1818,"t":"iat","u":"/docs/4/server/channel_token_auth","h":"#iat","p":1798},{"i":1820,"t":"jti","u":"/docs/4/server/channel_token_auth","h":"#jti","p":1798},{"i":1822,"t":"override","u":"/docs/4/server/channel_token_auth","h":"#override","p":1798},{"i":1824,"t":"Example","u":"/docs/4/server/channel_token_auth","h":"#example","p":1798},{"i":1826,"t":"With gensubtoken cli command","u":"/docs/4/server/channel_token_auth","h":"#with-gensubtoken-cli-command","p":1798},{"i":1830,"t":"Client error codes","u":"/docs/4/server/codes","h":"#client-error-codes","p":1828},{"i":1832,"t":"Internal","u":"/docs/4/server/codes","h":"#internal","p":1828},{"i":1834,"t":"Unauthorized","u":"/docs/4/server/codes","h":"#unauthorized","p":1828},{"i":1836,"t":"Unknown Channel","u":"/docs/4/server/codes","h":"#unknown-channel","p":1828},{"i":1838,"t":"Permission Denied","u":"/docs/4/server/codes","h":"#permission-denied","p":1828},{"i":1840,"t":"Method Not Found","u":"/docs/4/server/codes","h":"#method-not-found","p":1828},{"i":1842,"t":"Already Subscribed","u":"/docs/4/server/codes","h":"#already-subscribed","p":1828},{"i":1844,"t":"Limit Exceeded","u":"/docs/4/server/codes","h":"#limit-exceeded","p":1828},{"i":1846,"t":"Bad Request","u":"/docs/4/server/codes","h":"#bad-request","p":1828},{"i":1848,"t":"Not Available","u":"/docs/4/server/codes","h":"#not-available","p":1828},{"i":1850,"t":"Token Expired","u":"/docs/4/server/codes","h":"#token-expired","p":1828},{"i":1852,"t":"Expired","u":"/docs/4/server/codes","h":"#expired","p":1828},{"i":1854,"t":"Too Many Requests","u":"/docs/4/server/codes","h":"#too-many-requests","p":1828},{"i":1856,"t":"Unrecoverable Position","u":"/docs/4/server/codes","h":"#unrecoverable-position","p":1828},{"i":1858,"t":"Client disconnect codes","u":"/docs/4/server/codes","h":"#client-disconnect-codes","p":1828},{"i":1860,"t":"DisconnectConnectionClosed","u":"/docs/4/server/codes","h":"#disconnectconnectionclosed","p":1828},{"i":1862,"t":"Non-terminal disconnect codes","u":"/docs/4/server/codes","h":"#non-terminal-disconnect-codes","p":1828},{"i":1864,"t":"Terminal disconnect codes","u":"/docs/4/server/codes","h":"#terminal-disconnect-codes","p":1828},{"i":1868,"t":"What is channel","u":"/docs/4/server/channels","h":"#what-is-channel","p":1866},{"i":1870,"t":"Channel name rules","u":"/docs/4/server/channels","h":"#channel-name-rules","p":1866},{"i":1872,"t":"namespace boundary (:)","u":"/docs/4/server/channels","h":"#namespace-boundary-","p":1866},{"i":1874,"t":"user channel boundary (#)","u":"/docs/4/server/channels","h":"#user-channel-boundary-","p":1866},{"i":1876,"t":"private channel prefix ($)","u":"/docs/4/server/channels","h":"#private-channel-prefix-","p":1866},{"i":1878,"t":"Channel is just a string","u":"/docs/4/server/channels","h":"#channel-is-just-a-string","p":1866},{"i":1880,"t":"Channel namespaces","u":"/docs/4/server/channels","h":"#channel-namespaces","p":1866},{"i":1882,"t":"Channel options","u":"/docs/4/server/channels","h":"#channel-options","p":1866},{"i":1884,"t":"presence","u":"/docs/4/server/channels","h":"#presence","p":1866},{"i":1886,"t":"join_leave","u":"/docs/4/server/channels","h":"#join_leave","p":1866},{"i":1888,"t":"force_push_join_leave","u":"/docs/4/server/channels","h":"#force_push_join_leave","p":1866},{"i":1890,"t":"history_size","u":"/docs/4/server/channels","h":"#history_size","p":1866},{"i":1892,"t":"history_ttl","u":"/docs/4/server/channels","h":"#history_ttl","p":1866},{"i":1894,"t":"force_positioning","u":"/docs/4/server/channels","h":"#force_positioning","p":1866},{"i":1896,"t":"force_recovery","u":"/docs/4/server/channels","h":"#force_recovery","p":1866},{"i":1898,"t":"allow_subscribe_for_client","u":"/docs/4/server/channels","h":"#allow_subscribe_for_client","p":1866},{"i":1900,"t":"allow_subscribe_for_anonymous","u":"/docs/4/server/channels","h":"#allow_subscribe_for_anonymous","p":1866},{"i":1902,"t":"allow_publish_for_subscriber","u":"/docs/4/server/channels","h":"#allow_publish_for_subscriber","p":1866},{"i":1904,"t":"allow_publish_for_client","u":"/docs/4/server/channels","h":"#allow_publish_for_client","p":1866},{"i":1906,"t":"allow_publish_for_anonymous","u":"/docs/4/server/channels","h":"#allow_publish_for_anonymous","p":1866},{"i":1908,"t":"allow_history_for_subscriber","u":"/docs/4/server/channels","h":"#allow_history_for_subscriber","p":1866},{"i":1910,"t":"allow_history_for_client","u":"/docs/4/server/channels","h":"#allow_history_for_client","p":1866},{"i":1912,"t":"allow_history_for_anonymous","u":"/docs/4/server/channels","h":"#allow_history_for_anonymous","p":1866},{"i":1914,"t":"allow_presence_for_subscriber","u":"/docs/4/server/channels","h":"#allow_presence_for_subscriber","p":1866},{"i":1916,"t":"allow_presence_for_client","u":"/docs/4/server/channels","h":"#allow_presence_for_client","p":1866},{"i":1918,"t":"allow_presence_for_anonymous","u":"/docs/4/server/channels","h":"#allow_presence_for_anonymous","p":1866},{"i":1920,"t":"allow_user_limited_channels","u":"/docs/4/server/channels","h":"#allow_user_limited_channels","p":1866},{"i":1922,"t":"channel_regex","u":"/docs/4/server/channels","h":"#channel_regex","p":1866},{"i":1924,"t":"proxy_subscribe","u":"/docs/4/server/channels","h":"#proxy_subscribe","p":1866},{"i":1926,"t":"proxy_publish","u":"/docs/4/server/channels","h":"#proxy_publish","p":1866},{"i":1928,"t":"proxy_sub_refresh","u":"/docs/4/server/channels","h":"#proxy_sub_refresh","p":1866},{"i":1930,"t":"subscribe_proxy_name","u":"/docs/4/server/channels","h":"#subscribe_proxy_name","p":1866},{"i":1932,"t":"publish_proxy_name","u":"/docs/4/server/channels","h":"#publish_proxy_name","p":1866},{"i":1934,"t":"sub_refresh_proxy_name","u":"/docs/4/server/channels","h":"#sub_refresh_proxy_name","p":1866},{"i":1936,"t":"Channel config examples","u":"/docs/4/server/channels","h":"#channel-config-examples","p":1866},{"i":1940,"t":"Configuration sources","u":"/docs/4/server/configuration","h":"#configuration-sources","p":1938},{"i":1942,"t":"Command-line flags","u":"/docs/4/server/configuration","h":"#command-line-flags","p":1938},{"i":1944,"t":"OS environment variables","u":"/docs/4/server/configuration","h":"#os-environment-variables","p":1938},{"i":1946,"t":"Configuration file","u":"/docs/4/server/configuration","h":"#configuration-file","p":1938},{"i":1948,"t":"Config file formats","u":"/docs/4/server/configuration","h":"#config-file-formats","p":1938},{"i":1950,"t":"JSON config format","u":"/docs/4/server/configuration","h":"#json-config-format","p":1938},{"i":1952,"t":"TOML config format","u":"/docs/4/server/configuration","h":"#toml-config-format","p":1938},{"i":1954,"t":"YAML config format","u":"/docs/4/server/configuration","h":"#yaml-config-format","p":1938},{"i":1956,"t":"Important options","u":"/docs/4/server/configuration","h":"#important-options","p":1938},{"i":1958,"t":"allowed_origins","u":"/docs/4/server/configuration","h":"#allowed_origins","p":1938},{"i":1960,"t":"address","u":"/docs/4/server/configuration","h":"#address","p":1938},{"i":1962,"t":"port","u":"/docs/4/server/configuration","h":"#port","p":1938},{"i":1964,"t":"engine","u":"/docs/4/server/configuration","h":"#engine","p":1938},{"i":1966,"t":"Advanced options","u":"/docs/4/server/configuration","h":"#advanced-options","p":1938},{"i":1968,"t":"client_channel_limit","u":"/docs/4/server/configuration","h":"#client_channel_limit","p":1938},{"i":1970,"t":"channel_max_length","u":"/docs/4/server/configuration","h":"#channel_max_length","p":1938},{"i":1972,"t":"client_user_connection_limit","u":"/docs/4/server/configuration","h":"#client_user_connection_limit","p":1938},{"i":1974,"t":"client_connection_limit","u":"/docs/4/server/configuration","h":"#client_connection_limit","p":1938},{"i":1976,"t":"client_connection_rate_limit","u":"/docs/4/server/configuration","h":"#client_connection_rate_limit","p":1938},{"i":1978,"t":"client_queue_max_size","u":"/docs/4/server/configuration","h":"#client_queue_max_size","p":1938},{"i":1980,"t":"client_concurrency","u":"/docs/4/server/configuration","h":"#client_concurrency","p":1938},{"i":1982,"t":"client_stale_close_delay","u":"/docs/4/server/configuration","h":"#client_stale_close_delay","p":1938},{"i":1984,"t":"allow_anonymous_connect_without_token","u":"/docs/4/server/configuration","h":"#allow_anonymous_connect_without_token","p":1938},{"i":1986,"t":"disallow_anonymous_connection_tokens","u":"/docs/4/server/configuration","h":"#disallow_anonymous_connection_tokens","p":1938},{"i":1988,"t":"gomaxprocs","u":"/docs/4/server/configuration","h":"#gomaxprocs","p":1938},{"i":1990,"t":"Endpoint configuration.","u":"/docs/4/server/configuration","h":"#endpoint-configuration","p":1938},{"i":1992,"t":"Default endpoints.","u":"/docs/4/server/configuration","h":"#default-endpoints","p":1938},{"i":1994,"t":"Admin endpoints.","u":"/docs/4/server/configuration","h":"#admin-endpoints","p":1938},{"i":1996,"t":"Debug endpoints.","u":"/docs/4/server/configuration","h":"#debug-endpoints","p":1938},{"i":1998,"t":"Health check endpoint","u":"/docs/4/server/configuration","h":"#health-check-endpoint","p":1938},{"i":2000,"t":"Custom internal ports","u":"/docs/4/server/configuration","h":"#custom-internal-ports","p":1938},{"i":2002,"t":"Disable default endpoints","u":"/docs/4/server/configuration","h":"#disable-default-endpoints","p":1938},{"i":2004,"t":"Customize handler endpoints","u":"/docs/4/server/configuration","h":"#customize-handler-endpoints","p":1938},{"i":2006,"t":"Signal handling","u":"/docs/4/server/configuration","h":"#signal-handling","p":1938},{"i":2008,"t":"Insecure modes","u":"/docs/4/server/configuration","h":"#insecure-modes","p":1938},{"i":2009,"t":"Insecure client connection","u":"/docs/4/server/configuration","h":"#insecure-client-connection","p":1938},{"i":2011,"t":"Insecure API mode","u":"/docs/4/server/configuration","h":"#insecure-api-mode","p":1938},{"i":2013,"t":"Insecure admin mode","u":"/docs/4/server/configuration","h":"#insecure-admin-mode","p":1938},{"i":2015,"t":"Setting time duration options","u":"/docs/4/server/configuration","h":"#setting-time-duration-options","p":1938},{"i":2017,"t":"Setting namespaces over env","u":"/docs/4/server/configuration","h":"#setting-namespaces-over-env","p":1938},{"i":2019,"t":"Anonymous usage stats","u":"/docs/4/server/configuration","h":"#anonymous-usage-stats","p":1938},{"i":2023,"t":"Memory engine","u":"/docs/4/server/engines","h":"#memory-engine","p":2021},{"i":2025,"t":"Memory engine options","u":"/docs/4/server/engines","h":"#memory-engine-options","p":2021},{"i":2027,"t":"Redis engine","u":"/docs/4/server/engines","h":"#redis-engine","p":2021},{"i":2029,"t":"Redis engine options","u":"/docs/4/server/engines","h":"#redis-engine-options","p":2021},{"i":2031,"t":"Configuring Redis TLS","u":"/docs/4/server/engines","h":"#configuring-redis-tls","p":2021},{"i":2033,"t":"Scaling with Redis tutorial","u":"/docs/4/server/engines","h":"#scaling-with-redis-tutorial","p":2021},{"i":2035,"t":"Redis Sentinel for high availability","u":"/docs/4/server/engines","h":"#redis-sentinel-for-high-availability","p":2021},{"i":2037,"t":"Redis Sentinel TLS","u":"/docs/4/server/engines","h":"#redis-sentinel-tls","p":2021},{"i":2039,"t":"Haproxy instead of Sentinel configuration","u":"/docs/4/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":2021},{"i":2041,"t":"Redis sharding","u":"/docs/4/server/engines","h":"#redis-sharding","p":2021},{"i":2043,"t":"Redis cluster","u":"/docs/4/server/engines","h":"#redis-cluster","p":2021},{"i":2045,"t":"KeyDB Engine","u":"/docs/4/server/engines","h":"#keydb-engine","p":2021},{"i":2047,"t":"Other Redis compatible","u":"/docs/4/server/engines","h":"#other-redis-compatible","p":2021},{"i":2049,"t":"Tarantool engine","u":"/docs/4/server/engines","h":"#tarantool-engine","p":2021},{"i":2051,"t":"Tarantool engine options","u":"/docs/4/server/engines","h":"#tarantool-engine-options","p":2021},{"i":2053,"t":"Nats broker","u":"/docs/4/server/engines","h":"#nats-broker","p":2021},{"i":2055,"t":"Options","u":"/docs/4/server/engines","h":"#options","p":2021},{"i":2059,"t":"version","u":"/docs/4/server/console_commands","h":"#version","p":2057},{"i":2061,"t":"genconfig","u":"/docs/4/server/console_commands","h":"#genconfig","p":2057},{"i":2063,"t":"checkconfig","u":"/docs/4/server/console_commands","h":"#checkconfig","p":2057},{"i":2065,"t":"gentoken","u":"/docs/4/server/console_commands","h":"#gentoken","p":2057},{"i":2067,"t":"gensubtoken","u":"/docs/4/server/console_commands","h":"#gensubtoken","p":2057},{"i":2069,"t":"checktoken","u":"/docs/4/server/console_commands","h":"#checktoken","p":2057},{"i":2071,"t":"checksubtoken","u":"/docs/4/server/console_commands","h":"#checksubtoken","p":2057},{"i":2075,"t":"Open files limit","u":"/docs/4/server/infra_tuning","h":"#open-files-limit","p":2073},{"i":2077,"t":"Ephemeral port exhaustion","u":"/docs/4/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":2073},{"i":2079,"t":"Sockets in TIME_WAIT state","u":"/docs/4/server/infra_tuning","h":"#sockets-in-time_wait-state","p":2073},{"i":2081,"t":"Proxy max connections","u":"/docs/4/server/infra_tuning","h":"#proxy-max-connections","p":2073},{"i":2083,"t":"Conntrack table","u":"/docs/4/server/infra_tuning","h":"#conntrack-table","p":2073},{"i":2085,"t":"Additional server protection","u":"/docs/4/server/infra_tuning","h":"#additional-server-protection","p":2073},{"i":2089,"t":"Prometheus","u":"/docs/4/server/monitoring","h":"#prometheus","p":2087},{"i":2091,"t":"Graphite","u":"/docs/4/server/monitoring","h":"#graphite","p":2087},{"i":2093,"t":"Grafana dashboard","u":"/docs/4/server/monitoring","h":"#grafana-dashboard","p":2087},{"i":2097,"t":"History design","u":"/docs/4/server/history_and_recovery","h":"#history-design","p":2095},{"i":2099,"t":"History iteration API","u":"/docs/4/server/history_and_recovery","h":"#history-iteration-api","p":2095},{"i":2101,"t":"Automatic message recovery","u":"/docs/4/server/history_and_recovery","h":"#automatic-message-recovery","p":2095},{"i":2105,"t":"Nginx configuration","u":"/docs/4/server/load_balancing","h":"#nginx-configuration","p":2103},{"i":2107,"t":"Separate domain for Centrifugo","u":"/docs/4/server/load_balancing","h":"#separate-domain-for-centrifugo","p":2103},{"i":2109,"t":"Embed to a location of web site","u":"/docs/4/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":2103},{"i":2111,"t":"worker_connections","u":"/docs/4/server/load_balancing","h":"#worker_connections","p":2103},{"i":2115,"t":"Overview","u":"/docs/4/server/presence","h":"#overview","p":2113},{"i":2117,"t":"Enabling Online Presence","u":"/docs/4/server/presence","h":"#enabling-online-presence","p":2113},{"i":2119,"t":"Retrieving presence on the client side","u":"/docs/4/server/presence","h":"#retrieving-presence-on-the-client-side","p":2113},{"i":2121,"t":"Join and leave events","u":"/docs/4/server/presence","h":"#join-and-leave-events","p":2113},{"i":2123,"t":"Implementation notes","u":"/docs/4/server/presence","h":"#implementation-notes","p":2113},{"i":2125,"t":"Conclusion","u":"/docs/4/server/presence","h":"#conclusion","p":2113},{"i":2129,"t":"Dynamic server-side subscriptions","u":"/docs/4/server/server_subs","h":"#dynamic-server-side-subscriptions","p":2127},{"i":2131,"t":"Automatic personal channel subscription","u":"/docs/4/server/server_subs","h":"#automatic-personal-channel-subscription","p":2127},{"i":2133,"t":"Maintain single user connection","u":"/docs/4/server/server_subs","h":"#maintain-single-user-connection","p":2127},{"i":2137,"t":"HTTP proxy","u":"/docs/4/server/proxy","h":"#http-proxy","p":2135},{"i":2139,"t":"HTTP request structure","u":"/docs/4/server/proxy","h":"#http-request-structure","p":2135},{"i":2141,"t":"Proxy HTTP headers","u":"/docs/4/server/proxy","h":"#proxy-http-headers","p":2135},{"i":2143,"t":"Proxy GRPC metadata","u":"/docs/4/server/proxy","h":"#proxy-grpc-metadata","p":2135},{"i":2145,"t":"Connect proxy","u":"/docs/4/server/proxy","h":"#connect-proxy","p":2135},{"i":2147,"t":"Refresh proxy","u":"/docs/4/server/proxy","h":"#refresh-proxy","p":2135},{"i":2149,"t":"RPC proxy","u":"/docs/4/server/proxy","h":"#rpc-proxy","p":2135},{"i":2151,"t":"Subscribe proxy","u":"/docs/4/server/proxy","h":"#subscribe-proxy","p":2135},{"i":2153,"t":"Publish proxy","u":"/docs/4/server/proxy","h":"#publish-proxy","p":2135},{"i":2155,"t":"Sub refresh proxy","u":"/docs/4/server/proxy","h":"#sub-refresh-proxy","p":2135},{"i":2157,"t":"Return custom error","u":"/docs/4/server/proxy","h":"#return-custom-error","p":2135},{"i":2159,"t":"Return custom disconnect","u":"/docs/4/server/proxy","h":"#return-custom-disconnect","p":2135},{"i":2161,"t":"GRPC proxy","u":"/docs/4/server/proxy","h":"#grpc-proxy","p":2135},{"i":2163,"t":"GRPC proxy options","u":"/docs/4/server/proxy","h":"#grpc-proxy-options","p":2135},{"i":2165,"t":"GRPC proxy example","u":"/docs/4/server/proxy","h":"#grpc-proxy-example","p":2135},{"i":2167,"t":"Header proxy rules","u":"/docs/4/server/proxy","h":"#header-proxy-rules","p":2135},{"i":2169,"t":"Binary mode","u":"/docs/4/server/proxy","h":"#binary-mode","p":2135},{"i":2171,"t":"Granular proxy mode","u":"/docs/4/server/proxy","h":"#granular-proxy-mode","p":2135},{"i":2173,"t":"Enable granular proxy mode","u":"/docs/4/server/proxy","h":"#enable-granular-proxy-mode","p":2135},{"i":2175,"t":"Defining a list of proxies","u":"/docs/4/server/proxy","h":"#defining-a-list-of-proxies","p":2135},{"i":2177,"t":"Granular connect and refresh","u":"/docs/4/server/proxy","h":"#granular-connect-and-refresh","p":2135},{"i":2179,"t":"Granular subscribe, publish, sub refresh","u":"/docs/4/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":2135},{"i":2181,"t":"Granular RPC","u":"/docs/4/server/proxy","h":"#granular-rpc","p":2135},{"i":2185,"t":"HTTP API","u":"/docs/4/server/server_api","h":"#http-api","p":2183},{"i":2187,"t":"HTTP API authorization","u":"/docs/4/server/server_api","h":"#http-api-authorization","p":2183},{"i":2189,"t":"publish","u":"/docs/4/server/server_api","h":"#publish","p":2183},{"i":2191,"t":"broadcast","u":"/docs/4/server/server_api","h":"#broadcast","p":2183},{"i":2193,"t":"subscribe","u":"/docs/4/server/server_api","h":"#subscribe","p":2183},{"i":2195,"t":"unsubscribe","u":"/docs/4/server/server_api","h":"#unsubscribe","p":2183},{"i":2197,"t":"disconnect","u":"/docs/4/server/server_api","h":"#disconnect","p":2183},{"i":2199,"t":"refresh","u":"/docs/4/server/server_api","h":"#refresh","p":2183},{"i":2201,"t":"presence","u":"/docs/4/server/server_api","h":"#presence","p":2183},{"i":2203,"t":"presence_stats","u":"/docs/4/server/server_api","h":"#presence_stats","p":2183},{"i":2205,"t":"history","u":"/docs/4/server/server_api","h":"#history","p":2183},{"i":2207,"t":"history_remove","u":"/docs/4/server/server_api","h":"#history_remove","p":2183},{"i":2209,"t":"channels","u":"/docs/4/server/server_api","h":"#channels","p":2183},{"i":2211,"t":"info","u":"/docs/4/server/server_api","h":"#info","p":2183},{"i":2213,"t":"Command pipelining","u":"/docs/4/server/server_api","h":"#command-pipelining","p":2183},{"i":2215,"t":"HTTP API libraries","u":"/docs/4/server/server_api","h":"#http-api-libraries","p":2183},{"i":2217,"t":"GRPC API","u":"/docs/4/server/server_api","h":"#grpc-api","p":2183},{"i":2219,"t":"GRPC example for Python","u":"/docs/4/server/server_api","h":"#grpc-example-for-python","p":2183},{"i":2221,"t":"GRPC example for Go","u":"/docs/4/server/server_api","h":"#grpc-example-for-go","p":2183},{"i":2223,"t":"GRPC API key authorization","u":"/docs/4/server/server_api","h":"#grpc-api-key-authorization","p":2183},{"i":2227,"t":"Using crt and key files","u":"/docs/4/server/tls","h":"#using-crt-and-key-files","p":2225},{"i":2229,"t":"Automatic certificates","u":"/docs/4/server/tls","h":"#automatic-certificates","p":2225},{"i":2231,"t":"TLS for GRPC API","u":"/docs/4/server/tls","h":"#tls-for-grpc-api","p":2225},{"i":2233,"t":"TLS for GRPC unidirectional stream","u":"/docs/4/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":2225},{"i":2237,"t":"Protobuf schema","u":"/docs/4/transports/client_protocol","h":"#protobuf-schema","p":2235},{"i":2239,"t":"Command-Reply","u":"/docs/4/transports/client_protocol","h":"#command-reply","p":2235},{"i":2241,"t":"Asynchronous pushes","u":"/docs/4/transports/client_protocol","h":"#asynchronous-pushes","p":2235},{"i":2243,"t":"Top level batching","u":"/docs/4/transports/client_protocol","h":"#top-level-batching","p":2235},{"i":2245,"t":"Ping Pong","u":"/docs/4/transports/client_protocol","h":"#ping-pong","p":2235},{"i":2247,"t":"Handle disconnects","u":"/docs/4/transports/client_protocol","h":"#handle-disconnects","p":2235},{"i":2249,"t":"Handle errors","u":"/docs/4/transports/client_protocol","h":"#handle-errors","p":2235},{"i":2251,"t":"Additional notes","u":"/docs/4/transports/client_protocol","h":"#additional-notes","p":2235},{"i":2255,"t":"List of client SDKs","u":"/docs/4/transports/client_sdk","h":"#list-of-client-sdks","p":2253},{"i":2257,"t":"Protobuf and JSON formats in SDKs","u":"/docs/4/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":2253},{"i":2259,"t":"SDK feature matrix","u":"/docs/4/transports/client_sdk","h":"#sdk-feature-matrix","p":2253},{"i":2261,"t":"Connection related features","u":"/docs/4/transports/client_sdk","h":"#connection-related-features","p":2253},{"i":2263,"t":"Client-side subscription related features","u":"/docs/4/transports/client_sdk","h":"#client-side-subscription-related-features","p":2253},{"i":2267,"t":"Options","u":"/docs/4/transports/http_stream","h":"#options","p":2265},{"i":2268,"t":"http_stream","u":"/docs/4/transports/http_stream","h":"#http_stream","p":2265},{"i":2270,"t":"http_stream_max_request_body_size","u":"/docs/4/transports/http_stream","h":"#http_stream_max_request_body_size","p":2265},{"i":2274,"t":"SockJS caveats","u":"/docs/4/transports/sockjs","h":"#sockjs-caveats","p":2272},{"i":2276,"t":"Sticky sessions","u":"/docs/4/transports/sockjs","h":"#sticky-sessions","p":2272},{"i":2278,"t":"Browser only","u":"/docs/4/transports/sockjs","h":"#browser-only","p":2272},{"i":2280,"t":"JSON only","u":"/docs/4/transports/sockjs","h":"#json-only","p":2272},{"i":2282,"t":"Options","u":"/docs/4/transports/sockjs","h":"#options","p":2272},{"i":2283,"t":"sockjs","u":"/docs/4/transports/sockjs","h":"#sockjs","p":2272},{"i":2285,"t":"sockjs_url","u":"/docs/4/transports/sockjs","h":"#sockjs_url","p":2272},{"i":2289,"t":"Bidirectional","u":"/docs/4/transports/overview","h":"#bidirectional","p":2287},{"i":2291,"t":"Unidirectional","u":"/docs/4/transports/overview","h":"#unidirectional","p":2287},{"i":2293,"t":"Unidirectional message types","u":"/docs/4/transports/overview","h":"#unidirectional-message-types","p":2287},{"i":2295,"t":"PING/PONG behavior","u":"/docs/4/transports/overview","h":"#pingpong-behavior","p":2287},{"i":2299,"t":"Options","u":"/docs/4/transports/sse","h":"#options","p":2297},{"i":2300,"t":"sse","u":"/docs/4/transports/sse","h":"#sse","p":2297},{"i":2302,"t":"sse_max_request_body_size","u":"/docs/4/transports/sse","h":"#sse_max_request_body_size","p":2297},{"i":2306,"t":"Supported data formats","u":"/docs/4/transports/uni_grpc","h":"#supported-data-formats","p":2304},{"i":2308,"t":"Options","u":"/docs/4/transports/uni_grpc","h":"#options","p":2304},{"i":2309,"t":"uni_grpc","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc","p":2304},{"i":2311,"t":"uni_grpc_port","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_port","p":2304},{"i":2313,"t":"uni_grpc_address","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_address","p":2304},{"i":2315,"t":"uni_grpc_max_receive_message_size","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":2304},{"i":2317,"t":"uni_grpc_tls","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls","p":2304},{"i":2319,"t":"uni_grpc_tls_cert","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":2304},{"i":2321,"t":"uni_grpc_tls_key","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_key","p":2304},{"i":2323,"t":"Example","u":"/docs/4/transports/uni_grpc","h":"#example","p":2304},{"i":2327,"t":"Connect command","u":"/docs/4/transports/uni_http_stream","h":"#connect-command","p":2325},{"i":2329,"t":"Supported data formats","u":"/docs/4/transports/uni_http_stream","h":"#supported-data-formats","p":2325},{"i":2331,"t":"Pings","u":"/docs/4/transports/uni_http_stream","h":"#pings","p":2325},{"i":2333,"t":"Options","u":"/docs/4/transports/uni_http_stream","h":"#options","p":2325},{"i":2334,"t":"uni_http_stream","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream","p":2325},{"i":2336,"t":"uni_http_stream_max_request_body_size","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":2325},{"i":2338,"t":"Connecting using CURL","u":"/docs/4/transports/uni_http_stream","h":"#connecting-using-curl","p":2325},{"i":2340,"t":"Browser example","u":"/docs/4/transports/uni_http_stream","h":"#browser-example","p":2325},{"i":2344,"t":"Client connection states","u":"/docs/4/transports/client_api","h":"#client-connection-states","p":2342},{"i":2346,"t":"Client common options","u":"/docs/4/transports/client_api","h":"#client-common-options","p":2342},{"i":2348,"t":"Client methods","u":"/docs/4/transports/client_api","h":"#client-methods","p":2342},{"i":2350,"t":"Client connection token","u":"/docs/4/transports/client_api","h":"#client-connection-token","p":2342},{"i":2352,"t":"Connection PING/PONG","u":"/docs/4/transports/client_api","h":"#connection-pingpong","p":2342},{"i":2354,"t":"Subscription states","u":"/docs/4/transports/client_api","h":"#subscription-states","p":2342},{"i":2356,"t":"Subscription management","u":"/docs/4/transports/client_api","h":"#subscription-management","p":2342},{"i":2358,"t":"Listen to channel publications","u":"/docs/4/transports/client_api","h":"#listen-to-channel-publications","p":2342},{"i":2360,"t":"Subscription recovery state","u":"/docs/4/transports/client_api","h":"#subscription-recovery-state","p":2342},{"i":2362,"t":"Subscription common options","u":"/docs/4/transports/client_api","h":"#subscription-common-options","p":2342},{"i":2364,"t":"Subscription methods","u":"/docs/4/transports/client_api","h":"#subscription-methods","p":2342},{"i":2366,"t":"Subscription token","u":"/docs/4/transports/client_api","h":"#subscription-token","p":2342},{"i":2368,"t":"Server-side subscriptions","u":"/docs/4/transports/client_api","h":"#server-side-subscriptions","p":2342},{"i":2370,"t":"Error codes","u":"/docs/4/transports/client_api","h":"#error-codes","p":2342},{"i":2372,"t":"Unsubscribe codes","u":"/docs/4/transports/client_api","h":"#unsubscribe-codes","p":2342},{"i":2374,"t":"Disconnect codes","u":"/docs/4/transports/client_api","h":"#disconnect-codes","p":2342},{"i":2376,"t":"RPC","u":"/docs/4/transports/client_api","h":"#rpc","p":2342},{"i":2378,"t":"Channel history API","u":"/docs/4/transports/client_api","h":"#channel-history-api","p":2342},{"i":2380,"t":"Presence and presence stats API","u":"/docs/4/transports/client_api","h":"#presence-and-presence-stats-api","p":2342},{"i":2382,"t":"SDK common best practices","u":"/docs/4/transports/client_api","h":"#sdk-common-best-practices","p":2342},{"i":2386,"t":"Connect command","u":"/docs/4/transports/uni_sse","h":"#connect-command","p":2384},{"i":2388,"t":"Supported data formats","u":"/docs/4/transports/uni_sse","h":"#supported-data-formats","p":2384},{"i":2390,"t":"Options","u":"/docs/4/transports/uni_sse","h":"#options","p":2384},{"i":2391,"t":"uni_sse","u":"/docs/4/transports/uni_sse","h":"#uni_sse","p":2384},{"i":2393,"t":"uni_sse_max_request_body_size","u":"/docs/4/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":2384},{"i":2395,"t":"Browser example","u":"/docs/4/transports/uni_sse","h":"#browser-example","p":2384},{"i":2399,"t":"Connect command","u":"/docs/4/transports/uni_websocket","h":"#connect-command","p":2397},{"i":2401,"t":"SubscribeRequest","u":"/docs/4/transports/uni_websocket","h":"#subscriberequest","p":2397},{"i":2403,"t":"Supported data formats","u":"/docs/4/transports/uni_websocket","h":"#supported-data-formats","p":2397},{"i":2405,"t":"Pings","u":"/docs/4/transports/uni_websocket","h":"#pings","p":2397},{"i":2407,"t":"Options","u":"/docs/4/transports/uni_websocket","h":"#options","p":2397},{"i":2408,"t":"uni_websocket","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket","p":2397},{"i":2410,"t":"uni_websocket_message_size_limit","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":2397},{"i":2412,"t":"Example","u":"/docs/4/transports/uni_websocket","h":"#example","p":2397},{"i":2416,"t":"Landing Page Images","u":"/docs/attributions","h":"#landing-page-images","p":2414},{"i":2420,"t":"Options","u":"/docs/4/transports/websocket","h":"#options","p":2418},{"i":2421,"t":"websocket_message_size_limit","u":"/docs/4/transports/websocket","h":"#websocket_message_size_limit","p":2418},{"i":2423,"t":"websocket_read_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_read_buffer_size","p":2418},{"i":2425,"t":"websocket_write_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_write_buffer_size","p":2418},{"i":2427,"t":"websocket_use_write_buffer_pool","u":"/docs/4/transports/websocket","h":"#websocket_use_write_buffer_pool","p":2418},{"i":2429,"t":"websocket_compression","u":"/docs/4/transports/websocket","h":"#websocket_compression","p":2418},{"i":2431,"t":"Protobuf binary protocol","u":"/docs/4/transports/websocket","h":"#protobuf-binary-protocol","p":2418},{"i":2439,"t":"How many connections can one Centrifugo instance handle?","u":"/docs/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":2437},{"i":2441,"t":"Memory usage per connection?","u":"/docs/faq","h":"#memory-usage-per-connection","p":2437},{"i":2443,"t":"Can Centrifugo scale horizontally?","u":"/docs/faq","h":"#can-centrifugo-scale-horizontally","p":2437},{"i":2445,"t":"Message delivery model","u":"/docs/faq","h":"#message-delivery-model","p":2437},{"i":2447,"t":"Message order guarantees","u":"/docs/faq","h":"#message-order-guarantees","p":2437},{"i":2449,"t":"Should I create channels explicitly?","u":"/docs/faq","h":"#should-i-create-channels-explicitly","p":2437},{"i":2451,"t":"What about best practices with the number of channels?","u":"/docs/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":2437},{"i":2453,"t":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":2437},{"i":2455,"t":"Can I have both binary and JSON clients in one channel?","u":"/docs/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":2437},{"i":2457,"t":"Online presence for chat apps - online status of your contacts","u":"/docs/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":2437},{"i":2459,"t":"Centrifugo stops accepting new connections, why?","u":"/docs/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":2437},{"i":2461,"t":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":2437},{"i":2463,"t":"Does Centrifugo work with HTTP/2?","u":"/docs/faq","h":"#does-centrifugo-work-with-http2","p":2437},{"i":2465,"t":"Does Centrifugo work with HTTP/3?","u":"/docs/faq","h":"#does-centrifugo-work-with-http3","p":2437},{"i":2467,"t":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":2437},{"i":2469,"t":"What if I need to send push notifications to mobile or web applications?","u":"/docs/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":2437},{"i":2471,"t":"How can I know a message is delivered to a client?","u":"/docs/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":2437},{"i":2473,"t":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":2437},{"i":2475,"t":"How to create a secure channel for two users only (private chat case)?","u":"/docs/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":2437},{"i":2477,"t":"What's the best way to organize channel configuration?","u":"/docs/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":2437},{"i":2479,"t":"Does Centrifugo support webhooks?","u":"/docs/faq","h":"#does-centrifugo-support-webhooks","p":2437},{"i":2481,"t":"Why Centrifugo does not have disconnect hooks?","u":"/docs/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":2437},{"i":2483,"t":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":2437},{"i":2485,"t":"How scalable is the online presence and join/leave features?","u":"/docs/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":2437},{"i":2487,"t":"How to send initial data to channel subscriber?","u":"/docs/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":2437},{"i":2489,"t":"Does Centrifugo support multitenancy?","u":"/docs/faq","h":"#does-centrifugo-support-multitenancy","p":2437},{"i":2491,"t":"I have not found an answer to my question here","u":"/docs/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":2437},{"i":2497,"t":"Centrifugo vs Socket.io","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-socketio","p":2495},{"i":2499,"t":"Centrifugo vs Pusher, Ably, Pubnub","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-pusher-ably-pubnub","p":2495},{"i":2501,"t":"Centrifugo vs Redis","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-redis","p":2495},{"i":2503,"t":"Centrifugo vs Kafka","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-kafka","p":2495},{"i":2505,"t":"Centrifugo vs Nats","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-nats","p":2495},{"i":2509,"t":"Connecting to a server","u":"/docs/getting-started/client_api","h":"#connecting-to-a-server","p":2507},{"i":2511,"t":"Disconnecting from a server","u":"/docs/getting-started/client_api","h":"#disconnecting-from-a-server","p":2507},{"i":2513,"t":"Reconnecting to a server","u":"/docs/getting-started/client_api","h":"#reconnecting-to-a-server","p":2507},{"i":2515,"t":"Connection lifecycle events","u":"/docs/getting-started/client_api","h":"#connection-lifecycle-events","p":2507},{"i":2517,"t":"Subscribe to a channel","u":"/docs/getting-started/client_api","h":"#subscribe-to-a-channel","p":2507},{"i":2519,"t":"Server-side subscriptions","u":"/docs/getting-started/client_api","h":"#server-side-subscriptions","p":2507},{"i":2521,"t":"Send RPC","u":"/docs/getting-started/client_api","h":"#send-rpc","p":2507},{"i":2523,"t":"Call channel history","u":"/docs/getting-started/client_api","h":"#call-channel-history","p":2507},{"i":2525,"t":"Presence and presence stats","u":"/docs/getting-started/client_api","h":"#presence-and-presence-stats","p":2507},{"i":2529,"t":"Idiomatic usage","u":"/docs/getting-started/design","h":"#idiomatic-usage","p":2527},{"i":2531,"t":"Message history considerations","u":"/docs/getting-started/design","h":"#message-history-considerations","p":2527},{"i":2533,"t":"Message delivery model","u":"/docs/getting-started/design","h":"#message-delivery-model","p":2527},{"i":2535,"t":"Message order guarantees","u":"/docs/getting-started/design","h":"#message-order-guarantees","p":2527},{"i":2537,"t":"Graceful degradation","u":"/docs/getting-started/design","h":"#graceful-degradation","p":2527},{"i":2539,"t":"Online presence considerations","u":"/docs/getting-started/design","h":"#online-presence-considerations","p":2527},{"i":2541,"t":"Scalability considerations","u":"/docs/getting-started/design","h":"#scalability-considerations","p":2527},{"i":2545,"t":"Centrifuge library for Go","u":"/docs/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":2543},{"i":2547,"t":"Framework integrations","u":"/docs/getting-started/ecosystem","h":"#framework-integrations","p":2543},{"i":2551,"t":"Simple integration","u":"/docs/getting-started/highlights","h":"#simple-integration","p":2549},{"i":2553,"t":"Great performance","u":"/docs/getting-started/highlights","h":"#great-performance","p":2549},{"i":2555,"t":"Built-in scalability","u":"/docs/getting-started/highlights","h":"#built-in-scalability","p":2549},{"i":2557,"t":"Strict client protocol","u":"/docs/getting-started/highlights","h":"#strict-client-protocol","p":2549},{"i":2559,"t":"Variety of real-time transports","u":"/docs/getting-started/highlights","h":"#variety-of-real-time-transports","p":2549},{"i":2561,"t":"Flexible authentication","u":"/docs/getting-started/highlights","h":"#flexible-authentication","p":2549},{"i":2563,"t":"Connection management","u":"/docs/getting-started/highlights","h":"#connection-management","p":2549},{"i":2565,"t":"Channel (room) concept","u":"/docs/getting-started/highlights","h":"#channel-room-concept","p":2549},{"i":2567,"t":"Different types of subscriptions","u":"/docs/getting-started/highlights","h":"#different-types-of-subscriptions","p":2549},{"i":2569,"t":"RPC over bidirectional connection","u":"/docs/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":2549},{"i":2571,"t":"Online presence information","u":"/docs/getting-started/highlights","h":"#online-presence-information","p":2549},{"i":2573,"t":"Message history in channels","u":"/docs/getting-started/highlights","h":"#message-history-in-channels","p":2549},{"i":2575,"t":"Embedded admin web UI","u":"/docs/getting-started/highlights","h":"#embedded-admin-web-ui","p":2549},{"i":2577,"t":"Cross-platform","u":"/docs/getting-started/highlights","h":"#cross-platform","p":2549},{"i":2579,"t":"Ready to deploy","u":"/docs/getting-started/highlights","h":"#ready-to-deploy","p":2549},{"i":2581,"t":"Open-source","u":"/docs/getting-started/highlights","h":"#open-source","p":2549},{"i":2583,"t":"PRO features","u":"/docs/getting-started/highlights","h":"#pro-features","p":2549},{"i":2587,"t":"0. Install","u":"/docs/getting-started/integration","h":"#0-install","p":2585},{"i":2589,"t":"1. Configure Centrifugo","u":"/docs/getting-started/integration","h":"#1-configure-centrifugo","p":2585},{"i":2591,"t":"2. Configure your backend","u":"/docs/getting-started/integration","h":"#2-configure-your-backend","p":2585},{"i":2593,"t":"3. Connect to Centrifugo","u":"/docs/getting-started/integration","h":"#3-connect-to-centrifugo","p":2585},{"i":2595,"t":"4. Subscribe to channels","u":"/docs/getting-started/integration","h":"#4-subscribe-to-channels","p":2585},{"i":2597,"t":"5. Publish to a channel","u":"/docs/getting-started/integration","h":"#5-publish-to-a-channel","p":2585},{"i":2599,"t":"6. Deploy to production","u":"/docs/getting-started/integration","h":"#6-deploy-to-production","p":2585},{"i":2601,"t":"7. Monitor Centrifugo","u":"/docs/getting-started/integration","h":"#7-monitor-centrifugo","p":2585},{"i":2603,"t":"8. Scale Centrifugo","u":"/docs/getting-started/integration","h":"#8-scale-centrifugo","p":2585},{"i":2605,"t":"9. Read FAQ","u":"/docs/getting-started/integration","h":"#9-read-faq","p":2585},{"i":2609,"t":"Background","u":"/docs/getting-started/introduction","h":"#background","p":2607},{"i":2613,"t":"Install from the binary release","u":"/docs/getting-started/installation","h":"#install-from-the-binary-release","p":2611},{"i":2615,"t":"Docker image","u":"/docs/getting-started/installation","h":"#docker-image","p":2611},{"i":2617,"t":"Docker-compose example","u":"/docs/getting-started/installation","h":"#docker-compose-example","p":2611},{"i":2619,"t":"Kubernetes Helm chart","u":"/docs/getting-started/installation","h":"#kubernetes-helm-chart","p":2611},{"i":2621,"t":"RPM and DEB packages for Linux","u":"/docs/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":2611},{"i":2623,"t":"With brew on macOS","u":"/docs/getting-started/installation","h":"#with-brew-on-macos","p":2611},{"i":2625,"t":"Build from source","u":"/docs/getting-started/installation","h":"#build-from-source","p":2611},{"i":2629,"t":"Client SDK migration","u":"/docs/getting-started/migration_v4","h":"#client-sdk-migration","p":2627},{"i":2631,"t":"Unidirectional transport migration","u":"/docs/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":2627},{"i":2633,"t":"SockJS migration","u":"/docs/getting-started/migration_v4","h":"#sockjs-migration","p":2627},{"i":2635,"t":"Channel ASCII enforced","u":"/docs/getting-started/migration_v4","h":"#channel-ascii-enforced","p":2627},{"i":2637,"t":"Subscription token migration","u":"/docs/getting-started/migration_v4","h":"#subscription-token-migration","p":2627},{"i":2639,"t":"User-limited channel migration","u":"/docs/getting-started/migration_v4","h":"#user-limited-channel-migration","p":2627},{"i":2641,"t":"Namespace configuration migration","u":"/docs/getting-started/migration_v4","h":"#namespace-configuration-migration","p":2627},{"i":2643,"t":"Proxy disconnect code changes","u":"/docs/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":2627},{"i":2645,"t":"Other configuration option changes","u":"/docs/getting-started/migration_v4","h":"#other-configuration-option-changes","p":2627},{"i":2647,"t":"Server API changes","u":"/docs/getting-started/migration_v4","h":"#server-api-changes","p":2627},{"i":2651,"t":"Client SDK token behaviour adjustments","u":"/docs/getting-started/migration_v5","h":"#client-sdk-token-behaviour-adjustments","p":2649},{"i":2653,"t":"Node communication format changed","u":"/docs/getting-started/migration_v5","h":"#node-communication-format-changed","p":2649},{"i":2655,"t":"Old HTTP API format is DEPRECATED","u":"/docs/getting-started/migration_v5","h":"#old-http-api-format-is-deprecated","p":2649},{"i":2657,"t":"Other changes","u":"/docs/getting-started/migration_v5","h":"#other-changes","p":2649},{"i":2659,"t":"Shutting down Centrifugo v2 doc site","u":"/docs/getting-started/migration_v5","h":"#shutting-down-centrifugo-v2-doc-site","p":2649},{"i":2665,"t":"Connection capabilities","u":"/docs/pro/capabilities","h":"#connection-capabilities","p":2663},{"i":2667,"t":"Caps processing behavior","u":"/docs/pro/capabilities","h":"#caps-processing-behavior","p":2663},{"i":2669,"t":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations","p":2663},{"i":2671,"t":"Revoking connection caps","u":"/docs/pro/capabilities","h":"#revoking-connection-caps","p":2663},{"i":2673,"t":"Example: wildcard match","u":"/docs/pro/capabilities","h":"#example-wildcard-match","p":2663},{"i":2675,"t":"Example: regex match","u":"/docs/pro/capabilities","h":"#example-regex-match","p":2663},{"i":2677,"t":"Example: different types of match","u":"/docs/pro/capabilities","h":"#example-different-types-of-match","p":2663},{"i":2679,"t":"Example: full access to all channels","u":"/docs/pro/capabilities","h":"#example-full-access-to-all-channels","p":2663},{"i":2681,"t":"Subscription capabilities","u":"/docs/pro/capabilities","h":"#subscription-capabilities","p":2663},{"i":2683,"t":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations-1","p":2663},{"i":2685,"t":"Revoking subscription permissions","u":"/docs/pro/capabilities","h":"#revoking-subscription-permissions","p":2663},{"i":2689,"t":"subscribe_cel","u":"/docs/pro/cel_expressions","h":"#subscribe_cel","p":2687},{"i":2691,"t":"Expression variables","u":"/docs/pro/cel_expressions","h":"#expression-variables","p":2687},{"i":2693,"t":"publish_cel","u":"/docs/pro/cel_expressions","h":"#publish_cel","p":2687},{"i":2695,"t":"history_cel","u":"/docs/pro/cel_expressions","h":"#history_cel","p":2687},{"i":2697,"t":"presence_cel","u":"/docs/pro/cel_expressions","h":"#presence_cel","p":2687},{"i":2701,"t":"Configuration","u":"/docs/pro/analytics","h":"#configuration","p":2699},{"i":2703,"t":"Connections table","u":"/docs/pro/analytics","h":"#connections-table","p":2699},{"i":2705,"t":"Subscriptions table","u":"/docs/pro/analytics","h":"#subscriptions-table","p":2699},{"i":2707,"t":"Operations table","u":"/docs/pro/analytics","h":"#operations-table","p":2699},{"i":2709,"t":"Publications table","u":"/docs/pro/analytics","h":"#publications-table","p":2699},{"i":2711,"t":"Notifications table","u":"/docs/pro/analytics","h":"#notifications-table","p":2699},{"i":2713,"t":"Query examples","u":"/docs/pro/analytics","h":"#query-examples","p":2699},{"i":2715,"t":"Development","u":"/docs/pro/analytics","h":"#development","p":2699},{"i":2717,"t":"How export works","u":"/docs/pro/analytics","h":"#how-export-works","p":2699},{"i":2719,"t":"Exposed metrics","u":"/docs/pro/analytics","h":"#exposed-metrics","p":2699},{"i":2725,"t":"Configuration","u":"/docs/pro/channel_patterns","h":"#configuration","p":2723},{"i":2727,"t":"Implementation details","u":"/docs/pro/channel_patterns","h":"#implementation-details","p":2723},{"i":2729,"t":"Variables","u":"/docs/pro/channel_patterns","h":"#variables","p":2723},{"i":2731,"t":"Using varibles","u":"/docs/pro/channel_patterns","h":"#using-varibles","p":2723},{"i":2735,"t":"client_write_delay","u":"/docs/pro/client_message_batching","h":"#client_write_delay","p":2733},{"i":2737,"t":"client_reply_without_queue","u":"/docs/pro/client_message_batching","h":"#client_reply_without_queue","p":2733},{"i":2739,"t":"client_max_messages_in_frame","u":"/docs/pro/client_message_batching","h":"#client_max_messages_in_frame","p":2733},{"i":2743,"t":"Overview","u":"/docs/pro/distributed_rate_limit","h":"#overview","p":2741},{"i":2745,"t":"Configuration","u":"/docs/pro/distributed_rate_limit","h":"#configuration","p":2741},{"i":2747,"t":"API description","u":"/docs/pro/distributed_rate_limit","h":"#api-description","p":2741},{"i":2749,"t":"rate_limit request","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-request","p":2741},{"i":2751,"t":"rate_limit result","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-result","p":2741},{"i":2755,"t":"Binary release","u":"/docs/pro/install_and_run","h":"#binary-release","p":2753},{"i":2757,"t":"Docker image","u":"/docs/pro/install_and_run","h":"#docker-image","p":2753},{"i":2759,"t":"Kubernetes","u":"/docs/pro/install_and_run","h":"#kubernetes","p":2753},{"i":2761,"t":"Debian and Ubuntu","u":"/docs/pro/install_and_run","h":"#debian-and-ubuntu","p":2753},{"i":2763,"t":"Centos","u":"/docs/pro/install_and_run","h":"#centos","p":2753},{"i":2765,"t":"Setting PRO license key","u":"/docs/pro/install_and_run","h":"#setting-pro-license-key","p":2753},{"i":2769,"t":"Example","u":"/docs/pro/connections","h":"#example","p":2767},{"i":2771,"t":"connections","u":"/docs/pro/connections","h":"#connections","p":2767},{"i":2777,"t":"Features","u":"/docs/pro/overview","h":"#features","p":2775},{"i":2779,"t":"Pricing","u":"/docs/pro/overview","h":"#pricing","p":2775},{"i":2781,"t":"Try for free in sandbox mode","u":"/docs/pro/overview","h":"#try-for-free-in-sandbox-mode","p":2775},{"i":2785,"t":"Faster HTTP API","u":"/docs/pro/performance","h":"#faster-http-api","p":2783},{"i":2787,"t":"Faster GRPC API","u":"/docs/pro/performance","h":"#faster-grpc-api","p":2783},{"i":2789,"t":"Faster HTTP proxy","u":"/docs/pro/performance","h":"#faster-http-proxy","p":2783},{"i":2791,"t":"Faster GRPC proxy","u":"/docs/pro/performance","h":"#faster-grpc-proxy","p":2783},{"i":2793,"t":"Faster JWT decoding","u":"/docs/pro/performance","h":"#faster-jwt-decoding","p":2783},{"i":2795,"t":"Faster GRPC unidirectional stream","u":"/docs/pro/performance","h":"#faster-grpc-unidirectional-stream","p":2783},{"i":2797,"t":"Examples","u":"/docs/pro/performance","h":"#examples","p":2783},{"i":2799,"t":"Publish HTTP API","u":"/docs/pro/performance","h":"#publish-http-api","p":2783},{"i":2801,"t":"History HTTP API","u":"/docs/pro/performance","h":"#history-http-api","p":2783},{"i":2809,"t":"Save to a file","u":"/docs/pro/tracing","h":"#save-to-a-file","p":2807},{"i":2813,"t":"How it works","u":"/docs/pro/token_revocation","h":"#how-it-works","p":2811},{"i":2815,"t":"Configure","u":"/docs/pro/token_revocation","h":"#configure","p":2811},{"i":2817,"t":"Redis persistence engine","u":"/docs/pro/token_revocation","h":"#redis-persistence-engine","p":2811},{"i":2819,"t":"Database persistence engine","u":"/docs/pro/token_revocation","h":"#database-persistence-engine","p":2811},{"i":2821,"t":"Revoke token API","u":"/docs/pro/token_revocation","h":"#revoke-token-api","p":2811},{"i":2822,"t":"revoke_token","u":"/docs/pro/token_revocation","h":"#revoke_token","p":2811},{"i":2824,"t":"Invalidate user tokens API","u":"/docs/pro/token_revocation","h":"#invalidate-user-tokens-api","p":2811},{"i":2825,"t":"invalidate_user_tokens","u":"/docs/pro/token_revocation","h":"#invalidate_user_tokens","p":2811},{"i":2829,"t":"In-memory per connection rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-connection-rate-limit","p":2827},{"i":2831,"t":"In-memory per user rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-user-rate-limit","p":2827},{"i":2833,"t":"Redis per user rate limit","u":"/docs/pro/rate_limiting","h":"#redis-per-user-rate-limit","p":2827},{"i":2835,"t":"Disconnecting abusive or misbehaving connections","u":"/docs/pro/rate_limiting","h":"#disconnecting-abusive-or-misbehaving-connections","p":2827},{"i":2839,"t":"Motivation and design choices","u":"/docs/pro/push_notifications","h":"#motivation-and-design-choices","p":2837},{"i":2841,"t":"Storage for tokens","u":"/docs/pro/push_notifications","h":"#storage-for-tokens","p":2837},{"i":2843,"t":"Efficient queuing","u":"/docs/pro/push_notifications","h":"#efficient-queuing","p":2837},{"i":2845,"t":"Unified secure topics","u":"/docs/pro/push_notifications","h":"#unified-secure-topics","p":2837},{"i":2847,"t":"Non-obtrusive proxying","u":"/docs/pro/push_notifications","h":"#non-obtrusive-proxying","p":2837},{"i":2849,"t":"Builtin analytics","u":"/docs/pro/push_notifications","h":"#builtin-analytics","p":2837},{"i":2851,"t":"Steps to integrate","u":"/docs/pro/push_notifications","h":"#steps-to-integrate","p":2837},{"i":2853,"t":"Configuration","u":"/docs/pro/push_notifications","h":"#configuration","p":2837},{"i":2855,"t":"FCM","u":"/docs/pro/push_notifications","h":"#fcm","p":2837},{"i":2857,"t":"HMS","u":"/docs/pro/push_notifications","h":"#hms","p":2837},{"i":2859,"t":"APNs","u":"/docs/pro/push_notifications","h":"#apns","p":2837},{"i":2861,"t":"Other options","u":"/docs/pro/push_notifications","h":"#other-options","p":2837},{"i":2863,"t":"Use PostgreSQL as queue","u":"/docs/pro/push_notifications","h":"#use-postgresql-as-queue","p":2837},{"i":2865,"t":"API description","u":"/docs/pro/push_notifications","h":"#api-description","p":2837},{"i":2866,"t":"device_register","u":"/docs/pro/push_notifications","h":"#device_register","p":2837},{"i":2868,"t":"device_update","u":"/docs/pro/push_notifications","h":"#device_update","p":2837},{"i":2870,"t":"device_remove","u":"/docs/pro/push_notifications","h":"#device_remove","p":2837},{"i":2872,"t":"device_list","u":"/docs/pro/push_notifications","h":"#device_list","p":2837},{"i":2874,"t":"device_topic_update","u":"/docs/pro/push_notifications","h":"#device_topic_update","p":2837},{"i":2876,"t":"device_topic_list","u":"/docs/pro/push_notifications","h":"#device_topic_list","p":2837},{"i":2878,"t":"user_topic_update","u":"/docs/pro/push_notifications","h":"#user_topic_update","p":2837},{"i":2880,"t":"user_topic_list","u":"/docs/pro/push_notifications","h":"#user_topic_list","p":2837},{"i":2882,"t":"send_push_notification","u":"/docs/pro/push_notifications","h":"#send_push_notification","p":2837},{"i":2884,"t":"cancel_push","u":"/docs/pro/push_notifications","h":"#cancel_push","p":2837},{"i":2886,"t":"update_push_status","u":"/docs/pro/push_notifications","h":"#update_push_status","p":2837},{"i":2888,"t":"Exposed metrics","u":"/docs/pro/push_notifications","h":"#exposed-metrics","p":2837},{"i":2890,"t":"Further reading and tutorials","u":"/docs/pro/push_notifications","h":"#further-reading-and-tutorials","p":2837},{"i":2894,"t":"How it works","u":"/docs/pro/user_block","h":"#how-it-works","p":2892},{"i":2896,"t":"Configure","u":"/docs/pro/user_block","h":"#configure","p":2892},{"i":2898,"t":"Redis persistence engine","u":"/docs/pro/user_block","h":"#redis-persistence-engine","p":2892},{"i":2900,"t":"Database persistence engine","u":"/docs/pro/user_block","h":"#database-persistence-engine","p":2892},{"i":2902,"t":"Block API","u":"/docs/pro/user_block","h":"#block--api","p":2892},{"i":2903,"t":"block_user","u":"/docs/pro/user_block","h":"#block_user","p":2892},{"i":2905,"t":"unblock_user","u":"/docs/pro/user_block","h":"#unblock_user","p":2892},{"i":2909,"t":"Options","u":"/docs/server/admin_web","h":"#options","p":2907},{"i":2911,"t":"Using custom web interface","u":"/docs/server/admin_web","h":"#using-custom-web-interface","p":2907},{"i":2913,"t":"Admin insecure mode","u":"/docs/server/admin_web","h":"#admin-insecure-mode","p":2907},{"i":2917,"t":"Client-side status update RPC","u":"/docs/pro/user_status","h":"#client-side-status-update-rpc","p":2915},{"i":2919,"t":"update_user_status server API","u":"/docs/pro/user_status","h":"#update_user_status-server-api","p":2915},{"i":2921,"t":"get_user_status server API","u":"/docs/pro/user_status","h":"#get_user_status-server-api","p":2915},{"i":2923,"t":"delete_user_status server API","u":"/docs/pro/user_status","h":"#delete_user_status-server-api","p":2915},{"i":2925,"t":"Configuration","u":"/docs/pro/user_status","h":"#configuration","p":2915},{"i":2929,"t":"Subscribe permission model","u":"/docs/server/channel_permissions","h":"#subscribe-permission-model","p":2927},{"i":2931,"t":"Publish permission model","u":"/docs/server/channel_permissions","h":"#publish-permission-model","p":2927},{"i":2933,"t":"History permission model","u":"/docs/server/channel_permissions","h":"#history-permission-model","p":2927},{"i":2935,"t":"Presence permission model","u":"/docs/server/channel_permissions","h":"#presence-permission-model","p":2927},{"i":2937,"t":"Positioning permission model","u":"/docs/server/channel_permissions","h":"#positioning-permission-model","p":2927},{"i":2939,"t":"Recovery permission model","u":"/docs/server/channel_permissions","h":"#recovery-permission-model","p":2927},{"i":2941,"t":"Join/Leave permission model","u":"/docs/server/channel_permissions","h":"#joinleave-permission-model","p":2927},{"i":2945,"t":"Connection JWT Claims","u":"/docs/server/authentication","h":"#connection-jwt-claims","p":2943},{"i":2947,"t":"sub","u":"/docs/server/authentication","h":"#sub","p":2943},{"i":2949,"t":"exp","u":"/docs/server/authentication","h":"#exp","p":2943},{"i":2951,"t":"iat","u":"/docs/server/authentication","h":"#iat","p":2943},{"i":2953,"t":"jti","u":"/docs/server/authentication","h":"#jti","p":2943},{"i":2955,"t":"aud","u":"/docs/server/authentication","h":"#aud","p":2943},{"i":2957,"t":"iss","u":"/docs/server/authentication","h":"#iss","p":2943},{"i":2959,"t":"info","u":"/docs/server/authentication","h":"#info","p":2943},{"i":2961,"t":"b64info","u":"/docs/server/authentication","h":"#b64info","p":2943},{"i":2963,"t":"channels","u":"/docs/server/authentication","h":"#channels","p":2943},{"i":2965,"t":"subs","u":"/docs/server/authentication","h":"#subs","p":2943},{"i":2967,"t":"meta","u":"/docs/server/authentication","h":"#meta","p":2943},{"i":2969,"t":"expire_at","u":"/docs/server/authentication","h":"#expire_at","p":2943},{"i":2971,"t":"Connection expiration","u":"/docs/server/authentication","h":"#connection-expiration","p":2943},{"i":2973,"t":"Examples","u":"/docs/server/authentication","h":"#examples","p":2943},{"i":2975,"t":"Simplest token","u":"/docs/server/authentication","h":"#simplest-token","p":2943},{"i":2977,"t":"Token with expiration","u":"/docs/server/authentication","h":"#token-with-expiration","p":2943},{"i":2979,"t":"Token with additional connection info","u":"/docs/server/authentication","h":"#token-with-additional-connection-info","p":2943},{"i":2981,"t":"Investigating problems with JWT","u":"/docs/server/authentication","h":"#investigating-problems-with-jwt","p":2943},{"i":2983,"t":"JSON Web Key support","u":"/docs/server/authentication","h":"#json-web-key-support","p":2943},{"i":2985,"t":"Dynamic JWKs endpoint","u":"/docs/server/authentication","h":"#dynamic-jwks-endpoint","p":2943},{"i":2989,"t":"Subscription JWT claims","u":"/docs/server/channel_token_auth","h":"#subscription-jwt-claims","p":2987},{"i":2991,"t":"sub","u":"/docs/server/channel_token_auth","h":"#sub","p":2987},{"i":2993,"t":"channel","u":"/docs/server/channel_token_auth","h":"#channel","p":2987},{"i":2995,"t":"info","u":"/docs/server/channel_token_auth","h":"#info","p":2987},{"i":2997,"t":"b64info","u":"/docs/server/channel_token_auth","h":"#b64info","p":2987},{"i":2999,"t":"exp","u":"/docs/server/channel_token_auth","h":"#exp","p":2987},{"i":3001,"t":"expire_at","u":"/docs/server/channel_token_auth","h":"#expire_at","p":2987},{"i":3003,"t":"aud","u":"/docs/server/channel_token_auth","h":"#aud","p":2987},{"i":3005,"t":"iss","u":"/docs/server/channel_token_auth","h":"#iss","p":2987},{"i":3007,"t":"iat","u":"/docs/server/channel_token_auth","h":"#iat","p":2987},{"i":3009,"t":"jti","u":"/docs/server/channel_token_auth","h":"#jti","p":2987},{"i":3011,"t":"override","u":"/docs/server/channel_token_auth","h":"#override","p":2987},{"i":3013,"t":"Example","u":"/docs/server/channel_token_auth","h":"#example","p":2987},{"i":3015,"t":"gensubtoken cli command","u":"/docs/server/channel_token_auth","h":"#gensubtoken-cli-command","p":2987},{"i":3017,"t":"Separate subscription token config","u":"/docs/server/channel_token_auth","h":"#separate-subscription-token-config","p":2987},{"i":3021,"t":"Client error codes","u":"/docs/server/codes","h":"#client-error-codes","p":3019},{"i":3023,"t":"Internal","u":"/docs/server/codes","h":"#internal","p":3019},{"i":3025,"t":"Unauthorized","u":"/docs/server/codes","h":"#unauthorized","p":3019},{"i":3027,"t":"Unknown Channel","u":"/docs/server/codes","h":"#unknown-channel","p":3019},{"i":3029,"t":"Permission Denied","u":"/docs/server/codes","h":"#permission-denied","p":3019},{"i":3031,"t":"Method Not Found","u":"/docs/server/codes","h":"#method-not-found","p":3019},{"i":3033,"t":"Already Subscribed","u":"/docs/server/codes","h":"#already-subscribed","p":3019},{"i":3035,"t":"Limit Exceeded","u":"/docs/server/codes","h":"#limit-exceeded","p":3019},{"i":3037,"t":"Bad Request","u":"/docs/server/codes","h":"#bad-request","p":3019},{"i":3039,"t":"Not Available","u":"/docs/server/codes","h":"#not-available","p":3019},{"i":3041,"t":"Token Expired","u":"/docs/server/codes","h":"#token-expired","p":3019},{"i":3043,"t":"Expired","u":"/docs/server/codes","h":"#expired","p":3019},{"i":3045,"t":"Too Many Requests","u":"/docs/server/codes","h":"#too-many-requests","p":3019},{"i":3047,"t":"Unrecoverable Position","u":"/docs/server/codes","h":"#unrecoverable-position","p":3019},{"i":3049,"t":"Client disconnect codes","u":"/docs/server/codes","h":"#client-disconnect-codes","p":3019},{"i":3051,"t":"DisconnectConnectionClosed","u":"/docs/server/codes","h":"#disconnectconnectionclosed","p":3019},{"i":3053,"t":"Non-terminal disconnect codes","u":"/docs/server/codes","h":"#non-terminal-disconnect-codes","p":3019},{"i":3055,"t":"Terminal disconnect codes","u":"/docs/server/codes","h":"#terminal-disconnect-codes","p":3019},{"i":3059,"t":"What is a channel","u":"/docs/server/channels","h":"#what-is-a-channel","p":3057},{"i":3061,"t":"Channel name rules","u":"/docs/server/channels","h":"#channel-name-rules","p":3057},{"i":3063,"t":"namespace boundary (:)","u":"/docs/server/channels","h":"#namespace-boundary-","p":3057},{"i":3065,"t":"user channel boundary (#)","u":"/docs/server/channels","h":"#user-channel-boundary-","p":3057},{"i":3067,"t":"private channel prefix ($)","u":"/docs/server/channels","h":"#private-channel-prefix-","p":3057},{"i":3069,"t":"Channel is just a string","u":"/docs/server/channels","h":"#channel-is-just-a-string","p":3057},{"i":3071,"t":"Channel namespaces","u":"/docs/server/channels","h":"#channel-namespaces","p":3057},{"i":3073,"t":"Channel options","u":"/docs/server/channels","h":"#channel-options","p":3057},{"i":3075,"t":"presence","u":"/docs/server/channels","h":"#presence","p":3057},{"i":3077,"t":"join_leave","u":"/docs/server/channels","h":"#join_leave","p":3057},{"i":3079,"t":"force_push_join_leave","u":"/docs/server/channels","h":"#force_push_join_leave","p":3057},{"i":3081,"t":"history_size","u":"/docs/server/channels","h":"#history_size","p":3057},{"i":3083,"t":"history_ttl","u":"/docs/server/channels","h":"#history_ttl","p":3057},{"i":3085,"t":"history_meta_ttl","u":"/docs/server/channels","h":"#history_meta_ttl","p":3057},{"i":3087,"t":"force_positioning","u":"/docs/server/channels","h":"#force_positioning","p":3057},{"i":3089,"t":"force_recovery","u":"/docs/server/channels","h":"#force_recovery","p":3057},{"i":3091,"t":"allow_subscribe_for_client","u":"/docs/server/channels","h":"#allow_subscribe_for_client","p":3057},{"i":3093,"t":"allow_subscribe_for_anonymous","u":"/docs/server/channels","h":"#allow_subscribe_for_anonymous","p":3057},{"i":3095,"t":"allow_publish_for_subscriber","u":"/docs/server/channels","h":"#allow_publish_for_subscriber","p":3057},{"i":3097,"t":"allow_publish_for_client","u":"/docs/server/channels","h":"#allow_publish_for_client","p":3057},{"i":3099,"t":"allow_publish_for_anonymous","u":"/docs/server/channels","h":"#allow_publish_for_anonymous","p":3057},{"i":3101,"t":"allow_history_for_subscriber","u":"/docs/server/channels","h":"#allow_history_for_subscriber","p":3057},{"i":3103,"t":"allow_history_for_client","u":"/docs/server/channels","h":"#allow_history_for_client","p":3057},{"i":3105,"t":"allow_history_for_anonymous","u":"/docs/server/channels","h":"#allow_history_for_anonymous","p":3057},{"i":3107,"t":"allow_presence_for_subscriber","u":"/docs/server/channels","h":"#allow_presence_for_subscriber","p":3057},{"i":3109,"t":"allow_presence_for_client","u":"/docs/server/channels","h":"#allow_presence_for_client","p":3057},{"i":3111,"t":"allow_presence_for_anonymous","u":"/docs/server/channels","h":"#allow_presence_for_anonymous","p":3057},{"i":3113,"t":"allow_user_limited_channels","u":"/docs/server/channels","h":"#allow_user_limited_channels","p":3057},{"i":3115,"t":"channel_regex","u":"/docs/server/channels","h":"#channel_regex","p":3057},{"i":3117,"t":"proxy_subscribe","u":"/docs/server/channels","h":"#proxy_subscribe","p":3057},{"i":3119,"t":"proxy_publish","u":"/docs/server/channels","h":"#proxy_publish","p":3057},{"i":3121,"t":"proxy_sub_refresh","u":"/docs/server/channels","h":"#proxy_sub_refresh","p":3057},{"i":3123,"t":"proxy_subscribe_stream","u":"/docs/server/channels","h":"#proxy_subscribe_stream","p":3057},{"i":3125,"t":"subscribe_proxy_name","u":"/docs/server/channels","h":"#subscribe_proxy_name","p":3057},{"i":3127,"t":"publish_proxy_name","u":"/docs/server/channels","h":"#publish_proxy_name","p":3057},{"i":3129,"t":"sub_refresh_proxy_name","u":"/docs/server/channels","h":"#sub_refresh_proxy_name","p":3057},{"i":3131,"t":"subscribe_stream_proxy_name","u":"/docs/server/channels","h":"#subscribe_stream_proxy_name","p":3057},{"i":3133,"t":"Channel config examples","u":"/docs/server/channels","h":"#channel-config-examples","p":3057},{"i":3137,"t":"version","u":"/docs/server/console_commands","h":"#version","p":3135},{"i":3139,"t":"genconfig","u":"/docs/server/console_commands","h":"#genconfig","p":3135},{"i":3141,"t":"checkconfig","u":"/docs/server/console_commands","h":"#checkconfig","p":3135},{"i":3143,"t":"gentoken","u":"/docs/server/console_commands","h":"#gentoken","p":3135},{"i":3145,"t":"gensubtoken","u":"/docs/server/console_commands","h":"#gensubtoken","p":3135},{"i":3147,"t":"checktoken","u":"/docs/server/console_commands","h":"#checktoken","p":3135},{"i":3149,"t":"checksubtoken","u":"/docs/server/console_commands","h":"#checksubtoken","p":3135},{"i":3153,"t":"Configuration sources","u":"/docs/server/configuration","h":"#configuration-sources","p":3151},{"i":3155,"t":"Command-line flags","u":"/docs/server/configuration","h":"#command-line-flags","p":3151},{"i":3157,"t":"OS environment variables","u":"/docs/server/configuration","h":"#os-environment-variables","p":3151},{"i":3159,"t":"Configuration file","u":"/docs/server/configuration","h":"#configuration-file","p":3151},{"i":3161,"t":"Config file formats","u":"/docs/server/configuration","h":"#config-file-formats","p":3151},{"i":3163,"t":"JSON config format","u":"/docs/server/configuration","h":"#json-config-format","p":3151},{"i":3165,"t":"TOML config format","u":"/docs/server/configuration","h":"#toml-config-format","p":3151},{"i":3167,"t":"YAML config format","u":"/docs/server/configuration","h":"#yaml-config-format","p":3151},{"i":3169,"t":"Important options","u":"/docs/server/configuration","h":"#important-options","p":3151},{"i":3171,"t":"allowed_origins","u":"/docs/server/configuration","h":"#allowed_origins","p":3151},{"i":3173,"t":"address","u":"/docs/server/configuration","h":"#address","p":3151},{"i":3175,"t":"port","u":"/docs/server/configuration","h":"#port","p":3151},{"i":3177,"t":"engine","u":"/docs/server/configuration","h":"#engine","p":3151},{"i":3179,"t":"Advanced options","u":"/docs/server/configuration","h":"#advanced-options","p":3151},{"i":3181,"t":"client_channel_limit","u":"/docs/server/configuration","h":"#client_channel_limit","p":3151},{"i":3183,"t":"channel_max_length","u":"/docs/server/configuration","h":"#channel_max_length","p":3151},{"i":3185,"t":"client_user_connection_limit","u":"/docs/server/configuration","h":"#client_user_connection_limit","p":3151},{"i":3187,"t":"client_connection_limit","u":"/docs/server/configuration","h":"#client_connection_limit","p":3151},{"i":3189,"t":"client_connection_rate_limit","u":"/docs/server/configuration","h":"#client_connection_rate_limit","p":3151},{"i":3191,"t":"client_queue_max_size","u":"/docs/server/configuration","h":"#client_queue_max_size","p":3151},{"i":3193,"t":"client_concurrency","u":"/docs/server/configuration","h":"#client_concurrency","p":3151},{"i":3195,"t":"client_stale_close_delay","u":"/docs/server/configuration","h":"#client_stale_close_delay","p":3151},{"i":3197,"t":"client_user_id_http_header","u":"/docs/server/configuration","h":"#client_user_id_http_header","p":3151},{"i":3199,"t":"allow_anonymous_connect_without_token","u":"/docs/server/configuration","h":"#allow_anonymous_connect_without_token","p":3151},{"i":3201,"t":"disallow_anonymous_connection_tokens","u":"/docs/server/configuration","h":"#disallow_anonymous_connection_tokens","p":3151},{"i":3203,"t":"gomaxprocs","u":"/docs/server/configuration","h":"#gomaxprocs","p":3151},{"i":3205,"t":"Endpoint configuration","u":"/docs/server/configuration","h":"#endpoint-configuration","p":3151},{"i":3207,"t":"Default endpoints","u":"/docs/server/configuration","h":"#default-endpoints","p":3151},{"i":3209,"t":"Admin endpoints","u":"/docs/server/configuration","h":"#admin-endpoints","p":3151},{"i":3211,"t":"Debug endpoints","u":"/docs/server/configuration","h":"#debug-endpoints","p":3151},{"i":3213,"t":"Health check endpoint","u":"/docs/server/configuration","h":"#health-check-endpoint","p":3151},{"i":3215,"t":"Swagger UI for server API","u":"/docs/server/configuration","h":"#swagger-ui-for-server-api","p":3151},{"i":3217,"t":"Custom internal ports","u":"/docs/server/configuration","h":"#custom-internal-ports","p":3151},{"i":3219,"t":"Disable default endpoints","u":"/docs/server/configuration","h":"#disable-default-endpoints","p":3151},{"i":3221,"t":"Customize handler endpoints","u":"/docs/server/configuration","h":"#customize-handler-endpoints","p":3151},{"i":3223,"t":"Signal handling","u":"/docs/server/configuration","h":"#signal-handling","p":3151},{"i":3225,"t":"Insecure modes","u":"/docs/server/configuration","h":"#insecure-modes","p":3151},{"i":3226,"t":"Insecure client connection","u":"/docs/server/configuration","h":"#insecure-client-connection","p":3151},{"i":3228,"t":"Disable client token signature check","u":"/docs/server/configuration","h":"#disable-client-token-signature-check","p":3151},{"i":3230,"t":"Insecure API mode","u":"/docs/server/configuration","h":"#insecure-api-mode","p":3151},{"i":3232,"t":"Insecure admin mode","u":"/docs/server/configuration","h":"#insecure-admin-mode","p":3151},{"i":3234,"t":"Setting time duration options","u":"/docs/server/configuration","h":"#setting-time-duration-options","p":3151},{"i":3236,"t":"Setting namespaces over env","u":"/docs/server/configuration","h":"#setting-namespaces-over-env","p":3151},{"i":3238,"t":"Anonymous usage stats","u":"/docs/server/configuration","h":"#anonymous-usage-stats","p":3151},{"i":3242,"t":"Open files limit","u":"/docs/server/infra_tuning","h":"#open-files-limit","p":3240},{"i":3244,"t":"Ephemeral port exhaustion","u":"/docs/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":3240},{"i":3246,"t":"Sockets in TIME_WAIT state","u":"/docs/server/infra_tuning","h":"#sockets-in-time_wait-state","p":3240},{"i":3248,"t":"Proxy max connections","u":"/docs/server/infra_tuning","h":"#proxy-max-connections","p":3240},{"i":3250,"t":"Conntrack table","u":"/docs/server/infra_tuning","h":"#conntrack-table","p":3240},{"i":3252,"t":"Additional server protection","u":"/docs/server/infra_tuning","h":"#additional-server-protection","p":3240},{"i":3256,"t":"History design","u":"/docs/server/history_and_recovery","h":"#history-design","p":3254},{"i":3258,"t":"History iteration API","u":"/docs/server/history_and_recovery","h":"#history-iteration-api","p":3254},{"i":3260,"t":"Automatic message recovery","u":"/docs/server/history_and_recovery","h":"#automatic-message-recovery","p":3254},{"i":3264,"t":"Memory engine","u":"/docs/server/engines","h":"#memory-engine","p":3262},{"i":3266,"t":"Memory engine options","u":"/docs/server/engines","h":"#memory-engine-options","p":3262},{"i":3268,"t":"Redis engine","u":"/docs/server/engines","h":"#redis-engine","p":3262},{"i":3270,"t":"Redis engine options","u":"/docs/server/engines","h":"#redis-engine-options","p":3262},{"i":3272,"t":"Configuring Redis TLS","u":"/docs/server/engines","h":"#configuring-redis-tls","p":3262},{"i":3274,"t":"Scaling with Redis tutorial","u":"/docs/server/engines","h":"#scaling-with-redis-tutorial","p":3262},{"i":3276,"t":"Redis Sentinel for high availability","u":"/docs/server/engines","h":"#redis-sentinel-for-high-availability","p":3262},{"i":3278,"t":"Redis Sentinel TLS","u":"/docs/server/engines","h":"#redis-sentinel-tls","p":3262},{"i":3280,"t":"Haproxy instead of Sentinel configuration","u":"/docs/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":3262},{"i":3282,"t":"Redis sharding","u":"/docs/server/engines","h":"#redis-sharding","p":3262},{"i":3284,"t":"Redis cluster","u":"/docs/server/engines","h":"#redis-cluster","p":3262},{"i":3286,"t":"Other Redis compatible","u":"/docs/server/engines","h":"#other-redis-compatible","p":3262},{"i":3288,"t":"Tarantool engine","u":"/docs/server/engines","h":"#tarantool-engine","p":3262},{"i":3290,"t":"Tarantool engine options","u":"/docs/server/engines","h":"#tarantool-engine-options","p":3262},{"i":3292,"t":"Nats broker","u":"/docs/server/engines","h":"#nats-broker","p":3262},{"i":3294,"t":"Options","u":"/docs/server/engines","h":"#options","p":3262},{"i":3298,"t":"Prometheus","u":"/docs/server/monitoring","h":"#prometheus","p":3296},{"i":3300,"t":"Graphite","u":"/docs/server/monitoring","h":"#graphite","p":3296},{"i":3302,"t":"Grafana dashboard","u":"/docs/server/monitoring","h":"#grafana-dashboard","p":3296},{"i":3306,"t":"Nginx configuration","u":"/docs/server/load_balancing","h":"#nginx-configuration","p":3304},{"i":3308,"t":"Separate domain for Centrifugo","u":"/docs/server/load_balancing","h":"#separate-domain-for-centrifugo","p":3304},{"i":3310,"t":"Embed to a location of web site","u":"/docs/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":3304},{"i":3312,"t":"worker_connections","u":"/docs/server/load_balancing","h":"#worker_connections","p":3304},{"i":3316,"t":"Metrics","u":"/docs/server/observability","h":"#metrics","p":3314},{"i":3317,"t":"Prometheus metrics","u":"/docs/server/observability","h":"#prometheus-metrics","p":3314},{"i":3319,"t":"Graphite metrics","u":"/docs/server/observability","h":"#graphite-metrics","p":3314},{"i":3321,"t":"Grafana dashboard","u":"/docs/server/observability","h":"#grafana-dashboard","p":3314},{"i":3323,"t":"Exposed metrics","u":"/docs/server/observability","h":"#exposed-metrics","p":3314},{"i":3325,"t":"Traces","u":"/docs/server/observability","h":"#traces","p":3314},{"i":3326,"t":"OpenTelemetry","u":"/docs/server/observability","h":"#opentelemetry","p":3314},{"i":3328,"t":"Logs","u":"/docs/server/observability","h":"#logs","p":3314},{"i":3332,"t":"Enabling online presence","u":"/docs/server/presence","h":"#enabling-online-presence","p":3330},{"i":3334,"t":"Retrieving presence on the client side","u":"/docs/server/presence","h":"#retrieving-presence-on-the-client-side","p":3330},{"i":3336,"t":"Join and leave events","u":"/docs/server/presence","h":"#join-and-leave-events","p":3330},{"i":3338,"t":"Implementation notes","u":"/docs/server/presence","h":"#implementation-notes","p":3330},{"i":3340,"t":"Conclusion","u":"/docs/server/presence","h":"#conclusion","p":3330},{"i":3344,"t":"Scalability concerns","u":"/docs/server/proxy_streams","h":"#scalability-concerns","p":3342},{"i":3346,"t":"Motivation and design","u":"/docs/server/proxy_streams","h":"#motivation-and-design","p":3342},{"i":3348,"t":"Unidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#unidirectional-subscription-streams","p":3342},{"i":3350,"t":"Bidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#bidirectional-subscription-streams","p":3342},{"i":3352,"t":"Granular proxy mode","u":"/docs/server/proxy_streams","h":"#granular-proxy-mode","p":3342},{"i":3354,"t":"Full example","u":"/docs/server/proxy_streams","h":"#full-example","p":3342},{"i":3358,"t":"Dynamic server-side subscriptions","u":"/docs/server/server_subs","h":"#dynamic-server-side-subscriptions","p":3356},{"i":3360,"t":"Automatic personal channel subscription","u":"/docs/server/server_subs","h":"#automatic-personal-channel-subscription","p":3356},{"i":3362,"t":"Maintain single user connection","u":"/docs/server/server_subs","h":"#maintain-single-user-connection","p":3356},{"i":3366,"t":"HTTP proxy","u":"/docs/server/proxy","h":"#http-proxy","p":3364},{"i":3368,"t":"HTTP request structure","u":"/docs/server/proxy","h":"#http-request-structure","p":3364},{"i":3370,"t":"Proxy HTTP headers","u":"/docs/server/proxy","h":"#proxy-http-headers","p":3364},{"i":3372,"t":"Proxy GRPC metadata","u":"/docs/server/proxy","h":"#proxy-grpc-metadata","p":3364},{"i":3374,"t":"Connect proxy","u":"/docs/server/proxy","h":"#connect-proxy","p":3364},{"i":3376,"t":"Refresh proxy","u":"/docs/server/proxy","h":"#refresh-proxy","p":3364},{"i":3378,"t":"RPC proxy","u":"/docs/server/proxy","h":"#rpc-proxy","p":3364},{"i":3380,"t":"Subscribe proxy","u":"/docs/server/proxy","h":"#subscribe-proxy","p":3364},{"i":3382,"t":"Publish proxy","u":"/docs/server/proxy","h":"#publish-proxy","p":3364},{"i":3384,"t":"Sub refresh proxy","u":"/docs/server/proxy","h":"#sub-refresh-proxy","p":3364},{"i":3386,"t":"Return custom error","u":"/docs/server/proxy","h":"#return-custom-error","p":3364},{"i":3388,"t":"Return custom disconnect","u":"/docs/server/proxy","h":"#return-custom-disconnect","p":3364},{"i":3390,"t":"GRPC proxy","u":"/docs/server/proxy","h":"#grpc-proxy","p":3364},{"i":3392,"t":"GRPC proxy options","u":"/docs/server/proxy","h":"#grpc-proxy-options","p":3364},{"i":3394,"t":"GRPC proxy example","u":"/docs/server/proxy","h":"#grpc-proxy-example","p":3364},{"i":3396,"t":"Header proxy rules","u":"/docs/server/proxy","h":"#header-proxy-rules","p":3364},{"i":3398,"t":"Binary mode","u":"/docs/server/proxy","h":"#binary-mode","p":3364},{"i":3400,"t":"Granular proxy mode","u":"/docs/server/proxy","h":"#granular-proxy-mode","p":3364},{"i":3402,"t":"Enable granular proxy mode","u":"/docs/server/proxy","h":"#enable-granular-proxy-mode","p":3364},{"i":3404,"t":"Defining a list of proxies","u":"/docs/server/proxy","h":"#defining-a-list-of-proxies","p":3364},{"i":3406,"t":"Granular connect and refresh","u":"/docs/server/proxy","h":"#granular-connect-and-refresh","p":3364},{"i":3408,"t":"Granular subscribe, publish, sub refresh","u":"/docs/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":3364},{"i":3410,"t":"Granular RPC","u":"/docs/server/proxy","h":"#granular-rpc","p":3364},{"i":3414,"t":"Using crt and key files","u":"/docs/server/tls","h":"#using-crt-and-key-files","p":3412},{"i":3416,"t":"Automatic certificates","u":"/docs/server/tls","h":"#automatic-certificates","p":3412},{"i":3418,"t":"TLS for GRPC API","u":"/docs/server/tls","h":"#tls-for-grpc-api","p":3412},{"i":3420,"t":"TLS for GRPC unidirectional stream","u":"/docs/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":3412},{"i":3424,"t":"HTTP API","u":"/docs/server/server_api","h":"#http-api","p":3422},{"i":3426,"t":"HTTP API authorization","u":"/docs/server/server_api","h":"#http-api-authorization","p":3422},{"i":3428,"t":"API methods","u":"/docs/server/server_api","h":"#api-methods","p":3422},{"i":3430,"t":"publish","u":"/docs/server/server_api","h":"#publish","p":3422},{"i":3432,"t":"broadcast","u":"/docs/server/server_api","h":"#broadcast","p":3422},{"i":3434,"t":"subscribe","u":"/docs/server/server_api","h":"#subscribe","p":3422},{"i":3436,"t":"unsubscribe","u":"/docs/server/server_api","h":"#unsubscribe","p":3422},{"i":3438,"t":"disconnect","u":"/docs/server/server_api","h":"#disconnect","p":3422},{"i":3440,"t":"refresh","u":"/docs/server/server_api","h":"#refresh","p":3422},{"i":3442,"t":"presence","u":"/docs/server/server_api","h":"#presence","p":3422},{"i":3444,"t":"presence_stats","u":"/docs/server/server_api","h":"#presence_stats","p":3422},{"i":3446,"t":"history","u":"/docs/server/server_api","h":"#history","p":3422},{"i":3448,"t":"history_remove","u":"/docs/server/server_api","h":"#history_remove","p":3422},{"i":3450,"t":"channels","u":"/docs/server/server_api","h":"#channels","p":3422},{"i":3452,"t":"info","u":"/docs/server/server_api","h":"#info","p":3422},{"i":3454,"t":"batch","u":"/docs/server/server_api","h":"#batch","p":3422},{"i":3456,"t":"HTTP API libraries","u":"/docs/server/server_api","h":"#http-api-libraries","p":3422},{"i":3458,"t":"GRPC API","u":"/docs/server/server_api","h":"#grpc-api","p":3422},{"i":3460,"t":"GRPC example for Python","u":"/docs/server/server_api","h":"#grpc-example-for-python","p":3422},{"i":3462,"t":"GRPC example for Go","u":"/docs/server/server_api","h":"#grpc-example-for-go","p":3422},{"i":3464,"t":"GRPC API key authorization","u":"/docs/server/server_api","h":"#grpc-api-key-authorization","p":3422},{"i":3466,"t":"Transport error mode","u":"/docs/server/server_api","h":"#transport-error-mode","p":3422},{"i":3468,"t":"Centrifugo error code to HTTP code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-http-code","p":3422},{"i":3470,"t":"Centrifugo error code to GRPC code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-grpc-code","p":3422},{"i":3474,"t":"Options","u":"/docs/transports/http_stream","h":"#options","p":3472},{"i":3475,"t":"http_stream","u":"/docs/transports/http_stream","h":"#http_stream","p":3472},{"i":3477,"t":"http_stream_max_request_body_size","u":"/docs/transports/http_stream","h":"#http_stream_max_request_body_size","p":3472},{"i":3481,"t":"Bidirectional","u":"/docs/transports/overview","h":"#bidirectional","p":3479},{"i":3483,"t":"Unidirectional","u":"/docs/transports/overview","h":"#unidirectional","p":3479},{"i":3485,"t":"PING/PONG behavior","u":"/docs/transports/overview","h":"#pingpong-behavior","p":3479},{"i":3489,"t":"List of client SDKs","u":"/docs/transports/client_sdk","h":"#list-of-client-sdks","p":3487},{"i":3491,"t":"Protobuf and JSON formats in SDKs","u":"/docs/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":3487},{"i":3493,"t":"SDK feature matrix","u":"/docs/transports/client_sdk","h":"#sdk-feature-matrix","p":3487},{"i":3495,"t":"Connection related features","u":"/docs/transports/client_sdk","h":"#connection-related-features","p":3487},{"i":3497,"t":"Client-side subscription related features","u":"/docs/transports/client_sdk","h":"#client-side-subscription-related-features","p":3487},{"i":3501,"t":"SockJS caveats","u":"/docs/transports/sockjs","h":"#sockjs-caveats","p":3499},{"i":3503,"t":"Sticky sessions","u":"/docs/transports/sockjs","h":"#sticky-sessions","p":3499},{"i":3505,"t":"Browser only","u":"/docs/transports/sockjs","h":"#browser-only","p":3499},{"i":3507,"t":"JSON only","u":"/docs/transports/sockjs","h":"#json-only","p":3499},{"i":3509,"t":"Options","u":"/docs/transports/sockjs","h":"#options","p":3499},{"i":3510,"t":"sockjs","u":"/docs/transports/sockjs","h":"#sockjs","p":3499},{"i":3512,"t":"sockjs_url","u":"/docs/transports/sockjs","h":"#sockjs_url","p":3499},{"i":3516,"t":"Options","u":"/docs/transports/sse","h":"#options","p":3514},{"i":3517,"t":"sse","u":"/docs/transports/sse","h":"#sse","p":3514},{"i":3519,"t":"sse_max_request_body_size","u":"/docs/transports/sse","h":"#sse_max_request_body_size","p":3514},{"i":3523,"t":"Unidirectional message types","u":"/docs/transports/uni_client_protocol","h":"#unidirectional-message-types","p":3521},{"i":3527,"t":"Supported data formats","u":"/docs/transports/uni_grpc","h":"#supported-data-formats","p":3525},{"i":3529,"t":"Options","u":"/docs/transports/uni_grpc","h":"#options","p":3525},{"i":3530,"t":"uni_grpc","u":"/docs/transports/uni_grpc","h":"#uni_grpc","p":3525},{"i":3532,"t":"uni_grpc_port","u":"/docs/transports/uni_grpc","h":"#uni_grpc_port","p":3525},{"i":3534,"t":"uni_grpc_address","u":"/docs/transports/uni_grpc","h":"#uni_grpc_address","p":3525},{"i":3536,"t":"uni_grpc_max_receive_message_size","u":"/docs/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":3525},{"i":3538,"t":"uni_grpc_tls","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls","p":3525},{"i":3540,"t":"uni_grpc_tls_cert","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":3525},{"i":3542,"t":"uni_grpc_tls_key","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_key","p":3525},{"i":3544,"t":"Example","u":"/docs/transports/uni_grpc","h":"#example","p":3525},{"i":3548,"t":"Protobuf schema","u":"/docs/transports/client_protocol","h":"#protobuf-schema","p":3546},{"i":3550,"t":"Command-Reply","u":"/docs/transports/client_protocol","h":"#command-reply","p":3546},{"i":3552,"t":"Asynchronous pushes","u":"/docs/transports/client_protocol","h":"#asynchronous-pushes","p":3546},{"i":3554,"t":"Top level batching","u":"/docs/transports/client_protocol","h":"#top-level-batching","p":3546},{"i":3556,"t":"Ping Pong","u":"/docs/transports/client_protocol","h":"#ping-pong","p":3546},{"i":3558,"t":"Handle disconnects","u":"/docs/transports/client_protocol","h":"#handle-disconnects","p":3546},{"i":3560,"t":"Handle errors","u":"/docs/transports/client_protocol","h":"#handle-errors","p":3546},{"i":3562,"t":"Additional notes","u":"/docs/transports/client_protocol","h":"#additional-notes","p":3546},{"i":3566,"t":"Connect command","u":"/docs/transports/uni_websocket","h":"#connect-command","p":3564},{"i":3568,"t":"SubscribeRequest","u":"/docs/transports/uni_websocket","h":"#subscriberequest","p":3564},{"i":3570,"t":"Supported data formats","u":"/docs/transports/uni_websocket","h":"#supported-data-formats","p":3564},{"i":3572,"t":"Pings","u":"/docs/transports/uni_websocket","h":"#pings","p":3564},{"i":3574,"t":"Options","u":"/docs/transports/uni_websocket","h":"#options","p":3564},{"i":3575,"t":"uni_websocket","u":"/docs/transports/uni_websocket","h":"#uni_websocket","p":3564},{"i":3577,"t":"uni_websocket_message_size_limit","u":"/docs/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":3564},{"i":3579,"t":"Example","u":"/docs/transports/uni_websocket","h":"#example","p":3564},{"i":3583,"t":"Connect command","u":"/docs/transports/uni_http_stream","h":"#connect-command","p":3581},{"i":3585,"t":"Supported data formats","u":"/docs/transports/uni_http_stream","h":"#supported-data-formats","p":3581},{"i":3587,"t":"Pings","u":"/docs/transports/uni_http_stream","h":"#pings","p":3581},{"i":3589,"t":"Options","u":"/docs/transports/uni_http_stream","h":"#options","p":3581},{"i":3590,"t":"uni_http_stream","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream","p":3581},{"i":3592,"t":"uni_http_stream_max_request_body_size","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":3581},{"i":3594,"t":"Connecting using CURL","u":"/docs/transports/uni_http_stream","h":"#connecting-using-curl","p":3581},{"i":3596,"t":"Browser example","u":"/docs/transports/uni_http_stream","h":"#browser-example","p":3581},{"i":3600,"t":"Connect command","u":"/docs/transports/uni_sse","h":"#connect-command","p":3598},{"i":3602,"t":"Supported data formats","u":"/docs/transports/uni_sse","h":"#supported-data-formats","p":3598},{"i":3604,"t":"Options","u":"/docs/transports/uni_sse","h":"#options","p":3598},{"i":3605,"t":"uni_sse","u":"/docs/transports/uni_sse","h":"#uni_sse","p":3598},{"i":3607,"t":"uni_sse_max_request_body_size","u":"/docs/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":3598},{"i":3609,"t":"Browser example","u":"/docs/transports/uni_sse","h":"#browser-example","p":3598},{"i":3615,"t":"Options","u":"/docs/transports/websocket","h":"#options","p":3613},{"i":3616,"t":"websocket_message_size_limit","u":"/docs/transports/websocket","h":"#websocket_message_size_limit","p":3613},{"i":3618,"t":"websocket_read_buffer_size","u":"/docs/transports/websocket","h":"#websocket_read_buffer_size","p":3613},{"i":3620,"t":"websocket_write_buffer_size","u":"/docs/transports/websocket","h":"#websocket_write_buffer_size","p":3613},{"i":3622,"t":"websocket_use_write_buffer_pool","u":"/docs/transports/websocket","h":"#websocket_use_write_buffer_pool","p":3613},{"i":3624,"t":"websocket_compression","u":"/docs/transports/websocket","h":"#websocket_compression","p":3613},{"i":3626,"t":"Protobuf binary protocol","u":"/docs/transports/websocket","h":"#protobuf-binary-protocol","p":3613},{"i":3628,"t":"Debugging with Postman, wscat, etc","u":"/docs/transports/websocket","h":"#debugging-with-postman-wscat-etc","p":3613},{"i":3632,"t":"Client connection states","u":"/docs/transports/client_api","h":"#client-connection-states","p":3630},{"i":3634,"t":"Client common options","u":"/docs/transports/client_api","h":"#client-common-options","p":3630},{"i":3636,"t":"Client methods","u":"/docs/transports/client_api","h":"#client-methods","p":3630},{"i":3638,"t":"Client connection token","u":"/docs/transports/client_api","h":"#client-connection-token","p":3630},{"i":3640,"t":"Connection PING/PONG","u":"/docs/transports/client_api","h":"#connection-pingpong","p":3630},{"i":3642,"t":"Subscription states","u":"/docs/transports/client_api","h":"#subscription-states","p":3630},{"i":3644,"t":"Subscription management","u":"/docs/transports/client_api","h":"#subscription-management","p":3630},{"i":3646,"t":"Listen to channel publications","u":"/docs/transports/client_api","h":"#listen-to-channel-publications","p":3630},{"i":3648,"t":"Subscription recovery state","u":"/docs/transports/client_api","h":"#subscription-recovery-state","p":3630},{"i":3650,"t":"Subscription common options","u":"/docs/transports/client_api","h":"#subscription-common-options","p":3630},{"i":3652,"t":"Subscription methods","u":"/docs/transports/client_api","h":"#subscription-methods","p":3630},{"i":3654,"t":"Subscription token","u":"/docs/transports/client_api","h":"#subscription-token","p":3630},{"i":3656,"t":"Server-side subscriptions","u":"/docs/transports/client_api","h":"#server-side-subscriptions","p":3630},{"i":3658,"t":"Error codes","u":"/docs/transports/client_api","h":"#error-codes","p":3630},{"i":3660,"t":"Unsubscribe codes","u":"/docs/transports/client_api","h":"#unsubscribe-codes","p":3630},{"i":3662,"t":"Disconnect codes","u":"/docs/transports/client_api","h":"#disconnect-codes","p":3630},{"i":3664,"t":"RPC","u":"/docs/transports/client_api","h":"#rpc","p":3630},{"i":3666,"t":"Channel history API","u":"/docs/transports/client_api","h":"#channel-history-api","p":3630},{"i":3668,"t":"Presence and presence stats API","u":"/docs/transports/client_api","h":"#presence-and-presence-stats-api","p":3630},{"i":3670,"t":"SDK common best practices","u":"/docs/transports/client_api","h":"#sdk-common-best-practices","p":3630}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/5",[0,4.801,1,3.159,2,6.232]],["t/7",[0,5.703,3,5.854]],["t/9",[4,6.242,5,7.403]],["t/11",[6,4.421,7,4.801,8,3.301]],["t/13",[0,4.801,9,6.232,10,4.69]],["t/15",[11,5.854,12,5.163]],["t/17",[13,7.403,14,5.854]],["t/19",[8,2.85,15,3.964,16,3.753,17,5.381]],["t/21",[18,6.164]],["t/25",[19,7.403,20,6.03]],["t/27",[21,7.425]],["t/29",[21,6.03,22,6.508]],["t/31",[23,7.686]],["t/33",[24,2.982,25,3.731]],["t/35",[8,3.301,26,5.778,27,6.232]],["t/37",[28,5.779]],["t/39",[1,3.159,25,3.141,29,3.712]],["t/41",[24,2.511,30,6.232,31,3.583]],["t/43",[32,3.588,33,4.512,34,3.964]],["t/45",[12,5.163,35,7.403]],["t/47",[36,4.591,37,4.69,38,6.232]],["t/49",[39,9.116]],["t/51",[11,7.208]],["t/53",[40,6.078]],["t/55",[41,4.442]],["t/57",[18,6.164]],["t/61",[42,2.915,43,5.479,44,5.778]],["t/63",[45,7.403,46,6.508]],["t/65",[47,6.242,48,4.808]],["t/67",[10,4.049,49,3.54,50,4.384,51,3.964]],["t/69",[31,3.583,52,5.255,53,2.712]],["t/71",[16,4.347,54,3.346,55,4.421]],["t/73",[56,5.703,57,4.031]],["t/75",[58,3.87,59,3.321]],["t/77",[1,3.159,53,2.712,60,5.077]],["t/79",[61,6.863,62,6.242]],["t/81",[60,6.03,63,3.588]],["t/83",[64,4.69,65,6.232,66,5.077]],["t/85",[11,5.854,60,6.03]],["t/87",[42,3.463,67,5.571]],["t/89",[18,6.164]],["t/93",[68,7.686]],["t/95",[69,4.69,70,6.232,71,6.232]],["t/97",[72,4.735,73,4.735,74,4.735,75,3.358,76,3.992]],["t/99",[41,3.037,63,3.021,77,5.778]],["t/101",[1,3.159,78,5.778,79,5.778]],["t/103",[1,3.753,80,7.403]],["t/105",[79,5.778,81,5.255,82,2.529]],["t/107",[82,2.529,83,5.255,84,4.215]],["t/109",[63,3.021,85,6.232,86,6.232]],["t/111",[16,3.753,87,4.384,88,3.887,89,4.049]],["t/113",[1,3.159,41,3.037,90,5.255]],["t/115",[18,6.164]],["t/119",[42,2.915,91,4.591,92,6.232]],["t/121",[93,9.116]],["t/123",[94,5.453,95,6.863]],["t/125",[94,4.591,96,4.801,97,4.591]],["t/127",[98,5.778,99,6.232,100,5.778]],["t/129",[98,5.778,100,5.778,101,5.077]],["t/131",[1,3.159,20,5.077,42,2.915]],["t/133",[102,6.508,103,5.571]],["t/135",[59,2.795,104,4.69,105,5.255]],["t/137",[60,7.425]],["t/139",[106,3.744,107,3.42,108,2.876,109,3.42,110,3.992]],["t/141",[18,6.164]],["t/145",[68,6.242,111,6.03]],["t/147",[42,2.915,91,4.591,112,6.232]],["t/149",[20,5.077,95,5.778,113,5.778]],["t/151",[111,6.03,114,6.242]],["t/153",[115,6.242,116,5.347]],["t/155",[117,4.801,118,4.155,119,3.864]],["t/157",[120,7.686]],["t/159",[42,3.463,121,7.403]],["t/161",[59,2.795,82,2.529,122,5.778]],["t/163",[101,6.03,122,6.863]],["t/165",[29,4.41,63,3.588]],["t/167",[60,6.03,123,6.242]],["t/169",[18,6.164]],["t/173",[113,8.451]],["t/175",[24,1.907,40,3.157,124,3.992,125,3.302,126,2.936]],["t/177",[24,2.168,127,4.538,128,2.973,129,4.145]],["t/179",[59,2.795,130,5.255,131,3.748]],["t/181",[1,2.143,24,1.703,29,2.518,82,1.715,132,3.564,133,2.902]],["t/183",[1,2.143,24,1.703,29,2.518,59,1.896,82,1.715,134,3.564]],["t/185",[24,1.907,25,2.386,135,3.992,136,3.42,137,3.992]],["t/187",[82,1.921,125,3.302,133,3.25,138,3.992,139,3.647]],["t/189",[59,2.124,82,1.921,125,3.302,139,3.647,140,3.992]],["t/191",[141,9.116]],["t/195",[63,2.608,142,5.381,143,4.989,144,3.887]],["t/197",[128,2.973,145,4.989,146,4.989,147,3.694]],["t/199",[148,6.863,149,7.403]],["t/201",[22,4.731,87,4.384,144,3.887,150,4.384]],["t/203",[53,2.342,64,4.049,151,2.973,152,3.094]],["t/205",[153,5.381,154,5.381,155,4.989,156,4.255]],["t/207",[1,3.159,53,2.712,157,5.778]],["t/209",[25,2.712,128,2.973,158,4.384,159,3.412]],["t/211",[159,3.412,160,4.384,161,3.694,162,5.381]],["t/213",[144,3.887,163,5.381,164,4.384,165,4.989]],["t/215",[166,7.403,167,5.347]],["t/217",[21,4.384,168,5.381,169,5.381,170,5.381]],["t/219",[18,6.164]],["t/223",[20,6.03,42,3.463]],["t/225",[69,4.69,77,5.778,171,5.778]],["t/227",[171,4.989,172,2.666,173,5.381,174,5.381]],["t/229",[8,2.85,54,2.889,175,3.887,176,4.731]],["t/231",[177,9.116]],["t/233",[26,5.778,178,6.232,179,3.678]],["t/235",[37,5.571,180,7.403]],["t/237",[36,5.453,181,6.863]],["t/239",[36,4.591,182,6.232,183,6.232]],["t/241",[84,5.006,184,4.749]],["t/243",[18,6.164]],["t/249",[42,2.915,67,4.69,185,6.232]],["t/251",[53,2.342,175,3.887,186,4.989,187,3.964]],["t/253",[53,2.061,175,3.42,186,4.39,187,3.488,188,3.647]],["t/255",[189,7.403,190,7.403]],["t/257",[1,3.753,104,5.571]],["t/259",[128,4.091,191,7.403]],["t/261",[6,5.251,187,5.453]],["t/263",[192,4.801,193,5.255,194,5.479]],["t/265",[175,5.347,195,6.508]],["t/267",[156,4.928,175,4.502,187,4.591]],["t/269",[18,6.164]],["t/274",[196,5.479,197,5.479,198,4.591]],["t/280",[199,7.686]],["t/282",[94,4.591,97,4.591,200,6.232]],["t/284",[20,6.03,42,3.463]],["t/286",[102,6.508,103,5.571]],["t/288",[6,3.817,8,2.85,50,4.384,51,3.964]],["t/290",[18,6.164]],["t/294",[201,9.116]],["t/296",[202,9.116]],["t/298",[42,4.264]],["t/300",[97,4.591,203,6.232,204,6.232]],["t/306",[7,3.256,42,1.977,82,1.715,84,2.858,188,3.256,205,3.716]],["t/308",[82,2.183,206,3.694,207,3.887,208,3.964]],["t/310",[42,2.915,209,4.591,210,5.479]],["t/312",[8,3.301,37,4.69,119,3.864]],["t/314",[8,3.301,36,4.591,211,4.928]],["t/316",[24,2.511,94,4.591,212,5.479]],["t/318",[24,2.168,213,4.049,214,4.384,215,4.731]],["t/320",[8,3.241,24,1.538,179,2.253,216,2.812,217,3.356,218,3.356]],["t/322",[24,1.703,63,2.049,188,3.256,219,3.716,220,2.818,221,2.858]],["t/324",[32,4.08,33,2.159,96,2.941,97,2.812,222,3.019,223,3.356]],["t/326",[42,2.215,64,3.563,81,3.992,82,1.921,224,4.162]],["t/328",[42,1.786,59,1.712,103,2.873,225,2.393,226,3.356,227,3.356,228,3.356]],["t/330",[42,2.915,229,4.155,230,5.479]],["t/332",[42,1.628,82,1.412,216,2.564,225,2.182,231,2.752,232,2.619,233,2.427,234,3.06]],["t/334",[6,2.708,111,3.11,175,2.757,187,2.812,235,3.356,236,3.356,237,2.545]],["t/336",[8,2.85,63,2.608,238,4.731,239,4.731]],["t/338",[0,2.941,8,2.022,63,1.85,64,2.873,82,1.549,88,2.757,179,2.253]],["t/340",[24,1.402,94,2.564,96,2.681,126,2.158,192,2.681,240,3.06,241,2.681,242,3.06]],["t/342",[24,1.703,172,2.094,213,3.181,216,3.114,243,3.716,244,3.716]],["t/344",[42,2.915,245,3.907,246,5.479]],["t/346",[42,2.915,247,3.583,248,5.479]],["t/348",[15,2.812,29,2.274,97,2.812,123,3.219,249,3.11,250,2.873,251,3.019]],["t/350",[12,3.302,32,3.157,33,2.678,250,3.563,252,3.115]],["t/352",[253,4.145,254,4.731,255,4.731,256,4.731]],["t/358",[207,5.347,257,6.508]],["t/360",[8,3.301,31,3.583,258,4.591]],["t/362",[8,3.301,37,4.69,119,3.864]],["t/364",[8,3.301,36,4.591,211,4.928]],["t/366",[259,6.508,260,6.508]],["t/368",[32,4.155,33,3.525,258,4.591]],["t/370",[12,5.163,258,5.453]],["t/374",[42,2.915,44,5.778,261,5.778]],["t/376",[53,2.342,63,2.608,147,3.694,193,4.538]],["t/378",[0,4.145,262,5.381,263,5.381,264,4.989]],["t/380",[63,3.021,144,4.502,265,6.232]],["t/382",[266,6.232,267,4.421,268,5.255]],["t/384",[24,2.168,55,3.817,192,4.145,269,3.753]],["t/386",[24,2.168,241,4.145,270,4.384,271,5.381]],["t/388",[25,3.731,272,7.403]],["t/390",[24,2.982,139,5.703]],["t/392",[53,2.712,61,5.778,82,2.529]],["t/394",[63,2.608,264,4.989,273,5.381,274,5.381]],["t/396",[275,6.863,276,6.508]],["t/398",[275,6.863,277,7.403]],["t/400",[118,4.936,278,7.403]],["t/402",[18,6.164]],["t/404",[87,6.03,279,6.242]],["t/406",[280,7.403,281,7.403]],["t/410",[91,5.453,282,6.508]],["t/412",[11,5.854,283,6.508]],["t/414",[12,5.163,284,6.508]],["t/416",[63,3.021,144,4.502,285,5.479]],["t/418",[10,4.049,50,4.384,51,3.964,286,4.731]],["t/420",[23,6.242,287,6.508]],["t/422",[82,3.004,288,6.03]],["t/424",[24,2.511,101,5.077,270,5.077]],["t/426",[25,3.141,232,4.69,289,4.69]],["t/428",[28,3.412,82,2.183,88,3.887,89,4.049]],["t/430",[32,4.155,33,3.525,290,5.479]],["t/432",[8,3.301,24,2.511,31,3.583]],["t/434",[156,4.255,237,3.588,291,4.731,292,3.753]],["t/436",[293,6.508,294,6.508]],["t/438",[295,6.508,296,5.854]],["t/440",[107,5.347,297,5.854]],["t/442",[67,5.571,252,4.87]],["t/446",[69,5.571,298,6.508]],["t/448",[42,2.915,124,5.255,172,3.087]],["t/450",[127,5.255,172,3.087,251,4.928]],["t/452",[42,2.915,82,2.529,130,5.255]],["t/454",[24,2.511,131,3.748,132,5.255]],["t/456",[24,2.511,134,5.255,179,3.678]],["t/458",[135,5.255,296,4.928,299,5.479]],["t/460",[42,2.915,138,5.255,300,5.479]],["t/462",[42,2.915,140,5.255,209,4.591]],["t/464",[301,5.479,302,5.077,303,5.479]],["t/468",[304,6.242,305,7.403]],["t/470",[306,8.451]],["t/472",[306,6.863,307,6.508]],["t/474",[118,4.936,308,6.03]],["t/476",[309,5.854,310,7.403]],["t/478",[311,8.451]],["t/480",[207,3.42,311,4.39,312,4.735,313,4.735,314,4.735]],["t/482",[102,6.508,315,7.403]],["t/484",[18,6.164]],["t/488",[308,7.425]],["t/490",[270,7.425]],["t/492",[87,6.03,279,6.242]],["t/496",[1,3.753,82,3.004]],["t/498",[1,3.753,247,4.256]],["t/500",[1,3.753,14,5.854]],["t/502",[15,4.591,82,2.529,316,5.479]],["t/504",[24,2.982,131,4.452]],["t/506",[1,3.159,25,3.141,29,3.712]],["t/508",[6,5.251,28,4.693]],["t/510",[24,2.511,31,3.583,317,5.255]],["t/512",[33,5.02,34,4.591]],["t/516",[229,6.078]],["t/518",[172,4.516]],["t/522",[69,4.69,220,4.155,318,4.928]],["t/524",[109,5.347,198,5.453]],["t/526",[41,3.037,109,4.502,110,5.255]],["t/528",[319,4.928,320,5.479,321,5.479]],["t/530",[322,4.731,323,4.731,324,4.538,325,4.731]],["t/532",[326,6.508,327,6.508]],["t/534",[107,5.347,199,6.242]],["t/538",[29,3.712,48,4.047,63,3.021]],["t/540",[31,3.583,55,4.421,328,6.232]],["t/542",[40,4.155,329,5.077,330,4.591]],["t/544",[331,6.232,332,6.232,333,5.778]],["t/546",[1,3.159,29,3.712,48,4.047]],["t/547",[51,3.964,334,5.381,335,2.505,336,4.538]],["t/549",[24,2.511,48,4.047,335,2.901]],["t/551",[333,4.989,337,3.236,338,4.538,339,4.538]],["t/553",[340,4.731,341,3.694,342,5.381,343,4.384]],["t/555",[53,2.061,58,2.475,150,3.857,324,3.992,344,3.563]],["t/557",[24,2.168,48,3.495,53,2.342,345,3.887]],["t/559",[48,4.047,59,2.795,151,3.444]],["t/561",[48,4.808,133,5.082]],["t/563",[48,4.047,54,3.346,172,3.087]],["t/565",[16,3.753,54,2.889,55,3.817,225,3.373]],["t/567",[55,4.421,167,4.502,346,5.077]],["t/569",[48,4.808,172,3.667]],["t/571",[43,4.731,159,3.412,261,4.989,347,5.381]],["t/575",[252,5.996]],["t/577",[348,6.508,349,4.369]],["t/579",[350,8.013]],["t/583",[41,3.607,351,7.403]],["t/587",[220,4.936,318,5.854]],["t/589",[109,5.347,198,5.453]],["t/591",[319,7.208]],["t/593",[352,6.508,353,6.508]],["t/595",[354,8.013]],["t/597",[47,4.538,67,4.049,116,3.887,161,3.694]],["t/603",[172,4.516]],["t/605",[82,3.004,355,4.936]],["t/607",[355,4.936,356,6.508]],["t/609",[41,3.607,357,6.508]],["t/611",[358,8.013]],["t/613",[229,4.936,359,6.508]],["t/617",[54,3.975,360,6.242]],["t/621",[53,2.712,151,3.444,361,3.998]],["t/623",[53,2.712,58,3.258,361,3.998]],["t/625",[59,2.795,151,3.444,361,3.998]],["t/627",[58,3.258,59,2.795,361,3.998]],["t/629",[133,4.278,361,3.998,362,5.479]],["t/631",[16,3.753,49,3.54,58,2.813,361,3.452]],["t/633",[41,4.442]],["t/635",[53,2.712,151,3.444,179,3.678]],["t/637",[31,3.583,53,2.712,151,3.444]],["t/643",[229,6.078]],["t/645",[172,4.516]],["t/647",[54,3.346,57,3.394,363,4.347]],["t/649",[57,3.394,117,4.801,363,4.347]],["t/651",[53,2.712,128,3.444,364,4.801]],["t/653",[53,2.342,126,3.337,128,2.973,365,4.538]],["t/657",[366,6.508,367,4.936]],["t/661",[229,6.078]],["t/663",[172,4.516]],["t/665",[54,3.346,57,3.394,363,4.347]],["t/667",[57,3.394,117,4.801,363,4.347]],["t/669",[53,2.712,126,3.864,368,5.479]],["t/671",[53,2.712,126,3.864,369,6.232]],["t/677",[28,3.002,29,2.82,63,2.295,150,3.857,222,3.744]],["t/679",[1,3.159,53,2.712,370,5.479]],["t/681",[1,3.159,53,2.712,371,5.479]],["t/683",[1,3.159,53,2.712,372,5.479]],["t/685",[172,4.516]],["t/689",[24,2.511,373,5.479,374,4.928]],["t/691",[125,4.347,269,4.347,375,4.928]],["t/693",[24,2.168,125,3.753,241,4.145,376,4.731]],["t/695",[24,2.168,125,3.753,126,3.337,375,4.255]],["t/697",[24,2.982,335,3.446]],["t/699",[179,5.38]],["t/701",[377,9.116]],["t/703",[378,7.686]],["t/705",[33,5.155]],["t/707",[379,9.116]],["t/709",[380,8.013]],["t/711",[381,8.013]],["t/713",[382,8.013]],["t/715",[383,7.022]],["t/717",[384,9.116]],["t/719",[385,9.116]],["t/721",[386,8.013]],["t/723",[387,8.013]],["t/725",[388,8.013]],["t/727",[389,8.013]],["t/729",[390,8.013]],["t/731",[24,2.168,41,2.622,159,3.412,335,2.505]],["t/733",[24,2.982,269,5.163]],["t/737",[391,7.208]],["t/739",[392,6.357]],["t/741",[393,7.208]],["t/743",[394,7.425]],["t/745",[395,7.425]],["t/747",[396,7.208]],["t/749",[397,7.208]],["t/751",[398,6.357]],["t/753",[399,7.208]],["t/755",[24,3.672]],["t/757",[392,6.357]],["t/759",[400,8.013]],["t/761",[401,7.208]],["t/763",[82,3.004,402,4.749]],["t/765",[41,4.442]],["t/767",[128,4.091,403,6.508]],["t/769",[128,4.091,402,4.749]],["t/771",[82,2.183,128,2.973,398,3.753,404,4.049]],["t/773",[133,4.278,405,5.479,406,5.479]],["t/775",[161,3.694,221,3.639,237,3.588,245,3.373]],["t/779",[63,3.021,108,3.785,184,3.998]],["t/781",[407,7.022]],["t/783",[408,8.013]],["t/785",[24,2.982,160,6.03]],["t/787",[409,4.693,410,6.508]],["t/789",[253,5.703,345,5.347]],["t/791",[131,4.452,411,6.508]],["t/793",[40,4.936,412,6.508]],["t/795",[341,5.082,413,6.03]],["t/797",[414,7.022]],["t/799",[128,4.091,402,4.749]],["t/801",[402,5.847]],["t/803",[7,5.703,341,5.082]],["t/805",[383,5.703,415,6.242]],["t/807",[63,3.021,108,3.785,247,3.583]],["t/809",[416,9.116]],["t/811",[417,9.116]],["t/813",[128,4.091,365,6.242]],["t/815",[341,5.082,413,6.03]],["t/817",[1,3.753,184,4.749]],["t/819",[402,5.847]],["t/821",[25,3.731,402,4.749]],["t/823",[418,9.116]],["t/825",[419,9.116]],["t/827",[78,6.863,184,4.749]],["t/829",[420,7.403,421,5.347]],["t/831",[14,5.854,422,6.863]],["t/833",[14,5.854,422,6.863]],["t/835",[40,4.936,82,3.004]],["t/837",[1,2.728,53,2.342,108,3.268,184,3.452]],["t/839",[407,7.022]],["t/841",[24,2.982,160,6.03]],["t/843",[253,5.703,345,5.347]],["t/845",[341,5.082,413,6.03]],["t/847",[414,7.022]],["t/849",[383,5.703,415,6.242]],["t/853",[335,4.243]],["t/855",[225,3.373,237,3.588,423,3.588,424,4.731]],["t/857",[292,4.347,349,3.678,425,4.155]],["t/861",[337,4.452,426,6.508]],["t/863",[337,4.452,427,6.508]],["t/865",[337,4.452,428,6.508]],["t/867",[337,4.452,429,6.508]],["t/869",[337,4.452,430,6.508]],["t/873",[107,5.347,172,3.667]],["t/875",[337,3.748,338,5.255,339,5.255]],["t/877",[4,5.255,115,5.255,431,4.801]],["t/879",[172,3.667,367,4.936]],["t/881",[152,3.583,159,3.951,367,4.155]],["t/883",[152,3.583,159,3.951,221,4.215]],["t/885",[152,3.583,159,3.951,432,5.479]],["t/887",[152,3.583,159,3.951,433,5.479]],["t/889",[335,3.446,434,6.508]],["t/891",[435,8.013]],["t/893",[436,8.013]],["t/895",[437,6.714]],["t/897",[57,4.963]],["t/899",[335,3.446,438,6.508]],["t/901",[439,8.013]],["t/903",[440,8.013]],["t/905",[441,8.013]],["t/907",[442,8.013]],["t/909",[443,9.116]],["t/911",[444,8.013]],["t/913",[445,8.013]],["t/915",[172,3.667,446,4.496]],["t/917",[55,5.251,446,4.496]],["t/919",[292,5.163,446,4.496]],["t/921",[164,6.03,446,4.496]],["t/923",[343,5.077,446,3.785,447,5.479]],["t/925",[407,4.801,423,4.155,437,4.591]],["t/927",[55,4.421,346,5.077,446,3.785]],["t/929",[105,5.255,423,4.155,446,3.785]],["t/931",[84,5.006,448,6.508]],["t/933",[349,4.369,425,4.936]],["t/934",[63,3.021,82,2.529,425,4.155]],["t/936",[53,2.712,349,3.678,425,4.155]],["t/938",[292,4.347,349,3.678,425,4.155]],["t/940",[51,3.964,116,3.887,335,2.505,336,4.538]],["t/942",[88,3.887,116,3.887,269,3.753,449,4.731]],["t/944",[34,4.591,207,4.502,378,5.255]],["t/948",[40,4.155,297,4.928,367,4.155]],["t/950",[437,4.591,450,5.479,451,5.479]],["t/952",[421,4.502,452,5.479,453,5.479]],["t/954",[59,2.795,82,2.529,454,5.479]],["t/956",[355,4.936,455,6.508]],["t/960",[31,4.256,456,5.854]],["t/962",[31,3.583,52,5.255,53,2.712]],["t/964",[8,3.301,136,4.502,330,4.591]],["t/968",[57,4.031,206,5.082]],["t/970",[57,3.394,206,4.278,335,2.901]],["t/972",[54,3.975,57,4.031]],["t/974",[54,3.346,57,3.394,335,2.901]],["t/976",[54,3.346,106,4.928,209,4.591]],["t/978",[54,2.889,414,4.145,457,4.049,458,4.731]],["t/980",[172,2.666,457,4.049,459,4.731,460,4.731]],["t/982",[54,3.975,461,6.508]],["t/984",[54,3.975,62,6.242]],["t/986",[57,4.031,462,6.863]],["t/988",[56,5.703,57,4.031]],["t/990",[56,4.801,57,3.394,335,2.901]],["t/992",[304,6.242,463,6.242]],["t/994",[335,4.243]],["t/998",[103,5.571,172,3.667]],["t/1000",[42,2.915,158,5.077,464,5.479]],["t/1002",[66,4.384,237,3.588,465,4.731,466,4.731]],["t/1004",[467,8.013]],["t/1008",[468,7.686]],["t/1010",[469,7.686]],["t/1012",[470,6.242,471,6.242]],["t/1016",[391,7.208]],["t/1018",[63,4.418]],["t/1020",[24,3.672]],["t/1022",[398,6.357]],["t/1024",[399,7.208]],["t/1026",[393,7.208]],["t/1028",[401,7.208]],["t/1030",[396,7.208]],["t/1032",[397,7.208]],["t/1034",[41,4.442]],["t/1038",[1,2.728,25,2.712,29,3.206,472,4.384]],["t/1040",[24,2.168,25,2.712,136,3.887,137,4.538]],["t/1042",[82,2.183,126,3.337,231,4.255,473,4.731]],["t/1046",[161,3.694,225,3.373,367,3.588,474,4.731]],["t/1048",[76,6.242,136,5.347]],["t/1050",[53,2.712,58,3.258,75,4.421]],["t/1052",[16,3.753,49,3.54,58,2.813,75,3.817]],["t/1056",[59,3.321,151,4.091]],["t/1058",[114,5.255,151,3.444,341,4.278]],["t/1060",[59,2.795,151,3.444,475,4.928]],["t/1062",[58,3.258,59,2.795,476,5.479]],["t/1064",[59,3.321,82,3.004]],["t/1066",[59,3.321,477,5.006]],["t/1068",[28,4.693,59,3.321]],["t/1070",[59,3.321,131,4.452]],["t/1072",[59,3.321,179,4.369]],["t/1074",[184,3.998,423,4.155,478,4.928]],["t/1076",[247,3.583,423,4.155,478,4.928]],["t/1078",[58,3.87,59,3.321]],["t/1080",[58,3.258,59,2.795,335,2.901]],["t/1082",[41,3.037,58,3.258,59,2.795]],["t/1084",[59,2.795,374,4.928,475,4.928]],["t/1086",[220,4.936,349,4.369]],["t/1088",[59,2.795,349,3.678,479,4.1]],["t/1090",[59,2.414,349,3.176,479,3.54,480,4.384]],["t/1092",[59,2.795,481,5.479,482,5.077]],["t/1094",[82,2.529,477,4.215,479,4.1]],["t/1096",[131,3.748,179,3.678,479,4.1]],["t/1098",[28,4.693,479,4.87]],["t/1104",[53,3.222,151,4.091]],["t/1106",[53,2.712,129,4.801,151,3.444]],["t/1108",[179,5.38]],["t/1110",[120,7.686]],["t/1112",[131,5.482]],["t/1114",[483,7.208]],["t/1116",[247,5.24]],["t/1118",[477,6.164]],["t/1120",[33,5.155]],["t/1122",[484,8.013]],["t/1124",[31,5.24]],["t/1126",[485,8.013]],["t/1128",[24,3.672]],["t/1130",[398,6.357]],["t/1132",[307,6.508,337,4.452]],["t/1134",[3,4.928,53,2.712,151,3.444]],["t/1136",[53,3.222,58,3.87]],["t/1138",[41,3.037,58,3.258,486,5.479]],["t/1140",[41,3.037,58,3.258,309,4.928]],["t/1142",[53,2.342,58,2.813,129,4.145,161,3.694]],["t/1146",[167,5.347,487,6.508]],["t/1148",[83,6.242,488,6.508]],["t/1150",[233,6.357]],["t/1152",[221,6.164]],["t/1154",[335,4.243]],["t/1155",[167,6.584]],["t/1157",[489,8.013]],["t/1161",[63,2.608,104,4.049,252,3.54,490,4.731]],["t/1163",[491,5.479,492,5.479,493,6.232]],["t/1165",[82,3.699]],["t/1167",[131,5.482]],["t/1169",[483,7.208]],["t/1171",[477,6.164]],["t/1173",[28,3.002,31,2.722,33,2.678,179,2.794,317,3.992]],["t/1175",[1,2.728,8,2.85,63,2.608,494,4.731]],["t/1177",[267,5.251,268,6.242]],["t/1179",[84,5.006,247,4.256]],["t/1181",[84,5.006,184,4.749]],["t/1183",[63,3.021,104,4.69,495,6.232]],["t/1185",[1,2.728,25,2.712,29,3.206,496,5.381]],["t/1187",[8,3.921,330,5.453]],["t/1189",[108,3.785,247,3.583,497,6.232]],["t/1191",[404,5.571,498,6.03]],["t/1195",[82,3.004,337,4.452]],["t/1197",[152,3.583,245,3.907,499,4.215]],["t/1199",[267,6.466]],["t/1201",[335,4.243]],["t/1202",[500,8.013]],["t/1204",[501,8.013]],["t/1206",[82,2.529,225,3.907,502,5.479]],["t/1208",[41,3.607,233,5.163]],["t/1212",[82,3.004,337,4.452]],["t/1214",[152,3.583,245,3.907,499,4.215]],["t/1216",[267,6.466]],["t/1218",[335,4.243]],["t/1219",[503,8.013]],["t/1221",[504,8.013]],["t/1223",[41,3.607,233,5.163]],["t/1227",[152,3.583,245,3.907,499,4.215]],["t/1229",[335,4.243]],["t/1230",[505,8.013]],["t/1232",[506,8.013]],["t/1234",[507,8.013]],["t/1236",[508,8.013]],["t/1238",[509,8.013]],["t/1240",[510,8.013]],["t/1242",[511,8.013]],["t/1244",[41,4.442]],["t/1248",[82,3.004,337,4.452]],["t/1250",[512,8.013]],["t/1252",[152,3.583,245,3.907,499,4.215]],["t/1254",[267,6.466]],["t/1256",[335,4.243]],["t/1257",[513,8.013]],["t/1259",[514,8.013]],["t/1261",[41,4.442]],["t/1265",[89,6.859]],["t/1267",[49,5.996]],["t/1269",[8,3.301,49,4.1,289,4.69]],["t/1273",[335,4.243]],["t/1274",[515,8.013]],["t/1276",[516,8.013]],["t/1278",[517,8.013]],["t/1280",[518,8.013]],["t/1282",[519,8.013]],["t/1284",[144,4.502,220,4.155,344,4.69]],["t/1288",[196,5.479,197,5.479,198,4.591]],["t/1292",[7,3.256,42,1.977,82,1.715,84,2.858,188,3.256,205,3.716]],["t/1294",[82,2.183,206,3.694,207,3.887,208,3.964]],["t/1296",[42,2.915,209,4.591,210,5.479]],["t/1298",[8,3.301,37,4.69,119,3.864]],["t/1300",[8,3.301,36,4.591,211,4.928]],["t/1302",[24,2.511,94,4.591,212,5.479]],["t/1304",[24,2.168,213,4.049,214,4.384,215,4.731]],["t/1306",[8,3.241,24,1.538,179,2.253,216,2.812,217,3.356,218,3.356]],["t/1308",[24,1.703,63,2.049,188,3.256,219,3.716,220,2.818,221,2.858]],["t/1310",[32,4.08,33,2.159,96,2.941,97,2.812,222,3.019,223,3.356]],["t/1312",[42,2.215,64,3.563,81,3.992,82,1.921,224,4.162]],["t/1314",[42,1.786,59,1.712,103,2.873,225,2.393,226,3.356,227,3.356,228,3.356]],["t/1316",[42,2.915,229,4.155,230,5.479]],["t/1318",[42,2.915,229,4.155,276,5.479]],["t/1320",[42,1.628,82,1.412,216,2.564,225,2.182,231,2.752,232,2.619,233,2.427,234,3.06]],["t/1322",[6,2.708,111,3.11,175,2.757,187,2.812,235,3.356,236,3.356,237,2.545]],["t/1324",[8,2.85,63,2.608,238,4.731,239,4.731]],["t/1326",[0,2.941,8,2.022,63,1.85,64,2.873,82,1.549,88,2.757,179,2.253]],["t/1328",[24,1.402,94,2.564,96,2.681,126,2.158,192,2.681,240,3.06,241,2.681,242,3.06]],["t/1330",[24,1.703,172,2.094,213,3.181,216,3.114,243,3.716,244,3.716]],["t/1332",[42,2.915,245,3.907,246,5.479]],["t/1334",[42,2.915,247,3.583,248,5.479]],["t/1336",[15,2.812,29,2.274,97,2.812,123,3.219,249,3.11,250,2.873,251,3.019]],["t/1338",[12,3.302,32,3.157,33,2.678,250,3.563,252,3.115]],["t/1340",[6,3.358,24,1.907,131,2.847,499,3.202,520,4.39]],["t/1342",[42,2.915,245,3.907,521,5.778]],["t/1344",[253,4.145,254,4.731,255,4.731,256,4.731]],["t/1352",[3,4.928,21,5.077,309,4.928]],["t/1354",[91,5.453,522,6.863]],["t/1358",[1,3.753,82,3.004]],["t/1360",[1,3.753,247,4.256]],["t/1362",[1,3.753,14,5.854]],["t/1364",[15,4.591,82,2.529,316,5.479]],["t/1366",[24,2.982,131,4.452]],["t/1368",[1,3.159,25,3.141,29,3.712]],["t/1370",[6,5.251,28,4.693]],["t/1372",[24,2.511,31,3.583,317,5.255]],["t/1374",[33,5.02,34,4.591]],["t/1378",[207,5.347,257,6.508]],["t/1380",[8,3.301,31,3.583,258,4.591]],["t/1382",[8,3.301,37,4.69,119,3.864]],["t/1384",[8,3.301,36,4.591,211,4.928]],["t/1386",[259,6.508,260,6.508]],["t/1388",[32,4.155,33,3.525,258,4.591]],["t/1390",[12,5.163,258,5.453]],["t/1394",[91,5.453,282,6.508]],["t/1396",[11,5.854,283,6.508]],["t/1398",[12,5.163,284,6.508]],["t/1400",[63,3.021,144,4.502,285,5.479]],["t/1402",[10,4.049,50,4.384,51,3.964,286,4.731]],["t/1404",[23,6.242,287,6.508]],["t/1406",[82,3.004,288,6.03]],["t/1408",[24,2.511,101,5.077,270,5.077]],["t/1410",[25,3.141,232,4.69,289,4.69]],["t/1412",[28,3.412,82,2.183,88,3.887,89,4.049]],["t/1414",[32,4.155,33,3.525,290,5.479]],["t/1416",[8,3.301,24,2.511,31,3.583]],["t/1418",[156,4.255,237,3.588,291,4.731,292,3.753]],["t/1420",[293,6.508,294,6.508]],["t/1422",[295,6.508,296,5.854]],["t/1424",[107,5.347,297,5.854]],["t/1426",[67,5.571,252,4.87]],["t/1430",[69,4.69,220,4.155,318,4.928]],["t/1432",[109,5.347,198,5.453]],["t/1434",[41,3.037,109,4.502,110,5.255]],["t/1436",[319,4.928,320,5.479,321,5.479]],["t/1438",[322,4.731,323,4.731,324,4.538,325,4.731]],["t/1440",[326,6.508,327,6.508]],["t/1442",[107,5.347,199,6.242]],["t/1446",[69,5.571,298,6.508]],["t/1448",[42,2.915,124,5.255,172,3.087]],["t/1450",[127,5.255,172,3.087,251,4.928]],["t/1452",[42,2.915,82,2.529,130,5.255]],["t/1454",[24,2.511,131,3.748,132,5.255]],["t/1456",[24,2.511,134,5.255,179,3.678]],["t/1458",[135,5.255,296,4.928,299,5.479]],["t/1460",[42,2.915,138,5.255,300,5.479]],["t/1462",[42,2.915,140,5.255,209,4.591]],["t/1464",[301,5.479,302,5.077,303,5.479]],["t/1468",[523,8.451]],["t/1472",[82,3.004,139,5.703]],["t/1474",[524,5.255,525,5.778,526,5.255]],["t/1476",[402,4.749,527,6.242]],["t/1478",[82,2.529,364,4.801,524,5.255]],["t/1480",[41,3.037,528,5.778,529,4.928]],["t/1482",[41,3.037,529,4.928,530,5.778]],["t/1484",[41,2.622,232,4.049,289,4.049,529,4.255]],["t/1486",[24,2.168,41,2.622,90,4.538,531,4.989]],["t/1488",[25,3.731,139,5.703]],["t/1490",[402,4.749,527,6.242]],["t/1492",[25,3.141,364,4.801,409,3.951]],["t/1498",[63,3.021,118,4.155,147,4.278]],["t/1500",[10,4.69,49,4.1,118,4.155]],["t/1502",[118,4.936,167,5.347]],["t/1504",[24,2.511,340,5.479,532,5.778]],["t/1506",[25,3.141,118,4.155,128,3.444]],["t/1508",[24,2.168,40,3.588,118,3.588,126,3.337]],["t/1510",[118,4.155,172,3.087,269,4.347]],["t/1512",[48,3.495,59,2.414,108,3.268,247,3.094]],["t/1514",[48,4.047,172,3.087,335,2.901]],["t/1516",[1,3.159,48,4.047,53,2.712]],["t/1520",[533,8.451]],["t/1522",[431,5.703,534,6.863]],["t/1524",[535,8.451]],["t/1526",[536,8.451]],["t/1528",[537,8.451]],["t/1532",[172,4.516]],["t/1534",[82,3.004,355,4.936]],["t/1536",[25,3.731,355,4.936]],["t/1538",[355,4.936,356,6.508]],["t/1540",[329,6.03,355,4.936]],["t/1542",[187,5.453,355,4.936]],["t/1544",[41,3.607,357,6.508]],["t/1546",[358,8.013]],["t/1548",[229,4.936,359,6.508]],["t/1552",[220,4.936,318,5.854]],["t/1554",[109,5.347,198,5.453]],["t/1556",[319,7.208]],["t/1558",[352,6.508,353,6.508]],["t/1560",[354,8.013]],["t/1562",[47,4.538,67,4.049,116,3.887,161,3.694]],["t/1566",[172,4.516]],["t/1568",[104,5.571,538,6.863]],["t/1570",[431,7.022]],["t/1572",[225,4.64,539,6.863]],["t/1576",[41,4.442]],["t/1578",[82,3.699]],["t/1582",[252,5.996]],["t/1584",[348,4.731,349,3.176,540,4.989,541,4.989]],["t/1586",[350,8.013]],["t/1590",[53,2.712,151,3.444,361,3.998]],["t/1592",[53,2.712,58,3.258,361,3.998]],["t/1594",[59,2.795,151,3.444,361,3.998]],["t/1596",[58,3.258,59,2.795,361,3.998]],["t/1598",[133,4.278,361,3.998,362,5.479]],["t/1600",[16,3.753,49,3.54,58,2.813,361,3.452]],["t/1602",[41,4.442]],["t/1604",[53,2.712,151,3.444,179,3.678]],["t/1606",[31,3.583,53,2.712,151,3.444]],["t/1610",[542,8.451]],["t/1612",[543,8.451]],["t/1614",[544,8.451]],["t/1622",[308,5.077,456,4.928,545,5.778]],["t/1624",[128,4.091,546,6.863]],["t/1626",[547,6.863,548,6.863]],["t/1628",[192,4.801,193,5.255,194,5.479]],["t/1630",[59,2.795,549,5.255,550,5.778]],["t/1632",[195,6.508,551,6.863]],["t/1634",[91,5.453,552,6.863]],["t/1636",[172,4.516]],["t/1638",[553,8.451]],["t/1640",[554,8.451]],["t/1642",[555,8.451]],["t/1644",[335,4.243]],["t/1646",[176,5.479,225,3.907,556,5.778]],["t/1648",[53,3.222,557,6.508]],["t/1649",[558,8.451]],["t/1651",[559,8.451]],["t/1653",[560,8.451]],["t/1655",[561,8.451]],["t/1657",[562,8.451]],["t/1659",[563,8.451]],["t/1661",[564,8.451]],["t/1663",[565,8.451]],["t/1665",[566,8.451]],["t/1667",[567,8.451]],["t/1669",[568,7.022]],["t/1671",[106,4.928,302,5.077,569,5.778]],["t/1675",[82,2.183,206,3.694,208,3.964,360,4.538]],["t/1677",[126,3.337,206,3.694,208,3.964,360,4.538]],["t/1679",[54,2.889,126,3.337,208,3.964,360,4.538]],["t/1681",[82,2.183,247,3.094,570,4.989,571,4.989]],["t/1685",[366,6.508,367,4.936]],["t/1689",[229,6.078]],["t/1691",[172,4.516]],["t/1693",[54,3.346,57,3.394,363,4.347]],["t/1695",[57,3.394,117,4.801,363,4.347]],["t/1697",[53,2.712,128,3.444,364,4.801]],["t/1698",[572,8.451]],["t/1700",[53,2.342,126,3.337,128,2.973,365,4.538]],["t/1701",[573,8.451]],["t/1705",[229,6.078]],["t/1707",[172,4.516]],["t/1709",[54,3.346,57,3.394,363,4.347]],["t/1711",[57,3.394,117,4.801,363,4.347]],["t/1713",[53,3.222,368,6.508]],["t/1714",[574,8.451]],["t/1716",[575,8.451]],["t/1720",[335,4.243]],["t/1722",[225,3.373,237,3.588,423,3.588,424,4.731]],["t/1724",[292,4.347,349,3.678,425,4.155]],["t/1728",[28,3.002,29,2.82,63,2.295,150,3.857,222,3.744]],["t/1730",[1,3.159,53,2.712,370,5.479]],["t/1732",[1,3.159,53,2.712,371,5.479]],["t/1734",[1,3.159,53,2.712,372,5.479]],["t/1736",[172,4.516]],["t/1740",[119,3.864,131,3.748,409,3.951]],["t/1742",[119,3.864,179,3.678,409,3.951]],["t/1744",[31,3.583,119,3.864,409,3.951]],["t/1746",[33,3.525,119,3.864,409,3.951]],["t/1748",[119,3.864,383,4.801,409,3.951]],["t/1750",[119,3.864,330,4.591,409,3.951]],["t/1752",[119,3.864,250,4.69,409,3.951]],["t/1756",[82,2.529,133,4.278,391,4.928]],["t/1758",[392,6.357]],["t/1760",[393,7.208]],["t/1762",[394,7.425]],["t/1764",[395,7.425]],["t/1766",[396,7.208]],["t/1768",[397,7.208]],["t/1770",[398,6.357]],["t/1772",[399,7.208]],["t/1774",[24,3.672]],["t/1776",[392,6.357]],["t/1778",[400,8.013]],["t/1780",[401,7.208]],["t/1782",[82,3.004,402,4.749]],["t/1784",[41,4.442]],["t/1786",[128,4.091,403,6.508]],["t/1788",[128,4.091,402,4.749]],["t/1790",[82,2.183,128,2.973,398,3.753,404,4.049]],["t/1792",[133,4.278,405,5.479,406,5.479]],["t/1794",[161,3.694,221,3.639,237,3.588,245,3.373]],["t/1796",[446,3.785,472,5.077,576,5.778]],["t/1800",[25,3.141,133,4.278,391,4.928]],["t/1802",[392,6.357]],["t/1804",[24,3.672]],["t/1806",[398,6.357]],["t/1808",[399,7.208]],["t/1810",[393,7.208]],["t/1812",[401,7.208]],["t/1814",[396,7.208]],["t/1816",[397,7.208]],["t/1818",[394,7.425]],["t/1820",[395,7.425]],["t/1822",[577,8.451]],["t/1824",[41,4.442]],["t/1826",[337,3.748,578,5.255,579,5.778]],["t/1830",[63,3.021,108,3.785,184,3.998]],["t/1832",[407,7.022]],["t/1834",[408,8.013]],["t/1836",[24,2.982,160,6.03]],["t/1838",[409,4.693,410,6.508]],["t/1840",[253,5.703,345,5.347]],["t/1842",[131,4.452,411,6.508]],["t/1844",[40,4.936,412,6.508]],["t/1846",[341,5.082,413,6.03]],["t/1848",[414,7.022]],["t/1850",[128,4.091,402,4.749]],["t/1852",[402,5.847]],["t/1854",[7,5.703,341,5.082]],["t/1856",[383,5.703,415,6.242]],["t/1858",[63,3.021,108,3.785,247,3.583]],["t/1860",[580,8.451]],["t/1862",[108,3.268,247,3.094,549,4.538,581,4.538]],["t/1864",[108,3.785,247,3.583,581,5.255]],["t/1868",[24,3.672]],["t/1870",[24,2.511,373,5.479,374,4.928]],["t/1872",[125,4.347,269,4.347,375,4.928]],["t/1874",[24,2.168,125,3.753,126,3.337,375,4.255]],["t/1876",[24,2.168,125,3.753,241,4.145,376,4.731]],["t/1878",[24,2.982,582,6.863]],["t/1880",[24,2.982,269,5.163]],["t/1882",[24,2.982,335,3.446]],["t/1884",[33,5.155]],["t/1886",[380,8.013]],["t/1888",[583,8.451]],["t/1890",[381,8.013]],["t/1892",[382,8.013]],["t/1894",[584,8.451]],["t/1896",[585,8.451]],["t/1898",[586,8.451]],["t/1900",[587,8.451]],["t/1902",[588,8.451]],["t/1904",[589,8.451]],["t/1906",[590,8.451]],["t/1908",[591,8.451]],["t/1910",[592,8.451]],["t/1912",[593,8.451]],["t/1914",[594,8.451]],["t/1916",[595,8.451]],["t/1918",[596,8.451]],["t/1920",[597,8.451]],["t/1922",[598,8.451]],["t/1924",[387,8.013]],["t/1926",[388,8.013]],["t/1928",[599,8.451]],["t/1930",[389,8.013]],["t/1932",[390,8.013]],["t/1934",[600,8.451]],["t/1936",[24,2.511,41,3.037,159,3.951]],["t/1940",[107,5.347,172,3.667]],["t/1942",[337,3.748,338,5.255,339,5.255]],["t/1944",[4,5.255,115,5.255,431,4.801]],["t/1946",[172,3.667,367,4.936]],["t/1948",[152,3.583,159,3.951,367,4.155]],["t/1950",[152,3.583,159,3.951,221,4.215]],["t/1952",[152,3.583,159,3.951,432,5.479]],["t/1954",[152,3.583,159,3.951,433,5.479]],["t/1956",[335,3.446,434,6.508]],["t/1958",[435,8.013]],["t/1960",[436,8.013]],["t/1962",[437,6.714]],["t/1964",[57,4.963]],["t/1966",[335,3.446,438,6.508]],["t/1968",[439,8.013]],["t/1970",[440,8.013]],["t/1972",[441,8.013]],["t/1974",[601,8.451]],["t/1976",[602,8.451]],["t/1978",[442,8.013]],["t/1980",[444,8.013]],["t/1982",[603,8.451]],["t/1984",[604,8.451]],["t/1986",[605,8.451]],["t/1988",[445,8.013]],["t/1990",[172,3.667,446,4.496]],["t/1992",[55,5.251,446,4.496]],["t/1994",[292,5.163,446,4.496]],["t/1996",[164,6.03,446,4.496]],["t/1998",[343,5.077,446,3.785,447,5.479]],["t/2000",[407,4.801,423,4.155,437,4.591]],["t/2002",[55,4.421,346,5.077,446,3.785]],["t/2004",[105,5.255,423,4.155,446,3.785]],["t/2006",[84,5.006,448,6.508]],["t/2008",[349,4.369,425,4.936]],["t/2009",[63,3.021,82,2.529,425,4.155]],["t/2011",[53,2.712,349,3.678,425,4.155]],["t/2013",[292,4.347,349,3.678,425,4.155]],["t/2015",[51,3.964,116,3.887,335,2.505,336,4.538]],["t/2017",[88,3.887,116,3.887,269,3.753,449,4.731]],["t/2019",[34,4.591,207,4.502,378,5.255]],["t/2023",[57,4.031,206,5.082]],["t/2025",[57,3.394,206,4.278,335,2.901]],["t/2027",[54,3.975,57,4.031]],["t/2029",[54,3.346,57,3.394,335,2.901]],["t/2031",[54,3.346,75,4.421,172,3.087]],["t/2033",[54,3.346,106,4.928,209,4.591]],["t/2035",[54,2.889,414,4.145,457,4.049,458,4.731]],["t/2037",[54,3.346,75,4.421,457,4.69]],["t/2039",[172,2.666,457,4.049,459,4.731,460,4.731]],["t/2041",[54,3.975,461,6.508]],["t/2043",[54,3.975,62,6.242]],["t/2045",[57,4.031,462,6.863]],["t/2047",[46,6.508,54,3.975]],["t/2049",[56,5.703,57,4.031]],["t/2051",[56,4.801,57,3.394,335,2.901]],["t/2053",[304,6.242,463,6.242]],["t/2055",[335,4.243]],["t/2059",[426,8.013]],["t/2061",[428,8.013]],["t/2063",[427,8.013]],["t/2065",[429,8.013]],["t/2067",[578,7.686]],["t/2069",[430,8.013]],["t/2071",[606,8.451]],["t/2075",[40,4.155,297,4.928,367,4.155]],["t/2077",[437,4.591,450,5.479,451,5.479]],["t/2079",[421,4.502,452,5.479,453,5.479]],["t/2081",[59,2.795,82,2.529,454,5.479]],["t/2083",[355,4.936,455,6.508]],["t/2085",[1,3.159,386,5.479,404,4.69]],["t/2089",[468,7.686]],["t/2091",[469,7.686]],["t/2093",[470,6.242,471,6.242]],["t/2097",[31,4.256,456,5.854]],["t/2099",[31,3.583,52,5.255,53,2.712]],["t/2101",[8,3.301,136,4.502,330,4.591]],["t/2105",[103,5.571,172,3.667]],["t/2107",[42,2.915,158,5.077,464,5.479]],["t/2109",[66,4.384,237,3.588,465,4.731,466,4.731]],["t/2111",[467,8.013]],["t/2115",[68,7.686]],["t/2117",[32,4.155,33,3.525,480,5.077]],["t/2119",[29,3.206,33,3.043,63,2.608,607,4.989]],["t/2121",[15,4.591,279,5.255,608,5.778]],["t/2123",[104,5.571,498,6.03]],["t/2125",[18,6.164]],["t/2129",[1,2.728,25,2.712,29,3.206,472,4.384]],["t/2131",[24,2.168,25,2.712,136,3.887,137,4.538]],["t/2133",[82,2.183,126,3.337,231,4.255,473,4.731]],["t/2137",[59,3.321,151,4.091]],["t/2139",[114,5.255,151,3.444,341,4.278]],["t/2141",[59,2.795,151,3.444,475,4.928]],["t/2143",[58,3.258,59,2.795,476,5.479]],["t/2145",[59,3.321,82,3.004]],["t/2147",[59,3.321,477,5.006]],["t/2149",[28,4.693,59,3.321]],["t/2151",[59,3.321,131,4.452]],["t/2153",[59,3.321,179,4.369]],["t/2155",[59,2.795,392,4.347,477,4.215]],["t/2157",[184,3.998,423,4.155,478,4.928]],["t/2159",[247,3.583,423,4.155,478,4.928]],["t/2161",[58,3.87,59,3.321]],["t/2163",[58,3.258,59,2.795,335,2.901]],["t/2165",[41,3.037,58,3.258,59,2.795]],["t/2167",[59,2.795,374,4.928,475,4.928]],["t/2169",[220,4.936,349,4.369]],["t/2171",[59,2.795,349,3.678,479,4.1]],["t/2173",[59,2.414,349,3.176,479,3.54,480,4.384]],["t/2175",[59,2.795,481,5.479,482,5.077]],["t/2177",[82,2.529,477,4.215,479,4.1]],["t/2179",[131,2.847,179,2.794,392,3.302,477,3.202,479,3.115]],["t/2181",[28,4.693,479,4.87]],["t/2185",[53,3.222,151,4.091]],["t/2187",[53,2.712,129,4.801,151,3.444]],["t/2189",[179,5.38]],["t/2191",[120,7.686]],["t/2193",[131,5.482]],["t/2195",[483,7.208]],["t/2197",[247,5.24]],["t/2199",[477,6.164]],["t/2201",[33,5.155]],["t/2203",[484,8.013]],["t/2205",[31,5.24]],["t/2207",[485,8.013]],["t/2209",[24,3.672]],["t/2211",[398,6.357]],["t/2213",[307,6.508,337,4.452]],["t/2215",[3,4.928,53,2.712,151,3.444]],["t/2217",[53,3.222,58,3.87]],["t/2219",[41,3.037,58,3.258,486,5.479]],["t/2221",[41,3.037,58,3.258,309,4.928]],["t/2223",[53,2.342,58,2.813,129,4.145,161,3.694]],["t/2227",[161,3.694,225,3.373,367,3.588,474,4.731]],["t/2229",[76,6.242,136,5.347]],["t/2231",[53,2.712,58,3.258,75,4.421]],["t/2233",[16,3.753,49,3.54,58,2.813,75,3.817]],["t/2237",[344,5.571,609,6.863]],["t/2239",[337,4.452,610,6.863]],["t/2241",[175,5.347,494,6.508]],["t/2243",[491,5.479,492,5.479,611,5.479]],["t/2245",[267,5.251,268,6.242]],["t/2247",[84,5.006,247,4.256]],["t/2249",[84,5.006,184,4.749]],["t/2251",[404,5.571,498,6.03]],["t/2255",[63,3.021,147,4.278,482,5.077]],["t/2257",[147,3.694,152,3.094,221,3.639,344,4.049]],["t/2259",[147,4.278,252,4.1,490,5.479]],["t/2261",[82,2.529,252,4.1,612,5.255]],["t/2263",[25,2.386,29,2.82,63,2.295,252,3.115,612,3.992]],["t/2267",[335,4.243]],["t/2268",[613,8.451]],["t/2270",[614,8.451]],["t/2274",[167,5.347,487,6.508]],["t/2276",[83,6.242,488,6.508]],["t/2278",[233,6.357]],["t/2280",[221,6.164]],["t/2282",[335,4.243]],["t/2283",[167,6.584]],["t/2285",[489,8.013]],["t/2289",[89,6.859]],["t/2291",[49,5.996]],["t/2293",[8,3.301,49,4.1,289,4.69]],["t/2295",[526,6.242,615,6.242]],["t/2299",[335,4.243]],["t/2300",[616,8.451]],["t/2302",[617,8.451]],["t/2306",[152,3.583,245,3.907,499,4.215]],["t/2308",[335,4.243]],["t/2309",[505,8.013]],["t/2311",[506,8.013]],["t/2313",[507,8.013]],["t/2315",[508,8.013]],["t/2317",[509,8.013]],["t/2319",[510,8.013]],["t/2321",[511,8.013]],["t/2323",[41,4.442]],["t/2327",[82,3.004,337,4.452]],["t/2329",[152,3.583,245,3.907,499,4.215]],["t/2331",[267,6.466]],["t/2333",[335,4.243]],["t/2334",[500,8.013]],["t/2336",[501,8.013]],["t/2338",[82,2.529,225,3.907,502,5.479]],["t/2340",[41,3.607,233,5.163]],["t/2344",[63,3.021,82,2.529,421,4.502]],["t/2346",[63,3.021,335,2.901,618,4.928]],["t/2348",[63,3.588,345,5.347]],["t/2350",[63,3.021,82,2.529,128,3.444]],["t/2352",[82,3.004,615,6.242]],["t/2354",[25,3.731,421,5.347]],["t/2356",[25,3.731,288,6.03]],["t/2358",[24,2.511,249,5.077,329,5.077]],["t/2360",[25,3.141,330,4.591,421,4.502]],["t/2362",[25,3.141,335,2.901,618,4.928]],["t/2364",[25,3.731,345,5.347]],["t/2366",[25,3.731,128,4.091]],["t/2368",[1,3.159,25,3.141,29,3.712]],["t/2370",[108,4.496,184,4.749]],["t/2372",[108,4.496,483,5.854]],["t/2374",[108,4.496,247,4.256]],["t/2376",[28,5.779]],["t/2378",[24,2.511,31,3.583,53,2.712]],["t/2380",[33,4.512,34,3.964,53,2.342]],["t/2382",[147,3.694,213,4.049,214,4.384,618,4.255]],["t/2386",[82,3.004,337,4.452]],["t/2388",[152,3.583,245,3.907,499,4.215]],["t/2390",[335,4.243]],["t/2391",[503,8.013]],["t/2393",[504,8.013]],["t/2395",[41,3.607,233,5.163]],["t/2399",[82,3.004,337,4.452]],["t/2401",[512,8.013]],["t/2403",[152,3.583,245,3.907,499,4.215]],["t/2405",[267,6.466]],["t/2407",[335,4.243]],["t/2408",[513,8.013]],["t/2410",[514,8.013]],["t/2412",[41,4.442]],["t/2416",[196,5.479,197,5.479,198,4.591]],["t/2420",[335,4.243]],["t/2421",[515,8.013]],["t/2423",[516,8.013]],["t/2425",[517,8.013]],["t/2427",[518,8.013]],["t/2429",[519,8.013]],["t/2431",[144,4.502,220,4.155,344,4.69]],["t/2439",[7,3.256,42,1.977,82,1.715,84,2.858,188,3.256,205,3.716]],["t/2441",[82,2.183,206,3.694,207,3.887,208,3.964]],["t/2443",[42,2.915,209,4.591,210,5.479]],["t/2445",[8,3.301,37,4.69,119,3.864]],["t/2447",[8,3.301,36,4.591,211,4.928]],["t/2449",[24,2.511,94,4.591,212,5.479]],["t/2451",[24,2.168,213,4.049,214,4.384,215,4.731]],["t/2453",[8,3.241,24,1.538,179,2.253,216,2.812,217,3.356,218,3.356]],["t/2455",[24,1.703,63,2.049,188,3.256,219,3.716,220,2.818,221,2.858]],["t/2457",[32,4.08,33,2.159,96,2.941,97,2.812,222,3.019,223,3.356]],["t/2459",[42,2.215,64,3.563,81,3.992,82,1.921,224,4.162]],["t/2461",[42,1.786,59,1.712,103,2.873,225,2.393,226,3.356,227,3.356,228,3.356]],["t/2463",[42,2.915,229,4.155,230,5.479]],["t/2465",[42,2.915,229,4.155,276,5.479]],["t/2467",[42,1.628,82,1.412,216,2.564,225,2.182,231,2.752,232,2.619,233,2.427,234,3.06]],["t/2469",[6,2.708,111,3.11,175,2.757,187,2.812,235,3.356,236,3.356,237,2.545]],["t/2471",[8,2.85,63,2.608,238,4.731,239,4.731]],["t/2473",[0,2.941,8,2.022,63,1.85,64,2.873,82,1.549,88,2.757,179,2.253]],["t/2475",[24,1.402,94,2.564,96,2.681,126,2.158,192,2.681,240,3.06,241,2.681,242,3.06]],["t/2477",[24,1.703,172,2.094,213,3.181,216,3.114,243,3.716,244,3.716]],["t/2479",[42,2.915,245,3.907,246,5.479]],["t/2481",[42,2.915,247,3.583,248,5.479]],["t/2483",[15,2.812,29,2.274,97,2.812,123,3.219,249,3.11,250,2.873,251,3.019]],["t/2485",[12,3.302,32,3.157,33,2.678,250,3.563,252,3.115]],["t/2487",[6,3.358,24,1.907,131,2.847,499,3.202,520,4.39]],["t/2489",[42,2.915,245,3.907,521,5.778]],["t/2491",[253,4.145,254,4.731,255,4.731,256,4.731]],["t/2497",[42,2.915,619,5.077,620,6.232]],["t/2499",[42,2.215,619,3.857,621,4.735,622,4.735,623,4.735]],["t/2501",[42,2.915,54,3.346,619,5.077]],["t/2503",[42,2.915,619,5.077,624,6.232]],["t/2505",[42,2.915,463,5.255,619,5.077]],["t/2509",[1,3.753,82,3.004]],["t/2511",[1,3.753,247,4.256]],["t/2513",[1,3.753,14,5.854]],["t/2515",[15,4.591,82,2.529,316,5.479]],["t/2517",[24,2.982,131,4.452]],["t/2519",[1,3.159,25,3.141,29,3.712]],["t/2521",[6,5.251,28,4.693]],["t/2523",[24,2.511,31,3.583,317,5.255]],["t/2525",[33,5.02,34,4.591]],["t/2529",[207,5.347,257,6.508]],["t/2531",[8,3.301,31,3.583,258,4.591]],["t/2533",[8,3.301,37,4.69,119,3.864]],["t/2535",[8,3.301,36,4.591,211,4.928]],["t/2537",[259,6.508,260,6.508]],["t/2539",[32,4.155,33,3.525,258,4.591]],["t/2541",[12,5.163,258,5.453]],["t/2545",[3,4.928,21,5.077,309,4.928]],["t/2547",[91,5.453,522,6.863]],["t/2551",[91,5.453,282,6.508]],["t/2553",[11,5.854,283,6.508]],["t/2555",[12,5.163,284,6.508]],["t/2557",[63,3.021,144,4.502,285,5.479]],["t/2559",[10,4.049,50,4.384,51,3.964,286,4.731]],["t/2561",[23,6.242,287,6.508]],["t/2563",[82,3.004,288,6.03]],["t/2565",[24,2.511,101,5.077,270,5.077]],["t/2567",[25,3.141,232,4.69,289,4.69]],["t/2569",[28,3.412,82,2.183,88,3.887,89,4.049]],["t/2571",[32,4.155,33,3.525,290,5.479]],["t/2573",[8,3.301,24,2.511,31,3.583]],["t/2575",[156,4.255,237,3.588,291,4.731,292,3.753]],["t/2577",[293,6.508,294,6.508]],["t/2579",[295,6.508,296,5.854]],["t/2581",[107,5.347,297,5.854]],["t/2583",[67,5.571,252,4.87]],["t/2587",[69,5.571,298,6.508]],["t/2589",[42,2.915,124,5.255,172,3.087]],["t/2591",[127,5.255,172,3.087,251,4.928]],["t/2593",[42,2.915,82,2.529,130,5.255]],["t/2595",[24,2.511,131,3.748,132,5.255]],["t/2597",[24,2.511,134,5.255,179,3.678]],["t/2599",[135,5.255,296,4.928,299,5.479]],["t/2601",[42,2.915,138,5.255,300,5.479]],["t/2603",[42,2.915,140,5.255,209,4.591]],["t/2605",[301,5.479,302,5.077,303,5.479]],["t/2609",[523,8.451]],["t/2613",[69,4.69,220,4.155,318,4.928]],["t/2615",[109,5.347,198,5.453]],["t/2617",[41,3.037,109,4.502,110,5.255]],["t/2619",[319,4.928,320,5.479,321,5.479]],["t/2621",[322,4.731,323,4.731,324,4.538,325,4.731]],["t/2623",[326,6.508,327,6.508]],["t/2625",[107,5.347,199,6.242]],["t/2629",[63,3.021,118,4.155,147,4.278]],["t/2631",[10,4.69,49,4.1,118,4.155]],["t/2633",[118,4.936,167,5.347]],["t/2635",[24,2.511,340,5.479,532,5.778]],["t/2637",[25,3.141,118,4.155,128,3.444]],["t/2639",[24,2.168,40,3.588,118,3.588,126,3.337]],["t/2641",[118,4.155,172,3.087,269,4.347]],["t/2643",[48,3.495,59,2.414,108,3.268,247,3.094]],["t/2645",[48,4.047,172,3.087,335,2.901]],["t/2647",[1,3.159,48,4.047,53,2.712]],["t/2651",[63,2.295,128,2.616,145,4.39,146,4.39,147,3.25]],["t/2653",[22,4.731,48,3.495,87,4.384,152,3.094]],["t/2655",[53,2.061,143,4.39,151,2.616,152,2.722,625,4.735]],["t/2657",[48,5.92]],["t/2659",[42,1.977,43,3.716,66,3.443,626,4.227,627,4.227,628,4.227]],["t/2665",[82,3.004,139,5.703]],["t/2667",[524,5.255,525,5.778,526,5.255]],["t/2669",[402,4.749,527,6.242]],["t/2671",[82,2.529,364,4.801,524,5.255]],["t/2673",[41,3.037,528,5.778,529,4.928]],["t/2675",[41,3.037,529,4.928,530,5.778]],["t/2677",[41,2.622,232,4.049,289,4.049,529,4.255]],["t/2679",[24,2.168,41,2.622,90,4.538,531,4.989]],["t/2681",[25,3.731,139,5.703]],["t/2683",[402,4.749,527,6.242]],["t/2685",[25,3.141,364,4.801,409,3.951]],["t/2689",[533,8.451]],["t/2691",[431,5.703,534,6.863]],["t/2693",[535,8.451]],["t/2695",[536,8.451]],["t/2697",[537,8.451]],["t/2701",[172,4.516]],["t/2703",[82,3.004,355,4.936]],["t/2705",[25,3.731,355,4.936]],["t/2707",[355,4.936,356,6.508]],["t/2709",[329,6.03,355,4.936]],["t/2711",[187,5.453,355,4.936]],["t/2713",[41,3.607,357,6.508]],["t/2715",[358,8.013]],["t/2717",[229,4.936,359,6.508]],["t/2719",[568,5.703,629,6.508]],["t/2725",[172,4.516]],["t/2727",[104,5.571,538,6.863]],["t/2729",[431,7.022]],["t/2731",[225,4.64,539,6.863]],["t/2735",[542,8.451]],["t/2737",[543,8.451]],["t/2739",[544,8.451]],["t/2743",[68,7.686]],["t/2745",[172,4.516]],["t/2747",[53,3.222,557,6.508]],["t/2749",[341,5.082,630,6.863]],["t/2751",[630,6.863,631,7.403]],["t/2755",[220,4.936,318,5.854]],["t/2757",[109,5.347,198,5.453]],["t/2759",[319,7.208]],["t/2761",[352,6.508,353,6.508]],["t/2763",[354,8.013]],["t/2765",[47,4.538,67,4.049,116,3.887,161,3.694]],["t/2769",[41,4.442]],["t/2771",[82,3.699]],["t/2777",[252,5.996]],["t/2779",[350,8.013]],["t/2781",[348,4.731,349,3.176,540,4.989,541,4.989]],["t/2785",[53,2.712,151,3.444,361,3.998]],["t/2787",[53,2.712,58,3.258,361,3.998]],["t/2789",[59,2.795,151,3.444,361,3.998]],["t/2791",[58,3.258,59,2.795,361,3.998]],["t/2793",[133,4.278,361,3.998,362,5.479]],["t/2795",[16,3.753,49,3.54,58,2.813,361,3.452]],["t/2797",[41,4.442]],["t/2799",[53,2.712,151,3.444,179,3.678]],["t/2801",[31,3.583,53,2.712,151,3.444]],["t/2809",[366,6.508,367,4.936]],["t/2813",[229,6.078]],["t/2815",[172,4.516]],["t/2817",[54,3.346,57,3.394,363,4.347]],["t/2819",[57,3.394,117,4.801,363,4.347]],["t/2821",[53,2.712,128,3.444,364,4.801]],["t/2822",[572,8.451]],["t/2824",[53,2.342,126,3.337,128,2.973,365,4.538]],["t/2825",[573,8.451]],["t/2829",[40,3.157,82,1.921,206,3.25,208,3.488,632,4.162]],["t/2831",[40,3.157,126,2.936,206,3.25,208,3.488,632,4.162]],["t/2833",[40,3.157,54,2.542,126,2.936,208,3.488,632,4.162]],["t/2835",[82,2.183,247,3.094,570,4.989,571,4.989]],["t/2839",[308,5.077,456,4.928,545,5.778]],["t/2841",[128,4.091,546,6.863]],["t/2843",[547,6.863,548,6.863]],["t/2845",[192,4.801,193,5.255,194,5.479]],["t/2847",[59,2.795,549,5.255,550,5.778]],["t/2849",[195,6.508,551,6.863]],["t/2851",[91,5.453,552,6.863]],["t/2853",[172,4.516]],["t/2855",[553,8.451]],["t/2857",[554,8.451]],["t/2859",[555,8.451]],["t/2861",[335,4.243]],["t/2863",[176,5.479,225,3.907,556,5.778]],["t/2865",[53,3.222,557,6.508]],["t/2866",[558,8.451]],["t/2868",[559,8.451]],["t/2870",[560,8.451]],["t/2872",[561,8.451]],["t/2874",[562,8.451]],["t/2876",[563,8.451]],["t/2878",[564,8.451]],["t/2880",[565,8.451]],["t/2882",[566,8.451]],["t/2884",[633,9.116]],["t/2886",[567,8.451]],["t/2888",[568,5.703,629,6.508]],["t/2890",[106,4.928,302,5.077,569,5.778]],["t/2894",[229,6.078]],["t/2896",[172,4.516]],["t/2898",[54,3.346,57,3.394,363,4.347]],["t/2900",[57,3.394,117,4.801,363,4.347]],["t/2902",[53,3.222,368,6.508]],["t/2903",[574,8.451]],["t/2905",[575,8.451]],["t/2909",[335,4.243]],["t/2911",[225,3.373,237,3.588,423,3.588,424,4.731]],["t/2913",[292,4.347,349,3.678,425,4.155]],["t/2917",[28,3.002,29,2.82,63,2.295,150,3.857,222,3.744]],["t/2919",[1,3.159,53,2.712,370,5.479]],["t/2921",[1,3.159,53,2.712,371,5.479]],["t/2923",[1,3.159,53,2.712,372,5.479]],["t/2925",[172,4.516]],["t/2929",[119,3.864,131,3.748,409,3.951]],["t/2931",[119,3.864,179,3.678,409,3.951]],["t/2933",[31,3.583,119,3.864,409,3.951]],["t/2935",[33,3.525,119,3.864,409,3.951]],["t/2937",[119,3.864,383,4.801,409,3.951]],["t/2939",[119,3.864,330,4.591,409,3.951]],["t/2941",[119,3.864,250,4.69,409,3.951]],["t/2945",[82,2.529,133,4.278,391,4.928]],["t/2947",[392,6.357]],["t/2949",[393,7.208]],["t/2951",[394,7.425]],["t/2953",[395,7.425]],["t/2955",[396,7.208]],["t/2957",[397,7.208]],["t/2959",[398,6.357]],["t/2961",[399,7.208]],["t/2963",[24,3.672]],["t/2965",[392,6.357]],["t/2967",[400,8.013]],["t/2969",[401,7.208]],["t/2971",[82,3.004,402,4.749]],["t/2973",[41,4.442]],["t/2975",[128,4.091,403,6.508]],["t/2977",[128,4.091,402,4.749]],["t/2979",[82,2.183,128,2.973,398,3.753,404,4.049]],["t/2981",[133,4.278,405,5.479,406,5.479]],["t/2983",[161,3.694,221,3.639,237,3.588,245,3.373]],["t/2985",[446,3.785,472,5.077,576,5.778]],["t/2989",[25,3.141,133,4.278,391,4.928]],["t/2991",[392,6.357]],["t/2993",[24,3.672]],["t/2995",[398,6.357]],["t/2997",[399,7.208]],["t/2999",[393,7.208]],["t/3001",[401,7.208]],["t/3003",[396,7.208]],["t/3005",[397,7.208]],["t/3007",[394,7.425]],["t/3009",[395,7.425]],["t/3011",[577,8.451]],["t/3013",[41,4.442]],["t/3015",[337,3.748,578,5.255,579,5.778]],["t/3017",[25,2.712,128,2.973,158,4.384,159,3.412]],["t/3021",[63,3.021,108,3.785,184,3.998]],["t/3023",[407,7.022]],["t/3025",[408,8.013]],["t/3027",[24,2.982,160,6.03]],["t/3029",[409,4.693,410,6.508]],["t/3031",[253,5.703,345,5.347]],["t/3033",[131,4.452,411,6.508]],["t/3035",[40,4.936,412,6.508]],["t/3037",[341,5.082,413,6.03]],["t/3039",[414,7.022]],["t/3041",[128,4.091,402,4.749]],["t/3043",[402,5.847]],["t/3045",[7,5.703,341,5.082]],["t/3047",[383,5.703,415,6.242]],["t/3049",[63,3.021,108,3.785,247,3.583]],["t/3051",[580,8.451]],["t/3053",[108,3.268,247,3.094,549,4.538,581,4.538]],["t/3055",[108,3.785,247,3.583,581,5.255]],["t/3059",[24,3.672]],["t/3061",[24,2.511,373,5.479,374,4.928]],["t/3063",[125,4.347,269,4.347,375,4.928]],["t/3065",[24,2.168,125,3.753,126,3.337,375,4.255]],["t/3067",[24,2.168,125,3.753,241,4.145,376,4.731]],["t/3069",[24,2.982,582,6.863]],["t/3071",[24,2.982,269,5.163]],["t/3073",[24,2.982,335,3.446]],["t/3075",[33,5.155]],["t/3077",[380,8.013]],["t/3079",[583,8.451]],["t/3081",[381,8.013]],["t/3083",[382,8.013]],["t/3085",[148,8.451]],["t/3087",[584,8.451]],["t/3089",[585,8.451]],["t/3091",[586,8.451]],["t/3093",[587,8.451]],["t/3095",[588,8.451]],["t/3097",[589,8.451]],["t/3099",[590,8.451]],["t/3101",[591,8.451]],["t/3103",[592,8.451]],["t/3105",[593,8.451]],["t/3107",[594,8.451]],["t/3109",[595,8.451]],["t/3111",[596,8.451]],["t/3113",[597,8.451]],["t/3115",[598,8.451]],["t/3117",[387,8.013]],["t/3119",[388,8.013]],["t/3121",[599,8.451]],["t/3123",[634,9.116]],["t/3125",[389,8.013]],["t/3127",[390,8.013]],["t/3129",[600,8.451]],["t/3131",[635,9.116]],["t/3133",[24,2.511,41,3.037,159,3.951]],["t/3137",[426,8.013]],["t/3139",[428,8.013]],["t/3141",[427,8.013]],["t/3143",[429,8.013]],["t/3145",[578,7.686]],["t/3147",[430,8.013]],["t/3149",[606,8.451]],["t/3153",[107,5.347,172,3.667]],["t/3155",[337,3.748,338,5.255,339,5.255]],["t/3157",[4,5.255,115,5.255,431,4.801]],["t/3159",[172,3.667,367,4.936]],["t/3161",[152,3.583,159,3.951,367,4.155]],["t/3163",[152,3.583,159,3.951,221,4.215]],["t/3165",[152,3.583,159,3.951,432,5.479]],["t/3167",[152,3.583,159,3.951,433,5.479]],["t/3169",[335,3.446,434,6.508]],["t/3171",[435,8.013]],["t/3173",[436,8.013]],["t/3175",[437,6.714]],["t/3177",[57,4.963]],["t/3179",[335,3.446,438,6.508]],["t/3181",[439,8.013]],["t/3183",[440,8.013]],["t/3185",[441,8.013]],["t/3187",[601,8.451]],["t/3189",[602,8.451]],["t/3191",[442,8.013]],["t/3193",[444,8.013]],["t/3195",[603,8.451]],["t/3197",[636,9.116]],["t/3199",[604,8.451]],["t/3201",[605,8.451]],["t/3203",[445,8.013]],["t/3205",[172,3.667,446,4.496]],["t/3207",[55,5.251,446,4.496]],["t/3209",[292,5.163,446,4.496]],["t/3211",[164,6.03,446,4.496]],["t/3213",[343,5.077,446,3.785,447,5.479]],["t/3215",[1,2.728,53,2.342,155,4.989,156,4.255]],["t/3217",[407,4.801,423,4.155,437,4.591]],["t/3219",[55,4.421,346,5.077,446,3.785]],["t/3221",[105,5.255,423,4.155,446,3.785]],["t/3223",[84,5.006,448,6.508]],["t/3225",[349,4.369,425,4.936]],["t/3226",[63,3.021,82,2.529,425,4.155]],["t/3228",[63,2.295,128,2.616,343,3.857,346,3.857,637,4.735]],["t/3230",[53,2.712,349,3.678,425,4.155]],["t/3232",[292,4.347,349,3.678,425,4.155]],["t/3234",[51,3.964,116,3.887,335,2.505,336,4.538]],["t/3236",[88,3.887,116,3.887,269,3.753,449,4.731]],["t/3238",[34,4.591,207,4.502,378,5.255]],["t/3242",[40,4.155,297,4.928,367,4.155]],["t/3244",[437,4.591,450,5.479,451,5.479]],["t/3246",[421,4.502,452,5.479,453,5.479]],["t/3248",[59,2.795,82,2.529,454,5.479]],["t/3250",[355,4.936,455,6.508]],["t/3252",[1,3.159,386,5.479,404,4.69]],["t/3256",[31,4.256,456,5.854]],["t/3258",[31,3.583,52,5.255,53,2.712]],["t/3260",[8,3.301,136,4.502,330,4.591]],["t/3264",[57,4.031,206,5.082]],["t/3266",[57,3.394,206,4.278,335,2.901]],["t/3268",[54,3.975,57,4.031]],["t/3270",[54,3.346,57,3.394,335,2.901]],["t/3272",[54,3.346,75,4.421,172,3.087]],["t/3274",[54,3.346,106,4.928,209,4.591]],["t/3276",[54,2.889,414,4.145,457,4.049,458,4.731]],["t/3278",[54,3.346,75,4.421,457,4.69]],["t/3280",[172,2.666,457,4.049,459,4.731,460,4.731]],["t/3282",[54,3.975,461,6.508]],["t/3284",[54,3.975,62,6.242]],["t/3286",[46,6.508,54,3.975]],["t/3288",[56,5.703,57,4.031]],["t/3290",[56,4.801,57,3.394,335,2.901]],["t/3292",[304,6.242,463,6.242]],["t/3294",[335,4.243]],["t/3298",[468,7.686]],["t/3300",[469,7.686]],["t/3302",[470,6.242,471,6.242]],["t/3306",[103,5.571,172,3.667]],["t/3308",[42,2.915,158,5.077,464,5.479]],["t/3310",[66,4.384,237,3.588,465,4.731,466,4.731]],["t/3312",[467,8.013]],["t/3316",[568,7.022]],["t/3317",[468,6.242,568,5.703]],["t/3319",[469,6.242,568,5.703]],["t/3321",[470,6.242,471,6.242]],["t/3323",[568,5.703,629,6.508]],["t/3325",[638,9.116]],["t/3326",[157,8.451]],["t/3328",[639,9.116]],["t/3332",[32,4.155,33,3.525,480,5.077]],["t/3334",[29,3.206,33,3.043,63,2.608,607,4.989]],["t/3336",[15,4.591,279,5.255,608,5.778]],["t/3338",[104,5.571,498,6.03]],["t/3340",[18,6.164]],["t/3344",[12,5.163,181,6.863]],["t/3346",[308,6.03,456,5.854]],["t/3348",[16,4.347,25,3.141,49,4.1]],["t/3350",[16,4.347,25,3.141,89,4.69]],["t/3352",[59,2.795,349,3.678,479,4.1]],["t/3354",[41,3.607,90,6.242]],["t/3358",[1,2.728,25,2.712,29,3.206,472,4.384]],["t/3360",[24,2.168,25,2.712,136,3.887,137,4.538]],["t/3362",[82,2.183,126,3.337,231,4.255,473,4.731]],["t/3366",[59,3.321,151,4.091]],["t/3368",[114,5.255,151,3.444,341,4.278]],["t/3370",[59,2.795,151,3.444,475,4.928]],["t/3372",[58,3.258,59,2.795,476,5.479]],["t/3374",[59,3.321,82,3.004]],["t/3376",[59,3.321,477,5.006]],["t/3378",[28,4.693,59,3.321]],["t/3380",[59,3.321,131,4.452]],["t/3382",[59,3.321,179,4.369]],["t/3384",[59,2.795,392,4.347,477,4.215]],["t/3386",[184,3.998,423,4.155,478,4.928]],["t/3388",[247,3.583,423,4.155,478,4.928]],["t/3390",[58,3.87,59,3.321]],["t/3392",[58,3.258,59,2.795,335,2.901]],["t/3394",[41,3.037,58,3.258,59,2.795]],["t/3396",[59,2.795,374,4.928,475,4.928]],["t/3398",[220,4.936,349,4.369]],["t/3400",[59,2.795,349,3.678,479,4.1]],["t/3402",[59,2.414,349,3.176,479,3.54,480,4.384]],["t/3404",[59,2.795,481,5.479,482,5.077]],["t/3406",[82,2.529,477,4.215,479,4.1]],["t/3408",[131,2.847,179,2.794,392,3.302,477,3.202,479,3.115]],["t/3410",[28,4.693,479,4.87]],["t/3414",[161,3.694,225,3.373,367,3.588,474,4.731]],["t/3416",[76,6.242,136,5.347]],["t/3418",[53,2.712,58,3.258,75,4.421]],["t/3420",[16,3.753,49,3.54,58,2.813,75,3.817]],["t/3424",[53,3.222,151,4.091]],["t/3426",[53,2.712,129,4.801,151,3.444]],["t/3428",[53,3.222,345,5.347]],["t/3430",[179,5.38]],["t/3432",[120,7.686]],["t/3434",[131,5.482]],["t/3436",[483,7.208]],["t/3438",[247,5.24]],["t/3440",[477,6.164]],["t/3442",[33,5.155]],["t/3444",[484,8.013]],["t/3446",[31,5.24]],["t/3448",[485,8.013]],["t/3450",[24,3.672]],["t/3452",[398,6.357]],["t/3454",[611,8.013]],["t/3456",[3,4.928,53,2.712,151,3.444]],["t/3458",[53,3.222,58,3.87]],["t/3460",[41,3.037,58,3.258,486,5.479]],["t/3462",[41,3.037,58,3.258,309,4.928]],["t/3464",[53,2.342,58,2.813,129,4.145,161,3.694]],["t/3466",[10,4.69,184,3.998,349,3.678]],["t/3468",[42,2.215,108,4.4,151,2.616,184,3.037]],["t/3470",[42,2.215,58,2.475,108,4.4,184,3.037]],["t/3474",[335,4.243]],["t/3475",[613,8.451]],["t/3477",[614,8.451]],["t/3481",[89,6.859]],["t/3483",[49,5.996]],["t/3485",[526,6.242,615,6.242]],["t/3489",[63,3.021,147,4.278,482,5.077]],["t/3491",[147,3.694,152,3.094,221,3.639,344,4.049]],["t/3493",[147,4.278,252,4.1,490,5.479]],["t/3495",[82,2.529,252,4.1,612,5.255]],["t/3497",[25,2.386,29,2.82,63,2.295,252,3.115,612,3.992]],["t/3501",[167,5.347,487,6.508]],["t/3503",[83,6.242,488,6.508]],["t/3505",[233,6.357]],["t/3507",[221,6.164]],["t/3509",[335,4.243]],["t/3510",[167,6.584]],["t/3512",[489,8.013]],["t/3516",[335,4.243]],["t/3517",[616,8.451]],["t/3519",[617,8.451]],["t/3523",[8,3.301,49,4.1,289,4.69]],["t/3527",[152,3.583,245,3.907,499,4.215]],["t/3529",[335,4.243]],["t/3530",[505,8.013]],["t/3532",[506,8.013]],["t/3534",[507,8.013]],["t/3536",[508,8.013]],["t/3538",[509,8.013]],["t/3540",[510,8.013]],["t/3542",[511,8.013]],["t/3544",[41,4.442]],["t/3548",[344,5.571,609,6.863]],["t/3550",[337,4.452,610,6.863]],["t/3552",[175,5.347,494,6.508]],["t/3554",[491,5.479,492,5.479,611,5.479]],["t/3556",[267,5.251,268,6.242]],["t/3558",[84,5.006,247,4.256]],["t/3560",[84,5.006,184,4.749]],["t/3562",[404,5.571,498,6.03]],["t/3566",[82,3.004,337,4.452]],["t/3568",[512,8.013]],["t/3570",[152,3.583,245,3.907,499,4.215]],["t/3572",[267,6.466]],["t/3574",[335,4.243]],["t/3575",[513,8.013]],["t/3577",[514,8.013]],["t/3579",[41,4.442]],["t/3583",[82,3.004,337,4.452]],["t/3585",[152,3.583,245,3.907,499,4.215]],["t/3587",[267,6.466]],["t/3589",[335,4.243]],["t/3590",[500,8.013]],["t/3592",[501,8.013]],["t/3594",[82,2.529,225,3.907,502,5.479]],["t/3596",[41,3.607,233,5.163]],["t/3600",[82,3.004,337,4.452]],["t/3602",[152,3.583,245,3.907,499,4.215]],["t/3604",[335,4.243]],["t/3605",[503,8.013]],["t/3607",[504,8.013]],["t/3609",[41,3.607,233,5.163]],["t/3615",[335,4.243]],["t/3616",[515,8.013]],["t/3618",[516,8.013]],["t/3620",[517,8.013]],["t/3622",[518,8.013]],["t/3624",[519,8.013]],["t/3626",[144,4.502,220,4.155,344,4.69]],["t/3628",[164,4.384,165,4.989,640,5.381,641,5.381]],["t/3632",[63,3.021,82,2.529,421,4.502]],["t/3634",[63,3.021,335,2.901,618,4.928]],["t/3636",[63,3.588,345,5.347]],["t/3638",[63,3.021,82,2.529,128,3.444]],["t/3640",[82,3.004,615,6.242]],["t/3642",[25,3.731,421,5.347]],["t/3644",[25,3.731,288,6.03]],["t/3646",[24,2.511,249,5.077,329,5.077]],["t/3648",[25,3.141,330,4.591,421,4.502]],["t/3650",[25,3.141,335,2.901,618,4.928]],["t/3652",[25,3.731,345,5.347]],["t/3654",[25,3.731,128,4.091]],["t/3656",[1,3.159,25,3.141,29,3.712]],["t/3658",[108,4.496,184,4.749]],["t/3660",[108,4.496,483,5.854]],["t/3662",[108,4.496,247,4.256]],["t/3664",[28,5.779]],["t/3666",[24,2.511,31,3.583,53,2.712]],["t/3668",[33,4.512,34,3.964,53,2.342]],["t/3670",[147,3.694,213,4.049,214,4.384,618,4.255]]],"invertedIndex":[["",{"_index":125,"t":{"175":{"position":[[3,1]]},"187":{"position":[[3,1]]},"189":{"position":[[3,1]]},"691":{"position":[[19,3]]},"693":{"position":[[23,3]]},"695":{"position":[[22,3]]},"1872":{"position":[[19,3]]},"1874":{"position":[[22,3]]},"1876":{"position":[[23,3]]},"3063":{"position":[[19,3]]},"3065":{"position":[[22,3]]},"3067":{"position":[[23,3]]}}}],["0",{"_index":298,"t":{"446":{"position":[[0,2]]},"1446":{"position":[[0,2]]},"2587":{"position":[[0,2]]}}}],["1",{"_index":124,"t":{"175":{"position":[[0,2]]},"448":{"position":[[0,2]]},"1448":{"position":[[0,2]]},"2589":{"position":[[0,2]]}}}],["2",{"_index":127,"t":{"177":{"position":[[0,2]]},"450":{"position":[[0,2]]},"1450":{"position":[[0,2]]},"2591":{"position":[[0,2]]}}}],["3",{"_index":130,"t":{"179":{"position":[[0,2]]},"452":{"position":[[0,2]]},"1452":{"position":[[0,2]]},"2593":{"position":[[0,2]]}}}],["4",{"_index":132,"t":{"181":{"position":[[0,2]]},"454":{"position":[[0,2]]},"1454":{"position":[[0,2]]},"2595":{"position":[[0,2]]}}}],["5",{"_index":134,"t":{"183":{"position":[[0,2]]},"456":{"position":[[0,2]]},"1456":{"position":[[0,2]]},"2597":{"position":[[0,2]]}}}],["6",{"_index":135,"t":{"185":{"position":[[0,2]]},"458":{"position":[[0,2]]},"1458":{"position":[[0,2]]},"2599":{"position":[[0,2]]}}}],["7",{"_index":138,"t":{"187":{"position":[[0,2]]},"460":{"position":[[0,2]]},"1460":{"position":[[0,2]]},"2601":{"position":[[0,2]]}}}],["8",{"_index":140,"t":{"189":{"position":[[0,2]]},"462":{"position":[[0,2]]},"1462":{"position":[[0,2]]},"2603":{"position":[[0,2]]}}}],["9",{"_index":301,"t":{"464":{"position":[[0,2]]},"1464":{"position":[[0,2]]},"2605":{"position":[[0,2]]}}}],["abli",{"_index":622,"t":{"2499":{"position":[[22,5]]}}}],["abus",{"_index":570,"t":{"1681":{"position":[[14,7]]},"2835":{"position":[[14,7]]}}}],["accept",{"_index":81,"t":{"105":{"position":[[0,6]]},"326":{"position":[[17,9]]},"1312":{"position":[[17,9]]},"2459":{"position":[[17,9]]}}}],["access",{"_index":531,"t":{"1486":{"position":[[14,6]]},"2679":{"position":[[14,6]]}}}],["ad",{"_index":102,"t":{"133":{"position":[[0,6]]},"286":{"position":[[0,6]]},"482":{"position":[[0,6]]}}}],["add",{"_index":98,"t":{"127":{"position":[[0,3]]},"129":{"position":[[0,3]]}}}],["addit",{"_index":404,"t":{"771":{"position":[[11,10]]},"1191":{"position":[[0,10]]},"1790":{"position":[[11,10]]},"2085":{"position":[[0,10]]},"2251":{"position":[[0,10]]},"2979":{"position":[[11,10]]},"3252":{"position":[[0,10]]},"3562":{"position":[[0,10]]}}}],["address",{"_index":436,"t":{"893":{"position":[[0,7]]},"1960":{"position":[[0,7]]},"3173":{"position":[[0,7]]}}}],["adjust",{"_index":146,"t":{"197":{"position":[[16,11]]},"2651":{"position":[[27,11]]}}}],["admin",{"_index":292,"t":{"434":{"position":[[9,5]]},"857":{"position":[[0,5]]},"919":{"position":[[0,5]]},"938":{"position":[[9,5]]},"1418":{"position":[[9,5]]},"1724":{"position":[[0,5]]},"1994":{"position":[[0,5]]},"2013":{"position":[[9,5]]},"2575":{"position":[[9,5]]},"2913":{"position":[[0,5]]},"3209":{"position":[[0,5]]},"3232":{"position":[[9,5]]}}}],["advanc",{"_index":438,"t":{"899":{"position":[[0,8]]},"1966":{"position":[[0,8]]},"3179":{"position":[[0,8]]}}}],["advic",{"_index":495,"t":{"1183":{"position":[[22,7]]}}}],["allow_anonymous_connect_without_token",{"_index":604,"t":{"1984":{"position":[[0,37]]},"3199":{"position":[[0,37]]}}}],["allow_history_for_anonym",{"_index":593,"t":{"1912":{"position":[[0,27]]},"3105":{"position":[[0,27]]}}}],["allow_history_for_cli",{"_index":592,"t":{"1910":{"position":[[0,24]]},"3103":{"position":[[0,24]]}}}],["allow_history_for_subscrib",{"_index":591,"t":{"1908":{"position":[[0,28]]},"3101":{"position":[[0,28]]}}}],["allow_presence_for_anonym",{"_index":596,"t":{"1918":{"position":[[0,28]]},"3111":{"position":[[0,28]]}}}],["allow_presence_for_cli",{"_index":595,"t":{"1916":{"position":[[0,25]]},"3109":{"position":[[0,25]]}}}],["allow_presence_for_subscrib",{"_index":594,"t":{"1914":{"position":[[0,29]]},"3107":{"position":[[0,29]]}}}],["allow_publish_for_anonym",{"_index":590,"t":{"1906":{"position":[[0,27]]},"3099":{"position":[[0,27]]}}}],["allow_publish_for_cli",{"_index":589,"t":{"1904":{"position":[[0,24]]},"3097":{"position":[[0,24]]}}}],["allow_publish_for_subscrib",{"_index":588,"t":{"1902":{"position":[[0,28]]},"3095":{"position":[[0,28]]}}}],["allow_subscribe_for_anonym",{"_index":587,"t":{"1900":{"position":[[0,29]]},"3093":{"position":[[0,29]]}}}],["allow_subscribe_for_cli",{"_index":586,"t":{"1898":{"position":[[0,26]]},"3091":{"position":[[0,26]]}}}],["allow_user_limited_channel",{"_index":597,"t":{"1920":{"position":[[0,27]]},"3113":{"position":[[0,27]]}}}],["allowed_origin",{"_index":435,"t":{"891":{"position":[[0,15]]},"1958":{"position":[[0,15]]},"3171":{"position":[[0,15]]}}}],["alreadi",{"_index":411,"t":{"791":{"position":[[0,7]]},"1842":{"position":[[0,7]]},"3033":{"position":[[0,7]]}}}],["analyt",{"_index":195,"t":{"265":{"position":[[5,9]]},"1632":{"position":[[8,9]]},"2849":{"position":[[8,9]]}}}],["anonym",{"_index":378,"t":{"703":{"position":[[0,9]]},"944":{"position":[[0,9]]},"2019":{"position":[[0,9]]},"3238":{"position":[[0,9]]}}}],["answer",{"_index":254,"t":{"352":{"position":[[20,6]]},"1344":{"position":[[20,6]]},"2491":{"position":[[20,6]]}}}],["api",{"_index":53,"t":{"69":{"position":[[18,3]]},"77":{"position":[[7,3]]},"203":{"position":[[9,3]]},"207":{"position":[[25,3]]},"251":{"position":[[33,3]]},"253":{"position":[[19,3]]},"376":{"position":[[19,3]]},"392":{"position":[[19,3]]},"555":{"position":[[13,3]]},"557":{"position":[[9,3]]},"621":{"position":[[12,3]]},"623":{"position":[[12,3]]},"635":{"position":[[13,3]]},"637":{"position":[[13,3]]},"651":{"position":[[13,3]]},"653":{"position":[[23,3]]},"669":{"position":[[11,3]]},"671":{"position":[[13,3]]},"679":{"position":[[26,3]]},"681":{"position":[[23,3]]},"683":{"position":[[26,3]]},"837":{"position":[[7,3]]},"936":{"position":[[9,3]]},"962":{"position":[[18,3]]},"1050":{"position":[[13,3]]},"1104":{"position":[[5,3]]},"1106":{"position":[[5,3]]},"1134":{"position":[[5,3]]},"1136":{"position":[[5,3]]},"1142":{"position":[[5,3]]},"1516":{"position":[[7,3]]},"1590":{"position":[[12,3]]},"1592":{"position":[[12,3]]},"1604":{"position":[[13,3]]},"1606":{"position":[[13,3]]},"1648":{"position":[[0,3]]},"1697":{"position":[[13,3]]},"1700":{"position":[[23,3]]},"1713":{"position":[[7,3]]},"1730":{"position":[[26,3]]},"1732":{"position":[[23,3]]},"1734":{"position":[[26,3]]},"2011":{"position":[[9,3]]},"2099":{"position":[[18,3]]},"2185":{"position":[[5,3]]},"2187":{"position":[[5,3]]},"2215":{"position":[[5,3]]},"2217":{"position":[[5,3]]},"2223":{"position":[[5,3]]},"2231":{"position":[[13,3]]},"2378":{"position":[[16,3]]},"2380":{"position":[[28,3]]},"2647":{"position":[[7,3]]},"2655":{"position":[[9,3]]},"2747":{"position":[[0,3]]},"2785":{"position":[[12,3]]},"2787":{"position":[[12,3]]},"2799":{"position":[[13,3]]},"2801":{"position":[[13,3]]},"2821":{"position":[[13,3]]},"2824":{"position":[[23,3]]},"2865":{"position":[[0,3]]},"2902":{"position":[[7,3]]},"2919":{"position":[[26,3]]},"2921":{"position":[[23,3]]},"2923":{"position":[[26,3]]},"3215":{"position":[[22,3]]},"3230":{"position":[[9,3]]},"3258":{"position":[[18,3]]},"3418":{"position":[[13,3]]},"3424":{"position":[[5,3]]},"3426":{"position":[[5,3]]},"3428":{"position":[[0,3]]},"3456":{"position":[[5,3]]},"3458":{"position":[[5,3]]},"3464":{"position":[[5,3]]},"3666":{"position":[[16,3]]},"3668":{"position":[[28,3]]}}}],["apn",{"_index":555,"t":{"1642":{"position":[[0,4]]},"2859":{"position":[[0,4]]}}}],["app",{"_index":97,"t":{"125":{"position":[[18,3]]},"282":{"position":[[20,3]]},"300":{"position":[[6,3]]},"324":{"position":[[25,4]]},"348":{"position":[[53,3]]},"1310":{"position":[[25,4]]},"1336":{"position":[[53,3]]},"2457":{"position":[[25,4]]},"2483":{"position":[[53,3]]}}}],["applic",{"_index":111,"t":{"145":{"position":[[0,11]]},"151":{"position":[[0,11]]},"334":{"position":[[59,13]]},"1322":{"position":[[59,13]]},"2469":{"position":[[59,13]]}}}],["ascii",{"_index":532,"t":{"1504":{"position":[[8,5]]},"2635":{"position":[[8,5]]}}}],["aspect",{"_index":35,"t":{"45":{"position":[[12,7]]}}}],["async",{"_index":26,"t":{"35":{"position":[[0,5]]},"233":{"position":[[12,5]]}}}],["asynchron",{"_index":494,"t":{"1175":{"position":[[0,12]]},"2241":{"position":[[0,12]]},"3552":{"position":[[0,12]]}}}],["aud",{"_index":396,"t":{"747":{"position":[[0,3]]},"1030":{"position":[[0,3]]},"1766":{"position":[[0,3]]},"1814":{"position":[[0,3]]},"2955":{"position":[[0,3]]},"3003":{"position":[[0,3]]}}}],["authent",{"_index":23,"t":{"31":{"position":[[0,14]]},"420":{"position":[[9,14]]},"1404":{"position":[[9,14]]},"2561":{"position":[[9,14]]}}}],["author",{"_index":129,"t":{"177":{"position":[[19,13]]},"1106":{"position":[[9,13]]},"1142":{"position":[[13,13]]},"2187":{"position":[[9,13]]},"2223":{"position":[[13,13]]},"3426":{"position":[[9,13]]},"3464":{"position":[[13,13]]}}}],["automat",{"_index":136,"t":{"185":{"position":[[5,9]]},"964":{"position":[[0,9]]},"1040":{"position":[[0,9]]},"1048":{"position":[[0,9]]},"2101":{"position":[[0,9]]},"2131":{"position":[[0,9]]},"2229":{"position":[[0,9]]},"3260":{"position":[[0,9]]},"3360":{"position":[[0,9]]},"3416":{"position":[[0,9]]}}}],["avail",{"_index":414,"t":{"797":{"position":[[4,9]]},"847":{"position":[[4,9]]},"978":{"position":[[24,12]]},"1848":{"position":[[4,9]]},"2035":{"position":[[24,12]]},"3039":{"position":[[4,9]]},"3276":{"position":[[24,12]]}}}],["b64info",{"_index":399,"t":{"753":{"position":[[0,7]]},"1024":{"position":[[0,7]]},"1772":{"position":[[0,7]]},"1808":{"position":[[0,7]]},"2961":{"position":[[0,7]]},"2997":{"position":[[0,7]]}}}],["backend",{"_index":251,"t":{"348":{"position":[[57,7]]},"450":{"position":[[18,7]]},"1336":{"position":[[57,7]]},"1450":{"position":[[18,7]]},"2483":{"position":[[57,7]]},"2591":{"position":[[18,7]]}}}],["background",{"_index":523,"t":{"1468":{"position":[[0,10]]},"2609":{"position":[[0,10]]}}}],["backward",{"_index":45,"t":{"63":{"position":[[0,9]]}}}],["bad",{"_index":413,"t":{"795":{"position":[[0,3]]},"815":{"position":[[0,3]]},"845":{"position":[[0,3]]},"1846":{"position":[[0,3]]},"3037":{"position":[[0,3]]}}}],["batch",{"_index":611,"t":{"2243":{"position":[[10,8]]},"3454":{"position":[[0,5]]},"3554":{"position":[[10,8]]}}}],["befor",{"_index":228,"t":{"328":{"position":[[54,6]]},"1314":{"position":[[54,6]]},"2461":{"position":[[54,6]]}}}],["behavior",{"_index":526,"t":{"1474":{"position":[[16,8]]},"2295":{"position":[[10,8]]},"2667":{"position":[[16,8]]},"3485":{"position":[[10,8]]}}}],["behaviour",{"_index":145,"t":{"197":{"position":[[6,9]]},"2651":{"position":[[17,9]]}}}],["benefit",{"_index":17,"t":{"19":{"position":[[21,8]]}}}],["bentho",{"_index":171,"t":{"225":{"position":[[16,7]]},"227":{"position":[[10,7]]}}}],["best",{"_index":213,"t":{"318":{"position":[[11,4]]},"342":{"position":[[11,4]]},"1304":{"position":[[11,4]]},"1330":{"position":[[11,4]]},"2382":{"position":[[11,4]]},"2451":{"position":[[11,4]]},"2477":{"position":[[11,4]]},"3670":{"position":[[11,4]]}}}],["better",{"_index":61,"t":{"79":{"position":[[0,6]]},"392":{"position":[[0,6]]}}}],["bidirect",{"_index":89,"t":{"111":{"position":[[19,13]]},"428":{"position":[[9,13]]},"1265":{"position":[[0,13]]},"1412":{"position":[[9,13]]},"2289":{"position":[[0,13]]},"2569":{"position":[[9,13]]},"3350":{"position":[[0,13]]},"3481":{"position":[[0,13]]}}}],["binari",{"_index":220,"t":{"322":{"position":[[16,6]]},"522":{"position":[[17,6]]},"587":{"position":[[0,6]]},"1086":{"position":[[0,6]]},"1284":{"position":[[9,6]]},"1308":{"position":[[16,6]]},"1430":{"position":[[17,6]]},"1552":{"position":[[0,6]]},"2169":{"position":[[0,6]]},"2431":{"position":[[9,6]]},"2455":{"position":[[16,6]]},"2613":{"position":[[17,6]]},"2755":{"position":[[0,6]]},"3398":{"position":[[0,6]]},"3626":{"position":[[9,6]]}}}],["block",{"_index":368,"t":{"669":{"position":[[0,5]]},"1713":{"position":[[0,5]]},"2902":{"position":[[0,5]]}}}],["block_us",{"_index":574,"t":{"1714":{"position":[[0,10]]},"2903":{"position":[[0,10]]}}}],["both",{"_index":219,"t":{"322":{"position":[[11,4]]},"1308":{"position":[[11,4]]},"2455":{"position":[[11,4]]}}}],["boundari",{"_index":375,"t":{"691":{"position":[[10,8]]},"695":{"position":[[13,8]]},"1872":{"position":[[10,8]]},"1874":{"position":[[13,8]]},"3063":{"position":[[10,8]]},"3065":{"position":[[13,8]]}}}],["brew",{"_index":326,"t":{"532":{"position":[[5,4]]},"1440":{"position":[[5,4]]},"2623":{"position":[[5,4]]}}}],["broadcast",{"_index":120,"t":{"157":{"position":[[0,12]]},"1110":{"position":[[0,9]]},"2191":{"position":[[0,9]]},"3432":{"position":[[0,9]]}}}],["broker",{"_index":304,"t":{"468":{"position":[[0,6]]},"992":{"position":[[5,6]]},"2053":{"position":[[5,6]]},"3292":{"position":[[5,6]]}}}],["browser",{"_index":233,"t":{"332":{"position":[[71,7]]},"1150":{"position":[[0,7]]},"1208":{"position":[[0,7]]},"1223":{"position":[[0,7]]},"1320":{"position":[[71,7]]},"2278":{"position":[[0,7]]},"2340":{"position":[[0,7]]},"2395":{"position":[[0,7]]},"2467":{"position":[[71,7]]},"3505":{"position":[[0,7]]},"3596":{"position":[[0,7]]},"3609":{"position":[[0,7]]}}}],["build",{"_index":199,"t":{"280":{"position":[[12,8]]},"534":{"position":[[0,5]]},"1442":{"position":[[0,5]]},"2625":{"position":[[0,5]]}}}],["built",{"_index":284,"t":{"414":{"position":[[0,5]]},"1398":{"position":[[0,5]]},"2555":{"position":[[0,5]]}}}],["builtin",{"_index":551,"t":{"1632":{"position":[[0,7]]},"2849":{"position":[[0,7]]}}}],["call",{"_index":317,"t":{"510":{"position":[[0,4]]},"1173":{"position":[[9,6]]},"1372":{"position":[[0,4]]},"2523":{"position":[[0,4]]}}}],["canari",{"_index":71,"t":{"95":{"position":[[15,6]]}}}],["cancel_push",{"_index":633,"t":{"2884":{"position":[[0,11]]}}}],["cap",{"_index":524,"t":{"1474":{"position":[[0,4]]},"1478":{"position":[[20,4]]},"2667":{"position":[[0,4]]},"2671":{"position":[[20,4]]}}}],["capabl",{"_index":139,"t":{"187":{"position":[[5,12]]},"189":{"position":[[5,12]]},"390":{"position":[[8,12]]},"1472":{"position":[[11,12]]},"1488":{"position":[[13,12]]},"2665":{"position":[[11,12]]},"2681":{"position":[[13,12]]}}}],["case",{"_index":242,"t":{"340":{"position":[[64,6]]},"1328":{"position":[[64,6]]},"2475":{"position":[[64,6]]}}}],["caveat",{"_index":487,"t":{"1146":{"position":[[7,7]]},"2274":{"position":[[7,7]]},"3501":{"position":[[7,7]]}}}],["cento",{"_index":354,"t":{"595":{"position":[[0,6]]},"1560":{"position":[[0,6]]},"2763":{"position":[[0,6]]}}}],["centrifug",{"_index":21,"t":{"27":{"position":[[11,11]]},"29":{"position":[[0,10]]},"217":{"position":[[12,11]]},"1352":{"position":[[0,10]]},"2545":{"position":[[0,10]]}}}],["centrifugo",{"_index":42,"t":{"61":{"position":[[0,10]]},"87":{"position":[[0,10]]},"119":{"position":[[26,10]]},"131":{"position":[[9,10]]},"147":{"position":[[27,11]]},"159":{"position":[[17,10]]},"223":{"position":[[6,10]]},"249":{"position":[[0,10]]},"284":{"position":[[9,10]]},"298":{"position":[[0,10]]},"306":{"position":[[29,10]]},"310":{"position":[[4,10]]},"326":{"position":[[0,10]]},"328":{"position":[[10,10]]},"330":{"position":[[5,10]]},"332":{"position":[[45,10]]},"344":{"position":[[5,10]]},"346":{"position":[[4,10]]},"374":{"position":[[0,10]]},"448":{"position":[[13,10]]},"452":{"position":[[14,10]]},"460":{"position":[[11,10]]},"462":{"position":[[9,10]]},"1000":{"position":[[20,10]]},"1292":{"position":[[29,10]]},"1296":{"position":[[4,10]]},"1312":{"position":[[0,10]]},"1314":{"position":[[10,10]]},"1316":{"position":[[5,10]]},"1318":{"position":[[5,10]]},"1320":{"position":[[45,10]]},"1332":{"position":[[5,10]]},"1334":{"position":[[4,10]]},"1342":{"position":[[5,10]]},"1448":{"position":[[13,10]]},"1452":{"position":[[14,10]]},"1460":{"position":[[11,10]]},"1462":{"position":[[9,10]]},"2107":{"position":[[20,10]]},"2439":{"position":[[29,10]]},"2443":{"position":[[4,10]]},"2459":{"position":[[0,10]]},"2461":{"position":[[10,10]]},"2463":{"position":[[5,10]]},"2465":{"position":[[5,10]]},"2467":{"position":[[45,10]]},"2479":{"position":[[5,10]]},"2481":{"position":[[4,10]]},"2489":{"position":[[5,10]]},"2497":{"position":[[0,10]]},"2499":{"position":[[0,10]]},"2501":{"position":[[0,10]]},"2503":{"position":[[0,10]]},"2505":{"position":[[0,10]]},"2589":{"position":[[13,10]]},"2593":{"position":[[14,10]]},"2601":{"position":[[11,10]]},"2603":{"position":[[9,10]]},"2659":{"position":[[14,10]]},"3308":{"position":[[20,10]]},"3468":{"position":[[0,10]]},"3470":{"position":[[0,10]]}}}],["certif",{"_index":76,"t":{"97":{"position":[[25,12]]},"1048":{"position":[[10,12]]},"2229":{"position":[[10,12]]},"3416":{"position":[[10,12]]}}}],["chang",{"_index":48,"t":{"65":{"position":[[8,6]]},"538":{"position":[[12,7]]},"546":{"position":[[12,7]]},"549":{"position":[[16,7]]},"557":{"position":[[20,7]]},"559":{"position":[[11,7]]},"561":{"position":[[4,7]]},"563":{"position":[[20,7]]},"569":{"position":[[20,7]]},"1512":{"position":[[22,7]]},"1514":{"position":[[27,7]]},"1516":{"position":[[11,7]]},"2643":{"position":[[22,7]]},"2645":{"position":[[27,7]]},"2647":{"position":[[11,7]]},"2653":{"position":[[26,7]]},"2657":{"position":[[6,7]]}}}],["channel",{"_index":24,"t":{"33":{"position":[[0,7]]},"41":{"position":[[20,7]]},"175":{"position":[[18,7]]},"177":{"position":[[5,7]]},"181":{"position":[[17,7]]},"183":{"position":[[17,7]]},"185":{"position":[[24,7]]},"316":{"position":[[16,8]]},"318":{"position":[[45,9]]},"320":{"position":[[69,8]]},"322":{"position":[[47,8]]},"340":{"position":[[23,7]]},"342":{"position":[[32,7]]},"384":{"position":[[18,7]]},"386":{"position":[[8,7]]},"390":{"position":[[0,7]]},"424":{"position":[[0,7]]},"432":{"position":[[19,8]]},"454":{"position":[[16,8]]},"456":{"position":[[14,7]]},"504":{"position":[[15,7]]},"510":{"position":[[5,7]]},"549":{"position":[[0,7]]},"557":{"position":[[0,8]]},"689":{"position":[[0,7]]},"693":{"position":[[8,7]]},"695":{"position":[[5,7]]},"697":{"position":[[0,7]]},"731":{"position":[[0,7]]},"733":{"position":[[0,7]]},"755":{"position":[[0,8]]},"785":{"position":[[8,7]]},"841":{"position":[[8,7]]},"1020":{"position":[[0,7]]},"1040":{"position":[[19,7]]},"1128":{"position":[[0,8]]},"1302":{"position":[[16,8]]},"1304":{"position":[[45,9]]},"1306":{"position":[[69,8]]},"1308":{"position":[[47,8]]},"1328":{"position":[[23,7]]},"1330":{"position":[[32,7]]},"1340":{"position":[[28,7]]},"1366":{"position":[[15,7]]},"1372":{"position":[[5,7]]},"1408":{"position":[[0,7]]},"1416":{"position":[[19,8]]},"1454":{"position":[[16,8]]},"1456":{"position":[[14,7]]},"1486":{"position":[[28,8]]},"1504":{"position":[[0,7]]},"1508":{"position":[[13,7]]},"1774":{"position":[[0,8]]},"1804":{"position":[[0,7]]},"1836":{"position":[[8,7]]},"1868":{"position":[[8,7]]},"1870":{"position":[[0,7]]},"1874":{"position":[[5,7]]},"1876":{"position":[[8,7]]},"1878":{"position":[[0,7]]},"1880":{"position":[[0,7]]},"1882":{"position":[[0,7]]},"1936":{"position":[[0,7]]},"2131":{"position":[[19,7]]},"2209":{"position":[[0,8]]},"2358":{"position":[[10,7]]},"2378":{"position":[[0,7]]},"2449":{"position":[[16,8]]},"2451":{"position":[[45,9]]},"2453":{"position":[[69,8]]},"2455":{"position":[[47,8]]},"2475":{"position":[[23,7]]},"2477":{"position":[[32,7]]},"2487":{"position":[[28,7]]},"2517":{"position":[[15,7]]},"2523":{"position":[[5,7]]},"2565":{"position":[[0,7]]},"2573":{"position":[[19,8]]},"2595":{"position":[[16,8]]},"2597":{"position":[[16,7]]},"2635":{"position":[[0,7]]},"2639":{"position":[[13,7]]},"2679":{"position":[[28,8]]},"2963":{"position":[[0,8]]},"2993":{"position":[[0,7]]},"3027":{"position":[[8,7]]},"3059":{"position":[[10,7]]},"3061":{"position":[[0,7]]},"3065":{"position":[[5,7]]},"3067":{"position":[[8,7]]},"3069":{"position":[[0,7]]},"3071":{"position":[[0,7]]},"3073":{"position":[[0,7]]},"3133":{"position":[[0,7]]},"3360":{"position":[[19,7]]},"3450":{"position":[[0,8]]},"3646":{"position":[[10,7]]},"3666":{"position":[[0,7]]}}}],["channel_max_length",{"_index":440,"t":{"903":{"position":[[0,18]]},"1970":{"position":[[0,18]]},"3183":{"position":[[0,18]]}}}],["channel_regex",{"_index":598,"t":{"1922":{"position":[[0,13]]},"3115":{"position":[[0,13]]}}}],["chart",{"_index":321,"t":{"528":{"position":[[16,5]]},"1436":{"position":[[16,5]]},"2619":{"position":[[16,5]]}}}],["chat",{"_index":96,"t":{"125":{"position":[[13,4]]},"324":{"position":[[20,4]]},"340":{"position":[[59,4]]},"1310":{"position":[[20,4]]},"1328":{"position":[[59,4]]},"2457":{"position":[[20,4]]},"2475":{"position":[[59,4]]}}}],["check",{"_index":343,"t":{"553":{"position":[[24,5]]},"923":{"position":[[7,5]]},"1998":{"position":[[7,5]]},"3213":{"position":[[7,5]]},"3228":{"position":[[31,5]]}}}],["checkconfig",{"_index":427,"t":{"863":{"position":[[0,11]]},"2063":{"position":[[0,11]]},"3141":{"position":[[0,11]]}}}],["checksubtoken",{"_index":606,"t":{"2071":{"position":[[0,13]]},"3149":{"position":[[0,13]]}}}],["checktoken",{"_index":430,"t":{"869":{"position":[[0,10]]},"2069":{"position":[[0,10]]},"3147":{"position":[[0,10]]}}}],["choic",{"_index":545,"t":{"1622":{"position":[[22,7]]},"2839":{"position":[[22,7]]}}}],["chrome",{"_index":70,"t":{"95":{"position":[[8,6]]}}}],["claim",{"_index":391,"t":{"737":{"position":[[0,6]]},"1016":{"position":[[0,6]]},"1756":{"position":[[15,6]]},"1800":{"position":[[17,6]]},"2945":{"position":[[15,6]]},"2989":{"position":[[17,6]]}}}],["cli",{"_index":579,"t":{"1826":{"position":[[17,3]]},"3015":{"position":[[12,3]]}}}],["client",{"_index":63,"t":{"81":{"position":[[0,6]]},"99":{"position":[[4,6]]},"109":{"position":[[8,6]]},"165":{"position":[[0,6]]},"195":{"position":[[13,6]]},"322":{"position":[[32,7]]},"336":{"position":[[43,7]]},"338":{"position":[[62,7]]},"376":{"position":[[8,6]]},"380":{"position":[[15,6]]},"394":{"position":[[11,6]]},"416":{"position":[[7,6]]},"538":{"position":[[0,6]]},"677":{"position":[[0,6]]},"779":{"position":[[0,6]]},"807":{"position":[[0,6]]},"934":{"position":[[9,6]]},"1018":{"position":[[0,6]]},"1161":{"position":[[0,6]]},"1175":{"position":[[23,6]]},"1183":{"position":[[0,6]]},"1308":{"position":[[32,7]]},"1324":{"position":[[43,7]]},"1326":{"position":[[62,7]]},"1400":{"position":[[7,6]]},"1498":{"position":[[0,6]]},"1728":{"position":[[0,6]]},"1830":{"position":[[0,6]]},"1858":{"position":[[0,6]]},"2009":{"position":[[9,6]]},"2119":{"position":[[27,6]]},"2255":{"position":[[8,6]]},"2263":{"position":[[0,6]]},"2344":{"position":[[0,6]]},"2346":{"position":[[0,6]]},"2348":{"position":[[0,6]]},"2350":{"position":[[0,6]]},"2455":{"position":[[32,7]]},"2471":{"position":[[43,7]]},"2473":{"position":[[62,7]]},"2557":{"position":[[7,6]]},"2629":{"position":[[0,6]]},"2651":{"position":[[0,6]]},"2917":{"position":[[0,6]]},"3021":{"position":[[0,6]]},"3049":{"position":[[0,6]]},"3226":{"position":[[9,6]]},"3228":{"position":[[8,6]]},"3334":{"position":[[27,6]]},"3489":{"position":[[8,6]]},"3497":{"position":[[0,6]]},"3632":{"position":[[0,6]]},"3634":{"position":[[0,6]]},"3636":{"position":[[0,6]]},"3638":{"position":[[0,6]]}}}],["client_anonym",{"_index":443,"t":{"909":{"position":[[0,16]]}}}],["client_channel_limit",{"_index":439,"t":{"901":{"position":[[0,20]]},"1968":{"position":[[0,20]]},"3181":{"position":[[0,20]]}}}],["client_concurr",{"_index":444,"t":{"911":{"position":[[0,18]]},"1980":{"position":[[0,18]]},"3193":{"position":[[0,18]]}}}],["client_connection_limit",{"_index":601,"t":{"1974":{"position":[[0,23]]},"3187":{"position":[[0,23]]}}}],["client_connection_rate_limit",{"_index":602,"t":{"1976":{"position":[[0,28]]},"3189":{"position":[[0,28]]}}}],["client_max_messages_in_fram",{"_index":544,"t":{"1614":{"position":[[0,28]]},"2739":{"position":[[0,28]]}}}],["client_queue_max_s",{"_index":442,"t":{"907":{"position":[[0,21]]},"1978":{"position":[[0,21]]},"3191":{"position":[[0,21]]}}}],["client_reply_without_queu",{"_index":543,"t":{"1612":{"position":[[0,26]]},"2737":{"position":[[0,26]]}}}],["client_stale_close_delay",{"_index":603,"t":{"1982":{"position":[[0,24]]},"3195":{"position":[[0,24]]}}}],["client_user_connection_limit",{"_index":441,"t":{"905":{"position":[[0,28]]},"1972":{"position":[[0,28]]},"3185":{"position":[[0,28]]}}}],["client_user_id_http_head",{"_index":636,"t":{"3197":{"position":[[0,26]]}}}],["client_write_delay",{"_index":542,"t":{"1610":{"position":[[0,18]]},"2735":{"position":[[0,18]]}}}],["cluster",{"_index":62,"t":{"79":{"position":[[7,10]]},"984":{"position":[[6,7]]},"2043":{"position":[[6,7]]},"3284":{"position":[[6,7]]}}}],["code",{"_index":108,"t":{"139":{"position":[[16,4]]},"779":{"position":[[13,5]]},"807":{"position":[[18,5]]},"837":{"position":[[17,5]]},"1189":{"position":[[11,4]]},"1512":{"position":[[17,4]]},"1830":{"position":[[13,5]]},"1858":{"position":[[18,5]]},"1862":{"position":[[24,5]]},"1864":{"position":[[20,5]]},"2370":{"position":[[6,5]]},"2372":{"position":[[12,5]]},"2374":{"position":[[11,5]]},"2643":{"position":[[17,4]]},"3021":{"position":[[13,5]]},"3049":{"position":[[18,5]]},"3053":{"position":[[24,5]]},"3055":{"position":[[20,5]]},"3468":{"position":[[17,4],[30,4]]},"3470":{"position":[[17,4],[30,4]]},"3658":{"position":[[6,5]]},"3660":{"position":[[12,5]]},"3662":{"position":[[11,5]]}}}],["command",{"_index":337,"t":{"551":{"position":[[5,7]]},"861":{"position":[[8,7]]},"863":{"position":[[12,7]]},"865":{"position":[[10,7]]},"867":{"position":[[9,7]]},"869":{"position":[[11,7]]},"875":{"position":[[0,7]]},"1132":{"position":[[0,7]]},"1195":{"position":[[8,7]]},"1212":{"position":[[8,7]]},"1248":{"position":[[8,7]]},"1826":{"position":[[21,7]]},"1942":{"position":[[0,7]]},"2213":{"position":[[0,7]]},"2239":{"position":[[0,7]]},"2327":{"position":[[8,7]]},"2386":{"position":[[8,7]]},"2399":{"position":[[8,7]]},"3015":{"position":[[16,7]]},"3155":{"position":[[0,7]]},"3550":{"position":[[0,7]]},"3566":{"position":[[8,7]]},"3583":{"position":[[8,7]]},"3600":{"position":[[8,7]]}}}],["common",{"_index":618,"t":{"2346":{"position":[[7,6]]},"2362":{"position":[[13,6]]},"2382":{"position":[[4,6]]},"3634":{"position":[[7,6]]},"3650":{"position":[[13,6]]},"3670":{"position":[[4,6]]}}}],["commun",{"_index":87,"t":{"111":{"position":[[0,13]]},"201":{"position":[[5,13]]},"404":{"position":[[5,9]]},"492":{"position":[[5,9]]},"2653":{"position":[[5,13]]}}}],["compat",{"_index":46,"t":{"63":{"position":[[10,13]]},"2047":{"position":[[12,10]]},"3286":{"position":[[12,10]]}}}],["compos",{"_index":110,"t":{"139":{"position":[[33,7]]},"526":{"position":[[7,7]]},"1434":{"position":[[7,7]]},"2617":{"position":[[7,7]]}}}],["concept",{"_index":270,"t":{"386":{"position":[[16,7]]},"424":{"position":[[15,7]]},"490":{"position":[[0,8]]},"1408":{"position":[[15,7]]},"2565":{"position":[[15,7]]}}}],["concern",{"_index":181,"t":{"237":{"position":[[9,8]]},"3344":{"position":[[12,8]]}}}],["conclus",{"_index":18,"t":{"21":{"position":[[0,10]]},"57":{"position":[[0,10]]},"89":{"position":[[0,10]]},"115":{"position":[[0,10]]},"141":{"position":[[0,10]]},"169":{"position":[[0,10]]},"219":{"position":[[0,10]]},"243":{"position":[[0,10]]},"269":{"position":[[0,10]]},"290":{"position":[[0,10]]},"402":{"position":[[0,10]]},"484":{"position":[[0,10]]},"2125":{"position":[[0,10]]},"3340":{"position":[[0,10]]}}}],["config",{"_index":159,"t":{"209":{"position":[[9,6]]},"211":{"position":[[8,6]]},"571":{"position":[[9,6]]},"731":{"position":[[16,6]]},"881":{"position":[[0,6]]},"883":{"position":[[5,6]]},"885":{"position":[[5,6]]},"887":{"position":[[5,6]]},"1936":{"position":[[8,6]]},"1948":{"position":[[0,6]]},"1950":{"position":[[5,6]]},"1952":{"position":[[5,6]]},"1954":{"position":[[5,6]]},"3017":{"position":[[28,6]]},"3133":{"position":[[8,6]]},"3161":{"position":[[0,6]]},"3163":{"position":[[5,6]]},"3165":{"position":[[5,6]]},"3167":{"position":[[5,6]]}}}],["configur",{"_index":172,"t":{"227":{"position":[[0,9]]},"342":{"position":[[40,14]]},"448":{"position":[[3,9]]},"450":{"position":[[3,9]]},"518":{"position":[[0,13]]},"563":{"position":[[6,13]]},"569":{"position":[[6,13]]},"603":{"position":[[0,13]]},"645":{"position":[[0,9]]},"663":{"position":[[0,9]]},"685":{"position":[[0,13]]},"873":{"position":[[0,13]]},"879":{"position":[[0,13]]},"915":{"position":[[9,14]]},"980":{"position":[[28,13]]},"998":{"position":[[6,13]]},"1330":{"position":[[40,14]]},"1448":{"position":[[3,9]]},"1450":{"position":[[3,9]]},"1510":{"position":[[10,13]]},"1514":{"position":[[6,13]]},"1532":{"position":[[0,13]]},"1566":{"position":[[0,13]]},"1636":{"position":[[0,13]]},"1691":{"position":[[0,9]]},"1707":{"position":[[0,9]]},"1736":{"position":[[0,13]]},"1940":{"position":[[0,13]]},"1946":{"position":[[0,13]]},"1990":{"position":[[9,14]]},"2031":{"position":[[0,11]]},"2039":{"position":[[28,13]]},"2105":{"position":[[6,13]]},"2477":{"position":[[40,14]]},"2589":{"position":[[3,9]]},"2591":{"position":[[3,9]]},"2641":{"position":[[10,13]]},"2645":{"position":[[6,13]]},"2701":{"position":[[0,13]]},"2725":{"position":[[0,13]]},"2745":{"position":[[0,13]]},"2815":{"position":[[0,9]]},"2853":{"position":[[0,13]]},"2896":{"position":[[0,9]]},"2925":{"position":[[0,13]]},"3153":{"position":[[0,13]]},"3159":{"position":[[0,13]]},"3205":{"position":[[9,13]]},"3272":{"position":[[0,11]]},"3280":{"position":[[28,13]]},"3306":{"position":[[6,13]]}}}],["connect",{"_index":82,"t":{"105":{"position":[[12,11]]},"107":{"position":[[0,10]]},"161":{"position":[[0,7]]},"181":{"position":[[28,10]]},"183":{"position":[[28,7]]},"187":{"position":[[21,10]]},"189":{"position":[[21,7]]},"306":{"position":[[9,11]]},"308":{"position":[[17,11]]},"326":{"position":[[31,12]]},"332":{"position":[[31,10]]},"338":{"position":[[44,10]]},"392":{"position":[[7,11]]},"422":{"position":[[0,10]]},"428":{"position":[[23,10]]},"452":{"position":[[3,7]]},"496":{"position":[[0,10]]},"502":{"position":[[0,10]]},"605":{"position":[[0,11]]},"763":{"position":[[0,10]]},"771":{"position":[[22,10]]},"835":{"position":[[0,10]]},"934":{"position":[[16,10]]},"954":{"position":[[10,11]]},"1042":{"position":[[21,10]]},"1064":{"position":[[0,7]]},"1094":{"position":[[9,7]]},"1165":{"position":[[0,7]]},"1195":{"position":[[0,7]]},"1206":{"position":[[0,10]]},"1212":{"position":[[0,7]]},"1248":{"position":[[0,7]]},"1292":{"position":[[9,11]]},"1294":{"position":[[17,11]]},"1312":{"position":[[31,12]]},"1320":{"position":[[31,10]]},"1326":{"position":[[44,10]]},"1358":{"position":[[0,10]]},"1364":{"position":[[0,10]]},"1406":{"position":[[0,10]]},"1412":{"position":[[23,10]]},"1452":{"position":[[3,7]]},"1472":{"position":[[0,10]]},"1478":{"position":[[9,10]]},"1534":{"position":[[0,11]]},"1578":{"position":[[0,11]]},"1675":{"position":[[14,10]]},"1681":{"position":[[37,11]]},"1756":{"position":[[0,10]]},"1782":{"position":[[0,10]]},"1790":{"position":[[22,10]]},"2009":{"position":[[16,10]]},"2081":{"position":[[10,11]]},"2133":{"position":[[21,10]]},"2145":{"position":[[0,7]]},"2177":{"position":[[9,7]]},"2261":{"position":[[0,10]]},"2327":{"position":[[0,7]]},"2338":{"position":[[0,10]]},"2344":{"position":[[7,10]]},"2350":{"position":[[7,10]]},"2352":{"position":[[0,10]]},"2386":{"position":[[0,7]]},"2399":{"position":[[0,7]]},"2439":{"position":[[9,11]]},"2441":{"position":[[17,11]]},"2459":{"position":[[31,12]]},"2467":{"position":[[31,10]]},"2473":{"position":[[44,10]]},"2509":{"position":[[0,10]]},"2515":{"position":[[0,10]]},"2563":{"position":[[0,10]]},"2569":{"position":[[23,10]]},"2593":{"position":[[3,7]]},"2665":{"position":[[0,10]]},"2671":{"position":[[9,10]]},"2703":{"position":[[0,11]]},"2771":{"position":[[0,11]]},"2829":{"position":[[14,10]]},"2835":{"position":[[37,11]]},"2945":{"position":[[0,10]]},"2971":{"position":[[0,10]]},"2979":{"position":[[22,10]]},"3226":{"position":[[16,10]]},"3248":{"position":[[10,11]]},"3362":{"position":[[21,10]]},"3374":{"position":[[0,7]]},"3406":{"position":[[9,7]]},"3495":{"position":[[0,10]]},"3566":{"position":[[0,7]]},"3583":{"position":[[0,7]]},"3594":{"position":[[0,10]]},"3600":{"position":[[0,7]]},"3632":{"position":[[7,10]]},"3638":{"position":[[7,10]]},"3640":{"position":[[0,10]]}}}],["conntrack",{"_index":455,"t":{"956":{"position":[[0,9]]},"2083":{"position":[[0,9]]},"3250":{"position":[[0,9]]}}}],["consider",{"_index":258,"t":{"360":{"position":[[16,14]]},"368":{"position":[[16,14]]},"370":{"position":[[12,14]]},"1380":{"position":[[16,14]]},"1388":{"position":[[16,14]]},"1390":{"position":[[12,14]]},"2531":{"position":[[16,14]]},"2539":{"position":[[16,14]]},"2541":{"position":[[12,14]]}}}],["considir",{"_index":527,"t":{"1476":{"position":[[11,14]]},"1490":{"position":[[11,14]]},"2669":{"position":[[11,14]]},"2683":{"position":[[11,14]]}}}],["contact",{"_index":223,"t":{"324":{"position":[[54,8]]},"1310":{"position":[[54,8]]},"2457":{"position":[[54,8]]}}}],["control",{"_index":122,"t":{"161":{"position":[[14,10]]},"163":{"position":[[5,10]]}}}],["convert",{"_index":347,"t":{"571":{"position":[[16,9]]}}}],["cpu",{"_index":314,"t":{"480":{"position":[[31,3]]}}}],["creat",{"_index":94,"t":{"123":{"position":[[0,8]]},"125":{"position":[[0,8]]},"282":{"position":[[0,8]]},"316":{"position":[[9,6]]},"340":{"position":[[7,6]]},"1302":{"position":[[9,6]]},"1328":{"position":[[7,6]]},"2449":{"position":[[9,6]]},"2475":{"position":[[7,6]]}}}],["cross",{"_index":293,"t":{"436":{"position":[[0,5]]},"1420":{"position":[[0,5]]},"2577":{"position":[[0,5]]}}}],["crt",{"_index":474,"t":{"1046":{"position":[[6,3]]},"2227":{"position":[[6,3]]},"3414":{"position":[[6,3]]}}}],["curl",{"_index":502,"t":{"1206":{"position":[[17,4]]},"2338":{"position":[[17,4]]},"3594":{"position":[[17,4]]}}}],["custom",{"_index":423,"t":{"855":{"position":[[6,6]]},"925":{"position":[[0,6]]},"929":{"position":[[0,9]]},"1074":{"position":[[7,6]]},"1076":{"position":[[7,6]]},"1722":{"position":[[6,6]]},"2000":{"position":[[0,6]]},"2004":{"position":[[0,9]]},"2157":{"position":[[7,6]]},"2159":{"position":[[7,6]]},"2911":{"position":[[6,6]]},"3217":{"position":[[0,6]]},"3221":{"position":[[0,9]]},"3386":{"position":[[7,6]]},"3388":{"position":[[7,6]]}}}],["dashboard",{"_index":471,"t":{"1012":{"position":[[8,9]]},"2093":{"position":[[8,9]]},"3302":{"position":[[8,9]]},"3321":{"position":[[8,9]]}}}],["data",{"_index":499,"t":{"1197":{"position":[[10,4]]},"1214":{"position":[[10,4]]},"1227":{"position":[[10,4]]},"1252":{"position":[[10,4]]},"1340":{"position":[[20,4]]},"2306":{"position":[[10,4]]},"2329":{"position":[[10,4]]},"2388":{"position":[[10,4]]},"2403":{"position":[[10,4]]},"2487":{"position":[[20,4]]},"3527":{"position":[[10,4]]},"3570":{"position":[[10,4]]},"3585":{"position":[[10,4]]},"3602":{"position":[[10,4]]}}}],["databas",{"_index":117,"t":{"155":{"position":[[0,8]]},"649":{"position":[[0,8]]},"667":{"position":[[0,8]]},"1695":{"position":[[0,8]]},"1711":{"position":[[0,8]]},"2819":{"position":[[0,8]]},"2900":{"position":[[0,8]]}}}],["deb",{"_index":323,"t":{"530":{"position":[[8,3]]},"1438":{"position":[[8,3]]},"2621":{"position":[[8,3]]}}}],["debian",{"_index":352,"t":{"593":{"position":[[0,6]]},"1558":{"position":[[0,6]]},"2761":{"position":[[0,6]]}}}],["debug",{"_index":164,"t":{"213":{"position":[[21,5]]},"921":{"position":[[0,5]]},"1996":{"position":[[0,5]]},"3211":{"position":[[0,5]]},"3628":{"position":[[0,9]]}}}],["decis",{"_index":190,"t":{"255":{"position":[[9,9]]}}}],["decod",{"_index":362,"t":{"629":{"position":[[11,8]]},"1598":{"position":[[11,8]]},"2793":{"position":[[11,8]]}}}],["default",{"_index":55,"t":{"71":{"position":[[17,7]]},"384":{"position":[[10,7]]},"540":{"position":[[24,7]]},"565":{"position":[[22,7]]},"567":{"position":[[19,7]]},"917":{"position":[[0,7]]},"927":{"position":[[8,7]]},"1992":{"position":[[0,7]]},"2002":{"position":[[8,7]]},"3207":{"position":[[0,7]]},"3219":{"position":[[8,7]]}}}],["defin",{"_index":481,"t":{"1092":{"position":[[0,8]]},"2175":{"position":[[0,8]]},"3404":{"position":[[0,8]]}}}],["degrad",{"_index":260,"t":{"366":{"position":[[9,11]]},"1386":{"position":[[9,11]]},"2537":{"position":[[9,11]]}}}],["delete_user_statu",{"_index":372,"t":{"683":{"position":[[0,18]]},"1734":{"position":[[0,18]]},"2923":{"position":[[0,18]]}}}],["deliv",{"_index":239,"t":{"336":{"position":[[28,9]]},"1324":{"position":[[28,9]]},"2471":{"position":[[28,9]]}}}],["deliveri",{"_index":37,"t":{"47":{"position":[[10,8]]},"235":{"position":[[5,8]]},"312":{"position":[[8,8]]},"362":{"position":[[8,8]]},"1298":{"position":[[8,8]]},"1382":{"position":[[8,8]]},"2445":{"position":[[8,8]]},"2533":{"position":[[8,8]]}}}],["demo",{"_index":177,"t":{"231":{"position":[[0,4]]}}}],["deni",{"_index":410,"t":{"787":{"position":[[11,6]]},"1838":{"position":[[11,6]]},"3029":{"position":[[11,6]]}}}],["deploy",{"_index":296,"t":{"438":{"position":[[9,6]]},"458":{"position":[[3,6]]},"1422":{"position":[[9,6]]},"1458":{"position":[[3,6]]},"2579":{"position":[[9,6]]},"2599":{"position":[[3,6]]}}}],["deprec",{"_index":625,"t":{"2655":{"position":[[23,10]]}}}],["descript",{"_index":557,"t":{"1648":{"position":[[4,11]]},"2747":{"position":[[4,11]]},"2865":{"position":[[4,11]]}}}],["design",{"_index":456,"t":{"960":{"position":[[8,6]]},"1622":{"position":[[15,6]]},"2097":{"position":[[8,6]]},"2839":{"position":[[15,6]]},"3256":{"position":[[8,6]]},"3346":{"position":[[15,6]]}}}],["detail",{"_index":538,"t":{"1568":{"position":[[15,7]]},"2727":{"position":[[15,7]]}}}],["develop",{"_index":358,"t":{"611":{"position":[[0,11]]},"1546":{"position":[[0,11]]},"2715":{"position":[[0,11]]}}}],["device_list",{"_index":561,"t":{"1655":{"position":[[0,11]]},"2872":{"position":[[0,11]]}}}],["device_regist",{"_index":558,"t":{"1649":{"position":[[0,15]]},"2866":{"position":[[0,15]]}}}],["device_remov",{"_index":560,"t":{"1653":{"position":[[0,13]]},"2870":{"position":[[0,13]]}}}],["device_topic_list",{"_index":563,"t":{"1659":{"position":[[0,17]]},"2876":{"position":[[0,17]]}}}],["device_topic_upd",{"_index":562,"t":{"1657":{"position":[[0,19]]},"2874":{"position":[[0,19]]}}}],["device_upd",{"_index":559,"t":{"1651":{"position":[[0,13]]},"2868":{"position":[[0,13]]}}}],["differ",{"_index":232,"t":{"332":{"position":[[61,9]]},"426":{"position":[[0,9]]},"1320":{"position":[[61,9]]},"1410":{"position":[[0,9]]},"1484":{"position":[[9,9]]},"2467":{"position":[[61,9]]},"2567":{"position":[[0,9]]},"2677":{"position":[[9,9]]}}}],["disabl",{"_index":346,"t":{"567":{"position":[[7,8]]},"927":{"position":[[0,7]]},"2002":{"position":[[0,7]]},"3219":{"position":[[0,7]]},"3228":{"position":[[0,7]]}}}],["disallow_anonymous_connection_token",{"_index":605,"t":{"1986":{"position":[[0,36]]},"3201":{"position":[[0,36]]}}}],["disconnect",{"_index":247,"t":{"346":{"position":[[29,10]]},"498":{"position":[[0,13]]},"807":{"position":[[7,10]]},"1076":{"position":[[14,10]]},"1116":{"position":[[0,10]]},"1179":{"position":[[7,11]]},"1189":{"position":[[0,10]]},"1334":{"position":[[29,10]]},"1360":{"position":[[0,13]]},"1512":{"position":[[6,10]]},"1681":{"position":[[0,13]]},"1858":{"position":[[7,10]]},"1862":{"position":[[13,10]]},"1864":{"position":[[9,10]]},"2159":{"position":[[14,10]]},"2197":{"position":[[0,10]]},"2247":{"position":[[7,11]]},"2374":{"position":[[0,10]]},"2481":{"position":[[29,10]]},"2511":{"position":[[0,13]]},"2643":{"position":[[6,10]]},"2835":{"position":[[0,13]]},"3049":{"position":[[7,10]]},"3053":{"position":[[13,10]]},"3055":{"position":[[9,10]]},"3388":{"position":[[14,10]]},"3438":{"position":[[0,10]]},"3558":{"position":[[7,11]]},"3662":{"position":[[0,10]]}}}],["disconnectconnectionclos",{"_index":580,"t":{"1860":{"position":[[0,26]]},"3051":{"position":[[0,26]]}}}],["django",{"_index":92,"t":{"119":{"position":[[14,6]]}}}],["doc",{"_index":628,"t":{"2659":{"position":[[28,3]]}}}],["docker",{"_index":109,"t":{"139":{"position":[[26,6]]},"524":{"position":[[0,6]]},"526":{"position":[[0,6]]},"589":{"position":[[0,6]]},"1432":{"position":[[0,6]]},"1434":{"position":[[0,6]]},"1554":{"position":[[0,6]]},"2615":{"position":[[0,6]]},"2617":{"position":[[0,6]]},"2757":{"position":[[0,6]]}}}],["document",{"_index":65,"t":{"83":{"position":[[4,13]]}}}],["domain",{"_index":464,"t":{"1000":{"position":[[9,6]]},"2107":{"position":[[9,6]]},"3308":{"position":[[9,6]]}}}],["down",{"_index":627,"t":{"2659":{"position":[[9,4]]}}}],["drop",{"_index":142,"t":{"195":{"position":[[0,8]]}}}],["durat",{"_index":336,"t":{"547":{"position":[[26,8]]},"940":{"position":[[13,8]]},"2015":{"position":[[13,8]]},"3234":{"position":[[13,8]]}}}],["dynam",{"_index":472,"t":{"1038":{"position":[[0,7]]},"1796":{"position":[[0,7]]},"2129":{"position":[[0,7]]},"2985":{"position":[[0,7]]},"3358":{"position":[[0,7]]}}}],["ecosystem",{"_index":39,"t":{"49":{"position":[[0,9]]}}}],["effici",{"_index":547,"t":{"1626":{"position":[[0,9]]},"2843":{"position":[[0,9]]}}}],["emb",{"_index":465,"t":{"1002":{"position":[[0,5]]},"2109":{"position":[[0,5]]},"3310":{"position":[[0,5]]}}}],["embed",{"_index":291,"t":{"434":{"position":[[0,8]]},"1418":{"position":[[0,8]]},"2575":{"position":[[0,8]]}}}],["emul",{"_index":263,"t":{"378":{"position":[[17,9]]}}}],["enabl",{"_index":480,"t":{"1090":{"position":[[0,6]]},"2117":{"position":[[0,8]]},"2173":{"position":[[0,6]]},"3332":{"position":[[0,8]]},"3402":{"position":[[0,6]]}}}],["endpoint",{"_index":446,"t":{"915":{"position":[[0,8]]},"917":{"position":[[8,10]]},"919":{"position":[[6,10]]},"921":{"position":[[6,10]]},"923":{"position":[[13,8]]},"927":{"position":[[16,9]]},"929":{"position":[[18,9]]},"1796":{"position":[[13,8]]},"1990":{"position":[[0,8]]},"1992":{"position":[[8,10]]},"1994":{"position":[[6,10]]},"1996":{"position":[[6,10]]},"1998":{"position":[[13,8]]},"2002":{"position":[[16,9]]},"2004":{"position":[[18,9]]},"2985":{"position":[[13,8]]},"3205":{"position":[[0,8]]},"3207":{"position":[[8,9]]},"3209":{"position":[[6,9]]},"3211":{"position":[[6,9]]},"3213":{"position":[[13,8]]},"3219":{"position":[[16,9]]},"3221":{"position":[[18,9]]}}}],["enforc",{"_index":340,"t":{"553":{"position":[[0,8]]},"1504":{"position":[[14,8]]},"2635":{"position":[[14,8]]}}}],["engin",{"_index":57,"t":{"73":{"position":[[10,6]]},"647":{"position":[[18,6]]},"649":{"position":[[21,6]]},"665":{"position":[[18,6]]},"667":{"position":[[21,6]]},"897":{"position":[[0,6]]},"968":{"position":[[7,6]]},"970":{"position":[[7,6]]},"972":{"position":[[6,6]]},"974":{"position":[[6,6]]},"986":{"position":[[6,6]]},"988":{"position":[[10,6]]},"990":{"position":[[10,6]]},"1693":{"position":[[18,6]]},"1695":{"position":[[21,6]]},"1709":{"position":[[18,6]]},"1711":{"position":[[21,6]]},"1964":{"position":[[0,6]]},"2023":{"position":[[7,6]]},"2025":{"position":[[7,6]]},"2027":{"position":[[6,6]]},"2029":{"position":[[6,6]]},"2045":{"position":[[6,6]]},"2049":{"position":[[10,6]]},"2051":{"position":[[10,6]]},"2817":{"position":[[18,6]]},"2819":{"position":[[21,6]]},"2898":{"position":[[18,6]]},"2900":{"position":[[21,6]]},"3177":{"position":[[0,6]]},"3264":{"position":[[7,6]]},"3266":{"position":[[7,6]]},"3268":{"position":[[6,6]]},"3270":{"position":[[6,6]]},"3288":{"position":[[10,6]]},"3290":{"position":[[10,6]]}}}],["env",{"_index":449,"t":{"942":{"position":[[24,3]]},"2017":{"position":[[24,3]]},"3236":{"position":[[24,3]]}}}],["environ",{"_index":115,"t":{"153":{"position":[[0,11]]},"877":{"position":[[3,11]]},"1944":{"position":[[3,11]]},"3157":{"position":[[3,11]]}}}],["ephemer",{"_index":450,"t":{"950":{"position":[[0,9]]},"2077":{"position":[[0,9]]},"3244":{"position":[[0,9]]}}}],["error",{"_index":184,"t":{"241":{"position":[[0,5]]},"779":{"position":[[7,5]]},"817":{"position":[[7,5]]},"827":{"position":[[6,5]]},"837":{"position":[[11,5]]},"1074":{"position":[[14,5]]},"1181":{"position":[[7,6]]},"1830":{"position":[[7,5]]},"2157":{"position":[[14,5]]},"2249":{"position":[[7,6]]},"2370":{"position":[[0,5]]},"3021":{"position":[[7,5]]},"3386":{"position":[[14,5]]},"3466":{"position":[[10,5]]},"3468":{"position":[[11,5]]},"3470":{"position":[[11,5]]},"3560":{"position":[[7,6]]},"3658":{"position":[[0,5]]}}}],["etc",{"_index":641,"t":{"3628":{"position":[[31,3]]}}}],["event",{"_index":15,"t":{"19":{"position":[[8,5]]},"348":{"position":[[39,6]]},"502":{"position":[[21,6]]},"1336":{"position":[[39,6]]},"1364":{"position":[[21,6]]},"2121":{"position":[[15,6]]},"2483":{"position":[[39,6]]},"2515":{"position":[[21,6]]},"3336":{"position":[[15,6]]}}}],["exampl",{"_index":41,"t":{"55":{"position":[[0,8]]},"99":{"position":[[11,7]]},"113":{"position":[[12,7]]},"526":{"position":[[15,7]]},"583":{"position":[[5,8]]},"609":{"position":[[6,8]]},"633":{"position":[[0,8]]},"731":{"position":[[23,7]]},"765":{"position":[[0,8]]},"1034":{"position":[[0,7]]},"1082":{"position":[[11,7]]},"1138":{"position":[[5,7]]},"1140":{"position":[[5,7]]},"1208":{"position":[[8,7]]},"1223":{"position":[[8,7]]},"1244":{"position":[[0,7]]},"1261":{"position":[[0,7]]},"1434":{"position":[[15,7]]},"1480":{"position":[[0,8]]},"1482":{"position":[[0,8]]},"1484":{"position":[[0,8]]},"1486":{"position":[[0,8]]},"1544":{"position":[[6,8]]},"1576":{"position":[[0,7]]},"1602":{"position":[[0,8]]},"1784":{"position":[[0,8]]},"1824":{"position":[[0,7]]},"1936":{"position":[[15,8]]},"2165":{"position":[[11,7]]},"2219":{"position":[[5,7]]},"2221":{"position":[[5,7]]},"2323":{"position":[[0,7]]},"2340":{"position":[[8,7]]},"2395":{"position":[[8,7]]},"2412":{"position":[[0,7]]},"2617":{"position":[[15,7]]},"2673":{"position":[[0,8]]},"2675":{"position":[[0,8]]},"2677":{"position":[[0,8]]},"2679":{"position":[[0,8]]},"2713":{"position":[[6,8]]},"2769":{"position":[[0,7]]},"2797":{"position":[[0,8]]},"2973":{"position":[[0,8]]},"3013":{"position":[[0,7]]},"3133":{"position":[[15,8]]},"3354":{"position":[[5,7]]},"3394":{"position":[[11,7]]},"3460":{"position":[[5,7]]},"3462":{"position":[[5,7]]},"3544":{"position":[[0,7]]},"3579":{"position":[[0,7]]},"3596":{"position":[[8,7]]},"3609":{"position":[[8,7]]}}}],["exceed",{"_index":412,"t":{"793":{"position":[[6,8]]},"1844":{"position":[[6,8]]},"3035":{"position":[[6,8]]}}}],["exclud",{"_index":217,"t":{"320":{"position":[[11,7]]},"1306":{"position":[[11,7]]},"2453":{"position":[[11,7]]}}}],["exhaust",{"_index":451,"t":{"950":{"position":[[15,10]]},"2077":{"position":[[15,10]]},"3244":{"position":[[15,10]]}}}],["exp",{"_index":393,"t":{"741":{"position":[[0,3]]},"1026":{"position":[[0,3]]},"1760":{"position":[[0,3]]},"1810":{"position":[[0,3]]},"2949":{"position":[[0,3]]},"2999":{"position":[[0,3]]}}}],["experi",{"_index":275,"t":{"396":{"position":[[0,13]]},"398":{"position":[[0,13]]}}}],["expir",{"_index":402,"t":{"763":{"position":[[11,10]]},"769":{"position":[[11,10]]},"799":{"position":[[6,7]]},"801":{"position":[[0,7]]},"819":{"position":[[0,7]]},"821":{"position":[[13,7]]},"1476":{"position":[[0,10]]},"1490":{"position":[[0,10]]},"1782":{"position":[[11,10]]},"1788":{"position":[[11,10]]},"1850":{"position":[[6,7]]},"1852":{"position":[[0,7]]},"2669":{"position":[[0,10]]},"2683":{"position":[[0,10]]},"2971":{"position":[[11,10]]},"2977":{"position":[[11,10]]},"3041":{"position":[[6,7]]},"3043":{"position":[[0,7]]}}}],["expire_at",{"_index":401,"t":{"761":{"position":[[0,9]]},"1028":{"position":[[0,9]]},"1780":{"position":[[0,9]]},"1812":{"position":[[0,9]]},"2969":{"position":[[0,9]]},"3001":{"position":[[0,9]]}}}],["explicitli",{"_index":212,"t":{"316":{"position":[[25,11]]},"1302":{"position":[[25,11]]},"2449":{"position":[[25,11]]}}}],["export",{"_index":359,"t":{"613":{"position":[[4,6]]},"1548":{"position":[[4,6]]},"2717":{"position":[[4,6]]}}}],["expos",{"_index":629,"t":{"2719":{"position":[[0,7]]},"2888":{"position":[[0,7]]},"3323":{"position":[[0,7]]}}}],["express",{"_index":534,"t":{"1522":{"position":[[0,10]]},"2691":{"position":[[0,10]]}}}],["express.j",{"_index":200,"t":{"282":{"position":[[9,10]]}}}],["fallback",{"_index":9,"t":{"13":{"position":[[10,8]]}}}],["faq",{"_index":303,"t":{"464":{"position":[[8,3]]},"1464":{"position":[[8,3]]},"2605":{"position":[[8,3]]}}}],["faster",{"_index":361,"t":{"621":{"position":[[0,6]]},"623":{"position":[[0,6]]},"625":{"position":[[0,6]]},"627":{"position":[[0,6]]},"629":{"position":[[0,6]]},"631":{"position":[[0,6]]},"1590":{"position":[[0,6]]},"1592":{"position":[[0,6]]},"1594":{"position":[[0,6]]},"1596":{"position":[[0,6]]},"1598":{"position":[[0,6]]},"1600":{"position":[[0,6]]},"2785":{"position":[[0,6]]},"2787":{"position":[[0,6]]},"2789":{"position":[[0,6]]},"2791":{"position":[[0,6]]},"2793":{"position":[[0,6]]},"2795":{"position":[[0,6]]}}}],["fcm",{"_index":553,"t":{"1638":{"position":[[0,3]]},"2855":{"position":[[0,3]]}}}],["featur",{"_index":252,"t":{"350":{"position":[[51,9]]},"442":{"position":[[4,8]]},"575":{"position":[[0,8]]},"1161":{"position":[[22,7]]},"1338":{"position":[[51,9]]},"1426":{"position":[[4,8]]},"1582":{"position":[[0,8]]},"2259":{"position":[[4,7]]},"2261":{"position":[[19,8]]},"2263":{"position":[[33,8]]},"2485":{"position":[[51,9]]},"2583":{"position":[[4,8]]},"2777":{"position":[[0,8]]},"3493":{"position":[[4,7]]},"3495":{"position":[[19,8]]},"3497":{"position":[[33,8]]}}}],["field",{"_index":332,"t":{"544":{"position":[[8,6]]}}}],["file",{"_index":367,"t":{"657":{"position":[[10,4]]},"879":{"position":[[14,4]]},"881":{"position":[[7,4]]},"948":{"position":[[5,5]]},"1046":{"position":[[18,5]]},"1685":{"position":[[10,4]]},"1946":{"position":[[14,4]]},"1948":{"position":[[7,4]]},"2075":{"position":[[5,5]]},"2227":{"position":[[18,5]]},"2809":{"position":[[10,4]]},"3159":{"position":[[14,4]]},"3161":{"position":[[7,4]]},"3242":{"position":[[5,5]]},"3414":{"position":[[18,5]]}}}],["flag",{"_index":339,"t":{"551":{"position":[[18,5]]},"875":{"position":[[13,5]]},"1942":{"position":[[13,5]]},"3155":{"position":[[13,5]]}}}],["flashback",{"_index":44,"t":{"61":{"position":[[14,10]]},"374":{"position":[[14,10]]}}}],["flexibl",{"_index":287,"t":{"420":{"position":[[0,8]]},"1404":{"position":[[0,8]]},"2561":{"position":[[0,8]]}}}],["forc",{"_index":422,"t":{"831":{"position":[[0,5]]},"833":{"position":[[0,5]]}}}],["force_posit",{"_index":584,"t":{"1894":{"position":[[0,17]]},"3087":{"position":[[0,17]]}}}],["force_push_join_leav",{"_index":583,"t":{"1888":{"position":[[0,21]]},"3079":{"position":[[0,21]]}}}],["force_recoveri",{"_index":585,"t":{"1896":{"position":[[0,14]]},"3089":{"position":[[0,14]]}}}],["format",{"_index":152,"t":{"203":{"position":[[13,6]]},"881":{"position":[[12,7]]},"883":{"position":[[12,6]]},"885":{"position":[[12,6]]},"887":{"position":[[12,6]]},"1197":{"position":[[15,7]]},"1214":{"position":[[15,7]]},"1227":{"position":[[15,7]]},"1252":{"position":[[15,7]]},"1948":{"position":[[12,7]]},"1950":{"position":[[12,6]]},"1952":{"position":[[12,6]]},"1954":{"position":[[12,6]]},"2257":{"position":[[18,7]]},"2306":{"position":[[15,7]]},"2329":{"position":[[15,7]]},"2388":{"position":[[15,7]]},"2403":{"position":[[15,7]]},"2653":{"position":[[19,6]]},"2655":{"position":[[13,6]]},"3161":{"position":[[12,7]]},"3163":{"position":[[12,6]]},"3165":{"position":[[12,6]]},"3167":{"position":[[12,6]]},"3491":{"position":[[18,7]]},"3527":{"position":[[15,7]]},"3570":{"position":[[15,7]]},"3585":{"position":[[15,7]]},"3602":{"position":[[15,7]]}}}],["found",{"_index":253,"t":{"352":{"position":[[11,5]]},"789":{"position":[[11,5]]},"843":{"position":[[11,5]]},"1344":{"position":[[11,5]]},"1840":{"position":[[11,5]]},"2491":{"position":[[11,5]]},"3031":{"position":[[11,5]]}}}],["frame",{"_index":493,"t":{"1163":{"position":[[10,7]]}}}],["framework",{"_index":522,"t":{"1354":{"position":[[0,9]]},"2547":{"position":[[0,9]]}}}],["free",{"_index":541,"t":{"1584":{"position":[[8,4]]},"2781":{"position":[[8,4]]}}}],["frontend",{"_index":189,"t":{"255":{"position":[[0,8]]}}}],["full",{"_index":90,"t":{"113":{"position":[[0,4]]},"1486":{"position":[[9,4]]},"2679":{"position":[[9,4]]},"3354":{"position":[[0,4]]}}}],["further",{"_index":569,"t":{"1671":{"position":[[0,7]]},"2890":{"position":[[0,7]]}}}],["futur",{"_index":166,"t":{"215":{"position":[[4,6]]}}}],["genconfig",{"_index":428,"t":{"865":{"position":[[0,9]]},"2061":{"position":[[0,9]]},"3139":{"position":[[0,9]]}}}],["gener",{"_index":72,"t":{"97":{"position":[[0,8]]}}}],["gensubtoken",{"_index":578,"t":{"1826":{"position":[[5,11]]},"2067":{"position":[[0,11]]},"3015":{"position":[[0,11]]},"3145":{"position":[[0,11]]}}}],["gentoken",{"_index":429,"t":{"867":{"position":[[0,8]]},"2065":{"position":[[0,8]]},"3143":{"position":[[0,8]]}}}],["get_user_statu",{"_index":371,"t":{"681":{"position":[[0,15]]},"1732":{"position":[[0,15]]},"2921":{"position":[[0,15]]}}}],["go",{"_index":309,"t":{"476":{"position":[[0,2]]},"1140":{"position":[[17,2]]},"1352":{"position":[[23,2]]},"2221":{"position":[[17,2]]},"2545":{"position":[[23,2]]},"3462":{"position":[[17,2]]}}}],["goal",{"_index":185,"t":{"249":{"position":[[15,5]]}}}],["gomaxproc",{"_index":445,"t":{"913":{"position":[[0,10]]},"1988":{"position":[[0,10]]},"3203":{"position":[[0,10]]}}}],["grace",{"_index":259,"t":{"366":{"position":[[0,8]]},"1386":{"position":[[0,8]]},"2537":{"position":[[0,8]]}}}],["grafana",{"_index":470,"t":{"1012":{"position":[[0,7]]},"2093":{"position":[[0,7]]},"3302":{"position":[[0,7]]},"3321":{"position":[[0,7]]}}}],["granular",{"_index":479,"t":{"1088":{"position":[[0,8]]},"1090":{"position":[[7,8]]},"1094":{"position":[[0,8]]},"1096":{"position":[[0,8]]},"1098":{"position":[[0,8]]},"2171":{"position":[[0,8]]},"2173":{"position":[[7,8]]},"2177":{"position":[[0,8]]},"2179":{"position":[[0,8]]},"2181":{"position":[[0,8]]},"3352":{"position":[[0,8]]},"3400":{"position":[[0,8]]},"3402":{"position":[[7,8]]},"3406":{"position":[[0,8]]},"3408":{"position":[[0,8]]},"3410":{"position":[[0,8]]}}}],["graphit",{"_index":469,"t":{"1010":{"position":[[0,8]]},"2091":{"position":[[0,8]]},"3300":{"position":[[0,8]]},"3319":{"position":[[0,8]]}}}],["great",{"_index":283,"t":{"412":{"position":[[0,5]]},"1396":{"position":[[0,5]]},"2553":{"position":[[0,5]]}}}],["grpc",{"_index":58,"t":{"75":{"position":[[0,4]]},"555":{"position":[[8,4]]},"623":{"position":[[7,4]]},"627":{"position":[[7,4]]},"631":{"position":[[7,4]]},"1050":{"position":[[8,4]]},"1052":{"position":[[8,4]]},"1062":{"position":[[6,4]]},"1078":{"position":[[0,4]]},"1080":{"position":[[0,4]]},"1082":{"position":[[0,4]]},"1136":{"position":[[0,4]]},"1138":{"position":[[0,4]]},"1140":{"position":[[0,4]]},"1142":{"position":[[0,4]]},"1592":{"position":[[7,4]]},"1596":{"position":[[7,4]]},"1600":{"position":[[7,4]]},"2143":{"position":[[6,4]]},"2161":{"position":[[0,4]]},"2163":{"position":[[0,4]]},"2165":{"position":[[0,4]]},"2217":{"position":[[0,4]]},"2219":{"position":[[0,4]]},"2221":{"position":[[0,4]]},"2223":{"position":[[0,4]]},"2231":{"position":[[8,4]]},"2233":{"position":[[8,4]]},"2787":{"position":[[7,4]]},"2791":{"position":[[7,4]]},"2795":{"position":[[7,4]]},"3372":{"position":[[6,4]]},"3390":{"position":[[0,4]]},"3392":{"position":[[0,4]]},"3394":{"position":[[0,4]]},"3418":{"position":[[8,4]]},"3420":{"position":[[8,4]]},"3458":{"position":[[0,4]]},"3460":{"position":[[0,4]]},"3462":{"position":[[0,4]]},"3464":{"position":[[0,4]]},"3470":{"position":[[25,4]]}}}],["guarante",{"_index":211,"t":{"314":{"position":[[14,10]]},"364":{"position":[[14,10]]},"1300":{"position":[[14,10]]},"1384":{"position":[[14,10]]},"2447":{"position":[[14,10]]},"2535":{"position":[[14,10]]}}}],["guid",{"_index":278,"t":{"400":{"position":[[10,5]]}}}],["handl",{"_index":84,"t":{"107":{"position":[[19,8]]},"241":{"position":[[6,8]]},"306":{"position":[[49,7]]},"931":{"position":[[7,8]]},"1179":{"position":[[0,6]]},"1181":{"position":[[0,6]]},"1292":{"position":[[49,7]]},"2006":{"position":[[7,8]]},"2247":{"position":[[0,6]]},"2249":{"position":[[0,6]]},"2439":{"position":[[49,7]]},"3223":{"position":[[7,8]]},"3558":{"position":[[0,6]]},"3560":{"position":[[0,6]]}}}],["handler",{"_index":105,"t":{"135":{"position":[[19,8]]},"929":{"position":[[10,7]]},"2004":{"position":[[10,7]]},"3221":{"position":[[10,7]]}}}],["haproxi",{"_index":459,"t":{"980":{"position":[[0,7]]},"2039":{"position":[[0,7]]},"3280":{"position":[[0,7]]}}}],["header",{"_index":475,"t":{"1060":{"position":[[11,7]]},"1084":{"position":[[0,6]]},"2141":{"position":[[11,7]]},"2167":{"position":[[0,6]]},"3370":{"position":[[11,7]]},"3396":{"position":[[0,6]]}}}],["health",{"_index":447,"t":{"923":{"position":[[0,6]]},"1998":{"position":[[0,6]]},"3213":{"position":[[0,6]]}}}],["helm",{"_index":320,"t":{"528":{"position":[[11,4]]},"1436":{"position":[[11,4]]},"2619":{"position":[[11,4]]}}}],["here",{"_index":256,"t":{"352":{"position":[[42,5]]},"1344":{"position":[[42,5]]},"2491":{"position":[[42,4]]}}}],["high",{"_index":458,"t":{"978":{"position":[[19,4]]},"2035":{"position":[[19,4]]},"3276":{"position":[[19,4]]}}}],["histori",{"_index":31,"t":{"41":{"position":[[9,7]]},"69":{"position":[[0,7]]},"360":{"position":[[8,7]]},"432":{"position":[[8,7]]},"510":{"position":[[13,7]]},"540":{"position":[[13,7]]},"637":{"position":[[0,7]]},"960":{"position":[[0,7]]},"962":{"position":[[0,7]]},"1124":{"position":[[0,7]]},"1173":{"position":[[25,8]]},"1372":{"position":[[13,7]]},"1380":{"position":[[8,7]]},"1416":{"position":[[8,7]]},"1606":{"position":[[0,7]]},"1744":{"position":[[0,7]]},"2097":{"position":[[0,7]]},"2099":{"position":[[0,7]]},"2205":{"position":[[0,7]]},"2378":{"position":[[8,7]]},"2523":{"position":[[13,7]]},"2531":{"position":[[8,7]]},"2573":{"position":[[8,7]]},"2801":{"position":[[0,7]]},"2933":{"position":[[0,7]]},"3256":{"position":[[0,7]]},"3258":{"position":[[0,7]]},"3446":{"position":[[0,7]]},"3666":{"position":[[8,7]]}}}],["history_cel",{"_index":536,"t":{"1526":{"position":[[0,11]]},"2695":{"position":[[0,11]]}}}],["history_disable_for_cli",{"_index":385,"t":{"719":{"position":[[0,26]]}}}],["history_meta_ttl",{"_index":148,"t":{"199":{"position":[[0,16]]},"3085":{"position":[[0,16]]}}}],["history_remov",{"_index":485,"t":{"1126":{"position":[[0,14]]},"2207":{"position":[[0,14]]},"3448":{"position":[[0,14]]}}}],["history_s",{"_index":381,"t":{"711":{"position":[[0,12]]},"1890":{"position":[[0,12]]},"3081":{"position":[[0,12]]}}}],["history_ttl",{"_index":382,"t":{"713":{"position":[[0,11]]},"1892":{"position":[[0,11]]},"3083":{"position":[[0,11]]}}}],["hm",{"_index":554,"t":{"1640":{"position":[[0,3]]},"2857":{"position":[[0,3]]}}}],["hook",{"_index":248,"t":{"346":{"position":[[40,6]]},"1334":{"position":[[40,6]]},"2481":{"position":[[40,6]]}}}],["horizont",{"_index":210,"t":{"310":{"position":[[21,13]]},"1296":{"position":[[21,13]]},"2443":{"position":[[21,13]]}}}],["http",{"_index":151,"t":{"203":{"position":[[4,4]]},"559":{"position":[[0,4]]},"621":{"position":[[7,4]]},"625":{"position":[[7,4]]},"635":{"position":[[8,4]]},"637":{"position":[[8,4]]},"1056":{"position":[[0,4]]},"1058":{"position":[[0,4]]},"1060":{"position":[[6,4]]},"1104":{"position":[[0,4]]},"1106":{"position":[[0,4]]},"1134":{"position":[[0,4]]},"1590":{"position":[[7,4]]},"1594":{"position":[[7,4]]},"1604":{"position":[[8,4]]},"1606":{"position":[[8,4]]},"2137":{"position":[[0,4]]},"2139":{"position":[[0,4]]},"2141":{"position":[[6,4]]},"2185":{"position":[[0,4]]},"2187":{"position":[[0,4]]},"2215":{"position":[[0,4]]},"2655":{"position":[[4,4]]},"2785":{"position":[[7,4]]},"2789":{"position":[[7,4]]},"2799":{"position":[[8,4]]},"2801":{"position":[[8,4]]},"3366":{"position":[[0,4]]},"3368":{"position":[[0,4]]},"3370":{"position":[[6,4]]},"3424":{"position":[[0,4]]},"3426":{"position":[[0,4]]},"3456":{"position":[[0,4]]},"3468":{"position":[[25,4]]}}}],["http/2",{"_index":230,"t":{"330":{"position":[[26,7]]},"1316":{"position":[[26,7]]},"2463":{"position":[[26,7]]}}}],["http/3",{"_index":276,"t":{"396":{"position":[[19,6]]},"1318":{"position":[[26,7]]},"2465":{"position":[[26,7]]}}}],["http_stream",{"_index":613,"t":{"2268":{"position":[[0,11]]},"3475":{"position":[[0,11]]}}}],["http_stream_max_request_body_s",{"_index":614,"t":{"2270":{"position":[[0,33]]},"3477":{"position":[[0,33]]}}}],["iat",{"_index":394,"t":{"743":{"position":[[0,3]]},"1762":{"position":[[0,3]]},"1818":{"position":[[0,3]]},"2951":{"position":[[0,3]]},"3007":{"position":[[0,3]]}}}],["idiomat",{"_index":257,"t":{"358":{"position":[[0,9]]},"1378":{"position":[[0,9]]},"2529":{"position":[[0,9]]}}}],["imag",{"_index":198,"t":{"274":{"position":[[13,6]]},"524":{"position":[[7,5]]},"589":{"position":[[7,5]]},"1288":{"position":[[13,6]]},"1432":{"position":[[7,5]]},"1554":{"position":[[7,5]]},"2416":{"position":[[13,6]]},"2615":{"position":[[7,5]]},"2757":{"position":[[7,5]]}}}],["implement",{"_index":104,"t":{"135":{"position":[[0,12]]},"257":{"position":[[7,14]]},"1161":{"position":[[7,14]]},"1183":{"position":[[7,14]]},"1568":{"position":[[0,14]]},"2123":{"position":[[0,14]]},"2727":{"position":[[0,14]]},"3338":{"position":[[0,14]]}}}],["import",{"_index":434,"t":{"889":{"position":[[0,9]]},"1956":{"position":[[0,9]]},"3169":{"position":[[0,9]]}}}],["improv",{"_index":60,"t":{"77":{"position":[[11,12]]},"81":{"position":[[7,12]]},"85":{"position":[[12,12]]},"137":{"position":[[14,8]]},"167":{"position":[[9,12]]}}}],["index",{"_index":99,"t":{"127":{"position":[[8,5]]}}}],["indic",{"_index":86,"t":{"109":{"position":[[15,10]]}}}],["info",{"_index":398,"t":{"751":{"position":[[0,4]]},"771":{"position":[[33,4]]},"1022":{"position":[[0,4]]},"1130":{"position":[[0,4]]},"1770":{"position":[[0,4]]},"1790":{"position":[[33,4]]},"1806":{"position":[[0,4]]},"2211":{"position":[[0,4]]},"2959":{"position":[[0,4]]},"2979":{"position":[[33,4]]},"2995":{"position":[[0,4]]},"3452":{"position":[[0,4]]}}}],["inform",{"_index":290,"t":{"430":{"position":[[16,11]]},"1414":{"position":[[16,11]]},"2571":{"position":[[16,11]]}}}],["initi",{"_index":520,"t":{"1340":{"position":[[12,7]]},"2487":{"position":[[12,7]]}}}],["input",{"_index":173,"t":{"227":{"position":[[18,5]]}}}],["insecur",{"_index":425,"t":{"857":{"position":[[6,8]]},"933":{"position":[[0,8]]},"934":{"position":[[0,8]]},"936":{"position":[[0,8]]},"938":{"position":[[0,8]]},"1724":{"position":[[6,8]]},"2008":{"position":[[0,8]]},"2009":{"position":[[0,8]]},"2011":{"position":[[0,8]]},"2013":{"position":[[0,8]]},"2913":{"position":[[6,8]]},"3225":{"position":[[0,8]]},"3226":{"position":[[0,8]]},"3230":{"position":[[0,8]]},"3232":{"position":[[0,8]]}}}],["instal",{"_index":69,"t":{"95":{"position":[[0,7]]},"225":{"position":[[0,7]]},"446":{"position":[[3,7]]},"522":{"position":[[0,7]]},"1430":{"position":[[0,7]]},"1446":{"position":[[3,7]]},"2587":{"position":[[3,7]]},"2613":{"position":[[0,7]]}}}],["instanc",{"_index":205,"t":{"306":{"position":[[40,8]]},"1292":{"position":[[40,8]]},"2439":{"position":[[40,8]]}}}],["instead",{"_index":460,"t":{"980":{"position":[[8,7]]},"2039":{"position":[[8,7]]},"3280":{"position":[[8,7]]}}}],["insuffici",{"_index":420,"t":{"829":{"position":[[0,12]]}}}],["integr",{"_index":91,"t":{"119":{"position":[[4,9]]},"147":{"position":[[4,9]]},"410":{"position":[[7,11]]},"1354":{"position":[[10,12]]},"1394":{"position":[[7,11]]},"1634":{"position":[[9,9]]},"2547":{"position":[[10,12]]},"2551":{"position":[[7,11]]},"2851":{"position":[[9,9]]}}}],["interact",{"_index":121,"t":{"159":{"position":[[0,11]]}}}],["interfac",{"_index":424,"t":{"855":{"position":[[17,9]]},"1722":{"position":[[17,9]]},"2911":{"position":[[17,9]]}}}],["intern",{"_index":407,"t":{"781":{"position":[[0,8]]},"839":{"position":[[0,8]]},"925":{"position":[[7,8]]},"1832":{"position":[[0,8]]},"2000":{"position":[[7,8]]},"3023":{"position":[[0,8]]},"3217":{"position":[[7,8]]}}}],["interv",{"_index":334,"t":{"547":{"position":[[5,8]]}}}],["introduc",{"_index":168,"t":{"217":{"position":[[0,11]]}}}],["invalid",{"_index":365,"t":{"653":{"position":[[0,10]]},"813":{"position":[[0,7]]},"1700":{"position":[[0,10]]},"2824":{"position":[[0,10]]}}}],["invalidate_user_token",{"_index":573,"t":{"1701":{"position":[[0,22]]},"2825":{"position":[[0,22]]}}}],["investig",{"_index":405,"t":{"773":{"position":[[0,13]]},"1792":{"position":[[0,13]]},"2981":{"position":[[0,13]]}}}],["iss",{"_index":397,"t":{"749":{"position":[[0,3]]},"1032":{"position":[[0,3]]},"1768":{"position":[[0,3]]},"1816":{"position":[[0,3]]},"2957":{"position":[[0,3]]},"3005":{"position":[[0,3]]}}}],["it'",{"_index":19,"t":{"25":{"position":[[4,4]]}}}],["iter",{"_index":52,"t":{"69":{"position":[[8,9]]},"962":{"position":[[8,9]]},"2099":{"position":[[8,9]]},"3258":{"position":[[8,9]]}}}],["javascript",{"_index":264,"t":{"378":{"position":[[30,10]]},"394":{"position":[[0,10]]}}}],["join",{"_index":279,"t":{"404":{"position":[[0,4]]},"492":{"position":[[0,4]]},"2121":{"position":[[0,4]]},"3336":{"position":[[0,4]]}}}],["join/leav",{"_index":250,"t":{"348":{"position":[[28,10]]},"350":{"position":[[40,10]]},"1336":{"position":[[28,10]]},"1338":{"position":[[40,10]]},"1752":{"position":[[0,10]]},"2483":{"position":[[28,10]]},"2485":{"position":[[40,10]]},"2941":{"position":[[0,10]]}}}],["join_leav",{"_index":380,"t":{"709":{"position":[[0,10]]},"1886":{"position":[[0,10]]},"3077":{"position":[[0,10]]}}}],["json",{"_index":221,"t":{"322":{"position":[[27,4]]},"775":{"position":[[0,4]]},"883":{"position":[[0,4]]},"1152":{"position":[[0,4]]},"1308":{"position":[[27,4]]},"1794":{"position":[[0,4]]},"1950":{"position":[[0,4]]},"2257":{"position":[[13,4]]},"2280":{"position":[[0,4]]},"2455":{"position":[[27,4]]},"2983":{"position":[[0,4]]},"3163":{"position":[[0,4]]},"3491":{"position":[[13,4]]},"3507":{"position":[[0,4]]}}}],["jti",{"_index":395,"t":{"745":{"position":[[0,3]]},"1764":{"position":[[0,3]]},"1820":{"position":[[0,3]]},"2953":{"position":[[0,3]]},"3009":{"position":[[0,3]]}}}],["jwk",{"_index":576,"t":{"1796":{"position":[[8,4]]},"2985":{"position":[[8,4]]}}}],["jwt",{"_index":133,"t":{"181":{"position":[[39,3]]},"187":{"position":[[32,3]]},"561":{"position":[[0,3]]},"629":{"position":[[7,3]]},"773":{"position":[[28,3]]},"1598":{"position":[[7,3]]},"1756":{"position":[[11,3]]},"1792":{"position":[[28,3]]},"1800":{"position":[[13,3]]},"2793":{"position":[[7,3]]},"2945":{"position":[[11,3]]},"2981":{"position":[[28,3]]},"2989":{"position":[[13,3]]}}}],["kafka",{"_index":624,"t":{"2503":{"position":[[14,5]]}}}],["keep",{"_index":191,"t":{"259":{"position":[[7,4]]}}}],["key",{"_index":161,"t":{"211":{"position":[[15,4]]},"597":{"position":[[20,3]]},"775":{"position":[[9,3]]},"1046":{"position":[[14,3]]},"1142":{"position":[[9,3]]},"1562":{"position":[[20,3]]},"1794":{"position":[[9,3]]},"2223":{"position":[[9,3]]},"2227":{"position":[[14,3]]},"2765":{"position":[[20,3]]},"2983":{"position":[[9,3]]},"3414":{"position":[[14,3]]},"3464":{"position":[[9,3]]}}}],["keycloak",{"_index":202,"t":{"296":{"position":[[0,8]]}}}],["keydb",{"_index":462,"t":{"986":{"position":[[0,5]]},"2045":{"position":[[0,5]]}}}],["know",{"_index":238,"t":{"336":{"position":[[10,4]]},"1324":{"position":[[10,4]]},"2471":{"position":[[10,4]]}}}],["kubernet",{"_index":319,"t":{"528":{"position":[[0,10]]},"591":{"position":[[0,10]]},"1436":{"position":[[0,10]]},"1556":{"position":[[0,10]]},"2619":{"position":[[0,10]]},"2759":{"position":[[0,10]]}}}],["lab",{"_index":169,"t":{"217":{"position":[[24,4]]}}}],["land",{"_index":196,"t":{"274":{"position":[[0,7]]},"1288":{"position":[[0,7]]},"2416":{"position":[[0,7]]}}}],["laravel",{"_index":112,"t":{"147":{"position":[[14,7]]}}}],["late",{"_index":180,"t":{"235":{"position":[[0,4]]}}}],["latenc",{"_index":315,"t":{"482":{"position":[[7,7]]}}}],["layer",{"_index":265,"t":{"380":{"position":[[3,8]]}}}],["leav",{"_index":608,"t":{"2121":{"position":[[9,5]]},"3336":{"position":[[9,5]]}}}],["level",{"_index":492,"t":{"1163":{"position":[[4,5]]},"2243":{"position":[[4,5]]},"3554":{"position":[[4,5]]}}}],["librari",{"_index":3,"t":{"7":{"position":[[10,9]]},"1134":{"position":[[9,9]]},"1352":{"position":[[11,7]]},"2215":{"position":[[9,9]]},"2545":{"position":[[11,7]]},"3456":{"position":[[9,9]]}}}],["licens",{"_index":47,"t":{"65":{"position":[[0,7]]},"597":{"position":[[12,7]]},"1562":{"position":[[12,7]]},"2765":{"position":[[12,7]]}}}],["lifecycl",{"_index":316,"t":{"502":{"position":[[11,9]]},"1364":{"position":[[11,9]]},"2515":{"position":[[11,9]]}}}],["limit",{"_index":40,"t":{"53":{"position":[[0,11]]},"175":{"position":[[10,7]]},"542":{"position":[[12,5]]},"793":{"position":[[0,5]]},"835":{"position":[[11,5]]},"948":{"position":[[11,5]]},"1508":{"position":[[5,7]]},"1844":{"position":[[0,5]]},"2075":{"position":[[11,5]]},"2639":{"position":[[5,7]]},"2829":{"position":[[30,5]]},"2831":{"position":[[24,5]]},"2833":{"position":[[20,5]]},"3035":{"position":[[0,5]]},"3242":{"position":[[11,5]]}}}],["line",{"_index":338,"t":{"551":{"position":[[13,4]]},"875":{"position":[[8,4]]},"1942":{"position":[[8,4]]},"3155":{"position":[[8,4]]}}}],["linux",{"_index":325,"t":{"530":{"position":[[25,5]]},"1438":{"position":[[25,5]]},"2621":{"position":[[25,5]]}}}],["list",{"_index":482,"t":{"1092":{"position":[[11,4]]},"2175":{"position":[[11,4]]},"2255":{"position":[[0,4]]},"3404":{"position":[[11,4]]},"3489":{"position":[[0,4]]}}}],["listen",{"_index":249,"t":{"348":{"position":[[18,6]]},"1336":{"position":[[18,6]]},"2358":{"position":[[0,6]]},"2483":{"position":[[18,6]]},"3646":{"position":[[0,6]]}}}],["locat",{"_index":466,"t":{"1002":{"position":[[11,8]]},"2109":{"position":[[11,8]]},"3310":{"position":[[11,8]]}}}],["log",{"_index":639,"t":{"3328":{"position":[[0,4]]}}}],["ltd",{"_index":170,"t":{"217":{"position":[[29,3]]}}}],["maco",{"_index":327,"t":{"532":{"position":[[13,5]]},"1440":{"position":[[13,5]]},"2623":{"position":[[13,5]]}}}],["maintain",{"_index":473,"t":{"1042":{"position":[[0,8]]},"2133":{"position":[[0,8]]},"3362":{"position":[[0,8]]}}}],["manag",{"_index":288,"t":{"422":{"position":[[11,10]]},"1406":{"position":[[11,10]]},"2356":{"position":[[13,10]]},"2563":{"position":[[11,10]]},"3644":{"position":[[13,10]]}}}],["mani",{"_index":7,"t":{"11":{"position":[[8,4]]},"306":{"position":[[4,4]]},"803":{"position":[[4,4]]},"1292":{"position":[[4,4]]},"1854":{"position":[[4,4]]},"2439":{"position":[[4,4]]},"3045":{"position":[[4,4]]}}}],["massiv",{"_index":13,"t":{"17":{"position":[[0,7]]}}}],["match",{"_index":529,"t":{"1480":{"position":[[18,5]]},"1482":{"position":[[15,5]]},"1484":{"position":[[28,5]]},"2673":{"position":[[18,5]]},"2675":{"position":[[15,5]]},"2677":{"position":[[28,5]]}}}],["matrix",{"_index":490,"t":{"1161":{"position":[[30,6]]},"2259":{"position":[[12,6]]},"3493":{"position":[[12,6]]}}}],["max",{"_index":454,"t":{"954":{"position":[[6,3]]},"2081":{"position":[[6,3]]},"3248":{"position":[[6,3]]}}}],["memori",{"_index":206,"t":{"308":{"position":[[0,6]]},"968":{"position":[[0,6]]},"970":{"position":[[0,6]]},"1294":{"position":[[0,6]]},"1675":{"position":[[3,6]]},"1677":{"position":[[3,6]]},"2023":{"position":[[0,6]]},"2025":{"position":[[0,6]]},"2441":{"position":[[0,6]]},"2829":{"position":[[3,6]]},"2831":{"position":[[3,6]]},"3264":{"position":[[0,6]]},"3266":{"position":[[0,6]]}}}],["messag",{"_index":8,"t":{"11":{"position":[[13,8]]},"19":{"position":[[0,7]]},"35":{"position":[[6,7]]},"229":{"position":[[5,8]]},"288":{"position":[[15,8]]},"312":{"position":[[0,7]]},"314":{"position":[[0,7]]},"320":{"position":[[19,7],[54,7]]},"336":{"position":[[17,7]]},"338":{"position":[[18,8]]},"360":{"position":[[0,7]]},"362":{"position":[[0,7]]},"364":{"position":[[0,7]]},"432":{"position":[[0,7]]},"964":{"position":[[10,7]]},"1175":{"position":[[30,8]]},"1187":{"position":[[0,7]]},"1269":{"position":[[15,7]]},"1298":{"position":[[0,7]]},"1300":{"position":[[0,7]]},"1306":{"position":[[19,7],[54,7]]},"1324":{"position":[[17,7]]},"1326":{"position":[[18,8]]},"1380":{"position":[[0,7]]},"1382":{"position":[[0,7]]},"1384":{"position":[[0,7]]},"1416":{"position":[[0,7]]},"2101":{"position":[[10,7]]},"2293":{"position":[[15,7]]},"2445":{"position":[[0,7]]},"2447":{"position":[[0,7]]},"2453":{"position":[[19,7],[54,7]]},"2471":{"position":[[17,7]]},"2473":{"position":[[18,8]]},"2531":{"position":[[0,7]]},"2533":{"position":[[0,7]]},"2535":{"position":[[0,7]]},"2573":{"position":[[0,7]]},"3260":{"position":[[10,7]]},"3523":{"position":[[15,7]]}}}],["meta",{"_index":400,"t":{"759":{"position":[[0,4]]},"1778":{"position":[[0,4]]},"2967":{"position":[[0,4]]}}}],["metadata",{"_index":476,"t":{"1062":{"position":[[11,8]]},"2143":{"position":[[11,8]]},"3372":{"position":[[11,8]]}}}],["method",{"_index":345,"t":{"557":{"position":[[13,6]]},"789":{"position":[[0,6]]},"843":{"position":[[0,6]]},"1840":{"position":[[0,6]]},"2348":{"position":[[7,7]]},"2364":{"position":[[13,7]]},"3031":{"position":[[0,6]]},"3428":{"position":[[4,7]]},"3636":{"position":[[7,7]]},"3652":{"position":[[13,7]]}}}],["metric",{"_index":568,"t":{"1669":{"position":[[0,7]]},"2719":{"position":[[8,7]]},"2888":{"position":[[8,7]]},"3316":{"position":[[0,7]]},"3317":{"position":[[11,7]]},"3319":{"position":[[9,7]]},"3323":{"position":[[8,7]]}}}],["migrat",{"_index":118,"t":{"155":{"position":[[9,10]]},"400":{"position":[[0,9]]},"474":{"position":[[14,7]]},"1498":{"position":[[11,9]]},"1500":{"position":[[25,9]]},"1502":{"position":[[7,9]]},"1506":{"position":[[19,9]]},"1508":{"position":[[21,9]]},"1510":{"position":[[24,9]]},"2629":{"position":[[11,9]]},"2631":{"position":[[25,9]]},"2633":{"position":[[7,9]]},"2637":{"position":[[19,9]]},"2639":{"position":[[21,9]]},"2641":{"position":[[24,9]]}}}],["misbehav",{"_index":571,"t":{"1681":{"position":[[25,11]]},"2835":{"position":[[25,11]]}}}],["mobil",{"_index":236,"t":{"334":{"position":[[45,6]]},"1322":{"position":[[45,6]]},"2469":{"position":[[45,6]]}}}],["mode",{"_index":349,"t":{"577":{"position":[[8,4]]},"857":{"position":[[15,4]]},"933":{"position":[[9,5]]},"936":{"position":[[13,4]]},"938":{"position":[[15,4]]},"1086":{"position":[[7,4]]},"1088":{"position":[[15,4]]},"1090":{"position":[[22,4]]},"1584":{"position":[[24,4]]},"1724":{"position":[[15,4]]},"2008":{"position":[[9,5]]},"2011":{"position":[[13,4]]},"2013":{"position":[[15,4]]},"2169":{"position":[[7,4]]},"2171":{"position":[[15,4]]},"2173":{"position":[[22,4]]},"2781":{"position":[[24,4]]},"2913":{"position":[[15,4]]},"3225":{"position":[[9,5]]},"3230":{"position":[[13,4]]},"3232":{"position":[[15,4]]},"3352":{"position":[[15,4]]},"3398":{"position":[[7,4]]},"3400":{"position":[[15,4]]},"3402":{"position":[[22,4]]},"3466":{"position":[[16,4]]}}}],["model",{"_index":119,"t":{"155":{"position":[[24,6]]},"312":{"position":[[17,5]]},"362":{"position":[[17,5]]},"1298":{"position":[[17,5]]},"1382":{"position":[[17,5]]},"1740":{"position":[[21,5]]},"1742":{"position":[[19,5]]},"1744":{"position":[[19,5]]},"1746":{"position":[[20,5]]},"1748":{"position":[[23,5]]},"1750":{"position":[[20,5]]},"1752":{"position":[[22,5]]},"2445":{"position":[[17,5]]},"2533":{"position":[[17,5]]},"2929":{"position":[[21,5]]},"2931":{"position":[[19,5]]},"2933":{"position":[[19,5]]},"2935":{"position":[[20,5]]},"2937":{"position":[[23,5]]},"2939":{"position":[[20,5]]},"2941":{"position":[[22,5]]}}}],["modern",{"_index":262,"t":{"378":{"position":[[0,6]]}}}],["monitor",{"_index":300,"t":{"460":{"position":[[3,7]]},"1460":{"position":[[3,7]]},"2601":{"position":[[3,7]]}}}],["more",{"_index":351,"t":{"583":{"position":[[0,4]]}}}],["motiv",{"_index":308,"t":{"474":{"position":[[0,10]]},"488":{"position":[[0,10]]},"1622":{"position":[[0,10]]},"2839":{"position":[[0,10]]},"3346":{"position":[[0,10]]}}}],["move",{"_index":273,"t":{"394":{"position":[[18,5]]}}}],["multiten",{"_index":521,"t":{"1342":{"position":[[24,13]]},"2489":{"position":[[24,13]]}}}],["name",{"_index":373,"t":{"689":{"position":[[8,4]]},"1870":{"position":[[8,4]]},"3061":{"position":[[8,4]]}}}],["namespac",{"_index":269,"t":{"384":{"position":[[26,10]]},"691":{"position":[[0,9]]},"733":{"position":[[8,10]]},"942":{"position":[[8,10]]},"1510":{"position":[[0,9]]},"1872":{"position":[[0,9]]},"1880":{"position":[[8,10]]},"2017":{"position":[[8,10]]},"2641":{"position":[[0,9]]},"3063":{"position":[[0,9]]},"3071":{"position":[[8,10]]},"3236":{"position":[[8,10]]}}}],["nat",{"_index":463,"t":{"992":{"position":[[0,4]]},"2053":{"position":[[0,4]]},"2505":{"position":[[14,4]]},"3292":{"position":[[0,4]]}}}],["need",{"_index":235,"t":{"334":{"position":[[10,4]]},"1322":{"position":[[10,4]]},"2469":{"position":[[10,4]]}}}],["new",{"_index":64,"t":{"83":{"position":[[0,3]]},"203":{"position":[[0,3]]},"326":{"position":[[27,3]]},"338":{"position":[[14,3]]},"1312":{"position":[[27,3]]},"1326":{"position":[[14,3]]},"2459":{"position":[[27,3]]},"2473":{"position":[[14,3]]}}}],["nginx",{"_index":103,"t":{"133":{"position":[[7,5]]},"286":{"position":[[7,5]]},"328":{"position":[[48,5]]},"998":{"position":[[0,5]]},"1314":{"position":[[48,5]]},"2105":{"position":[[0,5]]},"2461":{"position":[[48,5]]},"3306":{"position":[[0,5]]}}}],["node",{"_index":22,"t":{"29":{"position":[[11,4]]},"201":{"position":[[0,4]]},"2653":{"position":[[0,4]]}}}],["non",{"_index":549,"t":{"1630":{"position":[[0,3]]},"1862":{"position":[[0,3]]},"2847":{"position":[[0,3]]},"3053":{"position":[[0,3]]}}}],["normal",{"_index":416,"t":{"809":{"position":[[0,6]]}}}],["note",{"_index":498,"t":{"1191":{"position":[[11,5]]},"2123":{"position":[[15,5]]},"2251":{"position":[[11,5]]},"3338":{"position":[[15,5]]},"3562":{"position":[[11,5]]}}}],["notif",{"_index":187,"t":{"251":{"position":[[19,13]]},"253":{"position":[[5,13]]},"261":{"position":[[12,13]]},"267":{"position":[[5,13]]},"334":{"position":[[28,13]]},"1322":{"position":[[28,13]]},"1542":{"position":[[0,13]]},"2469":{"position":[[28,13]]},"2711":{"position":[[0,13]]}}}],["number",{"_index":215,"t":{"318":{"position":[[35,6]]},"1304":{"position":[[35,6]]},"2451":{"position":[[35,6]]}}}],["obtrus",{"_index":550,"t":{"1630":{"position":[[4,9]]},"2847":{"position":[[4,9]]}}}],["old",{"_index":143,"t":{"195":{"position":[[9,3]]},"2655":{"position":[[0,3]]}}}],["on",{"_index":188,"t":{"253":{"position":[[31,3]]},"306":{"position":[[25,3]]},"322":{"position":[[43,3]]},"1292":{"position":[[25,3]]},"1308":{"position":[[43,3]]},"2439":{"position":[[25,3]]},"2455":{"position":[[43,3]]}}}],["onlin",{"_index":32,"t":{"43":{"position":[[0,6]]},"324":{"position":[[0,6],[32,6]]},"350":{"position":[[20,6]]},"368":{"position":[[0,6]]},"430":{"position":[[0,6]]},"1310":{"position":[[0,6],[32,6]]},"1338":{"position":[[20,6]]},"1388":{"position":[[0,6]]},"1414":{"position":[[0,6]]},"2117":{"position":[[9,6]]},"2457":{"position":[[0,6],[32,6]]},"2485":{"position":[[20,6]]},"2539":{"position":[[0,6]]},"2571":{"position":[[0,6]]},"3332":{"position":[[9,6]]}}}],["open",{"_index":297,"t":{"440":{"position":[[0,4]]},"948":{"position":[[0,4]]},"1424":{"position":[[0,4]]},"2075":{"position":[[0,4]]},"2581":{"position":[[0,4]]},"3242":{"position":[[0,4]]}}}],["openapi",{"_index":153,"t":{"205":{"position":[[0,7]]}}}],["opentelemetri",{"_index":157,"t":{"207":{"position":[[0,13]]},"3326":{"position":[[0,13]]}}}],["oper",{"_index":356,"t":{"607":{"position":[[0,10]]},"1538":{"position":[[0,10]]},"2707":{"position":[[0,10]]}}}],["optimist",{"_index":272,"t":{"388":{"position":[[0,10]]}}}],["option",{"_index":335,"t":{"547":{"position":[[14,7]]},"549":{"position":[[8,7]]},"697":{"position":[[8,7]]},"731":{"position":[[8,7]]},"853":{"position":[[0,7]]},"889":{"position":[[10,7]]},"899":{"position":[[9,7]]},"940":{"position":[[22,7]]},"970":{"position":[[14,7]]},"974":{"position":[[13,7]]},"990":{"position":[[17,7]]},"994":{"position":[[0,7]]},"1080":{"position":[[11,7]]},"1154":{"position":[[0,7]]},"1201":{"position":[[0,7]]},"1218":{"position":[[0,7]]},"1229":{"position":[[0,7]]},"1256":{"position":[[0,7]]},"1273":{"position":[[0,7]]},"1514":{"position":[[20,6]]},"1644":{"position":[[6,7]]},"1720":{"position":[[0,7]]},"1882":{"position":[[8,7]]},"1956":{"position":[[10,7]]},"1966":{"position":[[9,7]]},"2015":{"position":[[22,7]]},"2025":{"position":[[14,7]]},"2029":{"position":[[13,7]]},"2051":{"position":[[17,7]]},"2055":{"position":[[0,7]]},"2163":{"position":[[11,7]]},"2267":{"position":[[0,7]]},"2282":{"position":[[0,7]]},"2299":{"position":[[0,7]]},"2308":{"position":[[0,7]]},"2333":{"position":[[0,7]]},"2346":{"position":[[14,7]]},"2362":{"position":[[20,7]]},"2390":{"position":[[0,7]]},"2407":{"position":[[0,7]]},"2420":{"position":[[0,7]]},"2645":{"position":[[20,6]]},"2861":{"position":[[6,7]]},"2909":{"position":[[0,7]]},"3073":{"position":[[8,7]]},"3169":{"position":[[10,7]]},"3179":{"position":[[9,7]]},"3234":{"position":[[22,7]]},"3266":{"position":[[14,7]]},"3270":{"position":[[13,7]]},"3290":{"position":[[17,7]]},"3294":{"position":[[0,7]]},"3392":{"position":[[11,7]]},"3474":{"position":[[0,7]]},"3509":{"position":[[0,7]]},"3516":{"position":[[0,7]]},"3529":{"position":[[0,7]]},"3574":{"position":[[0,7]]},"3589":{"position":[[0,7]]},"3604":{"position":[[0,7]]},"3615":{"position":[[0,7]]},"3634":{"position":[[14,7]]},"3650":{"position":[[20,7]]}}}],["order",{"_index":36,"t":{"47":{"position":[[0,5]]},"237":{"position":[[0,8]]},"239":{"position":[[16,8]]},"314":{"position":[[8,5]]},"364":{"position":[[8,5]]},"1300":{"position":[[8,5]]},"1384":{"position":[[8,5]]},"2447":{"position":[[8,5]]},"2535":{"position":[[8,5]]}}}],["organ",{"_index":244,"t":{"342":{"position":[[23,8]]},"1330":{"position":[[23,8]]},"2477":{"position":[[23,8]]}}}],["origin",{"_index":342,"t":{"553":{"position":[[17,6]]}}}],["os",{"_index":4,"t":{"9":{"position":[[0,2]]},"877":{"position":[[0,2]]},"1944":{"position":[[0,2]]},"3157":{"position":[[0,2]]}}}],["output",{"_index":174,"t":{"227":{"position":[[28,6]]}}}],["over",{"_index":88,"t":{"111":{"position":[[14,4]]},"338":{"position":[[27,4]]},"428":{"position":[[4,4]]},"942":{"position":[[19,4]]},"1326":{"position":[[27,4]]},"1412":{"position":[[4,4]]},"2017":{"position":[[19,4]]},"2473":{"position":[[27,4]]},"2569":{"position":[[4,4]]},"3236":{"position":[[19,4]]}}}],["overrid",{"_index":577,"t":{"1822":{"position":[[0,8]]},"3011":{"position":[[0,8]]}}}],["overview",{"_index":68,"t":{"93":{"position":[[0,8]]},"145":{"position":[[12,8]]},"2115":{"position":[[0,8]]},"2743":{"position":[[0,8]]}}}],["packag",{"_index":324,"t":{"530":{"position":[[12,8]]},"555":{"position":[[26,7]]},"1438":{"position":[[12,8]]},"2621":{"position":[[12,8]]}}}],["page",{"_index":197,"t":{"274":{"position":[[8,4]]},"1288":{"position":[[8,4]]},"2416":{"position":[[8,4]]}}}],["pars",{"_index":85,"t":{"109":{"position":[[0,7]]}}}],["pass",{"_index":27,"t":{"35":{"position":[[14,7]]}}}],["per",{"_index":208,"t":{"308":{"position":[[13,3]]},"1294":{"position":[[13,3]]},"1675":{"position":[[10,3]]},"1677":{"position":[[10,3]]},"1679":{"position":[[6,3]]},"2441":{"position":[[13,3]]},"2829":{"position":[[10,3]]},"2831":{"position":[[10,3]]},"2833":{"position":[[6,3]]}}}],["perform",{"_index":11,"t":{"15":{"position":[[0,11]]},"51":{"position":[[0,11]]},"85":{"position":[[0,11]]},"412":{"position":[[6,11]]},"1396":{"position":[[6,11]]},"2553":{"position":[[6,11]]}}}],["permiss",{"_index":409,"t":{"787":{"position":[[0,10]]},"1492":{"position":[[22,11]]},"1740":{"position":[[10,10]]},"1742":{"position":[[8,10]]},"1744":{"position":[[8,10]]},"1746":{"position":[[9,10]]},"1748":{"position":[[12,10]]},"1750":{"position":[[9,10]]},"1752":{"position":[[11,10]]},"1838":{"position":[[0,10]]},"2685":{"position":[[22,11]]},"2929":{"position":[[10,10]]},"2931":{"position":[[8,10]]},"2933":{"position":[[8,10]]},"2935":{"position":[[9,10]]},"2937":{"position":[[12,10]]},"2939":{"position":[[9,10]]},"2941":{"position":[[11,10]]},"3029":{"position":[[0,10]]}}}],["persist",{"_index":363,"t":{"647":{"position":[[6,11]]},"649":{"position":[[9,11]]},"665":{"position":[[6,11]]},"667":{"position":[[9,11]]},"1693":{"position":[[6,11]]},"1695":{"position":[[9,11]]},"1709":{"position":[[6,11]]},"1711":{"position":[[9,11]]},"2817":{"position":[[6,11]]},"2819":{"position":[[9,11]]},"2898":{"position":[[6,11]]},"2900":{"position":[[9,11]]}}}],["person",{"_index":137,"t":{"185":{"position":[[15,8]]},"1040":{"position":[[10,8]]},"2131":{"position":[[10,8]]},"3360":{"position":[[10,8]]}}}],["ping",{"_index":267,"t":{"382":{"position":[[11,4]]},"1177":{"position":[[0,4]]},"1199":{"position":[[0,5]]},"1216":{"position":[[0,5]]},"1254":{"position":[[0,5]]},"2245":{"position":[[0,4]]},"2331":{"position":[[0,5]]},"2405":{"position":[[0,5]]},"3556":{"position":[[0,4]]},"3572":{"position":[[0,5]]},"3587":{"position":[[0,5]]}}}],["ping/pong",{"_index":615,"t":{"2295":{"position":[[0,9]]},"2352":{"position":[[11,9]]},"3485":{"position":[[0,9]]},"3640":{"position":[[11,9]]}}}],["pipelin",{"_index":307,"t":{"472":{"position":[[12,10]]},"1132":{"position":[[8,10]]},"2213":{"position":[[8,10]]}}}],["pitfal",{"_index":178,"t":{"233":{"position":[[0,8]]}}}],["platform",{"_index":294,"t":{"436":{"position":[[6,8]]},"1420":{"position":[[6,8]]},"2577":{"position":[[6,8]]}}}],["pong",{"_index":268,"t":{"382":{"position":[[16,4]]},"1177":{"position":[[5,4]]},"2245":{"position":[[5,4]]},"3556":{"position":[[5,4]]}}}],["port",{"_index":437,"t":{"895":{"position":[[0,4]]},"925":{"position":[[16,5]]},"950":{"position":[[10,4]]},"1962":{"position":[[0,4]]},"2000":{"position":[[16,5]]},"2077":{"position":[[10,4]]},"3175":{"position":[[0,4]]},"3217":{"position":[[16,5]]},"3244":{"position":[[10,4]]}}}],["posit",{"_index":383,"t":{"715":{"position":[[0,8]]},"805":{"position":[[14,8]]},"849":{"position":[[14,8]]},"1748":{"position":[[0,11]]},"1856":{"position":[[14,8]]},"2937":{"position":[[0,11]]},"3047":{"position":[[14,8]]}}}],["possibl",{"_index":123,"t":{"167":{"position":[[0,8]]},"348":{"position":[[6,8]]},"1336":{"position":[[6,8]]},"2483":{"position":[[6,8]]}}}],["postgresql",{"_index":556,"t":{"1646":{"position":[[4,10]]},"2863":{"position":[[4,10]]}}}],["postman",{"_index":165,"t":{"213":{"position":[[32,7]]},"3628":{"position":[[15,8]]}}}],["practic",{"_index":214,"t":{"318":{"position":[[16,9]]},"1304":{"position":[[16,9]]},"2382":{"position":[[16,9]]},"2451":{"position":[[16,9]]},"3670":{"position":[[16,9]]}}}],["prefix",{"_index":376,"t":{"693":{"position":[[16,6]]},"1876":{"position":[[16,6]]},"3067":{"position":[[16,6]]}}}],["prerequisit",{"_index":93,"t":{"121":{"position":[[0,13]]}}}],["presenc",{"_index":33,"t":{"43":{"position":[[7,8],[20,8]]},"324":{"position":[[7,8]]},"350":{"position":[[27,8]]},"368":{"position":[[7,8]]},"430":{"position":[[7,8]]},"512":{"position":[[0,8],[13,8]]},"705":{"position":[[0,8]]},"1120":{"position":[[0,8]]},"1173":{"position":[[34,8]]},"1310":{"position":[[7,8]]},"1338":{"position":[[27,8]]},"1374":{"position":[[0,8],[13,8]]},"1388":{"position":[[7,8]]},"1414":{"position":[[7,8]]},"1746":{"position":[[0,8]]},"1884":{"position":[[0,8]]},"2117":{"position":[[16,8]]},"2119":{"position":[[11,8]]},"2201":{"position":[[0,8]]},"2380":{"position":[[0,8],[13,8]]},"2457":{"position":[[7,8]]},"2485":{"position":[[27,8]]},"2525":{"position":[[0,8],[13,8]]},"2539":{"position":[[7,8]]},"2571":{"position":[[7,8]]},"2935":{"position":[[0,8]]},"3075":{"position":[[0,8]]},"3332":{"position":[[16,8]]},"3334":{"position":[[11,8]]},"3442":{"position":[[0,8]]},"3668":{"position":[[0,8],[13,8]]}}}],["presence_cel",{"_index":537,"t":{"1528":{"position":[[0,12]]},"2697":{"position":[[0,12]]}}}],["presence_disable_for_cli",{"_index":379,"t":{"707":{"position":[[0,27]]}}}],["presence_stat",{"_index":484,"t":{"1122":{"position":[[0,14]]},"2203":{"position":[[0,14]]},"3444":{"position":[[0,14]]}}}],["presencemanag",{"_index":305,"t":{"468":{"position":[[11,15]]}}}],["preserv",{"_index":183,"t":{"239":{"position":[[25,9]]}}}],["price",{"_index":350,"t":{"579":{"position":[[0,7]]},"1586":{"position":[[0,7]]},"2779":{"position":[[0,7]]}}}],["privat",{"_index":241,"t":{"340":{"position":[[50,8]]},"386":{"position":[[0,7]]},"693":{"position":[[0,7]]},"1328":{"position":[[50,8]]},"1876":{"position":[[0,7]]},"2475":{"position":[[50,8]]},"3067":{"position":[[0,7]]}}}],["pro",{"_index":67,"t":{"87":{"position":[[11,3]]},"249":{"position":[[11,3]]},"442":{"position":[[0,3]]},"597":{"position":[[8,3]]},"1426":{"position":[[0,3]]},"1562":{"position":[[8,3]]},"2583":{"position":[[0,3]]},"2765":{"position":[[8,3]]}}}],["problem",{"_index":406,"t":{"773":{"position":[[14,8]]},"1792":{"position":[[14,8]]},"2981":{"position":[[14,8]]}}}],["process",{"_index":525,"t":{"1474":{"position":[[5,10]]},"2667":{"position":[[5,10]]}}}],["product",{"_index":299,"t":{"458":{"position":[[13,10]]},"1458":{"position":[[13,10]]},"2599":{"position":[[13,10]]}}}],["project",{"_index":95,"t":{"123":{"position":[[11,7]]},"149":{"position":[[18,7]]}}}],["prometheu",{"_index":468,"t":{"1008":{"position":[[0,10]]},"2089":{"position":[[0,10]]},"3298":{"position":[[0,10]]},"3317":{"position":[[0,10]]}}}],["properti",{"_index":38,"t":{"47":{"position":[[19,10]]}}}],["protect",{"_index":386,"t":{"721":{"position":[[0,9]]},"2085":{"position":[[18,10]]},"3252":{"position":[[18,10]]}}}],["protobuf",{"_index":344,"t":{"555":{"position":[[17,8]]},"1284":{"position":[[0,8]]},"2237":{"position":[[0,8]]},"2257":{"position":[[0,8]]},"2431":{"position":[[0,8]]},"3491":{"position":[[0,8]]},"3548":{"position":[[0,8]]},"3626":{"position":[[0,8]]}}}],["protocol",{"_index":144,"t":{"195":{"position":[[20,8]]},"201":{"position":[[19,8]]},"213":{"position":[[12,8]]},"380":{"position":[[22,8]]},"416":{"position":[[14,8]]},"1284":{"position":[[16,8]]},"1400":{"position":[[14,8]]},"2431":{"position":[[16,8]]},"2557":{"position":[[14,8]]},"3626":{"position":[[16,8]]}}}],["provid",{"_index":186,"t":{"251":{"position":[[4,9]]},"253":{"position":[[35,8]]}}}],["proxi",{"_index":59,"t":{"75":{"position":[[5,5]]},"135":{"position":[[13,5]]},"161":{"position":[[8,5]]},"179":{"position":[[15,5]]},"183":{"position":[[36,5]]},"189":{"position":[[29,5]]},"328":{"position":[[37,5]]},"559":{"position":[[5,5]]},"625":{"position":[[12,5]]},"627":{"position":[[12,5]]},"954":{"position":[[0,5]]},"1056":{"position":[[5,5]]},"1060":{"position":[[0,5]]},"1062":{"position":[[0,5]]},"1064":{"position":[[8,5]]},"1066":{"position":[[8,5]]},"1068":{"position":[[4,5]]},"1070":{"position":[[10,5]]},"1072":{"position":[[8,5]]},"1078":{"position":[[5,5]]},"1080":{"position":[[5,5]]},"1082":{"position":[[5,5]]},"1084":{"position":[[7,5]]},"1088":{"position":[[9,5]]},"1090":{"position":[[16,5]]},"1092":{"position":[[19,7]]},"1314":{"position":[[37,5]]},"1512":{"position":[[0,5]]},"1594":{"position":[[12,5]]},"1596":{"position":[[12,5]]},"1630":{"position":[[14,8]]},"2081":{"position":[[0,5]]},"2137":{"position":[[5,5]]},"2141":{"position":[[0,5]]},"2143":{"position":[[0,5]]},"2145":{"position":[[8,5]]},"2147":{"position":[[8,5]]},"2149":{"position":[[4,5]]},"2151":{"position":[[10,5]]},"2153":{"position":[[8,5]]},"2155":{"position":[[12,5]]},"2161":{"position":[[5,5]]},"2163":{"position":[[5,5]]},"2165":{"position":[[5,5]]},"2167":{"position":[[7,5]]},"2171":{"position":[[9,5]]},"2173":{"position":[[16,5]]},"2175":{"position":[[19,7]]},"2461":{"position":[[37,5]]},"2643":{"position":[[0,5]]},"2789":{"position":[[12,5]]},"2791":{"position":[[12,5]]},"2847":{"position":[[14,8]]},"3248":{"position":[[0,5]]},"3352":{"position":[[9,5]]},"3366":{"position":[[5,5]]},"3370":{"position":[[0,5]]},"3372":{"position":[[0,5]]},"3374":{"position":[[8,5]]},"3376":{"position":[[8,5]]},"3378":{"position":[[4,5]]},"3380":{"position":[[10,5]]},"3382":{"position":[[8,5]]},"3384":{"position":[[12,5]]},"3390":{"position":[[5,5]]},"3392":{"position":[[5,5]]},"3394":{"position":[[5,5]]},"3396":{"position":[[7,5]]},"3400":{"position":[[9,5]]},"3402":{"position":[[16,5]]},"3404":{"position":[[19,7]]}}}],["proxy_publish",{"_index":388,"t":{"725":{"position":[[0,13]]},"1926":{"position":[[0,13]]},"3119":{"position":[[0,13]]}}}],["proxy_sub_refresh",{"_index":599,"t":{"1928":{"position":[[0,17]]},"3121":{"position":[[0,17]]}}}],["proxy_subscrib",{"_index":387,"t":{"723":{"position":[[0,15]]},"1924":{"position":[[0,15]]},"3117":{"position":[[0,15]]}}}],["proxy_subscribe_stream",{"_index":634,"t":{"3123":{"position":[[0,22]]}}}],["public",{"_index":329,"t":{"542":{"position":[[0,11]]},"1540":{"position":[[0,12]]},"2358":{"position":[[18,12]]},"2709":{"position":[[0,12]]},"3646":{"position":[[18,12]]}}}],["publish",{"_index":179,"t":{"233":{"position":[[18,10]]},"320":{"position":[[27,9]]},"338":{"position":[[6,7]]},"456":{"position":[[3,7]]},"635":{"position":[[0,7]]},"699":{"position":[[0,7]]},"1072":{"position":[[0,7]]},"1096":{"position":[[23,7]]},"1108":{"position":[[0,7]]},"1173":{"position":[[16,8]]},"1306":{"position":[[27,9]]},"1326":{"position":[[6,7]]},"1456":{"position":[[3,7]]},"1604":{"position":[[0,7]]},"1742":{"position":[[0,7]]},"2153":{"position":[[0,7]]},"2179":{"position":[[20,8]]},"2189":{"position":[[0,7]]},"2453":{"position":[[27,9]]},"2473":{"position":[[6,7]]},"2597":{"position":[[3,7]]},"2799":{"position":[[0,7]]},"2931":{"position":[[0,7]]},"3382":{"position":[[0,7]]},"3408":{"position":[[20,8]]},"3430":{"position":[[0,7]]}}}],["publish_cel",{"_index":535,"t":{"1524":{"position":[[0,11]]},"2693":{"position":[[0,11]]}}}],["publish_proxy_nam",{"_index":390,"t":{"729":{"position":[[0,18]]},"1932":{"position":[[0,18]]},"3127":{"position":[[0,18]]}}}],["pubnub",{"_index":623,"t":{"2499":{"position":[[28,6]]}}}],["push",{"_index":175,"t":{"229":{"position":[[0,4]]},"251":{"position":[[14,4]]},"253":{"position":[[0,4]]},"265":{"position":[[0,4]]},"267":{"position":[[0,4]]},"334":{"position":[[23,4]]},"1322":{"position":[[23,4]]},"2241":{"position":[[13,6]]},"2469":{"position":[[23,4]]},"3552":{"position":[[13,6]]}}}],["pusher",{"_index":621,"t":{"2499":{"position":[[14,7]]}}}],["python",{"_index":486,"t":{"1138":{"position":[[17,6]]},"2219":{"position":[[17,6]]},"3460":{"position":[[17,6]]}}}],["queri",{"_index":357,"t":{"609":{"position":[[0,5]]},"1544":{"position":[[0,5]]},"2713":{"position":[[0,5]]}}}],["question",{"_index":255,"t":{"352":{"position":[[33,8]]},"1344":{"position":[[33,8]]},"2491":{"position":[[33,8]]}}}],["queu",{"_index":548,"t":{"1626":{"position":[[10,7]]},"2843":{"position":[[10,7]]}}}],["queue",{"_index":176,"t":{"229":{"position":[[23,5]]},"1646":{"position":[[18,5]]},"2863":{"position":[[18,5]]}}}],["quic",{"_index":79,"t":{"101":{"position":[[10,4]]},"105":{"position":[[7,4]]}}}],["rate",{"_index":632,"t":{"2829":{"position":[[25,4]]},"2831":{"position":[[19,4]]},"2833":{"position":[[15,4]]}}}],["rate_limit",{"_index":630,"t":{"2749":{"position":[[0,10]]},"2751":{"position":[[0,10]]}}}],["react",{"_index":203,"t":{"300":{"position":[[0,5]]}}}],["read",{"_index":302,"t":{"464":{"position":[[3,4]]},"1464":{"position":[[3,4]]},"1671":{"position":[[8,7]]},"2605":{"position":[[3,4]]},"2890":{"position":[[8,7]]}}}],["readi",{"_index":295,"t":{"438":{"position":[[0,5]]},"1422":{"position":[[0,5]]},"2579":{"position":[[0,5]]}}}],["real",{"_index":50,"t":{"67":{"position":[[15,4]]},"288":{"position":[[5,4]]},"418":{"position":[[11,4]]},"1402":{"position":[[11,4]]},"2559":{"position":[[11,4]]}}}],["reason",{"_index":497,"t":{"1189":{"position":[[20,6]]}}}],["receiv",{"_index":218,"t":{"320":{"position":[[42,9]]},"1306":{"position":[[42,9]]},"2453":{"position":[[42,9]]}}}],["reconnect",{"_index":14,"t":{"17":{"position":[[8,9]]},"500":{"position":[[0,12]]},"831":{"position":[[6,9]]},"833":{"position":[[9,9]]},"1362":{"position":[[0,12]]},"2513":{"position":[[0,12]]}}}],["recov",{"_index":384,"t":{"717":{"position":[[0,7]]}}}],["recoveri",{"_index":330,"t":{"542":{"position":[[22,8]]},"964":{"position":[[18,8]]},"1187":{"position":[[8,8]]},"1750":{"position":[[0,8]]},"2101":{"position":[[18,8]]},"2360":{"position":[[13,8]]},"2939":{"position":[[0,8]]},"3260":{"position":[[18,8]]},"3648":{"position":[[13,8]]}}}],["redesign",{"_index":266,"t":{"382":{"position":[[0,10]]}}}],["redi",{"_index":54,"t":{"71":{"position":[[0,5]]},"229":{"position":[[17,5]]},"563":{"position":[[0,5]]},"565":{"position":[[0,5]]},"617":{"position":[[0,5]]},"647":{"position":[[0,5]]},"665":{"position":[[0,5]]},"972":{"position":[[0,5]]},"974":{"position":[[0,5]]},"976":{"position":[[13,5]]},"978":{"position":[[0,5]]},"982":{"position":[[0,5]]},"984":{"position":[[0,5]]},"1679":{"position":[[0,5]]},"1693":{"position":[[0,5]]},"1709":{"position":[[0,5]]},"2027":{"position":[[0,5]]},"2029":{"position":[[0,5]]},"2031":{"position":[[12,5]]},"2033":{"position":[[13,5]]},"2035":{"position":[[0,5]]},"2037":{"position":[[0,5]]},"2041":{"position":[[0,5]]},"2043":{"position":[[0,5]]},"2047":{"position":[[6,5]]},"2501":{"position":[[14,5]]},"2817":{"position":[[0,5]]},"2833":{"position":[[0,5]]},"2898":{"position":[[0,5]]},"3268":{"position":[[0,5]]},"3270":{"position":[[0,5]]},"3272":{"position":[[12,5]]},"3274":{"position":[[13,5]]},"3276":{"position":[[0,5]]},"3278":{"position":[[0,5]]},"3282":{"position":[[0,5]]},"3284":{"position":[[0,5]]},"3286":{"position":[[6,5]]}}}],["redigo",{"_index":306,"t":{"470":{"position":[[0,6]]},"472":{"position":[[0,6]]}}}],["redis/redi",{"_index":310,"t":{"476":{"position":[[3,11]]}}}],["reduc",{"_index":313,"t":{"480":{"position":[[22,8]]}}}],["refactor",{"_index":149,"t":{"199":{"position":[[17,11]]}}}],["refresh",{"_index":477,"t":{"1066":{"position":[[0,7]]},"1094":{"position":[[21,7]]},"1118":{"position":[[0,7]]},"1171":{"position":[[0,7]]},"2147":{"position":[[0,7]]},"2155":{"position":[[4,7]]},"2177":{"position":[[21,7]]},"2179":{"position":[[33,7]]},"2199":{"position":[[0,7]]},"3376":{"position":[[0,7]]},"3384":{"position":[[4,7]]},"3406":{"position":[[21,7]]},"3408":{"position":[[33,7]]},"3440":{"position":[[0,7]]}}}],["regex",{"_index":530,"t":{"1482":{"position":[[9,5]]},"2675":{"position":[[9,5]]}}}],["relat",{"_index":612,"t":{"2261":{"position":[[11,7]]},"2263":{"position":[[25,7]]},"3495":{"position":[[11,7]]},"3497":{"position":[[25,7]]}}}],["releas",{"_index":318,"t":{"522":{"position":[[24,7]]},"587":{"position":[[7,7]]},"1430":{"position":[[24,7]]},"1552":{"position":[[7,7]]},"2613":{"position":[[24,7]]},"2755":{"position":[[7,7]]}}}],["remov",{"_index":333,"t":{"544":{"position":[[15,7]]},"551":{"position":[[24,7]]}}}],["repli",{"_index":610,"t":{"2239":{"position":[[8,5]]},"3550":{"position":[[8,5]]}}}],["request",{"_index":341,"t":{"553":{"position":[[9,7]]},"795":{"position":[[4,7]]},"803":{"position":[[9,8]]},"815":{"position":[[4,7]]},"845":{"position":[[4,7]]},"1058":{"position":[[5,7]]},"1846":{"position":[[4,7]]},"1854":{"position":[[9,8]]},"2139":{"position":[[5,7]]},"2749":{"position":[[11,7]]},"3037":{"position":[[4,7]]},"3045":{"position":[[9,8]]},"3368":{"position":[[5,7]]}}}],["result",{"_index":631,"t":{"2751":{"position":[[11,6]]}}}],["retriev",{"_index":607,"t":{"2119":{"position":[[0,10]]},"3334":{"position":[[0,10]]}}}],["return",{"_index":478,"t":{"1074":{"position":[[0,6]]},"1076":{"position":[[0,6]]},"2157":{"position":[[0,6]]},"2159":{"position":[[0,6]]},"3386":{"position":[[0,6]]},"3388":{"position":[[0,6]]}}}],["revers",{"_index":227,"t":{"328":{"position":[[29,7]]},"1314":{"position":[[29,7]]},"2461":{"position":[[29,7]]}}}],["revis",{"_index":271,"t":{"386":{"position":[[24,7]]}}}],["revok",{"_index":364,"t":{"651":{"position":[[0,6]]},"1478":{"position":[[0,8]]},"1492":{"position":[[0,8]]},"1697":{"position":[[0,6]]},"2671":{"position":[[0,8]]},"2685":{"position":[[0,8]]},"2821":{"position":[[0,6]]}}}],["revoke_token",{"_index":572,"t":{"1698":{"position":[[0,12]]},"2822":{"position":[[0,12]]}}}],["room",{"_index":101,"t":{"129":{"position":[[8,4]]},"163":{"position":[[0,4]]},"424":{"position":[[8,6]]},"1408":{"position":[[8,6]]},"2565":{"position":[[8,6]]}}}],["rpc",{"_index":28,"t":{"37":{"position":[[0,3]]},"428":{"position":[[0,3]]},"508":{"position":[[5,3]]},"677":{"position":[[26,3]]},"1068":{"position":[[0,3]]},"1098":{"position":[[9,3]]},"1173":{"position":[[0,3]]},"1370":{"position":[[5,3]]},"1412":{"position":[[0,3]]},"1728":{"position":[[26,3]]},"2149":{"position":[[0,3]]},"2181":{"position":[[9,3]]},"2376":{"position":[[0,3]]},"2521":{"position":[[5,3]]},"2569":{"position":[[0,3]]},"2917":{"position":[[26,3]]},"3378":{"position":[[0,3]]},"3410":{"position":[[9,3]]},"3664":{"position":[[0,3]]}}}],["rpm",{"_index":322,"t":{"530":{"position":[[0,3]]},"1438":{"position":[[0,3]]},"2621":{"position":[[0,3]]}}}],["rueidi",{"_index":311,"t":{"478":{"position":[[0,7]]},"480":{"position":[[13,8]]}}}],["rule",{"_index":374,"t":{"689":{"position":[[13,5]]},"1084":{"position":[[13,5]]},"1870":{"position":[[13,5]]},"2167":{"position":[[13,5]]},"3061":{"position":[[13,5]]},"3396":{"position":[[13,5]]}}}],["run",{"_index":77,"t":{"99":{"position":[[0,3]]},"225":{"position":[[12,3]]}}}],["sandbox",{"_index":348,"t":{"577":{"position":[[0,7]]},"1584":{"position":[[16,7]]},"2781":{"position":[[16,7]]}}}],["save",{"_index":366,"t":{"657":{"position":[[0,4]]},"1685":{"position":[[0,4]]},"2809":{"position":[[0,4]]}}}],["scalabl",{"_index":12,"t":{"15":{"position":[[19,11]]},"45":{"position":[[0,11]]},"350":{"position":[[4,8]]},"370":{"position":[[0,11]]},"414":{"position":[[9,11]]},"1338":{"position":[[4,8]]},"1390":{"position":[[0,11]]},"1398":{"position":[[9,11]]},"2485":{"position":[[4,8]]},"2541":{"position":[[0,11]]},"2555":{"position":[[9,11]]},"3344":{"position":[[0,11]]}}}],["scale",{"_index":209,"t":{"310":{"position":[[15,5]]},"462":{"position":[[3,5]]},"976":{"position":[[0,7]]},"1296":{"position":[[15,5]]},"1462":{"position":[[3,5]]},"2033":{"position":[[0,7]]},"2443":{"position":[[15,5]]},"2603":{"position":[[3,5]]},"3274":{"position":[[0,7]]}}}],["schema",{"_index":609,"t":{"2237":{"position":[[9,6]]},"3548":{"position":[[9,6]]}}}],["sdk",{"_index":147,"t":{"197":{"position":[[31,4]]},"376":{"position":[[15,3]]},"1498":{"position":[[7,3]]},"2255":{"position":[[15,4]]},"2257":{"position":[[29,4]]},"2259":{"position":[[0,3]]},"2382":{"position":[[0,3]]},"2629":{"position":[[7,3]]},"2651":{"position":[[7,3]]},"3489":{"position":[[15,4]]},"3491":{"position":[[29,4]]},"3493":{"position":[[0,3]]},"3670":{"position":[[0,3]]}}}],["secur",{"_index":192,"t":{"263":{"position":[[0,6]]},"340":{"position":[[16,6]]},"384":{"position":[[0,6]]},"1328":{"position":[[16,6]]},"1628":{"position":[[8,6]]},"2475":{"position":[[16,6]]},"2845":{"position":[[8,6]]}}}],["self",{"_index":73,"t":{"97":{"position":[[9,4]]}}}],["send",{"_index":6,"t":{"11":{"position":[[0,7]]},"261":{"position":[[7,4]]},"288":{"position":[[0,4]]},"334":{"position":[[18,4]]},"508":{"position":[[0,4]]},"1322":{"position":[[18,4]]},"1340":{"position":[[7,4]]},"1370":{"position":[[0,4]]},"2469":{"position":[[18,4]]},"2487":{"position":[[7,4]]},"2521":{"position":[[0,4]]}}}],["send_push_notif",{"_index":566,"t":{"1665":{"position":[[0,22]]},"2882":{"position":[[0,22]]}}}],["sentinel",{"_index":457,"t":{"978":{"position":[[6,8]]},"980":{"position":[[19,8]]},"2035":{"position":[[6,8]]},"2037":{"position":[[6,8]]},"2039":{"position":[[19,8]]},"3276":{"position":[[6,8]]},"3278":{"position":[[6,8]]},"3280":{"position":[[19,8]]}}}],["separ",{"_index":158,"t":{"209":{"position":[[0,8]]},"1000":{"position":[[0,8]]},"2107":{"position":[[0,8]]},"3017":{"position":[[0,8]]},"3308":{"position":[[0,8]]}}}],["seq/gen",{"_index":331,"t":{"544":{"position":[[0,7]]}}}],["server",{"_index":1,"t":{"5":{"position":[[10,6]]},"39":{"position":[[0,6]]},"77":{"position":[[0,6]]},"101":{"position":[[15,6]]},"103":{"position":[[0,6]]},"113":{"position":[[5,6]]},"131":{"position":[[20,6]]},"181":{"position":[[5,6]]},"183":{"position":[[5,6]]},"207":{"position":[[18,6]]},"257":{"position":[[0,6]]},"496":{"position":[[16,6]]},"498":{"position":[[21,6]]},"500":{"position":[[18,6]]},"506":{"position":[[0,6]]},"546":{"position":[[0,6]]},"679":{"position":[[19,6]]},"681":{"position":[[16,6]]},"683":{"position":[[19,6]]},"817":{"position":[[0,6]]},"837":{"position":[[0,6]]},"1038":{"position":[[8,6]]},"1175":{"position":[[13,6]]},"1185":{"position":[[0,6]]},"1358":{"position":[[16,6]]},"1360":{"position":[[21,6]]},"1362":{"position":[[18,6]]},"1368":{"position":[[0,6]]},"1516":{"position":[[0,6]]},"1730":{"position":[[19,6]]},"1732":{"position":[[16,6]]},"1734":{"position":[[19,6]]},"2085":{"position":[[11,6]]},"2129":{"position":[[8,6]]},"2368":{"position":[[0,6]]},"2509":{"position":[[16,6]]},"2511":{"position":[[21,6]]},"2513":{"position":[[18,6]]},"2519":{"position":[[0,6]]},"2647":{"position":[[0,6]]},"2919":{"position":[[19,6]]},"2921":{"position":[[16,6]]},"2923":{"position":[[19,6]]},"3215":{"position":[[15,6]]},"3252":{"position":[[11,6]]},"3358":{"position":[[8,6]]},"3656":{"position":[[0,6]]}}}],["session",{"_index":83,"t":{"107":{"position":[[11,7]]},"1148":{"position":[[7,8]]},"2276":{"position":[[7,8]]},"3503":{"position":[[7,8]]}}}],["set",{"_index":116,"t":{"153":{"position":[[12,8]]},"597":{"position":[[0,7]]},"940":{"position":[[0,7]]},"942":{"position":[[0,7]]},"1562":{"position":[[0,7]]},"2015":{"position":[[0,7]]},"2017":{"position":[[0,7]]},"2765":{"position":[[0,7]]},"3234":{"position":[[0,7]]},"3236":{"position":[[0,7]]}}}],["setup",{"_index":113,"t":{"149":{"position":[[0,5]]},"173":{"position":[[0,5]]}}}],["shard",{"_index":461,"t":{"982":{"position":[[6,8]]},"2041":{"position":[[6,8]]},"3282":{"position":[[6,8]]}}}],["shut",{"_index":626,"t":{"2659":{"position":[[0,8]]}}}],["shutdown",{"_index":417,"t":{"811":{"position":[[0,8]]}}}],["side",{"_index":29,"t":{"39":{"position":[[7,4]]},"165":{"position":[[7,4]]},"181":{"position":[[12,4]]},"183":{"position":[[12,4]]},"348":{"position":[[65,5]]},"506":{"position":[[7,4]]},"538":{"position":[[7,4]]},"546":{"position":[[7,4]]},"677":{"position":[[7,4]]},"1038":{"position":[[15,4]]},"1185":{"position":[[7,4]]},"1336":{"position":[[65,5]]},"1368":{"position":[[7,4]]},"1728":{"position":[[7,4]]},"2119":{"position":[[34,4]]},"2129":{"position":[[15,4]]},"2263":{"position":[[7,4]]},"2368":{"position":[[7,4]]},"2483":{"position":[[65,5]]},"2519":{"position":[[7,4]]},"2917":{"position":[[7,4]]},"3334":{"position":[[34,4]]},"3358":{"position":[[15,4]]},"3497":{"position":[[7,4]]},"3656":{"position":[[7,4]]}}}],["sign",{"_index":74,"t":{"97":{"position":[[14,6]]}}}],["signal",{"_index":448,"t":{"931":{"position":[[0,6]]},"2006":{"position":[[0,6]]},"3223":{"position":[[0,6]]}}}],["signatur",{"_index":637,"t":{"3228":{"position":[[21,9]]}}}],["simpl",{"_index":282,"t":{"410":{"position":[[0,6]]},"1394":{"position":[[0,6]]},"2551":{"position":[[0,6]]}}}],["simplest",{"_index":403,"t":{"767":{"position":[[0,8]]},"1786":{"position":[[0,8]]},"2975":{"position":[[0,8]]}}}],["simplifi",{"_index":163,"t":{"213":{"position":[[0,11]]}}}],["singl",{"_index":231,"t":{"332":{"position":[[24,6]]},"1042":{"position":[[9,6]]},"1320":{"position":[[24,6]]},"2133":{"position":[[9,6]]},"2467":{"position":[[24,6]]},"3362":{"position":[[9,6]]}}}],["site",{"_index":66,"t":{"83":{"position":[[18,4]]},"1002":{"position":[[27,4]]},"2109":{"position":[[27,4]]},"2659":{"position":[[32,4]]},"3310":{"position":[[27,4]]}}}],["skeleton",{"_index":80,"t":{"103":{"position":[[7,8]]}}}],["slow",{"_index":419,"t":{"825":{"position":[[0,4]]}}}],["socket",{"_index":452,"t":{"952":{"position":[[0,7]]},"2079":{"position":[[0,7]]},"3246":{"position":[[0,7]]}}}],["socket.io",{"_index":620,"t":{"2497":{"position":[[14,9]]}}}],["sockj",{"_index":167,"t":{"215":{"position":[[14,6]]},"567":{"position":[[0,6]]},"1146":{"position":[[0,6]]},"1155":{"position":[[0,6]]},"1502":{"position":[[0,6]]},"2274":{"position":[[0,6]]},"2283":{"position":[[0,6]]},"2633":{"position":[[0,6]]},"3501":{"position":[[0,6]]},"3510":{"position":[[0,6]]}}}],["sockjs_url",{"_index":489,"t":{"1157":{"position":[[0,10]]},"2285":{"position":[[0,10]]},"3512":{"position":[[0,10]]}}}],["sourc",{"_index":107,"t":{"139":{"position":[[9,6]]},"440":{"position":[[5,6]]},"534":{"position":[[11,6]]},"873":{"position":[[14,7]]},"1424":{"position":[[5,6]]},"1442":{"position":[[11,6]]},"1940":{"position":[[14,7]]},"2581":{"position":[[5,6]]},"2625":{"position":[[11,6]]},"3153":{"position":[[14,7]]}}}],["spec",{"_index":154,"t":{"205":{"position":[[8,4]]}}}],["special",{"_index":280,"t":{"406":{"position":[[0,7]]}}}],["sse",{"_index":616,"t":{"2300":{"position":[[0,3]]},"3517":{"position":[[0,3]]}}}],["sse_max_request_body_s",{"_index":617,"t":{"2302":{"position":[[0,25]]},"3519":{"position":[[0,25]]}}}],["sss",{"_index":496,"t":{"1185":{"position":[[26,5]]}}}],["stale",{"_index":418,"t":{"823":{"position":[[0,5]]}}}],["start",{"_index":20,"t":{"25":{"position":[[13,7]]},"131":{"position":[[0,8]]},"149":{"position":[[10,5]]},"223":{"position":[[0,5]]},"284":{"position":[[0,8]]}}}],["stat",{"_index":34,"t":{"43":{"position":[[29,5]]},"512":{"position":[[22,5]]},"944":{"position":[[16,5]]},"1374":{"position":[[22,5]]},"2019":{"position":[[16,5]]},"2380":{"position":[[22,5]]},"2525":{"position":[[22,5]]},"3238":{"position":[[16,5]]},"3668":{"position":[[22,5]]}}}],["state",{"_index":421,"t":{"829":{"position":[[13,5]]},"952":{"position":[[21,5]]},"2079":{"position":[[21,5]]},"2344":{"position":[[18,6]]},"2354":{"position":[[13,6]]},"2360":{"position":[[22,5]]},"3246":{"position":[[21,5]]},"3632":{"position":[[18,6]]},"3642":{"position":[[13,6]]},"3648":{"position":[[22,5]]}}}],["statu",{"_index":222,"t":{"324":{"position":[[39,6]]},"677":{"position":[[12,6]]},"1310":{"position":[[39,6]]},"1728":{"position":[[12,6]]},"2457":{"position":[[39,6]]},"2917":{"position":[[12,6]]}}}],["step",{"_index":552,"t":{"1634":{"position":[[0,5]]},"2851":{"position":[[0,5]]}}}],["sticki",{"_index":488,"t":{"1148":{"position":[[0,6]]},"2276":{"position":[[0,6]]},"3503":{"position":[[0,6]]}}}],["stop",{"_index":224,"t":{"326":{"position":[[11,5]]},"1312":{"position":[[11,5]]},"2459":{"position":[[11,5]]}}}],["storag",{"_index":546,"t":{"1624":{"position":[[0,7]]},"2841":{"position":[[0,7]]}}}],["stream",{"_index":16,"t":{"19":{"position":[[14,6]]},"71":{"position":[[6,7]]},"111":{"position":[[33,7]]},"565":{"position":[[6,7]]},"631":{"position":[[27,6]]},"1052":{"position":[[28,6]]},"1600":{"position":[[27,6]]},"2233":{"position":[[28,6]]},"2795":{"position":[[27,6]]},"3348":{"position":[[28,7]]},"3350":{"position":[[27,7]]},"3420":{"position":[[28,6]]}}}],["strict",{"_index":285,"t":{"416":{"position":[[0,6]]},"1400":{"position":[[0,6]]},"2557":{"position":[[0,6]]}}}],["string",{"_index":582,"t":{"1878":{"position":[[18,6]]},"3069":{"position":[[18,6]]}}}],["structur",{"_index":114,"t":{"151":{"position":[[12,9]]},"1058":{"position":[[13,9]]},"2139":{"position":[[13,9]]},"3368":{"position":[[13,9]]}}}],["sub",{"_index":392,"t":{"739":{"position":[[0,3]]},"757":{"position":[[0,4]]},"1758":{"position":[[0,3]]},"1776":{"position":[[0,4]]},"1802":{"position":[[0,3]]},"2155":{"position":[[0,3]]},"2179":{"position":[[29,3]]},"2947":{"position":[[0,3]]},"2965":{"position":[[0,4]]},"2991":{"position":[[0,3]]},"3384":{"position":[[0,3]]},"3408":{"position":[[29,3]]}}}],["sub_refresh_proxy_nam",{"_index":600,"t":{"1934":{"position":[[0,22]]},"3129":{"position":[[0,22]]}}}],["subscrib",{"_index":131,"t":{"179":{"position":[[5,9]]},"454":{"position":[[3,9]]},"504":{"position":[[0,9]]},"791":{"position":[[8,10]]},"1070":{"position":[[0,9]]},"1096":{"position":[[9,9]]},"1112":{"position":[[0,9]]},"1167":{"position":[[0,9]]},"1340":{"position":[[36,11]]},"1366":{"position":[[0,9]]},"1454":{"position":[[3,9]]},"1740":{"position":[[0,9]]},"1842":{"position":[[8,10]]},"2151":{"position":[[0,9]]},"2179":{"position":[[9,10]]},"2193":{"position":[[0,9]]},"2487":{"position":[[36,11]]},"2517":{"position":[[0,9]]},"2595":{"position":[[3,9]]},"2929":{"position":[[0,9]]},"3033":{"position":[[8,10]]},"3380":{"position":[[0,9]]},"3408":{"position":[[9,10]]},"3434":{"position":[[0,9]]}}}],["subscribe_cel",{"_index":533,"t":{"1520":{"position":[[0,13]]},"2689":{"position":[[0,13]]}}}],["subscribe_proxy_nam",{"_index":389,"t":{"727":{"position":[[0,20]]},"1930":{"position":[[0,20]]},"3125":{"position":[[0,20]]}}}],["subscribe_stream_proxy_nam",{"_index":635,"t":{"3131":{"position":[[0,27]]}}}],["subscribe_to_publish",{"_index":377,"t":{"701":{"position":[[0,20]]}}}],["subscriberequest",{"_index":512,"t":{"1250":{"position":[[0,16]]},"2401":{"position":[[0,16]]},"3568":{"position":[[0,16]]}}}],["subscript",{"_index":25,"t":{"33":{"position":[[8,13]]},"39":{"position":[[12,13]]},"185":{"position":[[32,12]]},"209":{"position":[[20,12]]},"388":{"position":[[11,13]]},"426":{"position":[[19,13]]},"506":{"position":[[12,13]]},"821":{"position":[[0,12]]},"1038":{"position":[[20,13]]},"1040":{"position":[[27,12]]},"1185":{"position":[[12,13]]},"1368":{"position":[[12,13]]},"1410":{"position":[[19,13]]},"1488":{"position":[[0,12]]},"1492":{"position":[[9,12]]},"1506":{"position":[[0,12]]},"1536":{"position":[[0,13]]},"1800":{"position":[[0,12]]},"2129":{"position":[[20,13]]},"2131":{"position":[[27,12]]},"2263":{"position":[[12,12]]},"2354":{"position":[[0,12]]},"2356":{"position":[[0,12]]},"2360":{"position":[[0,12]]},"2362":{"position":[[0,12]]},"2364":{"position":[[0,12]]},"2366":{"position":[[0,12]]},"2368":{"position":[[12,13]]},"2519":{"position":[[12,13]]},"2567":{"position":[[19,13]]},"2637":{"position":[[0,12]]},"2681":{"position":[[0,12]]},"2685":{"position":[[9,12]]},"2705":{"position":[[0,13]]},"2989":{"position":[[0,12]]},"3017":{"position":[[9,12]]},"3348":{"position":[[15,12]]},"3350":{"position":[[14,12]]},"3358":{"position":[[20,13]]},"3360":{"position":[[27,12]]},"3497":{"position":[[12,12]]},"3642":{"position":[[0,12]]},"3644":{"position":[[0,12]]},"3648":{"position":[[0,12]]},"3650":{"position":[[0,12]]},"3652":{"position":[[0,12]]},"3654":{"position":[[0,12]]},"3656":{"position":[[12,13]]}}}],["support",{"_index":245,"t":{"344":{"position":[[16,7]]},"775":{"position":[[13,7]]},"1197":{"position":[[0,9]]},"1214":{"position":[[0,9]]},"1227":{"position":[[0,9]]},"1252":{"position":[[0,9]]},"1332":{"position":[[16,7]]},"1342":{"position":[[16,7]]},"1794":{"position":[[13,7]]},"2306":{"position":[[0,9]]},"2329":{"position":[[0,9]]},"2388":{"position":[[0,9]]},"2403":{"position":[[0,9]]},"2479":{"position":[[16,7]]},"2489":{"position":[[16,7]]},"2983":{"position":[[13,7]]},"3527":{"position":[[0,9]]},"3570":{"position":[[0,9]]},"3585":{"position":[[0,9]]},"3602":{"position":[[0,9]]}}}],["swagger",{"_index":155,"t":{"205":{"position":[[17,7]]},"3215":{"position":[[0,7]]}}}],["switch",{"_index":312,"t":{"480":{"position":[[0,9]]}}}],["tab",{"_index":234,"t":{"332":{"position":[[79,5]]},"1320":{"position":[[79,5]]},"2467":{"position":[[79,5]]}}}],["tabl",{"_index":355,"t":{"605":{"position":[[12,5]]},"607":{"position":[[11,5]]},"956":{"position":[[10,5]]},"1534":{"position":[[12,5]]},"1536":{"position":[[14,5]]},"1538":{"position":[[11,5]]},"1540":{"position":[[13,5]]},"1542":{"position":[[14,5]]},"2083":{"position":[[10,5]]},"2703":{"position":[[12,5]]},"2705":{"position":[[14,5]]},"2707":{"position":[[11,5]]},"2709":{"position":[[13,5]]},"2711":{"position":[[14,5]]},"3250":{"position":[[10,5]]}}}],["tarantool",{"_index":56,"t":{"73":{"position":[[0,9]]},"988":{"position":[[0,9]]},"990":{"position":[[0,9]]},"2049":{"position":[[0,9]]},"2051":{"position":[[0,9]]},"3288":{"position":[[0,9]]},"3290":{"position":[[0,9]]}}}],["task",{"_index":2,"t":{"5":{"position":[[17,5]]}}}],["teardown",{"_index":141,"t":{"191":{"position":[[0,8]]}}}],["termin",{"_index":581,"t":{"1862":{"position":[[4,8]]},"1864":{"position":[[0,8]]},"3053":{"position":[[4,8]]},"3055":{"position":[[0,8]]}}}],["thank",{"_index":281,"t":{"406":{"position":[[8,6]]}}}],["throttl",{"_index":360,"t":{"617":{"position":[[6,10]]},"1675":{"position":[[25,10]]},"1677":{"position":[[19,10]]},"1679":{"position":[[15,10]]}}}],["throughput",{"_index":182,"t":{"239":{"position":[[0,10]]}}}],["time",{"_index":51,"t":{"67":{"position":[[20,4]]},"288":{"position":[[10,4]]},"418":{"position":[[16,4]]},"547":{"position":[[0,4]]},"940":{"position":[[8,4]]},"1402":{"position":[[16,4]]},"2015":{"position":[[8,4]]},"2559":{"position":[[16,4]]},"3234":{"position":[[8,4]]}}}],["time_wait",{"_index":453,"t":{"952":{"position":[[11,9]]},"2079":{"position":[[11,9]]},"3246":{"position":[[11,9]]}}}],["tl",{"_index":75,"t":{"97":{"position":[[21,3]]},"1050":{"position":[[0,3]]},"1052":{"position":[[0,3]]},"2031":{"position":[[18,3]]},"2037":{"position":[[15,3]]},"2231":{"position":[[0,3]]},"2233":{"position":[[0,3]]},"3272":{"position":[[18,3]]},"3278":{"position":[[15,3]]},"3418":{"position":[[0,3]]},"3420":{"position":[[0,3]]}}}],["tldr",{"_index":201,"t":{"294":{"position":[[0,4]]}}}],["token",{"_index":128,"t":{"177":{"position":[[13,5]]},"197":{"position":[[0,5]]},"209":{"position":[[33,5]]},"259":{"position":[[12,6]]},"651":{"position":[[7,5]]},"653":{"position":[[16,6]]},"767":{"position":[[9,5]]},"769":{"position":[[0,5]]},"771":{"position":[[0,5]]},"799":{"position":[[0,5]]},"813":{"position":[[8,5]]},"1506":{"position":[[13,5]]},"1624":{"position":[[12,6]]},"1697":{"position":[[7,5]]},"1700":{"position":[[16,6]]},"1786":{"position":[[9,5]]},"1788":{"position":[[0,5]]},"1790":{"position":[[0,5]]},"1850":{"position":[[0,5]]},"2350":{"position":[[18,5]]},"2366":{"position":[[13,5]]},"2637":{"position":[[13,5]]},"2651":{"position":[[11,5]]},"2821":{"position":[[7,5]]},"2824":{"position":[[16,6]]},"2841":{"position":[[12,6]]},"2975":{"position":[[9,5]]},"2977":{"position":[[0,5]]},"2979":{"position":[[0,5]]},"3017":{"position":[[22,5]]},"3041":{"position":[[0,5]]},"3228":{"position":[[15,5]]},"3638":{"position":[[18,5]]},"3654":{"position":[[13,5]]}}}],["toml",{"_index":432,"t":{"885":{"position":[[0,4]]},"1952":{"position":[[0,4]]},"3165":{"position":[[0,4]]}}}],["top",{"_index":491,"t":{"1163":{"position":[[0,3]]},"2243":{"position":[[0,3]]},"3554":{"position":[[0,3]]}}}],["topic",{"_index":194,"t":{"263":{"position":[[15,6]]},"1628":{"position":[[15,6]]},"2845":{"position":[[15,6]]}}}],["trace",{"_index":638,"t":{"3325":{"position":[[0,6]]}}}],["transport",{"_index":10,"t":{"13":{"position":[[19,9]]},"67":{"position":[[25,10]]},"418":{"position":[[21,10]]},"1402":{"position":[[21,10]]},"1500":{"position":[[15,9]]},"2559":{"position":[[21,10]]},"2631":{"position":[[15,9]]},"3466":{"position":[[0,9]]}}}],["tri",{"_index":540,"t":{"1584":{"position":[[0,3]]},"2781":{"position":[[0,3]]}}}],["tune",{"_index":5,"t":{"9":{"position":[[3,6]]}}}],["tutori",{"_index":106,"t":{"139":{"position":[[0,8]]},"976":{"position":[[19,8]]},"1671":{"position":[[20,9]]},"2033":{"position":[[19,8]]},"2890":{"position":[[20,9]]},"3274":{"position":[[19,8]]}}}],["two",{"_index":240,"t":{"340":{"position":[[35,3]]},"1328":{"position":[[35,3]]},"2475":{"position":[[35,3]]}}}],["type",{"_index":289,"t":{"426":{"position":[[10,5]]},"1269":{"position":[[23,5]]},"1410":{"position":[[10,5]]},"1484":{"position":[[19,5]]},"2293":{"position":[[23,5]]},"2567":{"position":[[10,5]]},"2677":{"position":[[19,5]]},"3523":{"position":[[23,5]]}}}],["typescript",{"_index":274,"t":{"394":{"position":[[27,10]]}}}],["ubuntu",{"_index":353,"t":{"593":{"position":[[11,6]]},"1558":{"position":[[11,6]]},"2761":{"position":[[11,6]]}}}],["ui",{"_index":156,"t":{"205":{"position":[[25,2]]},"267":{"position":[[19,2]]},"434":{"position":[[19,2]]},"1418":{"position":[[19,2]]},"2575":{"position":[[19,2]]},"3215":{"position":[[8,2]]}}}],["unauthor",{"_index":408,"t":{"783":{"position":[[0,12]]},"1834":{"position":[[0,12]]},"3025":{"position":[[0,12]]}}}],["unblock",{"_index":369,"t":{"671":{"position":[[0,7]]}}}],["unblock_us",{"_index":575,"t":{"1716":{"position":[[0,12]]},"2905":{"position":[[0,12]]}}}],["uni_grpc",{"_index":505,"t":{"1230":{"position":[[0,8]]},"2309":{"position":[[0,8]]},"3530":{"position":[[0,8]]}}}],["uni_grpc_address",{"_index":507,"t":{"1234":{"position":[[0,16]]},"2313":{"position":[[0,16]]},"3534":{"position":[[0,16]]}}}],["uni_grpc_max_receive_message_s",{"_index":508,"t":{"1236":{"position":[[0,33]]},"2315":{"position":[[0,33]]},"3536":{"position":[[0,33]]}}}],["uni_grpc_port",{"_index":506,"t":{"1232":{"position":[[0,13]]},"2311":{"position":[[0,13]]},"3532":{"position":[[0,13]]}}}],["uni_grpc_tl",{"_index":509,"t":{"1238":{"position":[[0,12]]},"2317":{"position":[[0,12]]},"3538":{"position":[[0,12]]}}}],["uni_grpc_tls_cert",{"_index":510,"t":{"1240":{"position":[[0,17]]},"2319":{"position":[[0,17]]},"3540":{"position":[[0,17]]}}}],["uni_grpc_tls_key",{"_index":511,"t":{"1242":{"position":[[0,16]]},"2321":{"position":[[0,16]]},"3542":{"position":[[0,16]]}}}],["uni_http_stream",{"_index":500,"t":{"1202":{"position":[[0,15]]},"2334":{"position":[[0,15]]},"3590":{"position":[[0,15]]}}}],["uni_http_stream_max_request_body_s",{"_index":501,"t":{"1204":{"position":[[0,37]]},"2336":{"position":[[0,37]]},"3592":{"position":[[0,37]]}}}],["uni_ss",{"_index":503,"t":{"1219":{"position":[[0,7]]},"2391":{"position":[[0,7]]},"3605":{"position":[[0,7]]}}}],["uni_sse_max_request_body_s",{"_index":504,"t":{"1221":{"position":[[0,29]]},"2393":{"position":[[0,29]]},"3607":{"position":[[0,29]]}}}],["uni_websocket",{"_index":513,"t":{"1257":{"position":[[0,13]]},"2408":{"position":[[0,13]]},"3575":{"position":[[0,13]]}}}],["uni_websocket_message_size_limit",{"_index":514,"t":{"1259":{"position":[[0,32]]},"2410":{"position":[[0,32]]},"3577":{"position":[[0,32]]}}}],["unidirect",{"_index":49,"t":{"67":{"position":[[0,14]]},"631":{"position":[[12,14]]},"1052":{"position":[[13,14]]},"1267":{"position":[[0,14]]},"1269":{"position":[[0,14]]},"1500":{"position":[[0,14]]},"1600":{"position":[[12,14]]},"2233":{"position":[[13,14]]},"2291":{"position":[[0,14]]},"2293":{"position":[[0,14]]},"2631":{"position":[[0,14]]},"2795":{"position":[[12,14]]},"3348":{"position":[[0,14]]},"3420":{"position":[[13,14]]},"3483":{"position":[[0,14]]},"3523":{"position":[[0,14]]}}}],["unifi",{"_index":193,"t":{"263":{"position":[[7,7]]},"376":{"position":[[0,7]]},"1628":{"position":[[0,7]]},"2845":{"position":[[0,7]]}}}],["unknown",{"_index":160,"t":{"211":{"position":[[0,7]]},"785":{"position":[[0,7]]},"841":{"position":[[0,7]]},"1836":{"position":[[0,7]]},"3027":{"position":[[0,7]]}}}],["unlimit",{"_index":328,"t":{"540":{"position":[[3,9]]}}}],["unrecover",{"_index":415,"t":{"805":{"position":[[0,13]]},"849":{"position":[[0,13]]},"1856":{"position":[[0,13]]},"3047":{"position":[[0,13]]}}}],["unsubscrib",{"_index":483,"t":{"1114":{"position":[[0,11]]},"1169":{"position":[[0,11]]},"2195":{"position":[[0,11]]},"2372":{"position":[[0,11]]},"3436":{"position":[[0,11]]},"3660":{"position":[[0,11]]}}}],["updat",{"_index":150,"t":{"201":{"position":[[28,6]]},"555":{"position":[[0,7]]},"677":{"position":[[19,6]]},"1728":{"position":[[19,6]]},"2917":{"position":[[19,6]]}}}],["update_push_statu",{"_index":567,"t":{"1667":{"position":[[0,18]]},"2886":{"position":[[0,18]]}}}],["update_user_statu",{"_index":370,"t":{"679":{"position":[[0,18]]},"1730":{"position":[[0,18]]},"2919":{"position":[[0,18]]}}}],["us",{"_index":225,"t":{"328":{"position":[[6,3]]},"332":{"position":[[18,3]]},"565":{"position":[[14,4]]},"855":{"position":[[0,5]]},"1046":{"position":[[0,5]]},"1206":{"position":[[11,5]]},"1314":{"position":[[6,3]]},"1320":{"position":[[18,3]]},"1572":{"position":[[0,5]]},"1646":{"position":[[0,3]]},"1722":{"position":[[0,5]]},"2227":{"position":[[0,5]]},"2338":{"position":[[11,5]]},"2461":{"position":[[6,3]]},"2467":{"position":[[18,3]]},"2731":{"position":[[0,5]]},"2863":{"position":[[0,3]]},"2911":{"position":[[0,5]]},"3414":{"position":[[0,5]]},"3594":{"position":[[11,5]]}}}],["usag",{"_index":207,"t":{"308":{"position":[[7,5]]},"358":{"position":[[10,5]]},"480":{"position":[[35,5]]},"944":{"position":[[10,5]]},"1294":{"position":[[7,5]]},"1378":{"position":[[10,5]]},"2019":{"position":[[10,5]]},"2441":{"position":[[7,5]]},"2529":{"position":[[10,5]]},"3238":{"position":[[10,5]]}}}],["user",{"_index":126,"t":{"175":{"position":[[5,4]]},"340":{"position":[[39,5]]},"653":{"position":[[11,4]]},"669":{"position":[[6,4]]},"671":{"position":[[8,4]]},"695":{"position":[[0,4]]},"1042":{"position":[[16,4]]},"1328":{"position":[[39,5]]},"1508":{"position":[[0,4]]},"1677":{"position":[[14,4]]},"1679":{"position":[[10,4]]},"1700":{"position":[[11,4]]},"1874":{"position":[[0,4]]},"2133":{"position":[[16,4]]},"2475":{"position":[[39,5]]},"2639":{"position":[[0,4]]},"2824":{"position":[[11,4]]},"2831":{"position":[[14,4]]},"2833":{"position":[[10,4]]},"3065":{"position":[[0,4]]},"3362":{"position":[[16,4]]}}}],["user_topic_list",{"_index":565,"t":{"1663":{"position":[[0,15]]},"2880":{"position":[[0,15]]}}}],["user_topic_upd",{"_index":564,"t":{"1661":{"position":[[0,17]]},"2878":{"position":[[0,17]]}}}],["v2",{"_index":43,"t":{"61":{"position":[[11,2]]},"571":{"position":[[0,2]]},"2659":{"position":[[25,2]]}}}],["v3",{"_index":261,"t":{"374":{"position":[[11,2]]},"571":{"position":[[6,2]]}}}],["variabl",{"_index":431,"t":{"877":{"position":[[15,9]]},"1522":{"position":[[11,9]]},"1570":{"position":[[0,9]]},"1944":{"position":[[15,9]]},"2691":{"position":[[11,9]]},"2729":{"position":[[0,9]]},"3157":{"position":[[15,9]]}}}],["varibl",{"_index":539,"t":{"1572":{"position":[[6,8]]},"2731":{"position":[[6,8]]}}}],["varieti",{"_index":286,"t":{"418":{"position":[[0,7]]},"1402":{"position":[[0,7]]},"2559":{"position":[[0,7]]}}}],["version",{"_index":426,"t":{"861":{"position":[[0,7]]},"2059":{"position":[[0,7]]},"3137":{"position":[[0,7]]}}}],["view",{"_index":100,"t":{"127":{"position":[[14,4]]},"129":{"position":[[13,4]]}}}],["vite",{"_index":204,"t":{"300":{"position":[[15,4]]}}}],["vs",{"_index":619,"t":{"2497":{"position":[[11,2]]},"2499":{"position":[[11,2]]},"2501":{"position":[[11,2]]},"2503":{"position":[[11,2]]},"2505":{"position":[[11,2]]}}}],["warn",{"_index":162,"t":{"211":{"position":[[20,8]]}}}],["way",{"_index":216,"t":{"320":{"position":[[4,3]]},"332":{"position":[[11,3]]},"342":{"position":[[16,3]]},"1306":{"position":[[4,3]]},"1320":{"position":[[11,3]]},"1330":{"position":[[16,3]]},"2453":{"position":[[4,3]]},"2467":{"position":[[11,3]]},"2477":{"position":[[16,3]]}}}],["web",{"_index":237,"t":{"334":{"position":[[55,3]]},"434":{"position":[[15,3]]},"775":{"position":[[5,3]]},"855":{"position":[[13,3]]},"1002":{"position":[[23,3]]},"1322":{"position":[[55,3]]},"1418":{"position":[[15,3]]},"1722":{"position":[[13,3]]},"1794":{"position":[[5,3]]},"2109":{"position":[[23,3]]},"2469":{"position":[[55,3]]},"2575":{"position":[[15,3]]},"2911":{"position":[[13,3]]},"2983":{"position":[[5,3]]},"3310":{"position":[[23,3]]}}}],["webhook",{"_index":246,"t":{"344":{"position":[[24,9]]},"1332":{"position":[[24,9]]},"2479":{"position":[[24,9]]}}}],["websocket",{"_index":0,"t":{"5":{"position":[[0,9]]},"7":{"position":[[0,9]]},"13":{"position":[[0,9]]},"338":{"position":[[34,9]]},"378":{"position":[[7,9]]},"1326":{"position":[[34,9]]},"2473":{"position":[[34,9]]}}}],["websocket_compress",{"_index":519,"t":{"1282":{"position":[[0,21]]},"2429":{"position":[[0,21]]},"3624":{"position":[[0,21]]}}}],["websocket_message_size_limit",{"_index":515,"t":{"1274":{"position":[[0,28]]},"2421":{"position":[[0,28]]},"3616":{"position":[[0,28]]}}}],["websocket_read_buffer_s",{"_index":516,"t":{"1276":{"position":[[0,26]]},"2423":{"position":[[0,26]]},"3618":{"position":[[0,26]]}}}],["websocket_use_write_buffer_pool",{"_index":518,"t":{"1280":{"position":[[0,31]]},"2427":{"position":[[0,31]]},"3622":{"position":[[0,31]]}}}],["websocket_write_buffer_s",{"_index":517,"t":{"1278":{"position":[[0,27]]},"2425":{"position":[[0,27]]},"3620":{"position":[[0,27]]}}}],["webtransport",{"_index":277,"t":{"398":{"position":[[19,12]]}}}],["what'",{"_index":243,"t":{"342":{"position":[[0,6]]},"1330":{"position":[[0,6]]},"2477":{"position":[[0,6]]}}}],["wildcard",{"_index":528,"t":{"1480":{"position":[[9,8]]},"2673":{"position":[[9,8]]}}}],["window",{"_index":30,"t":{"41":{"position":[[0,8]]}}}],["without",{"_index":226,"t":{"328":{"position":[[21,7]]},"1314":{"position":[[21,7]]},"2461":{"position":[[21,7]]}}}],["work",{"_index":229,"t":{"330":{"position":[[16,4]]},"516":{"position":[[7,5]]},"613":{"position":[[11,5]]},"643":{"position":[[7,5]]},"661":{"position":[[7,5]]},"1316":{"position":[[16,4]]},"1318":{"position":[[16,4]]},"1548":{"position":[[11,5]]},"1689":{"position":[[7,5]]},"1705":{"position":[[7,5]]},"2463":{"position":[[16,4]]},"2465":{"position":[[16,4]]},"2717":{"position":[[11,5]]},"2813":{"position":[[7,5]]},"2894":{"position":[[7,5]]}}}],["worker_connect",{"_index":467,"t":{"1004":{"position":[[0,18]]},"2111":{"position":[[0,18]]},"3312":{"position":[[0,18]]}}}],["write",{"_index":78,"t":{"101":{"position":[[0,7]]},"827":{"position":[[0,5]]}}}],["wscat",{"_index":640,"t":{"3628":{"position":[[24,6]]}}}],["yaml",{"_index":433,"t":{"887":{"position":[[0,4]]},"1954":{"position":[[0,4]]},"3167":{"position":[[0,4]]}}}]],"pipeline":["stemmer"]}},{"documents":[{"i":2,"t":"In order to get an understanding about possible hardware requirements for reasonably massive Centrifugo setup we made a test stand inside Kubernetes. Our goal was to run server based on Centrifuge library (the core of Centrifugo server) with one million WebSocket connections and send many messages to connected clients. While sending many messages we have been looking at delivery time latency. In fact we will see that about 30 million messages per minute (500k messages per second) will be delivered to connected clients and latency won't be larger than 200ms in 99 percentile. Server nodes have been run on machines with the following configuration: CPU Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz Linux Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64 GNU/Linux Some sysctl values: fs.file-max = 3276750fs.nr_open = 1048576net.ipv4.tcp_mem = 3086496 4115330 6172992net.ipv4.tcp_rmem = 8192 8388608 16777216net.ipv4.tcp_wmem = 4096 4194394 16777216net.core.rmem_max = 33554432net.core.wmem_max = 33554432 Kubernetes used these machines as its nodes. We started 20 Centrifuge-based server pods. Our clients connected to server pods using Centrifuge Protobuf protocol. To scale horizontally we used Redis Engine and sharded it to 5 different Redis instances (each Redis instance consumes 1 CPU max). To achieve many client connections we used 100 Kubernetes pods each generating about 10k client connections to server. Here are some numbers we achieved: 1 million WebSocket connections Each connection subscribed to 2 channels: one personal channel and one group channel (with 10 subscribers in it), i.e. we had about 1.1 million active channels at each moment. 28 million messages per minute (about 500k per second) delivered to clients 200k per minute constant connect/disconnect rate to simulate real-life situation where clients connect/disconnect from server 200ms delivery latency in 99 percentile The size of each published message was about 100 bytes And here are some numbers about final resource usage on server side (we don't actually interested in client side resource usage here): 40 CPU total for server nodes when load achieved values claimed above (20 pods, ~2 CPU each) 27 GB of RAM used mostly to handle 1 mln WebSocket connections, i.e. about 30kb RAM per connection 0.32 CPU usage on every Redis instance 100 mbit/sec rx и 150 mbit/sec tx of network used on each server pod The picture that demonstrates experiment (better to open image in new tab): This also demonstrates that to handle one million of WebSocket connections without many messages sent to clients you need about 10 CPU total for server nodes and about 5% of CPU on each of Redis instances. In this case CPU mostly spent on connect/disconnect flow, ping/pong frames, subscriptions to channels. If we enable history and history message recovery features we see an increased Redis CPU usage: 64% instead of 32% on the same workload. Other resources usage is pretty the same. The results mean that one can theoretically achieve the comparable numbers on single modern server machine. But numbers can vary a lot in case of different load scenarios. In this benchmark we looked at basic use case where we only connect many clients and send Publications to them. There are many features in Centrifuge library and in Centrifugo not covered by this artificial experiment. Also note that though benchmark was made for Centrifuge library for Centrifugo you can expect similar results. Read and write buffer sizes of websocket connections were set to 512 kb on server side (sizes of buffers affect memory usage), with Centrifugo this means that to reproduce the same configuration you need to set: { ... \"websocket_read_buffer_size\": 512, \"websocket_write_buffer_size\": 512}","s":"Million connections with Centrifugo","u":"/blog/2020/02/10/million-connections-with-centrifugo","h":"","p":1},{"i":4,"t":"I believe that in 2020 WebSocket is still an entertaining technology which is not so well-known and understood like HTTP. In this blog post I'd like to tell about state of WebSocket in Go language ecosystem, and a way we could write scalable WebSocket servers with Go and beyond Go. We won't talk a lot about WebSocket transport pros and cons – I'll provide links to other resources on this topic. Most advices here are generic enough and can be easily approximated to other programming languages. Also in this post we won't talk about ready to use solutions (if you are looking for it – check out Real-time Web Technologies guide by Phil Leggetter), just general considerations. There is not so much information about scaling WebSocket on the internet so if you are interested in WebSocket and real-time messaging technologies - keep on reading. If you don't know what WebSocket is – check out the following curious links: https://hpbn.co/websocket/ – a wonderful chapter of great book by Ilya Grigorik https://lucumr.pocoo.org/2012/9/24/websockets-101/ – valuable thoughts about WebSocket from Armin Ronacher As soon as you know WebSocket basics – we can proceed.","s":"Scaling WebSocket in Go and beyond","u":"/blog/2020/11/12/scaling-websocket","h":"","p":3},{"i":6,"t":"Speaking about scalable servers that work with many persistent WebSocket connections – I found several important tasks such a server should be able to do: Maintain many active connections Send many messages to clients Support WebSocket fallback to scale to every client Authenticate incoming connections and invalidate connections Survive massive reconnect of all clients without loosing messages note Of course not all of these points equally important in various situations. Below we will look at some tips which relate to these points.","s":"WebSocket server tasks","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-server-tasks","p":3},{"i":8,"t":"In Go language ecosystem we have several libraries which can be used as a building block for a WebSocket server. Package golang.org/x/net/websocket is considered deprecated. The default choice in the community is gorilla/websocket library. Made by Gary Burd (who also gifted us an awesome Redigo package to communicate with Redis) – it's widely used, performs well, has a very good API – so in most cases you should go with it. Some people think that library not actively maintained at moment – but this is not quite true, it implements full WebSocket RFC, so actually it can be considered done. In 2018 my ex-colleague Sergey Kamardin open-sourced gobwas/ws library. It provides a bit lower-level API than gorilla/websocket thus allows reducing RAM usage per connection and has nice optimizations for WebSocket upgrade process. It does not support WebSocket permessage-deflate compression but otherwise a good alternative you can consider using. If you have not read Sergey's famous post A Million WebSockets and Go – make a bookmark! One more library is nhooyr/websocket. It's the youngest one and actively maintained. It compiles to WASM which can be a cool thing for someone. The API is a bit different from what gorilla/websocket offers, and one of the big advantages I see is that it solves a problem with a proper WebSocket closing handshake which is a bit hard to do right with Gorilla WebSocket. You can consider all listed libraries except one from x/net for your project. Take a library, follow its examples (make attention to goroutine-safety of various API operations). Personally I prefer Gorilla WebSocket at moment since it's feature-complete and battle tested by tons of projects around Go world.","s":"WebSocket libraries","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-libraries","p":3},{"i":10,"t":"OK, so you have chosen a library and built a server on top of it. As soon as you put it in production the interesting things start happening. Let's start with several OS specific key things you should do to prepare for many connections from WebSocket clients. Every connection will cost you an open file descriptor, so you should tune a maximum number of open file descriptors your process can use. An errors like too many open files raise due to OS limit on file descriptors which is usually 256-1024 by default (see with ulimit -n on Unix). A nice overview on how to do this on different systems can be found in Riak docs. Wanna more connections? Make this limit higher. Nice tip here is to limit a maximum number of connections your process can serve – making it less than known file descriptor limit: // ulimit -n == 65535if conns.Len() >= 65500 { return errors.New(\"connection limit reached\")}conns.Add(conn) – otherwise you have a risk to not even able to look at pprof when things go bad. And you always need monitoring of open file descriptors. You can also consider using netutil.LimitListener for this task, but don't forget to put pprof on another port with another HTTP server instance in this case. Keep attention on Ephemeral ports problem which is often happens between your load balancer and your WebSocket server. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On balancer/server boundary you are limited in 65536 possible variants by default. But actually due to some OS limits and sockets in TIME_WAIT state the number is even less. A very good explanation and how to deal with it can be found in Pusher blog. Your possible number of connections also limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article. One more thing you can do is tune your network stack for performance. Do this only if you understand that you need it. Maybe start with this gist, but don't optimize without full understanding why you are doing this.","s":"OS tuning","u":"/blog/2020/11/12/scaling-websocket","h":"#os-tuning","p":3},{"i":12,"t":"Now let's speak about sending many messages. The general tips follows. Make payload smaller. This is obvious – fewer data means more effective work on all layers. BTW WebSocket framing overhead is minimal and adds only 2-8 bytes to your payload. You can read detailed dedicated research in Dissecting WebSocket's Overhead article. You can reduce an amount of data traveling over network with permessage-deflate WebSocket extension, so your data will be compressed. Though using permessage-deflate is not always a good thing for server due to poor performance of flate, so you should be prepared for a CPU and RAM resource usage on server side. While Gorilla WebSocket has a lot of optimizations internally by reusing flate writers, overhead is still noticeable. The increase value heavily depends on your load profile. Make less system calls. Every syscall will have a constant overhead, and actually in WebSocket server under load you will mostly see read and write system calls in your CPU profiles. An advice here – try to use client-server protocol that supports message batching, so you can join individual messages together. Use effective message serialization protocol. Maybe use code generation for JSON to avoid extensive usage of reflect package done by Go std lib. Maybe use sth like gogo/protobuf package which allows to speedup Protobuf marshalling and unmarshalling. Unfortunately Gogo Protobuf is going through hard times at this moment. Try to serialize a message only once when sending to many subscribers. Have a way to scale to several machines - more power, more possible messages. We will talk about this very soon.","s":"Sending many messages","u":"/blog/2020/11/12/scaling-websocket","h":"#sending-many-messages","p":3},{"i":14,"t":"Even in 2020 there are still users which cannot establish connection with WebSocket server. Actually the problem mostly appears with browsers. Some users still use old browsers. But they have a choice – install a newer browser. Still, there could also be users behind corporate proxies. Employees can have a trusted certificate installed on their machine so company proxy can re-encrypt even TLS traffic. Also, some browser extensions can block WebSocket traffic. One ready solution to this is Sockjs-Go library. This is a mature library that provides fallback transport for WebSocket. If client does not succeed with WebSocket connection establishment then client can use some of HTTP transports for client-server communication: EventSource aka Server-Sent Events, XHR-streaming, Long-Polling etc. The downside with those transports is that to achieve bidirectional communication you should use sticky sessions on your load balancer since SockJS keeps connection session state in process memory. We will talk about many instances of your WebSocket server very soon. You can implement WebSocket fallback yourself, this should be simple if you have a sliding window message stream on your backend which we will discuss very soon. Maybe look at GRPC, depending on application it could be better or worse than WebSocket – in general you can expect a better performance and less resource consumption from WebSocket for bidirectional communication case. My measurements for a bidirectional scenario showed 3x win for WebSocket (binary + GOGO protobuf) in terms of server CPU consumption and 4 times less RAM per connection. Though if you only need RPC then GRPC can be a better choice. But you need additional proxy to work with GRPC from a browser.","s":"WebSocket fallback transport","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-fallback-transport","p":3},{"i":16,"t":"You can optimize client-server protocol, tune your OS, but at some point you won't be able to use only one process on one server machine. You need to scale connections and work your server does over different server machines. Horizontal scaling is also good for a server high availability. Actually there are some sort of real-time applications where a single isolated process makes sense - for example multiplayer games where limited number of players play independent game rounds. As soon as you distribute connections over several machines you have to find a way to deliver a message to a certain user. The basic approach here is to publish messages to all server instances. This can work but this does not scale well. You need a sort of instance discovery to make this less painful. Here comes PUB/SUB, where you can connect WebSocket server instances over central PUB/SUB broker. Clients that establish connections with your WebSocket server subscribe to topics (channels) in a broker, and as soon as you publish a message to that topic it will be delivered to all active subscribers on WebSocket server instances. If server node does not have interested subscriber then it won't get a message from a broker thus you are getting effective network communication. Actually the main picture of this post illustrates exactly this architecture: Let's think about requirements for a broker for real-time messaging application. We want a broker: with reasonable performance and possibility to scale which maintains message order in topics can support millions of topics, where each topic should be ephemeral and lightweight – topics can be created when user comes to application and removed after user goes away possibility to keep a sliding window of messages inside channel to help us survive massive reconnect scenario (will talk about this later below, can be a separate part from broker actually) Personally when we talk about such brokers here are some options that come into my mind: RabbitMQ Kafka or Pulsar Nats or Nats-Streaming Tarantool Redis Sure there are more exist including libraries like ZeroMQ or nanomsg. Below I'll try to consider these solutions for the task of making scalable WebSocket server facing many user connections from Internet. If you are looking for unreliable at most once PUB/SUB then any of solutions mentioned above should be sufficient. Many real-time messaging apps are ok with at most once guarantee delivery. If you don't want to miss messages then things are a bit harder. Let's try to evaluate these options for a task where application has lots of different topics from which it wants to receive messages with at least once guarantee (having a personal topic per client is common thing in applications). A short analysis below can be a bit biased, but I believe thoughts are reasonable enough. I did not found enough information on the internet about scaling WebSocket beyond a single server process, so I'll try to fill the gap a little based on my personal knowledge without pretending to be absolutely objective in these considerations. In some posts on the internet about scaling WebSocket I saw advices to use RabbitMQ for PUB/SUB stuff in real-time messaging server. While this is a great messaging server, it does not like a high rate of queue bind and unbind type of load. It will work, but you will need to use a lot of server resources for not so big number of clients (imagine having millions of queues inside RabbitMQ). I have an example from my practice where RabbitMQ consumed about 70 CPU cores to serve real-time messages for 100k online connections. After replacing it with Redis keeping the same message delivery semantics we got only 0.3 CPU consumption on broker side. Kafka and Pulsar are great solutions, but not for this task I believe. The problem is again in dynamic ephemeral nature of our topics. Kafka also likes a more stable configuration of its topics. Keeping messages on disk can be an overkill for real-time messaging task. Also your consumers on Kafka server should pull from millions of different topics, not sure how well it performs, but my thoughts at moment - this should not perform very well. Kafka itself scales perfectly, you will definitely be able to achieve a goal but resource usage will be significant. Here is a post from Trello where they moved from RabbitMQ to Kafka for similar real-time messaging task and got about 5x resource usage improvements. Note also that the more partitions you have the more heavy failover process you get. Nats and Nats-Streaming. Raw Nats can only provide at most once guarantee. BTW recently Nats developers released native WebSocket support, so you can consider it for your application. Nats-Streaming server as broker will allow you to not lose messages. To be fair I don't have enough information about how well Nats-Streaming scales to millions of topics. An upcoming Jetstream which will be a part of Nats server can also be an interesting option – like Kafka it provides a persistent stream of messages for at least once delivery semantics. But again, it involves disk storage, a nice thing for backend microservices communication but can be an overkill for real-time messaging task. Sure Tarantool can fit to this task well too. It's fast, im-memory and flexible. Some possible problems with Tarantool are not so healthy state of its client libraries, complexity and the fact that it's heavily enterprise-oriented. You should invest enough time to benefit from it, but this can worth it actually. See an article on how to do a performant broker for WebSocket applications with Tarantool. Building PUB/SUB system on top of ZeroMQ will require you to build separate broker yourself. This could be an unnecessary complexity for your system. It's possible to implement PUB/SUB pattern with ZeroMQ and nanomsg without a central broker, but in this case messages without active subscribers on a server will be dropped on a consumer side thus all publications will travel to all server nodes. My personal choice at moment is Redis. While Redis PUB/SUB itself provides at most once guarantee, you can build at least once delivery on top of PUB/SUB and Redis data structures (though this can be challenging enough). Redis is very fast (especially when using pipelining protocol feature), and what is more important – very predictable. It gives you a good understanding of operation time complexity. You can shard topics over different Redis instances running in HA setup - with Sentinel or with Redis Cluster. It allows writing LUA procedures with some advanced logic which can be uploaded over client protocol thus feels like ordinary commands. You can use Redis to keep sliding window event stream which gives you access to missed messages from a certain position. We will talk about this later. OK, the end of opinionated thoughts here :) Depending on your choice the implementation of your system will vary and will have different properties – so try to evaluate possible solutions based on your application requirements. Anyway, whatever broker will be your choice, try to follow this rules to build effective PUB/SUB system: take into account message delivery guarantees of your system: at most once or at least once, ideally you should have an option to have both for different real-time features in your app make sure to use one or pool of connections between your server and a broker, don't create new connection per each client or topic that comes to your WebSocket server use effective serialization format between your WebSocket server and broker","s":"Performance is not scalability","u":"/blog/2020/11/12/scaling-websocket","h":"#performance-is-not-scalability","p":3},{"i":18,"t":"Let's talk about one more problem that is unique for Websocket servers compared to HTTP. Your app can have thousands or millions of active WebSocket connections. In contract to stateless HTTP APIs your application is stateful. It uses push model. As soon as you deploying your WebSocket server or reload your load balancer (Nginx maybe) – connections got dropped and all that army of users start reconnecting. And this can be like an avalanche actually. How to survive? First of all - use exponential backoff strategies on client side. I.e. reconnect with intervals like 1, 2, 4, 8, 16 seconds with some random jitter. Turn on various rate limiting strategies on your WebSocket server, some of them should be turned on your backend load balancer level (like controlling TCP connection establishment rate), some are application specific (maybe limit an amount of requests from certain user). One more interesting technique to survive massive reconnect is using JWT (JSON Web Token) for authentication. I'll try to explain why this can be useful. As soon as your client start reconnecting you will have to authenticate each connection. In massive setups with many persistent connection this can be a very significant load on your Session backend. Since you need an extra request to your session storage for every client coming back. This can be a no problem for some infrastructures but can be really disastrous for others. JWT allows to reduce this spike in load on session storage since it can have all required authentication information inside its payload. When using JWT make sure you have chosen a reasonable JWT expiration time – expiration interval depends on your application nature and just one of trade-offs you should deal with as developer. Don't forget about making an effective connection between your WebSocket server and broker – as soon as all clients start reconnecting you should resubscribe your server nodes to all topics as fast as possible. Use techniques like smart batching at this moment. Let's look at a small piece of code that demonstrates this technique. Imagine we have a source channel from which we get items to process. We don’t want to process items individually but in batch. For this we wait for first item coming from channel, then try to collect as many items from channel buffer as we want without blocking and timeouts involved. And then process slice of items we collected at once. For example build Redis pipeline from them and send to Redis in one connection write call. maxBatchSize := 50for { select { case item := <-sourceCh: batch := []string{item} loop: for len(batch) < maxBatchSize { select { case item := <-sourceCh: batch = append(batch, item) default: break loop } } // Do sth with collected batch of items. println(len(batch)) }} Look at a complete example in a Go playground: https://play.golang.org/p/u7SAGOLmDke. I also made a repo where I demonstrate how this technique together with Redis pipelining feature allows to fully utilize connection for a good performance https://github.com/FZambia/redigo-smart-batching. Another advice for those who run WebSocket services in Kubernetes. Learn how your ingress behaves – for example Nginx ingress can reload its configuration on every change inside Kubernetes services map resulting into closing all active WebSocket connections. Proxies like Envoy don't have this behaviour, so you can reduce number of mass disconnections in your system. You can also proxy WebSocket without using ingress at all over configured WebSocket service NodePort.","s":"Massive reconnect","u":"/blog/2020/11/12/scaling-websocket","h":"#massive-reconnect","p":3},{"i":20,"t":"Here comes a final part of this post. Maybe the most important one. Not only mass client re-connections could create a significant load on a session backend but also a huge load on your main application database. Why? Because WebSocket applications are stateful. Clients rely on a stream of messages coming from a backend to maintain its state actual. As soon as connection dropped client tries to reconnect. In some scenarios it also wants to restore its actual state. What if client reconnected after 3 seconds? How many state updates it could miss? Nobody knows. So to make sure state is actual client tries to get it from application database. This is again a significant spike in load on your main database in massive reconnect scenario. In can be really painful with many active connections. So what I think is nice to have for scenarios where we can't afford to miss messages (like in chat-like apps for example) is having effective and performant stream of messages inside each channel. Keep this stream in fast in-memory storage. This stream can have time retention and be limited in size (think about it as a sliding window of messages). I already mentioned that Redis can do this – it's possible to keep messages in Redis List or Redis Stream data structures. Other broker solutions could give you access to such a stream inside each channel out of the box. So as soon as client reconnects it can restore its state from fast in-memory event stream without even querying your database. Actually to survive mass reconnect scenario you don't need to keep such a stream for a long time – several minutes should be enough. You can even create your own Websocket fallback implementation (like Long-Polling) utilizing event stream with limited retention.","s":"Message event stream benefits","u":"/blog/2020/11/12/scaling-websocket","h":"#message-event-stream-benefits","p":3},{"i":22,"t":"Hope advices given here will be useful for a reader and will help writing a more robust and more scalable real-time application backends. Centrifugo server and Centrifuge library for Go language have most of the mechanics described here including the last one – message stream for topics limited by size and retention period. Both also have techniques to prevent message loss due to at most once nature of Redis PUB/SUB giving at least once delivery guarantee inside message history window size and retention period.","s":"Conclusion","u":"/blog/2020/11/12/scaling-websocket","h":"#conclusion","p":3},{"i":24,"t":"In this post I'll try to introduce Centrifuge - the heart of Centrifugo. Centrifuge is a real-time messaging library for the Go language. This post is going to be pretty long (looks like I am a huge fan of long reads) – so make sure you also have a drink (probably two) and let's go!","s":"Centrifuge – real-time messaging with Go","u":"/blog/2021/01/15/centrifuge-intro","h":"","p":23},{"i":26,"t":"I wrote several blog posts before (for example this one – yep, it's on Medium...) about an original motivation of Centrifugo server. danger Centrifugo server is not the same as Centrifuge library for Go. It's a full-featured project built on top of Centrifuge library. Naming can be confusing, but it's not too hard once you spend some time with ecosystem. In short – Centrifugo was implemented to help traditional web frameworks dealing with many persistent connections (like WebSocket or SockJS HTTP transports). So frameworks like Django or Ruby on Rails, or frameworks from the PHP world could be used on a backend but still provide real-time messaging features like chats, multiplayer browser games, etc for users. With a little help from Centrifugo. Now there are cases when Centrifugo server used in conjunction even with a backend written in Go. While Go mostly has no problems dealing with many concurrent connections – Centrifugo provides some features beyond simple message passing between a client and a server. That makes it useful, especially since design is pretty non-obtrusive and fits well microservices world. Centrifugo is used in some well-known projects (like ManyChat, Yoola.io, Spot.im, Badoo etc). At the end of 2018, I released Centrifugo v2 based on a real-time messaging library for Go language – Centrifuge – the subject of this post. It was a pretty hard experience to decouple Centrifuge out of the monolithic Centrifugo server – I was unable to make all the things right immediately, so Centrifuge library API went through several iterations where I introduced backward-incompatible changes. All those changes targeted to make Centrifuge a more generic tool and remove opinionated or limiting parts.","s":"How it's all started","u":"/blog/2021/01/15/centrifuge-intro","h":"#how-its-all-started","p":23},{"i":28,"t":"This is ... well, a framework to build real-time messaging applications with Go language. If you ever heard about socket.io – then you can think about Centrifuge as an analogue. I think the most popular applications these days are chats of different forms, but I want to emphasize that Centrifuge is not a framework to build chats – it's a generic instrument that can be used to create different sorts of real-time applications – real-time charts, multiplayer games. The obvious choice for real-time messaging transport to achieve fast and cross-platform bidirectional communication these days is WebSocket. Especially if you are targeting a browser environment. You mostly don't need to use WebSocket HTTP polyfills in 2021 (though there are still corner cases so Centrifuge supports SockJS polyfill). Centrifuge has its own custom protocol on top of plain WebSocket or SockJS frames. The reason why Centrifuge has its own protocol on top of underlying transport is that it provides several useful primitives to build real-time applications. The protocol described as strict Protobuf schema. It's possible to pass JSON or binary Protobuf-encoded data over the wire with Centrifuge. note GRPC is very handy these days too (and can be used in a browser with a help of additional proxies), some developers prefer using it for real-time messaging apps – especially when one-way communication needed. It can be a bit better from integration perspective but more resource-consuming on server side and a bit trickier to deploy. note Take a look at WebTransport – a brand-new spec for web browsers to allow fast communication between a client and a server on top of QUIC – it may be a good alternative to WebSocket in the future. This in a draft status at the moment, but it's already possible to play with in Chrome. Own protocol is one of the things that prove the framework status of Centrifuge. This dictates certain limits (for example, you can't just use an alternative message encoding) and makes developers use custom client connectors on a front-end side to communicate with a Centrifuge-based server (see more about connectors in ecosystem part). But protocol solves many practical tasks – and here we are going to look at real-time features it provides for a developer.","s":"So what is Centrifuge?","u":"/blog/2021/01/15/centrifuge-intro","h":"#so-what-is-centrifuge","p":23},{"i":30,"t":"To start working with Centrifuge you need to start Centrifuge server Node. Node is a core of Centrifuge – it has many useful methods – set event handlers, publish messages to channels, etc. We will look at some events and channels concept very soon. Also, Node abstracts away scalability aspects, so you don't need to think about how to scale WebSocket connections over different server instances and still have a way to deliver published messages to interested clients. For now, let's start a single instance of Node that will serve connections for us: node, err := centrifuge.New(centrifuge.DefaultConfig)if err != nil { log.Fatal(err)}if err := node.Run(); err != nil { log.Fatal(err)} It's also required to serve a WebSocket handler – this is possible just by registering centrifuge.WebsocketHandler in HTTP mux: wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})http.Handle(\"/connection/websocket\", wsHandler) Now it's possible to connect to a server (using Centrifuge connector for a browser called centrifuge-js): const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.connect(); Though connection will be rejected by the server since we also need to provide authentication details – Centrifuge expects explicitly provided connection Credentials to accept connection.","s":"Centrifuge Node","u":"/blog/2021/01/15/centrifuge-intro","h":"#centrifuge-node","p":23},{"i":32,"t":"Let's look at how we can tell Centrifuge details about connected user identity, so it could accept an incoming connection. There are two main ways to authenticate client connection in Centrifuge. The first one is over the native middleware mechanism. It's possible to wrap centrifuge.WebsocketHandler or centrifuge.SockjsHandler with middleware that checks user authentication and tells Centrifuge current user ID over context.Context: func auth(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cred := ¢rifuge.Credentials{ UserID: \"42\", } newCtx := centrifuge.SetCredentials(r.Context(), cred) r = r.WithContext(newCtx) h.ServeHTTP(w, r) })} So WebsocketHandler can be registered this way (note that a handler now wrapped by auth middleware): wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})http.Handle(\"/connection/websocket\", auth(wsHandler)) Another authentication way is a bit more generic – developers can authenticate connection based on custom token sent from a client inside first WebSocket/SockJS frame. This is called connect frame in terms of Centrifuge protocol. Any string token can be set – this opens a way to use JWT, Paceto, and any other kind of authentication tokens. For example see an authenticaton with JWT. note BTW it's also possible to pass any information from client side with a first connect message from client to server and return custom information about server state to a client. This is out of post scope though. Nothing prevents you to integrate Centrifuge with OAuth2 or another framework session mechanism – like Gin for example.","s":"Authentication","u":"/blog/2021/01/15/centrifuge-intro","h":"#authentication","p":23},{"i":34,"t":"As soon as a client connected and successfully authenticated it can subscribe to channels. Channel (room or topic in other systems) is a lightweight and ephemeral entity in Centrifuge. Channel can have different features (we will look at some channel features below). Channels created automatically as soon as the first subscriber joins and destroyed as soon as the last subscriber left. The application can have many real-time features – even on one app screen. So sometimes client subscribes to several channels – each related to a specific real-time feature (for example one channel for chat updates, one channel likes notification stream, etc). Channel is just an ASCII string. A developer is responsible to find the best channel naming convention suitable for an application. Channel naming convention is an important aspect since in many cases developers want to authorize subscription to a channel on the server side – so only authorized users could listen to specific channel updates. Let's look at a basic subscription example on the client-side: centrifuge.subscribe('example', function(msgCtx) { console.log(msgCtx)}) On the server-side, you need to define subscribe event handler. If subscribe event handler not set then the connection won't be able to subscribe to channels at all. Subscribe event handler is where a developer may check permissions of the current connection to read channel updates. Here is a basic example of subscribe event handler that simply allows subscriptions to channel example for all authenticated connections and reject subscriptions to all other channels: node.OnConnect(func(client *centrifuge.Client) { client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) { if e.Channel != \"example\" { cb(centrifuge.SubscribeReply{}, centrifuge.ErrorPermissionDenied) return } cb(centrifuge.SubscribeReply{}, nil) })}) You may notice a callback style of reacting to connection related things. While not being very idiomatic for Go it's very practical actually. The reason why we use callback style inside client event handlers is that it gives a developer possibility to control operation concurrency (i.e. process sth in separate goroutines or goroutine pool) and still control the order of events. See an example that demonstrates concurrency control in action. Now if some event published to a channel: // Here is how we can publish data to a channel.node.Publish(\"example\", []byte(`{\"input\": \"hello\"}`)) – data will be delivered to a subscribed client, and message will be printed to Javascript console. PUB/SUB in its usual form. note Though Centrifuge protocol based on Protobuf schema in example above we published a JSON message into a channel. By default, we can only send JSON to connections since default protocol format is JSON. But we can switch to Protobuf-based binary protocol by connecting to ws://localhost:8000/connection/websocket?format=protobuf endpoint – then it's possible to send binary data to clients.","s":"Channel subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#channel-subscriptions","p":23},{"i":36,"t":"While Centrifuge mostly shines when you need channel semantics it's also possible to send any data to connection directly – to achieve bidirectional asynchronous communication, just what a native WebSocket provides. To send a message to a server one can use the send method on the client-side: centrifuge.send({\"input\": \"hello\"}); On the server-side data will be available inside a message handler: client.OnMessage(func(e centrifuge.MessageEvent) { log.Printf(\"message from client: %s\", e.Data)}) And vice-versa, to send data to a client use Send method of centrifuge.Client: client.Send([]byte(`{\"input\": \"hello\"}`)) To listen to it on the client-side: centrifuge.on('message', function(data) { console.log(data);});","s":"Async message passing","u":"/blog/2021/01/15/centrifuge-intro","h":"#async-message-passing","p":23},{"i":38,"t":"RPC is a primitive for sending a request from a client to a server and waiting for a response (in this case all communication still happens via asynchronous message passing internally, but Centrifuge takes care of matching response data to request previously sent). On client side it's as simple as: const resp = await centrifuge.namedRPC('my_method', {}); On server side RPC event handler should be set to make calls available: client.OnRPC(func(e centrifuge.RPCEvent, cb centrifuge.RPCCallback) { if e.Method == \"my_method\" { cb(centrifuge.RPCReply{Data: []byte(`{\"result\": \"42\"}`)}, nil) return } cb(centrifuge.RPCReply{}, centrifuge.ErrorMethodNotFound)}) Note, that it's possible to pass the name of RPC and depending on it and custom request params return different results to a client – just like a regular HTTP request but over asynchronous WebSocket (or SockJS) connection.","s":"RPC","u":"/blog/2021/01/15/centrifuge-intro","h":"#rpc","p":23},{"i":40,"t":"In many cases, a client is a source of knowledge which channels it wants to subscribe to on a specific application screen. But sometimes you want to control subscriptions to channels on a server-side. This is also possible in Centrifuge. It's possible to provide a slice of channels to subscribe connection to at the moment of connection establishment phase: node.OnConnecting(func(ctx context.Context, e centrifuge.ConnectEvent) (centrifuge.ConnectReply, error) { return centrifuge.ConnectReply{ Subscriptions: map[string]centrifuge.SubscribeOptions{ \"example\": {}, }, }, nil}) Note, that OnConnecting does not follow callback-style – this is because it can only happen once at the start of each connection – so there is no need to control operation concurrency. In this case on the client-side you will have access to messages published to channels by listening to on('publish') event: centrifuge.on('publish', function(msgCtx) { console.log(msgCtx);}); Also, centrifuge.Client has Subscribe and Unsubscribe methods so it's possible to subscribe/unsubscribe client to/from channel somewhere in the middle of its long WebSocket session.","s":"Server-side subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#server-side-subscriptions","p":23},{"i":42,"t":"Every time a message published to a channel it's possible to provide custom history options. For example: node.Publish( \"example\", []byte(`{\"input\": \"hello\"}`), centrifuge.WithHistory(300, time.Minute),) In this case, Centrifuge will maintain a windowed Publication cache for a channel - or in other words, maintain a publication stream. This stream will have time retention (one minute in the example above) and the maximum size will be limited to the value provided during Publish (300 in the example above). Every message inside a history stream has an incremental offset field. Also, a stream has a field called epoch – this is a unique identifier of stream generation - thus client will have a possibility to distinguish situations where a stream is completely removed and there is no guarantee that no messages have been lost in between even if offset looks fine. Client protocol provides a possibility to paginate over a stream from a certain position with a limit: const streamPosition = {'offset': 0, epoch: 'xyz'} resp = await sub.history({since: streamPosition, limit: 10}); Iteration over history stream is a new feature which is just merged into Centrifuge master branch and can only be used from Javascript client at the moment. Also, Centrifuge has an automatic message recovery feature. Automatic recovery is very useful in scenarios when tons of persistent connections start reconnecting at once. I already described why this is useful in one of my previous posts about Websocket scalability. In short – since WebSocket connections are stateful then at the moment of mass reconnect they can create a very big spike in load on your main application database. Such mass reconnects are a usual thing in practice - for example when you reload your load balancers or re-deploying the Websocket server (new code version). Of course, recovery can also be useful for regular short network disconnects - when a user travels in the subway for example. But you always need a way to load an actual state from the main application database in case of an unsuccessful recovery. To enable automatic recovery you can provide the Recover flag in subscribe options: client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) { cb(centrifuge.SubscribeReply{ Options: centrifuge.SubscribeOptions{ Recover: true, }, }, nil)}) Obviously, recovery will work only for channels where history stream maintained. The limitation in recovery is that all missed publications sent to client in one protocol frame – pagination is not supported during recovery process. This means that recovery is mostly effective for not too long offline time without tons of missed messages.","s":"Windowed history in channel","u":"/blog/2021/01/15/centrifuge-intro","h":"#windowed-history-in-channel","p":23},{"i":44,"t":"Another cool thing Centrifuge exposes to developers is online presence information for channels. Presence information contains a list of active channel subscribers. This is useful to show the online status of players in a game for example. Also, it's possible to turn on Join/Leave message feature inside channels: so each time connection subscribes to a channel all channel subscribers receive a Join message with client information (client ID, user ID). As soon as the client unsubscribes Leave message is sent to remaining channel subscribers with information who left a channel. Here is how to enable both online presence and join/leave features for a subscription to channel: client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) { cb(centrifuge.SubscribeReply{ Options: centrifuge.SubscribeOptions{ Presence: true, JoinLeave: true, }, }, nil)}) On a client-side then it's possible to call for the presence and setting event handler for join/leave messages. The important thing to be aware of when using Join/Leave messages is that this feature can dramatically increase CPU utilization and overall traffic in channels with a big number of active subscribers – since on every client connect/disconnect event such Join or Leave message must be sent to all subscribers. The advice here – avoid using Join/Leave messages or be ready to scale (Join/Leave messages scale well when adding more Centrifuge Nodes – more about scalability below). One more thing to remember is that online presence information can also be pretty expensive to request in channels with many active subscribers – since it returns information about all connections – thus payload in response can be large. To help a bit with this situation Centrifuge has a presence stats client API method. Presence stats only contain two counters: the number of active connections in the channel and amount of unique users in the channel. If you still need to somehow process online presence in rooms with a massive number of active subscribers – then I think you better do it in near real-time - for example with fast OLAP like ClickHouse.","s":"Online presence and presence stats","u":"/blog/2021/01/15/centrifuge-intro","h":"#online-presence-and-presence-stats","p":23},{"i":46,"t":"To be fair it's not too hard to implement most of the features above inside one in-memory process. Yes, it takes time, but the code is mostly straightforward. When it comes to scalability things tend to be a bit harder. Centrifuge designed with the idea in mind that one machine is not enough to handle all application WebSocket connections. Connections should scale over application backend instances, and it should be simple to add more application nodes when the amount of users (connections) grows. Centrifuge abstracts scalability over the Node instance and two interfaces: Broker interface and PresenceManager interface. A broker is responsible for PUB/SUB and streaming semantics: type Broker interface { Run(BrokerEventHandler) error Subscribe(ch string) error Unsubscribe(ch string) error Publish(ch string, data []byte, opts PublishOptions) (StreamPosition, error) PublishJoin(ch string, info *ClientInfo) error PublishLeave(ch string, info *ClientInfo) error PublishControl(data []byte, nodeID string) error History(ch string, filter HistoryFilter) ([]*Publication, StreamPosition, error) RemoveHistory(ch string) error} See full version with comments in source code. Every Centrifuge Node subscribes to channels via a broker. This provides a possibility to scale connections over many node instances – published messages will flow only to nodes with active channel subscribers. It's and important thing to combine PUB/SUB with history inside a Broker implementation to achieve an atomicity of saving message into history stream and publishing it to PUB/SUB with generated offset. PresenceManager is responsible for online presence information management: type PresenceManager interface { Presence(ch string) (map[string]*ClientInfo, error) PresenceStats(ch string) (PresenceStats, error) AddPresence(ch string, clientID string, info *ClientInfo, expire time.Duration) error RemovePresence(ch string, clientID string) error} Full code with comments. Broker and PresenceManager together form an Engine interface: type Engine interface { Broker PresenceManager} By default, Centrifuge uses MemoryEngine that does not use any external services but limits developers to using only one Centrifuge Node (i.e. one server instance). Memory Engine is fast and can be suitable for some scenarios - even in production (with configured backup instance) – but as soon as the number of connections grows – you may need to load balance connections to different server instances. Here comes the Redis Engine. Redis Engine utilizes Redis for Broker and PresenceManager parts. History cache saved to Redis STREAM or Redis LIST data structures. For presence, Centrifuge uses a combination of HASH and ZSET structures. Centrifuge tries to fully utilize the connection between Node and Redis by using pipelining where possible and smart batching technique. All operations done in a single RTT with the help of Lua scripts loaded automatically to Redis on engine start. Redis is pretty fast and will allow your app to scale to some limits. When Redis starts being a bottleneck it's possible to shard data over different Redis instances. Client-side consistent sharding is built-in in Centrifuge and allows scaling further. It's also possible to achieve Redis's high availability with built-in Sentinel support. Redis Cluster supported too. So Redis Engine covers many options to communicate with Redis deployed in different ways. At Avito we served about 800k active connections in the messenger app with ease using a slightly adapted Centrifuge Redis Engine, so an approach proved to be working for rather big applications. We will look at some more concrete numbers below in the performance section. Both Broker and PresenceManager are pluggable, so it's possible to replace them with alternative implementations. Examples show how to use Nats server for at most once only PUB/SUB together with Centrifuge. Also, we have an example of full-featured Engine for Tarantool database – Tarantool Engine shows even better throughput for history and presence operations than Redis-based Engine (up to 10x for some ops).","s":"Scalability aspects","u":"/blog/2021/01/15/centrifuge-intro","h":"#scalability-aspects","p":23},{"i":48,"t":"Since Centrifuge is a messaging system I also want to describe its order and message delivery guarantees. Message ordering in channels supported. As soon as you publish messages into channels one after another of course. Message delivery model is at most once by default. This is mostly comes from PUB/SUB model – message can be dropped on Centrifuge level if subscriber is offline or simply on broker level – since Redis PUB/SUB also works with at most once guarantee. Though if you maintain history stream inside a channel then things become a bit different. In this case you can tell Centrifuge to check client position inside stream. Since every publication has a unique incremental offset Centrifuge can track that client has correct offset inside a channel stream. If Centrifuge detects any missed messages it disconnects a client with special code – thus make it reconnect and recover messages from history stream. Since a message first saved to history stream and then published to PUB/SUB inside broker these mechanisms allow achieving at least once message delivery guarantee. Even if stream completely expired or dropped from broker memory Centrifuge will give a client a tip that messages could be lost – so client has a chance to restore state from a main application database.","s":"Order and delivery properties","u":"/blog/2021/01/15/centrifuge-intro","h":"#order-and-delivery-properties","p":23},{"i":50,"t":"Here I want to be fair with my readers – Centrifuge is not ideal. This is a project maintained mostly by one person at the moment with all consequences. This hits an ecosystem a lot, can make some design choices opinionated or non-optimal. I mentioned in the first post that Centrifuge built on top of the custom protocol. The protocol is based on a strict Protobuf schema, works with JSON and binary data transfer, supports many features. But – this means that to connect to the Centrifuge-based server developers have to use custom connectors that can speak with Centrifuge over its custom protocol. The difficulty here is that protocol is asynchronous. Asynchronous protocols are harder to implement than synchronous ones. Multiplexing frames allows achieving good performance and fully utilize a single connection – but it hurts simplicity. At this moment Centrifuge has client connectors for: centrifuge-js - Javascript client for a browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-mobile - for mobile development based on centrifuge-go and gomobile project centrifuge-swift - for iOS native development centrifuge-java - for Android native development and general Java centrifuge-dart - for Dart and Flutter Not all clients support all protocol features. Another drawback is that all clients do not have a persistent maintainer – I mostly maintain everything myself. Connectors can have non-idiomatic and pretty dumb code since I had no previous experience with mobile development, they lack proper tests and documentation. This is unfortunate. The good thing is that all connectors feel very similar, I am quickly releasing new versions when someone sends a pull request with improvements or bug fixes. So all connectors are alive. I maintain a feature matrix in connectors to let users understand what's supported. Actually feature support is pretty nice throughout all these connectors - there are only several things missing and not so much work required to make all connectors full-featured. But I really need help here. It will be a big mistake to not mention Centrifugo as a big plus for Centrifuge library ecosystem. Centrifugo is a server deployed in many projects throughout the world. Many features of Centrifuge library and its connectors have already been tested by Centrifugo users. One more thing to mention is that Centrifuge does not have v1 release. It still evolves – I believe that the most dramatic changes have already been made and backward compatibility issues will be minimal in the next releases – but can't say for sure.","s":"Ecosystem","u":"/blog/2021/01/15/centrifuge-intro","h":"#ecosystem","p":23},{"i":52,"t":"I made a test stand in Kubernetes with one million connections. I can't call this a proper benchmark – since in a benchmark your main goal is to destroy a system, in my test I just achieved some reasonable numbers on limited hardware. These numbers should give a good insight into a possible throughput, latency, and estimate hardware requirements (at least approximately). Connections landed on different server pods, 5 Redis instances have been used to scale connections between pods. The detailed test stand description can be found in Centrifugo documentation. Some quick conclusions are: One connection costs about 30kb of RAM Redis broker CPU utilization increases linearly with more messages traveling around 1 million connections with 500k delivered messages per second with 200ms delivery latency in 99 percentile can be served with hardware amount equal to one modern physical server machine. The possible amount of messages can vary a lot depending on the number of channel subscribers though.","s":"Performance","u":"/blog/2021/01/15/centrifuge-intro","h":"#performance","p":23},{"i":54,"t":"Centrifuge does not allow subscribing on the same channel twice inside a single connection. It's not simple to add due to design decisions made – though there was no single user report about this in seven years of Centrifugo/Centrifuge history. Centrifuge does not support wildcard subscriptions. Not only because I never needed this myself but also due to some design choices made – so be aware of this. SockJS fallback does not support binary data - only JSON. If you want to use binary in your application then you can only use WebSocket with Centrifuge - there is no built-in fallback transport in this case. SockJS also requires sticky session support from your load balancer to emulate a stateful bidirectional connection with its HTTP fallback transports. Ideally, Centrifuge will go away from SockJS at some point, maybe when WebTransport becomes mature so users will have a choice between WebTransport or WebSocket. Websocket permessage-deflate compression supported (thanks to Gorilla WebSocket), but it can be pretty expensive in terms of CPU utilization and memory usage – the overhead depends on usage pattern, it's pretty hard to estimate in numbers. As said above you cannot only rely on Centrifuge for state recovery – it's still required to have a way to fully load application state from the main database. Also, I am not very happy with current error and disconnect handling throughout the connector ecosystem – this can be improved though, and I have some ideas for the future.","s":"Limitations","u":"/blog/2021/01/15/centrifuge-intro","h":"#limitations","p":23},{"i":56,"t":"I am adding examples to _examples folder of Centrifuge repo. These examples completely cover Centrifuge API - including things not mentioned here. Check out the tips & tricks section of README – it contains some additional insights about an implementation.","s":"Examples","u":"/blog/2021/01/15/centrifuge-intro","h":"#examples","p":23},{"i":58,"t":"I think Centrifuge could be a nice alternative to socket.io - with a better performance, main server implementation in Go language, and even more builtin features to build real-time apps. Centrifuge ecosystem definitely needs more work, especially in client connectors area, tutorials, community, stabilizing API, etc. Centrifuge fits pretty well proprietary application development where time matters and deadlines are close, so developers tend to choose a ready solution instead of writing their own. I believe Centrifuge can be a great time saver here. For Centrifugo server users Centrifuge package provides a way to write a more flexible server code adapted for business requirements but still use the same real-time core and have the same protocol features.","s":"Conclusion","u":"/blog/2021/01/15/centrifuge-intro","h":"#conclusion","p":23},{"i":60,"t":"After almost three years of Centrifugo v2 life cycle we are happy to announce the next major release of Centrifugo. During the last several months deep in our Centrifugal laboratory we had been synthesizing an improved version of the server. New Centrifugo v3 is targeting to improve Centrifugo adoption for basic real-time application cases, improves server performance and extends existing features with new functionality. It comes with unidirectional real-time transports, protocol speedups, super-fast engine implementation based on Tarantool, new documentation site, GRPC proxy, API extensions and PRO version which provides unique possibilities for business adopters.","s":"Centrifugo v3 released","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"","p":59},{"i":62,"t":"Centrifugo v2 life cycle has come to an end. Before discussing v3 let's look back at what has been done during the last three years. Centrifugo v2 was a pretty huge refactoring of v1. Since the v2 release, Centrifugo is built on top of new Centrifuge library for Go language. Centrifuge library evolved significantly since its initial release and now powers Grafana v8 real-time streaming among other things. Here is an awesome demo made by my colleague Alexander Zobnin that demonstrates real-time telemetry of Assetto Corsa sports car streamed in real-time to Grafana dashboard: Centrifugo integrated with Redis Streams, got Redis Cluster support, can now work with Nats server as a PUB/SUB broker. Notable additions of Centrifugo v2 were server-side subscriptions with some interesting features on top – like maintaining a single global connection from one user and automatic personal channel subscription upon user connect. A very good addition which increased Centrifugo adoption a lot was introduction of proxy to backend. This made Centrifugo fit many setups where JWT authentication and existing subscription permission model did not suit well before. Client ecosystem improved significantly. The fact that client protocol migrated to a strict Protobuf schema allowed to introduce binary protocol format (in addition to JSON) and simplify building client connectors. We now have much better and complete client libraries (compared to v1 situation). We also have an official Helm chart, Grafana dashboard for Prometheus datasource, and so on. Centrifugo is becoming more noticeable in a wider real-time technology community. For example, it was included in a periodic table of real-time created by Ably.com (one of the most powerful real-time messaging cloud services at the moment): Of course, there are many aspects where Centrifugo can be improved. And v3 addresses some of them. Below we will look at the most notable features and changes of the new major Centrifugo version.","s":"Centrifugo v2 flashbacks","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-v2-flashbacks","p":59},{"i":64,"t":"Let's start with the most important thing – backwards compatibility concerns. In Centrifugo v3 client protocol mostly stayed the same. We expect that most applications will be able to update without any change on a client-side. This was an important concern for v3 given how painful the update cycle can be on mobile devices and lessons learned from v1 to v2 migration. There is one breaking change though which can affect users who use history API manually from a client-side (we provide a temporary workaround to give apps a chance to migrate smoothly). On a server-side, much more changes happened, especially in the configuration: some options were renamed, some were removed. We provide a v2 to v3 configuration converter which can help dealing with changes. In most cases, all you should do is adapt Centrifugo configuration to match v3 changes and redeploy Centrifugo using v3 build instead of v2. All features are still there (or a replacement exists, like for channels API). For more details, refer to the v3 migration guide.","s":"Backwards compatibility","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#backwards-compatibility","p":59},{"i":66,"t":"As some of you know we considered changing Centrifugo license to AGPL v3 for a new release. After thinking a lot about this we decided to not step into this area. But the license has been changed: the license of OSS Centrifugo is now Apache 2.0 instead of MIT. Apache 2.0 is also a permissive OSS license, it's just a bit more concrete in some aspects.","s":"License change","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#license-change","p":59},{"i":68,"t":"Server-side subscriptions introduced in Centrifugo v2 and recent improvements in the underlying Centrifuge library opened a road for a unidirectional approach. This means that Centrifugo v3 provides a set of unidirectional real-time transports where messages flow only in one direction – from a server to a client. Why is this change important? Centrifugo originally concentrated on using bidirectional transports for client-server communication. Like WebSocket and SockJS. Bidirectional transports allow implementing some great protocol features since a client can communicate with a server in various ways after establishing a persistent connection. While this is a great opportunity this also leads to an increased complexity. Centrifugo users had to use special client connector libraries which abstracted underlying work into a simple public API. But internally connectors do many things: matching requests to responses, handling timeouts, handling an ordering, queuing operations, error handling. So the client connector is a pretty complex piece of software. But what if a user just needs to receive real-time updates from a stable set of channels known in connection time? Can we simplify everything and avoid using custom software on a client-side? With unidirectional transports, the answer is yes. Clients can now connect to Centrifugo using a bunch of unidirectional transports. And the greatest thing is that in this case, developers should not depend on Centrifugo client connectors at all – just use native browser APIs or GRPC-generated code. It's finally possible to consume events from Centrifugo using CURL (see an example). Using unidirectional transports you can still benefit from Centrifugo built-in scalability with various engines, utilize built-in authentication over JWT or the connect proxy feature. With subscribe server API (see below) it's even possible to subscribe unidirectional client to server-side channels dynamically. With refresh server API or the refresh proxy feature it's possible to manage a connection expiration. Centrifugo supports the following unidirectional transports: EventSource (SSE) HTTP streaming Unidirectional WebSocket Unidirectional GRPC stream We expect that introducing unidirectional transports will significantly increase Centrifugo adoption.","s":"Unidirectional real-time transports","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#unidirectional-real-time-transports","p":59},{"i":70,"t":"There was a rather important limitation of Centrifugo history API – it was not very suitable for keeping large streams because a call to a history could only return the entire channel history. Centrifugo v3 introduces an API to iterate over a stream. It's possible to do from the current stream beginning or end, in both directions – forward and backward, with configured limit. Also with certain starting stream position if it's known. This, among other things, can help to implement manual missed message recovery on a client-side to reduce the load on the application backend. Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }} caution This new API does not remove the need in having the main application database – that's still mandatory for idiomatic Centrifugo usage.","s":"History iteration API","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#history-iteration-api","p":59},{"i":72,"t":"In Centrifugo v3 Redis engine uses Redis Stream data structure by default for keeping channel history. Before v3 Redis Streams were supported by not enabled by default so almost nobody used them. This change is important in terms of introducing history iteration API described above – since Redis Streams allow doing iteration effectively.","s":"Redis Streams by default","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#redis-streams-by-default","p":59},{"i":74,"t":"As you may know, Centrifugo has several built-in engines that allow scaling Centrifugo nodes (using PUB/SUB) and keep shared history and presence state. Before v3 Centrifugo had in-memory and Redis (or KeyDB) engines available. Introducing a new engine to Centrifugo is pretty hard since the engine should provide a very robust PUB/SUB performance, fast history and presence operations, possibility to publish a message to PUB/SUB and save to history atomically. It also should allow dealing with ephemeral frequently changing subscriptions. It's typical for Centrifugo use case to have millions of users each subscribed to a unique channel and constantly connecting/disconnecting (thus subscribing/unsubscribing). In v3 we added experimental support for the Tarantool engine. It fits nicely all the requirements above and provides a huge performance speedup for history and presence operations compared to Redis. According to our benchmarks, the speedup can be up to 4-10x depending on operation. The PUB/SUB performance of Tarantool is comparable with Redis (10-20% worse according to our internal benchmarks to be exact, but that's pretty much the same). For example, let's look at Centrifugo benchmark where we recover zero messages (i.e. emulate a situations when many connections disconnected for a very short time interval due to load balancer reload). For Redis engine: Redis engine, single Redis instance BenchmarkRedisRecover 26883 ns/op 1204 B/op 28 allocs/op Compare it with the same operation measured with Tarantool engine: Tarantool engine, single Tarantool instance BenchmarkTarantoolRecover 6292 ns/op 563 B/op 10 allocs/op Tarantool can provide new storage properties (like synchronous replication), new adoption. We are pretty excited about adding it as an option. The reason why Tarantool support is experimental is because Tarantool integration involves one more moving piece – the Centrifuge Lua module which should be run by a Tarantool server. This increases deployment complexity and given the fact that many users have their own best practices in Tarantool deployment we are still evaluating a sufficient way to distribute Lua part. For now, we are targeting standalone (see examples in centrifugal/tarantool-centrifuge) and Cartridge Tarantool setups (with centrifugal/rotor). Refer to the Tarantool Engine documentation for more details.","s":"Tarantool engine","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#tarantool-engine","p":59},{"i":76,"t":"Centrifugo can now transform events received over persistent connections from users into GRPC calls to the application backend (in addition to the HTTP proxy available in v2). GRPC support should make Centrifugo ready for today's microservice architecture where GRPC is a huge player for inter-service communication. So we mostly just provide more choices for Centrifugo users here. GRPC has some good advantages – for example an application backend RPC layer which is responsible for communication with Centrifugo can now be generated from Protobuf definitions for all popular programming languages.","s":"GRPC proxy","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#grpc-proxy","p":59},{"i":78,"t":"Centrifugo v3 has some valuable server API improvements. The new subscribe API method allows subscribing connection to a channel at any point in time. This works by utilizing server-side subscriptions. So it's not only possible to subscribe connection to a list of server-side channels during the connection establishment phase – but also later during the connection lifetime. This may be very useful for the unidirectional approach - by emulating client-side subscribe call over request to application backend which in turn calls subscribe Centrifugo server API. Publish API now returns the current top stream position (offset and epoch) for channels with history enabled. Server history API inherited iteration possibilities described above. Channels command now returns a number of clients in a channel, also supports channel filtering by a pattern. Since we changed how channels call implemented internally there is no limitation anymore to call it when using Redis cluster. Admin web UI has been updated too to support new API methods, so you can play with new API from its actions tab.","s":"Server API improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#server-api-improvements","p":59},{"i":80,"t":"Centrifugo behaves a bit better in cluster mode: as soon as a node leaves a cluster gracefully (upon graceful termination) it sends a shutdown signal to the control channel thus giving other nodes a chance to immediately delete that node from the local registry.","s":"Better clustering","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#better-clustering","p":59},{"i":82,"t":"While preparing the v3 release we improved client connectors too. All existing client connectors now actualized to the latest protocol, support server-side subscriptions, history API. One important detail is that it's not required to set ?format=protobuf URL param now when connecting to Centrifugo from mobile devices - this is now managed internally by using the WebSocket subprotocol mechanism (requires using the latest client connector version and Centrifugo v3).","s":"Client improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#client-improvements","p":59},{"i":84,"t":"You are reading this post on a new project site. It's built with amazing Docusaurus. A lot of documents were actualized, extended, and rewritten. We also now have new chapters like: Main highlights Design overview History and recovery Error and disconnect codes. Server API and proxy documentation have been improved significantly.","s":"New documentation site","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#new-documentation-site","p":59},{"i":86,"t":"Centrifugo v3 has some notable performance improvements. JSON client protocol now utilizes a couple of libraries (easyjson for encoding and segmentio/encoding for unmarshaling). Actually we use a slightly customized version of easyjson library to achieve even faster performance than it provides out-of-the-box. Changes allowed to speed up JSON encoding and decoding up to 4-5x for small messages. For large payloads speed up can be even more noticeable – we observed up to 30x performance boost when serializing 5kb messages. For example, let's look at a JSON serialization benchmark result for 256 byte payload. Here is what we had before: Centrifugo v2 JSON encoding/decoding cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHzBenchmarkMarshal-12 5883 ns/op 1121 B/op 6 allocs/opBenchmarkMarshalParallel-12 1009 ns/op 1121 B/op 6 allocs/opBenchmarkUnmarshal-12 1717 ns/op 1328 B/op 16 allocs/opBenchmarkUnmarshalParallel-12 492.2 ns/op 1328 B/op 16 allocs/op And what we have now with mentioned JSON optimizations: Centrifugo v3 JSON encoding/decoding cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHzBenchmarkMarshal-12 461.3 ns/op 928 B/op 3 allocs/opBenchmarkMarshalParallel-12 250.6 ns/op 928 B/op 3 allocs/opBenchmarkUnmarshal-12 476.5 ns/op 136 B/op 3 allocs/opBenchmarkUnmarshalParallel-12 107.2 ns/op 136 B/op 3 allocs/op tip Centrifugo Protobuf protocol is still faster than JSON for encoding/decoding on a server-side. Of course, JSON encoding is only one part of Centrifugo – so you should not expect overall 4x performance improvement. But loaded setups should notice the difference and this should also be a good thing for reducing garbage collection pauses. Centrifugo inherited a couple of other improvements from the Centrifuge library. In-memory connection hub is now sharded – this should reduce lock contention between operations in different channels. In our artificial benchmarks we noticed a 3x better hub throughput, but in reality the benefit is heavily depends on the usage pattern. Centrifugo now allocates less during message broadcasting to a large number of subscribers. Also, an upgrade to Go 1.17 for builds results in ~5% performance boost overall, thanks to a new way of passing function arguments and results using registers instead of the stack introduced in Go 1.17.","s":"Performance improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#performance-improvements","p":59},{"i":88,"t":"The final notable thing is an introduction of Centrifugo PRO. This is an extended version of Centrifugo built on top of the OSS version. It provides some unique features targeting business adopters. Those who followed Centrifugo for a long time know that there were some attempts to make project development sustainable. Buy me a coffee and Opencollective approaches were not successful, during a year we got ~300$ of total contributions. While we appreciate these contributions a lot - this does not fairly justify a time spent on Centrifugo maintenance these days and does not allow bringing it to the next level. So here is an another attempt to monetize Centrifugo. Centrifugo PRO details and features described here in docs. Let's see how it goes. We believe that a set of additional functionality can provide great advantages for both small and large-scale Centrifugo setups. PRO features can give useful insights on a system, protect from client API misusing, reduce server resource usage, and more. PRO version will be released soon after Centrifugo v3 OSS.","s":"Centrifugo PRO","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-pro","p":59},{"i":90,"t":"There are some other changes introduced in v3 but not mentioned here. The full list can be found in the release notes and the migration guide. Hope we stepped into an exciting time of the v3 life cycle and many improvements will follow. Join our communities in Telegram and Discord if you have questions or want to follow Centrifugo development: Enjoy Centrifugo v3, and let the Centrifugal force be with you. Special thanks Special thanks to Anton Silischev for the help with v3 tests, examples and CI. To Leon Sorokin for the spinning CSS Centrifugo logo. To Michael Filonenko for the help with Tarantool. To German Saprykin for Dart magic. Thanks to the community members who tested out Centrifugo v3 beta, found bugs and sent improvements. Icons used here made by wanicon from www.flaticon.com","s":"Conclusion","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#conclusion","p":59},{"i":92,"t":"UPDATE: WebTransport spec is still evolving. Most information here is not actual anymore. For example the working group has no plan to implement both QuicTransport and HTTP3-based transports – only HTTP3 based WebTransport is going to be implemented. Maybe we will publish a follow-up of this post at some point.","s":"Experimenting with QUIC and WebTransport","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"","p":91},{"i":94,"t":"WebTransport is a new browser API offering low-latency, bidirectional, client-server messaging. If you have not heard about it before I suggest to first read a post called Experimenting with QuicTransport published recently on web.dev – it gives a nice overview to WebTransport and shows client-side code examples. Here we will concentrate on implementing server side. Some key points about WebTransport spec: WebTransport standard will provide a possibility to use streaming client-server communication using modern transports such as QUIC and HTTP/3 It can be a good alternative to WebSocket messaging, standard provides some capabilities that are not possible with current WebSocket spec: possibility to get rid of head-of-line blocking problems using individual streams for different data, the possibility to reuse a single connection to a server in different browser tabs WebTransport also defines an unreliable stream API using UDP datagrams (which is possible since QUIC is UDP-based) – which is what browsers did not have before without a rather complex WebRTC setup involving ICE, STUN, etc. This is sweet for in-browser real-time games. To help you figure out things here are links to current WebTransport specs: WebTransport overview – this spec gives an overview of WebTransport and provides requirements to transport layer WebTransport over QUIC – this spec describes QUIC-based transport for WebTransport WebTransport over HTTP/3 – this spec describes HTTP/3-based transport for WebTransport (actually HTTP/3 is a protocol defined on top of QUIC) At moment Chrome only implements trial possibility to try out WebTransport standard and only implements WebTransport over QUIC. Developers can initialize transport with code like this: const transport = new QuicTransport('quic-transport://localhost:4433/path'); In case of HTTP/3 transport one will use URL like 'https://localhost:4433/path' in transport constructor. All WebTransport underlying transports should support instantiation over URL – that's one of the spec requirements. I decided that this is a cool possibility to finally play with QUIC protocol and its Go implementation github.com/lucas-clemente/quic-go. danger Please keep in mind that all things described in this post are work in progress. WebTransport drafts, Quic-Go library, even QUIC protocol itself are subjects to change. You should not use it in production yet. Experimenting with QuicTransport post contains links to a client example and companion Python server implementation. We will use a linked client example to connect to a server that runs on localhost and uses github.com/lucas-clemente/quic-go library. To make our example work we need to open client example in Chrome, and actually, at this moment we need to install Chrome Canary. The reason behind this is that the quic-go library supports QUIC draft-29 while Chrome < 85 implements QuicTransport over draft-27. If you read this post at a time when Chrome stable 85 already released then most probably you don't need to install Canary release and just use your stable Chrome. We also need to generate self-signed certificates since WebTransport only works with a TLS layer, and we should make Chrome trust our certificates. Let's prepare our client environment before writing a server and first install Chrome Canary.","s":"Overview","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#overview","p":91},{"i":96,"t":"Go to https://www.google.com/intl/en/chrome/canary/, download and install Chrome Canary. We will use it to open client example. note If you have Chrome >= 85 then most probably you can skip this step.","s":"Install Chrome Canary","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#install-chrome-canary","p":91},{"i":98,"t":"Since WebTransport based on modern network transports like QUIC and HTTP/3 security is a keystone. For our experiment we will create a self-signed TLS certificate using openssl. Make sure you have openssl installed: $ which openssl/usr/bin/openssl Then run: openssl genrsa -des3 -passout pass:x -out server.pass.key 2048openssl rsa -passin pass:x -in server.pass.key -out server.keyrm server.pass.keyopenssl req -new -key server.key -out server.csr Set localhost for Common Name when asked. The self-signed TLS certificate generated from the server.key private key and server.csr files: openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt After these manipulations you should have server.crt and server.key files in your working directory. To help you with process here is my console output during these steps (click to open): ??? example \"My console output generating self-signed certificates\" ```bash$ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048Generating RSA private key, 2048 bit long modulus...........................................................................................+++.....................+++e is 65537 (0x10001)$ lsserver.pass.key$ openssl rsa -passin pass:x -in server.pass.key -out server.keywriting RSA key$ lsserver.key server.pass.key$ rm server.pass.key$ openssl req -new -key server.key -out server.csrYou are about to be asked to enter information that will be incorporatedinto your certificate request.What you are about to enter is what is called a Distinguished Name or a DN.There are quite a few fields but you can leave some blankFor some fields there will be a default value,If you enter '.', the field will be left blank.-----Country Name (2 letter code) []:RUState or Province Name (full name) []:Locality Name (eg, city) []:Organization Name (eg, company) []:Organizational Unit Name (eg, section) []:Common Name (eg, fully qualified host name) []:localhostEmail Address []:Please enter the following 'extra' attributesto be sent with your certificate requestA challenge password []:$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crtSignature oksubject=/C=RU/CN=localhostGetting Private key$ lsserver.crt server.csr server.key```","s":"Generate self-signed TLS certificates","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#generate-self-signed-tls-certificates","p":91},{"i":100,"t":"Now the last step. What we need to do is run Chrome Canary with some flags that will allow it to trust our self-signed certificates. I suppose there is an alternative way making Chrome trust your certificates, but I have not tried it. First let's find out a fingerprint of our cert: openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 In my case base64 fingerprint was pe2P0fQwecKFMc6kz3+Y5MuVwVwEtGXyST5vJeaOO/M=, yours will be different. Then run Chrome Canary with some additional flags that will make it trust out certs (close other Chrome Canary instances before running it): $ /Applications/Google\\ Chrome\\ Canary.app/Contents/MacOS/Google\\ Chrome\\ Canary \\ --origin-to-force-quic-on=localhost:4433 \\ --ignore-certificate-errors-spki-list=pe2P0fQwecKFMc6kz3+Y5MuVwVwEtGXyST5vJeaOO/M= This example is for MacOS, for your system see docs on how to run Chrome/Chromium with custom flags. Now you can open https://googlechrome.github.io/samples/quictransport/client.html URL in started browser and click Connect button. What? Connection not established? OK, this is fine since we need to run our server :)","s":"Run client example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#run-client-example","p":91},{"i":102,"t":"Maybe in future we will have libraries that are specified to work with WebTransport over QUIC or HTTP/3, but for now we should implement server manually. As said above we will use github.com/lucas-clemente/quic-go library to do this.","s":"Writing a QUIC server","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#writing-a-quic-server","p":91},{"i":104,"t":"First, let's define a simple skeleton for our server: package mainimport ( \"errors\" \"log\" \"github.com/lucas-clemente/quic-go\")// Config for WebTransportServerQuic.type Config struct { // ListenAddr sets an address to bind server to. ListenAddr string // TLSCertPath defines a path to .crt cert file. TLSCertPath string // TLSKeyPath defines a path to .key cert file TLSKeyPath string // AllowedOrigins represents list of allowed origins to connect from. AllowedOrigins []string}// WebTransportServerQuic can handle WebTransport QUIC connections according// to https://tools.ietf.org/html/draft-vvv-webtransport-quic-02.type WebTransportServerQuic struct { config Config}// NewWebTransportServerQuic creates new WebTransportServerQuic.func NewWebTransportServerQuic(config Config) *WebTransportServerQuic { return &WebTransportServerQuic{ config: config, }}// Run server.func (s *WebTransportServerQuic) Run() error { return errors.New(\"not implemented\")}func main() { server := NewWebTransportServerQuic(Config{ ListenAddr: \"0.0.0.0:4433\", TLSCertPath: \"server.crt\", TLSKeyPath: \"server.key\", AllowedOrigins: []string{\"localhost\", \"googlechrome.github.io\"}, }) if err := server.Run(); err != nil { log.Fatal(err) }}","s":"Server skeleton","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#server-skeleton","p":91},{"i":106,"t":"Let's concentrate on implementing Run method. We need to accept QUIC client connections. This can be done by creating quic.Listener instance and using its .Accept method to accept incoming client sessions. // Run server.func (s *WebTransportServerQuic) Run() error { listener, err := quic.ListenAddr(s.config.ListenAddr, s.generateTLSConfig(), nil) if err != nil { return err } for { sess, err := listener.Accept(context.Background()) if err != nil { return err } log.Printf(\"session accepted: %s\", sess.RemoteAddr().String()) go func() { defer func() { _ = sess.CloseWithError(0, \"bye\") log.Println(\"close session\") }() s.handleSession(sess) }() }}func (s *WebTransportServerQuic) handleSession(sess quic.Session) { // Not implemented yet. } An interesting thing to note is that QUIC allows closing connection with specific application-level integer code and custom string reason. Just like WebSocket if you worked with it. Also note, that we are starting our Listener with TLS configuration returned by s.generateTLSConfig() method. Let's take a closer look at how this method can be implemented. // https://tools.ietf.org/html/draft-vvv-webtransport-quic-02#section-3.1const alpnQuicTransport = \"wq-vvv-01\"func (s *WebTransportServerQuic) generateTLSConfig() *tls.Config { cert, err := tls.LoadX509KeyPair(s.config.TLSCertPath, s.config.TLSKeyPath) if err != nil { log.Fatal(err) } return &tls.Config{ Certificates: []tls.Certificate{cert}, NextProtos: []string{alpnQuicTransport}, }} Inside generateTLSConfig we load x509 certs from cert files generated above. WebTransport uses ALPN (Application-Layer Protocol Negotiation to prevent handshakes with a server that does not support WebTransport spec. This is just a string wq-vvv-01 inside NextProtos slice of our *tls.Config.","s":"Accept QUIC connections","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#accept-quic-connections","p":91},{"i":108,"t":"At this moment if you run a server and open a client example in Chrome then click Connect button – you should see that connection successfully established in event log area: Now if you try to send data to a server nothing will happen. That's because we have not implemented reading data from session streams. Streams in QUIC provide a lightweight, ordered byte-stream abstraction to an application. Streams can be unidirectional or bidirectional. Streams can be short-lived, streams can also be long-lived and can last the entire duration of a connection. Client example provides three possible ways to communicate with a server: Send a datagram Open a unidirectional stream Open a bidirectional stream Unfortunately, quic-go library does not support sending UDP datagrams at this moment. To do this quic-go should implement one more draft called An Unreliable Datagram Extension to QUIC. There is already an ongoing pull request that implements it. This means that it's too early for us to experiment with unreliable UDP WebTransport client-server communication in Go. By the way, the interesting facts about UDP over QUIC are that QUIC congestion control mechanism will still apply and QUIC datagrams can support acknowledgements. Implementing a unidirectional stream is possible with quic-go since the library supports creating and accepting unidirectional streams, but I'll leave this for a reader (though we will need accepting one unidirectional stream for parsing client indication anyway – see below). Here we will only concentrate on implementing a server for a bidirectional case. We are in the Centrifugo blog, and this is the most interesting type of stream for me personally.","s":"Connection Session handling","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#connection-session-handling","p":91},{"i":110,"t":"According to section-3.2 of Quic WebTransport spec in order to verify that the client's origin allowed connecting to the server, the user agent has to communicate the origin to the server. This is accomplished by sending a special message, called client indication, on stream 2, which is the first client-initiated unidirectional stream. Here we will implement this. In the beginning of our session handler we will accept a unidirectional stream initiated by a client. At moment spec defines two client indication keys: Origin and Path. In our case an origin value will be https://googlechrome.github.io and path will be /counter. Let's define some constants and structures: // client indication stream can not exceed 65535 bytes in length.// https://tools.ietf.org/html/draft-vvv-webtransport-quic-02#section-3.2const maxClientIndicationLength = 65535// define known client indication keys.type clientIndicationKey int16const ( clientIndicationKeyOrigin clientIndicationKey = 0 clientIndicationKeyPath = 1)// ClientIndication container.type ClientIndication struct { // Origin client indication value. Origin string // Path client indication value. Path string} Now what we should do is accept unidirectional stream inside session handler: func (s *WebTransportServerQuic) handleSession(sess quic.Session) { stream, err := sess.AcceptUniStream(context.Background()) if err != nil { log.Println(err) return } log.Printf(\"uni stream accepted, id: %d\", stream.StreamID()) indication, err := receiveClientIndication(stream) if err != nil { log.Println(err) return } log.Printf(\"client indication: %+v\", indication) if err := s.validateClientIndication(indication); err != nil { log.Println(err) return } // this method blocks. if err := s.communicate(sess); err != nil { log.Println(err) }}func receiveClientIndication(stream quic.ReceiveStream) (ClientIndication, error) { return ClientIndication{}, errors.New(\"not implemented yet\")}func (s *WebTransportServerQuic) validateClientIndication(indication ClientIndication) error { return errors.New(\"not implemented yet\")}func (s *WebTransportServerQuic) communicate(sess quic.Session) error { return errors.New(\"not implemented yet\")} As you can see to accept a unidirectional stream with data we can use .AcceptUniStream method of quic.Session. After accepting a stream we should read client indication data from it. According to spec it will contain a client indication in the following format: 0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Key (16) | Length (16) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Value (*) ...+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The code below parses client indication out of a stream data, we decode key-value pairs from uni stream until an end of stream (indicated by EOF): func receiveClientIndication(stream quic.ReceiveStream) (ClientIndication, error) { var clientIndication ClientIndication // read no more than maxClientIndicationLength bytes. reader := io.LimitReader(stream, maxClientIndicationLength) done := false for { if done { break } var key int16 err := binary.Read(reader, binary.BigEndian, &key) if err != nil { if err == io.EOF { done = true } else { return clientIndication, err } } var valueLength int16 err = binary.Read(reader, binary.BigEndian, &valueLength) if err != nil { return clientIndication, err } buf := make([]byte, valueLength) n, err := reader.Read(buf) if err != nil { if err == io.EOF { // still need to process indication value. done = true } else { return clientIndication, err } } if int16(n) != valueLength { return clientIndication, errors.New(\"read less than expected\") } value := string(buf) switch clientIndicationKey(key) { case clientIndicationKeyOrigin: clientIndication.Origin = value case clientIndicationKeyPath: clientIndication.Path = value default: log.Printf(\"skip unknown client indication key: %d: %s\", key, value) } } return clientIndication, nil} We also validate Origin inside validateClientIndication method of our server: var errBadOrigin = errors.New(\"bad origin\")func (s *WebTransportServerQuic) validateClientIndication(indication ClientIndication) error { u, err := url.Parse(indication.Origin) if err != nil { return errBadOrigin } if !stringInSlice(u.Host, s.config.AllowedOrigins) { return errBadOrigin } return nil}func stringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false} Do you have stringInSlice function in every Go project? I do :)","s":"Parsing client indication","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#parsing-client-indication","p":91},{"i":112,"t":"The final part here is accepting a bidirectional stream from a client, reading it, and sending responses back. Here we will just echo everything a client sends to a server back to a client. You can implement whatever bidirectional communication you want actually. Very similar to unidirectional case we can call .AcceptStream method of session to accept a bidirectional stream. func (s *WebTransportServerQuic) communicate(sess quic.Session) error { for { stream, err := sess.AcceptStream(context.Background()) if err != nil { return err } log.Printf(\"stream accepted: %d\", stream.StreamID()) if _, err := io.Copy(stream, stream); err != nil { return err } }} When you press Send button in client example it creates a bidirectional stream, sends data to it, then closes stream. Thus our code is sufficient. For a more complex communication that involves many concurrent streams you will have to write a more complex code that allows working with streams concurrently on server side.","s":"Communicating over bidirectional streams","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#communicating-over-bidirectional-streams","p":91},{"i":114,"t":"Full server code can be found in a Gist. Again – this is a toy example based on things that all work in progress.","s":"Full server example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#full-server-example","p":91},{"i":116,"t":"WebTransport is an interesting technology that can open new possibilities in modern Web development. At this moment it's possible to play with it using QUIC transport – here we looked at how one can do that. Though we still have to wait a bit until all these things will be suitable for production usage. Also, even when ready we will still have to think about WebTransport fallback options – since wide adoption of browsers that support some new technology and infrastructure takes time. Actually WebTransport spec authors consider fallback options in design. This was mentioned in IETF slides (PDF, 2.6MB), but I have not found any additional information beyond that. Personally, I think the most exciting thing about WebTransport is the possibility to exchange UDP datagrams, which can help a lot to in-browser gaming. Unfortunately, we can't test it at this moment with Go (but it's already possible using Python as server as shown in the example). WebTransport could be a nice candidate for a new Centrifugo transport next to WebSocket and SockJS – time will show.","s":"Conclusion","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#conclusion","p":91},{"i":118,"t":"In this tutorial, we will create a basic chat server using the Django framework and Centrifugo. Our chat application will have two pages: A page that lets you type the name of a chat room to join. A room view that lets you see messages posted in a chat room you joined. The room view will use a WebSocket to communicate with the Django server (with help from Centrifugo) and listen for any messages that are published to the room channel. caution This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step. The result will look like this: tip Some of you will notice that this tutorial looks very similar to Chat app tutorial of Django Channels. This is intentional to let Pythonistas already familiar with Django Channels feel how Centrifugo compares to Channels in terms of the integration process.","s":"Centrifugo integration with Django – building a basic chat application","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"","p":117},{"i":120,"t":"Why would Django developers want to integrate a project with Centrifugo for real-time messaging functionality? This is a good question especially since there is a popular Django Channels project which solves the same task. I found several points which could be a good motivation: Centrifugo is fast and scales well. We have an optimized Redis Engine with client-side sharding and Redis Cluster support. Centrifugo can also scale with KeyDB, Nats, or Tarantool. So it's possible to handle millions of connections distributed over different server nodes. Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for real-time servers that scale to many nodes. Check out our doc! With Centrifugo you don't need to rewrite the existing application to introduce real-time messaging features to your users. Centrifugo works as a separate service – so can be a universal tool in the developer's pocket, can migrate from one project to another, no matter what programming language or framework is used for business logic.","s":"Why integrate Django with Centrifugo","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#why-integrate-django-with-centrifugo","p":117},{"i":122,"t":"We assume that you are already familiar with basic Django concepts. If not take a look at the official Django tutorial first and then come back to this tutorial. Also, make sure you read a bit about Centrifugo – introduction and quickstart tutorial. We also assume that you have Django installed already. One possible way to quickly install Django locally is to create virtualenv, activate it, and install Django: python3 -m venv env. env/bin/activatepip install django Alos, make sure you have Centrifugo v3 installed already. This tutorial also uses Docker to run Redis. We use Redis as a Centrifugo engine – this allows us to have a scalable solution in the end. Using Redis is optional actually, Centrifugo uses a Memory engine by default (but it does not allow scaling Centrifugo nodes). We will also run Nginx with Docker to serve the entire app. Install Docker from its official website but I am sure you already have one.","s":"Prerequisites","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#prerequisites","p":117},{"i":124,"t":"First, let's create a Django project. From the command line, cd into a directory where you’d like to store your code, then run the following command: django-admin startproject mysite This will create a mysite directory in your current directory with the following contents: ❯ tree mysitemysite├── manage.py└── mysite ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py","s":"Creating a project","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-a-project","p":117},{"i":126,"t":"We will put the code for the chat server inside chat app. Make sure you’re in the same directory as manage.py and type this command: python3 manage.py startapp chat That’ll create a directory chat, which is laid out like this: ❯ tree chatchat├── __init__.py├── admin.py├── apps.py├── migrations│ └── __init__.py├── models.py├── tests.py└── views.py For this tutorial, we will only be working with chat/views.py and chat/__init__.py. Feel free to remove all other files from the chat directory. After removing unnecessary files, the chat directory should look like this: ❯ tree chatchat├── __init__.py└── views.py We need to tell our project that the chat app is installed. Edit the mysite/settings.py file and add 'chat' to the INSTALLED_APPS setting. It’ll look like this: # mysite/settings.pyINSTALLED_APPS = [ 'chat', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles',]","s":"Creating the chat app","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-the-chat-app","p":117},{"i":128,"t":"We will now create the first view, an index view that lets you type the name of a chat room to join. Create a templates directory in your chat directory. Within the templates directory, you have just created, create another directory called chat, and within that create a file called index.html to hold the template for the index view. Your chat directory should now look like this: ❯ tree chatchat├── __init__.py├── templates│ └── chat│ └── index.html└── views.py Put the following code in chat/templates/chat/index.html: chat/templates/chat/index.html Select a chat room
Type a room name to JOIN
Create the view function for the room view. Put the following code in chat/views.py: chat/views.py from django.shortcuts import renderdef index(request): return render(request, 'chat/index.html') To call the view, we need to map it to a URL - and for this, we need a URLconf. To create a URLconf in the chat directory, create a file called urls.py. Your app directory should now look like this: ❯ tree chatchat├── __init__.py├── templates│ └── chat│ └── index.html└── views.py└── urls.py In the chat/urls.py file include the following code: chat/urls.py from django.urls import pathfrom . import viewsurlpatterns = [ path('', views.index, name='index'),] The next step is to point the root URLconf at the chat.urls module. In mysite/urls.py, add an import for django.conf.urls.include and insert an include() in the urlpatterns list, so you have: mysite/urls.py from django.conf.urls import includefrom django.urls import pathfrom django.contrib import adminurlpatterns = [ path('chat/', include('chat.urls')), path('admin/', admin.site.urls),] Let’s verify that the index view works. Run the following command: python3 manage.py runserver You’ll see the following output on the command line: Watching for file changes with StatReloaderPerforming system checks...System check identified no issues (0 silenced).You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.Run 'python manage.py migrate' to apply them.October 21, 2020 - 18:49:39Django version 3.1.2, using settings 'mysite.settings'Starting development server at http://localhost:8000/Quit the server with CONTROL-C. Go to http://localhost:8000/chat/ in your browser and you should see the a text input to provide a room name. Type in \"lobby\" as the room name and press Enter. You should be redirected to the room view at http://localhost:8000/chat/room/lobby/ but we haven’t written the room view yet, so you’ll get a \"Page not found\" error page. Go to the terminal where you ran the runserver command and press Control-C to stop the server.","s":"Add the index view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-index-view","p":117},{"i":130,"t":"We will now create the second view, a room view that lets you see messages posted in a particular chat room. Create a new file chat/templates/chat/room.html. Your app directory should now look like this: chat├── __init__.py├── templates│ └── chat│ ├── index.html│ └── room.html├── urls.py└── views.py Create the view template for the room view in chat/templates/chat/room.html: chat/templates/chat/room.html Chat Room
    {{ room_name|json_script:\"room-name\" }} Create the view function for the room view in chat/views.py: chat/views.py from django.shortcuts import renderdef index(request): return render(request, 'chat/index.html')def room(request, room_name): return render(request, 'chat/room.html', { 'room_name': room_name }) Create the route for the room view in chat/urls.py: # chat/urls.pyfrom django.urls import path, re_pathfrom . import viewsurlpatterns = [ path('', views.index, name='index'), re_path('room/(?P[A-z0-9_-]+)/', views.room, name='room'),] Start the development server: python3 manage.py runserver Go to http://localhost:8000/chat/ in your browser and to see the index page. Type in \"lobby\" as the room name and press enter. You should be redirected to the room page at http://localhost:8000/chat/lobby/ which now displays an empty chat log. Type the message \"hello\" and press Enter. Nothing happens! In particular, the message does not appear in the chat log. Why? The room view is trying to open a WebSocket connection with Centrifugo using the URL ws://localhost:8000/connection/websocket but we haven’t started Centrifugo to accept WebSocket connections yet. If you open your browser’s JavaScript console, you should see an error that looks like this: WebSocket connection to 'ws://localhost:8000/connection/websocket' failed And since port 8000 has already been allocated we will start Centrifugo at a different port actually.","s":"Add the room view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-room-view","p":117},{"i":132,"t":"As promised we will use Centrifugo with Redis engine. So first thing to do before running Centrifugo is to start Redis: docker run -it --rm -p 6379:6379 redis:6 Then create a configuration file for Centrifugo: { \"port\": 8001, \"engine\": \"redis\", \"redis_address\": \"redis://localhost:6379\", \"allowed_origins\": \"http://localhost:9000\", \"proxy_connect_endpoint\": \"http://localhost:8000/chat/centrifugo/connect/\", \"proxy_publish_endpoint\": \"http://localhost:8000/chat/centrifugo/publish/\", \"proxy_subscribe_endpoint\": \"http://localhost:8000/chat/centrifugo/subscribe/\", \"proxy_http_headers\": [\"Cookie\"], \"namespaces\": [ { \"name\": \"rooms\", \"publish\": true, \"proxy_publish\": true, \"proxy_subscribe\": true } ]} And run Centrifugo with it like this: centrifugo -c config.json Let's describe some options we used here: port - sets the port Centrifugo runs on since we are running everything on localhost we make it different (8001) from the port allocated for the Django server (8000). engine - as promised we are using Redis engine so we can easily scale Centrifigo nodes to handle lots of WebSocket connections redis_address allows setting Redis address allowed_origins - we will connect from http://localhost:9000 so we need to allow it namespaces – we are using rooms: prefix when subscribing to a channel, i.e. using Centrifugo rooms namespace. Here we define this namespace and tell Centrifigo to proxy subscribe and publish events for channels in the namespace. tip It's a good practice to use different namespaces in Centrifugo for different real-time features as this allows enabling only required options for a specific task. Also, config has some options related to Centrifugo proxy feature. This feature allows proxying WebSocket events to the configured endpoints. We will proxy three types of events: Connect (called when a user establishes WebSocket connection with Centrifugo) Subscribe (called when a user wants to subscribe on a channel) Publish (called when a user tries to publish data to a channel)","s":"Starting Centrifugo server","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#starting-centrifugo-server","p":117},{"i":134,"t":"In Centrifugo config we set endpoints which we will soon implement inside our Django app. You may notice that the allowed origin has a URL with port 9000. That's because we want to proxy Cookie headers from a persistent connection established with Centrifugo to the Django app and need Centrifugo and Django to share the same origin (so browsers can send Django session cookies to Centrifugo). While not used in this tutorial (we will use fake tutorial-user as user ID here) – this can be useful if you decide to authenticate connections using Django native sessions framework later. To achieve this we should also add Nginx with a configuration like this: nginx.conf events { worker_connections 1024;}error_log /dev/stdout info;http { access_log /dev/stdout; server { listen 9000; server_name localhost; location / { proxy_pass http://host.docker.internal:8000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /connection/websocket { proxy_pass http://host.docker.internal:8001; proxy_http_version 1.1; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } }} Start Nginx (replace the path to nginx.conf to yours): docker run -it --rm -v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro -p 9000:9000 --add-host=host.docker.internal:host-gateway nginx Note that we are exposing port 9000 to localhost and use a possibility to use host.docker.internal host to communicate from inside Docker network with services which are running on localhost (on the host machine). See this answer on SO. Open http://localhost:9000. Nginx should now properly proxy requests to Django server and to Centrifugo, but we still need to do some things.","s":"Adding Nginx","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#adding-nginx","p":117},{"i":136,"t":"Well, now if you try to open a chat page with Nginx, Centrifugo, Django, and Redis running you will notice some errors in Centrifugo logs. That's because Centrifugo tries to proxy WebSocket connect events to Django to authenticate them but we have not created event handlers in Django yet. Let's fix this. Extend chat/urls.py: chat/urls.py from django.urls import path, re_pathfrom . import viewsurlpatterns = [ path('', views.index, name='index'), re_path('room/(?P[A-z0-9_-]+)/', views.room, name='room'), path('centrifugo/connect/', views.connect, name='connect'), path('centrifugo/subscribe/', views.subscribe, name='subscribe'), path('centrifugo/publish/', views.publish, name='publish'),] Extend chat/views.py: chat/views.py from django.http import JsonResponsefrom django.views.decorators.csrf import csrf_exempt@csrf_exemptdef connect(request): # In connect handler we must authenticate connection. # Here we return a fake user ID to Centrifugo to keep tutorial short. # More details about connect result format can be found in proxy docs: # https://centrifugal.dev/docs/server/proxy#connect-proxy logger.debug(request.body) response = { 'result': { 'user': 'tutorial-user' } } return JsonResponse(response)@csrf_exemptdef publish(request): # In publish handler we can validate publication request initialted by a user. # Here we return an empty object – thus allowing publication. # More details about publish result format can be found in proxy docs: # https://centrifugal.dev/docs/server/proxy#publish-proxy response = { 'result': {} } return JsonResponse(response)@csrf_exemptdef subscribe(request): # In subscribe handler we can validate user subscription request to a channel. # Here we return an empty object – thus allowing subscription. # More details about subscribe result format can be found in proxy docs: # https://centrifugal.dev/docs/server/proxy#subscribe-proxy response = { 'result': {} } return JsonResponse(response) connect view will accept all connections and return user ID as tutorial-user. In real app you most probably want to use Django sessions and return real authenticated user ID instead of tutorial-user. Since we told Centrifugo to proxy connection Cookie headers native Django user authentication will work just fine. Restart Django and try the chat app again. You should now successfully connect. Open a browser tab to the room page at http://localhost:9000/chat/room/lobby/. Open a second browser tab to the same room page. In the second browser tab, type the message \"hello\" and press Enter. You should now see \"hello\" echoed in the chat log in both the second browser tab and in the first browser tab. You now have a basic fully-functional chat server!","s":"Implementing proxy handlers","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#implementing-proxy-handlers","p":117},{"i":138,"t":"The list is large, but it's fun to do. To name some possible improvements: Replace tutorial-user used here with native Django session framework. We already proxying the Cookie header to Django from Centrifugo, so you can reuse native Django authentication. Only allow authenticated users to join rooms. Create Room model and add users to it – thus you will be able to check permissions inside subscribe and publish handlers. Create Message model to display chat history in Room. Replace Django devserver with something more suitable for production like Gunicorn. Check out Centrifugo possibilities like presence to display online users. Use cent Centrifugo HTTP API library to publish something to a user on behalf of a server. In this case you can avoid using publish proxy, publish messages to Django over convinient AJAX call - and then call Centrifugo HTTP API to publish message into a channel. You can replace connect proxy (which is an HTTP call from Centrifugo to Django on each connect) with JWT authentication. JWT authentication may result in a better application performance (since no additional proxy requests will be issued on connect). It can allow your Django app to handle millions of users on a reasonably small hardware and survive mass reconnects from all those users. More details can be found in Scaling WebSocket in Go and beyond blog post. Instead of using subscribe proxy you can put channel into connect proxy result or into JWT – thus using server-side subscriptions and avoid subscribe proxy HTTP call. One more thing I'd like to note is that if you aim to build a chat application like WhatsApp or Telegram where you have a screen with list of chats (which can be pretty long!) you should not create a separate channel for each room. In this case using separate channel per room does not scale well and you better use personal channel for each user to receive all user-related messages. And as soon as message published to a chat you can send message to each participant's channel. In this case, take a look at Centrifugo broadcast API.","s":"What could be improved","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#what-could-be-improved","p":117},{"i":140,"t":"The full example which can run by issuing a single docker compose up can be found on Github. It also has some CSS styles so that the chat looks like shown in the beginning.","s":"Tutorial source code with docker-compose","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#tutorial-source-code-with-docker-compose","p":117},{"i":142,"t":"Here we implemented a basic chat app with Django and Centrifugo. While a chat still requires work to be suitable for production this example can help understand core concepts of Centrifugo - specifically channel namespaces and proxy features. It's possible to use unidirectional Centrifugo transports instead of bidirectional WebSocket used here – in this case, you can go without using centrifuge-js at all. Centrifugo scales perfectly if you need to handle more connections – thanks to Centrifugo built-in PUB/SUB engines. It's also possible to use server-side subscriptions, keep channel history cache, use JWT authentication instead of connect proxy, enable channel presence, and more. All the power of Centrifugo is in your hands. Hope you enjoyed this tutorial. And let the Centrifugal force be with you! Join our community channels in case of any questions left after reading this.","s":"Conclusion","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#conclusion","p":117},{"i":144,"t":"In this tutorial, we will create a multi-room chat server using Laravel framework and Centrifugo real-time messaging server. Authenticated users of our chat app will be able to create new chat rooms, join existing rooms and instantly communicate inside rooms with the help of Centrifugo WebSocket real-time transport. caution This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step.","s":"Building a multi-room chat application with Laravel and Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"","p":143},{"i":146,"t":"The result will look like this: Sorry, your browser doesn't support embedded video. For the backend, we are using Laravel (version 8.65) as one of the most popular PHP frameworks. Centrifugo v3 will accept WebSocket client connections. And we will implement an integration layer between Laravel and Centrifugo. For CSS styles we are using recently released Bootstrap 5. Also, some vanilla JS instead of frameworks like React/Vue/whatever to make frontend Javascript code simple – so most developers out there could understand the mechanics. We are also using a bit old-fashioned server rendering here where server renders templates for different room routes (URLs) – i.e. our app is not a SPA app – mostly for the same reasons: to keep example short and let reader focus on Centrifugo and Laravel integration parts. To generate fake user avatars we are requesting images from https://robohash.org/ which can generate unique robot puctures based on some input string (username in our case). Robots like to chat with each other! tip We also have some ideas on further possible app improvements at the end of this post.","s":"Application overview","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-overview","p":143},{"i":148,"t":"Why would Laravel developers want to integrate a project with Centrifugo for real-time messaging functionality? That's a good question. There are several points which could be a good motivation: Centrifugo is open-source and self-hosted. So you can run it on your own infrastructure. Popular Laravel real-time broadcasting intergrations (Pusher and Ably) are paid cloud solutions. At scale Centrifugo will cost you less than cloud solutions. Of course cloud solutions do not require additional server setup – but everything is a trade-off right? So you should decide for youself. Centrifugo is fast and scales well. It has an optimized Redis Engine with client-side sharding and Redis Cluster support. Centrifugo can also scale with KeyDB, Nats, or Tarantool. So it's possible to handle millions of connections distributed over different Centrifugo nodes. Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for self-hosted real-time servers that scale to many nodes (like fast message history cache, or maintaining single user connection, both client-side and server-side subscriptions, etc). Centrifugo is lightweight, single binary server which works as a separate service – it can be a universal tool in the developer's pocket, can migrate with you from one project to another, no matter what programming language or framework is used for business logic. Hope this makes sense as a good motivation to give Centrifugo a try in your Laravel project. Let's get started!","s":"Why integrate Laravel with Centrifugo?","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#why-integrate-laravel-with-centrifugo","p":143},{"i":150,"t":"For the convenience of working with the example, we wrapped the end result into docker compose. To start the app clone examples repo, cd into v3/php_laravel_chat_tutorial directory and run: docker compose up At the first launch, the necessary images will be downloaded (will take some time and network bytes). When the main service is started, you should see something like this in container logs: ...app | Database seeding completed successfully.app | [10-Dec-2021 12:25:05] NOTICE: fpm is running, pid 112app | [10-Dec-2021 12:25:05] NOTICE: ready to handle connections Then go to http://localhost/ – you should see: Register (using some fake credentials) or sign up – and proceed to the chat rooms. Pay attention to the configuration of Centrifugo and Nginx. Also, on entrypoint which does some things: dependencies are installed via composer copying settings from .env.example db migrations are performed and the necessary npm packages are installed php-fpm starts","s":"Setup and start a project","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#setup-and-start-a-project","p":143},{"i":152,"t":"We assume you already familar with Laravel concepts, so we will just point you to some core aspects of the Laravel application structure and will pay more attention to Centrifugo integration parts.","s":"Application structure","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-structure","p":143},{"i":154,"t":"After the first launch of the application, all settings will be copied from the file .env.example to .env. Next, we will take a closer look at some settings.","s":"Environment settings","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#environment-settings","p":143},{"i":156,"t":"You can view the database structure here. We will use the following tables which will be then translated to the application models: Laravel standard user authentication tables. See https://laravel.com/docs/8.x/authentication. In the service we are using Laravel Breeze. For more information see official docs. rooms table. Basically - describes different rooms in the app every user can create. rooms many-to-many relation to users. Allows to add users into rooms when join button clicked or automatically upon room creation. messages. Keeps message history in rooms.","s":"Database migrations and models","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#database-migrations-and-models","p":143},{"i":158,"t":"For broadcasting we are using laravel-centrifugo library. It helps to simplify interaction between Laravel and Centrifugo by providing some convenient wrappers. Step-by-step configuration can be viewed in the readme file of this library. Pay attention to the CENTRIFUGO_API_KEY setting. It is used to send API requests from Laravel to Centrifugo and must match in .env and centrifugo.json files. And we also telling laravel-centrifugo the URL of Centrifugo. That's all we need to configure for this example app. See more information about Laravel broadcasting here. tip As an alternative to laravel-centrifugo, you can use phpcent – it's an official generic API client which allows publishing to Centrifugo HTTP API. But it does know nothing about Laravel broadcasting specifics.","s":"Broadcasting","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#broadcasting","p":143},{"i":160,"t":"When user opens a chat app it connects to Centrifugo over WebSocket transport. Let's take a closer look at Centrifugo server configuration file we use for this example app: { \"port\": 8000, \"engine\": \"memory\", \"api_key\": \"some-long-api-key-which-you-should-keep-secret\", \"allowed_origins\": [ \"http://localhost\", ], \"proxy_connect_endpoint\": \"http://nginx/centrifugo/connect/\", \"proxy_http_headers\": [ \"Cookie\" ], \"namespaces\": [ { \"name\": \"personal\" } ]} This configuration defines a connect proxy endpoint which is targeting Nginx and then proxied to Laravel. Centrifugo will proxy Cookie header of WebSocket HTTP Upgrade requests to Laravel – this allows using native Laravel authentication. We also defined a \"personal\" namespace – we will subscribe each user to a personal channel in this namespace inside connect proxy handler. Using namespaces for different real-time features is one of Centrifugo best-practices. Allowed origins must be properly set to prevent cross-site WebSocket connection hijacking.","s":"Interaction with Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#interaction-with-centrifugo","p":143},{"i":162,"t":"To use native Laravel user authentication middlewares, we will use Centrifugo proxy feature. When user connects to Centrifugo it's connection attempt will be transformed into HTTP request from Centrifugo to Laravel and will hit the connect proxy controller: class CentrifugoProxyController extends Controller{ public function connect() { return new JsonResponse([ 'result' => [ 'user' => (string) Auth::user()->id, 'channels' => [\"personal:#\".Auth::user()->id], ] ]); }} This controller protected by auth middleware. Since Centrifugo proxies Cookie header of initial WebSocket HTTP Upgrade request Laravel auth layer will work just fine. So in a controller you already has access to the current authenticated user. In the response from controller we tell Centrifugo the ID of connecting user and subscribe user to its personal channel (using user-limited channel feature of Centrifugo). Returning a channel in such way will subscribe user to it using server-side subscriptions mechanism. tip Note, that in our chat app we are using a single personal channel for each user to receive real-time updates from all rooms. We are not creating separate subscriptions for each room user joined too. This will allow us to scale more easily in the future, and basically the only viable solution in case of room list pagination in chat application like this. It does not mean you can not combine personal user channels and separate room channels for different tasks though. Some additional tips can be found in Centrifugo FAQ.","s":"Connect proxy controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#connect-proxy-controller","p":143},{"i":164,"t":"In RoomController we perform various actions with rooms: displaying rooms create rooms join users to rooms publish messages When we publish a message in a room, we send a message to the personal channel of all users joined to the room using the broadcast method of Centrifugo API. It allows publishing the same message into many channels. $message = Message::create([ 'sender_id' => Auth::user()->id, 'message' => $requestData[\"message\"], 'room_id' => $id,]);$room = Room::with('users')->find($id);$channels = [];foreach ($room->users as $user) { $channels[] = \"personal:#\" . $user->id;}$this->centrifugo->broadcast($channels, [ \"text\" => $message->message, \"createdAt\" => $message->created_at->toDateTimeString(), \"roomId\" => $id, \"senderId\" => Auth::user()->id, \"senderName\" => Auth::user()->name,]); We also add some fields to the published message which will be used when dynamically displaying a message coming from a WebSocket connection (see Client side below).","s":"Room controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#room-controller","p":143},{"i":166,"t":"Our chat is basically a one page with some variations dependng on the current route. So we use a single view for the entire chat app. On the page we have a form for creating rooms. The user who created the room automatically joins it upon creation. Other users need to join manually (using join button in the room). When sending a message (using the chat room message input), we make an AJAX request that hits RoomController shown above. A message saved into the database and then broadcasted to all users who joined this room. Here is a code that processes sending on ENTER: messageInput.onkeyup = function(e) { if (e.keyCode === 13) { e.preventDefault(); const message = messageInput.value; if (!message) { return; } const xhttp = new XMLHttpRequest(); xhttp.open(\"POST\", \"/rooms/\" + roomId + \"/publish\"); xhttp.setRequestHeader(\"X-CSRF-TOKEN\", csrfToken); xhttp.send(JSON.stringify({ message: message })); messageInput.value = ''; }}; After the message is processed on the server and broadcasted to Centrifugo it instantly comes to client-side. To receive the message we are connecting to Centrifugo WebSocket endpoint and wait for a message in the publish event handler: const url = \"ws://\" + window.location.host + \"/connection/websocket\";const centrifuge = new Centrifuge(url);centrifuge.on('connect', function(ctx) { console.log(\"connected to Centrifugo\", ctx);});centrifuge.on('disconnect', function(ctx) { console.log(\"disconnected from Centrifugo\", ctx);});centrifuge.on('publish', function(ctx) { if (ctx.data.roomId.toString() === currentRoomId) { addMessage(ctx.data); scrollToLastMessage(); } addRoomLastMessage(ctx.data);});centrifuge.connect(); We are using centrifuge-js client connector library to communicate with Centrifugo. This client abstracts away bidirectional asynchronous protocol complexity for us providing a simple way to listen connect, disconnect events and communicate with a server in various ways. In publish event handler we check whether the message belongs to the room the user is currently in. If yes, then we add it to the message history of the room. We also add this message to the room in the list on the left as the last chat message in room. If necessary, we crop the text for normal display. tip In our example we only subscribe each user to a single channel, but user can be subscribed to several server-side channels. To distinguish between them use ctx.channel inside publish event handler. And that's it! We went through all the main parts of the integration.","s":"Client side","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#client-side","p":143},{"i":168,"t":"As promised, here is a list with several possible app improvements: Transform to a single page app, use productive Javascript frameworks like React or VueJS instead of vanilla JS. Add message read statuses - as soon as one of the chat participants read the message mark it read in the database. Introduce user-to-user chats. Support pagination for the message history, maybe for chat room list also. Don't show all rooms in the system – add functionality to search room by name. Horizontal scaling (using multiple nodes of Centrifugo, for example with Redis Engine) – mostly one line in Centrifugo config if you have Redis running. Gracefully handle temporary disconnects by loading missed messages from the database or Centrifugo channel history cache. Optionally replace connect proxy with JWT authentication to reduce HTTP calls from Centrifugo to Laravel. This may drastically reduce resources for Laravel backend at scale. Try using Centrifugo RPC proxy feature to use WebSocket connection for message publish instead of issuing AJAX request.","s":"Possible improvements","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#possible-improvements","p":143},{"i":170,"t":"We built a chat app with Laravel and Centrifugo. While there is still an area for improvements, this example is not really the basic. It's already valuable in the current form and may be transformed into part of your production system with minimal tweaks. Hope you enjoyed this tutorial. If you have any questions after reading – join our community channels. We touched only part of Centrifugo concepts here – take a look at detailed Centrifugo docs nearby. And let the Centrifugal force be with you!","s":"Conclusion","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#conclusion","p":143},{"i":172,"t":"Let's say you develop an application and want a real-time connection which is subscribed to one channel. Let's also assume that this channel is used for user personal notifications. So only one user in the application can subcribe to that channel to receive its notifications in real-time. In this post we will look at various ways to achieve this with Centrifugo, and consider trade-offs of the available approaches. The main goal of this tutorial is to help Centrifugo newcomers be aware of all the ways to control channel permissions by reading just one document. And... well, there are actually 8 ways I found, not 101 😇","s":"101 ways to subscribe user on a personal channel in Centrifugo","u":"/blog/2022/07/29/101-way-to-subscribe","h":"","p":171},{"i":174,"t":"To make the post a bit easier to consume let's setup some things. Let's assume that the user for which we provide all the examples in this post has ID \"17\". Of course in real-life the examples given here can be extrapolated to any user ID. When you create a real-time connection to Centrifugo the connection is authenticated using the one of the following ways: using connection JWT using connection request proxy from Centrifugo to the configured endpoint of the application backend (connect proxy) As soon as the connection is successfully established and authenticated Centrifugo knows the ID of connected user. This is important to understand. And let's define a namespace in Centrifugo configuration which will be used for personal user channels: { ... \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} Defining namespaces for each new real-time feature is a good practice in Centrifugo. As an awesome improvement we also enabled presence in the personal namespace, so whenever users subscribe to a channel in this namespace Centrifugo will maintain online presence information for each channel. So you can find out all connections of the specific user existing at any moment. Defining presence is fully optional though - turn it of if you don't need presence information and don't want to spend additional server resources on maintaining presence.","s":"Setup","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#setup","p":171},{"i":176,"t":"tip Probably the most performant approach. All you need to do is to extend namespace configuration with allow_user_limited_channels option: { \"namespaces\": [ { \"name\": \"personal\", \"presence\": true, \"allow_user_limited_channels\": true } ]} On the client side you need to have sth like this (of course the ID of current user will be dynamic in real-life): const sub = centrifuge.newSubscription('personal:#17');sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe(); Here you are subscribing to a channel in personal namespace and listening to publications coming from a channel. Having # in channel name tells Centrifugo that this is a user-limited channel (because # is a special symbol that is treated in a special way by Centrifugo as soon as allow_user_limited_channels enabled). In this case the user ID part of user-limited channel is \"17\". So Centrifugo allows user with ID \"17\" to subscribe on personal:#17 channel. Other users won't be able to subscribe on it. To publish updates to subscription all you need to do is to publish to personal:#17 using server publish API (HTTP or GRPC).","s":"#1 – user-limited channel","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#1--user-limited-channel","p":171},{"i":178,"t":"tip Probably the most flexible approach, with reasonably good performance characteristics. Another way we will look at is using subscription JWT for subscribing. When you create Subscription object on the client side you can pass it a subscription token, and also provide a function to retrieve subscription token (useful to automatically handle token refresh, it also handles initial token loading). const token = await getSubscriptionToken('personal:17');const sub = centrifuge.newSubscription('personal:17', { token: token});sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe(); Inside getSubscriptionToken you can issue a request to the backend, for example in browser it's possible to do with fetch API. On the backend side you know the ID of current user due to the native session mechanism of your app, so you can decide whether current user has permission to subsribe on personal:17 or not. If yes – return subscription JWT according to our rules. If not - return empty string so subscription will go to unsubscribed state with unauthorized reason. Here are examples for generating subscription HMAC SHA-256 JWTs for channel personal:17 and HMAC secret key secret: Python NodeJS import jwtimport timeclaims = { \"sub\": \"17\", \"channel\": \"personal:17\" \"exp\": int(time.time()) + 30*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ 'sub': '17', 'channel': 'personal:17' }) .setProtectedHeader({ alg }) .setExpirationTime('30m') .sign(secret) console.log(token);})(); Since we set expiration time for subscription JWT tokens we also need to provide a getToken function to a client on the frontend side: const sub = centrifuge.newSubscription('personal:17', { getToken: async function (ctx) { const token = await getSubscriptionToken('personal:17'); return token; }});sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe(); This function will be called by SDK automatically to refresh subscription token when it's going to expire. And note that we omitted setting token option here – since SDK is smart enough to call provided getToken function to extract initial subscription token from the backend. The good thing in using subscription JWT approach is that you can provide token expiration time, so permissions to subscribe on a channel will be validated from time to time while connection is active. You can also provide additional channel context info which will be attached to presence information (using info claim of subscription JWT). And you can granularly control channel permissions using allow claim of token – and give client capabilities to publish, call history or presence information (this is Centrifugo PRO feature at this point). Token also allows to override some namespace options on per-subscription basis (with override claim). Using subscription tokens is a general approach for any channels where you need to check access first, not only for personal user channels.","s":"#2 - channel token authorization","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#2---channel-token-authorization","p":171},{"i":180,"t":"tip Probably the most secure approach. Subscription JWT gives client a way to subscribe on a channel, and avoid requesting your backend for permission on every resubscribe. Token approach is very good in massive reconnect scenario, when you have many connections and they all resubscribe at once (due to your load balancer reload, for example). But this means that if you unsubscribed client from a channel using server API, client can still resubscribe with token again - until token will expire. In some cases you may want to avoid this. Also, in some cases you want to be notified when someone subscribes to a channel. In this case you may use subscribe proxy feature. When using subscribe proxy every attempt of a client to subscribe on a channel will be translated to request (HTTP or GRPC) from Centrifugo to the application backend. Application backend can decide whether client is allowed to subscribe or not. One advantage of using subscribe proxy is that backend can additionally provide initial channel data for the subscribing client. This is possible using data field of subscribe result generated by backend subscribe handler. { \"proxy_subscribe_endpoint\": \"http://localhost:9000/centrifugo/subscribe\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true, \"proxy_subscribe\": true } ]} And on the backend side define a route /centrifugo/subscribe, check permissions of user upon subscription and return result to Centrifugo according to our subscribe proxy docs. Or simply run GRPC server using our proxy definitions and react on subscription attempt sent from Centrifugo to backend over GRPC. On the client-side code is as simple as: const sub = centrifuge.newSubscription('personal:17');sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe();","s":"#3 - subscribe proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#3---subscribe-proxy","p":171},{"i":182,"t":"tip The approach where you don't need to manage client-side subscriptions. Server-side subscriptions is a way to consume publications from channels without even create Subscription objects on the client side. In general, client side Subscription objects provide a more flexible and controllable way to work with subscriptions. Clients can subscribe/unsubscribe on channels at any point. Client-side subscriptions provide more details about state transitions. With server-side subscriptions though you are consuming publications directly from Client instance: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { token: 'CONNECTION-JWT'});client.on('publication', function(ctx) { console.log('publication received from server-side channel', ctx.channel, ctx.data);});client.connect(); In this case you don't have separate Subscription objects and need to look at ctx.channel upon receiving publication or to publication content to decide how to handle it. Server-side subscriptions could be a good choice if you are using Centrifugo unidirectional transports and don't need dynamic subscribe/unsubscribe behavior. The first way to subscribe client on a server-side channel is to include channels claim into connection JWT: { \"sub\": \"17\", \"channels\": [\"personal:17\"]} Upon successful connection user will be subscribed to a server-side channel by Centrifugo. One downside of using server-side channels is that errors in one server-side channel (like impossible to recover missed messages) may affect the entire connection and result into reconnects, while with client-side subscriptions individual subsription failures do not affect the entire connection. But having one server-side channel per-connection seems a very reasonable idea to me in many cases. And if you have stable set of subscriptions which do not require lifetime state management – this can be a nice approach without additional protocol/network overhead involved.","s":"#4 - server-side channel in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#4---server-side-channel-in-connection-jwt","p":171},{"i":184,"t":"Similar to the previous one for cases when you are authenticating connections over connect proxy instead of using JWT. This is possible using channels field of connect proxy handler result. The code on the client-side is the same as in Option #4 – since we only change the way how list of server-side channels is provided.","s":"#5 - server-side channel in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#5---server-side-channel-in-connect-proxy","p":171},{"i":186,"t":"tip Almost no code approach. As we pointed above Centrifugo knows an ID of the user due to authentication process. So why not combining this knowledge with automatic server-side personal channel subscription? Centrifugo provides exactly this with user personal channel feature. { \"user_subscribe_to_personal\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} This feature only subscribes non-anonymous users to personal channels (those with non-empty user ID). The configuration above will subscribe our user \"17\" to channel personal:#17 automatically after successful authentication.","s":"#6 - automatic personal channel subscription","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#6---automatic-personal-channel-subscription","p":171},{"i":188,"t":"Allows using client-side subscriptions, but skip receiving subscription token. This is only available in Centrifugo PRO at this point. So when generating JWT you can provide additional caps claim which contains channel resource capabilities: Python NodeJS import jwtimport timeclaims = { \"sub\": \"17\", \"exp\": int(time.time()) + 30*60, \"caps\": [ { \"channels\": [\"personal:17\"], \"allow\": [\"sub\"] } ]}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose');(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '17', caps: [ { \"channels\": [\"personal:17\"], \"allow\": [\"sub\"] } ] }) .setProtectedHeader({ alg }) .setExpirationTime('30m') .sign(secret) console.log(token);})(); While in case of single channel the benefit of using this approach is not really obvious, it can help when you are using several channels with stric access permissions per connection, where providing capabilities can help to save some traffic and CPU resources since we avoid generating subscription token for each individual channel.","s":"#7 – capabilities in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#7--capabilities-in-connection-jwt","p":171},{"i":190,"t":"This is very similar to the previous approach, but capabilities are passed to Centrifugo in connect proxy result. So if you are using connect proxy for auth then you can still provide capabilities in the same form as in JWT. This is also a Centrifugo PRO feature.","s":"#8 – capabilities in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#8--capabilities-in-connect-proxy","p":171},{"i":192,"t":"Which way to choose? Well, it depends. Since your application will have more than only a personal user channel in many cases you should decide which approach suits you better in each particular case – it's hard to give the universal advice. Client-side subscriptions are more flexible in general, so I'd suggest using them whenever possible. Though you may use unidirectional transports of Centrifugo where subscribing to channels from the client side is not simple to achieve (though still possible using our server subscribe API). Server-side subscriptions make more sense there. The good news is that all our official bidirectional client SDKs support all the approaches mentioned in this post. Hope designing the channel configuration on top of Centrifugo will be a pleasant experience for you. Attributions Internet network vector created by rawpixel.com - www.freepik.com Cyber security icons created by Smashicons - Flaticon","s":"Teardown","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#teardown","p":171},{"i":194,"t":"In Centrifugo v5 we're phasing out old client protocol support, introducing a more intuitive HTTP API, adjusting token management behaviour in SDKs, improving configuration process, and refactoring the history meta ttl option. As the result you get a cleaner, more user-friendly, and optimized Centrifugo experience. And we have important news about the project - check it out in the end of this post. What is Centrifugo? Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, GRPC, SockJS, WebTransport). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server. Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer. Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency. Let's proceed and take a look at most notable changes of Centrifugo v5.","s":"Centrifugo v5 released","u":"/blog/2023/06/29/centrifugo-v5-released","h":"","p":193},{"i":196,"t":"With the introduction of Centrifugo v4, our previous major release, we rolled out a new version of the client protocol along with a set of client SDKs designed to work in conjunction with it. Nevertheless, we maintained support for the old client protocol in Centrifugo v4 to facilitate a seamless migration of applications. In Centrifugo v5 we are discontinuing support for the old protocol. If you have been using Centrifugo v4 with the latest SDKs, this change should have no impact on you. From our perspective, removing support for the old protocol allows us to eliminate a considerable amount of non-obvious branching in the source code and cleanup Protobuf schema definitions.","s":"Dropping old client protocol","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#dropping-old-client-protocol","p":193},{"i":198,"t":"In Centrifugo v5 we are adjusting client SDK specification in the aspect of connection token management. Previously, returning an empty token string from getToken callback resulted in client disconnection with unauthorized reason. There was some problem with it though. We did not take into account the fact that empty token may be a valid scenario actually. Centrifugo supports options to avoid using token at all for anonymous access. So the lack of possibility to switch between token/no token scenarios did not allow users to easily implement login/logout workflow. The only way was re-initializing SDK. Now returning an empty string from getToken is a valid scenario which won't result into disconnect on the client side. It's still possible to disconnect client by returning a special error from getToken function. And we are putting back setToken method to our SDKs – so it's now possible to reset the token to be empty upon user logout. An abstract example in Javascript which demonstrates how login/logout flow may be now implemented with our SDK: const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', { // Provide function which returns empty string for anonymous users, // and proper JWT for authenticated users. getToken: getTokenImplementation});centrifuge.connect();loginButton.addEventListener('click', function() { centrifuge.disconnect(); // Do actual login here. centrifuge.connect();});logoutButton.addEventListener('click', function() { centrifuge.disconnect(); // Reset token - so that getToken will be called on next connect attempt. centrifuge.setToken(\"\"); // Do actual logout here. centrifuge.connect();}); We updated all our SDKs to inherit described behaviour - check out v5 migration guide for more details.","s":"Token behaviour adjustments in SDKs","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#token-behaviour-adjustments-in-sdks","p":193},{"i":200,"t":"One of Centrifugo's key advantages for real-time messaging tasks is its ephemeral channels and per-channel history. In version 5, we've improved one aspect of handling history by offering users the ability to tune the history meta TTL option at the channel namespace level. The history meta TTL is the duration Centrifugo retains meta information about each channel stream, such as the latest offset and current stream epoch. This data allows users to successfully restore connections upon reconnection, particularly useful when subscribed to mostly inactive channels where publications are infrequent. Although the history meta TTL can usually be reasonably large (significantly larger than history TTL), there are certain scenarios where it's beneficial to minimize it as much as possible. One such use case is illustrated in this example. Using Centrifugo SDK and channels with history, it's possible to reliably stream results of asynchronous tasks to clients. As another example, consider a ChatGPT use case where clients ask questions, subscribe to a channel with the answer, and then the response is streamed towards the client token by token. This all may be done over a secure, separate channel protected with a token. With the ability to use a relatively small history meta TTL in the channel namespace, implementing such things is now simpler. Hence, history_meta_ttl is now an option at the channel namespace level (instead of per-engine). However, setting it is optional as we have a global default value for it - see details in the doc.","s":"history_meta_ttl refactoring","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#history_meta_ttl-refactoring","p":193},{"i":202,"t":"When running in cluster Centrifugo nodes can communicate between each other using broker's PUB/SUB. Nodes exchange some information - like statistics, emulation requests, etc. In Centrifugo v5 we are simplifying and making inter-node communication protocol slightly faster by removing extra encoding layers from it's format. Something similar to what we did for our client protocol in Centrifugo v4. This change, however, leads to an incompatibility between Centrifugo v4 and v5 nodes in terms of their communication protocols. Consequently, Centrifugo v5 cannot be part of a cluster with Centrifugo v4 nodes.","s":"Node communication protocol update","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#node-communication-protocol-update","p":193},{"i":204,"t":"From the beginning Centrifugo HTTP API exposed one /api endpoint to make requests with all command types. To work properly HTTP API had to add one additional layer to request JSON payload to be able to distinguish between different API methods: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey API_KEY\" \\ --request POST \\ --data '{\"method\": \"publish\", \"params\": {\"channel\": \"test\", \"data\": {\"x\": 1}}}' \\ http://localhost:8000/api And it worked fine. It additionally supported request batching where users could send many commands to Centrifugo in one request using line-delimited JSON. However, the fact that we were accommodating various commands via a single API endpoint resulted in nested serialized payloads for each command. The top-level method would determine the structure of the params. We addressed this issue in the client protocol in Centrifugo v4, and now we're addressing a similar issue in the inter-node communication protocol in Centrifugo v5. At some point we introduced GRPC API in Centrifugo. In GRPC case we don't have a way to send batches of commands without defining a separate method to do so. These developments highlighted the need for us to align the HTTP API format more closely with the GRPC API. Specifically, we need to separate the command method from the actual method payload, moving towards a structure like this: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: API_KEY\" \\ --request POST \\ --data '{\"channel\": \"test\", \"data\": {\"x\": 1}}' \\ http://localhost:8000/api/publish Note: /api/publish instead of /api in path payload does not include method and params keys anymore we also support X-API-Key header for setting API key to be closer to OpenAPI specification (see more about OpenAPI below) In v5 we implemented the approach above. Many thanks to @StringNick for the help with the implementation and discussions. Our HTTP and GRPC API are very similar now. We've also introduced a new batch method to send multiple commands in both HTTP and GRPC APIs, a feature that was previously unavailable in GRPC. Documentation for v5 was updated to reflect this change. But it worth noting - old API format id still supported. It will be supported for some time while we are migrating our HTTP API libraries to use modern API version. Hopefully users won't be affected by this migration a lot, just switching to a new version of library at some point.","s":"New HTTP API format","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#new-http-api-format","p":193},{"i":206,"t":"One additional benefit of moving to the new HTTP format is the possibility to define a clear OpenAPI schema for each API method Centrifugo has. It was previously quite tricky due to the fact we had one endpoint capable to work with all kinds of commands. This change paves the way for generating HTTP clients based on our OpenAPI specification. We now have Swagger UI built-in. To access it, launch Centrifugo with the \"swagger\": true option and navigate to http://localhost:8000/swagger. The Swagger UI operates on the internal port, so if you're running Centrifugo using our Kubernetes Helm chart, it won't be exposed to the same ingress as client connection endpoints. This is similar to how our Prometheus, admin, API, and debug endpoints currently work.","s":"OpenAPI spec and Swagger UI","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#openapi-spec-and-swagger-ui","p":193},{"i":208,"t":"Another good addition is an OpenTelemetry tracing support for HTTP and GRPC server API requests. If you are using OpenTelemetry in your services you can now now enable tracing export in Centrifugo and find Centrifugo API request exported traces in your tracing collector UI. Description and simple example with Jaeger may be found in observability chapter. We only support OTLP HTTP export format and trace format defined in W3C spec: https://www.w3.org/TR/trace-context/.","s":"OpenTelemetry for server API","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#opentelemetry-for-server-api","p":193},{"i":210,"t":"With the introduction of JWKS support in Centrifugo v4 (a way to validate JWT tokens using a remote endpoint which manages keys and key rotation - see JWK spec) Centrifugo users can rely on JWKS provider (like Keycloak, AWS Cognito) for making authentication. But at the same time developers may want to work with channels using subscription tokens managed in a custom way – without using the same JWKS configuration used for connection tokens. Centrifugo v5 allows doing by introducing the separate_subscription_token_config option. When separate_subscription_token_config is true Centrifugo does not look at general token options at all when verifying subscription tokens and uses config options starting from subscription_token_ prefix instead. Here is an example how to use JWKS for connection tokens, but have HMAC-based verification for subscription tokens: config.json { \"token_jwks_public_endpoint\": \"https://example.com/openid-connect/certs\", \"separate_subscription_token_config\": true, \"subscription_token_hmac_secret_key\": \"separate_secret_which_must_be_strong\"}","s":"Separate config for subscription token","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#separate-config-for-subscription-token","p":193},{"i":212,"t":"With every release, Centrifugo offers more and more options. One thing we've noticed is that some options from previous Centrifugo options, which were already removed, still persist in the user's configuration file. Another issue is that a single typo in the configuration key can cost hours of debugging especially for Centrifugo new users. What is worse, the typo might result in unexpected behavior if the feature isn't properly tested before being run in production. In Centrifugo v5, we are addressing these problems. Now, Centrifugo logs on WARN level about unknown keys found in the configuration upon server start-up. Not only in the configuration file but also verifying the validity of environment variables (looking at those starting with CENTRIFUGO_ prefix). This should help clean up the configuration to align with the latest Centrifugo release and catch typos at an early stage. It looks like this: 08:25:33 [WRN] unknown key found in the namespace object key=watch namespace_name=xx08:25:33 [WRN] unknown key found in the proxy object key=type proxy_name=connect08:25:33 [WRN] unknown key found in the configuration file key=granulr_proxy_mode08:25:33 [WRN] unknown key found in the environment key=CENTRIFUGO_ADDRES These warnings do not prevent server to start so you can gradually clean up the configuration.","s":"Unknown config keys warnings","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#unknown-config-keys-warnings","p":193},{"i":214,"t":"Centrifugo v5 supports a special url parameter for bidirectional websocket which turns on using native WebSocket frame ping-pong mechanism instead of server-to-client application level pings Centrifugo uses by default. This simplifies debugging Centrifugo protocol with tools like Postman, wscat, websocat, etc. Previously it was inconvenient due to the fact Centrifugo sends periodic ping message to the client ({} in JSON protocol scenario) and expects pong response back within some time period. Otherwise Centrifugo closes connection. This results in problems with mentioned tools because you had to manually send {} pong message upon ping message. So typical session in wscat could look like this: ❯ wscat --connect ws://localhost:8000/connection/websocketConnected (press CTRL+C to quit)> {\"id\": 1, \"connect\": {}}< {\"id\":1,\"connect\":{\"client\":\"9ac9de4e-5289-4ad6-9aa7-8447f007083e\",\"version\":\"0.0.0\",\"ping\":25,\"pong\":true}}< {}Disconnected (code: 3012, reason: \"no pong\") The parameter is called cf_ws_frame_ping_pong, to use it connect to Centrifugo bidirectional WebSocket endpoint like ws://localhost:8000/websocket/connection?cf_ws_frame_ping_pong=true. Here is an example which demonstrates working with Postman WebSocket where we connect to local Centrifugo and subscribe to two channels test1 and test2: You can then proceed to Centrifugo admin web UI, publish something to these channels and see publications in Postman. Note, how we sent several JSON commands in one WebSocket frame to Centrifugo from Postman in the example above - this is possible since Centrifugo protocol supports batches of commands in line-delimited format. We consider this feature to be used only for debugging, in production prefer using our SDKs without using cf_ws_frame_ping_pong parameter – because app-level ping-pong is more efficient and our SDKs detect broken connections due to it.","s":"Simplifying protocol debug with Postman","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#simplifying-protocol-debug-with-postman","p":193},{"i":216,"t":"As you know SockJS is deprecated in Centrifugal ecosystem since Centrifugo v4. In this release we are not removing support for it – but we may do this in the next release. Unfortunately, SockJS client repo is poorly maintained these days. And some of its iframe-based transports are becoming archaic. If you depend on SockJS and you really need fallback for WebSocket – consider switching to Centrifugo own bidirectional emulation for the browser which works over HTTP-streaming (using modern fetch API with Readable streams) or SSE. It should be more performant and work without sticky sessions requirement (sticky sessions is an optimization in our implementation). More details may be found in Centrifugo v4 release post. If you think SockJS is still required for your use case - reach us out so we could think about the future steps together.","s":"The future of SockJS","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#the-future-of-sockjs","p":193},{"i":218,"t":"Finally, some important news we mentioned in the beginning! Centrifugo is now backed by the company called Centrifugal Labs LTD - a Cyprus-registered technology company. This should help us to finally launch Centrifugo PRO offering – the product we have been working on for a couple of years now and which has some unique and powerful features like real-time analytics or push notification API. As a Centrifugo user you will start noticing mentions of Centrifugal Labs LTD in our licenses, Github organization, throughout this web site. And that's mostly it - no radical changes at this point. We will still be working on improving Centrifugo trying to find a balance between OSS and PRO versions. Which is difficult TBH – but we will try. An ideal plan for us – make Centrifugo development sustainable enough to have the possibility for features from the PRO version flow to the OSS version eventually. The reality may be harder than this of course.","s":"Introducing Centrifugal Labs LTD","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#introducing-centrifugal-labs-ltd","p":193},{"i":220,"t":"That's all about most notable things happened in Centrifugo v5. We updated documentation to reflect the changes in v5, also some documentation chapters were rewritten. For example, take a look at the refreshed Design overview. Several more changes and details outlined in the migration guide for Centifugo v5. Please feel free to contact in the community rooms if you have questions about the release. And as usual, let the Centrifugal force be with you!","s":"Conclusion","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#conclusion","p":193},{"i":222,"t":"Centrifugo provides HTTP and GRPC APIs for publishing messages into channels. Publish server API is very straighforward to use - it's a simple request with a channel and data to be delivered to active WebSocket connections subscribed to a channel. Sometimes though Centrifugo users want to avoid synchronous calls to Centrifugo API delegating this work to asynchronous tasks. Many companies have convenient infrastructure for messaging processing tasks - like Kafka, Nats Jetstream, Redis, RabbitMQ, etc. Some using transactional outbox pattern to reliably deliver events upon database changes and have a ready infrastructure to push such events to some queue. From which want to re-publish events to Centrifugo. In this post we get familiar with a tool called Benthos and show how it may simplify integrating your asynchronous message flow with Centrifugo. And we discuss some non-obvious pitfalls of asynchronous publishing approach in regards to real-time applications.","s":"Asynchronous message streaming to Centrifugo with Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"","p":221},{"i":224,"t":"First start Centrifugo (with debug logging to see incoming API requests in logs): centrifugo genconfigcentrifugo -c config.json --log_level debug Hope this step is already simple for you, if not - check out Quickstart tutorial.","s":"Start Centrifugo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#start-centrifugo","p":221},{"i":226,"t":"Benthos is an awesome tool which allows consuming data from various inputs, process data, then output it into configured outputs. See more detailed description on Benthos' website. The number of inputs supported by Benthos is huge: check it out here. Most of the major systems widely used these days are supported. Benthos also supports many outputs – but here we only interested in message transfer to Centrifugo. There is no built-in Centrifugo output in Benthos but it provides a generic http_client output which may be used to send requests to any HTTP server. Benthos may also help with retries, provides tools for additional data processing and transformations. Just like Centrifugo Benthos written in Go language – so its installation is very straighforward and similar to Centrifugo. See official instructions. Let's assume you've installed Benthos and have benthos executable available in your system. Let's create Benthos configuration file: benthos create > config.yaml Take a look at generated config.yaml - it contains various options for Benthos instance, the most important (for the context of this post) are input and output sections. And after that you can start Benthos instance with: benthos -c config.yaml Now we need to tell Benthos from where to get data and how to send it to Centrifugo.","s":"Install and run Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#install-and-run-benthos","p":221},{"i":228,"t":"For our example here we will user Redis List as an input, won't add any additional data processing and will output messages consumed from Redis List into Centrifugo publish server HTTP API. To achieve this add the following as input in Benthos config.yaml: input: label: \"centrifugo_redis_consumer\" redis_list: url: \"redis://127.0.0.1:6379\" key: \"centrifugo.publish\" And configure the output like this: output: label: \"centrifugo_http_publisher\" http_client: url: \"http://localhost:8000/api/publish\" verb: POST headers: X-API-Key: \"\" timeout: 5s max_in_flight: 20 The output points to Centrifugo publish HTTP API. Replace with your Centrifugo api_key (found in Centrifugo configuration file).","s":"Configure Benthos input and output","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#configure-benthos-input-and-output","p":221},{"i":230,"t":"Start Benthos instance: benthos -c config.yaml You will see errors while Benthos tries to connect to input Redis source. So start Redis server: docker run --rm -it --name redis redis:7 Now connect to Redis (using redis-cli): docker exec -it redis redis-cli And push command to Redis list: 127.0.0.1:6379> rpush centrifugo.publish '{\"channel\": \"chat\", \"data\": {\"input\": \"test\"}}'(integer) 1 This message will be consumed from Redis list by Benthos and published to Centrifugo HTTP API. If you have active subscribers to channel chat – you will see messages delivered to them. That's it! tip When using Redis List input you can scale out Benthos instances to run several of them if needed.","s":"Push messages to Redis queue","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#push-messages-to-redis-queue","p":221},{"i":232,"t":"Here is a quick demonstration of the described integration. See how we push messages into Redis List and those are delivered to WebSocket clients: Sorry, your browser doesn't support embedded video.","s":"Demo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#demo","p":221},{"i":234,"t":"This all seems simple. But publishing messages asynchronously may highlight some pitfalls not visible or not applicable for synchronous publishing to Centrifugo API.","s":"Pitfalls of async publishing","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#pitfalls-of-async-publishing","p":221},{"i":236,"t":"Most of the time it will work just fine. But one day you can come across intermediate queue growth and increased delivery lag. This may happen due to temporary Centrifugo or worker process unavailability. As soon as system comes back to normal queued messages will be delivered. Depending on the real-time feature implemented this may be a concern to think about and decide whether this is desired or not. Your application should be designed accordingly to deal with late delivery. BTW late delivery may be a case even with synchronous publishing – it just almost never strikes. But theoretically client can reload browser page and load initial app state while message flying from the backend to client over Centrifugo. It's not Centrifugo specific actually - it's just a nature of networks and involved latencies. In general solution to prevent late delivery UX issues completely is using object versioning. Object version should be updated in the database on every change from which the real-time event is generated. Attach object version information to every real-time message. Also get object version on initial state load. This way you can compare versions and drop non-actual real-time messages on client side. Possible strategy may be using synchronous API for real-time features where at most once delivery is acceptable and use asynchronous delivery where you need to deliver messages with at least once guarantees. In a latter case you most probably designed proper idempotency behaviour on client side anyway.","s":"Late delivery","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#late-delivery","p":221},{"i":238,"t":"Another thing to consider is message ordering. Centrifugo itself may provide message ordering in channels. If you published one message to Centrifugo API, then another one – you can expect that messages will be delivered to a client in the same order. But as soon as you have an intermediary layer like Benthos or any other asynchronous worker process – then you must be careful about ordering. In case of Benthos and example here you can set max_in_flight parameter to 1 instead of 20 and keep only one instance of Benthos running to preserve ordering. In case of streaming from Kafka you can rely on Kafka message partitioning feature to preserve message ordering.","s":"Ordering concerns","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#ordering-concerns","p":221},{"i":240,"t":"If you preserved ordering in your asynchronous workers then the next thing to consider is throughput limitations. You have a limited number of workers, these workers send requests to Centrifugo one by one. In this case throughput is limited by the number of workers and RTT (round-trip time) between worker process and Centrifugo. If we talk about using Redis List structure as a queue - you can possibly shard data amongst different Redis List queues by some key to improve throughput. In this case you need to push messages where order should be preserved into a specific queue. In this case your get a setup similar to Kafka partitioning. In case of using manually partitioned queues or using Kafka you can have parallelism equal to the number of partitions. Let's say you have 20 workers which can publish messages in parallel and 5ms RTT time between worker and Centrifugo. In this case you can publish 20*(1000/5) = 4000 messages per second max. To improve throughput futher consider increasing worker number or batching publish requests to Centrifugo (using Centrifugo's batch API).","s":"Throughput when ordering preserved","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#throughput-when-ordering-preserved","p":221},{"i":242,"t":"When publishing asynchronously you should also don't forget about error handling. Benthos will handle network errors automatically for you. But there could be internal errors from Centrifugo returned as part of response. It's not very convenient to handle with Benthos out of the box – so we think about adding transport-level error mode to Centrifugo.","s":"Error handling","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#error-handling","p":221},{"i":244,"t":"Sometimes you want to publish to Centrifugo asynchronously using messaging systems convenient for your company. Usually you can write worker process to re-publish messages to Centrifugo. Sometimes it may be simplified using helpful tools like Benthos. Here we've shown how Benthos may be used to transfer messages from Redis List queue to Centrifugo API. With some modifications you can achieve the same for other input sources - such as Kafka, RabbitMQ, Nats Jetstream, etc. But publishing messages asynchronously highlights several pifalls - like late delivery, ordering issues, throughput considerations and error handling – which should be carefully addressed. Different real-time features may require different strategies.","s":"Conclusion","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#conclusion","p":221},{"i":246,"t":"This post introduces a new format in Centrifugal blog – interview with a Centrifugo user! Let's dive into an exciting chat with the engineering team of RabbitX platform, a global permissionless perpetuals exchange powered on Starknet. We will discover how Centrifugo helped RabbitX to build a broker platform with current trading volume of 25 million USD daily! 🚀🎉 [Q] Hey team - thanks for your desire to share your Centrifugo use case. First of all, could you provide some information about RabbitX - what is it?​ RabbitX is a global permissionless perpetuals exchange built on Starknet. RabbitX is building the most secure and liquid global derivatives network, giving you 24/7 access to global markets anywhere in the world, with 20x leverage. In its core there is an orderbook - where traders match against market makers, which require to support high throughput and low latency tech stack. The technologies that we are using: Tarantool as in-memory database and business logic server Centrifugo as our major websocket server Different stark tech to support decentralized settlement [Q] Great! What is the goal of Centrifugo in your project? Which real-time features you have?​ Almost all the information users see in our terminal is streamed over Centrifugo. We use it for financial order books, candlestick chart updates, and stat number updates. We can also send real-time personal user notifications via Centrifugo. Instead of all the words, here is a short recording of our terminal trading BTC: [Q] We know that you are using Centrifugo Tarantool engine - could you explain why and how it works in your case?​ Well, that's an interesting thing. We heavily use Tarantool in our system. It grants us immense flexibility, performance, and the power to craft whatever we envision. It ensures the atomicity essential for trading match-making. When we were in search of a WebSocket real-time bus for messages, we were pleasantly surprised to discover that Centrifugo integrates with Tarantool. In our scenario, this allowed us to bypass additional network round-trips, as we can stream data directly from Tarantool to Centrifugo channels. Reducing latency is paramount for financial instruments. Furthermore, I can mention that over our nine months in production, we didn't encounter any issues with Centrifugo – it performed flawlessly! Regarding authentication, we employ Centrifugo's JWT authentication and subscribe proxy. Thus, subscriptions are authorized on our specialized service written in Go. We're also actively using Centrifugo possibility to send initial channel data in the subscribe proxy response. One challenge we overcame was bridging the gap between the subscription's initial request and the continuous message stream in the order book component. To address this, we employed our own sequence numbers in events, coupled with Centrifugo's channel history – this allowed us to deal with missed events when needed. Actually the gaps in event stream are rare in practice and our workaround not needed most of the time, but now we're confident our users never experience this issue. [Q] Looking at RabbitX terminal app we see quite modern UI - could you share more details about it too?​ Our frontend is built on top of React in combination with TradingView Supercharts. And of course we are using centrifuge-js SDK for establishing connections with Centrifugo. [Q] So you are nine months in production at this point. Can you share some real world numbers related to your Centrifugo setup?​ At this point we can have up to a thousand active concurrent traders and send more than 60 messages per second towards one client in peak hours. All the load is served with a single Centrifugo instance (and we have one standby instance). [Q] Anything else you want to share with readers of Centrifugal blog?​ When we designed the system the main goal was to have a homogeneous tech zoo, with a small amount of different technologies, to keep the number of failure points as small as possible. Tarantool is a sort of technology that really allows us to achieve this, we were able to add different decentralized mechanics to our system because of that. It’s not only an in-memory database, but in reality the app server as well. In our case, the fact Centrifugo supports Tarantool broker was a big discovery – the integration went smoothly, and everything has been working great since then.","s":"Using Centrifugo in RabbitX","u":"/blog/2023/08/29/using-centrifugo-in-rabbitx","h":"","p":245},{"i":248,"t":"In our v5 release post, we announced the upcoming launch of Centrifugo PRO. We are happy to say that it was released soon after that, and at this point, we already have several customers of the PRO version. I think it's time to look at the current state of the PRO version and finally start talking more about its benefits. In this post, we will talk more about one of the coolest PRO features we have at this point: the push notifications API.","s":"Discovering Centrifugo PRO: push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"","p":247},{"i":250,"t":"When Centrifugo was originally created, its main goal was to help introduce real-time messaging features to existing systems, written in traditional frameworks which work on top of the worker/thread model. Serving many concurrent connections is a non-trivial task in general, and without native efficient concurrency support, it becomes mostly impossible without a shift in the technology stack. Integrating with Centrifugo makes it simple to introduce an efficient real-time layer, while keeping the existing application architecture. As time went on, Centrifugo got some unique features which now justify its usage even in conjunction with languages/frameworks with good concurrency support. Simply using Centrifugo for at-most-once PUB/SUB may already save a lot of development time. The task, which seems trivial at first glance, has a lot of challenges in practice: client SDKs with reconnect and channel multiplexing, scalability to many nodes, WebSocket fallbacks, etc. The combination of useful possibilities has made Centrifugo an attractive component for building enterprise-level applications. Let's be honest here - for pet projects, developers often prefer writing WebSocket communications themselves, and Centrifugo may be too heavy and an extra dependency. But in a corporate environment, the decision on which technology to use should take into account a lot of factors, like those we just mentioned above. Using a mature technology is often preferred to building from scratch and making all the mistakes along the way. With the PRO version, our goal is to provide even more value for established businesses when switching to Centrifugo. We want to solve tricky cases and simplify them for our customers; we want to step into related areas where we see we can provide sufficient value. One rule we try to follow for PRO features that extend Centrifugo’s scope is this: we are not trying to replicate something that already exists in other systems, but rather, we strive to improve upon it. We focus on solving practical issues that we observe, providing a unique value proposition for our customers. This post describes one such example — we will demonstrate our approach to push notifications, which is one the features of Centrifugo PRO.","s":"Centrifugo PRO goals","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#centrifugo-pro-goals","p":247},{"i":252,"t":"Why provide a push notifications API at all? Well, actually, real-time messages and push notifications are so close that many developers hardly see the difference before starting to work with both more closely. I’ve heard several stories where chat functionality on mobile devices was implemented using only native push notifications — without using a separate real-time transport like WebSocket while the app is in the foreground. While this is not a recommended approach due to the delivery properties of push notifications, it proves that real-time messages and push notifications are closely related concepts and sometimes may interchange with each other. When developers introduce WebSocket communication in an application, they often ask the question—what should I do next to deliver some important messages to a user who is currently not actively using the application? WebSockets are great when the app is in the foreground, but when the app goes to the background, the recommended approach is to close the WebSocket connection. This is important to save battery, and operating systems force the closing of connections after some time anyway. The delivery of important app data is then possible over push notifications. See a good overview of them on web.dev. Previously, Centrifugo positioned itself solely as a transport layer for real-time messages. In our FAQ, we emphasized this fact and suggested using separate software products to send push notifications. Now, with Centrifugo PRO, we provide this functionality to our customers. We have extended our server API with methods to manage and send push notifications. I promised to tell you why we believe our implementation is super cool. Let’s dive into the details.","s":"Why providing push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#why-providing-push-notifications-api","p":247},{"i":254,"t":"Push notifications are super handy, but there’s a bit to do to get them working right. Let's break it down! On the user's side (frontend)​ Request permission from the user to receive push notifications. Integrate with the platform-specific notification service (e.g., Apple Push Notification Service for iOS, Firebase Cloud Messaging for Android) to obtain the device token. Send the device token to the server for storage and future use. Integrate with the platform-specific notification handler to listen for incoming push notifications Handle incoming push notifications: display the notification content to the user, either as a banner, alert, or in-app message, depending on the user's preferences and the type of notification. Handle user actions on the notification, such as opening the app, dismissing the notification, or taking a specific action related to the notification content. On the server (backend)​ Store device tokens in a database when received from the client side Regularly clean up the database to remove stale or invalid device tokens. and handle scenarios where a device token becomes invalid or is revoked by the user, ensuring that no further notifications are sent to that device. Integrate with platform-specific notification services (e.g., APNS, FCM) to send notifications to devices. Handle errors or failures in sending notifications and implement retry mechanisms if necessary. Track the delivery status of each push notification sent out. Monitor the open rates, click-through rates, and other relevant metrics for the notifications. Use analytics to understand user behavior in response to notifications and refine the notification strategy based on insights gained. We believe that we were able to achieve a unique combination of design decisions which allows us to provide push notification support like no one else provides. Let’s dive into what makes our approach special!","s":"Push notifications API like no one provides","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-api-like-no-one-provides","p":247},{"i":256,"t":"When providing the push notification feature, other solutions like Pusher or Ably also offer their own SDKs for managing notifications on the client side. What we've learned, though, during the Centrifugo life cycle, is that creating and maintaining client SDKs for various environments (iOS, Android, Web, Flutter) is one of the hardest parts of the Centrifugo project. So the decision here was simple and natural: Centrifugo PRO does not introduce any client SDKs for push notifications on the client side. When integrating with Centrifugo, you can simply use the native SDKs provided by each platform. We bypass the complexities of SDK development and concentrate on server-side improvements. With this decision, we are not introducing any limitations to the client side. You get: Wealthy documentation and community support. Platforms like APNs provide comprehensive documentation, tutorials, and best practices, making the integration process smoother. Stability and reliability: native SDKs are rigorously tested and frequently updated by the platform providers. This ensures that they are stable, reliable, and free from critical bugs. Access to the latest features. As platform providers roll out new features or enhancements, native SDKs are usually the first to get updated. This ensures that your application can leverage the latest functionalities without waiting for SDKs to catch up. This approach was not possible with our real-time SDKs, as WebSocket communication is very low-level, and Centrifugo’s main goal was to provide some high-level features on top of it. However, with push notifications, proceeding without a custom SDK seems like a choice beneficial for everyone.","s":"Frontend decisions","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#frontend-decisions","p":247},{"i":258,"t":"The main work we did was on the server side. Let's go through the entire workflow of push notification delivery and describe what Centrifugo PRO provides for each step.","s":"Server implementation","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#server-implementation","p":247},{"i":260,"t":"Let's suppose you got the permission from the user and received the device push token. At this point you must save it to database for sending notifications later using this token. Centrifugo PRO provides API called device_register to do exactly this. At this point, we use PostgreSQL for storing tokens – which is a very popular SQL database. Probably we will add more storage backend options in the future. When calling Centrifugo device_register API you can provide user ID, list of topics to subscribe, platform from which the user came from (ios, android, web), also push notifications provider. To deliver push notifications to devices Centrifugo PRO integrates with the following push notification providers: fcm - Firebase Cloud Messaging (FCM) hms - Huawei Messaging Service (HMS) Push Kit apns - Apple Push Notification service (APNs) So we basically cover all the most popular platforms out of the box. After registering the device token, Centrifugo PRO returns a device_id to you. This device ID must be stored on the client device. As long as the frontend has this device_id, it can update the device's push token information from time to time to keep it current (by just calling device_register again, but with device_id attached). After saving the token, your backend can start sending push notifications to devices.","s":"How we keep tokens","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-keep-tokens","p":247},{"i":262,"t":"To send push notifications we provide another API called send_push_notification. You need to provide some filter in the API request to tell Centrifugo who you want to send notification. You also need to provide push notification payload. For example, using Centrifugo HTTP API: curl -X POST http://localhost:8000/api/send_push_notification \\-H \"Authorization: apikey \" \\-d @- <<'EOF'{ \"recipient\": { \"filter\": { \"topics\": [\"test\"] } }, \"notification\": { \"fcm\": { \"message\": { \"notification\": {\"title\": \"Hello\", \"body\": \"How are you?\"} } } }}EOF Here is another important decision we made: Centrifugo PRO allows you to specify raw JSON objects for each provider we support. In other words, we do not wrap the push notifications API for FCM, APNS, HMS - we give you a way to construct the entire push notification message. This means the Centrifugo push API supports all the fields of push notification payloads out-of-the-box, for all push providers. You can simply use the documentation of FCM, APNs, and send the constructed requests to Centrifugo. There is no need for us to update Centrifugo PRO in any way to support new fields added by providers to push APIs. When you send a push notification with a filter and push payload for each provider you want, it's queued by Centrifugo. We use Redis Streams for queuing and optionally a queue based on PostgreSQL (less efficient, but still robust enough). The fact that the notification is being queued means a very fast response time – so you can integrate with Centrifugo from within the hot paths of your application backend. You may additionally provide a push expiration time and a unique push identifier. If you have not provided a unique identifier, Centrifugo generates one for you and returns it in the response. The unique identifier may later be used to track push status in Centrifugo PRO's push notification analytics. We then have efficient workers which process the queue with minimal latency and send push notifications using batch requests for each provider - i.e., we do this in the most effective way possible. We conducted a benchmark of our worker system with FCM – and we can easily send several million pushes per minute. Another decision we made - Centrifugo PRO supports sending push notifications to a raw list of tokens. This makes it possible for our customers to use their own token storage. For example, such storage could already exist before you started using Centrifugo, or you might need a different storage/schema. In such cases, you can use Centrifugo just as an effective push sender server. Finally, Centrifugo PRO supports sending delayed push notification - to queue push for a later delivery, so for example you can send notification based on user time zone and let Centrifugo PRO send it when needed. Or you may send slightly delayed push notification together with real-time message and if client provided an ack to real-time message - cancel push notification.","s":"How we send notifications","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-send-notifications","p":247},{"i":264,"t":"FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that device can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM and HMS topics by introducing an additional API to manage device subscriptions to topics. Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs. Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. See user_topic_list and user_topic_update. As soon as user registers a device – it will be automatically subscribed to its own topics pre-created over the API. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with push notifications (with FCM in particular) – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call - Centrifugo PRO API gives you freedom here.","s":"Secure unified topics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#secure-unified-topics","p":247},{"i":266,"t":"Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Push providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API. This API allows updating ClickHouse table and add status for each push sent: delivered or interacted It's then possible to make queries to ClickHouse and build various analytical reports. Or use ClickHouse for real-time graphs - for example, from Grafana.","s":"Push analytics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-analytics","p":247},{"i":268,"t":"Finally, Centrifugo PRO provides a simple web UI for inspecting registered devices. It can simplify development, provide a way to look at live data, and send simple push notification alerts to users or topics.","s":"Push notifications UI","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-ui","p":247},{"i":270,"t":"We really believe in our push notifications and will be working hard to make them even better. The API we already have serves well to cover common push notification delivery use cases, but we won't stop here. Some areas for improvements are: functionality of built-in push notifications web UI, extending push analytics by providing user friendly UI for the insights about push delivery and engagement. The good thing is that we already have a ground for making this. Take a look at the documentation of Centrifugo PRO push notification API for more formal details and some things not mentioned here. Probably at the time you are reading this we already added something great to the API. Even though Centrifugo PRO is pretty new, it already has a lot of helpful features, and we have plans to add even more. You can see what’s coming up next on our Centrifugo PRO planned features board. We're excited to share more blog posts like this one in the future.","s":"Conclusion","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#conclusion","p":247},{"i":273,"t":"Attributions","s":"Attributions","u":"/docs/3/attributions","h":"","p":272},{"i":275,"t":"The following images have been used in the landing page. Icons made by Freepik https://www.flaticon.com/packs/web-development-19","s":"Landing Page Images","u":"/docs/3/attributions","h":"#landing-page-images","p":272},{"i":277,"t":"Centrifuge library Centrifugo is a server built on top of Centrifuge library for Go language. Due to its standalone language-agnostic nature Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a more tight integration of real-time server with application business logic you may consider using Centrifuge library to build something similar to Centrifugo but with customized behavior. Library README has detailed description, link to examples and introduction post. Many Centrifugo features should be re-implemented when using Centrifuge - like API layer, admin web UI, proxy etc (if you need those of course). And you need to write backend in Go language. But core functionality like a client-server protocol (all Centrifugo client connectors work with Centrifuge library based server) and Redis engine to scale come out of the box. tip Many things said in Centrifugo doc can be considered as extra documentation for Centrifuge library (for example part about infrastructure tuning or transport description). But not all of them.","s":"Centrifuge library","u":"/docs/3/ecosystem/centrifuge","h":"","p":276},{"i":279,"t":"Centrifugo is a scalable real-time messaging server in a language-agnostic way. In this tutorial we will integrate Centrifugo with NodeJS backend using a connect proxy feature of Centrifugo for user authentication and native session middleware of ExpressJS framework. Why would NodeJS developers want to integrate a project with Centrifugo? This is a good question especially since there are lots of various tools for real-time messaging available in NodeJS ecosystem. caution This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step. I found several points which could be a good motivation: Centrifugo scales well – we have a very optimized Redis Engine with client-side sharding and Redis Cluster support. We can also scale with KeyDB, Nats, or Tarantool. Centrifugo can scale to millions connections distributed over different server nodes. Centrifugo is pretty fast (written in Go) and can handle thousands of clients per node. Client protocol is optimized for thousands of messages per second. Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for real-time servers that scale to many nodes. Centrifugo works as a separate service – so can be a universal tool in developer's pocket, can migrate from one project to another, no matter what programming language or framework is used for a business logic. Having said this all – let's move to a tutorial itself.","s":"Centrifugo integration with NodeJS tutorial","u":"/blog/2021/10/18/integrating-with-nodejs","h":"","p":278},{"i":281,"t":"Not a super-cool app to be honest. Our goal here is to give a reader an idea how integration with Centrifugo could look like. There are many possible apps which could be built on top of this knowledge. The end result here will allow application user to authenticate and once authenticated – connect to Centrifugo. Centrifugo will proxy connection requests to NodeJS backend and native ExpressJS session middleware will be used for connection authentication. We will also send some periodical real-time messages to a user personal channel. The full source code of this tutorial located on Github. You can clone examples repo and run this demo by simply writing: docker compose up","s":"What we are building","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#what-we-are-building","p":278},{"i":283,"t":"Start new NodeJS app: npm init Install dependencies: npm install express express-session cookie-parser axios morgan Create index.js file. index.js const express = require('express');const cookieParser = require(\"cookie-parser\");const sessions = require('express-session');const morgan = require('morgan');const axios = require('axios');const app = express();const port = 3000;app.use(express.json());const oneDay = 1000 * 60 * 60 * 24;app.use(sessions({ secret: \"this_is_my_secret_key\", saveUninitialized: true, cookie: { maxAge: oneDay }, resave: false}));app.use(cookieParser());app.use(express.urlencoded({ extended: true }))app.use(express.json())app.use(express.static('static'));app.use(morgan('dev'));app.get('/', (req, res) => { if (req.session.userid) { res.sendFile('views/app.html', { root: __dirname }); } else res.sendFile('views/login.html', { root: __dirname })});app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`);}); Create login.html file in views folder: views/login.html

    Login (username: demo-user, password: demo-pass)

    Also create app.html file in views folder: views/app.html
    Make attention that we import centrifuge-js client here which abstracts away Centrifugo bidirectional WebSocket protocol. Let's write an HTTP handler for login form: index.js const myusername = 'demo-user'const mypassword = 'demo-pass'app.post('/login', (req, res) => { if (req.body.username == myusername && req.body.password == mypassword) { req.session.userid = req.body.username; res.redirect('/'); } else { res.send('Invalid username or password'); }}); In this example we use hardcoded username and password for out single user. Of course in real app you will have a database with user credentials. But since our goal is only show integration with Centrifugo – we are skipping these hard parts here. Also create a handler for a logout request: index.js app.get('/logout', (req, res) => { req.session.destroy(); res.redirect('/');}); Now if you run an app with node index.js you will see a login form using which you can authenticate. At this point this is a mostly convenient NodeJS application, let's add Centrifugo integration.","s":"Creating Express.js app","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#creating-expressjs-app","p":278},{"i":285,"t":"Run Centrifugo with config.json like this: config.json { \"token_hmac_secret_key\": \"secret\", \"admin\": true, \"admin_password\": \"password\", \"admin_secret\": \"my_admin_secret\", \"api_key\": \"my_api_key\", \"allowed_origins\": [ \"http://localhost:9000\" ], \"user_subscribe_to_personal\": true, \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_http_headers\": [ \"Cookie\" ]} I.e.: ./centrifugo -c config.json Create app.js file in static folder: static/app.js function drawText(text) { const div = document.createElement('div'); div.innerHTML = text; document.getElementById('log').appendChild(div);}const centrifuge = new Centrifuge('ws://localhost:9000/connection/websocket');centrifuge.on('connect', function () { drawText('Connected to Centrifugo');});centrifuge.on('disconnect', function () { drawText('Disconnected from Centrifugo');});centrifuge.on('publish', function (ctx) { drawText('Publication, time = ' + ctx.data.time);});centrifuge.connect();","s":"Starting Centrifugo","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#starting-centrifugo","p":278},{"i":287,"t":"Since we are going to use native session auth of ExpressJS we can't just connect from localhost:3000 (where our NodeJS app is served) to Centrifugo running on localhost:8000 – browser won't send a Cookie header to Centrifugo in this case. Due to this reason we need a reverse proxy which will terminate a traffic from frontend and proxy requests to NodeJS process or to Centrifugo depending on URL path. In this case both browser and NodeJS app will share the same origin – so Cookie will be sent to Centrifugo in WebSocket Upgrade request. tip Alternatively, we could also use JWT authentication of Centrifugo but that's a topic for another tutorial. Here we are using connect proxy feature for auth. Nginx config will look like this: server { listen 9000; server_name localhost; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /connection { proxy_pass http://localhost:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; }} Run Nginx and open http://localhost:9000. After authenticating in app you should see an attempt to connect to a WebSocket endpoint. But connection will fail since we need to implement connect proxy handler in NodeJS app. index.js app.post('/centrifugo/connect', (req, res) => { if (req.session.userid) { res.json({ result: { user: req.session.userid } }); } else res.json({ disconnect: { code: 1000, reason: \"unauthorized\", reconnect: false } });}); Restart NodeJS process and try opening an app again. Application should now successfully connect to Centrifugo.","s":"Adding Nginx","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#adding-nginx","p":278},{"i":289,"t":"Let's also periodically publish current server time to a client's personal channel. In Centrifugo configuration we set a user_subscribe_to_personal option which turns on automatic subscription to a personal channel for each connected user. We can use axios library and send publish API requests to Centrifugo periodically (according to API docs): index.js const centrifugoApiClient = axios.create({ baseURL: `http://centrifugo:8000/api`, headers: { Authorization: `apikey my_api_key`, 'Content-Type': 'application/json', },});setInterval(async () => { try { await centrifugoApiClient.post('', { method: 'publish', params: { channel: '#' + myusername, // construct personal channel name. data: { time: Math.floor(new Date().getTime() / 1000), }, }, }); } catch (e) { console.error(e.message); }}, 5000); After restarting NodeJS you should see periodical updates on application web page. You can also log in into Centrifugo admin web UI http://localhost:8000 using password password - and play with other available server API from within web interface.","s":"Send real-time messages","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#send-real-time-messages","p":278},{"i":291,"t":"While not being super useful this example can help understanding core concepts of Centrifugo - specifically connect proxy feature and server API. It's possible to use unidirectional Centrifugo transports instead of bidrectional WebSocket used here – in this case you can go without using centrifuge-js at all. This application scales perfectly if you need to handle more connections – thanks to Centrifugo builtin PUB/SUB engines. It's also possible to use client-side subscriptions, keep channel history cache, enable channel presence and more. All the power of Centrifugo is in your hands.","s":"Conclusion","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#conclusion","p":278},{"i":293,"t":"Securing user authentication and management can often be a challenging task when developing a modern application. As a result, many developers choose to delegate this responsibility to third-party identity providers, such as Okta, Auth0, or Keycloak. In this blog post, we'll go through the process of setting up Single Sign-On (SSO) authentication using Keycloak - popular and powerful identity provider. After setting up SSO we will create React application and connect to Centrifugo using access token generated by Keycloak for our test user:","s":"Setting up Keycloak SSO authentication flow and connecting to Centrifugo WebSocket","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"","p":292},{"i":295,"t":"The integraion is possible since Centrifugo works with standard JWT for authentication and additionally supports JSON Web Key specification. Here is a final source code.","s":"TLDR","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#tldr","p":292},{"i":297,"t":"First, run Keycloak using the following Docker command: docker run --rm -it -p 8080:8080 \\ -e KEYCLOAK_ADMIN=admin \\ -e KEYCLOAK_ADMIN_PASSWORD=admin \\ quay.io/keycloak/keycloak:21.0.1 start-dev After starting Keycloak, go to http://localhost:8080/admin and login. Then perform the following tasks: Create a new realm named myrealm. Create a new client named myclient. Set valid redirect URIs to http://localhost:5173/*, and web origins as http://localhost:5173. Create a user named myuser and set a password for it (in Credentials tab). See this guide for additional details and illustrations of the process. Make sure your created client is public (this is default) since we will request token directly from the web application.","s":"Keycloak","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#keycloak","p":292},{"i":299,"t":"Next, run Centrifugo using the following Docker command: docker run --rm -it -p 8000:8000 \\ -e CENTRIFUGO_ALLOWED_ORIGINS=\"http://localhost:5173\" \\ -e CENTRIFUGO_TOKEN_JWKS_PUBLIC_ENDPOINT=\"http://host.docker.internal:8080/realms/myrealm/protocol/openid-connect/certs\" \\ -e CENTRIFUGO_ALLOW_USER_LIMITED_CHANNELS=true \\ -e CENTRIFUGO_ADMIN=true \\ -e CENTRIFUGO_ADMIN_SECRET=secret \\ -e CENTRIFUGO_ADMIN_PASSWORD=admin \\ centrifugo/centrifugo:v4.1.2 centrifugo Some comments about environment variables used here: CENTRIFUGO_TOKEN_JWKS_PUBLIC_ENDPOINT allows tell Centrifugo to use JSON Web Key spec when validating tokens, we point to Keycloak's JWKS endpoint CENTRIFUGO_ALLOWED_ORIGINS is required since we will build Vite + React based app running on http://localhost:5173 CENTRIFUGO_ALLOW_USER_LIMITED_CHANNELS - not required to connect, but you will see in the source code that we additionally subscribe to a user personal channel CENTRIFUGO_ADMIN, CENTRIFUGO_ADMIN_SECRET, CENTRIFUGO_ADMIN_PASSWORD - to enable Centrifugo admin web UI Also note we are using host.docker.internal to access host port from inside the Docker network.","s":"Centrifugo","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#centrifugo","p":292},{"i":301,"t":"Now, let's create a new React app using very popular Vite tool: npm create vite@latest keycloak_sso_auth -- --template reactcd keycloak_sso_authnpm install Also, install the necessary additional packages for the React app: npm install --save @react-keycloak/web centrifuge keycloak-js And start the development server: npm run dev Navigate to http://localhost:5173/. You should see default Vite template working, we are going to modify it a bit. caution Use localhost, not 127.0.0.1 - since we used localhost for Keyloak and Centrifugo configurations above. Add the following into main.jsx: import React from 'react'import ReactDOM from 'react-dom/client'import { ReactKeycloakProvider } from '@react-keycloak/web'import App from './App'import './index.css'import Keycloak from \"keycloak-js\";const keycloakClient = new Keycloak({ url: \"http://localhost:8080\", realm: \"myrealm\", clientId: \"myclient\"})ReactDOM.createRoot(document.getElementById('root')).render( ,) Note that we configured Keycloak instance pointing it to our Keycloak server. We also use @react-keycloak/web package to wrap React app into ReactKeycloakProvider component. It simplifies working with Keycloak by providing some useful hooks - we are using this hook below. Our App component inside App.jsx may look like this: import React, { useState, useEffect } from 'react';import logo from './assets/centrifugo.svg'import { Centrifuge } from \"centrifuge\";import { useKeycloak } from '@react-keycloak/web'import './App.css'function App() { const { keycloak, initialized } = useKeycloak() if (!initialized) { return null; } return (

    SSO with Keycloak and Centrifugo

    {keycloak.authenticated ? (

    Logged in as {keycloak.tokenParsed?.preferred_username}

    ) : ( )}
    );}export default App This is actually enough for SSO flow to start working! You can click on login button and make sure that it's possible to use myuser credentials to log into the application. And log out after that. The only missing part is Centrifugo. We can initialize connection inside useEffect hook of App component: useEffect(() => { if (!initialized || !keycloak.authenticated) { return; } const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: keycloak.token, getToken: function () { return new Promise((resolve, reject) => { keycloak.updateToken(5).then(function () { resolve(keycloak.token); }).catch(function (err) { reject(err); keycloak.logout(); }); }) } }); centrifuge.connect(); return () => { centrifuge.disconnect(); };}, [keycloak, initialized]); The important thing here is how we configure tokens: we are using Keycloak client methods to set initial token and refresh the token when required. I also added some extra elements to the code to make it look a bit nicer. For example, we can listen to Centriffuge client state changes and show connection indicator on the page: function App() { const [connectionState, setConnectionState] = useState(\"disconnected\"); const stateToEmoji = { \"disconnected\": \"🔴\", \"connecting\": \"🟠\", \"connected\": \"🟢\" } ... useEffect(() => { ... centrifuge.on('state', function (ctx) { setConnectionState(ctx.newState); }) ... return ( ... {stateToEmoji[connectionState]} You can find more details about Centrifugo client SDK API and states in client SDK spec. If you look at source code on Github - you will also find an example of channel subscription to a user personal channel: function App() { ... const [publishedData, setPublishedData] = useState(\"\"); ... useEffect(() => { ... const userChannel = \"#\" + keycloak.tokenParsed?.sub; const sub = centrifuge.newSubscription(userChannel); sub.on(\"publication\", function (ctx) { setPublishedData(JSON.stringify(ctx.data)); }).subscribe(); ... You can now: test the SSO setup by logging into application making sure connection is successful try publishing a message into a user channel via the Centrifugo Web UI. The published message will appear on application screen in real-time. That's it! We have successfully set up Keycloak SSO authentication with Centrifugo and a React application. Again, source code is on Github.","s":"React app with Vite","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#react-app-with-vite","p":292},{"i":303,"t":"Framework integrations tip In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. But there are some community-driven projects that provide integration for more native experience or even some additional functionality on top of Centrifugo. laravel-centrifugo integration with Laravel framework laravel-centrifugo-broadcaster one more integration with Laravel framework to consider CentrifugoBundle integration with Symfony framework Django-instant integration with Django framework","s":"Framework integrations","u":"/docs/3/ecosystem/integrations","h":"","p":302},{"i":305,"t":"On this page","s":"Frequently Asked Questions","u":"/docs/3/faq","h":"","p":304},{"i":307,"t":"This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here. Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case. You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.","s":"How many connections can one Centrifugo instance handle?","u":"/docs/3/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":304},{"i":309,"t":"Depending on transport used and features enabled the amount of RAM required per each connection can vary. For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections. For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.","s":"Memory usage per connection?","u":"/docs/3/faq","h":"#memory-usage-per-connection","p":304},{"i":311,"t":"Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker. See engines and scalability considerations.","s":"Can Centrifugo scale horizontally?","u":"/docs/3/faq","h":"#can-centrifugo-scale-horizontally","p":304},{"i":313,"t":"See design overview","s":"Message delivery model","u":"/docs/3/faq","h":"#message-delivery-model","p":304},{"i":315,"t":"See design overview.","s":"Message order guarantees","u":"/docs/3/faq","h":"#message-order-guarantees","p":304},{"i":317,"t":"No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel. When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).","s":"Should I create channels explicitly?","u":"/docs/3/faq","h":"#should-i-create-channels-explicitly","p":304},{"i":319,"t":"Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application. But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc. One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.","s":"What about best practices with the number of channels?","u":"/docs/3/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":304},{"i":321,"t":"Currently, no. We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this: Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID) Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates. Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point. Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel. In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.","s":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/3/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":304},{"i":323,"t":"No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere. Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end). To summarize: if you are using binary Protobuf clients and binary payloads everywhere – you are fine. if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine. if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.","s":"Can I have both binary and JSON clients in one channel?","u":"/docs/3/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":304},{"i":325,"t":"While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel. You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.","s":"Online presence for chat apps - online status of your contacts","u":"/docs/3/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":304},{"i":327,"t":"The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.","s":"Centrifugo stops accepting new connections, why?","u":"/docs/3/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":304},{"i":329,"t":"Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.","s":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/3/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":304},{"i":331,"t":"Yes, Centrifugo works with HTTP/2. You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable: GODEBUG=\"http2server=0\" centrifugo -c config.json Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.","s":"Does Centrifugo work with HTTP/2?","u":"/docs/3/faq","h":"#does-centrifugo-work-with-http2","p":304},{"i":333,"t":"If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.","s":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/3/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":304},{"i":335,"t":"Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via GCM, or browsers over Web Push API. This is a goal for another software. But the reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.","s":"What if I need to send push notifications to mobile or web applications?","u":"/docs/3/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":304},{"i":337,"t":"You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.","s":"How can I know a message is delivered to a client?","u":"/docs/3/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":304},{"i":339,"t":"It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend. We suggest using one of the available approaches: When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API. Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network. Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself). Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.","s":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/3/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":304},{"i":341,"t":"There are several ways to achieve it: use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers you can use subscribe proxy feature to validate subscriptions, see chapter about proxy finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations","s":"How to create a secure channel for two users only (private chat case)?","u":"/docs/3/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":304},{"i":343,"t":"In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled. For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients. The same relates to other channel options.","s":"What's the best way to organize channel configuration?","u":"/docs/3/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":304},{"i":345,"t":"Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there. Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.","s":"Does Centrifugo support webhooks?","u":"/docs/3/faq","h":"#does-centrifugo-support-webhooks","p":304},{"i":347,"t":"Centrifugo does not support disconnect hooks at this point. First of all, there is no guarantee that the disconnect process will have a time to execute on the client-side (as the client can just switch off its device or simply lose internet connection). This means that a server may notice a connection loss with some delay (thanks to PING/PONG). Also, Centrifugo node can be unexpectedly killed. So there is a chance that disconnect event won't have a chance to be emitted to the backend. One more reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. To reduce the load during connect process Centrifugo has JWT authentication. Even if disconnect events were queued/rate-limited there could be situations when your app processes disconnect hook while user already reconnected and connect event processed. This is a racy situation which you will need to handle somehow (possibly based on unique client ID attached to each connection). If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend from the client-side and update user status somewhere on the backend (use Redis maybe). This is a pretty robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo refresh proxy for the task of periodic backend pinging. In this case you will notice that user (or particular client) left app with some delay – this may be a acceptable trade-off in many cases. Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together.","s":"Why Centrifugo does not have disconnect hooks?","u":"/docs/3/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":304},{"i":349,"t":"No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.","s":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/3/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":304},{"i":351,"t":"Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel). There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers. You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs. The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.","s":"How scalable is the online presence and join/leave features?","u":"/docs/3/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":304},{"i":353,"t":"Ask in our community rooms:","s":"I have not found an answer to my question here:","u":"/docs/3/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":304},{"i":355,"t":"flow_diagrams For swimlines.io: Client <- App Backend: JWTnote:The backend generates JWT for a user and passes it to the client side.Client -> Centrifugo: Client connects to Centrifugo with JWT...: {fas-spinner} Persistent connection establishedClient -> Centrifugo: Client issues channel subscribe requestsCentrifugo -->> Client: Client receives real-time updates from channels Client -> Centrifugo: Connect requestnote:Client connects to Centrifugo without JWT.Centrifugo -> App backend: Sends request further (via HTTP or GRPC)note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.App backend -> Centrifugo: Connect replyCentrifugo -> Client: Connect Reply...: {fas-spinner} Persistent connection established Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers. Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers.","s":"flow_diagrams","u":"/docs/3/flow_diagrams","h":"","p":354},{"i":357,"t":"On this page","s":"Design overview","u":"/docs/3/getting-started/design","h":"","p":356},{"i":359,"t":"Originally Centrifugo was built with the unidirectional flow as the main approach. Though Centrifugo itself used a bidirectional protocol between a client and a server to allow client dynamically create subscriptions, Centrifugo did not allow using it for sending data from client to server. With this approach publications travel only from server to a client. All requests that generate new data first go to the application backend (for example over AJAX call of backend API). The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event from a backend side to Centrifugo API. This is a pretty natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is decoupled from the application in this case. During Centrifugo v2 life cycle this paradigm evolved a bit. It's now possible to send RPC requests from client to Centrifugo and the request will be then proxied to the application backend. Also, connection attempts and publications to channels can now be proxied. So bidirectional connection between client and Centrifugo is now available for utilizing by developers in both directions. For example, here is how publish diagram could look like when using publish request proxy feature: So at the moment, the number of possible integration ways increased.","s":"Idiomatic usage","u":"/docs/3/getting-started/design","h":"#idiomatic-usage","p":356},{"i":361,"t":"Idiomatic Centrifugo usage requires having the main application database from which initial and actual state can be loaded at any point in time. While Centrifugo has channel history, it has been mostly designed to reduce the load on the main application database when all users reconnect at once (in case of load balancer configuration reload, Centrifugo restart, temporary network problems, etc). This allows to radically reduce the load on the application main database during reconnect storm. Since such disconnects are usually pretty short in time having a reasonably small number of messages cached in history is sufficient. The addition of history iteration API shifts possible use cases a bit. Calling history chunk by chunk allows keeping larger number of publications per channel. But depending on Engine used and configuration of the underlying storage history stream persistence characteristics can vary. For example, with Memory Engine history will be lost upon Centrifugo restart. With Redis or Tarantool engines history will survive Centrifugo restarts but depending on a storage configuration it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis RDB and AOF, configure replication for storage high-availability, use Redis Cluster or maybe synchronous replication with Tarantool. Centrifugo provides ways to distinguish whether the missed messages can't be restored from Centrifugo history upon recovery so a client should restore state from the main application database. So Centrifugo message history can be used as a complementary way to restore messages and thus reduce a load on the main application database most of the time.","s":"Message history considerations","u":"/docs/3/getting-started/design","h":"#message-history-considerations","p":356},{"i":363,"t":"By default, the message delivery model of Centrifugo is at most once. With history and the position/recovery features enabled it's possible to achieve at least once guarantee within history retention time and size. After abnormal disconnect clients have an option to recover missed messages from the publication stream cache that Centrifugo maintains. Without the positioning or recovery features enabled a message sent to Centrifugo can be theoretically lost while moving towards clients. Centrifugo tries to do its best to prevent message loss on a way to online clients, but the application should tolerate a loss. As noted Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. Also, it compensates at most once delivery of broker (Redis, Tarantool) PUB/SUB by using additional publication offset checks and periodic offset synchronization. At this moment Centrifugo message recovery is designed for a short-term disconnect period (think no more than one hour for a typical chat application, but this can vary). After this period (which can be configured per channel basis) Centrifugo removes messages from the channel history cache. In this case, Centrifugo may tell the client that some messages can not be recovered, so your application state should be loaded from the main database.","s":"Message delivery model","u":"/docs/3/getting-started/design","h":"#message-delivery-model","p":356},{"i":365,"t":"Message order in channels is guaranteed to be the same while you publish messages into channel one after another or publish them in one request. If you do parallel publications into the same channel then Centrifugo can't guarantee message order since those may be processed concurrently by Centrifugo.","s":"Message order guarantees","u":"/docs/3/getting-started/design","h":"#message-order-guarantees","p":356},{"i":367,"t":"It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely only on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in AJAX call response and render it. This way user that posts a comment will think that everything works just fine. Be careful to not draw comments twice in this case - think about idempotent identifiers for your entities.","s":"Graceful degradation","u":"/docs/3/getting-started/design","h":"#graceful-degradation","p":356},{"i":369,"t":"Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to the network failures and unexpected shut down of Centrifugo node, there are chances that clients can be presented in a presence up to one minute more (until presence entry expiration). Also, channel presence does not scale well for channels with lots of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows the response size becomes larger. In some cases, presence_stats API call can be sufficient to avoid receiving the entire presence state.","s":"Online presence considerations","u":"/docs/3/getting-started/design","h":"#online-presence-considerations","p":356},{"i":371,"t":"Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with Nats broker. See engines. All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications. But, if you approach broker resource limits (CPU or memory) then it's possible: Use Centrifugo consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool) Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters) Nats broker should scale well itself in cluster setup All brokers can be set up in highly available way so there won't be a single point of failure. All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact and the fact that Centrifugo provides hooks for the application to understand history loss makes the process of resharding mostly automatic. As soon as you need to add additional broker shard (when using client-side sharding) you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently part of the data will stay on the same broker nodes. Applications should handle cases that channel data moved to another shard and restore a state from the main application database when needed.","s":"Scalability considerations","u":"/docs/3/getting-started/design","h":"#scalability-considerations","p":356},{"i":373,"t":"Today we are excited to announce the next generation of Centrifugo – Centrifugo v4. The release takes Centrifugo to the next level in terms of client protocol performance, WebSocket fallback simplicity, SDK ecosystem and channel security model. It also comes with a couple of cutting-edge technologies to experiment with such as HTTP/3 and WebTransport. About Centrifugo If you've never heard of Centrifugo before, it's an open-source scalable real-time messaging server written in Go language. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, GRPC, SockJS). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server. Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer. Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.","s":"Centrifugo v4 released – a little revolution","u":"/blog/2022/07/19/centrifugo-v4-released","h":"","p":372},{"i":375,"t":"Let's start from looking back a bit. Centrifugo v3 was released last year. It had a great list of improvements – like unidirectional transports support (EventSource, HTTP-streaming and GRPC), GRPC transport for proxy, history iteration API, faster JSON protocol, super-fast but experimental Tarantool engine implementation, and others. During the Centrifugo v3 lifecycle we added even more JSON protocol optimizations and introduced a granular proxy mode. Experimental Tarantool engine has also evolved a bit. But Centrifugo v3 did not contain anything... let's say revolutional. Revolutional for Centrifugo itself, community, or even the entire field of open-source real-time messaging. With this release, we feel that we bring innovation to the ecosystem. Now let's talk about it and introduce all the major things of the brand new v4 release.","s":"Centrifugo v3 flashbacks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#centrifugo-v3-flashbacks","p":372},{"i":377,"t":"The most challenging part of Centrifugo project is not a server itself. Client SDKs are the hardest part of the ecosystem. We try to time additional improvements to the SDKs with each major release of the server. But this time the SDKs are the centerpiece of the v4 release. Centrifugo uses bidirectional asynchronous protocol between client and server. On top of this protocol SDK provides a request-response over an asynchronous connection, reconnection logic, subscription management and multiplexing, timeout and error handling, ping-pong, token refresh, etc. Some of these things are not that trivial to implement. And all this should be implemented in different programming languages. As you may know, we have official real-time SDKs in Javascript, Dart, Swift, Java and Go. While implementing the same protocol and same functions, all SDKs behaved slightly differently. That was the result of the missing SDK specification. Without a strict SDK spec, it was hard to document things, hard to explain the exact details of the real-time SDK behavior. What we did earlier in the Centrifugo documentation – was pointing users to specific SDK Github repo to look for behaviour details. The coolest thing about Centrifugo v4 is the next generation SDK API. We now have a client SDK API specification. It's a source of truth for SDKs behavior which try to follow the spec closely. The new SDK API is the result of several iterations and reflections on possible states, transitions, token refresh mechanism, etc. Users in our Telegram group may remember how it all started: And after several iterations these prototypes turned into working mechanisms with well-defined behaviour: A few things that have been revised from the ground up: Client states, transitions, events Subscription states, transitions, events Connection and subscription token refresh behavior Ping-pong behavior (see details below) Resubscribe logic (SDKs can now resubscribe with backoff) Error handling Unified backoff behavior (based on full jitter technique) We now also have a separation between temporary and non-temporary protocol errors – this allows us to handle subscription internal server errors on the SDK level, making subscriptions more resilient, with automatic resubscriptions, and to ensure individual subscription failures do not affect the entire connection. The mechanics described in the client SDK API specification are now implemented in all of our official SDKs. The SDKs now support all major client protocol features that currently exist. We believe this is a big step forward for the Centrifugo ecosystem and community.","s":"Unified client SDK API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#unified-client-sdk-api","p":372},{"i":379,"t":"WebSocket is supported almost everywhere these days. But there is a case that we believe is the last one preventing users to connect over WebSocket - corporate proxies. With the root certificate installed on employee computer machines, these proxies can block WebSocket traffic, even if it's wrapped in a TLS layer. That's really annoying, and often developers choose to not support clients connecting from such \"broken\" environments at all. Prior to v4, Centrifugo users could use the SockJS polyfill library to fill this gap. SockJS is great software – stable and field proven. It is still used by some huge real-time messaging players out there to polyfill the WebSocket transport. But SockJS is an extra frontend dependency with a bunch of legacy transports, and the future of it is unknown. SockJS comes with a notable overhead – it's an aditional protocol wrapper, consumes more memory per connection on a server (at least when using SockJS-Go library – the only choice for implementing SockJS server in Go language these days). When using SockJS, Centrifugo users were losing the ability to use our main pure WebSocket transport because SockJS uses its own WebSocket implementation on a server side. SockJS does not support binary data transfer – only JSON format can be used with it. As you know, our main WebSocket transport works fine with binary in case of using Protobuf protocol format. So with SockJS we don't have fallback for WebSocket with a binary data transfer. And finally, if you want to use SockJS with a distributed backend, you must enable sticky session support on the load-balancer level. This way you can point requests from the client to the server to the correct server node – the one which maintains a persistent unidirectional HTTP connection. We danced around the idea of replacing SockJS for a long time. But only now we are ready to provide our alternative to it – meet Centrifugo own bidirectional emulation layer. It's based on two additional transports: HTTP-streaming (using modern browser ReadableStream API in JavaScript, supports both binary Protobuf and JSON transfer) Eventsource (Server-Sent Events, SSE) – while a bit older choice and works with JSON only EventSource transport is loved by many developers and can provide fallback in slightly older browsers which don't have ReadableStream, so we implemented bidirectional emulation with it too. So when the fallback is used, you always have a real-time, persistent connection in server -> to -> client direction. Requests in client -> to -> server direction are regular HTTP – similar to how SockJS works. But our bidirectional emulation layer does not require sticky sessions – Centrifugo can proxy client-to-server requests to the correct node in the cluster. Having sticky sessions is an optimization for Centrifugo bidirectional emulation layer, not a requirement. We believe that this is a game changer for our users – no need to bother about proper load balancing, especially since in most cases 95% or even more users will be able to connect using the WebSocket transport. Here is a simplified diagram of how it works: The bidirectional emulation layer is only supported by the Javascript SDK (centrifuge-js) – as we think fallbacks mostly make sense for browsers. If we find use cases where other SDKs can benefit from HTTP based transport – we can expand on them later. Let's look at example of using this feature from the Javascript side. To use fallbacks, all you need to do is to set up a list of desired transports with endpoints: const transports = [ { transport: 'websocket', endpoint: 'wss://your_centrifugo.com/connection/websocket' }, { transport: 'http_stream', endpoint: 'https://your_centrifugo.com/connection/http_stream' }, { transport: 'sse', endpoint: 'https://your_centrifugo.com/connection/sse' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() note We are using explicit transport endpoints in the above example due to the fact that transport endpoints can be configured separately in Centrifugo – there is no single entry point for all transports. Like the one in Socket.IO or SockJS when developer can only point client to the base address. In Centrifugo case, we are requesting an explicit transport/endpoint configuration from the SDK user. By the way, a few advantages of HTTP-based transport over WebSocket: Sessions can be automatically multiplexed within a single connection by the browser when the server is running over HTTP/2, while with WebSocket browsers open a separate connection in each browser tab Better compression support (may be enabled on load balancer level) WebSocket requires special configuration in some load balancers to get started (ex. Nginx) SockJS is still supported by Centrifugo and centrifuge-js, but it's now DEPRECATED.","s":"Modern WebSocket emulation in Javascript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#modern-websocket-emulation-in-javascript","p":372},{"i":381,"t":"Not only the API of client SDK has changed, but also the format of Centrifugo protocol messages. New format is more human-readable (in JSON case, of course), has a more compact ping message size (more on that below). The client protocol is now one-shot encode/decode compatible. Previously, Centrifugo protocol had a layered structure and we had to encode some messages before appending them to the top-level message. Or decode two or three times to unwrap the message envelope. To achieve good performance when encoding and decoding client protocol messages, Centrifugo had to use various optimization techniques – like buffer memory pools, byte slice memory pools. By restructuring the message format, we were able to avoid layering, which allowed us to slightly increase the performance of encoding/decoding without additional optimization tricks. We also simplified the client protocol documentation overview a bit.","s":"No layering in client protocol","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#no-layering-in-client-protocol","p":372},{"i":383,"t":"In many cases in practice (when dealing with persistent connections like WebSocket), pings and pongs are the most dominant types of messages passed between client and server. Your application may have many concurrent connections, but only a few of them receive the useful payload. But at the same time, we still need to send pings and respond with pongs. Thus, optimizing the ping-pong process can significantly reduce server resource usage. One optimization comes from the revised PING-PONG behaviour. Previous versions of Centrifugo and SDKs sent ping/pong in both \"client->to->server\" and \"server->to->client\" directions (for WebSocket transport). This allowed finding non-active connections on both client and server sides. In Centrifugo v4 we only send pings from a server to a client and expect pong from a client. On the client-side, we have a timer which fires if there hasn't been a ping from the server within the configured time, so we still have a way to detect closed connections. Sending pings only in one direction results in 2 times less ping-pong messages - and this should be really noticable for Centrifugo installations with thousands of concurrent connections. In our experiments with 10k connections, server CPU usage was reduced by 30% compared to Centrifugo v3. Pings and pongs are application-level messages. Ping is just an empty asynchronous reply – for example in JSON case it's a 2-byte message: {}. Pong is an empty command – also, {} in JSON case. Having application-level pings from the server also allows unifying the PING format for all unidirectional transports. Another improvement is that Centrifugo now randomizes the time it sends first ping to the client (but no longer than the configured ping interval). This allows to spread ping-pongs in time, providing a smoother CPU profile, especially after a massive reconnect scenario.","s":"Redesigned PING-PONG","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#redesigned-ping-pong","p":372},{"i":385,"t":"Data security and privacy are more important than ever in today's world. And as Centrifugo becomes more popular and widely used, the need to be secure by default only increases. Previously, by default, clients could subcribe to all channels in a namespace (except private channels, which are now revised – see details below). It was possible to use \"protected\": true option to make namespace protected, but we are not sure if everyone did that. This is extra configuration and additional knowledge on how Centrifugo works. Also, a common confusion we ran into: if server-side subscriptions were dictated by a connection JWT, many users would expect client-side subscriptions to those channels to not work. But without the protected option enabled, this was not the case. In Centrifugo v4, by default, it is not possible to subscribe to a channel in a namespace. The namespace must be configured to allow subscriptions from clients, or token authorization must be used. There are a bunch of new namespace options to tune the namespace behavior. Also the ability to provide a regular expression for channels in the namespace. The new permission-related channel option names better reflect the purpose of the option. For example, compare \"publish\": true and \"allow_publish_for_client\": true. The second one is more readable and provides a better understanding of the effect once turned on. Centrifugo is now more strict when checking channel name. Only ASCII symbols allowed – it was already mentioned in docs before, but wasn't actually enforced. Now we are fixing this. We understand that these changes will make running Centrifugo more of a challenge, especially when all you want is a public access to all the channels without worrying too much about permissions. It's still possible to achieve, but now the intent must be expicitly expressed in the config. Check out the updated documentation about channels and namespaces. Our v4 migration guide contains an automatic converter for channel namespace options.","s":"Secure by default channel namespaces","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#secure-by-default-channel-namespaces","p":372},{"i":387,"t":"A private channel is a special channel starting with $ that could not be subscribed to without a subscription JWT. Prior to v4, having a known prefix allowed us to distinguish between public channels and private channels. But since namespaces are now non-public by default, this distinction is not really important. This means 2 things: it's now possible to subscribe to any channel by having a valid subscription JWT (not just those that start with $) channels beginning with $ can only be subscribed with a subscription JWT, even if they belong to a namespace where subscriptions allowed for all clients. This is for security compatibility between v3 and v4. Another notable change in a subscription JWT – client claim is now DEPRECATED. There is no need to put it in the subscription token anymore. Centrifugo supports it only for backwards compatibility, but it will be completely removed in the future releases. The reason we're removing client claim is actually interesting. Due to the fact that client claim was a required part of the subscription JWT applications could run into a situation where during the massive reconnect scenario (say, million connections reconnect) many requests for new subscription tokens can be generated because the subscription token must contain the client ID generated by Centrifugo for the new connection. That could make it unusually hard for the application backend to handle the load. With a connection JWT we had no such problem – as connections could simply reuse the previous token to reconnect to Centrifugo. Now the subscription token behaves just like the connection token, so we get a scalable solution for token-based subscriptions as well. What's more, this change paved the way for another big improvement...","s":"Private channel concept revised","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#private-channel-concept-revised","p":372},{"i":389,"t":"The improvement we just mentioned is called optimistic subscriptions. If any of you are familiar with the QUIC protocol, then optimistic subscriptions are somewhat similar to the 0-RTT feature in QUIC. The idea is simple – we can include subscription commands to the first frame sent to the server. Previously, we sent subscriptions only after receiving a successful Connect Reply to a Connect Command from a server. But with the new changes in token behaviour, it seems so logical to put subscribe commands within the initial connect frame. Especially since Centrifugo protocol always supported batching of commands. Even token-based subscriptions can now be included into the initial frame during reconnect process, since the previous token can be reused now. The benefit is awesome – in most scenarios, we save one RTT of latency when connecting to Centrifugo and subscribing to channels (which is actually the most common way to use Centrifugo). While not visible on localhost, this is pretty important in real-life. And this is less syscalls for the server after all, resulting in less CPU usage. Optimistic subscriptions are also great for bidirectional emulation with HTTP, as they avoid the long path of proxying a request to the correct Centrifugo node when connecting. Optimistic subscriptions are now only part of centrifuge-js. At some point, we plan to roll out this important optimization to all other client SDKs.","s":"Optimistic subscriptions","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#optimistic-subscriptions","p":372},{"i":391,"t":"The channel capabilities feature is introduced as part of Centrifugo PRO. Initially, we aimed to make it a part of the OSS version. But the lack of feedback on this feature made us nervous it's really needed. So adding it to PRO, where we still have room to evaluate the idea, seemed like the safer decision at the moment. Centrifugo allows configuring channel permissions on a per-namespace level. When creating a new real-time feature, it is recommended to create a new namespace for it and configure permissions. But to achieve a better channel permission control within a namespace the Channel capabilities can be used now. The channel capability feature provides a possibility to set capabilities on an individual connection basis, or an individual channel subscription basis. For example, in a connection JWT developers can set sth like: { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] } ]} And this tells Centrifugo that the connection is able to subscribe on channels news or user_42 using client-side subscriptionsat any time while the connection is active. Centrifugo also supports wildcard and regex channel matches. Subscription JWT can provide capabilities for the channel too, so permissions may be controlled on an individual subscription basis, ex. the ability to publish and call history API may be expressed with allow claim in subscription JWT: { \"allow\": [\"pub\", \"hst\"]} Read more about this mechanism in Channel capabilities chapter.","s":"Channel capabilities","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#channel-capabilities","p":372},{"i":393,"t":"Another addition to Centrifugo PRO is the improved connection API. Previously, we could only return all connections from a specific user. The API now supports filtering all connections: by user ID, by subscribed channel, by additional meta information attached to the connection. The filtering works by user ID or with a help of CEL expressions (Common Expression Language). CEL expressions provide a developer-friendly, fast and secure (as they are not Turing-complete) way to evaluate some conditions. They are used in some Google services (ex. Firebase), in Envoy RBAC configuration, etc. If you've never seen it before – take a look, cool project. We are also evaluating how to use CEL expressions for a dynamic and efficient channel permission checks, but that's an early story. The connections API call result contains more useful information: a list of client's active channels, information about the tokens used to connect and subscribe, meta information attached to the connection.","s":"Better connections API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#better-connections-api","p":372},{"i":395,"t":"It's no secret that centrifuge-js is the most popular SDK in the Centrifugo ecosystem. We put additional love to it – and centrifuge-js is now fully written in Typescript ❤️ This was a long awaited improvement, and it finally happened! The entire public API is strictly typed. The cool thing is that even EventEmitter events and event handlers are the subject to type checks - this should drastically simplify and speedup development and also help to reduce error possibility.","s":"Javascript client moved to TypeScript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#javascript-client-moved-to-typescript","p":372},{"i":397,"t":"Centrifugo v4 has an experimental HTTP/3 support. Once TLS is enabled and \"http3\": true option is set all the endpoints on an external port will be served by a HTTP/3 server based on lucas-clemente/quic-go implementation. It's worth noting that WebSocket will still use HTTP/1.1 for its Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and EventSource should work just fine with HTTP/3. HTTP/3 does not currently work with our ACME autocert TLS - i.e. you need to explicitly provide paths to cert and key files as described here.","s":"Experimenting with HTTP/3","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-http3","p":372},{"i":399,"t":"Having HTTP/3 on board allowed us to make one more thing. Some of you may remember the post Experimenting with QUIC and WebTransport published in our blog before. We danced around the idea to add WebTransport to Centrifugo since then. WebTransport IETF specification is still a draft, it changed a lot since our first blog post about it. But WebTransport object is already part of Chrome (since v97) and things seem to be very close to the release. So we added experimental WebTransport support to Centrifugo v4. This is made possible with the help of the marten-seemann/webtransport-go library. To use WebTransport you need to run HTTP/3 experimental server and enable WebTransport endpoint with \"webtransport\": true option in the configuration. Then you can connect to that endpoint using centrifuge-js. For example, let's enable WebTransport and use WebSocket as a fallback option: const transports = [ { transport: 'webtransport', endpoint: 'https://your_centrifugo.com/connection/webtransport' }, { transport: 'websocket', endpoint: 'wss://your_centrifugo.com/connection/websocket' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in HTTP/3 and specifically WebTransport non-TLS communication is simply not supported by the specification. In Centrifugo case, we utilize the bidirectional reliable stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication formats are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional. Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification we can not recommended it for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket, now we are more confident that Centrifugo will be able to provide a proper support of it.","s":"Experimenting with WebTransport","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-webtransport","p":372},{"i":401,"t":"The migration guide contains steps to upgrade your Centrifugo from version 3 to version 4. While there are many changes in the v4 release, it should be possible to migrate to Centrifugo v4 without changing the code on the client side at all. And then, after updating the server, gradually update the client-side to the latest version of the stack.","s":"Migration guide","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#migration-guide","p":372},{"i":403,"t":"To sum it up, here are some benefits of Centrifugo v4: unified experience thoughout application frontend environments an optimized protocol which is generally faster, more compact and human-readable in JSON case, provides more resilient behavior for subscriptions revised channel namespace security model, more granular permission control more efficient and flexible use of subscription tokens better initial latency – thanks to optimistic subscriptions and the ability to pre-create subscription tokens (as the client claim not needed anymore) the ability to use more efficient WebSocket bidirectional emulation in the browser without having to worry about sticky sessions, unless you want to optimize the real-time infrastructure That's it. We now begin the era of v4 and it is going to be awesome, no doubt.","s":"Conclusion","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#conclusion","p":372},{"i":405,"t":"The release contains many changes that strongly affect developing with Centrifugo. And of course you may have some questions or issues regarding new or changed concepts. Join our communities in Telegram (the most active) and Discord: Enjoy Centrifugo v4, and let the Centrifugal force be with you.","s":"Join community","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#join-community","p":372},{"i":407,"t":"The refactoring of client SDKs and introducing unified behavior based on the common spec was the hardest part of Centrifugo v4 release. Many thanks to Vitaly Puzrin (who is the author of several popular open-source libraries such as markdown-it, fontello, and others). We had a series of super productive sessions with him on client SDK API design. Some great ideas emerged from these sessions and the result seems like a huge step forward for Centrifugal projects. Also, thanks to Anton Silischev who helped a lot with WebTransport prototypes earlier this year, so we could quickly adopt WebTransport for v4. tip As some of you know, Centrifugo server is built on top of the Centrifuge library for Go. Most of the optimizations and improvements described here are now also part of Centrifuge library. With its new unified SDK behavior and bidirectional emulation layer, it seems a solid alternative to Socket.IO in the Go language ecosystem. In some cases, Centrifuge library can be a more flexible solution than Centrifugo, since Centrifugo (as a standalone server) dictates some mechanics and rules that must be followed. In the case of Centrifugo, the business logic must live on the application backend side, with Centrifuge library it can be kept closer to the real-time transport layer. Attributions This post used images from freepik.com: background by liuzishan. Also image by kenshinstock.","s":"Special thanks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#special-thanks","p":372},{"i":409,"t":"On this page","s":"Main highlights","u":"/docs/3/getting-started/highlights","h":"","p":408},{"i":411,"t":"Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.). It works as a standalone service with well-defined communication contracts. It fits very well in both monolithic and microservice architecture. Application developers should not change backend philosophy at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.","s":"Simple integration","u":"/docs/3/getting-started/highlights","h":"#simple-integration","p":408},{"i":413,"t":"Centrifugo is pretty fast. It's written in Go language, uses fast and battle-tested open-source libraries internally, has some internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTT with broker, connection hub sharding to avoid contention, JSON and Protobuf encoding speedups over code generation and other. See a Million WebSocket with Centrifugo post in our blog to see some real-world numbers.","s":"Great performance","u":"/docs/3/getting-started/highlights","h":"#great-performance","p":408},{"i":415,"t":"Centrifugo scales to many machines with a help of PUB/SUB brokers. The main PUB/SUB engine Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck also. There are other options to scale: KeyDB, Nats, Tarantool.","s":"Built-in scalability","u":"/docs/3/getting-started/highlights","h":"#built-in-scalability","p":408},{"i":417,"t":"Centrifugo supports JSON and binary Protobuf protocol for client-server communication. The bidirectional protocol is defined by strict schema and several ready-to-use connectors wrap this protocol, handle asynchronous message passing, timeouts, reconnect, and various Centrifugo client API features.","s":"Strict client protocol","u":"/docs/3/getting-started/highlights","h":"#strict-client-protocol","p":408},{"i":419,"t":"The main transport in Centrifugo is WebSocket. It's a bidirectional transport on top of TCP with low overhead. For browsers that do not support WebSocket Centrifugo provides SockJS support. Centrifugo v3 also introduced support for unidirectional transports for real-time updates: like SSE (EventSource), HTTP streaming, GRPC unidirectional stream. Using unidirectional transport is sufficient for many real-time applications and does not require using custom client connectors – just native APIs or GRPC-generated code.","s":"Variety of real-time transports","u":"/docs/3/getting-started/highlights","h":"#variety-of-real-time-transports","p":408},{"i":421,"t":"Centrifugo can authenticate connections using JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon connection attempt. It's possible to proxy original request headers or request metadata (in the case of GRPC connection). It supports the JWK specification.","s":"Flexible authentication","u":"/docs/3/getting-started/highlights","h":"#flexible-authentication","p":408},{"i":423,"t":"Connections can expire, developers can choose a way to handle connection refresh – using client-side refresh workflow, or server-side call from Centrifugo to the application backend.","s":"Connection management","u":"/docs/3/getting-started/highlights","h":"#connection-management","p":408},{"i":425,"t":"Centrifugo is a PUB/SUB server – users subscribe to channels to receive real-time updates. Message sent to a channel will be delivered to all active subscribers. There are several different types of channels to deal with permissions.","s":"Channel (room) concept","u":"/docs/3/getting-started/highlights","h":"#channel-room-concept","p":408},{"i":427,"t":"Centrifugo is unique in terms of the fact that it supports both client-side and server-side channel subscriptions.","s":"Different types of subscriptions","u":"/docs/3/getting-started/highlights","h":"#different-types-of-subscriptions","p":408},{"i":429,"t":"You can fully utilize bidirectional persistent connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing external traffic and, in most cases, provides better latency characteristics.","s":"RPC over bidirectional connection","u":"/docs/3/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":408},{"i":431,"t":"It's possible to turn on an online presence feature for channels so you will have information about active channel subscribers. Channel join and leave events (when a user subscribes/unsubscribes) can also be sent.","s":"Online presence information","u":"/docs/3/getting-started/highlights","h":"#online-presence-information","p":408},{"i":433,"t":"Optionally Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With a channel history, Centrifugo can help to survive the mass reconnect scenario. Clients can automatically recover missed messages from a cache – thus reducing the load on your primary database. It's also possible to manually iterate over a stream from a client or a server-side.","s":"Message history in channels","u":"/docs/3/getting-started/highlights","h":"#message-history-in-channels","p":408},{"i":435,"t":"Built-in administrative web UI allows publishing messages to channels, looking at Centrifugo cluster state, monitoring stats, etc.","s":"Embedded admin web UI","u":"/docs/3/getting-started/highlights","h":"#embedded-admin-web-ui","p":408},{"i":437,"t":"Centrifugo works on Linux, macOS, and Windows.","s":"Cross-platform","u":"/docs/3/getting-started/highlights","h":"#cross-platform","p":408},{"i":439,"t":"Centrifugo supports various deploy ways: in Docker, using prepared RPM or DEB packages, via Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, has an official Grafana dashboard for Prometheus data source.","s":"Ready to deploy","u":"/docs/3/getting-started/highlights","h":"#ready-to-deploy","p":408},{"i":441,"t":"Centrifugo stands on top of open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All client connectors are also MIT-licensed.","s":"Open-source","u":"/docs/3/getting-started/highlights","h":"#open-source","p":408},{"i":443,"t":"Centrifugo PRO extends Centrifugo with several unique features which can give interesting advantages for business adopters. With Centrifugo PRO it's possible to trace specific user or specific channel events in real-time. Centrifugo PRO integrates with ClickHouse for real-time connection analytics. This all may help with understanding client behavior, inspect and analyze an application on a very granular level. Centrifugo PRO offers even more extensions that tend to be useful in practice. This includes user active status and throttling features. Active status is useful to build messenger-like applications where you want to show online indicators of users based on last activity time, throttling can help you limit the number of operations each user may execute on a Centrifugo cluster. For additional details, refer to the Centrifugo PRO documentation.","s":"Pro features","u":"/docs/3/getting-started/highlights","h":"#pro-features","p":408},{"i":445,"t":"On this page","s":"Integration guide","u":"/docs/3/getting-started/integration","h":"","p":444},{"i":447,"t":"First, you need to do is download/install Centrifugo server. See install chapter for details.","s":"0. Install","u":"/docs/3/getting-started/integration","h":"#0-install","p":444},{"i":449,"t":"Create basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and chapter about server API for API description. The simplest way to do this automatically is by using genconfig command: ./centrifugo genconfig – which will generate config.json file for you with all required fields. Properly configure allowed_origins option.","s":"1. Configure Centrifugo","u":"/docs/3/getting-started/integration","h":"#1-configure-centrifugo","p":444},{"i":451,"t":"In the configuration file of your application backend register several variables: Centrifugo secret and Centrifugo API key you set on a previous step and Centrifugo API address. By default, the API address is http://localhost:8000/api. You must never reveal token secret and API key to your users.","s":"2. Configure your backend","u":"/docs/3/getting-started/integration","h":"#2-configure-your-backend","p":444},{"i":453,"t":"Now your users can start connecting to Centrifugo. You should get a client library (see list of available client SDK) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here. Every client should provide a connection token (JWT) on connect. You must generate this token on your backend side using Centrifugo secret key you set to backend configuration (note that in the case of RSA tokens you are generating JWT with a private key). See how to generate this JWT in special chapter. You pass this token from the backend to your frontend app (pass it in template context or use separate request from client-side to get user-specific JWT from backend side). And use this token when connecting to Centrifugo (for example browser client has a special method setToken). There is also a way to authenticate connections without using JWT - see chapter about proxying to backend. You are connecting to Centrifugo using one of the available transports. At this moment you can choose from: WebSocket, with JSON or binary protobuf protocol. See more info in a chapter about WebSocket transport SockJS (only supports JSON protocol). See more info about SockJS transport","s":"3. Connect to Centrifugo","u":"/docs/3/getting-started/integration","h":"#3-connect-to-centrifugo","p":444},{"i":455,"t":"After connecting to Centrifugo subscribe clients to channels they are interested in. See more about channels in special chapter. All client libraries provide a way to handle messages coming to a client from a channel after subscribing to it. There is also a way to subscribe connection to a list of channels on the server side at the moment of connection establishment. See chapter about server-side subscriptions.","s":"4. Subscribe to channels","u":"/docs/3/getting-started/integration","h":"#4-subscribe-to-channels","p":444},{"i":457,"t":"So everything should work now – as soon as a user opens some page of your application it must successfully connect to Centrifugo and subscribe to a channel (or channels). Now let's imagine you want to send a real-time message to users subscribed on a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, the administrator just created a new post, the user pressed the like button, etc. Anyway, this is an event your backend just got, and you want to immediately share it with interested users. You can do this using Centrifugo HTTP API. To simplify your life we have several API libraries for different languages. You can publish messages into a channel using one of those libraries or you can simply follow API description to construct API requests yourself - this is very simple. Also Centrifugo supports GRPC API. As soon as you published a message to the channel it must be delivered to your client.","s":"5. Publish to channel","u":"/docs/3/getting-started/integration","h":"#5-publish-to-channel","p":444},{"i":459,"t":"To put this all into production you need to deploy Centrifugo on your production server. To help you with this we have many things like Docker image, rpm and deb packages, Nginx configuration. See Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.","s":"6. Deploy to production","u":"/docs/3/getting-started/integration","h":"#6-deploy-to-production","p":444},{"i":461,"t":"Don't forget to monitor your production Centrifugo setup.","s":"7. Monitor Centrifugo","u":"/docs/3/getting-started/integration","h":"#7-monitor-centrifugo","p":444},{"i":463,"t":"As soon as you are close to machine resource limits you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using Redis engine.","s":"8. Scale Centrifugo","u":"/docs/3/getting-started/integration","h":"#8-scale-centrifugo","p":444},{"i":465,"t":"That's all for basics. The documentation actually covers lots of other concepts Centrifugo server has: scalability, private channels, admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ.","s":"9. Read FAQ","u":"/docs/3/getting-started/integration","h":"#9-read-faq","p":444},{"i":467,"t":"The main objective of Centrifugo is to manage persistent client connections established over various real-time transports (including WebSocket, HTTP-Streaming, SSE, WebTransport, etc – see here) and offer an API for publishing data towards established connections. Clients subscribe to channels, hence Centrifugo implements PUB/SUB mechanics to transmit published data to all online channel subscribers. Centrifugo employs Redis as its primary scalability option – so that it's possible to distribute client connections amongst numerous Centrifugo nodes without worrying about channel subscribers connected to separate nodes. Redis is incredibly mature, simple, and fast in-memory storage. Due to various built-in data structures and PUB/SUB support Redis is a perfect fit to be both Centrifugo Broker and PresenceManager (we will describe what's this shortly). In Centrifugo v4.1.0 we introduced an updated implementation of our Redis Engine (Engine in Centrifugo == Broker + PresenceManager) which provides sufficient performance improvements to our users. This post discusses the factors that prompted us to update Redis Engine implementation and provides some insight into the results we managed to achieve. We'll examine a few well-known Go libraries for Redis communication and contrast them against Centrifugo tasks.","s":"Improving Centrifugo Redis Engine throughput and allocation efficiency with Rueidis Go library","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"","p":466},{"i":469,"t":"Before we get started, let's define what Centrifugo's Broker and PresenceManager terms mean. Broker is an interface responsible for maintaining subscriptions from different Centrifugo nodes (initiated by client connections). That helps to scale client connections over many Centrifugo instances and not worry about the same channel subscribers being connected to different nodes – since all Centrifugo nodes connected with PUB/SUB. Messages published to one node are delivered to a channel subscriber connected to another node. Another major part of Broker is keeping an expiring publication history for channels (streams). So that Centrifugo may provide a fast cache for messages missed by clients upon going offline for a short period and compensate at most once delivery of Redis PUB/SUB using Publication incremental offsets. Centrifugo uses STREAM and HASH data structures in Redis to store channel history and stream meta information. In general Centrifugo architecture may be perfectly illustrated by this picture (Gophers are Centrifugo nodes all connected to Broker, and sockets are WebSockets): PresenceManager is an interface responsible for managing online presence information - list of currently active channel subscribers. While the connection is alive we periodically update presence entries for channels connection subscribed to (for channels where presence is enabled). Presence data should expire if not updated by a client connection for some time. Centrifugo uses two Redis data structures for managing presence in channels - HASH and ZSET.","s":"Broker and PresenceManager","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#broker-and-presencemanager","p":466},{"i":471,"t":"For a long time, the gomodule/redigo package served as the foundation for the Redis Engine implementation in Centrifugo. Huge props go to Mr Gary Burd for creating it. Redigo offers a connection Pool to Redis. A simple usage of it involves getting the connection from the pool, issuing request to Redis over that connection, and then putting the connection back to the pool after receiving the result from Redis. Let's write a simple benchmark which demonstrates simple usage of Redigo and measures SET operation performance: func BenchmarkRedigo(b *testing.B) { pool := redigo.Pool{ MaxIdle: 128, MaxActive: 128, Wait: true, Dial: func() (redigo.Conn, error) { return redigo.Dial(\"tcp\", \":6379\") }, } defer pool.Close() b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { c := pool.Get() _, err := c.Do(\"SET\", \"redigo\", \"test\") if err != nil { b.Fatal(err) } c.Close() } })} Let's run it: BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/op Seems pretty fast, but we can improve it further.","s":"Redigo","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo","p":466},{"i":473,"t":"To increase a throughput in Centrifugo, instead of using Redigo's Pool for each operation, we acquired a dedicated connection from the Pool and utilized Redis pipelining to send multiple commands where possible. Redis pipelining improves performance by executing multiple commands using a single client-server-client round trip. Instead of executing many commands one by one, you can queue the commands in a pipeline and then execute the queued commands as if it is a single command. Redis processes commands in order and sends individual response for each command. Given a single CPU nature of Redis, reducing the number of active connections when using pipelining has a positive impact on throughput – therefore pipelining is beneficial from this angle as well. You can quickly estimate the benefits of pipelining by running Redis locally and running redis-benchmark which comes with Redis distribution over it: > redis-benchmark -n 100000 set key valueSummary: throughput summary: 84674.01 requests per second And with pipelining: > redis-benchmark -n 100000 -P 64 set key valueSummary: throughput summary: 666880.00 requests per second In Centrifugo we are using smart batching technique for collecting pipeline (also described in one of the previous posts in this blog). To demonstrate benefits from using pipelining let's look at the following benchmark: const ( maxCommandsInPipeline = 512 numPipelineWorkers = 1)type command struct { errCh chan error}type sender struct { cmdCh chan command pool redigo.Pool}func newSender(pool redigo.Pool) *sender { p := &sender{ cmdCh: make(chan command), pool: pool, } go func() { for { for i := 0; i < numPipelineWorkers; i++ { p.runPipelineRoutine() } } }() return p}func (s *sender) send() error { errCh := make(chan error, 1) cmd := command{ errCh: errCh, } // Submit command to be executed by runPipelineRoutine. s.cmdCh <- cmd return <-errCh}func (s *sender) runPipelineRoutine() { conn := p.pool.Get() defer conn.Close() for { select { case cmd := <-s.cmdCh: commands := []command{cmd} conn.Send(\"set\", \"redigo\", \"test\") loop: // Collect batch of commands to send to Redis in one RTT. for i := 0; i < maxCommandsInPipeline; i++ { select { case cmd := <-s.cmdCh: commands = append(commands, cmd) conn.Send(\"set\", \"redigo\", \"test\") default: break loop } } // Flush all collected commands to the network. err := conn.Flush() if err != nil { for i := 0; i < len(commands); i++ { commands[i].errCh <- err } continue } // Read responses to commands, they come in order. for i := 0; i < len(commands); i++ { _, err := conn.Receive() commands[i].errCh <- err } } }}func BenchmarkRedigoPipelininig(b *testing.B) { pool := redigo.Pool{ Wait: true, Dial: func() (redigo.Conn, error) { return redigo.Dial(\"tcp\", \":6379\") }, } defer pool.Close() sender := newSender(pool) b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { err := sender.send() if err != nil { b.Fatal(err) } } })} This is a strategy that we employed in Centrifugo for a long time. As you can see code with automatic pipelining gets more complex, and in real life it's even more complicated to support different types of commands, channel send timeouts, and server shutdowns. What about the performance of this approach? BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/opBenchmarkRedigoPipelininig-8 1840758 604.7 ns/op 176 B/op 4 allocs/op Operation latency reduced from 4648 ns/op to 604.7 ns/op – not bad right? It's worth mentioning that upon increased RTT between application and Redis the approach with pipelining will provide worse throughput. But it still can be better than in pool-based approach. Let's say we have latency 5ms between app and Redis. This means that with pool size of 128 you will be able to issue up to 128 * (1000 / 5) = 25600 requests per second over 128 connections. With the pipelining approach above the theoretical limit is 512 * (1000 / 5) = 102400 requests per second over a single connection (though in case of using code for pipelining shown above we need to have larger parallelism, say 512 instead of 128). And it can scale further if you increase numPipelineWorkers to work over several connections in paralell. Though increasing numPipelineWorkers has negative effect on CPU – we will discuss this later in this post. Redigo is an awesome battle-tested library that served us great for a long time.","s":"Redigo with pipelining","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo-with-pipelining","p":466},{"i":475,"t":"There are three modes in which Centrifugo can work with Redis these days: Connecting to a standalone single Redis instance Connecting to Redis in master-replica configuration, where Redis Sentinel controls the failover process Connecting to Redis Cluster All modes additionally can be used with client-side consistent sharding. So it's possible to scale Redis even without a Redis Cluster setup. Unfortunately, with pure Redigo library, it's only possible to implement [ 1 ] – i.e. connecting to a single standalone Redis instance. To support the scheme with Sentinel you whether need to have a proxy between the application and Redis which proxies the connection to Redis master. For example, with Haproxy it's possible in this way: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 on-marked-down shutdown-sessions on-marked-up shutdown-backup-sessions server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:6379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin Or, you need to additionally import FZambia/sentinel library - which provides a communication layer with Redis Sentinel on top of Redigo's connection Pool. For communicating with Redis Cluster one more library may be used – mna/redisc which is also a layer on top of redigo basic functionality. Combining redigo + FZambia/sentinel + mna/redisc we managed to implement all three connection modes. This worked, though resulted in rather tricky Redis setup. Also, it was difficult to re-use existing pipelining code we had for a standalone Redis with Redis Cluster. As a result, Centrifugo only used pipelining in a standalone or Sentinel Redis cases. When using Redis Cluster, however, Centrifugo merely used the connection pool to issue requests thus not benefiting from request pipelining. Due to this we had some code duplication to send the same requests in various Redis configurations. Another thing is that Redigo uses interface{} for command construction. To send command to Redis Redigo has Do method which accepts name of the command and variadic interface{} arguments to construct command arguments: Do(commandName string, args ...interface{}) (reply interface{}, err error) While this works well and you can issue any command to Redis, you need to be very accurate when constructing a command. This also adds some allocation overhead. As we know more memory allocations lead to the increased CPU utilization because the allocation process itself requires more processing power and the GC is under more strain. At some point we felt that eliminating additional dependencies (even though I am the author of one of them) and reducing allocations in Redis communication layer is a nice step forward for Centrifugo. So we started looking around for redigo alternatives. To summarize, here is what we wanted from Redis library: Possibility to work with all three Redis setup options we support: standalone, master-replica(s) with Sentinel, Redis Cluster, so we can depend on one library instead of three Less memory allocations (and more type-safety API is a plus) Support working with RESP2-only Redis servers as we need that for backwards compatibility. And some vendors like Redis Enterprise still support RESP2 protocol only The library should be actively maintained","s":"Motivation to migrate","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#motivation-to-migrate","p":466},{"i":477,"t":"The most obvious alternative to Redigo is go-redis/redis package. It's popular, regularly gets updates, used by a huge amount of Go projects (Grafana, Thanos, etc.). And maintained by Vladimir Mihailenco who created several more awesome Go libraries, like msgpack for example. I personally successfully used go-redis/redis in several other projects I worked on. To avoid setup boilerplate for various Redis installation variations go-redis/redis has UniversalClient. From docs: UniversalClient is a wrapper client which, based on the provided options, represents either a ClusterClient, a FailoverClient, or a single-node Client. This can be useful for testing cluster-specific applications locally or having different clients in different environments. In terms of implementation go-redis/redis also has internal pool of connections to Redis, similar to redigo. It's also possible to use Client.Pipeline method to allocate a Pipeliner interface and use it for pipelining. So UniversalClient reduces setup boilerplate for different Redis installation types and number of dependencies we had, and it provide very similar way to pipeline requests so we could easily re-implement things we had with Redigo. Go-redis also provides more type-safety when constructing commands compared to Redigo, almost every command in Redis is implemented as a separate method of Client, for example Publish defined as: func (c Client) Publish(ctx context.Context, channel string, message interface{}) *IntCmd You can see though that we still have interface{} here for message argument type. I suppose this was implemented in such way for convenience – to pass both string or []byte. But it still produces some extra allocations. Without pipelining the simplest program with go-redis/redis may look like this: func BenchmarkGoredis(b *testing.B) { client := redis.NewUniversalClient(&redis.UniversalOptions{ Addrs: []string{\":6379\"}, PoolSize: 128, }) defer client.Close() b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { resp := client.Set(context.Background(), \"goredis\", \"test\", 0) if resp.Err() != nil { b.Fatal(resp.Err()) } } })} Let's run it: BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/opBenchmarkGoredis-8 268444 4561 ns/op 244 B/op 8 allocs/op Result is pretty comparable to Redigo, though Go-redis allocates more (btw most of allocations come from the connection liveness check upon getting from the pool which can not be turned off). It's interesting – if we dive deeper into what is it we can discover that this is the only way in Go to check connection was closed without reading data from it. The approach was originally introduced by go-sql-driver/mysql, it's not cross-platform, and related issue may be found in Go issue tracker. But as I said in Centrifugo we already used pipelining over the dedicated connection for all operations so we avoid frequently getting connections from the pool. And early experiments proved that go-redis may provide some performance benefits for our use case. At some point @j178 sent a pull request to Centrifuge library with Broker and PresenceManager implementations based on go-redis/redis. The amount of code to cover all the various Redis setups was reduced, we got only one dependency instead of three 🔥 But what about performance? Here we will show results for several operations which are typical for Centrifugo: Publish a message to a channel without saving it to the history - this is just a Redis PUBLISH command going through Redis PUB/SUB system (RedisPublish) Publish message to a channel with saving it to history - this involves executing the LUA script on Redis side where we add a publication to STREAM data structure, update meta information HASH, and finally PUBLISH to PUB/SUB (RedisPublish_History) Subscribe to a channel - that's a SUBSCRIBE Redis command, this is important to have it fast as Centrifugo should be able to re-subscribe to all the channels in the system upon mass client reconnect scenario (RedisSubscribe) Recovering missed publication state from channel STREAM, this is again may be called lots of times when all clients reconnect at once (RedisRecover). Updating connection presence information - many connections may periodically update their channel online presence information in Redis (RedisAddPresence) Here are the benchmark results we got when comparing redigo (v1.8.9) implementation (old) and go-redis/redis (v9.0.0-rc.2) implementation (new) with Redis v6.2.7 on Mac with M1 processor and benchmark paralellism 128: ❯ benchstat redigo_p128.txt goredis_p128.txtname old time/op new time/op deltaRedisPublish-8 1.45µs ±10% 1.88µs ± 4% +29.32% (p=0.000 n=10+10)RedisPublish_History-8 12.5µs ± 6% 9.7µs ± 3% -22.77% (p=0.000 n=10+10)RedisSubscribe-8 1.47µs ±24% 1.47µs ±10% ~ (p=0.469 n=10+10)RedisRecover-8 18.4µs ± 2% 6.3µs ± 0% -65.78% (p=0.000 n=10+8)RedisAddPresence-8 3.72µs ± 1% 3.40µs ± 1% -8.74% (p=0.000 n=10+10)name old alloc/op new alloc/op deltaRedisPublish-8 483B ± 0% 499B ± 0% +3.37% (p=0.000 n=9+10)RedisPublish_History-8 1.30kB ± 0% 1.08kB ± 0% -16.67% (p=0.000 n=10+10)RedisSubscribe-8 892B ± 2% 662B ± 6% -25.83% (p=0.000 n=10+10)RedisRecover-8 1.25kB ± 1% 1.00kB ± 0% -19.91% (p=0.000 n=10+10)RedisAddPresence-8 907B ± 0% 827B ± 0% -8.82% (p=0.002 n=7+8)name old allocs/op new allocs/op deltaRedisPublish-8 10.0 ± 0% 9.0 ± 0% -10.00% (p=0.000 n=10+10)RedisPublish_History-8 29.0 ± 0% 25.0 ± 0% -13.79% (p=0.000 n=10+10)RedisSubscribe-8 22.0 ± 0% 14.0 ± 0% -36.36% (p=0.000 n=8+7)RedisRecover-8 29.0 ± 0% 23.0 ± 0% -20.69% (p=0.000 n=10+10)RedisAddPresence-8 18.0 ± 0% 17.0 ± 0% -5.56% (p=0.000 n=10+10) danger Please note that this benchmark is not a pure performance comparison of two Go libraries for Redis – it's a performance comparison of Centrifugo Engine methods upon switching to a new library. Or visualized in Grafana: note Centrifugo benchmarks results shown in the post use parallelism 128. If someone interested to check numbers for paralellism 1 or 16 – check out this comment on Github. We observe a noticeable reduction in allocations in these benchmarks and in most benchmarks (presented here and other not listed in this post) we observed a reduced latency. Overall, results convinced us that the migration from redigo to go-redis/redis may provide Centrifugo with everything we aimed for – all the goals for a redigo alternative outlined above were successfully fullfilled. One good thing go-redis/redis allowed us to do is to use Redis pipelining also in a Redis Cluster case. It's possible due to the fact that go-redis/redis re-maps pipeline objects internally based on keys to execute pipeline on the correct node of Redis Cluster. Actually, we could do the same based on redigo + mna/redisc, but here we got it for free. BTW, there is a page with comparison between redigo and go-redis/redis in go-redis/redis docs which outlines some things I mentioned here and some others. But we have not migrated to go-redis/redis in the end. And the reason is another library – rueidis.","s":"Go-redis/redis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#go-redisredis","p":466},{"i":479,"t":"While results were good with go-redis/redis we also made an attempt to implement Redis Engine on top of rueian/rueidis library written by @rueian. According to docs, rueidis is: A fast Golang Redis client that supports Client Side Caching, Auto Pipelining, Generics OM, RedisJSON, RedisBloom, RediSearch, RedisAI, RedisGears, etc. The readme of rueidis contains benchmark results where it hugely outperforms go-redis/redis in terms of operation latency/throughput in both single Redis and Redis Custer setups: rueidis works with standalone Redis, Sentinel Redis and Redis Cluster out of the box. Just like UniversalClient of go-redis/redis. So it also allowed us to reduce code boilerplate to work with all these setups. Again, let's try to write a simple program like we had for Redigo and Go-redis above: func BenchmarkRueidis(b *testing.B) { client, err := rueidis.NewClient(rueidis.ClientOption{ InitAddress: []string{\":6379\"}, }) if err != nil { b.Fatal(err) } b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { cmd := client.B().Set().Key(\"rueidis\").Value(\"test\").Build() res := client.Do(context.Background(), cmd) if res.Error() != nil { b.Fatal(res.Error()) } } })} And run it: BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/opBenchmarkGoredis-8 268444 4561 ns/op 244 B/op 8 allocs/opBenchmarkRueidis-8 2908591 418.5 ns/op 4 B/op 1 allocs/op rueidis library comes with automatic implicit pipelining, so you can send each request in isolated way while rueidis makes sure the request becomes part of the pipeline sent to Redis – thus utilizing the connection between an application and Redis most efficiently with maximized throughput. The idea of implicit pipelining with Redis is not new and Go ecosystem already had joomcode/redispipe library which implemented it (though it comes with some limitations which made it unsuitable for Centrifugo use case). So applications that use a pool-based approach for communication with Redis may observe dramatic improvements in latency and throughput when switching to the Rueidis library. For Centrifugo we didn't expect such a huge speed-up as shown in the above graphs since we already used pipelining in Redis Engine. But rueidis implements some ideas which allow it to be efficient. Insights about these ideas are provided by Rueidis author in a \"Writing a High-Performance Golang Client Library\" series of posts on Medium: Part 1: Batching on Pipeline Part 2: Reading Again From Channels? Part 3: Remove the Bad Busy Loops With the Sync.Cond I did some prototypes with rueidis which were super-promising in terms of performance. There were some issues found during that early prototyping (mostly with PUB/SUB) – but all of them were quickly resolved by Rueian. Until v0.0.80 release rueidis did not support RESP2 though, so we could not replace our Redis Engine implementation with it. But as soon as it got RESP2 support we opened a pull request with alternative implementation. Since auto-pipelining is used in rueidis by default we were able to remove some of our own pipelining management code – so the Engine implementation is more concise now. One more thing to mention is a simpler PUB/SUB code we were able to write with rueidis. One example is that in redigo case we had to periodically PING PUB/SUB connection to maintain it alive, rueidis does this automatically. Regarding performance, here are the benchmark results we got when comparing redigo (v1.8.9) implementation (old) and rueidis (v0.0.90) implementation (new): ❯ benchstat redigo_p128.txt rueidis_p128.txtname old time/op new time/op deltaRedisPublish-8 1.45µs ±10% 0.56µs ± 1% -61.53% (p=0.000 n=10+9)RedisPublish_History-8 12.5µs ± 6% 9.7µs ± 1% -22.43% (p=0.000 n=10+9)RedisSubscribe-8 1.47µs ±24% 1.45µs ± 1% ~ (p=0.484 n=10+9)RedisRecover-8 18.4µs ± 2% 6.2µs ± 1% -66.08% (p=0.000 n=10+10)RedisAddPresence-8 3.72µs ± 1% 3.60µs ± 1% -3.34% (p=0.000 n=10+10)name old alloc/op new alloc/op deltaRedisPublish-8 483B ± 0% 91B ± 0% -81.16% (p=0.000 n=9+10)RedisPublish_History-8 1.30kB ± 0% 0.39kB ± 0% -70.08% (p=0.000 n=10+8)RedisSubscribe-8 892B ± 2% 360B ± 0% -59.66% (p=0.000 n=10+10)RedisRecover-8 1.25kB ± 1% 0.36kB ± 1% -71.52% (p=0.000 n=10+10)RedisAddPresence-8 907B ± 0% 151B ± 1% -83.34% (p=0.000 n=7+9)name old allocs/op new allocs/op deltaRedisPublish-8 10.0 ± 0% 2.0 ± 0% -80.00% (p=0.000 n=10+10)RedisPublish_History-8 29.0 ± 0% 10.0 ± 0% -65.52% (p=0.000 n=10+10)RedisSubscribe-8 22.0 ± 0% 6.0 ± 0% -72.73% (p=0.002 n=8+10)RedisRecover-8 29.0 ± 0% 7.0 ± 0% -75.86% (p=0.000 n=10+10)RedisAddPresence-8 18.0 ± 0% 3.0 ± 0% -83.33% (p=0.000 n=10+10) Or visualized in Grafana: 2.5x times more publication throughput than we had before! Instead of 700k publications/sec, we went towards 1.7 million publications/sec due to drastically decreased publish operation latency (1.45µs -> 0.59µs). This means that our previous Engine implementation under-utilized Redis, and Rueidis just pushes us towards Redis limits. The latency of most other operations is also reduced. The allocation effectiveness of the implementation based on \"rueidis\" is best. As you can see rueidis helped us to generate sufficiently fewer memory allocations for all our Redis operations. Allocation improvements directly affect Centrifugo node CPU usage. Though we will talk about CPU more later below. For Redis Cluster case we also got benchmark results similar to the standalone Redis results above. I might add that I enjoyed building commands with rueidis. All Redis commands may be constructed using a builder approach. Rueidis comes with builders generated for all Redis commands. As an illustration, this is a process of building a PUBLISH Redis command: This drastically reduces a chance to make a stupid mistake while constructing a command. Instead of always opening Redis docs to see a command syntax it's now possible to just start typing - and quickly come to the complete command to send.","s":"Rueidis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#rueidis","p":466},{"i":481,"t":"After making all these benchmarks and implementing Engine in Rueidis I decided to check whether Centrifugo consumes less CPU with it. I expected a notable CPU reduction as Rueidis Engine implementation allocates much less than Redigo-based. Turned out it's not that simple. I ran Centrifugo with some artificial load and noticed that CPU consumption of the new implementation is actually... worse than we had with Redigo-based engine under equal conditions!😩 But why? As I mentioned above Redis pipelining is a technique when several commands may be combined into one batch to send over the network. In case of automatic pipelining the size of generated batches start playing a crucial role in application and Redis CPU usage – since smaller command batches result into more read/write system calls to the kernel on both application and Redis server sides. That's why projects like Twemproxy which sit between app and Redis have sich a good effect on Redis CPU usage among other things. As we have seen above, Rueidis provides a better throughput and latency, but it's more agressive in terms of flushing data to the network. So in its default configuration we get smaller batches under th equal conditions than we had before in our own pipelining implementation based on Redigo (shown in the beginning of this post). Luckily, there is an option in Rueidis called MaxFlushDelay which allows to slow down write loop a bit to give Rueidis a chance to collect more commands to send in one batch. When this option is used Rueidis will make a pause after each network flush not bigger than selected value of MaxFlushDelay (please note, that this is a delay after flushing collected pipeline commands, not an additional delay for each request). Using some reasonable value it's possible to drastically reduce both application and Redis CPU utilization. To demonstrate this I created a repo: https://github.com/FZambia/pipelines. This repo contains three benchmarks where we use automatic pipelining: based on redigo, based on go-redis/redis and rueidis. In these benchmarks we produce concurrent requests, but instead of pushing the system towards the limits we are limiting number of requests sent to Redis, so we put all libraries in equal conditions. To rate limit requests we are using uber-go/ratelimit library. For example, to allow rate no more than 100k commands per second we can do sth like this: rl := ratelimit.New(100, ratelimit.Per(time.Millisecond))for { rl.Take() ...} We limit requests per second we could actually just write ratelimit.New(100000) – but we aim to get a more smooth distribution of requests over time - so using millisecond resolution. Let's run all the benchmarks in the default configuration: Average CPU usage during the test (a bit rough but enough for demonstration): Redigo Go-redis/redis Rueidis Application CPU, % 95 99 116 Redis CPU, % 36 35 42 OK, Rueidis-based implementation is the worst here despite of allocating less than others. So let's try to change this by setting MaxFlushDelay to sth like 100 microseconds: Now CPU usage is: Redigo Go-redis/redis Rueidis Application CPU, % 95 99 59 Redis CPU, % 36 35 12 So we can achieve great CPU usage reduction. CPU went from 116% to 59% for the application side, and from 42% to only 12% for Redis! We are sacrificing latency though. Given the fact the CPU utilization reduction is very notable the trade-off is pretty fair. caution It's definitely possible to improve CPU usage in Redigo and Go-redis/redis cases too – using similar technique. But the goal here was to improve Rueidis-based engine implementation to make it comparable or better than our Redigo-based implementation in terms of CPU utilization. As you can see we were able to achieve better CPU results just by using 100 microseconds delay after each network flush. In real life, where we are not running Redis on localhost and have some network latency in between application and Redis, this delay should be insignificant at all. Indeed, adding MaxFlushDelay can even improve (!) the latency you have. You may wonder what happened with benchmarks we showed above after we added MaxFlushDelay option. In Centrifugo we chose default value 100 microseconds, and here are results on localhost (old without delay, new with delay): > benchstat rueidis_p128.txt rueidis_delay_p128.txtname old time/op new time/op deltaRedisPublish-8 559ns ± 1% 468ns ± 0% -16.35% (p=0.000 n=9+8)RedisPublish_History-8 9.72µs ± 1% 9.67µs ± 1% -0.52% (p=0.007 n=9+8)RedisSubscribe-8 1.45µs ± 1% 1.27µs ± 1% -12.49% (p=0.000 n=9+10)RedisRecover-8 6.25µs ± 1% 5.85µs ± 0% -6.32% (p=0.000 n=10+10)RedisAddPresence-8 3.60µs ± 1% 3.33µs ± 1% -7.52% (p=0.000 n=10+10)(rest is not important here...) It's even better for this set of benchmarks. Though while it's better for these benchmarks the numbers may differ for other under different conditions. For example, in the benchmarks we run we use concurrency 128, if we reduce concurrency we will notice reduced throughput – as batches Rueidis collects become smaller. Smaller batches + some delay to collect = less requests per second to send. The problem is that the value to pause Rueidis write loop is a very use case specific, it's pretty hard to provide a reasonable default for it. Depending on request rate/size, network latency etc. you may choose a larger or smaller delay. In v4.1.0 we start with hardcoded 100 microsecond MaxFlushDelay which seems sufficient for most use cases and showed good results in the benchmarks - though possibly we will have to make it tunable later. To check that Centrifugo benchmarks also utilize less CPU I added rate limiter (50k rps per second) to benchmarks and compared version without MaxFlushDelay and with 100 microsecond MaxFlushDelay: 50k req per second Without delay With 100mks delay BenchmarkPublish Centrifugo - 75%, Redis - 24% Centrifugo - 44%, Redis - 9% BenchmarkPublish_History Centrifugo - 80% , Redis - 67% Centrifugo - 55%, Redis - 50% BenchmarkSubscribe Centrifugo - 80%, Redis - 30% Centrifugo - 45% , Redis - 14% BenchmarkRecover Centrifugo - 84%, Redis - 51% Centrifugo - 51%, Redis - 36% BenchmarkPresence Centrifugo - 114%, Redis - 69% Centrifugo - 90%, Redis - 60% note In this test I replaced BenchmarkAddPresence with BenchmarkPresence (get information about all online subscribers in channel) to also make sure we have CPU reduction when using read-intensive method, i.e. when Redis response is reasonably large. We observe a notable CPU usage improvement here. Hope you understand now why increasing numPipelineWorkers value in the pipelining code showed before results into increased CPU usage on app and Redis sides – due to smaller batch sizes and more read/write system calls as the consequence. note BTW, would it be a nice thing if Go benchmarking suite could show a CPU usage of the process in addition to time and alloc stats? 🤔","s":"Switching to Rueidis: reducing CPU usage","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#switching-to-rueidis-reducing-cpu-usage","p":466},{"i":483,"t":"The last thing to check is how new implementation works upon increased RTT between application and Redis. To add artificial latency on localhost on Linux one can use tc tool as shown here by Daniel Stenberg. But I am on MacOS so the simplest way I found was using Shopify/toxiproxy. Sth like running a server: toxyproxy-server And then in another terminal I used toxiproxy-cli to create toxic Redis proxy with additional latency on port 26379: toxiproxy-cli create -l localhost:26379 -u localhost:6379 toxic_redistoxiproxy-cli toxic add -t latency -a latency=5 toxic_redis The benchmark results are (old is Redigo-based, new is Rueidis-based): > benchstat redigo_latency_p128.txt rueidis_delay_latency_p128.txtname old time/op new time/op deltaRedisPublish-8 31.5µs ± 1% 5.6µs ± 3% -82.26% (p=0.000 n=9+10)RedisPublish_History-8 62.8µs ± 3% 10.6µs ± 4% -83.05% (p=0.000 n=10+10)RedisSubscribe-8 1.52µs ± 5% 6.05µs ± 8% +298.70% (p=0.000 n=8+10)RedisRecover-8 48.3µs ± 3% 7.3µs ± 4% -84.80% (p=0.000 n=10+10)RedisAddPresence-8 52.3µs ± 4% 5.8µs ± 2% -88.94% (p=0.000 n=10+10)(rest is not important here...) We see that new Engine implementation behaves much better for most cases. But what happened to Subscribe operation? It did not change at all in Redigo case – the same performance as if there is no additional latency involved! Turned out that when we call Subscribe in Redigo case, Redigo only flushes data to the network without waiting synchronously for subscribe result. It makes sense in general and we can listen to subscribe notifications asynchronously, but in Centrifugo we relied on the returned error thinking that it includes succesful subscription result from Redis - meaning that we already subscribed to a channel at that point. And this could theoretically lead to some rare bugs in Centrifugo. Rueidis library waits for subscribe response. So here the behavior of rueidis while differs from redigo in terms of throughput under increased latency just fits Centrifugo better in terms of behavior. So we go with it.","s":"Adding latency","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#adding-latency","p":466},{"i":485,"t":"Migrating from Redigo to Rueidis library was not just a task of rewriting code, we had to carefully test various aspects of Redis Engine behaviour – latency, throughput, CPU utilization of application, and even CPU utilization of Redis itself under the equal application load conditions. I think that we will find more projects in Go ecosystem using rueidis library shortly. Not just because of its allocation efficiency and out-of-the-box throughput, but also due to a convenient type-safe command API. For most Centrifugo users this migration means more efficient CPU usage as new implementation allocates less memory (less work to allocate and less strain on GC) and we tried to find a reasonable batch size to reduce the number of system calls for common operations. While latency and throughput of single Centrifugo node should be better as we make concurrent Redis calls from many goroutines. Hopefully readers will learn some tips from this post which can help to achieve effective communication with Redis from Go or another programming language. A few key takeaways: Redis pipelining may increase throughput and reduce latency, it can also reduce CPU utilization of Redis Don't blindly trust Go benchmark numbers but also think about CPU effect of changes you made (sometimes of the external system also) Reduce the number of system calls to decrease CPU utilization Everything is a trade-off – latency or resource usage? Your own WebSocket server or Centrifugo? Don't rely on someone's else benchmarks, including those published here. Measure for your own use case. Take into account your load profile, paralellism, network latency, data size, etc. P.S. One thing worth mentioning and which may be helpful for someone is that during our comparison experiments we discovered that Redis 7 has a major latency increase compared to Redis 6 when executing Lua scripts. So if you have performance sensitive code with Lua scripts take a look at this Redis issue. With the help of Redis developers some things already improved in unstable Redis branch, hopefully that issue will be closed at the time you read this post.","s":"Conclusion","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#conclusion","p":466},{"i":487,"t":"On this page","s":"Centrifugo introduction","u":"/docs/3/getting-started/introduction","h":"","p":486},{"i":489,"t":"Centrifugo was born to help applications with a server-side written in a language or a framework without built-in concurrency support. In this case, dealing with persistent connections is a real headache that usually can only be resolved by introducing a shift in the technology stack and spending enough time to create a production-ready solution. For example, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others have poor and not performant support of working with many persistent connections for the real-time messaging task. In this case, Centrifugo is a very straightforward and non-obtrusive way to introduce real-time updates and handle lots of persistent connections without radical changes in application backend architecture. Developers could proceed writing a backend with a favorite language or favorite framework, keep existing architecture – and just let Centrifugo deal with persistent connections. At the moment, Centrifugo provides some advanced and unique features that can simplify a developer's life and save months of development, even if the application backend is built with the asynchronous concurrent language. One example is that Centrifugo can scale out-of-the-box to many machines with several supported brokers. And there are more things to mention – see detailed highlights further in the docs.","s":"Motivation","u":"/docs/3/getting-started/introduction","h":"#motivation","p":486},{"i":491,"t":"As mentioned above, Centrifugo runs as a standalone service that cares about handling persistent connections from application users. Application backend and frontend can be written in any programming language. Clients connect to Centrifugo and subscribe to channels. As soon as some event happens application backend can publish a message with event payload into a channel using Centrifugo API. The message will be delivered to all clients currently connected and subscribed to a channel. So Centrifugo is a user-facing PUB/SUB server in a nutshell. Here is a simplified scheme:","s":"Concepts","u":"/docs/3/getting-started/introduction","h":"#concepts","p":486},{"i":493,"t":"We have rooms in Telegram and Discord: See you there!","s":"Join community","u":"/docs/3/getting-started/introduction","h":"#join-community","p":486},{"i":495,"t":"On this page","s":"Client API showcase","u":"/docs/3/getting-started/client_api","h":"","p":494},{"i":497,"t":"Each Centrifugo client allows connecting to a server. const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.connect(); In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to: const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.setToken('')centrifuge.connect(); See authentication chapter for more information on how to generate connection JWT. If you are using connect proxy then you may go without setting JWT.","s":"Connecting to a server","u":"/docs/3/getting-started/client_api","h":"#connecting-to-a-server","p":494},{"i":499,"t":"After connecting you can disconnect from a server at any moment. centrifuge.disconnect();","s":"Disconnecting from a server","u":"/docs/3/getting-started/client_api","h":"#disconnecting-from-a-server","p":494},{"i":501,"t":"Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.","s":"Reconnecting to a server","u":"/docs/3/getting-started/client_api","h":"#reconnecting-to-a-server","p":494},{"i":503,"t":"All client implementations allow setting handlers on connect and disconnect events. For example: centrifuge.on('connect', function(connectCtx){ console.log('connected', connectCtx)});centrifuge.on('disconnect', function(disconnectCtx){ console.log('disconnected', disconnectCtx)});","s":"Connection lifecycle events","u":"/docs/3/getting-started/client_api","h":"#connection-lifecycle-events","p":494},{"i":505,"t":"Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel. centrifuge.subscribe('channel', function(messageCtx) { console.log(messageCtx);}) Client can subscribe to different channels. Subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events. In idiomatic case messages published to channels from application backend over Centrifugo server API. Though it's not always true. Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel properly configured. Client keeps last seen Publication offset and restores missed publications since known offset upon reconnecting. If recovery failed then client implementation provides a flag inside subscribe event to let the application know that some publications were missed – so you may need to load state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery in a dedicated chapter.","s":"Subscribe to a channel","u":"/docs/3/getting-started/client_api","h":"#subscribe-to-a-channel","p":494},{"i":507,"t":"To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance: centrifuge.on('publish', function(messageCtx) { console.log(messageCtx);}); It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.","s":"Server-side subscriptions","u":"/docs/3/getting-started/client_api","h":"#server-side-subscriptions","p":494},{"i":509,"t":"A client can send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the WebSocket or SockJS connection. RPC is only available when RPC proxy configured. const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"Send RPC","u":"/docs/3/getting-started/client_api","h":"#send-rpc","p":494},{"i":511,"t":"Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel: Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Call channel history","u":"/docs/3/getting-started/client_api","h":"#call-channel-history","p":494},{"i":513,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats","u":"/docs/3/getting-started/client_api","h":"#presence-and-presence-stats","p":494},{"i":515,"t":"On this page","s":"Database-driven namespace configuration","u":"/docs/3/pro/db_namespaces","h":"","p":514},{"i":517,"t":"As soon as you point Centrifugo PRO to an admin storage and enable storage namespace management, Centrifugo will load namespaces from database table on start. Changes made in web UI will then propagate to all running Centrifugo nodes in up to 30 seconds. info Centrifugo nodes cache namespace configuration in memory so if Centrifugo temporarily lost connection to a database it will continue working with previous namespace configuration until connection problems will be resolved.","s":"How it works","u":"/docs/3/pro/db_namespaces","h":"#how-it-works","p":514},{"i":519,"t":"By default namespace database management is off – i.e. namespaces loaded on Centrifugo start from a configuration file (or environment variable). To enable namespace management through database add the following into configuration file: config.json { ... \"admin_storage\": { \"enabled\": true, \"storage_type\": \"sqlite\", \"storage_dsn\": \"/path/to/centrifugo.db\", \"manage_namespaces\": true }} Centrifugo PRO supports several SQL database backends to keep namespace information: SQLite (storage_type: sqlite) PostgreSQL (storage_type: postgresql) MySQL (storage_type: mysql) Each storage type has its own storage_dsn format. For SQLite it's just a path to a db file. PostgreSQL dsn format described here. Example: config.json { ... \"admin_storage\": { \"enabled\": true, \"storage_type\": \"postgresql\", \"storage_dsn\": \"host=localhost user=postgres password=mysecretpassword dbname=centrifugo port=5432 sslmode=disable\", \"manage_namespaces\": true }} MySQL dsn format described here. Example: config.json { ... \"admin_storage\": { \"enabled\": true, \"storage_type\": \"mysql\", \"storage_dsn\": \"user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local\", \"manage_namespaces\": true }}","s":"Configuration","u":"/docs/3/pro/db_namespaces","h":"#configuration","p":514},{"i":521,"t":"On this page","s":"Install Centrifugo","u":"/docs/3/getting-started/installation","h":"","p":520},{"i":523,"t":"For a local development the simplest way to get Centrifugo is from binary release (i.e. single all-contained executable file). Binary releases available on Github. Download latest release for your operating system, unpack it and you are done. Centrifugo is pre-built for: Linux 64-bit (linux_amd64) Linux 32-bit (linux_386) Linux ARM 64-bit (linux_arm64) MacOS (darwin_amd64) MacOS on Apple Silicon (darwin_arm64) Windows (windows_amd64) FreeBSD (freebsd_amd64) ARM v6 (linux_armv6) Archives contain a single statically compiled binary centrifugo file that is ready to run: ./centrifugo -h See the version of Centrifugo: ./centrifugo version Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo then there is genconfig command which generates a minimal configuration file to get started: ./centrifugo genconfig It creates a configuration file config.json with some auto-generated option values in a current directory (by default). tip It's possible to generate file in YAML or TOML format, i.e. ./centrifugo genconfig -c config.toml Having a configuration file you can finally run Centrifugo instance: ./centrifugo --config=config.json We will talk about a configuration in detail in the next sections. You can also put or symlink centrifugo into your bin OS directory and run it from anywhere: centrifugo --config=config.json","s":"Install from the binary release","u":"/docs/3/getting-started/installation","h":"#install-from-the-binary-release","p":520},{"i":525,"t":"Centrifugo server has a docker image available on Docker Hub. docker pull centrifugo/centrifugo Run: docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json Note that docker allows setting nofile limits in command-line arguments which is pretty important to handle lots of simultaneous persistent connections and not run out of open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter. caution Pin to the exact Docker Image tag in production, for example: centrifugo/centrifugo:v3.0.0, this will help to avoid unexpected problems during re-deploy process.","s":"Docker image","u":"/docs/3/getting-started/installation","h":"#docker-image","p":520},{"i":527,"t":"Create configuration file config.json: { \"token_hmac_secret_key\": \"my_secret\", \"api_key\": \"my_api_key\", \"admin_password\": \"password\", \"admin_secret\": \"secret\", \"admin\": true} Create docker-compose.yml: centrifugo: container_name: centrifugo image: centrifugo/centrifugo:v3 volumes: - ./config.json:/centrifugo/config.json command: centrifugo -c config.json ports: - 8000:8000 ulimits: nofile: soft: 65535 hard: 65535 Run with: docker-compose up","s":"Docker-compose example","u":"/docs/3/getting-started/installation","h":"#docker-compose-example","p":520},{"i":529,"t":"See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.","s":"Kubernetes Helm chart","u":"/docs/3/getting-started/installation","h":"#kubernetes-helm-chart","p":520},{"i":531,"t":"Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io. At moment, we support versions of the following distributions: 64-bit Debian 9 Stretch 64-bit Debian 10 Buster 64-bit Debian 11 Bullseye 64-bit Ubuntu 16.04 Xenial 64-bit Ubuntu 18.04 Bionic 64-bit Ubuntu 20.04 Focal Fossa 64-bit Centos 7 64-bit Centos 8 See full list of available packages and installation instructions. Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.","s":"RPM and DEB packages for Linux","u":"/docs/3/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":520},{"i":533,"t":"If you are developing on macOS then you can install Centrifugo over brew: brew tap centrifugal/centrifugobrew install centrifugo","s":"With brew on macOS","u":"/docs/3/getting-started/installation","h":"#with-brew-on-macos","p":520},{"i":535,"t":"You need Go language installed: git clone https://github.com/centrifugal/centrifugo.gitcd centrifugogo build./centrifugo","s":"Build from source","u":"/docs/3/getting-started/installation","h":"#build-from-source","p":520},{"i":537,"t":"On this page","s":"Migrating to v3","u":"/docs/3/getting-started/migration_v3","h":"","p":536},{"i":539,"t":"Client protocol has some backward incompatible changes regarding working with history API and removing deprecated fields.","s":"Client-side changes","u":"/docs/3/getting-started/migration_v3","h":"#client-side-changes","p":536},{"i":541,"t":"Call to history API from client-side now does not return all publications from history cache. It returns only information about a stream with zero publications. Clients should explicitly provide a limit when calling history API. Also, the maximum allowed limit can be set by client_history_max_publication_limit option (by default 300). We provide a boolean flag use_unlimited_history_by_default on configuration file top level to enable previous behavior while you migrate client applications to use explicit limit.","s":"No unlimited history by default","u":"/docs/3/getting-started/migration_v3","h":"#no-unlimited-history-by-default","p":536},{"i":543,"t":"The maximum number of messages that can be recovered is now limited by client_recovery_max_publication_limit option which is by default 300.","s":"Publication limit for recovery","u":"/docs/3/getting-started/migration_v3","h":"#publication-limit-for-recovery","p":536},{"i":545,"t":"Deprecated seq/gen now removed and Centrifugo uses offset field for a position in a stream. This means that there is no need for v3_use_offset option anymore – it's not used in Centrifugo v3.","s":"Seq/Gen fields removed","u":"/docs/3/getting-started/migration_v3","h":"#seqgen-fields-removed","p":536},{"i":548,"t":"In Centrifugo v3 all time intervals should be configured using duration. For example \"proxy_connect_timeout\": 1 should be changed to \"proxy_connect_timeout\": \"1s\". We provide a configuration converter which takes this change into account.","s":"Time interval options are duration","u":"/docs/3/getting-started/migration_v3","h":"#time-interval-options-are-duration","p":536},{"i":550,"t":"In Centrifugo v3 history_recover option becomes recover. Option history_lifetime renamed to history_ttl and it's now a duration. Option server_side removed, see protected option as a replacement. We provide a configuration converter which takes these changes into account.","s":"Channel options changes","u":"/docs/3/getting-started/migration_v3","h":"#channel-options-changes","p":536},{"i":552,"t":"Configuring over command-line flags is not very convenient for production deployments, Centrifugo v3 reduced the number of command-line flags available – it mostly has flags frequently useful for development now.","s":"Some command-line flags removed","u":"/docs/3/getting-started/migration_v3","h":"#some-command-line-flags-removed","p":536},{"i":554,"t":"In Centrifugo v3 you should explicitly set a list of allowed origins which are allowed to connect to client transport endpoints. config.json { ... \"allowed_origins\": [\"https://mysite.com\"]} There is a way to disable origin check, but it's discouraged and insecure in case you are using connect proxy feature. config.json { ... \"allowed_origins\": [\"*\"]}","s":"Enforced request Origin check","u":"/docs/3/getting-started/migration_v3","h":"#enforced-request-origin-check","p":536},{"i":556,"t":"In Centrifugo v3 we addressed an issue where package name in Protobuf definitions resulted in some inconvenience and attempts to rename it. But it's not possible to rename it since GRPC uses it as part of RPC methods internally. Now GRPC API package looks like this: package centrifugal.centrifugo.api; This means you need to regenerate your GRPC code which communicates with Centrifugo using the latest Protobuf definitions. Refer to the GRPC API doc.","s":"Updated GRPC API Protobuf package","u":"/docs/3/getting-started/migration_v3","h":"#updated-grpc-api-protobuf-package","p":536},{"i":558,"t":"The response format of channels API call changed in v3. See description in API doc. The channels method has new additional possibilities like showing the number of connections in a channel and filter channels by pattern. info Channels API call still has the same concern as before: this method does not scale well for many active channels in a system and is mostly recommended for administrative/debug purposes.","s":"Channels API method changed","u":"/docs/3/getting-started/migration_v3","h":"#channels-api-method-changed","p":536},{"i":560,"t":"When using HTTP proxy you should now set an explicit list of headers you want to proxy. To mimic the behavior of Centrifugo v2 add to your configuration: config.json { \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} If you had a list of extra HTTP headers using proxy_extra_http_headers then additionally extend list above with values from proxy_extra_http_headers. Then you can remove proxy_extra_http_headers - it's not used anymore. Another important change is how Centrifugo proxies binary data over HTTP JSON proxy. Previously proxy mode (whether to use base64 fields or not) could be configured using encoding=binary URL param of connection. With Centrifugo v3 it's only possible to use binary mode by enabling \"proxy_binary_encoding\": true option. BTW according to our community poll only 2% of Centrifugo users used binary mode in HTTP proxy. If you have problems with new behavior – write about your situation to our community chats – and we will see what's possible.","s":"HTTP proxy changes","u":"/docs/3/getting-started/migration_v3","h":"#http-proxy-changes","p":536},{"i":562,"t":"eto claim of subscription JWT removed. But since Centrifugo v3 introduced an additional expire_at claim it's still possible to implement one-time subscription tokens without enabling subscription expiration workflow by setting \"expire_at: 0\" in subscription JWT claims.","s":"JWT changes","u":"/docs/3/getting-started/migration_v3","h":"#jwt-changes","p":536},{"i":564,"t":"Redis configuration was a bit messy - especially in the Redis sharding case, in v3 we decided to clean up it a bit. Make it more explicit and reduce the number of possible ways to configure. Refer to the Redis Engine docs for the new configuration details. The important thing is that there is no separate redis_host and redis_port option anymore – those are replaced with single redis_address option.","s":"Redis configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#redis-configuration-changes","p":536},{"i":566,"t":"Centrifugo v3 will use Redis Stream data structure to keep history instead of lists. danger This requires Redis >= 5.0.1 to work. If you still need List data structure or have an old Redis version you can use \"redis_use_lists\": true to mimic the default behavior of Centrifugo v2.","s":"Redis streams used by default","u":"/docs/3/getting-started/migration_v3","h":"#redis-streams-used-by-default","p":536},{"i":568,"t":"Our poll showed that most Centrifugo users do not use SockJS transport. In v3 it's disabled by default. You can enable it by setting \"sockjs\": true in configuration.","s":"SockJS disabled by default","u":"/docs/3/getting-started/migration_v3","h":"#sockjs-disabled-by-default","p":536},{"i":570,"t":"Here is a full list of configuration option changes. We provide a best-effort configuration converter. allowed_origins is now required to be set to authorize requests with Origin header v3_use_offset removed redis_streams removed tls_autocert_force_rsa removed redis_pubsub_num_workers removed sockjs_disable removed secret renamed to token_hmac_secret_key history_lifetime renamed to history_ttl history_recover renamed to recover client_presence_ping_interval renamed to client_presence_update_interval client_ping_interval renamed to websocket_ping_interval client_message_write_timeout renamed to websocket_write_timeout client_request_max_size renamed to websocket_message_size_limit client_presence_expire_interval renamed to presence_ttl memory_history_meta_ttl renamed to history_meta_ttl redis_history_meta_ttl renamed to history_meta_ttl redis_sequence_ttl renamed to history_meta_ttl redis_presence_ttl renamed to presence_ttl presence_ttl should be converted to duration websocket_write_timeout should be converted to duration websocket_ping_interval should be converted to duration client_presence_update_interval should be converted to duration history_ttl should be converted to duration history_meta_ttl should be converted to duration nats_dial_timeout should be converted to duration nats_write_timeout should be converted to duration graphite_interval should be converted to duration shutdown_timeout should be converted to duration shutdown_termination_delay should be converted to duration proxy_connect_timeout should be converted to duration proxy_refresh_timeout should be converted to duration proxy_rpc_timeout should be converted to duration proxy_subscribe_timeout should be converted to duration proxy_publish_timeout should be converted to duration client_expired_close_delay should be converted to duration client_expired_sub_close_delay should be converted to duration client_stale_close_delay should be converted to duration client_channel_position_check_delay should be converted to duration node_info_metrics_aggregate_interval should be converted to duration websocket_ping_interval should be converted to duration websocket_write_timeout should be converted to duration sockjs_heartbeat_delay should be converted to duration redis_idle_timeout should be converted to duration redis_connect_timeout should be converted to duration redis_read_timeout should be converted to duration redis_write_timeout should be converted to duration redis_cluster_addrs renamed to redis_cluster_address redis_sentinels renamed to redis_sentinel_address redis_master_name renamed to redis_sentinel_master_name","s":"Other configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#other-configuration-changes","p":536},{"i":572,"t":"Here is a converter between Centrifugo v2 and v3 JSON configuration. It can help to translate most of the things automatically for you. If you are using Centrifugo with TOML format then you can use online converter as initial step. Or yaml-to-json and json-to-yaml for YAML. tip It's fully client-side: your data won't be sent anywhere. danger Unfortunately, we can't migrate environment variables and command-line flags automatically - so if you are using env vars or command-line flags to configure Centrifugo you still need to migrate manually. Also, be aware: this converter tool is the best effort only – we can not guarantee it solves all corner cases, especially in Redis configuration. You may still need to fix some things manually, for example - properly fill allowed_origins. Convert Here will be configuration for v3 Here will be log of changes made in your config","s":"v2 to v3 config converter","u":"/docs/3/getting-started/migration_v3","h":"#v2-to-v3-config-converter","p":536},{"i":574,"t":"On this page","s":"Centrifugo PRO overview","u":"/docs/3/pro/overview","h":"","p":573},{"i":576,"t":"Centrifugo PRO includes the following features: Everything from Centrifugo OSS Channel and user tracing provides a way to look at all client protocol frames in the specified channel or per user ID. Real-time analytics with ClickHouse for a great system observability, reporting and trending. User status feature to understand activity state for a list of users. Operation throttling to protect client API from misusing and frontend bugs. User connections API to query for all active user sessions with additional information. User blocking API to block/unblock abusive users by ID. JWT revoking and invalidation to revoking tokens by token ID (JTI) and invalidating user's tokens on issue time basis. Faster performance to reduce resource usage on server side. Singleflight for online presence and history to reduce load on the broker. Near real-time CPU and RSS memory usage stats. info PRO features can change with time. We reserve a right to move features from PRO to OSS version if there is a clear signal that this is required to do for the Centrifugo ecosystem.","s":"Features","u":"/docs/3/pro/overview","h":"#features","p":573},{"i":578,"t":"You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example: Centrifugo handles up to 50 concurrent connections up to 2 server nodes supported up to 20 API requests per second allowed This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future. caution Centrifugo PRO is distributed under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept license terms.","s":"Sandbox mode","u":"/docs/3/pro/overview","h":"#sandbox-mode","p":573},{"i":580,"t":"To run without limits Centrifugo PRO requires a license key. At this point we are not issuing license keys for Centrifugo PRO as we are in the process of defining the pricing strategy for it. Please contact us over centrifugal.dev@gmail.com, we can add you to the list of interested customers and will appreciate if you share which features you are mostly interested in.","s":"Pricing","u":"/docs/3/pro/overview","h":"#pricing","p":573},{"i":582,"t":"On this page","s":"Quickstart tutorial ⏱️","u":"/docs/3/getting-started/quickstart","h":"","p":581},{"i":584,"t":"Several more examples are located on Github – check out this repo. Also, check out our blog with several tutorials.","s":"More examples","u":"/docs/3/getting-started/quickstart","h":"#more-examples","p":581},{"i":586,"t":"On this page","s":"Install and run PRO version","u":"/docs/3/pro/install_and_run","h":"","p":585},{"i":588,"t":"Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).","s":"Binary release","u":"/docs/3/pro/install_and_run","h":"#binary-release","p":585},{"i":590,"t":"Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro: docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v3.2.2 centrifugo -c config.json","s":"Docker image","u":"/docs/3/pro/install_and_run","h":"#docker-image","p":585},{"i":592,"t":"You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag: values.yaml ...image: registry: docker.io repository: centrifugo/centrifugo-pro tag: v3.2.2","s":"Kubernetes","u":"/docs/3/pro/install_and_run","h":"#kubernetes","p":585},{"i":594,"t":"DEB package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v3.2.2/centrifugo-pro_3.2.2-0_amd64.debsudo dpkg -i centrifugo-pro_3.2.2-0_amd64.deb","s":"Debian and Ubuntu","u":"/docs/3/pro/install_and_run","h":"#debian-and-ubuntu","p":585},{"i":596,"t":"RPM package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v3.2.2/centrifugo-pro-3.2.2-0.x86_64.rpmsudo yum install centrifugo-pro-3.2.2-0.x86_64.rpm","s":"Centos","u":"/docs/3/pro/install_and_run","h":"#centos","p":585},{"i":598,"t":"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits. Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this: config.json { ... \"license\": \"\"} tip If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.","s":"Setting PRO license key","u":"/docs/3/pro/install_and_run","h":"#setting-pro-license-key","p":585},{"i":600,"t":"Singleflight Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. This option can radically reduce a load on a broker in the following situations: Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature) Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine. To enable: config.json { ... \"use_singleflight\": true} Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.","s":"Singleflight","u":"/docs/3/pro/singleflight","h":"","p":599},{"i":602,"t":"On this page","s":"Analytics with ClickHouse","u":"/docs/3/pro/analytics","h":"","p":601},{"i":604,"t":"To enable integration with ClickHouse add the following section to a configuration file: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\", \"tcp://127.0.0.1:9001\", \"tcp://127.0.0.1:9002\", \"tcp://127.0.0.1:9003\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"centrifugo_cluster\", \"export_connections\": true, \"export_operations\": true, \"export_http_headers\": [ \"User-Agent\", \"Origin\", \"X-Real-Ip\", ] }} All ClickHouse analytics options scoped to clickhouse_analytics section of configuration. Toggle this feature using enabled boolean option. tip While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo. Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option. You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database. export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available. export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available. export_http_headers is a list of HTTP headers to export for connection information. export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport. skip_schema_initialization (new in Centrifugo PRO v3.1.1) - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process. skip_ping_on_start (new in Centrifugo PRO v3.1.1) - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.","s":"Configuration","u":"/docs/3/pro/analytics","h":"#configuration","p":601},{"i":606,"t":"SHOW CREATE TABLE centrifugo.connections;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections( `client` FixedString(36), `user` String, `name` String, `version` String, `transport` String, `channels` Array(String), `headers.key` Array(String), `headers.value` Array(String), `metadata.key` Array(String), `metadata.value` Array(String), `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└─────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.connections_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections_distributed( `client` FixedString(36), `user` String, `name` String, `version` String, `transport` String, `channels` Array(String), `headers.key` Array(String), `headers.value` Array(String), `metadata.key` Array(String), `metadata.value` Array(String), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Connections table","u":"/docs/3/pro/analytics","h":"#connections-table","p":601},{"i":608,"t":"SHOW CREATE TABLE centrifugo.operations;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations( `client` FixedString(36), `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.operations_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `client` FixedString(36), `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Operations table","u":"/docs/3/pro/analytics","h":"#operations-table","p":601},{"i":610,"t":"Show unique users which were connected: SELECT DISTINCT userFROM centrifugo.connections_distributed;┌─user─────┐│ user_1 ││ user_2 ││ user_3 ││ user_4 ││ user_5 │└──────────┘ Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111): SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish');┌─count()─┐│ 4502 │└─────────┘ The same for a specific user: SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');┌─count()─┐│ 1214 │└─────────┘ Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since connections table contain periodic snapshot entries, clients could subscribe/unsubscribe in between snapshots – this is reflected in operations table): SELECT COUNT(Distinct(user))FROM centrifugo.connections_distributedWHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));┌─uniqExact(user)─┐│ 101 │└─────────────────┘ Show top 10 users which called publish operation during last one minute: SELECT COUNT(op) AS num_ops, userFROM centrifugo.operations_distributedWHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))GROUP BY userORDER BY num_ops DESCLIMIT 10;┌─num_ops─┬─user─────┐│ 56 │ user_200 ││ 11 │ user_75 ││ 6 │ user_87 ││ 6 │ user_65 ││ 6 │ user_39 ││ 5 │ user_28 ││ 5 │ user_63 ││ 5 │ user_89 ││ 3 │ user_32 ││ 3 │ user_52 │└─────────┴──────────┘","s":"Query examples","u":"/docs/3/pro/analytics","h":"#query-examples","p":601},{"i":612,"t":"The recommended way to run ClickHouse in production is with cluster. But during development you may want to run Centrifugo with single instance ClickHouse. To do this set only one ClickHouse dsn and do not set cluster name: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"\", \"export_connections\": true, \"export_operations\": true, \"export_http_headers\": [ \"Origin\", \"User-Agent\" ] }} Run ClickHouse locally: docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click yandex/clickhouse-server Run ClickHouse client: docker run -it --rm --link click:clickhouse-server yandex/clickhouse-client --host clickhouse-server Issue queries: :) SELECT * FROM centrifugo.operations┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘","s":"Development","u":"/docs/3/pro/analytics","h":"#development","p":601},{"i":614,"t":"When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 5 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are not configurable at the moment but just reach us out if you need to tune these values. Several metrics are exposed to monitor export process health: centrifugo_clickhouse_analytics_flush_duration_seconds summary centrifugo_clickhouse_analytics_batch_size summary centrifugo_clickhouse_analytics_drop_count counter","s":"How export works","u":"/docs/3/pro/analytics","h":"#how-export-works","p":601},{"i":616,"t":"On this page","s":"Operation throttling","u":"/docs/3/pro/throttling","h":"","p":615},{"i":618,"t":"At this moment Centrifugo PRO provides throttling over Redis. It's only possible to throttle by the user ID. Requests from anonymous users can't be throttled. Throttling with Redis uses token bucket algorithm internally. Here is a list of operations that can be throttled: connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with method resolution) An example configuration: config.json { ... \"redis_throttling\": { \"enabled\": false, \"redis_address\": \"localhost:6379\", \"buckets\": { \"publish\": { \"enabled\": true, \"interval\": \"1s\", \"rate\": 1, \"capacity\": 1 }, \"rpc\": { \"enabled\": true, \"interval\": \"1s\", \"rate\": 10, \"capacity\": 1, \"method_override\": [ { \"method\": \"updateActiveStatus\", \"interval\": \"20s\", \"rate\": 1, \"capacity\": 1 } ] } } }} This configuration enables throttling and throttles publish attempts in a way that only 1 publication is possible in 1 second from the same user. Redis configuration for throttling feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for throttling feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_throttling\": { \"enabled\": false, \"use_redis_from_engine\": true, \"buckets\": { \"publish\": { \"enabled\": true, \"interval\": \"1s\", \"rate\": 1, \"capacity\": 1 } } }} In this case throttling will simply connect to Redis instances configured for an Engine.","s":"Redis throttling","u":"/docs/3/pro/throttling","h":"#redis-throttling","p":615},{"i":620,"t":"On this page","s":"Faster performance","u":"/docs/3/pro/performance","h":"","p":619},{"i":622,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.","s":"Faster HTTP API","u":"/docs/3/pro/performance","h":"#faster-http-api","p":619},{"i":624,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC API","u":"/docs/3/pro/performance","h":"#faster-grpc-api","p":619},{"i":626,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster HTTP proxy","u":"/docs/3/pro/performance","h":"#faster-http-proxy","p":619},{"i":628,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC proxy","u":"/docs/3/pro/performance","h":"#faster-grpc-proxy","p":619},{"i":630,"t":"Centrifugo PRO has an optimized decoding of JWT claims.","s":"Faster JWT decoding","u":"/docs/3/pro/performance","h":"#faster-jwt-decoding","p":619},{"i":632,"t":"Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.","s":"Faster GRPC unidirectional stream","u":"/docs/3/pro/performance","h":"#faster-grpc-unidirectional-stream","p":619},{"i":634,"t":"Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.","s":"Examples","u":"/docs/3/pro/performance","h":"#examples","p":619},{"i":636,"t":"Sorry, your browser doesn't support embedded video. In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.","s":"Publish HTTP API","u":"/docs/3/pro/performance","h":"#publish-http-api","p":619},{"i":638,"t":"Sorry, your browser doesn't support embedded video. In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.","s":"History HTTP API","u":"/docs/3/pro/performance","h":"#history-http-api","p":619},{"i":640,"t":"CPU and RSS stats A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI. Here is how this looks like: The information updated in near real-time (with several seconds delay). It's also available as part of info API.","s":"CPU and RSS stats","u":"/docs/3/pro/process_stats","h":"","p":639},{"i":642,"t":"On this page","s":"Token revocation API","u":"/docs/3/pro/token_revocation","h":"","p":641},{"i":644,"t":"By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart. But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/3/pro/token_revocation","h":"#how-it-works","p":641},{"i":646,"t":"Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/3/pro/token_revocation","h":"#configure","p":641},{"i":648,"t":"Revocation data can be kept in Redis. To enable this configuration should be: { ... \"token_revoke\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/3/pro/token_revocation","h":"#redis-persistence-engine","p":641},{"i":650,"t":"Revocation data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"token_revoke\": { \"persistence_engine\": \"database\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/3/pro/token_revocation","h":"#database-persistence-engine","p":641},{"i":652,"t":"Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT). caution This API assumes that JWTs you are using contain \"jti\" claim which is a unique token ID (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"revoke_token\", \"params\": {\"uid\": \"xxx-xxx-xxx\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api Revoke token params​ Parameter name Parameter type Required Description uid string yes Token unique ID (JTI claim in case of JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). Revoke token result​ Empty object at the moment.","s":"Revoke token API","u":"/docs/3/pro/token_revocation","h":"#revoke-token-api","p":641},{"i":654,"t":"Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application. caution This API assumes that JWTs you are using contain \"iat\" claim which is a time token was issued at (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"invalidate_user_tokens\", \"params\": {\"user\": \"test\", \"issued_before\": 1635845022, \"expire_at\": 1635845122}}' \\ http://localhost:8000/api Invalidate user tokens params​ Parameter name Parameter type Required Description user string yes User ID to invalidate tokens for issued_before int yes All tokens issued at before this time will be considered revoked (in case of JWT this requires iat to be properly set in JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). Invalidate user tokens result​ Empty object at the moment.","s":"Invalidate user tokens API","u":"/docs/3/pro/token_revocation","h":"#invalidate-user-tokens-api","p":641},{"i":656,"t":"On this page","s":"User and channel tracing","u":"/docs/3/pro/tracing","h":"","p":655},{"i":658,"t":"It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing. curl -X POST http://localhost:8000/admin/trace -H \"Authorization: token \" -d '{\"type\": \"user\", \"entity\": \"56\"}' -o trace.txt Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future.","s":"Save to a file","u":"/docs/3/pro/tracing","h":"#save-to-a-file","p":655},{"i":660,"t":"On this page","s":"User blocking API","u":"/docs/3/pro/user_block","h":"","p":659},{"i":662,"t":"By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart. But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/3/pro/user_block","h":"#how-it-works","p":659},{"i":664,"t":"User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/3/pro/user_block","h":"#configure","p":659},{"i":666,"t":"Blocking data can be kept in Redis. To enable this configuration should be: { ... \"user_block\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/3/pro/user_block","h":"#redis-persistence-engine","p":659},{"i":668,"t":"Blocking data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"user_block\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/3/pro/user_block","h":"#database-persistence-engine","p":659},{"i":670,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"block_user\", \"params\": {\"user\": \"2695\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api Block user params​ Parameter name Parameter type Required Description user string yes User ID to block expire_at int no Unix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache). Block user result​ Empty object at the moment.","s":"Block user API","u":"/docs/3/pro/user_block","h":"#block-user-api","p":659},{"i":672,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"unblock_user\", \"params\": {\"user\": \"2695\"}}' \\ http://localhost:8000/api Unblock user params​ Parameter name Parameter type Required Description user string yes User ID to unblock Unblock user result​ Empty object at the moment.","s":"Unblock user API","u":"/docs/3/pro/user_block","h":"#unblock-user-api","p":659},{"i":674,"t":"On this page","s":"User connections API","u":"/docs/3/pro/user_connections","h":"","p":673},{"i":676,"t":"On this page","s":"User status","u":"/docs/3/pro/user_status","h":"","p":675},{"i":678,"t":"Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript: await centrifuge.namedRPC('update_user_status', {}); note Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example. This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.","s":"Client-side status update RPC","u":"/docs/3/pro/user_status","h":"#client-side-status-update-rpc","p":675},{"i":680,"t":"It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"update_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Update user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to update status for Update user status result​ Empty object at the moment.","s":"update_user_status server API","u":"/docs/3/pro/user_status","h":"#update_user_status-server-api","p":675},{"i":682,"t":"Now on a backend side you have access to a bulk API to effectively get status of particular users. Call RPC method of server API (over HTTP or GRPC): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"get_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api You should get a response like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\", \"active\":1627107289, \"online\":1627107289 } ] }} In case information about last status update time not available the response will be like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\" } ] }} I.e. status object will present in a response but active field won't be set for status object. Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time). Get user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to get status for Get user status result​ Field name Field type Optional Description statuses array of UserStatus no Statuses for each user in params (same order) UserStatus​ Field name Field type Optional Description user string no User ID active integer yes Last active time (Unix seconds) online integer yes Last online time (Unix seconds)","s":"get_user_status server API","u":"/docs/3/pro/user_status","h":"#get_user_status-server-api","p":675},{"i":684,"t":"If you need to clear user status information for some reason there is a delete_user_status server API call: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"delete_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Delete user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to delete status for Delete user status result​ Empty object at the moment.","s":"delete_user_status server API","u":"/docs/3/pro/user_status","h":"#delete_user_status-server-api","p":675},{"i":686,"t":"To enable Redis active status feature: config.json { ... \"redis_active_status\": { \"enabled\": true, \"redis_address\": \"127.0.0.1:6379\" }} Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_active_status\": { \"enabled\": true, \"use_redis_from_engine\": true, }} In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine. expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day: config.json { ... \"redis_active_status\": { ... \"expire_interval\": \"24h\" }}","s":"Configuration","u":"/docs/3/pro/user_status","h":"#configuration","p":675},{"i":688,"t":"On this page","s":"Channels","u":"/docs/3/server/channels","h":"","p":687},{"i":690,"t":"Only ASCII symbols must be used in channel string. Channel name length limited by 255 characters by default (can be changed via configuration file option channel_max_length). Several symbols in channel names reserved for Centrifugo internal needs: : – for namespace channel boundary (see below) $ – for private channel prefix (see below) # – for user channel boundary (see below) * – for the future Centrifugo needs & – for the future Centrifugo needs / – for the future Centrifugo needs","s":"Channel name rules","u":"/docs/3/server/channels","h":"#channel-name-rules","p":687},{"i":692,"t":": – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about available channel options and more about namespaces below. If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public. info A namespace is part of the channel name. If a user subscribed to a channel with namespace, like public:chat – then you need to publish messages into public:chat channel to be delivered to the user. We often see some confusion from developers trying to publish messages into chat and thinking that namespace is somehow stripped upon subscription. It's not true.","s":"namespace boundary (:)","u":"/docs/3/server/channels","h":"#namespace-boundary-","p":687},{"i":694,"t":"If the channel starts with $ then it is considered private. The subscription on a private channel must be properly signed by your backend. Use private channels if you pass sensitive data inside the channel and want to control access permissions on your backend. For example $secrets is a private channel, $public:chat - is a private channel that belongs to namespace public. Subscription request to private channels requires additional JWT from your application backend. Read detailed chapter about private channels. If you need a personal channel for a single user (or maybe a channel for a short and stable set of users) then consider using a user-limited channel (see below) as a simpler alternative that does not require an additional subscription token from your backend. Also, consider using server-side subscriptions or subscribe proxy feature of Centrifugo to model channels with restrictive access.","s":"private channel prefix ($)","u":"/docs/3/server/channels","h":"#private-channel-prefix-","p":687},{"i":696,"t":"# – is a user channel boundary. This is a separator to create personal channels for users (we call this user-limited channels) without the need to provide a subscription token. For example, if the channel is news#42 then the only user with ID 42 can subscribe to this channel (Centrifugo knows user ID because clients provide it in connection credentials with connection JWT). If you want to create a user-limited channel in namespace personal then you can use a name like personal:user#42 for example. tip Channel like $personal:user#42 - i.e. channel with both private prefix $ and user channel boundary # does not have a lot of sense, most probably you can just use personal:user#42 as the ID of the user protected by authentication JWT or set by application backend when the connect proxy feature is used. Moreover, you can provide several user IDs in channel name separated by a comma: dialog#42,43 – in this case only the user with ID 42 and user with ID 43 will be able to subscribe on this channel. This is useful for channels with a static list of allowed users, for example for single user personal messages channel, for dialog channel between certainly defined users. As soon as you need to manage access to a channel dynamically for many users this channel type does not suit well.","s":"user channel boundary (#)","u":"/docs/3/server/channels","h":"#user-channel-boundary-","p":687},{"i":698,"t":"Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.","s":"Channel options","u":"/docs/3/server/channels","h":"#channel-options","p":687},{"i":700,"t":"publish (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). Keep in mind that your application will never receive such messages. In an idiomatic use case, all messages must be published to Centrifugo by an application backend using Centrifugo API (HTTP or GRPC). Or using publish proxy. Since in those cases your application has a chance to validate a message, save it into a database, and only after that broadcast to all subscribers. But the publish option still can be useful to send something without backend-side validation and saving it into a database. This option can also be handy for demos and quick prototyping real-time app ideas.","s":"publish","u":"/docs/3/server/channels","h":"#publish","p":687},{"i":702,"t":"subscribe_to_publish (boolean, default false) - when the publish option is enabled client can publish into a channel without being subscribed to it. This option enables an automatic check that the client subscribed to a channel before allowing a client to publish.","s":"subscribe_to_publish","u":"/docs/3/server/channels","h":"#subscribe_to_publish","p":687},{"i":704,"t":"anonymous (boolean, default false) – this option enables anonymous user access (i.e. for a user with an empty user ID). In most situations, your application works with authenticated users so every user has its unique user ID (set inside JWT sub claim or provided by backend when using connect proxy). But if you provide real-time features for public access you may need unauthenticated access to some channels. Turn on this option and use an empty string as a user ID. See also related global option client_anonymous which allows anonymous users to connect without JWT.","s":"anonymous","u":"/docs/3/server/channels","h":"#anonymous","p":687},{"i":706,"t":"presence (boolean, default false) – enable/disable online presence information for channels. Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels. Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk).","s":"presence","u":"/docs/3/server/channels","h":"#presence","p":687},{"i":708,"t":"presence_disable_for_client (boolean, default false) – allows making presence calls available only for a server-side API. By default, presence information is available for both client and server-side APIs.","s":"presence_disable_for_client","u":"/docs/3/server/channels","h":"#presence_disable_for_client","p":687},{"i":710,"t":"join_leave (boolean, default false) – enable/disable sending join(leave) messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info. caution Keep in mind that join/leave messages can generate a big number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature or to scale Centrifugo.","s":"join_leave","u":"/docs/3/server/channels","h":"#join_leave","p":687},{"i":712,"t":"history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted. Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below). caution Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk).","s":"history_size","u":"/docs/3/server/channels","h":"#history_size","p":687},{"i":714,"t":"history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision). As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels. By default history TTL duration is zero – this means that channel history is disabled. Again – to turn on history you should wisely configure both history_size and history_ttl options. For example for top-level channels (which do not belong to a namespace): config.json { ... \"history_size\": 10, \"history_ttl\": \"60s\"}","s":"history_ttl","u":"/docs/3/server/channels","h":"#history_ttl","p":687},{"i":716,"t":"position (boolean, default false) – when the position feature is on Centrifugo tries to compensate at most once delivery of PUB/SUB messages checking client position inside a stream. If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API. position option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream).","s":"position","u":"/docs/3/server/channels","h":"#position","p":687},{"i":718,"t":"recover (boolean, default false) – when enabled Centrifugo will try to recover missed publications after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the position option described above. recover option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages). Also, note that not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.","s":"recover","u":"/docs/3/server/channels","h":"#recover","p":687},{"i":720,"t":"history_disable_for_client (boolean, default false) – allows making history available only for a server-side API. By default false – i.e. history calls are available for both client and server-side APIs. note History recovery mechanism if enabled will continue to work for clients anyway even if history_disable_for_client is on.","s":"history_disable_for_client","u":"/docs/3/server/channels","h":"#history_disable_for_client","p":687},{"i":722,"t":"protected (boolean, default false) – when on will prevent a client to subscribe to arbitrary channels in a namespace. In this case, Centrifugo will only allow a client to subscribe on user-limited channels, on channels returned by the proxy response, or channels listed inside JWT. Client-side subscriptions to arbitrary channels will be rejected with PermissionDenied error. Server-side channels belonging to the protected namespace passed by the client itself during connect will be ignored.","s":"protected","u":"/docs/3/server/channels","h":"#protected","p":687},{"i":724,"t":"proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter","s":"proxy_subscribe","u":"/docs/3/server/channels","h":"#proxy_subscribe","p":687},{"i":726,"t":"proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter","s":"proxy_publish","u":"/docs/3/server/channels","h":"#proxy_publish","p":687},{"i":728,"t":"subscribe_proxy_name (string, default \"\") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.","s":"subscribe_proxy_name","u":"/docs/3/server/channels","h":"#subscribe_proxy_name","p":687},{"i":730,"t":"publish_proxy_name (string, default \"\") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.","s":"publish_proxy_name","u":"/docs/3/server/channels","h":"#publish_proxy_name","p":687},{"i":732,"t":"Let's look at how to set some of these options in a config: config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"anonymous\": true, \"publish\": true, \"subscribe_to_publish\": true, \"presence\": true, \"join_leave\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"recover\": true} Here we set channel options on config top-level – these options will affect channels without namespace. Below we describe namespaces and how to define channel options for a namespace.","s":"Channel options config example","u":"/docs/3/server/channels","h":"#channel-options-config-example","p":687},{"i":734,"t":"It's possible to configure a list of channel namespaces. Namespaces are optional but very useful. A namespace allows setting custom options for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application. Namespace has a name, and the same channel options (with the same defaults) as described above. name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$). If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator: public:messages gossips:messages Where public and gossips are namespace names. Centrifugo will look for : symbol in the channel name, will extract the namespace name, and will apply namespace options whenever required. All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured: config.json { \"token_hmac_secret_key\": \"very-long-secret-key\", \"api_key\": \"secret-api-key\", \"anonymous\": true, \"publish\": true, \"presence\": true, \"join_leave\": true, \"history_size\": 10, \"history_ttl\": \"30s\", \"namespaces\": [ { \"name\": \"public\", \"publish\": true, \"anonymous\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"recover\": true }, { \"name\": \"gossips\", \"presence\": true, \"join_leave\": true } ]} Channel news will use globally defined channel options. Channel public:news will use public namespace options. Channel gossips:news will use gossips namespace options. Channel xxx:hello will result into subscription error since there is no xxx namespace defined in configuration above. Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2. note There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly.","s":"Channel namespaces","u":"/docs/3/server/channels","h":"#channel-namespaces","p":687},{"i":736,"t":"On this page","s":"Client authentication","u":"/docs/3/server/authentication","h":"","p":735},{"i":738,"t":"Centrifugo uses the following claims in a JWT: sub, exp, iat, jti, info, b64info, channels, subs.","s":"Claims","u":"/docs/3/server/authentication","h":"#claims","p":735},{"i":740,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). If a user is not currently authenticated in an application, but you want to let him connect to Centrifugo anyway – you can use an empty string as a user ID in sub claim. This is called anonymous access. In this case, the anonymous option must be enabled in Centrifugo configuration for channels that the client will subscribe to.","s":"sub","u":"/docs/3/server/authentication","h":"#sub","p":735},{"i":742,"t":"This is a UNIX timestamp seconds when the token will expire. This is a standard JWT claim - all JWT libraries for different languages provide an API to set it. If exp claim is not provided then Centrifugo won't expire connection. When provided special algorithm will find connections with exp in the past and activate the connection refresh mechanism. Refresh mechanism allows connection to survive and be prolonged. In case of refresh failure, the client connection will be eventually closed by Centrifugo and won't be accepted until new valid and actual credentials are provided in the connection token. You can use the connection expiration mechanism in cases when you don't want users of your app to be subscribed on channels after being banned/deactivated in the application. Or to protect your users from token leakage (providing a reasonably short time of expiration). Choose exp value wisely, you don't need small values because the refresh mechanism will hit your application often with refresh requests. But setting this value too large can lead to slow user connection deactivation. This is a trade-off. Read more about connection expiration below.","s":"exp","u":"/docs/3/server/authentication","h":"#exp","p":735},{"i":744,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/3/server/authentication","h":"#iat","p":735},{"i":746,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/3/server/authentication","h":"#jti","p":735},{"i":748,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But you can force this check by setting token_audience string option: config.json { \"token_audience\": \"centrifugo\"} caution Setting token_audience will also affect subscription tokens (used for private channels).","s":"aud","u":"/docs/3/server/authentication","h":"#aud","p":735},{"i":750,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But you can force this check by setting token_issuer string option: config.json { \"token_issuer\": \"my_app\"} caution Setting token_issuer will also affect subscription tokens (used for private channels).","s":"iss","u":"/docs/3/server/authentication","h":"#iss","p":735},{"i":752,"t":"This claim is optional - this is additional information about client connection that can be provided for Centrifugo. This information will be included in presence information, join/leave events, and channel publication if it was published from a client-side.","s":"info","u":"/docs/3/server/authentication","h":"#info","p":735},{"i":754,"t":"If you are using binary Protobuf protocol you may want info to be custom bytes. Use this field in this case. This field contains a base64 representation of your bytes. After receiving Centrifugo will decode base64 back to bytes and will embed the result into various places described above.","s":"b64info","u":"/docs/3/server/authentication","h":"#b64info","p":735},{"i":756,"t":"An optional array of strings with server-side channels to subscribe a client to. See more details about server-side subscriptions. caution By providing a list of channels in JWT with channels claim you are not making them automatically unaccessible by other users. Other users can still call a client-side .subscribe() method and subscribe to these channels if channel permissions allow doing this. If you need to protect channels from being subscribed by other connections then you can use private channels inside this channels array (i.e. starting with $) or turn on protected option for channels namespaces.","s":"channels","u":"/docs/3/server/authentication","h":"#channels","p":735},{"i":758,"t":"An optional map of channels with options. This is like a channels claim but allows more control over server-side subscription since every channel can be annotated with info, data, and so on using options. tip This claim is called subs as a shortcut from subscriptions. The claim sub described above is a standart JWT claim to provide a user ID (it's a shortcut from subject). While claims have similar names they have different purpose in a connection JWT. caution By providing a map of channels in JWT with subs claim you are not making channels automatically unaccessible by other users. Other users can still call a client-side .subscribe() method and subscribe to these channels if channel permissions allow doing this. If you need to protect channels from being subscribed by other connections then you can use private channels inside this subs map (i.e. starting with $) or turn on protected option for channels namespaces. Example: { ... \"subs\": { \"channel1\": { \"data\": {\"welcome\": \"welcome to channel1\"} }, \"channel2\": { \"data\": {\"welcome\": \"welcome to channel2\"} } }} Subscribe options:​ Field Type Optional Description info JSON object yes Custom channel info b64info string yes Custom channel info in Base64 - to pass binary channel info data JSON object yes Custom JSON data to return in subscription context inside Connect reply b64data string yes Same as data but in Base64 to send binary data override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false}","s":"subs","u":"/docs/3/server/authentication","h":"#subs","p":735},{"i":760,"t":"Meta is an additional JSON object (ex. {\"key\": \"value\"}) that will be attached to a connection. Unlike info it's never exposed to clients and only accessible on a backend side. It will be included in proxy calls from Centrifugo to the application backend. Also, there is a get_user_connections API method in Centrifugo PRO that returns this data in the user connection object.","s":"meta","u":"/docs/3/server/authentication","h":"#meta","p":735},{"i":762,"t":"By default, Centrifugo looks on exp claim to configure connection expiration. In most cases this is fine, but there could be situations where you wish to decouple token expiration check with connection expiration time. As soon as the expire_at claim is provided (set) in JWT Centrifugo relies on it for setting connection expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the connection should expire. Set it to the future time for expiring connection at some point Set it to 0 to disable connection expiration (but still check token exp claim).","s":"expire_at","u":"/docs/3/server/authentication","h":"#expire_at","p":735},{"i":764,"t":"As said above exp claim in a connection token allows expiring client connection at some point in time. Let's look in detail at what happens when Centrifugo detects that the connection is going to expire. First, you should do is enable client expiration mechanism in Centrifugo providing a connection JWT with expiration: import jwtimport timetoken = jwt.encode({\"sub\": \"42\", \"exp\": int(time.time()) + 10*60}, \"secret\").decode()print(token) Let's suppose that you set exp field to timestamp that will expire in 10 minutes and the client connected to Centrifugo with this token. During 10 minutes the connection will be kept by Centrifugo. When this time passed Centrifugo gives the connection some time (configured, 25 seconds by default) to refresh its credentials and provide a new valid token with new exp. When a client first connects to Centrifugo it receives the ttl value in connect reply. That ttl value contains the number of seconds after which the client must send the refresh command with new credentials to Centrifugo. Centrifugo clients must handle this ttl field and automatically start the refresh process. For example, a Javascript browser client will send an AJAX POST request to your application when it's time to refresh credentials. By default, this request goes to /centrifuge/refresh URL endpoint. In response your server must return JSON with a new connection JWT: { \"token\": token} So you must just return the same connection JWT for your user when rendering the page initially. But with actual valid exp. Javascript client will then send them to Centrifugo server and connection will be refreshed for a time you set in exp. In this case, you know which user wants to refresh its connection because this is just a general request to your app - so your session mechanism will tell you about the user. If you don't want to refresh the connection for this user - just return 403 Forbidden on refresh request to your application backend. Javascript client also has options to hook into a refresh mechanism to implement your custom way of refreshing. Other Centrifugo clients also should have hooks to refresh credentials but depending on client API for this can be different - see specific client docs.","s":"Connection expiration","u":"/docs/3/server/authentication","h":"#connection-expiration","p":735},{"i":766,"t":"Let's look at how to generate connection HS256 JWT in Python:","s":"Examples","u":"/docs/3/server/authentication","h":"#examples","p":735},{"i":768,"t":"Python NodeJS import jwttoken = jwt.encode({\"sub\": \"42\"}, \"secret\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret');console.log(token); Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users. Then you can pass this token to your client side and use it when connecting to Centrifugo: Using centrifuge-js v2.x var centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\");centrifuge.setToken(token);centrifuge.connect();","s":"Simplest token","u":"/docs/3/server/authentication","h":"#simplest-token","p":735},{"i":770,"t":"HS256 token that will be valid for 5 minutes: Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"exp\": int(time.time()) + 5*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret', { expiresIn: 5 * 60 });console.log(token);","s":"Token with expiration","u":"/docs/3/server/authentication","h":"#token-with-expiration","p":735},{"i":772,"t":"Let's attach user name: Python NodeJS import jwtclaims = {\"sub\": \"42\", \"info\": {\"name\": \"Alexander Emelin\"}}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42', info: {\"name\": \"Alexander Emelin\"} }, 'secret');console.log(token);","s":"Token with additional connection info","u":"/docs/3/server/authentication","h":"#token-with-additional-connection-info","p":735},{"i":774,"t":"You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.","s":"Investigating problems with JWT","u":"/docs/3/server/authentication","h":"#investigating-problems-with-jwt","p":735},{"i":776,"t":"Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT). A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address). As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels. At the moment Centrifugo caches keys loaded from an endpoint for one hour. Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment). Only RSA algorithm is supported. JWKS support enabled both connection and private channel subscription tokens.","s":"JSON Web Key support","u":"/docs/3/server/authentication","h":"#json-web-key-support","p":735},{"i":778,"t":"On this page","s":"Error and disconnect codes","u":"/docs/3/server/codes","h":"","p":777},{"i":780,"t":"Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client. Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).","s":"Client error codes","u":"/docs/3/server/codes","h":"#client-error-codes","p":777},{"i":782,"t":"Code: 100, Message: \"internal server error\". Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.","s":"Internal","u":"/docs/3/server/codes","h":"#internal","p":777},{"i":784,"t":"Code: 101, Message: \"unauthorized\". Error Unauthorized says that request is unauthorized.","s":"Unauthorized","u":"/docs/3/server/codes","h":"#unauthorized","p":777},{"i":786,"t":"Code: 102, Message: \"unknown channel\". Error Unknown Channel means that channel name does not exist. Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.","s":"Unknown Channel","u":"/docs/3/server/codes","h":"#unknown-channel","p":777},{"i":788,"t":"Code: 103, Message: \"permission denied\". Error Permission Denied means that access to resource not allowed.","s":"Permission Denied","u":"/docs/3/server/codes","h":"#permission-denied","p":777},{"i":790,"t":"Code: 104, Message: \"method not found\". Error Method Not Found means that method sent in command does not exist.","s":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found","p":777},{"i":792,"t":"Code: 105, Message: \"already subscribed\". Error Already Subscribed returned when client wants to subscribe on channel it already subscribed to.","s":"Already Subscribed","u":"/docs/3/server/codes","h":"#already-subscribed","p":777},{"i":794,"t":"Code: 106, Message: \"limit exceeded\". Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.","s":"Limit Exceeded","u":"/docs/3/server/codes","h":"#limit-exceeded","p":777},{"i":796,"t":"Code: 107, Message: \"bad request\". Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.","s":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request","p":777},{"i":798,"t":"Code: 108, Message: \"not available\". Error Not Available means that resource is not enabled. For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.","s":"Not Available","u":"/docs/3/server/codes","h":"#not-available","p":777},{"i":800,"t":"Code: 109, Message: \"token expired\". Error Token Expired indicates that connection token expired.","s":"Token Expired","u":"/docs/3/server/codes","h":"#token-expired","p":777},{"i":802,"t":"Code: 110, Message: \"expired\". Error Expired indicates that connection expired (no token involved).","s":"Expired","u":"/docs/3/server/codes","h":"#expired","p":777},{"i":804,"t":"Code: 111, Message: \"too many requests\". Error Too Many Requests means that server rejected request due to rate limiting strategies.","s":"Too Many Requests","u":"/docs/3/server/codes","h":"#too-many-requests","p":777},{"i":806,"t":"Code: 112, Message: \"unrecoverable position\". Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch passed.","s":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position","p":777},{"i":808,"t":"Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes). note We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. Actually most client connectors don't provide access to reading a disconnect code these days (only a reason). This is what we are planning to improve.","s":"Client disconnect codes","u":"/docs/3/server/codes","h":"#client-disconnect-codes","p":777},{"i":810,"t":"Code: 3000. DisconnectNormal is clean disconnect when client cleanly closed connection. This is mostly useful for server metrics, since client never receives this disconnect code (since already gone).","s":"Normal","u":"/docs/3/server/codes","h":"#normal","p":777},{"i":812,"t":"Code: 3001, Reason: \"shutdown\", Reconnect: true. Disconnect Shutdown sent when node is going to shut down.","s":"Shutdown","u":"/docs/3/server/codes","h":"#shutdown","p":777},{"i":814,"t":"Code: 3002, Reason: \"invalid token\", Reconnect: false. Disconnect Invalid Token sent when client came with invalid token.","s":"Invalid Token","u":"/docs/3/server/codes","h":"#invalid-token","p":777},{"i":816,"t":"Code: 3003, Reason: \"bad request\", Reconnect: false. Disconnect Bad Request sent when client uses malformed protocol","s":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-1","p":777},{"i":818,"t":"Code: 3004, Reason: \"internal server error\", Reconnect: true. Disconnect Server Error sent when internal error occurred on server.","s":"Server Error","u":"/docs/3/server/codes","h":"#server-error","p":777},{"i":820,"t":"Code: 3005, Reason: \"expired\", Reconnect: true. Disconnect Expired sent when client connection expired.","s":"Expired","u":"/docs/3/server/codes","h":"#expired-1","p":777},{"i":822,"t":"Code: 3006, Reason: \"subscription expired\", Reconnect: true. Disconnect Subscription Expired sent when client subscription expired.","s":"Subscription Expired","u":"/docs/3/server/codes","h":"#subscription-expired","p":777},{"i":824,"t":"Code: 3007, Reason: \"stale\", Reconnect: false. Disconnect Stale sent to close connection that did not become authenticated in configured interval after dialing. Usually this means a broken client implementation.","s":"Stale","u":"/docs/3/server/codes","h":"#stale","p":777},{"i":826,"t":"Code: 3008, Reason: \"slow\", Reconnect: true. Disconnect Slow sent when a client can't read messages fast enough.","s":"Slow","u":"/docs/3/server/codes","h":"#slow","p":777},{"i":828,"t":"Code: 3009, Reason: \"write error\", Reconnect: true. Disconnect Write Error sent when an error occurred while writing to client connection.","s":"Write Error","u":"/docs/3/server/codes","h":"#write-error","p":777},{"i":830,"t":"Code: 3010, Reason: \"insufficient state\", Reconnect: true. Disconnect Insufficient State sent when server detects wrong client position in channel Publication stream. Disconnect allows client to restore missed publications on reconnect.","s":"Insufficient State","u":"/docs/3/server/codes","h":"#insufficient-state","p":777},{"i":832,"t":"Code: 3011, Reason: \"force reconnect\", Reconnect: true. Disconnect Force Reconnect sent when server disconnects connection but want it to return back shortly.","s":"Force Reconnect","u":"/docs/3/server/codes","h":"#force-reconnect","p":777},{"i":834,"t":"Code: 3012, Reason: \"force disconnect\", Reconnect: false. Disconnect Force No Reconnect sent when server disconnects connection and asks it to not reconnect again.","s":"Force No Reconnect","u":"/docs/3/server/codes","h":"#force-no-reconnect","p":777},{"i":836,"t":"Code: 3013, Reason: \"connection limit\", Reconnect: false. Disconnect Connection Limit can be sent when client connection exceeds a configured connection limit (per user ID or due to other rule).","s":"Connection Limit","u":"/docs/3/server/codes","h":"#connection-limit","p":777},{"i":838,"t":"Server API errors are errors that can be returned to a API caller in replies to commands (in both HTTP and GRPC server APIs).","s":"Server API error codes","u":"/docs/3/server/codes","h":"#server-api-error-codes","p":777},{"i":840,"t":"Code: 100, Message: \"internal server error\". ErrorInternal means server error, if returned this is a signal that something went wrong with Centrifugo itself.","s":"Internal","u":"/docs/3/server/codes","h":"#internal-1","p":777},{"i":842,"t":"Code: 102, Message: \"unknown channel\". Error Unknown Channel means that namespace in channel name does not exist.","s":"Unknown channel","u":"/docs/3/server/codes","h":"#unknown-channel-1","p":777},{"i":844,"t":"Code: 104, Message: \"method not found\". Error Method Not Found means that method sent in command does not exist in Centrifugo.","s":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found-1","p":777},{"i":846,"t":"Code: 107, Message: \"bad request\". Error Bad Request says that Centrifugo can not parse received data because it is malformed or there are required fields missing.","s":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-2","p":777},{"i":848,"t":"Code: 108, Message: \"not available\". Error Not Available means that resource is not enabled.","s":"Not Available","u":"/docs/3/server/codes","h":"#not-available-1","p":777},{"i":850,"t":"Code: 112, Message: \"unrecoverable position\". ErrorUnrecoverablePosition means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch.","s":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position-1","p":777},{"i":852,"t":"On this page","s":"Admin web UI","u":"/docs/3/server/admin_web","h":"","p":851},{"i":854,"t":"admin (boolean, default: false) – enables/disables admin web UI admin_password (string, default: \"\") – this is a password to log into admin web interface admin_secret (string, default: \"\") - this is a secret key for authentication token set on successful login. Make both admin_password and admin_secret strong and keep them in secret. After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface. tip Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.","s":"Options","u":"/docs/3/server/admin_web","h":"#options","p":851},{"i":856,"t":"If you want to use custom web interface you can specify path to web interface directory dist: config.json { ..., \"admin\": true, \"admin_password\": \"\", \"admin_secret\": \"\", \"admin_web_path\": \"\"} This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.","s":"Using custom web interface","u":"/docs/3/server/admin_web","h":"#using-custom-web-interface","p":851},{"i":858,"t":"There is also an option to run Centrifugo in insecure admin mode - in this case you don't need to set admin_password and admin_secret in config – in web interface you will be logged in automatically without any password. Note that this is only an option for production if you protected admin web interface with firewall rules. Otherwise anyone in internet will have full access to admin functionality described above. To enable insecure admin mode: config.json { ..., \"admin\": true, \"admin_insecure\": true, \"admin_password\": \"\", \"admin_secret\": \"\"}","s":"Admin insecure mode","u":"/docs/3/server/admin_web","h":"#admin-insecure-mode","p":851},{"i":860,"t":"On this page","s":"Console commands","u":"/docs/3/server/console_commands","h":"","p":859},{"i":862,"t":"To show Centrifugo version and exit run: centrifugo version","s":"version command","u":"/docs/3/server/console_commands","h":"#version-command","p":859},{"i":864,"t":"Centrifugo has special command to check configuration file checkconfig: centrifugo checkconfig --config=config.json If any errors found during validation – program will exit with error message and exit code 1.","s":"checkconfig command","u":"/docs/3/server/console_commands","h":"#checkconfig-command","p":859},{"i":866,"t":"Another command is genconfig: centrifugo genconfig -c config.json It will automatically generate the minimal required configuration file. If any errors happen – program will exit with error message and exit code 1. genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file: centrifugo genconfig -c config.toml","s":"genconfig command","u":"/docs/3/server/console_commands","h":"#genconfig-command","p":859},{"i":868,"t":"Another command is gentoken: centrifugo gentoken -c config.json -u 28282 It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1.","s":"gentoken command","u":"/docs/3/server/console_commands","h":"#gentoken-command","p":859},{"i":870,"t":"One more command is checktoken: centrifugo checktoken -c config.json It will validate your connection JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1.","s":"checktoken command","u":"/docs/3/server/console_commands","h":"#checktoken-command","p":859},{"i":872,"t":"On this page","s":"Configure Centrifugo","u":"/docs/3/server/configuration","h":"","p":871},{"i":874,"t":"Centrifugo can be configured in several ways.","s":"Configuration sources","u":"/docs/3/server/configuration","h":"#configuration-sources","p":871},{"i":876,"t":"Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer environment or configuration file sources. Command-line options have the highest priority when set than other ways to configure Centrifugo.","s":"Command-line flags","u":"/docs/3/server/configuration","h":"#command-line-flags","p":871},{"i":878,"t":"All Centrifugo options can be set over env in the format CENTRIFUGO_ (i.e. option name with CENTRIFUGO_ prefix, all in uppercase). Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags. Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use \"true\" value for an environment variable (or simply \"1\"). To set false use \"false\" or \"0\". Example: export CENTRIFUGO_PROMETHEUS=\"1\" Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example: export CENTRIFUGO_ALLOWED_ORIGINS=\"https://mysite1.example.com https://mysite2.example.com\" For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name. Empty environment variables are considered unset (!) and will fall back to the next configuration source.","s":"OS environment variables","u":"/docs/3/server/configuration","h":"#os-environment-variables","p":871},{"i":880,"t":"Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable prevails over the same option in config file). A simple way to start with Centrifugo is to run: centrifugo genconfig This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo: centrifugo -c config.json","s":"Configuration file","u":"/docs/3/server/configuration","h":"#configuration-file","p":871},{"i":882,"t":"Centrifugo supports three configuration file formats: JSON, YAML, or TOML.","s":"Config file formats","u":"/docs/3/server/configuration","h":"#config-file-formats","p":871},{"i":884,"t":"Here is an example of Centrifugo JSON configuration file: config.json { \"allowed_origins\": [\"http://localhost:3000\"], \"token_hmac_secret_key\": \"\", \"api_key\": \"\"} token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT. api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients. allowed_origins option described below.","s":"JSON config format","u":"/docs/3/server/configuration","h":"#json-config-format","p":871},{"i":886,"t":"Centrifugo also supports TOML format for configuration file: centrifugo --config=config.toml Where config.toml contains: config.toml allowed_origins: [ \"http://localhost:3000\" ]token_hmac_secret_key = \"\"api_key = \"\"log_level = \"debug\" In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.","s":"TOML config format","u":"/docs/3/server/configuration","h":"#toml-config-format","p":871},{"i":888,"t":"YAML format is also supported: config.yaml allowed_origins: - \"http://localhost:3000\"token_hmac_secret_key: \"\"api_key: \"\"log_level: debug With YAML remember to use spaces, not tabs when writing a configuration file.","s":"YAML config format","u":"/docs/3/server/configuration","h":"#yaml-config-format","p":871},{"i":890,"t":"Let's describe some important options you can configure when running Centrifugo.","s":"Important options","u":"/docs/3/server/configuration","h":"#important-options","p":871},{"i":892,"t":"This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins. As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array. Connection requests without Origin header set are passing through without any checks (i.e. always allowed). For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way: \"allowed_origins\": [ \"http://localhost:3000\"] When connecting from https://example.com: \"allowed_origins\": [ \"https://example.com\"] Origin pattern can contain wildcard symbol * to match subdomains: \"allowed_origins\": [ \"https://*.example.com\"] – in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check. It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature): \"allowed_origins\": [ \"*\"]","s":"allowed_origins","u":"/docs/3/server/configuration","h":"#allowed_origins","p":871},{"i":894,"t":"Bind your Centrifugo to a specific interface address (string, by default \"\" - listen on all available interfaces).","s":"address","u":"/docs/3/server/configuration","h":"#address","p":871},{"i":896,"t":"Port to bind Centrifugo to (string, by default \"8000\").","s":"port","u":"/docs/3/server/configuration","h":"#port","p":871},{"i":898,"t":"Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.","s":"engine","u":"/docs/3/server/configuration","h":"#engine","p":871},{"i":900,"t":"These options allow tweaking server behavior, in most cases default values are good to start with.","s":"Advanced options","u":"/docs/3/server/configuration","h":"#advanced-options","p":871},{"i":902,"t":"Default: 128 Sets the maximum number of different channel subscriptions a single client can have. tip When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.","s":"client_channel_limit","u":"/docs/3/server/configuration","h":"#client_channel_limit","p":871},{"i":904,"t":"Default: 255 Sets the maximum length of the channel name.","s":"channel_max_length","u":"/docs/3/server/configuration","h":"#channel_max_length","p":871},{"i":906,"t":"Default: 0 The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited. The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).","s":"client_user_connection_limit","u":"/docs/3/server/configuration","h":"#client_user_connection_limit","p":871},{"i":908,"t":"Default: 1048576 Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.","s":"client_queue_max_size","u":"/docs/3/server/configuration","h":"#client_queue_max_size","p":871},{"i":910,"t":"Default: false Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID) and only can subscribe to channels with anonymous option enabled.","s":"client_anonymous","u":"/docs/3/server/configuration","h":"#client_anonymous","p":871},{"i":912,"t":"Default: 0 client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently. By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).","s":"client_concurrency","u":"/docs/3/server/configuration","h":"#client_concurrency","p":871},{"i":914,"t":"Default: 0 By default, Centrifugo runs on all available CPU cores. To limit the number of cores Centrifugo can utilize in one moment use this option.","s":"gomaxprocs","u":"/docs/3/server/configuration","h":"#gomaxprocs","p":871},{"i":916,"t":"After Centrifugo started there are several endpoints available.","s":"Endpoint configuration.","u":"/docs/3/server/configuration","h":"#endpoint-configuration","p":871},{"i":918,"t":"Bidirectional WebSocket default endpoint: ws://localhost:8000/connection/websocket Bidirectional SockJS default endpoint (disabled by default): http://localhost:8000/connection/sockjs Unidirectional EventSource endpoint (disabled by default): http://localhost:8000/connection/uni_sse Unidirectional HTTP streaming endpoint (disabled by default): http://localhost:8000/connection/uni_http_stream Unidirectional WebSocket endpoint (disabled by default): http://localhost:8000/connection/uni_websocket Unidirectional EventSource (SSE) endpoint (disabled by default): http://localhost:8000/connection/uni_sse Server HTTP API endpoint: http://localhost:8000/api By default, all endpoints work on port 8000. This can be changed with port option: { \"port\": 9000} In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.","s":"Default endpoints.","u":"/docs/3/server/configuration","h":"#default-endpoints","p":871},{"i":920,"t":"Admin web UI endpoint works on root path by default, i.e. http://localhost:8000. For more details about admin web UI, refer to the Admin web UI documentation.","s":"Admin endpoints.","u":"/docs/3/server/configuration","h":"#admin-endpoints","p":871},{"i":922,"t":"Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config: { ... \"debug\": true} And endpoint: http://localhost:8000/debug/pprof/ – will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.","s":"Debug endpoints.","u":"/docs/3/server/configuration","h":"#debug-endpoints","p":871},{"i":924,"t":"Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag: centrifugo -c config.json --health","s":"Health check endpoint","u":"/docs/3/server/configuration","h":"#health-check-endpoint","p":871},{"i":926,"t":"We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal: API endpoint (/api) - for HTTP API requests Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format Health check endpoint (/health) - used to do health checks Debug endpoints (/debug/pprof) - used to inspect internal server state It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration. Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work. To run internal endpoints on custom port use internal_port option: { ... \"internal_port\": 9000} So admin web interface will work on address: http://localhost:9000 Also, debug page will be available on a new custom port too: http://localhost:9000/debug/pprof/ The same for API and Prometheus endpoints.","s":"Custom internal ports","u":"/docs/3/server/configuration","h":"#custom-internal-ports","p":871},{"i":928,"t":"To disable websocket endpoint set websocket_disable boolean option to true. To disable API endpoint set api_disable boolean option to true.","s":"Disable default endpoints","u":"/docs/3/server/configuration","h":"#disable-default-endpoints","p":871},{"i":930,"t":"It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options: admin_handler_prefix (default \"\") - to control Admin panel URL prefix websocket_handler_prefix (default \"/connection/websocket\") - to control WebSocket URL prefix sockjs_handler_prefix (default \"/connection/sockjs\") - to control SockJS URL prefix uni_sse_handler_prefix (default \"/connection/uni_sse\") - to control unidirectional Eventsource URL prefix uni_http_stream_handler_prefix (default \"/connection/uni_http_stream\") - to control unidirectional HTTP streaming URL prefix uni_websocket_handler_prefix (default \"/connection/uni_websocket\") - to control unidirectional WebSocket URL prefix api_handler_prefix (default \"/api\") - to control HTTP API URL prefix prometheus_handler_prefix (default \"/metrics\") - to control Prometheus URL prefix health_handler_prefix (default \"/health\") - to control health check URL prefix","s":"Customize handler endpoints","u":"/docs/3/server/configuration","h":"#customize-handler-endpoints","p":871},{"i":932,"t":"It's possible to send HUP signal to Centrifugo to reload a configuration: kill -HUP Though at moment this will only reload token secrets and channel options (top-level and namespaces). Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.","s":"Signal handling","u":"/docs/3/server/configuration","h":"#signal-handling","p":871},{"i":935,"t":"The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. This mode can be useful for demo projects based on Centrifugo, local projects, or real-time application prototyping. Don't use it in production.","s":"Insecure client connection","u":"/docs/3/server/configuration","h":"#insecure-client-connection","p":871},{"i":937,"t":"This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules. The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.","s":"Insecure API mode","u":"/docs/3/server/configuration","h":"#insecure-api-mode","p":871},{"i":939,"t":"This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).","s":"Insecure admin mode","u":"/docs/3/server/configuration","h":"#insecure-admin-mode","p":871},{"i":941,"t":"Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use \"5s\". The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option). Valid time units are \"ms\" (milliseconds), \"s\" (seconds), \"m\" (minutes), \"h\" (hours). Some examples: \"1000ms\" // 1000 milliseconds\"1s\" // 1 second\"12h\" // 12 hours\"720h\" // 30 days","s":"Setting time duration options","u":"/docs/3/server/configuration","h":"#setting-time-duration-options","p":871},{"i":943,"t":"While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special: CENTRIFUGO_NAMESPACES='[{\"name\": \"ns1\"}, {\"name\": \"ns2\"}]' ./centrifugo I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.","s":"Setting namespaces over env","u":"/docs/3/server/configuration","h":"#setting-namespaces-over-env","p":871},{"i":945,"t":"Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage. Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal. To disable sending usage stats set usage_stats_disable option: config.json { \"usage_stats_disable\": true}","s":"Anonymous usage stats","u":"/docs/3/server/configuration","h":"#anonymous-usage-stats","p":871},{"i":947,"t":"On this page","s":"Infrastructure tuning","u":"/docs/3/server/infra_tuning","h":"","p":946},{"i":949,"t":"You should increase a max number of open files Centrifugo process can open if you want to handle more connections. To get the current open files limit run: ulimit -n On Linux you can check limits for a running process using: cat /proc//limits The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default. See this document to get more info on how to increase this number. If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536. You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).","s":"Open files limit","u":"/docs/3/server/infra_tuning","h":"#open-files-limit","p":946},{"i":951,"t":"Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default). In order to eliminate a problem you can: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces See a post in Pusher blog about this problem and more detailed solution steps.","s":"Ephemeral port exhaustion","u":"/docs/3/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":946},{"i":953,"t":"On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state. Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side. Look how many socket descriptors in TIME_WAIT state. netstat -an |grep TIME_WAIT | grep | wc -l Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html The advices here are similar to ephemeral port exhaustion problem: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces","s":"Sockets in TIME_WAIT state","u":"/docs/3/server/infra_tuning","h":"#sockets-in-time_wait-state","p":946},{"i":955,"t":"Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established. Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.","s":"Proxy max connections","u":"/docs/3/server/infra_tuning","h":"#proxy-max-connections","p":946},{"i":957,"t":"More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.","s":"Conntrack table","u":"/docs/3/server/infra_tuning","h":"#conntrack-table","p":946},{"i":959,"t":"On this page","s":"History and recovery","u":"/docs/3/server/history_and_recovery","h":"","p":958},{"i":961,"t":"History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero. Centrifugo is not designed to keep publications streams forever. Streams are ephemeral and can expire or can be lost at any moment. But Centrifugo provides a way for an application or a client to understand that stream history lost. In this case, the main application database should be the source of truth and state recovery. When history is on every publication is published into a channel saved into history. Depending on the engine used history stream implementation can differ. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside spaces. Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of truth anymore. tip History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.","s":"History design","u":"/docs/3/server/history_and_recovery","h":"#history-design","p":958},{"i":963,"t":"History iteration based on three fields: limit since reverse Combining these fields you can iterate over a stream in both directions. Get current stream top offset and epoch: history(limit: 0, since: null, reverse: false) Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: false) Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: true) Get history from the current beginning (up to 10): history(limit: 10, since: null, reverse: false) Get history from the current end in reversed direction (up to 10): history(limit: 10, since: null, reverse: true) Get up to 10 publications since known stream position (described by offset and epoch values): history(limit: 10, since: {offset: 0, epoch: \"epoch\"}, reverse: false) Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest): history(limit: 10, since: {offset: 11, epoch: \"epoch\"}, reverse: true) Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }}","s":"History iteration API","u":"/docs/3/server/history_and_recovery","h":"#history-iteration-api","p":958},{"i":965,"t":"One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while. In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnecting at the same time). danger Message recovery protocol feature designed to be used together with reasonably small Publication stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time. To enable recovery mechanism for channels set the recover boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not. The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default. Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client libraries (for bidirectional transport), but it's good to know how recovery works under the hood. The recovery feature heavily relies on offset and epoch values described above. epoch handles cases when history storage has been restarted while the client was in a disconnected state so publication numeration in a channel started from scratch. For example at the moment Memory engine does not persist publication sequences on disk so every restart will start numeration from scratch. After each restart a new epoch field is generated, and we can understand in the recovery process that the client could miss messages thus returning the correct recovered flag in a subscribe Reply. This also applies to the Redis engine – if you do not use AOF with fsync then sequences can be lost after Redis restart. When using the Redis engine you need to use a fully in-memory model strategy or AOF with fsync to guarantee the reliability of the recovered flag sent by Centrifugo. When a server receives subscribe command with the boolean flag recover set to true and offset, epoch set to values last seen by a client (see SubscribeRequest type in protocol definitions) it will try to find all missed publications from history cache. Recovered publications will be passed to the client in a subscribe Reply in the correct order, so your publication handler will be automatically called to process each missed message. You can also manually implement your recovery algorithm on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client reconnects completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.","s":"Automatic message recovery","u":"/docs/3/server/history_and_recovery","h":"#automatic-message-recovery","p":958},{"i":967,"t":"On this page","s":"Engines, scalability","u":"/docs/3/server/engines","h":"","p":966},{"i":969,"t":"Used by default. Supports only one node. Nice choice to start with. Supports all features keeping everything in Centrifugo node process memory. You don't need to install Redis when using this engine. Advantages: Super fast since it does not involve network at all Does not require separate broker setup Disadvantages: Does not allow scaling nodes (actually you still can scale Centrifugo with Memory engine but you have to publish data into each Centrifugo node and you won't have consistent history and presence state throughout Centrifugo nodes) Does not persist message history in channels between Centrifugo restarts","s":"Memory engine","u":"/docs/3/server/engines","h":"#memory-engine","p":966},{"i":971,"t":"history_meta_ttl​ Duration, default 0s. history_meta_ttl sets a time in seconds of history stream metadata expiration. Stream metadata is information about the current offset number in the channel and epoch value. By default, metadata for channels does not expire. Though in some cases – when channels are created for а short time and then not used anymore – created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory leak. Setting a reasonable value to this option (usually much bigger than the history retention period) can help. In this case, unused channel metadata will eventually expire.","s":"Memory engine options","u":"/docs/3/server/engines","h":"#memory-engine-options","p":966},{"i":973,"t":"Redis is an open-source, in-memory data structure store, used as a database, cache, and message broker. Centrifugo Redis engine allows scaling Centrifugo nodes to different machines. Nodes will use Redis as a message broker (utilizing Redis PUB/SUB for node communication) and keep presence and history data in Redis. Minimal Redis version is 5.0.1","s":"Redis engine","u":"/docs/3/server/engines","h":"#redis-engine","p":966},{"i":975,"t":"Several configuration options related to Redis engine. redis_address​ String, default \"127.0.0.1:6379\" - Redis server address. redis_password​ String, default \"\" - Redis password. redis_user​ Available since Centrifugo v3.2.0 String, default \"\" - Redis user for ACL-based auth. redis_db​ Integer, default 0 - number of Redis db to use. redis_tls​ Boolean, default false - enable Redis TLS connection. redis_tls_skip_verify​ Boolean, default false - disable Redis TLS host verification. redis_prefix​ String, default \"centrifugo\" – custom prefix to use for channels and keys in Redis. redis_use_lists​ Boolean, default false – turns on using Redis Lists instead of Stream data structure for keeping history (not recommended, keeping this for backwards compatibility mostly). history_meta_ttl​ Similar to a Memory engine Redis engine also looks at history_meta_ttl option (duration, default 0) - which sets a time of history stream metadata expiration in Redis Engine (with seconds resolution). Meta key in Redis is a HASH that contains the current offset number in channel and epoch value. By default, metadata for channels does not expire. Though in some cases – when channels are created for а short time and then not used anymore – created stream metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory leak. Setting a reasonable value to this option (usually much bigger than the history retention period) can help. In this case, unused channel metadata will eventually expire.","s":"Redis engine options","u":"/docs/3/server/engines","h":"#redis-engine-options","p":966},{"i":977,"t":"Let's see how to start several Centrifugo nodes using the Redis Engine. We will start 3 Centrifugo nodes and all those nodes will be connected via Redis. First, you should have Redis running. As soon as it's running - we can launch 3 Centrifugo instances. Open your terminal and start the first one: centrifugo --config=config.json --port=8000 --engine=redis --redis_address=127.0.0.1:6379 If your Redis is on the same machine and runs on its default port you can omit redis_address option in the command above. Then open another terminal and start another Centrifugo instance: centrifugo --config=config.json --port=8001 --engine=redis --redis_address=127.0.0.1:6379 Note that we use another port number (8001) as port 8000 is already busy by our first Centrifugo instance. If you are starting Centrifugo instances on different machines then you most probably can use the same port number (8000 or whatever you want) for all instances. And finally, let's start the third instance: centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379 Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes. To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation. tip In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers. Here is a live example where we locally start two Centrifugo nodes both connected to local Redis: Sorry, your browser doesn't support embedded video.","s":"Scaling with Redis tutorial","u":"/docs/3/server/engines","h":"#scaling-with-redis-tutorial","p":966},{"i":979,"t":"Centrifugo supports the official way to add high availability to Redis - Redis Sentinel. For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name: redis_sentinel_address (string, default \"\") - comma separated list of Sentinel addresses for HA. At least one known server required. redis_sentinel_master_name (string, default \"\") - name of Redis master Sentinel monitors Also: redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1 redis_sentinel_user (available since v3.2.0) - optional string user (used only in Redis ACL-based auth). So you can start Centrifugo which will use Sentinels to discover Redis master instances like this: centrifugo --config=config.json Where config.json: config.json { ... \"engine\": \"redis\", \"redis_sentinel_address\": \"127.0.0.1:26379\", \"redis_sentinel_master_name\": \"mymaster\"} Sentinel configuration files can look like this: port 26379sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 10000sentinel failover-timeout mymaster 60000 You can find how to properly set up Sentinels in official documentation. Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.","s":"Redis Sentinel for high availability","u":"/docs/3/server/engines","h":"#redis-sentinel-for-high-availability","p":966},{"i":981,"t":"Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case. For example, you can use something like this in Haproxy config: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:16379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin And then just point Centrifugo to this Haproxy: centrifugo --config=config.json --engine=redis --redis_address=\"localhost:16379\"","s":"Haproxy instead of Sentinel configuration","u":"/docs/3/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":966},{"i":983,"t":"Centrifugo has built-in Redis sharding support. This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale. At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples. To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"127.0.0.1:6379\", \"127.0.0.1:6380\", ]} To start Centrifugo with Redis instances on different hosts: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"192.168.1.34:6379\", \"192.168.1.35:6379\", ]} If you also need to customize AUTH password, Redis DB number then you can use an extended address notation. note Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers. When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).","s":"Redis sharding","u":"/docs/3/server/engines","h":"#redis-sharding","p":966},{"i":985,"t":"Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\" ]} You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start. To set the same over environment variable: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001\" CENTRIFUGO_ENGINE=redis ./centrifugo If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\", \"localhost:30101,localhost:30102,localhost:30103\" ]} Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump). To reproduce the same over environment variable use space to separate different clusters: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001,localhost:30002 localhost:30101,localhost:30102\" CENTRIFUGO_ENGINE=redis ./centrifugo","s":"Redis cluster","u":"/docs/3/server/engines","h":"#redis-cluster","p":966},{"i":987,"t":"EXPERIMENTAL Centrifugo Redis engine seamlessly works with KeyDB. KeyDB server is compatible with Redis and provides several additional features beyond. caution We can't give any promises about compatibility with KeyDB in the future Centrifugo releases - while KeyDB is fully compatible with Redis things should work just fine. That's why we consider this as EXPERIMENTAL feature. Use KeyDB instead of Redis only if you are sure you need it. Nothing stops you from running several Redis instances per each core you have, configure sharding, and obtain even better performance than KeyDB can provide (due to lack of synchronization between threads in Redis). To run Centrifugo with KeyDB all you need to do is use redis engine but run the KeyDB server instead of Redis.","s":"KeyDB Engine","u":"/docs/3/server/engines","h":"#keydb-engine","p":966},{"i":989,"t":"EXPERIMENTAL Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics. caution EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves. There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge. info To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together. There are several supported Tarantool topologies to which Centrifugo can connect: One standalone Tarantool instance Many standalone Tarantool instances and consistently shard data between them Tarantool running in Cartridge Tarantool with replica and automatic failover in Cartridge Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7) After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes): config.json { ... \"engine\": \"tarantool\", \"tarantool_address\": \"127.0.0.1:3301\"} See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework. See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.","s":"Tarantool engine","u":"/docs/3/server/engines","h":"#tarantool-engine","p":966},{"i":991,"t":"tarantool_address​ String or array of strings. Default tcp://127.0.0.1:3301. Connection address to Tarantool. tarantool_mode​ String, default standalone A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication). All modes support client-side consistent sharding (similar to what Redis engine provides). tarantool_user​ String, default \"\". Allows setting a user. tarantool_password​ String, default \"\". Allows setting a password. history_meta_ttl​ Duration, default 0s. Same option as for Memory engine and Redis engine also applies to Tarantool case.","s":"Tarantool engine options","u":"/docs/3/server/engines","h":"#tarantool-engine-options","p":966},{"i":993,"t":"It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below. Limitations: Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available. Nats wildcard channel subscriptions with symbols * and > not supported. First start Nats server: $ nats-server[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set][3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready Then start Centrifugo with broker option: centrifugo --broker=nats --config=config.json And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine): centrifugo --broker=nats --config=config.json --port=8001 Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.","s":"Nats broker","u":"/docs/3/server/engines","h":"#nats-broker","p":966},{"i":995,"t":"nats_url​ String, default nats://127.0.0.1:4222. Connection url in format nats://derek:pass@localhost:4222. nats_prefix​ String, default centrifugo. Prefix for channels used by Centrifugo inside Nats. nats_dial_timeout​ Duration, default 1s. Timeout for dialing with Nats. nats_write_timeout​ Duration, default 1s. Write (and flush) timeout for a connection to Nats.","s":"Options","u":"/docs/3/server/engines","h":"#options","p":966},{"i":997,"t":"On this page","s":"Load balancing","u":"/docs/3/server/load_balancing","h":"","p":996},{"i":999,"t":"Although it's possible to use Centrifugo without any reverse proxy before it, it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with edge cases when handling HTTP/Websocket connections from the wild. Also you probably want some sort of load balancing eventually between Centrifugo nodes so that proxy can be such a balancer too. In this section we will look at Nginx configuration to deploy Centrifugo. Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections. There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).","s":"Nginx configuration","u":"/docs/3/server/load_balancing","h":"#nginx-configuration","p":996},{"i":1001,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}#server {# listen 80;# server_name centrifugo.example.com;# rewrite ^(.*) https://$server_name$1 permanent;#}server { server_name centrifugo.example.com; listen 80; #listen 443 ssl; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_certificate /etc/nginx/ssl/server.crt; #ssl_certificate_key /etc/nginx/ssl/server.key; include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1000; gzip_proxied any; # Only retry if there was a communication error, not a timeout # on the Centrifugo server (to avoid propagating \"queries of death\" # to all frontends) proxy_next_upstream error; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; location /connection { proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location / { proxy_pass http://centrifugo; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }}","s":"Separate domain for Centrifugo","u":"/docs/3/server/load_balancing","h":"#separate-domain-for-centrifugo","p":996},{"i":1003,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}server { # ... your web site Nginx config location /centrifugo/ { rewrite ^/centrifugo/(.*) /$1 break; proxy_pass http://centrifugo; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } location /centrifugo/connection { rewrite ^/centrifugo(.*) $1 break; proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}","s":"Embed to a location of web site","u":"/docs/3/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":996},{"i":1005,"t":"You may also need to update worker_connections option of Nginx: events { worker_connections 65535;}","s":"worker_connections","u":"/docs/3/server/load_balancing","h":"#worker_connections","p":996},{"i":1007,"t":"On this page","s":"Monitoring","u":"/docs/3/server/monitoring","h":"","p":1006},{"i":1009,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus","u":"/docs/3/server/monitoring","h":"#prometheus","p":1006},{"i":1011,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite","u":"/docs/3/server/monitoring","h":"#graphite","p":1006},{"i":1013,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/3/server/monitoring","h":"#grafana-dashboard","p":1006},{"i":1015,"t":"On this page","s":"Private channels","u":"/docs/3/server/private_channels","h":"","p":1014},{"i":1017,"t":"Private channel subscription token claims are: client, channel, info, b64info, exp and expire_at. What do they mean? Let's describe it in detail.","s":"Claims","u":"/docs/3/server/private_channels","h":"#claims","p":1014},{"i":1019,"t":"Required. Client ID which wants to subscribe on a channel (string). note Centrifugo server generates a unique client ID for each incoming connection. This client ID regenerated for a client on every reconnect. You must use this client ID for a private channel subscription token. If you are using centrifuge-js library then Client ID and channels that the user wants to subscribe will be automatically added to AJAX POST request to application backend. In other cases refer to specific client documentation (in most cases you will have client ID and channel in private subscription event context).","s":"client","u":"/docs/3/server/private_channels","h":"#client","p":1014},{"i":1021,"t":"Required. Channel that client tries to subscribe to (string).","s":"channel","u":"/docs/3/server/private_channels","h":"#channel","p":1014},{"i":1023,"t":"Optional. Additional information for connection inside this channel (valid JSON).","s":"info","u":"/docs/3/server/private_channels","h":"#info","p":1014},{"i":1025,"t":"Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.","s":"b64info","u":"/docs/3/server/private_channels","h":"#b64info","p":1014},{"i":1027,"t":"Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time. At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs. Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.","s":"exp","u":"/docs/3/server/private_channels","h":"#exp","p":1014},{"i":1029,"t":"Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the subscription should expire. Set it to the future time for expiring subscription at some point Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.","s":"expire_at","u":"/docs/3/server/private_channels","h":"#expire_at","p":1014},{"i":1031,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.","s":"aud","u":"/docs/3/server/private_channels","h":"#aud","p":1014},{"i":1033,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.","s":"iss","u":"/docs/3/server/private_channels","h":"#iss","p":1014},{"i":1035,"t":"So to generate a subscription token you can use something like this in Python (assuming client ID is xxxx-xxx-xxx-xxxx and the private channel is $gossips): import jwttoken = jwt.encode({ \"client\": \"xxxx-xxx-xxx-xxxx\", \"channel\": \"$gossips\"}, \"secret\", algorithm=\"HS256\").decode()print(token) Where \"secret\" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).","s":"Example","u":"/docs/3/server/private_channels","h":"#example","p":1014},{"i":1037,"t":"On this page","s":"Server-side subscriptions","u":"/docs/3/server/server_subs","h":"","p":1036},{"i":1039,"t":"See subscribe and unsubscribe server API","s":"Dynamic server-side subscriptions","u":"/docs/3/server/server_subs","h":"#dynamic-server-side-subscriptions","p":1036},{"i":1041,"t":"It's possible to automatically subscribe a user to a personal server-side channel. To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel. For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above. As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default \"\") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.","s":"Automatic personal channel subscription","u":"/docs/3/server/server_subs","h":"#automatic-personal-channel-subscription","p":1036},{"i":1043,"t":"Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes. user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect). This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel. Example config: { \"user_subscribe_to_personal\": true, \"user_personal_single_connection\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} note Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.","s":"Maintain single user connection","u":"/docs/3/server/server_subs","h":"#maintain-single-user-connection","p":1036},{"i":1045,"t":"On this page","s":"Configure TLS","u":"/docs/3/server/tls","h":"","p":1044},{"i":1047,"t":"In first way you already have cert and key files. For development you can create self-signed certificate - see this instruction as example. config.json { ... \"tls\": true, \"tls_key\": \"server.key\", \"tls_cert\": \"server.crt\"} And run: ./centrifugo --config=config.json","s":"Using crt and key files","u":"/docs/3/server/tls","h":"#using-crt-and-key-files","p":1044},{"i":1049,"t":"For automatic certificates from Let's Encrypt add into configuration file: config.json { ... \"tls_autocert\": true, \"tls_autocert_host_whitelist\": \"www.example.com\", \"tls_autocert_cache_dir\": \"/tmp/certs\", \"tls_autocert_email\": \"user@example.com\", \"tls_autocert_http\": true, \"tls_autocert_http_addr\": \":80\"} tls_autocert (boolean) says Centrifugo that you want automatic certificate handling using ACME provider. tls_autocert_host_whitelist (string) is a string with your app domain address. This can be comma-separated list. It's optional but recommended for extra security. tls_autocert_cache_dir (string) is a path to a folder to cache issued certificate files. This is optional but will increase performance. tls_autocert_email (string) is optional - it's an email address ACME provider will send notifications about problems with your certificates. tls_autocert_http (boolean) is an option to handle http_01 ACME challenge on non-TLS port. tls_autocert_http_addr (string) can be used to set address for handling http_01 ACME challenge (default is :80) When configured correctly and your domain is valid (localhost will not work) - certificates will be retrieved on first request to Centrifugo. Also Let's Encrypt certificates will be automatically renewed. There are two options that allow Centrifugo to support TLS client connections from older browsers such as Chrome 49 on Windows XP and IE8 on XP: tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys. tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints. uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.","s":"Automatic certificates","u":"/docs/3/server/tls","h":"#automatic-certificates","p":1044},{"i":1051,"t":"You can provide custom certificate files to configure TLS for GRPC API server. grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server","s":"TLS for GRPC API","u":"/docs/3/server/tls","h":"#tls-for-grpc-api","p":1044},{"i":1053,"t":"Starting from Centrifugo v3.0.0 you can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint. uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server","s":"TLS for GRPC unidirectional stream","u":"/docs/3/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":1044},{"i":1055,"t":"On this page","s":"Proxy to backend","u":"/docs/3/server/proxy","h":"","p":1054},{"i":1057,"t":"HTTP proxy in Centrifugo converts client connection events into HTTP call to the application backend.","s":"HTTP proxy","u":"/docs/3/server/proxy","h":"#http-proxy","p":1054},{"i":1059,"t":"All proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).","s":"HTTP request structure","u":"/docs/3/server/proxy","h":"#http-request-structure","p":1054},{"i":1061,"t":"The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example: config.json { ... \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} Alternatively, you can set a list of headers via an environment variable (space separated): export CENTRIFUGO_PROXY_HTTP_HEADERS=\"Cookie User-Agent X-B3-TraceId X-B3-SpanId\" ./centrifugo note Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend.","s":"Proxy HTTP headers","u":"/docs/3/server/proxy","h":"#proxy-http-headers","p":1054},{"i":1063,"t":"When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied. See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.","s":"Proxy GRPC metadata","u":"/docs/3/server/proxy","h":"#proxy-grpc-metadata","p":1054},{"i":1065,"t":"With the following options in the configuration file: { ... \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_connect_timeout\": \"1s\"} – connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request. danger Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections. Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. tip If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server. This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired). Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\"} Expected response example: {\"result\": {\"user\": \"56\"}} This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result. Several app examples which use connect proxy can be found in our blog: With NodeJS With Django With Laravel Connect request fields​ This is what sent from Centrifugo to application backend in case of connect proxy request. Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) name string yes optional name of the client (this field will only be set if provided by a client on connect) version string yes optional version of the client (this field will only be set if provided by a client on connect) data JSON yes optional data from client (this field will only be set if provided by a client on connect) b64data string yes optional data from the client in base64 format (if the binary proxy mode is used) channels Array of strings yes list of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result Connect result fields​ This is what application returns to Centrifugo inside result field in case of connect proxy request. Field Type Optional Description user string no user ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests expire_at integer yes a timestamp when connection must be considered expired. If not set or set to 0 connection won't expire at all info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages data JSON yes a custom data to send to the client in connect command response. b64data string yes a custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client channels array of strings yes allows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions subs map of SubscribeOptions yes map of channels with options to subscribe connection to. See more details about server-side subscriptions meta JSON object (ex. {\"key\": \"value\"}) yes a custom data to attach to connection (this won't be exposed to client-side) Options​ proxy_connect_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend. Example​ Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return \"56\" as user ID): class CentrifugoConnectHandler(tornado.web.RequestHandler): def check_xsrf_cookie(self): pass def post(self): self.set_header('Content-Type', 'application/json; charset=\"utf-8\"') data = json.dumps({ 'result': { 'user': '56' } }) self.write(data)def main(): options.parse_command_line() app = tornado.web.Application([ (r'/centrifugo/connect', CentrifugoConnectHandler), ]) app.listen(3000) tornado.ioloop.IOLoop.instance().start()if __name__ == '__main__': main() This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side. We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea.","s":"Connect proxy","u":"/docs/3/server/proxy","h":"#connect-proxy","p":1054},{"i":1067,"t":"With the following options in the configuration file: { ... \"proxy_refresh_endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"proxy_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side. The payload sent to app backend in refresh request (when the connection is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_refresh_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.","s":"Refresh proxy","u":"/docs/3/server/proxy","h":"#refresh-proxy","p":1054},{"i":1069,"t":"With the following option in the configuration file: { ... \"proxy_rpc_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_rpc_timeout\": \"1s\"} RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket (or SockJS) connection in a bidirectional way. Payload example sent to app backend in RPC request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"method\": \"getCurrentPrice\", \"data\":{\"params\": {\"object_id\": 12}}} Expected response example: {\"result\": {\"data\": {\"answer\": \"2019\"}}} RPC request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process method string yes an RPC method string, if the client does not use named RPC call then method will be omitted data JSON yes RPC custom data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) RPC result fields​ Field Type Optional Description data JSON yes RPC response - any valid JSON is supported b64data string yes can be set instead of data for binary response encoded in base64 format Options​ proxy_rpc_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend. See below on how to return a custom error.","s":"RPC proxy","u":"/docs/3/server/proxy","h":"#rpc-proxy","p":1054},{"i":1071,"t":"With the following option in the configuration file: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\"} – subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel. tip Subscribe proxy does not proxy private and user-limited channels at the moment. That's because those are already providing a level of security (user-limited channels check current user ID, private channels require subscription token). In some cases you may use subscribe proxy as a replacement for private channels actually: if you prefer to check permissions using the proxy to backend mechanism – just stop using $ prefixes in channels, properly configure subscribe proxy and validate subscriptions upon proxy from Centrifugo to your backend (issued each time user tries to subscribe on a channel for which subscribe proxy enabled). Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace. So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} Or for channels in namespace sun: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_subscribe\": true }]} Payload example sent to app backend in subscribe request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\"} Expected response example if subscription is allowed: {\"result\": {}} Subscribe request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to subscribe to meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) data JSON yes custom data from client sent with subscription request (this field will only be set if provided by a client on subscribe). Available since Centrifugo v3.1.1 b64data string yes optional subscription data from the client in base64 format (if the binary proxy mode is used). Available since Centrifugo v3.1.1 Subscribe result fields​ Field Type Optional Description info JSON yes a channel info JSON b64info string yes a binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using data JSON yes a custom data to send to the client in subscribe command reply. b64data string yes a custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false} See below on how to return an error in case you don't want to allow subscribing. Options​ proxy_subscribe_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.","s":"Subscribe proxy","u":"/docs/3/server/proxy","h":"#subscribe-proxy","p":1054},{"i":1073,"t":"With the following option in the configuration file: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\"} – publish calls sent by a client will be proxied to proxy_publish_endpoint. This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side. Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too. So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"publish\": true, \"proxy_publish\": true} Or for channels in namespace sun: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"publish\": true, \"proxy_publish\": true }]} Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace). Payload example sent to app backend in a publish request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\", \"data\":{\"input\":\"hello\"}} Expected response example if publish is allowed: {\"result\": {}} Publish request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to publish to data JSON yes data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Publish result fields​ Field Type Optional Description data JSON yes an optional JSON data to send into a channel instead of original data sent by a client b64data string yes a binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing skip_history bool yes when set to true Centrifugo won't save publication to the channel history See below on how to return an error in case you don't want to allow publishing. Options​ proxy_publish_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.","s":"Publish proxy","u":"/docs/3/server/proxy","h":"#publish-proxy","p":1054},{"i":1075,"t":"Application backend can return JSON object that contains an error to return it to the client: { \"error\": { \"code\": 1000, \"message\": \"custom error\" }} Application should use error codes >= 1000, error codes in the range 0-999 are reserved by Centrifugo internal protocol. Error code field is uint32 internally. note Returning custom error does not apply to response on refresh request as there is no sense in returning an error (will not reach client anyway).","s":"Return custom error","u":"/docs/3/server/proxy","h":"#return-custom-error","p":1054},{"i":1077,"t":"Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way: { \"disconnect\": { \"code\": 4000, \"reconnect\": false, \"reason\": \"custom disconnect\" }} Application must use numbers in the range 4000-4999 for custom disconnect codes. Code is uint32 internally. Numbers below 4000 are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 symbols. note Returning custom disconnect does not apply to response on refresh request as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason.","s":"Return custom disconnect","u":"/docs/3/server/proxy","h":"#return-custom-disconnect","p":1054},{"i":1079,"t":"Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server. GRPC service definitions can be found in the Centrifugo repository: proxy.proto. tip GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions. Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept). All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy: config.json { ... \"proxy_connect_endpoint\": \"grpc://localhost:12000\", \"proxy_connect_timeout\": \"1s\"} Refresh proxy: config.json { ... \"proxy_refresh_endpoint\": \"grpc://localhost:12000\", \"proxy_refresh_timeout\": \"1s\"} Or for RPC proxy: config.json { ... \"proxy_rpc_endpoint\": \"grpc://localhost:12000\", \"proxy_rpc_timeout\": \"1s\"} For publish proxy in namespace chat: config.json { ... \"proxy_publish_endpoint\": \"grpc://localhost:12000\", \"proxy_publish_timeout\": \"1s\" \"namespaces\": [ { \"name\": \"chat\", \"publish\": true, \"proxy_publish\": true } ]} Use subscribe proxy for all channels without namespaces: config.json { ... \"proxy_subscribe_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} So the same as for HTTP, just the different endpoint scheme.","s":"GRPC proxy","u":"/docs/3/server/proxy","h":"#grpc-proxy","p":1054},{"i":1081,"t":"Some additional options exist to control GRPC proxy behavior. proxy_grpc_cert_file​ String, default: \"\". Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. proxy_grpc_credentials_key​ String, default \"\" (i.e. not used). Add custom key to per-RPC credentials. proxy_grpc_credentials_value​ String, default \"\" (i.e. not used). A custom value for proxy_grpc_credentials_key.","s":"GRPC proxy options","u":"/docs/3/server/proxy","h":"#grpc-proxy-options","p":1054},{"i":1083,"t":"We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.: Copy proxy Protobuf definitions Generate GRPC code Run backend service with you custom business logic Point Centrifugo to it.","s":"GRPC proxy example","u":"/docs/3/server/proxy","h":"#grpc-proxy-example","p":1054},{"i":1085,"t":"Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios: Client protocol type Proxy type Client headers Client metadata HTTP HTTP In proxy request headers N/A GRPC GRPC N/A In proxy request metadata HTTP GRPC In proxy request metadata N/A GRPC HTTP N/A In proxy request headers","s":"Header proxy rules","u":"/docs/3/server/proxy","h":"#header-proxy-rules","p":1054},{"i":1087,"t":"As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding. Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this. Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side). But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON. In this case it's possible to turn Centrifugo proxy into binary mode by using: config.json { ... \"proxy_binary_encoding\": true} Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues. While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine. Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.","s":"Binary mode","u":"/docs/3/server/proxy","h":"#binary-mode","p":1054},{"i":1089,"t":"New in Centrifugo v3.1.0. By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture). Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.","s":"Granular proxy mode","u":"/docs/3/server/proxy","h":"#granular-proxy-mode","p":1054},{"i":1091,"t":"Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below. config.json { ... \"granular_proxy_mode\": true}","s":"Enable granular proxy mode","u":"/docs/3/server/proxy","h":"#enable-granular-proxy-mode","p":1054},{"i":1093,"t":"When using granular proxy mode on configuration top level you can define \"proxies\" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint. Here is an example: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"connect\", \"endpoint\": \"http://localhost:3000/centrifugo/connect\", \"timeout\": \"500ms\", \"http_headers\": [\"Cookie\"] }, { \"name\": \"refresh\", \"endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"timeout\": \"500ms\" }, { \"name\": \"subscribe1\", \"endpoint\": \"http://localhost:3001/centrifugo/subscribe\" }, { \"name\": \"publish1\", \"endpoint\": \"http://localhost:3001/centrifugo/publish\" }, { \"name\": \"rpc1\", \"endpoint\": \"http://localhost:3001/centrifugo/rpc\" }, { \"name\": \"subscribe2\", \"endpoint\": \"http://localhost:3002/centrifugo/subscribe\" }, { \"name\": \"publish2\", \"endpoint\": \"grpc://localhost:3002\" } { \"name\": \"rpc2\", \"endpoint\": \"grpc://localhost:3002\" } ]} Let's look at all fields for a proxy object which is possible to set for each proxy inside \"proxies\" array. Field name Field type Required Description name string yes Unique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$ endpoint string yes HTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC. timeout duration (string) no Proxy request timeout, default \"1s\" http_headers array of strings no List of headers to proxy, by default no headers grpc_metadata array of strings no List of GRPC metadata keys to proxy, by default no metadata keys binary_encoding bool no Use base64 for payloads include_connection_meta bool no Include meta information (attached on connect) grpc_cert_file string no Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. grpc_credentials_key string no Add custom key to per-RPC credentials. grpc_credentials_value string no A custom value for grpc_credentials_key.","s":"Defining a list of proxies","u":"/docs/3/server/proxy","h":"#defining-a-list-of-proxies","p":1054},{"i":1095,"t":"As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event. To enable connect proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\"} We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea. Let's also add refresh proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\", \"refresh_proxy_name\": \"refresh\"}","s":"Granular connect and refresh","u":"/docs/3/server/proxy","h":"#granular-connect-and-refresh","p":1054},{"i":1097,"t":"Subscribe and publish proxy work per-namespace. This means that subscribe_proxy_name and publish_proxy_name are just a channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object. config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_proxy_name\": \"subscribe1\", \"publish\": true, \"publish_proxy_name\": \"publish1\" }, { \"name\": \"ns2\", \"subscribe_proxy_name\": \"subscribe2\", \"publish\": true, \"publish_proxy_name\": \"publish2\" } ]} If namespace does not have \"subscribe_proxy_name\" or \"subscribe_proxy_name\" is empty then no subscribe proxy will be used for a namespace. If namespace does not have \"publish_proxy_name\" or \"publish_proxy_name\" is empty then no publish proxy will be used for a namespace. tip You can define subscribe_proxy_name and publish_proxy_name on configuration top level – and in this case publish and subscribe requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.","s":"Granular subscribe and publish","u":"/docs/3/server/proxy","h":"#granular-subscribe-and-publish","p":1054},{"i":1099,"t":"Analogous to channel namespaces it's possible to configure rpc namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [...], \"rpc_namespaces\": [ { \"name\": \"rpc_ns1\", \"rpc_proxy_name\": \"rpc1\", }, { \"name\": \"rpc_ns2\", \"rpc_proxy_name\": \"rpc2\" } ]} The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces). Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start. tip The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.","s":"Granular RPC","u":"/docs/3/server/proxy","h":"#granular-rpc","p":1054},{"i":1101,"t":"Client real-time SDKs The following SDKs allow connecting to Centrifugo from the application frontend: No need in clients for unidirectional approach Client libraries listed here speak Centrifugo bidirectional protocol (WebSocket). If you aim to use unidirectional approach you don't need client connectors – just use standard APIs. See the difference here. centrifuge-js – for browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-mobile - for iOS/Android with centrifuge-go as basis and gomobile centrifuge-dart - for Dart and Flutter centrifuge-swift – for native iOS development centrifuge-java – for native Android development and general Java See a description of client protocol if you want to write a custom client bidirectional connector.","s":"Client real-time SDKs","u":"/docs/3/transports/client_sdk","h":"","p":1100},{"i":1103,"t":"On this page","s":"Server API","u":"/docs/3/server/server_api","h":"","p":1102},{"i":1105,"t":"Server HTTP API works on /api endpoint (by default). It has a simple request format: this is an HTTP POST request with application/json Content-Type and with JSON command body. Here we will look at available methods and parameters tip In some cases, you can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests.","s":"HTTP API","u":"/docs/3/server/server_api","h":"#http-api","p":1102},{"i":1107,"t":"HTTP API protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like: config.json { ... \"api_key\": \"\"} This API key must be set in the request Authorization header in this way: Authorization: apikey It's also possible to pass API key over URL query param. This solves some edge cases where it's not possible to use the Authorization header. Simply add ?api_key= query param to the API endpoint. Keep in mind that passing the API key in the Authorization header is a recommended way. It's possible to disable API key check on Centrifugo side using the api_insecure configuration option. Be sure to protect the API endpoint by firewall rules, in this case, to prevent anyone on the internet to send commands over your unprotected Centrifugo API endpoint. API key auth is not very safe for man-in-the-middle so we also recommended running Centrifugo with TLS. A command is a JSON object with two properties: method and params. method is the name of the API command you want to call. params is an object with command arguments. Each method can have its own params Before looking at all available commands here is a CURL that calls info command: curl --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"info\", \"params\": {}}' \\ http://localhost:8000/api Here is a live example: Your browser does not support the video tag. Now let's investigate each API method in detail.","s":"HTTP API authorization","u":"/docs/3/server/server_api","h":"#http-api-authorization","p":1102},{"i":1109,"t":"Publish command allows publishing data into a channel. Most probably this is a command you'll use most. It looks like this: { \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } } } Let's apply all information said above and send publish command to Centrifugo. We will send a request using the requests library for Python. import jsonimport requestscommand = { \"method\": \"publish\", \"params\": { \"channel\": \"docs\", \"data\": { \"content\": \"1\" } }}api_key = \"YOUR_API_KEY\"data = json.dumps(command)headers = {'Content-type': 'application/json', 'Authorization': 'apikey ' + api_key}resp = requests.post(\"https://centrifuge.example.com/api\", data=data, headers=headers)print(resp.json()) The same using httpie console tool: echo '{\"method\": \"publish\", \"params\": {\"channel\": \"chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \" -vvvPOST /api HTTP/1.1Accept: application/json, */*Accept-Encoding: gzip, deflateAuthorization: apikey KEYConnection: keep-aliveContent-Length: 80Content-Type: application/jsonHost: localhost:8000User-Agent: HTTPie/0.9.8{ \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } }}HTTP/1.1 200 OKContent-Length: 3Content-Type: application/jsonDate: Thu, 17 May 2018 22:01:42 GMT{ \"result\": {}} In case of error response object can contain error field (here we artificially publishing to a channel with unknown namespace): echo '{\"method\": \"publish\", \"params\": {\"channel\": \"unknown:chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \"HTTP/1.1 200 OKContent-Length: 55Content-Type: application/jsonDate: Thu, 17 May 2018 22:03:09 GMT{ \"error\": { \"code\": 102, \"message\": \"namespace not found\" }} error object contains error code and message - this is also the same for other commands described below. Publish params​ Parameter name Parameter type Required Description channel string yes Name of channel to publish data any JSON yes Custom JSON data to publish into a channel skip_history bool no Skip adding publication to history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients (available since v3.2.0) Publish result​ Field name Field type Optional Description offset integer yes Offset of publication in history stream epoch string yes Epoch of current stream","s":"publish","u":"/docs/3/server/server_api","h":"#publish","p":1102},{"i":1111,"t":"Similar to publish but allows to send the same data into many channels. { \"method\": \"broadcast\", \"params\": { \"channels\": [\"CHANNEL_1\", \"CHANNEL_2\"], \"data\": { \"text\": \"hello\" } }} Broadcast params​ Parameter name Parameter type Required Description channels Array of strings yes List of channels to publish data to data any JSON yes Custom JSON data to publish into each channel skip_history bool no Skip adding publications to channels' history for this request tags map[string]string no Publication tags (available since v3.2.0) - map with arbitrary string keys and values which is attached to publication and will be delivered to clients Broadcast result​ Field name Field type Optional Description responses Array of publish responses no Responses for each individual publish (with possible error and publish result)","s":"broadcast","u":"/docs/3/server/server_api","h":"#broadcast","p":1102},{"i":1113,"t":"subscribe allows subscribing user to a channel. Subscribe params​ Parameter name Parameter type Required Description user string yes User ID to subscribe channel string yes Name of channel to subscribe user to info any JSON no Attach custom data to subscription (will be used in presence and join/leave messages) b64info string no info in base64 for binary mode (will be decoded by Centrifugo) client string no Specific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs) session string no Specific client session to subscribe (user still required to be set). Available since Centrifugo v3.2.0 data any JSON no Custom subscription data (will be sent to client in Subscribe push) b64data string no Same as data but in base64 format (will be decoded by Centrifugo) recover_since StreamPosition object no Stream position to recover from override Override object no Allows dynamically override some channel options defined in Centrifugo configuration (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false} Subscribe result​ Empty object at the moment.","s":"subscribe","u":"/docs/3/server/server_api","h":"#subscribe","p":1102},{"i":1115,"t":"unsubscribe allows unsubscribing user from a channel. { \"method\": \"unsubscribe\", \"params\": { \"channel\": \"CHANNEL NAME\", \"user\": \"USER ID\" }} Unsubscribe params​ Parameter name Parameter type Required Description user string yes User ID to unsubscribe channel string yes Name of channel to unsubscribe user to client string no Specific client ID to unsubscribe (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Available since Centrifugo v3.2.0 Unsubscribe result​ Empty object at the moment.","s":"unsubscribe","u":"/docs/3/server/server_api","h":"#unsubscribe","p":1102},{"i":1117,"t":"disconnect allows disconnecting a user by ID. { \"method\": \"disconnect\", \"params\": { \"user\": \"USER ID\" }} Disconnect params​ Parameter name Parameter type Required Description user string yes User ID to disconnect client string no Specific client ID to disconnect (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Available since Centrifugo v3.2.0 whitelist Array of strings no Array of client IDs to keep disconnect Disconnect object no Provide custom disconnect object, see below Disconnect object​ Field name Field type Required Description code int yes Disconnect code reason string yes Disconnect reason reconnect bool no Reconnect advice Disconnect result​ Empty object at the moment.","s":"disconnect","u":"/docs/3/server/server_api","h":"#disconnect","p":1102},{"i":1119,"t":"refresh allows refreshing user connection (mostly useful when unidirectional transports are used). Refresh params​ Parameter name Parameter type Required Description user string yes User ID to refresh client string no Client ID to refresh (user still required to be set) session string no Specific client session to refresh (user still required to be set). Available since Centrifugo v3.2.0 expired bool no Mark connection as expired and close with Disconnect Expired reason expire_at int no Unix time (in seconds) in the future when the connection will expire Refresh result​ Empty object at the moment.","s":"refresh","u":"/docs/3/server/server_api","h":"#refresh","p":1102},{"i":1121,"t":"presence allows getting channel online presence information (all clients currently subscribed on this channel). tip Presence in channels is not enabled by default. See how to enable it over channel options. { \"method\": \"presence\", \"params\": { \"channel\": \"chat\" }} Example: fz@centrifugo: echo '{\"method\": \"presence\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 127Content-Type: application/jsonDate: Thu, 17 May 2018 22:13:17 GMT{ \"result\": { \"presence\": { \"c54313b2-0442-499a-a70c-051f8588020f\": { \"client\": \"c54313b2-0442-499a-a70c-051f8588020f\", \"user\": \"42\" }, \"adad13b1-0442-499a-a70c-051f858802da\": { \"client\": \"adad13b1-0442-499a-a70c-051f858802da\", \"user\": \"42\" } } }} Presence params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence result​ Field name Field type Optional Description presence Map of client ID (string) to ClientInfo object no Offset of publication in history stream ClientInfo​ Field name Field type Optional Description client string no Client ID user string no User ID conn_info JSON yes Optional connection info chan_info JSON yes Optional channel info","s":"presence","u":"/docs/3/server/server_api","h":"#presence","p":1102},{"i":1123,"t":"presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID). { \"method\": \"presence_stats\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"presence_stats\", \"params\": {\"channel\": \"public:chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": { \"num_clients\": 0, \"num_users\": 0 }} Presence stats params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence stats result​ Field name Field type Optional Description num_clients integer no Total number of clients in channel num_users integer no Total number of unique users in channel","s":"presence_stats","u":"/docs/3/server/server_api","h":"#presence_stats","p":1102},{"i":1125,"t":"history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter. tip History in channels is not enabled by default. See how to enable it over channel options. { \"method\": \"history\", \"params\": { \"channel\": \"chat\", \"limit\": 2 }} Example: echo '{\"method\": \"history\", \"params\": {\"channel\": \"chat\", \"limit\": 2}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 129Content-Type: application/jsonDate: Wed, 21 Jul 2021 05:30:48 GMT{ \"result\": { \"epoch\": \"qFhv\", \"offset\": 4, \"publications\": [ { \"data\": { \"text\": \"hello\" }, \"offset\": 2 }, { \"data\": { \"text\": \"hello\" }, \"offset\": 3 } ] }} History params​ Parameter name Parameter type Required Description channel string yes Name of channel to call history from limit int no Limit number of returned publications, if not set in request then only current stream position information will present in result (without any publications) since StreamPosition object no To return publications after this position reverse bool no Iterate in reversed order (from latest to earliest) StreamPosition​ Field name Field type Required Description offset integer yes Offset in a stream epoch string yes Stream epoch History result​ Field name Field type Optional Description publications Array of publication objects yes List of publications in channel offset integer yes Top offset in history stream epoch string yes Epoch of current stream","s":"history","u":"/docs/3/server/server_api","h":"#history","p":1102},{"i":1127,"t":"history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state. { \"method\": \"history_remove\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"history_remove\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": {}} History remove params​ Parameter name Parameter type Required Description channel string yes Name of channel to remove history History remove result​ Empty object at the moment.","s":"history_remove","u":"/docs/3/server/server_api","h":"#history_remove","p":1102},{"i":1129,"t":"channels return active channels (with one or more active subscribers in it). { \"method\": \"channels\", \"params\": {}} Channels params​ Parameter name Parameter type Required Description pattern string no Pattern to filter channels Channels result​ Field name Field type Optional Description channels Map of string to ChannelInfo no Map where key is channel and value is ChannelInfo (see below) ChannelInfo​ Field name Field type Optional Description num_clients integer no Total number of connections currently subscribed to a channel caution Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels RPC extension will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.","s":"channels","u":"/docs/3/server/server_api","h":"#channels","p":1102},{"i":1131,"t":"info method allows getting information about running Centrifugo nodes. Example: echo '{\"method\": \"info\", \"params\": {}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 184Content-Type: application/jsonDate: Thu, 17 May 2018 22:07:58 GMT{ \"result\": { \"nodes\": [ { \"name\": \"Alexanders-MacBook-Pro.local_8000\", \"num_channels\": 0, \"num_clients\": 0, \"num_users\": 0, \"uid\": \"f844a2ed-5edf-4815-b83c-271974003db9\", \"uptime\": 0, \"version\": \"\" } ] }} Info params​ Empty object at the moment. Info result​ Field name Field type Optional Description nodes Array of Node objects no Information about all nodes in a cluster","s":"info","u":"/docs/3/server/server_api","h":"#info","p":1102},{"i":1133,"t":"It's possible to combine several commands into one request to Centrifugo. To do this use JSON streaming format. This can improve server throughput and reduce traffic traveling around.","s":"Command pipelining","u":"/docs/3/server/server_api","h":"#command-pipelining","p":1102},{"i":1135,"t":"Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header. But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually: cent for Python phpcent for PHP gocent for Go rubycent for Ruby Also, there are API libraries created by community: crystalcent API client for Crystal language cent.js API client for NodeJS Centrifugo.AspNetCore API client for ASP.NET Core tip Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.","s":"HTTP API libraries","u":"/docs/3/server/server_api","h":"#http-api-libraries","p":1102},{"i":1137,"t":"Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC. GRPC API is also useful if you want to publish binary data to Centrifugo channels. tip GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description. You can enable GRPC API in Centrifugo using grpc_api option: config.json { ... \"grpc_api\": true} By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option. Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file. Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.","s":"GRPC API","u":"/docs/3/server/server_api","h":"#grpc-api","p":1102},{"i":1139,"t":"For example for Python you need to run sth like this according to GRPC docs: pip install grpcio-toolspython -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo: import grpcimport api_pb2_grpc as api_grpcimport api_pb2 as api_pbchannel = grpc.insecure_channel('localhost:10000')stub = api_grpc.CentrifugoApiStub(channel)try: resp = stub.Info(api_pb.InfoRequest())except grpc.RpcError as err: # GRPC level error. print(err.code(), err.details())else: if resp.error.code: # Centrifugo server level error. print(resp.error.code, resp.error.message) else: print(resp.result) Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.","s":"GRPC example for Python","u":"/docs/3/server/server_api","h":"#grpc-example-for-python","p":1102},{"i":1141,"t":"Here is a simple example of how to run Centrifugo with the GRPC Go client. You need protoc, protoc-gen-go and protoc-gen-go-grpc installed. First start Centrifugo itself with GRPC API enabled: CENTRIFUGO_GRPC_API=1 centrifugo --config config.json In another terminal tab: mkdir centrifugo_grpc_examplecd centrifugo_grpc_example/touch main.gogo mod init centrifugo_examplemkdir apiprotocd apiprotowget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto Run protoc to generate code: protoc -I ./ api.proto --go_out=. --go-grpc_out=. Put the following code to main.go file (created on the last step above): package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure()) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoApiClient(conn) for { resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} Then run: go run main.go The program starts and periodically publishes the same payload into chat:index channel.","s":"GRPC example for Go","u":"/docs/3/server/server_api","h":"#grpc-example-for-go","p":1102},{"i":1143,"t":"You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey . For example in Go language: package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")type keyAuth struct { key string}func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{ \"authorization\": \"apikey \" + t.key, }, nil}func (t keyAuth) RequireTransportSecurity() bool { return false}func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{\"xxx\"})) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoClient(conn) for { resp, err := client.Publish(context.Background(), &PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} For other languages refer to GRPC docs.","s":"GRPC API key authorization","u":"/docs/3/server/server_api","h":"#grpc-api-key-authorization","p":1102},{"i":1145,"t":"On this page","s":"SockJS","u":"/docs/3/transports/sockjs","h":"","p":1144},{"i":1147,"t":"caution There are several important caveats to know when using SockJS – see below.","s":"SockJS caveats","u":"/docs/3/transports/sockjs","h":"#sockjs-caveats","p":1144},{"i":1149,"t":"First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend. For example, with Nginx sticky support can be enabled with ip_hash directive for upstream: upstream centrifugo { ip_hash; server 127.0.0.1:8000; server 127.0.0.2:8000;} With this configuration Nginx will proxy connections with the same ip address to the same upstream backend. But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair. So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.","s":"Sticky sessions","u":"/docs/3/transports/sockjs","h":"#sticky-sessions","p":1144},{"i":1151,"t":"SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.","s":"Browser only","u":"/docs/3/transports/sockjs","h":"#browser-only","p":1144},{"i":1153,"t":"One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.","s":"JSON only","u":"/docs/3/transports/sockjs","h":"#json-only","p":1144},{"i":1156,"t":"Boolean, default: false. Enables SockJS transport.","s":"sockjs","u":"/docs/3/transports/sockjs","h":"#sockjs","p":1144},{"i":1158,"t":"Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.","s":"sockjs_url","u":"/docs/3/transports/sockjs","h":"#sockjs_url","p":1144},{"i":1160,"t":"On this page","s":"Client protocol","u":"/docs/3/transports/client_protocol","h":"","p":1159},{"i":1162,"t":"First we will look at list of features bidirectional client library should support. If you are an author of client library you can use this list as a checklist. Our current client feature matrix looks like this: connect to server (both Centrifugo and Centrifuge-based) using JSON protocol format connect to server (both Centrifugo and Centrifuge-based) using Protobuf protocol format connect with token (JWT in Centrifugo case, any string token in Centrifuge library case) connect to server with custom headers (not available in a browser) automatic reconnect in case of connection problems (server restart, unavailable network) an exponential backoff for reconnect process possibility to set handlers for connect and disconnect events extract and expose disconnect code and reason subscribe to a channel and provide a way to handle asynchronous Publications coming from it handle Join and Leave messages from a channel handle Unsubscribe notifications provide publish method of Subscription object provide unsubscribe method of Subscription provide presence method of Subscription provide presence stats method of Subscription provide history method of Subscription provide publish method on top level provide unsubscribe method on top level provide presence method on top level provide presence stats method on top level provide history method on top level send asynchronous messages to server handle asynchronous messages from server send RPC requests to server publish to channel without being subscribed subscribe to private (token-protected) channels with a token implement client-side connection token refresh mechanism implement private channel subscription token refresh mechanism client protocol level ping/pong to find a broken connection automatic reconnect in case of connect or subscribe command timeouts handle connection expired error handle subscription expired error server-side subscriptions message recovery mechanism for client-side subscriptions message recovery mechanism for server-side subscriptions This document describes protocol specifics for Websocket transport which supports binary and text formats to transfer data. As Centrifugo and Centrifuge library for Go have various types of messages it serializes protocol messages using JSON or Protobuf formats. info SockJS works almost the same way as JSON websocket described here but has its own extra framing on top of Centrifuge protocol messages. SockJS can only work with JSON - it's not possible to transfer binary data over it.","s":"Client implementation feature matrix","u":"/docs/3/transports/client_protocol","h":"#client-implementation-feature-matrix","p":1159},{"i":1164,"t":"Centrifuge protocol defined in Protobuf schema. That schema is a source of the truth. Below we describe messages from that schema. In bidirectional case client sends Command to server and server sends Reply to client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages. One request from client to server and one response from server to client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data. When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \\n: {\"id\": 1, \"method\": 1, \"params\": {\"channel\": \"ch1\"}}{\"id\": 2, \"method\": 1, \"params\": {\"channel\": \"ch2\"}} For example here is how we do this in Javascript client when JSON format used: function encodeCommands(commands) { const encodedCommands = []; for (const i in commands) { if (commands.hasOwnProperty(i)) { encodedCommands.push(JSON.stringify(commands[i])); } } return encodedCommands.join('\\n');} info This doc will use JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo. When Protobuf format used then many Command can be sent from client to server in length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example. The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there. For example here is how we read server response and extracting individual replies in Javascript client when JSON format used: function decodeReplies(data) { const replies = []; const encodedReplies = data.split('\\n'); for (const i in encodedReplies) { if (encodedReplies.hasOwnProperty(i)) { if (!encodedReplies[i]) { continue; } const reply = JSON.parse(encodedReplies[i]); replies.push(reply); } } return replies;} For Protobuf case see existing client implementations for decoding example. As you can see each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous protocol where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before. So you can expect something like this in response after sending commands to server: {\"id\": 1, \"result\": {}}{\"id\": 2, \"result\": {}} Besides id Reply from server to client have two important fields: result and error. result contains useful payload object which is different for various Reply messages. error contains error description if Command processing resulted in some error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can parse result object appropriately. error looks like this in JSON case: { \"code\": 100, \"message\": \"internal server error\"} We will talk more about error handling below. The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to client in any moment - not as reaction to issued Command but as message from server to client in arbitrary time. For example this can be a message published into channel. Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow. Communication with Centrifuge/Centrifugo server starts with issuing connect command.","s":"Top level framing","u":"/docs/3/transports/client_protocol","h":"#top-level-framing","p":1159},{"i":1166,"t":"First of all client must dial with a server and then send connect Command to it. Default Websocket endpoint in Centrifugo is: ws://centrifugo.example.com/connection/websocket In case of using TLS: wss://centrifugo.example.com/connection/websocket After a successful dial to WebSocket endpoint client must send connect command to server to authorize itself. connect command looks like: { \"id\": 1, \"method\": 0, \"params\": { \"token\": \"JWT\", \"data\": {} }} All methods defined in Protobuf schema: message Command { uint32 id = 1; enum MethodType { CONNECT = 0; SUBSCRIBE = 1; UNSUBSCRIBE = 2; PUBLISH = 3; PRESENCE = 4; PRESENCE_STATS = 5; HISTORY = 6; PING = 7; SEND = 8; RPC = 9; REFRESH = 10; SUB_REFRESH = 11; } MethodType method = 2; bytes params = 3;} So here we are using a enum value for CONNECT (0). Params fields: optional string token - connection token. Can be omitted if token-based auth not used. data - can contain custom connect data, for example it can contain client settings. In response to connect command server sends a connect reply. It looks this way: { \"id\": 1, \"result\":{ \"client\": \"421bf374-dd01-4f82-9def-8c31697e956f\", \"version\": \"2.0.0\" }} result has some fields: string client - unique client connection ID server issued to this connection string version - server version optional bool expires - whether a server will expire connection at some point optional int32 ttl - time in seconds until connection expires","s":"Connect","u":"/docs/3/transports/client_protocol","h":"#connect","p":1159},{"i":1168,"t":"As soon as client successfully connected and got unique connection ID it is ready to subscribe on channels. To do this it must send subscribe command to server: { \"id\": 2, \"method\": 1, \"params\": { \"channel\": \"ch1\" }} Fields that can be set in params are: string channel - channel to subscribe In response to subscribe a client receives reply like: { \"id\": 2, \"result\": {}} result can have the following fields that relate to subscription expiration: optional bool expires - indicates whether subscription expires or not. optional uint32 ttl - number of seconds until subscription expire. Also several fields that relate to message recovery: optional bool recoverable - means that messages can be recovered in this subscription. optional uint64 offset - current publication offset inside channel optional string epoch - current epoch inside channel optional array publications - this is an array of missed publications in channel. When received client must call general publication event handler for each message in this array. optional bool recovered - this flag set to true when server thinks that all missed publications successfully recovered and send in subscribe reply (in publications array) and false otherwise. See more about meaning of recovery related fields in special doc chapter. After a client received a successful reply on subscribe command it will receive asynchronous reply messages published to this channel. Messages can be of several types: Publication message Join message Leave message Unsubscribe message See more about asynchronous messages below.","s":"Subscribe","u":"/docs/3/transports/client_protocol","h":"#subscribe","p":1159},{"i":1170,"t":"When client wants to unsubscribe from a channel and therefore stop receiving asynchronous subscription messages from connection related to channel it must call unsubscribe command: { \"id\": 3, \"method\": 2, \"params\": { \"channel\": \"ch1\" }} Actually server response does not mean a lot for a client - it must immediately remove channel subscription from internal implementation data structures and ignore all messages related to channel.","s":"Unsubscribe","u":"/docs/3/transports/client_protocol","h":"#unsubscribe","p":1159},{"i":1172,"t":"It's possible to turn on client connection expiration mechanism on a server. While enabled server will keep track of connections whose time of life is close to the end (connection lifetime set on connection authentication phase). In this case connection will be closed. Client can prevent closing connection refreshing its connection credentials. To do this it must send refresh command to server. refresh command is similar to connect: { \"id\": 4, \"method\": 10, \"params\": { \"token\": \"\" }} The tip whether a connection must be refreshed by a client comes in reply to connect command shown above - fields expires and ttl. When client connection expire mechanism is on the value of field expires in connect reply is true. In this case client implementation should look at ttl value which is seconds left until connection will be considered expired. Client must send refresh command after this ttl seconds. Server gives client a configured window to refresh token after ttl passed and then closes connection if client have not updated its token. When connecting with already expired token an error will be returned (with code 109). In this case client should refresh its token and reconnect with exponential backoff.","s":"Refresh","u":"/docs/3/transports/client_protocol","h":"#refresh","p":1159},{"i":1174,"t":"The mechanics of these calls is simple - client sends command and expects response from server. publish command allows to publish a message into a channel from a client. tip To publish from client publish option in Centrifugo configuration must be set to true history allows asking a server for channel history if enabled. presence allows asking a server for channel presence information if enabled. presence_stats allows asking for short presence info (num clients and unique users in a channel).","s":"RPC-like calls: publish, history, presence","u":"/docs/3/transports/client_protocol","h":"#rpc-like-calls-publish-history-presence","p":1159},{"i":1176,"t":"There are several types of asynchronous messages that can come from a server to a client. All of them relate to the current client subscriptions. The most important message is Publication: { \"result\":{ \"channel\":\"ch1\", \"data\":{ \"data\":{\"input\":\"1\"}, \"info\":{ \"user\":\"2694\", \"client\":\"5c48510e-cf49-4fa8-a9b2-490b22231e74\", \"conn_info\":{\"name\":\"Alexander\"}, \"chan_info\":{} } } }} Publication is a message published into channel. Note that there is no id field in this message - this symptom allows to distinguish it from Reply to Command. Next message is Join message: { \"result\":{ \"type\":1, \"channel\":\"ch1\", \"data\":{ \"info\":{ \"user\":\"2694\", \"client\":\"5c48510e-cf49-4fa8-a9b2-490b22231e74\", \"conn_info\":{\"name\":\"Alexander\"}, \"chan_info\":{} } } }} Join messages sent when someone joined (subscribed on) channel. tip To enable Join and Leave messages join_leave option must be enabled in Centrifugo for a channel namespace. Leave messages sent when someone left (unsubscribed from) channel. { \"result\":{ \"type\":2, \"channel\":\"ch1\", \"data\":{ \"info\":{ \"user\":\"2694\", \"client\":\"5c48510e-cf49-4fa8-a9b2-490b22231e74\", \"conn_info\":{\"name\":\"Alexander\"}, \"chan_info\":{} } } }} Finally Unsubscribe message that means that server unsubscribed current client from a channel: { \"result\":{ \"type\":3, \"channel\":\"ch1\", \"data\":{} }} It's possible to distinguish between different types of asynchronous messages looking at type field (for Publication this field not set or 0).","s":"Asynchronous server-to-client messages","u":"/docs/3/transports/client_protocol","h":"#asynchronous-server-to-client-messages","p":1159},{"i":1178,"t":"To maintain connection alive and detect broken connections client must periodically send ping commands to server and expect replies to it. Ping command looks like: { \"id\":32, \"method\":\"ping\"} Server just echoes this command back. When client does not receive ping reply for some time it must consider connection broken and try to reconnect. Recommended ping interval is 25 seconds, recommended period to wait for pong is 1-5 seconds. Though those numbers can vary.","s":"Ping Pong","u":"/docs/3/transports/client_protocol","h":"#ping-pong","p":1159},{"i":1180,"t":"Client should handle disconnect advices from server. In websocket case disconnect advice is sent in reason field of CLOSE Websocket frame. Reason contains string which is disconnect object encoded into JSON (even in case of Protobuf scenario). That objects looks like: { \"reason\": \"shutdown\", \"reconnect\": true } It contains string reason of connection closing and advice to reconnect or not. Client should take this reconnect advice into account. In case of network problems and random disconnect from server without well known reason client should always try to reconnect with exponential intervals.","s":"Handle disconnects","u":"/docs/3/transports/client_protocol","h":"#handle-disconnects","p":1159},{"i":1182,"t":"This section contains advices to error handling in client implementations. Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems. Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again. Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side. The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance. Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).","s":"Handle errors","u":"/docs/3/transports/client_protocol","h":"#handle-errors","p":1159},{"i":1184,"t":"Here are some advices about client public API. Examples here are in Javascript language. This is just an attempt to help in developing a client - but rules here is not obligatorily the best way to implement client. Create client instance: const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", {}); Set connection token (in case of using Centrifugo): centrifuge.setToken(\"XXX\") Connect to server: centrifuge.connect(); 2 event handlers can be set to centrifuge object: connect and disconnect centrifuge.on('connect', function(context) { console.log(context);});centrifuge.on('disconnect', function(context) { console.log(context);}); Client created in disconnected state with reconnect attribute set to true and reconnecting flag set to false . After connect() called state goes to connecting. It's only possible to connect from disconnected state. Every time connect() called reconnect flag of client must be set to true. After each failed connect attempt state must be set to disconnected, disconnect event must be emitted (only if reconnecting flag is false), and then reconnecting flag must be set to true (if client should continue reconnecting) to not emit disconnect event again after next in a row connect attempt failure. In case of failure next connection attempt must be scheduled automatically with backoff strategy. On successful connect reconnecting flag must be set to false, backoff retry must be reset and connect event must be emitted. When connection lost then the same set of actions as when connect failed must be performed. Client must allow to subscribe on channels: var subscription = centrifuge.subscribe(\"channel\", eventHandlers); Subscription object created and control immediately returned to caller - subscribing must be performed asynchronously. This is required because client can automatically reconnect later so event-based model better suites for subscriptions. Subscription should support several event handlers: handler for publication received from channel join message handler leave message handler error handler subscribe success event handler unsubscribe event handler Every time client connects to server it must restore all subscriptions. Every time client disconnects from server it must call unsubscribe handlers for all active subscriptions and then emit disconnect event. Client must periodically (once in 25 secs, configurable) send ping messages to server. If pong has not beed received in 5 secs (configurable) then client must disconnect from server and try to reconnect with backoff strategy. Client can automatically batch several requests into one frame to server and also must be able to handle several replies received from server in one frame.","s":"Client implementation advices","u":"/docs/3/transports/client_protocol","h":"#client-implementation-advices","p":1159},{"i":1186,"t":"It's also possible to subscribe connection to channels on server side. In this case we call this server-side subscription. Client should only handle asynchronous messages coming from a server without need to create subscriptions on client side. SSS should be kept separate from client-side subs SSS requires new event handlers on top-level of Client - Subscribe, Publish, Join, Leave, Unsubscribe, event handlers will be called with event context similar to client-side subs case but with channel field Connect Reply contains SSS set by a server on connect, on reconnect client has a chance to recover missed Publications Server side subscription can happen at any moment - Sub Push will be sent to client","s":"Server side subscriptions (SSS)","u":"/docs/3/transports/client_protocol","h":"#server-side-subscriptions-sss","p":1159},{"i":1188,"t":"Client should automatically recover messages after being disconnected due to network problems and set appropriate fields in subscribe event context. Two important fields in onSubscribeSuccess event context are isRecovered and isResubscribe. First field let user know what server thinks about subscription state - were all messages recovered or not. The second field must only be true if resubscribe was caused by temporary network connection lost. If user initiated resubscribe himself (calling unsubscribe method and then subscribe method) then recover workflow should not be used and isResubscribe must be false.","s":"Message recovery","u":"/docs/3/transports/client_protocol","h":"#message-recovery","p":1159},{"i":1190,"t":"In case of Websocket it is sent by server in CLOSE Websocket frame. This is a string containing JSON object with fields: reason (string) and reconnect (bool). Client should give users access to these fields in disconnect event and automatically follow reconnect advice.","s":"Disconnect code and reason","u":"/docs/3/transports/client_protocol","h":"#disconnect-code-and-reason","p":1159},{"i":1192,"t":"Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in reply to a subscribe command.","s":"Additional notes","u":"/docs/3/transports/client_protocol","h":"#additional-notes","p":1159},{"i":1194,"t":"On this page","s":"Unidirectional HTTP streaming","u":"/docs/3/transports/uni_http_stream","h":"","p":1193},{"i":1196,"t":"It's possible to pass initial connect command by posting a JSON body to a streaming endpoint. Refer to the full Connect command description – it's the same as for unidirectional WebSocket.","s":"Connect command","u":"/docs/3/transports/uni_http_stream","h":"#connect-command","p":1193},{"i":1198,"t":"JSON","s":"Supported data formats","u":"/docs/3/transports/uni_http_stream","h":"#supported-data-formats","p":1193},{"i":1200,"t":"Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/3/transports/uni_http_stream","h":"#pings","p":1193},{"i":1203,"t":"Boolean, default: false. Enables unidirectional HTTP streaming endpoint. config.json { ... \"uni_http_stream\": true}","s":"uni_http_stream","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream","p":1193},{"i":1205,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"uni_http_stream_max_request_body_size","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":1193},{"i":1207,"t":"Let's look how simple it is to connect to Centrifugo using HTTP streaming. We will start from scratch, generate new configuration file: centrifugo genconfig Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect: config.json { ... \"uni_http_stream\": true, \"user_subscribe_to_personal\": true} Run Centrifugo: centrifugo -c config.json In separate terminal window create token for a user: ❯ go run main.go gentoken -u user12HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request: curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}' Open one more terminal window and publish message to a personal user channel: curl -X POST http://localhost:8000/api \\ -d '{\"method\": \"publish\", \"params\": {\"channel\": \"#user12\", \"data\": {\"input\": \"hello\"}}}' \\ -H \"Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc\" You should see this message received in a terminal window with established connection to HTTP streaming endpoint: ❯ curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}'{\"type\":6,\"data\":{\"client\":\"cf5dc239-83ac-4d0f-b9ed-9733d7f7b61b\",\"version\":\"dev\",\"subs\":{\"#user12\":{}}}}nullnullnullnullnull{\"channel\":\"#user12\",\"data\":{\"data\":{\"input\": \"hello\"}}} null messages are pings from a server. That's all, happy streaming!","s":"Connecting using CURL","u":"/docs/3/transports/uni_http_stream","h":"#connecting-using-curl","p":1193},{"i":1209,"t":"A basic browser example can be found here.","s":"Browser example","u":"/docs/3/transports/uni_http_stream","h":"#browser-example","p":1193},{"i":1211,"t":"On this page","s":"Unidirectional SSE (EventSource)","u":"/docs/3/transports/uni_sse","h":"","p":1210},{"i":1213,"t":"Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example: const url = new URL('http://localhost:8000/connection/uni_sse');url.searchParams.append(\"cf_connect\", JSON.stringify({ 'token': ''}));const eventSource = new EventSource(url); Refer to the full Connect command description – it's the same as for unidirectional WebSocket. The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases. tip Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.","s":"Connect command","u":"/docs/3/transports/uni_sse","h":"#connect-command","p":1210},{"i":1215,"t":"JSON","s":"Supported data formats","u":"/docs/3/transports/uni_sse","h":"#supported-data-formats","p":1210},{"i":1217,"t":"Centrifugo sends SSE data like this as pings: event: pingdata: I.e. with event name ping and empty data. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/3/transports/uni_sse","h":"#pings","p":1210},{"i":1220,"t":"Boolean, default: false. Enables unidirectional SSE (EventSource) endpoint. config.json { ... \"uni_sse\": true}","s":"uni_sse","u":"/docs/3/transports/uni_sse","h":"#uni_sse","p":1210},{"i":1222,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes (when using HTTP POST requests to connect).","s":"uni_sse_max_request_body_size","u":"/docs/3/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":1210},{"i":1224,"t":"A basic browser example can be found here.","s":"Browser example","u":"/docs/3/transports/uni_sse","h":"#browser-example","p":1210},{"i":1226,"t":"On this page","s":"Unidirectional GRPC","u":"/docs/3/transports/uni_grpc","h":"","p":1225},{"i":1228,"t":"JSON and binary.","s":"Supported data formats","u":"/docs/3/transports/uni_grpc","h":"#supported-data-formats","p":1225},{"i":1231,"t":"Boolean, default: false. Enables unidirectional GRPC endpoint. config.json { ... \"uni_grpc\": true}","s":"uni_grpc","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc","p":1225},{"i":1233,"t":"String, default \"11000\". Port to listen on.","s":"uni_grpc_port","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_port","p":1225},{"i":1235,"t":"String, default \"\" (listen on all interfaces) Address to bind uni GRPC to.","s":"uni_grpc_address","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_address","p":1225},{"i":1237,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from GRPC connection in bytes.","s":"uni_grpc_max_receive_message_size","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":1225},{"i":1239,"t":"Boolean, default: false Enable custom TLS for unidirectional GRPC server.","s":"uni_grpc_tls","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls","p":1225},{"i":1241,"t":"String, default: \"\". Path to cert file.","s":"uni_grpc_tls_cert","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":1225},{"i":1243,"t":"String, default: \"\". Path to key file.","s":"uni_grpc_tls_key","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_key","p":1225},{"i":1245,"t":"A basic example can be found here. It uses Go language, but for other languages approach is mostly the same: Copy Protobuf definitions Generate GRPC client code Use generated code to connect to Centrifugo Process Push messages, drop unknown Push types, handle those necessary for the application.","s":"Example","u":"/docs/3/transports/uni_grpc","h":"#example","p":1225},{"i":1247,"t":"On this page","s":"Unidirectional WebSocket","u":"/docs/3/transports/uni_websocket","h":"","p":1246},{"i":1249,"t":"It's possible to send connect command as first WebSocket message (as JSON). Field name Field type Required Description token string no Connection JWT, not required when using the connect proxy feature. data any JSON no Custom JSON connection data name string no Application name version string no Application version subs map of channel to SubscribeRequest no Pass an information about desired subscriptions to a server","s":"Connect command","u":"/docs/3/transports/uni_websocket","h":"#connect-command","p":1246},{"i":1251,"t":"Field name Field type Required Description recover boolean no Whether a client wants to recover from a certain position offset integer no Known stream position offset when recover is used epoch string no Known stream position epoch when recover is used","s":"SubscribeRequest","u":"/docs/3/transports/uni_websocket","h":"#subscriberequest","p":1246},{"i":1253,"t":"JSON","s":"Supported data formats","u":"/docs/3/transports/uni_websocket","h":"#supported-data-formats","p":1246},{"i":1255,"t":"Centrifugo uses empty messages (frame with no payload at all) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/3/transports/uni_websocket","h":"#pings","p":1246},{"i":1258,"t":"Boolean, default: false. Enables unidirectional WebSocket endpoint. config.json { ... \"uni_websocket\": true}","s":"uni_websocket","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket","p":1246},{"i":1260,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from WebSocket connection in bytes.","s":"uni_websocket_message_size_limit","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":1246},{"i":1262,"t":"Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal. First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example: config.json { \"token_hmac_secret_key\": \"secret\", \"uni_websocket\":true, \"user_subscribe_to_personal\": true} Run Centrifugo: ./centrifugo -c config.json In another terminal: ❯ ./centrifugo -c config.json -u test_userHMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw Install wscat and run: wscat -c \"ws://localhost:8000/connection/uni_websocket\" This will establish a connection with a server and you then can send connect command to a server: ❯ wscat -c \"ws://localhost:8000/connection/uni_websocket\"Connected (press CTRL+C to quit)> {\"token\": \"eyJh..5lz1Iw\", \"subs\": {\"abc\": {}}}< {\"type\":6,\"data\":{\"client\":\"8ceaa299-4c7b-4254-9d65-c61b6883833a\",\"version\":\"3.0.0\",\"subs\":{\"#test_user\":{\"recoverable\":true,\"epoch\":\"StoH\",\"positioned\":true},\"abc\":{\"recoverable\":true,\"epoch\":\"nNgd\",\"positioned\":true},\"expires\":true,\"ttl\":604653}} The connection will receive pings (empty messages) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.","s":"Example","u":"/docs/3/transports/uni_websocket","h":"#example","p":1246},{"i":1264,"t":"On this page","s":"Real-time transports","u":"/docs/3/transports/overview","h":"","p":1263},{"i":1266,"t":"Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus. Bidirectional transports come with a cost that developers need to use a special client connector library which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state and request queueing/timeouts/errors. Centrifugo has client SDKs for bidirectional communication for popular environments.","s":"Bidirectional","u":"/docs/3/transports/overview","h":"#bidirectional","p":1263},{"i":1268,"t":"Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time. The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource, HTTP streaming), or GRPC generated code to receive real-time updates from Centrifugo – thus avoiding dependency to a client connector that abstracts bidirectional communication. The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo).","s":"Unidirectional","u":"/docs/3/transports/overview","h":"#unidirectional","p":1263},{"i":1270,"t":"In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as: message Push { enum PushType { PUBLICATION = 0; JOIN = 1; LEAVE = 2; UNSUBSCRIBE = 3; MESSAGE = 4; SUBSCRIBE = 5; CONNECT = 6; DISCONNECT = 7; REFRESH = 8; } PushType type = 1; string channel = 2; bytes data = 3;} So unidirectional connection will receive various pushes. All you need to do is look at Push type and process it or skip it. In most cases you will be most interested in CONNECT and PUBLICATION types. tip In case of unidirectional WebSocket, EventSource and HTTP-streaming which currently work only with JSON data field of Push will come as an embedded JSON instead of bytes (again – the same mechanism as for Centrifugo bidirectional JSON protocol). Just try using any unidirectional transport and you will quickly get the idea.","s":"Unidirectional message types","u":"/docs/3/transports/overview","h":"#unidirectional-message-types","p":1263},{"i":1272,"t":"On this page","s":"WebSocket","u":"/docs/3/transports/websocket","h":"","p":1271},{"i":1275,"t":"Default: 65536 (64KB) Maximum allowed size of a message received from WebSocket connection in bytes.","s":"websocket_message_size_limit","u":"/docs/3/transports/websocket","h":"#websocket_message_size_limit","p":1271},{"i":1277,"t":"In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size). config.json { ... \"websocket_read_buffer_size\": 512}","s":"websocket_read_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_read_buffer_size","p":1271},{"i":1279,"t":"In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused): config.json { ... \"websocket_write_buffer_size\": 512}","s":"websocket_write_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_write_buffer_size","p":1271},{"i":1281,"t":"If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.","s":"websocket_use_write_buffer_pool","u":"/docs/3/transports/websocket","h":"#websocket_use_write_buffer_pool","p":1271},{"i":1283,"t":"An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire. We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally. caution Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable. To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature. Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client. It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.","s":"websocket_compression","u":"/docs/3/transports/websocket","h":"#websocket_compression","p":1271},{"i":1285,"t":"In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if: you want less traffic on wire as Protobuf is very compact you want maximum performance on server-side as Protobuf encoding/decoding is very efficient you can sacrifice human-readable JSON for your application Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like: wss://centrifugo.example.com/connection/websocket?format=protobuf After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes. tip You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.","s":"Protobuf binary protocol","u":"/docs/3/transports/websocket","h":"#protobuf-binary-protocol","p":1271},{"i":1287,"t":"Attributions","s":"Attributions","u":"/docs/4/attributions","h":"","p":1286},{"i":1289,"t":"The following images have been used in the landing page. Icons made by kerismaker: https://www.flaticon.com/packs/web-maintenance-35","s":"Landing Page Images","u":"/docs/4/attributions","h":"#landing-page-images","p":1286},{"i":1291,"t":"On this page","s":"Frequently Asked Questions","u":"/docs/4/faq","h":"","p":1290},{"i":1293,"t":"This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here. Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case. You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.","s":"How many connections can one Centrifugo instance handle?","u":"/docs/4/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":1290},{"i":1295,"t":"Depending on transport used and features enabled the amount of RAM required per each connection can vary. For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections. For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.","s":"Memory usage per connection?","u":"/docs/4/faq","h":"#memory-usage-per-connection","p":1290},{"i":1297,"t":"Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker. See engines and scalability considerations.","s":"Can Centrifugo scale horizontally?","u":"/docs/4/faq","h":"#can-centrifugo-scale-horizontally","p":1290},{"i":1299,"t":"See design overview","s":"Message delivery model","u":"/docs/4/faq","h":"#message-delivery-model","p":1290},{"i":1301,"t":"See design overview.","s":"Message order guarantees","u":"/docs/4/faq","h":"#message-order-guarantees","p":1290},{"i":1303,"t":"No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel. When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).","s":"Should I create channels explicitly?","u":"/docs/4/faq","h":"#should-i-create-channels-explicitly","p":1290},{"i":1305,"t":"Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application. But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc. One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.","s":"What about best practices with the number of channels?","u":"/docs/4/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":1290},{"i":1307,"t":"Currently, no. We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this: Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID) Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates. Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point. Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel. In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.","s":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/4/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":1290},{"i":1309,"t":"No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere. Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end). To summarize: if you are using binary Protobuf clients and binary payloads everywhere – you are fine. if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine. if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.","s":"Can I have both binary and JSON clients in one channel?","u":"/docs/4/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":1290},{"i":1311,"t":"While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel. You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.","s":"Online presence for chat apps - online status of your contacts","u":"/docs/4/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":1290},{"i":1313,"t":"The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.","s":"Centrifugo stops accepting new connections, why?","u":"/docs/4/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":1290},{"i":1315,"t":"Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.","s":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/4/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":1290},{"i":1317,"t":"Yes, Centrifugo works with HTTP/2. This is provided by built-in Go http server implementation. You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable: GODEBUG=\"http2server=0\" centrifugo -c config.json Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.","s":"Does Centrifugo work with HTTP/2?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http2","p":1290},{"i":1319,"t":"Centrifugo v4 added an experimental HTTP/3 support. As soon as you enabled TLS and provided \"http3\": true option all endpoints on external port will be served by HTTP/3 server based on github.com/quic-go/quic-go implementation. This (among other benefits which HTTP/3 can provide) is a step towards a proper WebTransport support. For now we support WebTransport experimentally. It's worth noting that WebSocket transport does not work over HTTP/3, it still starts with HTTP/1.1 Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and Eventsource should work just fine with HTTP/3. HTTP/3 does not work with ACME autocert TLS at the moment - i.e. you need to explicitly provide paths to cert and key files as described here.","s":"Does Centrifugo work with HTTP/3?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http3","p":1290},{"i":1321,"t":"If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.","s":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/4/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":1290},{"i":1323,"t":"Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via FCM, or browsers over Web Push API. We are preparing our own push notifications API as part of Centrifugo PRO version. This is under construction though. The reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.","s":"What if I need to send push notifications to mobile or web applications?","u":"/docs/4/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":1290},{"i":1325,"t":"You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.","s":"How can I know a message is delivered to a client?","u":"/docs/4/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":1290},{"i":1327,"t":"It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend. We suggest using one of the available approaches: When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API. Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network. Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself). Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.","s":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/4/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":1290},{"i":1329,"t":"There are several ways to achieve it: use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers you can use subscribe proxy feature to validate subscriptions, see chapter about proxy finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations","s":"How to create a secure channel for two users only (private chat case)?","u":"/docs/4/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":1290},{"i":1331,"t":"In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled. For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients. The same relates to other channel options.","s":"What's the best way to organize channel configuration?","u":"/docs/4/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":1290},{"i":1333,"t":"Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there. Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.","s":"Does Centrifugo support webhooks?","u":"/docs/4/faq","h":"#does-centrifugo-support-webhooks","p":1290},{"i":1335,"t":"Centrifugo does not support disconnect hooks at this point. First of all, there is no guarantee that the disconnect process will have a time to execute on the client-side (as the client can just switch off its device or simply lose internet connection). This means that a server may notice a connection loss with some delay (thanks to PING/PONG mechanism). Centrifugo node can be unexpectedly killed. So there is a chance that disconnect event won't have a chance to be emitted to the backend. One more reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. To reduce the load during connect process Centrifugo has JWT authentication. Even if disconnect events were queued/rate-limited there could be situations when your app processes disconnect hook while user already reconnected and connect event processed. This is a racy situation which you will need to handle somehow (possibly based on unique client ID attached to each connection). If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend from the client-side and update user status somewhere on the backend (use Redis maybe). This is a pretty robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo refresh proxy for the task of periodic backend pinging. In this case you will notice that user (or particular client) left app with some delay – this may be a acceptable trade-off in many cases. Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together.","s":"Why Centrifugo does not have disconnect hooks?","u":"/docs/4/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":1290},{"i":1337,"t":"No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.","s":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/4/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":1290},{"i":1339,"t":"Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel). There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers. You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs. The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.","s":"How scalable is the online presence and join/leave features?","u":"/docs/4/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":1290},{"i":1341,"t":"Sometimes you need to send some initial state towards channel subscriber. Centrifugo provides a way to attach any data to a successful subscribe reply when using subscribe proxy feature. See data and b64data fields. This data will be part of subscribed event context. And of course, you can always simply send request to get initial data from the application backend before or after subscribing to a channel without Centrifugo connection involved (i.e. using sth like general AJAX/HTTP call or passing data to the template when rendering an application page).","s":"How to send initial data to channel subscriber?","u":"/docs/4/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":1290},{"i":1343,"t":"If you want to use Centrifugo with different projects the recommended approach is to have different Centrifugo installations for each project. Multitenancy is better to solve on infrastructure level in case of Centrifugo. It's possible to share one Redis setup though by setting unique redis_prefix. But we recommend having completely isolated setups.","s":"Does Centrifugo support multitenancy?","u":"/docs/4/faq","h":"#does-centrifugo-support-multitenancy","p":1290},{"i":1345,"t":"Ask in our community rooms:","s":"I have not found an answer to my question here:","u":"/docs/4/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":1290},{"i":1347,"t":"flow_diagrams For swimlanes.io: Client <- App Backend: JWTnote:The backend generates JWT for a user and passes it to the client side.Client -> Centrifugo: Client connects to Centrifugo with JWT...: {fas-spinner} Persistent connection establishedClient -> Centrifugo: Client issues channel subscribe requestsCentrifugo -->> Client: Client receives real-time updates from channels Client -> Centrifugo: Connect requestnote:Client connects to Centrifugo without JWT.Centrifugo -> App backend: Sends request further (via HTTP or GRPC)note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.App backend -> Centrifugo: Connect replyCentrifugo -> Client: Connect Reply...: {fas-spinner} Persistent connection established Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers. Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers.","s":"flow_diagrams","u":"/docs/4/flow_diagrams","h":"","p":1346},{"i":1349,"t":"Join community If you find Centrifugo interesting – please welcome to our community rooms in Telegram (the most active) and Discord: We are trying to create a respectful and curious community. In those rooms you may find answers to questions not fully covered by the documentation, share your thoughts and ideas on the real-time messaging topics and Centrifugo in particular. We also have Twitter account and Youtube channel. See you there!","s":"Join community","u":"/docs/4/getting-started/community","h":"","p":1348},{"i":1351,"t":"On this page","s":"Ecosystem notes","u":"/docs/4/getting-started/ecosystem","h":"","p":1350},{"i":1353,"t":"Centrifugo is built on top of Centrifuge library for Go language. Due to its standalone language-agnostic nature Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a more tight integration of real-time server with application business logic you may consider using Centrifuge library to build something similar to Centrifugo but with customized behavior. Centrifuge library can be considered as Socket.IO analogue in Go language ecosystem. Library README has detailed description, link to examples and introduction post. Many Centrifugo features should be re-implemented when using Centrifuge - like API layer, admin web UI, proxy etc (if you need those of course). And you need to write in Go language. But the core functionality like a client-server protocol (all Centrifugo client SDKs work with Centrifuge library based server) and Redis engine to scale come out of the box – in most cases this is enough to start building an app. tip Many things said in Centrifugo doc can be considered as an extra documentation for Centrifuge library (for example part about infrastructure tuning or transport description). But not all of them.","s":"Centrifuge library for Go","u":"/docs/4/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":1350},{"i":1355,"t":"There are some community-driven projects that provide integration with frameworks for more native experience. tip In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. Integrating directly may allow using all Centrifugo features without limitations which can be introduced by third-party wrapper. laravel-centrifugo integration with Laravel framework laravel-centrifugo-broadcaster one more integration with Laravel framework to consider CentrifugoBundle integration with Symfony framework Django-instant integration with Django framework roadrunner-php/centrifugo integration with RoadRunner spiral/roadrunner-bridge integration with Spiral Framework","s":"Framework integrations","u":"/docs/4/getting-started/ecosystem","h":"#framework-integrations","p":1350},{"i":1357,"t":"On this page","s":"Client API showcase","u":"/docs/4/getting-started/client_api","h":"","p":1356},{"i":1359,"t":"Each Centrifugo client allows connecting to a server. const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.connect(); In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to: const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.setToken('')centrifuge.connect(); See authentication chapter for more information on how to generate connection JWT. If you are using connect proxy then you may go without setting JWT.","s":"Connecting to a server","u":"/docs/4/getting-started/client_api","h":"#connecting-to-a-server","p":1356},{"i":1361,"t":"After connecting you can disconnect from a server at any moment. centrifuge.disconnect();","s":"Disconnecting from a server","u":"/docs/4/getting-started/client_api","h":"#disconnecting-from-a-server","p":1356},{"i":1363,"t":"Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.","s":"Reconnecting to a server","u":"/docs/4/getting-started/client_api","h":"#reconnecting-to-a-server","p":1356},{"i":1365,"t":"All client implementations allow setting handlers on connect and disconnect events. For example: centrifuge.on('connect', function(connectCtx){ console.log('connected', connectCtx)});centrifuge.on('disconnect', function(disconnectCtx){ console.log('disconnected', disconnectCtx)});","s":"Connection lifecycle events","u":"/docs/4/getting-started/client_api","h":"#connection-lifecycle-events","p":1356},{"i":1367,"t":"Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel. centrifuge.subscribe('channel', function(messageCtx) { console.log(messageCtx);}) Client can subscribe to different channels. Subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events. In idiomatic case messages published to channels from application backend over Centrifugo server API. Though it's not always true. Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel properly configured. Client keeps last seen Publication offset and restores missed publications since known offset upon reconnecting. If recovery failed then client implementation provides a flag inside subscribe event to let the application know that some publications were missed – so you may need to load state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery in a dedicated chapter.","s":"Subscribe to a channel","u":"/docs/4/getting-started/client_api","h":"#subscribe-to-a-channel","p":1356},{"i":1369,"t":"To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance: centrifuge.on('publish', function(messageCtx) { console.log(messageCtx);}); It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.","s":"Server-side subscriptions","u":"/docs/4/getting-started/client_api","h":"#server-side-subscriptions","p":1356},{"i":1371,"t":"A client can send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the WebSocket or SockJS connection. RPC is only available when RPC proxy configured. const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"Send RPC","u":"/docs/4/getting-started/client_api","h":"#send-rpc","p":1356},{"i":1373,"t":"Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel: Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Call channel history","u":"/docs/4/getting-started/client_api","h":"#call-channel-history","p":1356},{"i":1375,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats","u":"/docs/4/getting-started/client_api","h":"#presence-and-presence-stats","p":1356},{"i":1377,"t":"On this page","s":"Design overview","u":"/docs/4/getting-started/design","h":"","p":1376},{"i":1379,"t":"Originally Centrifugo was built with the unidirectional flow as the main approach. Though Centrifugo itself used a bidirectional protocol between a client and a server to allow client dynamically create subscriptions, Centrifugo did not allow using it for sending data from client to server. With this approach publications travel only from server to a client. All requests that generate new data first go to the application backend (for example over AJAX call of backend API). The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event from a backend side to Centrifugo API. This is a pretty natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is decoupled from the application in this case. During Centrifugo v2 life cycle this paradigm evolved a bit. It's now possible to send RPC requests from client to Centrifugo and the request will be then proxied to the application backend. Also, connection attempts and publications to channels can now be proxied. So bidirectional connection between client and Centrifugo is now available for utilizing by developers in both directions. For example, here is how publish diagram could look like when using publish request proxy feature: So at the moment, the number of possible integration ways increased.","s":"Idiomatic usage","u":"/docs/4/getting-started/design","h":"#idiomatic-usage","p":1376},{"i":1381,"t":"Idiomatic Centrifugo usage requires having the main application database from which initial and actual state can be loaded at any point in time. While Centrifugo has channel history, it has been mostly designed to reduce the load on the main application database when all users reconnect at once (in case of load balancer configuration reload, Centrifugo restart, temporary network problems, etc). This allows to radically reduce the load on the application main database during reconnect storm. Since such disconnects are usually pretty short in time having a reasonably small number of messages cached in history is sufficient. The addition of history iteration API shifts possible use cases a bit. Manually calling history chunk by chunk allows keeping larger number of publications per channel. Depending on Engine used and configuration of the underlying storage history stream persistence characteristics can vary. For example, with Memory Engine history will be lost upon Centrifugo restart. With Redis or Tarantool engines history will survive Centrifugo restarts but depending on a storage configuration it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis RDB and AOF, configure replication for storage high-availability, use Redis Cluster or maybe synchronous replication with Tarantool. Centrifugo provides ways to distinguish whether the missed messages can't be restored from Centrifugo history upon recovery so a client should restore state from the main application database. So Centrifugo message history can be used as a complementary way to restore messages and thus reduce a load on the main application database most of the time.","s":"Message history considerations","u":"/docs/4/getting-started/design","h":"#message-history-considerations","p":1376},{"i":1383,"t":"By default, the message delivery model of Centrifugo is at most once. With history and the positioning/recovery features enabled it's possible to achieve at least once guarantee within history retention time and size. After abnormal disconnect clients have an option to recover missed messages from the publication channel stream history that Centrifugo maintains. Without the positioning or recovery features enabled a message sent to Centrifugo can be theoretically lost while moving towards clients. Centrifugo tries to do its best to prevent message loss on a way to online clients, but the application should tolerate a loss. As noted Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. Also, it compensates at most once delivery of broker PUB/SUB system (Redis, Tarantool) by using additional publication offset checks and periodic offset synchronization. So publication loss missed in PUB/SUB layer will be detected eventually and client may catch up the state loading it from history. At this moment Centrifugo message recovery is designed for a short-term disconnect period (think no more than one hour for a typical chat application, but this can vary). After this period (which can be configured per channel basis) Centrifugo removes messages from the channel history cache. In this case, Centrifugo may tell the client that some messages can not be recovered, so your application state should be loaded from the main database.","s":"Message delivery model","u":"/docs/4/getting-started/design","h":"#message-delivery-model","p":1376},{"i":1385,"t":"Message order in channels is guaranteed to be the same while you publish messages into channel one after another or publish them in one request. If you do parallel publications into the same channel then Centrifugo can't guarantee message order since those may be processed concurrently by Centrifugo.","s":"Message order guarantees","u":"/docs/4/getting-started/design","h":"#message-order-guarantees","p":1376},{"i":1387,"t":"It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely only on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in AJAX call response and render it. This way user that posts a comment will think that everything works just fine. Be careful to not draw comments twice in this case - think about idempotent identifiers for your entities.","s":"Graceful degradation","u":"/docs/4/getting-started/design","h":"#graceful-degradation","p":1376},{"i":1389,"t":"Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to the network failures and unexpected shut down of Centrifugo node, there are chances that clients can be presented in a presence up to one minute more (until presence entry expiration). Also, channel presence does not scale well for channels with lots of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows the response size becomes larger. In some cases, presence_stats API call can be sufficient to avoid receiving the entire presence state.","s":"Online presence considerations","u":"/docs/4/getting-started/design","h":"#online-presence-considerations","p":1376},{"i":1391,"t":"Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with Nats broker. See engines. All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications. But, if you approach broker resource limits (CPU or memory) then it's possible: Use Centrifugo consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool) Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters) Nats broker should scale well itself in cluster setup All brokers can be set up in highly available way so there won't be a single point of failure. All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact and the fact that Centrifugo provides hooks for the application to understand history loss makes the process of resharding mostly automatic. As soon as you need to add additional broker shard (when using client-side sharding) you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently part of the data will stay on the same broker nodes. Applications should handle cases that channel data moved to another shard and restore a state from the main application database when needed.","s":"Scalability considerations","u":"/docs/4/getting-started/design","h":"#scalability-considerations","p":1376},{"i":1393,"t":"On this page","s":"Main highlights","u":"/docs/4/getting-started/highlights","h":"","p":1392},{"i":1395,"t":"Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.). It works as a standalone service with well-defined communication contracts. It nicely fits both monolithic and microservice architectures. Application developers should not change backend philosophy and technology stack at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.","s":"Simple integration","u":"/docs/4/getting-started/highlights","h":"#simple-integration","p":1392},{"i":1397,"t":"Centrifugo is fast. It's written in Go language, built on top of fast and battle-tested open-source libraries, has some smart internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTT with broker, connection hub sharding to avoid lock contention, JSON and Protobuf encoding speedups over code generation and other. See a Million WebSocket with Centrifugo post in our blog to see some real-world numbers.","s":"Great performance","u":"/docs/4/getting-started/highlights","h":"#great-performance","p":1392},{"i":1399,"t":"Centrifugo scales well to many machines with a help of PUB/SUB brokers. So as soon as you have more client connections in the application – you can spread them over different Centrifugo nodes which will be connected together into a cluster. The main PUB/SUB engine Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck also. There are other options to scale: KeyDB, Nats, Tarantool. See docs.","s":"Built-in scalability","u":"/docs/4/getting-started/highlights","h":"#built-in-scalability","p":1392},{"i":1401,"t":"Centrifugo supports JSON and binary Protobuf protocol for client-server communication. The bidirectional protocol is defined by a strict schema and several ready-to-use SDKs wrap this protocol, handle asynchronous message passing, timeouts, reconnects, and various Centrifugo client API features. See the detailed information about client real-time transports in a dedicated section.","s":"Strict client protocol","u":"/docs/4/getting-started/highlights","h":"#strict-client-protocol","p":1392},{"i":1403,"t":"The main transport in Centrifugo is WebSocket. For browsers that do not support WebSocket Centrifugo provides its own bidirectional WebSocket emulation layer based on HTTP-streaming and SSE (EventSource), and also supports SockJS as an older but battle-tested WebSocket polyfill option, and WebTransport in experimental form. Centrifugo also supports unidirectional transports for real-time updates: like SSE (EventSource), HTTP streaming, GRPC unidirectional stream. Using unidirectional transport is sufficient for many real-time applications and does not require using our client SDKs – just native standards or GRPC-generated code. See the detailed information about client real-time transports in a dedicated section.","s":"Variety of real-time transports","u":"/docs/4/getting-started/highlights","h":"#variety-of-real-time-transports","p":1392},{"i":1405,"t":"Centrifugo can authenticate connections using JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon client connection to Centrifugo. It's possible to proxy original request headers or request metadata (in the case of GRPC connection). It supports the JWK specification.","s":"Flexible authentication","u":"/docs/4/getting-started/highlights","h":"#flexible-authentication","p":1392},{"i":1407,"t":"Connections can expire, developers can choose a way to handle connection refresh – using a client-side refresh workflow, or a server-side call from Centrifugo to the application backend.","s":"Connection management","u":"/docs/4/getting-started/highlights","h":"#connection-management","p":1392},{"i":1409,"t":"Centrifugo is a PUB/SUB server – users subscribe on channels to receive real-time updates. Message sent to a channel is delivered to all online channel subscribers.","s":"Channel (room) concept","u":"/docs/4/getting-started/highlights","h":"#channel-room-concept","p":1392},{"i":1411,"t":"Centrifugo supports client-side (initiated by a client) and server-side (forced by a server) channel subscriptions.","s":"Different types of subscriptions","u":"/docs/4/getting-started/highlights","h":"#different-types-of-subscriptions","p":1392},{"i":1413,"t":"You can fully utilize bidirectional connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing external traffic and, in most cases, provides better latency characteristics.","s":"RPC over bidirectional connection","u":"/docs/4/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":1392},{"i":1415,"t":"Online presence feature for channels provides information about active channel subscribers. Also, channel join and leave events (when someone subscribes/unsubscribes) can be received on the client side.","s":"Online presence information","u":"/docs/4/getting-started/highlights","h":"#online-presence-information","p":1392},{"i":1417,"t":"Optionally Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With a channel history, Centrifugo can help to survive the mass reconnect scenario – clients can automatically catch up missed state from a fast cache thus reducing the load on your primary database. It's also possible to manually iterate over a stream from a client or a server-side.","s":"Message history in channels","u":"/docs/4/getting-started/highlights","h":"#message-history-in-channels","p":1392},{"i":1419,"t":"Built-in admin UI allows publishing messages to channels, look at Centrifugo cluster information, and more.","s":"Embedded admin web UI","u":"/docs/4/getting-started/highlights","h":"#embedded-admin-web-ui","p":1392},{"i":1421,"t":"Centrifugo works on Linux, macOS, and Windows.","s":"Cross-platform","u":"/docs/4/getting-started/highlights","h":"#cross-platform","p":1392},{"i":1423,"t":"Centrifugo supports various deploy ways: in Docker, using prepared RPM or DEB packages, via Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, has an official Grafana dashboard for Prometheus data source.","s":"Ready to deploy","u":"/docs/4/getting-started/highlights","h":"#ready-to-deploy","p":1392},{"i":1425,"t":"Centrifugo stands on top of open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All our official client SDKs and API libraries are MIT-licensed.","s":"Open-source","u":"/docs/4/getting-started/highlights","h":"#open-source","p":1392},{"i":1427,"t":"Centrifugo PRO extends Centrifugo with several unique features which can give interesting advantages for business adopters. For additional details, refer to the Centrifugo PRO documentation.","s":"Pro features","u":"/docs/4/getting-started/highlights","h":"#pro-features","p":1392},{"i":1429,"t":"On this page","s":"Install Centrifugo","u":"/docs/4/getting-started/installation","h":"","p":1428},{"i":1431,"t":"For a local development you can download prebuilt Centrifugo binary release (i.e. single all-contained executable file) for your system. Binary releases available on Github. Download latest release for your operating system, unpack it and you are done. Centrifugo is pre-built for: Linux 64-bit (linux_amd64) Linux 32-bit (linux_386) Linux ARM 64-bit (linux_arm64) MacOS (darwin_amd64) MacOS on Apple Silicon (darwin_arm64) Windows (windows_amd64) FreeBSD (freebsd_amd64) ARM v6 (linux_armv6) Archives contain a single statically compiled binary centrifugo file that is ready to run: ./centrifugo If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack centrifugo binary to your current working directory: curl -sSLf https://centrifugal.dev/install.sh | sh See the version of Centrifugo: ./centrifugo version Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo then there is genconfig command which generates a minimal configuration file to get started: ./centrifugo genconfig It creates a configuration file config.json with some auto-generated option values in a current directory (by default). tip It's possible to generate file in YAML or TOML format, i.e. ./centrifugo genconfig -c config.toml Having a configuration file you can finally run Centrifugo instance: ./centrifugo --config=config.json We will talk about a configuration in detail in the next sections. You can also put or symlink centrifugo into your bin OS directory and run it from anywhere: centrifugo --config=config.json","s":"Install from the binary release","u":"/docs/4/getting-started/installation","h":"#install-from-the-binary-release","p":1428},{"i":1433,"t":"Centrifugo server has a docker image available on Docker Hub. docker pull centrifugo/centrifugo Run: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json Note that docker allows setting nofile limits in command-line arguments which is pretty important to handle lots of simultaneous persistent connections and not run out of open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter. caution Pin to the exact Docker Image tag in production, for example: centrifugo/centrifugo:v4.0.0, this will help to avoid unexpected problems during re-deploy process.","s":"Docker image","u":"/docs/4/getting-started/installation","h":"#docker-image","p":1428},{"i":1435,"t":"Create configuration file config.json: { \"token_hmac_secret_key\": \"my_secret\", \"api_key\": \"my_api_key\", \"admin_password\": \"password\", \"admin_secret\": \"secret\", \"admin\": true} Create docker-compose.yml: version: \"3.9\"services: centrifugo: container_name: centrifugo image: centrifugo/centrifugo:v4 volumes: - ./config.json:/centrifugo/config.json command: centrifugo -c config.json ports: - 8000:8000 ulimits: nofile: soft: 65535 hard: 65535 Run with: docker-compose up","s":"Docker-compose example","u":"/docs/4/getting-started/installation","h":"#docker-compose-example","p":1428},{"i":1437,"t":"See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.","s":"Kubernetes Helm chart","u":"/docs/4/getting-started/installation","h":"#kubernetes-helm-chart","p":1428},{"i":1439,"t":"Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io. At moment, we support versions of the following distributions: 64-bit Debian 8 Jessie 64-bit Debian 9 Stretch 64-bit Debian 10 Buster 64-bit Debian 11 Bullseye 64-bit Ubuntu 16.04 Xenial 64-bit Ubuntu 18.04 Bionic 64-bit Ubuntu 20.04 Focal Fossa 64-bit Centos 7 64-bit Centos 8 See full list of available packages and installation instructions. Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.","s":"RPM and DEB packages for Linux","u":"/docs/4/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":1428},{"i":1441,"t":"If you are developing on macOS then you can install Centrifugo over brew: brew tap centrifugal/centrifugobrew install centrifugo","s":"With brew on macOS","u":"/docs/4/getting-started/installation","h":"#with-brew-on-macos","p":1428},{"i":1443,"t":"You need Go language installed: git clone https://github.com/centrifugal/centrifugo.gitcd centrifugogo build./centrifugo","s":"Build from source","u":"/docs/4/getting-started/installation","h":"#build-from-source","p":1428},{"i":1445,"t":"On this page","s":"Integration guide","u":"/docs/4/getting-started/integration","h":"","p":1444},{"i":1447,"t":"First, you need to do is download/install Centrifugo server. See install chapter for details.","s":"0. Install","u":"/docs/4/getting-started/integration","h":"#0-install","p":1444},{"i":1449,"t":"Create basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and chapter about server API for API description. The simplest way to do this automatically is by using genconfig command: ./centrifugo genconfig – which will generate config.json file for you with a minimal set of fields to start from. Properly configure allowed_origins option.","s":"1. Configure Centrifugo","u":"/docs/4/getting-started/integration","h":"#1-configure-centrifugo","p":1444},{"i":1451,"t":"In the configuration file of your application backend register several variables: Centrifugo token secret (if you decided to stick with JWT authentication) and Centrifugo API key you set on a previous step, also Centrifugo API endpoint address. By default, the API address is http://localhost:8000/api. You must never reveal token secret and API key to your users.","s":"2. Configure your backend","u":"/docs/4/getting-started/integration","h":"#2-configure-your-backend","p":1444},{"i":1453,"t":"Now your users can start connecting to Centrifugo. You should get a client library (see list of available client SDKs) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here. Every client should provide a connection token (JWT) on connect. You must generate this token on your backend side using Centrifugo secret key you set to backend configuration (note that in the case of RSA tokens you are generating JWT with a private key). See how to generate this JWT in special chapter. You pass this token from the backend to your frontend app (pass it in template context or use separate request from client-side to get user-specific JWT from backend side). And use this token when connecting to Centrifugo (for example browser client has a special method setToken). There is also a way to authenticate connections without using JWT - see chapter about proxying to backend. You are connecting to Centrifugo using one of the available transports.","s":"3. Connect to Centrifugo","u":"/docs/4/getting-started/integration","h":"#3-connect-to-centrifugo","p":1444},{"i":1455,"t":"After connecting to Centrifugo subscribe clients to channels they are interested in. See more about channels in special chapter. All bidirectional client SDKs provide a way to handle messages coming to a client from a channel after subscribing to it. Learn more about client SDK possibilities from client SDK API spec. There is also a way to subscribe connection to a list of channels on the server side at the moment of connection establishment. See chapter about server-side subscriptions.","s":"4. Subscribe to channels","u":"/docs/4/getting-started/integration","h":"#4-subscribe-to-channels","p":1444},{"i":1457,"t":"Everything should work now – as soon as a user opens some page of your application it must successfully connect to Centrifugo and subscribe to a channel (or channels). Now let's imagine you want to send a real-time message to users subscribed on a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, the administrator just created a new post, the user pressed the \"like\" button, etc. Anyway, this is an event your backend just got, and you want to immediately send it to interested users. You can do this using Centrifugo HTTP API. To simplify your life we have several API libraries for different languages. You can publish messages into a channel using one of those libraries or you can simply follow API description to construct API requests yourself - this is very simple. Also Centrifugo supports GRPC API. As soon as you published a message to the channel it must be delivered to your online client subscribed to that channel.","s":"5. Publish to channel","u":"/docs/4/getting-started/integration","h":"#5-publish-to-channel","p":1444},{"i":1459,"t":"To put this all into production you need to deploy Centrifugo on your production server. To help you with this we have many things like Docker image, rpm and deb packages, Nginx configuration. See Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.","s":"6. Deploy to production","u":"/docs/4/getting-started/integration","h":"#6-deploy-to-production","p":1444},{"i":1461,"t":"Don't forget to configure metrics monitoring your production Centrifugo setup. This may help you to understand what's going on with Centrifugo setup, understand number of connections, operation count and latency distributions, etc.","s":"7. Monitor Centrifugo","u":"/docs/4/getting-started/integration","h":"#7-monitor-centrifugo","p":1444},{"i":1463,"t":"As soon as you are close to machine resource limits you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using Redis engine, or with KeyDB, or with Tarantool, or with Nats broker. Engines and scalability chapter describes available options in detail.","s":"8. Scale Centrifugo","u":"/docs/4/getting-started/integration","h":"#8-scale-centrifugo","p":1444},{"i":1465,"t":"That's all for basics. The documentation actually covers lots of other concepts Centrifugo server has: scalability, private channels, admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ – it contains lot of useful information.","s":"9. Read FAQ","u":"/docs/4/getting-started/integration","h":"#9-read-faq","p":1444},{"i":1467,"t":"On this page","s":"Centrifugo introduction","u":"/docs/4/getting-started/introduction","h":"","p":1466},{"i":1469,"t":"Centrifugo was born a decade ago to help applications with a server-side written in a language or a framework without built-in concurrency support. In this case, dealing with persistent connections is a real headache that usually can only be resolved by introducing a shift in the technology stack and spending time to create a production-ready solution. For example, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others have poor or not really performant support of working with many persistent connections for the real-time messaging tasks. In this case, Centrifugo is a straightforward and non-obtrusive way to introduce real-time updates and handle lots of persistent connections without radical changes in the application backend architecture. Developers could proceed writing the application backend with a favorite language or favorite framework, keep existing architecture – and just let Centrifugo deal with persistent connections and be a real-time messaging transport layer. These days Centrifugo provides some advanced and unique features that can simplify a developer's life and save months of development. Even if the application backend is built with the asynchronous concurrent language. One example is that Centrifugo has built-in support for scalability to many machines to handle more connections and still making sure channel subscribers on different Centrifugo nodes receive all the publications. Centrifugo fits well modern architectures and may be a universal real-time component regardless of the application technology stack. There are more things to mention, the documentation uncovers them step by step.","s":"Background","u":"/docs/4/getting-started/introduction","h":"#background","p":1466},{"i":1471,"t":"On this page","s":"Channel capabilities","u":"/docs/4/pro/capabilities","h":"","p":1470},{"i":1473,"t":"Connection capabilities can be set: in connection JWT (in caps claim) in connect proxy result (caps field) For example, here we are issuing permissions to subscribe on channel news and channel user_42 to a client: { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] } ]} Known capabilities: sub - subscribe to a channel to receive publications from it pub - publish into a channel (your backend won't be able to process the publication in this case) prs - call presence and presence stats API, also consume join/leave events upon subscribing hst - call history API, also make Subscription positioned or recoverable upon subscribing","s":"Connection capabilities","u":"/docs/4/pro/capabilities","h":"#connection-capabilities","p":1470},{"i":1475,"t":"Centrifugo processes caps objects till it finds a match to a channel. At this point it applies permissions in the matched object and stops processing remaining caps. If no match found – then 103 permission denied returned to a client (of course if namespace does not have other permission-related options enabled). Let's consider example like this: WRONG! { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"pub\"] }, { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, ]} Here we have two entries for channel news, but when client subscribes on news only the first entry will be taken into considiration by Centrifugo – so Subscription attempt will be rejected (since first cap object does not have sub capability). In real life you don't really want to have cap objects with identical channels – but below we will introduce wildcard matching where understanding how caps processed becomes important. Another example: WRONG! { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"pub\", \"hst\", \"prs\"] }, ]} One could expect that client will have [\"sub\", \"pub\", \"hst\", \"prs\"] capabilities for a channel user_42. But it's not true since Centrifugo processes caps objects and channels inside caps object in order – it finds a match to user_42 in first caps object, it contains only \"sub\" capability, processing stops. So user can subscribe to a channel, but can not publish, can not call history and presence APIs even though those capabilities are mentioned in caps object. The correct way to give all caps to the channel user_42 would be to split channels into different caps objects: CORRECT { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] }, ]} The processing behaves like this to avoid potential problems with possibly conflicting matches (mostly when using wildcard and regex matching – see below) and still allow overriding capabilities for specific channels.","s":"Caps processing behavior","u":"/docs/4/pro/capabilities","h":"#caps-processing-behavior","p":1470},{"i":1477,"t":"In JWT auth case – capabilities in JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In connect proxy case – capabilities will work until client connection close (disconnect) or connection refresh triggered (with refresh proxy you can provide an updated set of capabilities).","s":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations","p":1470},{"i":1479,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a connection expiration, then upon refresh: if using proxy – provide new caps in refresh proxy result, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). if JWT auth - provide new caps in connection token, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). In case of using connect proxy – you can disconnect a user (or client) with a reconnect code. New capabilities will be asked upon reconnection. In case of using token auth – revoke token (Centrifugo PRO feature) and disconnect user (or client) with reconnect code. Upon reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking connection caps","u":"/docs/4/pro/capabilities","h":"#revoking-connection-caps","p":1470},{"i":1481,"t":"It's possible to use wildcards in channel resource names. For example, let's give a permission to subscribe on all channels in news namespace. { \"caps\": [ { \"channels\": [\"news:*\"], \"match\": \"wildcard\", \"allow\": [\"sub\"] } ]} note Match type is used for all channels in caps object. If you need different matching behavior for different channels then split them on different caps objects.","s":"Example: wildcard match","u":"/docs/4/pro/capabilities","h":"#example-wildcard-match","p":1470},{"i":1483,"t":"Or regex: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } ]}","s":"Example: regex match","u":"/docs/4/pro/capabilities","h":"#example-regex-match","p":1470},{"i":1485,"t":"Of course it's possible to combine different types of match inside one caps array: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } { \"channels\": [\"user_42\"], \"allow\": [\"sub\"] } ]}","s":"Example: different types of match","u":"/docs/4/pro/capabilities","h":"#example-different-types-of-match","p":1470},{"i":1487,"t":"Let's look how to allow all permissions to a client: { \"caps\": [ { \"channels\": [\"*\"], \"match\": \"wildcard\", \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] } ]} Full access warn Should we mention that giving full access to a client is something to wisely consider? 🤔","s":"Example: full access to all channels","u":"/docs/4/pro/capabilities","h":"#example-full-access-to-all-channels","p":1470},{"i":1489,"t":"Subscription capabilities can be set: in subscription JWT (in allow claim) in subscribe proxy result (allow field) Subscription token already belongs to a channel (it has a channel claim). So users with a valid subscription token can subscribe to a channel. But it's possible to additionally grant channel permissions to a user for publishing and calling presence and history using allow claim: { \"allow\": [\"pub\", \"hst\", \"prs\"]} Putting sub permission to the Subscription token does not make much sense – Centrifugo only expects valid token for a subscription permission check.","s":"Subscription capabilities","u":"/docs/4/pro/capabilities","h":"#subscription-capabilities","p":1470},{"i":1491,"t":"In JWT auth case – capabilities in subscription JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In subscribe proxy case – capabilities will work until client unsubscribe (or connection close).","s":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations-1","p":1470},{"i":1493,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a subscription expiration, then upon refresh: provide new caps in subscription token, Centrifugo will update channel caps. In case of using subscribe proxy – you can unsubscribe a user (or client) with a resubscribe code. Or disconnect with reconnect code. New capabilities will be set up upon resubscription/reconnection. In case of using JWT auth – revoke token (Centrifugo PRO feature) and unsubscribe/disconnect user (or client) with resubscribe/reconnect code. Upon resubscription/reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking subscription permissions","u":"/docs/4/pro/capabilities","h":"#revoking-subscription-permissions","p":1470},{"i":1495,"t":"Quickstart tutorial ⏱️ In this tutorial we will build a very simple browser application with Centrifugo. Users will connect to Centrifugo over WebSocket, subscribe to a channel, and start receiving all channel publications (messages published to that channel). In our case, we will send a counter value to all channel subscribers to update counter widget in all open browser tabs in real-time. First you need to install Centrifugo. In this example, we are using a binary file release which is fine for development. Once you have Centrifugo binary available on your machine you can generate minimal required configuration file with the following command: ./centrifugo genconfig This helper command will generate config.json file in the working directory with a content like this: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": []} Now we can start a server. Let's start Centrifugo with a built-in admin web interface: ./centrifugo --config=config.json --admin We could also enable the admin web interface by not using --admin flag but by adding \"admin\": true option to the JSON configuration file: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin\": true, \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": []} And then running Centrifugo only with a path to a configuration file: ./centrifugo --config=config.json Now open http://localhost:8000. You should see Centrifugo admin web panel. Enter admin_password value from the configuration file to log in (in our case it's d0683813-0916-4c49-979f-0e08a686b727, but you will have a different value). Inside the admin panel, you should see that one Centrifugo node is running, and it does not have connected clients: Now let's create index.html file with our simple app: index.html Centrifugo quick start
    -
    Note that we are using centrifuge-js 3.1.0 in this example, getting it from CDN, you better use its latest version at the moment of reading this tutorial. In real Javascript app you most probably will load centrifuge from NPM. In index.html above we created an instance of a Centrifuge client passing Centrifugo server default WebSocket endpoint address to it, then we subscribed to a channel called channel and provided a callback function to process incoming real-time messages (publications). Upon receiving a new publication we update page HTML and setting counter value to page title. We call .subscribe() to initialte subscription and .connect() method of Client to start a WebSocket connection. We also handle Client state transitions (disconnected, connecting, connected) and Subscription state transitions (unsubscribed, subscribing, subscribed) – see detailed description in client SDK spec. Now you need to serve this file with an HTTP server. In a real-world Javascript application, you will serve your HTML files with a web server of your choice – but for this simple example we can use a simple built-in Centrifugo static file server: ./centrifugo serve --port 3000 Alternatively, if you have Python 3 installed: python3 -m http.server 3000 These commands start a simple static file web server that serves the current directory on port 3000. Make sure you still have Centrifugo server running. Open http://localhost:3000/. Now if you look at browser developer tools or in Centrifugo logs you will notice that a connection can not be successfully established: 2021-09-01 10:17:33 [INF] request Origin is not authorized due to empty allowed_origins origin=http://localhost:3000 That's because we have not set allowed_origins in the configuration. Modify allowed_origins like this: config.json { ... \"allowed_origins\": [\"http://localhost:3000\"]} Allowed origins is a security option for request originating from web browsers – see more details in server configuration docs. Restart Centrifugo after modifying allowed_origins in a configuration file. Now if you reload a browser window with an application you should see new information logs in server output: 2022-06-10 09:44:21 [INF] invalid connection token error=\"invalid token: token format is not valid\" client=a65a8463-6a36-421d-814a-0083c88365292022-06-10 09:44:21 [INF] disconnect after handling command client=a65a8463-6a36-421d-814a-0083c8836529 command=\"id:1 connect:{token:\\\"\\\" name:\\\"js\\\"}\" reason=\"invalid token\" user= We still can not connect. That's because the client should provide a valid JWT (JSON Web Token) to authenticate itself. This token must be generated on your backend and passed to a client-side (over template variables or using separate AJAX call – whatever way you prefer). Since in our simple example we don't have an application backend we can quickly generate an example token for a user using centrifugo sub-command gentoken. Like this: ./centrifugo gentoken -u 123722 – where -u flag sets user ID. The output should be like this: HMAC SHA-256 JWT for user \"123722\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw – you will have another token value since this one is based on randomly generated token_hmac_secret_key from the configuration file we created at the beginning of this tutorial. See token authentication docs for information about proper token generation in a real application. Now we can copy generated HMAC SHA-256 JWT and paste it into Centrifugo constructor instead of placeholder in index.html file. I.e.: const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw\"}); If you reload your browser tab – the connection will be successfully established, but the client still can not subscribe to a channel: 2022-06-10 09:45:49 [INF] client command error error=\"permission denied\" client=88116489-350f-447f-9ff3-ab61c9341efe code=103 command=\"id:2 subscribe:{channel:\\\"channel\\\"}\" reply=\"id:2 error:{code:103 message:\\\"permission denied\\\"}\" user=123722 We need to give client a permission to subscribe on the channel channel. There are several ways to do this. For example, client can provide subscription JWT for a channel. But here we will use an option to allow all authenticated clients subscribe to any channel. To do this let's extend a server configuration with allow_subscribe_for_client option: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin\": true, \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": [\"http://localhost:3000\"], \"allow_subscribe_for_client\": true} tip A good practice with Centrifugo is configuring channel namespaces for different types of real-time features you have in the application. By defining namespaces you can achieve a granular control over channel behavior and permissions. Restart Centrifugo – and after doing this everything should start working. Client can successfully connect and successfully subscribe to a channel now. Open developer tools and look at WebSocket frames panel, you should see sth like this: Note, that in this example we generated connection JWT – but it has expiration time, so after some time Centrifugo stops accepting those tokens. In real-life you need to add a token refresh function to a client to rotate tokens. See out client API SDK spec. OK, the last thing we need to do here is to publish a new counter value to a channel and make sure our app works properly. We can do this over Centrifugo API sending an HTTP request to default API endpoint http://localhost:8000/api, but let's do this over the admin web panel first. Open Centrifugo admin web panel in another browser tab (http://localhost:8000/) and go to Actions section. Select publish action, insert channel name that you want to publish to – in our case this is a string channel and insert into data area JSON like this: { \"value\": 1} Click PUBLISH button and check out the application browser tab – counter value must be immediately received and displayed. Open several browser tabs with our app and make sure all tabs receive a message as soon as you publish it. BTW, let's also look at how you can publish data to a channel over Centrifugo server API from a terminal using curl tool: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey d7627bb6-2292-4911-82e1-615c0ed3eebb\" \\ --request POST \\ --data '{\"method\": \"publish\", \"params\": {\"channel\": \"channel\", \"data\": {\"value\": 2}}}' \\ http://localhost:8000/api – where for Authorization header we set api_key value from Centrifugo config file generated above. We did it! We built the simplest browser real-time app with Centrifugo and its Javascript client. It does not have a backend, it's not very useful, to be honest, but it should give you an insight on how to start working with Centrifugo server. Read more about Centrifugo server in the next documentations chapters – it can do much much more than we just showed here. Integration guide describes a process of idiomatic Centrifugo integration with your application backend.","s":"Quickstart tutorial ⏱️","u":"/docs/4/getting-started/quickstart","h":"","p":1494},{"i":1497,"t":"On this page","s":"Migrating to v4","u":"/docs/4/getting-started/migration_v4","h":"","p":1496},{"i":1499,"t":"New generation of client protocol requires using the latest versions of client SDKs. During the next several days we will release the following SDK versions which are compatible with Centrifugo v4: centrifuge-js >= v3.0.0 centrifuge-go >= v0.9.0 centrifuge-dart >= v0.9.0 centrifuge-swift >= v0.5.0 centrifuge-java >= v0.2.0 New client SDKs support only new client protocol – you can not connect to Centrifugo v3 with them. If you have a production system where you want to upgrade Centrifugo from v3 to v4 then the plan is: danger If you are using private channels (starting with $) or user-limited channels (containing #) then carefully read about subscription token migration and user-limited channels migration below. Upgrade Centrifugo and its configuration to adopt changes in v4. In Centrifugo v4 config turn on use_client_protocol_v1_by_default. Run Centrifugo v4 – all current clients should continue working with it. Then on the client-side uprade client SDK version to the one which works with Centrifugo v4, adopt changes in SDK API dictated by our new client SDK API spec. Important thing – add ?cf_protocol_version=v2 URL param to the connection endpoint to tell Centrifugo that modern generation of protocol is being used by the connection (otherwise, it assumes old protocol since we have use_client_protocol_v1_by_default option enabled). As soon as all your clients migrated to use new protocol generation you can remove use_client_protocol_v1_by_default option from the server configuration. After that you can remove ?cf_protocol_version=v2 from connection endpoint on the client-side. tip If you are using mobile client SDKs then most probably some time must pass while clients update their apps to use an updated Centrifugo SDK version. tip Starting from Centrifugo v4.1.1 it's possible to completely turn off client protocol v1 by setting disable_client_protocol_v1 boolean option to true.","s":"Client SDK migration","u":"/docs/4/getting-started/migration_v4","h":"#client-sdk-migration","p":1496},{"i":1501,"t":"Client protocol framing also changed in unidirectional transports. The good news is that Centrifugo v4 still supports previous format for unidirectional transports. When you are enabling use_client_protocol_v1_by_default option described above you also make unidirectional transports to work over old protocol format. So your existing clients will continue working just fine with Centrifugo v4. Then the same steps to migrate described above can be applied to unidirectional transport case. The only difference that in unidirectional approach you are not using Centrifugo SDKs.","s":"Unidirectional transport migration","u":"/docs/4/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":1496},{"i":1503,"t":"SockJS is now DEPRECATED in Centrifugo. Centrifugo v4 may be the last release which supports it. We now offer our own bidirectional emulation layer on top of HTTP-streaming and EventSource. See additional information in Centrifugo v4 introduction post.","s":"SockJS migration","u":"/docs/4/getting-started/migration_v4","h":"#sockjs-migration","p":1496},{"i":1505,"t":"Centrifugo v2 and v3 docs mentioned the fact that channels must contain only ASCII characters. But it was not actually enforced by a server. Now Centrifugo is more strict. If a channel has non-ASCII characters then the 107: bad request error will be returned to the client. Please reach us out if this behavior is not suitable for your use case – we can discuss the use case and think on a proper solution together.","s":"Channel ASCII enforced","u":"/docs/4/getting-started/migration_v4","h":"#channel-ascii-enforced","p":1496},{"i":1507,"t":"Subscription token now requires sub claim (current user ID) to be set. In most cases the only change which is required to smoothly migrate to v4 without breaking things is to add a boolean option \"skip_user_check_in_subscription_token\": true to a Centrifugo v4 configuration. This skips the check of sub claim to contain the current user ID set to a connection during authentication. After that start adding sub claim (with current user ID) to subscription tokens. As soon as all subscription tokens in your system contain user ID in sub claim you can remove the skip_user_check_in_subscription_token from a server configuration. One more important note is that client claim in subscription token in Centrifugo v4 only supported for backwards compatibility. It must not be included into new subscription tokens. It's worth mentioning that Centrifugo v4 does not allow subscribing on channels starting with $ without token even if namespace marked as available for subscribing using sth like allow_subscribe_for_client option. This is done to prevent potential security risk during v3 -> v4 migration when client previously not available to subscribe to channels starting with $ in any case may get permissions to do so.","s":"Subscription token migration","u":"/docs/4/getting-started/migration_v4","h":"#subscription-token-migration","p":1496},{"i":1509,"t":"User-limited channel support should now be allowed over a separate channel namespace option allow_user_limited_channels. See below the namespace option converter which takes this change into account.","s":"User-limited channel migration","u":"/docs/4/getting-started/migration_v4","h":"#user-limited-channel-migration","p":1496},{"i":1511,"t":"In Centrifugo v4 namespace configuration options have been changed. Centrifugo now has secure by default namespaces. First thing to do is to read the new docs about channels and namespaces. Then you can use the following converter which will transform your old namespace configuration to a new one. This converter tries to keep backwards compatibility – i.e. it should be possible to deploy Centrifugo with namespace configuration from converter output and have the same behaviour as before regarding channel permissions. We believe that new option names should provide a more readable configuration and may help to reveal some potential security improvements in your namespace configuration – i.e. making it more strict and protective. caution Do not blindly deploy things to production – test your system first, go through the possible usage scenarios and/or test cases. tip It's fully client-side: your data won't be sent anywhere. Convert Here will be configuration for v4 Here will be log of changes made in your config","s":"Namespace configuration migration","u":"/docs/4/getting-started/migration_v4","h":"#namespace-configuration-migration","p":1496},{"i":1513,"t":"reconnect flag from custom disconnect code is removed. Reconnect advice is now determined by disconnect code value. This allowed us avoiding using JSON in WebSocket CLOSE frame reason. See proxy docs docs for more details.","s":"Proxy disconnect code changes","u":"/docs/4/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":1496},{"i":1515,"t":"Several other non-namespace related options have been renamed or removed: client_anonymous option renamed to allow_anonymous_connect_without_token – new name better describes the purpose of this option which was previously not clear. Converter above takes this into account. use_unlimited_history_by_default option was removed. It was used to help migrating from Centrifugo v2 to v3.","s":"Other configuration option changes","u":"/docs/4/getting-started/migration_v4","h":"#other-configuration-option-changes","p":1496},{"i":1517,"t":"The only breaking change is that user_connections API method (which is available in Centrifugo PRO only) was renamed to connections. The method is more generic now with a broader possibilities – so previous name does not match the current behavior.","s":"Server API changes","u":"/docs/4/getting-started/migration_v4","h":"#server-api-changes","p":1496},{"i":1519,"t":"On this page","s":"CEL expressions","u":"/docs/4/pro/cel_expressions","h":"","p":1518},{"i":1521,"t":"We suppose that the main operation for which developers may use CEL expressions in Centrifugo is a subscribe operation. Let's look at it in detail. It's possible to configure subscribe_cel for a channel namespace (subscribe_cel is just an additional namespace channel option, with same rules applied). This expression should be a valid CEL expression. config.json { \"namespaces\": [ { \"name\": \"admin\", \"subscribe_cel\": \"'admin' in meta.roles\" } ]} In the example we are using custom meta information (must be an object) attached to the connection. As mentioned before in the doc this meta may be attached to the connection: when set in the connect proxy result or provided in JWT as meta claim An expression is evaluated for every subscription attempt to a channel in a namespace. So if meta attached to the connection is sth like this: { \"roles\": [\"admin\"]} – then for every channel in the admin namespace defined above expression will be evaluated to True and subscription will be accepted by Centrifugo. tip meta must be JSON object (any {}) for CEL expressions to work.","s":"subscribe_cel","u":"/docs/4/pro/cel_expressions","h":"#subscribe_cel","p":1518},{"i":1523,"t":"Inside the expression developers can use some variables which are injected by Centrifugo to the CEL runtime. Information about current user ID, meta information attached to the connection, all the variables defined in matched channel pattern will be available for CEL expression evaluation. Say client with user ID 123 subscribes to a channel /users/4 which matched the channel pattern /users/:user: Variable Type Example Description subscribed bool false Whether client is subscribed to channel, always false for subscribe operation user string \"123\" Current authenticated user ID (known from from JWT or connect proxy result) meta map[string]any {\"roles\": [\"admin\"]} Meta information attached to the connection by the apllication backend (in JWT or over connect proxy result) channel string \"/users/4\" Channel client tries to subscribe vars map[string]string {\"user\": \"4\"} Extracted variables from the matched channel pattern. It's empty in case of using channels without variables. In this case, to allow admin to subscribe on any user's channel or allow non-admin user to subscribe only on its own channel, you may construct an expression like this: { ... \"subscribe_cel\": \"vars.user == user or 'admin' in meta.roles\"} Let's look at one more example. Say client with user ID 123 subscribes to a channel /example.com/users/4 which matched the channel pattern /:tenant/users/:user. The permission check may be transformed into sth like this (assuming meta information has information about current connection tenant): { \"namespaces\": [ { \"name\": \"/:tenant/users/:user\", \"subscribe_cel\": \"vars.tenant == meta.tenant && (vars.user == user or 'admin' in meta.roles)\" } ]}","s":"Expression variables","u":"/docs/4/pro/cel_expressions","h":"#expression-variables","p":1518},{"i":1525,"t":"CEL expression to check permissions to publish into a channel. Same expression variables are available.","s":"publish_cel","u":"/docs/4/pro/cel_expressions","h":"#publish_cel","p":1518},{"i":1527,"t":"CEL expression to check permissions for channel history. Same expression variables are available.","s":"history_cel","u":"/docs/4/pro/cel_expressions","h":"#history_cel","p":1518},{"i":1529,"t":"CEL expression to check permissions for channel presence. Same expression variables are available.","s":"presence_cel","u":"/docs/4/pro/cel_expressions","h":"#presence_cel","p":1518},{"i":1531,"t":"On this page","s":"Analytics with ClickHouse","u":"/docs/4/pro/analytics","h":"","p":1530},{"i":1533,"t":"To enable integration with ClickHouse add the following section to a configuration file: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\", \"tcp://127.0.0.1:9001\", \"tcp://127.0.0.1:9002\", \"tcp://127.0.0.1:9003\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"centrifugo_cluster\", \"export_connections\": true, \"export_subscriptions\": true, \"export_operations\": true, \"export_publications\": true, \"export_notifications\": true, \"export_http_headers\": [ \"User-Agent\", \"Origin\", \"X-Real-Ip\" ] }} All ClickHouse analytics options scoped to clickhouse_analytics section of configuration. Toggle this feature using enabled boolean option. tip While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo. Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option. You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database. export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available. export_subscriptions tells Centrifugo to export subscription information snapshots. Information about subscription will be exported once a subscription established and then periodically while connection alive. See below on table structure to see which fields are available. export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available. export_publications tells Centrifugo to export publications for channels to a separate ClickHouse table. export_notifications tells Centrifugo to export push notifications to a separate ClickHouse table. export_http_headers is a list of HTTP headers to export for connection information. export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport. skip_schema_initialization - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process. skip_ping_on_start - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.","s":"Configuration","u":"/docs/4/pro/analytics","h":"#configuration","p":1530},{"i":1535,"t":"SHOW CREATE TABLE centrifugo.connections;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└─────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.connections_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections_distributed( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Connections table","u":"/docs/4/pro/analytics","h":"#connections-table","p":1530},{"i":1537,"t":"SHOW CREATE TABLE centrifugo.subscriptions┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.subscriptions_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions_distributed( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'subscriptions', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Subscriptions table","u":"/docs/4/pro/analytics","h":"#subscriptions-table","p":1530},{"i":1539,"t":"SHOW CREATE TABLE centrifugo.operations;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.operations_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Operations table","u":"/docs/4/pro/analytics","h":"#operations-table","p":1530},{"i":1541,"t":"SHOW CREATE TABLE centrifugo.publications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.publications( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.publications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'publications', murmurHash3_64(channel)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Publications table","u":"/docs/4/pro/analytics","h":"#publications-table","p":1530},{"i":1543,"t":"🚧 This PRO feature is under construction together with push notification API. SHOW CREATE TABLE centrifugo.notifications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.notifications( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.notifications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'notifications', murmurHash3_64(uid)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Notifications table","u":"/docs/4/pro/analytics","h":"#notifications-table","p":1530},{"i":1545,"t":"Show unique users which were connected: SELECT DISTINCT userFROM centrifugo.connections_distributed;┌─user─────┐│ user_1 ││ user_2 ││ user_3 ││ user_4 ││ user_5 │└──────────┘ Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111): SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish');┌─count()─┐│ 4502 │└─────────┘ The same for a specific user: SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');┌─count()─┐│ 1214 │└─────────┘ Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since subscriptions table contain periodic snapshot entries, clients could unsubscribe in between snapshots – this is reflected in operations table): SELECT COUNT(Distinct(user))FROM centrifugo.subscriptions_distributedWHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));┌─uniqExact(user)─┐│ 101 │└─────────────────┘ Show top 10 users which called publish operation during last one minute: SELECT COUNT(op) AS num_ops, userFROM centrifugo.operations_distributedWHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))GROUP BY userORDER BY num_ops DESCLIMIT 10;┌─num_ops─┬─user─────┐│ 56 │ user_200 ││ 11 │ user_75 ││ 6 │ user_87 ││ 6 │ user_65 ││ 6 │ user_39 ││ 5 │ user_28 ││ 5 │ user_63 ││ 5 │ user_89 ││ 3 │ user_32 ││ 3 │ user_52 │└─────────┴──────────┘ Show total number of push notifications to iOS devices sent during last 24 hours: SELECT COUNT(*)FROM centrifugo.notificationsWHERE (time > (now() - toIntervalHour(24))) AND (platform = 'ios')┌─count()─┐│ 31200 │└─────────┘","s":"Query examples","u":"/docs/4/pro/analytics","h":"#query-examples","p":1530},{"i":1547,"t":"The recommended way to run ClickHouse in production is with cluster. See an example of such cluster configuration made with Docker Compose. But during development you may want to run Centrifugo with single instance ClickHouse. To do this set only one ClickHouse dsn and do not set cluster name: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"\", \"export_connections\": true, \"export_subscriptions\": true, \"export_publications\": true, \"export_operations\": true, \"export_http_headers\": [ \"Origin\", \"User-Agent\" ] }} Run ClickHouse locally: docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click clickhouse/clickhouse-server Run ClickHouse client: docker run -it --rm --link click:clickhouse-server --entrypoint clickhouse-client clickhouse/clickhouse-server --host clickhouse-server Issue queries: :) SELECT * FROM centrifugo.operations┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘","s":"Development","u":"/docs/4/pro/analytics","h":"#development","p":1530},{"i":1549,"t":"When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 10 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are configurable. Centrifugo PRO uses very efficient code for writing data to ClickHouse, so analytics feature should only add a little overhead for Centrifugo node. Several metrics are exposed to monitor export process health: centrifugo_clickhouse_analytics_flush_duration_seconds summary centrifugo_clickhouse_analytics_batch_size summary centrifugo_clickhouse_analytics_drop_count counter","s":"How export works","u":"/docs/4/pro/analytics","h":"#how-export-works","p":1530},{"i":1551,"t":"On this page","s":"Install and run PRO version","u":"/docs/4/pro/install_and_run","h":"","p":1550},{"i":1553,"t":"Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).","s":"Binary release","u":"/docs/4/pro/install_and_run","h":"#binary-release","p":1550},{"i":1555,"t":"Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v4.0.0-beta.10 centrifugo -c config.json","s":"Docker image","u":"/docs/4/pro/install_and_run","h":"#docker-image","p":1550},{"i":1557,"t":"You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag: values.yaml ...image: registry: docker.io repository: centrifugo/centrifugo-pro tag: v4.0.0-beta.10","s":"Kubernetes","u":"/docs/4/pro/install_and_run","h":"#kubernetes","p":1550},{"i":1559,"t":"DEB package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v4.0.0-beta.10/centrifugo-pro_4.0.0-beta.10_amd64.debsudo dpkg -i centrifugo-pro_4.0.0-beta.10_amd64.deb","s":"Debian and Ubuntu","u":"/docs/4/pro/install_and_run","h":"#debian-and-ubuntu","p":1550},{"i":1561,"t":"RPM package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v4.0.0-beta.10/centrifugo-pro-4.0.0-beta.10.x86_64.rpmsudo yum install centrifugo-pro-4.0.0-beta.10.x86_64.rpm","s":"Centos","u":"/docs/4/pro/install_and_run","h":"#centos","p":1550},{"i":1563,"t":"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits. Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this: config.json { ... \"license\": \"\"} tip If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.","s":"Setting PRO license key","u":"/docs/4/pro/install_and_run","h":"#setting-pro-license-key","p":1550},{"i":1565,"t":"On this page","s":"Channel patterns","u":"/docs/4/pro/channel_patterns","h":"","p":1564},{"i":1567,"t":"Let's look at the example: { // rest of the config ... \"channel_patterns\": true, // required to turn on the feature. \"namespaces\": [ { \"name\": \"/users/:name\" // namespace options may go here ... }, { \"name\": \"/events/:project/:type\" // namespace options may go here ... } ]} As soon as namespace name starts with / - it's considered a channel pattern. Just like an HTTP path it consists of segments delimited by /. The : symbol in the segment beginning defines a variable part – more information below. In this case a channel to be used must be sth like /users/mario - i.e. start with / and match one of the patterns defined in the configuration. So this channel pattern matching mechanics behaves mostly like HTTP route matching in many frameworks. Given the configuration example above: if channel is /users/mario, then the namespace with the name /users/:name will match and we apply all the options defined for it to the channel. if channel is /events/42/news, then the namespace with the name /events/:project/:type will match. if channel is /events/42, then no namespace will match and the unknown channel error will be returned. Basic example demonstrating use of pattern channels in JS const client := new Centrifuge(\"ws://...\", {});const sub = client.newSubscription('/users/mario');sub.subscribe();client.connect();","s":"Configuration","u":"/docs/4/pro/channel_patterns","h":"#configuration","p":1564},{"i":1569,"t":"Some implementation restrictions and details to know about: When using channel patterns feature : symbol in a namespace name defines a variable part. It's not related to a namespace separator anymore – the entire channel is matched over the channel pattern. Similar to the HTTP routes semantics. So namespace separator is not needed at all when using channel patterns. Centrifugo only allows explicit channel pattern matching which do not result into channel pattern conflicts in runtime, this is checked during configuration validation on server start. Explicitly defined static patterns (without variables) have precedence over patterns with variables. There is no analogue of top-level namespace (like we have for standard namespace configuration) for channels starting with /. If a channel does not match any explicitly defined pattern then Centrifugo returns the 102: unknown channel error. If you define channel_regex inside channel pattern options – then regex matches over the entire channel (since variable parts are located in the namespace name in this case). Channel pattern must only contain ASCII characters. Duplicate variable names are not allowed inside an individual pattern, i.e. defining /users/:user/:user will result into validation error on start.","s":"Implementation details","u":"/docs/4/pro/channel_patterns","h":"#implementation-details","p":1564},{"i":1571,"t":": in the channel pattern name helps to define a variable to match against. Named parameters only match a single segment of the channel: Channel pattern \"/users/:name\":/users/mary ✅ match/users/john ✅ match/users/mary/info ❌ no match /users ❌ no match Another example for channel pattern /news/:type/:subtype, i.e. with multiple variables: Channel pattern \"/news/:type/:subtype\":/news/sport/football ✅ match/news/sport/volleyball ✅ match/news/sport ❌ no match/news ❌ no match Channel patterns support mid-segment variables, so the following is possible: Channel pattern \"/personal/user_:user\":/personal/user_mary ✅ match/personal/user_john ✅ match/personal/user_ ❌ no match","s":"Variables","u":"/docs/4/pro/channel_patterns","h":"#variables","p":1564},{"i":1573,"t":"Additional benefits of using channel patterns may be achieved together with Centrifugo PRO CEL expressions. Channel pattern variables are available inside CEL expressions for evaluation in a custom way.","s":"Using varibles","u":"/docs/4/pro/channel_patterns","h":"#using-varibles","p":1564},{"i":1575,"t":"On this page","s":"Connections API","u":"/docs/4/pro/connections","h":"","p":1574},{"i":1577,"t":"Let's look at the quick example. First, generate a JWT for user 42: $ centrifugo genconfig Generate token for some user to be used in the example connections: $ centrifugo gentoken -u 42HMAC SHA-256 JWT for user 42 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y Run Centrifugo with uni_http_stream transport enabled (it will allow us connecting from the terminal with curl): CENTRIFUGO_UNI_HTTP_STREAM=1 centrifugo -c config.json Create new terminal window and run: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' In another terminal create one more connection: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' Now let's call connections over HTTP API: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"connections\", \"params\": {\"user\": \"42\"}}' \\ http://localhost:8000/api The result: { \"result\": { \"connections\": { \"db8bc772-2654-4283-851a-f29b888ace74\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" }, \"4bc3ca70-ecc5-439d-af14-a78ae18e31c7\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" } } }} Here we can see that user has 2 connections from terminal app. Each connection can be annotated with meta JSON information which is set during connection establishment (over meta claim of JWT or by returning meta in the connect proxy result).","s":"Example","u":"/docs/4/pro/connections","h":"#example","p":1574},{"i":1579,"t":"Returns information about active connections according to the request. connections params​ Parameter name Parameter type Required Description user string no fast filter by User ID expression string no CEL expression to filter users connections result​ Field name Field type Optional Description connections map[string]ConnectionInfo no active user connections map where key is client ID and value is ConnectionInfo ConnectionInfo​ Field name Field type Optional Description app_name string yes client app name (if provided by client) app_version string yes client app version (if provided by client) transport string no client connection transport protocol string no client connection protocol (json or protobuf) user string yes client user ID state ConnectionState yes connection state ConnectionState object​ Field name Field type Optional Description channels map[string]ChannelContext yes Channels client subscribed to connection_token ConnectionTokenInfo yes information about connection token subscription_tokens map yes information about channel tokens used to subscribe meta JSON object yes meta information attached to a connection ChannelContext object​ Field name Field type Optional Description source int yes The source of channel subscription ConnectionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued SubscriptionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued","s":"connections","u":"/docs/4/pro/connections","h":"#connections","p":1574},{"i":1581,"t":"On this page","s":"Centrifugo PRO overview","u":"/docs/4/pro/overview","h":"","p":1580},{"i":1583,"t":"Centrifugo PRO is packed with the following features: Everything from Centrifugo OSS 🔍 Channel and user tracing allows watching client protocol frames in channel or per user ID in real time. 💹 Real-time analytics with ClickHouse for a great system observability, reporting and trending. 🛡️ Operation throttling to protect server from the real-time API misusing and frontend bugs. 🔥 Push notification API to manage device tokens and send mobile and browser push notifications. 🟢 User status API feature allows understanding activity state for a list of users. 🔌 Connections API to query, filter and inspect active connections. ✋ User blocking API to block/unblock abusive users by ID. 🛑 JWT revoking and invalidation API to revoke tokens by ID and invalidate user's tokens based on issue time. 💪 Channel capabilities for controlling channel permissions per connection or per subscription. 📜 Channel patterns allow defining channel configuration like HTTP routes with parameters. ✍️ CEL expressions to write custom efficient permission rules for channel operations. 🚀 Faster performance to reduce resource usage on server side. 🔮 Singleflight for online presence and history to reduce load on the broker. 🍔 Message batching control for advanced tuning of client connection write behaviour. 🪵 CPU and RSS memory usage stats of Centrifugo nodes in admin UI. info PRO features can change with time. We reserve a right to move features from PRO to OSS version if there is a clear signal that this is required to do for the ecosystem.","s":"Features","u":"/docs/4/pro/overview","h":"#features","p":1580},{"i":1585,"t":"You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example: Centrifugo handles up to 20 concurrent connections up to 2 server nodes supported up to 10 API requests per second allowed This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future. Centrifugo PRO license agreement Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.","s":"Try for free in sandbox mode","u":"/docs/4/pro/overview","h":"#try-for-free-in-sandbox-mode","p":1580},{"i":1587,"t":"To run without limits Centrifugo PRO requires a license key. At this point we are not issuing license keys for Centrifugo PRO as we are in the process of defining pricing strategy and distribution model for it. Please contact us over centrifugal.dev@gmail.com – so we can add you to the list of interested customers. Will appreciate if you share which PRO features you are mostly interested in.","s":"Pricing","u":"/docs/4/pro/overview","h":"#pricing","p":1580},{"i":1589,"t":"On this page","s":"Faster performance","u":"/docs/4/pro/performance","h":"","p":1588},{"i":1591,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.","s":"Faster HTTP API","u":"/docs/4/pro/performance","h":"#faster-http-api","p":1588},{"i":1593,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC API","u":"/docs/4/pro/performance","h":"#faster-grpc-api","p":1588},{"i":1595,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster HTTP proxy","u":"/docs/4/pro/performance","h":"#faster-http-proxy","p":1588},{"i":1597,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC proxy","u":"/docs/4/pro/performance","h":"#faster-grpc-proxy","p":1588},{"i":1599,"t":"Centrifugo PRO has an optimized decoding of JWT claims.","s":"Faster JWT decoding","u":"/docs/4/pro/performance","h":"#faster-jwt-decoding","p":1588},{"i":1601,"t":"Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.","s":"Faster GRPC unidirectional stream","u":"/docs/4/pro/performance","h":"#faster-grpc-unidirectional-stream","p":1588},{"i":1603,"t":"Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.","s":"Examples","u":"/docs/4/pro/performance","h":"#examples","p":1588},{"i":1605,"t":"Sorry, your browser doesn't support embedded video. In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.","s":"Publish HTTP API","u":"/docs/4/pro/performance","h":"#publish-http-api","p":1588},{"i":1607,"t":"Sorry, your browser doesn't support embedded video. In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.","s":"History HTTP API","u":"/docs/4/pro/performance","h":"#history-http-api","p":1588},{"i":1609,"t":"On this page","s":"Message batching control","u":"/docs/4/pro/client_message_batching","h":"","p":1608},{"i":1611,"t":"The client_write_delay is a duration option, it is a time Centrifugo will try to collect messages inside each connection message write loop before sending them towards the connection. Enabling client_write_delay may reduce CPU usage of both server and client in case of high message rate inside individual connections. The reduction happens due to the lesser number of system calls to execute. Enabling client_write_delay limits the maximum throughput of messages towards the connection which may be achieved. For example, if client_write_delay is 100ms then the max throughput per second will be (1000 / 100) * client_max_messages_in_frame (16 by default), i.e. 160 messages per second. Though this should be more than enough for target Centrifugo use cases (frontend apps). Example: config.json { // Rest of config here ... \"client_write_delay\": \"100ms\"}","s":"client_write_delay","u":"/docs/4/pro/client_message_batching","h":"#client_write_delay","p":1608},{"i":1613,"t":"The client_reply_without_queue is a boolean option to not use client queue for replies to commands. When true replies are written to the transport without going through the connection message queue.","s":"client_reply_without_queue","u":"/docs/4/pro/client_message_batching","h":"#client_reply_without_queue","p":1608},{"i":1615,"t":"The client_max_messages_in_frame is an integer option which controls the maximum number of messages which may be joined by Centrifugo into one transport frame. By default, 16. Use -1 for unlimited number.","s":"client_max_messages_in_frame","u":"/docs/4/pro/client_message_batching","h":"#client_max_messages_in_frame","p":1608},{"i":1617,"t":"CPU and RSS stats A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI. Here is how this looks like: The information updated in near real-time (with several seconds delay). It's also available as part of info API.","s":"CPU and RSS stats","u":"/docs/4/pro/process_stats","h":"","p":1616},{"i":1619,"t":"Singleflight Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. It will do this by using in-memory component called singleflight. tip While it can seem similar, singleflight is not a cache. It only combines identical parallel requests into one. If requests come one after another – they will be sent separately to the broker or presence storage. This option can radically reduce a load on a broker in the following situations: Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature) Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine. To enable: config.json { ... \"use_singleflight\": true} Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.","s":"Singleflight","u":"/docs/4/pro/singleflight","h":"","p":1618},{"i":1621,"t":"On this page","s":"Push notification API","u":"/docs/4/pro/push_notifications","h":"","p":1620},{"i":1623,"t":"We tried to be practical with our Push Notification API, let's look at its design choices and implementation properties we were able to achieve.","s":"Motivation and design choices","u":"/docs/4/pro/push_notifications","h":"#motivation-and-design-choices","p":1620},{"i":1625,"t":"To start delivering push notifications in the application, developers usually need to integrate with providers such as FCM, HMS, and APNs. This integration typically requires the storage of device tokens in the application database and the implementation of sending push messages to provider push services. Centrifugo PRO simplifies the process by providing a backend for device token storage, following best practices in token management. It reacts to errors and periodically removes stale devices/tokens to maintain a working set of device tokens based on provider recommendations.","s":"Storage for tokens","u":"/docs/4/pro/push_notifications","h":"#storage-for-tokens","p":1620},{"i":1627,"t":"Additionally, Centrifugo PRO provides an efficient, scalable queuing mechanism for sending push notifications. Developers can send notifications from the app backend to Centrifugo API with minimal latency and let Centrifugo process sending to FCM, HMS, APNs concurrently using built-in workers. In our tests, we achieved hundreds of thousands of pushes in tens of seconds.","s":"Efficient queuing","u":"/docs/4/pro/push_notifications","h":"#efficient-queuing","p":1620},{"i":1629,"t":"FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that client can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM, HMS topics by introducing an additional API to manage device subscriptions to topics. tip In some cases you may have real-time channels and device subscription topics with matching names – to send messages to both online and offline users. Though it's up to you. Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs. Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. Then – as soon as user registers a device – it will be automatically subscribed to its own topics. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with FCM – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call.","s":"Unified secure topics","u":"/docs/4/pro/push_notifications","h":"#unified-secure-topics","p":1620},{"i":1631,"t":"Unlike other solutions that combine different provider push sending APIs into a unified API, Centrifugo PRO provides a non-obtrusive proxy for all the mentioned providers. Developers can send notification payloads in a format defined by each provider. It's also possible to send notifications into native FCM, HMS topics or send to raw FCM, HMS, APNs tokens using Centrifugo PRO's push API, allowing them to combine native provider primitives with those added by Centrifugo (i.e., sending to a list of device IDs or to a list of topics).","s":"Non-obtrusive proxying","u":"/docs/4/pro/push_notifications","h":"#non-obtrusive-proxying","p":1620},{"i":1633,"t":"Furthermore, Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API.","s":"Builtin analytics","u":"/docs/4/pro/push_notifications","h":"#builtin-analytics","p":1620},{"i":1635,"t":"Add provider SDK on the frontend side, follow provider instructions for your platform to obtain a push token for a device. For example, for FCM see instructions for iOS, Android, Flutter, Web Browser). The same for HMS or APNs – frontend part should be handled by their native SDKs. Call Centrifugo PRO backend API with the obtained token. From the application backend call Centrifugo device_register API to register the device in Centrifugo PRO storage. Optionally provide list of topics to subscribe device to. Centrifugo returns a registered device object. Pass a generated device ID to the frontend and save it on the frontend together with a token received from FCM. Call Centrifugo send_push_notification API whenever it's time to deliver a push notification. At any moment you can inspect device storage by calling device_list API. Once user logs out from the app, you can detach user ID from device by using device_update or remove device with device_remove API.","s":"Steps to integrate","u":"/docs/4/pro/push_notifications","h":"#steps-to-integrate","p":1620},{"i":1637,"t":"In Centrifugo PRO you can configure one push provider or use all of them – this choice is up to you.","s":"Configuration","u":"/docs/4/pro/push_notifications","h":"#configuration","p":1620},{"i":1639,"t":"As mentioned above Centrifigo uses PostgreSQL for token storage. To enable push notifications make sure database section defined in the configration and fcm is in the push_notifications.enabled_providers list. Centrifugo PRO uses Redis for queuing push notification requests, so Redis address should be configured also. Finally, to integrate with FCM a path to the credentials file must be provided (see how to create one in this instruction). So the full configuration to start sending push notifications over FCM may look like this: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"fcm\"], \"fcm_credentials_file_path\": \"/path/to/service/account/credentials.json\" }} tip Actually, PostgreSQL database configuration is optional here – you can use push notifications API without it. In this case you will be able to send notifications to FCM, HMS, APNs raw tokens, FCM and HMS native topics and conditions. I.e. using Centrifugo as an efficient proxy for push notifications (for example if you already keep tokens in your database). But sending to device ids and topics, and token/topic management APIs won't be available for usage.","s":"FCM","u":"/docs/4/pro/push_notifications","h":"#fcm","p":1620},{"i":1641,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"hms\"], \"hms_app_id\": \"\", \"hms_app_secret\": \"\", }} tip See example how to get app id and app secret here.","s":"HMS","u":"/docs/4/pro/push_notifications","h":"#hms","p":1620},{"i":1643,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"apns\"], \"apns_endpoint\": \"development\", \"apns_bundle_id\": \"com.example.your_app\", \"apns_auth\": \"token\", \"apns_token_auth_key_path\": \"/path/to/auth/key/file.p8\", \"apns_token_key_id\": \"\", \"apns_token_team_id\": \"your_team_id\", }} We also support auth over p12 certificates with the following options: push_notifications.apns_cert_p12_path push_notifications.apns_cert_p12_b64 push_notifications.apns_cert_p12_password","s":"APNs","u":"/docs/4/pro/push_notifications","h":"#apns","p":1620},{"i":1645,"t":"push_notifications.max_inactive_device_days​ This option configures the number of days to keep device without updates. By default Centrifugo does not remove inactive devices.","s":"Other options","u":"/docs/4/pro/push_notifications","h":"#other-options","p":1620},{"i":1647,"t":"Coming soon 🚧 Centrifugo PRO utilizes Redis Streams as the default queue engine for push notifications. However, it also offers the option to employ PostgreSQL for queuing. It's as simple as: config.json { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"queue_engine\": \"database\", // rest of the options... }} tip Queue based on Redis streams is faster, so if you start with PostgreSQL based queue – you have an option to switch to a more performant implementation later. Though active push notifications will be lost during a switch.","s":"Use PostgreSQL as queue","u":"/docs/4/pro/push_notifications","h":"#use-postgresql-as-queue","p":1620},{"i":1650,"t":"Registers or updates device information. device_register request​ Field Type Required Description id string No ID of the device being registered (provide it when updating). provider string Yes Provider of the device token (valid choices: fcm, hms, apns). token string Yes Push notification token for the device. platform string Yes Platform of the device (valid choices: ios, android, web). user string No User associated with the device. topics array of strings No Device topic subscriptions. This should be a full list which replaces all the topics previously accociated with the device. User topics managed by UserTopic model will be automatically attached. tags map No Additional tags for the device (indexed key-value data). meta map No Additional metadata for the device (not indexed). device_register result​ Field Name Type Required Description id string Yes The device ID that was registered/updated.","s":"device_register","u":"/docs/4/pro/push_notifications","h":"#device_register","p":1620},{"i":1652,"t":"Call this method to update device. For example, when user logs out the app and you need to detach user ID from the device. device_update request​ Field Type Required Description ids repeated string No Device ids to filter users repeated string No Device users filter provider_tokens repeated DeviceProviderTokens No Provider tokens filter user_update DeviceUserUpdate No Optional user update object meta_update DeviceMetaUpdate No Optional device meta update object tags_update DeviceTagsUpdate No Optional device tags update object topics_update DeviceChannelsUpdate No Optional topics update object DeviceUserUpdate: Field Type Required Description user string Yes User to set DeviceMetaUpdate: Field Type Required Description meta map Yes Meta to set DeviceTagsUpdate: Field Type Required Description tags map Yes Tags to set DeviceChannelsUpdate: Field Type Required Description topics repeated string Yes Channels to set device_update result​ Empty object.","s":"device_update","u":"/docs/4/pro/push_notifications","h":"#device_update","p":1620},{"i":1654,"t":"Removes device from storage. This may be also called when user logs out the app and you don't need its device token after that. device_remove request​ Field Name Type Required Description ids repeated string No A list of device IDs to be removed users repeated string No A list of device user IDs to filter devices to remove provider_tokens ProviderTokens No Provider tokens to remove device_remove result​ Empty object.","s":"device_remove","u":"/docs/4/pro/push_notifications","h":"#device_remove","p":1620},{"i":1656,"t":"Returns a paginated list of registered devices according to request filter conditions. device_list request​ Field Type Required Description ids repeated string No List of device IDs to filter results. providers repeated string No List of device token providers to filter results. provider_tokens repeated ProviderTokens No Provider tokens to filter results. platforms repeated string No List of device platforms to filter results. users repeated string No List of device users to filter results. since string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_topics bool No Flag indicating whether to include topics information for each device. include_tags bool No Flag indicating whether to include tags information for each device. include_meta bool No Flag indicating whether to include meta information for each device. device_list result​ Field Name Type Required Description items repeated Device Yes A list of devices has_more bool Yes A flag indicating whether there are more devices available Device: Field Name Type Description id string The device's ID. provider string The device's token provider. token string The device's token. platform string The device's platform. user string The user associated with the device. topics array of strings Only included if include_topics was true tags map Only included if include_tags was true meta map Only included if include_meta was true","s":"device_list","u":"/docs/4/pro/push_notifications","h":"#device_list","p":1620},{"i":1658,"t":"Manage mapping of device to topics. device_topic_update request​ Field Type Required Description device_id string Yes Device ID. op string Yes add or remove or set topics repeated string No List of topics. device_topic_update result​ Empty object.","s":"device_topic_update","u":"/docs/4/pro/push_notifications","h":"#device_topic_update","p":1620},{"i":1660,"t":"List device to topic mapping. device_topic_list request​ Field Type Required Description device_ids repeated string No List of device IDs to filter results. device_providers repeated string No List of device token providers to filter results. device_provider_tokens repeated ProviderTokens No Provider tokens to filter results. device_platforms repeated string No List of device platforms to filter results. device_users repeated string No List of device users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Channel prefix to filter results. since string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_device bool No Flag indicating whether to include Device information for each object. device_topic_list result​ Field Name Type Required Description items repeated DeviceChannel Yes A list of DeviceChannel objects has_more bool Yes A flag indicating whether there are more devices available DeviceChannel: Field Type Required Description id string Yes ID of DeviceChannel device_id string Yes Device ID topic string Yes Channel","s":"device_topic_list","u":"/docs/4/pro/push_notifications","h":"#device_topic_list","p":1620},{"i":1662,"t":"Manage mapping of topics with users. These user topics will be automatically attached to user devices upon registering. And removed from device upon deattaching user. user_topic_update request​ Field Type Required Description user string Yes User ID. op string Yes add or remove or set topics repeated string No List of topics. user_topic_update result​ Empty object.","s":"user_topic_update","u":"/docs/4/pro/push_notifications","h":"#user_topic_update","p":1620},{"i":1664,"t":"List user to topic mapping. user_topic_list request​ Field Type Required Description users repeated string No List of users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Channel prefix to filter results. since string No Cursor for pagination (last id in previous batch, empty for first page). limit int32 No Maximum number of UserTopic objects to retrieve. user_topic_list result​ Field Name Type Description items repeated UserTopic A list of UserTopic objects has_more bool A flag indicating whether there are more devices available UserTopic: Field Type Required Description id string Yes ID of UserTopic user string Yes User ID topic string Yes Channel","s":"user_topic_list","u":"/docs/4/pro/push_notifications","h":"#user_topic_list","p":1620},{"i":1666,"t":"Send push notification to specific device_ids, or to topics, or native provider identifiers like fcm_tokens, or to fcm_topic. Request will be queued by Centrifugo, consumed by Centrifugo built-in workers and sent to the provider API. send_push_notification request​ Field name Type Required Description recipient PushRecipient Yes Recipient of push notification notification PushNotification Yes Push notification to send PushRecipient (you must set only one of the following fields): Field Type Required Description device_ids repeated string No Send to a list of device IDs (managed by Centrifugo) topics repeated string No Send to topics (managed by Centrifugo) fcm_tokens repeated string No Send to a list of FCM native tokens fcm_topic string No Send to a FCM native topic fcm_condition string No Send to a FCM native condition hms_tokens repeated string No Send to a list of HMS native tokens hms_topic string No Send to a HMS native topic hms_condition string No Send to a HMS native condition apns_tokens repeated string No Send to a list of APNs native tokens PushNotification: Field Type Required Description uid string No Unique send id, used for Centrifugo builtin analytics expire_at int64 No Unix timestamp when Centrifugo stops attempting to send this notification (this does not relate to notification TTL fields) fcm FcmPushNotification No Notification for FCM hms HmsPushNotification No Notification for HMS apns ApnsPushNotification No Notification for APNs FcmPushNotification: Field Type Required Description message JSON object Yes FCM Message described in FCM docs. HmsPushNotification: Field Type Required Description message JSON object Yes HMS Message described in HMS Push Kit docs. ApnsPushNotification: Field Type Required Description headers map No APNs headers payload JSON object Yes APNs payload send_push_notification result​ Field Name Type Description uid string Unique send id, matches uid in request if it was provided","s":"send_push_notification","u":"/docs/4/pro/push_notifications","h":"#send_push_notification","p":1620},{"i":1668,"t":"This API call is experimental, some changes may happen here. Centrifugo PRO also allows tracking status of push notification delivery and interaction. It's possible to use update_push_status API to save the updated status of push notification to the notifications analytics table. Then it's possible to build insights into push notification effectiveness by querying the table. The update_push_status API supposes that you are using uid field with each notification sent and you are using Centrifugo PRO generated device IDs (as described in steps to integrate). This is a part of server API at the moment, so you need to proxy requests to this endpoint over your backend. We can consider making this API suitable for requests from the client side – please reach out if your use case requires it. update_push_status request​ Field Type Required Description uid string Yes uid (unique send id) from send_push_notification status string Yes Status of push notification - delivered or interacted device_id string Yes Device ID msg_id string No Message ID update_push_status result​ Empty object.","s":"update_push_status","u":"/docs/4/pro/push_notifications","h":"#update_push_status","p":1620},{"i":1670,"t":"Several metrics are available to monitor the state of Centrifugo push worker system: centrifugo_push_notification_count - counter, shows total count of push notifications sent to providers (splitted by provider, recipient type, platform, success, error code). centrifugo_push_queue_consuming_lag - gauge, shows the lag of queues, should be close to zero most of the time. Splitted by provider and name of queue. centrifugo_push_consuming_inflight_jobs - gauge, shows immediate number of workers proceccing pushes. Splitted by provider and name of queue. centrifugo_push_job_duration_seconds - summary, provides insights about worker job duration timings. Splitted by provider and recipient type.","s":"Metrics","u":"/docs/4/pro/push_notifications","h":"#metrics","p":1620},{"i":1672,"t":"Coming soon.","s":"Further reading and tutorials","u":"/docs/4/pro/push_notifications","h":"#further-reading-and-tutorials","p":1620},{"i":1674,"t":"On this page","s":"Operation throttling","u":"/docs/4/pro/throttling","h":"","p":1673},{"i":1676,"t":"In-memory throttling is an efficient way to limit number of operations allowed on a per-connection basis – i.e. inside each individual real-time connection. Our throttling implementation uses token bucket algorithm internally. The list of operations which can be throttled on a per-connection level is: subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) In addition, Centrifugo allows defining two special buckets containers: total – define it to limit the total number of commands per interval (all commands sent from client count), these buckets will always be checked if defined, every command from the client always consumes token from total buckets default - define it if you don't want to configure some command buckets explicitly, default buckets will be used in case command buckets is not configured explicitly. config.json { ... \"client_command_throttling\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"total\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 20 }, { \"interval\": \"60s\", \"rate\": 50 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 }, ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"updateActiveStatus\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} tip Centrifugo real-time SDKs written in a way that if client receives an error during connect – it will try to reconnect to a server with backoff algorithm. The same for subscribing to channels (i.e. error from subscribe command) – subscription request will be retried with a backoff. Refresh and subscription refresh will be also retried automatically by SDK upon errors after in several seconds. Retries of other commands should be handled manually from the client side if needed – though usually you should choose throttling limits in a way that normal users of your app never hit the limits.","s":"In-memory per connection throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-connection-throttling","p":1673},{"i":1678,"t":"Another type of throttling in Centrifugo PRO is a per user ID in-memory throttling. Like per client throttling this one is also very efficient since also uses in-memory token buckets. The difference is that instead of throttling per individual client this type of throttling takes user ID into account. This type of throttling only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be throttled with it. The list of operations which can be throttled is similar to the in-memory throttling described above. But with additional connect method: total default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"user_command_throttling\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"updateActiveStatus\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }}","s":"In-memory per user throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-user-throttling","p":1673},{"i":1680,"t":"The next type of throttling in Centrifugo PRO is a distributed per user ID throttling with Redis as a bucket state storage. In this case limits are global for the entire Centrifugo cluster. If one user executed two commands on different Centrifugo nodes, Centrifugo consumes two tokens from the same bucket kept in Redis. Since this throttling goes to Redis to check limits, it adds some latency to a command processing. Our implementation tries to provide good throughput characteristics though – in our tests single Redis instance can handle more than 100k limit check requests per second. And it's possible to scale Redis in the same ways as for Centrifugo Redis Engine. This type of throttling only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be throttled with it. The implementation also uses token bucket algorithm internally. The list of operations which can be throttled is similar to the in-memory user command throttling described above. But without special bucket total: default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"redis_user_command_throttling\": { \"enabled\": true, \"redis_address\": \"localhost:6379\", \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"updateActiveStatus\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} Redis configuration for throttling feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for throttling feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis configuration declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_user_command_throttling\": { \"enabled\": true, \"use_redis_from_engine\": true, ... }} In this case throttling will simply connect to Redis instances configured for an Engine.","s":"Redis per user throttling","u":"/docs/4/pro/throttling","h":"#redis-per-user-throttling","p":1673},{"i":1682,"t":"Above we showed how you can define throttling strategies to protect server resources and prevent execution of many commands inside the connection and from certain user. But there are scenarios when abusive or broken connections may generate a significant load on the server just by calling commands and getting error responses due to throttling or due to other reasons (like malformed command). Centrifugo PRO provides a way to configure error limits per connection to deal with this case. Error limits are configured as in-memory buckets operating on a per-connection level. When these buckets are full due to lots of errors for an individual connection Centrifugo disconnects the client (with advice to not reconnect, so our SDKs may follow it). This way it's possible to get rid of the connection and rely on HTTP infrastracture tools to deal with client reconnections. Since WebSocket or other our transports (except unidirectional GRPC, but it's usually not available to the public port) are HTTP-based (or start with HTTP request in WebSocket Upgrade case) – developers can use Nginx limit_req_zone directive, Cloudflare rules, iptables, and so on, to protect Centrifugo from unwanted connections. tip Centrifugo PRO does not count internal errors for the error limit buckets – as internal errors is usually not a client's fault. The configuration on error limits per connection may look like this: config.json { ... \"client_error_limits\": { \"enabled\": true, \"total\": { \"buckets\" : [ { \"interval\": \"5s\", \"rate\": 20 } ] } }}","s":"Disconnecting abusive or misbehaving connections","u":"/docs/4/pro/throttling","h":"#disconnecting-abusive-or-misbehaving-connections","p":1673},{"i":1684,"t":"On this page","s":"User and channel tracing","u":"/docs/4/pro/tracing","h":"","p":1683},{"i":1686,"t":"It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing. curl -X POST http://localhost:8000/admin/trace -H \"Authorization: token \" -d '{\"type\": \"user\", \"entity\": \"56\"}' -o trace.txt Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future as PRO version evolves.","s":"Save to a file","u":"/docs/4/pro/tracing","h":"#save-to-a-file","p":1683},{"i":1688,"t":"On this page","s":"Token revocation API","u":"/docs/4/pro/token_revocation","h":"","p":1687},{"i":1690,"t":"By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart. But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/4/pro/token_revocation","h":"#how-it-works","p":1687},{"i":1692,"t":"Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/4/pro/token_revocation","h":"#configure","p":1687},{"i":1694,"t":"Revocation data can be kept in Redis. To enable this configuration should be: { ... \"token_revoke\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/4/pro/token_revocation","h":"#redis-persistence-engine","p":1687},{"i":1696,"t":"Revocation data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"token_revoke\": { \"persistence_engine\": \"database\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/4/pro/token_revocation","h":"#database-persistence-engine","p":1687},{"i":1699,"t":"Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT). caution This API assumes that JWTs you are using contain \"jti\" claim which is a unique token ID (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"revoke_token\", \"params\": {\"uid\": \"xxx-xxx-xxx\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api revoke_token params​ Parameter name Parameter type Required Description uid string yes Token unique ID (JTI claim in case of JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). revoke_token result​ Empty object at the moment.","s":"revoke_token","u":"/docs/4/pro/token_revocation","h":"#revoke_token","p":1687},{"i":1702,"t":"Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application. caution This API assumes that JWTs you are using contain \"iat\" claim which is a time token was issued at (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"invalidate_user_tokens\", \"params\": {\"user\": \"test\", \"issued_before\": 1635845022, \"expire_at\": 1635845122}}' \\ http://localhost:8000/api invalidate_user_tokens params​ Parameter name Parameter type Required Description user string yes User ID to invalidate tokens for issued_before int no All tokens issued at before this Unix time will be considered revoked (in case of JWT this requires iat to be properly set in JWT), if not provided server uses current time expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). invalidate_user_tokens result​ Empty object.","s":"invalidate_user_tokens","u":"/docs/4/pro/token_revocation","h":"#invalidate_user_tokens","p":1687},{"i":1704,"t":"On this page","s":"User blocking API","u":"/docs/4/pro/user_block","h":"","p":1703},{"i":1706,"t":"By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart. But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/4/pro/user_block","h":"#how-it-works","p":1703},{"i":1708,"t":"User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/4/pro/user_block","h":"#configure","p":1703},{"i":1710,"t":"Blocking data can be kept in Redis. To enable this configuration should be: { ... \"user_block\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/4/pro/user_block","h":"#redis-persistence-engine","p":1703},{"i":1712,"t":"Blocking data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"user_block\": { \"persistence_engine\": \"database\" }} tip To quickly start local PostgreSQL database: docker run -it --rm -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:15","s":"Database persistence engine","u":"/docs/4/pro/user_block","h":"#database-persistence-engine","p":1703},{"i":1715,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"block_user\", \"params\": {\"user\": \"2695\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api block_user params​ Parameter name Parameter type Required Description user string yes User ID to block expire_at int no Unix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache). block_user result​ Empty object at the moment.","s":"block_user","u":"/docs/4/pro/user_block","h":"#block_user","p":1703},{"i":1717,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"unblock_user\", \"params\": {\"user\": \"2695\"}}' \\ http://localhost:8000/api unblock_user params​ Parameter name Parameter type Required Description user string yes User ID to unblock unblock_user result​ Empty object at the moment.","s":"unblock_user","u":"/docs/4/pro/user_block","h":"#unblock_user","p":1703},{"i":1719,"t":"On this page","s":"Admin web UI","u":"/docs/4/server/admin_web","h":"","p":1718},{"i":1721,"t":"admin (boolean, default: false) – enables/disables admin web UI admin_password (string, default: \"\") – this is a password to log into admin web interface admin_secret (string, default: \"\") - this is a secret key for authentication token set on successful login. Make both admin_password and admin_secret strong and keep them in secret. After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface. tip Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.","s":"Options","u":"/docs/4/server/admin_web","h":"#options","p":1718},{"i":1723,"t":"If you want to use custom web interface you can specify path to web interface directory dist: config.json { ..., \"admin\": true, \"admin_password\": \"\", \"admin_secret\": \"\", \"admin_web_path\": \"\"} This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.","s":"Using custom web interface","u":"/docs/4/server/admin_web","h":"#using-custom-web-interface","p":1718},{"i":1725,"t":"There is also an option to run Centrifugo in insecure admin mode - in this case you don't need to set admin_password and admin_secret in config – in web interface you will be logged in automatically without any password. Note that this is only an option for production if you protected admin web interface with firewall rules. Otherwise anyone in internet will have full access to admin functionality described above. To enable insecure admin mode: config.json { ..., \"admin\": true, \"admin_insecure\": true, \"admin_password\": \"\", \"admin_secret\": \"\"}","s":"Admin insecure mode","u":"/docs/4/server/admin_web","h":"#admin-insecure-mode","p":1718},{"i":1727,"t":"On this page","s":"User status API","u":"/docs/4/pro/user_status","h":"","p":1726},{"i":1729,"t":"Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript: await centrifuge.rpc('update_user_status', {}); note Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example. This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.","s":"Client-side status update RPC","u":"/docs/4/pro/user_status","h":"#client-side-status-update-rpc","p":1726},{"i":1731,"t":"It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"update_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Update user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to update status for Update user status result​ Empty object at the moment.","s":"update_user_status server API","u":"/docs/4/pro/user_status","h":"#update_user_status-server-api","p":1726},{"i":1733,"t":"Now on a backend side you have access to a bulk API to effectively get status of particular users. Call RPC method of server API (over HTTP or GRPC): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"get_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api You should get a response like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\", \"active\":1627107289, \"online\":1627107289 } ] }} In case information about last status update time not available the response will be like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\" } ] }} I.e. status object will present in a response but active field won't be set for status object. Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time). Get user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to get status for Get user status result​ Field name Field type Optional Description statuses array of UserStatus no Statuses for each user in params (same order) UserStatus​ Field name Field type Optional Description user string no User ID active integer yes Last active time (Unix seconds) online integer yes Last online time (Unix seconds)","s":"get_user_status server API","u":"/docs/4/pro/user_status","h":"#get_user_status-server-api","p":1726},{"i":1735,"t":"If you need to clear user status information for some reason there is a delete_user_status server API call: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"delete_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Delete user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to delete status for Delete user status result​ Empty object at the moment.","s":"delete_user_status server API","u":"/docs/4/pro/user_status","h":"#delete_user_status-server-api","p":1726},{"i":1737,"t":"To enable Redis user status feature: config.json { ... \"user_status\": { \"enabled\": true, \"redis_address\": \"127.0.0.1:6379\" }} Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"user_status\": { \"enabled\": true, \"use_redis_from_engine\": true, }} In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine. expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day: config.json { ... \"user_status\": { ... \"expire_interval\": \"24h\" }}","s":"Configuration","u":"/docs/4/pro/user_status","h":"#configuration","p":1726},{"i":1739,"t":"On this page","s":"Channel permission model","u":"/docs/4/server/channel_permissions","h":"","p":1738},{"i":1741,"t":"By default, client's attempt to subscribe on a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel subscribe permissions: Provide subscription token Configure subscribe proxy Use user-limited channels Use subscribe_allowed_for_client namespace option Subscribe capabilities in connection token Subscribe capabilities in connect proxy Below, we are describing those in detail. Provide subscription token​ A client can provide a subscription token in subscribe request. See the format of the token. If client provides a valid token then subscription will be accepted. In Centrifugo PRO subscription token can additionally grant publish, history and presence permissions to a client. caution For namespaces with allow_subscribe_for_client option ON Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without token. This limitation exists to help users migrate to Centrifugo v4 without security risks. Configure subscribe proxy​ If client subscribes on a namespace with configured subscribe proxy then depending on proxy response subscription will be accepted or not. If a namespace has configured subscribe proxy, but user came with a token – then subscribe proxy is not used, we are relying on token in this case. If a namespace has subscribe proxy, but user subscribes on a user-limited channel – then subscribe proxy is not used also. Use user-limited channels​ If client subscribes on a user-limited channel and there is a user ID match then subscription will be accepted. caution User-limited channels must be enabled in a namespace using allow_user_limited_channels option. Use allow_subscribe_for_client namespace option​ allow_subscribe_for_client allows all authenticated non-anonymous connections to subscribe on all channels in a namespace. caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security. To additionally allow subscribing to anonymous connections take a look at allow_subscribe_for_anonymous option. Subscribe capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user subscribe to channels. Subscribe capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow user subscribe to channels.","s":"Subscribe permission model","u":"/docs/4/server/channel_permissions","h":"#subscribe-permission-model","p":1738},{"i":1743,"t":"tip In idiomatic Centrifugo use case data should be published to channels from the application backend (over server API). In this case backend can validate data, save it into persistent storage before publishing in real-time towards connections. When publishing from the client-side backend does not receive publication data at all – it just goes through Centrifugo (except using publish proxy). There are cases when direct publications from the client-side are desired (like typing indicators in chat applications) though. By default, client's attempt to publish data into a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel publish permissions: Configure publish proxy Use allow_publish_for_subscriber namespace option Use allow_publish_for_client namespace option Publish capabilities in connection token Publish capability in subscription token Publish capabilities in connect proxy Publish capability in subscribe proxy Use allow_publish_for_client namespace option​ allow_publish_for_client allows publications to channels of a namespace for all client connections. Use allow_publish_for_subscriber namespace option​ allow_publish_for_subscriber allows publications to channels of a namespace for all connections subscribed on a channel they want to publish data into. Configure publish proxy​ If client publishes to a namespace with configured publish proxy then depending on proxy response publication will be accepted or not. Configured publish proxy always used??? (what if user has permission in token or allow_publish_for_client?) Publish capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to channels. Publish capability in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to a channel. Publish capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client publish to certain channels. Publish capability in subscribe proxy​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber publish to channel.","s":"Publish permission model","u":"/docs/4/server/channel_permissions","h":"#publish-permission-model","p":1738},{"i":1745,"t":"By default, client's attempt to call history from a channel (with history retention configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel history permissions. Use allow_history_for_subscriber namespace option​ allow_history_for_subscriber allows history requests to all channels in a namespace for all client connections subscribed on a channel they want to call history for. Use allow_history_for_client namespace option​ allow_history_for_client allows history requests to all channels in a namespace for all client connections. History capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history for channels. History capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history from a channel. History capabilities in connect proxy​ This is a Centrifugo PRO feature. Connect proxy can return capability object to allow client call history from certain channels. History capability in subscribe proxy response​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber call history from channel.","s":"History permission model","u":"/docs/4/server/channel_permissions","h":"#history-permission-model","p":1738},{"i":1747,"t":"By default, client's attempt to call presence from a channel (with channel presence configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel presence permissions. Presence capability in subscribe proxy response​ Subscribe proxy can return capability object to allow subscriber call presence from channel. Use allow_presence_for_subscriber namespace option​ allow_presence_for_subscriber allows presence requests to all channels in a namespace for all client connections subscribed on a channel they want to call presence for. Use allow_presence_for_client namespace option​ allow_presence_for_client allows presence requests to all channels in a namespace for all client connections. Presence capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence for channels. Presence capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence of a channel. Presence capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client call presence from certain channels.","s":"Presence permission model","u":"/docs/4/server/channel_permissions","h":"#presence-permission-model","p":1738},{"i":1749,"t":"Server can whether turn on positioning for all channels in a namespace using \"force_positioning\": true option or client can create positioned subscriptions (but in this case client must have access to history capability).","s":"Positioning permission model","u":"/docs/4/server/channel_permissions","h":"#positioning-permission-model","p":1738},{"i":1751,"t":"Server can whether turn on automatic recovery for all channels in a namespace using \"force_recovery\": true option or client can create recoverable subscriptions (but in this case client must have access to history capability).","s":"Recovery permission model","u":"/docs/4/server/channel_permissions","h":"#recovery-permission-model","p":1738},{"i":1753,"t":"Server can whether force sending join/leave messages to all subscribers for all channels in a namespace using \"force_push_join_leave\": true option or client can ask server to include join/leave messages upon subscribing (but in this case client must have access to presence capability).","s":"Join/Leave permission model","u":"/docs/4/server/channel_permissions","h":"#joinleave-permission-model","p":1738},{"i":1755,"t":"On this page","s":"Client JWT authentication","u":"/docs/4/server/authentication","h":"","p":1754},{"i":1757,"t":"For connection JWT Centrifugo uses the some standart claims defined in rfc7519, also some custom Centrifugo-specific.","s":"Connection JWT claims","u":"/docs/4/server/authentication","h":"#connection-jwt-claims","p":1754},{"i":1759,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). If a user is not currently authenticated in an application, but you want to let him connect to Centrifugo anyway – you can use an empty string as a user ID in sub claim. This is called anonymous access. In this case, you may need to enable corresponding channel namespace options which enable access to protocol features for anonymous users.","s":"sub","u":"/docs/4/server/authentication","h":"#sub","p":1754},{"i":1761,"t":"This is a UNIX timestamp seconds when the token will expire. This is a standard JWT claim - all JWT libraries for different languages provide an API to set it. If exp claim is not provided then Centrifugo won't expire connection. When provided special algorithm will find connections with exp in the past and activate the connection refresh mechanism. Refresh mechanism allows connection to survive and be prolonged. In case of refresh failure, the client connection will be eventually closed by Centrifugo and won't be accepted until new valid and actual credentials are provided in the connection token. You can use the connection expiration mechanism in cases when you don't want users of your app to be subscribed on channels after being banned/deactivated in the application. Or to protect your users from token leakage (providing a reasonably short time of expiration). Choose exp value wisely, you don't need small values because the refresh mechanism will hit your application often with refresh requests. But setting this value too large can lead to slow user connection deactivation. This is a trade-off. Read more about connection expiration below.","s":"exp","u":"/docs/4/server/authentication","h":"#exp","p":1754},{"i":1763,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/4/server/authentication","h":"#iat","p":1754},{"i":1765,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/4/server/authentication","h":"#jti","p":1754},{"i":1767,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But you can force this check by setting token_audience string option: config.json { \"token_audience\": \"centrifugo\"} caution Setting token_audience will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.","s":"aud","u":"/docs/4/server/authentication","h":"#aud","p":1754},{"i":1769,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But you can force this check by setting token_issuer string option: config.json { \"token_issuer\": \"my_app\"} caution Setting token_issuer will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.","s":"iss","u":"/docs/4/server/authentication","h":"#iss","p":1754},{"i":1771,"t":"This claim is optional - this is additional information about client connection that can be provided for Centrifugo. This information will be included in presence information, join/leave events, and channel publication if it was published from a client-side.","s":"info","u":"/docs/4/server/authentication","h":"#info","p":1754},{"i":1773,"t":"If you are using binary Protobuf protocol you may want info to be custom bytes. Use this field in this case. This field contains a base64 representation of your bytes. After receiving Centrifugo will decode base64 back to bytes and will embed the result into various places described above.","s":"b64info","u":"/docs/4/server/authentication","h":"#b64info","p":1754},{"i":1775,"t":"An optional array of strings with server-side channels to subscribe a client to. See more details about server-side subscriptions.","s":"channels","u":"/docs/4/server/authentication","h":"#channels","p":1754},{"i":1777,"t":"An optional map of channels with options. This is like a channels claim but allows more control over server-side subscription since every channel can be annotated with info, data, and so on using options. tip This claim is called subs as a shortcut from subscriptions. The claim sub described above is a standart JWT claim to provide a user ID (it's a shortcut from subject). While claims have similar names they have different purpose in a connection JWT. Example: { ... \"subs\": { \"channel1\": { \"data\": {\"welcome\": \"welcome to channel1\"} }, \"channel2\": { \"data\": {\"welcome\": \"welcome to channel2\"} } }} Subscribe options:​ Field Type Optional Description info JSON object yes Custom channel info b64info string yes Custom channel info in Base64 - to pass binary channel info data JSON object yes Custom JSON data to return in subscription context inside Connect reply b64data string yes Same as data but in Base64 to send binary data override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false}","s":"subs","u":"/docs/4/server/authentication","h":"#subs","p":1754},{"i":1779,"t":"Meta is an additional JSON object (ex. {\"key\": \"value\"}) that will be attached to a connection. Unlike info it's never exposed to clients inside presence and join/leave payloads and only accessible on a backend side. It may be included in proxy calls from Centrifugo to the application backend (see proxy_include_connection_meta option). Also, there is a connections API method in Centrifugo PRO that returns this data in the connection description object.","s":"meta","u":"/docs/4/server/authentication","h":"#meta","p":1754},{"i":1781,"t":"By default, Centrifugo looks on exp claim to configure connection expiration. In most cases this is fine, but there could be situations where you wish to decouple token expiration check with connection expiration time. As soon as the expire_at claim is provided (set) in JWT Centrifugo relies on it for setting connection expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the connection should expire. Set it to the future time for expiring connection at some point Set it to 0 to disable connection expiration (but still check token exp claim).","s":"expire_at","u":"/docs/4/server/authentication","h":"#expire_at","p":1754},{"i":1783,"t":"As said above exp claim in a connection token allows expiring client connection at some point in time. Let's look in detail at what happens when Centrifugo detects that the connection is going to expire. First, you should do is enable client expiration mechanism in Centrifugo providing a connection JWT with expiration: import jwtimport timetoken = jwt.encode({\"sub\": \"42\", \"exp\": int(time.time()) + 10*60}, \"secret\").decode()print(token) Let's suppose that you set exp field to timestamp that will expire in 10 minutes and the client connected to Centrifugo with this token. During 10 minutes the connection will be kept by Centrifugo. When this time passed Centrifugo gives the connection some time (configured, 25 seconds by default) to refresh its credentials and provide a new valid token with new exp. When a client first connects to Centrifugo it receives the ttl value in connect reply. That ttl value contains the number of seconds after which the client must send the refresh command with new credentials to Centrifugo. Centrifugo clients must handle this ttl field and automatically start the refresh process. For example, a Javascript browser client will send an AJAX POST request to your application when it's time to refresh credentials. By default, this request goes to /centrifuge/refresh URL endpoint. In response your server must return JSON with a new connection JWT: { \"token\": token} So you must just return the same connection JWT for your user when rendering the page initially. But with actual valid exp. Javascript client will then send them to Centrifugo server and connection will be refreshed for a time you set in exp. In this case, you know which user wants to refresh its connection because this is just a general request to your app - so your session mechanism will tell you about the user. If you don't want to refresh the connection for this user - just return 403 Forbidden on refresh request to your application backend. Javascript client also has options to hook into a refresh mechanism to implement your custom way of refreshing. Other Centrifugo clients also should have hooks to refresh credentials but depending on client API for this can be different - see specific client docs.","s":"Connection expiration","u":"/docs/4/server/authentication","h":"#connection-expiration","p":1754},{"i":1785,"t":"Let's look at how to generate connection HS256 JWT in Python:","s":"Examples","u":"/docs/4/server/authentication","h":"#examples","p":1754},{"i":1787,"t":"Python NodeJS import jwttoken = jwt.encode({\"sub\": \"42\"}, \"secret\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret');console.log(token); Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users. Then you can pass this token to your client side and use it when connecting to Centrifugo: Using centrifuge-js v3 var centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: token});centrifuge.connect(); See more details about working with connection tokens and handling token expiration on the client-side in the real-time SDK API spec.","s":"Simplest token","u":"/docs/4/server/authentication","h":"#simplest-token","p":1754},{"i":1789,"t":"HS256 token that will be valid for 5 minutes: Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"exp\": int(time.time()) + 5*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret', { expiresIn: 5 * 60 });console.log(token);","s":"Token with expiration","u":"/docs/4/server/authentication","h":"#token-with-expiration","p":1754},{"i":1791,"t":"Let's attach user name: Python NodeJS import jwtclaims = {\"sub\": \"42\", \"info\": {\"name\": \"Alexander Emelin\"}}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42', info: {\"name\": \"Alexander Emelin\"} }, 'secret');console.log(token);","s":"Token with additional connection info","u":"/docs/4/server/authentication","h":"#token-with-additional-connection-info","p":1754},{"i":1793,"t":"You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.","s":"Investigating problems with JWT","u":"/docs/4/server/authentication","h":"#investigating-problems-with-jwt","p":1754},{"i":1795,"t":"Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT). A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address). As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels. tip Read a tutorial in our blog about using Centrifugo with Keycloak SSO. In that case connection tokens are verified using public key loaded from the JWKS endpoint of Keycloak. At the moment Centrifugo caches keys loaded from an endpoint for one hour. Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment). Only RSA algorithm is supported. Once enabled JWKS used for both connection and channel subscription tokens.","s":"JSON Web Key support","u":"/docs/4/server/authentication","h":"#json-web-key-support","p":1754},{"i":1797,"t":"Available since Centrifugo v4.1.3 It's possible to extract variables from iss and aud JWT claims using Go regexp named groups, then use variables extracted during iss or aud matching to construct a JWKS endpoint dynamically upon token validation. In this case JWKS endpoint may be set in config as template. To achieve this Centrifugo provides two additional options: token_issuer_regex - match JWT issuer (iss claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. token_audience_regex - match JWT audience (aud claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. Let's look at the example: { \"token_issuer_regex\": \"https://example.com/auth/realms/(?P[A-z]+)\", \"token_jwks_public_endpoint\": \"https://keycloak:443/{{realm}}/protocol/openid-connect/certs\",} To use variable in token_jwks_public_endpoint it must be wrapped in {{ }}. When using token_issuer_regex and token_audience_regex make sure token_issuer and token_audience not used in the config - otherwise and error will be returned on Centrifugo start. caution Setting token_issuer_regex and token_audience_regex will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.","s":"Dynamic JWKs endpoint","u":"/docs/4/server/authentication","h":"#dynamic-jwks-endpoint","p":1754},{"i":1799,"t":"On this page","s":"Channel JWT authorization","u":"/docs/4/server/channel_token_auth","h":"","p":1798},{"i":1801,"t":"For subscription JWT Centrifugo uses some standard claims defined in rfc7519, also some custom Centrifugo-specific.","s":"Subscription JWT claims","u":"/docs/4/server/channel_token_auth","h":"#subscription-jwt-claims","p":1798},{"i":1803,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). The value must match a user in connection JWT – since it's the same real-time connection. The missing claim will mean that token issued for anonymous user (i.e. with empty user ID).","s":"sub","u":"/docs/4/server/channel_token_auth","h":"#sub","p":1798},{"i":1805,"t":"Required. Channel that client tries to subscribe to with this token (string).","s":"channel","u":"/docs/4/server/channel_token_auth","h":"#channel","p":1798},{"i":1807,"t":"Optional. Additional information for connection inside this channel (valid JSON).","s":"info","u":"/docs/4/server/channel_token_auth","h":"#info","p":1798},{"i":1809,"t":"Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.","s":"b64info","u":"/docs/4/server/channel_token_auth","h":"#b64info","p":1798},{"i":1811,"t":"Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time. At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs. Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.","s":"exp","u":"/docs/4/server/channel_token_auth","h":"#exp","p":1798},{"i":1813,"t":"Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the subscription should expire. Set it to the future time for expiring subscription at some point Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.","s":"expire_at","u":"/docs/4/server/channel_token_auth","h":"#expire_at","p":1798},{"i":1815,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.","s":"aud","u":"/docs/4/server/channel_token_auth","h":"#aud","p":1798},{"i":1817,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.","s":"iss","u":"/docs/4/server/channel_token_auth","h":"#iss","p":1798},{"i":1819,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/4/server/channel_token_auth","h":"#iat","p":1798},{"i":1821,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/4/server/channel_token_auth","h":"#jti","p":1798},{"i":1823,"t":"One more claim is override. This is an object which allows overriding channel options for the particular channel subscriber which comes with subscription token. Field Type Optional Description presence BoolValue yes override presence channel option join_leave BoolValue yes override join_leave channel option force_push_join_leave BoolValue yes override force_push_join_leave channel option force_recovery BoolValue yes override force_recovery channel option force_positioning BoolValue yes override force_positioning channel option BoolValue is an object like this: { \"value\": true/false} So for example, you want to turn off emitting a presence information for a particular subscriber in a channel: { ... \"override\": { \"presence\": { \"value\": false } }}","s":"override","u":"/docs/4/server/channel_token_auth","h":"#override","p":1798},{"i":1825,"t":"So to generate a subscription token you can use something like this in Python (assuming user ID is 42 and the channel is gossips): import jwttoken = jwt.encode({ \"sub\": \"42\", \"channel\": \"$gossips\"}, \"secret\", algorithm=\"HS256\").decode()print(token) Where \"secret\" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).","s":"Example","u":"/docs/4/server/channel_token_auth","h":"#example","p":1798},{"i":1827,"t":"During development you can quickly generate valid subscription token using Centrifugo gensubtoken cli command. ./centrifugo gensubtoken -u 123722 -s channel You should see an output like this: HMAC SHA-256 JWT for user \"123722\" and channel \"channel\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDg0MzgsImNoYW5uZWwiOiJjaGFubmVsIn0.JyRI3ovNV-abV8VxCmZCD556o2F2mNL1UoU58gNR-uI But in real app subscription JWT must be generated by your application backend.","s":"With gensubtoken cli command","u":"/docs/4/server/channel_token_auth","h":"#with-gensubtoken-cli-command","p":1798},{"i":1829,"t":"On this page","s":"Error and disconnect codes","u":"/docs/4/server/codes","h":"","p":1828},{"i":1831,"t":"Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client. Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).","s":"Client error codes","u":"/docs/4/server/codes","h":"#client-error-codes","p":1828},{"i":1833,"t":"Code: 100Message: \"internal server error\"Temporary: true Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.","s":"Internal","u":"/docs/4/server/codes","h":"#internal","p":1828},{"i":1835,"t":"Code: 101Message: \"unauthorized\" Error Unauthorized says that request is unauthorized.","s":"Unauthorized","u":"/docs/4/server/codes","h":"#unauthorized","p":1828},{"i":1837,"t":"Code: 102Message: \"unknown channel\" Error Unknown Channel means that channel name does not exist. Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.","s":"Unknown Channel","u":"/docs/4/server/codes","h":"#unknown-channel","p":1828},{"i":1839,"t":"Code: 103Message: \"permission denied\" Error Permission Denied means that access to resource not allowed.","s":"Permission Denied","u":"/docs/4/server/codes","h":"#permission-denied","p":1828},{"i":1841,"t":"Code: 104Message: \"method not found\" Error Method Not Found means that method sent in command does not exist.","s":"Method Not Found","u":"/docs/4/server/codes","h":"#method-not-found","p":1828},{"i":1843,"t":"Code: 105Message: \"already subscribed\" Error Already Subscribed returned when client wants to subscribe on channel it already subscribed to.","s":"Already Subscribed","u":"/docs/4/server/codes","h":"#already-subscribed","p":1828},{"i":1845,"t":"Code: 106Message: \"limit exceeded\" Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.","s":"Limit Exceeded","u":"/docs/4/server/codes","h":"#limit-exceeded","p":1828},{"i":1847,"t":"Code: 107Message: \"bad request\" Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.","s":"Bad Request","u":"/docs/4/server/codes","h":"#bad-request","p":1828},{"i":1849,"t":"Code: 108Message: \"not available\" Error Not Available means that resource is not enabled. For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.","s":"Not Available","u":"/docs/4/server/codes","h":"#not-available","p":1828},{"i":1851,"t":"Code: 109Message: \"token expired\" Error Token Expired indicates that connection token expired. Our SDKs handle it in a special way by updating token.","s":"Token Expired","u":"/docs/4/server/codes","h":"#token-expired","p":1828},{"i":1853,"t":"Code: 110Message: \"expired\" Error Expired indicates that connection expired (no token involved).","s":"Expired","u":"/docs/4/server/codes","h":"#expired","p":1828},{"i":1855,"t":"Code: 111Message: \"too many requests\"Temporary: true Error Too Many Requests means that server rejected request due to rate limiting strategies.","s":"Too Many Requests","u":"/docs/4/server/codes","h":"#too-many-requests","p":1828},{"i":1857,"t":"Code: 112Message: \"unrecoverable position\" Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch passed.","s":"Unrecoverable Position","u":"/docs/4/server/codes","h":"#unrecoverable-position","p":1828},{"i":1859,"t":"Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes). note We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. We expect these codes are mostly useful for logs and metrics.","s":"Client disconnect codes","u":"/docs/4/server/codes","h":"#client-disconnect-codes","p":1828},{"i":1861,"t":"Code: 3000Reason: \"connection closed\" DisconnectConnectionClosed is a special Disconnect object used when client connection was closed without any advice from a server side. This can be a clean disconnect, or temporary disconnect of the client due to internet connection loss. Server can not distinguish the actual reason of disconnect.","s":"DisconnectConnectionClosed","u":"/docs/4/server/codes","h":"#disconnectconnectionclosed","p":1828},{"i":1863,"t":"Client will reconnect after receiving such codes. Shutdown​ Code: 3001Reason: \"shutdown\" Disconnect Shutdown may be sent when node is going to shut down. DisconnectServerError​ Code: 3004Reason: \"internal server error\" DisconnectServerError issued when internal error occurred on server. DisconnectExpired​ Code: 3005Reason: \"connection expired\" DisconnectSubExpired​ Code: 3006Reason: \"subscription expired\" DisconnectSubExpired issued when client subscription expired. DisconnectSlow​ Code: 3008Reason: \"slow\" DisconnectSlow issued when client can't read messages fast enough. DisconnectWriteError​ Code: 3009Reason: \"write error\" DisconnectWriteError issued when an error occurred while writing to client connection. DisconnectInsufficientState​ Code: 3010Reason: \"insufficient state\" DisconnectInsufficientState issued when server detects wrong client position in channel Publication stream. Disconnect allows clien to restore missed publications on reconnect. DisconnectForceReconnect​ Code: 3011Reason: \"force reconnect\" DisconnectForceReconnect issued when server disconnects connection for some reason and whants it to reconnect. DisconnectNoPong​ Code: 3012Reason: \"no pong\" DisconnectNoPong may be issued when server disconnects bidirectional connection due to no pong received to application-level server-to-client pings in a configured time. DisconnectTooManyRequests​ Code: 3013Reason: \"too many requests\" DisconnectTooManyRequests may be issued when client sends too many commands to a server.","s":"Non-terminal disconnect codes","u":"/docs/4/server/codes","h":"#non-terminal-disconnect-codes","p":1828},{"i":1865,"t":"Client won't reconnect upon receiving such code. DisconnectInvalidToken​ Code: 3500Reason: \"invalid token\" DisconnectInvalidToken issued when client came with invalid token. DisconnectBadRequest​ Code: 3501Reason: \"bad request\" DisconnectBadRequest issued when client uses malformed protocol frames. DisconnectStale​ Code: 3502Reason: \"stale\" DisconnectStale issued to close connection that did not become authenticated in configured interval after dialing. DisconnectForceNoReconnect​ Code: 3503Reason: \"force disconnect\" DisconnectForceNoReconnect issued when server disconnects connection and asks it to not reconnect again. DisconnectConnectionLimit​ Code: 3504Reason: \"connection limit\" DisconnectConnectionLimit can be issued when client connection exceeds a configured connection limit (per user ID or due to other rule). DisconnectChannelLimit​ Code: 3505Reason: \"channel limit\" DisconnectChannelLimit can be issued when client connection exceeds a configured channel limit. DisconnectInappropriateProtocol​ Code: 3506Reason: \"inappropriate protocol\" DisconnectInappropriateProtocol can be issued when client connection format can not handle incoming data. For example, this happens when JSON-based clients receive binary data in a channel. This is usually an indicator of programmer error, JSON clients can not handle binary. DisconnectPermissionDenied​ Code: 3507Reason: \"permission denied\" DisconnectPermissionDenied may be issued when client attempts accessing a server without enough permissions. DisconnectNotAvailable​ Code: 3508Reason: \"not available\" DisconnectNotAvailable may be issued when ErrorNotAvailable does not fit message type, for example we issue DisconnectNotAvailable when client sends asynchronous message without MessageHandler set on server side. DisconnectTooManyErrors​ Code: 3509Reason: \"too many errors\" DisconnectTooManyErrors may be issued when client generates too many errors.","s":"Terminal disconnect codes","u":"/docs/4/server/codes","h":"#terminal-disconnect-codes","p":1828},{"i":1867,"t":"On this page","s":"Channels and namespaces","u":"/docs/4/server/channels","h":"","p":1866},{"i":1869,"t":"Centrifugo is a PUB/SUB system - it has publishers and subscribers. Channel is a route for publications. Clients can subscribe to a channel to receive all real-time messages published to a channel. A channel subscriber can also ask for a channel online presence or channel history information. Channel is just a string - news, comments, personal_feed are valid channel names. Though this string has some predefined rules as we will see below. You can define different channel behavior using a set of available channel options. Channels are ephemeral – you don't need to create them explicitly. Channels created automatically by Centrifugo as soon as the first client subscribes to a channel. As soon as the last subscriber leaves a channel - it's automatically cleaned up. Channel can belong to a channel namespace. Channel namespacing is a mechanism to define different behavior for different channels in Centrifugo. Using namespaces is a recommended way to manage channels – to turn on only those channel options which are required for a specific real-time feature you are implementing on top of Centrifugo. caution When using channel namespaces make sure you defined a namespace in configuration. Subscription attempts to a channel within a non-defined namespace will result into 102: unknown channel errors.","s":"What is channel","u":"/docs/4/server/channels","h":"#what-is-channel","p":1866},{"i":1871,"t":"Only ASCII symbols must be used in a channel string. Channel name length limited by 255 characters by default (controlled by configuration option channel_max_length). Several symbols in channel names reserved for Centrifugo internal needs: : – for namespace channel boundary (see below) # – for user channel boundary (see below) $ – for private channel prefix (see below) * – for the future Centrifugo needs & – for the future Centrifugo needs / – for the future Centrifugo needs","s":"Channel name rules","u":"/docs/4/server/channels","h":"#channel-name-rules","p":1866},{"i":1873,"t":": – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about about namespaces and channel options below. If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public. info A namespace is part of the channel name. If a user subscribed to a channel with namespace, like public:chat – then you need to publish messages into public:chat channel to be delivered to the user. We often see some confusion from developers trying to publish messages into chat and thinking that namespace is somehow stripped upon subscription. It's not true.","s":"namespace boundary (:)","u":"/docs/4/server/channels","h":"#namespace-boundary-","p":1866},{"i":1875,"t":"# – is a user channel boundary. This is a separator to create personal channels for users (we call this user-limited channels) without the need to provide a subscription token. For example, if the channel is news#42 then the only user with ID 42 can subscribe to this channel (Centrifugo knows user ID because clients provide it in connection credentials with connection JWT). If you want to create a user-limited channel in namespace personal then you can use a name like personal:user#42 for example. Moreover, you can provide several user IDs in channel name separated by a comma: dialog#42,43 – in this case only the user with ID 42 and user with ID 43 will be able to subscribe on this channel. This is useful for channels with a static list of allowed users, for example for single user personal messages channel, for dialog channel between certainly defined users. As soon as you need to manage access to a channel dynamically for many users this channel type does not suit well. tip User-limited channels must be enabled for a channel namespace using allow_user_limited_channels option. See below more information about channel options and channel namespaces.","s":"user channel boundary (#)","u":"/docs/4/server/channels","h":"#user-channel-boundary-","p":1866},{"i":1877,"t":"Centrifugo v4 has this option to achieve compatibility with previous Centrifugo versions. Previously (in Centrifugo v1, v2 and v3) only channels starting with $ could be subscribed with a subscription JWT. In Centrifugo v4 that's not the case anymore – clients can subscribe to any channel with a subscription token (if the token is valid – then subscription to a channel is accepted). But for namespaces with allow_subscribe_for_client option enabled Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without a subscription token. This limitation exists to help users migrate to Centrifugo v4 without security risks.","s":"private channel prefix ($)","u":"/docs/4/server/channels","h":"#private-channel-prefix-","p":1866},{"i":1879,"t":"Keep in mind that a channel is uniquely identified by its string representation. Do not expect that channels $news and news are the same. They are different because strings are not equal. So if a user subscribed to $news then user won't receive messages published to news. Channels dialog#42,43 and dialog#43,42 are two different channels too. Centrifugo only applies permission checks when a user subscribes to a channel. So if user-limited channels are enabled then the user with ID 42 will be able to subscribe on both dialog#42,43 and dialog#43,42. But Centrifugo does no magic regarding channel strings when keeping channel->to->subscribers map. So if the user subscribed on dialog#42,43 you must publish messages to exactly that channel: dialog#42,43. The same applies to channels with namespaces. Do not expect that channels chat:index and index are the same – they are different, moreover, belong to different namespaces. We'll look at the concept of channel namespaces in Centrifugo shortly.","s":"Channel is just a string","u":"/docs/4/server/channels","h":"#channel-is-just-a-string","p":1866},{"i":1881,"t":"It's possible to configure a list of channel namespaces. Namespaces are optional but very useful. A namespace allows setting custom options for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application. Namespace has a name, and the same channel options (with the same defaults) as described above. name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$). If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator: public:messages gossips:messages Where public and gossips are namespace names. Centrifugo looks for : symbol in the channel name, if found – extracts the namespace name, and applies namespace options while processing protocol commands from a client. All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured: config.json { \"token_hmac_secret_key\": \"very-long-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"30s\", \"namespaces\": [ { \"name\": \"facts\", \"history_size\": 10, \"history_ttl\": \"300s\" }, { \"name\": \"gossips\" } ]} Channel news will use globally defined channel options. Channel facts:sport will use facts namespace options. Channel gossips:sport will use gossips namespace options. Channel xxx:hello will result into subscription error since there is no xxx namespace defined in the configuration above. Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2. note There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly. There are many options which can be set for channel namespace (on top-level and to named one) to modify behavior of channels belonging to a namespace. Below we describe all these options.","s":"Channel namespaces","u":"/docs/4/server/channels","h":"#channel-namespaces","p":1866},{"i":1883,"t":"Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.","s":"Channel options","u":"/docs/4/server/channels","h":"#channel-options","p":1866},{"i":1885,"t":"presence (boolean, default false) – enable/disable online presence information for channels in a namespace. Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels. Let's say you have a channel chat:index and 2 users (with ID 2694 and 56) subscribed to it. And user 2694 has 2 connections to Centrifugo in different browser tabs. In presence data you may see sth like this: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"presence\", \"params\": {\"channel\": \"chat:index\"}}' \\ http://localhost:8000/api{ \"result\": { \"presence\": { \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\": { \"user\": \"2694\", \"client\": \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\", \"conn_info\": {\"name\": \"Alex\"} }, \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\": { \"user\": \"2694\", \"client\": \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\", \"conn_info\": {\"name\": \"Alex\"} } \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\": { \"user\": \"56\", \"client\": \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\", \"conn_info\": {\"name\": \"Alice\"} } } }} To call presence API from the client connection side client must have permission to do so. See presence permission model. caution Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or in a broker memory/disk). So only use it for channels where presence is required. See more details about online presence design.","s":"presence","u":"/docs/4/server/channels","h":"#presence","p":1866},{"i":1887,"t":"join_leave (boolean, default false) – enable/disable sending join and leave messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info (similar to entry inside presence information). Enabling join_leave means that Join/Leave messages will start being emitted, but by default they are not delivered to clients subscribed to a channel. You need to force this using namespace option force_push_join_leave or explicitly provide intent from a client-side (in this case client must have permission to call presence API). caution Keep in mind that join/leave messages can generate a huge number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature. It's hard to say what is \"large\" for you though – just estimate the load based on the fact that each subscribe/unsubscribe event in a channel with N subscribers will result into N messages broadcasted to all. If all clients reconnect at the same time the amount of generated messages is N^2. Join/leave messages distributed only with at most once delivery guarantee.","s":"join_leave","u":"/docs/4/server/channels","h":"#join_leave","p":1866},{"i":1889,"t":"Boolean, default false. When on all clients will receive join/leave events for a channel in a namespace automatically – without explicit intent to consume join/leave messages from the client side. If pushing join/leave is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel presence (by having an explicit capability or if allowed on a namespace level).","s":"force_push_join_leave","u":"/docs/4/server/channels","h":"#force_push_join_leave","p":1866},{"i":1891,"t":"history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted. Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below). caution Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk). So only use history for channels where it's required.","s":"history_size","u":"/docs/4/server/channels","h":"#history_size","p":1866},{"i":1893,"t":"history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision). As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels. By default history TTL duration is zero – this means that channel history is disabled. Again – to turn on history you should wisely configure both history_size and history_ttl options. For example for top-level channels (which do not belong to a namespace): config.json { ... \"history_size\": 10, \"history_ttl\": \"60s\"} Let's look at example. You enabled history for a namespace chat and sent two messages in channel chat:index. Then history will contain sth like this: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"history\", \"params\": {\"channel\": \"chat:index\", \"limit\": 100}}' \\ http://localhost:8000/api{ \"result\": { \"publications\": [ { \"data\": { \"input\": \"1\" }, \"offset\": 1 }, { \"data\": { \"input\": \"2\" }, \"offset\": 2 } ], \"epoch\": \"gWuY\", \"offset\": 2 }} To call history API from the client connection side client must have permission to do so. See history permission model. See additional information about offsets and epoch in History and recovery chapter. tip History persistence properties are dictated by Centrifugo engine used. For example, when using memory engine history is only kept till Centrifugo node restart. In Redis engine case persistence is determined by a Redis server persistence configuration (same for KeyDB and Tarantool).","s":"history_ttl","u":"/docs/4/server/channels","h":"#history_ttl","p":1866},{"i":1895,"t":"force_positioning (boolean, default false) – when the force_positioning option is on Centrifugo forces all subscriptions in a namespace to be positioned. I.e. Centrifugo will try to compensate at most once delivery of PUB/SUB broker checking client position inside a stream. If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API. force_positioning option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream). If positioning is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).","s":"force_positioning","u":"/docs/4/server/channels","h":"#force_positioning","p":1866},{"i":1897,"t":"force_recovery (boolean, default false) – when the position option is on Centrifugo forces all subscriptions in a namespace to be recoverable. When enabled Centrifugo will try to recover missed publications in channels after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the force_positioning option described above. force_recovery option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages). If recovery is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level). tip Not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.","s":"force_recovery","u":"/docs/4/server/channels","h":"#force_recovery","p":1866},{"i":1899,"t":"allow_subscribe_for_client (boolean, default false) – when on all non-anonymous clients will be able to subscribe to any channel in a namespace. To additionally allow anonymous users to subscribe turn on allow_subscribe_for_anonymous (see below). caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.","s":"allow_subscribe_for_client","u":"/docs/4/server/channels","h":"#allow_subscribe_for_client","p":1866},{"i":1901,"t":"allow_subscribe_for_anonymous (boolean, default false) – turn on if anonymous clients (with empty user ID) should be able to subscribe on channels in a namespace.","s":"allow_subscribe_for_anonymous","u":"/docs/4/server/channels","h":"#allow_subscribe_for_anonymous","p":1866},{"i":1903,"t":"allow_publish_for_subscriber (boolean, default false) - when the allow_publish_for_subscriber option is enabled client can publish into a channel in namespace directly from the client side over real-time connection but only if client subscribed to that channel. danger Keep in mind that in this case subscriber can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage). allow_publish_for_subscriber (or allow_publish_for_client mentioned below) option still can be useful to send something without backend-side validation and saving it into a database – for example, this option may be handy for demos and quick prototyping real-time app ideas.","s":"allow_publish_for_subscriber","u":"/docs/4/server/channels","h":"#allow_publish_for_subscriber","p":1866},{"i":1905,"t":"allow_publish_for_client (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). It's like allow_publish_for_subscriber – but client should not be a channel subscriber to publish. danger Keep in mind that in this case client can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).","s":"allow_publish_for_client","u":"/docs/4/server/channels","h":"#allow_publish_for_client","p":1866},{"i":1907,"t":"allow_publish_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to publish into channels in a namespace.","s":"allow_publish_for_anonymous","u":"/docs/4/server/channels","h":"#allow_publish_for_anonymous","p":1866},{"i":1909,"t":"allow_history_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call history API from that channel.","s":"allow_history_for_subscriber","u":"/docs/4/server/channels","h":"#allow_history_for_subscriber","p":1866},{"i":1911,"t":"allow_history_for_client (boolean, default false) – allows all clients to call history information in a namespace.","s":"allow_history_for_client","u":"/docs/4/server/channels","h":"#allow_history_for_client","p":1866},{"i":1913,"t":"allow_history_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call history from channels in a namespace.","s":"allow_history_for_anonymous","u":"/docs/4/server/channels","h":"#allow_history_for_anonymous","p":1866},{"i":1915,"t":"allow_presence_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call presence information from that channel.","s":"allow_presence_for_subscriber","u":"/docs/4/server/channels","h":"#allow_presence_for_subscriber","p":1866},{"i":1917,"t":"allow_presence_for_client (boolean, default false) – allows all clients to call presence information in a namespace.","s":"allow_presence_for_client","u":"/docs/4/server/channels","h":"#allow_presence_for_client","p":1866},{"i":1919,"t":"allow_presence_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call presence from channels in a namespace.","s":"allow_presence_for_anonymous","u":"/docs/4/server/channels","h":"#allow_presence_for_anonymous","p":1866},{"i":1921,"t":"allow_user_limited_channels (boolean, default false) - allows using user-limited channels in a namespace for checking subscribe permission. note If client subscribes to a user-limited channel while this option is off then server rejects subscription with 103: permission denied error.","s":"allow_user_limited_channels","u":"/docs/4/server/channels","h":"#allow_user_limited_channels","p":1866},{"i":1923,"t":"channel_regex (string, default \"\") – is an option to set a regular expression for channels allowed in the namespace. By default Centrifugo does not limit channel name variations. For example, if you have a namespace chat, then channel names inside this namespace are not really limited, it can be chat:index, chat:1, chat:2, chat:zzz and so on. But if you want to be strict and know possible channel patterns you can use channel_regex option. This is especially useful in namespaces where all clients can subscribe to channels. For example, let's only allow digits after chat: for channel names in a chat namespace: { \"namespaces\": [ { \"name\": \"chat\", \"allow_subscribe_for_client\": true, \"channel_regex\": \"^[\\d+]$\" } ]} danger Note, that we are skipping chat: part in regex. Since namespace prefix is the same for all channels in a namespace we only match the rest (after the prefix) of channel name. Channel regex only checked for client-side subscriptions, if you are using server-side subscriptions Centrifugo won't check the regex. Centrifugo uses Go language regexp package for regular expressions.","s":"channel_regex","u":"/docs/4/server/channels","h":"#channel_regex","p":1866},{"i":1925,"t":"proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter","s":"proxy_subscribe","u":"/docs/4/server/channels","h":"#proxy_subscribe","p":1866},{"i":1927,"t":"proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter","s":"proxy_publish","u":"/docs/4/server/channels","h":"#proxy_publish","p":1866},{"i":1929,"t":"proxy_sub_refresh (boolean, default false) – turns on sub refresh proxy, more info in proxy chapter","s":"proxy_sub_refresh","u":"/docs/4/server/channels","h":"#proxy_sub_refresh","p":1866},{"i":1931,"t":"subscribe_proxy_name (string, default \"\") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.","s":"subscribe_proxy_name","u":"/docs/4/server/channels","h":"#subscribe_proxy_name","p":1866},{"i":1933,"t":"publish_proxy_name (string, default \"\") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.","s":"publish_proxy_name","u":"/docs/4/server/channels","h":"#publish_proxy_name","p":1866},{"i":1935,"t":"sub_refresh_proxy_name (string, default \"\") – turns on sub refresh proxy when granular proxy mode is used. Note that proxy_sub_refresh option defined above is ignored in granular proxy mode.","s":"sub_refresh_proxy_name","u":"/docs/4/server/channels","h":"#sub_refresh_proxy_name","p":1866},{"i":1937,"t":"Let's look at how to set some of these options in a config. In this example we turning on presence, history features, forcing publication recovery. Also allowing all client connections (including anonymous users) to subscribe to channels and call publish, history, presence APIs if subscribed. config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true} Here we set channel options on config top-level – these options will affect channels without namespace. In many cases defining namespaces is a recommended approach so you can manage options for every real-time feature separately. With namespaces the above config may transform to: config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"namespaces\": [ { \"name\": \"feed\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true } ]} In this case channels should be prefixed with feed: to follow the behavior configured for a feed namespace.","s":"Channel config examples","u":"/docs/4/server/channels","h":"#channel-config-examples","p":1866},{"i":1939,"t":"On this page","s":"Configure Centrifugo","u":"/docs/4/server/configuration","h":"","p":1938},{"i":1941,"t":"Centrifugo can be configured in several ways.","s":"Configuration sources","u":"/docs/4/server/configuration","h":"#configuration-sources","p":1938},{"i":1943,"t":"Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer environment or configuration file sources. Command-line options have the highest priority when set than other ways to configure Centrifugo.","s":"Command-line flags","u":"/docs/4/server/configuration","h":"#command-line-flags","p":1938},{"i":1945,"t":"All Centrifugo options can be set over env in the format CENTRIFUGO_ (i.e. option name with CENTRIFUGO_ prefix, all in uppercase). Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags. Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use \"true\" value for an environment variable (or simply \"1\"). To set false use \"false\" or \"0\". Example: export CENTRIFUGO_PROMETHEUS=\"1\" Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example: export CENTRIFUGO_ALLOWED_ORIGINS=\"https://mysite1.example.com https://mysite2.example.com\" For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name. Empty environment variables are considered unset (!) and will fall back to the next configuration source.","s":"OS environment variables","u":"/docs/4/server/configuration","h":"#os-environment-variables","p":1938},{"i":1947,"t":"Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable prevails over the same option in config file). A simple way to start with Centrifugo is to run: centrifugo genconfig This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo: centrifugo -c config.json","s":"Configuration file","u":"/docs/4/server/configuration","h":"#configuration-file","p":1938},{"i":1949,"t":"Centrifugo supports three configuration file formats: JSON, YAML, or TOML.","s":"Config file formats","u":"/docs/4/server/configuration","h":"#config-file-formats","p":1938},{"i":1951,"t":"Here is an example of Centrifugo JSON configuration file: config.json { \"allowed_origins\": [\"http://localhost:3000\"], \"token_hmac_secret_key\": \"\", \"api_key\": \"\"} token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT. api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients. allowed_origins option described below.","s":"JSON config format","u":"/docs/4/server/configuration","h":"#json-config-format","p":1938},{"i":1953,"t":"Centrifugo also supports TOML format for configuration file: centrifugo --config=config.toml Where config.toml contains: config.toml allowed_origins: [ \"http://localhost:3000\" ]token_hmac_secret_key = \"\"api_key = \"\"log_level = \"debug\" In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.","s":"TOML config format","u":"/docs/4/server/configuration","h":"#toml-config-format","p":1938},{"i":1955,"t":"YAML format is also supported: config.yaml allowed_origins: - \"http://localhost:3000\"token_hmac_secret_key: \"\"api_key: \"\"log_level: debug With YAML remember to use spaces, not tabs when writing a configuration file.","s":"YAML config format","u":"/docs/4/server/configuration","h":"#yaml-config-format","p":1938},{"i":1957,"t":"Let's describe some important options you can configure when running Centrifugo.","s":"Important options","u":"/docs/4/server/configuration","h":"#important-options","p":1938},{"i":1959,"t":"This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins. As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array. Connection requests without Origin header set are passing through without any checks (i.e. always allowed). For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way: \"allowed_origins\": [ \"http://localhost:3000\"] When connecting from https://example.com: \"allowed_origins\": [ \"https://example.com\"] Origin pattern can contain wildcard symbol * to match subdomains: \"allowed_origins\": [ \"https://*.example.com\"] – in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check. It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature): \"allowed_origins\": [ \"*\"]","s":"allowed_origins","u":"/docs/4/server/configuration","h":"#allowed_origins","p":1938},{"i":1961,"t":"Bind your Centrifugo to a specific interface address (string, by default \"\" - listen on all available interfaces).","s":"address","u":"/docs/4/server/configuration","h":"#address","p":1938},{"i":1963,"t":"Port to bind Centrifugo to (string, by default \"8000\").","s":"port","u":"/docs/4/server/configuration","h":"#port","p":1938},{"i":1965,"t":"Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.","s":"engine","u":"/docs/4/server/configuration","h":"#engine","p":1938},{"i":1967,"t":"These options allow tweaking server behavior, in most cases default values are good to start with.","s":"Advanced options","u":"/docs/4/server/configuration","h":"#advanced-options","p":1938},{"i":1969,"t":"Default: 128 Sets the maximum number of different channel subscriptions a single client can have. tip When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.","s":"client_channel_limit","u":"/docs/4/server/configuration","h":"#client_channel_limit","p":1938},{"i":1971,"t":"Default: 255 Sets the maximum length of the channel name.","s":"channel_max_length","u":"/docs/4/server/configuration","h":"#channel_max_length","p":1938},{"i":1973,"t":"Default: 0 The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited. The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).","s":"client_user_connection_limit","u":"/docs/4/server/configuration","h":"#client_user_connection_limit","p":1938},{"i":1975,"t":"Added in Centrifugo v4.0.1 Default: 0 When set to a value > 0 client_connection_limit limits the max number of connections single Centrifugo node can handle. It acts on HTTP middleware level and stops processing request if the condition met. It logs a warning into logs in this case and increments centrifugo_node_client_connection_limit Prometheus counter. Client SDKs will attempt reconnecting. Some motivation behind this option may be found in this issue. Note, that at this point client_connection_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_limit","u":"/docs/4/server/configuration","h":"#client_connection_limit","p":1938},{"i":1977,"t":"Added in Centrifugo v4.1.1 Default: 0 client_connection_rate_limit sets the maximum number of HTTP requests to establish a new real-time connection a single Centrifugo node will accept per second (on real-time transport endpoints). All requests outside the limit will get 503 Service Unavailable code in response. Our SDKs handle this with backoff reconnection. By default, no limit is used. Note, that at this point client_connection_rate_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_rate_limit","u":"/docs/4/server/configuration","h":"#client_connection_rate_limit","p":1938},{"i":1979,"t":"Default: 1048576 Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.","s":"client_queue_max_size","u":"/docs/4/server/configuration","h":"#client_queue_max_size","p":1938},{"i":1981,"t":"Default: 0 client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently. By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).","s":"client_concurrency","u":"/docs/4/server/configuration","h":"#client_concurrency","p":1938},{"i":1983,"t":"Duration, default: 10s This option allows tuning the maximum time Centrifugo will wait for the connect frame (which contains authentication information) from the client after establishing connection. Default value should be reasonable for most use cases.","s":"client_stale_close_delay","u":"/docs/4/server/configuration","h":"#client_stale_close_delay","p":1938},{"i":1985,"t":"Default: false Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID). Access to channel operations should be explicitly enabled for anonymous connections.","s":"allow_anonymous_connect_without_token","u":"/docs/4/server/configuration","h":"#allow_anonymous_connect_without_token","p":1938},{"i":1987,"t":"Added in Centrifugo v4.1.1 Default: false When the option is set Centrifugo won't accept connections from anonymous users even if they provided a valid JWT. I.e. if token is valid, but sub claim is empty – then Centrifugo closes connection with advice to not reconnect again.","s":"disallow_anonymous_connection_tokens","u":"/docs/4/server/configuration","h":"#disallow_anonymous_connection_tokens","p":1938},{"i":1989,"t":"Default: 0 By default, Centrifugo runs on all available CPU cores (also Centrifugo can look at cgroup limits when rnning in Docker/Kubernetes). To limit the number of cores Centrifugo can utilize in one moment use this option.","s":"gomaxprocs","u":"/docs/4/server/configuration","h":"#gomaxprocs","p":1938},{"i":1991,"t":"After Centrifugo started there are several endpoints available.","s":"Endpoint configuration.","u":"/docs/4/server/configuration","h":"#endpoint-configuration","p":1938},{"i":1993,"t":"Bidirectional WebSocket default endpoint: ws://localhost:8000/connection/websocket Bidirectional emulation with HTTP-streaming (disabled by default): ws://localhost:8000/connection/http_stream Bidirectional emulation with SSE (EventSource) (disabled by default): ws://localhost:8000/connection/sse Bidirectional SockJS default endpoint (disabled by default): http://localhost:8000/connection/sockjs Unidirectional EventSource endpoint (disabled by default): http://localhost:8000/connection/uni_sse Unidirectional HTTP streaming endpoint (disabled by default): http://localhost:8000/connection/uni_http_stream Unidirectional WebSocket endpoint (disabled by default): http://localhost:8000/connection/uni_websocket Unidirectional SSE (EventSource) endpoint (disabled by default): http://localhost:8000/connection/uni_sse Server HTTP API endpoint: http://localhost:8000/api By default, all endpoints work on port 8000. This can be changed with port option: { \"port\": 9000} In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.","s":"Default endpoints.","u":"/docs/4/server/configuration","h":"#default-endpoints","p":1938},{"i":1995,"t":"Admin web UI endpoint works on root path by default, i.e. http://localhost:8000. For more details about admin web UI, refer to the Admin web UI documentation.","s":"Admin endpoints.","u":"/docs/4/server/configuration","h":"#admin-endpoints","p":1938},{"i":1997,"t":"Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config: { ... \"debug\": true} And endpoint: http://localhost:8000/debug/pprof/ – will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.","s":"Debug endpoints.","u":"/docs/4/server/configuration","h":"#debug-endpoints","p":1938},{"i":1999,"t":"Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag: centrifugo -c config.json --health","s":"Health check endpoint","u":"/docs/4/server/configuration","h":"#health-check-endpoint","p":1938},{"i":2001,"t":"We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal: API endpoint (/api) - for HTTP API requests Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format Health check endpoint (/health) - used to do health checks Debug endpoints (/debug/pprof) - used to inspect internal server state It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration. Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work. To run internal endpoints on custom port use internal_port option: { ... \"internal_port\": 9000} So admin web interface will work on address: http://localhost:9000 Also, debug page will be available on a new custom port too: http://localhost:9000/debug/pprof/ The same for API and Prometheus endpoints.","s":"Custom internal ports","u":"/docs/4/server/configuration","h":"#custom-internal-ports","p":1938},{"i":2003,"t":"To disable websocket endpoint set websocket_disable boolean option to true. To disable API endpoint set api_disable boolean option to true.","s":"Disable default endpoints","u":"/docs/4/server/configuration","h":"#disable-default-endpoints","p":1938},{"i":2005,"t":"It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options: admin_handler_prefix (default \"\") - to control Admin panel URL prefix websocket_handler_prefix (default \"/connection/websocket\") - to control WebSocket URL prefix http_stream_handler_prefix (default \"/connection/http_stream\") - to control HTTP-streaming URL prefix sse_handler_prefix (default \"/connection/sse\") - to control SSE/EventSource URL prefix sockjs_handler_prefix (default \"/connection/sockjs\") - to control SockJS URL prefix uni_sse_handler_prefix (default \"/connection/uni_sse\") - to control unidirectional Eventsource URL prefix uni_http_stream_handler_prefix (default \"/connection/uni_http_stream\") - to control unidirectional HTTP streaming URL prefix uni_websocket_handler_prefix (default \"/connection/uni_websocket\") - to control unidirectional WebSocket URL prefix api_handler_prefix (default \"/api\") - to control HTTP API URL prefix prometheus_handler_prefix (default \"/metrics\") - to control Prometheus URL prefix health_handler_prefix (default \"/health\") - to control health check URL prefix","s":"Customize handler endpoints","u":"/docs/4/server/configuration","h":"#customize-handler-endpoints","p":1938},{"i":2007,"t":"It's possible to send HUP signal to Centrifugo to reload a configuration: kill -HUP Though at moment this will only reload token secrets and channel options (top-level and namespaces). Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.","s":"Signal handling","u":"/docs/4/server/configuration","h":"#signal-handling","p":1938},{"i":2010,"t":"The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. It also disables permission checks on client API level - for presence and history calls. This mode can be useful for demo projects based on Centrifugo, integration tests, local projects, or real-time application prototyping. Don't use it in production until you 100% know what you are doing.","s":"Insecure client connection","u":"/docs/4/server/configuration","h":"#insecure-client-connection","p":1938},{"i":2012,"t":"This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules. The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.","s":"Insecure API mode","u":"/docs/4/server/configuration","h":"#insecure-api-mode","p":1938},{"i":2014,"t":"This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).","s":"Insecure admin mode","u":"/docs/4/server/configuration","h":"#insecure-admin-mode","p":1938},{"i":2016,"t":"Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use \"5s\". The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option). Valid time units are \"ms\" (milliseconds), \"s\" (seconds), \"m\" (minutes), \"h\" (hours). Some examples: \"1000ms\" // 1000 milliseconds\"1s\" // 1 second\"12h\" // 12 hours\"720h\" // 30 days","s":"Setting time duration options","u":"/docs/4/server/configuration","h":"#setting-time-duration-options","p":1938},{"i":2018,"t":"While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special: CENTRIFUGO_NAMESPACES='[{\"name\": \"ns1\"}, {\"name\": \"ns2\"}]' ./centrifugo I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.","s":"Setting namespaces over env","u":"/docs/4/server/configuration","h":"#setting-namespaces-over-env","p":1938},{"i":2020,"t":"Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage. Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal. To disable sending usage stats set usage_stats_disable option: config.json { \"usage_stats_disable\": true}","s":"Anonymous usage stats","u":"/docs/4/server/configuration","h":"#anonymous-usage-stats","p":1938},{"i":2022,"t":"On this page","s":"Engines and scalability","u":"/docs/4/server/engines","h":"","p":2021},{"i":2024,"t":"Used by default. Supports only one node. Nice choice to start with. Supports all features keeping everything in Centrifugo node process memory. You don't need to install Redis when using this engine. Advantages: Super fast since it does not involve network at all Does not require separate broker setup Disadvantages: Does not allow scaling nodes (actually you still can scale Centrifugo with Memory engine but you have to publish data into each Centrifugo node and you won't have consistent history and presence state throughout Centrifugo nodes) Does not persist message history in channels between Centrifugo restarts.","s":"Memory engine","u":"/docs/4/server/engines","h":"#memory-engine","p":2021},{"i":2026,"t":"history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. When using a history in a channel, Centrifugo keeps some metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help to expire metadata faster (or slower if you need it). The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Memory engine options","u":"/docs/4/server/engines","h":"#memory-engine-options","p":2021},{"i":2028,"t":"Redis is an open-source, in-memory data structure store, used as a database, cache, and message broker. Centrifugo Redis engine allows scaling Centrifugo nodes to different machines. Nodes will use Redis as a message broker (utilizing Redis PUB/SUB for node communication) and keep presence and history data in Redis. Minimal Redis version is 5.0.1 With Redis it's possible to come to the architecture like this:","s":"Redis engine","u":"/docs/4/server/engines","h":"#redis-engine","p":2021},{"i":2030,"t":"Several configuration options related to Redis engine. redis_address​ String, default \"127.0.0.1:6379\" - Redis server address. redis_password​ String, default \"\" - Redis password. redis_user​ String, default \"\" - Redis user for ACL-based auth. redis_db​ Integer, default 0 - number of Redis db to use. redis_prefix​ String, default \"centrifugo\" – custom prefix to use for channels and keys in Redis. redis_use_lists​ Boolean, default false – turns on using Redis Lists instead of Stream data structure for keeping history (not recommended, keeping this for backwards compatibility mostly). redis_force_resp2​ Available since Centrifugo v4.1.3 Boolean, default false. If set to true it forces using RESP2 protocol for communicating with Redis. By default, Redis client used by Centrifugo tries to detect supported Redis protocol automatically trying RESP3 first. history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. Similar to a Memory engine Redis engine also looks at history_meta_ttl option. Meta key in Redis is a HASH that contains the current offset number in channel and the stream epoch value. When using a history in a channel, Centrifugo saves metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help. The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Redis engine options","u":"/docs/4/server/engines","h":"#redis-engine-options","p":2021},{"i":2032,"t":"Some options may help you configuring TLS-protected communication between Centrifugo and Redis. redis_tls​ Boolean, default false - enable Redis TLS connection. redis_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_tls_skip_verify – but it will be removed in v5. redis_tls_cert​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_tls_cert_pem option. redis_tls_key​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_tls_key_pem option. redis_tls_root_ca​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_tls_root_ca_pem option. redis_tls_server_name​ Added in Centrifugo v4.1.0 String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Configuring Redis TLS","u":"/docs/4/server/engines","h":"#configuring-redis-tls","p":2021},{"i":2034,"t":"Let's see how to start several Centrifugo nodes using the Redis Engine. We will start 3 Centrifugo nodes and all those nodes will be connected via Redis. First, you should have Redis running. As soon as it's running - we can launch 3 Centrifugo instances. Open your terminal and start the first one: centrifugo --config=config.json --port=8000 --engine=redis --redis_address=127.0.0.1:6379 If your Redis is on the same machine and runs on its default port you can omit redis_address option in the command above. Then open another terminal and start another Centrifugo instance: centrifugo --config=config.json --port=8001 --engine=redis --redis_address=127.0.0.1:6379 Note that we use another port number (8001) as port 8000 is already busy by our first Centrifugo instance. If you are starting Centrifugo instances on different machines then you most probably can use the same port number (8000 or whatever you want) for all instances. And finally, let's start the third instance: centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379 Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes. To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation. tip In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers. Here is a live example where we locally start two Centrifugo nodes both connected to local Redis: Sorry, your browser doesn't support embedded video.","s":"Scaling with Redis tutorial","u":"/docs/4/server/engines","h":"#scaling-with-redis-tutorial","p":2021},{"i":2036,"t":"Centrifugo supports the official way to add high availability to Redis - Redis Sentinel. For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name: redis_sentinel_address (string, default \"\") - comma separated list of Sentinel addresses for HA. At least one known server required. redis_sentinel_master_name (string, default \"\") - name of Redis master Sentinel monitors Also: redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1 redis_sentinel_user - optional string user (used only in Redis ACL-based auth). So you can start Centrifugo which will use Sentinels to discover Redis master instances like this: centrifugo --config=config.json Where config.json: config.json { ... \"engine\": \"redis\", \"redis_sentinel_address\": \"127.0.0.1:26379\", \"redis_sentinel_master_name\": \"mymaster\"} Sentinel configuration file may look like this (for 3-node Sentinel setup with quorum 2): port 26379sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 10000sentinel failover-timeout mymaster 60000 You can find how to properly set up Sentinels in official documentation. Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.","s":"Redis Sentinel for high availability","u":"/docs/4/server/engines","h":"#redis-sentinel-for-high-availability","p":2021},{"i":2038,"t":"To configure TLS for Redis Sentinel use the following options. redis_sentinel_tls​ Boolean, default false - enable Redis TLS connection. redis_sentinel_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_sentinel_tls_skip_verify – but it will be removed in v5. redis_sentinel_tls_cert​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_sentinel_tls_cert_pem option. redis_sentinel_tls_key​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_sentinel_tls_key_pem option. redis_sentinel_tls_root_ca​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_sentinel_tls_root_ca_pem option. redis_sentinel_tls_server_name​ Added in Centrifugo v4.1.0 String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Redis Sentinel TLS","u":"/docs/4/server/engines","h":"#redis-sentinel-tls","p":2021},{"i":2040,"t":"Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case. For example, you can use something like this in Haproxy config: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:16379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin And then just point Centrifugo to this Haproxy: centrifugo --config=config.json --engine=redis --redis_address=\"localhost:16379\"","s":"Haproxy instead of Sentinel configuration","u":"/docs/4/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":2021},{"i":2042,"t":"Centrifugo has built-in Redis sharding support. This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale. At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples. To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"127.0.0.1:6379\", \"127.0.0.1:6380\", ]} To start Centrifugo with Redis instances on different hosts: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"192.168.1.34:6379\", \"192.168.1.35:6379\", ]} If you also need to customize AUTH password, Redis DB number then you can use an extended address notation. note Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers. When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).","s":"Redis sharding","u":"/docs/4/server/engines","h":"#redis-sharding","p":2021},{"i":2044,"t":"Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\" ]} You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start. To set the same over environment variable: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001\" CENTRIFUGO_ENGINE=redis ./centrifugo If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\", \"localhost:30101,localhost:30102,localhost:30103\" ]} Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump). To reproduce the same over environment variable use space to separate different clusters: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001,localhost:30002 localhost:30101,localhost:30102\" CENTRIFUGO_ENGINE=redis ./centrifugo","s":"Redis cluster","u":"/docs/4/server/engines","h":"#redis-cluster","p":2021},{"i":2046,"t":"EXPERIMENTAL Centrifugo Redis engine seamlessly works with KeyDB. KeyDB server is compatible with Redis and provides several additional features beyond. caution We can't give any promises about compatibility with KeyDB in the future Centrifugo releases - while KeyDB is fully compatible with Redis things should work just fine. That's why we consider this as EXPERIMENTAL feature. Use KeyDB instead of Redis only if you are sure you need it. Nothing stops you from running several Redis instances per each core you have, configure sharding, and obtain even better performance than KeyDB can provide (due to lack of synchronization between threads in Redis). To run Centrifugo with KeyDB all you need to do is use redis engine but run the KeyDB server instead of Redis.","s":"KeyDB Engine","u":"/docs/4/server/engines","h":"#keydb-engine","p":2021},{"i":2048,"t":"Other storages which are compatible with Centrifugo may work, but we did not make enough testing with them. Some of them still evolving and do not fully support Redis protocol. So if you want to use these storages with Centrifugo – please read carefully the notes below: AWS Elasticache – it was reported to work, but we suggest you testing the setup including failover tests and work under load. DragonflyDB - it's mostly compatible, the only problem with DragonflyDB v1.0.0 we observed is failing test regarding history iteration in reversed order (not very common). We have not tested a Redis Cluster emulation mode provided by DragonflyDB yet. We suggest you testing the setup including failover tests and work under load.","s":"Other Redis compatible","u":"/docs/4/server/engines","h":"#other-redis-compatible","p":2021},{"i":2050,"t":"EXPERIMENTAL Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics. caution EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves. There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge. info To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together. There are several supported Tarantool topologies to which Centrifugo can connect: One standalone Tarantool instance Many standalone Tarantool instances and consistently shard data between them Tarantool running in Cartridge Tarantool with replica and automatic failover in Cartridge Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7) After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes): config.json { ... \"engine\": \"tarantool\", \"tarantool_address\": \"127.0.0.1:3301\"} See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework. See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.","s":"Tarantool engine","u":"/docs/4/server/engines","h":"#tarantool-engine","p":2021},{"i":2052,"t":"tarantool_address​ String or array of strings. Default tcp://127.0.0.1:3301. Connection address to Tarantool. tarantool_mode​ String, default standalone A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication). All modes support client-side consistent sharding (similar to what Redis engine provides). tarantool_user​ String, default \"\". Allows setting a user. tarantool_password​ String, default \"\". Allows setting a password. history_meta_ttl​ Duration, default 2160h. Same option as for Memory engine and Redis engine also applies to Tarantool case.","s":"Tarantool engine options","u":"/docs/4/server/engines","h":"#tarantool-engine-options","p":2021},{"i":2054,"t":"It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below. Limitations: Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available. Nats wildcard channel subscriptions with symbols * and > not supported. First start Nats server: $ nats-server[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set][3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready Then start Centrifugo with broker option: centrifugo --broker=nats --config=config.json And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine): centrifugo --broker=nats --config=config.json --port=8001 Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.","s":"Nats broker","u":"/docs/4/server/engines","h":"#nats-broker","p":2021},{"i":2056,"t":"nats_url​ String, default nats://127.0.0.1:4222. Connection url in format nats://derek:pass@localhost:4222. nats_prefix​ String, default centrifugo. Prefix for channels used by Centrifugo inside Nats. nats_dial_timeout​ Duration, default 1s. Timeout for dialing with Nats. nats_write_timeout​ Duration, default 1s. Write (and flush) timeout for a connection to Nats.","s":"Options","u":"/docs/4/server/engines","h":"#options","p":2021},{"i":2058,"t":"On this page","s":"Helper CLI commands","u":"/docs/4/server/console_commands","h":"","p":2057},{"i":2060,"t":"To show Centrifugo version and exit run: centrifugo version","s":"version","u":"/docs/4/server/console_commands","h":"#version","p":2057},{"i":2062,"t":"Another command is genconfig: centrifugo genconfig -c config.json It will automatically generate the minimal required configuration file. This is mostly useful for development. If any errors happen – program will exit with error message and exit code 1. genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file: centrifugo genconfig -c config.toml","s":"genconfig","u":"/docs/4/server/console_commands","h":"#genconfig","p":2057},{"i":2064,"t":"Centrifugo has special command to check configuration file checkconfig: centrifugo checkconfig --config=config.json If any errors found during validation – program will exit with error message and exit code 1.","s":"checkconfig","u":"/docs/4/server/console_commands","h":"#checkconfig","p":2057},{"i":2066,"t":"Another command is gentoken: centrifugo gentoken -c config.json -u 28282 It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gentoken","u":"/docs/4/server/console_commands","h":"#gentoken","p":2057},{"i":2068,"t":"Another command is gensubtoken: centrifugo gensubtoken -c config.json -u 28282 -s channel It will automatically generate HMAC SHA-256 based subscription token for channel channel and user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -s channel -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gensubtoken","u":"/docs/4/server/console_commands","h":"#gensubtoken","p":2057},{"i":2070,"t":"One more command is checktoken: centrifugo checktoken -c config.json It will validate your connection JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checktoken","u":"/docs/4/server/console_commands","h":"#checktoken","p":2057},{"i":2072,"t":"One more command is checksubtoken: centrifugo checksubtoken -c config.json It will validate your subscription JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checksubtoken","u":"/docs/4/server/console_commands","h":"#checksubtoken","p":2057},{"i":2074,"t":"On this page","s":"Infrastructure tuning","u":"/docs/4/server/infra_tuning","h":"","p":2073},{"i":2076,"t":"You should increase a max number of open files Centrifugo process can open if you want to handle more connections. To get the current open files limit run: ulimit -n On Linux you can check limits for a running process using: cat /proc//limits The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default. See this document to get more info on how to increase this number. If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536. You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).","s":"Open files limit","u":"/docs/4/server/infra_tuning","h":"#open-files-limit","p":2073},{"i":2078,"t":"Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default). In order to eliminate a problem you can: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces See a post in Pusher blog about this problem and more detailed solution steps.","s":"Ephemeral port exhaustion","u":"/docs/4/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":2073},{"i":2080,"t":"On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state. Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side. Look how many socket descriptors in TIME_WAIT state. netstat -an |grep TIME_WAIT | grep | wc -l Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html The advices here are similar to ephemeral port exhaustion problem: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces","s":"Sockets in TIME_WAIT state","u":"/docs/4/server/infra_tuning","h":"#sockets-in-time_wait-state","p":2073},{"i":2082,"t":"Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established. Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.","s":"Proxy max connections","u":"/docs/4/server/infra_tuning","h":"#proxy-max-connections","p":2073},{"i":2084,"t":"More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.","s":"Conntrack table","u":"/docs/4/server/infra_tuning","h":"#conntrack-table","p":2073},{"i":2086,"t":"You should also consider adding additional protection to your Centrifugo endpoints. Centrifugo itself provides several options (described in configuration section) regarding server protection from the malicious behavior. Though an additional layer of DDOS protection on network or infrastructure level is highly recommended. For example, you may want to limit the number of connections coming from particular IP address. Here we list some possible ways you can use to protect your Centrifugo installation: Adding Nginx limit_conn_zone configuration Using stick tables of Haproxy Configuring rate limiting rules with Cloudflare The list is not exhaustive of course.","s":"Additional server protection","u":"/docs/4/server/infra_tuning","h":"#additional-server-protection","p":2073},{"i":2088,"t":"On this page","s":"Metrics monitoring","u":"/docs/4/server/monitoring","h":"","p":2087},{"i":2090,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus","u":"/docs/4/server/monitoring","h":"#prometheus","p":2087},{"i":2092,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite","u":"/docs/4/server/monitoring","h":"#graphite","p":2087},{"i":2094,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/4/server/monitoring","h":"#grafana-dashboard","p":2087},{"i":2096,"t":"On this page","s":"History and recovery","u":"/docs/4/server/history_and_recovery","h":"","p":2095},{"i":2098,"t":"History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero. Centrifugo is designed with an idea that history streams are ephemeral (can be created on the fly without explicit create call from Centrifugo users) and can expire or can be lost at any moment. Centrifugo provides a way for a client to understand that channel history lost. In this case, the main application database should be the source of truth and state recovery. When history is on every message published into a channel is saved into a history stream. The persistence properties of history are dictated by a Centrifugo engine used. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside Tarantool spaces. Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of state recovery anymore. tip History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.","s":"History design","u":"/docs/4/server/history_and_recovery","h":"#history-design","p":2095},{"i":2100,"t":"History iteration based on three fields: limit since reverse Combining these fields you can iterate over a stream in both directions. Get current stream top offset and epoch: history(limit: 0, since: null, reverse: false) Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: false) Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: true) Get history from the current beginning (up to 10): history(limit: 10, since: null, reverse: false) Get history from the current end in reversed direction (up to 10): history(limit: 10, since: null, reverse: true) Get up to 10 publications since known stream position (described by offset and epoch values): history(limit: 10, since: {offset: 0, epoch: \"epoch\"}, reverse: false) Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest): history(limit: 10, since: {offset: 11, epoch: \"epoch\"}, reverse: true) Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }}","s":"History iteration API","u":"/docs/4/server/history_and_recovery","h":"#history-iteration-api","p":2095},{"i":2102,"t":"One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while. In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnect at the same time). danger Message recovery protocol feature designed to be used together with reasonably small history stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time. To enable recovery mechanism for channels set the force_recovery boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. It's also possible to ask for enabling recovery from the client-side when configuring Subscription object – in this case client must have a permission to call history API. When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not. The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default. Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client SDKs (for bidirectional transport). The recovery process works this way: Let's suppose client subscribes on a channel with recovery on. Client receives subscribe reply from Centrifugo with two values: stream epoch and stream top offset, those values are saved by an SDK implementation. Every received publication has incremental offset, client SDK increments locally saved offset on each publication from a channel. Let's say at this point client disconnected for a while. Upon resubscribing to a channel SDK provides last seen epoch anf offset to Centrifugo. Centrifugo tries to load all the missed publications starting from the stream position provided by a client. If Centrifugo decides it can successfully recover client's state – then all missed publications returned in subscribe reply and client receives recovered: true in subscribed event context, and publication event handler of Subscription is called for every missed publication. Otherwise no publications returned and recovered flag of subscribed event context is set to false. epoch is useful for cases when history storage is temporary and can lose the history stream entirely. In this case comparing epoch values gives Centrifugo a tip that while publications exist and theoretically have same offsets as before - the stream is actually different, so it's impossible to use it for the recovery process. To summarize, here is a list of possible scenarios when Centrifugo can't recover client's state for a channel and provides recovered: false flag in subscribed event context: number of missed publications exceeds client_recovery_max_publication_limit option number of missed publications exceeds history_size namespace option client was away for a long time and history stream expired according to history_ttl namespace option storage used by Centrifugo engine lost the stream (restart, number of shards changed, cleared by the administrator, etc.) Having said this all, Centrifugo recovery is nice to keep the continuity of the connection and subscription. This speed-ups resubscribe in many cases and helps the backend to survive mass reconnect scenario since you avoid lots of requests for state loading. For setups with millions of connections this can be a life-saver. But we recommend applications to always have a way to load the state from the main application database. For example, on app reload. You can also manually implement your own recovery logic on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client resubscribe completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.","s":"Automatic message recovery","u":"/docs/4/server/history_and_recovery","h":"#automatic-message-recovery","p":2095},{"i":2104,"t":"On this page","s":"Load balancing","u":"/docs/4/server/load_balancing","h":"","p":2103},{"i":2106,"t":"Although it's possible to use Centrifugo without any reverse proxy before it, it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with edge cases when handling HTTP/Websocket connections from the wild. Also you probably want some sort of load balancing eventually between Centrifugo nodes so that proxy can be such a balancer too. In this section we will look at Nginx configuration to deploy Centrifugo. Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections. There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).","s":"Nginx configuration","u":"/docs/4/server/load_balancing","h":"#nginx-configuration","p":2103},{"i":2108,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}#server {# listen 80;# server_name centrifugo.example.com;# rewrite ^(.*) https://$server_name$1 permanent;#}server { server_name centrifugo.example.com; listen 80; #listen 443 ssl; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_certificate /etc/nginx/ssl/server.crt; #ssl_certificate_key /etc/nginx/ssl/server.key; include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1000; gzip_proxied any; # Only retry if there was a communication error, not a timeout # on the Centrifugo server (to avoid propagating \"queries of death\" # to all frontends) proxy_next_upstream error; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; location /connection { proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location / { proxy_pass http://centrifugo; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }}","s":"Separate domain for Centrifugo","u":"/docs/4/server/load_balancing","h":"#separate-domain-for-centrifugo","p":2103},{"i":2110,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}server { # ... your web site Nginx config location /centrifugo/ { rewrite ^/centrifugo/(.*) /$1 break; proxy_pass http://centrifugo; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } location /centrifugo/connection { rewrite ^/centrifugo(.*) $1 break; proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}","s":"Embed to a location of web site","u":"/docs/4/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":2103},{"i":2112,"t":"You may also need to update worker_connections option of Nginx: events { worker_connections 65535;}","s":"worker_connections","u":"/docs/4/server/load_balancing","h":"#worker_connections","p":2103},{"i":2114,"t":"On this page","s":"Online presence","u":"/docs/4/server/presence","h":"","p":2113},{"i":2116,"t":"Online presence provides an instantaneous snapshot of users currently connected to a specific channel. This information includes the user's unique ID and the connection timestamp.","s":"Overview","u":"/docs/4/server/presence","h":"#overview","p":2113},{"i":2118,"t":"To enable Online Presence, you need to set the presence option to true for the specific channel in your server configuration. { \"namespaces\": [{ \"namespace\": \"public\", \"presence\": true }]} After enabling this you can query presence information over server HTTP/GRPC presence call: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey YOUR_API_KEY\" \\ --request POST \\ --data '{\"method\": \"presence\", \"params\": {\"channel\": \"public:test\"}}' \\ http://localhost:8000/api See description of presence API.","s":"Enabling Online Presence","u":"/docs/4/server/presence","h":"#enabling-online-presence","p":2113},{"i":2120,"t":"Once presence enabled, you can retrieve the presence information on the client side too by calling the presence method on the channel. To do this you need to give the client permission to call presence. Once done, presence may be retrieved from the subscription: const presenceData = await subscription.presence(channel); It's also available on the top-level of the client (for example, if you need to call presence for server-side subscription): const presenceData = await client.presence(channel); If the permission check has passed successfully – both methods will return an object containing information about currently subscribed clients.","s":"Retrieving presence on the client side","u":"/docs/4/server/presence","h":"#retrieving-presence-on-the-client-side","p":2113},{"i":2122,"t":"Online Presence feature also allows real-time tracking of users joining or leaving a channel by subscribing to join and leave events: subscription.on('join', function(joinCtx) { console.log('client joined:', joinCtx);});subscription.on('leave', function(leaveCtx) { console.log('client left:', leaveCtx);}); And the same on client top-level for the needs of server-side subscriptions (analogous to the presence call described above). These events provide real-time updates and can be used to keep track of user activity and manage live interactions.","s":"Join and leave events","u":"/docs/4/server/presence","h":"#join-and-leave-events","p":2113},{"i":2124,"t":"The Online Presence feature might increase the load on your Centrifugo server, since Centrifugo need to maintain an addition data structure. Therefore, it is recommended to use this feature judiciously based on your server's capability and the necessity of real-time presence data in your application. Always make sure to secure the presence data, as it could expose sensitive information about user activity in your application. Ensure appropriate security measures are in place. Join and leave events delivered with at most once guarantee. See more about presence design in design overview chapter. Also check out FAQ.","s":"Implementation notes","u":"/docs/4/server/presence","h":"#implementation-notes","p":2113},{"i":2126,"t":"The Online Presence feature of Centrifugo is a highly useful tool for real-time applications. It provides instant and live data about user activity, which can be critical for interactive features like chats, collaborative tools, multiplayer games, or live tracking systems. Make sure to configure and use this feature appropriately to get the most out of your real-time application.","s":"Conclusion","u":"/docs/4/server/presence","h":"#conclusion","p":2113},{"i":2128,"t":"On this page","s":"Server-side subscriptions","u":"/docs/4/server/server_subs","h":"","p":2127},{"i":2130,"t":"See subscribe and unsubscribe server API","s":"Dynamic server-side subscriptions","u":"/docs/4/server/server_subs","h":"#dynamic-server-side-subscriptions","p":2127},{"i":2132,"t":"It's possible to automatically subscribe a user to a personal server-side channel. To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel. For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above. As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default \"\") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.","s":"Automatic personal channel subscription","u":"/docs/4/server/server_subs","h":"#automatic-personal-channel-subscription","p":2127},{"i":2134,"t":"Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes. user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect). This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel. Example config: config.json { \"user_subscribe_to_personal\": true, \"user_personal_single_connection\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} note Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.","s":"Maintain single user connection","u":"/docs/4/server/server_subs","h":"#maintain-single-user-connection","p":2127},{"i":2136,"t":"On this page","s":"Proxy events to the backend","u":"/docs/4/server/proxy","h":"","p":2135},{"i":2138,"t":"HTTP proxy in Centrifugo converts client connection events into HTTP calls to the application backend.","s":"HTTP proxy","u":"/docs/4/server/proxy","h":"#http-proxy","p":2135},{"i":2140,"t":"All proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).","s":"HTTP request structure","u":"/docs/4/server/proxy","h":"#http-request-structure","p":2135},{"i":2142,"t":"The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example: config.json { ... \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} Alternatively, you can set a list of headers via an environment variable (space separated): export CENTRIFUGO_PROXY_HTTP_HEADERS=\"Cookie User-Agent X-B3-TraceId X-B3-SpanId\" ./centrifugo note Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend.","s":"Proxy HTTP headers","u":"/docs/4/server/proxy","h":"#proxy-http-headers","p":2135},{"i":2144,"t":"When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied. See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.","s":"Proxy GRPC metadata","u":"/docs/4/server/proxy","h":"#proxy-grpc-metadata","p":2135},{"i":2146,"t":"With the following options in the configuration file: { ... \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_connect_timeout\": \"1s\"} – connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request. danger Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections. Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. In many cases your existing session mechanism will provide user authentication details to the connect proxy handler. tip If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server. This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired). Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\"} Expected response example: {\"result\": {\"user\": \"56\"}} This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result. Several app examples which use connect proxy can be found in our blog: With NodeJS With Django With Laravel Connect request fields​ This is what sent from Centrifugo to application backend in case of connect proxy request. Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) name string yes optional name of the client (this field will only be set if provided by a client on connect) version string yes optional version of the client (this field will only be set if provided by a client on connect) data JSON yes optional data from client (this field will only be set if provided by a client on connect) b64data string yes optional data from the client in base64 format (if the binary proxy mode is used) channels Array of strings yes list of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result Connect result fields​ This is what application returns to Centrifugo inside result field in case of connect proxy request. Field Type Optional Description user string no user ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests expire_at integer yes a timestamp when connection must be considered expired. If not set or set to 0 connection won't expire at all info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages data JSON yes a custom data to send to the client in connect command response. b64data string yes a custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client channels array of strings yes allows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions subs map of SubscribeOptions yes map of channels with options to subscribe connection to. See more details about server-side subscriptions meta JSON object (ex. {\"key\": \"value\"}) yes a custom data to attach to connection (this won't be exposed to client-side) Options​ proxy_connect_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. Example​ Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return \"56\" as user ID): class CentrifugoConnectHandler(tornado.web.RequestHandler): def check_xsrf_cookie(self): pass def post(self): self.set_header('Content-Type', 'application/json; charset=\"utf-8\"') data = json.dumps({ 'result': { 'user': '56' } }) self.write(data)def main(): options.parse_command_line() app = tornado.web.Application([ (r'/centrifugo/connect', CentrifugoConnectHandler), ]) app.listen(3000) tornado.ioloop.IOLoop.instance().start()if __name__ == '__main__': main() This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side. We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea. What if connection is unauthenticated/unauthorized to connect?​ In this case return a disconnect object as a response. See Return custom disconnect section. Depending on whether you want connection to reconnect or not (usually not) you can select the appropriate disconnect code. Sth like this in response: { \"disconnect\": { \"code\": 4501, \"reason\": \"unauthorized\" }} – may be sufficient enough. Choosing codes and reason is up to the developer, but follow the rules described in Return custom disconnect section.","s":"Connect proxy","u":"/docs/4/server/proxy","h":"#connect-proxy","p":2135},{"i":2148,"t":"With the following options in the configuration file: { ... \"proxy_refresh_endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"proxy_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side. The payload sent to app backend in refresh request (when the connection is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Refresh proxy","u":"/docs/4/server/proxy","h":"#refresh-proxy","p":2135},{"i":2150,"t":"With the following option in the configuration file: { ... \"proxy_rpc_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_rpc_timeout\": \"1s\"} RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket (or SockJS) connection in a bidirectional way. Payload example sent to app backend in RPC request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"method\": \"getCurrentPrice\", \"data\":{\"params\": {\"object_id\": 12}}} Expected response example: {\"result\": {\"data\": {\"answer\": \"2019\"}}} RPC request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process method string yes an RPC method string, if the client does not use named RPC call then method will be omitted data JSON yes RPC custom data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) RPC result fields​ Field Type Optional Description data JSON yes RPC response - any valid JSON is supported b64data string yes can be set instead of data for binary response encoded in base64 format Options​ proxy_rpc_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. See below on how to return a custom error.","s":"RPC proxy","u":"/docs/4/server/proxy","h":"#rpc-proxy","p":2135},{"i":2152,"t":"With the following option in the configuration file: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\"} – subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel. tip Subscribe proxy does not proxy private and user-limited channels at the moment. That's because those are already providing a level of security (user-limited channels check current user ID, private channels require subscription token). In some cases you may use subscribe proxy as a replacement for private channels actually: if you prefer to check permissions using the proxy to backend mechanism – just stop using $ prefixes in channels, properly configure subscribe proxy and validate subscriptions upon proxy from Centrifugo to your backend (issued each time user tries to subscribe on a channel for which subscribe proxy enabled). Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace. So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} Or for channels in namespace sun: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_subscribe\": true }]} Payload example sent to app backend in subscribe request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\"} Expected response example if subscription is allowed: {\"result\": {}} Subscribe request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to subscribe to meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) data JSON yes custom data from client sent with subscription request (this field will only be set if provided by a client on subscribe). b64data string yes optional subscription data from the client in base64 format (if the binary proxy mode is used). Subscribe result fields​ Field Type Optional Description info JSON yes a channel info JSON b64info string yes a binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using data JSON yes a custom data to send to the client in subscribe command reply. b64data string yes a custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} See below on how to return an error in case you don't want to allow subscribing. Options​ proxy_subscribe_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. What if connection is not allowed to subscribe?​ In this case you can return error object as a subscribe handler response. See return custom error section. In general, frontend applications should not try to subscribe to channels for which access is not allowed. But these situations can happen or malicious user can try to subscribe to a channel. In most scenarios returning: { \"error\": { \"code\": 403, \"message\": \"permission denied\" }} – is sufficient enough. Error code may be not 403 actually, no real reason to force HTTP semantics here - so it's up to Centrifugo user to decide. Just keep it in range [400, 1999] as described here. If case of returning response above, on client side unsubscribed event of Subscription object will be called with error code 403. Subscription won't resubscribe automatically after that.","s":"Subscribe proxy","u":"/docs/4/server/proxy","h":"#subscribe-proxy","p":2135},{"i":2154,"t":"With the following option in the configuration file: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\"} – publish calls sent by a client will be proxied to proxy_publish_endpoint. This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side. Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too. So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"publish\": true, \"proxy_publish\": true} Or for channels in namespace sun: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"publish\": true, \"proxy_publish\": true }]} Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace). Payload example sent to app backend in a publish request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\", \"data\":{\"input\":\"hello\"}} Expected response example if publish is allowed: {\"result\": {}} Publish request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to publish to data JSON yes data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Publish result fields​ Field Type Optional Description data JSON yes an optional JSON data to send into a channel instead of original data sent by a client b64data string yes a binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing skip_history bool yes when set to true Centrifugo won't save publication to the channel history See below on how to return an error in case you don't want to allow publishing. Options​ proxy_publish_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Publish proxy","u":"/docs/4/server/proxy","h":"#publish-proxy","p":2135},{"i":2156,"t":"Added in Centrifugo v4.1.1 With the following options in the configuration file: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_sub_refresh_endpoint when it's time to refresh the subscription. Centrifugo itself will ask your backend about subscription validity instead of subscription refresh workflow on the client-side. Like subscribe and publish proxy types, sub refresh proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_sub_refresh that enables sub refresh proxy for channels in the namespace. Only subscriptions which have expiration time will be validated over sub refresh proxy endpoint. Sub refresh proxy may be used as a periodical Subscription liveness callback from Centrifugo to app backend. caution In the current implementation the delay of Subscription refresh requests from Centrifugo to application backend may be up to one minute (was implemented this way from a simplicity and efficiency perspective). We assume this should be enough for many scenarios. But this may be improved if needed. Please reach us out with a detailed description of your use case where you want more accurate requests to refresh subscriptions. So to enable sub refresh proxy for channels without namespace define proxy_sub_refresh on a top configuration level: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh\": true} Or for channels in namespace sun: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_sub_refresh\": true }]} The payload sent to app backend in sub refresh request (when the subscription is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"channel\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Sub refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no channel for which Subscription is going to expire meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Sub refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a channel info JSON b64info string yes binary channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_sub_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Sub refresh proxy","u":"/docs/4/server/proxy","h":"#sub-refresh-proxy","p":2135},{"i":2158,"t":"Application backend can return JSON object that contains an error to return it to the client: { \"error\": { \"code\": 1000, \"message\": \"custom error\" }} Applications must use error codes in range [400, 1999]. Error code field is uint32 internally. note Returning custom error does not apply to response for refresh and sub refresh proxy requests as there is no sense in returning an error (will not reach client anyway).","s":"Return custom error","u":"/docs/4/server/proxy","h":"#return-custom-error","p":2135},{"i":2160,"t":"Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way: { \"disconnect\": { \"code\": 4500, \"reason\": \"disconnect reason\" }} Application must use numbers in the range 4000-4999 for custom disconnect codes: codes in range [4000, 4499] give client an advice to reconnect codes in range [4500, 4999] are terminal codes – client won't reconnect upon receiving it. Code is uint32 internally. Numbers outside of 4000-4999 range are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 ASCII symbols (i.e. 32 bytes max). note Returning custom disconnect does not apply to response for refresh and sub refresh proxy requests as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason.","s":"Return custom disconnect","u":"/docs/4/server/proxy","h":"#return-custom-disconnect","p":2135},{"i":2162,"t":"Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server. GRPC service definitions can be found in the Centrifugo repository: proxy.proto. tip GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions. Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept). All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy: config.json { ... \"proxy_connect_endpoint\": \"grpc://localhost:12000\", \"proxy_connect_timeout\": \"1s\"} Refresh proxy: config.json { ... \"proxy_refresh_endpoint\": \"grpc://localhost:12000\", \"proxy_refresh_timeout\": \"1s\"} Or for RPC proxy: config.json { ... \"proxy_rpc_endpoint\": \"grpc://localhost:12000\", \"proxy_rpc_timeout\": \"1s\"} For publish proxy in namespace chat: config.json { ... \"proxy_publish_endpoint\": \"grpc://localhost:12000\", \"proxy_publish_timeout\": \"1s\" \"namespaces\": [ { \"name\": \"chat\", \"publish\": true, \"proxy_publish\": true } ]} Use subscribe proxy for all channels without namespaces: config.json { ... \"proxy_subscribe_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} So the same as for HTTP, just the different endpoint scheme.","s":"GRPC proxy","u":"/docs/4/server/proxy","h":"#grpc-proxy","p":2135},{"i":2164,"t":"Some additional options exist to control GRPC proxy behavior. proxy_grpc_cert_file​ String, default: \"\". Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. proxy_grpc_credentials_key​ String, default \"\" (i.e. not used). Add custom key to per-RPC credentials. proxy_grpc_credentials_value​ String, default \"\" (i.e. not used). A custom value for proxy_grpc_credentials_key.","s":"GRPC proxy options","u":"/docs/4/server/proxy","h":"#grpc-proxy-options","p":2135},{"i":2166,"t":"We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.: Copy proxy Protobuf definitions Generate GRPC code Run backend service with you custom business logic Point Centrifugo to it.","s":"GRPC proxy example","u":"/docs/4/server/proxy","h":"#grpc-proxy-example","p":2135},{"i":2168,"t":"Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios: Client protocol type Proxy type Client headers Client metadata HTTP HTTP In proxy request headers N/A GRPC GRPC N/A In proxy request metadata HTTP GRPC In proxy request metadata N/A GRPC HTTP N/A In proxy request headers","s":"Header proxy rules","u":"/docs/4/server/proxy","h":"#header-proxy-rules","p":2135},{"i":2170,"t":"As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding. Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this. Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side). But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON. In this case it's possible to turn Centrifugo proxy into binary mode by using: config.json { ... \"proxy_binary_encoding\": true} Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues. While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine. Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.","s":"Binary mode","u":"/docs/4/server/proxy","h":"#binary-mode","p":2135},{"i":2172,"t":"By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture). Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.","s":"Granular proxy mode","u":"/docs/4/server/proxy","h":"#granular-proxy-mode","p":2135},{"i":2174,"t":"Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below. config.json { ... \"granular_proxy_mode\": true}","s":"Enable granular proxy mode","u":"/docs/4/server/proxy","h":"#enable-granular-proxy-mode","p":2135},{"i":2176,"t":"When using granular proxy mode on configuration top level you can define \"proxies\" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint. Here is an example: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"connect\", \"endpoint\": \"http://localhost:3000/centrifugo/connect\", \"timeout\": \"500ms\", \"http_headers\": [\"Cookie\"] }, { \"name\": \"refresh\", \"endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"timeout\": \"500ms\" }, { \"name\": \"subscribe1\", \"endpoint\": \"http://localhost:3001/centrifugo/subscribe\" }, { \"name\": \"publish1\", \"endpoint\": \"http://localhost:3001/centrifugo/publish\" }, { \"name\": \"rpc1\", \"endpoint\": \"http://localhost:3001/centrifugo/rpc\" }, { \"name\": \"subscribe2\", \"endpoint\": \"http://localhost:3002/centrifugo/subscribe\" }, { \"name\": \"publish2\", \"endpoint\": \"grpc://localhost:3002\" } { \"name\": \"rpc2\", \"endpoint\": \"grpc://localhost:3002\" } ]} Let's look at all fields for a proxy object which is possible to set for each proxy inside \"proxies\" array. Field name Field type Required Description name string yes Unique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$ endpoint string yes HTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC. timeout duration (string) no Proxy request timeout, default \"1s\" http_headers array of strings no List of headers to proxy, by default no headers grpc_metadata array of strings no List of GRPC metadata keys to proxy, by default no metadata keys binary_encoding bool no Use base64 for payloads include_connection_meta bool no Include meta information (attached on connect) grpc_cert_file string no Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. grpc_credentials_key string no Add custom key to per-RPC credentials. grpc_credentials_value string no A custom value for grpc_credentials_key.","s":"Defining a list of proxies","u":"/docs/4/server/proxy","h":"#defining-a-list-of-proxies","p":2135},{"i":2178,"t":"As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event. To enable connect proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\"} We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea. Let's also add refresh proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\", \"refresh_proxy_name\": \"refresh\"}","s":"Granular connect and refresh","u":"/docs/4/server/proxy","h":"#granular-connect-and-refresh","p":2135},{"i":2180,"t":"Subscribe, publish and sub refresh proxies work per-namespace. This means that subscribe_proxy_name, publish_proxy_name and sub_refresh_proxy_name are just channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object. config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_proxy_name\": \"subscribe1\", \"publish\": true, \"publish_proxy_name\": \"publish1\" }, { \"name\": \"ns2\", \"subscribe_proxy_name\": \"subscribe2\", \"publish\": true, \"publish_proxy_name\": \"publish2\" } ]} If namespace does not have \"subscribe_proxy_name\" or \"subscribe_proxy_name\" is empty then no subscribe proxy will be used for a namespace. If namespace does not have \"publish_proxy_name\" or \"publish_proxy_name\" is empty then no publish proxy will be used for a namespace. If namespace does not have \"sub_refresh_proxy_name\" or \"sub_refresh_proxy_name\" is empty then no sub refresh proxy will be used for a namespace. tip You can define subscribe_proxy_name, publish_proxy_name, sub_refresh_proxy_name on configuration top level – and in this case publish, subscribe and sub refresh requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.","s":"Granular subscribe, publish, sub refresh","u":"/docs/4/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":2135},{"i":2182,"t":"Analogous to channel namespaces it's possible to configure rpc namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [...], \"rpc_namespaces\": [ { \"name\": \"rpc_ns1\", \"rpc_proxy_name\": \"rpc1\", }, { \"name\": \"rpc_ns2\", \"rpc_proxy_name\": \"rpc2\" } ]} The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces). Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start. tip The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.","s":"Granular RPC","u":"/docs/4/server/proxy","h":"#granular-rpc","p":2135},{"i":2184,"t":"On this page","s":"Server API walkthrough","u":"/docs/4/server/server_api","h":"","p":2183},{"i":2186,"t":"Server HTTP API works on /api endpoint (by default). It has a simple request format: this is an HTTP POST request with application/json Content-Type and with JSON command body. Here we will look at available command methods and parameters. tip In some cases, you can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests.","s":"HTTP API","u":"/docs/4/server/server_api","h":"#http-api","p":2183},{"i":2188,"t":"HTTP API is protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like: config.json { ... \"api_key\": \"\"} This API key must be set in the request Authorization header in this way: Authorization: apikey It's also possible to pass API key over URL query param. This solves some edge cases where it's not possible to use the Authorization header. Simply add ?api_key= query param to the API endpoint. Keep in mind that passing the API key in the Authorization header is a recommended way. It's possible to disable API key check on Centrifugo side using the api_insecure configuration option. Be sure to protect the API endpoint by firewall rules in this case – to prevent anyone on the internet to send commands over your unprotected Centrifugo API endpoint. API key auth is not very safe for man-in-the-middle so we also recommended running Centrifugo with TLS. A command is a JSON object with two properties: method and params. method is the name of the API command you want to call. params is an object with command arguments. Each method can have its own params Before looking at all available commands here is a CURL that calls info command: curl --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"info\", \"params\": {}}' \\ http://localhost:8000/api Here is a live example: Your browser does not support the video tag. Now let's investigate each API method in detail.","s":"HTTP API authorization","u":"/docs/4/server/server_api","h":"#http-api-authorization","p":2183},{"i":2190,"t":"Publish command allows publishing data into a channel (we call this message publication in Centrifugo). Most probably this is a command you'll use most of the time. It looks like this: { \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } }} Let's apply all information said above and send publish command to Centrifugo. We will send a request using the requests library for Python. import jsonimport requestscommand = { \"method\": \"publish\", \"params\": { \"channel\": \"docs\", \"data\": { \"content\": \"1\" } }}api_key = \"YOUR_API_KEY\"data = json.dumps(command)headers = {'Content-type': 'application/json', 'Authorization': 'apikey ' + api_key}resp = requests.post(\"https://centrifuge.example.com/api\", data=data, headers=headers)print(resp.json()) The same using httpie console tool: echo '{\"method\": \"publish\", \"params\": {\"channel\": \"chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \" -vvvPOST /api HTTP/1.1Accept: application/json, */*Accept-Encoding: gzip, deflateAuthorization: apikey KEYConnection: keep-aliveContent-Length: 80Content-Type: application/jsonHost: localhost:8000User-Agent: HTTPie/0.9.8{ \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } }}HTTP/1.1 200 OKContent-Length: 3Content-Type: application/jsonDate: Thu, 17 May 2018 22:01:42 GMT{ \"result\": {}} In case of error response object can contain error field. For example, let's publish to a channel with unknown namespace: echo '{\"method\": \"publish\", \"params\": {\"channel\": \"unknown:chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \"HTTP/1.1 200 OKContent-Length: 55Content-Type: application/jsonDate: Thu, 17 May 2018 22:03:09 GMT{ \"error\": { \"code\": 102, \"message\": \"namespace not found\" }} error object contains error code and message - this is also the same for other commands described below. Publish params​ Parameter name Parameter type Required Description channel string yes Name of channel to publish data any JSON yes Custom JSON data to publish into a channel skip_history bool no Skip adding publication to history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Publish result​ Field name Field type Optional Description offset integer yes Offset of publication in history stream epoch string yes Epoch of current stream","s":"publish","u":"/docs/4/server/server_api","h":"#publish","p":2183},{"i":2192,"t":"Similar to publish but allows to send the same data into many channels. { \"method\": \"broadcast\", \"params\": { \"channels\": [\"CHANNEL_1\", \"CHANNEL_2\"], \"data\": { \"text\": \"hello\" } }} Broadcast params​ Parameter name Parameter type Required Description channels Array of strings yes List of channels to publish data to data any JSON yes Custom JSON data to publish into each channel skip_history bool no Skip adding publications to channels' history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Broadcast result​ Field name Field type Optional Description responses Array of publish responses no Responses for each individual publish (with possible error and publish result)","s":"broadcast","u":"/docs/4/server/server_api","h":"#broadcast","p":2183},{"i":2194,"t":"subscribe allows subscribing user to a channel. Subscribe params​ Parameter name Parameter type Required Description user string yes User ID to subscribe channel string yes Name of channel to subscribe user to info any JSON no Attach custom data to subscription (will be used in presence and join/leave messages) b64info string no info in base64 for binary mode (will be decoded by Centrifugo) client string no Specific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs) session string no Specific client session to subscribe (user still required to be set) data any JSON no Custom subscription data (will be sent to client in Subscribe push) b64data string no Same as data but in base64 format (will be decoded by Centrifugo) recover_since StreamPosition object no Stream position to recover from override Override object no Allows dynamically override some channel options defined in Centrifugo configuration (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} Subscribe result​ Empty object at the moment.","s":"subscribe","u":"/docs/4/server/server_api","h":"#subscribe","p":2183},{"i":2196,"t":"unsubscribe allows unsubscribing user from a channel. { \"method\": \"unsubscribe\", \"params\": { \"channel\": \"CHANNEL NAME\", \"user\": \"USER ID\" }} Unsubscribe params​ Parameter name Parameter type Required Description user string yes User ID to unsubscribe channel string yes Name of channel to unsubscribe user to client string no Specific client ID to unsubscribe (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Unsubscribe result​ Empty object at the moment.","s":"unsubscribe","u":"/docs/4/server/server_api","h":"#unsubscribe","p":2183},{"i":2198,"t":"disconnect allows disconnecting a user by ID. { \"method\": \"disconnect\", \"params\": { \"user\": \"USER ID\" }} Disconnect params​ Parameter name Parameter type Required Description user string yes User ID to disconnect client string no Specific client ID to disconnect (user still required to be set) session string no Specific client session to disconnect (user still required to be set). whitelist Array of strings no Array of client IDs to keep disconnect Disconnect object no Provide custom disconnect object, see below Disconnect object​ Field name Field type Required Description code int yes Disconnect code reason string yes Disconnect reason Disconnect result​ Empty object at the moment.","s":"disconnect","u":"/docs/4/server/server_api","h":"#disconnect","p":2183},{"i":2200,"t":"refresh allows refreshing user connection (mostly useful when unidirectional transports are used). Refresh params​ Parameter name Parameter type Required Description user string yes User ID to refresh client string no Client ID to refresh (user still required to be set) session string no Specific client session to refresh (user still required to be set). expired bool no Mark connection as expired and close with Disconnect Expired reason expire_at int no Unix time (in seconds) in the future when the connection will expire Refresh result​ Empty object at the moment.","s":"refresh","u":"/docs/4/server/server_api","h":"#refresh","p":2183},{"i":2202,"t":"presence allows getting channel online presence information (all clients currently subscribed on this channel). tip Presence in channels is not enabled by default. See how to enable it over channel options. Also check out dedicated chapter about it. { \"method\": \"presence\", \"params\": { \"channel\": \"chat\" }} Example: fz@centrifugo: echo '{\"method\": \"presence\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 127Content-Type: application/jsonDate: Thu, 17 May 2018 22:13:17 GMT{ \"result\": { \"presence\": { \"c54313b2-0442-499a-a70c-051f8588020f\": { \"client\": \"c54313b2-0442-499a-a70c-051f8588020f\", \"user\": \"42\" }, \"adad13b1-0442-499a-a70c-051f858802da\": { \"client\": \"adad13b1-0442-499a-a70c-051f858802da\", \"user\": \"42\" } } }} Presence params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence result​ Field name Field type Optional Description presence Map of client ID (string) to ClientInfo object no Offset of publication in history stream ClientInfo​ Field name Field type Optional Description client string no Client ID user string no User ID conn_info JSON yes Optional connection info chan_info JSON yes Optional channel info","s":"presence","u":"/docs/4/server/server_api","h":"#presence","p":2183},{"i":2204,"t":"presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID). { \"method\": \"presence_stats\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"presence_stats\", \"params\": {\"channel\": \"public:chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": { \"num_clients\": 0, \"num_users\": 0 }} Presence stats params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence stats result​ Field name Field type Optional Description num_clients integer no Total number of clients in channel num_users integer no Total number of unique users in channel","s":"presence_stats","u":"/docs/4/server/server_api","h":"#presence_stats","p":2183},{"i":2206,"t":"history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter. tip History in channels is not enabled by default. See how to enable it over channel options. { \"method\": \"history\", \"params\": { \"channel\": \"chat\", \"limit\": 2 }} Example: echo '{\"method\": \"history\", \"params\": {\"channel\": \"chat\", \"limit\": 2}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 129Content-Type: application/jsonDate: Wed, 21 Jul 2021 05:30:48 GMT{ \"result\": { \"epoch\": \"qFhv\", \"offset\": 4, \"publications\": [ { \"data\": { \"text\": \"hello\" }, \"offset\": 2 }, { \"data\": { \"text\": \"hello\" }, \"offset\": 3 } ] }} History params​ Parameter name Parameter type Required Description channel string yes Name of channel to call history from limit int no Limit number of returned publications, if not set in request then only current stream position information will present in result (without any publications) since StreamPosition object no To return publications after this position reverse bool no Iterate in reversed order (from latest to earliest) StreamPosition​ Field name Field type Required Description offset integer yes Offset in a stream epoch string yes Stream epoch History result​ Field name Field type Optional Description publications Array of publication objects yes List of publications in channel offset integer yes Top offset in history stream epoch string yes Epoch of current stream","s":"history","u":"/docs/4/server/server_api","h":"#history","p":2183},{"i":2208,"t":"history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state. { \"method\": \"history_remove\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"history_remove\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": {}} History remove params​ Parameter name Parameter type Required Description channel string yes Name of channel to remove history History remove result​ Empty object at the moment.","s":"history_remove","u":"/docs/4/server/server_api","h":"#history_remove","p":2183},{"i":2210,"t":"channels return active channels (with one or more active subscribers in it). { \"method\": \"channels\", \"params\": {}} Channels params​ Parameter name Parameter type Required Description pattern string no Pattern to filter channels, we are using gobwas/glob library for matching Channels result​ Field name Field type Optional Description channels Map of string to ChannelInfo no Map where key is channel and value is ChannelInfo (see below) ChannelInfo​ Field name Field type Optional Description num_clients integer no Total number of connections currently subscribed to a channel caution Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels API call will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.","s":"channels","u":"/docs/4/server/server_api","h":"#channels","p":2183},{"i":2212,"t":"info method allows getting information about running Centrifugo nodes. Example: echo '{\"method\": \"info\", \"params\": {}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 184Content-Type: application/jsonDate: Thu, 17 May 2018 22:07:58 GMT{ \"result\": { \"nodes\": [ { \"name\": \"Alexanders-MacBook-Pro.local_8000\", \"num_channels\": 0, \"num_clients\": 0, \"num_users\": 0, \"uid\": \"f844a2ed-5edf-4815-b83c-271974003db9\", \"uptime\": 0, \"version\": \"\" } ] }} Info params​ Empty object at the moment. Info result​ Field name Field type Optional Description nodes Array of Node objects no Information about all nodes in a cluster","s":"info","u":"/docs/4/server/server_api","h":"#info","p":2183},{"i":2214,"t":"It's possible to combine several commands into one request to Centrifugo. To do this use JSON streaming format. This can improve server throughput and reduce traffic traveling around. Example: curl --header \"Authorization: apikey \" \\ --request POST \\ --data $'{\"method\": \"publish\", \"params\": {\"channel\": \"test1\", \"data\": {\"test\": 1}}}\\n{\"method\": \"publish\", \"params\": {\"channel\": \"test2\", \"data\": {\"test\": 2}}}' \\ http://localhost:8000/api{\"result\":{}}{\"result\":{}} Note that with CURL we had to use $ to properly send new line \\n character in data.","s":"Command pipelining","u":"/docs/4/server/server_api","h":"#command-pipelining","p":2183},{"i":2216,"t":"Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header. But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually: cent for Python phpcent for PHP gocent for Go rubycent for Ruby Also, there are API libraries created by community: crystalcent API client for Crystal language cent.js API client for NodeJS Centrifugo.AspNetCore API client for ASP.NET Core tip Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.","s":"HTTP API libraries","u":"/docs/4/server/server_api","h":"#http-api-libraries","p":2183},{"i":2218,"t":"Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC. GRPC API is also useful if you want to publish binary data to Centrifugo channels. tip GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description. You can enable GRPC API in Centrifugo using grpc_api option: config.json { ... \"grpc_api\": true} By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option. Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file. Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.","s":"GRPC API","u":"/docs/4/server/server_api","h":"#grpc-api","p":2183},{"i":2220,"t":"For example for Python you need to run sth like this according to GRPC docs: pip install grpcio-toolspython -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo: import grpcimport api_pb2_grpc as api_grpcimport api_pb2 as api_pbchannel = grpc.insecure_channel('localhost:10000')stub = api_grpc.CentrifugoApiStub(channel)try: resp = stub.Info(api_pb.InfoRequest())except grpc.RpcError as err: # GRPC level error. print(err.code(), err.details())else: if resp.error.code: # Centrifugo server level error. print(resp.error.code, resp.error.message) else: print(resp.result) Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.","s":"GRPC example for Python","u":"/docs/4/server/server_api","h":"#grpc-example-for-python","p":2183},{"i":2222,"t":"Here is a simple example of how to run Centrifugo with the GRPC Go client. You need protoc, protoc-gen-go and protoc-gen-go-grpc installed. First start Centrifugo itself with GRPC API enabled: CENTRIFUGO_GRPC_API=1 centrifugo --config config.json In another terminal tab: mkdir centrifugo_grpc_examplecd centrifugo_grpc_example/touch main.gogo mod init centrifugo_examplemkdir apiprotocd apiprotowget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto Run protoc to generate code: protoc -I ./ api.proto --go_out=. --go-grpc_out=. Put the following code to main.go file (created on the last step above): package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure()) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoApiClient(conn) for { resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} Then run: go run main.go The program starts and periodically publishes the same payload into chat:index channel.","s":"GRPC example for Go","u":"/docs/4/server/server_api","h":"#grpc-example-for-go","p":2183},{"i":2224,"t":"You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey . For example in Go language: package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")type keyAuth struct { key string}func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{ \"authorization\": \"apikey \" + t.key, }, nil}func (t keyAuth) RequireTransportSecurity() bool { return false}func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{\"xxx\"})) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoClient(conn) for { resp, err := client.Publish(context.Background(), &PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} For other languages refer to GRPC docs.","s":"GRPC API key authorization","u":"/docs/4/server/server_api","h":"#grpc-api-key-authorization","p":2183},{"i":2226,"t":"On this page","s":"Configure TLS","u":"/docs/4/server/tls","h":"","p":2225},{"i":2228,"t":"In first way you already have cert and key files. For development you can create self-signed certificate - see this instruction as example. config.json { ... \"tls\": true, \"tls_key\": \"server.key\", \"tls_cert\": \"server.crt\"} And run: ./centrifugo --config=config.json","s":"Using crt and key files","u":"/docs/4/server/tls","h":"#using-crt-and-key-files","p":2225},{"i":2230,"t":"For automatic certificates from Let's Encrypt add into configuration file: config.json { ... \"tls_autocert\": true, \"tls_autocert_host_whitelist\": \"www.example.com\", \"tls_autocert_cache_dir\": \"/tmp/certs\", \"tls_autocert_email\": \"user@example.com\", \"tls_autocert_http\": true, \"tls_autocert_http_addr\": \":80\"} tls_autocert (boolean) says Centrifugo that you want automatic certificate handling using ACME provider. tls_autocert_host_whitelist (string) is a string with your app domain address. This can be comma-separated list. It's optional but recommended for extra security. tls_autocert_cache_dir (string) is a path to a folder to cache issued certificate files. This is optional but will increase performance. tls_autocert_email (string) is optional - it's an email address ACME provider will send notifications about problems with your certificates. tls_autocert_http (boolean) is an option to handle http_01 ACME challenge on non-TLS port. tls_autocert_http_addr (string) can be used to set address for handling http_01 ACME challenge (default is :80) When configured correctly and your domain is valid (localhost will not work) - certificates will be retrieved on first request to Centrifugo. Also Let's Encrypt certificates will be automatically renewed. There are two options that allow Centrifugo to support TLS client connections from older browsers such as Chrome 49 on Windows XP and IE8 on XP: tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys. tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints. uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.","s":"Automatic certificates","u":"/docs/4/server/tls","h":"#automatic-certificates","p":2225},{"i":2232,"t":"You can provide custom certificate files to configure TLS for GRPC API server. grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server","s":"TLS for GRPC API","u":"/docs/4/server/tls","h":"#tls-for-grpc-api","p":2225},{"i":2234,"t":"You can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint. uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server","s":"TLS for GRPC unidirectional stream","u":"/docs/4/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":2225},{"i":2236,"t":"On this page","s":"Client protocol","u":"/docs/4/transports/client_protocol","h":"","p":2235},{"i":2238,"t":"Centrifugo is built on top of Centrifuge library for Go. Centrifuge library uses its own framing for wrapping Centrifuge-specific messages – synchronous commands from a client to a server (which expect replies from a server) and asynchronous pushes. Centrifuge client protocol is defined by a Protobuf schema. This is the source of truth. tip At the moment Protobuf schema contains some fields which are only used in client protocol v1. This is for backwards compatibility – server supports clients connecting over both client protocol v2 and client protocol v1. Client protocol v1 is considered deprecated and will be removed at some point in the future (giving enough time to our users to migrate).","s":"Protobuf schema","u":"/docs/4/transports/client_protocol","h":"#protobuf-schema","p":2235},{"i":2240,"t":"In bidirectional case client sends Command to a server and server sends Reply to a client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages. Each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous transport where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before on SDK level. In JSON case client can send command like this: {\"id\": 1, \"subscribe\": {\"channel\": \"example\"}} And client can expect something like this in response: {\"id\": 1, \"subscribe\": {}} Reply for different commands has corresponding field with command result (\"subscribe\" in example above). Reply can also contain error if Command processing resulted into an error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can handle result object appropriately. error looks like this in JSON case: { \"code\": 100, \"message\": \"internal server error\", \"temporary\": true} I.e. reply with error may look like this: {\"id\": 1, \"error\": {\"code\": 100, \"message\": \"internal server error\"}} We will talk more about error handling below. Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow. Current commands: connect – sent to authenticate connection, sth like hello from a client which can carry authentication token and arbitrary data. subscribe – sent to subscribe to a channel unsubscribe - sent to unsubscribe from a channel publish - sent to publish data into a channel presence - sent to request presence information from a channel presence_stats - sent to request presence stats information from a channel history - sent to request history information for a channel send - sent to send async message to a server (this command is a bit special since it must not contain id - as we don't wait for any response from a server in this case). rpc - sent to send RPC to a channel (execute arbitrary logic and wait for response) refresh - sent to refresh connection token sub_refresh - sent to refresh channel subscription token","s":"Command-Reply","u":"/docs/4/transports/client_protocol","h":"#command-reply","p":2235},{"i":2242,"t":"The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to a client at any moment - not as reaction to issued Command but as a message from a server to a client at arbitrary time. For example, this can be a message published into channel. There are several types of asynchronous messages that can come from a server to a client. pub is a message published into channel join messages sent when someone joined (subscribed on) channel. leave messages sent when someone left (unsubscribed from) channel. unsubscribe message sent when a server unsubscribed current client from a channel: subscribe may be sent when a server subscribes client to a channel. disconnect may be sent be a server before closing connection and contains disconnect code/reason message may be sent when server sends asynchronous message to a client connect push can be sent in unidirectional transport case refresh may be sent when a server refreshes client credentials (useful in unidirectional transports)","s":"Asynchronous pushes","u":"/docs/4/transports/client_protocol","h":"#asynchronous-pushes","p":2235},{"i":2244,"t":"To reduce number of system calls one request from a client to a server and one response from a server to a client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data. When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \\n: {\"id\": 1, \"subscribe\": {\"channel\": \"ch1\"}}{\"id\": 2, \"subscribe\": {\"channel\": \"ch2\"}} Here is an example how we do this in Javascript client when JSON format used: function encodeCommands(commands) { const encodedCommands = []; for (const i in commands) { if (commands.hasOwnProperty(i)) { encodedCommands.push(JSON.stringify(commands[i])); } } return encodedCommands.join('\\n');} info This doc uses JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo. When Protobuf format used then many Command can be sent from a client to a server in a length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example. The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there. tip Server can even send reply to a command and asynchronous message batched together in a one frame. For example here is how we read server response and extracting individual replies in Javascript client when JSON format used: function decodeReplies(data) { const replies = []; const encodedReplies = data.split('\\n'); for (const i in encodedReplies) { if (encodedReplies.hasOwnProperty(i)) { if (!encodedReplies[i]) { continue; } const reply = JSON.parse(encodedReplies[i]); replies.push(reply); } } return replies;} For Protobuf case see existing client implementations for decoding example.","s":"Top level batching","u":"/docs/4/transports/client_protocol","h":"#top-level-batching","p":2235},{"i":2246,"t":"To maintain connection alive and detect broken connections server periodically sends empty commands to clients and expects empty replies from them. When client does not receive ping from a server for some time it can consider connection broken and try to reconnect. Usually a server sends pings every 25 seconds.","s":"Ping Pong","u":"/docs/4/transports/client_protocol","h":"#ping-pong","p":2235},{"i":2248,"t":"Client should handle disconnect advices from server. In websocket case disconnect advice is sent in CLOSE Websocket frame. Disconnect advice contains uint32 code and human-readable string reason.","s":"Handle disconnects","u":"/docs/4/transports/client_protocol","h":"#handle-disconnects","p":2235},{"i":2250,"t":"This section contains advices to error handling in client implementations. Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems. Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again. Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side. The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance. Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).","s":"Handle errors","u":"/docs/4/transports/client_protocol","h":"#handle-errors","p":2235},{"i":2252,"t":"Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in a reply to a subscribe command.","s":"Additional notes","u":"/docs/4/transports/client_protocol","h":"#additional-notes","p":2235},{"i":2254,"t":"On this page","s":"Client real-time SDKs","u":"/docs/4/transports/client_sdk","h":"","p":2253},{"i":2256,"t":"centrifuge-js – for browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-dart - for Dart and Flutter centrifuge-swift – for native iOS development centrifuge-java – for native Android development and general Java See a description of client protocol if you want to write a custom bidirectional connector or eager to learn how Centrifugo protocol internals are structured.","s":"List of client SDKs","u":"/docs/4/transports/client_sdk","h":"#list-of-client-sdks","p":2253},{"i":2258,"t":"Centrifugo real-time SDKs work using two possible serialization formats: JSON and Protobuf. The entire bidirectional client protocol is described by the Protobuf schema. But those Protobuf messages may be also encoded as JSON objects (in JSON representation bytes fields in the Protobuf schema is replaced by the embedded JSON object in Centrifugo case). Our Javascript SDK - centrifuge-js - uses JSON serialization for protocol frames by default. This makes communication with Centrifugo server convenient as we are exchanging human-readable JSON frames between client and server. And it makes it possible to use centrifuge-js without extra dependency to protobuf.js library. It's possible to switch to Protobuf protocol with centrifuge-js SDK though, in case you want more compact Centrifuge protocol representation, faster decode/encode speeds on Centrifugo server side, or payloads you need to pass are custom binary. See more details on how to use centrifuge-js with Protobuf serialization in README. centrifuge-go real-time SDK for Go language also supports both JSON and Protobuf formats when communicating with Centrifugo server. Other SDKs, like centrifuge-dart, centrifuge-swift, centrifuge-java work using only Protobuf serialization for Centrifuge protocol internally. So they utilize the fastest and the most compact wire representation by default. Note, that while internally in those SDKs the serialization format is Protobuf, you can still send JSON towards these clients as JSON objects may be encoded as UTF-8 bytes. So these SDKs may work with both custom binary and JSON payloads. There are some important notes about JSON and Protobuf interoperability mentioned in our FAQ.","s":"Protobuf and JSON formats in SDKs","u":"/docs/4/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":2253},{"i":2260,"t":"Below you can find an information regarding support of different features in our official client SDKs","s":"SDK feature matrix","u":"/docs/4/transports/client_sdk","h":"#sdk-feature-matrix","p":2253},{"i":2262,"t":"Client feature js dart swift go java connect to a server ✅ ✅ ✅ ✅ ✅ setting client options ✅ ✅ ✅ ✅ ✅ automatic reconnect with backoff algorithm ✅ ✅ ✅ ✅ ✅ client state changes ✅ ✅ ✅ ✅ ✅ command-reply ✅ ✅ ✅ ✅ ✅ command timeouts ✅ ✅ ✅ ✅ ✅ async pushes ✅ ✅ ✅ ✅ ✅ ping-pong ✅ ✅ ✅ ✅ ✅ connection token refresh ✅ ✅ ✅ ✅ ✅ handle disconnect advice from server ✅ ✅ ✅ ✅ ✅ server-side subscriptions ✅ ✅ ✅ ✅ ✅ batching API ✅ bidirectional WebSocket emulation ✅","s":"Connection related features","u":"/docs/4/transports/client_sdk","h":"#connection-related-features","p":2253},{"i":2264,"t":"Client feature js dart swift go java subscrbe to a channel ✅ ✅ ✅ ✅ ✅ setting subscription options ✅ ✅ ✅ ✅ ✅ automatic resubscribe with backoff algorithm ✅ ✅ ✅ ✅ ✅ subscription state changes ✅ ✅ ✅ ✅ ✅ subscription command-reply ✅ ✅ ✅ ✅ ✅ subscription async pushes ✅ ✅ ✅ ✅ ✅ subscription token refresh ✅ ✅ ✅ ✅ ✅ handle unsubscribe advice from server ✅ ✅ ✅ ✅ ✅ manage subscription registry ✅ ✅ ✅ ✅ ✅ optimistic subscriptions ✅","s":"Client-side subscription related features","u":"/docs/4/transports/client_sdk","h":"#client-side-subscription-related-features","p":2253},{"i":2266,"t":"On this page","s":"HTTP streaming, with bidirectional emulation","u":"/docs/4/transports/http_stream","h":"","p":2265},{"i":2269,"t":"Boolean, default: false. Enables HTTP streaming endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"http_stream\": true}","s":"http_stream","u":"/docs/4/transports/http_stream","h":"#http_stream","p":2265},{"i":2271,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"http_stream_max_request_body_size","u":"/docs/4/transports/http_stream","h":"#http_stream_max_request_body_size","p":2265},{"i":2273,"t":"On this page","s":"SockJS","u":"/docs/4/transports/sockjs","h":"","p":2272},{"i":2275,"t":"caution There are several important caveats to know when using SockJS – see below.","s":"SockJS caveats","u":"/docs/4/transports/sockjs","h":"#sockjs-caveats","p":2272},{"i":2277,"t":"First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend. For example, with Nginx sticky support can be enabled with ip_hash directive for upstream: upstream centrifugo { ip_hash; server 127.0.0.1:8000; server 127.0.0.2:8000;} With this configuration Nginx will proxy connections with the same ip address to the same upstream backend. But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair. So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.","s":"Sticky sessions","u":"/docs/4/transports/sockjs","h":"#sticky-sessions","p":2272},{"i":2279,"t":"SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.","s":"Browser only","u":"/docs/4/transports/sockjs","h":"#browser-only","p":2272},{"i":2281,"t":"One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.","s":"JSON only","u":"/docs/4/transports/sockjs","h":"#json-only","p":2272},{"i":2284,"t":"Boolean, default: false. Enables SockJS transport.","s":"sockjs","u":"/docs/4/transports/sockjs","h":"#sockjs","p":2272},{"i":2286,"t":"Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.","s":"sockjs_url","u":"/docs/4/transports/sockjs","h":"#sockjs_url","p":2272},{"i":2288,"t":"On this page","s":"Real-time transports","u":"/docs/4/transports/overview","h":"","p":2287},{"i":2290,"t":"Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus. Bidirectional transports come with a cost that developers need to use a special client connector library (SDK) which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state, handle request queueing/timeouts/errors, etc. Centrifugo has several official client SDKs for popular environments. All of them work over WebSocket transport. Our Javascript SDK also offers bidirectional fallbacks over HTTP-Streaming, Server-Sent Events (SSE) or SockJS.","s":"Bidirectional","u":"/docs/4/transports/overview","h":"#bidirectional","p":2287},{"i":2292,"t":"Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time. The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource/SSE, HTTP-streaming), or GRPC generated code to receive real-time updates from Centrifugo. Thus avoiding dependency to a client connector that abstracts bidirectional communication. The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo).","s":"Unidirectional","u":"/docs/4/transports/overview","h":"#unidirectional","p":2287},{"i":2294,"t":"In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as: message Push { string channel = 2; Publication pub = 4; Join join = 5; Leave leave = 6; Unsubscribe unsubscribe = 7; Message message = 8; Subscribe subscribe = 9; Connect connect = 10; Disconnect disconnect = 11; Refresh refresh = 12;} tip Some numbers in Protobuf definitions skipped for backwards compatibility with previous client protocol version. So unidirectional connection will receive various pushes. Every push contains one of the following objects: Publication Join Leave Unsubscribe Message Subscribe Connect Disconnect Refresh Some pushes belong to a channel which may be set on Push top level. All you need to do is look at Push, process messages you are interested in and ignore others. In most cases you will be most interested in pushes which contain Connect or Publication messages. For example, according to protocol schema Publication message type looks like this: message Publication { bytes data = 4; ClientInfo info = 5; uint64 offset = 6; map tags = 7;} tip In JSON protocol case Centrifugo replaces bytes type with embedded JSON. Just try using any unidirectional transport and you will quickly get the idea.","s":"Unidirectional message types","u":"/docs/4/transports/overview","h":"#unidirectional-message-types","p":2287},{"i":2296,"t":"Centrifugo server periodically sends pings to clients and expects pong from clients that works over bidirectional transports. Sending ping and receiving pong allows to find broken connections faster. Centrifugo sends pings on the Centrifugo client protocol level, thus it's possible for clients to handle ping messages on the client side to make sure connection is not broken (our bidirectional SDKs do this automatically). By default Centrifugo sends pings every 25 seconds. This may be changed using ping_interval option (duration, default \"25s\"). Centrifugo expects pong message from bidirectional client SDK after sending ping to it. By default, it waits no more than 8 seconds before closing a connection. This may be changed using pong_timeout option (duration, default \"8s\"). In most cases default ping/pong intervals are fine so you don't really need to tweak them. Reducing timeouts may help you to find non-gracefully closed connections faster, but will increase network traffic and CPU resource usage since ping/pongs are sent faster. caution ping_interval must be greater than pong_timeout in the current implementation. Here is a scheme how ping/pong works in bidirectional and unidirectional client scenarios:","s":"PING/PONG behavior","u":"/docs/4/transports/overview","h":"#pingpong-behavior","p":2287},{"i":2298,"t":"On this page","s":"SSE (EventSource), with bidirectional emulation","u":"/docs/4/transports/sse","h":"","p":2297},{"i":2301,"t":"Boolean, default: false. Enables SSE (EventSource) endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"sse\": true}","s":"sse","u":"/docs/4/transports/sse","h":"#sse","p":2297},{"i":2303,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"sse_max_request_body_size","u":"/docs/4/transports/sse","h":"#sse_max_request_body_size","p":2297},{"i":2305,"t":"On this page","s":"Unidirectional GRPC","u":"/docs/4/transports/uni_grpc","h":"","p":2304},{"i":2307,"t":"JSON and binary.","s":"Supported data formats","u":"/docs/4/transports/uni_grpc","h":"#supported-data-formats","p":2304},{"i":2310,"t":"Boolean, default: false. Enables unidirectional GRPC endpoint. config.json { ... \"uni_grpc\": true}","s":"uni_grpc","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc","p":2304},{"i":2312,"t":"String, default \"11000\". Port to listen on.","s":"uni_grpc_port","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_port","p":2304},{"i":2314,"t":"String, default \"\" (listen on all interfaces) Address to bind uni GRPC to.","s":"uni_grpc_address","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_address","p":2304},{"i":2316,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from GRPC connection in bytes.","s":"uni_grpc_max_receive_message_size","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":2304},{"i":2318,"t":"Boolean, default: false Enable custom TLS for unidirectional GRPC server.","s":"uni_grpc_tls","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls","p":2304},{"i":2320,"t":"String, default: \"\". Path to cert file.","s":"uni_grpc_tls_cert","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":2304},{"i":2322,"t":"String, default: \"\". Path to key file.","s":"uni_grpc_tls_key","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_key","p":2304},{"i":2324,"t":"A basic example will come soon as we update docs for v4. In general, algorithm is like this: Copy Protobuf definitions Generate GRPC client code Use generated code to connect to Centrifugo Process Push messages, drop unknown Push types, handle those necessary for the application.","s":"Example","u":"/docs/4/transports/uni_grpc","h":"#example","p":2304},{"i":2326,"t":"On this page","s":"Unidirectional HTTP streaming","u":"/docs/4/transports/uni_http_stream","h":"","p":2325},{"i":2328,"t":"It's possible to pass initial connect command by posting a JSON body to a streaming endpoint. Refer to the full Connect command description – it's the same as for unidirectional WebSocket.","s":"Connect command","u":"/docs/4/transports/uni_http_stream","h":"#connect-command","p":2325},{"i":2330,"t":"JSON","s":"Supported data formats","u":"/docs/4/transports/uni_http_stream","h":"#supported-data-formats","p":2325},{"i":2332,"t":"Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/4/transports/uni_http_stream","h":"#pings","p":2325},{"i":2335,"t":"Boolean, default: false. Enables unidirectional HTTP streaming endpoint. config.json { ... \"uni_http_stream\": true}","s":"uni_http_stream","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream","p":2325},{"i":2337,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"uni_http_stream_max_request_body_size","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":2325},{"i":2339,"t":"Let's look how simple it is to connect to Centrifugo using HTTP streaming. We will start from scratch, generate new configuration file: centrifugo genconfig Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect: config.json { ... \"uni_http_stream\": true, \"user_subscribe_to_personal\": true} Run Centrifugo: centrifugo -c config.json In separate terminal window create token for a user: ❯ go run main.go gentoken -u user12HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request: curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}' Open one more terminal window and publish message to a personal user channel: curl -X POST http://localhost:8000/api \\ -d '{\"method\": \"publish\", \"params\": {\"channel\": \"#user12\", \"data\": {\"input\": \"hello\"}}}' \\ -H \"Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc\" You should see this messages coming from server. {} messages are pings from a server. That's all, happy streaming!","s":"Connecting using CURL","u":"/docs/4/transports/uni_http_stream","h":"#connecting-using-curl","p":2325},{"i":2341,"t":"A basic browser will come soon as we update docs for v4.","s":"Browser example","u":"/docs/4/transports/uni_http_stream","h":"#browser-example","p":2325},{"i":2343,"t":"On this page","s":"Client SDK API","u":"/docs/4/transports/client_api","h":"","p":2342},{"i":2345,"t":"Client connection has 4 states: disconnected connecting connected closed note closed state is only implemented by SDKs where it makes sense (need to clean up allocated resources when app gracefully shuts down – for example in Java SDK we close thread executors used internally). When a new Client is created it has a disconnected state. To connect to a server connect() method must be called. After calling connect Client moves to the connecting state. If a Client can't connect to a server it attempts to create a connection with an exponential backoff algorithm (with full jitter). If a connection to a server is successful then the state becomes connected. If a connection is lost (due to a missing network for example, or due to reconnect advice received from a server, or due to some client-side error that can't be recovered without reconnecting) Client goes to the connecting state again. In this state Client tries to reconnect (again, with an exponential backoff algorithm). The Client's state can become disconnected. This happens when Client's disconnect() method was called by a developer. Also, this can happen due to server advice from a server, or due to a terminal problem that happened on the client-side. Here is a program where we create a Client instance, set callbacks to listen to state updates and establish a connection with a server: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('connecting', function(ctx) { console.log('connecting', ctx);});client.on('connected', function(ctx) { console.log('connected', ctx);});client.on('disconnected', function(ctx) { console.log('disconnected', ctx);});client.connect(); final onEvent = (dynamic event) { print('client event> $event');};final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);client.connecting.listen(onEvent);client.connected.listen(onEvent);client.disconnected.listen(onEvent);await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate { func onConnecting(_ c: CentrifugeClient, _ e: CentrifugeConnectingEvent) { print(\"connecting\", e.code, e.reason) } func onConnected(_ client: CentrifugeClient, _ e: CentrifugeConnectedEvent) { print(\"connected with id\", e.client) } func onDisconnected(_ client: CentrifugeClient, _ e: CentrifugeDisconnectedEvent) { print(\"disconnected\", e.code, e.reason) }}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())client.connect() EventListener listener = new EventListener() { @Override public void onConnected(Client client, ConnectedEvent event) { System.out.println(\"connected\"); } @Override public void onConnecting(Client client, ConnectingEvent event) { System.out.printf(\"connecting: %s%n\", event.getReason()); } @Override public void onDisconnected(Client client, DisconnectedEvent event) { System.out.printf(\"disconnected %d %s\", event.getCode(), event.getReason()); }};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)defer client.Close()client.OnConnecting(func(e centrifuge.ConnectingEvent) { log.Printf(\"Connecting - %d (%s)\", e.Code, e.Reason)})client.OnConnected(func(e centrifuge.ConnectedEvent) { log.Printf(\"Connected with ID %s\", e.ClientID)})client.OnDisconnected(func(e centrifuge.DisconnectedEvent) { log.Printf(\"Disconnected: %d (%s)\", e.Code, e.Reason)})_ = client.connect() In case of successful connection Client states will transition like this: disconnected (initial) -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server and then successfully reconnected: connected -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server, but got a terminal error upon reconnection: connected -> connecting (on('connecting') called) -> disconnected (on('disconnected') called). In case of already connected Client came across terminal condition (for example, if during a connection token refresh application found that user has no permission to connect anymore): connected -> disconnected (on('disconnected') called). Both connecting and disconnected events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why the Client went to the connecting state or to the disconnected state. This diagram demonstrates possible Client state transitions: You can also listen for all errors happening internally (which are not reflected by state changes, for example, transport errors happening on initial connect, transport during reconnect, connection token refresh related errors, etc) while the client works by using error event: client.on('error', function(ctx) { console.log('client error', ctx);}); If you want to disconnect from a server call .disconnect() method: client.disconnect(); In this case on('disconnected') will be called. You can call connect() again when you need to establish a connection. closed state implemented in SDKs where resources like internal queues, thread executors, etc must be cleaned up when the Client is not needed anymore. All subscriptions should automatically go to the unsubscribed state upon closing. The client is not usable after going to a closed state.","s":"Client connection states","u":"/docs/4/transports/client_api","h":"#client-connection-states","p":2342},{"i":2347,"t":"There are several common options available when creating Client instance. option to set connection token and callback to get connection token upon expiration (see below mode details) option to set connect data option to configure operation timeout tweaks for reconnect backoff algorithm (min delay, max delay) configure max delay of server pings (to detect broken connection) configure headers to send in WebSocket upgrade request (except centrifuge-js) configure client name and version for analytics purpose","s":"Client common options","u":"/docs/4/transports/client_api","h":"#client-common-options","p":2342},{"i":2349,"t":"connect() – connect to a server disconnect() - disconnect from a server close() - close Client if not needed anymore send(data) - send asynchronous message to a server rpc(method, data) - send arbitrary RPC and wait for response","s":"Client methods","u":"/docs/4/transports/client_api","h":"#client-methods","p":2342},{"i":2351,"t":"All SDKs support connecting to Centrifugo with JWT. Initial connection token can be set in Client option upon initialization. Example: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE'}); If the token sets connection expiration then the client SDK will keep the token refreshed. It does this by calling a special callback function. This callback must return a new token. If a new token with updated connection expiration is returned from callback then it's sent to Centrifugo. If your callback returns an empty string – this means the user has no permission to connect to Centrifugo and the Client will move to a disconnected state. In case of error returned by your callback SDK will retry the operation after some jittered time. An example: function getToken(url, ctx) { return new Promise((resolve, reject) => { fetch(url, { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify(ctx) }) .then(res => { if (!res.ok) { throw new Error(`Unexpected status code ${res.status}`); } return res.json(); }) .then(data => { resolve(data.token); }) .catch(err => { reject(err); }); });}const client = new Centrifuge( 'ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE', getToken: function (ctx) { return getToken('/centrifuge/connection_token', ctx); } }); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authentication. In this case SDK should attempt to get a connection token before establishing an initial connection.","s":"Client connection token","u":"/docs/4/transports/client_api","h":"#client-connection-token","p":2342},{"i":2353,"t":"PINGs sent by a server, a client should answer with PONGs upon receiving PING. If a client does not receive PING from a server for a long time (ping interval + configured delay) – the connection is considered broken and will be re-established.","s":"Connection PING/PONG","u":"/docs/4/transports/client_api","h":"#connection-pingpong","p":2342},{"i":2355,"t":"Client allows subscribing on channels. This can be done by creating Subscription object. const sub = centrifuge.newSubscription(channel);sub.subscribe(); When anewSubscription method is called Client allocates a new Subscription instance and saves it in the internal subscription registry. Having a registry of allocated subscriptions allows SDK to manage resubscribes upon reconnecting to a server. Centrifugo connectors do not allow creating two subscriptions to the same channel – in this case, newSubscription can throw an exception. Subscription has 3 states: unsubscribed subscribing subscribed When a new Subscription is created it has an unsubscribed state. To initiate the actual process of subscribing to a channel subscribe() method of Subscription instance should be called. After calling subscribe() Subscription moves to subscribing state. If subscription to a channel is not successful then depending on error type subscription can automatically resubscribe (with exponential backoff) or go to an unsubscribed state (upon non-temporary error). If subscription to a channel is successful then the state becomes subscribed. Javascript Dart Swift Java Go const sub = client.newSubscription(channel);sub.on('subscribing', function(ctx) { console.log('subscribing');});sub.on('subscribed', function(ctx) { console.log('subscribed');});sub.on('unsubscribed', function(ctx) { console.log('unsubscribed');});sub.subscribe(); final onSubscriptionEvent = (dynamic event) async { print('subscription $channel> $event');};final subscription = client.newSubscription(channel);subscription.subscribing.listen(onSubscriptionEvent);subscription.subscribed.listen(onSubscriptionEvent);subscription.unsubscribed.listen(onSubscriptionEvent);await subscription.subscribe(); class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onSubscribing(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribingEvent) { print(\"subscribing\", e.code, e.reason) } func onSubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribedEvent) { print(\"subscribed\") } func onUnsubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeUnsubscribedEvent) { print(\"unsubscribed\", e.code, e.reason) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")} SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onSubscribed(Subscription sub, SubscribedEvent event) { System.out.println(\"subscribed to \" + sub.getChannel()); } @Override public void onSubscribing(Subscription sub, SubscribingEvent event) { System.out.printf(\"subscribing \" + sub.getChannel()); } @Override public void onUnsubscribed(Subscription sub, UnsubscribedEvent event) { System.out.println(\"unsubscribed \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();} sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnSubscribing(func(e centrifuge.SubscribingEvent) { log.Printf(\"Subscribing on channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})sub.OnSubscribed(func(e centrifuge.SubscribedEvent) { log.Printf(\"Subscribed on channel %s\", sub.Channel)})sub.OnUnsubscribed(func(e centrifuge.UnsubscribedEvent) { log.Printf(\"Unsubscribed from channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})err = sub.Subscribe()if err != nil { log.Fatalln(err)} Subscriptions also go to subscribing state when Client connection (i.e. transport) becomes unavailable. Upon connection re-establishement all subscriptions which are not in unsubscribed state will resubscribe automatically. In case of successful subscription states will transition like this: unsubscribed (initial) -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). In case of connected and subscribed Client temporary lost a connection with a server and then succesfully reconnected and resubscribed: subscribed -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). Both subscribing and unsubscribed events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why Subscription went to subscribing state or to unsubscribed state. This diagram demonstrates possible Subscription state transitions: You can listen for all errors happening internally in Subscription (which are not reflected by state changes, for example, temporary subscribe errors, subscription token related errors, etc) by using error event: sub.on('error', function(ctx) { console.log(\"subscription error\", ctx);}); If you want to unsubscribe from a channel call .unsubscribe() method: sub.unsubscribe(); In this case on('unsubscribed') will be called. Subscription still kept in Client's registry, but no resubscription attempts will be made. You can call subscribe() again when you need Subscription again. Or you can remove Subscription from Client's registry (see below).","s":"Subscription states","u":"/docs/4/transports/client_api","h":"#subscription-states","p":2342},{"i":2357,"t":"The client SDK provides several methods to manage the internal registry of client-side subscriptions. newSubscription(channel, options) allocates a new Subscription in the registry or throws an exception if the Subscription is already there. We will discuss common Subscription options below. getSubscription(channel) returns the existing Subscription by a channel from the registry (or null if it does not exist). removeSubscription(sub) removes Subscription from Client's registry. Subscription is automatically unsubscribed before being removed. Use this to free resources if you don't need a Subscription to a channel anymore. subscriptions() returns all registered subscriptions, so you can iterate over all and do some action if required (for example, you want to unsubscribe/remove all subscriptions).","s":"Subscription management","u":"/docs/4/transports/client_api","h":"#subscription-management","p":2342},{"i":2359,"t":"Of course the main point of having Subscriptions is the ability to listen for publications (i.e. messages published to a channel). sub.on('publication', function(ctx) { console.log(\"received publication\", ctx);}); Publication context has several fields: data - publication payload, this can be JSON or binary data offset - optional offset inside history stream, this is an incremental number tags - optional tags, this is a map with string keys and string values info - optional information about client connection who published this (only exists if publication comes from client-side publish() API). So minimal code where we connect to a server and listen for messages published into example channel may look like: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = client.newSubscription('example').on('publication', function(ctx) { console.log(\"received publication from a channel\", ctx.data);});sub.subscribe();client.connect(); final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);final subscription = client.newSubscription(channel);subscription.publication.listen((event) { print(event);});await subscription.subscribe();await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate {}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onPublication(_ s: CentrifugeSubscription, _ e: CentrifugePublicationEvent) { print(\"publication\", e.data) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")}client.connect() EventListener listener = new EventListener() {};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onPublication(Subscription sub, PublicationEvent event) { System.out.println(\"publication from \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();}client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)// defer client.Close()sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnPublication(func(e centrifuge.PublicationEvent) { log.Printf(\"Publication from channel\")})err = sub.Subscribe()if err != nil { log.Fatalln(err)}if err = client.connect(); err != nil { log.Fatalln(err)} Note, that we can call subscribe() before making a connection to a server – and this will work just fine, subscription goes to subscribing state and will be subscribed upon succesfull connection. And of course, it's possible to call .subscribe() after .connect().","s":"Listen to channel publications","u":"/docs/4/transports/client_api","h":"#listen-to-channel-publications","p":2342},{"i":2361,"t":"Subscriptions to channels with recovery option enabled maintain stream position information internally. On every publication received this information updated and used to recover missed publications upon resubscribe (caused by reconnect for example). When you call unsubscribe() Subscription position state is not cleared. So it's possible to call subscribe() later and catch up a state. The recovery process result – i.e. whether all missed publications recovered or not – can be found in on('subscribed') event context. Centrifuge protocol provides two fields: wasRecovering - boolean flag that tells whether recovery was used during subscription process resulted into subscribed state. Can be useful if you want to distinguish first subscribe attempt (when subscription does not have any position information yet) recovered - boolean flag that tells whether Centrifugo thinks that all missed publications can be successfully recovered and there is no need to load state from the main application database. It's always false when wasRecovering is false.","s":"Subscription recovery state","u":"/docs/4/transports/client_api","h":"#subscription-recovery-state","p":2342},{"i":2363,"t":"There are several common options available when creating Subscription instance. option to set subscription token and callback to get subscription token upon expiration (see below more details) option to set subscription data (attached to every subscribe/resubscribe request) options to tweak resubscribe backoff algorithm option to start Subscription since known Stream Position (i.e. attempt recovery on first subscribe) option to ask server to make subscription positioned (if not forced by a server) option to ask server to make subscription recoverable (if not forced by a server) option to ask server to push Join/Leave messages (if not forced by a server)","s":"Subscription common options","u":"/docs/4/transports/client_api","h":"#subscription-common-options","p":2342},{"i":2365,"t":"subscribe() – start subscribing to a channel unsubscribe() - unsubscribe from a channel publish(data) - publish data to Subscription channel history(options) - request Subscription channel history presence() - request Subscription channel online presence information presenceStats() - request Subscription channel online presence stats information (number of client connections and unique users in a channel).","s":"Subscription methods","u":"/docs/4/transports/client_api","h":"#subscription-methods","p":2342},{"i":2367,"t":"All SDKs support subscribing to Centrifugo channels with JWT. Channel subscription token can be set as a Subscription option upon initialization. Example: const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE'});sub.subscribe(); If token sets subscription expiration client SDK will keep token refreshed. It does this by calling special callback function. This callback must return a new token. If new token with updated subscription expiration returned from a calbback then it's sent to Centrifugo. If your callback returns an empty string – this means user has no permission to subscribe to a channel anymore and subscription will be unsubscribed. In case of error returned by your callback SDK will retry operation after some jittered time. An example: function getToken(url, ctx) { return new Promise((resolve, reject) => { fetch(url, { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify(ctx) }) .then(res => { if (!res.ok) { throw new Error(`Unexpected status code ${res.status}`); } return res.json(); }) .then(data => { resolve(data.token); }) .catch(err => { reject(err); }); });}const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE', getToken: function (ctx) { // ctx has channel in the Subscription token case. return getToken('/centrifuge/subscription_token', ctx); },});sub.subscribe(); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authorization for a channel subscription. In this case SDK should attempt to get a subscription token before initial subscribe.","s":"Subscription token","u":"/docs/4/transports/client_api","h":"#subscription-token","p":2342},{"i":2369,"t":"We encourage using client-side subscriptions where possible as they provide a better control and isolation from connection. But in some cases you may want to use server-side subscriptions (i.e. subscriptions created by server upon connection establishment). Technically, client SDK keeps server-side subscriptions in internal registry (similar to client-side subscriptions but without possibility to control them). To listen for server-side subscription events use callbacks as shown in example below: const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('subscribed', function(ctx) { // Called when subscribed to a server-side channel upon Client moving to // connected state or during connection lifetime if server sends Subscribe // push message. console.log('subscribed to server-side channel', ctx.channel);});client.on('subscribing', function(ctx) { // Called when existing connection lost (Client reconnects) or Client // explicitly disconnected. Client continue keeping server-side subscription // registry with stream position information where applicable. console.log('subscribing to server-side channel', ctx.channel);});client.on('unsubscribed', function(ctx) { // Called when server sent unsubscribe push or server-side subscription // previously existed in SDK registry disappeared upon Client reconnect. console.log('unsubscribed from server-side channel', ctx.channel);});client.on('publication', function(ctx) { // Called when server sends Publication over server-side subscription. console.log('publication receive from server-side channel', ctx.channel, ctx.data);});client.connect(); Server-side subscription events mostly mimic events of client-side subscriptions. But again – they do not provide control to the client and managed entirely by a server side. Additionally, Client has several top-level methods to call with server-side subscription related operations: publish(channel, data) history(channel, options) presence(channel) presenceStats(channel)","s":"Server-side subscriptions","u":"/docs/4/transports/client_api","h":"#server-side-subscriptions","p":2342},{"i":2371,"t":"Server can return error codes in range 100-1999. Error codes in interval 0-399 reserved by Centrifuge/Centrifugo server. Codes in range [400, 1999] may be returned by application code built on top of Centrifuge/Centrifugo. Server errors contain a temporary boolean flag which works as a signal that error may be fixed by a later retry. Errors with codes 0-100 can be used by client-side implementation. Client-side errors may not have code attached at all since in many languages error can be distinguished by its type.","s":"Error codes","u":"/docs/4/transports/client_api","h":"#error-codes","p":2342},{"i":2373,"t":"Server may return unsubscribe codes. Server unsubscribe codes must be in range [2000, 2999]. Unsubscribe codes >= 2500 coming from server to client result into automatic resubscribe attempt (i.e. client goes to subscribing state). Codes < 2500 result into going to unsubscribed state. Client implementation can use codes <2000 for client-side specific unsubscribe reasons.","s":"Unsubscribe codes","u":"/docs/4/transports/client_api","h":"#unsubscribe-codes","p":2342},{"i":2375,"t":"Server may send custom disconnect codes to a client. Custom disconnect codes must be in range [3000, 4999]. Client automatically reconnects upon receiving code in range 3000-3499, 4000-4499 (i.e. Client goes to connecting state). Other codes result into going to disconnected state. Client implementation can use codes <3000 for client-side specific disconnect reasons.","s":"Disconnect codes","u":"/docs/4/transports/client_api","h":"#disconnect-codes","p":2342},{"i":2377,"t":"An SDK provides a way to send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the real-time connection. RPC is only available when RPC proxy configured (so Centrifugo proxies the RPC to your application backend). const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"RPC","u":"/docs/4/transports/client_api","h":"#rpc","p":2342},{"i":2379,"t":"SDK provides a method to call publication history inside a channel (only for channels where history is enabled) to get last publications in a channel. Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Channel history API","u":"/docs/4/transports/client_api","h":"#channel-history-api","p":2342},{"i":2381,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats API","u":"/docs/4/transports/client_api","h":"#presence-and-presence-stats-api","p":2342},{"i":2383,"t":"Callbacks must be fast. Avoid blocking operations inside event handlers. Callbacks caused by protocol messages received from a server are called synchronously and connection read loop is blocked while such callbacks are being executed. Consider doing heavy work asynchronously. Do not blindly rely on the current Client or Subscription state when making client API calls – state can change at any moment, so don't forget to handle errors. Disconnect from a server when a mobile application goes to the background since a mobile OS can kill the connection at some point without any callbacks called.","s":"SDK common best practices","u":"/docs/4/transports/client_api","h":"#sdk-common-best-practices","p":2342},{"i":2385,"t":"On this page","s":"Unidirectional SSE (EventSource)","u":"/docs/4/transports/uni_sse","h":"","p":2384},{"i":2387,"t":"Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example: const url = new URL('http://localhost:8000/connection/uni_sse');url.searchParams.append(\"cf_connect\", JSON.stringify({ 'token': ''}));const eventSource = new EventSource(url); Refer to the full Connect command description – it's the same as for unidirectional WebSocket. The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases. tip Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.","s":"Connect command","u":"/docs/4/transports/uni_sse","h":"#connect-command","p":2384},{"i":2389,"t":"JSON","s":"Supported data formats","u":"/docs/4/transports/uni_sse","h":"#supported-data-formats","p":2384},{"i":2392,"t":"Boolean, default: false. Enables unidirectional SSE (EventSource) endpoint. config.json { ... \"uni_sse\": true}","s":"uni_sse","u":"/docs/4/transports/uni_sse","h":"#uni_sse","p":2384},{"i":2394,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"uni_sse_max_request_body_size","u":"/docs/4/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":2384},{"i":2396,"t":"A basic browser will come soon as we update docs for v4.","s":"Browser example","u":"/docs/4/transports/uni_sse","h":"#browser-example","p":2384},{"i":2398,"t":"On this page","s":"Unidirectional WebSocket","u":"/docs/4/transports/uni_websocket","h":"","p":2397},{"i":2400,"t":"It's possible to send connect command as first WebSocket message (as JSON). Field name Field type Required Description token string no Connection JWT, not required when using the connect proxy feature. data any JSON no Custom JSON connection data name string no Application name version string no Application version subs map of channel to SubscribeRequest no Pass an information about desired subscriptions to a server","s":"Connect command","u":"/docs/4/transports/uni_websocket","h":"#connect-command","p":2397},{"i":2402,"t":"Field name Field type Required Description recover boolean no Whether a client wants to recover from a certain position offset integer no Known stream position offset when recover is used epoch string no Known stream position epoch when recover is used","s":"SubscribeRequest","u":"/docs/4/transports/uni_websocket","h":"#subscriberequest","p":2397},{"i":2404,"t":"JSON","s":"Supported data formats","u":"/docs/4/transports/uni_websocket","h":"#supported-data-formats","p":2397},{"i":2406,"t":"Centrifugo uses empty commands ({} in JSON case) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/4/transports/uni_websocket","h":"#pings","p":2397},{"i":2409,"t":"Boolean, default: false. Enables unidirectional WebSocket endpoint. config.json { ... \"uni_websocket\": true}","s":"uni_websocket","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket","p":2397},{"i":2411,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from WebSocket connection in bytes.","s":"uni_websocket_message_size_limit","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":2397},{"i":2413,"t":"Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal. First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example: config.json { \"token_hmac_secret_key\": \"secret\", \"uni_websocket\":true, \"user_subscribe_to_personal\": true} Run Centrifugo: ./centrifugo -c config.json In another terminal: ❯ ./centrifugo gentoken -c config.json -u test_userHMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw Install wscat and run: wscat -c \"ws://localhost:8000/connection/uni_websocket\" This will establish a connection with a server and you then can send connect command to a server: ❯ wscat -c \"ws://localhost:8000/connection/uni_websocket\"Connected (press CTRL+C to quit)> {\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2NTY1MDMwNDV9.3UYL-UCUBp27TybeBK7Z0OenwdsKwCMRe46fuEjJnzI\", \"subs\": {\"abc\": {}}}< {\"connect\":{\"client\":\"bfd28799-b958-4791-b9e9-b011eaef68c1\",\"version\":\"0.0.0\",\"subs\":{\"#test_user\":{}},\"expires\":true,\"ttl\":604407,\"ping\":25,\"session\":\"57b1287b-44ec-45c8-93fc-696c5294af25\"}} The connection will receive server pings (empty commands {}) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.","s":"Example","u":"/docs/4/transports/uni_websocket","h":"#example","p":2397},{"i":2415,"t":"Attributions","s":"Attributions","u":"/docs/attributions","h":"","p":2414},{"i":2417,"t":"The following images have been used in the landing page. Icons made by kerismaker: https://www.flaticon.com/packs/web-maintenance-35","s":"Landing Page Images","u":"/docs/attributions","h":"#landing-page-images","p":2414},{"i":2419,"t":"On this page","s":"WebSocket","u":"/docs/4/transports/websocket","h":"","p":2418},{"i":2422,"t":"Default: 65536 (64KB) Maximum allowed size of a message received from WebSocket connection in bytes.","s":"websocket_message_size_limit","u":"/docs/4/transports/websocket","h":"#websocket_message_size_limit","p":2418},{"i":2424,"t":"In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size). config.json { ... \"websocket_read_buffer_size\": 512}","s":"websocket_read_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_read_buffer_size","p":2418},{"i":2426,"t":"In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused): config.json { ... \"websocket_write_buffer_size\": 512}","s":"websocket_write_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_write_buffer_size","p":2418},{"i":2428,"t":"If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.","s":"websocket_use_write_buffer_pool","u":"/docs/4/transports/websocket","h":"#websocket_use_write_buffer_pool","p":2418},{"i":2430,"t":"An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire. We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally. caution Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable. To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature. Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client. It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.","s":"websocket_compression","u":"/docs/4/transports/websocket","h":"#websocket_compression","p":2418},{"i":2432,"t":"In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if: you want less traffic on wire as Protobuf is very compact you want maximum performance on server-side as Protobuf encoding/decoding is very efficient you can sacrifice human-readable JSON for your application Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like: wss://centrifugo.example.com/connection/websocket?format=protobuf After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes. tip You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.","s":"Protobuf binary protocol","u":"/docs/4/transports/websocket","h":"#protobuf-binary-protocol","p":2418},{"i":2434,"t":"WebTransport WebTransport is an API offering low-latency, bidirectional, client-server messaging on top of HTTP/3. See Using WebTransport article that gives a good overview of it. danger WebTransport support in Centrifugo is EXPERIMENTAL and not recommended for production usage. WebTransport IETF specification is not finished yet and may have breaking changes. To use WebTransport you first need to run HTTP/3 experimental server and enable webtransport endpoint: config.json { \"http3\": true, \"tls\": true, \"tls_cert\": \"path/to/crt\", \"tls_key\": \"path/to/key\", \"webtransport\": true} In HTTP/3 and WebTransport case TLS is required. tip At the time of writing only Chrome (since v97) supports WebTransport API. If you are experimenting with self-signed certificates you may need to run Chrome with flags to force HTTP/3 on origin and ignore certificate errors: /path/to/your/Chrome --origin-to-force-quic-on=localhost:8000 --ignore-certificate-errors-spki-list=TSZTiMjLG+DNjESXdJh3f+S8C+RhsFCav7T24VNuCPQ= Where the value of --ignore-certificate-errors-spki-list is a certificate fingerprint obtained this way: openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 With not self-signed certs things should work just fine in Chrome. Here is a video tutorial that shows this in action: After starting Centrifugo with HTTP/3 and WebTransport endpoint you can connect to that endpoint (by default – /connection/webtransport) using centrifuge-js. For example, let's enable WebTransport and will use WebSocket as a fallback option: const transports = [ { transport: 'webtransport', endpoint: 'https://localhost:8000/connection/webtransport' }, { transport: 'websocket', endpoint: 'wss://localhost:8000/connection/websocket' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in WebTransport case non-TLS http:// scheme is simply not supported by the specification. tip Make sure you run Centrifugo without load balancer or reverse proxy in front, or make sure your proxy can proxy HTTP/3 traffic to Centrifugo. In Centrifugo case, we utilize a single bidirectional stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional. Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification it's an experimental feature. And it's not recommended for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket.","s":"WebTransport","u":"/docs/4/transports/webtransport","h":"","p":2433},{"i":2436,"t":"flow_diagrams For swimlanes.io: Client <- App Backend: JWTnote:The backend generates JWT for a user and passes it to the client side.Client -> Centrifugo: Client connects to Centrifugo with JWT...: {fas-spinner} Persistent connection establishedClient -> Centrifugo: Client issues channel subscribe requestsCentrifugo -->> Client: Client receives real-time updates from channels Client -> Centrifugo: Connect requestnote:Client connects to Centrifugo without JWT.Centrifugo -> App backend: Sends request further (via HTTP or GRPC)note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.App backend -> Centrifugo: Connect replyCentrifugo -> Client: Connect Reply...: {fas-spinner} Persistent connection established Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers. Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers.","s":"flow_diagrams","u":"/docs/flow_diagrams","h":"","p":2435},{"i":2438,"t":"On this page","s":"Frequently Asked Questions","u":"/docs/faq","h":"","p":2437},{"i":2440,"t":"This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here. Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case. You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.","s":"How many connections can one Centrifugo instance handle?","u":"/docs/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":2437},{"i":2442,"t":"Depending on transport used and features enabled the amount of RAM required per each connection can vary. For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections. For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.","s":"Memory usage per connection?","u":"/docs/faq","h":"#memory-usage-per-connection","p":2437},{"i":2444,"t":"Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker. See engines and scalability considerations.","s":"Can Centrifugo scale horizontally?","u":"/docs/faq","h":"#can-centrifugo-scale-horizontally","p":2437},{"i":2446,"t":"See design overview","s":"Message delivery model","u":"/docs/faq","h":"#message-delivery-model","p":2437},{"i":2448,"t":"See design overview.","s":"Message order guarantees","u":"/docs/faq","h":"#message-order-guarantees","p":2437},{"i":2450,"t":"No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel. When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).","s":"Should I create channels explicitly?","u":"/docs/faq","h":"#should-i-create-channels-explicitly","p":2437},{"i":2452,"t":"Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application. But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc. One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.","s":"What about best practices with the number of channels?","u":"/docs/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":2437},{"i":2454,"t":"Currently, no. We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this: Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID) Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates. Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point. Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel. In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.","s":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":2437},{"i":2456,"t":"No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere. Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end). To summarize: if you are using binary Protobuf clients and binary payloads everywhere – you are fine. if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine. if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.","s":"Can I have both binary and JSON clients in one channel?","u":"/docs/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":2437},{"i":2458,"t":"While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel. You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.","s":"Online presence for chat apps - online status of your contacts","u":"/docs/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":2437},{"i":2460,"t":"The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.","s":"Centrifugo stops accepting new connections, why?","u":"/docs/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":2437},{"i":2462,"t":"Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.","s":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":2437},{"i":2464,"t":"Yes, Centrifugo works with HTTP/2. This is provided by built-in Go http server implementation. You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable: GODEBUG=\"http2server=0\" centrifugo -c config.json Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.","s":"Does Centrifugo work with HTTP/2?","u":"/docs/faq","h":"#does-centrifugo-work-with-http2","p":2437},{"i":2466,"t":"Centrifugo v4 added an experimental HTTP/3 support. As soon as you enabled TLS and provided \"http3\": true option all endpoints on external port will be served by HTTP/3 server based on github.com/quic-go/quic-go implementation. This (among other benefits which HTTP/3 can provide) is a step towards a proper WebTransport support. For now we support WebTransport experimentally. It's worth noting that WebSocket transport does not work over HTTP/3, it still starts with HTTP/1.1 Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and Eventsource should work just fine with HTTP/3. HTTP/3 does not work with ACME autocert TLS at the moment - i.e. you need to explicitly provide paths to cert and key files as described here.","s":"Does Centrifugo work with HTTP/3?","u":"/docs/faq","h":"#does-centrifugo-work-with-http3","p":2437},{"i":2468,"t":"If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.","s":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":2437},{"i":2470,"t":"Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via FCM, or browsers over Web Push API. We are preparing our own push notifications API as part of Centrifugo PRO version. This is under construction though. The reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.","s":"What if I need to send push notifications to mobile or web applications?","u":"/docs/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":2437},{"i":2472,"t":"You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.","s":"How can I know a message is delivered to a client?","u":"/docs/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":2437},{"i":2474,"t":"It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend. We suggest using one of the available approaches: When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API. Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network. Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself). Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.","s":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":2437},{"i":2476,"t":"There are several ways to achieve it: use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers you can use subscribe proxy feature to validate subscriptions, see chapter about proxy finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations","s":"How to create a secure channel for two users only (private chat case)?","u":"/docs/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":2437},{"i":2478,"t":"In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled. For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients. The same relates to other channel options.","s":"What's the best way to organize channel configuration?","u":"/docs/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":2437},{"i":2480,"t":"Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there. Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.","s":"Does Centrifugo support webhooks?","u":"/docs/faq","h":"#does-centrifugo-support-webhooks","p":2437},{"i":2482,"t":"UPDATE Centrifugo PRO now solves the pitfalls mentioned here with its Channel State Events feature. Centrifugo does not support disconnect hooks at this point. We understand that this may be useful for some use cases but there are some pitfalls which prevent us adding such hooks to Centrifugo. Let's consider a case when Centrifugo node is unexpectedly killed. In this case there is no chance for Centrifugo to emit disconnect events for connections on that node. While this may be rare thing in practice – it may lead to inconsistent state in you app if you'd rely on disconnect hooks. Another reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. Even if disconnect events were queued, rate-limited, or suppressed for quickly reconnected clients there could be situations when your app processes disconnect hook after user already reconnected. This is a racy situation which also can lead to the inconsistency if not properly addressed. Is there a workaround though? If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend while client connection is active and update status somewhere on the backend (possibly using Redis for this). Then periodically do clealup logic for connections/users not updated for a configured interval. This is a robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo connect proxy + refresh proxy for getting notified about initial connection and get periodic refresh requests while connection is alive. The trade-off of the described workaround scenario is that you will notice disconnection only with some delay – this may be a acceptable in many cases though. Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together. All the pitfalls and workarounds here may be also applied to unsubscribe event hooks.","s":"Why Centrifugo does not have disconnect hooks?","u":"/docs/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":2437},{"i":2484,"t":"No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.","s":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":2437},{"i":2486,"t":"Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel). There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers. You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs. The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.","s":"How scalable is the online presence and join/leave features?","u":"/docs/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":2437},{"i":2488,"t":"Sometimes you need to send some initial state towards channel subscriber. Centrifugo provides a way to attach any data to a successful subscribe reply when using subscribe proxy feature. See data and b64data fields. This data will be part of subscribed event context. And of course, you can always simply send request to get initial data from the application backend before or after subscribing to a channel without Centrifugo connection involved (i.e. using sth like general AJAX/HTTP call or passing data to the template when rendering an application page).","s":"How to send initial data to channel subscriber?","u":"/docs/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":2437},{"i":2490,"t":"If you want to use Centrifugo with different projects the recommended approach is to have different Centrifugo installations for each project. Multitenancy is better to solve on infrastructure level in case of Centrifugo. It's possible to share one Redis setup though by setting unique redis_prefix. But we recommend having completely isolated setups.","s":"Does Centrifugo support multitenancy?","u":"/docs/faq","h":"#does-centrifugo-support-multitenancy","p":2437},{"i":2492,"t":"Ask in our community rooms:","s":"I have not found an answer to my question here","u":"/docs/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":2437},{"i":2494,"t":"Join community If you find Centrifugo interesting, you are welcome to join our community rooms on Telegram (the most active) and Discord: We strive to create a respectful and curious community. In these rooms, you may find answers to questions not fully covered by the documentation, share your thoughts and ideas on real-time messaging topics and Centrifugo in particular. We also have a Twitter account and a YouTube channel. See you there!","s":"Join community","u":"/docs/getting-started/community","h":"","p":2493},{"i":2496,"t":"On this page","s":"Comparing with others","u":"/docs/getting-started/comparisons","h":"","p":2495},{"i":2498,"t":"Socket.io is a library; you need to write your own server on top of it. Centrifugo is a ready-to-use standalone server. Because of this, Socket.io may give you more flexibility, but you are mostly limited to writing code in JavaScript on the backend to get the most out of it. Since Centrifugo is a standalone server, it's a universal, language-agnostic element that integrates well with a backend written in any language. But because it's universal, it comes with integration rules, its own mechanics, and limitations of such a design.","s":"Centrifugo vs Socket.io","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-socketio","p":2495},{"i":2500,"t":"The main difference is that Centrifugo is a self-hosted solution, while the mentioned technologies are all cloud SaaS platforms. So, when using Centrifugo, you need to configure and run it on your own; you need to be skilled in engineering. The benefit is obvious – it's much cheaper once the integration is done. And all the data stays within your organization. With cloud services, all the hard work of setting up an infrastructure for a WebSocket server and its maintenance is done for you. But it's more expensive, and the data flows through an external network.","s":"Centrifugo vs Pusher, Ably, Pubnub","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-pusher-ably-pubnub","p":2495},{"i":2502,"t":"A popular question from newcomers – does Centrifugo provide the same as Redis PUB/SUB? The answer is that Centrifugo and Redis can't be directly compared at all. Centrifugo uses Redis internally for PUB/SUB scalability, keeping channel message history, and online presence. You can build a system similar to Centrifugo on top of Redis – but you would need to write a lot of code, i.e., replicate everything that Centrifugo provides out of the box – like real-time client SDKs, client protocol, re-implement all the transport endpoints, write efficient Redis integration, etc.","s":"Centrifugo vs Redis","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-redis","p":2495},{"i":2504,"t":"At first glance, Centrifugo provides concepts similar to Apache Kafka - it has channels which seem similar to Kafka's topics, channel history with time and size retention policies. So sometimes people ask whether Centrifugo may be used as a lightweight Kafka replacement. But Centrifugo and Kafka were designed for different purposes. Centrifugo is a real-time messaging system with push semantics. It provides a lightweight PUB/SUB with ephemeral in-memory channels, designed to be exposed to frontend real-time apps. It has limited guarantees about message persistence in channel history, though it provides client hooks to be notified about message loss and recover the state from the main application database. Kafka is a persistent disk-based message bus that you can't easily expose to frontend users. It won't work well with millions of topics where users connect and disconnect constantly – this would cause constant repartitioning and eventually require much more resources. Kafka fits well for service-to-service communication where topics may be pre-created and under control; Centrifugo fits well for frontend-to-backend real-time communication.","s":"Centrifugo vs Kafka","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-kafka","p":2495},{"i":2506,"t":"This is also a popular comparison request. Especially since both Centrifugo and Nats are written in the Go language, and Nats also supports connections from the application client side over the WebSocket protocol. Nats is a very powerful messaging system, and it also has a built-in Jetstream system to provide at least once delivery guarantees. It has a larger community, more SDKs for various languages, incredible performance. How Centrifugo is special: Centrifugo actually uses Nats as one of the options for a PUB/SUB broker, i.e., as a PUB/SUB scalability backend. Centrifugo was originally designed to be exposed to application frontend clients, providing various convenient authentication and channel authorization mechanisms for client-side integration. It supports more transports, including WebSocket fallbacks. It provides many unique features that are out of scope for Nats – like online presence, individual GRPC subscription streams, or some of Centrifugo PRO features like push notifications support, real-time analytics with ClickHouse, etc. Topics in Jetstream still should be pre-created, while Centrifugo has ephemeral channels – created on the fly, even when using a channel history cache. Centrifugo provides client connection event proxy features - it's possible to delegate authentication, channel authorization to the application backend, and provides channel state events (when a channel is occupied or vacated) in the PRO version. Nats is great, and we are constantly looking for tighter integration with Nats. But both systems have unique sets of features and may be better or worse for various tasks.","s":"Centrifugo vs Nats","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-nats","p":2495},{"i":2508,"t":"On this page","s":"Client API showcase","u":"/docs/getting-started/client_api","h":"","p":2507},{"i":2510,"t":"Each Centrifugo client allows connecting to a server. const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.connect(); In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to: const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.setToken('')centrifuge.connect(); See authentication chapter for more information on how to generate connection JWT. If you are using connect proxy then you may go without setting JWT.","s":"Connecting to a server","u":"/docs/getting-started/client_api","h":"#connecting-to-a-server","p":2507},{"i":2512,"t":"After connecting you can disconnect from a server at any moment. centrifuge.disconnect();","s":"Disconnecting from a server","u":"/docs/getting-started/client_api","h":"#disconnecting-from-a-server","p":2507},{"i":2514,"t":"Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.","s":"Reconnecting to a server","u":"/docs/getting-started/client_api","h":"#reconnecting-to-a-server","p":2507},{"i":2516,"t":"All client implementations allow setting handlers on connect and disconnect events. For example: centrifuge.on('connect', function(connectCtx){ console.log('connected', connectCtx)});centrifuge.on('disconnect', function(disconnectCtx){ console.log('disconnected', disconnectCtx)});","s":"Connection lifecycle events","u":"/docs/getting-started/client_api","h":"#connection-lifecycle-events","p":2507},{"i":2518,"t":"Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel. centrifuge.subscribe('channel', function(messageCtx) { console.log(messageCtx);}) Clients can subscribe to different channels. The subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events. In the idiomatic case, messages are published to channels from the application backend over the Centrifugo server API. However, this is not always the case. Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel is properly configured. The client keeps the last seen Publication offset and restores missed publications since the known offset upon reconnecting. If recovery fails, then the client implementation provides a flag inside the subscribe event to let the application know that some publications were missed – so you may need to load the state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery can be found in a dedicated chapter.","s":"Subscribe to a channel","u":"/docs/getting-started/client_api","h":"#subscribe-to-a-channel","p":2507},{"i":2520,"t":"To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance: centrifuge.on('publish', function(messageCtx) { console.log(messageCtx);}); It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.","s":"Server-side subscriptions","u":"/docs/getting-started/client_api","h":"#server-side-subscriptions","p":2507},{"i":2522,"t":"A client can send an RPC to the server. RPC is a call that is not related to channels at all. It's just a way to call a server method from the client side over the WebSocket or SockJS connection. RPC is only available when an RPC proxy is configured. const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"Send RPC","u":"/docs/getting-started/client_api","h":"#send-rpc","p":2507},{"i":2524,"t":"Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel: Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Call channel history","u":"/docs/getting-started/client_api","h":"#call-channel-history","p":2507},{"i":2526,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats","u":"/docs/getting-started/client_api","h":"#presence-and-presence-stats","p":2507},{"i":2528,"t":"On this page","s":"Design overview","u":"/docs/getting-started/design","h":"","p":2527},{"i":2530,"t":"Centrifugo is a standalone server that abstracts away the complexity of working with many persistent connections and efficiently broadcasting messages from the application backend. The fact that Centrifugo acts as a separate service dictates some idiomatic patterns for how to integrate with Centrifugo for real-time message delivery. Usually, you want to deliver content created by a user in your app to other users in real time. Each user may have several real-time connections with Centrifugo. For example, a user opened several browser tabs, with each tab creating a separate connection. Or a user has two mobile devices and created a separate connection to your app from each of them. We call a connection a client in Centrifugo. Thus, the words connection and client are synonyms for us. All requests from users that generate new data should first go to the application backend – i.e., calling the app backend API from the client side. The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event to a channel using Centrifugo server API. This event is then efficiently broadcast by Centrifugo to all active channel subscribers. The following diagram shows the process (assuming the client that generates new content is also a channel subscriber and thus also receives the real-time message): This is usually a natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is fully decoupled from the application in this case. Centrifugo serves the role of a real-time transport layer in this case, and you may design the app with graceful degradation in mind – so that removing Centrifugo won't be a fatal problem for the application – it will continue working, just without the real-time features. If the original source of events is your app backend (without any user involvement) – then the above diagram simplifies to: So the backend publishes data to channels and if there are active subscribers – events are delivered. If there are no active subscribers then events are dropped by Centrifugo (or, in case of using history features in channels, events may be temporarily kept in the Centrifugo history stream). It's also possible to utilize Centrifugo's bidirectional connection for sending requests to the backend. To achieve this, Centrifugo provides event proxy features. It's possible to send RPC (with a custom request-response) requests from the client to Centrifugo and the request will then be proxied to the application backend (see RPC proxy). Moreover, the proxy provides a way to utilize the bidirectional connection for publishing into channels (using publish proxy). But again – in most real scenarios, your backend must validate the publication attempt, so the scheme will look like this:","s":"Idiomatic usage","u":"/docs/getting-started/design","h":"#idiomatic-usage","p":2527},{"i":2532,"t":"Idiomatic Centrifugo usage requires having the main application database from which the initial and actual state can be loaded at any point in time. While Centrifugo has channel history, it has been mostly designed to be a hot cache to reduce the load on the main application database when all users reconnect at once (in case of a load balancer configuration reload, Centrifugo restart, temporary network problems, etc.). This allows for a radical reduction in the load on the application's main database during a reconnect storm. Since such disconnects are usually pretty short in time, having a reasonably small number of messages cached in history is sufficient. The addition of the history iteration API shifts possible use cases a bit. Manually calling history chunk by chunk allows for keeping a larger number of publications per channel. Depending on the Engine used and the configuration of the underlying storage, history stream persistence characteristics can vary. For example, with the Memory Engine, history will be lost upon Centrifugo restart. With Redis or Tarantool engines, history will survive Centrifugo restarts, but depending on the storage configuration, it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis AOF with fsync for maximum durability, or configure replication for high availability, use Redis Cluster, or maybe synchronous replication with Tarantool. When using history with automatic recovery, Centrifugo provides clients with a flag to distinguish whether the missed messages were all successfully restored from Centrifugo history upon recovery or not. If not – the client may restore the state from the main application database. Centrifugo message history can be used as a complementary way to restore messages and thus reduce the load on the main application database most of the time.","s":"Message history considerations","u":"/docs/getting-started/design","h":"#message-history-considerations","p":2527},{"i":2534,"t":"By default, the message delivery model of Centrifugo is 'at most once'. With history and the positioning/recovery features enabled, it's possible to achieve 'at least once' guarantee within the history retention time and size. After an abnormal disconnect, clients have an option to recover missed messages from the publication channel stream history that Centrifugo maintains. Without the positioning or recovery features enabled, a message sent to Centrifugo could theoretically be lost while moving towards clients. Centrifugo makes its best effort only to prevent message loss on the way to online clients, but the application should tolerate the loss. As noted, Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. It also compensates for the 'at most once' delivery of a broker PUB/SUB system (Redis, Tarantool) by using additional publication offset checks and periodic offset synchronization. So publication loss missed in the PUB/SUB layer will be detected eventually, and the client may catch up on the state by loading it from history.","s":"Message delivery model","u":"/docs/getting-started/design","h":"#message-delivery-model","p":2527},{"i":2536,"t":"Message order in channels is guaranteed to be the same when you publish messages into a channel one after another or publish them in one request. If you do parallel publications into the same channel, then Centrifugo can't guarantee message order since those are processed in parallel.","s":"Message order guarantees","u":"/docs/getting-started/design","h":"#message-order-guarantees","p":2527},{"i":2538,"t":"It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely solely on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in the AJAX call response and render it. This way, the user that posts a comment will think that everything works just fine. Be careful not to draw comments twice in this case because you may also receive the same data from a channel - think about idempotent identifiers for your entities.","s":"Graceful degradation","u":"/docs/getting-started/design","h":"#graceful-degradation","p":2527},{"i":2540,"t":"Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to network failures and the unexpected shutdown of a Centrifugo node, there are chances that clients can be present in a presence for up to one minute more (until the presence entry expires). Also, channel presence does not scale well for channels with a lot of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows, the response size becomes larger. In some cases, the presence_stats API call can be sufficient to avoid receiving the entire presence state.","s":"Online presence considerations","u":"/docs/getting-started/design","h":"#online-presence-considerations","p":2527},{"i":2542,"t":"Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with the Nats broker. See engines. All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications. But if you approach broker resource limits (CPU or memory), then it's possible: Use Centrifugo's consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool). Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters). Nats broker should scale well itself in a cluster setup. All brokers can be set up in a highly available way so there won't be a single point of failure. All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact, and the fact that Centrifugo provides hooks for the application to understand history loss, makes the process of resharding mostly automatic. As soon as you need to add an additional broker shard (when using client-side sharding), you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently, part of the data will stay on the same broker nodes. Applications should handle cases where channel data moved to another shard and restore the state from the main application database when needed (i.e., when the recovered flag provided by the SDK is false).","s":"Scalability considerations","u":"/docs/getting-started/design","h":"#scalability-considerations","p":2527},{"i":2544,"t":"On this page","s":"Ecosystem notes","u":"/docs/getting-started/ecosystem","h":"","p":2543},{"i":2546,"t":"Centrifugo is built on top of the Centrifuge library for the Go language. Due to its standalone language-agnostic nature, Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a tighter integration of a real-time server with application business logic, you may consider using the Centrifuge library to build something similar to Centrifugo but with customized behavior. The Centrifuge library can be considered as an analogue of Socket.IO in the Go language ecosystem. The library's README has a detailed description, a link to examples, and an introduction post. Many Centrifugo features should be re-implemented when using Centrifuge - like the API layer, admin web UI, proxy, etc. (if you need those of course). And you need to write in the Go language. But the core functionality like a client-server protocol (all Centrifugo client SDKs work with a Centrifuge library-based server) and Redis engine to scale come out of the box – in most cases, this is enough to start building an app. tip Many things said in the Centrifugo doc can be considered as extra documentation for the Centrifuge library (for example, parts about infrastructure tuning or transport description). But not all of them.","s":"Centrifuge library for Go","u":"/docs/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":2543},{"i":2548,"t":"There are some community-driven projects that provide integration with frameworks for more native experience. tip In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. Integrating directly may allow using all Centrifugo features without limitations which can be introduced by third-party wrapper. laravel-centrifugo integration with Laravel framework laravel-centrifugo-broadcaster one more integration with Laravel framework to consider CentrifugoBundle integration with Symfony framework Django-instant integration with Django framework roadrunner-php/centrifugo integration with RoadRunner spiral/roadrunner-bridge integration with Spiral Framework","s":"Framework integrations","u":"/docs/getting-started/ecosystem","h":"#framework-integrations","p":2543},{"i":2550,"t":"On this page","s":"Main highlights","u":"/docs/getting-started/highlights","h":"","p":2549},{"i":2552,"t":"Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.). It works as a standalone service with well-defined communication contracts. It nicely fits both monolithic and microservice architectures. Application developers should not change the backend philosophy and technology stack at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.","s":"Simple integration","u":"/docs/getting-started/highlights","h":"#simple-integration","p":2549},{"i":2554,"t":"Centrifugo is fast. It's written in the Go language, built on top of fast and battle-tested open-source libraries, has some smart internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTTs with the broker, connection hub sharding to avoid lock contention, JSON and Protobuf encoding speedups through code generation, and others. See the Million WebSocket with Centrifugo post on our blog to see some real-world numbers.","s":"Great performance","u":"/docs/getting-started/highlights","h":"#great-performance","p":2549},{"i":2556,"t":"Centrifugo scales well to many machines with the help of PUB/SUB brokers. So as soon as you have more client connections in the application – you can spread them over different Centrifugo nodes which will be connected together into a cluster. The main PUB/SUB engine that Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck either. There are other options to scale: KeyDB, Nats, Tarantool. See docs about available engines.","s":"Built-in scalability","u":"/docs/getting-started/highlights","h":"#built-in-scalability","p":2549},{"i":2558,"t":"Centrifugo supports JSON and binary Protobuf protocols for client-server communication. The bidirectional protocol is defined by a strict schema and several ready-to-use SDKs wrap this protocol, handle asynchronous message passing, timeouts, reconnects, and various Centrifugo client API features. See detailed information about client real-time transports in a dedicated section.","s":"Strict client protocol","u":"/docs/getting-started/highlights","h":"#strict-client-protocol","p":2549},{"i":2560,"t":"The main transport in Centrifugo is WebSocket. For browsers that do not support WebSocket, Centrifugo provides its own bidirectional WebSocket emulation layer based on HTTP-streaming (using Fetch and Readable streams browser APIs) and SSE (EventSource). It also supports SockJS as an older but battle-tested WebSocket polyfill option. Additionally, WebTransport is supported in an experimental form. In addition to bidirectional transports, Centrifugo also supports a unidirectional approach for real-time updates: using SSE (EventSource), HTTP-streaming, and GRPC unidirectional stream. Utilizing a unidirectional transport is sufficient for many real-time applications and does not require using our client SDKs – just native standards or GRPC-generated code. See detailed information about client real-time transports in a dedicated section.","s":"Variety of real-time transports","u":"/docs/getting-started/highlights","h":"#variety-of-real-time-transports","p":2549},{"i":2562,"t":"Centrifugo can authenticate connections by checking JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon a client connection to Centrifugo. It's possible to proxy original request headers or request metadata (in the case of a GRPC connection). It supports the JWK specification.","s":"Flexible authentication","u":"/docs/getting-started/highlights","h":"#flexible-authentication","p":2549},{"i":2564,"t":"Connections can expire; developers can choose a way to handle connection refresh – using a client-side refresh workflow or a server-side call from Centrifugo to the application backend. Centrifugo provides APIs to disconnect users, unsubscribe users from channels, and inspect active channels.","s":"Connection management","u":"/docs/getting-started/highlights","h":"#connection-management","p":2549},{"i":2566,"t":"Centrifugo is a PUB/SUB server – users subscribe to channels to receive real-time updates. A message sent to a channel is delivered to all online channel subscribers.","s":"Channel (room) concept","u":"/docs/getting-started/highlights","h":"#channel-room-concept","p":2549},{"i":2568,"t":"Centrifugo supports client-side (initiated by the client) and server-side (forced by the server) channel subscriptions.","s":"Different types of subscriptions","u":"/docs/getting-started/highlights","h":"#different-types-of-subscriptions","p":2549},{"i":2570,"t":"You can fully utilize bidirectional connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing incoming traffic.","s":"RPC over bidirectional connection","u":"/docs/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":2549},{"i":2572,"t":"The online presence feature for channels provides information about active channel subscribers. Also, channel join and leave events (when someone subscribes/unsubscribes) can be received on the client side.","s":"Online presence information","u":"/docs/getting-started/highlights","h":"#online-presence-information","p":2549},{"i":2574,"t":"Optionally, Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With channel history, Centrifugo can help to survive the mass reconnect scenario – clients can automatically catch up on missed state from a fast cache thus reducing the load on your primary database. It's also possible to manually iterate over a history stream from the client or from the application backend side.","s":"Message history in channels","u":"/docs/getting-started/highlights","h":"#message-history-in-channels","p":2549},{"i":2576,"t":"The built-in admin UI allows publishing messages to channels, looking at Centrifugo cluster information, and more.","s":"Embedded admin web UI","u":"/docs/getting-started/highlights","h":"#embedded-admin-web-ui","p":2549},{"i":2578,"t":"Centrifugo works on Linux, macOS, and Windows.","s":"Cross-platform","u":"/docs/getting-started/highlights","h":"#cross-platform","p":2549},{"i":2580,"t":"Centrifugo supports various deployment methods: in Docker, using prepared RPM or DEB packages, via a Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, and has an official Grafana dashboard for the Prometheus data source.","s":"Ready to deploy","u":"/docs/getting-started/highlights","h":"#ready-to-deploy","p":2549},{"i":2582,"t":"Centrifugo stands on top of the open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All our official client SDKs and API libraries are MIT-licensed.","s":"Open-source","u":"/docs/getting-started/highlights","h":"#open-source","p":2549},{"i":2584,"t":"Centrifugo PRO extends Centrifugo with several unique features which can provide interesting advantages for business adopters. For additional details, refer to the Centrifugo PRO documentation.","s":"PRO features","u":"/docs/getting-started/highlights","h":"#pro-features","p":2549},{"i":2586,"t":"On this page","s":"Integration guide","u":"/docs/getting-started/integration","h":"","p":2585},{"i":2588,"t":"The first thing you need to do is download/install the Centrifugo server. See the install chapter for details.","s":"0. Install","u":"/docs/getting-started/integration","h":"#0-install","p":2585},{"i":2590,"t":"Create a basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set, and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and the chapter about server API for the API description. The simplest way to do this automatically is by using the genconfig command: ./centrifugo genconfig – which will generate config.json file for you with a minimal set of fields to start from. Properly configure allowed_origins option.","s":"1. Configure Centrifugo","u":"/docs/getting-started/integration","h":"#1-configure-centrifugo","p":2585},{"i":2592,"t":"In the configuration file of your application backend, register several variables: the Centrifugo token secret (if you decided to stick with JWT authentication) and the Centrifugo API key you set in the previous step, as well as the Centrifugo API endpoint address. By default, the API address is http://localhost:8000/api. You must never reveal the token secret and API key to your users.","s":"2. Configure your backend","u":"/docs/getting-started/integration","h":"#2-configure-your-backend","p":2585},{"i":2594,"t":"Now your users can start connecting to Centrifugo. You should get a client library (see the list of available client SDKs) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here. Every client should provide a connection token (JWT) upon connecting. You must generate this token on your backend side using the Centrifugo secret key you set in the backend configuration (note that in the case of RSA tokens, you are generating JWT with a private key). See how to generate this JWT in the special chapter. You pass this token from the backend to your frontend app (pass it in the template context or use a separate request from the client-side to get a user-specific JWT from the backend side). And use this token when connecting to Centrifugo (for example, the browser client has a special method setToken). There is also a way to authenticate connections without using JWT - see the chapter about proxying to the backend. You connect to Centrifugo using one of the available transports.","s":"3. Connect to Centrifugo","u":"/docs/getting-started/integration","h":"#3-connect-to-centrifugo","p":2585},{"i":2596,"t":"After connecting to Centrifugo, subscribe clients to channels they are interested in. See more about channels in the special chapter. All bidirectional client SDKs provide a way to handle messages coming to a client from a channel after subscribing to it. Learn more about client SDK possibilities from the client SDK API spec. There is also a way to subscribe a connection to a list of channels on the server side at the moment of connection establishment. See the chapter about server-side subscriptions.","s":"4. Subscribe to channels","u":"/docs/getting-started/integration","h":"#4-subscribe-to-channels","p":2585},{"i":2598,"t":"Everything should work now – as soon as a user opens some page of your application, they must successfully connect to Centrifugo and subscribe to a channel (or channels). Now let's imagine you want to send a real-time message to users subscribed to a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, an administrator just created a new post, a user pressed the \"like\" button, etc. Anyway, this is an event your backend has just received, and you want to immediately send it to interested users. You can do this using the Centrifugo HTTP API. To simplify your life, we have several API libraries for different languages. You can publish messages into a channel using one of those libraries, or you can simply follow the API description to construct API requests yourself - this is very simple. Centrifugo also supports a GRPC API. As soon as you have published a message to the channel, it must be delivered to your online clients subscribed to that channel.","s":"5. Publish to a channel","u":"/docs/getting-started/integration","h":"#5-publish-to-a-channel","p":2585},{"i":2600,"t":"To put all this into production, you need to deploy Centrifugo on your production server. To help you with this, we have many things like a Docker image, rpm and deb packages, Nginx configuration. See the Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.","s":"6. Deploy to production","u":"/docs/getting-started/integration","h":"#6-deploy-to-production","p":2585},{"i":2602,"t":"Don't forget to configure metrics monitoring for your production Centrifugo setup. This may help you to understand what's going on with your Centrifugo setup, understand the number of connections, operation count, and latency distributions, etc.","s":"7. Monitor Centrifugo","u":"/docs/getting-started/integration","h":"#7-monitor-centrifugo","p":2585},{"i":2604,"t":"As soon as you are close to machine resource limits, you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using the Redis engine, or with KeyDB, or with Tarantool, or with a Nats broker. The Engines and scalability chapter describes available options in detail.","s":"8. Scale Centrifugo","u":"/docs/getting-started/integration","h":"#8-scale-centrifugo","p":2585},{"i":2606,"t":"That's all for the basics. The documentation actually covers a lot of other concepts Centrifugo server has: scalability, private channels, the admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ – it contains a lot of useful information.","s":"9. Read FAQ","u":"/docs/getting-started/integration","h":"#9-read-faq","p":2585},{"i":2608,"t":"On this page","s":"Centrifugo introduction","u":"/docs/getting-started/introduction","h":"","p":2607},{"i":2610,"t":"Centrifugo was born more than a decade ago to help applications whose server-side code was written in a language or framework lacking built-in concurrency support. In such cases, managing persistent connections can be a real headache, usually resolvable only by altering the technology stack and investing time in developing a production-ready solution. For instance, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others offer limited or suboptimal support for handling numerous persistent connections for real-time messaging tasks. Here, Centrifugo provides a straightforward and non-obtrusive way to introduce real-time updates and manage many persistent connections without radical changes in the application backend architecture. Developers can continue to work on the application's backend using their preferred language or framework, and keep the existing architecture. Just let Centrifugo deal with persistent connections and be the real-time messaging transport layer. These days, Centrifugo offers advanced and unique features that can significantly simplify a developer's workload and save months (if not years) of development time, even if the application's backend is built with an asynchronous concurrent language or framework. One example is Centrifugo's built-in support for scaling across numerous machines to accommodate more connections while ensuring that channel subscribers on different Centrifugo nodes receive all publications. Or the fact that Centrifugo has a bunch of real-time SDKs which provide subscription multiplexing over a WebSocket connection, robust reconnect logic, built-in ping-pong, etc. And there are more things to mention: the documentation uncovers features step by step. Centrifugo fits well with modern architectures and can serve as a universal real-time component, regardless of the application's technology stack. It stands as a viable self-hosted alternative to cloud solutions like Pusher, Ably, or PubNub.","s":"Background","u":"/docs/getting-started/introduction","h":"#background","p":2607},{"i":2612,"t":"On this page","s":"Install Centrifugo","u":"/docs/getting-started/installation","h":"","p":2611},{"i":2614,"t":"For local development, you can download the prebuilt Centrifugo binary release (i.e., a single all-contained executable file) for your system. Binary releases are available on Github. Download the latest release for your operating system, unpack it, and you are done. Centrifugo is pre-built for: Linux 64-bit (linux_amd64) Linux 32-bit (linux_386) Linux ARM 64-bit (linux_arm64) MacOS (darwin_amd64) MacOS on Apple Silicon (darwin_arm64) Windows (windows_amd64) FreeBSD (freebsd_amd64) ARM v6 (linux_armv6) Archives contain a single statically compiled binary centrifugo file that is ready to run. ./centrifugo If you are unsure which distribution you need, then on Linux or MacOS you can use the following command to download and unpack the centrifugo binary to your current working directory: curl -sSLf https://centrifugal.dev/install.sh | sh See the version of Centrifugo: ./centrifugo version Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo, then there is a genconfig command which generates a minimal configuration file to get started: ./centrifugo genconfig This creates a configuration file config.json with some auto-generated option values in the current directory (by default). tip It's possible to generate a file in YAML or TOML format, for example: ./centrifugo genconfig -c config.toml With a configuration file, you can finally run a Centrifugo instance: ./centrifugo --config=config.json We will talk about configuration in detail in the next sections. You can also put or symlink centrifugo into your bin OS directory and run it from anywhere: centrifugo --config=config.json","s":"Install from the binary release","u":"/docs/getting-started/installation","h":"#install-from-the-binary-release","p":2611},{"i":2616,"t":"Centrifugo server has a docker image available on Docker Hub. docker pull centrifugo/centrifugo Run: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo:v5 centrifugo -c config.json Note that docker allows setting nofile limits in command-line arguments, which is quite important to handle many simultaneous persistent connections and not run out of the open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter. caution Pin to the exact Docker Image tag in production, for instance: centrifugo/centrifugo:v5.0.0. This will help to avoid unexpected problems during redeployment process.","s":"Docker image","u":"/docs/getting-started/installation","h":"#docker-image","p":2611},{"i":2618,"t":"Create configuration file config.json: { \"token_hmac_secret_key\": \"my_secret\", \"api_key\": \"my_api_key\", \"admin_password\": \"password\", \"admin_secret\": \"secret\", \"admin\": true} Create docker-compose.yml: version: \"3.9\"services: centrifugo: container_name: centrifugo image: centrifugo/centrifugo:v5 volumes: - ./config.json:/centrifugo/config.json command: centrifugo -c config.json ports: - 8000:8000 ulimits: nofile: soft: 65535 hard: 65535 Run with: docker-compose up","s":"Docker-compose example","u":"/docs/getting-started/installation","h":"#docker-compose-example","p":2611},{"i":2620,"t":"See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.","s":"Kubernetes Helm chart","u":"/docs/getting-started/installation","h":"#kubernetes-helm-chart","p":2611},{"i":2622,"t":"Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io. At moment, we support versions of the following distributions: 64-bit Debian 10 Buster 64-bit Debian 11 Bullseye 64-bit Ubuntu 18.04 Bionic 64-bit Ubuntu 20.04 Focal Fossa 64-bit Ubuntu 20.04 Jammy 64-bit Centos 7 See full list of available packages and installation instructions. Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.","s":"RPM and DEB packages for Linux","u":"/docs/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":2611},{"i":2624,"t":"If you are developing on macOS then you can install Centrifugo over brew: brew tap centrifugal/centrifugobrew install centrifugo","s":"With brew on macOS","u":"/docs/getting-started/installation","h":"#with-brew-on-macos","p":2611},{"i":2626,"t":"You need Go language installed. Then: git clone https://github.com/centrifugal/centrifugo.gitcd centrifugogo build./centrifugo","s":"Build from source","u":"/docs/getting-started/installation","h":"#build-from-source","p":2611},{"i":2628,"t":"On this page","s":"Migrating to v4","u":"/docs/getting-started/migration_v4","h":"","p":2627},{"i":2630,"t":"New generation of client protocol requires using the latest versions of client SDKs. During the next several days we will release the following SDK versions which are compatible with Centrifugo v4: centrifuge-js >= v3.0.0 centrifuge-go >= v0.9.0 centrifuge-dart >= v0.9.0 centrifuge-swift >= v0.5.0 centrifuge-java >= v0.2.0 New client SDKs support only new client protocol – you can not connect to Centrifugo v3 with them. If you have a production system where you want to upgrade Centrifugo from v3 to v4 then the plan is: danger If you are using private channels (starting with $) or user-limited channels (containing #) then carefully read about subscription token migration and user-limited channels migration below. Upgrade Centrifugo and its configuration to adopt changes in v4. In Centrifugo v4 config turn on use_client_protocol_v1_by_default. Run Centrifugo v4 – all current clients should continue working with it. Then on the client-side uprade client SDK version to the one which works with Centrifugo v4, adopt changes in SDK API dictated by our new client SDK API spec. Important thing – add ?cf_protocol_version=v2 URL param to the connection endpoint to tell Centrifugo that modern generation of protocol is being used by the connection (otherwise, it assumes old protocol since we have use_client_protocol_v1_by_default option enabled). As soon as all your clients migrated to use new protocol generation you can remove use_client_protocol_v1_by_default option from the server configuration. After that you can remove ?cf_protocol_version=v2 from connection endpoint on the client-side. tip If you are using mobile client SDKs then most probably some time must pass while clients update their apps to use an updated Centrifugo SDK version. tip Starting from Centrifugo v4.1.1 it's possible to completely turn off client protocol v1 by setting disable_client_protocol_v1 boolean option to true.","s":"Client SDK migration","u":"/docs/getting-started/migration_v4","h":"#client-sdk-migration","p":2627},{"i":2632,"t":"Client protocol framing also changed in unidirectional transports. The good news is that Centrifugo v4 still supports previous format for unidirectional transports. When you are enabling use_client_protocol_v1_by_default option described above you also make unidirectional transports to work over old protocol format. So your existing clients will continue working just fine with Centrifugo v4. Then the same steps to migrate described above can be applied to unidirectional transport case. The only difference that in unidirectional approach you are not using Centrifugo SDKs.","s":"Unidirectional transport migration","u":"/docs/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":2627},{"i":2634,"t":"SockJS is now DEPRECATED in Centrifugo. Centrifugo v4 may be the last release which supports it. We now offer our own bidirectional emulation layer on top of HTTP-streaming and EventSource. See additional information in Centrifugo v4 introduction post.","s":"SockJS migration","u":"/docs/getting-started/migration_v4","h":"#sockjs-migration","p":2627},{"i":2636,"t":"Centrifugo v2 and v3 docs mentioned the fact that channels must contain only ASCII characters. But it was not actually enforced by a server. Now Centrifugo is more strict. If a channel has non-ASCII characters then the 107: bad request error will be returned to the client. Please reach us out if this behavior is not suitable for your use case – we can discuss the use case and think on a proper solution together.","s":"Channel ASCII enforced","u":"/docs/getting-started/migration_v4","h":"#channel-ascii-enforced","p":2627},{"i":2638,"t":"Subscription token now requires sub claim (current user ID) to be set. In most cases the only change which is required to smoothly migrate to v4 without breaking things is to add a boolean option \"skip_user_check_in_subscription_token\": true to a Centrifugo v4 configuration. This skips the check of sub claim to contain the current user ID set to a connection during authentication. After that start adding sub claim (with current user ID) to subscription tokens. As soon as all subscription tokens in your system contain user ID in sub claim you can remove the skip_user_check_in_subscription_token from a server configuration. One more important note is that client claim in subscription token in Centrifugo v4 only supported for backwards compatibility. It must not be included into new subscription tokens. It's worth mentioning that Centrifugo v4 does not allow subscribing on channels starting with $ without token even if namespace marked as available for subscribing using sth like allow_subscribe_for_client option. This is done to prevent potential security risk during v3 -> v4 migration when client previously not available to subscribe to channels starting with $ in any case may get permissions to do so.","s":"Subscription token migration","u":"/docs/getting-started/migration_v4","h":"#subscription-token-migration","p":2627},{"i":2640,"t":"User-limited channel support should now be allowed over a separate channel namespace option allow_user_limited_channels. See below the namespace option converter which takes this change into account.","s":"User-limited channel migration","u":"/docs/getting-started/migration_v4","h":"#user-limited-channel-migration","p":2627},{"i":2642,"t":"In Centrifugo v4 namespace configuration options have been changed. Centrifugo now has secure by default namespaces. First thing to do is to read the new docs about channels and namespaces. Then you can use the following converter which will transform your old namespace configuration to a new one. This converter tries to keep backwards compatibility – i.e. it should be possible to deploy Centrifugo with namespace configuration from converter output and have the same behaviour as before regarding channel permissions. We believe that new option names should provide a more readable configuration and may help to reveal some potential security improvements in your namespace configuration – i.e. making it more strict and protective. caution Do not blindly deploy things to production – test your system first, go through the possible usage scenarios and/or test cases. tip It's fully client-side: your data won't be sent anywhere. Convert Here will be configuration for v4 Here will be log of changes made in your config","s":"Namespace configuration migration","u":"/docs/getting-started/migration_v4","h":"#namespace-configuration-migration","p":2627},{"i":2644,"t":"reconnect flag from custom disconnect code is removed. Reconnect advice is now determined by disconnect code value. This allowed us avoiding using JSON in WebSocket CLOSE frame reason. See proxy docs docs for more details.","s":"Proxy disconnect code changes","u":"/docs/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":2627},{"i":2646,"t":"Several other non-namespace related options have been renamed or removed: client_anonymous option renamed to allow_anonymous_connect_without_token – new name better describes the purpose of this option which was previously not clear. Converter above takes this into account. use_unlimited_history_by_default option was removed. It was used to help migrating from Centrifugo v2 to v3.","s":"Other configuration option changes","u":"/docs/getting-started/migration_v4","h":"#other-configuration-option-changes","p":2627},{"i":2648,"t":"The only breaking change is that user_connections API method (which is available in Centrifugo PRO only) was renamed to connections. The method is more generic now with a broader possibilities – so previous name does not match the current behavior.","s":"Server API changes","u":"/docs/getting-started/migration_v4","h":"#server-api-changes","p":2627},{"i":2650,"t":"On this page","s":"Migrating to v5","u":"/docs/getting-started/migration_v5","h":"","p":2649},{"i":2652,"t":"In Centrifugo v5 client SDK spec was adjusted in regards how SDKs work with tokens. Returning an empty string from getToken function (for Javascript, and the same for analogous functions in other SDKs) is a valid scenario which won't result into disconnect on the client side. It's still possible to disconnect client by returning a special error from getToken function. We updated all our SDKs to inherit this behaviour. Specifically, here is a list of SDK versions which work according to adjusted spec: centrifuge-js >= v4.0.0 centrifuge-go >= v0.10.0 centrifuge-dart >= v0.10.0 centrifuge-swift >= v0.6.0 centrifuge-java >= v0.3.0 Nothing prevents you from updating Centrifugo v4 to v5 first and then migrate to new client versions or doing vice versa. This change is client-side only. But we bind it to major server release to make it more notable – as it changes the core SDK behavior.","s":"Client SDK token behaviour adjustments","u":"/docs/getting-started/migration_v5","h":"#client-sdk-token-behaviour-adjustments","p":2649},{"i":2654,"t":"Avoid running Centrifugo v5 in the same cluster with Centrifugo v4 nodes – v4 and v5 have backwards incompatible node communication protocols.","s":"Node communication format changed","u":"/docs/getting-started/migration_v5","h":"#node-communication-format-changed","p":2649},{"i":2656,"t":"Prefer using new HTTP API format instead of old one where possible. The old format still works and enabled by default. But we are planning to migrate our API libraries to the new format eventually – and then remove the old format. If you are using one of our HTTP API libs - at some point a new version will be released which will seamlessly migrate you to the modern HTTP API format. If you are using hand-written requests – then some refactoring is required. It should be rather straighforward: replace request path from /api to /api/ replace payload from having method and params on top level. Payload does not include method and params keys anymore. Please refer to: https://centrifugal.dev/blog/2023/06/29/centrifugo-v5-released#new-http-api-format prefer using X-API-Key: Centrifugo quick start
    -
    Note that we are using centrifuge-js 5.0.1 in this example, getting it from a CDN. You should use its latest version at the moment of reading this tutorial. In a real Javascript app, you would most likely load centrifuge from NPM. In the index.html above, we created an instance of a Centrifuge client by passing the Centrifugo server's default WebSocket endpoint address to it. Then we subscribed to a channel called channel and provided a callback function to process incoming real-time messages (publications). Upon receiving a new publication, we update the page's HTML by setting the counter value to the page title. We call .subscribe() to initiate the subscription and the .connect() method of the Client to start a WebSocket connection. We also handle Client state transitions (disconnected, connecting, connected) and Subscription state transitions (unsubscribed, subscribing, subscribed) – see a detailed description in client SDK spec. Now you need to serve this file with an HTTP server. In a real-world Javascript application, you would serve your HTML files with a web server of your choice – but for this simple example, we can use a simple built-in Centrifugo static file server: ./centrifugo serve --port 3000 Alternatively, if you have Python 3 installed: python3 -m http.server 3000 These commands start a simple static file web server that serves the current directory on port 3000. Make sure you still have Centrifugo server running. Open http://localhost:3000/. Now if you look at browser developer tools or in Centrifugo logs you will notice that a connection can not be successfully established: 2021-09-01 10:17:33 [INF] request Origin is not authorized due to empty allowed_origins origin=http://localhost:3000 That's because we have not set allowed_origins in the configuration. Modify allowed_origins like this: config.json { ... \"allowed_origins\": [\"http://localhost:3000\"]} Allowed origins is a security option for request originating from web browsers – see more details in server configuration docs. Restart Centrifugo after modifying allowed_origins in a configuration file. Now if you reload a browser window with an application you should see new information logs in server output: 2022-06-10 09:44:21 [INF] invalid connection token error=\"invalid token: token format is not valid\" client=a65a8463-6a36-421d-814a-0083c88365292022-06-10 09:44:21 [INF] disconnect after handling command client=a65a8463-6a36-421d-814a-0083c8836529 command=\"id:1 connect:{token:\\\"\\\" name:\\\"js\\\"}\" reason=\"invalid token\" user= We still cannot connect. That's because the client must provide a valid JWT (JSON Web Token) to authenticate itself. This token must be generated on your backend and passed to the client-side (over template variables or using a separate AJAX call – whichever way you prefer). Since in our simple example we don't have an application backend, we can quickly generate an example token for a user using the centrifugo sub-command gentoken, like this: ./centrifugo gentoken -u 123722 – where -u flag sets user ID. The output should be like this: HMAC SHA-256 JWT for user \"123722\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw – you will have a different token value since this one is based on the randomly generated token_hmac_secret_key from the configuration file we created at the beginning of this tutorial. See the token authentication docs for information about proper token generation in a real application. Now we can copy generated HMAC SHA-256 JWT and paste it into Centrifugo constructor instead of placeholder in index.html file. I.e.: const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw\"}); If you reload your browser tab – the connection will be successfully established, but the client still cannot subscribe to a channel: 2022-06-10 09:45:49 [INF] client command error error=\"permission denied\" client=88116489-350f-447f-9ff3-ab61c9341efe code=103 command=\"id:2 subscribe:{channel:\\\"channel\\\"}\" reply=\"id:2 error:{code:103 message:\\\"permission denied\\\"}\" user=123722 We need to give the client permission to subscribe to the channel channel. There are several ways to do this. For example, the client can provide a subscription JWT for a channel. But here we will use an option to allow all authenticated clients to subscribe to any channel. To do this let's extend the server configuration with the allow_subscribe_for_client option: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin\": true, \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": [\"http://localhost:3000\"], \"allow_subscribe_for_client\": true} tip A good practice with Centrifugo is to configure channel namespaces for the different types of real-time features you have in the application. By defining namespaces, you can achieve granular control over channel behavior and permissions. Restart Centrifugo – and after doing this, everything should start working. The client can now successfully connect and subscribe to a channel. Open the developer tools and look at the WebSocket frames panel; you should see something like this: Note that in this example, we generated a connection JWT – but it has an expiration time, so after some time, Centrifugo will stop accepting those tokens. In real life, you need to add a token refresh function to the client to rotate tokens. See our client API SDK spec. OK, the last thing we need to do here is to publish a new counter value to a channel and make sure our app works properly. We can do this over the Centrifugo API by sending an HTTP request to the default API endpoint http://localhost:8000/api, but let's do this over the admin web panel first. Open the Centrifugo admin web panel in another browser tab (http://localhost:8000/) and go to the Actions section. Select the publish action, insert the channel name that you want to publish to – in our case, this is the string channel – and insert into the data area JSON like this: { \"value\": 1} Click PUBLISH button and check out the application browser tab – counter value must be immediately received and displayed. Open several browser tabs with our app and make sure all tabs receive a message as soon as you publish it. BTW, let's also look at how you can publish data to a channel over Centrifugo server API from a terminal using curl tool: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: d7627bb6-2292-4911-82e1-615c0ed3eebb\" \\ --request POST \\ --data '{\"channel\": \"channel\", \"data\": {\"value\": 2}}' \\ http://localhost:8000/api/publish – where for Authorization header we set api_key value from Centrifugo config file generated above. We did it! We built the simplest browser real-time app with Centrifugo and its JavaScript client. It doesn't have a backend; it's not very useful, to be honest, but it should give you an insight into how to start working with the Centrifugo server. Read more about the Centrifugo server in the next documentation chapters – it can do much more than we just showed here. The Integration guide describes the process of idiomatic Centrifugo integration with your application backend.","s":"Quickstart tutorial ⏱️","u":"/docs/getting-started/quickstart","h":"","p":2661},{"i":2664,"t":"On this page","s":"Channel capabilities","u":"/docs/pro/capabilities","h":"","p":2663},{"i":2666,"t":"Connection capabilities can be set: in connection JWT (in caps claim) in connect proxy result (caps field) For example, here we are issuing permissions to subscribe on channel news and channel user_42 to a client: { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] } ]} Known capabilities: sub - subscribe to a channel to receive publications from it pub - publish into a channel (your backend won't be able to process the publication in this case) prs - call presence and presence stats API, also consume join/leave events upon subscribing hst - call history API, also make Subscription positioned or recoverable upon subscribing","s":"Connection capabilities","u":"/docs/pro/capabilities","h":"#connection-capabilities","p":2663},{"i":2668,"t":"Centrifugo processes caps objects till it finds a match to a channel. At this point it applies permissions in the matched object and stops processing remaining caps. If no match found – then 103 permission denied returned to a client (of course if namespace does not have other permission-related options enabled). Let's consider example like this: WRONG! { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"pub\"] }, { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, ]} Here we have two entries for channel news, but when client subscribes on news only the first entry will be taken into considiration by Centrifugo – so Subscription attempt will be rejected (since first cap object does not have sub capability). In real life you don't really want to have cap objects with identical channels – but below we will introduce wildcard matching where understanding how caps processed becomes important. Another example: WRONG! { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"pub\", \"hst\", \"prs\"] }, ]} One could expect that client will have [\"sub\", \"pub\", \"hst\", \"prs\"] capabilities for a channel user_42. But it's not true since Centrifugo processes caps objects and channels inside caps object in order – it finds a match to user_42 in first caps object, it contains only \"sub\" capability, processing stops. So user can subscribe to a channel, but can not publish, can not call history and presence APIs even though those capabilities are mentioned in caps object. The correct way to give all caps to the channel user_42 would be to split channels into different caps objects: CORRECT { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] }, ]} The processing behaves like this to avoid potential problems with possibly conflicting matches (mostly when using wildcard and regex matching – see below) and still allow overriding capabilities for specific channels.","s":"Caps processing behavior","u":"/docs/pro/capabilities","h":"#caps-processing-behavior","p":2663},{"i":2670,"t":"In JWT auth case – capabilities in JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In connect proxy case – capabilities will work until client connection close (disconnect) or connection refresh triggered (with refresh proxy you can provide an updated set of capabilities).","s":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations","p":2663},{"i":2672,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a connection expiration, then upon refresh: if using proxy – provide new caps in refresh proxy result, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). if JWT auth - provide new caps in connection token, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). In case of using connect proxy – you can disconnect a user (or client) with a reconnect code. New capabilities will be asked upon reconnection. In case of using token auth – revoke token (Centrifugo PRO feature) and disconnect user (or client) with reconnect code. Upon reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking connection caps","u":"/docs/pro/capabilities","h":"#revoking-connection-caps","p":2663},{"i":2674,"t":"It's possible to use wildcards in channel resource names. For example, let's give a permission to subscribe on all channels in news namespace. { \"caps\": [ { \"channels\": [\"news:*\"], \"match\": \"wildcard\", \"allow\": [\"sub\"] } ]} note Match type is used for all channels in caps object. If you need different matching behavior for different channels then split them on different caps objects.","s":"Example: wildcard match","u":"/docs/pro/capabilities","h":"#example-wildcard-match","p":2663},{"i":2676,"t":"Or regex: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } ]}","s":"Example: regex match","u":"/docs/pro/capabilities","h":"#example-regex-match","p":2663},{"i":2678,"t":"Of course it's possible to combine different types of match inside one caps array: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } { \"channels\": [\"user_42\"], \"allow\": [\"sub\"] } ]}","s":"Example: different types of match","u":"/docs/pro/capabilities","h":"#example-different-types-of-match","p":2663},{"i":2680,"t":"Let's look how to allow all permissions to a client: { \"caps\": [ { \"channels\": [\"*\"], \"match\": \"wildcard\", \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] } ]} Full access warn Should we mention that giving full access to a client is something to wisely consider? 🤔","s":"Example: full access to all channels","u":"/docs/pro/capabilities","h":"#example-full-access-to-all-channels","p":2663},{"i":2682,"t":"Subscription capabilities can be set: in subscription JWT (in allow claim) in subscribe proxy result (allow field) Subscription token already belongs to a channel (it has a channel claim). So users with a valid subscription token can subscribe to a channel. But it's possible to additionally grant channel permissions to a user for publishing and calling presence and history using allow claim: { \"allow\": [\"pub\", \"hst\", \"prs\"]} Putting sub permission to the Subscription token does not make much sense – Centrifugo only expects valid token for a subscription permission check.","s":"Subscription capabilities","u":"/docs/pro/capabilities","h":"#subscription-capabilities","p":2663},{"i":2684,"t":"In JWT auth case – capabilities in subscription JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In subscribe proxy case – capabilities will work until client unsubscribe (or connection close).","s":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations-1","p":2663},{"i":2686,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a subscription expiration, then upon refresh: provide new caps in subscription token, Centrifugo will update channel caps. In case of using subscribe proxy – you can unsubscribe a user (or client) with a resubscribe code. Or disconnect with reconnect code. New capabilities will be set up upon resubscription/reconnection. In case of using JWT auth – revoke token (Centrifugo PRO feature) and unsubscribe/disconnect user (or client) with resubscribe/reconnect code. Upon resubscription/reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking subscription permissions","u":"/docs/pro/capabilities","h":"#revoking-subscription-permissions","p":2663},{"i":2688,"t":"On this page","s":"CEL expressions","u":"/docs/pro/cel_expressions","h":"","p":2687},{"i":2690,"t":"We suppose that the main operation for which developers may use CEL expressions in Centrifugo is a subscribe operation. Let's look at it in detail. It's possible to configure subscribe_cel for a channel namespace (subscribe_cel is just an additional namespace channel option, with same rules applied). This expression should be a valid CEL expression. config.json { \"namespaces\": [ { \"name\": \"admin\", \"subscribe_cel\": \"'admin' in meta.roles\" } ]} In the example we are using custom meta information (must be an object) attached to the connection. As mentioned before in the doc this meta may be attached to the connection: when set in the connect proxy result or provided in JWT as meta claim An expression is evaluated for every subscription attempt to a channel in a namespace. So if meta attached to the connection is sth like this: { \"roles\": [\"admin\"]} – then for every channel in the admin namespace defined above expression will be evaluated to True and subscription will be accepted by Centrifugo. tip meta must be JSON object (any {}) for CEL expressions to work.","s":"subscribe_cel","u":"/docs/pro/cel_expressions","h":"#subscribe_cel","p":2687},{"i":2692,"t":"Inside the expression developers can use some variables which are injected by Centrifugo to the CEL runtime. Information about current user ID, meta information attached to the connection, all the variables defined in matched channel pattern will be available for CEL expression evaluation. Say client with user ID 123 subscribes to a channel /users/4 which matched the channel pattern /users/:user: Variable Type Example Description subscribed bool false Whether client is subscribed to channel, always false for subscribe operation user string \"123\" Current authenticated user ID (known from from JWT or connect proxy result) meta map[string]any {\"roles\": [\"admin\"]} Meta information attached to the connection by the apllication backend (in JWT or over connect proxy result) channel string \"/users/4\" Channel client tries to subscribe vars map[string]string {\"user\": \"4\"} Extracted variables from the matched channel pattern. It's empty in case of using channels without variables. In this case, to allow admin to subscribe on any user's channel or allow non-admin user to subscribe only on its own channel, you may construct an expression like this: { ... \"subscribe_cel\": \"vars.user == user or 'admin' in meta.roles\"} Let's look at one more example. Say client with user ID 123 subscribes to a channel /example.com/users/4 which matched the channel pattern /:tenant/users/:user. The permission check may be transformed into sth like this (assuming meta information has information about current connection tenant): { \"namespaces\": [ { \"name\": \"/:tenant/users/:user\", \"subscribe_cel\": \"vars.tenant == meta.tenant && (vars.user == user or 'admin' in meta.roles)\" } ]}","s":"Expression variables","u":"/docs/pro/cel_expressions","h":"#expression-variables","p":2687},{"i":2694,"t":"CEL expression to check permissions to publish into a channel. Same expression variables are available.","s":"publish_cel","u":"/docs/pro/cel_expressions","h":"#publish_cel","p":2687},{"i":2696,"t":"CEL expression to check permissions for channel history. Same expression variables are available.","s":"history_cel","u":"/docs/pro/cel_expressions","h":"#history_cel","p":2687},{"i":2698,"t":"CEL expression to check permissions for channel presence. Same expression variables are available.","s":"presence_cel","u":"/docs/pro/cel_expressions","h":"#presence_cel","p":2687},{"i":2700,"t":"On this page","s":"Analytics with ClickHouse","u":"/docs/pro/analytics","h":"","p":2699},{"i":2702,"t":"To enable integration with ClickHouse add the following section to a configuration file: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\", \"tcp://127.0.0.1:9001\", \"tcp://127.0.0.1:9002\", \"tcp://127.0.0.1:9003\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"centrifugo_cluster\", \"export_connections\": true, \"export_subscriptions\": true, \"export_operations\": true, \"export_publications\": true, \"export_notifications\": true, \"export_http_headers\": [ \"User-Agent\", \"Origin\", \"X-Real-Ip\" ] }} All ClickHouse analytics options scoped to clickhouse_analytics section of configuration. Toggle this feature using enabled boolean option. tip While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo. Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option. You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database. export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available. export_subscriptions tells Centrifugo to export subscription information snapshots. Information about subscription will be exported once a subscription established and then periodically while connection alive. See below on table structure to see which fields are available. export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available. export_publications tells Centrifugo to export publications for channels to a separate ClickHouse table. export_notifications tells Centrifugo to export push notifications to a separate ClickHouse table. export_http_headers is a list of HTTP headers to export for connection information. export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport. skip_schema_initialization - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process. skip_ping_on_start - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.","s":"Configuration","u":"/docs/pro/analytics","h":"#configuration","p":2699},{"i":2704,"t":"SHOW CREATE TABLE centrifugo.connections;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└─────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.connections_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections_distributed( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Connections table","u":"/docs/pro/analytics","h":"#connections-table","p":2699},{"i":2706,"t":"SHOW CREATE TABLE centrifugo.subscriptions┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.subscriptions_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions_distributed( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'subscriptions', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Subscriptions table","u":"/docs/pro/analytics","h":"#subscriptions-table","p":2699},{"i":2708,"t":"SHOW CREATE TABLE centrifugo.operations;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.operations_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Operations table","u":"/docs/pro/analytics","h":"#operations-table","p":2699},{"i":2710,"t":"SHOW CREATE TABLE centrifugo.publications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.publications( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.publications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'publications', murmurHash3_64(channel)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Publications table","u":"/docs/pro/analytics","h":"#publications-table","p":2699},{"i":2712,"t":"🚧 This PRO feature is under construction together with push notification API. SHOW CREATE TABLE centrifugo.notifications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.notifications( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.notifications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'notifications', murmurHash3_64(uid)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Notifications table","u":"/docs/pro/analytics","h":"#notifications-table","p":2699},{"i":2714,"t":"Show unique users which were connected: SELECT DISTINCT userFROM centrifugo.connections_distributed;┌─user─────┐│ user_1 ││ user_2 ││ user_3 ││ user_4 ││ user_5 │└──────────┘ Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111): SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish');┌─count()─┐│ 4502 │└─────────┘ The same for a specific user: SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');┌─count()─┐│ 1214 │└─────────┘ Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since subscriptions table contain periodic snapshot entries, clients could unsubscribe in between snapshots – this is reflected in operations table): SELECT COUNT(Distinct(user))FROM centrifugo.subscriptions_distributedWHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));┌─uniqExact(user)─┐│ 101 │└─────────────────┘ Show top 10 users which called publish operation during last one minute: SELECT COUNT(op) AS num_ops, userFROM centrifugo.operations_distributedWHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))GROUP BY userORDER BY num_ops DESCLIMIT 10;┌─num_ops─┬─user─────┐│ 56 │ user_200 ││ 11 │ user_75 ││ 6 │ user_87 ││ 6 │ user_65 ││ 6 │ user_39 ││ 5 │ user_28 ││ 5 │ user_63 ││ 5 │ user_89 ││ 3 │ user_32 ││ 3 │ user_52 │└─────────┴──────────┘ Show total number of push notifications to iOS devices sent during last 24 hours: SELECT COUNT(*)FROM centrifugo.notificationsWHERE (time > (now() - toIntervalHour(24))) AND (platform = 'ios')┌─count()─┐│ 31200 │└─────────┘","s":"Query examples","u":"/docs/pro/analytics","h":"#query-examples","p":2699},{"i":2716,"t":"The recommended way to run ClickHouse in production is with cluster. See an example of such cluster configuration made with Docker Compose. But during development you may want to run Centrifugo with single instance ClickHouse. To do this set only one ClickHouse dsn and do not set cluster name: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"\", \"export_connections\": true, \"export_subscriptions\": true, \"export_publications\": true, \"export_operations\": true, \"export_http_headers\": [ \"Origin\", \"User-Agent\" ] }} Run ClickHouse locally: docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click clickhouse/clickhouse-server Run ClickHouse client: docker run -it --rm --link click:clickhouse-server --entrypoint clickhouse-client clickhouse/clickhouse-server --host clickhouse-server Issue queries: :) SELECT * FROM centrifugo.operations┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘","s":"Development","u":"/docs/pro/analytics","h":"#development","p":2699},{"i":2718,"t":"When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 10 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are configurable. Centrifugo PRO uses very efficient code for writing data to ClickHouse, so analytics feature should only add a little overhead for Centrifugo node.","s":"How export works","u":"/docs/pro/analytics","h":"#how-export-works","p":2699},{"i":2720,"t":"Several metrics are exposed to monitor export process health: centrifugo_clickhouse_analytics_drop_count​ Type: Counter Labels: type Description: Total count of drops. Usage: Useful for tracking the number of data drops in ClickHouse analytics, helping identify potential issues with data processing. centrifugo_clickhouse_analytics_flush_duration_seconds​ Type: Summary Labels: type, retries, result Description: Duration of ClickHouse data flush in seconds. Usage: Helps in monitoring the performance of data flush operations in ClickHouse, aiding in performance tuning and issue resolution. centrifugo_clickhouse_analytics_batch_size​ Type: Summary Labels: type Description: Distribution of batch sizes for ClickHouse flush. Usage: Useful for understanding the size of data batches being flushed to ClickHouse, helping optimize performance.","s":"Exposed metrics","u":"/docs/pro/analytics","h":"#exposed-metrics","p":2699},{"i":2722,"t":"Channel state events Centrifugo PRO has a feature to enable channel state event webhooks to be sent to your configured backend endpoint: channel occupied event - called whenever first subscriber occipies the channel channel vacated event - called whenever last subscriber leaves the channel Preview state This feature is in the preview state now. We still need some time before it will be ready for usage in production. But starting from Centrifugo PRO v5.1.1 the feature is avalable for evaluation. To enable the feature you must use redis engine. Also, only channels with presence enabled and channel_state_events explicitly configured may deliver channel state notifications. When enabling channel state proxy Centrifugo PRO starts using another approach to create Redis keys for presence for namespaces where channel state events enabled, this is an important implementation detail. So the minimal config can look like this (occupied and vacated events for channels in chat namespace will be sent to proxy_channel_state_endpoint): config.json { ... \"engine\": \"redis\", \"proxy_channel_state_endpoint\": \"http://localhost:3000/centrifugo/channel_events\", \"namespaces\": [ { \"name\": \"chat\", \"presence\": true, \"channel_state_events\": [\"occupied\", \"vacated\"] } ]} The proxy endpoint is an extension of Centrifugo OSS proxy and proto definitions may be found in the same proxy.proto file - see NotifyChannelState rpc. Example of the payload your backend request handler will receive: { \"events\": [ {\"channel\": \"chat:index\", \"type\": \"occupied\", \"time_ms\": 1697206286533}, ]} Payload may contain a batch of events, that's why events is an array – this is important for achieving a high event throughput. Your backend must be fast enough to keep up with the events rate and volume, otherwise event queues will grow and eventually new events will be dropped by Centrifugo PRO. Respond with empty object without error set to let Centrifugo PRO know that events were processed successfully. If last channel client resubscribes to a channel fast (during 5 secs) – then Centrifugo PRO won't send vacated events. If client does not resubscribe during 5 secs - event will be sent. So vacated events always delivered with a delay. This is implemented in such way to avoid unnecessary webhooks for quick reconnect scenarios. Centrifugo PRO does the best effort delivering channel state events, making retries when the backend endpoint is unavailable (with exponential backoff), also survives cases when Centrifugo node dies unexpectedly. But there are rare scenarios, when notifications may be lost – like when data lost in Redis. For such cases we recommend syncing the state periodically looking at channel presence information using server API – this depends on the use case though.","s":"Channel state events","u":"/docs/pro/channel_state_events","h":"","p":2721},{"i":2724,"t":"On this page","s":"Channel patterns","u":"/docs/pro/channel_patterns","h":"","p":2723},{"i":2726,"t":"Let's look at the example: { // rest of the config ... \"channel_patterns\": true, // required to turn on the feature. \"namespaces\": [ { \"name\": \"/users/:name\" // namespace options may go here ... }, { \"name\": \"/events/:project/:type\" // namespace options may go here ... } ]} As soon as namespace name starts with / - it's considered a channel pattern. Just like an HTTP path it consists of segments delimited by /. The : symbol in the segment beginning defines a variable part – more information below. In this case a channel to be used must be sth like /users/mario - i.e. start with / and match one of the patterns defined in the configuration. So this channel pattern matching mechanics behaves mostly like HTTP route matching in many frameworks. Given the configuration example above: if channel is /users/mario, then the namespace with the name /users/:name will match and we apply all the options defined for it to the channel. if channel is /events/42/news, then the namespace with the name /events/:project/:type will match. if channel is /events/42, then no namespace will match and the unknown channel error will be returned. Basic example demonstrating use of pattern channels in JS const client := new Centrifuge(\"ws://...\", {});const sub = client.newSubscription('/users/mario');sub.subscribe();client.connect();","s":"Configuration","u":"/docs/pro/channel_patterns","h":"#configuration","p":2723},{"i":2728,"t":"Some implementation restrictions and details to know about: When using channel patterns feature : symbol in a namespace name defines a variable part. It's not related to a namespace separator anymore – the entire channel is matched over the channel pattern. Similar to the HTTP routes semantics. So namespace separator is not needed at all when using channel patterns. Centrifugo only allows explicit channel pattern matching which do not result into channel pattern conflicts in runtime, this is checked during configuration validation on server start. Explicitly defined static patterns (without variables) have precedence over patterns with variables. There is no analogue of top-level namespace (like we have for standard namespace configuration) for channels starting with /. If a channel does not match any explicitly defined pattern then Centrifugo returns the 102: unknown channel error. If you define channel_regex inside channel pattern options – then regex matches over the entire channel (since variable parts are located in the namespace name in this case). Channel pattern must only contain ASCII characters. Duplicate variable names are not allowed inside an individual pattern, i.e. defining /users/:user/:user will result into validation error on start.","s":"Implementation details","u":"/docs/pro/channel_patterns","h":"#implementation-details","p":2723},{"i":2730,"t":": in the channel pattern name helps to define a variable to match against. Named parameters only match a single segment of the channel: Channel pattern \"/users/:name\":/users/mary ✅ match/users/john ✅ match/users/mary/info ❌ no match /users ❌ no match Another example for channel pattern /news/:type/:subtype, i.e. with multiple variables: Channel pattern \"/news/:type/:subtype\":/news/sport/football ✅ match/news/sport/volleyball ✅ match/news/sport ❌ no match/news ❌ no match Channel patterns support mid-segment variables, so the following is possible: Channel pattern \"/personal/user_:user\":/personal/user_mary ✅ match/personal/user_john ✅ match/personal/user_ ❌ no match","s":"Variables","u":"/docs/pro/channel_patterns","h":"#variables","p":2723},{"i":2732,"t":"Additional benefits of using channel patterns may be achieved together with Centrifugo PRO CEL expressions. Channel pattern variables are available inside CEL expressions for evaluation in a custom way.","s":"Using varibles","u":"/docs/pro/channel_patterns","h":"#using-varibles","p":2723},{"i":2734,"t":"On this page","s":"Message batching control","u":"/docs/pro/client_message_batching","h":"","p":2733},{"i":2736,"t":"The client_write_delay is a duration option, it is a time Centrifugo will try to collect messages inside each connection message write loop before sending them towards the connection. Enabling client_write_delay may reduce CPU usage of both server and client in case of high message rate inside individual connections. The reduction happens due to the lesser number of system calls to execute. Enabling client_write_delay limits the maximum throughput of messages towards the connection which may be achieved. For example, if client_write_delay is 100ms then the max throughput per second will be (1000 / 100) * client_max_messages_in_frame (16 by default), i.e. 160 messages per second. Though this should be more than enough for target Centrifugo use cases (frontend apps). Example: config.json { // Rest of config here ... \"client_write_delay\": \"100ms\"}","s":"client_write_delay","u":"/docs/pro/client_message_batching","h":"#client_write_delay","p":2733},{"i":2738,"t":"The client_reply_without_queue is a boolean option to not use client queue for replies to commands. When true replies are written to the transport without going through the connection message queue.","s":"client_reply_without_queue","u":"/docs/pro/client_message_batching","h":"#client_reply_without_queue","p":2733},{"i":2740,"t":"The client_max_messages_in_frame is an integer option which controls the maximum number of messages which may be joined by Centrifugo into one transport frame. By default, 16. Use -1 for unlimited number.","s":"client_max_messages_in_frame","u":"/docs/pro/client_message_batching","h":"#client_max_messages_in_frame","p":2733},{"i":2742,"t":"On this page","s":"Distributed rate limit API","u":"/docs/pro/distributed_rate_limit","h":"","p":2741},{"i":2744,"t":"Centrifugo distributed rate limiting is a high performance zero-configuration Redis-based token bucket with milliseconds precision. Zero configuration in this case means that you don't have to preconfigure buckets in Centrifugo – bucket configuration is a part of request to check allowed limits. curl -X POST http://localhost:8000/api/rate_limit \\-H \"Authorization: apikey \" \\-d @- <<'EOF'{ \"key\": \"rate_limit_test\", \"interval\": 60000, \"rate\": 10}EOF Example result: { \"result\": { \"allowed\": true, \"tokens_left\": 9 }} Or, when no tokens left in a bucket: { \"result\": { \"allowed\": false, \"tokens_left\": 0, \"allowed_in\": 5208, \"server_time\": 1694627573210, }} In your app code call rate_limit API of Centrifugo PRO every time some action is executed and check allowed flag to allow or discard the action. Centrifugo PRO also returns allowed_in and server_time fields to help understanding when action will be allowed. These two fields are only appended when tokens_left are less than requested score. allowed_in + server_time will provide you a timestamp in the future (in milliseconds) when action is possible to be executed. So you can delay next action execution till that time if possible.","s":"Overview","u":"/docs/pro/distributed_rate_limit","h":"#overview","p":2741},{"i":2746,"t":"To enable distributed rate limiter: config.json { ... \"distributed_rate_limit\": { \"enabled\": true, \"redis_address\": \"localhost:6379\" } } Note, that just like most of other features in Centrifugo it's possible to configure Redis shards here or use Redis Cluster.","s":"Configuration","u":"/docs/pro/distributed_rate_limit","h":"#configuration","p":2741},{"i":2748,"t":"Now let's look at API description.","s":"API description","u":"/docs/pro/distributed_rate_limit","h":"#api-description","p":2741},{"i":2750,"t":"Field Type Required Description key string Yes Key for a bucket - you can construct keys whatever way you like interval integer Yes Interval in milliseconds rate integer Yes Allowed rate per provided interval score integer No Score for the current action, if not provided the default score 1 is used","s":"rate_limit request","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-request","p":2741},{"i":2752,"t":"Field Name Type Required Description allowed bool Yes Whether desired action is allowed at this point in time tokens_left integer Yes How many tokens left in a bucket allowed_in integer No Milliseconds till desired score will be allowed again server_time integer No Server time as Unix epoch in milliseconds used to calculate result","s":"rate_limit result","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-result","p":2741},{"i":2754,"t":"On this page","s":"Install and run PRO version","u":"/docs/pro/install_and_run","h":"","p":2753},{"i":2756,"t":"Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below). If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack centrifugo binary to your current working directory: curl -sSLf https://centrifugal.dev/install_pro.sh | sh","s":"Binary release","u":"/docs/pro/install_and_run","h":"#binary-release","p":2753},{"i":2758,"t":"Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v5.1.1 centrifugo -c config.json","s":"Docker image","u":"/docs/pro/install_and_run","h":"#docker-image","p":2753},{"i":2760,"t":"You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag: values.yaml ...image: registry: docker.io repository: centrifugo/centrifugo-pro tag: v5.1.1","s":"Kubernetes","u":"/docs/pro/install_and_run","h":"#kubernetes","p":2753},{"i":2762,"t":"DEB package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.1/centrifugo-pro_5.1.1_amd64.debsudo dpkg -i centrifugo-pro_5.1.1_amd64.deb","s":"Debian and Ubuntu","u":"/docs/pro/install_and_run","h":"#debian-and-ubuntu","p":2753},{"i":2764,"t":"RPM package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.1/centrifugo-pro-5.1.1.x86_64.rpmsudo yum install centrifugo-pro-5.1.1.x86_64.rpm","s":"Centos","u":"/docs/pro/install_and_run","h":"#centos","p":2753},{"i":2766,"t":"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits. Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this: config.json { ... \"license\": \"\"} tip If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.","s":"Setting PRO license key","u":"/docs/pro/install_and_run","h":"#setting-pro-license-key","p":2753},{"i":2768,"t":"On this page","s":"Connections API","u":"/docs/pro/connections","h":"","p":2767},{"i":2770,"t":"Let's look at the quick example. First, generate a JWT for user 42: $ centrifugo genconfig Generate token for some user to be used in the example connections: $ centrifugo gentoken -u 42HMAC SHA-256 JWT for user 42 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y Run Centrifugo with uni_http_stream transport enabled (it will allow us connecting from the terminal with curl): CENTRIFUGO_UNI_HTTP_STREAM=1 centrifugo -c config.json Create new terminal window and run: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' In another terminal create one more connection: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' Now let's call connections over HTTP API: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"42\"}' \\ http://localhost:8000/api/connections The result: { \"result\": { \"connections\": { \"db8bc772-2654-4283-851a-f29b888ace74\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" }, \"4bc3ca70-ecc5-439d-af14-a78ae18e31c7\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" } } }} Here we can see that user has 2 connections from terminal app. Each connection can be annotated with meta JSON information which is set during connection establishment (over meta claim of JWT or by returning meta in the connect proxy result).","s":"Example","u":"/docs/pro/connections","h":"#example","p":2767},{"i":2772,"t":"Returns information about active connections according to the request. connections params​ Parameter name Parameter type Required Description user string no fast filter by User ID expression string no CEL expression to filter users connections result​ Field name Field type Optional Description connections map[string]ConnectionInfo no active user connections map where key is client ID and value is ConnectionInfo ConnectionInfo​ Field name Field type Optional Description app_name string yes client app name (if provided by client) app_version string yes client app version (if provided by client) transport string no client connection transport protocol string no client connection protocol (json or protobuf) user string yes client user ID state ConnectionState yes connection state ConnectionState object​ Field name Field type Optional Description channels map[string]ChannelContext yes Channels client subscribed to connection_token ConnectionTokenInfo yes information about connection token subscription_tokens map yes information about channel tokens used to subscribe meta JSON object yes meta information attached to a connection ChannelContext object​ Field name Field type Optional Description source int yes The source of channel subscription ConnectionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued SubscriptionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued","s":"connections","u":"/docs/pro/connections","h":"#connections","p":2767},{"i":2774,"t":"Observability enhancements Centrifugo PRO has some enhancements to exposed metrics. At this moment it provides channel namespace resolution to the following metrics: centrifugo_transport_messages_sent centrifugo_transport_messages_sent_size centrifugo_transport_messages_received centrifugo_transport_messages_received_size Since channel namespace resolution may add some overhead (though negligible in most cases), Centrifugo PRO requires it to be explicitly enabled using two boolean config options (available since Centrifugo PRO v5.1.1): config.json { ... \"channel_namespace_for_transport_messages_sent\": true, \"channel_namespace_for_transport_messages_received\": true} First option channel_namespace_for_transport_messages_sent enables channel namespace label for: centrifugo_transport_messages_sent centrifugo_transport_messages_sent_size Second option channel_namespace_for_transport_messages_received enables for: centrifugo_transport_messages_received centrifugo_transport_messages_received_size.","s":"Observability enhancements","u":"/docs/pro/observability_enhancements","h":"","p":2773},{"i":2776,"t":"On this page","s":"Centrifugo PRO","u":"/docs/pro/overview","h":"","p":2775},{"i":2778,"t":"Centrifugo PRO is packed with the following features: Everything from Centrifugo OSS 🔍 Channel and user tracing allows watching client protocol frames in channel or per user ID in real time. 💹 Real-time analytics with ClickHouse for a great system observability, reporting and trending. 🛡️ Operation rate limits to protect server from the real-time API misusing and frontend bugs. 🔥 Push notification API to manage device tokens and send mobile and browser push notifications. 🟢 User status API feature allows understanding activity state for a list of users. 🔌 Connections API to query, filter and inspect active connections. ✋ User blocking API to block/unblock abusive users by ID. 🛑 JWT revoking and invalidation API to revoke tokens by ID and invalidate user's tokens based on issue time. 🔔 Channel state events to be notified on the backend about channel occupied and vacated events. 💪 Channel capabilities for controlling channel permissions per connection or per subscription. 📜 Channel patterns allow defining channel configuration like HTTP routes with parameters. ✍️ CEL expressions to write custom efficient permission rules for channel operations. 🚀 Faster performance to reduce resource usage on server side. 🔮 Singleflight for online presence and history to reduce load on the broker. 🍔 Message batching control for advanced tuning of client connection write behaviour. 🧐 Observability enhancements for additional more granular system state insights. 🪵 CPU and RSS memory usage stats of Centrifugo nodes in admin UI. Also, explore our Centrifugo PRO planned features board for a concise overview of upcoming features which are currently in progress and enhancements planned for a future.","s":"Features","u":"/docs/pro/overview","h":"#features","p":2775},{"i":2780,"t":"Centrifugo PRO requires a license key to run. The price for a license key is 3,499 EUR. A license key has an expiration date — six months since the purchase. Once your key expires, Centrifugo PRO will continue to work, but you will lose access to updates for versions released after the expiration date of your key. This model is like subscription, yet provides the flexibility to delay subsequent payments until an upgrade is required. Our services are exclusively available to corporate and business clients at this time. To purchase the license key, please reach out to us at sales@centrifugal.dev. We kindly ask that your email includes specific information about your company, such as its name and address. We would also value a brief description of how you plan to utilize Centrifugo PRO. The purchase is just an online payment by card (or other available method) in Centrifugal Labs store on Lemon Squeezy platform. We will share a payment link in response to your request – and everything else is automated.","s":"Pricing","u":"/docs/pro/overview","h":"#pricing","p":2775},{"i":2782,"t":"You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example: Centrifugo handles up to 20 concurrent connections up to 2 server nodes supported up to 5 API requests per second allowed This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future. Centrifugo PRO license agreement Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.","s":"Try for free in sandbox mode","u":"/docs/pro/overview","h":"#try-for-free-in-sandbox-mode","p":2775},{"i":2784,"t":"On this page","s":"Faster performance","u":"/docs/pro/performance","h":"","p":2783},{"i":2786,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.","s":"Faster HTTP API","u":"/docs/pro/performance","h":"#faster-http-api","p":2783},{"i":2788,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC API","u":"/docs/pro/performance","h":"#faster-grpc-api","p":2783},{"i":2790,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster HTTP proxy","u":"/docs/pro/performance","h":"#faster-http-proxy","p":2783},{"i":2792,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC proxy","u":"/docs/pro/performance","h":"#faster-grpc-proxy","p":2783},{"i":2794,"t":"Centrifugo PRO has an optimized decoding of JWT claims.","s":"Faster JWT decoding","u":"/docs/pro/performance","h":"#faster-jwt-decoding","p":2783},{"i":2796,"t":"Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.","s":"Faster GRPC unidirectional stream","u":"/docs/pro/performance","h":"#faster-grpc-unidirectional-stream","p":2783},{"i":2798,"t":"Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.","s":"Examples","u":"/docs/pro/performance","h":"#examples","p":2783},{"i":2800,"t":"Sorry, your browser doesn't support embedded video. In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.","s":"Publish HTTP API","u":"/docs/pro/performance","h":"#publish-http-api","p":2783},{"i":2802,"t":"Sorry, your browser doesn't support embedded video. In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.","s":"History HTTP API","u":"/docs/pro/performance","h":"#history-http-api","p":2783},{"i":2804,"t":"CPU and RSS stats A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI. Here is how this looks like: The information updated in near real-time (with several seconds delay). It's also available as part of info API.","s":"CPU and RSS stats","u":"/docs/pro/process_stats","h":"","p":2803},{"i":2806,"t":"Singleflight Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. It will do this by using in-memory component called singleflight. tip While it can seem similar, singleflight is not a cache. It only combines identical parallel requests into one. If requests come one after another – they will be sent separately to the broker or presence storage. This option can radically reduce a load on a broker in the following situations: Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature) Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine. To enable: config.json { ... \"use_singleflight\": true} Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.","s":"Singleflight","u":"/docs/pro/singleflight","h":"","p":2805},{"i":2808,"t":"On this page","s":"User and channel tracing","u":"/docs/pro/tracing","h":"","p":2807},{"i":2810,"t":"It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing. curl -X POST http://localhost:8000/admin/trace -H \"Authorization: token \" -d '{\"type\": \"user\", \"entity\": \"56\"}' -o trace.txt Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future as PRO version evolves.","s":"Save to a file","u":"/docs/pro/tracing","h":"#save-to-a-file","p":2807},{"i":2812,"t":"On this page","s":"Token revocation API","u":"/docs/pro/token_revocation","h":"","p":2811},{"i":2814,"t":"By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart. But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/pro/token_revocation","h":"#how-it-works","p":2811},{"i":2816,"t":"Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/pro/token_revocation","h":"#configure","p":2811},{"i":2818,"t":"Revocation data can be kept in Redis. To enable this configuration should be: { ... \"token_revoke\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/pro/token_revocation","h":"#redis-persistence-engine","p":2811},{"i":2820,"t":"Revocation data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"token_revoke\": { \"persistence_engine\": \"database\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/pro/token_revocation","h":"#database-persistence-engine","p":2811},{"i":2823,"t":"Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT). caution This API assumes that JWTs you are using contain \"jti\" claim which is a unique token ID (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"uid\": \"xxx-xxx-xxx\", \"expire_at\": 1635845122}' \\ http://localhost:8000/api/revoke_token revoke_token params​ Parameter name Parameter type Required Description uid string yes Token unique ID (JTI claim in case of JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). revoke_token result​ Empty object at the moment.","s":"revoke_token","u":"/docs/pro/token_revocation","h":"#revoke_token","p":2811},{"i":2826,"t":"Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application. caution This API assumes that JWTs you are using contain \"iat\" claim which is a time token was issued at (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"test\", \"issued_before\": 1635845022, \"expire_at\": 1635845122}' \\ http://localhost:8000/api/invalidate_user_tokens invalidate_user_tokens params​ Parameter name Parameter type Required Description user string yes User ID to invalidate tokens for issued_before int no All tokens issued at before this Unix time will be considered revoked (in case of JWT this requires iat to be properly set in JWT), if not provided server uses current time expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). invalidate_user_tokens result​ Empty object.","s":"invalidate_user_tokens","u":"/docs/pro/token_revocation","h":"#invalidate_user_tokens","p":2811},{"i":2828,"t":"On this page","s":"Operation rate limits","u":"/docs/pro/rate_limiting","h":"","p":2827},{"i":2830,"t":"In-memory rate limit is an efficient way to limit number of operations allowed on a per-connection basis – i.e. inside each individual real-time connection. Our rate limit implementation uses token bucket algorithm internally. The list of operations which can be rate limited on a per-connection level is: subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) In addition, Centrifugo allows defining two special buckets containers: total – define it to limit the total number of commands per interval (all commands sent from client count), these buckets will always be checked if defined, every command from the client always consumes token from total buckets default - define it if you don't want to configure some command buckets explicitly, default buckets will be used in case command buckets is not configured explicitly. config.json { ... \"client_command_rate_limit\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"total\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 20 }, { \"interval\": \"60s\", \"rate\": 50 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 }, ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"update_user_status\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} tip Centrifugo real-time SDKs written in a way that if client receives an error during connect – it will try to reconnect to a server with backoff algorithm. The same for subscribing to channels (i.e. error from subscribe command) – subscription request will be retried with a backoff. Refresh and subscription refresh will be also retried automatically by SDK upon errors after in several seconds. Retries of other commands should be handled manually from the client side if needed – though usually you should choose rate limit limits in a way that normal users of your app never hit the limits.","s":"In-memory per connection rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-connection-rate-limit","p":2827},{"i":2832,"t":"Another type of rate limit in Centrifugo PRO is a per user ID in-memory rate limit. Like per client rate limit this one is also very efficient since also uses in-memory token buckets. The difference is that instead of rate limit per individual client this type of rate limit takes user ID into account. This type of rate limit only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be rate limited with it. The list of operations which can be rate limited is similar to the in-memory rate limit described above. But with additional connect method: total default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"user_command_rate_limit\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"update_user_status\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }}","s":"In-memory per user rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-user-rate-limit","p":2827},{"i":2834,"t":"The next type of rate limit in Centrifugo PRO is a distributed per user ID rate limit with Redis as a bucket state storage. In this case limits are global for the entire Centrifugo cluster. If one user executed two commands on different Centrifugo nodes, Centrifugo consumes two tokens from the same bucket kept in Redis. Since this rate limit goes to Redis to check limits, it adds some latency to a command processing. Our implementation tries to provide good throughput characteristics though – in our tests single Redis instance can handle more than 100k limit check requests per second. And it's possible to scale Redis in the same ways as for Centrifugo Redis Engine. This type of rate limit only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be rate limited with it. The implementation also uses token bucket algorithm internally. The list of operations which can be rate limited is similar to the in-memory user command rate limit described above. But without special bucket total: default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"redis_user_command_rate_limit\": { \"enabled\": true, \"redis_address\": \"localhost:6379\", \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"update_user_status\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} Redis configuration for rate limit feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for rate limit feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom rate limit Redis configuration declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_user_command_rate_limit\": { \"enabled\": true, \"use_redis_from_engine\": true, ... }} In this case rate limit will simply connect to Redis instances configured for an Engine.","s":"Redis per user rate limit","u":"/docs/pro/rate_limiting","h":"#redis-per-user-rate-limit","p":2827},{"i":2836,"t":"Above we showed how you can define rate limit strategies to protect server resources and prevent execution of many commands inside the connection and from certain user. But there are scenarios when abusive or broken connections may generate a significant load on the server just by calling commands and getting error responses due to rate limit or due to other reasons (like malformed command). Centrifugo PRO provides a way to configure error limits per connection to deal with this case. Error limits are configured as in-memory buckets operating on a per-connection level. When these buckets are full due to lots of errors for an individual connection Centrifugo disconnects the client (with advice to not reconnect, so our SDKs may follow it). This way it's possible to get rid of the connection and rely on HTTP infrastracture tools to deal with client reconnections. Since WebSocket or other our transports (except unidirectional GRPC, but it's usually not available to the public port) are HTTP-based (or start with HTTP request in WebSocket Upgrade case) – developers can use Nginx limit_req_zone directive, Cloudflare rules, iptables, and so on, to protect Centrifugo from unwanted connections. tip Centrifugo PRO does not count internal errors for the error limit buckets – as internal errors is usually not a client's fault. The configuration on error limits per connection may look like this: config.json { ... \"client_error_limits\": { \"enabled\": true, \"total\": { \"buckets\" : [ { \"interval\": \"5s\", \"rate\": 20 } ] } }}","s":"Disconnecting abusive or misbehaving connections","u":"/docs/pro/rate_limiting","h":"#disconnecting-abusive-or-misbehaving-connections","p":2827},{"i":2838,"t":"On this page","s":"Push notification API","u":"/docs/pro/push_notifications","h":"","p":2837},{"i":2840,"t":"We tried to be practical with our Push Notification API, let's look at its design choices and implementation properties we were able to achieve.","s":"Motivation and design choices","u":"/docs/pro/push_notifications","h":"#motivation-and-design-choices","p":2837},{"i":2842,"t":"To start delivering push notifications in the application, developers usually need to integrate with providers such as FCM, HMS, and APNs. This integration typically requires the storage of device tokens in the application database and the implementation of sending push messages to provider push services. Centrifugo PRO simplifies the process by providing a backend for device token storage, following best practices in token management. It reacts to errors and periodically removes stale devices/tokens to maintain a working set of device tokens based on provider recommendations.","s":"Storage for tokens","u":"/docs/pro/push_notifications","h":"#storage-for-tokens","p":2837},{"i":2844,"t":"Additionally, Centrifugo PRO provides an efficient, scalable queuing mechanism for sending push notifications. Developers can send notifications from the app backend to Centrifugo API with minimal latency and let Centrifugo process sending to FCM, HMS, APNs concurrently using built-in workers. In our tests, we achieved several millions pushes per minute. Centrifugo PRO also supports delayed push notifications feature – to queue push for a later delivery, so for example you can send notification based on user time zone and let Centrifugo PRO send it when needed.","s":"Efficient queuing","u":"/docs/pro/push_notifications","h":"#efficient-queuing","p":2837},{"i":2846,"t":"FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that client can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM, HMS topics by introducing an additional API to manage device subscriptions to topics. tip In some cases you may have real-time channels and device subscription topics with matching names – to send messages to both online and offline users. Though it's up to you. Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs. Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. Then – as soon as user registers a device – it will be automatically subscribed to its own topics. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with FCM – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call.","s":"Unified secure topics","u":"/docs/pro/push_notifications","h":"#unified-secure-topics","p":2837},{"i":2848,"t":"Unlike other solutions that combine different provider push sending APIs into a unified API, Centrifugo PRO provides a non-obtrusive proxy for all the mentioned providers. Developers can send notification payloads in a format defined by each provider. It's also possible to send notifications into native FCM, HMS topics or send to raw FCM, HMS, APNs tokens using Centrifugo PRO's push API, allowing them to combine native provider primitives with those added by Centrifugo (i.e., sending to a list of device IDs or to a list of topics).","s":"Non-obtrusive proxying","u":"/docs/pro/push_notifications","h":"#non-obtrusive-proxying","p":2837},{"i":2850,"t":"Furthermore, Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API.","s":"Builtin analytics","u":"/docs/pro/push_notifications","h":"#builtin-analytics","p":2837},{"i":2852,"t":"Add provider SDK on the frontend side, follow provider instructions for your platform to obtain a push token for a device. For example, for FCM see instructions for iOS, Android, Flutter, Web Browser). The same for HMS or APNs – frontend part should be handled by their native SDKs. Call Centrifugo PRO backend API with the obtained token. From the application backend call Centrifugo device_register API to register the device in Centrifugo PRO storage. Optionally provide list of topics to subscribe device to. Centrifugo returns a registered device object. Pass a generated device ID to the frontend and save it on the frontend together with a token received from FCM. Call Centrifugo send_push_notification API whenever it's time to deliver a push notification. At any moment you can inspect device storage by calling device_list API. Once user logs out from the app, you can detach user ID from device by using device_update or remove device with device_remove API.","s":"Steps to integrate","u":"/docs/pro/push_notifications","h":"#steps-to-integrate","p":2837},{"i":2854,"t":"In Centrifugo PRO you can configure one push provider or use all of them – this choice is up to you.","s":"Configuration","u":"/docs/pro/push_notifications","h":"#configuration","p":2837},{"i":2856,"t":"As mentioned above Centrifigo uses PostgreSQL for token storage. To enable push notifications make sure database section defined in the configration and fcm is in the push_notifications.enabled_providers list. Centrifugo PRO uses Redis for queuing push notification requests, so Redis address should be configured also. Finally, to integrate with FCM a path to the credentials file must be provided (see how to create one in this instruction). So the full configuration to start sending push notifications over FCM may look like this: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"fcm\"], \"fcm_credentials_file_path\": \"/path/to/service/account/credentials.json\" }} tip Actually, PostgreSQL database configuration is optional here – you can use push notifications API without it. In this case you will be able to send notifications to FCM, HMS, APNs raw tokens, FCM and HMS native topics and conditions. I.e. using Centrifugo as an efficient proxy for push notifications (for example if you already keep tokens in your database). But sending to device ids and topics, and token/topic management APIs won't be available for usage.","s":"FCM","u":"/docs/pro/push_notifications","h":"#fcm","p":2837},{"i":2858,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"hms\"], \"hms_app_id\": \"\", \"hms_app_secret\": \"\", }} tip See example how to get app id and app secret here.","s":"HMS","u":"/docs/pro/push_notifications","h":"#hms","p":2837},{"i":2860,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"apns\"], \"apns_endpoint\": \"development\", \"apns_bundle_id\": \"com.example.your_app\", \"apns_auth\": \"token\", \"apns_token_auth_key_path\": \"/path/to/auth/key/file.p8\", \"apns_token_key_id\": \"\", \"apns_token_team_id\": \"your_team_id\", }} We also support auth over p12 certificates with the following options: push_notifications.apns_cert_p12_path push_notifications.apns_cert_p12_b64 push_notifications.apns_cert_p12_password","s":"APNs","u":"/docs/pro/push_notifications","h":"#apns","p":2837},{"i":2862,"t":"push_notifications.max_inactive_device_days​ This integer option configures the number of days to keep device without updates. By default Centrifugo does not remove inactive devices. push_notifications.enable_redis_delayed_scheduler​ Boolean option which enables Redis scheduler to process delayed push notifications. It's off by default since produces additional requests to Redis. When using PostgreSQL as push notifications queue engine you don't need to enable sheduler explicitly. push_notifications.dry_run​ Boolean option, when true Centrifugo PRO does not send push notifications to FCM, APNs, HMS providers but instead just print logs. Useful for development. push_notifications.dry_run_latency​ Duration. When set together with push_notifications.dry_run every dry-run request will cause some delay in workers emulating real-world latency. Useful for development.","s":"Other options","u":"/docs/pro/push_notifications","h":"#other-options","p":2837},{"i":2864,"t":"Centrifugo PRO utilizes Redis Streams as the default queue engine for push notifications. However, it also offers the option to employ PostgreSQL for queuing. It's as simple as: config.json { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"queue_engine\": \"database\", // rest of the options... }} tip Queue based on Redis streams is generally more efficient, so if you start with PostgreSQL based queue – you have an option to switch to a more performant implementation later. Though in-flight and currently queued push notifications will be lost during a switch.","s":"Use PostgreSQL as queue","u":"/docs/pro/push_notifications","h":"#use-postgresql-as-queue","p":2837},{"i":2867,"t":"Registers or updates device information. device_register request​ Field Type Required Description id string No ID of the device being registered (provide it when updating). provider string Yes Provider of the device token (valid choices: fcm, hms, apns). token string Yes Push notification token for the device. platform string Yes Platform of the device (valid choices: ios, android, web). user string No User associated with the device. topics array of strings No Device topic subscriptions. This should be a full list which replaces all the topics previously accociated with the device. User topics managed by UserTopic model will be automatically attached. meta map No Additional custom metadata for the device device_register result​ Field Name Type Required Description id string Yes The device ID that was registered/updated.","s":"device_register","u":"/docs/pro/push_notifications","h":"#device_register","p":2837},{"i":2869,"t":"Call this method to update device. For example, when user logs out the app and you need to detach user ID from the device. device_update request​ Field Type Required Description ids repeated string No Device ids to filter users repeated string No Device users filter user_update DeviceUserUpdate No Optional user update object meta_update DeviceMetaUpdate No Optional device meta update object topics_update DeviceTopicsUpdate No Optional topics update object DeviceUserUpdate: Field Type Required Description user string Yes User to set DeviceMetaUpdate: Field Type Required Description meta map Yes Meta to set DeviceTopicsUpdate: Field Type Required Description op string Yes Operation to make: add, remove or set topics repeated string Yes Topics for the operation device_update result​ Empty object.","s":"device_update","u":"/docs/pro/push_notifications","h":"#device_update","p":2837},{"i":2871,"t":"Removes device from storage. This may be also called when user logs out the app and you don't need its device token after that. device_remove request​ Field Name Type Required Description ids repeated string No A list of device IDs to be removed users repeated string No A list of device user IDs to filter devices to remove device_remove result​ Empty object.","s":"device_remove","u":"/docs/pro/push_notifications","h":"#device_remove","p":2837},{"i":2873,"t":"Returns a paginated list of registered devices according to request filter conditions. device_list request​ Field Type Required Description filter DeviceFilter Yes How to filter results cursor string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_total_count bool No Flag indicating whether to include total count for the current filter. include_topics bool No Flag indicating whether to include topics information for each device. include_meta bool No Flag indicating whether to include meta information for each device. DeviceFilter: Field Type Required Description ids repeated string No List of device IDs to filter results. providers repeated string No List of device token providers to filter results. platforms repeated string No List of device platforms to filter results. users repeated string No List of device users to filter results. topics repeated string No List of topics to filter results. device_list result​ Field Name Type Required Description items repeated Device Yes A list of devices next_cursor string No Cursor string for retreiving the next page, if not set - then no next page exists total_count integer No Total count value (if include_total_count used) Device: Field Name Type Required Description id string Yes The device's ID. provider string Yes The device's token provider. token string Yes The device's token. platform string Yes The device's platform. user string No The user associated with the device. topics array of strings No Only included if include_topics was true meta map No Only included if include_meta was true","s":"device_list","u":"/docs/pro/push_notifications","h":"#device_list","p":2837},{"i":2875,"t":"Manage mapping of device to topics. device_topic_update request​ Field Type Required Description device_id string Yes Device ID. op string Yes add or remove or set topics repeated string No List of topics. device_topic_update result​ Empty object.","s":"device_topic_update","u":"/docs/pro/push_notifications","h":"#device_topic_update","p":2837},{"i":2877,"t":"List device to topic mapping. device_topic_list request​ Field Type Required Description filter DeviceTopicFilter No List of device IDs to filter results. cursor string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_device bool No Flag indicating whether to include Device information for each object. include_total_count bool No Flag indicating whether to include total count info to response. DeviceTopicFilter: Field Type Required Description device_ids repeated string No List of device IDs to filter results. device_providers repeated string No List of device token providers to filter results. device_platforms repeated string No List of device platforms to filter results. device_users repeated string No List of device users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Topic prefix to filter results. device_topic_list result​ Field Name Type Required Description items repeated DeviceTopic Yes A list of DeviceChannel objects next_cursor string No Cursor string for retreiving the next page, if not set - then no next page exists total_count integer No Total count value (if include_total_count used) DeviceTopic: Field Type Required Description id string Yes ID of DeviceTopic object device_id string Yes Device ID topic string Yes Topic","s":"device_topic_list","u":"/docs/pro/push_notifications","h":"#device_topic_list","p":2837},{"i":2879,"t":"Manage mapping of topics with users. These user topics will be automatically attached to user devices upon registering. And removed from device upon deattaching user. user_topic_update request​ Field Type Required Description user string Yes User ID. op string Yes add or remove or set topics repeated string No List of topics. user_topic_update result​ Empty object.","s":"user_topic_update","u":"/docs/pro/push_notifications","h":"#user_topic_update","p":2837},{"i":2881,"t":"List user to topic mapping. user_topic_list request​ Field Type Required Description flter UserTopicFilter No Filter object. cursor string No Cursor for pagination (last id in previous batch, empty for first page). limit int32 No Maximum number of UserTopic objects to retrieve. include_total_count bool No Flag indicating whether to include total count info to response. UserTopicFilter: Field Type Required Description users repeated string No List of users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Channel prefix to filter results. user_topic_list result​ Field Name Type Description items repeated UserTopic A list of UserTopic objects next_cursor string No total_count integer No UserTopic: Field Type Required Description id string Yes ID of UserTopic user string Yes User ID topic string Yes Topic","s":"user_topic_list","u":"/docs/pro/push_notifications","h":"#user_topic_list","p":2837},{"i":2883,"t":"Send push notification to specific device_ids, or to topics, or native provider identifiers like fcm_tokens, or to fcm_topic. Request will be queued by Centrifugo, consumed by Centrifugo built-in workers and sent to the provider API. send_push_notification request​ Field name Type Required Description recipient PushRecipient Yes Recipient of push notification notification PushNotification Yes Push notification to send uid string No Unique send id, used for Centrifugo builtin analytics or to cancel delayed push. We recommend using UUID v4 for it send_at int64 No Optional Unix time in the future (in seconds) when to send push notification, push will be queued until that time. PushRecipient (you must set only one of the following fields): Field Type Required Description filter DeviceFilter No Send to device IDs based on Centrifugo device storage filter fcm_tokens repeated string No Send to a list of FCM native tokens fcm_topic string No Send to a FCM native topic fcm_condition string No Send to a FCM native condition hms_tokens repeated string No Send to a list of HMS native tokens hms_topic string No Send to a HMS native topic hms_condition string No Send to a HMS native condition apns_tokens repeated string No Send to a list of APNs native tokens PushNotification: Field Type Required Description expire_at int64 No Unix timestamp when Centrifugo stops attempting to send this notification. Note, it's Centrifugo specific and does not relate to notification TTL fields. We generally recommend to always set this to a reasonable value to protect your app from old push notifications sending fcm FcmPushNotification No Notification for FCM hms HmsPushNotification No Notification for HMS apns ApnsPushNotification No Notification for APNs FcmPushNotification: Field Type Required Description message JSON object Yes FCM Message described in FCM docs. HmsPushNotification: Field Type Required Description message JSON object Yes HMS Message described in HMS Push Kit docs. ApnsPushNotification: Field Type Required Description headers map No APNs headers payload JSON object Yes APNs payload send_push_notification result​ Field Name Type Description uid string Unique send id, matches uid in request if it was provided","s":"send_push_notification","u":"/docs/pro/push_notifications","h":"#send_push_notification","p":2837},{"i":2885,"t":"Cancel delayed push notification (which was sent with custom send_at value). update_push_status request​ Field Type Required Description uid string Yes uid of push notification to cancel update_push_status result​ Empty object.","s":"cancel_push","u":"/docs/pro/push_notifications","h":"#cancel_push","p":2837},{"i":2887,"t":"This API call is experimental, some changes may happen here. Centrifugo PRO also allows tracking status of push notification delivery and interaction. It's possible to use update_push_status API to save the updated status of push notification to the notifications analytics table. Then it's possible to build insights into push notification effectiveness by querying the table. The update_push_status API supposes that you are using uid field with each notification sent and you are using Centrifugo PRO generated device IDs (as described in steps to integrate). This is a part of server API at the moment, so you need to proxy requests to this endpoint over your backend. We can consider making this API suitable for requests from the client side – please reach out if your use case requires it. update_push_status request​ Field Type Required Description uid string Yes uid (unique send id) from send_push_notification status string Yes Status of push notification - delivered or interacted device_id string Yes Device ID msg_id string No Message ID update_push_status result​ Empty object.","s":"update_push_status","u":"/docs/pro/push_notifications","h":"#update_push_status","p":2837},{"i":2889,"t":"Several metrics are available to monitor the state of Centrifugo push worker system: centrifugo_push_notification_count​ Type: Counter Labels: provider, recipient_type, platform, success, err_code Description: Total count of push notifications. Usage: Helps in tracking the number and success rate of push notifications sent, providing insights for optimization and troubleshooting. centrifugo_push_queue_consuming_lag​ Type: Gauge Labels: provider, queue Description: Queue consuming lag in seconds. Usage: Useful for monitoring the delay in processing jobs from the queue, helping identify potential bottlenecks and ensuring timely processing. centrifugo_push_consuming_inflight_jobs​ Type: Gauge Labels: provider, queue Description: Number of inflight jobs being consumed. Usage: Helps in tracking the load on the job processing system, ensuring that resources are being utilized efficiently. centrifugo_push_job_duration_seconds​ Type: Summary Labels: provider, recipient_type Description: Duration of push processing job in seconds. Usage: Useful for monitoring the performance of job processing, helping in performance tuning and issue resolution.","s":"Exposed metrics","u":"/docs/pro/push_notifications","h":"#exposed-metrics","p":2837},{"i":2891,"t":"Coming soon.","s":"Further reading and tutorials","u":"/docs/pro/push_notifications","h":"#further-reading-and-tutorials","p":2837},{"i":2893,"t":"On this page","s":"User blocking API","u":"/docs/pro/user_block","h":"","p":2892},{"i":2895,"t":"By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart. But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/pro/user_block","h":"#how-it-works","p":2892},{"i":2897,"t":"User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/pro/user_block","h":"#configure","p":2892},{"i":2899,"t":"Blocking data can be kept in Redis. To enable this configuration should be: { ... \"user_block\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/pro/user_block","h":"#redis-persistence-engine","p":2892},{"i":2901,"t":"Blocking data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"user_block\": { \"persistence_engine\": \"database\" }} tip To quickly start local PostgreSQL database: docker run -it --rm -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:15","s":"Database persistence engine","u":"/docs/pro/user_block","h":"#database-persistence-engine","p":2892},{"i":2904,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"2695\", \"expire_at\": 1635845122}' \\ http://localhost:8000/api/block_user block_user params​ Parameter name Parameter type Required Description user string yes User ID to block expire_at int no Unix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache). block_user result​ Empty object at the moment.","s":"block_user","u":"/docs/pro/user_block","h":"#block_user","p":2892},{"i":2906,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"2695\"}' \\ http://localhost:8000/api/unblock_user unblock_user params​ Parameter name Parameter type Required Description user string yes User ID to unblock unblock_user result​ Empty object at the moment.","s":"unblock_user","u":"/docs/pro/user_block","h":"#unblock_user","p":2892},{"i":2908,"t":"On this page","s":"Admin web UI","u":"/docs/server/admin_web","h":"","p":2907},{"i":2910,"t":"admin (boolean, default: false) – enables/disables admin web UI admin_password (string, default: \"\") – this is a password to log into admin web interface admin_secret (string, default: \"\") - this is a secret key for authentication token set on successful login. Make both admin_password and admin_secret strong and keep them in secret. After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface. tip Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.","s":"Options","u":"/docs/server/admin_web","h":"#options","p":2907},{"i":2912,"t":"If you want to use custom web interface you can specify path to web interface directory dist: config.json { ..., \"admin\": true, \"admin_password\": \"\", \"admin_secret\": \"\", \"admin_web_path\": \"\"} This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.","s":"Using custom web interface","u":"/docs/server/admin_web","h":"#using-custom-web-interface","p":2907},{"i":2914,"t":"There is also an option to run Centrifugo in insecure admin mode. In this mode, it's unnecessary to set admin_password and admin_secret in the configuration – you will be automatically logged into the web interface without any password. Note that this mode should only be considered for production if you have protected the admin web interface with firewall rules. Without such protection, anyone on the internet would have full access to the admin functionalities described above. To enable insecure admin mode: config.json { ..., \"admin\": true, \"admin_insecure\": true, \"admin_password\": \"\", \"admin_secret\": \"\"}","s":"Admin insecure mode","u":"/docs/server/admin_web","h":"#admin-insecure-mode","p":2907},{"i":2916,"t":"On this page","s":"User status API","u":"/docs/pro/user_status","h":"","p":2915},{"i":2918,"t":"Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript: await centrifuge.rpc('update_user_status', {}); note Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example. This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.","s":"Client-side status update RPC","u":"/docs/pro/user_status","h":"#client-side-status-update-rpc","p":2915},{"i":2920,"t":"It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports): curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"users\": [\"42\"]}' \\ http://localhost:8000/api/update_user_status Update user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to update status for Update user status result​ Empty object at the moment.","s":"update_user_status server API","u":"/docs/pro/user_status","h":"#update_user_status-server-api","p":2915},{"i":2922,"t":"Now on a backend side you have access to a bulk API to effectively get status of particular users. Call RPC method of server API (over HTTP or GRPC): curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"users\": [\"42\"]}' \\ http://localhost:8000/api/get_user_status You should get a response like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\", \"active\":1627107289, \"online\":1627107289 } ] }} In case information about last status update time not available the response will be like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\" } ] }} I.e. status object will present in a response but active field won't be set for status object. Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time). Get user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to get status for Get user status result​ Field name Field type Optional Description statuses array of UserStatus no Statuses for each user in params (same order) UserStatus​ Field name Field type Optional Description user string no User ID active integer yes Last active time (Unix seconds) online integer yes Last online time (Unix seconds)","s":"get_user_status server API","u":"/docs/pro/user_status","h":"#get_user_status-server-api","p":2915},{"i":2924,"t":"If you need to clear user status information for some reason there is a delete_user_status server API call: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"users\": [\"42\"]}' \\ http://localhost:8000/api/delete_user_status Delete user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to delete status for Delete user status result​ Empty object at the moment.","s":"delete_user_status server API","u":"/docs/pro/user_status","h":"#delete_user_status-server-api","p":2915},{"i":2926,"t":"To enable Redis user status feature: config.json { ... \"user_status\": { \"enabled\": true, \"redis_address\": \"127.0.0.1:6379\" }} Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"user_status\": { \"enabled\": true, \"use_redis_from_engine\": true, }} In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine. expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day: config.json { ... \"user_status\": { ... \"expire_interval\": \"24h\" }}","s":"Configuration","u":"/docs/pro/user_status","h":"#configuration","p":2915},{"i":2928,"t":"On this page","s":"Channel permission model","u":"/docs/server/channel_permissions","h":"","p":2927},{"i":2930,"t":"By default, client's attempt to subscribe on a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel subscribe permissions: Provide subscription token Configure subscribe proxy Use user-limited channels Use subscribe_allowed_for_client namespace option Subscribe capabilities in connection token Subscribe capabilities in connect proxy Below, we are describing those in detail. Provide subscription token​ A client can provide a subscription token in subscribe request. See the format of the token. If client provides a valid token then subscription will be accepted. In Centrifugo PRO subscription token can additionally grant publish, history and presence permissions to a client. caution For namespaces with allow_subscribe_for_client option ON Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without token. This limitation exists to help users migrate to Centrifugo v4 without security risks. Configure subscribe proxy​ If client subscribes on a namespace with configured subscribe proxy then depending on proxy response subscription will be accepted or not. If a namespace has configured subscribe proxy, but user came with a token – then subscribe proxy is not used, we are relying on token in this case. If a namespace has subscribe proxy, but user subscribes on a user-limited channel – then subscribe proxy is not used also. Use user-limited channels​ If client subscribes on a user-limited channel and there is a user ID match then subscription will be accepted. caution User-limited channels must be enabled in a namespace using allow_user_limited_channels option. Use allow_subscribe_for_client namespace option​ allow_subscribe_for_client allows all authenticated non-anonymous connections to subscribe on all channels in a namespace. caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security. To additionally allow subscribing to anonymous connections take a look at allow_subscribe_for_anonymous option. Subscribe capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user subscribe to channels. Subscribe capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow user subscribe to channels.","s":"Subscribe permission model","u":"/docs/server/channel_permissions","h":"#subscribe-permission-model","p":2927},{"i":2932,"t":"tip In idiomatic Centrifugo use case data should be published to channels from the application backend (over server API). In this case backend can validate data, save it into persistent storage before publishing in real-time towards connections. When publishing from the client-side backend does not receive publication data at all – it just goes through Centrifugo (except using publish proxy). There are cases when direct publications from the client-side are desired (like typing indicators in chat applications) though. By default, client's attempt to publish data into a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel publish permissions: Configure publish proxy Use allow_publish_for_subscriber namespace option Use allow_publish_for_client namespace option Publish capabilities in connection token Publish capability in subscription token Publish capabilities in connect proxy Publish capability in subscribe proxy Use allow_publish_for_client namespace option​ allow_publish_for_client allows publications to channels of a namespace for all client connections. Use allow_publish_for_subscriber namespace option​ allow_publish_for_subscriber allows publications to channels of a namespace for all connections subscribed on a channel they want to publish data into. Configure publish proxy​ If client publishes to a namespace with configured publish proxy then depending on proxy response publication will be accepted or not. Configured publish proxy always used??? (what if user has permission in token or allow_publish_for_client?) Publish capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to channels. Publish capability in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to a channel. Publish capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client publish to certain channels. Publish capability in subscribe proxy​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber publish to channel.","s":"Publish permission model","u":"/docs/server/channel_permissions","h":"#publish-permission-model","p":2927},{"i":2934,"t":"By default, client's attempt to call history from a channel (with history retention configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel history permissions. Use allow_history_for_subscriber namespace option​ allow_history_for_subscriber allows history requests to all channels in a namespace for all client connections subscribed on a channel they want to call history for. Use allow_history_for_client namespace option​ allow_history_for_client allows history requests to all channels in a namespace for all client connections. History capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history for channels. History capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history from a channel. History capabilities in connect proxy​ This is a Centrifugo PRO feature. Connect proxy can return capability object to allow client call history from certain channels. History capability in subscribe proxy response​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber call history from channel.","s":"History permission model","u":"/docs/server/channel_permissions","h":"#history-permission-model","p":2927},{"i":2936,"t":"By default, client's attempt to call presence from a channel (with channel presence configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel presence permissions. Presence capability in subscribe proxy response​ Subscribe proxy can return capability object to allow subscriber call presence from channel. Use allow_presence_for_subscriber namespace option​ allow_presence_for_subscriber allows presence requests to all channels in a namespace for all client connections subscribed on a channel they want to call presence for. Use allow_presence_for_client namespace option​ allow_presence_for_client allows presence requests to all channels in a namespace for all client connections. Presence capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence for channels. Presence capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence of a channel. Presence capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client call presence from certain channels.","s":"Presence permission model","u":"/docs/server/channel_permissions","h":"#presence-permission-model","p":2927},{"i":2938,"t":"Server can whether turn on positioning for all channels in a namespace using \"force_positioning\": true option or client can create positioned subscriptions (but in this case client must have access to history capability).","s":"Positioning permission model","u":"/docs/server/channel_permissions","h":"#positioning-permission-model","p":2927},{"i":2940,"t":"Server can whether turn on automatic recovery for all channels in a namespace using \"force_recovery\": true option or client can create recoverable subscriptions (but in this case client must have access to history capability).","s":"Recovery permission model","u":"/docs/server/channel_permissions","h":"#recovery-permission-model","p":2927},{"i":2942,"t":"Server can whether force sending join/leave messages to all subscribers for all channels in a namespace using \"force_push_join_leave\": true option or client can ask server to include join/leave messages upon subscribing (but in this case client must have access to presence capability).","s":"Join/Leave permission model","u":"/docs/server/channel_permissions","h":"#joinleave-permission-model","p":2927},{"i":2944,"t":"On this page","s":"Client JWT authentication","u":"/docs/server/authentication","h":"","p":2943},{"i":2946,"t":"For connection JWT, Centrifugo uses some standard claims defined in RFC 7519, as well as custom Centrifugo-specific claims.","s":"Connection JWT Claims","u":"/docs/server/authentication","h":"#connection-jwt-claims","p":2943},{"i":2948,"t":"This standard JWT claim must contain the ID of the current application user (as a string). If a user is not authenticated in the application but you wish to allow them to connect to Centrifugo, an empty string can be used as the user ID in the sub claim. This facilitates anonymous access. In such cases, you might need to enable the corresponding channel namespace options that allow protocol features for anonymous users.","s":"sub","u":"/docs/server/authentication","h":"#sub","p":2943},{"i":2950,"t":"This claim specifies the UNIX timestamp (in seconds) when the token will expire. It is a standard JWT claim - all JWT libraries across different programming languages provide an API to set it. If the exp claim is not included, Centrifugo will not expire the connection. When included, a special algorithm will identify connections with an exp in the past and initiate the connection refresh mechanism. The refresh mechanism allows a connection to be extended. If the refresh fails, Centrifugo will eventually close the client connection, which will not be accepted again until new valid and current credentials are provided in the connection token. The connection expiration mechanism can be utilized in scenarios where you do not want users to remain subscribed to channels after being banned or deactivated in the application. It also serves to protect users from token leakage by setting a reasonably short expiration time. Choose the exp value judiciously; too short a value can lead to frequent application hits with refresh requests, whereas too long a value can result in delayed user connection deactivation. It's a matter of balance. Further details on connection expiration can be found below.","s":"exp","u":"/docs/server/authentication","h":"#exp","p":2943},{"i":2952,"t":"This represents the UNIX time when the token was issued (in seconds). Refer to the definition in RFC 7519. This claim is optional but can be advantageous in conjunction with Centrifugo PRO's token revocation features.","s":"iat","u":"/docs/server/authentication","h":"#iat","p":2943},{"i":2954,"t":"This is a unique identifier for the token. Refer to the definition in RFC 7519. This claim is optional but can be beneficial in conjunction with Centrifugo PRO's token revocation features.","s":"jti","u":"/docs/server/authentication","h":"#jti","p":2943},{"i":2956,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But you can force this check by setting token_audience string option: config.json { \"token_audience\": \"centrifugo\"} caution Setting token_audience will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.","s":"aud","u":"/docs/server/authentication","h":"#aud","p":2943},{"i":2958,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But you can force this check by setting token_issuer string option: config.json { \"token_issuer\": \"my_app\"} caution Setting token_issuer will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.","s":"iss","u":"/docs/server/authentication","h":"#iss","p":2943},{"i":2960,"t":"This optional claim provides additional information about the client's connection for Centrifugo. This information will be included in presence data, join/leave events, and client-side channel publications.","s":"info","u":"/docs/server/authentication","h":"#info","p":2943},{"i":2962,"t":"For those utilizing the binary Protobuf protocol and requiring the info to be custom bytes, this field should be used. It contains a base64 encoded representation of your bytes. Centrifugo will decode the base64 back into bytes upon receipt and incorporate the result into the various places described above.","s":"b64info","u":"/docs/server/authentication","h":"#b64info","p":2943},{"i":2964,"t":"This is an optional array of strings identifying the server-side channels to which the client will be subscribed. Further details can be found in the documentation on server-side subscriptions. tip It's important to note that the channels claim is sometimes misinterpreted by users as a list of channel permissions. It does not serve that purpose. Instead, using this claim causes the client to be automatically subscribed to the specified channels upon connection, making it unnecessary to invoke the subscribe API from the client side. More information can be found in the server-side subscriptions documentation.","s":"channels","u":"/docs/server/authentication","h":"#channels","p":2943},{"i":2966,"t":"This optional claim is a map of channels with options, providing a more detailed approach to server-side subscriptions compared to the channels claim, as it allows for the annotation of each channel with additional information and data through options. tip The term subs is shorthand for subscriptions. It should not be confused with the sub claim mentioned earlier, which is a standard JWT claim used to provide a user ID (short for subject). Despite their similar names, these claims serve distinct purposes within a connection JWT. Example: { ... \"subs\": { \"channel1\": { \"data\": {\"welcome\": \"welcome to channel1\"} }, \"channel2\": { \"data\": {\"welcome\": \"welcome to channel2\"} } }} Subscribe options:​ Field Type Optional Description info JSON object yes Custom channel info b64info string yes Custom channel info in Base64 - to pass binary channel info data JSON object yes Custom JSON data to return in subscription context inside Connect reply b64data string yes Same as data but in Base64 to send binary data override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false}","s":"subs","u":"/docs/server/authentication","h":"#subs","p":2943},{"i":2968,"t":"meta is an additional JSON object (e.g., {\"key\": \"value\"}) that is attached to a connection. It differs from info as it is never disclosed to clients within presence and join/leave events; it is only accessible on the server side. It can be included in proxy calls from Centrifugo to the application backend (refer to the proxy_include_connection_meta option). In Centrifugo PRO, there is a connections API method that returns this metadata within the connection description object.","s":"meta","u":"/docs/server/authentication","h":"#meta","p":2943},{"i":2970,"t":"Although Centrifugo typically uses the exp claim to manage connection expiration, there may be scenarios where you want to separate the token expiration check from the connection expiration time. When the expire_at claim is included in the JWT, Centrifugo uses it to determine the connection expiration time, while the JWT expiration is still verified using the exp claim. expire_at is a UNIX timestamp indicating when the connection should expire. To expire the connection at a specific future time, set it to that time. To prevent connection expiration, set it to 0 (token exp claim will still be checked).","s":"expire_at","u":"/docs/server/authentication","h":"#expire_at","p":2943},{"i":2972,"t":"As mentioned, the exp claim in a connection token is designed to expire the client connection at some point in time. Here's a detailed look at the process when Centrifugo identifies that the connection is going to expire. First, activate the client expiration mechanism in Centrifugo by providing a connection JWT with an exp claim: import jwtimport timetoken = jwt.encode({\"sub\": \"42\", \"exp\": int(time.time()) + 10*60}, \"secret\", algorithm=\"HS256\")print(token) Assuming the exp claim is set to expire in 10 minutes, the client connects to Centrifugo with this token. Centrifugo will maintain the connection for the specified duration. Once the time elapses, Centrifugo allows a grace period (default is 25 seconds) for the client to refresh its credentials with a new valid token containing an updated exp. Upon initial connection, the client receives a ttl value in the connect response, indicating the seconds remaining before it must initiate a refresh command with new credentials. Centrifugo SDKs handle this ttl internally and automatically begin the refresh process. SDKs provide mechanisms to hook into this process and provide a function to get new token. It's up to developer to decide how to load new token from the backend – in web browser this is usually a simple fetch request and response may look like this: { \"token\": token} You should provide the same connection JWT you issued when the page was initially rendered, but with an updated and valid exp. Our SDKs will then send this token to the Centrifugo server, and the connection will be extended for the period set in the new exp. When you load new token from your app backend user authentication must be facilitated by your app's session mechanism. So you know for whom you are are going to generate an updated token.","s":"Connection expiration","u":"/docs/server/authentication","h":"#connection-expiration","p":2943},{"i":2974,"t":"Let's look at how to generate connection HS256 JWT in Python:","s":"Examples","u":"/docs/server/authentication","h":"#examples","p":2943},{"i":2976,"t":"Python NodeJS import jwttoken = jwt.encode({\"sub\": \"42\"}, \"secret\").decode()print(token) const jose = require('jose');(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42' }) .setProtectedHeader({ alg }) .sign(secret) console.log(token);})(); Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users. Then you can pass this token to your client side and use it when connecting to Centrifugo: Using centrifuge-js v3 var centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: token});centrifuge.connect(); See more details about working with connection tokens and handling token expiration on the client-side in the real-time SDK API spec.","s":"Simplest token","u":"/docs/server/authentication","h":"#simplest-token","p":2943},{"i":2978,"t":"HS256 token that will be valid for 5 minutes: Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"exp\": int(time.time()) + 5*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42' }) .setProtectedHeader({ alg }) .setExpirationTime('5m') .sign(secret) console.log(token);})();","s":"Token with expiration","u":"/docs/server/authentication","h":"#token-with-expiration","p":2943},{"i":2980,"t":"Let's attach user name: Python NodeJS import jwtclaims = {\"sub\": \"42\", \"info\": {\"name\": \"Alexander Emelin\"}}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42', info: {\"name\": \"Alexander Emelin\"} }) .setProtectedHeader({ alg }) .setExpirationTime('5m') .sign(secret) console.log(token);})();","s":"Token with additional connection info","u":"/docs/server/authentication","h":"#token-with-additional-connection-info","p":2943},{"i":2982,"t":"You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.","s":"Investigating problems with JWT","u":"/docs/server/authentication","h":"#investigating-problems-with-jwt","p":2943},{"i":2984,"t":"Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT). A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address). As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels. tip Read a tutorial in our blog about using Centrifugo with Keycloak SSO. In that case connection tokens are verified using public key loaded from the JWKS endpoint of Keycloak. At the moment Centrifugo caches keys loaded from an endpoint for one hour. Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment). Centrifugo supports the following key types (kty) for JWKs tokens: RSA EC (since Centrifugo v5.1.0) Once enabled JWKS used for both connection and channel subscription tokens.","s":"JSON Web Key support","u":"/docs/server/authentication","h":"#json-web-key-support","p":2943},{"i":2986,"t":"It's possible to extract variables from iss and aud JWT claims using Go regexp named groups, then use variables extracted during iss or aud matching to construct a JWKS endpoint dynamically upon token validation. In this case JWKS endpoint may be set in config as template. To achieve this Centrifugo provides two additional options: token_issuer_regex - match JWT issuer (iss claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. token_audience_regex - match JWT audience (aud claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. Let's look at the example: { \"token_issuer_regex\": \"https://example.com/auth/realms/(?P[A-z]+)\", \"token_jwks_public_endpoint\": \"https://keycloak:443/{{realm}}/protocol/openid-connect/certs\",} To use variable in token_jwks_public_endpoint it must be wrapped in {{ }}. When using token_issuer_regex and token_audience_regex make sure token_issuer and token_audience not used in the config - otherwise and error will be returned on Centrifugo start. caution Setting token_issuer_regex and token_audience_regex will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.","s":"Dynamic JWKs endpoint","u":"/docs/server/authentication","h":"#dynamic-jwks-endpoint","p":2943},{"i":2988,"t":"On this page","s":"Channel JWT authorization","u":"/docs/server/channel_token_auth","h":"","p":2987},{"i":2990,"t":"For subscription JWT Centrifugo uses some standard claims defined in rfc7519, also some custom Centrifugo-specific.","s":"Subscription JWT claims","u":"/docs/server/channel_token_auth","h":"#subscription-jwt-claims","p":2987},{"i":2992,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). The value must match a user in connection JWT – since it's the same real-time connection. The missing claim will mean that token issued for anonymous user (i.e. with empty user ID).","s":"sub","u":"/docs/server/channel_token_auth","h":"#sub","p":2987},{"i":2994,"t":"Required. Channel that client tries to subscribe to with this token (string).","s":"channel","u":"/docs/server/channel_token_auth","h":"#channel","p":2987},{"i":2996,"t":"Optional. Additional information for connection inside this channel (valid JSON).","s":"info","u":"/docs/server/channel_token_auth","h":"#info","p":2987},{"i":2998,"t":"Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.","s":"b64info","u":"/docs/server/channel_token_auth","h":"#b64info","p":2987},{"i":3000,"t":"Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time. At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs. Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.","s":"exp","u":"/docs/server/channel_token_auth","h":"#exp","p":2987},{"i":3002,"t":"Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the subscription should expire. Set it to the future time for expiring subscription at some point Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.","s":"expire_at","u":"/docs/server/channel_token_auth","h":"#expire_at","p":2987},{"i":3004,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.","s":"aud","u":"/docs/server/channel_token_auth","h":"#aud","p":2987},{"i":3006,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.","s":"iss","u":"/docs/server/channel_token_auth","h":"#iss","p":2987},{"i":3008,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/server/channel_token_auth","h":"#iat","p":2987},{"i":3010,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/server/channel_token_auth","h":"#jti","p":2987},{"i":3012,"t":"One more claim is override. This is an object which allows overriding channel options for the particular channel subscriber which comes with subscription token. Field Type Optional Description presence BoolValue yes override presence channel option join_leave BoolValue yes override join_leave channel option force_push_join_leave BoolValue yes override force_push_join_leave channel option force_recovery BoolValue yes override force_recovery channel option force_positioning BoolValue yes override force_positioning channel option BoolValue is an object like this: { \"value\": true/false} So for example, you want to turn off emitting a presence information for a particular subscriber in a channel: { ... \"override\": { \"presence\": { \"value\": false } }}","s":"override","u":"/docs/server/channel_token_auth","h":"#override","p":2987},{"i":3014,"t":"So to generate a subscription token you can use something like this in Python (assuming user ID is 42 and the channel is gossips): Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"channel\": \"$gossips\", \"exp\": int(time.time()) + 3600}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42', channel: '$gossips' }) .setProtectedHeader({ alg }) .setExpirationTime('1h') .sign(secret) console.log(token);})(); Where \"secret\" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).","s":"Example","u":"/docs/server/channel_token_auth","h":"#example","p":2987},{"i":3016,"t":"During development you can quickly generate valid subscription token using Centrifugo gensubtoken cli command. ./centrifugo gensubtoken -u 123722 -s channel You should see an output like this: HMAC SHA-256 JWT for user \"123722\" and channel \"channel\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDg0MzgsImNoYW5uZWwiOiJjaGFubmVsIn0.JyRI3ovNV-abV8VxCmZCD556o2F2mNL1UoU58gNR-uI But in real app subscription JWT must be generated by your application backend.","s":"gensubtoken cli command","u":"/docs/server/channel_token_auth","h":"#gensubtoken-cli-command","p":2987},{"i":3018,"t":"When separate_subscription_token_config boolean option is true Centrifugo does not look at general token options at all when verifying subscription tokens and uses config options starting from subscription_token_ prefix instead. Here is an example how to use JWKS for connection tokens, but have HMAC-based verification for subscription tokens: config.json { \"token_jwks_public_endpoint\": \"https://example.com/openid-connect/certs\", \"separate_subscription_token_config\": true, \"subscription_token_hmac_secret_key\": \"separate_secret_which_must_be_strong\"} All the options which are available for connection token configuration may be re-used for a separate subscription token configuration – just prefix them with subscription_token_ instead of token_.","s":"Separate subscription token config","u":"/docs/server/channel_token_auth","h":"#separate-subscription-token-config","p":2987},{"i":3020,"t":"On this page","s":"Error and disconnect codes","u":"/docs/server/codes","h":"","p":3019},{"i":3022,"t":"Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client. Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).","s":"Client error codes","u":"/docs/server/codes","h":"#client-error-codes","p":3019},{"i":3024,"t":"Code: 100Message: \"internal server error\"Temporary: true Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.","s":"Internal","u":"/docs/server/codes","h":"#internal","p":3019},{"i":3026,"t":"Code: 101Message: \"unauthorized\" Error Unauthorized says that request is unauthorized.","s":"Unauthorized","u":"/docs/server/codes","h":"#unauthorized","p":3019},{"i":3028,"t":"Code: 102Message: \"unknown channel\" Error Unknown Channel means that channel name does not exist. Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.","s":"Unknown Channel","u":"/docs/server/codes","h":"#unknown-channel","p":3019},{"i":3030,"t":"Code: 103Message: \"permission denied\" Error Permission Denied means that access to resource not allowed.","s":"Permission Denied","u":"/docs/server/codes","h":"#permission-denied","p":3019},{"i":3032,"t":"Code: 104Message: \"method not found\" Error Method Not Found means that method sent in command does not exist.","s":"Method Not Found","u":"/docs/server/codes","h":"#method-not-found","p":3019},{"i":3034,"t":"Code: 105Message: \"already subscribed\" Error Already Subscribed returned when a client attempts to subscribe to a channel to which it is already subscribed. In Centrifugo, a client can only have one subscription to a specific channel. When using client-side subscriptions, this error may signal a bug in the SDK or the SDK being used in a way that was not planned. This error may also be returned by the server when a client tries to subscribe to a channel but is already subscribed to it using server-side subscriptions.","s":"Already Subscribed","u":"/docs/server/codes","h":"#already-subscribed","p":3019},{"i":3036,"t":"Code: 106Message: \"limit exceeded\" Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.","s":"Limit Exceeded","u":"/docs/server/codes","h":"#limit-exceeded","p":3019},{"i":3038,"t":"Code: 107Message: \"bad request\" Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.","s":"Bad Request","u":"/docs/server/codes","h":"#bad-request","p":3019},{"i":3040,"t":"Code: 108Message: \"not available\" Error Not Available means that resource is not enabled. For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.","s":"Not Available","u":"/docs/server/codes","h":"#not-available","p":3019},{"i":3042,"t":"Code: 109Message: \"token expired\" Error Token Expired indicates that connection token expired. Our SDKs handle it in a special way by updating token.","s":"Token Expired","u":"/docs/server/codes","h":"#token-expired","p":3019},{"i":3044,"t":"Code: 110Message: \"expired\" Error Expired indicates that connection expired (no token involved).","s":"Expired","u":"/docs/server/codes","h":"#expired","p":3019},{"i":3046,"t":"Code: 111Message: \"too many requests\"Temporary: true Error Too Many Requests means that server rejected request due to rate limiting strategies.","s":"Too Many Requests","u":"/docs/server/codes","h":"#too-many-requests","p":3019},{"i":3048,"t":"Code: 112Message: \"unrecoverable position\" Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch passed.","s":"Unrecoverable Position","u":"/docs/server/codes","h":"#unrecoverable-position","p":3019},{"i":3050,"t":"Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes). note We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. We expect these codes are mostly useful for logs and metrics.","s":"Client disconnect codes","u":"/docs/server/codes","h":"#client-disconnect-codes","p":3019},{"i":3052,"t":"Code: 3000Reason: \"connection closed\" DisconnectConnectionClosed is a special Disconnect object used when client connection was closed without any advice from a server side. This can be a clean disconnect, or temporary disconnect of the client due to internet connection loss. Server can not distinguish the actual reason of disconnect.","s":"DisconnectConnectionClosed","u":"/docs/server/codes","h":"#disconnectconnectionclosed","p":3019},{"i":3054,"t":"Client will reconnect after receiving such codes. Shutdown​ Code: 3001Reason: \"shutdown\" Disconnect Shutdown may be sent when node is going to shut down. DisconnectServerError​ Code: 3004Reason: \"internal server error\" DisconnectServerError issued when internal error occurred on server. DisconnectExpired​ Code: 3005Reason: \"connection expired\" DisconnectSubExpired​ Code: 3006Reason: \"subscription expired\" DisconnectSubExpired issued when client subscription expired. DisconnectSlow​ Code: 3008Reason: \"slow\" DisconnectSlow issued when client can't read messages fast enough. DisconnectWriteError​ Code: 3009Reason: \"write error\" DisconnectWriteError issued when an error occurred while writing to client connection. DisconnectInsufficientState​ Code: 3010Reason: \"insufficient state\" DisconnectInsufficientState issued when server detects wrong client position in channel Publication stream. Disconnect allows clien to restore missed publications on reconnect. DisconnectForceReconnect​ Code: 3011Reason: \"force reconnect\" DisconnectForceReconnect issued when server disconnects connection for some reason and whants it to reconnect. DisconnectNoPong​ Code: 3012Reason: \"no pong\" DisconnectNoPong may be issued when server disconnects bidirectional connection due to no pong received to application-level server-to-client pings in a configured time. DisconnectTooManyRequests​ Code: 3013Reason: \"too many requests\" DisconnectTooManyRequests may be issued when client sends too many commands to a server.","s":"Non-terminal disconnect codes","u":"/docs/server/codes","h":"#non-terminal-disconnect-codes","p":3019},{"i":3056,"t":"Client won't reconnect upon receiving such code. DisconnectInvalidToken​ Code: 3500Reason: \"invalid token\" DisconnectInvalidToken issued when client came with invalid token. DisconnectBadRequest​ Code: 3501Reason: \"bad request\" DisconnectBadRequest issued when client uses malformed protocol frames. DisconnectStale​ Code: 3502Reason: \"stale\" DisconnectStale issued to close connection that did not become authenticated in configured interval after dialing. DisconnectForceNoReconnect​ Code: 3503Reason: \"force disconnect\" DisconnectForceNoReconnect issued when server disconnects connection and asks it to not reconnect again. DisconnectConnectionLimit​ Code: 3504Reason: \"connection limit\" DisconnectConnectionLimit can be issued when client connection exceeds a configured connection limit (per user ID or due to other rule). DisconnectChannelLimit​ Code: 3505Reason: \"channel limit\" DisconnectChannelLimit can be issued when client connection exceeds a configured channel limit. DisconnectInappropriateProtocol​ Code: 3506Reason: \"inappropriate protocol\" DisconnectInappropriateProtocol can be issued when client connection format can not handle incoming data. For example, this happens when JSON-based clients receive binary data in a channel. This is usually an indicator of programmer error, JSON clients can not handle binary. DisconnectPermissionDenied​ Code: 3507Reason: \"permission denied\" DisconnectPermissionDenied may be issued when client attempts accessing a server without enough permissions. DisconnectNotAvailable​ Code: 3508Reason: \"not available\" DisconnectNotAvailable may be issued when ErrorNotAvailable does not fit message type, for example we issue DisconnectNotAvailable when client sends asynchronous message without MessageHandler set on server side. DisconnectTooManyErrors​ Code: 3509Reason: \"too many errors\" DisconnectTooManyErrors may be issued when client generates too many errors.","s":"Terminal disconnect codes","u":"/docs/server/codes","h":"#terminal-disconnect-codes","p":3019},{"i":3058,"t":"On this page","s":"Channels and namespaces","u":"/docs/server/channels","h":"","p":3057},{"i":3060,"t":"Centrifugo operates on a PUB/SUB model - it has publishers and subscribers. A channel acts as a conduit for publications. Clients can subscribe to a channel to receive all the real-time messages published there. Subscribers to a channel may also request information about the channel's online presence or its history. A channel is simply a string - names like news, comments, personal_feed are examples of valid channel names. However, there are predefined rules for these strings, as we will discuss later. You can define different behaviors for a channel using a range of available channel options. Channels are ephemeral – there is no need to create them explicitly. Channels are automatically created by Centrifugo as soon as the first client subscribes. Similarly, when the last subscriber leaves, the channel is automatically cleaned up. A channel can be part of a channel namespace. Channel namespacing is a mechanism to define different behaviors for various channels within Centrifugo. Using namespaces is the recommended approach to manage channels – enabling only those channel options which are necessary for the specific real-time feature you are implementing with Centrifugo. caution Ensure you have defined a namespace in the configuration when using channel namespaces. Attempts to subscribe to a channel within an undefined namespace will result in 102: unknown channel errors.","s":"What is a channel","u":"/docs/server/channels","h":"#what-is-a-channel","p":3057},{"i":3062,"t":"Only ASCII symbols must be used in a channel string. Channel name length limited by 255 characters by default (controlled by configuration option channel_max_length). Several symbols in channel names reserved for Centrifugo internal needs: : – for namespace channel boundary (see below) # – for user channel boundary (see below) $ – for private channel prefix (see below) / – for Channel Patterns in Centrifugo PRO * – for the future Centrifugo needs & – for the future Centrifugo needs","s":"Channel name rules","u":"/docs/server/channels","h":"#channel-name-rules","p":3057},{"i":3064,"t":": – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about about namespaces and channel options below. If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public. info A namespace is a inalienable component of the channel name. If a user is subscribed to a channel with a namespace, such as public:chat, then you must publish messages to the public:chat channel for them to be delivered to the user. There is often confusion among developers who try to publish messages to chat, mistakenly believing that the namespace is stripped upon subscription. This is not the case. You must publish exactly to the same channel string you used for subscribing.","s":"namespace boundary (:)","u":"/docs/server/channels","h":"#namespace-boundary-","p":3057},{"i":3066,"t":"# symbol serves as the user channel boundary. It acts as a separator to create personal channels for users—referred to as user-limited channels—without requiring a subscription token. For instance, if the channel is named news#42, then only the user with ID 42 can subscribe to this channel. Centrifugo identifies the user ID from the connection credentials provided in the connection JWT. To create a user-limited channel within the personal namespace, you might use a name such as personal:user#42. Furthermore, it's possible to specify multiple user IDs in the channel name, separated by a comma: dialog#42,43. In this case, only users with IDs 42 and 43 are permitted to subscribe to this channel. This setup is ideal for channels that have a static list of allowed users, such as channels for personal messages to a single user or dialogue channels between specific users. However, for dynamic access management of a channel for numerous users, this type of channel is not appropriate. tip User-limited channels must be enabled for a channel namespace using allow_user_limited_channels option. See below more information about channel options and channel namespaces.","s":"user channel boundary (#)","u":"/docs/server/channels","h":"#user-channel-boundary-","p":3057},{"i":3068,"t":"Centrifugo maintains compatibility with its previous versions which had concept of private channels. In earlier versions — specifically Centrifugo v1, v2, and v3—only – only channels beginning with $ required a subscription JWT for subscribing. With Centrifugo v4, this is no longer the case; clients can subscribe to any channel if they have a valid subscription token. However, for namespaces where the allow_subscribe_for_client option is activated, Centrifugo prohibits subscriptions to channels that start with the private_channel_prefix (which defaults to $) unless a subscription token is provided. This restriction is designed to facilitate a secure migration to Centrifugo v4 or later versions.","s":"private channel prefix ($)","u":"/docs/server/channels","h":"#private-channel-prefix-","p":3057},{"i":3070,"t":"Bear in mind that a channel is uniquely identified by its string representation. Do not assume that channels $news and news are the same; they differ because their strings are not identical. Thus, if a user is subscribed to $news, they will not receive messages published to news. The channels dialog#42,43 and dialog#43,42 are considered different as well. Centrifugo only applies permission checks when a user subscribes to a channel. So if user-limited channels are enabled then the user with ID 42 will be able to subscribe on both dialog#42,43 and dialog#43,42. But Centrifugo does no magic regarding channel strings when keeping channel->to->subscribers map. So if the user subscribed on dialog#42,43 you must publish messages to exactly that channel: dialog#42,43. The same reasoning applies to channels within namespaces. Channels chat:index and index are not the same — they are distinct and, moreover, they belong to different namespaces. The concept of channel namespaces in Centrifugo will be discussed shortly.","s":"Channel is just a string","u":"/docs/server/channels","h":"#channel-is-just-a-string","p":3057},{"i":3072,"t":"Centrifugo allows configuring a list of channel namespaces. Namespaces are optional but very useful. A namespace is a container for options applied for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application. A namespace acts as a container for options that are applied to channels starting with the namespace name. This provides great control over channel behavior, so you have a flexible way to define different channel options for various real-time features in the application. Namespace has a name, and can contain all the channel options described below (with the same defaults). name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$). If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator: public:messages gossips:messages Where public and gossips are namespace names. Centrifugo looks for : symbol in the channel name, if found – extracts the namespace name, and applies namespace options while processing protocol commands from a client. All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured: config.json { \"token_hmac_secret_key\": \"very-long-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"30s\", \"namespaces\": [ { \"name\": \"facts\", \"history_size\": 10, \"history_ttl\": \"300s\" }, { \"name\": \"gossips\" } ]} Channel news will use globally defined channel options. Channel facts:sport will use facts namespace options. Channel gossips:sport will use gossips namespace options. Channel xxx:hello will result into subscription error since there is no xxx namespace defined in the configuration above. Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2. note There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly. There are many options which can be set for channel namespace (on top-level and to named one) to modify behavior of channels belonging to a namespace. Below we describe all these options.","s":"Channel namespaces","u":"/docs/server/channels","h":"#channel-namespaces","p":3057},{"i":3074,"t":"Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.","s":"Channel options","u":"/docs/server/channels","h":"#channel-options","p":3057},{"i":3076,"t":"presence (boolean, default false) – enable/disable online presence information for channels in a namespace. Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels. Let's say you have a channel chat:index and 2 users (with ID 2694 and 56) subscribed to it. And user 2694 has 2 connections to Centrifugo in different browser tabs. In presence data you may see sth like this: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat:index\"}' \\ http://localhost:8000/api/presence{ \"result\": { \"presence\": { \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\": { \"user\": \"2694\", \"client\": \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\", \"conn_info\": {\"name\": \"Alex\"} }, \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\": { \"user\": \"2694\", \"client\": \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\", \"conn_info\": {\"name\": \"Alex\"} } \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\": { \"user\": \"56\", \"client\": \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\", \"conn_info\": {\"name\": \"Alice\"} } } }} To call presence API from the client connection side client must have permission to do so. See presence permission model. caution Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or in a broker memory/disk). So only use it for channels where presence is required. See more details about online presence design.","s":"presence","u":"/docs/server/channels","h":"#presence","p":3057},{"i":3078,"t":"join_leave (boolean, default false) – enable/disable sending join and leave messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info (similar to entry inside presence information). Enabling join_leave means that Join/Leave messages will start being emitted, but by default they are not delivered to clients subscribed to a channel. You need to force this using namespace option force_push_join_leave or explicitly provide intent from a client-side (in this case client must have permission to call presence API). caution Keep in mind that join/leave messages can generate a huge number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature. It's hard to say what is \"large\" for you though – just estimate the load based on the fact that each subscribe/unsubscribe event in a channel with N subscribers will result into N messages broadcasted to all. If all clients reconnect at the same time the amount of generated messages is N^2. Join/leave messages distributed only with at most once delivery guarantee.","s":"join_leave","u":"/docs/server/channels","h":"#join_leave","p":3057},{"i":3080,"t":"Boolean, default false. When on all clients will receive join/leave events for a channel in a namespace automatically – without explicit intent to consume join/leave messages from the client side. If pushing join/leave is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel presence (by having an explicit capability or if allowed on a namespace level).","s":"force_push_join_leave","u":"/docs/server/channels","h":"#force_push_join_leave","p":3057},{"i":3082,"t":"history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted. Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below). caution Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk). So only use history for channels where it's required.","s":"history_size","u":"/docs/server/channels","h":"#history_size","p":3057},{"i":3084,"t":"history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision). As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels. By default history TTL duration is zero – this means that channel history is disabled. Again – to turn on history you should wisely configure both history_size and history_ttl options. For example for top-level channels (which do not belong to a namespace): config.json { ... \"history_size\": 10, \"history_ttl\": \"60s\"} Let's look at example. You enabled history for a namespace chat and sent two messages in channel chat:index. Then history will contain sth like this: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat:index\", \"limit\": 100}' \\ http://localhost:8000/api/history{ \"result\": { \"publications\": [ { \"data\": { \"input\": \"1\" }, \"offset\": 1 }, { \"data\": { \"input\": \"2\" }, \"offset\": 2 } ], \"epoch\": \"gWuY\", \"offset\": 2 }} To call history API from the client connection side client must have permission to do so. See history permission model. See additional information about offsets and epoch in History and recovery chapter. tip History persistence properties are dictated by Centrifugo engine used. For example, when using memory engine history is only kept till Centrifugo node restart. In Redis engine case persistence is determined by a Redis server persistence configuration (same for KeyDB and Tarantool).","s":"history_ttl","u":"/docs/server/channels","h":"#history_ttl","p":3057},{"i":3086,"t":"history_meta_ttl (duration) – sets a time of history stream metadata expiration (with seconds precision). When using a history in a channel, Centrifugo keeps some metadata for each channel stream. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help to expire metadata faster (or slower if you need it). The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration. If not specified Centrifugo uses a global history_meta_ttl which is 30 days. This should be a good default for most use cases to avoid tweaking history_meta_ttl on a namespace level at all.","s":"history_meta_ttl","u":"/docs/server/channels","h":"#history_meta_ttl","p":3057},{"i":3088,"t":"force_positioning (boolean, default false) – when the force_positioning option is on Centrifugo forces all subscriptions in a namespace to be positioned. I.e. Centrifugo will try to compensate at most once delivery of PUB/SUB broker checking client position inside a stream. If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API. force_positioning option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream). If positioning is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).","s":"force_positioning","u":"/docs/server/channels","h":"#force_positioning","p":3057},{"i":3090,"t":"force_recovery (boolean, default false) – when the position option is on Centrifugo forces all subscriptions in a namespace to be recoverable. When enabled Centrifugo will try to recover missed publications in channels after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the force_positioning option described above. force_recovery option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages). If recovery is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level). tip Not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.","s":"force_recovery","u":"/docs/server/channels","h":"#force_recovery","p":3057},{"i":3092,"t":"allow_subscribe_for_client (boolean, default false) – when on all non-anonymous clients will be able to subscribe to any channel in a namespace. To additionally allow anonymous users to subscribe turn on allow_subscribe_for_anonymous (see below). caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.","s":"allow_subscribe_for_client","u":"/docs/server/channels","h":"#allow_subscribe_for_client","p":3057},{"i":3094,"t":"allow_subscribe_for_anonymous (boolean, default false) – turn on if anonymous clients (with empty user ID) should be able to subscribe on channels in a namespace.","s":"allow_subscribe_for_anonymous","u":"/docs/server/channels","h":"#allow_subscribe_for_anonymous","p":3057},{"i":3096,"t":"allow_publish_for_subscriber (boolean, default false) - when the allow_publish_for_subscriber option is enabled client can publish into a channel in namespace directly from the client side over real-time connection but only if client subscribed to that channel. danger Keep in mind that in this case subscriber can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage). allow_publish_for_subscriber (or allow_publish_for_client mentioned below) option still can be useful to send something without backend-side validation and saving it into a database – for example, this option may be handy for demos and quick prototyping real-time app ideas.","s":"allow_publish_for_subscriber","u":"/docs/server/channels","h":"#allow_publish_for_subscriber","p":3057},{"i":3098,"t":"allow_publish_for_client (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). It's like allow_publish_for_subscriber – but client should not be a channel subscriber to publish. danger Keep in mind that in this case client can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).","s":"allow_publish_for_client","u":"/docs/server/channels","h":"#allow_publish_for_client","p":3057},{"i":3100,"t":"allow_publish_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to publish into channels in a namespace.","s":"allow_publish_for_anonymous","u":"/docs/server/channels","h":"#allow_publish_for_anonymous","p":3057},{"i":3102,"t":"allow_history_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call history API from that channel.","s":"allow_history_for_subscriber","u":"/docs/server/channels","h":"#allow_history_for_subscriber","p":3057},{"i":3104,"t":"allow_history_for_client (boolean, default false) – allows all clients to call history information in a namespace.","s":"allow_history_for_client","u":"/docs/server/channels","h":"#allow_history_for_client","p":3057},{"i":3106,"t":"allow_history_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call history from channels in a namespace.","s":"allow_history_for_anonymous","u":"/docs/server/channels","h":"#allow_history_for_anonymous","p":3057},{"i":3108,"t":"allow_presence_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call presence information from that channel.","s":"allow_presence_for_subscriber","u":"/docs/server/channels","h":"#allow_presence_for_subscriber","p":3057},{"i":3110,"t":"allow_presence_for_client (boolean, default false) – allows all clients to call presence information in a namespace.","s":"allow_presence_for_client","u":"/docs/server/channels","h":"#allow_presence_for_client","p":3057},{"i":3112,"t":"allow_presence_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call presence from channels in a namespace.","s":"allow_presence_for_anonymous","u":"/docs/server/channels","h":"#allow_presence_for_anonymous","p":3057},{"i":3114,"t":"allow_user_limited_channels (boolean, default false) - allows using user-limited channels in a namespace for checking subscribe permission. note If client subscribes to a user-limited channel while this option is off then server rejects subscription with 103: permission denied error.","s":"allow_user_limited_channels","u":"/docs/server/channels","h":"#allow_user_limited_channels","p":3057},{"i":3116,"t":"channel_regex (string, default \"\") – is an option to set a regular expression for channels allowed in the namespace. By default Centrifugo does not limit channel name variations. For example, if you have a namespace chat, then channel names inside this namespace are not really limited, it can be chat:index, chat:1, chat:2, chat:zzz and so on. But if you want to be strict and know possible channel patterns you can use channel_regex option. This is especially useful in namespaces where all clients can subscribe to channels. For example, let's only allow digits after chat: for channel names in a chat namespace: { \"namespaces\": [ { \"name\": \"chat\", \"allow_subscribe_for_client\": true, \"channel_regex\": \"^[\\d+]$\" } ]} danger Note, that we are skipping chat: part in regex. Since namespace prefix is the same for all channels in a namespace we only match the rest (after the prefix) of channel name. Channel regex only checked for client-side subscriptions, if you are using server-side subscriptions Centrifugo won't check the regex. Centrifugo uses Go language regexp package for regular expressions.","s":"channel_regex","u":"/docs/server/channels","h":"#channel_regex","p":3057},{"i":3118,"t":"proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter","s":"proxy_subscribe","u":"/docs/server/channels","h":"#proxy_subscribe","p":3057},{"i":3120,"t":"proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter","s":"proxy_publish","u":"/docs/server/channels","h":"#proxy_publish","p":3057},{"i":3122,"t":"proxy_sub_refresh (boolean, default false) – turns on sub refresh proxy, more info in proxy chapter","s":"proxy_sub_refresh","u":"/docs/server/channels","h":"#proxy_sub_refresh","p":3057},{"i":3124,"t":"proxy_subscribe_stream (boolean, default false) - turns on subscribe stream proxy, see subscription streams","s":"proxy_subscribe_stream","u":"/docs/server/channels","h":"#proxy_subscribe_stream","p":3057},{"i":3126,"t":"subscribe_proxy_name (string, default \"\") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.","s":"subscribe_proxy_name","u":"/docs/server/channels","h":"#subscribe_proxy_name","p":3057},{"i":3128,"t":"publish_proxy_name (string, default \"\") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.","s":"publish_proxy_name","u":"/docs/server/channels","h":"#publish_proxy_name","p":3057},{"i":3130,"t":"sub_refresh_proxy_name (string, default \"\") – turns on sub refresh proxy when granular proxy mode is used. Note that proxy_sub_refresh option defined above is ignored in granular proxy mode.","s":"sub_refresh_proxy_name","u":"/docs/server/channels","h":"#sub_refresh_proxy_name","p":3057},{"i":3132,"t":"subscribe_stream_proxy_name (string, default \"\") – turns on subscribe stream proxy when granular proxy mode is used. Note that proxy_subscribe_stream option defined above is ignored in granular proxy mode.","s":"subscribe_stream_proxy_name","u":"/docs/server/channels","h":"#subscribe_stream_proxy_name","p":3057},{"i":3134,"t":"Let's look at how to set some of these options in a config. In this example we turning on presence, history features, forcing publication recovery. Also allowing all client connections (including anonymous users) to subscribe to channels and call publish, history, presence APIs if subscribed. config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true} Here we set channel options on config top-level – these options will affect channels without namespace. In many cases defining namespaces is a recommended approach so you can manage options for every real-time feature separately. With namespaces the above config may transform to: config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"namespaces\": [ { \"name\": \"feed\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true } ]} In this case channels should be prefixed with feed: to follow the behavior configured for a feed namespace.","s":"Channel config examples","u":"/docs/server/channels","h":"#channel-config-examples","p":3057},{"i":3136,"t":"On this page","s":"Helper CLI commands","u":"/docs/server/console_commands","h":"","p":3135},{"i":3138,"t":"To show Centrifugo version and exit run: centrifugo version","s":"version","u":"/docs/server/console_commands","h":"#version","p":3135},{"i":3140,"t":"Another command is genconfig: centrifugo genconfig -c config.json It will automatically generate the minimal required configuration file. This is mostly useful for development. If any errors happen – program will exit with error message and exit code 1. genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file: centrifugo genconfig -c config.toml","s":"genconfig","u":"/docs/server/console_commands","h":"#genconfig","p":3135},{"i":3142,"t":"Centrifugo has special command to check configuration file checkconfig: centrifugo checkconfig --config=config.json If any errors found during validation – program will exit with error message and exit code 1.","s":"checkconfig","u":"/docs/server/console_commands","h":"#checkconfig","p":3135},{"i":3144,"t":"Another command is gentoken: centrifugo gentoken -c config.json -u 28282 It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gentoken","u":"/docs/server/console_commands","h":"#gentoken","p":3135},{"i":3146,"t":"Another command is gensubtoken: centrifugo gensubtoken -c config.json -u 28282 -s channel It will automatically generate HMAC SHA-256 based subscription token for channel channel and user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -s channel -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gensubtoken","u":"/docs/server/console_commands","h":"#gensubtoken","p":3135},{"i":3148,"t":"One more command is checktoken: centrifugo checktoken -c config.json It will validate your connection JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checktoken","u":"/docs/server/console_commands","h":"#checktoken","p":3135},{"i":3150,"t":"One more command is checksubtoken: centrifugo checksubtoken -c config.json It will validate your subscription JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checksubtoken","u":"/docs/server/console_commands","h":"#checksubtoken","p":3135},{"i":3152,"t":"On this page","s":"Configure Centrifugo","u":"/docs/server/configuration","h":"","p":3151},{"i":3154,"t":"Centrifugo can be configured in several ways: using command-line flags (highest priority), environment variables (second priority after flags), configuration file (lowest priority).","s":"Configuration sources","u":"/docs/server/configuration","h":"#configuration-sources","p":3151},{"i":3156,"t":"Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer using environment variables or configuration file. Command-line options have the highest priority when set than other ways to configure Centrifugo.","s":"Command-line flags","u":"/docs/server/configuration","h":"#command-line-flags","p":3151},{"i":3158,"t":"All Centrifugo options can be set over env in the format CENTRIFUGO_ (i.e. option name with CENTRIFUGO_ prefix, all in uppercase). Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags. Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use \"true\" value for an environment variable (or simply \"1\"). To set false use \"false\" or \"0\". Example: export CENTRIFUGO_PROMETHEUS=\"1\" Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example: export CENTRIFUGO_ALLOWED_ORIGINS=\"https://mysite1.example.com https://mysite2.example.com\" For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name. Empty environment variables are considered unset (!) and will fall back to the next configuration source.","s":"OS environment variables","u":"/docs/server/configuration","h":"#os-environment-variables","p":3151},{"i":3160,"t":"Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable is preferred over the same option in config file). A simple way to start with Centrifugo is to run: centrifugo genconfig This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo: centrifugo -c config.json","s":"Configuration file","u":"/docs/server/configuration","h":"#configuration-file","p":3151},{"i":3162,"t":"Centrifugo supports three configuration file formats: JSON, YAML, or TOML.","s":"Config file formats","u":"/docs/server/configuration","h":"#config-file-formats","p":3151},{"i":3164,"t":"Here is an example of Centrifugo JSON configuration file: config.json { \"allowed_origins\": [\"http://localhost:3000\"], \"token_hmac_secret_key\": \"\", \"api_key\": \"\"} token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT. api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients. allowed_origins option described below.","s":"JSON config format","u":"/docs/server/configuration","h":"#json-config-format","p":3151},{"i":3166,"t":"Centrifugo also supports TOML format for configuration file: centrifugo --config=config.toml Where config.toml contains: config.toml allowed_origins: [ \"http://localhost:3000\" ]token_hmac_secret_key = \"\"api_key = \"\"log_level = \"debug\" In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.","s":"TOML config format","u":"/docs/server/configuration","h":"#toml-config-format","p":3151},{"i":3168,"t":"YAML format is also supported: config.yaml allowed_origins: - \"http://localhost:3000\"token_hmac_secret_key: \"\"api_key: \"\"log_level: debug With YAML remember to use spaces, not tabs when writing a configuration file.","s":"YAML config format","u":"/docs/server/configuration","h":"#yaml-config-format","p":3151},{"i":3170,"t":"Let's describe some important options you can configure when running Centrifugo.","s":"Important options","u":"/docs/server/configuration","h":"#important-options","p":3151},{"i":3172,"t":"This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins. As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array. Connection requests without Origin header set are passing through without any checks (i.e. always allowed). For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way: \"allowed_origins\": [ \"http://localhost:3000\"] When connecting from https://example.com: \"allowed_origins\": [ \"https://example.com\"] Origin pattern can contain wildcard symbol * to match subdomains: \"allowed_origins\": [ \"https://*.example.com\"] – in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check. It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature): \"allowed_origins\": [ \"*\"]","s":"allowed_origins","u":"/docs/server/configuration","h":"#allowed_origins","p":3151},{"i":3174,"t":"Bind your Centrifugo to a specific interface address (string, by default \"\" - listen on all available interfaces).","s":"address","u":"/docs/server/configuration","h":"#address","p":3151},{"i":3176,"t":"Port to bind Centrifugo to (string, by default \"8000\").","s":"port","u":"/docs/server/configuration","h":"#port","p":3151},{"i":3178,"t":"Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.","s":"engine","u":"/docs/server/configuration","h":"#engine","p":3151},{"i":3180,"t":"These options allow tweaking server behavior, in most cases default values are good to start with.","s":"Advanced options","u":"/docs/server/configuration","h":"#advanced-options","p":3151},{"i":3182,"t":"Default: 128 Sets the maximum number of different channel subscriptions a single client can have. tip When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.","s":"client_channel_limit","u":"/docs/server/configuration","h":"#client_channel_limit","p":3151},{"i":3184,"t":"Default: 255 Sets the maximum length of the channel name.","s":"channel_max_length","u":"/docs/server/configuration","h":"#channel_max_length","p":3151},{"i":3186,"t":"Default: 0 The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited. The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).","s":"client_user_connection_limit","u":"/docs/server/configuration","h":"#client_user_connection_limit","p":3151},{"i":3188,"t":"Default: 0 When set to a value > 0 client_connection_limit limits the max number of connections single Centrifugo node can handle. It acts on HTTP middleware level and stops processing request if the condition met. It logs a warning into logs in this case and increments centrifugo_node_client_connection_limit Prometheus counter. Client SDKs will attempt reconnecting. Some motivation behind this option may be found in this issue. Note, that at this point client_connection_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_limit","u":"/docs/server/configuration","h":"#client_connection_limit","p":3151},{"i":3190,"t":"Default: 0 client_connection_rate_limit sets the maximum number of HTTP requests to establish a new real-time connection a single Centrifugo node will accept per second (on real-time transport endpoints). All requests outside the limit will get 503 Service Unavailable code in response. Our SDKs handle this with backoff reconnection. By default, no limit is used. Note, that at this point client_connection_rate_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_rate_limit","u":"/docs/server/configuration","h":"#client_connection_rate_limit","p":3151},{"i":3192,"t":"Default: 1048576 Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.","s":"client_queue_max_size","u":"/docs/server/configuration","h":"#client_queue_max_size","p":3151},{"i":3194,"t":"Default: 0 client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently. By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).","s":"client_concurrency","u":"/docs/server/configuration","h":"#client_concurrency","p":3151},{"i":3196,"t":"Duration, default: 10s This option allows tuning the maximum time Centrifugo will wait for the connect frame (which contains authentication information) from the client after establishing connection. Default value should be reasonable for most use cases.","s":"client_stale_close_delay","u":"/docs/server/configuration","h":"#client_stale_close_delay","p":3151},{"i":3198,"t":"String, default: \"\" Available since v5.1.1 Usually to authenticate client connections with Centrifugo you need to use JWT authentication or connect proxy. Sometimes though it may be convenient to pass user ID information in incoming HTTP request headers. This is usually the case when application backend infrastructure has some authentication proxy (like Envoy, etc). This proxy may set authenticated user ID to some header and proxy requests further to Centrifugo. When client_user_id_http_header is set to some non-empty header name Centrifugo will try to extract the authenticated user ID for client connections from that header. This mechanism works for all real-time transports based on HTTP (this also includes WebSocket since it starts with HTTP Upgrade request). Example: config.json { ... \"client_user_id_http_header\": \"X-User-Id\"} When using this way for user authentication – you can not set connection expiration and additional connection info which is possible to do using other authentication ways mentioned above. Security warning When using authentication over proxy ensure your proxy strips the header you are using for auth if it comes from the client or forbids such requests to avoid malicious usage. Only your authentication proxy must set the header with user ID.","s":"client_user_id_http_header","u":"/docs/server/configuration","h":"#client_user_id_http_header","p":3151},{"i":3200,"t":"Default: false Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID). Access to channel operations should be explicitly enabled for anonymous connections.","s":"allow_anonymous_connect_without_token","u":"/docs/server/configuration","h":"#allow_anonymous_connect_without_token","p":3151},{"i":3202,"t":"Default: false When the option is set Centrifugo won't accept connections from anonymous users even if they provided a valid JWT. I.e. if token is valid, but sub claim is empty – then Centrifugo closes connection with advice to not reconnect again.","s":"disallow_anonymous_connection_tokens","u":"/docs/server/configuration","h":"#disallow_anonymous_connection_tokens","p":3151},{"i":3204,"t":"Default: 0 By default, Centrifugo runs on all available CPU cores (also Centrifugo can look at cgroup limits when rnning in Docker/Kubernetes). To limit the number of cores Centrifugo can utilize in one moment use this option.","s":"gomaxprocs","u":"/docs/server/configuration","h":"#gomaxprocs","p":3151},{"i":3206,"t":"After Centrifugo started there are several endpoints available.","s":"Endpoint configuration","u":"/docs/server/configuration","h":"#endpoint-configuration","p":3151},{"i":3208,"t":"Bidirectional WebSocket default endpoint: ws://localhost:8000/connection/websocket Bidirectional emulation with HTTP-streaming (disabled by default): ws://localhost:8000/connection/http_stream Bidirectional emulation with SSE (EventSource) (disabled by default): ws://localhost:8000/connection/sse Bidirectional SockJS default endpoint (disabled by default): http://localhost:8000/connection/sockjs Unidirectional EventSource endpoint (disabled by default): http://localhost:8000/connection/uni_sse Unidirectional HTTP streaming endpoint (disabled by default): http://localhost:8000/connection/uni_http_stream Unidirectional WebSocket endpoint (disabled by default): http://localhost:8000/connection/uni_websocket Unidirectional SSE (EventSource) endpoint (disabled by default): http://localhost:8000/connection/uni_sse Server HTTP API endpoint: http://localhost:8000/api By default, all endpoints work on port 8000. This can be changed with port option: { \"port\": 9000} In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.","s":"Default endpoints","u":"/docs/server/configuration","h":"#default-endpoints","p":3151},{"i":3210,"t":"Admin web UI endpoint works on root path by default, i.e. http://localhost:8000. For more details about admin web UI, refer to the Admin web UI documentation.","s":"Admin endpoints","u":"/docs/server/configuration","h":"#admin-endpoints","p":3151},{"i":3212,"t":"Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config: { ... \"debug\": true} And endpoint: http://localhost:8000/debug/pprof/ – will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.","s":"Debug endpoints","u":"/docs/server/configuration","h":"#debug-endpoints","p":3151},{"i":3214,"t":"Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag: centrifugo -c config.json --health","s":"Health check endpoint","u":"/docs/server/configuration","h":"#health-check-endpoint","p":3151},{"i":3216,"t":"Use swagger boolean option (by default false) to enable Swagger UI for server HTTP API. UI will be available on path /swagger. Also available over command-line flag: centrifugo -c config.json --swagger","s":"Swagger UI for server API","u":"/docs/server/configuration","h":"#swagger-ui-for-server-api","p":3151},{"i":3218,"t":"We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal: API endpoint (/api) - for HTTP API requests Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format Health check endpoint (/health) - used to do health checks Debug endpoints (/debug/pprof) - used to inspect internal server state Swagger UI endpoint (/swagger) - used for showing embedded Swagger UI for server HTTP API It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration. Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work. To run internal endpoints on custom port use internal_port option: { ... \"internal_port\": 9000} So admin web interface will work on address: http://localhost:9000 Also, debug page will be available on a new custom port too: http://localhost:9000/debug/pprof/ The same for API and Prometheus endpoints.","s":"Custom internal ports","u":"/docs/server/configuration","h":"#custom-internal-ports","p":3151},{"i":3220,"t":"To disable websocket endpoint set websocket_disable boolean option to true. To disable API endpoint set api_disable boolean option to true.","s":"Disable default endpoints","u":"/docs/server/configuration","h":"#disable-default-endpoints","p":3151},{"i":3222,"t":"It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options: admin_handler_prefix (default \"\") - to control Admin panel URL prefix websocket_handler_prefix (default \"/connection/websocket\") - to control WebSocket URL prefix http_stream_handler_prefix (default \"/connection/http_stream\") - to control HTTP-streaming URL prefix sse_handler_prefix (default \"/connection/sse\") - to control SSE/EventSource URL prefix emulation_handler_prefix (default \"/emulation\") - to control emulation endpoint prefix sockjs_handler_prefix (default \"/connection/sockjs\") - to control SockJS URL prefix uni_sse_handler_prefix (default \"/connection/uni_sse\") - to control unidirectional Eventsource URL prefix uni_http_stream_handler_prefix (default \"/connection/uni_http_stream\") - to control unidirectional HTTP streaming URL prefix uni_websocket_handler_prefix (default \"/connection/uni_websocket\") - to control unidirectional WebSocket URL prefix api_handler_prefix (default \"/api\") - to control HTTP API URL prefix prometheus_handler_prefix (default \"/metrics\") - to control Prometheus URL prefix health_handler_prefix (default \"/health\") - to control health check URL prefix","s":"Customize handler endpoints","u":"/docs/server/configuration","h":"#customize-handler-endpoints","p":3151},{"i":3224,"t":"It's possible to send HUP signal to Centrifugo to reload a configuration: kill -HUP Though at moment this will only reload token secrets and channel options (top-level and namespaces). Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.","s":"Signal handling","u":"/docs/server/configuration","h":"#signal-handling","p":3151},{"i":3227,"t":"The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. It also disables permission checks on client API level - for presence and history calls. This mode can be useful for demo projects based on Centrifugo, integration tests, local projects, or real-time application prototyping. Don't use it in production until you 100% know what you are doing.","s":"Insecure client connection","u":"/docs/server/configuration","h":"#insecure-client-connection","p":3151},{"i":3229,"t":"Available since Centrifugo v5.0.4 The boolean option client_insecure_skip_token_signature_verify (default false), if enabled – tells Centrifugo to skip JWT signature verification - for both connection and subscription tokens. This is absolutely insecure and must only be used for development and testing purposes. Token claims are parsed as usual - so token should still follow JWT format.","s":"Disable client token signature check","u":"/docs/server/configuration","h":"#disable-client-token-signature-check","p":3151},{"i":3231,"t":"This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules. The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.","s":"Insecure API mode","u":"/docs/server/configuration","h":"#insecure-api-mode","p":3151},{"i":3233,"t":"This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).","s":"Insecure admin mode","u":"/docs/server/configuration","h":"#insecure-admin-mode","p":3151},{"i":3235,"t":"Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use \"5s\". The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option). Valid time units are \"ms\" (milliseconds), \"s\" (seconds), \"m\" (minutes), \"h\" (hours). Some examples: \"1000ms\" // 1000 milliseconds\"1s\" // 1 second\"12h\" // 12 hours\"720h\" // 30 days","s":"Setting time duration options","u":"/docs/server/configuration","h":"#setting-time-duration-options","p":3151},{"i":3237,"t":"While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special: CENTRIFUGO_NAMESPACES='[{\"name\": \"ns1\"}, {\"name\": \"ns2\"}]' ./centrifugo I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.","s":"Setting namespaces over env","u":"/docs/server/configuration","h":"#setting-namespaces-over-env","p":3151},{"i":3239,"t":"Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage. Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal. To disable sending usage stats set usage_stats_disable option: config.json { \"usage_stats_disable\": true}","s":"Anonymous usage stats","u":"/docs/server/configuration","h":"#anonymous-usage-stats","p":3151},{"i":3241,"t":"On this page","s":"Infrastructure tuning","u":"/docs/server/infra_tuning","h":"","p":3240},{"i":3243,"t":"You should increase a max number of open files Centrifugo process can open if you want to handle more connections. To get the current open files limit run: ulimit -n On Linux you can check limits for a running process using: cat /proc//limits The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default. See this document to get more info on how to increase this number. If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536. You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).","s":"Open files limit","u":"/docs/server/infra_tuning","h":"#open-files-limit","p":3240},{"i":3245,"t":"Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default). In order to eliminate a problem you can: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces See a post in Pusher blog about this problem and more detailed solution steps.","s":"Ephemeral port exhaustion","u":"/docs/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":3240},{"i":3247,"t":"On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state. Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side. Look how many socket descriptors in TIME_WAIT state. netstat -an |grep TIME_WAIT | grep | wc -l Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html The advices here are similar to ephemeral port exhaustion problem: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces","s":"Sockets in TIME_WAIT state","u":"/docs/server/infra_tuning","h":"#sockets-in-time_wait-state","p":3240},{"i":3249,"t":"Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established. Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.","s":"Proxy max connections","u":"/docs/server/infra_tuning","h":"#proxy-max-connections","p":3240},{"i":3251,"t":"More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.","s":"Conntrack table","u":"/docs/server/infra_tuning","h":"#conntrack-table","p":3240},{"i":3253,"t":"You should also consider adding additional protection to your Centrifugo endpoints. Centrifugo itself provides several options (described in configuration section) regarding server protection from the malicious behavior. Though an additional layer of DDOS protection on network or infrastructure level is highly recommended. For example, you may want to limit the number of connections coming from particular IP address. Here we list some possible ways you can use to protect your Centrifugo installation: Adding Nginx limit_conn_zone configuration Using stick tables of Haproxy Configuring rate limiting rules with Cloudflare The list is not exhaustive of course.","s":"Additional server protection","u":"/docs/server/infra_tuning","h":"#additional-server-protection","p":3240},{"i":3255,"t":"On this page","s":"History and recovery","u":"/docs/server/history_and_recovery","h":"","p":3254},{"i":3257,"t":"History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero. Centrifugo is designed with an idea that history streams are ephemeral (can be created on the fly without explicit create call from Centrifugo users) and can expire or can be lost at any moment. Centrifugo provides a way for a client to understand that channel history lost. In this case, the main application database should be the source of truth and state recovery. When history is on every message published into a channel is saved into a history stream. The persistence properties of history are dictated by a Centrifugo engine used. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside Tarantool spaces. Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of state recovery anymore. tip History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.","s":"History design","u":"/docs/server/history_and_recovery","h":"#history-design","p":3254},{"i":3259,"t":"History iteration based on three fields: limit since reverse Combining these fields you can iterate over a stream in both directions. Get current stream top offset and epoch: history(limit: 0, since: null, reverse: false) Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: false) Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: true) Get history from the current beginning (up to 10): history(limit: 10, since: null, reverse: false) Get history from the current end in reversed direction (up to 10): history(limit: 10, since: null, reverse: true) Get up to 10 publications since known stream position (described by offset and epoch values): history(limit: 10, since: {offset: 0, epoch: \"epoch\"}, reverse: false) Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest): history(limit: 10, since: {offset: 11, epoch: \"epoch\"}, reverse: true) Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }}","s":"History iteration API","u":"/docs/server/history_and_recovery","h":"#history-iteration-api","p":3254},{"i":3261,"t":"One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while. In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnect at the same time). danger Message recovery protocol feature designed to be used together with reasonably small history stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time. To enable recovery mechanism for channels set the force_recovery boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. It's also possible to ask for enabling recovery from the client-side when configuring Subscription object – in this case client must have a permission to call history API. When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not. The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default. Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client SDKs (for bidirectional transport). The recovery process works this way: Let's suppose client subscribes on a channel with recovery on. Client receives subscribe reply from Centrifugo with two values: stream epoch and stream top offset, those values are saved by an SDK implementation. Every received publication has incremental offset, client SDK increments locally saved offset on each publication from a channel. Let's say at this point client disconnected for a while. Upon resubscribing to a channel SDK provides last seen epoch anf offset to Centrifugo. Centrifugo tries to load all the missed publications starting from the stream position provided by a client. If Centrifugo decides it can successfully recover client's state – then all missed publications returned in subscribe reply and client receives recovered: true in subscribed event context, and publication event handler of Subscription is called for every missed publication. Otherwise no publications returned and recovered flag of subscribed event context is set to false. epoch is useful for cases when history storage is temporary and can lose the history stream entirely. In this case comparing epoch values gives Centrifugo a tip that while publications exist and theoretically have same offsets as before - the stream is actually different, so it's impossible to use it for the recovery process. To summarize, here is a list of possible scenarios when Centrifugo can't recover client's state for a channel and provides recovered: false flag in subscribed event context: number of missed publications exceeds client_recovery_max_publication_limit option number of missed publications exceeds history_size namespace option client was away for a long time and history stream expired according to history_ttl namespace option storage used by Centrifugo engine lost the stream (restart, number of shards changed, cleared by the administrator, etc.) Having said this all, Centrifugo recovery is nice to keep the continuity of the connection and subscription. This speed-ups resubscribe in many cases and helps the backend to survive mass reconnect scenario since you avoid lots of requests for state loading. For setups with millions of connections this can be a life-saver. But we recommend applications to always have a way to load the state from the main application database. For example, on app reload. You can also manually implement your own recovery logic on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client resubscribe completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.","s":"Automatic message recovery","u":"/docs/server/history_and_recovery","h":"#automatic-message-recovery","p":3254},{"i":3263,"t":"On this page","s":"Engines and scalability","u":"/docs/server/engines","h":"","p":3262},{"i":3265,"t":"Used by default. Supports only one node. Nice choice to start with. Supports all features keeping everything in Centrifugo node process memory. You don't need to install Redis when using this engine. Advantages: Super fast since it does not involve network at all Does not require separate broker setup Disadvantages: Does not allow scaling nodes (actually you still can scale Centrifugo with Memory engine but you have to publish data into each Centrifugo node and you won't have consistent history and presence state throughout Centrifugo nodes) Does not persist message history in channels between Centrifugo restarts.","s":"Memory engine","u":"/docs/server/engines","h":"#memory-engine","p":3262},{"i":3267,"t":"history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. When using a history in a channel, Centrifugo keeps some metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help to expire metadata faster (or slower if you need it). The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Memory engine options","u":"/docs/server/engines","h":"#memory-engine-options","p":3262},{"i":3269,"t":"Redis is an open-source, in-memory data structure store, used as a database, cache, and message broker. Centrifugo Redis engine allows scaling Centrifugo nodes to different machines. Nodes will use Redis as a message broker (utilizing Redis PUB/SUB for node communication) and keep presence and history data in Redis. Minimal Redis version is 5.0.1 With Redis it's possible to come to the architecture like this:","s":"Redis engine","u":"/docs/server/engines","h":"#redis-engine","p":3262},{"i":3271,"t":"Several configuration options related to Redis engine. redis_address​ String, default \"127.0.0.1:6379\" - Redis server address. redis_password​ String, default \"\" - Redis password. redis_user​ String, default \"\" - Redis user for ACL-based auth. redis_db​ Integer, default 0 - number of Redis db to use. redis_prefix​ String, default \"centrifugo\" – custom prefix to use for channels and keys in Redis. redis_use_lists​ Boolean, default false – turns on using Redis Lists instead of Stream data structure for keeping history (not recommended, keeping this for backwards compatibility mostly). redis_force_resp2​ Boolean, default false. If set to true it forces using RESP2 protocol for communicating with Redis. By default, Redis client used by Centrifugo tries to detect supported Redis protocol automatically trying RESP3 first. history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. Similar to a Memory engine Redis engine also looks at history_meta_ttl option. Meta key in Redis is a HASH that contains the current offset number in channel and the stream epoch value. When using a history in a channel, Centrifugo saves metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help. The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Redis engine options","u":"/docs/server/engines","h":"#redis-engine-options","p":3262},{"i":3273,"t":"Some options may help you configuring TLS-protected communication between Centrifugo and Redis. redis_tls​ Boolean, default false - enable Redis TLS connection. redis_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_tls_skip_verify – but it will be removed in v5. redis_tls_cert​ String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_tls_cert_pem option. redis_tls_key​ String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_tls_key_pem option. redis_tls_root_ca​ String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_tls_root_ca_pem option. redis_tls_server_name​ String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Configuring Redis TLS","u":"/docs/server/engines","h":"#configuring-redis-tls","p":3262},{"i":3275,"t":"Let's see how to start several Centrifugo nodes using the Redis Engine. We will start 3 Centrifugo nodes and all those nodes will be connected via Redis. First, you should have Redis running. As soon as it's running - we can launch 3 Centrifugo instances. Open your terminal and start the first one: centrifugo --config=config.json --port=8000 --engine=redis --redis_address=127.0.0.1:6379 If your Redis is on the same machine and runs on its default port you can omit redis_address option in the command above. Then open another terminal and start another Centrifugo instance: centrifugo --config=config.json --port=8001 --engine=redis --redis_address=127.0.0.1:6379 Note that we use another port number (8001) as port 8000 is already busy by our first Centrifugo instance. If you are starting Centrifugo instances on different machines then you most probably can use the same port number (8000 or whatever you want) for all instances. And finally, let's start the third instance: centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379 Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes. To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation. tip In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers. Here is a live example where we locally start two Centrifugo nodes both connected to local Redis: Sorry, your browser doesn't support embedded video.","s":"Scaling with Redis tutorial","u":"/docs/server/engines","h":"#scaling-with-redis-tutorial","p":3262},{"i":3277,"t":"Centrifugo supports the official way to add high availability to Redis - Redis Sentinel. For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name: redis_sentinel_address (string, default \"\") - comma separated list of Sentinel addresses for HA. At least one known server required. redis_sentinel_master_name (string, default \"\") - name of Redis master Sentinel monitors Also: redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1 redis_sentinel_user - optional string user (used only in Redis ACL-based auth). So you can start Centrifugo which will use Sentinels to discover Redis master instances like this: centrifugo --config=config.json Where config.json: config.json { ... \"engine\": \"redis\", \"redis_sentinel_address\": \"127.0.0.1:26379\", \"redis_sentinel_master_name\": \"mymaster\"} Sentinel configuration file may look like this (for 3-node Sentinel setup with quorum 2): port 26379sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 10000sentinel failover-timeout mymaster 60000 You can find how to properly set up Sentinels in official documentation. Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.","s":"Redis Sentinel for high availability","u":"/docs/server/engines","h":"#redis-sentinel-for-high-availability","p":3262},{"i":3279,"t":"To configure TLS for Redis Sentinel use the following options. redis_sentinel_tls​ Boolean, default false - enable Redis TLS connection. redis_sentinel_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_sentinel_tls_skip_verify – but it will be removed in v5. redis_sentinel_tls_cert​ String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_sentinel_tls_cert_pem option. redis_sentinel_tls_key​ String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_sentinel_tls_key_pem option. redis_sentinel_tls_root_ca​ String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_sentinel_tls_root_ca_pem option. redis_sentinel_tls_server_name​ String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Redis Sentinel TLS","u":"/docs/server/engines","h":"#redis-sentinel-tls","p":3262},{"i":3281,"t":"Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case. For example, you can use something like this in Haproxy config: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:16379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin And then just point Centrifugo to this Haproxy: centrifugo --config=config.json --engine=redis --redis_address=\"localhost:16379\"","s":"Haproxy instead of Sentinel configuration","u":"/docs/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":3262},{"i":3283,"t":"Centrifugo has built-in Redis sharding support. This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale. At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples. To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"127.0.0.1:6379\", \"127.0.0.1:6380\", ]} To start Centrifugo with Redis instances on different hosts: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"192.168.1.34:6379\", \"192.168.1.35:6379\", ]} If you also need to customize AUTH password, Redis DB number then you can use an extended address notation. note Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers. When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).","s":"Redis sharding","u":"/docs/server/engines","h":"#redis-sharding","p":3262},{"i":3285,"t":"Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\" ]} You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start. To set the same over environment variable: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001\" CENTRIFUGO_ENGINE=redis ./centrifugo If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\", \"localhost:30101,localhost:30102,localhost:30103\" ]} Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump). To reproduce the same over environment variable use space to separate different clusters: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001,localhost:30002 localhost:30101,localhost:30102\" CENTRIFUGO_ENGINE=redis ./centrifugo","s":"Redis cluster","u":"/docs/server/engines","h":"#redis-cluster","p":3262},{"i":3287,"t":"When using Redis engine it's possible to point Centrifugo not only to Redis itself, but also to the other Redis compatible server. Such servers may work just fine if implement Redis protocol and support all the data structures Centrifugo uses and have PUB/SUB implemented. Some known options: AWS Elasticache – it was reported to work, but we suggest you testing the setup including failover tests and work under load. KeyDB – should work fine with Centrifugo, no known problems at this point regarding Centrifugo compatibility. DragonflyDB - should work fine starting from DragonflyDB 1.3.0 and with redis_force_resp2 Centrifugo option on. We have not tested a Redis Cluster emulation mode provided by DragonflyDB yet. We suggest you testing the setup including failover tests and work under load.","s":"Other Redis compatible","u":"/docs/server/engines","h":"#other-redis-compatible","p":3262},{"i":3289,"t":"EXPERIMENTAL Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics. caution EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves. There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge. info To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together. There are several supported Tarantool topologies to which Centrifugo can connect: One standalone Tarantool instance Many standalone Tarantool instances and consistently shard data between them Tarantool running in Cartridge Tarantool with replica and automatic failover in Cartridge Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7) After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes): config.json { ... \"engine\": \"tarantool\", \"tarantool_address\": \"127.0.0.1:3301\"} See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework. See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.","s":"Tarantool engine","u":"/docs/server/engines","h":"#tarantool-engine","p":3262},{"i":3291,"t":"tarantool_address​ String or array of strings. Default tcp://127.0.0.1:3301. Connection address to Tarantool. tarantool_mode​ String, default standalone A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication). All modes support client-side consistent sharding (similar to what Redis engine provides). tarantool_user​ String, default \"\". Allows setting a user. tarantool_password​ String, default \"\". Allows setting a password. history_meta_ttl​ Duration, default 2160h. Same option as for Memory engine and Redis engine also applies to Tarantool case.","s":"Tarantool engine options","u":"/docs/server/engines","h":"#tarantool-engine-options","p":3262},{"i":3293,"t":"It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below. Limitations: Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available. Nats wildcard channel subscriptions with symbols * and > not supported. First start Nats server: $ nats-server[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set][3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready Then start Centrifugo with broker option: centrifugo --broker=nats --config=config.json And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine): centrifugo --broker=nats --config=config.json --port=8001 Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.","s":"Nats broker","u":"/docs/server/engines","h":"#nats-broker","p":3262},{"i":3295,"t":"nats_url​ String, default nats://127.0.0.1:4222. Connection url in format nats://derek:pass@localhost:4222. nats_prefix​ String, default centrifugo. Prefix for channels used by Centrifugo inside Nats. nats_dial_timeout​ Duration, default 1s. Timeout for dialing with Nats. nats_write_timeout​ Duration, default 1s. Write (and flush) timeout for a connection to Nats.","s":"Options","u":"/docs/server/engines","h":"#options","p":3262},{"i":3297,"t":"On this page","s":"Metrics monitoring","u":"/docs/server/monitoring","h":"","p":3296},{"i":3299,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus","u":"/docs/server/monitoring","h":"#prometheus","p":3296},{"i":3301,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite","u":"/docs/server/monitoring","h":"#graphite","p":3296},{"i":3303,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/server/monitoring","h":"#grafana-dashboard","p":3296},{"i":3305,"t":"On this page","s":"Load balancing","u":"/docs/server/load_balancing","h":"","p":3304},{"i":3307,"t":"Although it's possible to use Centrifugo without any reverse proxy before it, it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with edge cases when handling HTTP/Websocket connections from the wild. Also you probably want some sort of load balancing eventually between Centrifugo nodes so that proxy can be such a balancer too. In this section we will look at Nginx configuration to deploy Centrifugo. Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections. There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).","s":"Nginx configuration","u":"/docs/server/load_balancing","h":"#nginx-configuration","p":3304},{"i":3309,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}#server {# listen 80;# server_name centrifugo.example.com;# rewrite ^(.*) https://$server_name$1 permanent;#}server { server_name centrifugo.example.com; listen 80; #listen 443 ssl; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_certificate /etc/nginx/ssl/server.crt; #ssl_certificate_key /etc/nginx/ssl/server.key; include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1000; gzip_proxied any; # Only retry if there was a communication error, not a timeout # on the Centrifugo server (to avoid propagating \"queries of death\" # to all frontends) proxy_next_upstream error; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; location /connection { proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location / { proxy_pass http://centrifugo; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }}","s":"Separate domain for Centrifugo","u":"/docs/server/load_balancing","h":"#separate-domain-for-centrifugo","p":3304},{"i":3311,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}server { # ... your web site Nginx config location /centrifugo/ { rewrite ^/centrifugo/(.*) /$1 break; proxy_pass http://centrifugo; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } location /centrifugo/connection { rewrite ^/centrifugo(.*) $1 break; proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}","s":"Embed to a location of web site","u":"/docs/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":3304},{"i":3313,"t":"You may also need to update worker_connections option of Nginx: events { worker_connections 65535;}","s":"worker_connections","u":"/docs/server/load_balancing","h":"#worker_connections","p":3304},{"i":3315,"t":"On this page","s":"Server observability","u":"/docs/server/observability","h":"","p":3314},{"i":3318,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus metrics","u":"/docs/server/observability","h":"#prometheus-metrics","p":3314},{"i":3320,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite metrics","u":"/docs/server/observability","h":"#graphite-metrics","p":3314},{"i":3322,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/server/observability","h":"#grafana-dashboard","p":3314},{"i":3324,"t":"Here is a description of various metrics exposed by Centrifugo. centrifugo_node_messages_sent_count​ Type: Counter Labels: type Description: Tracks the number of messages sent by a node to the broker. Usage: Use this metric to monitor the outgoing message rate and detect any anomalies or spikes in the data flow. centrifugo_node_messages_received_count​ Type: Counter Labels: type Description: Measures the number of messages received from the broker. Usage: Helps in understanding the incoming message rate and ensures the node is receiving data as expected. centrifugo_node_action_count​ Type: Counter Labels: action Description: Counts the number of various actions called within the node. Usage: Useful for tracking specific actions' usage and frequency. centrifugo_node_num_clients​ Type: Gauge Description: Shows the current number of clients connected to the node. Usage: Monitor the client connections to ensure the node is not reaching its capacity. centrifugo_node_num_users​ Type: Gauge Description: Displays the number of unique users connected to the node. Usage: Helps in understanding user engagement and capacity planning. centrifugo_node_num_subscriptions​ Type: Gauge Description: Indicates the number of active subscriptions. Usage: Use this to monitor the subscription levels and identify any potential issues or required optimizations. centrifugo_node_num_nodes​ Type: Gauge Description: Shows the total number of nodes in the cluster. Usage: Essential for monitoring the size of the cluster and ensuring that all nodes are operational. centrifugo_node_build_info​ Type: Gauge Labels: version Description: Provides build information of the node. Usage: Helps in tracking the version of the application running across different environments. centrifugo_node_num_channels​ Type: Gauge Description: Counts the number of channels with one or more subscribers. Usage: Useful for monitoring the activity and utilization of channels. centrifugo_node_survey_duration_seconds​ Type: Summary Labels: op Description: Captures the duration of surveys conducted by the node. Usage: Helps in performance monitoring and identifying any delays or issues in survey operations. centrifugo_client_num_reply_errors​ Type: Counter Labels: method, code Description: Counts the number of errors in replies sent to clients. Usage: Critical for error monitoring and ensuring smooth client interactions. centrifugo_client_num_server_disconnects​ Type: Counter Labels: code Description: Tracks the number of server-initiated disconnects. Usage: Use this to monitor the health of client connections and identify potential issues with the server. centrifugo_client_command_duration_seconds​ Type: Summary Labels: method Description: Measures the duration of commands executed by clients. Usage: Essential for performance monitoring and ensuring timely responses to client commands. centrifugo_client_recover​ Type: Counter Labels: recovered Description: Counts the number of recover operations performed. Usage: Helps in tracking the system's resilience and recovery mechanisms. centrifugo_client_connection_limit_reached_total​ Type: Counter Labels: None Description: Number of refused connections due to the node client connection limit. Usage: Useful for monitoring the load on the Centrifugo node and identifying when clients are being refused connections due to reaching the connection limit. centrifugo_transport_connect_count​ Type: Counter Labels: transport Description: Measures the number of connections to specific transports. Usage: Use this to monitor the usage of different transports and ensure they are functioning as expected. centrifugo_transport_messages_sent​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Tracks the number of messages sent to client connections over specific transports. Usage: Essential for understanding the data flow and performance of different transports. centrifugo_transport_messages_sent_size​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Measures the size of messages sent to client connections over specific transports. Usage: Helps in monitoring the network bandwidth usage and optimizing the data transfer. centrifugo_transport_messages_received​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Counts the number of messages received from client connections over specific transports. Usage: Important for ensuring that messages are being successfully received and processed. centrifugo_transport_messages_received_size​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Measures the size of messages received from client connections over specific transports. Usage: Use this metric to monitor the incoming data size and optimize the application's performance. centrifugo_proxy_duration_seconds​ Type: Summary & Histogram Labels: protocol, type Description: Captures the duration of proxy calls. Usage: Critical for understanding the performance of proxy calls and identifying any potential bottlenecks or issues. centrifugo_proxy_errors​ Type: Counter Labels: protocol, type Description: Counts the number of errors occurred during proxy calls. Usage: Helps in monitoring the reliability of proxy services and ensuring error-free operations. centrifugo_granular_proxy_duration_seconds​ Type: Summary & Histogram Labels: type, name Description: Measures the duration of granular proxy calls. Usage: Use this to get more detailed insights into the performance of granular proxy operations. centrifugo_granular_proxy_errors​ Type: Counter Labels: type, name Description: Counts the number of errors in granular proxy calls. Usage: Essential for error tracking and ensuring the stability of granular proxy services. centrifugo_api_command_duration_seconds​ Type: Summary Labels: protocol, method Description: Tracks the duration of API commands. Usage: Helps in monitoring the API performance and ensuring timely responses. centrifugo_api_command_duration_seconds_histogram​ Type: Histogram Labels: protocol, method Description: Tracks the duration of API commands. Usage: Helps in monitoring the API performance and ensuring timely responses.","s":"Exposed metrics","u":"/docs/server/observability","h":"#exposed-metrics","p":3314},{"i":3327,"t":"At this point Centrifugo can export traces for HTTP and GRPC server API requests in OpenTelemetry format. To enable: { ... \"opentelemetry\": true, \"opentelemetry_api\": true} OpenTelemetry must be explicitly turned on to avoid tracing overhead when it's not needed. To configure OpenTelemetry export behaviour we are relying on OpenTelemetry environment vars supporting only HTTP export endpoints for now. So a simple example to run Centrifugo with server API tracing would be running Jaeger with COLLECTOR_OTLP_ENABLED: docker run --rm -it --name jaeger \\ -e COLLECTOR_OTLP_ENABLED=true \\ -p 16686:16686 \\ -p 4318:4318 \\ jaegertracing/all-in-one:latest Then start Centrifugo: OTEL_EXPORTER_OTLP_ENDPOINT=\"http://localhost:4318\" CENTRIFUGO_OPENTELEMETRY=1 CENTRIFUGO_OPENTELEMETRY_API=1 ./centrifugo Send some API requests - and open http://localhost:16686 to see traces in Jaeger UI. By default, Centrifugo exports traces in http/protobuf format. If you want to use GRPC exporter then it's possible to turn it on by setting environment variable OTEL_EXPORTER_OTLP_PROTOCOL to grpc (GRPC exporter format supported since Centrifugo v5.0.3).","s":"OpenTelemetry","u":"/docs/server/observability","h":"#opentelemetry","p":3314},{"i":3329,"t":"Logging may be configured using log_level option. It may have the following values: none trace debug info (default) warn error We generally do not recommend anything below info to be used in production. By default Centrifugo logs to STDOUT. Usually this is what you need when running servers on modern infrastructures. Logging into file may be configured using log_file option.","s":"Logs","u":"/docs/server/observability","h":"#logs","p":3314},{"i":3331,"t":"On this page","s":"Online presence","u":"/docs/server/presence","h":"","p":3330},{"i":3333,"t":"To enable online presence, you need to set the presence option to true for the specific channel namespace in your Centrifugo configuration. { \"namespaces\": [{ \"namespace\": \"public\", \"presence\": true }]} After enabling this you can query presence information over server HTTP/GRPC presence call: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: YOUR_API_KEY\" \\ --request POST \\ --data '{\"channel\": \"public:test\"}' \\ http://localhost:8000/api/presence See description of presence API. Also, a shorter version of presence which only contains two counters - number of clients and number of unique users in channel - may be called: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: YOUR_API_KEY\" \\ --request POST \\ --data '{\"channel\": \"public:test\"}' \\ http://localhost:8000/api/presence_stats See description of presence stats API.","s":"Enabling online presence","u":"/docs/server/presence","h":"#enabling-online-presence","p":3330},{"i":3335,"t":"Once presence enabled, you can retrieve the presence information on the client side too by calling the presence method on the channel. To do this you need to give the client permission to call presence. Once done, presence may be retrieved from the subscription: const resp = await subscription.presence(channel); It's also available on the top-level of the client (for example, if you need to call presence for server-side subscription): const resp = await client.presence(channel); If the permission check has passed successfully – both methods will return an object containing information about currently subscribed clients. Also, presenceStats method is avalable: const resp = await subscription.presenceStats(channel);","s":"Retrieving presence on the client side","u":"/docs/server/presence","h":"#retrieving-presence-on-the-client-side","p":3330},{"i":3337,"t":"It's also possible to enable real-time tracking of users joining or leaving a channel by listening to join and leave events on the client side. By default, Centrifugo does not send these events and they must be explicitly turned on for channel namespace: { \"namespaces\": [{ \"namespace\": \"public\", \"presence\": true, \"join_leave\": true, \"force_push_join_leave\": true }]} Then on the client side: subscription.on('join', function(joinCtx) { console.log('client joined:', joinCtx);});subscription.on('leave', function(leaveCtx) { console.log('client left:', leaveCtx);}); And the same on client top-level for the needs of server-side subscriptions (analogous to the presence call described above). These events provide real-time updates and can be used to keep track of user activity and manage live interactions. You can combine join/leave events with presence information and maintain a list of currently active subscribers - for example show the list of online players in the game room updated in real-time.","s":"Join and leave events","u":"/docs/server/presence","h":"#join-and-leave-events","p":3330},{"i":3339,"t":"The online presence feature might increase the load on your Centrifugo server, since Centrifugo need to maintain an addition data structure. Therefore, it is recommended to use this feature judiciously based on your server's capability and the necessity of real-time presence data in your application. Always make sure to secure the presence data, as it could expose sensitive information about user activity in your application. Ensure appropriate security measures are in place. Join and leave events delivered with at most once guarantee. See more about presence design in design overview chapter. Also check out FAQ which mentions scalability concerns for presence data and join/leave events.","s":"Implementation notes","u":"/docs/server/presence","h":"#implementation-notes","p":3330},{"i":3341,"t":"The online presence feature of Centrifugo is a highly useful tool for real-time applications. It provides instant and live data about user activity, which can be critical for interactive features in chats, collaborative tools, multiplayer games, or live tracking systems. Make sure to configure and use this feature appropriately to get the most out of your real-time application.","s":"Conclusion","u":"/docs/server/presence","h":"#conclusion","p":3330},{"i":3343,"t":"On this page","s":"Proxy subscription streams","u":"/docs/server/proxy_streams","h":"","p":3342},{"i":3345,"t":"Using proxy subscription streams increases resource usage on both Centrifugo and app backend sides because it involves more moving parts such as goroutines, additional buffers, connections, etc. The feature is quite niche. Read carefully the motivation described in this doc. If you don't really need proxy streams – prefer using Centrifugo usual approach by always publishing messages to channels over Centrifugo publish API whenever an event happens. This is efficient and Centrifugo just drops messages in case of no active subscribers in a channel. I.e. follow our idiomatic guidelines. tip Use proxy subscription streams only when really needed. Specifically, proxy subscription stream may be very useful to stream data for a limited time upon some user action in the app. At the same time proxy subscription streams should scale well horizontally with adding more servers. But scaling GRPC is more involved and using GRPC streams results into more resources utilized than with the common Centrifugo approach, so make sure the resource consumption is sufficient for your system by performing load tests with your expected load profile. The thing is that sometimes proxy streams is the only way to achieve the desired behaviour – at that point they shine even though require more resources and developer effort. Also, not every use case involves tens of thousands of subscriptions/connections to worry about – be realistic about your practical situation.","s":"Scalability concerns","u":"/docs/server/proxy_streams","h":"#scalability-concerns","p":3342},{"i":3347,"t":"Here is a diagram which shows the sequence of events happening when using subscription streams: Subscription streams generally solve a task of integrating with third-party streaming providers or external process, possibly with custom filtering. They come into play when it's not feasible to continuously stream all data to various channels, and when you need to deallocate resources on the backend side as soon as stream is not needed anymore. Subscription streams may be also considered as streaming requests – an isolated way to stream something from the backend to the client or from the client to the backend. Let's describe a real-life use case. Say you have Loki for keeping logs, it provides a streaming API for tailing logs. You decided to stream logs towards your app's clients. When client subscribes to some channel in Centrifugo and the unidirectional stream established between Centrifugo and your backend – you can make sure client has proper permissions for the requested resource and backend then starts tailing Loki logs (or other third-party system, this may be Twitter streaming API, MQTT broker, GraphQL subscription, or streaming query to the real-time database such as RethinkDB). As soon as backend receives log events from Loki it transfers them towards client over Centrifugo. Client can provide custom data upon subscribing to a channel which makes it possible to pass query filters from the frontend app to the backend. In the example with Loki above this may be a LogQL query. In case of proxy subscription streams all the client authentication may be delegated to common Centrifugo mechanisms, so when the channel stream is established you know the ID of user (obtained by Centrifugo from JWT auth process or over connect proxy). You can additionally check channel permissions at the moment of stream establishement. As soon as client unsubscribes from the channel – Centrifugo closes the unidirectional GRPC stream – so your backend will notice that. If client disconnects – stream is closed also. If for some reason connection between Centrifugo and backend is closed – then Centrifugo will unsubscribe a client with insufficient state reason and a client will soon resubscribe to a channel (managed automatically by our SDKs). You may wonder – what about the same channel name used for subscribing to such a stream by different connections. Proxy stream is an individual link between a client and a backend – Centrifugo transfers stream data published to the GRPC stream by the backend only to the client connection to whom the stream belongs. I.e. messages sent by the backend to GRPC stream are not broadcasted to other channel subscribers. But if you will use server API for publishing – then message will be broadcasted to all channel subscribers even if they are currently using proxy stream within that channel. Presence and join/leave features will work as usual for channels with subscription proxy streams. If different connections use the same channel they will be able to use presence (if enabled) to see who else is currently in the channel, and may receive join/leave messages (if enabled). Channel history for proxy subscription streams For the case of proxy subscription streams Centrifugo channel history and recovery features do not really make sense. Proxy stream is an individual direct link between client and your backend through Centrifugo which is always re-established from scratch upon re-subscription or connection drops. The benefit of the history and its semantics are not clear in this case and can only bring undesired overhead (because Centrifugo will have to use broker, now messages just go directly towards connections without broker/engine involved at all). Only for client-side subscriptions Subscription streams work only with client-side subscriptions (i.e. when client explicitly subscribes to a channel on the application's frontend side). Server-side subscriptions won't initiate a GRPC stream to the backend. Don't forget that Centrifugo namespace system is very flexible – so you can always combine different approaches using different channel namespaces. You can always use subscription streams only for some channels belonging to a specific namespace.","s":"Motivation and design","u":"/docs/server/proxy_streams","h":"#motivation-and-design","p":3342},{"i":3349,"t":"From the configuration point of view subscription streams may be enabled for channel namespace just as additional type of proxy. The important difference is that only GRPC endpoints may be used - as we are using GRPC streaming RPCs for this functionality. You can configure subscription streams for channels very similar to how subscribe proxy is configured. First, configure subscribe stream proxy, pointing it to the backend which implements our proxy stream GRPC service contract: config.json { ... \"proxy_subscribe_stream_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_stream_timeout\": \"3s\"} Only grpc:// endpoints are supported since we are heavily relying on GRPC streaming ecosystem here. In this case proxy_subscribe_stream_timeout defines a time how long Centrifugo waits for a first message from a stream which contains subscription details to transfer to a client. Then you can enable subscription streams for channels on a namespace level: config.json { ... \"proxy_subscribe_stream_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_stream_timeout\": \"3s\", \"namespaces\": [ { \"name\": \"streams\", \"proxy_subscribe_stream\": true } ]} info You can not use subscribe, publish, sub_refresh proxy configurations together with stream proxy configuration inside one channel namespace. That's it on Centrifugo side. Now on the app backend you should implement GRPC service according to the following definitions: service CentrifugoProxy { ... // SubscribeUnidirectional allows handling unidirectional subscription streams. rpc SubscribeUnidirectional(SubscribeRequest) returns (stream StreamSubscribeResponse); ...} GRPC service definitions can be found in the Centrifugo repository: proxy.proto - same as we described before, probably you already have a service which implements some methods from it. If you don't – just follow GRPC tutorials for your programming language to generate server stubs from our Protobuf schema – and you are ready to describe stream logic. Here we are looking at unidirectional subscription stream – so the next thing to do is to implement streaming handler on the application backend side which contains stream business logic, i.e. implement SubscribeUnidirectional streaming rpc handler. tip You can write GRPC handlers to handle proxy subscription streams in any language with good GRPC support. A basic example of such handler in Go may look like this (error handling skipped for brevity): package mainimport ( \"fmt\" \"log\" \"math\" \"net\" \"strconv\" \"time\" pb \"example/proxyproto\" \"google.golang.org/grpc\")type streamServer struct { pb.UnimplementedCentrifugoProxyServer}func (s *streamerServer) SubscribeUnidirectional( req *pb.SubscribeRequest, stream pb.CentrifugoProxy_SubscribeUnidirectionalServer,) error { started := time.Now() fmt.Println(\"unidirectional subscribe called with request\", req) defer func() { fmt.Println(\"unidirectional subscribe finished, elapsed\", time.Since(started)) }() _ = stream.Send(&pb.StreamSubscribeResponse{ SubscribeResponse: &pb.SubscribeResponse{}, }) // Now publish data to a stream every 1 second. for { select { case <-stream.Context().Done(): return stream.Context().Err() case <-time.After(1000 * time.Millisecond): } pub := &pb.Publication{Data: []byte(`{\"input\": \"` + strconv.Itoa(i) + `\"}`)} _ = stream.Send(&pb.StreamSubscribeResponse{Publication: pub}) }}func main() { lis, _ := net.Listen(\"tcp\", \":12000\") s := grpc.NewServer(grpc.MaxConcurrentStreams(math.MaxUint32)) pb.RegisterCentrifugoProxyServer(s, &streamServer{}) _ = s.Serve(lis)} tip Note we have increased grpc.MaxConcurrentStreams for server to handle more simultaneous streams than allowed by default. Usually default is 100 but can differ in various GRPC server implementations. If you expect more streams then you need a bigger value. Centrifugo has some rules about messages in streams. Upon stream establishement Centrifugo expects backend to send first message from a stream - this is a StreamSubscribeResponse with SubscribeResponse in it. Centrifugo waits for this message before replying to the client's subscription command. This way we can communicate initial state with a client and make sure streaming is properly established with all permission checks passed. After sending initial message you can send events (publications) as they appear in your system. Now everything should be ready to test it out from the client side: just subscribe to a channel where stream proxy is on with our SDK – and you will see your stream handler called and data streamed from it to a client. For example, with our Javascript SDK: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { getToken: getTokenImplementation});client.connect();const sub = client.newSubscription('streams:123e4567-e89b-12d3-a456-426614174000', { data: {}}).on('publication', function(ctx) { console.log(\"received publication from a channel\", ctx.data);});sub.subscribe(); Again, while we are still looking for a proper semantics of subscription streams we recommend using unique channel names for all on-demand streams you are establishing.","s":"Unidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#unidirectional-subscription-streams","p":3342},{"i":3351,"t":"In addition to unidirectional streams, Centrifugo supports bidirectional streams upon client channel subscription. In this case client gets a possibility to stream any data to the backend utilizing bidirectional communication. Client can send messages to a bidirectional stream by using .publish(data) method of a Subscription object. In terms of general design bidirectional streams behave similar to unidirectional streams as described above. When enabling subscription streams, Centrifugo uses unidirectional GRPC streams by default – as those should fit most of the use cases proxy subscription streams were introduced for. To tell Centrifugo use bidirectional streaming add proxy_subscribe_stream_bidirectional flag to the namespace configuration: config.json { ... \"proxy_subscribe_stream_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_stream_timeout\": \"3s\", \"namespaces\": [ { \"name\": \"streams\", \"proxy_subscribe_stream\": true, \"proxy_subscribe_stream_bidirectional\": true } ]} On the backend you need to implement the following streaming handler: service CentrifugoProxy { ... // SubscribeBidirectional allows handling bidirectional subscription streams. rpc SubscribeBidirectional(stream StreamSubscribeRequest) returns (stream StreamSubscribeResponse); ...} The first StreamSubscribeRequest message in stream will contain SubscribeRequest and Centrifugo expects StreamSubscribeResponse with SubscribeResponse from the backend – just like in unidirectional case described above. An example of such handler in Go language which echoes back all publications from client (error handling skipped for brevity): func (s *streamerServer) SubscribeBidirectional( stream pb.CentrifugoProxy_SubscribeBidirectionalServer,) error { started := time.Now() fmt.Println(\"bidirectional subscribe called\") defer func() { fmt.Println(\"bidirectional subscribe finished, elapsed\", time.Since(started)) }() // First message always contains SubscribeRequest. req, _ := stream.Recv() fmt.Println(\"subscribe request received\", req.SubscribeRequest) _ = stream.Send(&pb.StreamSubscribeResponse{ SubscribeResponse: &pb.SubscribeResponse{}, }) // The following messages contain publications from client. for { req, _ = stream.Recv() data := req.Publication.Data fmt.Println(\"data from client\", string(data)) var cd clientData pub := &pb.Publication{Data: data} _ = stream.Send(&pb.StreamSubscribeResponse{Publication: pub}) }}","s":"Bidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#bidirectional-subscription-streams","p":3342},{"i":3353,"t":"Granular proxy mode works with subscription streams in the same manner as for other Centrifugo proxy types. Here is an example how you can define different subscribe stream proxies for different namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"stream_1\", \"endpoint\": \"grpc://localhost:3000\", \"timeout\": \"500ms\", }, { \"name\": \"stream_2\", \"endpoint\": \"grpc://localhost:3001\", \"timeout\": \"500ms\", } ], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_stream_proxy_name\": \"stream_1\" }, { \"name\": \"ns2\", \"subscribe_stream_proxy_name\": \"stream_2\" } ]}","s":"Granular proxy mode","u":"/docs/server/proxy_streams","h":"#granular-proxy-mode","p":3342},{"i":3355,"t":"Full example which demonstrates proxy subscribe stream backend implemented in Go language may be found in Centrifugo examples repo.","s":"Full example","u":"/docs/server/proxy_streams","h":"#full-example","p":3342},{"i":3357,"t":"On this page","s":"Server-side subscriptions","u":"/docs/server/server_subs","h":"","p":3356},{"i":3359,"t":"See subscribe and unsubscribe server API","s":"Dynamic server-side subscriptions","u":"/docs/server/server_subs","h":"#dynamic-server-side-subscriptions","p":3356},{"i":3361,"t":"It's possible to automatically subscribe a user to a personal server-side channel. To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel. For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above. As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default \"\") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.","s":"Automatic personal channel subscription","u":"/docs/server/server_subs","h":"#automatic-personal-channel-subscription","p":3356},{"i":3363,"t":"Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes. user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect). This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel. Example config: config.json { \"user_subscribe_to_personal\": true, \"user_personal_single_connection\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} note Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.","s":"Maintain single user connection","u":"/docs/server/server_subs","h":"#maintain-single-user-connection","p":3356},{"i":3365,"t":"On this page","s":"Proxy events to the backend","u":"/docs/server/proxy","h":"","p":3364},{"i":3367,"t":"HTTP proxy in Centrifugo converts client connection events into HTTP calls to the application backend.","s":"HTTP proxy","u":"/docs/server/proxy","h":"#http-proxy","p":3364},{"i":3369,"t":"All HTTP proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).","s":"HTTP request structure","u":"/docs/server/proxy","h":"#http-request-structure","p":3364},{"i":3371,"t":"The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example: config.json { ... \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} Alternatively, you can set a list of headers via an environment variable (space separated): export CENTRIFUGO_PROXY_HTTP_HEADERS=\"Cookie User-Agent X-B3-TraceId X-B3-SpanId\" ./centrifugo note Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend. Starting from Centrifugo v5.0.2 it's possible to configure static set of headers to be appended to all HTTP proxy requests: config.json { ... \"proxy_static_http_headers\": { \"X-Custom-Header\": \"custom value\" }} proxy_static_http_headers is a map with string keys and string values. You may also set it over environment variable using JSON object string: export CENTRIFUGO_PROXY_STATIC_HTTP_HEADERS='{\"X-Custom-Header\": \"custom value\"}' Static headers may be overriden by the header from client connection request if you proxy the header with the same name inside proxy_http_headers option showed above.","s":"Proxy HTTP headers","u":"/docs/server/proxy","h":"#proxy-http-headers","p":3364},{"i":3373,"t":"When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied. See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.","s":"Proxy GRPC metadata","u":"/docs/server/proxy","h":"#proxy-grpc-metadata","p":3364},{"i":3375,"t":"With the following options in the configuration file: { ... \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_connect_timeout\": \"1s\"} – connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request. danger Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections. Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. In many cases your existing session mechanism will provide user authentication details to the connect proxy handler. tip If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server. This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired). Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\"} Expected response example: {\"result\": {\"user\": \"56\"}} This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result. Several app examples which use connect proxy can be found in our blog: With NodeJS With Django With Laravel Connect request fields​ This is what sent from Centrifugo to application backend in case of connect proxy request. Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) name string yes optional name of the client (this field will only be set if provided by a client on connect) version string yes optional version of the client (this field will only be set if provided by a client on connect) data JSON yes optional data from client (this field will only be set if provided by a client on connect) b64data string yes optional data from the client in base64 format (if the binary proxy mode is used) channels Array of strings yes list of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result Connect result fields​ This is what application returns to Centrifugo inside result field in case of connect proxy request. Field Type Optional Description user string no user ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests expire_at integer yes a timestamp (Unix seconds in the future) when connection must be considered expired. If not set or set to 0 connection won't expire at all info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages data JSON yes a custom data to send to the client in connect command response. b64data string yes a custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client channels array of strings yes allows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions subs map of SubscribeOptions yes map of channels with options to subscribe connection to. See more details about server-side subscriptions meta JSON object (ex. {\"key\": \"value\"}) yes a custom data to attach to connection (this won't be exposed to client-side) Options​ proxy_connect_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. Example​ Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return \"56\" as user ID): class CentrifugoConnectHandler(tornado.web.RequestHandler): def check_xsrf_cookie(self): pass def post(self): self.set_header('Content-Type', 'application/json; charset=\"utf-8\"') data = json.dumps({ 'result': { 'user': '56' } }) self.write(data)def main(): options.parse_command_line() app = tornado.web.Application([ (r'/centrifugo/connect', CentrifugoConnectHandler), ]) app.listen(3000) tornado.ioloop.IOLoop.instance().start()if __name__ == '__main__': main() This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side. We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea. What if connection is unauthenticated/unauthorized to connect?​ In this case return a disconnect object as a response. See Return custom disconnect section. Depending on whether you want connection to reconnect or not (usually not) you can select the appropriate disconnect code. Sth like this in response: { \"disconnect\": { \"code\": 4501, \"reason\": \"unauthorized\" }} – may be sufficient enough. Choosing codes and reason is up to the developer, but follow the rules described in Return custom disconnect section.","s":"Connect proxy","u":"/docs/server/proxy","h":"#connect-proxy","p":3364},{"i":3377,"t":"With the following options in the configuration file: { ... \"proxy_refresh_endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"proxy_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side. The payload sent to app backend in refresh request (when the connection is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Refresh proxy","u":"/docs/server/proxy","h":"#refresh-proxy","p":3364},{"i":3379,"t":"With the following option in the configuration file: { ... \"proxy_rpc_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_rpc_timeout\": \"1s\"} RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket connection (or any other bidirectional transport Centrifugo supports) in a bidirectional way. Payload example sent to app backend in RPC request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"method\": \"getCurrentPrice\", \"data\":{\"params\": {\"object_id\": 12}}} Expected response example: {\"result\": {\"data\": {\"answer\": \"2019\"}}} RPC request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process method string yes an RPC method string, if the client does not use named RPC call then method will be omitted data JSON yes RPC custom data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) RPC result fields​ Field Type Optional Description data JSON yes RPC response - any valid JSON is supported b64data string yes can be set instead of data for binary response encoded in base64 format Options​ proxy_rpc_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. See below on how to return a custom error.","s":"RPC proxy","u":"/docs/server/proxy","h":"#rpc-proxy","p":3364},{"i":3381,"t":"With the following option in the configuration file: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\"} – subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel. info Subscribe proxy does not proxy subscriptions with token and subscriptions to user-limited channels at the moment. That's because those are already providing channel access control. Subscribe proxy assumes that all the permission management happens on the backend side when processing proxy request. So if you need to get subscribe proxy requests for all channels in the system - do not use subscription tokens and user-limited channels. Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace. So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} Or for channels in namespace sun: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_subscribe\": true }]} Payload example sent to app backend in subscribe request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\"} Expected response example if subscription is allowed: {\"result\": {}} Subscribe request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to subscribe to meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) data JSON yes custom data from client sent with subscription request (this field will only be set if provided by a client on subscribe). b64data string yes optional subscription data from the client in base64 format (if the binary proxy mode is used). Subscribe result fields​ Field Type Optional Description info JSON yes a channel info JSON b64info string yes a binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using data JSON yes a custom data to send to the client in subscribe command reply. b64data string yes a custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) expire_at integer yes a timestamp (Unix seconds in the future) when subscription must be considered expired. If not set or set to 0 subscription won't expire at all. Supported since Centrifugo v5.0.4 Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} See below on how to return an error in case you don't want to allow subscribing. Options​ proxy_subscribe_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. What if connection is not allowed to subscribe?​ In this case you can return error object as a subscribe handler response. See return custom error section. In general, frontend applications should not try to subscribe to channels for which access is not allowed. But these situations can happen or malicious user can try to subscribe to a channel. In most scenarios returning: { \"error\": { \"code\": 403, \"message\": \"permission denied\" }} – is sufficient enough. Error code may be not 403 actually, no real reason to force HTTP semantics here - so it's up to Centrifugo user to decide. Just keep it in range [400, 1999] as described here. If case of returning response above, on client side unsubscribed event of Subscription object will be called with error code 403. Subscription won't resubscribe automatically after that.","s":"Subscribe proxy","u":"/docs/server/proxy","h":"#subscribe-proxy","p":3364},{"i":3383,"t":"With the following option in the configuration file: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\"} – publish calls sent by a client will be proxied to proxy_publish_endpoint. This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side. Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too. So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"publish\": true, \"proxy_publish\": true} Or for channels in namespace sun: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"publish\": true, \"proxy_publish\": true }]} Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace). Payload example sent to app backend in a publish request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\", \"data\":{\"input\":\"hello\"}} Expected response example if publish is allowed: {\"result\": {}} Publish request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to publish to data JSON yes data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Publish result fields​ Field Type Optional Description data JSON yes an optional JSON data to send into a channel instead of original data sent by a client b64data string yes a binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing skip_history bool yes when set to true Centrifugo won't save publication to the channel history See below on how to return an error in case you don't want to allow publishing. Options​ proxy_publish_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Publish proxy","u":"/docs/server/proxy","h":"#publish-proxy","p":3364},{"i":3385,"t":"With the following options in the configuration file: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_sub_refresh_endpoint when it's time to refresh the subscription. Centrifugo itself will ask your backend about subscription validity instead of subscription refresh workflow on the client-side. Like subscribe and publish proxy types, sub refresh proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_sub_refresh that enables sub refresh proxy for channels in the namespace. Only subscriptions which have expiration time will be validated over sub refresh proxy endpoint. Sub refresh proxy may be used as a periodical Subscription liveness callback from Centrifugo to app backend. caution In the current implementation the delay of Subscription refresh requests from Centrifugo to application backend may be up to one minute (was implemented this way from a simplicity and efficiency perspective). We assume this should be enough for many scenarios. But this may be improved if needed. Please reach us out with a detailed description of your use case where you want more accurate requests to refresh subscriptions. So to enable sub refresh proxy for channels without namespace define proxy_sub_refresh on a top configuration level: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh\": true} Or for channels in namespace sun: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_sub_refresh\": true }]} The payload sent to app backend in sub refresh request (when the subscription is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"channel\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Sub refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no channel for which Subscription is going to expire meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Sub refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the subscription as expired - the client will be disconnected expire_at integer yes a timestamp in the future (Unix seconds) when subscription must be considered expired info JSON yes a channel info JSON b64info string yes binary channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_sub_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Sub refresh proxy","u":"/docs/server/proxy","h":"#sub-refresh-proxy","p":3364},{"i":3387,"t":"Application backend can return JSON object that contains an error to return it to the client: { \"error\": { \"code\": 1000, \"message\": \"custom error\" }} Applications must use error codes in range [400, 1999]. Error code field is uint32 internally. note Returning custom error does not apply to response for refresh and sub refresh proxy requests as there is no sense in returning an error (will not reach client anyway). I.e. custom error is only processed for connect, subscribe, publish and rpc proxy types.","s":"Return custom error","u":"/docs/server/proxy","h":"#return-custom-error","p":3364},{"i":3389,"t":"Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way: { \"disconnect\": { \"code\": 4500, \"reason\": \"disconnect reason\" }} Application must use numbers in the range 4000-4999 for custom disconnect codes: codes in range [4000, 4499] give client an advice to reconnect codes in range [4500, 4999] are terminal codes – client won't reconnect upon receiving it. Code is uint32 internally. Numbers outside of 4000-4999 range are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 ASCII symbols (i.e. 32 bytes max). note Returning custom disconnect does not apply to response for refresh and sub refresh proxy requests as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason. I.e. custom disconnect is only processed for connect, subscribe, publish and rpc proxy types.","s":"Return custom disconnect","u":"/docs/server/proxy","h":"#return-custom-disconnect","p":3364},{"i":3391,"t":"Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server. GRPC service definitions can be found in the Centrifugo repository: proxy.proto. tip GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions. Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept). All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy: config.json { ... \"proxy_connect_endpoint\": \"grpc://localhost:12000\", \"proxy_connect_timeout\": \"1s\"} Refresh proxy: config.json { ... \"proxy_refresh_endpoint\": \"grpc://localhost:12000\", \"proxy_refresh_timeout\": \"1s\"} Or for RPC proxy: config.json { ... \"proxy_rpc_endpoint\": \"grpc://localhost:12000\", \"proxy_rpc_timeout\": \"1s\"} For publish proxy in namespace chat: config.json { ... \"proxy_publish_endpoint\": \"grpc://localhost:12000\", \"proxy_publish_timeout\": \"1s\" \"namespaces\": [ { \"name\": \"chat\", \"publish\": true, \"proxy_publish\": true } ]} Use subscribe proxy for all channels without namespaces: config.json { ... \"proxy_subscribe_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} So the same as for HTTP, just the different endpoint scheme.","s":"GRPC proxy","u":"/docs/server/proxy","h":"#grpc-proxy","p":3364},{"i":3393,"t":"Some additional options exist to control GRPC proxy behavior. proxy_grpc_cert_file​ String, default: \"\". Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. proxy_grpc_credentials_key​ String, default \"\" (i.e. not used). Add custom key to per-RPC credentials. proxy_grpc_credentials_value​ String, default \"\" (i.e. not used). A custom value for proxy_grpc_credentials_key. proxy_grpc_compression​ Bool, default false (i.e. not used). If true then gzip compression will be used for each GRPC proxy call (available since Centrifugo v5.1.0).","s":"GRPC proxy options","u":"/docs/server/proxy","h":"#grpc-proxy-options","p":3364},{"i":3395,"t":"We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.: Copy proxy Protobuf definitions Generate GRPC code Run backend service with you custom business logic Point Centrifugo to it.","s":"GRPC proxy example","u":"/docs/server/proxy","h":"#grpc-proxy-example","p":3364},{"i":3397,"t":"Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios: Client protocol type Proxy type Client headers Client metadata HTTP HTTP In proxy request headers N/A GRPC GRPC N/A In proxy request metadata HTTP GRPC In proxy request metadata N/A GRPC HTTP N/A In proxy request headers","s":"Header proxy rules","u":"/docs/server/proxy","h":"#header-proxy-rules","p":3364},{"i":3399,"t":"As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding. Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this. Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side). But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON. In this case it's possible to turn Centrifugo proxy into binary mode by using: config.json { ... \"proxy_binary_encoding\": true} Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues. While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine. Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.","s":"Binary mode","u":"/docs/server/proxy","h":"#binary-mode","p":3364},{"i":3401,"t":"By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture). Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.","s":"Granular proxy mode","u":"/docs/server/proxy","h":"#granular-proxy-mode","p":3364},{"i":3403,"t":"Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below. config.json { ... \"granular_proxy_mode\": true}","s":"Enable granular proxy mode","u":"/docs/server/proxy","h":"#enable-granular-proxy-mode","p":3364},{"i":3405,"t":"When using granular proxy mode on configuration top level you can define \"proxies\" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint. Here is an example: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"connect\", \"endpoint\": \"http://localhost:3000/centrifugo/connect\", \"timeout\": \"500ms\", \"http_headers\": [\"Cookie\"] }, { \"name\": \"refresh\", \"endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"timeout\": \"500ms\" }, { \"name\": \"subscribe1\", \"endpoint\": \"http://localhost:3001/centrifugo/subscribe\" }, { \"name\": \"publish1\", \"endpoint\": \"http://localhost:3001/centrifugo/publish\" }, { \"name\": \"rpc1\", \"endpoint\": \"http://localhost:3001/centrifugo/rpc\" }, { \"name\": \"subscribe2\", \"endpoint\": \"http://localhost:3002/centrifugo/subscribe\" }, { \"name\": \"publish2\", \"endpoint\": \"grpc://localhost:3002\" } { \"name\": \"rpc2\", \"endpoint\": \"grpc://localhost:3002\" } ]} Let's look at all fields for a proxy object which is possible to set for each proxy inside \"proxies\" array. Field name Field type Required Description name string yes Unique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$ endpoint string yes HTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC. timeout duration (string) no Proxy request timeout, default \"1s\" http_headers array of strings no List of headers to proxy, by default no headers static_http_headers map[string]string no Static set of headers to add to HTTP proxy requests. Note these headers only appended to HTTP proxy requests from Centrifugo to backend. Available since Centrifugo v5.0.2 grpc_metadata array of strings no List of GRPC metadata keys to proxy, by default no metadata keys binary_encoding bool no Use base64 for payloads include_connection_meta bool no Include meta information (attached on connect) grpc_cert_file string no Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. grpc_credentials_key string no Add custom key to per-RPC credentials. grpc_credentials_value string no A custom value for grpc_credentials_key. grpc_compression bool no If true then gzip compression will be used for each GRPC proxy call (available since Centrifugo v5.1.0)","s":"Defining a list of proxies","u":"/docs/server/proxy","h":"#defining-a-list-of-proxies","p":3364},{"i":3407,"t":"As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event. To enable connect proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\"} We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea. Let's also add refresh proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\", \"refresh_proxy_name\": \"refresh\"}","s":"Granular connect and refresh","u":"/docs/server/proxy","h":"#granular-connect-and-refresh","p":3364},{"i":3409,"t":"Subscribe, publish and sub refresh proxies work per-namespace. This means that subscribe_proxy_name, publish_proxy_name and sub_refresh_proxy_name are just channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object. config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_proxy_name\": \"subscribe1\", \"publish\": true, \"publish_proxy_name\": \"publish1\" }, { \"name\": \"ns2\", \"subscribe_proxy_name\": \"subscribe2\", \"publish\": true, \"publish_proxy_name\": \"publish2\" } ]} If namespace does not have \"subscribe_proxy_name\" or \"subscribe_proxy_name\" is empty then no subscribe proxy will be used for a namespace. If namespace does not have \"publish_proxy_name\" or \"publish_proxy_name\" is empty then no publish proxy will be used for a namespace. If namespace does not have \"sub_refresh_proxy_name\" or \"sub_refresh_proxy_name\" is empty then no sub refresh proxy will be used for a namespace. tip You can define subscribe_proxy_name, publish_proxy_name, sub_refresh_proxy_name on configuration top level – and in this case publish, subscribe and sub refresh requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.","s":"Granular subscribe, publish, sub refresh","u":"/docs/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":3364},{"i":3411,"t":"Analogous to channel namespaces it's possible to configure rpc namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [...], \"rpc_namespaces\": [ { \"name\": \"rpc_ns1\", \"rpc_proxy_name\": \"rpc1\", }, { \"name\": \"rpc_ns2\", \"rpc_proxy_name\": \"rpc2\" } ]} The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces). Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start. tip The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.","s":"Granular RPC","u":"/docs/server/proxy","h":"#granular-rpc","p":3364},{"i":3413,"t":"On this page","s":"Configure TLS","u":"/docs/server/tls","h":"","p":3412},{"i":3415,"t":"In first way you already have cert and key files. For development you can create self-signed certificate - see this instruction as example. config.json { ... \"tls\": true, \"tls_key\": \"server.key\", \"tls_cert\": \"server.crt\"} And run: ./centrifugo --config=config.json","s":"Using crt and key files","u":"/docs/server/tls","h":"#using-crt-and-key-files","p":3412},{"i":3417,"t":"For automatic certificates from Let's Encrypt add into configuration file: config.json { ... \"tls_autocert\": true, \"tls_autocert_host_whitelist\": \"www.example.com\", \"tls_autocert_cache_dir\": \"/tmp/certs\", \"tls_autocert_email\": \"user@example.com\", \"tls_autocert_http\": true, \"tls_autocert_http_addr\": \":80\"} tls_autocert (boolean) says Centrifugo that you want automatic certificate handling using ACME provider. tls_autocert_host_whitelist (string) is a string with your app domain address. This can be comma-separated list. It's optional but recommended for extra security. tls_autocert_cache_dir (string) is a path to a folder to cache issued certificate files. This is optional but will increase performance. tls_autocert_email (string) is optional - it's an email address ACME provider will send notifications about problems with your certificates. tls_autocert_http (boolean) is an option to handle http_01 ACME challenge on non-TLS port. tls_autocert_http_addr (string) can be used to set address for handling http_01 ACME challenge (default is :80) When configured correctly and your domain is valid (localhost will not work) - certificates will be retrieved on first request to Centrifugo. Also Let's Encrypt certificates will be automatically renewed. There are two options that allow Centrifugo to support TLS client connections from older browsers such as Chrome 49 on Windows XP and IE8 on XP: tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys. tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints. uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.","s":"Automatic certificates","u":"/docs/server/tls","h":"#automatic-certificates","p":3412},{"i":3419,"t":"You can provide custom certificate files to configure TLS for GRPC API server. grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server","s":"TLS for GRPC API","u":"/docs/server/tls","h":"#tls-for-grpc-api","p":3412},{"i":3421,"t":"You can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint. uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server","s":"TLS for GRPC unidirectional stream","u":"/docs/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":3412},{"i":3423,"t":"On this page","s":"Server API walkthrough","u":"/docs/server/server_api","h":"","p":3422},{"i":3425,"t":"Server HTTP API works on /api path prefix (by default). The request format is super-simple: this is an HTTP POST request to a specific method API path with application/json Content-Type, X-API-Key header and with JSON body. Instead of many words, here is an example how to call publish method: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"test\", \"data\": {\"value\": \"test_value\"}}' \\ http://localhost:8000/api/publish tip You can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests structures. Below we look at all aspects of HTTP API in detail, starting with information about authorization.","s":"HTTP API","u":"/docs/server/server_api","h":"#http-api","p":3422},{"i":3427,"t":"HTTP API is protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like: config.json { ... \"api_key\": \"\"} This API key must be set in the request X-API-Key header in this way: X-API-Key: It's also possible to pass API key over URL query param. Simply add ?api_key= query param to the API endpoint. Keep in mind that passing the API key in the X-API-Key header is a recommended way as it is considered more secure. To disable API key check on Centrifugo side you can use api_insecure configuration option. Use it in development only or make sure to protect the API endpoint by proxy or firewall rules in production – to prevent anyone with access to the endpoint to send commands over your unprotected Centrifugo API. API key auth is not very safe for man-in-the-middle so we also recommended protecting Centrifugo with TLS.","s":"HTTP API authorization","u":"/docs/server/server_api","h":"#http-api-authorization","p":3422},{"i":3429,"t":"Server API supports many methods. Let's describe them starting with the most important publish operation.","s":"API methods","u":"/docs/server/server_api","h":"#api-methods","p":3422},{"i":3431,"t":"Publish method allows publishing data into a channel (we call this message publication in Centrifugo). Most probably this is a command you'll use most of the time. Here is an example of publishing message to Centrifugo: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\", \"data\": {\"text\": \"hello\"}}' \\ http://localhost:8000/api/publish In case of successful publish you will get a response like this: { \"result\": {}} As an additional example, let's take a look how to publish to Centrifugo with requests library for Python: import jsonimport requestsapi_key = \"YOUR_API_KEY\"data = json.dumps({ \"channel\": \"docs\", \"data\": { \"content\": \"1\" }})headers = {'Content-type': 'application/json', 'X-API-Key': api_key}resp = requests.post(\"https://centrifuge.example.com/api/publish\", data=data, headers=headers)print(resp.json()) In case of publication error, response object will contain error field. For example, let's publish to an unknown namespace (not defined in Centrifugo configuration): curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"unknown:chat\", \"data\": {\"text\": \"hello\"}}' \\ http://localhost:8000/api/publish In response you will also get 200 OK, but payload will contain error field instead of result: { \"error\": { \"code\": 102, \"message\": \"namespace not found\" }} error object contains error code and message - this is also the same for other commands described below. Publish request​ Field name Field type Required Description channel string yes Name of channel to publish data any JSON yes Custom JSON data to publish into a channel skip_history bool no Skip adding publication to history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Publish result​ Field name Field type Optional Description offset integer yes Offset of publication in history stream epoch string yes Epoch of current stream","s":"publish","u":"/docs/server/server_api","h":"#publish","p":3422},{"i":3433,"t":"broadcast is similar to publish but allows to efficiently send the same data into many channels: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channels\": [\"user:1\", \"user:2\"], \"data\": {\"text\": \"hello\"}}' \\ http://localhost:8000/api/broadcast Broadcast request​ Field name Field type Required Description channels Array of strings yes List of channels to publish data to data any JSON yes Custom JSON data to publish into each channel skip_history bool no Skip adding publications to channels' history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Broadcast result​ Field name Field type Optional Description responses Array of publish responses no Responses for each individual publish (with possible error and publish result)","s":"broadcast","u":"/docs/server/server_api","h":"#broadcast","p":3422},{"i":3435,"t":"subscribe allows subscribing active user's sessions to a channel. Note, it's mostly for dynamic server-side subscriptions. Subscribe request​ Field name Field type Required Description user string yes User ID to subscribe channel string yes Name of channel to subscribe user to info any JSON no Attach custom data to subscription (will be used in presence and join/leave messages) b64info string no info in base64 for binary mode (will be decoded by Centrifugo) client string no Specific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs) session string no Specific client session to subscribe (user still required to be set) data any JSON no Custom subscription data (will be sent to client in Subscribe push) b64data string no Same as data but in base64 format (will be decoded by Centrifugo) recover_since StreamPosition object no Stream position to recover from override Override object no Allows dynamically override some channel options defined in Centrifugo configuration (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} Subscribe result​ Empty object at the moment.","s":"subscribe","u":"/docs/server/server_api","h":"#subscribe","p":3422},{"i":3437,"t":"unsubscribe allows unsubscribing user from a channel. Unsubscribe request​ Field name Field type Required Description user string yes User ID to unsubscribe channel string yes Name of channel to unsubscribe user to client string no Specific client ID to unsubscribe (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Unsubscribe result​ Empty object at the moment.","s":"unsubscribe","u":"/docs/server/server_api","h":"#unsubscribe","p":3422},{"i":3439,"t":"disconnect allows disconnecting a user by ID. Disconnect request​ Field name Field type Required Description user string yes User ID to disconnect client string no Specific client ID to disconnect (user still required to be set) session string no Specific client session to disconnect (user still required to be set). whitelist Array of strings no Array of client IDs to keep disconnect Disconnect object no Provide custom disconnect object, see below Disconnect object​ Field name Field type Required Description code int yes Disconnect code reason string yes Disconnect reason Disconnect result​ Empty object at the moment.","s":"disconnect","u":"/docs/server/server_api","h":"#disconnect","p":3422},{"i":3441,"t":"refresh allows refreshing user connection (mostly useful when unidirectional transports are used). Refresh request​ Field name Field type Required Description user string yes User ID to refresh client string no Client ID to refresh (user still required to be set) session string no Specific client session to refresh (user still required to be set). expired bool no Mark connection as expired and close with Disconnect Expired reason expire_at int no Unix time (in seconds) in the future when the connection will expire Refresh result​ Empty object at the moment.","s":"refresh","u":"/docs/server/server_api","h":"#refresh","p":3422},{"i":3443,"t":"presence allows getting channel online presence information (all clients currently subscribed on this channel). tip Presence in channels is not enabled by default. See how to enable it over channel options. Also check out dedicated chapter about it. curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\"}' \\ http://localhost:8000/api/presence Example response: { \"result\": { \"presence\": { \"c54313b2-0442-499a-a70c-051f8588020f\": { \"client\": \"c54313b2-0442-499a-a70c-051f8588020f\", \"user\": \"42\" }, \"adad13b1-0442-499a-a70c-051f858802da\": { \"client\": \"adad13b1-0442-499a-a70c-051f858802da\", \"user\": \"42\" } } }} Presence request​ Field name Field type Required Description channel string yes Name of channel to call presence from Presence result​ Field name Field type Optional Description presence Map of client ID (string) to ClientInfo object no Offset of publication in history stream ClientInfo​ Field name Field type Optional Description client string no Client ID user string no User ID conn_info JSON yes Optional connection info chan_info JSON yes Optional channel info","s":"presence","u":"/docs/server/server_api","h":"#presence","p":3422},{"i":3445,"t":"presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID). curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\"}' \\ http://localhost:8000/api/presence_stats Example response: { \"result\": { \"num_clients\": 0, \"num_users\": 0 }} Presence stats request​ Field name Field type Required Description channel string yes Name of channel to call presence from Presence stats result​ Field name Field type Optional Description num_clients integer no Total number of clients in channel num_users integer no Total number of unique users in channel","s":"presence_stats","u":"/docs/server/server_api","h":"#presence_stats","p":3422},{"i":3447,"t":"history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter. tip History in channels is not enabled by default. See how to enable it over channel options. curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\", \"limit\": 2}' \\ http://localhost:8000/api/history Example response: { \"result\": { \"epoch\": \"qFhv\", \"offset\": 4, \"publications\": [ { \"data\": { \"text\": \"hello\" }, \"offset\": 2 }, { \"data\": { \"text\": \"hello\" }, \"offset\": 3 } ] }} History request​ Field name Field type Required Description channel string yes Name of channel to call history from limit int no Limit number of returned publications, if not set in request then only current stream position information will present in result (without any publications) since StreamPosition object no To return publications after this position reverse bool no Iterate in reversed order (from latest to earliest) StreamPosition​ Field name Field type Required Description offset integer yes Offset in a stream epoch string yes Stream epoch History result​ Field name Field type Optional Description publications Array of publication objects yes List of publications in channel offset integer yes Top offset in history stream epoch string yes Epoch of current stream","s":"history","u":"/docs/server/server_api","h":"#history","p":3422},{"i":3449,"t":"history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state. History remove request​ Field name Field type Required Description channel string yes Name of channel to remove history History remove result​ Empty object at the moment.","s":"history_remove","u":"/docs/server/server_api","h":"#history_remove","p":3422},{"i":3451,"t":"channels return active channels (with one or more active subscribers in it). curl --header \"X-API-Key: \" \\ --request POST \\ --data '{}' \\ http://localhost:8000/api/channels Channels request​ Field name Field type Required Description pattern string no Pattern to filter channels, we are using gobwas/glob library for matching Channels result​ Field name Field type Optional Description channels Map of string to ChannelInfo no Map where key is channel and value is ChannelInfo (see below) ChannelInfo​ Field name Field type Optional Description num_clients integer no Total number of connections currently subscribed to a channel caution Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels API call will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.","s":"channels","u":"/docs/server/server_api","h":"#channels","p":3422},{"i":3453,"t":"info method allows getting information about running Centrifugo nodes. Example response: { \"result\": { \"nodes\": [ { \"name\": \"Alexanders-MacBook-Pro.local_8000\", \"num_channels\": 0, \"num_clients\": 0, \"num_users\": 0, \"uid\": \"f844a2ed-5edf-4815-b83c-271974003db9\", \"uptime\": 0, \"version\": \"\" } ] }} Info request​ Empty object at the moment. Info result​ Field name Field type Optional Description nodes Array of Node objects no Information about all nodes in a cluster","s":"info","u":"/docs/server/server_api","h":"#info","p":3422},{"i":3455,"t":"Batch allows sending many commands in one request. Commands processed sequentially by Centrifugo, users should check individual error in each returned reply. Useful to avoid RTT latency penalty for each command sent, this is an analogue of pipelining. Example with two publications in one request: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"commands\": [{\"publish\": {\"channel\": \"test1\", \"data\": {}}}, {\"publish\": {\"channel\": \"x:test2\", \"data\": {}}}]}' \\ http://localhost:8000/api/batch Example response: { \"replies\":[ {\"publish\":{}}, {\"error\":{\"code\":102,\"message\":\"unknown channel\"}} ]}","s":"batch","u":"/docs/server/server_api","h":"#batch","p":3422},{"i":3457,"t":"Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header. But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually: cent for Python phpcent for PHP gocent for Go rubycent for Ruby Also, there are API libraries created by community: crystalcent API client for Crystal language cent.js API client for NodeJS Centrifugo.AspNetCore API client for ASP.NET Core tip Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.","s":"HTTP API libraries","u":"/docs/server/server_api","h":"#http-api-libraries","p":3422},{"i":3459,"t":"Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC. GRPC API is also useful if you want to publish binary data to Centrifugo channels. tip GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description. You can enable GRPC API in Centrifugo using grpc_api option: config.json { ... \"grpc_api\": true} By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option. Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file. Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.","s":"GRPC API","u":"/docs/server/server_api","h":"#grpc-api","p":3422},{"i":3461,"t":"For example for Python you need to run sth like this according to GRPC docs: pip install grpcio-toolspython -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo: import grpcimport api_pb2_grpc as api_grpcimport api_pb2 as api_pbchannel = grpc.insecure_channel('localhost:10000')stub = api_grpc.CentrifugoApiStub(channel)try: resp = stub.Info(api_pb.InfoRequest())except grpc.RpcError as err: # GRPC level error. print(err.code(), err.details())else: if resp.error.code: # Centrifugo server level error. print(resp.error.code, resp.error.message) else: print(resp.result) Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.","s":"GRPC example for Python","u":"/docs/server/server_api","h":"#grpc-example-for-python","p":3422},{"i":3463,"t":"Here is a simple example of how to run Centrifugo with the GRPC Go client. You need protoc, protoc-gen-go and protoc-gen-go-grpc installed. First start Centrifugo itself with GRPC API enabled: CENTRIFUGO_GRPC_API=1 centrifugo --config config.json In another terminal tab: mkdir centrifugo_grpc_examplecd centrifugo_grpc_example/touch main.gogo mod init centrifugo_examplemkdir apiprotocd apiprotowget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto Run protoc to generate code: protoc -I ./ api.proto --go_out=. --go-grpc_out=. Put the following code to main.go file (created on the last step above): package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure()) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoApiClient(conn) for { resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} Then run: go run main.go The program starts and periodically publishes the same payload into chat:index channel.","s":"GRPC example for Go","u":"/docs/server/server_api","h":"#grpc-example-for-go","p":3422},{"i":3465,"t":"You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey . For example in Go language: package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")type keyAuth struct { key string}func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{ \"authorization\": \"apikey \" + t.key, }, nil}func (t keyAuth) RequireTransportSecurity() bool { return false}func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{\"xxx\"})) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoClient(conn) for { resp, err := client.Publish(context.Background(), &PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} For other languages refer to GRPC docs.","s":"GRPC API key authorization","u":"/docs/server/server_api","h":"#grpc-api-key-authorization","p":3422},{"i":3467,"t":"By default, Centrifugo server API never returns transport level errors - for example it always returns 200 OK for HTTP API and never returns GRPC transport-level errors. Centrifugo returns its custom errors from API calls inside optional error field of response as we showed above in this doc. This means that API call to Centrifigo API may returns 200 OK, but in the error field you may find Centrifugo-specific 100: internal error. Since Centrifugo v5.1.0 Centrifigo has an option to use transport-native error codes instead of Centrifugo error field in the response. The main motivation is make API calls friendly to integrate with the network ecosystem - for automatic retries, better logging, etc. In many situations this may be more obvious for humans also. Let's show an example. Without any special options HTTP request to Centrifigo server API which contains error in response looks like this: ❯ echo '{}' | http POST \"http://localhost:8000/api/publish\"HTTP/1.1 200 OKContent-Length: 46Content-Type: application/jsonDate: Sat, 19 Aug 2023 07:23:40 GMT{ \"error\": { \"code\": 107, \"message\": \"bad request\" }} Note - it returns 200 OK even though response contains error field. With transport error mode request-response may be transformed into the following: ❯ echo '{}' | http POST \"http://localhost:8000/api/publish\" \"X-Centrifugo-Error-Mode: transport\"HTTP/1.1 400 Bad RequestContent-Length: 36Content-Type: application/jsonDate: Sat, 19 Aug 2023 07:23:59 GMT{ \"code\": 107, \"message\": \"bad request\"} Transport error mode may be turned on globally: using \"api_error_mode\": \"transport\" option for HTTP server API using \"grpc_api_error_mode\": \"transport\" option for GRPC server API Also, this mode may be used on per-request basis: by setting custom header X-Centrifugo-Error-Mode: transport for HTTP (as we just showed in the example) adding custom metadata key x-centrifugo-error-mode: transport for GRPC caution Note, that transport error mode does not help a lot with Batch and Broadcast APIs which are quite special because these calls contain many independent operations. For these calls you still need to look at individual error objects in response. To achieve the goal we have an internal matching of Centrifugo API error codes to HTTP and GRPC error codes.","s":"Transport error mode","u":"/docs/server/server_api","h":"#transport-error-mode","p":3422},{"i":3469,"t":"func MapErrorToHTTPCode(err *Error) int { switch err.Code { case ErrorInternal.Code: // 100 -> HTTP 500 return http.StatusInternalServerError case ErrorUnknownChannel.Code, ErrorNotFound.Code: // 102, 104 -> HTTP 404 return http.StatusNotFound case ErrorBadRequest.Code, ErrorNotAvailable.Code: // 107, 108 -> HTTP 400 return http.StatusBadRequest case ErrorUnrecoverablePosition.Code: // 112 -> HTTP 416 return http.StatusRequestedRangeNotSatisfiable case ErrorConflict.Code: // 113 -> HTTP 409 return http.StatusConflict default: // Default to Internal Error for unmapped errors. // In general should be avoided - all new API errors must be explicitly described here. return http.StatusInternalServerError // HTTP 500 }}","s":"Centrifugo error code to HTTP code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-http-code","p":3422},{"i":3471,"t":"func MapErrorToGRPCCode(err *Error) codes.Code { switch err.Code { case ErrorInternal.Code: // 100 return codes.Internal case ErrorUnknownChannel.Code, ErrorNotFound.Code: // 102, 104 return codes.NotFound case ErrorBadRequest.Code, ErrorNotAvailable.Code: // 107, 108 return codes.InvalidArgument case ErrorUnrecoverablePosition.Code: // 112 return codes.OutOfRange case ErrorConflict.Code: // 113 return codes.AlreadyExists default: // Default to Internal Error for unmapped errors. // In general should be avoided - all new API errors must be explicitly described here. return codes.Internal }}","s":"Centrifugo error code to GRPC code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-grpc-code","p":3422},{"i":3473,"t":"On this page","s":"HTTP streaming, with bidirectional emulation","u":"/docs/transports/http_stream","h":"","p":3472},{"i":3476,"t":"Boolean, default: false. Enables HTTP streaming endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"http_stream\": true} When enabling http_stream you can connect to /connection/http_stream from centrifuge-js. Note that our bidirectional emulation also uses /emulation endpoint of Centrifugo to send requests from client to server. This is required because HTTP streaming is a unidirectional transport in its nature. So we use HTTP call to send data from client to server and proxy this call to the correct Centrifugo node which handles the connection. Thus achieving bidirectional behaviour - see details about Centrifugo bidirectional emulation layer. Make sure /emulation endpoint is available for requests from the client side too. If required, you can also control both HTTP streaming connection url prefix and emulation endpoint prefix, see customizing endpoints.","s":"http_stream","u":"/docs/transports/http_stream","h":"#http_stream","p":3472},{"i":3478,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"http_stream_max_request_body_size","u":"/docs/transports/http_stream","h":"#http_stream_max_request_body_size","p":3472},{"i":3480,"t":"On this page","s":"Real-time transports","u":"/docs/transports/overview","h":"","p":3479},{"i":3482,"t":"Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus and where Centrifugo really shines. Bidirectional transports come with a cost that developers need to use a special client connector library (SDK) which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state, handle request queueing/timeouts/errors, etc. And of course to multiplex subscriptions to different channels over a single connection. Centrifugo has several official client SDKs for popular environments. All of them work over WebSocket transport. Our Javascript SDK also offers bidirectional fallbacks over HTTP-Streaming, Server-Sent Events (SSE), SockJS, and has an experimental support for WebTransport.","s":"Bidirectional","u":"/docs/transports/overview","h":"#bidirectional","p":3479},{"i":3484,"t":"Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time. The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource/SSE, HTTP-streaming), or GRPC generated code to receive real-time updates from Centrifugo. Thus avoiding dependency to a client connector that abstracts bidirectional communication. The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo). Learn more about unidirectional protocol and available unidirectional transports.","s":"Unidirectional","u":"/docs/transports/overview","h":"#unidirectional","p":3479},{"i":3486,"t":"Centrifugo server periodically sends pings to clients and expects pong from clients that works over bidirectional transports. Sending ping and receiving pong allows to find broken connections faster. Centrifugo sends pings on the Centrifugo client protocol level, thus it's possible for clients to handle ping messages on the client side to make sure connection is not broken (our bidirectional SDKs do this automatically). By default Centrifugo sends pings every 25 seconds. This may be changed using ping_interval option (duration, default \"25s\"). Centrifugo expects pong message from bidirectional client SDK after sending ping to it. By default, it waits no more than 8 seconds before closing a connection. This may be changed using pong_timeout option (duration, default \"8s\"). In most cases default ping/pong intervals are fine so you don't really need to tweak them. Reducing timeouts may help you to find non-gracefully closed connections faster, but will increase network traffic and CPU resource usage since ping/pongs are sent faster. caution ping_interval must be greater than pong_timeout in the current implementation. Here is a scheme how ping/pong works in bidirectional and unidirectional client scenarios:","s":"PING/PONG behavior","u":"/docs/transports/overview","h":"#pingpong-behavior","p":3479},{"i":3488,"t":"On this page","s":"Client real-time SDKs","u":"/docs/transports/client_sdk","h":"","p":3487},{"i":3490,"t":"Here is a list of SDKs maintained by Centrifugal Labs: centrifuge-js – for a browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-dart - for Dart and Flutter (mobile and web) centrifuge-swift – for native iOS development centrifuge-java – for native Android development and general Java SDKs driven by the community: centrifuge-csharp - SDK in C# for .NET and Unity 2022.3+ See a description of client protocol if you want to write a custom bidirectional connector or eager to learn how Centrifugo protocol internals are structured. In case of any question how protocol works take a look at existing SDK source code or reach out in the community rooms.","s":"List of client SDKs","u":"/docs/transports/client_sdk","h":"#list-of-client-sdks","p":3487},{"i":3492,"t":"Centrifugo real-time SDKs work using two possible serialization formats: JSON and Protobuf. The entire bidirectional client protocol is described by the Protobuf schema. But those Protobuf messages may be also encoded as JSON objects (in JSON representation bytes fields in the Protobuf schema is replaced by the embedded JSON object in Centrifugo case). Our Javascript SDK - centrifuge-js - uses JSON serialization for protocol frames by default. This makes communication with Centrifugo server convenient as we are exchanging human-readable JSON frames between client and server. And it makes it possible to use centrifuge-js without extra dependency to protobuf.js library. It's possible to switch to Protobuf protocol with centrifuge-js SDK though, in case you want more compact Centrifuge protocol representation, faster decode/encode speeds on Centrifugo server side, or payloads you need to pass are custom binary. See more details on how to use centrifuge-js with Protobuf serialization in README. centrifuge-go real-time SDK for Go language also supports both JSON and Protobuf formats when communicating with Centrifugo server. Other SDKs, like centrifuge-dart, centrifuge-swift, centrifuge-java work using only Protobuf serialization for Centrifuge protocol internally. So they utilize the fastest and the most compact wire representation by default. Note, that while internally in those SDKs the serialization format is Protobuf, you can still send JSON towards these clients as JSON objects may be encoded as UTF-8 bytes. So these SDKs may work with both custom binary and JSON payloads. There are some important notes about JSON and Protobuf interoperability mentioned in our FAQ.","s":"Protobuf and JSON formats in SDKs","u":"/docs/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":3487},{"i":3494,"t":"Below you can find an information regarding support of different features in our official client SDKs","s":"SDK feature matrix","u":"/docs/transports/client_sdk","h":"#sdk-feature-matrix","p":3487},{"i":3496,"t":"Client feature js dart swift go java connect to a server ✅ ✅ ✅ ✅ ✅ setting client options ✅ ✅ ✅ ✅ ✅ automatic reconnect with backoff algorithm ✅ ✅ ✅ ✅ ✅ client state changes ✅ ✅ ✅ ✅ ✅ command-reply ✅ ✅ ✅ ✅ ✅ command timeouts ✅ ✅ ✅ ✅ ✅ async pushes ✅ ✅ ✅ ✅ ✅ ping-pong ✅ ✅ ✅ ✅ ✅ connection token refresh ✅ ✅ ✅ ✅ ✅ handle disconnect advice from server ✅ ✅ ✅ ✅ ✅ server-side subscriptions ✅ ✅ ✅ ✅ ✅ batching API ✅ bidirectional WebSocket emulation ✅","s":"Connection related features","u":"/docs/transports/client_sdk","h":"#connection-related-features","p":3487},{"i":3498,"t":"Client feature js dart swift go java subscrbe to a channel ✅ ✅ ✅ ✅ ✅ setting subscription options ✅ ✅ ✅ ✅ ✅ automatic resubscribe with backoff algorithm ✅ ✅ ✅ ✅ ✅ subscription state changes ✅ ✅ ✅ ✅ ✅ subscription command-reply ✅ ✅ ✅ ✅ ✅ subscription async pushes ✅ ✅ ✅ ✅ ✅ subscription token refresh ✅ ✅ ✅ ✅ ✅ handle unsubscribe advice from server ✅ ✅ ✅ ✅ ✅ manage subscription registry ✅ ✅ ✅ ✅ ✅ optimistic subscriptions ✅","s":"Client-side subscription related features","u":"/docs/transports/client_sdk","h":"#client-side-subscription-related-features","p":3487},{"i":3500,"t":"On this page","s":"SockJS","u":"/docs/transports/sockjs","h":"","p":3499},{"i":3502,"t":"caution There are several important caveats to know when using SockJS – see below.","s":"SockJS caveats","u":"/docs/transports/sockjs","h":"#sockjs-caveats","p":3499},{"i":3504,"t":"First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend. For example, with Nginx sticky support can be enabled with ip_hash directive for upstream: upstream centrifugo { ip_hash; server 127.0.0.1:8000; server 127.0.0.2:8000;} With this configuration Nginx will proxy connections with the same ip address to the same upstream backend. But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair. So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.","s":"Sticky sessions","u":"/docs/transports/sockjs","h":"#sticky-sessions","p":3499},{"i":3506,"t":"SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.","s":"Browser only","u":"/docs/transports/sockjs","h":"#browser-only","p":3499},{"i":3508,"t":"One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.","s":"JSON only","u":"/docs/transports/sockjs","h":"#json-only","p":3499},{"i":3511,"t":"Boolean, default: false. Enables SockJS transport.","s":"sockjs","u":"/docs/transports/sockjs","h":"#sockjs","p":3499},{"i":3513,"t":"Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.","s":"sockjs_url","u":"/docs/transports/sockjs","h":"#sockjs_url","p":3499},{"i":3515,"t":"On this page","s":"SSE (EventSource), with bidirectional emulation","u":"/docs/transports/sse","h":"","p":3514},{"i":3518,"t":"Boolean, default: false. Enables SSE (EventSource) endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"sse\": true} When enabling sse you can connect to /connection/sse from centrifuge-js. Note that our bidirectional emulation also uses /emulation endpoint of Centrifugo to send requests from client to server. This is required because SSE/EventSource is a unidirectional transport in its nature. So we use HTTP call to send data from client to server and proxy this call to the correct Centrifugo node which handles the connection. Thus achieving bidirectional behaviour - see details about Centrifugo bidirectional emulation layer. Make sure /emulation endpoint is available for requests from the client side too. If required, you can also control both SSE connection url prefix and emulation endpoint prefix, see customizing endpoints.","s":"sse","u":"/docs/transports/sse","h":"#sse","p":3514},{"i":3520,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"sse_max_request_body_size","u":"/docs/transports/sse","h":"#sse_max_request_body_size","p":3514},{"i":3522,"t":"On this page","s":"Unidirectional client protocol","u":"/docs/transports/uni_client_protocol","h":"","p":3521},{"i":3524,"t":"In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as: message Push { string channel = 2; Publication pub = 4; Join join = 5; Leave leave = 6; Unsubscribe unsubscribe = 7; Message message = 8; Subscribe subscribe = 9; Connect connect = 10; Disconnect disconnect = 11; Refresh refresh = 12;} tip Some numbers in Protobuf definitions skipped for backwards compatibility with previous client protocol version. So unidirectional connection will receive various pushes. Every push contains one of the following objects: Publication Join Leave Unsubscribe Message Subscribe Connect Disconnect Refresh Some pushes belong to a channel which may be set on Push top level. All you need to do is look at Push, process messages you are interested in and ignore others. In most cases you will be most interested in pushes which contain Connect or Publication messages. For example, according to protocol schema Publication message type looks like this: message Publication { bytes data = 4; ClientInfo info = 5; uint64 offset = 6; map tags = 7;} tip In JSON protocol case Centrifugo replaces bytes type with embedded JSON. Just try using any unidirectional transport and you will quickly get the idea.","s":"Unidirectional message types","u":"/docs/transports/uni_client_protocol","h":"#unidirectional-message-types","p":3521},{"i":3526,"t":"On this page","s":"Unidirectional GRPC","u":"/docs/transports/uni_grpc","h":"","p":3525},{"i":3528,"t":"JSON and binary.","s":"Supported data formats","u":"/docs/transports/uni_grpc","h":"#supported-data-formats","p":3525},{"i":3531,"t":"Boolean, default: false. Enables unidirectional GRPC endpoint. config.json { ... \"uni_grpc\": true}","s":"uni_grpc","u":"/docs/transports/uni_grpc","h":"#uni_grpc","p":3525},{"i":3533,"t":"String, default \"11000\". Port to listen on.","s":"uni_grpc_port","u":"/docs/transports/uni_grpc","h":"#uni_grpc_port","p":3525},{"i":3535,"t":"String, default \"\" (listen on all interfaces) Address to bind uni GRPC to.","s":"uni_grpc_address","u":"/docs/transports/uni_grpc","h":"#uni_grpc_address","p":3525},{"i":3537,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from GRPC connection in bytes.","s":"uni_grpc_max_receive_message_size","u":"/docs/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":3525},{"i":3539,"t":"Boolean, default: false Enable custom TLS for unidirectional GRPC server.","s":"uni_grpc_tls","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls","p":3525},{"i":3541,"t":"String, default: \"\". Path to cert file.","s":"uni_grpc_tls_cert","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":3525},{"i":3543,"t":"String, default: \"\". Path to key file.","s":"uni_grpc_tls_key","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_key","p":3525},{"i":3545,"t":"We don't have example here yet. In general, the algorithm is like this: Copy Protobuf definitions Generate GRPC client code Use generated code to connect to Centrifugo Process Push messages, drop unknown Pushes, handle those necessary for the application.","s":"Example","u":"/docs/transports/uni_grpc","h":"#example","p":3525},{"i":3547,"t":"On this page","s":"Client protocol","u":"/docs/transports/client_protocol","h":"","p":3546},{"i":3549,"t":"Centrifugo is built on top of Centrifuge library for Go. Centrifuge library uses its own framing for wrapping Centrifuge-specific messages – synchronous commands from a client to a server (which expect replies from a server) and asynchronous pushes. Centrifuge client protocol is defined by a Protobuf schema. This is the source of truth. tip At the moment Protobuf schema contains some fields which are only used in client protocol v1. This is for backwards compatibility – server supports clients connecting over both client protocol v2 and client protocol v1. Client protocol v1 is considered deprecated and will be removed at some point in the future (giving enough time to our users to migrate).","s":"Protobuf schema","u":"/docs/transports/client_protocol","h":"#protobuf-schema","p":3546},{"i":3551,"t":"In bidirectional case client sends Command to a server and server sends Reply to a client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages. Each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous transport where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before on SDK level. In JSON case client can send command like this: {\"id\": 1, \"subscribe\": {\"channel\": \"example\"}} And client can expect something like this in response: {\"id\": 1, \"subscribe\": {}} Reply for different commands has corresponding field with command result (\"subscribe\" in example above). Reply can also contain error if Command processing resulted into an error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can handle result object appropriately. error looks like this in JSON case: { \"code\": 100, \"message\": \"internal server error\", \"temporary\": true} I.e. reply with error may look like this: {\"id\": 1, \"error\": {\"code\": 100, \"message\": \"internal server error\"}} We will talk more about error handling below. Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow. Current commands: connect – sent to authenticate connection, sth like hello from a client which can carry authentication token and arbitrary data. subscribe – sent to subscribe to a channel unsubscribe - sent to unsubscribe from a channel publish - sent to publish data into a channel presence - sent to request presence information from a channel presence_stats - sent to request presence stats information from a channel history - sent to request history information for a channel send - sent to send async message to a server (this command is a bit special since it must not contain id - as we don't wait for any response from a server in this case). rpc - sent to send RPC to a channel (execute arbitrary logic and wait for response) refresh - sent to refresh connection token sub_refresh - sent to refresh channel subscription token","s":"Command-Reply","u":"/docs/transports/client_protocol","h":"#command-reply","p":3546},{"i":3553,"t":"The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to a client at any moment - not as reaction to issued Command but as a message from a server to a client at arbitrary time. For example, this can be a message published into channel. There are several types of asynchronous messages that can come from a server to a client. pub is a message published into channel join messages sent when someone joined (subscribed on) channel. leave messages sent when someone left (unsubscribed from) channel. unsubscribe message sent when a server unsubscribed current client from a channel: subscribe may be sent when a server subscribes client to a channel. disconnect may be sent be a server before closing connection and contains disconnect code/reason message may be sent when server sends asynchronous message to a client connect push can be sent in unidirectional transport case refresh may be sent when a server refreshes client credentials (useful in unidirectional transports)","s":"Asynchronous pushes","u":"/docs/transports/client_protocol","h":"#asynchronous-pushes","p":3546},{"i":3555,"t":"To reduce number of system calls one request from a client to a server and one response from a server to a client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data. When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \\n: {\"id\": 1, \"subscribe\": {\"channel\": \"ch1\"}}{\"id\": 2, \"subscribe\": {\"channel\": \"ch2\"}} Here is an example how we do this in Javascript client when JSON format used: function encodeCommands(commands) { const encodedCommands = []; for (const i in commands) { if (commands.hasOwnProperty(i)) { encodedCommands.push(JSON.stringify(commands[i])); } } return encodedCommands.join('\\n');} info This doc uses JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo. When Protobuf format used then many Command can be sent from a client to a server in a length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example. The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there. tip Server can even send reply to a command and asynchronous message batched together in a one frame. For example here is how we read server response and extracting individual replies in Javascript client when JSON format used: function decodeReplies(data) { const replies = []; const encodedReplies = data.split('\\n'); for (const i in encodedReplies) { if (encodedReplies.hasOwnProperty(i)) { if (!encodedReplies[i]) { continue; } const reply = JSON.parse(encodedReplies[i]); replies.push(reply); } } return replies;} For Protobuf case see existing client implementations for decoding example.","s":"Top level batching","u":"/docs/transports/client_protocol","h":"#top-level-batching","p":3546},{"i":3557,"t":"To maintain connection alive and detect broken connections server periodically sends empty commands to clients and expects empty replies from them. When client does not receive ping from a server for some time it can consider connection broken and try to reconnect. Usually a server sends pings every 25 seconds.","s":"Ping Pong","u":"/docs/transports/client_protocol","h":"#ping-pong","p":3546},{"i":3559,"t":"Client should handle disconnect advices from server. In websocket case disconnect advice is sent in CLOSE Websocket frame. Disconnect advice contains uint32 code and human-readable string reason.","s":"Handle disconnects","u":"/docs/transports/client_protocol","h":"#handle-disconnects","p":3546},{"i":3561,"t":"This section contains advices to error handling in client implementations. Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems. Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again. Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side. The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance. Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).","s":"Handle errors","u":"/docs/transports/client_protocol","h":"#handle-errors","p":3546},{"i":3563,"t":"Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in a reply to a subscribe command.","s":"Additional notes","u":"/docs/transports/client_protocol","h":"#additional-notes","p":3546},{"i":3565,"t":"On this page","s":"Unidirectional WebSocket","u":"/docs/transports/uni_websocket","h":"","p":3564},{"i":3567,"t":"It's possible to send connect command as first WebSocket message (as JSON). Field name Field type Required Description token string no Connection JWT, not required when using the connect proxy feature. data any JSON no Custom JSON connection data name string no Application name version string no Application version subs map of channel to SubscribeRequest no Pass an information about desired subscriptions to a server","s":"Connect command","u":"/docs/transports/uni_websocket","h":"#connect-command","p":3564},{"i":3569,"t":"Field name Field type Required Description recover boolean no Whether a client wants to recover from a certain position offset integer no Known stream position offset when recover is used epoch string no Known stream position epoch when recover is used","s":"SubscribeRequest","u":"/docs/transports/uni_websocket","h":"#subscriberequest","p":3564},{"i":3571,"t":"JSON","s":"Supported data formats","u":"/docs/transports/uni_websocket","h":"#supported-data-formats","p":3564},{"i":3573,"t":"Centrifugo uses empty commands ({} in JSON case) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/transports/uni_websocket","h":"#pings","p":3564},{"i":3576,"t":"Boolean, default: false. Enables unidirectional WebSocket endpoint. config.json { ... \"uni_websocket\": true}","s":"uni_websocket","u":"/docs/transports/uni_websocket","h":"#uni_websocket","p":3564},{"i":3578,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from WebSocket connection in bytes.","s":"uni_websocket_message_size_limit","u":"/docs/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":3564},{"i":3580,"t":"Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal. First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example: config.json { \"token_hmac_secret_key\": \"secret\", \"uni_websocket\":true, \"user_subscribe_to_personal\": true} Run Centrifugo: ./centrifugo -c config.json In another terminal: ❯ ./centrifugo gentoken -c config.json -u test_userHMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw Install wscat and run: wscat -c \"ws://localhost:8000/connection/uni_websocket\" This will establish a connection with a server and you then can send connect command to a server: ❯ wscat -c \"ws://localhost:8000/connection/uni_websocket\"Connected (press CTRL+C to quit)> {\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2NTY1MDMwNDV9.3UYL-UCUBp27TybeBK7Z0OenwdsKwCMRe46fuEjJnzI\", \"subs\": {\"abc\": {}}}< {\"connect\":{\"client\":\"bfd28799-b958-4791-b9e9-b011eaef68c1\",\"version\":\"0.0.0\",\"subs\":{\"#test_user\":{}},\"expires\":true,\"ttl\":604407,\"ping\":25,\"session\":\"57b1287b-44ec-45c8-93fc-696c5294af25\"}} The connection will receive server pings (empty commands {}) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.","s":"Example","u":"/docs/transports/uni_websocket","h":"#example","p":3564},{"i":3582,"t":"On this page","s":"Unidirectional HTTP streaming","u":"/docs/transports/uni_http_stream","h":"","p":3581},{"i":3584,"t":"It's possible to pass initial connect command by posting a JSON body to a streaming endpoint. Refer to the full Connect command description – it's the same as for unidirectional WebSocket.","s":"Connect command","u":"/docs/transports/uni_http_stream","h":"#connect-command","p":3581},{"i":3586,"t":"JSON","s":"Supported data formats","u":"/docs/transports/uni_http_stream","h":"#supported-data-formats","p":3581},{"i":3588,"t":"Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/transports/uni_http_stream","h":"#pings","p":3581},{"i":3591,"t":"Boolean, default: false. Enables unidirectional HTTP streaming endpoint. config.json { ... \"uni_http_stream\": true}","s":"uni_http_stream","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream","p":3581},{"i":3593,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"uni_http_stream_max_request_body_size","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":3581},{"i":3595,"t":"Let's look how simple it is to connect to Centrifugo using HTTP streaming. We will start from scratch, generate new configuration file: centrifugo genconfig Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect: config.json { ... \"uni_http_stream\": true, \"user_subscribe_to_personal\": true} Run Centrifugo: centrifugo -c config.json In separate terminal window create token for a user: ❯ go run main.go gentoken -u user12HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request: curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}' Open one more terminal window and publish message to a personal user channel: curl -X POST http://localhost:8000/api/publish \\ -d '{\"channel\": \"#user12\", \"data\": {\"input\": \"hello\"}}' \\ -H \"Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc\" You should see this messages coming from server. {} messages are pings from a server. That's all, happy streaming!","s":"Connecting using CURL","u":"/docs/transports/uni_http_stream","h":"#connecting-using-curl","p":3581},{"i":3597,"t":"A basic browser will come soon as we update docs for v4.","s":"Browser example","u":"/docs/transports/uni_http_stream","h":"#browser-example","p":3581},{"i":3599,"t":"On this page","s":"Unidirectional SSE (EventSource)","u":"/docs/transports/uni_sse","h":"","p":3598},{"i":3601,"t":"Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example: const url = new URL('http://localhost:8000/connection/uni_sse');url.searchParams.append(\"cf_connect\", JSON.stringify({ 'token': ''}));const eventSource = new EventSource(url); Refer to the full Connect command description – it's the same as for unidirectional WebSocket. The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases. tip Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.","s":"Connect command","u":"/docs/transports/uni_sse","h":"#connect-command","p":3598},{"i":3603,"t":"JSON","s":"Supported data formats","u":"/docs/transports/uni_sse","h":"#supported-data-formats","p":3598},{"i":3606,"t":"Boolean, default: false. Enables unidirectional SSE (EventSource) endpoint. config.json { ... \"uni_sse\": true}","s":"uni_sse","u":"/docs/transports/uni_sse","h":"#uni_sse","p":3598},{"i":3608,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"uni_sse_max_request_body_size","u":"/docs/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":3598},{"i":3610,"t":"Coming soon.","s":"Browser example","u":"/docs/transports/uni_sse","h":"#browser-example","p":3598},{"i":3612,"t":"WebTransport WebTransport is an API offering low-latency, bidirectional, client-server messaging on top of HTTP/3 (with QUIC under the hood). See Using WebTransport article that gives a good overview of it. danger WebTransport support in Centrifugo is EXPERIMENTAL and not recommended for production usage. WebTransport IETF specification is not finished yet and may have breaking changes. To use WebTransport you first need to run HTTP/3 experimental server and enable webtransport endpoint: config.json { \"http3\": true, \"tls\": true, \"tls_cert\": \"path/to/crt\", \"tls_key\": \"path/to/key\", \"webtransport\": true} In HTTP/3 and WebTransport case TLS is required. tip At the time of writing only Chrome (since v97) supports WebTransport API. If you are experimenting with self-signed certificates you may need to run Chrome with flags to force HTTP/3 on origin and ignore certificate errors: /path/to/your/Chrome --origin-to-force-quic-on=localhost:8000 --ignore-certificate-errors-spki-list=TSZTiMjLG+DNjESXdJh3f+S8C+RhsFCav7T24VNuCPQ= Where the value of --ignore-certificate-errors-spki-list is a certificate fingerprint obtained this way: openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 With not self-signed certs things should work just fine in Chrome. Here is a video tutorial that shows this in action: After starting Centrifugo with HTTP/3 and WebTransport endpoint you can connect to that endpoint (by default – /connection/webtransport) using centrifuge-js. For example, let's enable WebTransport and will use WebSocket as a fallback option: const transports = [ { transport: 'webtransport', endpoint: 'https://localhost:8000/connection/webtransport' }, { transport: 'websocket', endpoint: 'wss://localhost:8000/connection/websocket' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in WebTransport case non-TLS http:// scheme is simply not supported by the specification. Also, Chrome may not automatically close WebTransport sessions upon browser reload, so consider adding: addEventListener(\"unload\", (event) => { centrifuge.disconnect() }); tip Make sure you run Centrifugo without load balancer or reverse proxy in front, or make sure your proxy can proxy HTTP/3 traffic to Centrifugo. In Centrifugo case, we utilize a single bidirectional stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional. Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification it's an experimental feature. And it's not recommended for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket.","s":"WebTransport","u":"/docs/transports/webtransport","h":"","p":3611},{"i":3614,"t":"On this page","s":"WebSocket","u":"/docs/transports/websocket","h":"","p":3613},{"i":3617,"t":"Default: 65536 (64KB) Maximum allowed size of a message received from WebSocket connection in bytes.","s":"websocket_message_size_limit","u":"/docs/transports/websocket","h":"#websocket_message_size_limit","p":3613},{"i":3619,"t":"In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size). config.json { ... \"websocket_read_buffer_size\": 512}","s":"websocket_read_buffer_size","u":"/docs/transports/websocket","h":"#websocket_read_buffer_size","p":3613},{"i":3621,"t":"In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused): config.json { ... \"websocket_write_buffer_size\": 512}","s":"websocket_write_buffer_size","u":"/docs/transports/websocket","h":"#websocket_write_buffer_size","p":3613},{"i":3623,"t":"If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.","s":"websocket_use_write_buffer_pool","u":"/docs/transports/websocket","h":"#websocket_use_write_buffer_pool","p":3613},{"i":3625,"t":"An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire. We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally. caution Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable. To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature. Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client. It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.","s":"websocket_compression","u":"/docs/transports/websocket","h":"#websocket_compression","p":3613},{"i":3627,"t":"In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if: you want less traffic on wire as Protobuf is very compact you want maximum performance on server-side as Protobuf encoding/decoding is very efficient you can sacrifice human-readable JSON for your application Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like: wss://centrifugo.example.com/connection/websocket?format=protobuf After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes. tip You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.","s":"Protobuf binary protocol","u":"/docs/transports/websocket","h":"#protobuf-binary-protocol","p":3613},{"i":3629,"t":"Centrifugo supports a special url parameter for bidirectional websocket which turns on using native WebSocket frame ping-pong mechanism instead of server-to-client application level pings Centrifugo uses by default. This simplifies debugging Centrifugo protocol with tools like Postman, wscat, websocat, etc. By default, it may be inconvenient due to the fact Centrifugo sends periodic ping message to the client ({} in JSON protocol scenario) and expects pong response back within some time period. Otherwise Centrifugo closes connection. This results in problems with mentioned tools because you had to manually send {} pong message upon ping message. So typical session in wscat could look like this: ❯ wscat --connect ws://localhost:8000/connection/websocketConnected (press CTRL+C to quit)> {\"id\": 1, \"connect\": {}}< {\"id\":1,\"connect\":{\"client\":\"9ac9de4e-5289-4ad6-9aa7-8447f007083e\",\"version\":\"0.0.0\",\"ping\":25,\"pong\":true}}< {}Disconnected (code: 3012, reason: \"no pong\") The parameter is called cf_ws_frame_ping_pong, to use it connect to Centrifugo bidirectional WebSocket endpoint like ws://localhost:8000/connection/websocket?cf_ws_frame_ping_pong=true. Here is an example which demonstrates working with Postman WebSocket where we connect to local Centrifugo and subscribe to two channels test1 and test2: You can then proceed to Centrifugo admin web UI, publish something to these channels and see publications in Postman. Note, how we sent several JSON commands in one WebSocket frame to Centrifugo from Postman in the example above - this is possible since Centrifugo protocol supports batches of commands in line-delimited format. We consider this feature to be used only for debugging, in production prefer using our SDKs without using cf_ws_frame_ping_pong parameter – because app-level ping-pong is more efficient and our SDKs detect broken connections due to it.","s":"Debugging with Postman, wscat, etc","u":"/docs/transports/websocket","h":"#debugging-with-postman-wscat-etc","p":3613},{"i":3631,"t":"On this page","s":"Client SDK API","u":"/docs/transports/client_api","h":"","p":3630},{"i":3633,"t":"Client connection has 4 states: disconnected connecting connected closed note closed state is only implemented by SDKs where it makes sense (need to clean up allocated resources when app gracefully shuts down – for example in Java SDK we close thread executors used internally). When a new Client is created it has a disconnected state. To connect to a server connect() method must be called. After calling connect Client moves to the connecting state. If a Client can't connect to a server it attempts to create a connection with an exponential backoff algorithm (with full jitter). If a connection to a server is successful then the state becomes connected. If a connection is lost (due to a missing network for example, or due to reconnect advice received from a server, or due to some client-side error that can't be recovered without reconnecting) Client goes to the connecting state again. In this state Client tries to reconnect (again, with an exponential backoff algorithm). The Client's state can become disconnected. This happens when Client's disconnect() method was called by a developer. Also, this can happen due to server advice from a server, or due to a terminal problem that happened on the client-side. Here is a program where we create a Client instance, set callbacks to listen to state updates and establish a connection with a server: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('connecting', function(ctx) { console.log('connecting', ctx);});client.on('connected', function(ctx) { console.log('connected', ctx);});client.on('disconnected', function(ctx) { console.log('disconnected', ctx);});client.connect(); final onEvent = (dynamic event) { print('client event> $event');};final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);client.connecting.listen(onEvent);client.connected.listen(onEvent);client.disconnected.listen(onEvent);await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate { func onConnecting(_ c: CentrifugeClient, _ e: CentrifugeConnectingEvent) { print(\"connecting\", e.code, e.reason) } func onConnected(_ client: CentrifugeClient, _ e: CentrifugeConnectedEvent) { print(\"connected with id\", e.client) } func onDisconnected(_ client: CentrifugeClient, _ e: CentrifugeDisconnectedEvent) { print(\"disconnected\", e.code, e.reason) }}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())client.connect() EventListener listener = new EventListener() { @Override public void onConnected(Client client, ConnectedEvent event) { System.out.println(\"connected\"); } @Override public void onConnecting(Client client, ConnectingEvent event) { System.out.printf(\"connecting: %s%n\", event.getReason()); } @Override public void onDisconnected(Client client, DisconnectedEvent event) { System.out.printf(\"disconnected %d %s\", event.getCode(), event.getReason()); }};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)defer client.Close()client.OnConnecting(func(e centrifuge.ConnectingEvent) { log.Printf(\"Connecting - %d (%s)\", e.Code, e.Reason)})client.OnConnected(func(e centrifuge.ConnectedEvent) { log.Printf(\"Connected with ID %s\", e.ClientID)})client.OnDisconnected(func(e centrifuge.DisconnectedEvent) { log.Printf(\"Disconnected: %d (%s)\", e.Code, e.Reason)})_ = client.connect() In case of successful connection Client states will transition like this: disconnected (initial) -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server and then successfully reconnected: connected -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server, but got a terminal error upon reconnection: connected -> connecting (on('connecting') called) -> disconnected (on('disconnected') called). In case of already connected Client came across terminal condition (for example, if during a connection token refresh application found that user has no permission to connect anymore): connected -> disconnected (on('disconnected') called). Both connecting and disconnected events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why the Client went to the connecting state or to the disconnected state. This diagram demonstrates possible Client state transitions: You can also listen for all errors happening internally (which are not reflected by state changes, for example, transport errors happening on initial connect, transport during reconnect, connection token refresh related errors, etc) while the client works by using error event: client.on('error', function(ctx) { console.log('client error', ctx);}); If you want to disconnect from a server call .disconnect() method: client.disconnect(); In this case on('disconnected') will be called. You can call connect() again when you need to establish a connection. closed state implemented in SDKs where resources like internal queues, thread executors, etc must be cleaned up when the Client is not needed anymore. All subscriptions should automatically go to the unsubscribed state upon closing. The client is not usable after going to a closed state.","s":"Client connection states","u":"/docs/transports/client_api","h":"#client-connection-states","p":3630},{"i":3635,"t":"There are several common options available when creating Client instance. option to set connection token and callback to get connection token upon expiration (see below mode details) option to set connect data option to configure operation timeout tweaks for reconnect backoff algorithm (min delay, max delay) configure max delay of server pings (to detect broken connection) configure headers to send in WebSocket upgrade request (except centrifuge-js) configure client name and version for analytics purpose","s":"Client common options","u":"/docs/transports/client_api","h":"#client-common-options","p":3630},{"i":3637,"t":"connect() – connect to a server disconnect() - disconnect from a server close() - close Client if not needed anymore send(data) - send asynchronous message to a server rpc(method, data) - send arbitrary RPC and wait for response","s":"Client methods","u":"/docs/transports/client_api","h":"#client-methods","p":3630},{"i":3639,"t":"All SDKs support connecting to Centrifugo with JWT. Initial connection token can be set in Client option upon initialization. Example: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE'}); If the token sets connection expiration then the client SDK will keep the token refreshed. It does this by calling a special callback function. This callback must return a new token. If a new token with updated connection expiration is returned from callback then it's sent to Centrifugo. In case of error returned by your callback SDK will retry the operation after some jittered time. An example: async function getToken() { if (!loggedIn) { return \"\"; // Empty token or pre-generated token for anonymous users. } // Fetch your application backend. const res = await fetch('http://localhost:8000/centrifugo/connection_token'); if (!res.ok) { if (res.status === 403) { // Return special error to not proceed with token refreshes, // client will be disconnected. throw new Centrifuge.UnauthorizedError(); } // Any other error thrown will result into token refresh re-attempts. throw new Error(`Unexpected status code ${res.status}`); } const data = await res.json(); return data.token;}const client = new Centrifuge( 'ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE', // Optional, getToken is enough. getToken: getToken }); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authentication. In this case SDK should attempt to get a connection token before establishing an initial connection.","s":"Client connection token","u":"/docs/transports/client_api","h":"#client-connection-token","p":3630},{"i":3641,"t":"PINGs sent by a server, a client should answer with PONGs upon receiving PING. If a client does not receive PING from a server for a long time (ping interval + configured delay) – the connection is considered broken and will be re-established.","s":"Connection PING/PONG","u":"/docs/transports/client_api","h":"#connection-pingpong","p":3630},{"i":3643,"t":"Client allows subscribing on channels. This can be done by creating Subscription object. const sub = centrifuge.newSubscription(channel);sub.subscribe(); When anewSubscription method is called Client allocates a new Subscription instance and saves it in the internal subscription registry. Having a registry of allocated subscriptions allows SDK to manage resubscribes upon reconnecting to a server. Centrifugo connectors do not allow creating two subscriptions to the same channel – in this case, newSubscription can throw an exception. Subscription has 3 states: unsubscribed subscribing subscribed When a new Subscription is created it has an unsubscribed state. To initiate the actual process of subscribing to a channel subscribe() method of Subscription instance should be called. After calling subscribe() Subscription moves to subscribing state. If subscription to a channel is not successful then depending on error type subscription can automatically resubscribe (with exponential backoff) or go to an unsubscribed state (upon non-temporary error). If subscription to a channel is successful then the state becomes subscribed. Javascript Dart Swift Java Go const sub = client.newSubscription(channel);sub.on('subscribing', function(ctx) { console.log('subscribing');});sub.on('subscribed', function(ctx) { console.log('subscribed');});sub.on('unsubscribed', function(ctx) { console.log('unsubscribed');});sub.subscribe(); final onSubscriptionEvent = (dynamic event) async { print('subscription $channel> $event');};final subscription = client.newSubscription(channel);subscription.subscribing.listen(onSubscriptionEvent);subscription.subscribed.listen(onSubscriptionEvent);subscription.unsubscribed.listen(onSubscriptionEvent);await subscription.subscribe(); class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onSubscribing(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribingEvent) { print(\"subscribing\", e.code, e.reason) } func onSubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribedEvent) { print(\"subscribed\") } func onUnsubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeUnsubscribedEvent) { print(\"unsubscribed\", e.code, e.reason) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")} SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onSubscribed(Subscription sub, SubscribedEvent event) { System.out.println(\"subscribed to \" + sub.getChannel()); } @Override public void onSubscribing(Subscription sub, SubscribingEvent event) { System.out.printf(\"subscribing \" + sub.getChannel()); } @Override public void onUnsubscribed(Subscription sub, UnsubscribedEvent event) { System.out.println(\"unsubscribed \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();} sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnSubscribing(func(e centrifuge.SubscribingEvent) { log.Printf(\"Subscribing on channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})sub.OnSubscribed(func(e centrifuge.SubscribedEvent) { log.Printf(\"Subscribed on channel %s\", sub.Channel)})sub.OnUnsubscribed(func(e centrifuge.UnsubscribedEvent) { log.Printf(\"Unsubscribed from channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})err = sub.Subscribe()if err != nil { log.Fatalln(err)} Subscriptions also go to subscribing state when Client connection (i.e. transport) becomes unavailable. Upon connection re-establishement all subscriptions which are not in unsubscribed state will resubscribe automatically. In case of successful subscription states will transition like this: unsubscribed (initial) -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). In case of connected and subscribed Client temporary lost a connection with a server and then succesfully reconnected and resubscribed: subscribed -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). Both subscribing and unsubscribed events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why Subscription went to subscribing state or to unsubscribed state. This diagram demonstrates possible Subscription state transitions: You can listen for all errors happening internally in Subscription (which are not reflected by state changes, for example, temporary subscribe errors, subscription token related errors, etc) by using error event: sub.on('error', function(ctx) { console.log(\"subscription error\", ctx);}); If you want to unsubscribe from a channel call .unsubscribe() method: sub.unsubscribe(); In this case on('unsubscribed') will be called. Subscription still kept in Client's registry, but no resubscription attempts will be made. You can call subscribe() again when you need Subscription again. Or you can remove Subscription from Client's registry (see below).","s":"Subscription states","u":"/docs/transports/client_api","h":"#subscription-states","p":3630},{"i":3645,"t":"The client SDK provides several methods to manage the internal registry of client-side subscriptions. newSubscription(channel, options) allocates a new Subscription in the registry or throws an exception if the Subscription is already there. We will discuss common Subscription options below. getSubscription(channel) returns the existing Subscription by a channel from the registry (or null if it does not exist). removeSubscription(sub) removes Subscription from Client's registry. Subscription is automatically unsubscribed before being removed. Use this to free resources if you don't need a Subscription to a channel anymore. subscriptions() returns all registered subscriptions, so you can iterate over all and do some action if required (for example, you want to unsubscribe/remove all subscriptions).","s":"Subscription management","u":"/docs/transports/client_api","h":"#subscription-management","p":3630},{"i":3647,"t":"Of course the main point of having Subscriptions is the ability to listen for publications (i.e. messages published to a channel). sub.on('publication', function(ctx) { console.log(\"received publication\", ctx);}); Publication context has several fields: data - publication payload, this can be JSON or binary data offset - optional offset inside history stream, this is an incremental number tags - optional tags, this is a map with string keys and string values info - optional information about client connection who published this (only exists if publication comes from client-side publish() API). So minimal code where we connect to a server and listen for messages published into example channel may look like: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = client.newSubscription('example').on('publication', function(ctx) { console.log(\"received publication from a channel\", ctx.data);});sub.subscribe();client.connect(); final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);final subscription = client.newSubscription(channel);subscription.publication.listen((event) { print(event);});await subscription.subscribe();await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate {}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onPublication(_ s: CentrifugeSubscription, _ e: CentrifugePublicationEvent) { print(\"publication\", e.data) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")}client.connect() EventListener listener = new EventListener() {};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onPublication(Subscription sub, PublicationEvent event) { System.out.println(\"publication from \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();}client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)// defer client.Close()sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnPublication(func(e centrifuge.PublicationEvent) { log.Printf(\"Publication from channel\")})err = sub.Subscribe()if err != nil { log.Fatalln(err)}if err = client.Connect(); err != nil { log.Fatalln(err)} Note, that we can call subscribe() before making a connection to a server – and this will work just fine, subscription goes to subscribing state and will be subscribed upon succesfull connection. And of course, it's possible to call .subscribe() after .connect().","s":"Listen to channel publications","u":"/docs/transports/client_api","h":"#listen-to-channel-publications","p":3630},{"i":3649,"t":"Subscriptions to channels with recovery option enabled maintain stream position information internally. On every publication received this information updated and used to recover missed publications upon resubscribe (caused by reconnect for example). When you call unsubscribe() Subscription position state is not cleared. So it's possible to call subscribe() later and catch up a state. The recovery process result – i.e. whether all missed publications recovered or not – can be found in on('subscribed') event context. Centrifuge protocol provides two fields: wasRecovering - boolean flag that tells whether recovery was used during subscription process resulted into subscribed state. Can be useful if you want to distinguish first subscribe attempt (when subscription does not have any position information yet) recovered - boolean flag that tells whether Centrifugo thinks that all missed publications can be successfully recovered and there is no need to load state from the main application database. It's always false when wasRecovering is false.","s":"Subscription recovery state","u":"/docs/transports/client_api","h":"#subscription-recovery-state","p":3630},{"i":3651,"t":"There are several common options available when creating Subscription instance. option to set subscription token and callback to get subscription token upon expiration (see below more details) option to set subscription data (attached to every subscribe/resubscribe request) options to tweak resubscribe backoff algorithm option to start Subscription since known Stream Position (i.e. attempt recovery on first subscribe) option to ask server to make subscription positioned (if not forced by a server) option to ask server to make subscription recoverable (if not forced by a server) option to ask server to push Join/Leave messages (if not forced by a server)","s":"Subscription common options","u":"/docs/transports/client_api","h":"#subscription-common-options","p":3630},{"i":3653,"t":"subscribe() – start subscribing to a channel unsubscribe() - unsubscribe from a channel publish(data) - publish data to Subscription channel history(options) - request Subscription channel history presence() - request Subscription channel online presence information presenceStats() - request Subscription channel online presence stats information (number of client connections and unique users in a channel).","s":"Subscription methods","u":"/docs/transports/client_api","h":"#subscription-methods","p":3630},{"i":3655,"t":"All SDKs support subscribing to Centrifugo channels with JWT. Channel subscription token can be set as a Subscription option upon initialization. Example: const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE'});sub.subscribe(); If token sets subscription expiration client SDK will keep token refreshed. It does this by calling special callback function. This callback must return a new token. If new token with updated subscription expiration returned from a calbback then it's sent to Centrifugo. If your callback returns an empty string – this means user has no permission to subscribe to a channel anymore and subscription will be unsubscribed. In case of error returned by your callback SDK will retry operation after some jittered time. An example: async function getToken(ctx) { // Fetch your application backend. const res = await fetch('http://localhost:8000/centrifugo/subscription_token', { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify({ channel: ctx.channel }) }); if (!res.ok) { if (res.status === 403) { // Return special error to not proceed with token refreshes, // client will be disconnected. throw new Centrifuge.UnauthorizedError(); } // Any other error thrown will result into token refresh re-attempts. throw new Error(`Unexpected status code ${res.status}`); } const data = await res.json(); return data.token;}const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE', // Optional, getToken is enough. getToken: getToken});sub.subscribe(); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authorization for a channel subscription. In this case SDK should attempt to get a subscription token before initial subscribe.","s":"Subscription token","u":"/docs/transports/client_api","h":"#subscription-token","p":3630},{"i":3657,"t":"We encourage using client-side subscriptions where possible as they provide a better control and isolation from connection. But in some cases you may want to use server-side subscriptions (i.e. subscriptions created by server upon connection establishment). Technically, client SDK keeps server-side subscriptions in internal registry (similar to client-side subscriptions but without possibility to control them). To listen for server-side subscription events use callbacks as shown in example below: const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('subscribed', function(ctx) { // Called when subscribed to a server-side channel upon Client moving to // connected state or during connection lifetime if server sends Subscribe // push message. console.log('subscribed to server-side channel', ctx.channel);});client.on('subscribing', function(ctx) { // Called when existing connection lost (Client reconnects) or Client // explicitly disconnected. Client continue keeping server-side subscription // registry with stream position information where applicable. console.log('subscribing to server-side channel', ctx.channel);});client.on('unsubscribed', function(ctx) { // Called when server sent unsubscribe push or server-side subscription // previously existed in SDK registry disappeared upon Client reconnect. console.log('unsubscribed from server-side channel', ctx.channel);});client.on('publication', function(ctx) { // Called when server sends Publication over server-side subscription. console.log('publication receive from server-side channel', ctx.channel, ctx.data);});client.connect(); Server-side subscription events mostly mimic events of client-side subscriptions. But again – they do not provide control to the client and managed entirely by a server side. Additionally, Client has several top-level methods to call with server-side subscription related operations: publish(channel, data) history(channel, options) presence(channel) presenceStats(channel)","s":"Server-side subscriptions","u":"/docs/transports/client_api","h":"#server-side-subscriptions","p":3630},{"i":3659,"t":"Server can return error codes in range 100-1999. Error codes in interval 0-399 reserved by Centrifuge/Centrifugo server. Codes in range [400, 1999] may be returned by application code built on top of Centrifuge/Centrifugo. Server errors contain a temporary boolean flag which works as a signal that error may be fixed by a later retry. Errors with codes 0-100 can be used by client-side implementation. Client-side errors may not have code attached at all since in many languages error can be distinguished by its type.","s":"Error codes","u":"/docs/transports/client_api","h":"#error-codes","p":3630},{"i":3661,"t":"Server may return unsubscribe codes. Server unsubscribe codes must be in range [2000, 2999]. Unsubscribe codes >= 2500 coming from server to client result into automatic resubscribe attempt (i.e. client goes to subscribing state). Codes < 2500 result into going to unsubscribed state. Client implementation can use codes <2000 for client-side specific unsubscribe reasons.","s":"Unsubscribe codes","u":"/docs/transports/client_api","h":"#unsubscribe-codes","p":3630},{"i":3663,"t":"Server may send custom disconnect codes to a client. Custom disconnect codes must be in range [3000, 4999]. Client automatically reconnects upon receiving code in range 3000-3499, 4000-4499 (i.e. Client goes to connecting state). Other codes result into going to disconnected state. Client implementation can use codes <3000 for client-side specific disconnect reasons.","s":"Disconnect codes","u":"/docs/transports/client_api","h":"#disconnect-codes","p":3630},{"i":3665,"t":"An SDK provides a way to send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the real-time connection. RPC is only available when RPC proxy configured (so Centrifugo proxies the RPC to your application backend). const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"RPC","u":"/docs/transports/client_api","h":"#rpc","p":3630},{"i":3667,"t":"SDK provides a method to call publication history inside a channel (only for channels where history is enabled) to get last publications in a channel. Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Channel history API","u":"/docs/transports/client_api","h":"#channel-history-api","p":3630},{"i":3669,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats API","u":"/docs/transports/client_api","h":"#presence-and-presence-stats-api","p":3630},{"i":3671,"t":"Callbacks must be fast. Avoid blocking operations inside event handlers. Callbacks caused by protocol messages received from a server are called synchronously and connection read loop is blocked while such callbacks are being executed. Consider doing heavy work asynchronously. Do not blindly rely on the current Client or Subscription state when making client API calls – state can change at any moment, so don't forget to handle errors. Disconnect from a server when a mobile application goes to the background since a mobile OS can kill the connection at some point without any callbacks called.","s":"SDK common best practices","u":"/docs/transports/client_api","h":"#sdk-common-best-practices","p":3630}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/2",[0,1.305,1,1.313,2,0.609,3,1.932,4,0.701,5,0.796,6,1.634,7,0.549,8,1.216,9,2.532,10,1.222,11,1.802,12,0.911,13,4.043,14,1.681,15,1.426,16,1.904,17,1.545,18,3.058,19,2.303,20,1.413,21,1.966,22,4.727,23,2.323,24,1.657,25,1.655,26,2.829,27,2.052,28,1.327,29,1.364,30,2.446,31,0.552,32,3.232,33,1.288,34,0.996,35,1.573,36,3.36,37,3.445,38,3.91,39,1.591,40,2.06,41,1.025,42,1.657,43,3.91,44,3.343,45,3.91,46,2.552,47,3.259,48,0.917,49,0.879,50,5.502,51,2.26,52,2.435,53,2.435,54,2.435,55,1.229,56,0.959,57,2.435,58,1.634,59,2.058,60,2.435,61,2.435,62,2.435,63,1.736,64,2.435,65,2.435,66,2.435,67,2.435,68,1.482,69,2.435,70,2.79,71,2.435,72,2.435,73,2.435,74,2.435,75,2.435,76,1.681,77,2.435,78,2.435,79,1.883,80,2.435,81,2.435,82,2.435,83,2.435,84,0.885,85,0.783,86,2.662,87,6.96,88,1.018,89,0.843,90,1.065,91,1.883,92,2.821,93,0.957,94,1.222,95,2.426,96,1.377,97,2.754,98,3.038,99,1.425,100,2.311,101,3.278,102,3.259,103,0.756,104,1.989,105,1.616,106,2.257,107,1.001,108,2.019,109,1.174,110,1.191,111,1.657,112,1.937,113,1.426,114,1.802,115,1.04,116,0.843,117,2.26,118,2.435,119,2.058,120,5.168,121,1.322,122,2.435,123,0.77,124,1.425,125,1.288,126,2.495,127,0.741,128,1.078,129,1.35,130,2.946,131,3.457,132,1.518,133,0.967,134,1.124,135,1.288,136,2.435,137,2.509,138,1.577,139,1.014,140,0.885,141,2.26,142,2.058,143,3.184,144,1.872,145,1.572,146,2.435,147,2.26,148,2.435,149,4.213,150,2.435,151,2.435,152,2.435,153,1.156,154,2.144,155,2.532,156,2.662,157,1.305,158,1.129,159,1.506,160,0.792,161,1.538,162,0.794,163,0.896,164,1.074,165,1.158,166,2.26,167,1.736,168,1.883,169,1.236,170,0.668,171,0.614,172,1.354,173,1.209,174,1.162,175,1.236,176,1.84,177,1.091,178,1.768,179,1.729,180,2.26,181,1.28,182,1.308,183,1.678,184,1.708,185,1.555,186,1.065,187,1.573,188,1.634,189,1.209,190,1.161,191,2.827,192,1.331,193,0.822,194,1.736,195,1.989,196,0.896,197,1.007,198,1.1,199,1.115,200,1.119,201,1.15,202,2.691,203,0.936,204,4.208,205,2.058,206,1.463,207,0.922,208,2.058,209,2.058,210,2.058]],["t/4",[16,1.01,23,4.081,27,1.288,29,1.893,31,1.929,41,3.582,48,2.202,56,1.119,84,0.588,90,2.557,103,2.642,105,1.697,123,2.691,130,3.093,133,2.321,135,3.093,189,2.904,192,3.196,200,2.688,201,2.763,211,4.038,212,5.149,213,2.208,214,5.847,215,6.927,216,2.763,217,3.093,218,5.847,219,1.68,220,3.34,221,2.916,222,5.149,223,3.055,224,2.299,225,3.327,226,3.829,227,3.581,228,1.719,229,3.219,230,4.522,231,5.71,232,1.974,233,1.964,234,5.847,235,4.777,236,1.427,237,5.79,238,3.113,239,3.113,240,2.968,241,4.245,242,3.824,243,2.968,244,3.34,245,3.241,246,2.986,247,3.47,248,2.547,249,4.038,250,5.847,251,5.847,252,4.522,253,3.265,254,1.745,255,3.654,256,2.03,257,4.02,258,5.149,259,5.847,260,5.149,261,2.75,262,3.547,263,5.427,264,5.847,265,5.847,266,5.847,267,4.64,268,5.149,269,4.943,270,5.847,271,5.847,272,2.221,273,4.327]],["t/6",[6,5.396,16,1.833,23,3.289,24,1.9,25,2.389,26,4.095,27,2.339,28,1.556,29,2.604,56,0.737,90,3.517,115,3.436,125,4.254,162,2.622,196,2.958,229,4.426,274,6.219,275,2.31,276,3.697,277,3.836,278,2.674,279,4.257,280,4.747,281,3.531,282,4.059,283,2.158,284,5.026,285,3.083,286,4.524,287,5.326,288,4.925,289,2.949,290,6.219,291,4.396,292,4.286,293,5.951,294,4.015,295,3.001,296,2.876,297,4.081]],["t/8",[9,2.761,10,2.306,16,0.793,19,4.81,21,2.56,22,2.645,23,3.722,24,0.69,34,1.086,36,1.88,48,1.73,56,0.895,84,0.872,92,1.579,96,1.502,110,2.246,115,3.034,116,2.459,131,1.934,134,2.121,143,3.471,158,2.131,165,0.955,174,1.267,200,2.112,216,2.17,221,1.574,225,3.238,226,2.067,227,2.814,236,1.121,278,1.528,282,3.583,283,1.233,294,2.293,298,2.604,299,2.736,300,3.994,301,4.594,302,4.171,303,3.335,304,0.898,305,2.712,306,2.925,307,8.677,308,4.263,309,4.263,310,4.594,311,3.471,312,3.4,313,1.967,314,3.4,315,2.025,316,1.9,317,3.293,318,2.206,319,4.263,320,2.446,321,3.222,322,1.333,323,1.719,324,2.257,325,2.968,326,2.902,327,3.222,328,2.814,329,4.263,330,4.594,331,4.594,332,2.212,333,4.594,334,4.562,335,3.552,336,1.735,337,1.986,338,1.049,339,2.15,340,2.842,341,2.345,342,2.787,343,1.516,344,3.645,345,3.645,346,3.222,347,2.814,348,2.736,349,4.594,350,4.594,351,2.384,352,4.594,353,1.163,354,4.594,355,4.594,356,3.883,357,4.594,358,3.552,359,1.88,360,3.042,361,2.871,362,3.335,363,3.042,364,3.004,365,2.16,366,2.666,367,2.17,368,3.222,369,2.645,370,3.335,371,5.633,372,1.595,373,3.083,374,4.594,375,3.829,376,2.43,377,0.953,378,3.645,379,3.552,380,4.046,381,2.017,382,2.645,383,2.968,384,3.552,385,3.753,386,3.172,387,2.968]],["t/10",[1,3.301,2,1.532,16,1.326,19,1.574,21,0.998,23,1.896,24,1.626,26,2.108,28,0.498,29,1.232,33,2.013,34,1.814,56,1.064,84,0.615,85,2.468,96,1.244,97,1.579,98,1.241,105,1.105,106,2.991,126,1.705,133,2.43,134,1.757,135,2.013,138,1.424,153,1.807,158,4.079,162,1.241,164,1.56,165,0.791,175,1.932,217,2.013,219,1.093,220,2.174,224,1.496,225,1.262,254,1.826,256,2.125,272,1.446,277,2.919,278,1.265,280,2.247,296,1.361,302,1.626,304,1.197,315,1.678,316,1.574,317,1.765,324,1.87,332,2.948,340,3.786,341,1.942,343,2.02,347,2.331,351,2.055,353,1.549,359,3.599,365,2.878,378,3.02,388,2.554,389,3.532,390,1.705,391,1.487,392,3.524,393,1.734,394,2.839,395,1.396,396,6.384,397,1.468,398,1.244,399,2.669,400,2.763,401,3.651,402,6.996,403,3.645,404,2.765,405,1.124,406,3.806,407,3.22,408,3.993,409,1.79,410,2.331,411,3.806,412,4.226,413,4.293,414,2.026,415,2.489,416,1.626,417,3.806,418,1.765,419,3.806,420,3.217,421,2.209,422,3.866,423,3.806,424,3.806,425,3.806,426,1.304,427,3.806,428,3.806,429,3.02,430,2.801,431,6.12,432,2.227,433,2.039,434,2.354,435,3.806,436,2.52,437,2.666,438,4.236,439,2.308,440,1.557,441,1.921,442,2.943,443,2.489,444,1.531,445,2.227,446,2.247,447,2.687,448,3.217,449,3.496,450,5.173,451,2.943,452,2.431,453,2.266,454,3.217,455,2.876,456,3.217,457,3.806,458,1.989,459,2.763,460,3.217,461,1.977,462,3.217,463,2.125,464,3.02,465,2.459,466,2.489,467,2.763,468,2.247,469,3.532,470,2.554]],["t/12",[2,1.117,16,1.66,23,2.978,25,2.063,26,2.392,27,2.429,28,0.585,31,1.012,34,1.055,47,2.613,48,1.681,50,3.77,56,0.636,68,1.571,84,1.047,88,2.902,89,2.405,90,1.952,103,2.156,105,1.296,107,1.061,108,2.139,116,1.546,119,3.773,128,1.976,130,2.362,131,2.923,132,1.217,134,2.061,138,2.599,143,3.373,144,1.984,153,2.119,169,2.266,175,2.266,183,1.778,189,2.217,197,1.846,200,3.192,201,2.109,213,1.686,225,2.303,228,1.312,231,2.996,239,2.376,272,1.696,274,3.452,275,1.282,278,1.484,283,1.198,296,1.596,300,3.906,315,1.968,316,1.846,317,2.071,326,2.82,338,1.019,339,2.09,341,2.278,344,5.51,345,5.51,346,3.131,351,2.332,353,2.156,359,1.827,369,2.57,371,3.542,395,1.637,399,3.131,407,1.873,416,2.967,422,2.82,433,2.392,466,2.919,468,4.099,471,1.717,472,3.138,473,4.143,474,3.373,475,4.143,476,2.296,477,3.822,478,2.511,479,2.884,480,6.073,481,2.475,482,1.852,483,2.613,484,1.755,485,3.131,486,4.464,487,4.464,488,4.464,489,2.761,490,2.919,491,1.249,492,5.041,493,3.931,494,6.944,495,2,496,2.53,497,4.464,498,2.305,499,2.884,500,1.96,501,5.869,502,1.777,503,4.143,504,2.511,505,2.67,506,2.457,507,2.253,508,2.44,509,2.347,510,5.37,511,1.321,512,1.428,513,1.952,514,3.131,515,4.464,516,4.143,517,2.511,518,4.464,519,3.452,520,3.773,521,4.464,522,3.083,523,4.143,524,2.55,525,1.952,526,2.996]],["t/14",[16,1.822,19,2.936,21,1.205,23,3.82,24,1.467,26,1.583,27,1.012,28,1.136,29,1.487,31,1.042,36,1.88,47,2.689,50,2.494,56,0.795,84,0.872,88,1.92,97,1.906,101,2.269,103,1.426,130,2.43,134,2.121,138,1.719,143,3.471,144,2.041,157,4.649,163,1.69,164,1.81,165,0.955,190,2.191,197,1.9,198,2.076,207,1.74,212,4.046,213,3.277,219,1.32,224,1.806,225,1.524,231,3.083,232,2.929,236,1.121,244,2.624,245,2.547,256,1.595,272,2.697,275,1.32,284,4.437,299,2.736,305,4.191,306,3.576,315,2.025,316,2.936,323,1.719,343,1.516,365,2.16,422,4.485,430,3.25,441,2.319,446,2.712,468,2.712,492,3.335,500,2.017,523,4.263,527,1.761,528,3.471,529,3.883,530,4.38,531,2.584,532,3.436,533,4.594,534,3.042,535,3.883,536,2.398,537,4.263,538,3.552,539,2.736,540,3.645,541,2.624,542,3.552,543,2.4,544,4.121,545,3.045,546,3.552,547,4.594,548,2.624,549,4.594,550,1.675,551,4.594,552,2.332,553,2.257,554,3.883,555,2.017,556,4.263,557,2.121,558,3.601,559,3.471,560,3.507,561,3.753,562,2.094,563,3.883,564,2.761,565,1.392,566,2.968,567,3.229,568,1.221,569,3.645,570,6.001,571,3.172,572,2.257,573,4.263,574,3.883,575,2.025,576,2.511,577,2.017,578,1.675]],["t/16",[0,0.812,1,0.817,2,1.366,4,1.096,5,0.903,6,1.017,8,0.757,12,1.034,14,1.047,15,0.513,16,1.92,17,1.013,19,1.143,20,0.88,21,0.999,22,2.703,23,2.507,24,1.085,26,0.952,27,2.45,28,0.878,29,0.491,30,3.169,31,1.918,33,0.802,34,0.358,36,1.131,40,1.351,41,1.163,46,1.063,47,2.229,48,0.571,49,0.316,50,1.5,56,0.5,84,0.674,89,1.319,90,3.158,91,1.172,92,2.639,94,0.761,96,2.002,97,2.265,98,0.901,99,2.229,101,0.749,105,1.585,106,0.94,107,1.116,109,0.432,110,2.296,115,1.181,116,0.957,121,0.823,123,2.429,127,0.841,130,2.014,131,1.163,132,0.753,133,1.512,134,2.168,135,1.462,138,0.567,140,0.551,153,0.719,154,1.335,160,0.493,162,1.242,164,0.971,165,0.315,174,0.762,179,0.471,186,1.208,188,1.017,189,1.372,190,0.723,192,0.828,193,0.512,196,0.557,197,0.627,199,0.694,201,0.716,207,0.574,211,1.908,216,2.579,221,1.305,224,0.596,228,0.446,229,0.834,230,1.172,231,2.555,235,2.257,236,0.929,238,4.995,239,0.807,240,2.771,245,2.603,252,1.172,254,0.825,255,2.38,256,1.63,262,1.676,269,3.219,272,1.05,275,1.094,276,0.697,277,0.723,278,0.504,279,0.608,280,3.958,281,0.665,282,0.765,283,0.741,288,0.928,289,0.556,292,0.612,295,1.421,298,2.661,302,1.181,305,2.248,306,1.139,313,0.863,315,2.07,316,1.575,317,1.282,320,0.807,323,1.034,334,1.453,337,1.646,338,0.631,340,0.938,341,0.774,343,1.55,351,1.577,353,1.381,359,1.558,362,1.1,365,1.299,376,0.802,377,0.573,381,0.665,388,1.854,391,1.079,395,1.013,396,1.1,403,0.903,408,0.508,416,2.333,421,0.88,422,0.957,439,1.676,440,1.131,447,1.213,466,0.991,476,0.408,477,2.096,479,0.979,490,0.991,491,1.527,499,0.979,500,0.665,505,2.1,510,1.172,525,3.358,527,0.953,528,0.741,550,0.553,552,2.011,561,1.238,563,2.336,564,1.66,565,0.459,568,1.919,570,1.281,579,1.854,580,0.488,581,1.854,582,1.172,583,0.859,584,1.172,585,1.938,586,1.238,587,1.145,588,1.407,589,1.281,590,0.757,591,0.802,592,1.646,593,0.694,594,1.407,595,1.335,596,1.963,597,3.68,598,2.763,599,4.598,600,0.938,601,0.66,602,1.238,603,1.203,604,0.928,605,0.928,606,1.081,607,0.983,608,0.73,609,0.919,610,1.081,611,0.585,612,1.66,613,1.159,614,0.802,615,0.823,616,4.808,617,4.962,618,2.763,619,4.573,620,2.41,621,2.24,622,0.709,623,0.674,624,3.808,625,2.763,626,1.281,627,1.203,628,0.726,629,0.895,630,0.947,631,3.311,632,1.379,633,1.1,634,1.97,635,0.51,636,1.444,637,0.903,638,0.846,639,1.516,640,1.516,641,1.172,642,1.335,643,1.281,644,1.238,645,1.516,646,1.407,647,0.528,648,1.516,649,1.516,650,1.727,651,0.938,652,1.516,653,0.443,654,1.238,655,0.919,656,1.516,657,1.1,658,0.674,659,0.911,660,1.908,661,1.516,662,1.49,663,0.859,664,1.031,665,1.407,666,1.1,667,2.433,668,2.763,669,1.145,670,1.49,671,1.281,672,0.828,673,1.238,674,1.516,675,0.895,676,1.238,677,0.741,678,1.335,679,1.1,680,1.1,681,0.88,682,1.172,683,0.526,684,0.734,685,0.783,686,1.1,687,1.172,688,1.335,689,1.281,690,0.873,691,0.774,692,1.122,693,0.866,694,1.395,695,1.516,696,0.991,697,1.516,698,2.555,699,1.335,700,1.516,701,1.407,702,0.947,703,1.122,704,1.238,705,0.866,706,1.004,707,0.783,708,1.1,709,0.919,710,1.1,711,1.516,712,1.379,713,1.172,714,0.979,715,0.723,716,1.238,717,1.516,718,1.203,719,0.846,720,1.281,721,1.203,722,1.516,723,0.503,724,0.685,725,0.716,726,0.919,727,1.335,728,0.979,729,1.004,730,1.145,731,0.737,732,0.919,733,1.238,734,0.622,735,1.031,736,0.6]],["t/18",[2,0.695,4,0.799,5,0.908,6,3.165,8,1.387,9,1.669,12,1.765,13,3.424,15,0.94,16,1.401,21,1.9,22,1.599,23,3.197,24,1.551,25,0.825,26,1.625,28,0.949,29,1.527,31,0.63,39,1.049,46,1.068,49,0.984,56,1.163,84,0.946,85,1.976,92,2.112,98,0.906,100,1.153,106,0.945,108,1.331,109,0.962,113,0.94,115,2.015,116,0.962,121,2.561,132,0.757,133,1.872,134,1.283,135,1.469,138,2.712,155,2.834,162,1.538,164,0.708,165,0.98,174,0.766,182,0.862,185,1.774,201,1.312,202,1.774,219,1.355,224,1.092,225,0.921,231,1.864,235,2.269,238,1.479,239,1.479,248,1.21,254,0.829,272,2.335,276,1.277,285,2.357,288,2.888,289,2.976,294,1.387,298,1.574,299,1.654,304,0.543,315,1.224,316,1.148,317,1.288,318,0.628,332,1.338,336,1.049,338,1.077,339,2.208,343,2.029,351,1.584,353,1.193,365,2.218,367,1.312,377,1.276,383,1.795,389,2.578,395,1.73,397,1.071,408,1.58,416,1.187,436,1.839,437,1.21,440,1.137,441,2.38,443,1.816,444,1.117,446,1.64,458,1.451,468,2.784,472,1.255,477,1.529,483,1.626,489,1.718,491,0.777,500,1.219,502,0.711,505,1.814,506,4.856,508,1.518,509,1.46,511,0.822,512,0.889,517,1.562,525,1.215,527,0.957,528,1.358,536,1.303,557,1.283,560,3.036,565,1.429,568,1.633,592,1.654,596,1.971,599,1.325,605,1.584,621,1.325,630,0.952,654,2.269,664,1.89,673,2.269,683,0.964,690,1.599,691,2.407,694,1.402,706,1.839,710,3.424,737,1.948,738,2.269,739,2.777,740,1.165,741,1.669,742,1.612,743,2.888,744,2.495,745,2.777,746,2.777,747,1.797,748,1.948,749,1.669,750,3.084,751,2.527,752,2.099,753,2.204,754,2.099,755,1.971,756,1.21,757,1.078,758,5.603,759,2.357,760,0.762,761,2.446,762,1.736,763,1.754,764,1.736,765,1.529,766,2.777,767,1.981,768,2.348,769,1.682,770,1.981,771,2.578,772,1.701,773,3.424,774,1.394,775,2.446,776,7.98,777,2.777,778,1.626,779,4.548,780,1.372,781,2.348,782,4.716,783,2.777,784,3.209,785,4.716,786,2.777,787,3.564,788,2.777,789,2.777,790,1.795,791,2.777,792,2.777,793,2.777,794,1.795,795,1.685,796,1.351,797,2.777,798,3.137,799,1.948,800,5.704,801,1.918,802,1.043,803,1.451,804,2.204,805,1.754,806,2.016,807,0.996,808,2.777]],["t/20",[2,1.133,6,3.039,12,2.628,21,1.187,23,2.175,24,1.292,26,2.419,27,2.309,28,1.452,31,1.592,37,2.801,39,1.71,56,0.643,92,2.955,98,2.289,105,1.314,109,1.099,115,1.935,126,2.029,129,2.51,133,1.798,134,4.473,138,3.218,162,1.477,164,1.154,190,4.621,207,2.659,221,1.552,224,4.361,240,2.298,245,2.51,247,1.846,256,2.986,257,2.139,272,2.667,278,1.506,279,1.816,281,3.082,282,2.285,284,2.83,288,2.773,289,3.845,313,1.027,315,1.996,320,3.738,323,1.695,340,2.801,351,1.521,372,1.572,377,0.94,408,2.352,430,3.214,447,1.988,468,2.673,476,1.22,477,2.492,505,2.7,541,2.586,550,2.56,552,4.033,553,3.45,554,3.827,560,2.237,563,3.827,564,2.721,565,2.127,568,2.285,595,3.988,596,2.934,599,2.16,601,3.058,607,2.497,621,2.16,628,2.17,630,1.552,632,3.505,636,2.366,662,2.442,673,5.736,691,2.311,694,3.544,706,2.999,707,2.338,712,2.261,724,2.046,765,2.492,768,3.827,796,2.203,806,5.097,809,2.892,810,3.992,811,2.607,812,4.343,813,2.721,814,1.81,815,3.699,816,2.381,817,4.528,818,2.11,819,4.591,820,2.16,821,2.926,822,2.51]],["t/22",[7,0.57,12,2.917,16,1.346,18,3.178,19,3.223,21,2.044,27,2.578,30,4.524,31,1.767,56,0.714,84,0.783,92,2.678,105,3.018,123,2.465,126,4.659,172,2.505,201,3.682,225,2.585,226,3.507,229,4.29,238,4.149,239,4.149,353,2.632,407,3.269,408,2.611,525,4.547,552,2.56,564,4.684,565,2.361,568,2.071,597,3.735,611,3.007,623,3.463,631,4.728,664,5.303,712,3.891,734,3.2,758,6.027,819,6.801,823,5.89,824,5.303,825,5.558,826,6.027,827,3.665,828,3.016,829,3.81,830,4.41,831,4.562,832,4.871]],["t/24",[7,0.679,18,4.748,19,3.838,27,2.044,29,3.005,31,2.104,56,0.85,123,2.935,181,4.879,200,4.267,221,3.991,225,4.22,226,4.176,235,7.583,351,3.117,395,3.404,505,3.569,553,5.722,621,4.427,809,5.928,833,4.91,834,9.281,835,9.281,836,9.281,837,5.221,838,4.344]],["t/26",[7,0.89,16,1.787,17,1.614,18,4.471,19,3.945,21,1.154,23,1.364,24,1.032,26,2.366,27,1.86,28,0.576,31,1.915,56,1.005,84,0.959,103,1.367,123,2.172,144,1.956,156,2.781,165,0.915,174,2.329,179,1.367,181,3.611,213,1.662,216,3.245,217,2.329,219,1.264,220,2.514,221,2.354,225,3.165,226,1.981,227,2.696,230,3.404,232,1.486,236,1.676,247,1.795,248,1.917,276,2.024,278,2.284,313,1.915,318,0.995,323,1.648,324,2.163,327,3.087,351,2.836,353,1.114,359,1.801,365,2.07,369,3.954,370,3.195,375,3.704,377,0.914,387,4.438,390,1.972,391,1.72,408,1.475,430,2.015,440,1.801,447,1.933,458,3.589,463,4.716,471,1.693,524,2.514,525,1.925,527,0.893,530,1.827,545,1.888,555,3.015,557,2.033,562,2.006,565,2.081,584,3.404,585,3.087,608,2.12,611,2.65,638,2.458,643,3.721,684,2.131,692,3.258,693,2.514,709,2.67,726,2.67,727,3.877,802,2.578,818,2.051,833,2.329,839,4.402,840,1.853,841,4.402,842,4.085,843,2.234,844,3.326,845,2.599,846,1.254,847,3.258,848,3.721,849,4.085,850,2.723,851,3.404,852,3.721,853,3.493,854,2.995,855,2.514,856,2.576,857,1.853,858,2.015,859,2.12,860,3.493,861,4.402,862,4.402,863,4.402,864,4.402,865,2.751,866,3.493,867,3.139,868,3.721,869,4.402,870,3.195,871,2.995,872,2.645,873,2.812,874,3.721,875,3.326,876,2.576]],["t/28",[2,1.513,5,1.226,16,1.312,17,1.375,18,4.564,21,1.586,23,2.701,26,1.292,27,1.921,28,0.792,29,1.958,31,2.438,34,0.886,56,1.025,84,1.081,88,2.527,89,3.312,96,1.977,99,2.194,101,1.852,103,1.164,105,1.088,116,1.298,123,3.401,130,1.983,132,1.648,133,1.488,144,1.666,157,2.009,160,1.22,164,1.542,165,0.779,169,1.903,174,1.034,196,2.224,197,1.55,213,1.416,216,1.771,219,1.077,225,2.006,226,1.687,227,2.296,228,1.102,232,2.041,236,1.476,248,1.633,278,1.247,280,2.213,283,1.006,298,4.307,305,2.213,306,3.593,313,1.723,316,1.55,317,1.739,320,3.219,334,3.179,338,0.856,348,3.601,351,1.259,353,1.53,359,1.534,364,2.452,376,1.983,377,0.778,382,2.159,391,2.969,408,1.256,440,1.534,447,1.646,463,4.243,474,2.833,476,1.01,491,1.049,512,1.2,530,3.154,536,1.036,545,2.594,558,1.556,567,1.395,568,2.317,575,1.653,578,1.367,581,2.516,584,2.899,585,2.63,587,2.833,592,2.233,605,1.259,607,1.334,611,1.446,630,1.285,655,2.274,683,2.639,694,3.052,709,3.668,726,2.274,742,2.176,816,1.971,818,2.818,820,1.788,828,1.451,857,1.578,875,2.833,877,3.169,878,2.899,879,2.775,880,2.296,881,4.411,882,2.775,883,3.063,884,3.479,885,2.589,886,3.302,887,2.319,888,1.833,889,5.111,890,2.775,891,3.063,892,2.1,893,3.749,894,2.63,895,3.169,896,2.551,897,2.194,898,3.035,899,2.775,900,3.063,901,1.709,902,2.674,903,3.749,904,2.296,905,3.479,906,2.253,907,2.551,908,1.771,909,2.899,910,3.16,911,2.674,912,2.899,913,2.589,914,3.539,915,3.302]],["t/30",[2,2.027,4,1.574,16,1.842,18,5.034,20,3.174,23,2.51,24,1.793,26,1.884,27,1.784,28,0.716,29,1.77,40,2.673,46,4.381,56,1.24,84,0.814,85,3.102,90,2.391,96,1.787,97,3.361,109,1.268,127,2.465,133,2.171,135,2.893,160,1.779,164,2.46,186,2.391,197,2.261,198,2.471,203,1.215,213,2.065,219,1.571,228,1.607,229,3.01,236,1.977,272,2.077,275,1.571,285,2.096,313,1.836,316,2.261,320,2.911,395,2.005,421,4.702,471,3.115,484,2.15,491,1.53,502,1.399,530,2.269,550,2.954,555,2.401,610,3.899,914,3.2,916,1.934,917,4.259,918,3.256,919,3.899,920,4.046,921,6.298,922,5.468,923,4.741,924,5.468,925,5.468,926,4.815,927,3.349,928,5.075,929,5.468,930,7.518,931,5.075,932,5.075,933,2.875,934,2.503,935,4.622,936,3.417,937,3.031,938,3.123,939,2.893]],["t/32",[2,1.844,12,1.805,16,1.272,17,1.768,18,4.399,21,1.264,24,1.709,27,1.062,28,1.413,29,1.561,34,1.14,56,1.189,84,0.485,89,1.67,103,1.497,132,1.314,158,2.236,163,1.773,169,3.74,196,2.71,197,1.994,203,1.071,221,1.653,223,3.85,224,1.896,228,2.944,246,1.692,247,1.966,254,2.199,285,4.136,286,2.712,313,1.671,334,2.535,353,1.22,377,1.529,395,1.768,426,2.525,437,3.21,463,2.692,471,1.854,479,3.115,484,1.896,491,2.061,502,1.234,527,1.815,560,2.382,576,2.636,601,2.1,683,1.674,685,2.49,747,3.408,759,2.397,760,2.452,827,3.465,831,2.822,838,2.257,857,2.03,892,2.559,901,2.197,917,2.535,927,2.953,928,4.475,930,4.475,931,4.475,932,4.475,939,2.551,940,3.644,941,6.618,942,5.014,943,4.822,944,1.81,945,1.48,946,3.826,947,3.115,948,4.822,949,7.369,950,4.822,951,4.822,952,8.943,953,4.822,954,7.369,955,4.822,956,4.822,957,2.6,958,4.822,959,4.822,960,4.822,961,4.822,962,4.822,963,2.712,964,4.822,965,4.822,966,1.22,967,4.822,968,4.475,969,4.822,970,3.826,971,3.281,972,4.822,973,4.822]],["t/34",[0,1.772,2,1.366,5,1.081,12,1.238,16,0.943,17,2.002,18,2.226,21,1.828,24,1.532,25,1.622,26,1.881,27,1.203,28,1.262,29,1.768,31,1.238,34,0.782,40,1.617,41,1.393,56,1.113,84,0.333,88,2.282,89,2.414,96,1.081,98,1.079,105,1.585,107,2.705,109,2.044,113,1.119,123,1.727,127,2.122,132,1.9,134,1.527,140,1.203,155,1.988,164,0.843,165,0.688,170,2.218,174,2.232,192,2.984,196,1.217,197,1.368,200,1.521,203,0.735,213,1.249,225,1.097,238,1.761,246,1.161,272,2.648,278,1.1,279,1.326,285,2.093,295,1.234,297,2.771,304,1.068,313,1.238,316,2.258,338,0.755,343,1.092,359,1.354,377,2.117,379,4.222,381,1.452,395,1.213,397,2.106,409,1.556,416,1.413,426,1.134,430,1.514,439,2.006,471,1.272,476,1.878,498,1.708,507,1.67,512,2.23,517,1.861,527,0.671,550,3.719,552,1.087,555,1.452,568,1.451,575,2.407,591,1.75,597,1.585,605,1.111,606,2.359,607,1.177,613,1.388,630,1.134,655,2.006,665,3.07,683,2.809,712,1.651,735,2.251,736,1.309,747,1.26,756,3.036,814,2.786,818,1.541,829,1.617,846,1.555,856,3.195,882,2.448,897,1.936,917,4.708,920,2.448,923,1.936,936,2.067,944,1.242,966,0.837,974,1.875,975,1.97,976,2.284,977,1.119,978,2.702,979,2.046,980,2.558,981,2.067,982,1.548,983,2.359,984,1.313,985,2.026,986,5.459,987,2.284,988,2.477,989,1.728,990,3.308,991,3.07,992,3.07,993,1.242,994,1.322,995,1.708,996,3.308,997,2.913,998,2.913,999,2.913,1000,2.796,1001,2.913,1002,3.308,1003,4.808,1004,3.308,1005,3.312,1006,4.615,1007,1.988,1008,1.834,1009,2.09,1010,2.046,1011,3.308,1012,2.448,1013,1.92,1014,3.07,1015,1.821,1016,2.702,1017,2.191,1018,3.308,1019,1.128]],["t/36",[2,1.881,12,2.813,16,1.752,18,3.065,21,1.971,23,2.328,24,1.129,25,3.818,27,2.236,28,1.611,56,1.053,84,1.02,101,3.713,109,1.176,132,3.132,144,3.34,164,1.916,236,1.834,306,3.097,313,1.704,476,3.095,558,3.119,580,2.422,660,5.19,685,3.881,916,3.59,917,3.951,989,3.927,997,6.619,1013,5.89,1020,6.619,1021,4.748,1022,3.836,1023,7.516,1024,7.516,1025,7.516,1026,7.516,1027,4.437,1028,6.619,1029,6.975,1030,6.975,1031,7.516,1032,7.516,1033,7.516,1034,7.516]],["t/38",[2,1.664,16,1.61,18,2.71,23,2.059,24,0.999,25,1.975,27,1.464,28,1.411,56,1.2,96,2.173,132,2.542,163,2.445,165,1.382,182,2.064,196,2.445,203,1.476,213,2.51,219,1.909,306,2.739,313,2.114,351,2.232,376,3.516,394,3.083,426,3.196,476,1.79,491,1.859,495,2.978,500,2.918,502,1.701,545,2.851,550,2.424,562,3.029,577,4.729,580,2.142,757,2.67,778,3.89,846,1.894,857,3.926,892,2.308,895,5.618,917,3.494,923,3.89,934,3.042,957,3.585,984,3.702,1000,5.618,1022,4.76,1035,3.685,1036,4.662,1037,3.069,1038,4.199,1039,4.071,1040,3.767,1041,6.646,1042,6.646,1043,6.646,1044,6.646,1045,6.646,1046,6.646,1047,6.646,1048,6.646,1049,6.646,1050,6.646,1051,2.991,1052,5.274]],["t/40",[2,2.651,16,1.113,18,2.628,23,1.996,24,1.592,26,2.22,27,1.419,28,1.387,48,2.426,56,1.189,85,2.071,98,2.101,107,2.516,109,1.902,116,2.231,127,1.961,132,2.487,164,1.643,165,1.897,170,2.501,196,2.37,236,1.572,313,2.068,332,3.103,377,1.337,381,2.829,394,2.989,397,2.486,405,1.902,426,2.208,525,2.818,528,3.15,550,2.35,553,3.166,560,3.183,568,1.712,605,2.164,644,5.264,724,2.911,756,3.974,781,5.446,856,3.771,916,2.279,923,3.771,946,5.113,980,4.983,981,4.027,989,3.367,991,5.98,992,5.98,997,5.674,1005,3.908,1006,5.446,1053,5.446,1054,6.443,1055,4.324,1056,6.443,1057,9.123,1058,6.443,1059,6.443,1060,6.443,1061,5.446,1062,2.875,1063,5.264,1064,6.443,1065,5.446,1066,5.446]],["t/42",[2,1.839,12,1.336,16,0.616,18,2.997,21,1.927,23,2.277,24,0.873,27,2.054,28,1.109,29,1.156,31,1.666,37,2.208,56,1.007,68,1.256,84,0.851,85,1.147,89,2.012,103,1.108,107,0.848,109,1.15,112,1.641,116,2.012,125,1.888,126,1.599,127,1.768,134,1.648,138,2.751,140,2.113,144,1.586,153,1.694,160,1.89,162,1.164,163,1.313,164,0.91,165,1.208,169,1.812,171,0.9,172,2.723,173,5.641,174,1.603,183,1.422,190,1.702,193,2.481,221,1.223,224,2.285,228,1.049,229,1.965,236,2.067,275,1.025,276,1.641,281,1.567,282,3.709,283,0.958,289,2.695,291,1.951,313,0.809,316,2.403,322,1.036,337,1.543,343,1.178,359,1.461,362,2.591,377,2.075,383,2.306,385,4.747,404,1.613,408,2.838,409,1.678,430,1.634,433,1.912,440,1.461,441,1.801,444,1.436,445,2.089,477,1.965,490,2.334,491,1.625,502,0.913,511,1.056,525,1.561,527,0.724,541,2.039,552,3.731,553,1.754,564,2.145,568,1.544,592,2.126,601,2.531,607,1.27,608,1.719,614,1.256,631,2.165,632,2.901,638,3.245,655,2.165,725,1.686,742,2.072,743,2.186,768,3.017,806,4.218,807,1.28,810,2.394,819,2.334,820,1.702,828,1.381,872,2.145,892,1.239,923,2.089,934,1.634,977,2.487,998,3.143,999,3.143,1000,3.017,1001,3.143,1003,3.143,1012,2.641,1013,2.072,1015,1.965,1039,2.186,1040,2.023,1052,2.832,1067,3.569,1068,3.569,1069,3.569,1070,1.888,1071,2.916,1072,2.492,1073,2.364,1074,2.395,1075,3.575,1076,1.942,1077,3.018,1078,2.395,1079,2.072,1080,1.843,1081,3.712,1082,4.076,1083,1.606,1084,3.569,1085,3.569,1086,3.017,1087,2.545,1088,3.143,1089,2.023,1090,1.471,1091,3.569,1092,3.569,1093,3.055,1094,1.656,1095,3.312,1096,3.017,1097,2.545]],["t/44",[2,1.598,6,2.692,12,1.501,18,3.242,21,1.052,24,1.195,26,1.382,27,2.527,28,1.38,31,1.447,46,1.543,50,2.178,56,1.083,84,0.799,90,2.791,98,1.308,105,1.853,106,2.704,107,2.725,109,1.969,115,4.227,120,3.722,123,1.268,125,2.122,132,1.093,157,2.149,163,2.348,164,1.023,170,1.1,171,1.011,174,2.193,175,2.036,181,2.109,203,0.891,213,1.515,216,1.895,229,2.208,239,2.135,244,2.291,254,3.144,272,1.524,279,1.608,281,1.761,295,1.497,313,1.447,318,0.906,320,2.135,322,1.853,334,2.109,337,1.734,343,1.324,353,2.012,358,3.102,359,3.253,362,2.912,372,1.393,377,1.325,426,1.375,437,1.747,444,1.614,472,1.812,489,2.481,502,1.026,507,3.222,513,1.754,527,1.295,544,2.328,550,2.328,572,1.971,585,2.813,586,3.277,611,1.547,614,0.685,635,1.35,658,4.396,683,1.393,694,2.024,734,1.647,755,1.676,757,0.917,796,1.951,838,1.877,910,2.096,916,1.419,917,2.109,923,2.348,945,1.96,975,2.389,979,2.481,984,1.592,998,3.532,999,3.532,1000,3.39,1001,3.532,1003,3.532,1062,1.79,1095,3.722,1098,2.348,1099,4.061,1100,2.348,1101,5.841,1102,3.538,1103,3.277,1104,4.011,1105,2.912,1106,3.532,1107,2.813,1108,1.904,1109,3.183,1110,2.813,1111,2.592,1112,3.513,1113,2.656,1114,3.277,1115,3.031,1116,4.011,1117,2.481]],["t/46",[2,1.879,12,1.58,16,0.963,17,0.895,18,4.136,21,1.743,23,0.756,24,1.401,26,1.454,27,0.93,28,0.319,29,0.79,31,0.553,34,0.577,46,3.391,47,1.428,49,0.509,56,0.808,84,0.886,85,1.357,90,2.906,92,3.946,93,4.119,94,2.118,96,1.823,97,3.659,101,2.085,103,0.758,105,0.708,106,1.435,107,1.003,109,0.66,113,0.826,115,1.803,127,1.284,128,1.868,132,0.665,138,1.58,140,0.887,144,1.084,145,0.91,157,1.307,164,0.622,167,1.74,172,2.136,174,1.164,181,1.283,186,1.067,190,1.164,193,0.824,194,1.74,207,1.599,228,0.717,229,2.323,236,0.595,240,1.238,254,0.728,272,0.927,275,0.701,279,0.978,283,1.132,295,0.91,304,0.477,306,1.005,313,1.702,315,1.075,323,2.087,324,2.741,326,1.541,332,1.175,334,1.283,338,0.963,343,0.805,348,1.453,353,1.068,359,1.727,362,1.771,369,1.405,372,0.847,376,1.291,377,0.876,381,1.853,390,1.891,393,1.112,405,3.258,408,1.414,421,1.416,430,1.932,440,0.998,441,1.231,447,1.071,476,1.502,482,1.012,489,1.509,491,1.859,505,0.938,506,1.343,509,2.219,511,1.65,525,1.067,527,0.495,552,1.832,562,1.112,565,0.739,568,1.766,572,2.074,579,1.637,580,0.786,593,1.117,596,1.764,597,3.185,599,4.656,611,0.941,614,0.417,615,1.325,619,1.453,620,2.166,630,1.446,633,1.771,653,1.629,658,1.084,659,1.466,660,1.685,683,0.847,687,1.887,694,2.13,707,2.179,710,1.771,714,1.576,715,1.164,716,1.993,734,1.002,742,1.416,758,1.887,769,0.87,773,1.771,795,1.48,796,2.053,798,1.245,810,1.005,838,1.142,858,1.117,882,1.805,912,1.887,919,1.74,966,2.852,977,0.826,984,1.675,987,1.685,1008,1.352,1035,1.352,1070,1.291,1075,1.187,1082,2.96,1090,1.005,1099,1.916,1118,0.956,1119,1.771,1120,2.149,1121,1.372,1122,3.448,1123,4.819,1124,7.452,1125,2.44,1126,2.44,1127,2.44,1128,2.44,1129,1.844,1130,2.44,1131,2.44,1132,2.298,1133,4.425,1134,2.44,1135,2.44,1136,2.44,1137,2.44,1138,1.44,1139,2.44,1140,2.44,1141,2.795,1142,2.585,1143,2.149,1144,2.13,1145,1.153,1146,2.44,1147,2.44,1148,2.44,1149,2.062,1150,2.44,1151,3.917,1152,2.44,1153,2.44,1154,2.44,1155,1.805,1156,1.993,1157,1.711,1158,2.264,1159,1.805,1160,1.844,1161,1.805,1162,1.26,1163,1.525,1164,2.44,1165,2.44,1166,2.44,1167,1.993,1168,2.44,1169,1.887,1170,2.149,1171,2.264,1172,1.362,1173,2.44,1174,1.525,1175,1.043,1176,2.264,1177,1.685]],["t/48",[0,4.313,12,3.976,18,4.849,21,1.422,27,2.938,28,1.486,30,5.572,56,0.973,92,1.863,96,1.772,101,2.678,107,1.288,109,1.662,127,2.449,144,2.409,165,1.127,172,3.086,193,1.83,197,2.242,207,2.053,223,2.833,224,2.131,246,1.902,272,2.06,275,1.557,282,2.736,283,1.455,289,1.988,291,2.963,296,1.939,304,1.06,334,2.85,336,3.039,337,2.343,338,1.238,351,1.821,359,2.218,383,3.503,416,2.316,430,2.481,437,2.361,444,2.181,511,1.604,525,4.198,552,3.906,568,1.441,596,2.266,597,4.601,599,4.579,601,2.361,605,1.821,631,5.823,632,2.706,706,5.33,712,2.706,725,2.561,741,4.837,747,2.066,769,1.933,807,1.944,810,2.234,812,3.353,827,2.549,828,2.098,995,2.799,1074,3.638,1075,3.915,1079,3.147,1093,2.85,1097,3.866,1144,2.736,1178,2.905,1179,3.353,1180,3.503,1181,2.943,1182,2.399,1183,3.463]],["t/50",[1,1.925,4,1.027,7,0.538,9,2.145,10,2.917,16,1.003,17,2.695,18,5.199,19,2.403,21,1.927,24,0.873,25,1.061,26,2.532,28,1.109,56,0.916,84,0.359,88,1.492,89,3.462,101,1.763,103,1.108,105,2.133,110,1.745,116,2.012,134,1.648,144,2.582,156,2.255,160,1.161,164,0.91,169,1.812,174,2.757,181,3.055,183,1.422,186,1.561,189,1.772,199,1.634,211,2.465,213,1.348,221,1.223,225,2.438,226,1.606,227,3.559,253,1.993,274,2.76,275,1.669,276,1.641,278,1.187,282,4.275,283,2.273,305,2.107,315,1.573,316,1.476,317,2.695,323,1.336,324,1.754,338,0.815,340,2.208,341,1.822,351,1.952,353,0.903,359,3.007,360,2.364,362,4.218,366,2.072,375,3.964,387,2.306,390,1.599,391,1.394,437,1.555,476,0.962,481,1.979,491,0.998,511,1.056,512,1.142,522,2.465,527,1.179,530,1.481,575,1.573,605,1.199,611,1.377,621,1.702,628,3.522,632,1.782,633,2.591,669,2.697,677,1.745,683,3.238,684,3.557,685,3.795,687,2.76,721,2.832,727,3.143,733,2.916,742,2.072,747,1.36,757,0.816,765,1.965,795,2.165,796,1.736,802,1.34,816,1.876,820,2.772,825,2.545,858,1.634,859,2.799,873,2.28,892,2.552,896,2.429,897,2.089,914,6.648,933,1.876,1007,2.145,1009,2.255,1015,1.965,1022,2.966,1089,2.023,1090,1.471,1106,3.143,1184,3.143,1185,2.697,1186,2.545,1187,3.569,1188,2.107,1189,2.832,1190,3.569,1191,3.017,1192,2.055,1193,4.749,1194,3.312,1195,2.255,1196,2.364,1197,3.671,1198,2.429,1199,3.632,1200,2.76,1201,3.017,1202,2.039,1203,3.312,1204,3.569,1205,2.832,1206,1.763,1207,2.395,1208,2.697,1209,2.832,1210,2.591,1211,3.312,1212,2.832,1213,3.899,1214,2.832,1215,3.312,1216,2.641,1217,2.503,1218,2.165,1219,1.476,1220,1.9]],["t/52",[2,2.189,3,8.132,4,1.748,5,1.985,7,0.444,9,3.649,10,5.144,11,6.471,13,4.408,14,4.193,16,1.51,21,2.687,22,5.035,24,1.786,27,2.258,30,3.524,32,5.076,36,2.485,38,5.635,39,2.293,40,2.969,43,5.635,44,4.818,45,5.635,47,3.554,50,3.297,56,0.556,84,0.61,87,8.115,90,2.655,92,3.005,95,3.496,96,1.985,97,2.52,100,2.52,101,2.999,106,3.486,107,1.443,109,0.95,143,4.589,147,5.635,175,3.082,187,3.923,188,4.075,189,3.015,191,5.868,197,2.511,242,3.971,277,2.896,293,4.493,317,2.816,353,1.537,366,3.524,386,4.193,400,4.408,408,2.034,416,2.595,421,3.524,440,2.485,484,2.387,489,5.409,490,3.971,500,2.666,502,1.554,599,2.896,601,2.645,712,3.031,796,2.954,816,3.192,978,4.961,1174,3.795,1206,2.999,1221,3.923,1222,4.33,1223,5.132,1224,2.07,1225,4.193,1226,6.072,1227,5.132,1228,6.072]],["t/54",[4,2.239,9,4.673,12,1.938,18,4.539,23,3.217,24,1.169,50,2.811,56,1.02,84,0.782,106,1.761,107,1.23,109,0.81,131,3.274,138,2.911,140,1.883,145,1.932,164,1.32,165,1.076,170,1.419,172,1.664,173,2.571,179,1.608,181,4.088,186,3.401,197,3.216,207,1.961,213,1.955,219,1.487,224,3.672,225,1.717,227,3.171,228,1.522,232,2.625,283,2.786,284,5.837,292,2.09,305,4.591,313,2.118,316,2.141,338,1.182,344,4.108,345,4.108,346,3.631,369,2.981,371,4.108,390,2.32,405,1.528,407,3.262,440,2.118,441,2.613,468,3.056,476,1.395,480,3.27,482,2.148,500,2.273,512,1.656,527,1.578,545,4.006,546,4.003,558,2.148,559,3.912,560,2.557,562,2.359,568,2.067,575,3.428,576,2.83,601,2.255,605,1.739,610,3.692,677,2.531,705,2.957,733,4.23,795,3.14,796,2.518,807,1.856,810,2.133,811,2.981,858,3.56,904,4.763,908,2.446,914,3.03,944,1.943,1105,3.758,1110,3.631,1121,2.912,1178,2.774,1203,4.804,1213,3.474,1222,3.692,1229,3.758,1230,3.631,1231,4.003,1232,5.177,1233,3.912,1234,5.177,1235,3.523,1236,3.202,1237,3.111,1238,3.692,1239,4.108]],["t/56",[18,4.837,56,1.087,105,2.776,194,6.819,246,3.355,247,3.899,296,3.42,318,2.16,323,3.579,359,3.913,377,2.462,383,6.178,578,3.487,623,4.249,628,4.583,794,6.178,1100,3.517,1108,4.539,1172,5.339,1221,6.178,1240,9.562,1241,7.587,1242,8.874,1243,6.819]],["t/58",[4,1.934,7,0.491,16,1.871,18,5.035,20,3.899,28,0.88,31,2.66,84,0.675,89,2.326,105,1.95,123,2.971,157,3.599,164,1.712,174,2.591,177,3.01,179,2.917,181,3.531,201,4.438,211,4.638,213,2.537,216,3.174,225,2.228,226,3.022,227,4.114,228,1.975,236,1.639,244,3.837,245,3.724,262,4.075,275,1.93,298,3.807,300,3.779,306,2.768,315,2.961,318,1.518,320,3.576,323,2.514,340,4.155,348,4,353,2.742,367,3.174,430,3.075,511,1.987,527,1.363,555,2.949,568,1.785,601,2.926,630,2.302,672,3.672,683,3.262,693,3.837,696,4.393,709,4.075,878,5.194,914,3.931,1120,5.916,1170,5.916,1244,5.194,1245,5.076,1246,3.965,1247,5.916,1248,6.717,1249,5.488,1250,6.717,1251,4.449,1252,5.916,1253,3.724]],["t/60",[2,1.806,7,0.885,16,1.705,17,2.646,18,2.942,31,2.238,89,2.499,93,2.837,123,3.122,124,4.223,160,3.66,165,1.5,174,1.99,192,3.944,232,2.435,233,2.423,236,1.761,278,2.399,315,3.181,318,1.63,323,2.701,444,2.903,492,5.238,519,5.58,536,1.994,567,2.685,568,1.918,596,3.016,620,3.704,622,3.377,677,5.501,684,3.492,694,3.642,829,3.528,865,4.51,875,5.453,1072,3.095,1090,4.067,1206,3.564,1220,3.841,1233,5.453,1239,5.725,1253,4,1254,4.719,1255,5.58,1256,6.354,1257,5.238,1258,5.725,1259,7.215,1260,7.215,1261,7.215,1262,3.704,1263,6.923,1264,4.377,1265,3.51,1266,2.903,1267,5.061,1268,4.719]],["t/62",[7,0.889,8,2.022,9,3.865,16,1.111,18,2.623,19,3.308,21,1.687,24,0.966,26,2.216,27,0.892,28,1.193,29,2.082,31,2.399,33,2.142,56,0.371,88,1.692,89,2.227,92,2.21,105,1.175,109,0.634,110,1.98,116,1.402,123,3.346,124,2.37,125,2.142,132,1.104,135,2.142,155,2.434,157,2.17,160,2.092,170,2.194,174,1.774,175,2.055,181,2.129,185,2.587,186,1.771,189,2.011,215,2.796,216,1.913,225,1.343,226,1.822,227,2.48,253,2.261,275,1.163,282,2.044,283,1.087,285,1.553,291,2.214,295,1.511,298,2.295,306,1.669,311,3.06,316,1.674,317,1.878,326,2.558,329,3.758,338,0.924,353,1.025,359,1.657,377,0.84,383,2.617,390,1.814,391,2.513,395,1.485,461,2.103,471,3.077,498,2.091,512,1.296,526,4.316,527,1.305,536,1.119,552,2.628,565,1.227,566,2.617,575,1.785,578,2.917,596,1.692,597,1.941,599,1.931,607,1.44,619,2.412,622,1.895,623,1.8,677,3.144,684,3.113,693,2.313,715,1.931,726,2.456,736,1.602,741,2.434,759,1.317,763,2.558,798,2.067,802,1.52,809,2.587,829,1.98,830,1.717,833,2.142,840,2.708,865,5.695,885,2.796,896,2.756,897,2.37,901,1.845,914,2.37,920,2.997,977,1.37,994,1.618,1072,1.737,1090,1.669,1178,2.17,1216,4.759,1217,2.84,1233,3.06,1254,2.648,1255,3.131,1257,2.94,1262,3.302,1263,2.84,1269,3.423,1270,5.103,1271,1.87,1272,5.705,1273,4.049,1274,2.997,1275,3.131,1276,4.049,1277,4.049,1278,4.049,1279,4.049,1280,4.049,1281,4.049,1282,4.86,1283,4.759,1284,2.558,1285,1.602,1286,2.94,1287,2.756,1288,2.245,1289,2.39,1290,2.214,1291,2.888,1292,2.531,1293,4.049,1294,4.049,1295,4.049,1296,3.213,1297,1.822]],["t/64",[7,0.754,16,1.059,21,1.608,28,1.349,49,2.149,56,0.562,84,0.885,85,1.971,89,2.123,109,0.959,132,2.809,144,2.725,162,1.999,165,1.275,172,1.971,174,1.691,177,2.747,179,1.904,197,2.535,198,2.77,206,3.684,213,2.315,236,2.149,249,4.234,253,3.423,279,3.531,298,3.475,318,1.99,353,2.228,359,2.509,394,2.844,395,2.248,458,3.204,484,2.41,527,1.244,568,1.629,595,5.399,608,2.953,611,2.365,614,1.047,622,2.87,630,2.101,659,3.684,709,3.719,712,3.061,790,3.961,799,4.3,802,4.478,814,3.519,824,4.172,865,6.44,873,3.916,1037,2.831,1170,5.399,1183,3.916,1193,3.961,1216,4.537,1218,3.719,1255,4.741,1262,6.374,1288,5.712,1298,7.443,1299,3.961,1300,3.077,1301,6.131,1302,3.449,1303,3.651,1304,4.865,1305,5.182,1306,4.537,1307,3.961,1308,5.69,1309,3.306]],["t/66",[7,0.84,160,2.954,177,4.069,189,4.51,257,4.29,302,3.88,313,2.059,320,4.834,334,4.774,353,2.298,471,3.493,684,4.395,802,4.311,920,6.72,994,3.629,1171,8.428,1245,6.863,1262,4.662,1310,8.657,1311,9.081,1312,5.801,1313,4.898,1314,7.255,1315,9.383,1316,9.383,1317,7.676]],["t/68",[0,2.07,2,1.941,7,0.876,16,1.88,18,1.576,19,2.562,21,1.013,23,1.919,24,1.459,26,1.331,27,0.851,28,1.529,31,1.758,34,1.464,48,1.455,56,0.568,84,1.094,89,1.338,93,1.519,99,2.261,103,1.2,107,1.472,109,0.969,123,1.959,129,2.142,132,2.114,145,2.894,158,1.792,164,0.985,165,0.803,167,2.755,170,1.059,174,2.139,175,3.144,181,2.031,183,1.539,193,1.304,198,1.746,203,1.376,213,1.459,217,2.044,219,1.11,228,1.136,229,2.127,232,3.82,236,0.943,262,3.758,275,1.11,276,1.776,279,1.549,283,1.037,285,1.481,294,3.093,295,1.442,306,2.553,313,1.758,318,2.005,323,1.446,338,0.882,359,2.535,377,0.802,381,1.696,390,2.776,405,1.141,430,1.769,471,1.486,491,1.081,495,1.731,500,1.696,511,1.143,513,1.69,527,1.257,528,1.889,530,1.603,536,1.712,545,1.657,548,2.207,550,1.409,552,2.035,558,2.571,562,1.761,567,2.305,593,1.769,635,1.301,663,2.19,666,2.805,677,1.889,682,2.988,683,1.342,685,1.995,698,4.157,702,2.415,757,0.884,759,1.257,769,1.378,775,3.403,780,1.909,796,1.879,802,1.45,814,1.544,833,3.277,843,1.961,865,2.415,892,1.342,894,4.345,914,5.193,919,2.755,984,1.534,1037,1.784,1118,1.514,1145,1.825,1182,1.71,1202,2.207,1262,1.983,1263,2.71,1266,4.818,1270,3.066,1289,2.281,1318,2.988,1319,2.441,1320,2.755,1321,3.864,1322,2.629,1323,2.468,1324,4.497,1325,2.629,1326,3.266,1327,3.864,1328,1.87,1329,2.647,1330,2.344]],["t/70",[0,2.392,2,1.117,7,0.623,19,1.846,27,0.983,28,0.585,49,0.931,56,1.291,68,1.571,84,0.698,85,2.232,105,1.296,109,1.087,112,2.052,123,1.412,131,1.879,132,1.217,138,1.671,160,1.452,164,1.138,172,3.091,173,2.217,213,1.686,217,2.362,225,1.481,243,2.266,256,1.55,279,1.79,313,1.574,316,1.846,318,2.172,322,1.296,323,1.671,339,2.09,359,1.827,377,0.927,387,2.884,408,2.855,426,1.53,491,1.942,502,1.777,552,3.781,565,1.353,568,1.845,592,2.659,601,1.944,608,2.15,609,2.708,611,1.722,632,2.229,636,2.333,641,3.452,725,2.109,726,5.169,734,2.851,747,1.701,765,2.457,802,1.676,810,1.839,833,2.362,872,6.259,873,2.851,921,5.03,923,4.064,944,1.676,987,3.083,1009,2.82,1075,2.171,1077,2.319,1081,4.435,1082,3.131,1111,2.884,1262,2.292,1285,1.766,1302,2.511,1319,6.073,1331,2.659,1332,2.761,1333,3.241,1334,3.773,1335,3.452,1336,4.212,1337,5.947,1338,3.647,1339,3.773,1340,5.869,1341,3.773,1342,3.773,1343,5.869,1344,3.773,1345,3.773,1346,3.773,1347,2.884,1348,3.773,1349,3.773,1350,3.773,1351,3.773,1352,2.79,1353,2.734,1354,4.435,1355,2.57,1356,3.773,1357,3.773,1358,3.773,1359,3.773,1360,3.773,1361,3.773,1362,3.773,1363,3.773,1364,2.071,1365,2.392,1366,4.464]],["t/72",[7,0.646,56,0.809,84,1.134,92,4.501,93,3.471,109,1.381,140,3.21,171,2.226,172,3.626,256,3.065,279,3.539,283,2.369,304,2.206,318,1.995,338,2.015,470,5.924,476,2.378,477,4.859,552,4.083,576,4.825,707,4.558,802,3.314,815,7.212,828,3.416,833,4.67,840,3.716,872,6.779,1262,5.791]],["t/74",[2,0.941,4,1.082,5,1.229,7,0.749,8,1.877,15,1.272,16,0.649,18,2.472,21,0.986,22,2.165,24,0.565,26,2.088,27,1.335,29,1.217,31,0.853,33,1.989,34,0.889,46,1.446,56,0.345,84,0.609,86,2.375,90,1.644,92,3.518,93,4.669,97,2.515,98,1.226,107,0.893,109,0.588,112,2.787,113,1.272,117,3.49,125,1.989,127,1.144,138,1.407,140,1.367,160,2.477,165,0.782,170,1.031,172,2.808,175,1.909,179,1.882,181,4.003,185,4.864,186,2.651,191,5.11,207,1.424,213,1.42,224,1.478,228,1.105,236,1.858,253,2.1,256,1.306,257,1.777,278,1.25,283,1.627,313,0.853,315,3.357,316,2.507,337,1.625,338,1.384,340,2.326,353,1.534,369,2.165,377,1.258,381,3.835,390,1.685,395,1.379,407,1.577,439,2.281,441,1.898,444,1.513,446,2.22,447,1.651,458,1.965,471,1.446,484,1.478,495,1.685,500,1.651,519,4.687,527,1.23,569,2.984,571,2.597,580,1.212,590,1.877,597,4.186,614,0.642,620,6.35,629,2.22,634,2.682,638,2.1,655,2.281,675,2.22,690,2.165,691,1.919,693,2.148,694,1.898,698,2.523,716,4.952,728,2.43,742,3.518,743,2.303,751,2.015,775,3.312,802,1.412,807,1.349,809,2.402,824,2.559,826,2.908,833,1.989,840,1.583,875,2.842,901,1.714,985,2.303,1093,1.977,1099,2.617,1108,2.877,1143,3.312,1144,1.898,1175,1.607,1176,3.49,1188,2.22,1206,1.858,1237,2.26,1262,3.112,1263,2.637,1309,2.028,1365,2.015,1367,2.201,1368,2.239,1369,2.682,1370,2.783,1371,3.312,1372,3.76,1373,3.76,1374,3.788,1375,3.41,1376,2.35,1377,2.597,1378,3.76,1379,3.76,1380,4.81,1381,3.76,1382,4.81,1383,4.81,1384,3.76,1385,3.76,1386,3.76,1387,2.637,1388,2.984,1389,2.842,1390,2.402,1391,3.179,1392,3.179,1393,3.179]],["t/76",[7,0.911,24,1.164,56,0.71,88,3.237,103,2.405,105,2.248,144,3.442,219,2.225,226,3.485,236,1.89,243,3.931,244,4.424,276,3.561,283,2.079,305,4.573,306,4.267,317,3.593,351,2.601,353,1.96,363,5.13,377,1.608,471,3.983,478,4.357,491,2.167,502,1.982,527,2.101,536,2.141,550,2.825,565,3.138,567,4.634,568,2.752,577,3.401,578,2.825,580,2.496,586,6.328,604,4.744,635,2.608,672,4.234,692,5.732,798,3.954,809,4.948,865,4.841,880,4.744,984,3.075,1394,4.573,1395,6.328,1396,5.99]],["t/78",[2,2.115,7,0.618,16,2.014,24,1.648,28,1.107,31,1.314,56,0.531,84,0.85,92,1.991,106,1.971,107,2.771,109,1.968,127,1.763,132,2.72,140,2.107,160,3.246,161,3.66,170,1.589,171,1.461,172,2.717,248,2.524,268,5.102,275,1.664,283,2.268,292,2.339,313,1.314,316,2.396,318,2.841,323,2.168,338,1.323,372,2.012,391,2.263,408,1.941,426,2.897,471,3.251,491,1.621,495,2.596,502,2.807,528,2.833,552,1.903,565,1.755,568,1.54,587,4.378,593,2.652,612,3.482,677,2.833,705,3.309,715,2.763,723,1.922,725,2.737,755,2.422,757,1.325,796,2.818,802,2.175,814,2.315,828,2.242,872,3.482,916,2.99,944,2.175,1010,3.584,1053,4.897,1072,3.625,1075,2.818,1077,3.009,1138,3.42,1237,3.482,1262,2.974,1266,2.331,1397,4.733,1398,3.701,1399,3.235,1400,2.79,1401,3.167]],["t/80",[7,0.694,25,2.82,46,4.943,109,1.485,157,5.085,272,3.605,334,4.989,337,4.102,712,4.738,715,5.631,756,4.134,801,6.553,870,6.889,1102,5.261,1183,6.062,1285,3.755,1402,4.118,1403,7.023,1404,7.023,1405,5.703,1406,6.768,1407,6.285,1408,8.022,1409,5.812,1410,6.656]],["t/82",[4,3.108,7,0.79,16,1.425,21,2.164,23,2.556,24,1.24,28,1.576,84,1.085,89,2.858,132,2.249,134,3.81,170,2.262,172,2.653,203,1.833,279,3.308,283,2.214,313,1.871,318,1.864,399,5.787,471,4.629,484,3.244,495,3.697,622,3.862,677,4.034,684,3.994,827,3.88,914,7.045,1051,3.713,1090,3.4,1145,3.898,1193,5.332,1262,5.543,1300,4.142,1411,6.489,1412,8.251,1413,4.286,1414,8.251]],["t/84",[16,1.591,134,4.255,160,3.771,172,2.962,173,4.575,189,4.575,200,4.236,221,3.158,261,4.333,313,2.089,318,2.082,375,4.969,390,4.128,405,2.72,415,6.025,471,3.543,511,2.726,536,2.546,601,4.013,677,4.505,807,3.304,858,4.217,1206,5.725,1264,5.589,1268,6.025,1270,7.311,1415,9.214,1416,9.214,1417,8.551,1418,7.527]],["t/86",[7,0.769,8,1.772,16,0.613,18,1.447,19,3.027,21,0.931,24,0.533,27,1.613,28,0.465,29,1.149,50,4.584,51,5.368,56,0.852,63,7.816,84,0.581,88,1.483,89,2.003,94,1.782,95,2.043,96,1.891,101,1.753,105,1.03,106,1.207,107,0.843,109,0.555,128,1.571,131,1.494,132,0.967,134,1.639,138,1.328,157,1.902,160,1.155,177,1.59,182,2.273,191,3.882,195,2.9,198,1.604,207,1.344,213,1.34,225,1.919,228,1.043,236,0.866,247,1.447,291,1.94,296,1.269,298,2.012,315,4.098,317,1.646,338,0.81,339,2.708,341,1.812,342,2.153,353,0.898,359,1.452,377,0.737,381,1.558,395,1.302,410,2.174,422,2.242,430,2.648,440,1.452,446,2.095,447,1.558,467,2.577,471,3.247,472,2.614,498,3.78,499,2.293,500,1.558,510,4.473,512,3.507,573,3.294,628,1.701,676,2.9,677,3.579,702,2.218,705,2.027,752,4.371,774,1.782,779,2.627,796,1.726,802,1.332,813,5.073,821,2.293,833,1.878,840,1.494,857,1.494,865,2.218,892,1.232,898,3.675,927,2.174,1072,1.522,1090,1.463,1107,4.057,1111,3.737,1169,2.745,1174,2.218,1175,3.607,1238,2.531,1262,2.969,1265,1.726,1283,2.627,1380,8.697,1382,8.697,1383,4.59,1398,2.267,1419,4.199,1420,5.784,1421,3.549,1422,3.549,1423,3.882,1424,4.057,1425,1.914,1426,2.531,1427,3.549,1428,4.473,1429,3.549,1430,5.981,1431,5.784,1432,5.784,1433,5.784,1434,5.784,1435,3.549,1436,5.784,1437,4.057,1438,5.784,1439,3.549,1440,5.784,1441,3.549,1442,5.784,1443,5.784,1444,3.549,1445,3.549,1446,5.784,1447,3.549,1448,3.549,1449,5.784,1450,3.549,1451,3.549,1452,3.549,1453,3.294,1454,4.473,1455,3.126,1456,1.735,1457,3.126,1458,2.415,1459,2.153,1460,5.784,1461,2.682]],["t/88",[7,0.954,8,3.017,16,1.043,28,0.791,31,1.976,34,1.428,48,2.275,84,0.607,90,2.643,105,2.53,129,3.35,130,3.197,131,2.544,137,3.599,166,5.608,174,2.819,189,3.001,203,1.342,211,4.173,233,3.757,236,2.126,257,2.855,262,3.666,272,2.296,318,1.365,336,2.282,338,1.379,339,2.829,351,2.029,353,1.529,359,2.473,363,4.002,375,3.259,390,2.708,391,2.361,395,2.216,416,2.582,418,2.803,437,2.632,444,2.431,484,2.376,553,2.97,557,2.791,578,2.204,593,2.766,609,3.666,683,2.098,684,2.925,712,3.017,734,2.481,774,3.033,828,2.339,875,4.567,881,3.508,1072,2.592,1073,4.002,1090,4.212,1111,3.905,1220,3.217,1221,3.905,1233,4.567,1253,3.35,1262,3.102,1263,4.238,1264,3.666,1265,2.939,1283,4.472,1286,4.387,1314,5.505,1462,4.374,1463,5.608,1464,6.043,1465,6.043,1466,6.043,1467,3.508,1468,8.714,1469,5.322,1470,6.043,1471,4.937,1472,5.108,1473,5.322,1474,6.043,1475,3.017,1476,4.673]],["t/90",[7,0.866,9,4.124,10,4.787,18,2.799,26,2.364,31,1.556,48,3.59,84,0.69,105,2.768,124,4.017,163,2.524,196,2.524,247,2.799,249,4.739,277,4.548,306,3.929,324,3.373,372,2.383,377,1.424,507,3.464,605,2.305,611,3.679,620,3.523,628,3.289,677,4.662,683,2.383,684,3.322,802,2.576,823,5.186,833,3.631,1182,4.221,1199,4.289,1208,5.186,1238,7.815,1255,5.307,1262,6.388,1288,3.805,1313,3.701,1388,5.446,1477,5.307,1478,5.607,1479,4.384,1480,4.813,1481,3.408,1482,6.369,1483,6.369,1484,6.863,1485,6.863,1486,6.863,1487,6.863,1488,6.044,1489,6.369,1490,6.863,1491,6.863,1492,6.863,1493,6.863,1494,5.446,1495,6.863,1496,6.863,1497,5.607,1498,6.863,1499,6.863]],["t/92",[17,4.27,48,3.494,56,0.85,105,2.694,111,6.316,127,2.825,134,4.286,213,3.505,221,3.181,225,3.079,232,3.133,254,2.77,275,2.666,292,3.746,323,4.358,377,1.926,468,5.479,734,3.811,814,3.709,904,7.131,906,5.578,1175,3.966,1217,6.51,1399,5.182,1500,6.51,1501,8.613,1502,9.239]],["t/94",[2,2.392,4,1.397,5,0.939,8,1.434,15,0.972,16,1.651,17,2.31,19,2.605,21,1.272,23,1.503,24,0.729,27,1.069,28,1.252,31,1.1,32,1.667,56,0.921,84,1.052,89,2.182,96,1.586,103,0.892,105,1.408,116,1.68,123,0.908,127,0.874,129,1.592,132,1.322,133,1.14,134,2.241,141,2.665,156,3.065,158,1.332,160,1.578,161,1.814,162,0.937,164,1.888,165,0.597,186,1.256,187,1.856,200,2.231,201,1.357,221,2.538,225,2.745,232,3.531,236,1.537,237,4.287,247,1.979,256,0.997,275,1.81,281,1.261,283,1.302,292,1.159,299,1.71,306,1.183,317,1.332,318,1.096,323,3.36,340,1.777,348,1.71,351,1.629,358,2.221,359,1.985,361,1.795,365,1.351,377,1.717,391,1.122,393,1.309,395,1.053,398,0.939,399,2.014,415,4.12,430,1.315,476,0.774,478,2.729,491,2.315,496,1.628,502,0.735,505,1.105,508,1.57,511,1.435,530,3.073,532,3.049,534,1.902,538,2.221,539,2.89,543,1.501,552,2.069,555,1.261,558,1.192,572,1.411,585,2.014,587,2.17,611,1.108,615,1.559,627,2.279,666,3.522,670,1.549,682,2.221,683,0.997,684,2.348,690,1.654,698,1.927,712,2.422,747,1.849,802,1.078,820,1.37,828,2.438,837,1.616,840,2.652,845,1.695,866,2.279,877,2.428,888,1.404,894,2.014,904,7.586,906,5.744,907,7.573,909,4.872,911,7.164,934,1.315,944,1.821,993,1.821,1100,1.056,1271,1.326,1312,1.834,1320,2.048,1365,1.539,1413,2.52,1501,5.846,1503,2.279,1504,1.878,1505,2.665,1506,3.752,1507,6.007,1508,1.474,1509,2.279,1510,2.872,1511,1.742,1512,4.273,1513,2.529,1514,2.872,1515,2.872,1516,2.872,1517,2.872,1518,2.872,1519,2.872,1520,2.872,1521,2.872,1522,2.872,1523,2.529,1524,2.872,1525,4.273,1526,4.101,1527,1.759,1528,2.529,1529,2.872,1530,1.604,1531,1.777,1532,5.548,1533,2.872,1534,4.503,1535,2.048,1536,1.954]],["t/96",[28,1.355,56,0.948,84,1.04,158,4.798,196,3.805,225,3.431,377,2.147,532,5.007,837,5.819,911,8.876,1313,5.579,1532,9.11,1534,9.6,1537,10.344,1538,7.377,1539,6.399]],["t/98",[15,1.328,17,1.439,48,1.477,56,0.82,84,0.394,103,1.948,105,1.139,108,1.881,153,1.863,156,2.479,158,1.82,160,2.04,163,1.443,187,2.535,203,0.872,232,1.324,247,4.641,254,1.171,275,1.127,304,0.767,321,2.752,324,1.928,334,2.063,343,1.295,351,1.318,377,0.814,398,3.411,401,2.225,502,1.004,511,1.161,532,1.899,539,5.828,540,3.113,543,3.277,553,1.928,607,1.396,611,1.514,621,1.871,637,3.735,708,2.848,762,2.452,795,2.38,846,3.428,881,3.64,904,2.403,907,2.67,979,2.427,1016,5.124,1072,1.683,1076,2.618,1078,2.633,1102,2.175,1172,2.191,1297,1.766,1313,2.116,1409,2.403,1507,2.848,1527,2.403,1531,2.427,1535,5.587,1536,5.331,1540,2.014,1541,3.924,1542,9.619,1543,3.924,1544,6.272,1545,6.272,1546,6.272,1547,8.95,1548,10.436,1549,3.924,1550,6.18,1551,6.272,1552,3.924,1553,3.924,1554,7.102,1555,8.95,1556,9.786,1557,3.611,1558,4.094,1559,4.473,1560,5.301,1561,6.272,1562,6.272,1563,4.74,1564,3.924,1565,2.535,1566,4.154,1567,2.798,1568,3.924,1569,3.924,1570,3.034,1571,3.924,1572,3.924,1573,3.924,1574,3.924,1575,3.924,1576,3.924,1577,2.752,1578,3.924,1579,6.921,1580,3.924,1581,3.924,1582,3.924,1583,2.904,1584,3.924,1585,3.924,1586,3.924,1587,3.924,1588,3.317,1589,3.924,1590,3.924,1591,8.95,1592,3.924,1593,3.456,1594,3.924,1595,3.317,1596,3.924,1597,2.297,1598,3.924,1599,3.924,1600,3.924,1601,2.175,1602,3.924,1603,3.924,1604,3.924]],["t/100",[15,3.961,16,1.01,24,1.279,34,1.382,56,1.155,85,1.88,96,1.911,97,2.427,158,2.712,164,2.169,165,1.216,228,1.719,247,3.47,338,1.335,348,3.482,351,2.858,367,2.763,377,1.214,388,3.924,395,2.144,405,1.726,416,2.499,418,2.712,471,3.273,505,2.249,528,2.859,530,2.427,538,7.757,539,5.974,575,2.578,578,2.132,591,3.093,747,2.228,829,2.859,840,2.462,843,2.968,892,2.03,907,3.979,911,8.712,1080,3.019,1094,4.653,1313,3.154,1413,3.037,1481,2.904,1532,9.702,1535,4.17,1536,3.979,1542,9.312,1559,4.17,1560,4.943,1563,4.419,1567,4.17,1605,3.979,1606,7.494,1607,5.162,1608,5.149,1609,5.149,1610,5.149,1611,5.149,1612,5.149,1613,5.149,1614,5.149,1615,5.149,1616,4.394,1617,5.847,1618,5.427,1619,5.847,1620,5.847,1621,5.847,1622,3.482,1623,5.149,1624,5.847,1625,4.101,1626,5.847,1627,5.847,1628,4.17]],["t/102",[16,1.757,19,5.096,84,1.023,140,3.701,225,3.376,275,2.924,323,3.809,468,6.008,471,3.914,491,2.847,904,6.233,907,6.926,908,4.809,1302,5.725,1507,7.388,1525,8.963,1526,8.603,1629,6.576]],["t/104",[15,2.822,16,1.705,24,1.253,56,1.298,145,2.123,160,1.851,203,1.264,225,1.887,300,3.2,338,1.299,372,1.975,395,2.087,398,1.86,401,2.959,405,2.462,426,2.859,562,2.593,601,2.478,607,2.024,651,3.519,747,2.168,843,2.888,904,5.108,907,5.676,921,4.924,923,3.33,926,5.01,966,2.752,993,3.707,1027,3.359,1297,2.56,1375,3.2,1525,5.01,1526,4.809,1555,4.648,1563,4.299,1607,5.059,1630,5.689,1631,4.299,1632,2.74,1633,5.467,1634,5.689,1635,6.45,1636,9.875,1637,9.875,1638,3.998,1639,5.689,1640,9.875,1641,9.875,1642,4.514,1643,9.787,1644,5.01,1645,5.01,1646,5.689,1647,5.689,1648,5.689,1649,8.341,1650,5.28,1651,5.28,1652,5.689,1653,5.689,1654,5.689,1655,5.689,1656,5.689]],["t/106",[5,1.459,12,2.599,15,2.884,16,0.771,23,1.383,24,1.043,28,0.909,29,1.445,49,0.931,56,1.31,84,0.698,85,1.435,89,1.546,97,1.852,103,1.386,135,2.362,138,1.671,140,1.623,164,1.138,196,2.554,225,1.481,275,1.282,283,1.198,286,2.511,323,3.189,326,2.82,336,1.686,338,1.019,359,1.827,367,2.109,368,3.131,376,2.362,395,2.547,397,1.722,401,1.583,405,1.318,426,3.295,478,2.511,511,1.321,539,2.659,543,2.333,560,3.43,568,1.845,607,1.588,781,3.773,831,2.613,892,1.55,904,5.219,906,2.683,907,5.799,916,3.401,921,7.026,923,5.627,926,3.931,939,5.086,947,5.506,966,1.757,989,3.628,1027,5.675,1320,3.183,1353,2.734,1559,3.183,1607,5.169,1643,7.203,1644,3.931,1645,7.505,1650,4.143,1657,4.464,1658,4.464,1659,6.944,1660,4.464,1661,4.464,1662,4.464,1663,4.464,1664,3.038,1665,4.464,1666,4.464,1667,4.464,1668,4.464,1669,4.143,1670,3.931,1671,2.278,1672,3.647,1673,4.143,1674,4.464,1675,4.464,1676,6.944,1677,4.464,1678,6.944,1679,8.521,1680,4.464,1681,4.464,1682,4.464,1683,6.944,1684,4.464,1685,4.464,1686,3.773,1687,3.452]],["t/108",[0,2.545,2,1.823,7,0.347,15,1.607,16,1.85,19,3.012,21,1.91,24,1.331,25,2.632,28,1.301,33,2.512,34,1.721,56,0.667,105,1.379,110,2.322,116,2.522,128,2.102,135,3.853,156,3,158,4.11,164,1.211,165,0.987,183,1.892,197,1.964,200,2.183,213,1.793,220,2.713,225,3.295,228,2.141,235,3.88,236,1.777,283,2.377,295,1.772,306,3.001,313,1.077,323,4.01,353,1.202,377,1.512,394,2.203,471,1.826,476,1.962,491,1.328,492,3.448,502,1.215,505,1.826,522,3.279,528,2.322,550,1.732,552,4.309,553,2.334,558,3.677,560,2.346,568,1.262,606,3.387,607,1.689,627,5.779,638,2.652,653,1.387,669,3.589,729,3.145,756,2.069,757,1.086,820,2.265,825,3.387,827,2.233,829,2.322,904,2.909,907,8.264,909,3.673,911,3.387,919,3.387,939,3.853,971,3.232,974,2.692,1102,2.633,1245,3.589,1254,3.106,1266,4.31,1320,3.387,1331,2.828,1365,2.545,1512,7.803,1513,8.749,1567,3.387,1628,3.387,1632,2.287,1688,4.601,1689,2.31,1690,4.749,1691,3.88,1692,4.749,1693,2.578,1694,4.749,1695,3.88,1696,2.804]],["t/110",[0,0.972,12,1.216,16,0.762,24,0.273,25,0.539,27,0.399,28,1.251,34,0.429,35,1.172,48,0.683,56,1.375,68,3.12,84,0.182,95,2.542,100,2.857,105,0.526,108,2.967,116,0.628,119,1.533,128,1.438,164,0.462,165,0.918,198,0.819,200,1.494,213,0.685,217,0.959,225,0.602,247,0.739,295,0.677,299,1.08,304,0.355,306,0.747,322,1.282,323,2.014,326,3.398,338,0.414,343,0.599,353,0.459,372,1.128,375,0.978,395,0.665,398,2.446,405,1.828,413,1.272,422,1.146,426,3.627,446,2.606,471,0.697,476,1.189,483,2.584,502,0.464,511,0.536,527,0.368,552,3.139,560,1.605,707,0.936,726,1.1,736,0.718,747,0.691,752,2.456,790,1.172,813,2.653,825,1.293,838,0.849,843,3.797,904,1.99,906,2.653,907,2.211,916,1.562,917,1.708,921,6.767,923,4.953,939,3.275,945,0.557,947,2.853,966,1.361,993,1.657,1017,1.201,1027,3.655,1083,2.786,1100,0.667,1113,1.201,1172,1.013,1182,0.803,1265,0.882,1266,2.164,1271,1.501,1332,1.122,1352,1.133,1353,1.111,1355,1.044,1375,1.828,1425,0.978,1437,3.097,1635,1.402,1638,2.578,1643,4.547,1644,1.597,1645,1.597,1651,4.097,1669,1.683,1670,3.888,1673,1.683,1695,1.482,1696,6.116,1697,1.813,1698,1.252,1699,1.044,1700,1.252,1701,1.813,1702,1.813,1703,1.439,1704,2.513,1705,1.842,1706,1.813,1707,4.415,1708,1.813,1709,3.25,1710,1.813,1711,3.25,1712,3.25,1713,10.118,1714,1.813,1715,1.813,1716,5.38,1717,1.813,1718,2.01,1719,1.683,1720,4.415,1721,1.813,1722,1.813,1723,1.813,1724,3.25,1725,3.25,1726,3.25,1727,1.683,1728,1.813,1729,3.267,1730,3.336,1731,1.813,1732,1.252,1733,1.005,1734,1.597,1735,3.476,1736,1.813,1737,1.137,1738,3.25,1739,3.25,1740,3.25,1741,3.25,1742,5.38,1743,1.813,1744,1.813,1745,1.813,1746,1.813,1747,1.813,1748,1.813,1749,1.813,1750,1.813,1751,1.813,1752,1.813,1753,1.158,1754,0.715,1755,1.813,1756,4.415,1757,1.813,1758,1.813,1759,1.158,1760,1.813,1761,1.813,1762,1.813,1763,1.533,1764,1.813,1765,0.965,1766,3.25,1767,1.813]],["t/112",[16,1.505,25,3.324,26,2.082,28,1.465,56,1.251,105,2.53,129,3.35,132,1.647,134,2.791,165,1.256,199,2.766,200,2.778,201,2.855,275,1.736,306,3.591,316,2.499,323,2.262,337,2.612,338,1.379,353,2.205,367,2.855,377,1.254,405,1.784,426,2.987,447,2.653,476,1.628,502,1.546,511,2.578,552,4.282,558,4.643,560,2.985,605,2.029,607,2.149,629,3.567,690,3.479,698,5.848,730,4.567,763,5.505,856,5.101,916,2.138,921,7.294,923,5.101,939,5.407,947,3.905,984,2.399,1027,3.567,1202,3.452,1266,2.431,1353,3.701,1628,4.309,1643,5.108,1670,5.322,1718,3.738,1719,5.608,1727,5.608,1768,3.817,1769,6.043,1770,6.043,1771,6.043,1772,6.043,1773,4.238]],["t/114",[16,1.847,17,3.922,56,0.98,275,3.072,277,5.101,324,5.255,359,4.376,377,2.22,469,9.925,511,3.164,662,5.768,1528,9.418,1774,10.694]],["t/116",[2,2.819,7,0.448,10,3.077,16,1.059,21,1.608,23,1.899,29,1.985,31,1.996,56,0.944,84,0.885,105,1.78,110,2.997,116,3.049,131,2.581,134,2.831,135,3.243,158,2.844,160,3.352,187,3.961,189,3.045,197,2.535,213,3.325,215,6.08,225,2.034,230,4.741,232,2.972,244,3.502,248,2.671,254,1.829,277,2.924,283,1.645,284,5.503,302,2.62,313,1.996,314,4.537,320,4.688,334,3.223,340,3.792,359,3.603,376,3.243,377,1.272,393,2.794,430,2.806,522,4.234,530,3.654,545,2.63,563,5.182,572,3.013,578,2.236,585,4.3,587,4.633,611,2.365,614,1.504,628,2.938,683,2.129,764,3.832,778,3.588,816,3.223,820,2.924,858,2.806,904,7.305,906,3.684,907,4.172,987,4.234,988,2.782,1220,3.264,1263,4.3,1388,4.865,1512,5.399,1513,5.399,1530,3.423,1733,3.399,1775,4.741,1776,6.131,1777,6.131,1778,4.633,1779,3.832,1780,6.131]],["t/118",[7,0.922,16,1.57,20,3.721,23,1.986,27,2.002,29,2.943,34,1.515,55,3.236,56,0.587,84,1.062,109,1.798,127,1.951,182,1.991,185,4.095,192,3.504,197,2.651,199,4.161,213,2.421,221,2.197,296,2.293,306,2.642,316,2.651,343,2.116,351,2.153,447,2.815,463,3.58,498,3.31,507,4.588,568,1.704,576,3.504,607,2.28,611,2.473,630,2.197,653,1.873,682,4.957,684,3.103,721,5.087,799,4.496,818,5.654,820,3.058,838,3.001,846,1.826,850,7.109,855,3.662,901,2.922,918,3.818,975,7.226,989,3.35,1246,7.163,1262,3.291,1313,3.457,1364,2.974,1781,2.849,1782,7.213,1783,6.376,1784,5.646,1785,5.087,1786,6.411,1787,5.419]],["t/120",[2,1.52,7,0.905,16,1.51,21,1.592,22,3.496,24,0.912,26,2.092,27,1.926,28,0.795,31,2.323,46,3.363,56,0.801,84,0.61,90,4.482,92,3.005,93,2.387,94,3.048,96,1.985,109,0.95,123,3.241,132,1.655,133,2.411,145,2.266,164,1.548,174,2.412,179,1.885,216,2.869,226,2.732,236,1.482,243,3.082,246,2.131,247,3.566,275,1.744,277,2.896,278,2.019,280,3.584,283,1.629,292,2.451,313,1.377,317,4.056,341,3.099,364,3.971,375,5.527,418,2.816,437,2.645,444,2.443,463,3.39,491,1.698,527,1.232,568,1.614,590,3.031,605,2.039,613,2.547,619,3.616,620,3.117,622,2.842,683,2.108,694,3.065,709,5.304,715,2.896,719,3.39,798,3.099,821,3.923,833,3.212,844,4.589,850,5.409,876,3.554,880,3.719,901,2.767,1249,4.961,1253,3.366,1265,2.954,1288,3.366,1368,3.616,1479,3.878,1788,5.347,1789,4.589,1790,4.696,1791,4.818,1792,5.347]],["t/122",[2,1.588,7,0.884,15,3.054,21,2.367,29,2.055,46,2.441,56,0.827,84,1.15,90,2.775,92,3.609,93,3.548,115,2.712,134,2.931,192,3.469,200,2.918,207,2.404,228,1.866,229,3.493,245,3.518,304,1.241,334,3.336,338,2.06,351,3.031,376,3.357,421,3.684,532,6.08,596,2.652,607,2.257,614,1.084,621,5.01,630,2.175,726,3.85,744,3.357,747,2.418,763,4.009,820,5.457,850,7.77,918,3.779,1207,4.259,1246,6.754,1262,3.258,1286,4.607,1290,4.934,1331,3.779,1409,3.887,1787,5.364,1793,5.283,1794,5.364,1795,6.346,1796,5.036,1797,4.696,1798,6.346,1799,4.382,1800,6.346,1801,6.346,1802,5.909,1803,5.89]],["t/124",[15,2.926,48,4.191,56,1.262,375,4.663,395,3.171,511,2.558,607,3.959,723,3.692,747,3.295,850,6.886,944,3.246,1400,4.164,1456,4.227,1511,5.245,1565,7.954,1804,7.615,1805,8.646,1806,5.655,1807,8.646,1808,12.31,1809,7.615,1810,8.646,1811,7.309,1812,7.309,1813,8.646,1814,8.646,1815,7.615,1816,8.646]],["t/126",[12,2.501,16,1.154,29,3.03,56,1.17,164,1.703,179,2.075,203,1.484,223,3.491,247,2.725,275,1.919,351,2.244,375,3.604,392,3.847,401,3.832,482,2.773,511,1.977,532,3.234,607,2.376,608,4.508,621,3.187,630,3.208,653,1.952,704,5.459,721,5.302,723,2.216,818,6.336,1246,3.944,1288,3.704,1565,7.563,1796,5.302,1809,8.243,1811,7.911,1812,9.131,1817,6.682,1818,6.682,1819,6.682,1820,6.682,1821,8.686,1822,6.682,1823,6.682,1824,6.682,1825,6.682,1826,8.243,1827,5.648,1828,6.682,1829,4.85,1830,6.682,1831,6.682,1832,6.682,1833,6.682,1834,6.682,1835,6.682,1836,6.682,1837,6.682,1838,6.682,1839,6.682,1840,6.682]],["t/128",[15,0.976,16,1.091,29,1.577,34,1.151,48,3.124,56,1.258,84,0.29,164,1.242,203,0.641,212,2.541,225,1.616,236,0.704,246,1.012,275,1.399,277,1.376,279,3.841,292,1.165,372,1.002,375,1.556,392,2.804,401,2.634,405,0.852,416,1.233,426,2.166,437,1.257,445,1.689,471,2.431,482,1.197,483,1.689,502,1.9,507,1.456,511,1.87,530,1.197,607,3.58,611,1.113,623,2.164,630,0.989,653,1.846,683,1.002,723,2.097,747,1.099,756,2.122,802,1.083,803,1.508,818,4.69,846,2.868,855,1.648,934,2.229,963,1.623,975,5.705,1055,3.269,1083,1.298,1090,1.189,1160,2.18,1219,1.193,1220,1.536,1265,3.075,1288,2.7,1313,1.556,1389,2.18,1400,1.39,1405,1.734,1413,1.499,1511,1.75,1530,1.611,1565,6.19,1566,1.911,1579,3.767,1638,1.383,1693,2.644,1698,1.992,1733,1.599,1735,1.864,1773,3.416,1781,1.526,1782,2.289,1783,7.35,1796,2.289,1809,4.289,1811,4.117,1812,4.117,1815,4.289,1821,4.52,1826,4.289,1827,4.117,1841,5.164,1842,5.729,1843,3.044,1844,5.342,1845,2.885,1846,4.871,1847,2.678,1848,2.678,1849,1.456,1850,2.357,1851,2.885,1852,2.885,1853,6.959,1854,2.885,1855,4.52,1856,2.289,1857,4.798,1858,2.541,1859,4.871,1860,4.52,1861,2.885,1862,2.885,1863,4.871,1864,2.885,1865,2.885,1866,2.885,1867,2.541,1868,2.289,1869,2.885,1870,2.885,1871,2.541,1872,5.866,1873,2.885,1874,2.885,1875,2.885,1876,2.439,1877,2.678,1878,2.678,1879,2.678,1880,2.678,1881,2.885,1882,6.32,1883,4.289,1884,4.289,1885,4.871,1886,2.541,1887,2.541,1888,2.541,1889,1.992,1890,2.885,1891,4.871,1892,2.885,1893,2.289,1894,2.885,1895,2.885,1896,2.885,1897,2.885,1898,2.885,1899,2.885,1900,2.885,1901,2.885,1902,2.885,1903,2.541,1904,4.52,1905,4.871,1906,2.541,1907,2.885,1908,2.885,1909,2.885,1910,2.885,1911,2.885,1912,2.885,1913,1.508,1914,2.885,1915,2.885,1916,2.885,1917,2.885,1918,2.541,1919,2.885,1920,2.885,1921,2.885,1922,2.885,1923,2.406,1924,2.678,1925,1.963,1926,2.678,1927,2.541,1928,2.885,1929,2.678,1930,2.541,1931,1.936]],["t/130",[7,0.495,16,0.548,18,1.295,23,2.098,24,1.018,27,2.537,29,1.709,34,1.6,39,1.199,56,1.314,84,0.319,85,2.177,96,1.038,134,1.466,158,2.448,160,1.717,221,1.088,225,1.053,279,2.715,394,1.473,401,1.126,405,0.937,426,2.704,438,2.542,471,2.604,483,1.858,505,1.221,529,2.684,530,1.318,607,3.114,630,1.088,653,1.541,683,1.102,818,4.08,820,1.514,846,1.503,934,4.799,939,1.68,971,2.16,975,6.243,1013,1.843,1015,1.748,1016,2.594,1055,2.131,1160,3.988,1265,4.259,1347,5.658,1413,1.649,1458,2.16,1565,2.051,1579,5.237,1632,2.542,1638,2.529,1773,3.701,1781,1.654,1782,2.519,1783,7.353,1796,2.519,1811,2.684,1812,2.684,1815,2.796,1826,2.796,1827,4.461,1841,2.594,1842,3.644,1844,2.684,1847,2.946,1848,2.946,1849,1.602,1850,2.594,1853,4.081,1857,5.098,1858,2.796,1867,2.796,1868,2.519,1871,2.796,1872,4.898,1876,2.684,1877,2.946,1878,2.946,1879,2.946,1880,4.898,1883,2.796,1884,2.796,1886,2.796,1887,2.796,1888,2.796,1904,2.946,1924,2.946,1926,2.946,1927,2.796,1929,2.946,1932,3.451,1933,6.772,1934,3.175,1935,3.175,1936,3.175,1937,2.946,1938,2.946,1939,3.175,1940,5.277,1941,3.988,1942,6.772,1943,3.175,1944,3.175,1945,3.175,1946,3.175,1947,3.175,1948,3.175,1949,3.175,1950,5.277,1951,3.175,1952,2.796,1953,2.946,1954,2.399,1955,2.455,1956,2.946,1957,3.175,1958,2.946,1959,1.405,1960,3.175,1961,3.175,1962,3.175,1963,3.175,1964,3.175,1965,3.175,1966,3.175,1967,3.175,1968,3.175,1969,2.684,1970,3.175,1971,2.946,1972,4.898,1973,3.175,1974,3.175,1975,3.175,1976,6.772,1977,3.175,1978,2.264,1979,3.175,1980,2.946,1981,2.946,1982,2.264,1983,2.946,1984,2.946,1985,2.946,1986,3.175,1987,2.076,1988,1.232,1989,3.831,1990,3.175,1991,1.964,1992,2.227]],["t/132",[4,1.285,7,0.914,15,3.524,16,0.771,23,2.64,24,1.445,31,1.012,46,1.717,49,1.448,56,1.01,84,1.108,85,1.435,90,1.952,92,3.579,93,3.78,96,2.786,105,2.016,107,2.284,109,1.504,113,1.511,123,1.412,127,2.926,145,1.666,164,1.138,171,1.126,174,2.35,189,2.217,203,1.542,223,2.333,241,3.241,280,2.635,296,1.596,297,2.266,313,1.012,317,2.071,322,2.474,338,2.195,351,1.499,359,1.827,395,1.637,397,1.722,401,1.583,438,4.63,476,1.203,502,2.181,505,1.717,527,1.729,528,2.183,536,2.657,550,3.108,607,1.588,614,1.456,653,1.304,655,2.708,747,1.701,828,1.728,840,1.879,846,1.272,850,2.761,975,5.075,993,1.676,1019,1.522,1202,2.55,1254,2.919,1297,2.009,1458,3.038,1531,2.761,1577,3.131,1633,1.952,1802,2.511,1923,2.205,1992,3.131,1993,5.51,1994,2.884,1995,4.464,1996,4.464,1997,5.869,1998,3.998,1999,4.464,2000,4.03,2001,5.37,2002,3.304,2003,4.464,2004,3.452,2005,4.464,2006,3.373,2007,4.464,2008,3.452,2009,2.761,2010,3.562,2011,3.083,2012,3.038,2013,1.38,2014,5.37,2015,2.44]],["t/134",[2,1.061,7,0.743,12,2.496,15,2.257,16,1.152,24,1.239,25,1.26,34,1.002,47,2.481,49,0.884,56,1.128,84,1.085,85,1.363,101,2.094,105,1.231,114,4.935,123,2.109,153,2.012,158,1.967,164,1.7,179,1.316,196,1.559,203,0.942,213,1.601,272,1.611,276,1.949,285,1.625,306,1.747,323,1.587,338,0.968,342,4.045,359,1.735,438,3.212,449,3.809,463,2.367,471,1.63,482,2.767,498,2.189,527,1.353,528,2.073,530,1.759,536,1.843,550,1.546,560,3.294,605,1.424,612,2.548,630,2.286,659,2.548,685,2.189,744,4.945,757,0.969,798,2.164,843,3.385,850,6.676,945,1.302,989,2.215,1019,1.446,1098,2.481,1246,3.937,1312,2.708,1325,2.885,1333,6.785,1352,2.65,1365,2.272,1367,2.481,1413,2.202,1531,5.099,1577,2.973,1597,6.316,1618,3.934,1633,1.854,1638,2.032,1802,3.751,1913,2.215,1954,3.204,1994,2.739,2001,3.279,2009,4.125,2016,6.23,2017,1.656,2018,3.584,2019,6.669,2020,3.584,2021,4.24,2022,6.669,2023,4.24,2024,4.24,2025,3.464,2026,4.309,2027,5.04,2028,4.24,2029,5.04,2030,9.306,2031,5.359,2032,5.04,2033,6.189,2034,5.873,2035,4.125,2036,4.24,2037,3.279,2038,3.279,2039,3.279,2040,3.279,2041,2.65,2042,5.04,2043,3.934,2044,4.24,2045,3.584,2046,4.24,2047,4.24,2048,3.934]],["t/136",[7,0.661,15,1.137,16,0.58,23,1.041,24,1.612,27,0.74,34,0.794,39,2.662,56,1.281,84,0.338,92,1.155,105,2.046,107,1.314,109,0.526,123,1.749,127,1.683,158,3.27,161,5.705,170,1.517,177,1.506,179,1.044,182,3.016,192,1.837,193,1.867,216,1.588,256,1.167,275,0.965,277,3.362,279,3.276,285,3.132,337,2.391,338,1.263,353,1.784,395,1.232,405,0.992,418,3.27,426,3.677,471,3.142,484,2.771,498,1.735,505,2.711,527,2.322,530,3.748,536,2.965,550,2.017,560,1.66,605,1.129,607,1.195,630,1.896,638,1.876,647,1.926,653,0.982,662,1.812,685,1.735,734,1.38,736,2.789,744,1.778,747,1.281,757,1.265,795,2.038,818,3.807,837,1.89,850,6.009,917,4.295,939,1.778,945,2.164,974,1.905,975,3.294,984,2.798,1013,3.21,1080,1.735,1209,2.667,1246,4.823,1264,3.355,1265,1.635,1365,1.801,1579,2.599,1632,2.664,1638,2.651,1754,2.182,1768,2.123,1773,2.357,1781,2.209,1783,2.357,1827,4.675,1883,4.871,1884,2.96,1886,2.96,1887,2.96,1888,2.96,1980,3.119,1981,3.119,1982,2.396,1983,3.119,1984,3.119,1985,3.119,1988,2.147,2009,2.079,2017,1.313,2018,2.841,2049,3.361,2050,3.361,2051,3.361,2052,3.361,2053,3.361,2054,3.361,2055,3.361,2056,3.361,2057,3.361,2058,3.361,2059,3.361,2060,3.361,2061,3.361,2062,3.361,2063,3.361,2064,3.361,2065,5.531,2066,3.361,2067,3.119,2068,3.361,2069,3.361,2070,3.361,2071,3.361,2072,3.361,2073,1.863,2074,3.361]],["t/138",[2,1.57,3,3.113,5,1.283,7,0.763,12,1.469,16,1.083,19,1.623,21,1.029,22,2.259,23,1.216,24,1.345,25,1.166,27,2.299,29,1.27,36,1.606,56,0.575,84,1.101,90,2.743,98,2.919,105,1.139,107,1.862,109,1.633,110,1.918,127,3.176,132,1.069,145,1.464,157,3.361,165,1.629,170,1.076,172,1.261,177,1.758,181,2.063,182,1.948,196,1.443,216,1.854,219,2.571,220,2.241,221,1.345,222,3.456,225,1.302,230,3.034,246,2.201,247,1.6,272,1.491,277,1.871,285,3.432,288,2.403,289,1.439,297,1.991,298,2.224,313,0.89,315,1.73,318,1.77,337,2.711,338,1.432,353,1.983,359,1.606,372,2.178,376,2.076,392,2.259,393,1.788,463,2.191,482,1.628,484,1.543,491,1.098,496,2.224,502,2.29,507,1.98,513,2.743,527,2.381,536,3.028,553,1.928,557,1.812,560,1.938,568,1.667,578,1.431,607,2.787,613,2.631,630,1.345,635,1.321,658,1.744,659,4.708,677,1.918,685,3.238,741,3.769,757,0.897,759,2.549,774,1.97,806,2.848,818,4.17,820,1.871,846,1.118,850,6.777,917,2.063,975,5.828,980,3.034,987,2.709,994,1.568,1099,1.348,1111,2.535,1219,1.623,1246,2.316,1338,3.206,1459,2.38,1477,3.034,1987,4.102,2009,2.427,2017,1.533,2075,3.924,2076,3.383,2077,3.924,2078,3.317,2079,3.924,2080,3.924,2081,2.599,2082,3.206,2083,3.924,2084,3.924]],["t/140",[15,3.529,29,3.377,186,4.561,277,4.975,324,5.125,377,2.165,818,4.86,1006,8.816,1175,4.457,1219,4.313,1332,6.452,1488,9.185,1779,6.518,1802,5.867,2085,7.882,2086,7.097]],["t/142",[1,3.493,2,2.292,4,1.864,7,0.925,16,1.118,18,3.734,20,3.759,23,2.006,24,1.376,56,0.839,84,1.224,90,2.832,93,2.546,105,2.658,109,1.807,132,1.765,145,2.417,162,2.112,164,1.651,165,1.903,170,1.776,171,1.633,172,2.082,174,1.786,177,4.103,192,3.54,200,2.977,213,2.446,225,2.148,232,2.186,256,2.249,275,1.86,285,2.483,306,2.669,313,2.076,323,2.424,353,2.317,377,1.344,390,2.902,393,2.952,397,2.498,507,3.269,526,4.346,536,2.531,558,2.688,597,3.104,611,2.498,630,2.22,671,5.474,759,2.107,818,4.266,823,4.894,850,4.006,918,3.857,933,3.405,979,4.006,987,4.472,1070,3.426,1099,2.225,1238,4.618,1246,3.823,1266,2.605,1479,4.137,1480,4.542,1481,3.216,2010,2.092,2087,5.703]],["t/144",[7,0.95,12,2.899,16,1.788,20,4.496,23,2.4,27,1.706,31,2.348,55,3.91,56,0.71,84,1.041,123,3.275,160,2.52,197,3.203,199,3.546,213,2.925,232,2.615,285,2.97,306,3.192,351,2.601,447,3.401,463,4.325,507,3.91,527,1.572,607,3.683,611,2.988,622,3.626,630,2.655,682,5.99,684,3.749,799,5.433,818,5.436,855,4.424,918,4.613,975,7.417,1246,6.887,1262,3.976,1313,4.178,1364,3.593,1784,6.822,2088,7.746,2089,4.792,2090,6.547]],["t/146",[1,3.168,2,1.47,5,1.92,7,0.736,16,1.474,17,2.154,21,1.54,23,1.82,24,0.883,28,0.769,29,1.902,56,0.922,84,1.011,95,3.382,96,1.92,98,1.916,103,2.651,105,1.705,113,1.988,144,2.611,159,3.634,165,1.221,177,2.632,179,1.824,182,1.824,221,2.013,247,2.396,256,2.04,283,1.576,296,2.101,323,2.199,334,3.088,351,1.973,377,1.219,440,2.404,444,2.363,447,2.579,463,4.767,478,3.305,511,1.738,527,1.192,530,2.438,531,3.305,562,2.677,565,1.78,630,3.447,638,3.28,677,2.872,682,4.543,683,2.04,684,2.843,726,3.563,757,1.343,818,2.737,825,4.189,827,2.762,853,4.661,880,3.598,901,3.891,933,3.088,939,3.108,966,1.487,975,3.498,1006,4.965,1015,3.234,1090,2.421,1121,3.305,1163,3.671,1262,3.016,1413,3.051,1488,5.173,1842,4.057,1857,3.796,1978,4.189,2018,4.965,2089,6.222,2091,4.189,2092,3.942,2093,3.711,2094,3.942,2095,5.874,2096,4.543,2097,5.452,2098,5.874,2099,3.28,2100,5.874,2101,6.318,2102,5.874,2103,4.799,2104,5.874,2105,5.874,2106,8.538,2107,5.874,2108,5.452]],["t/148",[2,1.24,4,1.426,7,0.922,8,2.473,15,1.676,16,1.753,21,1.299,22,2.852,24,1.13,26,1.707,27,1.657,28,0.985,31,2.062,46,2.893,56,0.833,84,0.498,85,1.592,90,4.44,92,2.585,93,1.948,94,2.487,96,1.619,123,2.876,132,2.479,145,1.849,158,2.298,170,1.358,172,1.592,174,1.366,186,3.289,216,2.34,226,2.229,236,1.209,243,2.514,245,5.041,247,2.02,275,1.423,278,1.647,282,2.5,283,1.329,291,2.708,292,2,313,1.123,317,4.218,332,2.386,341,2.528,351,1.664,370,3.596,375,4.904,395,1.817,400,3.596,422,3.129,437,2.158,444,1.993,459,3.596,463,2.766,491,1.386,505,1.905,527,1.005,555,2.175,575,2.184,578,1.806,583,2.808,590,2.473,605,1.664,606,3.533,613,2.078,619,2.95,620,2.543,683,1.72,694,3.797,709,3.005,712,2.473,715,2.363,719,2.766,734,2.034,764,3.096,770,3.533,798,2.528,821,3.201,823,3.744,844,5.684,876,2.899,880,3.034,901,2.258,1070,2.621,1202,2.83,1249,4.047,1253,2.746,1265,2.41,1288,2.746,1296,7.215,1312,3.164,1365,2.654,1368,2.95,1459,3.005,1479,3.164,1535,5.364,1597,4.403,1788,4.363,1790,3.831,1791,3.931,1792,4.363,2089,5.625,2109,4.954,2110,4.363,2111,4.954,2112,4.954]],["t/150",[7,0.453,15,3.001,24,0.93,31,1.404,34,2.096,49,1.291,56,1.097,84,0.622,85,3.33,112,4.077,128,2.74,145,2.31,153,2.939,159,3.83,182,1.922,203,1.375,225,2.054,244,3.536,273,4.581,275,1.778,300,3.483,315,2.729,359,2.533,376,3.275,377,1.84,378,4.912,383,4,498,4.579,500,2.718,532,4.292,601,2.697,630,3.039,726,3.755,744,3.275,747,2.359,794,4,798,3.16,810,2.551,818,2.885,853,4.912,890,6.562,927,3.792,938,3.536,942,4.213,975,3.687,1035,3.432,1100,2.277,1175,3.789,1288,3.432,1536,4.213,1538,4.415,1565,4,1632,2.982,1802,4.988,1804,5.452,2018,5.233,2076,3.339,2085,7.829,2113,3.954,2114,5.058,2115,6.191,2116,4.678,2117,6.323,2118,5.233,2119,6.191,2120,8.867,2121,8.867,2122,8.867,2123,5.233,2124,6.191,2125,5.745,2126,5.452,2127,5.452,2128,3.954,2129,5.745,2130,4.678,2131,5.058]],["t/152",[7,0.757,20,6.004,292,4.175,353,2.618,378,8.208,447,4.542,568,2.749,707,5.341,820,4.934,901,4.714,918,6.16,920,7.655,1793,6.054,2089,7.699,2126,9.11,2132,10.344]],["t/154",[29,3.462,203,2.82,376,5.657,401,3.793,568,2.842,747,4.075,1220,5.693,1672,8.737,1799,7.385,2116,8.082,2128,6.831,2129,9.925]],["t/156",[26,3.597,27,2.3,34,2.468,48,2.953,84,1.05,96,2.563,105,2.276,172,2.521,192,4.287,254,2.34,256,2.723,285,3.007,297,3.98,338,1.79,353,1.984,418,3.638,461,6.097,482,3.254,507,3.958,527,2.54,568,2.084,607,2.789,630,2.688,707,4.049,741,4.713,798,4.003,810,3.231,828,3.035,975,7.983,977,2.654,1285,3.103,1290,4.287,1506,4.67,1567,5.592,1628,5.592,1783,5.5,2089,6.459,2133,6.222,2134,7.842,2135,7.842,2136,7.278]],["t/158",[7,0.975,19,4.035,25,2.108,28,0.929,34,1.677,49,2.035,56,0.65,84,1.121,103,2.203,105,2.059,127,2.159,164,1.809,203,1.576,219,2.038,223,3.707,236,1.731,254,2.117,257,3.352,296,2.537,313,1.608,318,2.52,338,1.619,348,4.225,353,1.795,377,1.472,378,5.629,397,2.737,401,3.461,440,2.903,611,2.737,630,2.431,757,1.622,971,4.827,1037,3.276,1243,5.059,1289,4.188,1290,3.878,1313,5.262,1365,3.801,1413,3.685,1459,6.765,1783,4.976,1799,4.899,1856,5.629,2089,8.246,2113,4.531,2126,6.247,2137,4.827,2138,6.584,2139,7.094,2140,5.996]],["t/160",[7,0.815,12,2.24,16,1.034,21,1.57,23,3.15,24,1.674,29,1.938,31,1.357,49,1.805,56,1.249,84,1.022,93,2.353,96,1.957,98,1.952,107,1.422,109,0.937,110,4.97,123,1.893,158,2.776,174,1.651,203,1.33,207,2.267,219,1.719,232,2.02,256,2.078,285,2.295,318,1.352,338,1.976,342,3.631,376,3.166,377,1.242,395,2.195,398,1.957,401,2.123,438,2.883,491,1.674,527,1.756,536,3.078,553,2.942,630,2.966,655,3.631,685,3.091,744,3.166,757,1.369,818,2.789,831,3.503,843,3.038,846,1.705,875,4.523,886,5.271,917,3.147,985,3.666,993,3.249,1019,2.041,1268,3.915,1672,4.89,1913,3.128,1992,4.198,2000,3.474,2002,4.43,2008,4.629,2009,5.354,2010,3.598,2017,2.338,2089,6.288,2125,5.555,2141,2.841,2142,2.764,2143,5.986,2144,5.06]],["t/162",[7,0.868,16,0.839,23,1.505,24,1.627,31,1.102,56,1.121,84,1.088,90,2.125,96,1.588,98,2.417,107,1.761,109,1.857,110,4.393,123,1.537,132,1.324,160,1.581,165,1.01,170,2.032,174,2.044,182,1.509,183,1.935,186,2.125,192,2.656,193,1.64,196,1.787,197,2.009,219,2.129,223,2.539,228,1.428,241,3.527,245,2.693,275,1.396,277,2.317,280,2.868,281,2.133,285,2.842,296,2.651,313,1.102,338,1.109,342,2.947,353,1.23,372,1.687,408,1.628,426,2.54,478,2.733,507,2.452,527,2.638,536,2.483,568,1.291,578,1.772,607,1.728,613,3.109,630,1.665,635,1.636,685,2.509,724,2.195,756,4.715,757,1.695,814,1.942,818,3.454,820,2.317,827,2.285,908,2.295,941,5.485,944,1.824,945,2.759,963,4.169,966,1.23,975,5.987,984,1.929,1080,2.509,1081,3.103,1142,2.976,1185,3.672,1264,2.947,1265,2.363,1271,2.244,1394,2.868,1462,2.439,1475,2.426,2009,3.005,2017,1.898,2089,5.558,2145,3.855,2146,4.859,2147,4.859,2148,4.509,2149,4.859,2150,4.509,2151,3.595]],["t/164",[7,0.644,23,1.899,24,0.921,25,1.822,26,2.112,27,3.016,28,0.803,34,1.449,56,1.297,84,0.885,109,1.612,110,4.305,127,3.427,132,1.671,179,1.904,294,3.061,295,2.288,315,2.703,318,1.385,338,1.4,482,2.544,507,4.444,527,2.42,596,2.562,607,2.181,663,3.475,846,1.747,916,2.169,945,3.164,975,7.618,1010,3.792,1076,2.049,1459,3.719,1925,4.172,1987,5.759,2148,9.563,2152,5.69,2153,6.131,2154,6.131,2155,6.131,2156,6.131,2157,6.131,2158,6.131,2159,6.131,2160,6.131,2161,6.131,2162,6.131,2163,6.131,2164,6.131,2165,6.131,2166,5.69,2167,6.131,2168,6.131]],["t/166",[7,0.699,12,1.399,16,1.31,18,2.46,19,1.546,21,0.98,23,1.158,24,0.907,25,1.792,27,2.804,28,0.993,56,1.264,84,0.96,89,1.295,98,1.219,105,1.085,107,1.433,109,0.944,127,2.649,132,1.644,140,1.359,160,1.963,164,0.953,172,1.202,186,2.638,192,2.043,228,1.773,236,0.912,246,1.312,278,1.243,294,1.866,296,1.337,306,2.486,343,1.991,351,1.255,372,1.298,377,0.776,426,1.281,440,1.53,447,1.641,482,2.503,507,4.393,511,1.106,524,2.135,527,2.072,550,3.174,558,1.551,562,1.704,596,1.562,601,1.628,607,2.146,610,2.666,630,1.281,635,1.259,698,2.509,757,0.855,760,1.025,778,2.188,807,1.341,810,1.54,818,4.056,829,1.828,871,2.544,882,2.766,901,1.704,914,2.188,917,3.987,919,2.666,933,1.965,934,3.472,944,2.265,975,7.059,977,1.265,979,2.312,989,1.953,1019,1.275,1022,1.908,1078,2.509,1118,1.465,1144,1.887,1185,2.825,1285,1.479,1302,2.103,1331,2.226,1365,2.003,1413,1.942,1459,3.659,1579,2.891,1628,2.666,1779,2.336,1781,1.89,1783,2.622,1857,2.415,1867,3.292,1868,2.966,1871,3.292,1925,2.544,1953,3.469,1956,3.469,1958,3.469,1971,3.469,1972,5.599,1978,2.666,1987,2.445,2081,2.476,2090,3.16,2117,2.666,2136,3.469,2152,3.469,2166,3.469,2169,3.16,2170,3.738,2171,3.738,2172,3.738,2173,3.738,2174,3.738,2175,3.738,2176,2.891,2177,3.738,2178,3.738,2179,3.16,2180,3.738,2181,3.738,2182,5.161,2183,3.738,2184,3.738,2185,3.738,2186,3.738,2187,3.738,2188,3.738,2189,3.738,2190,1.942,2191,2.289,2192,3.738,2193,3.16,2194,3.054]],["t/168",[2,1.43,7,0.849,15,1.934,21,2.194,23,1.771,24,1.258,27,2.555,46,2.198,56,0.767,84,1.096,90,3.66,91,4.42,92,2.876,93,2.247,105,1.659,109,0.894,127,1.739,130,3.023,133,2.269,138,2.139,144,2.54,145,2.133,172,2.69,174,1.576,177,3.75,186,2.499,200,4.552,219,1.642,272,2.171,278,1.9,283,1.534,285,2.191,339,3.917,372,2.906,377,1.186,393,2.605,416,2.442,463,3.191,468,3.374,482,3.473,502,1.462,505,2.198,527,1.698,536,2.313,565,1.732,572,2.809,577,2.509,614,0.976,630,2.868,632,2.853,659,3.435,677,2.794,757,1.307,759,1.859,807,2.05,810,3.448,818,4.613,833,3.023,846,1.628,933,3.005,975,5.896,1007,3.435,1015,3.146,1070,3.023,1081,3.65,1219,2.363,1265,2.78,1303,3.404,1394,3.374,1403,4.229,1511,3.467,1633,2.499,1781,1.791,1993,4.535,2081,3.785,2089,5.177,2097,5.304,2195,5.715,2196,4.42,2197,5.715,2198,3.835,2199,4.669,2200,4.535,2201,4.42]],["t/170",[7,0.889,18,3.432,29,2.725,56,1.002,105,2.443,109,1.317,192,4.601,200,3.869,213,3.178,268,7.412,306,3.468,313,1.908,376,4.452,377,1.747,390,3.771,393,3.836,416,3.596,418,3.904,447,4.803,481,4.666,484,3.309,507,4.248,630,2.885,677,4.115,765,4.633,818,3.922,820,4.014,823,6.36,882,6.228,918,5.012,944,3.159,1245,6.36,1246,4.968,1394,4.968,1479,5.376,1480,5.903,1481,4.179,2089,5.206,2202,6.002,2203,8.416,2204,7.114]],["t/172",[7,0.758,14,5.349,21,3.059,24,1.164,29,2.508,31,2.348,56,0.71,84,0.779,101,3.826,107,1.84,109,1.949,110,3.787,123,3.275,134,3.577,200,3.561,216,3.66,221,2.655,228,3.43,267,6.146,277,3.695,294,3.867,302,3.31,395,3.798,483,4.534,527,2.101,568,2.752,580,2.496,593,3.546,601,3.374,605,2.601,611,2.988,635,2.608,683,2.69,756,3.374,770,5.524,771,7.189,982,4.848,994,3.095,1105,5.623,1206,3.826,1246,4.573,1793,4.534,2205,7.189,2206,7.189]],["t/174",[1,2.839,7,0.86,8,2.628,16,0.909,21,1.38,24,1.884,31,1.785,48,1.982,49,1.642,56,1.078,84,1.052,98,2.567,99,3.08,105,1.528,107,1.25,109,1.476,110,4.612,116,1.823,123,2.983,124,3.08,130,2.784,133,3.126,160,1.712,164,1.342,171,1.327,174,1.452,197,2.176,221,2.698,228,1.547,236,1.284,247,2.146,254,2.349,257,2.487,272,2,279,2.11,282,3.974,285,3.019,291,2.877,311,3.977,317,2.441,322,1.528,334,2.767,351,1.768,359,2.154,377,1.634,395,3.459,397,2.03,527,2.386,528,2.573,536,2.176,565,1.595,568,1.399,578,1.919,591,2.784,605,1.768,607,1.872,614,0.899,622,2.464,655,3.193,658,2.339,677,2.573,755,2.2,757,1.204,759,1.712,795,3.193,824,3.581,846,1.499,848,4.449,945,2.896,974,2.983,993,3.541,1019,1.795,1099,4.042,1793,3.08,2010,3.62,2207,5.263,2208,3.486,2209,5.263,2210,3.821]],["t/176",[7,0.758,16,1.069,28,0.811,41,2.606,49,1.291,56,1.224,84,0.622,105,1.797,107,2.461,109,1.949,110,4.335,123,1.958,124,3.623,127,3.153,132,1.687,164,2.641,165,1.287,170,1.697,171,1.561,193,2.09,219,1.778,223,3.235,228,1.82,272,2.352,291,3.384,296,2.214,315,2.729,318,1.399,322,2.574,338,1.413,408,2.971,447,2.718,517,3.483,527,2.528,567,2.304,593,2.834,596,2.588,614,1.058,636,3.235,663,3.509,814,2.474,837,3.483,846,2.526,934,2.834,944,2.324,945,3.181,989,3.235,1099,2.127,1182,3.925,1264,3.755,1959,2.74,2010,3.346,2182,4.213,2208,5.873,2211,7.667,2212,6.191,2213,5.452,2214,3.72,2215,5.233,2216,8.229]],["t/178",[2,0.781,5,1.7,7,0.228,12,1.168,24,0.469,28,0.876,29,1.01,31,1.769,36,1.277,56,1.219,84,0.942,103,1.615,105,1.51,107,1.236,109,1.629,110,1.525,115,1.333,127,0.95,132,1.823,138,1.168,139,2.785,145,1.941,160,1.692,164,1.326,170,3.33,172,1.003,174,0.861,196,1.148,203,1.155,224,1.227,225,1.725,228,0.917,233,1.048,236,2.116,240,1.584,246,1.095,254,1.552,257,1.474,279,1.251,292,1.259,296,1.116,313,1.179,315,1.375,317,2.413,318,0.705,338,1.187,359,1.277,377,1.08,398,1.02,407,1.309,410,1.911,426,2.292,437,1.359,502,1.711,527,1.357,530,1.295,560,1.541,565,2.027,578,1.138,593,3.061,601,1.359,607,1.11,614,0.889,630,1.069,647,1.086,685,1.611,696,2.04,712,1.558,724,1.41,731,1.518,747,1.189,756,1.359,757,0.713,759,3.047,760,3.466,769,2.385,773,2.265,827,1.467,837,1.755,857,1.314,934,4.547,944,1.952,945,0.958,966,0.79,977,1.76,994,2.673,1040,3.791,1062,1.392,1099,1.787,1118,1.223,1132,2.143,1192,1.796,1219,1.29,1265,4.556,1271,2.402,1312,1.993,1329,2.222,1347,2.016,1375,1.755,1508,1.602,1530,1.742,1754,1.231,1959,3.454,1988,1.211,2010,1.008,2099,1.742,2142,3.604,2182,3.539,2190,1.621,2208,3.445,2213,4.581,2217,2.225,2218,2.094,2219,3.12,2220,5.201,2221,3.12,2222,3.12,2223,2.476,2224,3.12,2225,6.873,2226,2.358,2227,3.361,2228,2.016,2229,2.309,2230,2.476,2231,1.911,2232,2.309,2233,3.12,2234,2.309,2235,2.225,2236,2.476,2237,2.637,2238,2.476,2239,4.127,2240,2.225,2241,2.476,2242,2.476,2243,2.896,2244,2.476,2245,2.358,2246,4.949,2247,2.04,2248,3.12,2249,2.549,2250,2.174,2251,2.225,2252,2.155,2253,1.755,2254,1.495,2255,3.12,2256,3.286,2257,2.016]],["t/180",[2,1.184,6,3.175,7,0.647,15,1.601,16,1.254,21,1.241,24,0.711,26,1.63,28,1.539,56,1.077,84,1.135,103,1.469,107,3.069,109,1.674,110,2.313,132,1.98,138,1.771,163,1.74,165,1.838,170,2.424,174,1.305,182,2.255,183,1.885,190,2.257,213,1.787,219,1.359,228,1.391,236,1.155,246,1.66,289,1.735,296,1.692,316,1.956,317,2.195,318,1.069,322,2.108,338,1.08,360,3.133,363,3.133,377,0.982,407,1.985,418,2.195,426,1.622,441,2.388,476,1.956,491,1.323,511,1.4,513,3.176,525,2.069,527,0.96,536,2.956,562,2.156,565,3.562,567,3.289,568,1.93,593,3.324,605,2.439,662,2.552,672,2.586,712,2.362,743,2.898,757,1.661,759,1.539,760,2.424,769,1.687,772,5.414,837,2.661,846,1.348,917,2.487,934,2.166,993,1.776,994,2.902,995,2.443,1007,2.843,1062,2.111,1076,1.581,1099,1.626,1271,2.185,1285,1.872,1312,3.022,1375,2.661,1462,3.646,1540,2.429,1733,2.623,1959,2.094,1978,3.374,2006,3.575,2010,1.528,2012,3.22,2133,3.754,2182,3.22,2190,2.457,2213,4.167,2258,3.999,2259,2.927,2260,4.731,2261,4.731,2262,4.731]],["t/182",[4,1.254,5,1.424,7,0.498,16,2.095,21,2.2,24,1.64,26,1.501,27,0.96,28,1.589,29,1.41,36,1.783,56,0.943,84,0.685,97,1.808,99,3.987,103,1.353,107,1.619,109,1.942,132,3.594,133,3.331,139,1.814,145,1.626,160,1.417,162,2.222,164,2.139,165,1.416,170,3.468,182,1.353,193,3.202,197,1.801,203,0.968,206,4.094,224,2.679,228,2.466,232,1.47,236,1.662,275,1.251,289,1.598,292,1.758,296,1.558,305,2.572,316,1.801,317,2.021,340,2.695,353,1.724,405,1.286,430,1.994,480,2.752,484,1.713,508,2.381,527,0.884,556,4.043,578,1.589,593,3.118,607,1.549,613,1.827,623,1.936,632,2.175,635,2.294,636,2.276,647,2.921,663,2.469,666,3.162,690,2.508,696,2.849,747,1.66,756,1.898,759,1.417,760,1.194,934,1.994,1021,2.752,1063,5.566,1093,2.29,1121,2.451,1145,3.219,1266,1.753,1285,2.696,1312,2.783,1331,4.057,1397,3.559,1456,2.13,1467,2.529,1959,1.928,2182,2.964,2194,5.566,2208,2.885,2225,3.837,2263,3.292,2264,3.008,2265,4.356,2266,3.837,2267,3.837,2268,2.305,2269,3.369,2270,4.356,2271,2.885,2272,3.008,2273,4.356]],["t/184",[2,2.273,16,1.568,21,2.381,24,1.893,28,1.189,56,0.832,84,1.155,109,1.797,132,3.13,165,1.888,177,4.069,179,2.82,182,2.82,199,4.157,228,2.67,236,2.216,285,3.482,372,3.153,446,5.361,491,2.54,511,2.687,536,3.174,614,1.551,759,2.954,802,3.409,917,4.774,1076,3.035,1089,5.147]],["t/186",[7,0.742,16,1.298,49,1.568,56,1.177,107,2.411,109,1.925,110,6.282,132,2.049,140,3.69,170,2.061,174,2.799,236,1.834,257,3.551,285,3.891,292,3.034,296,2.688,322,2.946,343,2.481,407,3.153,511,2.224,527,2.607,557,3.471,593,3.44,603,5.964,644,6.14,846,2.141,859,4.888,945,3.116,977,3.434,1099,2.583,1142,4.603,1467,4.362,1988,2.918,2010,2.428,2208,4.977,2216,6.975,2274,5.044,2275,5.812,2276,4.001]],["t/188",[7,0.411,24,0.844,28,0.735,36,2.297,50,3.048,56,1.302,84,0.985,98,1.831,103,2.565,109,1.884,130,4.369,132,1.53,139,2.338,160,2.687,165,1.167,170,2.687,186,2.455,233,1.885,236,2.015,278,1.867,279,2.251,292,2.266,338,2.237,474,4.242,508,3.069,513,2.455,544,3.258,578,2.047,580,1.809,593,2.57,601,2.445,611,3.186,635,1.89,702,3.508,724,2.537,759,1.826,760,2.963,765,3.09,934,4.946,994,2.243,1040,3.182,1100,2.065,1144,2.833,1192,3.232,1265,2.731,1508,4.24,1530,3.135,1539,3.473,1959,4.782,2142,3.814,2208,5.47,2225,7.273,2229,4.154,2230,4.454,2231,3.438,2232,4.154,2234,4.154,2235,4.003,2236,4.454,2238,4.454,2239,6.553,2240,4.003,2241,4.454,2242,4.454,2243,5.21,2244,4.454,2245,4.242,2277,6.124,2278,5.614,2279,5.21,2280,5.614]],["t/190",[7,0.884,24,1.817,84,0.991,174,2.72,179,3.062,182,3.062,199,4.513,213,3.724,233,3.312,236,2.406,316,4.077,536,3.341,593,4.513,759,3.208,857,4.151,882,7.297,963,5.547,1089,5.589,1508,6.206]],["t/192",[2,2.292,7,0.67,16,1.581,26,2.231,28,1.391,49,1.351,56,0.593,84,1.068,98,2.112,101,3.199,103,2.011,107,2.175,109,1.662,110,3.166,132,2.895,153,3.074,156,4.091,157,3.47,160,2.107,165,1.903,170,2.511,197,3.786,213,2.446,216,3.06,221,2.22,222,5.703,228,1.904,232,2.186,239,3.448,255,4.048,283,1.738,313,1.468,317,3.004,318,1.463,351,2.175,353,2.688,369,3.729,391,2.53,500,2.843,527,1.314,558,2.688,562,2.952,568,1.721,583,3.671,593,4.191,607,3.257,628,3.104,696,4.235,712,3.233,823,4.894,858,2.964,1251,4.289,1266,2.605,1287,4.407,1290,3.54,1312,4.137,1497,5.291,1504,4.235,1540,3.325,1790,5.008,1932,4.235,2210,4.701,2250,2.707,2281,6.476,2282,5.139,2283,6.476,2284,6.476,2285,6.476,2286,6.476,2287,6.476,2288,6.476]],["t/194",[7,0.934,16,1.347,23,1.61,24,0.781,27,1.718,28,1.021,29,1.683,31,2.123,40,2.541,49,1.084,56,0.476,84,0.784,89,2.701,109,0.813,123,2.961,156,3.284,158,2.411,160,1.691,165,1.081,172,1.671,182,1.614,187,3.359,216,2.456,219,2.241,221,1.782,226,2.339,229,2.861,232,2.633,246,1.824,247,3.181,273,3.847,278,1.729,279,2.084,283,2.512,298,2.946,313,1.179,318,1.175,332,2.504,338,1.187,341,2.653,343,1.716,353,1.974,375,2.803,376,2.75,395,1.906,476,1.4,478,2.924,491,1.454,500,2.282,527,1.9,530,2.157,531,2.924,545,2.23,552,1.707,555,2.282,558,2.157,562,2.369,565,1.575,567,1.934,568,1.381,578,1.896,584,4.02,585,3.646,593,2.379,597,2.491,604,3.184,614,0.888,626,4.394,630,1.782,658,2.31,677,2.541,683,1.805,719,2.903,726,3.153,760,1.425,802,1.951,805,3.284,818,2.422,833,2.75,867,3.707,876,3.042,904,3.184,918,3.096,942,3.537,1053,4.394,1141,3.443,1142,3.184,1145,2.456,1193,3.359,1253,2.882,1266,2.091,1269,4.394,1283,3.847,1287,3.537,1290,2.842,1688,3.284,1849,2.624,2090,4.394,2250,3.913,2289,5.04,2290,4.247,2291,5.198,2292,4.578,2293,2.822,2294,5.198,2295,4.394,2296,4.247,2297,4.02,2298,3.928,2299,4.394]],["t/196",[7,0.902,28,1.509,55,5.817,84,0.76,88,3.16,89,4.271,160,2.46,203,1.68,247,3.083,252,5.847,275,2.172,282,3.816,283,3.093,332,3.641,338,1.726,474,5.714,489,4.677,511,2.237,531,6.483,568,2.009,608,3.641,672,4.133,684,3.659,802,2.838,854,5.145,858,3.461,859,3.641,897,4.425,902,5.392,1088,6.658,1089,4.285,1090,3.115,1257,5.488,1286,5.488,1288,4.191,1411,4.544,2250,4.259,2289,4.885,2300,6.658,2301,7.017,2302,7.561,2303,6.391,2304,7.561,2305,7.561,2306,5.714,2307,6.177,2308,7.561]],["t/198",[2,2.217,5,1.552,7,0.533,18,1.937,24,1.095,28,1.301,33,2.512,41,1.999,56,1.165,84,0.477,105,2.114,132,1.294,134,4.091,155,2.854,160,1.545,167,3.387,182,2.262,190,4.226,197,1.964,213,1.793,228,1.396,236,1.159,241,3.448,246,1.667,247,1.937,249,3.279,283,1.274,285,1.821,313,1.651,323,2.726,338,1.084,353,1.202,365,2.233,366,2.757,376,2.512,377,0.986,392,2.734,397,1.832,405,1.402,426,3.405,440,1.943,471,3.407,484,1.867,502,1.215,513,2.077,527,2.016,541,2.713,614,0.811,724,2.146,732,2.881,759,1.545,760,3.227,763,3,805,3,807,3.177,814,1.898,828,1.838,916,1.68,919,3.387,920,3.514,934,2.174,966,2.242,1005,2.881,1015,2.614,1017,3.145,1038,3,1145,2.244,1182,2.102,1205,3.768,1220,2.528,1265,4.833,1271,2.193,1285,1.879,1288,2.633,1398,3.033,1462,2.384,1754,2.873,1969,4.014,1988,4.159,2226,3.589,2246,7.929,2250,4.478,2264,3.279,2276,3.878,2289,4.706,2292,4.182,2309,4.749,2310,7.284,2311,2.968,2312,4.014,2313,6.76,2314,5.779,2315,4.749,2316,5.779,2317,3.673,2318,4.749,2319,4.749]],["t/200",[2,1.834,5,1.564,7,0.536,21,2.334,24,0.719,27,1.054,28,1.166,31,1.085,34,1.131,36,2.998,42,3.256,68,1.684,84,1.081,93,1.881,95,2.755,98,1.56,107,1.741,109,1.993,123,1.513,144,2.126,145,1.786,165,1.523,172,3.914,177,2.144,182,1.486,190,2.282,193,1.615,203,1.063,253,2.672,254,1.428,280,4.325,281,3.908,289,1.755,302,2.045,304,0.936,313,1.661,323,1.791,326,3.023,336,2.766,338,1.092,359,1.958,361,2.991,363,3.169,377,1.52,398,1.564,403,2.85,409,2.25,418,2.22,437,2.084,439,2.903,471,2.817,476,1.289,481,2.653,484,1.881,491,1.338,527,1.486,552,3.276,592,2.85,602,3.909,613,2.007,614,1.521,677,2.339,760,2.441,774,2.402,812,2.96,920,3.541,944,1.796,974,2.712,984,1.9,1022,2.442,1075,2.328,1077,2.485,1090,1.972,1111,3.092,1270,3.797,1284,3.023,1285,1.894,1325,3.256,1411,2.876,1475,2.389,1479,3.056,1540,2.456,1557,2.755,1689,2.328,1849,5.426,2010,2.875,2250,2,2293,5.837,2320,3.7,2321,3.909,2322,5.059,2323,4.785,2324,4.785,2325,3.797,2326,4.785,2327,3.616,2328,4.045,2329,3.797,2330,4.785,2331,2.825,2332,4.785,2333,3.797,2334,4.441,2335,3.356]],["t/202",[7,0.977,15,2.621,28,1.014,46,4.992,55,5.888,84,0.779,89,4.04,98,2.526,199,3.546,254,2.311,306,4.807,313,1.756,351,2.601,440,4.238,447,3.401,478,4.357,555,3.401,576,4.234,597,3.712,608,3.731,715,4.94,736,3.065,757,1.771,762,4.841,802,2.908,874,6.547,898,3.888,1169,5.99,1184,6.822,1237,4.655,1289,4.573,1322,5.271,1396,5.99,1423,5.198,1778,5.854,2076,4.178,2289,7.539,2336,7.746,2337,7.746]],["t/204",[7,0.671,10,2.845,19,2.344,21,1.887,25,2.138,26,1.953,28,0.453,31,0.785,33,1.831,34,0.818,41,1.457,46,1.331,55,1.747,56,0.995,84,0.57,89,1.963,96,1.132,98,1.129,100,2.352,109,0.887,127,1.054,133,1.374,134,1.599,140,1.259,160,1.844,162,1.129,164,1.445,165,0.72,174,0.955,177,1.551,182,1.075,186,1.514,187,2.237,189,1.719,196,2.085,199,2.594,203,0.769,206,2.08,213,1.307,219,2.83,221,1.942,228,1.018,275,1.628,283,2.232,292,2.288,294,1.728,295,1.292,306,1.426,316,1.431,318,2.925,323,2.121,336,1.307,351,1.163,353,1.434,367,1.636,391,1.352,397,2.186,398,2.719,440,1.417,471,2.18,472,3.758,476,2.241,478,1.947,482,1.437,506,3.961,510,2.677,512,1.813,514,2.428,527,1.15,531,1.947,566,2.237,567,3.666,578,1.262,593,1.585,611,1.335,613,2.377,623,1.538,653,2.102,675,2.044,683,1.202,703,2.562,707,2.926,723,3.45,734,1.421,736,2.243,757,2.253,802,1.3,814,1.383,833,2.998,916,3.84,945,1.063,988,1.571,993,1.3,1017,2.293,1019,1.933,1035,1.919,1038,2.187,1051,3.238,1078,2.323,1080,1.787,1090,2.335,1098,2.026,1206,1.71,1219,2.344,1238,2.469,1288,3.142,1297,2.55,1328,2.743,1332,2.141,1396,2.677,1399,1.933,1418,2.828,1456,2.771,1511,2.1,1638,1.659,1672,2.828,1913,1.809,2017,3.584,2031,4.13,2141,2.69,2200,2.747,2259,2.141,2289,4.65,2290,2.828,2321,2.828,2331,2.044,2338,2.893,2339,1.919,2340,1.977,2341,2.616,2342,3.213,2343,2.677,2344,2.747,2345,3.213,2346,2.677,2347,3.462,2348,4.991,2349,3.462,2350,2.356,2351,3.213]],["t/206",[2,1.756,7,0.811,13,5.093,15,2.374,17,2.573,21,2.539,24,1.054,28,1.268,33,3.711,41,2.953,84,0.705,98,2.288,103,2.178,160,2.282,179,2.178,199,3.211,219,2.781,228,2.062,275,2.781,318,2.188,321,4.92,322,2.036,381,3.08,390,3.143,397,2.706,407,2.943,438,3.379,471,2.698,495,3.143,578,2.558,614,1.199,675,4.141,702,4.384,723,2.327,724,3.17,736,2.776,800,6.511,802,2.633,885,4.844,897,4.106,916,2.482,944,2.633,968,6.511,993,2.633,1019,3.78,1038,4.432,1098,4.106,1291,5.003,1292,4.384,1400,3.379,1401,5.293,1508,3.601,2116,5.301,2348,8.527,2352,4.646,2353,6.178,2354,6.511,2355,9.764,2356,6.511,2357,7.015,2358,7.015,2359,4.533]],["t/208",[7,0.79,16,1.425,84,0.829,171,2.08,219,3.102,261,3.88,277,3.936,283,2.898,317,3.827,318,2.44,377,1.713,437,3.594,471,4.153,562,3.761,567,3.07,578,3.009,591,4.365,736,4.273,757,2.469,798,4.212,906,4.959,993,3.097,1224,2.814,1401,4.511,1426,5.884,2253,4.642,2360,10.022,2361,9.621,2362,7.604,2363,8.251,2364,7.658,2365,8.251,2366,8.251,2367,8.251]],["t/210",[7,0.837,17,2.327,24,1.356,29,2.055,31,1.439,34,1.5,49,1.324,55,3.203,56,0.827,84,1.263,85,2.04,103,1.971,105,1.842,109,0.993,162,2.07,170,2.88,177,2.844,179,2.803,228,2.653,236,1.549,275,1.823,283,1.703,285,2.433,322,2.62,338,1.449,351,2.131,377,1.317,398,2.95,470,4.259,527,1.288,605,2.131,614,1.794,683,2.204,759,2.065,760,3.543,811,3.654,833,3.357,892,2.204,906,3.814,1019,2.164,1145,4.264,1286,4.607,1633,2.775,1698,4.382,1754,2.503,2013,1.962,2015,3.469,2227,4.101,2289,4.101,2368,8.62,2369,5.364,2370,5.589,2371,5.036,2372,5.589,2373,6.346,2374,9.748,2375,5.89,2376,4.796,2377,4.908,2378,5.89,2379,5.185,2380,5.89,2381,5.89]],["t/212",[7,0.873,10,2.745,15,1.85,16,1.399,21,1.434,29,2.623,49,2.575,85,3.102,160,1.779,174,1.508,182,1.698,186,2.391,213,2.065,276,2.514,277,5.433,336,2.065,353,2.05,359,2.237,361,3.417,365,2.571,393,2.492,398,3.899,400,3.969,401,3.422,437,2.382,471,2.103,498,2.823,527,1.109,536,1.511,557,2.525,569,4.339,608,2.633,611,2.109,614,1.648,647,2.82,684,3.921,709,3.317,820,2.608,831,3.2,840,2.302,888,3.96,1008,3.031,1089,3.099,1175,4.123,1219,2.261,1285,2.164,1297,2.46,1411,3.286,1632,2.633,1691,4.467,1698,3.776,1753,7.276,1754,2.157,1913,2.857,2010,1.766,2015,2.989,2268,2.893,2289,3.533,2321,4.467,2345,5.075,2359,3.533,2382,3.835,2383,9.649,2384,3.454,2385,4.228,2386,5.468,2387,5.88,2388,2.66,2389,4.622,2390,5.88,2391,3.776,2392,5.468,2393,5.468,2394,10.668,2395,5.468,2396,5.468,2397,5.468,2398,5.468,2399,5.468,2400,5.468,2401,5.075]],["t/214",[2,1.125,5,1.47,7,0.916,16,0.776,21,1.179,23,3.237,24,1.662,25,2.075,27,1.886,28,0.914,29,1.456,31,1.019,33,2.378,34,1.063,56,0.958,84,1.112,89,2.964,100,1.866,105,1.305,107,1.068,109,1.093,127,1.368,140,1.635,155,2.702,162,1.466,163,1.654,169,3.544,174,1.24,177,2.014,182,1.396,190,2.144,193,1.517,196,1.654,198,2.031,248,1.958,273,3.327,275,1.291,278,1.495,283,1.874,302,1.921,304,0.879,321,3.153,336,2.637,347,2.754,353,1.138,365,2.114,367,2.124,377,1.449,382,2.588,393,2.049,407,2.929,502,1.15,506,2.475,511,1.33,512,2.234,555,1.974,558,2.897,560,2.221,568,1.195,628,2.155,630,1.541,685,2.321,723,2.316,736,1.779,755,1.879,763,2.84,807,1.612,827,2.114,830,2.961,838,2.104,876,4.087,945,1.38,984,1.785,1019,1.533,1038,2.84,1181,2.441,1182,1.99,1285,1.779,1289,2.654,1302,2.529,1370,3.327,1400,2.165,1401,2.458,1409,2.754,1413,2.335,1511,2.727,1773,3.153,1843,2.81,2076,2.425,2250,2.918,2289,2.905,2341,3.397,2359,4.511,2402,3.971,2403,5.395,2404,6.674,2405,8.956,2406,6.993,2407,4.172,2408,3.959,2409,4.172,2410,3.673,2411,4.172,2412,4.172,2413,4.172,2414,4.172,2415,4.172,2416,3.959,2417,6.48,2418,4.496,2419,3.477,2420,3.567,2421,2.492,2422,2.677]],["t/216",[4,2.745,7,0.802,17,2.517,18,2.799,23,2.126,28,0.899,55,4.813,56,0.874,84,0.959,162,2.238,164,1.75,165,1.427,187,4.435,213,2.592,219,1.971,221,2.352,227,4.203,232,2.317,247,2.799,257,3.242,275,2.739,277,3.273,282,3.464,283,1.842,284,4.289,302,2.933,303,4.982,315,3.025,318,1.551,320,5.077,323,2.569,341,3.503,353,2.413,484,2.698,491,1.92,500,3.013,509,3.608,522,4.739,530,2.848,545,5.079,552,3.132,558,2.848,559,7.206,560,4.711,608,3.305,684,5.304,765,3.778,794,4.435,881,3.983,908,3.242,1017,4.545,1178,3.677,1220,3.654,1237,4.124,1313,3.701,1330,4.163,1336,4.163,2223,5.446,2423,6.863,2424,5.801,2425,6.863,2426,4.289]],["t/218",[2,1.613,7,0.889,18,3.72,31,1.461,56,0.971,85,2.071,123,2.038,129,5.058,144,2.863,160,2.096,167,4.595,174,2.516,213,2.433,215,4.449,233,3.557,240,3.27,248,2.807,275,2.621,279,2.583,291,3.522,292,2.601,318,1.456,351,2.164,361,4.027,393,2.936,440,2.637,441,3.252,444,2.592,471,3.509,498,3.327,502,1.649,505,3.509,526,4.324,527,1.307,540,7.239,591,3.409,611,2.486,628,4.373,633,4.677,677,3.15,683,2.237,733,5.264,740,2.703,763,4.07,802,2.419,927,3.946,982,3.016,1090,4.364,1213,4.324,1233,4.869,1268,4.214,1310,4.214,1314,5.763,1332,3.986,1365,3.452,1419,4.677,1457,5.674,1463,5.98,1500,4.519,1593,5.674,2086,4.384,2116,4.869,2427,7.454,2428,8.034,2429,6.443,2430,3.68,2431,4.267,2432,5.98,2433,6.443,2434,4.07]],["t/220",[7,0.637,18,3.55,29,2.819,249,6.012,261,4.094,278,2.895,306,3.587,353,2.203,359,3.562,376,4.606,377,1.807,394,4.038,409,4.094,415,5.694,484,3.423,514,6.106,684,4.214,721,6.908,802,4.197,814,3.479,858,3.985,975,5.185,1206,5.523,1283,6.443,1288,4.826,1329,3.72,1365,4.665,1417,8.08,1479,5.561,1481,4.323,1527,5.332,1829,6.32,2289,7.98,2435,8.08,2436,8.706,2437,7.667]],["t/222",[7,0.881,16,1.085,23,1.947,24,0.944,26,2.165,27,2.301,31,1.425,40,4.381,84,0.901,92,2.159,107,1.493,109,1.635,115,2.685,123,1.987,127,3.466,167,4.481,197,2.598,219,1.805,221,2.153,236,1.533,244,3.589,275,1.805,280,5.29,281,2.759,313,1.425,316,2.598,318,2.36,343,2.074,474,4.748,476,1.693,502,2.293,513,2.748,527,1.275,540,4.986,541,3.589,550,3.809,555,2.759,562,2.864,566,4.06,567,2.338,568,1.67,572,3.088,593,2.876,605,3.01,616,5.533,617,4.65,619,3.742,650,3.927,689,5.311,705,3.589,740,2.636,757,1.437,764,5.601,802,2.359,810,2.589,859,3.026,876,3.677,901,2.864,981,3.927,1022,5.332,1188,3.709,1285,2.486,1289,3.709,1787,5.311,2113,4.013,2329,4.986,2438,5.533,2439,4.561,2440,6.283,2441,6.283,2442,4.859,2443,5.533,2444,3.887]],["t/224",[7,0.88,34,2.312,85,3.145,246,3.433,247,3.99,286,5.504,318,2.211,562,4.459,747,3.728,757,2.237,820,4.667,823,7.394,1246,5.776,1313,5.277,1632,5.795,1794,8.27,1923,4.833,2013,3.024,2359,7.774,2445,9.784,2446,9.08]],["t/226",[7,0.835,16,0.948,25,2.415,26,1.892,27,1.21,29,1.778,34,1.921,49,1.695,56,0.886,84,0.817,85,1.765,97,3.372,99,3.214,103,2.523,105,2.359,106,1.868,135,2.905,164,1.4,199,2.514,219,1.578,221,1.882,223,2.87,225,1.822,226,2.471,236,1.983,246,1.927,247,2.239,279,2.202,283,2.596,294,4.057,311,4.15,314,4.064,316,2.271,338,1.254,343,2.682,353,1.39,376,2.905,390,2.461,395,2.98,401,1.948,416,3.472,465,3.548,471,2.112,476,2.88,484,2.159,532,3.933,578,2.003,580,1.77,607,2.89,611,2.119,614,0.938,757,1.256,809,3.508,855,3.137,876,4.756,881,3.188,1100,2.02,1172,3.066,1186,3.916,1224,1.873,1257,3.987,1290,3.002,1394,3.242,1566,7.913,1793,3.214,1803,5.097,1857,6.25,1923,2.713,2253,3.089,2438,4.836,2442,10.757,2447,5.097,2448,3.188,2449,4.836,2450,3.397,2451,7.675]],["t/228",[7,0.889,16,1.26,27,1.608,41,3.073,48,2.748,49,2.075,86,4.611,92,3.419,99,4.272,101,3.605,105,2.119,127,3.028,219,2.858,221,2.502,277,3.481,292,2.946,318,2.557,343,2.409,372,3.454,377,1.515,398,3.252,401,2.589,476,1.966,482,4.129,527,1.481,578,2.662,659,4.386,780,3.605,1413,5.167,1566,8.05,1857,7.314,2017,2.851,2031,3.624,2138,9.233,2141,3.465,2346,5.644,2442,5.644,2447,6.774,2451,5.791,2452,8.128,2453,7.299,2454,7.299,2455,7.299,2456,6.774,2457,7.299,2458,7.299,2459,5.298,2460,6.774]],["t/230",[7,0.508,15,3.251,16,1.198,24,1.444,27,2.117,34,2.271,40,3.392,56,0.636,84,0.966,85,3.089,90,3.034,92,4.77,97,3.987,99,4.061,100,2.879,107,1.648,109,1.504,115,2.965,127,2.112,164,1.769,219,1.993,247,2.829,278,2.307,296,2.481,318,1.568,332,3.342,372,3.827,405,2.048,471,2.668,476,1.869,505,2.668,723,2.301,740,2.91,818,4.477,846,1.977,1365,3.718,1577,4.866,1802,5.405,1857,7.122,1923,3.427,2442,9.661,2451,5.505,2456,6.439,2461,6.938,2462,8.122,2463,6.938,2464,5.037,2465,6.938,2466,6.938]],["t/232",[23,3.128,27,2.224,28,1.322,34,2.386,40,4.936,92,3.469,105,2.931,155,6.068,283,2.709,372,3.506,530,4.19,557,4.662,740,4.235,828,3.907,901,4.601,1225,6.972,2091,7.2,2092,6.775,2093,6.378,2094,6.775]],["t/234",[7,0.782,27,2.356,127,3.864,318,2.416,562,4.874,568,2.842,1022,5.458,1188,6.313,1418,8.737,2272,7.385,2443,9.418,2467,8.737]],["t/236",[2,1.245,7,0.667,21,1.304,27,2.409,28,1.332,30,6.682,31,2.61,32,2.887,40,3.689,56,0.456,84,0.916,103,2.343,123,3.459,127,1.514,132,2.056,134,3.484,138,2.824,153,2.361,164,1.268,165,1.568,174,2.081,175,2.524,184,3.488,185,3.177,224,2.966,228,1.462,245,2.757,254,1.484,272,1.889,275,1.429,313,1.71,318,1.124,320,2.648,323,1.861,343,1.642,366,2.887,383,3.214,394,2.307,397,1.919,407,2.086,416,2.125,430,2.276,458,2.599,479,3.214,491,1.391,500,2.183,525,3.299,530,2.064,550,1.814,565,1.507,568,1.322,596,3.153,630,1.705,631,3.017,647,3.542,650,3.108,664,3.384,690,2.863,706,3.294,729,3.294,743,3.046,750,3.252,763,3.142,802,1.867,805,3.142,810,2.049,814,1.987,831,2.911,837,2.798,858,3.453,859,2.395,881,2.887,939,2.631,1022,2.538,1080,2.568,1090,4.507,1188,4.454,1219,2.057,1236,3.076,1271,3.484,1298,4.204,1303,2.962,1312,3.177,1323,3.177,1781,1.558,2190,2.583,2193,4.204,2254,2.384,2350,3.384,2468,4.973,2469,3.946,2470,4.38,2471,3.488,2472,3.434,2473,4.973,2474,8.46,2475,4.973,2476,4.204,2477,4.973,2478,4.973,2479,3.61]],["t/238",[0,7.094,7,0.742,15,2.543,21,3.013,27,2.917,28,0.984,40,3.674,56,0.93,86,4.748,97,3.119,100,3.119,105,2.182,109,1.176,127,2.287,165,2.11,174,2.073,177,3.368,179,2.334,198,3.396,203,1.67,236,1.834,256,2.61,272,2.855,302,3.211,318,1.698,343,2.481,359,3.075,377,1.56,437,4.421,478,4.228,552,2.469,617,7.51,670,4.053,678,6.619,811,4.327,1022,3.836,1036,5.271,2402,3.486,2442,8.885,2460,6.975,2471,5.271,2480,7.516,2481,9.418]],["t/240",[0,4.606,2,1.484,7,0.811,8,2.96,21,2.254,25,1.762,27,2.227,31,1.949,36,2.426,39,2.239,56,0.543,70,3.927,84,1.115,86,3.746,92,2.954,94,2.976,96,1.938,106,3.772,127,3.077,164,1.512,165,2.447,175,3.009,199,2.714,231,3.979,293,4.388,302,3.673,318,1.34,343,1.957,359,2.426,372,2.985,395,2.175,397,2.288,398,1.938,408,3.387,440,3.517,476,1.597,506,4.732,589,5.012,617,6.361,650,6.931,677,4.203,678,8.905,707,3.062,740,2.487,757,1.966,1022,3.027,1159,6.361,1174,6.931,1220,3.157,1302,3.336,2320,4.585,2471,9.097,2481,7.978,2482,5.222,2483,5.503,2484,6.13,2485,5.503,2486,5.93,2487,4.705,2488,5.93]],["t/242",[7,0.848,56,0.844,127,2.804,133,3.658,145,4.732,153,4.374,232,3.11,247,3.757,313,2.089,316,3.81,320,4.905,336,3.479,405,3.928,426,3.158,436,6.102,447,4.045,495,4.128,821,5.953,977,3.118,984,3.658,1022,4.703,1108,4.374,1402,3.998,2113,5.885,2442,8.963]],["t/244",[0,3.844,4,2.065,7,0.821,27,2.658,30,4.164,31,1.627,56,0.657,84,1.128,92,2.465,96,3.214,101,3.544,105,2.083,123,2.269,127,3.414,145,2.677,174,1.979,179,2.228,201,3.39,252,5.548,278,2.386,281,3.15,318,1.621,332,3.455,343,2.368,372,2.491,405,2.118,409,3.374,416,3.066,540,5.693,541,4.098,555,3.15,605,2.41,611,2.768,616,6.318,617,5.309,619,4.273,650,4.484,689,6.064,750,4.692,876,4.199,981,6.145,1022,5.019,1174,4.484,1186,5.116,1219,2.967,1289,4.235,1297,3.228,1418,5.862,1779,4.484,1857,4.636,2113,4.583,2321,5.862,2442,7.604,2471,5.032,2474,6.658,2489,7.175,2490,7.175,2491,5.309]],["t/246",[0,2.246,2,1.049,4,0.697,7,0.852,8,1.209,14,2.895,16,0.957,18,2.261,20,1.405,21,1.454,22,1.394,23,1.299,24,0.364,25,1.647,27,1.221,28,0.317,29,0.784,31,1.499,32,2.433,33,1.281,34,0.991,36,0.991,39,0.914,56,0.685,84,0.883,93,1.648,96,1.812,97,1.74,101,1.196,105,0.703,106,2.248,107,0.996,109,0.868,110,1.184,115,1.791,123,2.091,134,1.118,135,1.281,138,0.906,153,1.99,156,1.529,160,0.788,164,1.069,165,1.153,170,0.664,172,0.778,174,0.668,177,1.085,186,1.059,187,1.564,190,1.155,207,1.588,215,3.829,216,1.981,220,2.395,221,0.83,225,0.803,236,0.591,254,1.251,256,0.841,257,1.144,262,2.543,263,3.891,275,1.204,283,1.488,285,1.607,291,1.323,292,2.238,297,1.229,298,2.376,315,1.848,321,1.698,337,1.046,338,1.266,339,1.133,351,0.813,353,1.061,359,0.991,362,1.758,375,1.306,387,2.709,390,1.878,391,0.946,393,1.911,395,0.888,416,2.369,421,1.405,440,0.991,458,1.265,467,1.758,471,0.931,476,1.129,482,1.005,484,0.952,489,1.498,491,1.173,499,1.564,526,2.813,527,1.341,528,1.184,536,1.159,550,2.022,552,2.171,578,0.883,579,1.625,581,1.625,589,2.046,594,2.247,599,2,601,1.055,605,0.813,611,0.934,620,4.508,628,1.16,630,1.437,632,1.209,638,1.352,642,3.692,655,1.469,696,1.583,708,1.758,712,1.209,719,1.352,724,1.094,730,1.83,736,0.958,737,1.698,747,0.923,757,0.554,759,0.788,761,2.132,765,1.333,770,3.954,774,2.104,798,1.236,810,1.727,814,1.675,818,1.128,825,1.726,827,1.139,833,1.281,855,1.383,856,1.417,858,1.108,871,1.647,884,2.247,885,1.672,887,2.593,901,1.911,933,1.273,944,0.909,982,1.133,984,0.961,988,1.099,1007,1.455,1021,1.529,1035,1.342,1037,1.936,1071,1.978,1112,1.333,1142,1.483,1143,2.132,1174,1.513,1175,1.035,1182,1.072,1202,1.383,1219,1.734,1236,1.498,1238,1.726,1253,1.342,1257,1.758,1258,3.326,1271,1.936,1284,4.176,1297,1.089,1304,1.921,1305,2.046,1365,1.297,1367,3.869,1388,1.921,1401,1.323,1405,3.332,1419,1.758,1457,2.132,1503,1.921,1540,1.243,1778,3.168,2041,1.513,2099,1.352,2199,1.978,2250,1.012,2271,1.603,2290,3.425,2320,3.242,2331,1.429,2384,1.529,2444,1.498,2472,1.672,2482,2.132,2492,2.421,2493,2.046,2494,4.192,2495,8.184,2496,4.192,2497,4.192,2498,4.192,2499,3.326,2500,1.978,2501,1.758,2502,2.421,2503,2.421,2504,8.184,2505,2.421,2506,2.421,2507,2.421,2508,2.421,2509,4.192,2510,1.872,2511,2.421,2512,2.247,2513,2.421,2514,4.192,2515,1.792,2516,2.421,2517,5.544,2518,2.421,2519,4.192,2520,2.421,2521,4.192,2522,2.421,2523,2.421,2524,2.421,2525,1.978,2526,2.421,2527,2.421,2528,2.421,2529,1.647,2530,2.247,2531,2.247,2532,2.421,2533,2.421,2534,1.978,2535,2.421,2536,2.046,2537,4.192,2538,2.247,2539,2.421,2540,2.421,2541,3.425,2542,2.421,2543,2.132,2544,2.421,2545,1.483,2546,1.83,2547,1.921,2548,1.872,2549,1.978,2550,2.421,2551,2.421,2552,2.421,2553,2.421,2554,2.132,2555,2.421,2556,2.421,2557,2.421]],["t/248",[7,0.633,21,2.267,29,2.799,31,1.96,85,2.78,129,4.793,174,2.385,221,3.815,224,3.399,231,7.47,233,4.366,272,3.285,278,2.875,292,4.493,313,1.96,318,1.954,320,4.603,353,2.817,684,5.388,688,7.615,702,5.404,740,3.627,820,4.124,892,3.002,944,3.246,982,4.047,1090,4.587,1239,6.861,1256,7.615,2116,6.534,2289,5.587,2558,8.024]],["t/250",[2,1.001,7,0.838,9,2.403,14,4.396,21,2.08,23,1.973,24,0.601,26,2.193,27,0.881,28,0.524,31,2.051,34,0.945,46,1.538,48,1.505,56,0.366,68,2.791,84,0.909,103,1.242,105,1.161,109,0.626,123,2.013,131,1.683,140,1.454,144,1.777,155,2.403,162,2.076,165,0.831,174,2.495,189,3.94,201,1.889,215,5.478,221,1.37,228,1.175,229,2.201,233,2.664,236,1.936,256,1.388,275,1.149,280,3.758,281,1.755,283,1.708,284,2.499,289,1.466,297,2.029,298,3.609,306,1.647,317,1.855,336,1.51,351,2.138,353,1.012,364,4.164,375,2.156,376,2.115,377,0.83,382,3.666,391,1.562,395,1.466,416,2.721,421,2.321,430,2.914,444,2.561,463,2.233,467,2.902,471,1.538,478,2.249,500,1.755,505,2.449,525,1.748,528,1.955,535,3.38,546,3.092,555,1.755,557,1.846,562,1.822,568,1.692,593,1.83,597,1.916,601,1.742,604,2.449,605,2.138,607,1.422,611,1.542,622,3.713,628,1.916,629,2.36,655,3.862,677,1.955,679,2.902,683,2.211,685,2.064,699,3.521,708,2.902,731,1.945,732,2.425,740,1.677,741,2.403,747,1.524,762,2.499,820,3.037,828,1.547,833,3.368,843,2.029,849,3.711,854,2.721,855,2.284,856,4.643,859,1.926,871,2.721,888,1.955,892,2.211,901,1.822,970,3.172,982,1.872,995,2.064,1017,2.648,1090,1.647,1142,2.449,1144,2.018,1178,2.142,1189,3.172,1214,3.172,1219,1.653,1230,2.804,1245,3.021,1253,2.216,1264,2.425,1285,1.582,1289,2.36,1313,2.156,1387,2.804,1426,2.851,1471,3.267,2076,2.156,2103,3.267,2250,1.671,2269,3.092,2272,2.761,2301,3.711,2353,3.521,2421,3.529,2546,3.021,2559,3.998,2560,5.909,2561,3.172,2562,3.998,2563,3.711,2564,3.998,2565,3.092,2566,3.998,2567,3.998,2568,3.267,2569,2.959,2570,3.711,2571,3.711,2572,3.998]],["t/252",[2,1.198,7,0.536,16,0.826,23,3.09,24,1.101,25,2.177,26,1.649,27,2.197,30,4.252,31,2.437,33,2.531,34,1.731,40,2.339,56,0.438,84,1.003,85,1.538,96,1.564,98,1.56,115,2.045,123,3.154,134,2.21,162,1.56,211,3.304,216,2.261,223,2.5,232,2.473,233,1.607,236,1.788,262,2.903,275,1.375,278,1.591,279,3.569,297,2.429,306,1.972,317,2.22,318,1.655,323,2.742,353,1.211,358,3.7,367,5.079,381,2.101,393,2.181,407,2.007,415,3.129,416,2.045,471,1.84,476,1.289,478,2.692,484,1.881,491,1.338,527,0.971,568,1.947,593,3.353,609,2.903,613,3.073,630,3.418,670,2.581,683,2.544,685,2.471,725,2.261,728,3.092,729,3.169,734,1.965,740,5.107,818,2.23,833,2.531,840,2.015,877,4.045,883,3.909,892,1.661,912,3.7,916,1.693,918,2.85,944,1.796,981,2.991,982,5.699,1038,3.023,1144,2.415,1145,2.261,1193,3.092,1220,2.547,1264,2.903,1265,3.564,1267,3.356,1300,2.402,1324,3.474,1481,2.376,1504,3.129,1505,4.441,1557,2.755,1903,4.214,1993,3.797,2151,3.541,2493,4.045,2573,4.785,2574,4.785,2575,4.441,2576,7.326,2577,3.582,2578,4.785,2579,4.785,2580,4.045,2581,4.785,2582,4.441]],["t/254",[1,2.349,16,1.176,17,1.598,21,1.142,25,2.493,27,1.501,28,0.57,30,2.529,84,0.685,98,1.421,101,2.152,121,3.699,132,1.857,145,3.54,158,3.16,163,2.506,190,2.078,211,3.008,236,1.662,247,1.776,275,1.251,281,1.913,283,1.169,286,3.832,287,4.512,297,2.211,323,1.63,334,2.29,338,0.994,351,1.463,370,3.162,376,2.305,382,2.508,395,1.598,397,3.66,405,1.286,434,2.695,444,1.753,500,1.913,524,2.488,527,2.089,565,1.32,593,1.994,608,2.098,630,2.335,635,2.294,653,1.273,691,2.224,740,4.785,750,2.849,757,0.996,760,2.823,790,2.815,798,4.282,810,2.807,827,2.049,858,1.994,887,5.19,900,3.559,901,3.823,908,2.058,910,2.276,917,2.29,982,6.51,984,1.73,989,2.276,994,1.741,1010,4.214,1142,2.668,1163,2.723,1175,1.861,1178,2.334,1179,2.695,1182,1.928,1196,2.885,1198,2.964,1221,2.815,1230,3.055,1267,3.055,1296,3.457,1300,5.726,1456,3.331,1567,3.107,1806,2.849,1903,3.837,1987,2.849,2099,2.433,2117,3.107,2268,2.305,2271,2.885,2382,4.778,2390,3.162,2430,2.488,2448,2.529,2493,3.682,2529,2.964,2583,4.356,2584,2.849,2585,6.323,2586,3.292,2587,3.457,2588,2.643,2589,4.356,2590,4.043,2591,4.356,2592,4.043,2593,3.457,2594,2.752,2595,2.572,2596,2.594,2597,4.356,2598,2.488,2599,4.356,2600,4.356]],["t/256",[2,1.216,7,0.735,10,2.439,14,3.355,16,0.839,21,1.274,23,1.505,28,1.417,31,1.102,84,0.488,98,1.584,105,1.41,123,1.537,124,2.844,132,2.74,160,1.581,162,2.417,174,2.773,197,2.009,233,1.632,236,2.784,245,2.693,247,1.981,248,2.116,282,2.452,283,1.304,294,2.426,305,2.868,306,3.054,316,2.009,336,2.799,343,1.604,351,1.632,361,3.037,375,2.62,391,1.898,408,1.628,409,2.285,447,2.133,459,3.527,562,2.214,568,1.291,579,3.26,593,2.224,601,2.116,607,1.728,655,2.947,664,3.306,666,3.527,677,2.375,683,1.687,685,4.64,698,3.26,724,2.195,740,3.769,747,1.851,778,2.844,799,3.408,814,2.962,833,3.921,887,6.219,888,2.375,892,1.687,901,3.378,982,4.706,985,2.976,995,2.509,1072,2.084,1145,2.295,1175,2.076,1196,3.218,1198,3.306,1200,3.757,1206,3.661,1208,3.672,1230,5.198,1246,2.868,1247,4.279,1255,3.757,1265,2.363,1320,3.465,1369,3.465,1411,4.454,1503,3.855,1829,3.527,2110,4.279,2250,5.345,2272,3.355,2300,4.279,2321,3.969,2328,4.107,2329,5.881,2391,3.355,2512,4.509,2529,5.043,2534,3.969,2570,4.509,2595,2.868,2601,4.279,2602,4.859,2603,4.859,2604,4.509,2605,4.859,2606,3.527,2607,4.279,2608,4.859,2609,3.672]],["t/258",[7,0.751,16,1.772,30,5.955,98,3.346,132,2.797,225,3.403,233,3.446,236,2.504,275,2.947,395,3.763,524,5.861,601,4.469,740,4.304,828,3.971,982,4.803,1313,5.534,1331,6.11,2311,6.413]],["t/260",[7,0.772,25,2.369,27,1.756,28,0.701,31,1.808,40,2.617,48,2.015,56,0.49,84,0.801,85,1.721,107,1.272,192,2.926,194,3.817,233,3.2,236,2.576,238,2.849,247,2.183,248,2.331,254,1.597,256,1.858,292,3.218,316,2.213,318,1.801,353,1.354,372,1.858,395,1.963,426,1.834,482,2.221,502,2.438,527,1.933,553,2.63,565,2.416,603,4.247,612,3.216,614,0.914,635,1.802,662,2.887,691,2.732,740,5.285,760,3.245,798,4.069,810,3.285,814,2.139,821,3.458,837,3.011,880,4.883,887,4.932,901,2.439,908,2.529,927,3.278,944,2.009,945,2.448,982,5.54,994,2.139,1144,4.024,1196,3.545,1198,3.642,1296,4.247,1300,5.941,1605,3.642,1806,5.214,2099,2.989,2254,2.565,2586,4.045,2587,4.247,2595,4.706,2596,4.748,2610,7.2,2611,3.642,2612,4.714,2613,4.045,2614,4.932,2615,5.352,2616,4.714,2617,6.794,2618,4.714]],["t/262",[2,1.286,7,0.898,9,3.088,10,1.543,16,0.531,17,1.884,21,0.806,22,1.77,25,3.385,27,1.703,28,0.403,30,1.785,31,1.95,32,1.785,33,1.626,36,1.258,37,1.902,56,1.108,84,1.04,85,0.988,92,1.056,96,1.005,98,2.158,103,0.955,105,0.892,113,1.04,123,1.625,129,1.704,160,1,164,2.193,165,0.639,183,2.046,191,2.063,213,1.161,219,0.883,221,1.054,223,1.607,228,1.946,233,2.889,236,2.78,238,1.637,240,1.56,241,2.232,247,1.254,278,1.022,279,1.233,281,2.256,283,2.308,313,0.697,316,1.271,318,2.101,338,0.702,343,1.015,351,1.033,372,1.068,377,1.374,398,1.005,416,1.314,422,1.942,426,1.054,437,2.883,444,2.662,445,3.874,472,2.991,477,2.828,481,1.704,502,0.787,506,1.692,509,1.616,512,0.984,527,0.624,552,1.01,565,0.932,568,0.817,605,1.726,612,3.088,614,0.525,622,1.439,647,1.07,650,4.136,681,2.982,691,2.622,694,1.552,712,1.535,740,5.527,757,1.513,760,1.409,769,1.097,814,1.229,820,1.466,821,1.987,826,2.378,840,1.294,892,1.068,901,1.401,910,1.607,942,2.092,982,5.896,984,2.04,988,1.395,995,1.588,1008,1.704,1013,1.785,1071,2.512,1076,1.717,1108,1.459,1138,3.907,1169,2.378,1179,1.902,1206,1.519,1230,3.604,1323,4.227,1328,1.488,1331,1.831,1629,1.987,1638,1.474,1718,1.902,1734,4.525,1843,1.922,2031,1.527,2259,1.902,2339,1.704,2421,2.848,2430,1.756,2471,3.604,2595,3.033,2596,4.605,2611,2.092,2614,1.902,2619,2.378,2620,3.075,2621,2.063,2622,2.44,2623,2.708,2624,1.848,2625,2.735,2626,2.853,2627,2.512,2628,2.853,2629,3.075,2630,2.853,2631,3.117,2632,2.853,2633,2.378,2634,2.708]],["t/264",[7,0.73,12,1.791,21,1.921,25,1.422,34,1.131,56,0.816,84,0.736,96,1.564,105,1.389,106,1.627,107,1.741,111,3.256,115,2.045,132,1.997,162,1.56,170,2.441,179,2.275,197,1.979,213,1.807,228,2.154,233,3.35,236,1.788,238,6.996,246,1.679,247,1.951,272,3.382,276,3.368,281,2.101,313,1.085,318,2.429,323,1.791,324,2.351,364,3.129,365,2.25,372,1.661,382,2.755,387,3.092,390,2.144,409,2.25,482,1.986,491,2.049,502,1.224,527,2.47,541,2.733,565,1.45,568,1.272,578,1.745,607,2.606,630,1.64,632,2.389,651,5.506,660,3.304,685,2.471,712,2.389,740,2.007,814,1.912,827,2.25,833,3.875,838,2.24,858,2.19,927,2.931,945,1.469,977,2.479,982,4.167,994,1.912,1062,2.135,1108,2.271,1111,4.734,1145,3.461,1178,2.564,1188,2.825,1219,1.979,1285,1.894,1300,6.393,1395,3.909,1632,2.305,1932,3.129,2099,2.672,2259,2.96,2314,3.797,2472,3.304,2595,2.825,2596,5.94,2610,3.616,2614,6.17,2635,4.214,2636,5.985,2637,3.616,2638,4.214,2639,4.214,2640,4.045]],["t/266",[2,1.892,7,0.745,30,5.914,31,1.714,40,3.696,84,1.159,98,2.466,123,2.391,163,3.748,228,2.223,233,3.422,236,2.486,281,3.319,294,3.775,298,4.285,313,1.714,318,2.302,338,1.726,351,2.539,361,7.203,377,1.569,461,3.927,482,3.138,740,5.401,814,3.021,822,4.191,910,3.951,982,5.395,1117,7.629,1221,4.885,1231,5.847,1272,5.392,2137,6.933,2322,5.221,2430,6.583,2596,4.503,2641,5.007,2642,6.391,2643,5.999,2644,7.017]],["t/268",[7,0.716,25,2.907,29,3.168,129,5.424,228,2.876,233,3.286,236,2.936,238,5.209,248,4.262,476,2.636,527,1.985,562,5.483,683,3.397,740,4.104,927,5.992,982,4.58,1289,5.776,1300,4.911,1401,5.348,1688,6.18,2590,9.08,2641,6.48]],["t/270",[7,0.754,21,1.608,29,1.985,30,5.111,31,1.39,34,1.449,41,2.581,84,0.616,105,2.556,157,3.285,160,1.994,165,1.275,174,2.429,181,3.223,189,3.045,194,4.372,197,2.535,200,2.819,211,4.234,216,2.897,220,3.502,221,2.101,233,3.461,236,1.496,248,2.671,262,3.719,275,1.761,317,2.844,318,2.328,351,2.957,353,2.607,359,3.603,369,3.53,376,3.243,390,2.747,421,3.559,430,4.716,482,2.544,484,2.41,527,1.244,596,2.562,611,2.365,628,2.938,637,3.651,677,2.997,740,5.209,765,3.375,820,5.372,837,3.449,908,2.897,982,5.272,1108,2.91,1175,2.62,1206,3.029,1220,3.264,1221,3.961,1245,4.633,1264,3.719,1265,2.982,1367,3.588,1388,4.865,1401,4.813,1500,6.176,1931,4.114,2076,3.306,2290,5.009,2295,5.182,2430,3.502,2645,5.69,2646,5.69,2647,6.131,2648,6.131,2649,5.399]],["t/273",[2282,9.444]],["t/275",[9,6.537,48,4.096,84,1.094,159,6.729,683,3.777,1223,9.195,1497,8.888,1781,3.409,2650,10.878,2651,9.58,2652,10.096]],["t/277",[7,0.898,16,1.915,17,2.175,18,5.006,19,5.076,20,3.442,26,2.961,28,1.126,31,1.344,48,2.232,84,0.864,89,2.053,90,2.593,92,2.038,93,2.331,123,1.875,164,2.578,174,1.636,199,2.714,201,2.801,221,2.032,225,2.851,226,4.55,232,2.001,237,4.035,247,2.418,248,2.583,275,1.703,291,3.241,296,2.121,298,3.361,302,3.673,318,1.34,323,2.219,353,2.175,359,2.426,377,1.784,390,2.657,391,2.316,403,3.531,407,2.487,418,2.75,447,2.603,478,3.336,484,2.331,536,1.639,541,3.387,555,2.603,557,2.738,565,1.797,568,1.576,596,2.478,664,4.035,683,2.059,719,3.311,731,2.884,762,3.706,764,3.706,821,3.831,892,2.059,901,3.918,913,4.095,914,3.471,1206,2.929,1224,2.931,1243,4.228,1253,3.287,1265,2.884,1286,4.304,1390,3.787,1400,2.856,1401,3.241,2076,3.198,2268,3.137,2297,4.585,2640,5.012,2653,5.503]],["t/279",[7,0.963,16,1.544,20,2.799,21,1.264,22,2.776,24,1.107,26,1.661,27,1.97,28,1.171,31,2.028,36,3.015,39,1.821,46,3.44,55,2.434,56,0.989,84,0.899,89,1.67,90,4.379,92,2.532,93,1.896,94,2.42,96,1.576,123,2.828,132,1.314,145,1.799,174,2.032,181,2.535,189,2.394,197,1.994,199,2.207,213,1.821,216,2.278,225,1.599,226,3.316,227,2.953,228,1.417,229,2.654,236,1.177,243,2.447,247,1.966,275,1.385,277,2.3,278,1.603,283,1.294,285,1.849,292,1.946,294,2.407,316,1.994,317,3.418,341,3.761,351,1.619,375,3.974,395,1.768,437,2.1,444,1.94,447,2.117,463,4.114,491,1.349,527,0.978,536,1.333,560,2.382,565,1.461,580,1.554,590,2.407,605,1.619,613,2.023,619,2.871,620,2.475,636,2.519,670,2.6,675,2.846,682,3.729,683,1.674,684,2.334,685,2.49,694,2.434,709,4.47,715,2.3,719,2.692,737,5.168,798,2.461,799,3.382,821,3.115,844,3.644,855,4.209,876,4.313,901,3.358,918,2.871,941,3.568,1192,5.149,1246,5.911,1249,3.939,1253,2.673,1262,2.475,1288,2.673,1313,2.6,1364,2.236,1368,2.871,1479,3.08,1784,4.246,1788,4.246,1790,3.729,1791,3.826,1792,4.246,2297,3.729,2654,4.246]],["t/281",[2,1.837,7,0.83,14,5.069,15,2.484,24,1.706,25,2.181,26,2.529,27,1.617,29,2.377,31,1.664,56,0.673,84,0.738,105,2.899,109,1.149,110,3.589,123,2.322,182,2.28,201,3.468,285,4.353,324,3.608,332,3.536,338,1.676,358,5.677,377,1.524,390,3.289,391,2.868,511,2.172,525,3.21,527,2.027,536,2.029,560,3.626,565,2.224,568,1.951,630,3.423,644,5.998,685,3.791,712,3.665,726,4.453,757,1.679,794,4.744,825,5.235,830,3.114,901,3.346,941,5.433,995,3.791,1121,4.13,1175,3.137,1192,4.227,1246,4.334,1267,5.149,1274,5.433,1802,4.13,2026,4.744,2085,5.548,2086,4.995,2114,5.998,2565,5.677,2654,6.465]],["t/283",[7,0.529,14,2.41,15,1.181,18,1.423,23,1.081,28,0.457,34,0.825,46,1.342,56,1.311,84,0.574,85,1.122,89,1.209,105,1.656,123,1.104,144,1.551,160,1.136,186,1.526,201,1.649,219,1.003,237,2.375,247,1.423,279,1.4,285,1.338,291,1.908,292,1.409,322,1.656,351,1.172,369,2.01,377,0.724,378,2.77,395,2.093,401,2.567,438,1.681,447,1.532,471,1.342,482,1.448,500,1.532,527,1.468,532,2.762,541,4.134,558,1.448,560,2.819,568,0.928,572,1.715,607,2.973,610,2.489,630,3.158,757,0.798,810,1.438,882,5.356,901,2.6,917,3,919,2.489,933,1.835,934,2.612,938,1.994,989,1.824,1076,1.907,1160,2.638,1192,3.285,1241,4.527,1264,2.117,1274,6.185,1539,2.159,1554,5.742,1601,4.634,1783,4.002,1853,8.072,1855,5.295,1857,4.676,1858,3.074,1889,3.94,1937,3.239,1938,3.239,2009,3.53,2041,3.566,2108,7.757,2113,2.229,2131,4.662,2142,1.612,2314,2.77,2317,4.412,2655,2.95,2656,4.349,2657,3.49,2658,5.295,2659,5.706,2660,8.116,2661,3.49,2662,3.49,2663,3.49,2664,3.49,2665,3.49,2666,3.49,2667,3.49,2668,3.49,2669,3.49,2670,3.49,2671,5.706,2672,2.342,2673,3.49,2674,3.49,2675,3.49,2676,3.49,2677,3.49,2678,3.49,2679,3.49,2680,5.295,2681,3.49,2682,5.706,2683,3.49,2684,3.49,2685,3.49,2686,3.49,2687,3.49,2688,3.49,2689,3.49,2690,3.49,2691,3.49,2692,3.49,2693,3.49,2694,3.49,2695,3.49,2696,5.706,2697,3.49,2698,3.49,2699,3.49,2700,3.49,2701,3.49,2702,3.49,2703,3.49,2704,3.49,2705,3.074,2706,3.49,2707,3.49,2708,3.49,2709,3.49,2710,3.49,2711,5.295,2712,3.49,2713,5.706,2714,3.49,2715,5.706,2716,3.49,2717,5.706,2718,3.49,2719,3.239,2720,3.49,2721,3.49]],["t/285",[7,0.719,15,2.428,18,2.926,31,1.627,56,1.339,113,2.428,160,2.334,322,2.854,401,2.545,607,2.552,934,3.284,1241,5.693,1265,5.871,1347,4.636,1400,3.455,1601,3.977,1853,5.548,1923,3.544,1925,4.882,2000,4.164,2001,5.548,2002,5.309,2008,5.548,2009,4.438,2013,3.468,2141,3.406,2142,3.313,2274,4.815,2722,4.098,2723,4.815,2724,4.815,2725,7.175,2726,5.862,2727,5.208,2728,7.175,2729,4.954,2730,7.175,2731,7.175,2732,7.175,2733,7.175,2734,7.175,2735,7.175,2736,7.175,2737,7.175,2738,7.175,2739,7.175,2740,7.175,2741,7.175]],["t/287",[5,2.163,7,0.786,15,2.239,16,0.725,23,2.05,24,1.751,25,1.247,29,1.359,34,0.992,41,1.767,56,1.243,84,0.823,105,1.218,114,4.896,123,2.093,158,3.069,163,1.544,164,1.687,165,1.376,174,1.158,179,1.303,182,1.303,225,1.392,238,2.234,285,2.537,289,1.539,296,1.501,323,1.571,342,4.968,343,2.184,348,2.5,407,1.761,421,2.436,437,1.828,449,3.779,471,1.614,500,1.843,505,1.614,511,1.242,527,0.852,530,2.746,536,2.569,541,2.397,544,2.436,560,2.073,568,1.115,630,3.467,662,2.264,685,2.167,734,1.724,744,3.5,757,1.513,759,1.365,807,1.505,816,2.207,843,2.13,917,2.207,963,3.722,974,2.379,989,2.193,1019,1.431,1192,5.823,1246,2.478,1333,6.748,1337,2.596,1365,2.249,1367,2.457,1405,2.522,1413,2.18,1462,2.107,1531,2.596,1554,3.33,1597,5.441,1633,1.836,1638,2.012,1737,1.469,1991,2.596,2001,3.246,2009,4.093,2016,3.172,2017,1.64,2025,3.429,2026,4.275,2027,5,2029,5,2030,9.279,2031,5.336,2032,5,2033,6.141,2034,5.827,2035,4.093,2042,5,2043,3.895,2073,2.327,2099,2.344,2226,3.172,2654,3.696,2660,3.696,2672,2.817,2680,6.141,2742,4.197,2743,4.197,2744,2.944,2745,3.047,2746,4.197,2747,5.406]],["t/289",[7,0.732,16,1.464,19,2.407,24,0.875,25,1.729,31,1.923,34,1.376,49,1.214,56,1.315,84,0.852,98,1.898,109,1.72,110,4.891,127,3.045,170,1.596,203,1.293,248,4.358,318,2.261,395,2.135,418,2.7,476,1.568,505,2.238,527,1.181,568,1.547,580,1.875,587,4.398,614,0.994,653,1.7,755,2.433,757,1.331,814,2.326,830,4.243,846,1.658,916,2.059,934,2.664,944,2.185,977,1.97,988,2.641,1040,3.299,1051,2.619,1055,3.906,1123,3.182,1192,3.351,1375,3.274,1400,2.803,1401,3.182,1456,2.846,1601,4.701,1632,2.803,1699,3.351,1781,1.824,1843,3.638,2017,2.274,2073,3.227,2274,3.906,2338,2.971,2339,3.227,2391,4.019,2625,3.099,2658,5.402,2660,5.126,2672,3.906,2711,5.402,2726,4.755,2745,4.225,2748,5.82,2749,5.82,2750,5.82,2751,5.82,2752,5.82,2753,5.82,2754,5.82,2755,5.82,2756,5.82,2757,5.82]],["t/291",[1,4.053,2,2.54,7,0.9,16,1.298,18,3.065,20,4.362,23,2.328,24,1.525,28,0.984,56,0.93,84,1.292,90,3.287,93,2.955,105,2.182,109,1.588,132,2.049,145,2.805,162,2.451,164,1.916,165,1.562,170,2.061,171,1.895,172,2.416,174,2.073,177,3.368,225,2.493,232,2.537,256,2.61,313,2.301,318,1.698,353,2.568,377,1.56,397,2.899,526,5.044,536,2.077,568,1.997,597,3.602,611,2.899,671,6.353,918,4.476,933,3.951,1008,4.166,1070,3.976,1099,2.583,1238,5.36,1244,5.812,1266,3.024,1267,5.271,2087,6.619,2758,7.516]],["t/293",[7,0.574,10,3.936,24,1.178,26,2.702,84,1.05,103,2.435,182,2.435,186,3.429,187,5.067,203,2.32,220,4.479,221,2.688,225,2.601,236,2.548,280,4.629,281,3.443,285,4.003,343,2.588,524,4.479,526,5.263,527,2.119,568,2.775,607,2.789,683,3.626,708,5.693,724,3.543,760,2.15,880,4.803,940,7.891,984,3.113,1007,4.713,1145,3.705,1175,4.462,1251,5.194,1536,5.336,1540,4.026,2371,9.315,2439,5.693,2759,5.926,2760,6.407,2761,7.842,2762,7.842,2763,6.906,2764,8.826]],["t/295",[2,2.589,7,0.757,105,3.003,129,5.734,248,4.506,275,2.971,283,2.776,285,3.966,332,4.982,397,3.991,398,3.381,511,3.06,512,3.31,759,3.365,1506,6.16,2259,6.399,2765,10.344]],["t/297",[15,3.302,28,1.278,34,1.677,48,3.673,56,1.022,84,0.713,85,3.137,160,3.174,161,4.481,193,2.394,203,2.167,225,2.353,248,4.25,249,4.899,280,4.188,304,1.387,315,3.127,343,2.342,351,2.382,484,2.789,527,1.439,568,1.885,578,2.587,602,5.796,607,4.272,621,3.384,723,2.353,747,2.703,757,1.622,760,1.945,843,3.6,846,3.177,938,4.052,1021,4.481,1055,6.548,1577,4.976,1601,3.933,1754,2.798,1802,5.488,1927,6.247,1994,4.584,2317,5.486,2371,7.742,2766,7.094,2767,7.094,2768,7.094,2769,7.094,2770,6.584,2771,7.094,2772,6.584,2773,6.584,2774,7.094,2775,5.996,2776,8.592,2777,6.584]],["t/299",[4,2.617,7,0.841,12,2.399,15,3.574,17,2.351,24,0.963,34,1.515,48,2.414,56,1.213,84,1.155,105,1.861,107,1.523,109,1.003,110,3.134,153,3.043,171,1.616,196,2.358,223,3.35,248,3.96,292,2.588,298,3.633,332,3.087,338,1.463,398,2.096,438,3.087,511,1.897,512,2.051,527,1.301,630,2.197,723,2.126,724,2.897,760,1.758,888,3.134,906,3.852,1007,3.852,1019,2.186,1055,8.457,1141,4.246,1220,3.413,1400,3.087,1401,3.504,1577,4.496,1597,3.752,1754,2.529,1802,5.941,1994,4.142,2048,5.949,2259,3.965,2368,4.571,2379,5.237,2388,3.118,2776,5.646,2778,4.654,2779,6.411,2780,6.411,2781,6.411,2782,6.411,2783,6.411,2784,6.411,2785,6.411,2786,6.411,2787,6.411,2788,6.411,2789,5.949,2790,6.411,2791,6.411,2792,6.411,2793,6.411]],["t/301",[2,0.575,4,0.661,7,0.581,8,1.147,10,1.153,12,1.499,15,0.778,16,0.692,18,2.172,24,1.087,27,0.883,28,0.835,29,1.725,31,0.521,34,0.543,48,0.865,49,1.111,56,1.335,84,0.91,85,1.288,97,0.954,105,0.667,107,0.546,109,0.834,110,1.123,123,0.727,127,1.219,134,1.061,140,0.836,160,2.075,167,1.639,170,0.63,196,0.845,203,0.89,224,1.575,225,0.762,236,0.561,240,1.166,247,0.937,248,1.001,275,1.53,279,2.136,285,0.881,292,0.928,295,0.858,300,2.254,304,0.783,313,0.521,316,0.95,318,0.519,332,1.93,334,2.106,351,1.789,353,0.581,359,0.94,377,0.832,395,0.843,426,2.723,447,1.009,471,1.541,482,0.954,484,0.903,505,0.884,511,1.576,527,0.813,529,1.942,532,2.578,568,1.695,572,1.129,578,0.838,591,2.119,607,1.425,621,1.911,630,3.506,632,1.147,662,1.239,683,0.798,760,1.748,762,1.436,802,0.863,807,0.824,876,1.345,880,1.407,906,1.381,916,0.813,921,1.356,933,1.208,934,3.911,936,1.436,938,1.313,942,1.564,974,1.302,980,1.777,989,1.201,1007,6.147,1035,1.274,1108,1.091,1144,1.16,1151,2.133,1175,0.982,1265,3.518,1271,3.668,1289,1.356,1329,0.982,1347,2.589,1364,1.066,1365,1.231,1401,1.256,1413,1.194,1467,1.334,1489,2.133,1531,2.478,1567,1.639,1628,5.158,1632,2.565,1696,1.356,1781,0.72,1842,2.766,1853,4.931,1959,1.017,1969,1.942,1994,2.589,2017,1.565,2086,2.726,2117,1.639,2131,4.352,2246,1.7,2250,1.674,2314,1.823,2316,1.823,2317,3.098,2356,2.133,2362,1.452,2371,8.357,2546,4.025,2764,5.39,2770,2.133,2772,2.133,2773,2.133,2776,2.024,2777,2.133,2789,3.718,2794,2.298,2795,2.298,2796,2.298,2797,2.298,2798,4.006,2799,1.564,2800,1.942,2801,2.298,2802,2.298,2803,2.298,2804,2.298,2805,2.298,2806,6.377,2807,4.006,2808,2.298,2809,2.298,2810,2.298,2811,2.298,2812,2.298,2813,2.298,2814,2.298,2815,4.006,2816,3.483,2817,2.298,2818,4.006,2819,7.233,2820,2.298,2821,2.298,2822,2.298,2823,4.006,2824,2.298,2825,1.668,2826,4.006,2827,2.298,2828,2.298,2829,4.006,2830,4.006,2831,4.006,2832,2.298,2833,1.823,2834,2.298,2835,2.024,2836,2.298,2837,2.298,2838,2.298,2839,2.024,2840,1.877,2841,2.298,2842,2.298,2843,3.528,2844,2.298,2845,2.298,2846,2.298,2847,2.298,2848,2.298,2849,4.006,2850,2.298,2851,2.298,2852,2.298,2853,2.298,2854,2.298,2855,2.298,2856,2.298,2857,2.298,2858,2.298]],["t/303",[7,0.92,19,3.283,21,2.082,34,1.877,56,0.728,103,2.466,156,5.016,162,2.589,236,1.938,249,5.483,278,2.64,296,2.84,302,3.393,306,3.272,326,5.016,353,2.664,375,4.282,391,3.102,430,4.819,463,7.308,578,2.896,685,4.1,850,6.513,901,6.351,1265,3.863,1313,4.282,1459,4.817,2089,7.781,2759,6.001,2760,6.487,2859,6.712,2860,6.993,2861,6.993,2862,6.487]],["t/305",[1781,3.73]],["t/307",[3,5.964,7,0.55,8,3.752,10,3.773,11,5.562,21,1.971,22,4.327,23,2.328,24,1.129,26,2.589,27,2.236,28,1.329,31,1.704,46,2.89,56,0.689,84,0.755,103,2.334,105,2.182,109,1.176,121,4.081,123,2.377,126,3.368,140,2.733,165,1.562,171,1.895,174,2.073,197,3.108,213,2.838,220,4.293,221,2.576,232,2.537,292,3.034,305,4.437,315,3.313,346,5.271,353,1.902,392,4.327,434,6.278,491,2.102,500,3.3,555,3.3,571,7.934,583,4.26,590,3.752,591,3.976,592,4.476,611,2.899,622,3.518,637,4.476,657,5.456,1224,2.563,1325,5.114,1479,4.801,1504,4.915,1754,2.964,2568,6.14,2863,6.619,2864,5.456]],["t/309",[4,2.151,7,0.547,16,1.29,23,3.132,24,1.986,31,2.292,35,4.828,36,4.136,56,0.926,84,0.751,86,4.72,96,2.442,98,3.296,100,3.1,108,3.581,123,2.363,131,4.255,142,6.315,143,9.275,145,2.788,165,1.553,171,1.884,174,2.061,188,6.783,198,3.376,205,6.315,207,3.829,228,2.196,232,3.412,337,3.229,353,1.891,377,2.098,400,7.338,489,4.622,500,4.438,545,3.205,571,5.159,662,4.029,892,2.594,985,4.576,2864,5.424,2865,6.58,2866,5.928,2867,6.58]],["t/311",[34,2.527,84,1.075,92,3.675,93,4.992,229,5.887,252,8.27,390,4.792,599,5.101,619,6.369,620,5.49,1118,4.191,1368,6.369]],["t/313",[34,2.761,415,7.64,858,5.347]],["t/315",[34,2.761,415,7.64,858,5.347]],["t/317",[12,3.15,27,2.41,28,1.591,84,1.222,107,2.599,109,2.013,172,3.907,173,4.179,174,2.321,272,3.197,304,1.646,318,1.902,468,4.968,495,3.771,502,2.154,557,3.886,564,5.058,596,3.518,607,2.993,612,5.058,747,3.207,819,5.504,829,5.348,830,3.569,977,4.354,978,6.876,1062,3.756,1072,3.61,2218,5.648,2868,4.248]],["t/319",[4,1.496,5,1.699,7,0.685,16,0.898,21,2.455,24,0.781,25,1.545,26,3.585,27,1.718,28,1.226,40,2.541,56,0.858,84,1.046,98,3.053,106,1.768,107,1.853,109,2.068,110,3.814,111,7.587,116,1.8,126,2.329,132,1.417,133,2.064,164,2.387,165,1.622,169,3.959,170,2.139,183,2.07,186,2.273,189,3.874,256,1.805,272,1.975,289,1.906,316,2.15,318,1.175,353,2.369,365,2.444,372,1.805,377,1.079,382,2.993,407,2.181,416,2.221,432,3.042,439,3.153,440,2.127,447,2.282,458,2.716,527,2.262,555,2.282,565,1.575,568,2.073,578,1.896,593,3.57,606,3.707,613,3.927,615,2.822,630,2.674,635,1.75,679,5.663,809,3.32,814,2.077,818,4.363,837,2.924,976,3.59,980,6.032,1081,5.98,1167,7.649,1271,2.4,1322,5.308,1323,3.32,1459,4.732,1476,4.02,1783,3.646,1987,3.4,2467,4.247,2869,4.578,2870,4.578,2871,3.847]],["t/321",[4,1.595,5,1.811,7,0.711,15,1.875,16,0.957,27,2.803,28,1.569,56,0.891,98,1.807,105,1.608,107,1.316,109,1.68,113,1.875,127,2.489,132,1.51,165,2.021,171,1.397,172,3.126,173,2.751,182,1.72,199,2.536,228,2.404,236,1.996,257,2.617,281,2.432,289,3,292,2.236,302,2.367,320,2.949,338,1.265,343,1.829,351,1.861,365,3.846,366,3.216,369,3.19,437,2.413,445,3.242,455,6.181,458,2.895,459,4.022,490,3.623,491,1.55,502,1.418,525,2.423,550,2.02,552,1.82,568,2.174,581,5.489,604,3.393,617,4.1,633,4.022,635,2.754,636,2.895,684,2.681,734,2.275,757,1.267,798,2.828,801,3.825,815,4.526,831,3.242,908,2.617,944,3.07,945,3.297,995,2.86,1108,4.615,1138,3.27,1180,3.58,1183,3.539,1265,2.695,1285,2.192,1302,3.116,1322,3.77,1419,4.022,2199,4.526,2479,5.937,2872,4.879,2873,4.879,2874,4.284,2875,3.825,2876,6.053,2877,4.879,2878,4.879,2879,4.284]],["t/323",[2,1.672,7,0.79,17,2.45,25,2.781,28,1.614,56,1.073,84,1.177,88,4.515,89,4.054,109,1.046,128,2.957,133,2.653,162,2.179,164,1.703,175,3.391,275,1.919,313,1.515,315,2.945,377,1.387,405,1.973,407,2.803,440,2.734,472,4.881,476,2.521,483,3.911,491,1.869,505,2.57,512,4.282,558,2.773,575,5.781,605,2.244,698,4.484,726,4.053,898,4.698,914,3.911,1080,5.578,1307,4.317,1616,3.45,1754,2.635,2547,5.302,2880,5.302,2881,5.648,2882,9.131,2883,5.459,2884,5.302]],["t/325",[7,0.751,84,1.166,92,2.629,98,2.495,109,1.607,110,3.741,116,2.65,165,1.591,174,3.198,186,3.346,216,3.615,228,3.02,229,4.212,233,2.57,242,5.004,317,3.549,318,1.729,351,2.57,364,5.004,372,2.657,377,2.132,390,3.429,426,2.623,458,3.999,527,2.515,580,2.466,591,4.048,613,3.21,630,3.521,637,4.557,658,5.745,693,4.371,798,3.906,818,3.566,837,4.305,910,5.368,1099,3.53,1179,4.734,1367,4.479,2196,5.918,2421,4.242,2885,6.072]],["t/327",[2,2.449,5,3.198,23,3.031,24,1.47,26,3.371,158,4.538,220,5.589,246,3.433,247,3.99,276,4.498,351,3.286,365,4.601,401,3.47,408,3.278,418,4.538,420,8.27,458,5.112,466,6.399,534,6.48,628,4.689,828,3.786,880,5.992,1336,5.935,2204,8.27]],["t/329",[7,0.769,19,4.349,28,1.377,84,1.057,138,3.936,197,4.349,225,3.488,316,4.349,338,2.401,441,5.308,536,2.906,840,4.427,858,4.814,1118,4.121,1506,6.263]],["t/331",[7,0.912,15,3.008,16,1.535,23,2.754,84,0.894,144,3.951,219,3.255,232,3.825,256,3.087,275,3.255,283,2.385,351,2.986,491,2.487,545,3.813,548,5.078,552,2.92,583,5.039,615,4.826,888,4.346,1118,3.484,1266,3.576,1330,5.392,1923,4.391,2013,2.748,2388,4.324,2886,9.054,2887,4.826,2888,7.829,2889,7.829,2890,6.718]],["t/333",[17,3.857,23,3.258,24,1.58,84,1.057,98,3.429,160,3.421,161,6.643,219,3.021,232,3.55,275,3.021,530,4.364,607,3.74,894,7.376,977,3.559,2886,7.634]],["t/335",[5,2.074,7,0.464,14,4.382,16,1.096,25,3.399,27,2.52,28,1.645,29,2.055,31,2.381,34,1.5,84,0.638,96,2.074,105,1.842,123,3.322,158,2.944,163,2.334,164,1.618,181,3.336,200,5.26,245,5.003,246,2.227,248,2.764,256,2.204,257,2.998,281,2.786,313,1.439,318,1.434,332,3.056,377,1.317,437,3.931,440,2.597,491,1.775,530,2.634,562,2.892,565,1.923,635,2.137,658,2.82,740,5.07,810,2.615,830,2.692,847,4.696,981,3.966,982,6.182,1035,5.003,1097,4.526,1196,4.203,1198,4.318,1300,6.067,1324,4.607,1479,4.054,2198,4.259,2586,4.796,2587,5.036,2595,3.746,2606,4.607,2633,6.98,2891,6.346]],["t/337",[7,0.897,25,3,27,2.702,28,1.73,109,1.58,272,3.836,281,4.433,318,2.281,343,3.333,565,3.059,568,2.683,596,4.22,635,3.399,2529,6.87,2633,7.808,2892,8.011]],["t/339",[2,1.211,7,0.833,12,1.812,16,1.276,21,1.269,23,2.29,27,2.38,28,1.314,40,2.366,56,0.677,84,1.009,103,1.503,109,1.691,110,2.366,113,1.638,127,3.811,131,2.038,133,1.922,153,2.298,155,2.909,162,2.923,164,1.234,165,1.864,171,1.22,174,2.038,193,1.634,197,2.001,219,2.882,221,1.659,225,1.605,228,2.635,232,1.634,248,2.108,298,2.743,313,1.097,318,1.67,338,1.105,343,1.598,375,2.61,377,1.534,393,2.206,440,1.981,472,2.187,491,1.354,502,2.294,524,2.765,527,1.499,536,2.042,538,3.743,544,2.809,550,1.765,557,2.235,558,2.009,565,3.871,567,2.75,568,2.872,577,3.245,578,1.765,580,1.56,593,2.215,601,2.108,605,1.625,614,0.827,630,1.659,635,1.63,670,2.61,690,2.787,747,1.844,756,3.219,757,2.294,796,4.361,810,1.994,857,2.038,981,3.025,1021,4.668,1144,2.443,1320,3.451,1394,2.857,1504,3.165,1558,2.529,1754,2.915,2081,3.205,2099,2.703,2113,3.092,2133,3.84,2893,3.743,2894,3.743]],["t/341",[2,1.664,5,2.173,17,2.438,26,3.213,31,1.507,34,1.571,41,2.798,56,0.855,84,0.937,85,2.137,101,3.283,103,2.064,107,2.922,109,2.049,125,3.516,129,3.685,170,2.557,174,1.833,200,3.056,216,3.14,228,2.741,236,1.622,257,3.14,261,4.385,278,2.21,302,2.84,353,1.682,369,3.827,398,2.173,408,3.124,444,2.674,527,2.587,536,2.577,553,3.266,565,2.014,601,2.895,607,3.317,630,2.278,663,3.767,693,3.796,757,1.52,810,2.739,846,3.069,945,3.308,957,3.585,1144,3.355,1220,3.538,1536,4.523,1558,3.473,1754,2.622,2142,3.069,2892,5.274,2895,5.853,2896,5.853,2897,5.853,2898,4.154,2899,5.853]],["t/343",[4,2.423,27,2.41,28,1.102,31,2.48,84,1.1,96,2.751,109,1.902,123,3.459,125,4.452,138,3.15,164,2.789,171,2.758,174,3.017,175,4.272,179,2.613,278,2.799,297,4.272,347,5.155,377,1.747,416,3.596,544,4.885,568,2.237,607,2.993,614,2.076,630,2.885,635,2.834,818,3.922,1101,6.064,1182,3.725,1504,5.504,2010,3.533,2900,5.26]],["t/345",[5,2.926,7,0.833,24,1.88,34,2.116,56,0.82,107,2.127,127,2.725,158,4.153,174,2.469,228,2.632,236,2.185,283,2.402,295,3.341,306,3.689,338,2.044,502,2.291,536,3.146,550,3.265,558,3.715,560,4.422,577,4.997,827,4.21,830,3.797,901,4.08,1007,5.38,1035,4.963,1318,6.923,1329,3.825,2816,7.443,2901,7.314]],["t/347",[2,1.635,5,2.649,7,0.848,16,1.128,17,1.514,18,1.684,19,1.707,20,2.396,21,1.083,22,2.377,24,1.679,26,2.25,28,1.463,31,1.481,33,2.184,41,1.738,46,1.588,56,0.742,84,0.657,85,1.327,90,1.806,92,1.419,98,1.346,103,1.282,125,3.455,132,2.209,138,1.545,140,1.501,145,1.541,164,1.665,165,2.086,168,3.193,179,1.282,181,2.171,183,1.645,189,2.05,220,2.358,225,1.37,226,1.858,243,2.095,244,2.358,245,4.492,255,2.58,257,1.951,280,2.437,283,1.753,285,1.583,289,2.395,292,1.667,320,2.198,339,1.933,343,3.312,353,1.045,360,2.734,366,2.396,376,2.184,386,2.851,408,1.383,430,1.89,444,1.661,468,2.437,498,3.372,502,1.057,505,1.588,509,2.171,527,1.644,536,1.141,550,3.891,565,2.791,566,2.668,574,3.49,593,1.89,596,1.726,622,1.933,628,1.979,630,2.238,631,2.504,632,2.061,636,3.413,672,2.257,686,2.997,719,2.305,732,2.504,747,1.573,759,1.343,770,2.944,796,2.008,807,4.47,814,1.65,816,2.171,820,1.969,826,3.193,830,2.77,832,2.58,856,2.417,858,2.99,910,2.157,939,2.184,945,1.268,979,2.554,985,2.529,995,2.132,1005,2.504,1017,2.734,1065,3.49,1072,1.771,1114,3.373,1183,4.172,1202,2.358,1238,2.944,1253,2.289,1300,2.072,1329,1.764,1932,2.7,2254,1.979,2403,2.132,2450,2.554,2631,3.962,2816,5.299,2874,3.193,2902,3.12,2903,3.12,2904,2.771,2905,3.832,2906,3.636,2907,3.636,2908,3.12,2909,3.373]],["t/349",[5,2.751,21,2.207,28,1.591,31,1.908,56,1.002,84,0.846,89,2.915,105,2.443,107,2.599,109,1.317,140,3.06,144,3.74,145,3.141,165,1.75,179,2.613,199,3.852,242,5.504,257,3.976,337,3.638,507,4.248,536,2.326,550,4.432,565,2.55,580,3.525,630,2.885,633,6.11,807,3.923,828,3.257,830,3.569,944,3.159,1062,3.756,1101,4.666,1102,4.666,1304,6.678,2403,4.346,2816,7.154]],["t/351",[2,1.43,5,2.736,7,0.418,26,2.883,27,2.181,28,1.296,31,1.296,84,0.574,85,1.837,92,1.964,93,2.247,98,2.729,103,1.775,106,2.846,107,2.879,109,1.897,115,4.657,123,1.807,138,2.139,163,2.102,164,2.133,165,1.188,174,1.576,179,1.775,182,1.775,233,1.919,236,2.042,242,3.738,254,3.252,257,2.7,272,3.179,278,1.9,289,2.096,302,2.442,316,2.363,317,2.651,318,1.291,322,1.659,324,2.809,341,2.917,353,1.446,385,4.669,416,2.442,430,2.616,444,2.299,458,2.986,468,3.374,489,3.535,490,3.738,502,1.462,522,3.946,524,3.264,527,1.698,550,3.052,576,3.124,611,2.205,613,2.397,658,2.54,693,3.264,709,3.467,737,4.008,755,2.389,774,2.869,798,2.917,910,4.373,916,2.022,1036,4.008,1099,3.402,1100,2.102,1101,5.488,1110,4.008,1115,4.319,1178,3.062,1222,4.076,1494,4.535,2201,4.42,2910,4.831,2911,5.286,2912,5.033,2913,5.033,2914,5.033]],["t/353",[306,4.814,975,6.957,1557,6.726]],["t/355",[7,0.976,16,1.183,24,1.827,25,2.504,27,1.509,28,1.709,31,2.593,40,3.349,56,1.21,103,1.362,107,2.002,109,1.49,115,2.927,123,3.617,127,3.601,162,1.43,219,1.26,223,2.292,276,3.149,468,4.043,476,1.845,491,1.916,527,1.39,528,2.145,565,3.684,568,2.24,601,2.984,614,1.17,630,3.264,635,1.477,757,1.003,759,2.228,810,2.822,814,3.368,857,1.847,938,2.506,1035,2.432,1144,3.457,1163,2.742,1219,1.814,1754,3.324,2625,3.646,2799,4.661,2915,3.863,2916,4.387,2917,3.863,2918,3.863,2919,8.387,2920,6.032,2921,3.863,2922,3.863,2923,7.421,2924,3.863,2925,3.863,2926,3.863,2927,3.863,2928,2.145,2929,6.032,2930,6.032,2931,6.032,2932,6.032,2933,6.032,2934,6.032,2935,6.032]],["t/357",[1781,3.73]],["t/359",[2,1.995,7,0.922,16,1.645,24,1.198,25,2.369,27,1.179,28,1.55,29,1.733,31,1.213,56,0.49,84,0.958,89,1.854,103,1.662,105,1.554,106,1.82,109,0.838,116,1.854,123,1.693,124,3.133,127,2.9,132,1.459,160,1.741,162,1.745,165,1.113,167,3.817,170,1.468,174,2.199,175,2.716,181,2.814,193,2.691,197,2.213,225,1.775,228,1.573,275,1.537,276,2.461,313,1.213,318,1.801,334,2.814,338,1.82,343,1.767,377,1.655,390,2.398,440,3.262,471,3.664,476,2.148,490,3.5,491,1.497,502,1.37,536,2.633,550,1.952,553,2.63,558,3.308,565,3.42,568,3,576,2.926,577,2.35,580,1.725,593,3.649,601,2.331,607,1.904,663,3.034,664,3.642,670,2.887,683,1.858,734,2.198,747,2.04,757,2.414,796,2.604,810,2.205,843,2.716,865,3.345,867,3.817,901,2.439,1072,2.296,1144,2.701,1217,3.754,1255,4.139,1266,2.153,1319,3.381,1462,2.687,1754,2.111,2081,3.545,2311,3.345,2936,4.714,2937,4.967,2938,3.961]],["t/361",[2,1.129,4,1.299,5,1.475,7,0.874,27,2.13,28,0.591,31,1.945,36,1.846,42,3.07,49,2.183,56,0.413,84,0.972,92,2.948,93,3.373,106,2.381,109,1.096,131,1.9,134,2.083,138,3.917,144,2.005,153,2.142,165,1.455,171,1.138,172,3.943,173,2.241,181,2.372,188,3.028,193,1.523,207,1.709,216,2.132,224,2.753,228,2.058,236,1.101,256,1.567,276,3.219,281,1.981,288,2.763,289,2.568,292,1.821,302,1.928,318,1.019,334,2.372,337,1.95,338,1.598,339,4.016,365,2.122,376,2.387,377,1.453,409,2.122,441,2.277,468,2.663,500,3.074,502,1.155,525,1.973,527,0.915,552,1.482,555,1.981,568,2.782,578,1.645,579,3.028,580,1.454,601,4.559,620,3.594,629,2.663,632,2.253,636,3.658,638,2.519,691,5.342,715,2.152,728,2.915,732,2.737,743,2.763,774,2.265,807,1.618,810,4.313,812,5.307,816,2.372,858,2.065,872,2.711,894,3.164,1009,2.85,1070,2.387,1072,1.935,1078,3.028,1079,4.064,1188,2.663,1271,2.083,1285,3.395,1303,2.687,1387,4.911,2073,5.36,2190,2.343,2217,3.217,2431,2.988,2561,3.58,2939,3.58,2940,6.166,2941,4.187,2942,3.814,2943,3.973]],["t/363",[2,1.374,7,0.929,21,1.44,27,2.856,28,1.4,30,4.717,31,1.245,36,2.247,49,1.145,84,0.552,92,1.887,101,2.713,109,1.272,116,1.902,126,2.461,138,2.055,153,2.607,162,1.791,163,2.02,165,1.142,171,2.049,172,3.11,173,4.804,174,2.668,184,3.852,188,3.685,193,2.743,196,2.02,223,2.87,224,2.159,228,1.614,246,1.927,282,2.772,304,1.074,313,1.245,320,2.924,353,1.39,407,2.304,502,1.405,505,2.112,525,4.23,552,1.804,568,2.571,576,3.002,578,2.003,597,2.632,599,2.619,601,2.392,608,2.645,614,0.938,620,2.819,631,3.331,632,4.057,638,4.538,658,2.44,675,3.242,725,2.595,741,3.3,807,3.469,810,2.263,818,2.559,819,3.591,830,4.103,831,3.214,832,5.079,858,2.514,977,1.858,985,3.363,1070,4.299,1075,3.953,1079,3.188,1093,5.086,1188,3.242,1370,4.064,1843,3.432,2257,3.548,2331,3.242,2384,3.469,2944,5.492,2945,4.836,2946,4.064,2947,4.247]],["t/365",[0,6.386,7,0.872,21,3.125,27,2.851,109,2.025,127,3.627,179,3.701,193,3.252,343,3.18,437,4.197,557,4.449,631,7.229,757,2.203,816,5.065,856,5.639,2484,6.871]],["t/367",[7,0.77,84,0.798,109,1.243,133,3.152,160,3.843,165,1.651,221,3.609,228,3.095,275,3.024,320,5.605,377,1.648,426,2.722,430,3.635,445,4.648,476,2.139,491,2.221,498,4.1,502,2.032,527,2.397,565,2.406,568,2.798,635,2.673,811,4.572,858,3.635,976,5.483,984,3.152,1036,5.569,1080,4.1,1141,8.668,1202,4.536,1229,5.764,1404,5.876,1987,5.193,2081,6.973,2101,5.876,2479,5.764,2577,3.882,2948,6.712,2949,6.301]],["t/369",[7,0.528,21,1.892,28,1.293,31,1.636,33,3.817,37,4.463,42,4.91,46,2.775,56,0.661,84,0.725,90,3.155,92,2.479,93,2.837,106,2.454,107,2.345,109,1.893,115,4.218,126,3.233,153,3.425,165,1.5,189,3.583,216,3.409,224,3.881,272,2.741,318,1.63,353,1.826,407,4.141,426,3.383,502,1.846,513,3.155,620,3.704,629,4.259,635,2.429,658,3.207,769,2.573,858,3.303,984,2.865,1099,4.495,1122,5.895,1162,3.726,1175,3.083,1178,3.866,1180,4.662,1183,4.609,1331,5.879,1733,4,2271,4.779,2385,5.58,2434,4.558,2584,4.719,2875,4.983,2911,4.558,2950,5.145,2951,3.972,2952,5.238]],["t/371",[1,2.815,2,1.958,7,0.817,8,2.606,28,0.683,31,1.183,33,4.139,34,1.234,36,2.136,39,1.971,41,2.198,46,2.007,49,1.089,50,2.834,56,0.478,84,0.944,90,3.422,91,4.036,92,3.583,93,3.076,94,5.886,96,2.558,97,2.166,109,0.817,130,2.761,132,1.423,144,2.32,145,2.92,164,1.995,165,1.085,172,2.515,179,1.621,186,2.283,203,1.16,207,1.977,216,2.466,224,2.052,228,1.534,236,1.274,240,2.649,272,1.983,283,2.519,292,2.107,313,1.774,343,1.723,351,1.753,390,2.339,407,2.19,408,1.749,437,2.274,439,3.166,440,3.202,441,2.634,447,2.292,476,3.009,482,3.247,568,2.771,578,1.903,580,1.682,593,2.389,599,5.965,601,2.274,619,4.66,620,4.017,658,2.32,670,2.815,675,3.081,694,2.634,715,4.477,737,3.661,757,1.194,769,1.862,810,2.151,812,3.229,822,2.894,832,3.262,858,2.389,977,1.766,1099,1.794,1162,4.846,1175,2.23,1299,3.373,1368,4.66,2073,2.894,2271,3.457,2816,3.414,2953,4.412,2954,4.036,2955,4.597]],["t/373",[7,0.943,16,1.381,23,2.478,24,0.808,27,1.762,28,1.047,31,2.166,40,2.628,55,2.713,56,0.733,84,0.804,89,2.77,103,1.669,109,1.252,123,3.021,156,3.395,158,2.493,165,1.117,187,3.473,215,3.712,216,2.539,219,1.544,225,1.783,226,3.599,227,3.292,229,2.959,232,2.7,278,1.787,281,2.36,283,2.146,284,3.359,298,3.047,313,1.813,315,2.369,332,2.589,336,2.03,338,1.227,376,2.843,476,1.448,478,3.024,491,1.503,500,2.36,527,1.623,530,2.231,545,2.305,552,1.766,555,2.36,558,2.231,562,2.45,565,1.629,567,2,568,1.428,576,2.938,578,1.96,584,4.157,585,3.77,593,2.46,596,2.247,597,2.576,604,3.292,626,4.543,630,1.842,658,2.389,683,1.866,684,2.602,719,3.001,741,3.23,818,2.504,840,2.263,855,3.07,867,3.833,876,3.146,877,4.543,904,3.292,918,3.201,942,3.658,1141,3.56,1142,3.292,1191,4.543,1193,3.473,1220,4.258,1236,3.325,1253,2.98,1256,4.734,1266,2.162,1287,3.658,1290,2.938,1388,4.265,1419,3.902,1507,3.902,1540,2.759,1688,3.395,2090,4.543,2250,3.992,2296,4.391,2297,4.157,2298,4.062,2299,4.543,2449,4.734,2956,4.543,2957,5.375,2958,4.265]],["t/375",[7,0.846,27,1.434,29,2.108,31,1.476,55,3.286,56,0.597,85,2.093,89,3.182,93,3.613,123,2.059,158,3.019,160,2.118,172,2.093,219,1.87,227,3.987,231,4.368,232,3.102,262,3.949,283,1.747,306,2.682,318,1.471,323,2.436,332,3.135,334,4.831,341,3.323,353,1.647,359,2.664,372,2.26,395,3.906,430,4.206,471,2.504,512,2.94,536,2.54,548,3.718,552,2.138,567,3.419,620,4.717,670,3.511,677,3.183,684,5.155,694,3.286,721,5.165,763,4.112,767,4.642,829,3.183,833,4.861,872,3.912,905,6.041,1072,2.792,1076,2.175,1100,2.394,1108,3.09,1217,4.566,1233,4.919,1257,4.726,1262,5.468,1266,2.619,1267,4.566,1331,3.877,1374,5.743,1402,2.824,1423,4.368,1473,5.733,2554,5.733,2959,6.51,2960,3.778,2961,9.19,2962,6.51]],["t/377",[2,0.871,7,0.673,16,1.441,17,1.277,24,1.085,28,1.294,29,1.127,31,1.892,34,0.823,48,1.311,55,2.873,56,0.522,84,0.35,85,1.119,89,3.187,96,1.861,98,1.135,103,1.081,111,2.369,123,1.8,145,2.696,160,1.132,162,1.135,170,2.709,174,0.96,179,1.768,182,1.768,206,2.092,211,2.404,216,1.645,224,2.84,225,1.155,226,1.566,227,3.487,236,0.849,243,1.767,257,1.645,275,1,278,1.893,283,0.934,289,1.277,292,1.405,295,1.299,306,1.434,313,0.789,318,1.885,323,3.123,324,1.711,332,1.676,336,1.314,338,0.795,351,1.169,353,0.881,359,3.415,362,2.527,367,1.645,369,3.278,375,1.877,391,1.36,397,3.219,405,2.464,440,2.33,447,2.5,471,3.539,484,2.84,491,0.974,495,1.56,505,2.189,508,1.903,514,2.441,527,1.155,550,2.076,555,2.5,558,1.444,578,1.269,613,1.46,622,1.629,632,1.738,670,1.877,677,1.702,684,2.755,708,2.527,719,3.179,749,3.421,754,2.63,755,1.455,757,0.796,758,2.692,760,1.981,761,3.065,772,3.487,780,1.719,794,2.249,801,2.404,805,3.596,827,3.397,828,1.347,859,1.676,872,3.421,896,2.369,906,3.421,944,1.307,977,1.178,984,1.382,993,1.307,1015,1.916,1022,2.906,1109,2.762,1145,1.645,1169,2.692,1175,1.487,1189,2.762,1195,2.199,1197,2.199,1199,2.175,1206,2.812,1220,1.853,1257,4.133,1265,1.693,1290,3.112,1303,3.39,1313,1.877,1329,3.086,1331,2.073,1333,2.527,1376,2.175,1477,2.692,1583,2.576,2086,2.369,2250,5.516,2263,5.459,2268,4.868,2271,2.305,2403,2.94,2404,3.636,2529,2.369,2558,3.23,2560,3.23,2601,3.065,2646,3.23,2963,3.481,2964,2.942,2965,2.692,2966,2.576,2967,2.942,2968,2.762,2969,3.065,2970,3.065]],["t/379",[4,1.487,7,0.65,15,0.748,16,1.679,17,2.276,18,2.107,19,1.601,21,1.355,23,3.299,24,1.335,26,0.761,27,0.487,28,1.017,29,0.715,31,1.171,33,1.169,36,0.904,46,1.489,47,1.293,49,1.078,55,1.115,56,1.152,84,1.138,85,0.71,88,1.618,89,1.341,98,0.721,99,1.293,105,0.641,123,1.224,129,1.225,132,1.055,133,1.537,138,2.322,140,0.803,144,0.982,157,1.184,158,1.025,160,0.719,161,1.396,163,0.813,164,0.987,165,1.467,171,0.976,174,0.609,186,1.693,187,1.428,196,0.813,199,1.011,203,0.491,207,0.837,211,2.673,213,1.462,219,2.027,225,1.284,226,0.994,228,1.138,232,3.885,236,0.945,244,1.262,247,0.901,257,1.044,262,1.34,275,1.782,276,1.78,281,0.97,282,1.115,283,2.383,284,4.41,292,2.086,299,1.316,303,1.604,305,2.285,313,1.407,318,0.499,320,1.176,323,1.934,328,1.353,334,1.162,336,1.462,341,1.128,346,1.55,348,1.316,351,0.742,353,0.98,363,1.463,366,1.282,372,0.767,377,0.804,386,1.526,395,0.81,407,0.927,430,1.772,433,1.184,441,3.132,471,1.489,476,1.043,478,3.969,480,1.396,491,1.445,500,0.97,512,1.653,527,1.575,530,3.222,532,1.069,535,1.868,536,1.428,537,2.051,539,1.316,543,1.155,544,1.282,545,4.77,548,2.211,550,0.806,552,0.726,553,1.086,558,2.928,559,3.904,560,3.065,565,0.669,575,2.735,578,0.806,583,1.252,585,1.55,586,1.805,590,1.103,591,1.169,596,0.923,601,1.686,605,0.742,612,1.328,613,1.624,636,1.155,641,1.709,642,1.946,659,1.328,666,1.604,683,1.794,686,1.604,702,1.381,709,1.34,715,1.054,734,0.907,736,1.532,744,1.169,757,1.419,762,1.381,765,1.216,809,1.411,829,1.08,831,1.293,838,1.034,878,1.709,881,2.247,888,1.08,889,3.272,908,1.044,933,2.035,934,1.772,942,1.503,977,0.748,1015,2.844,1019,2.648,1052,1.753,1076,0.738,1080,1.141,1121,1.243,1169,1.709,1175,0.944,1180,2.502,1182,0.978,1186,3.685,1189,1.753,1237,4.24,1251,1.463,1266,0.889,1283,1.635,1289,1.304,1297,0.994,1319,2.446,1324,1.604,1326,1.868,1330,2.348,1365,1.184,1583,1.635,1753,1.411,1843,1.381,1856,1.753,1889,1.526,2099,1.234,2250,2.16,2322,1.526,2422,1.316,2472,1.526,2866,1.753,2871,2.865,2882,1.868,2886,1.604,2938,1.635,2951,1.216,2971,2.21,2972,2.21,2973,2.051,2974,2.21,2975,2.21,2976,2.21,2977,2.051,2978,2.21,2979,3.871,2980,2.051,2981,2.21,2982,2.21,2983,2.051,2984,2.21,2985,2.051,2986,1.946,2987,2.21,2988,2.21,2989,1.868,2990,2.194,2991,2.21]],["t/381",[7,0.777,21,1.698,27,2.863,28,1.512,31,1.468,56,0.593,84,0.651,89,4.218,101,3.199,126,2.902,128,2.866,160,2.107,162,2.112,165,1.346,175,3.287,202,4.137,207,3.468,291,3.54,294,3.233,295,2.417,315,4.036,317,3.004,318,1.463,334,3.405,336,2.446,338,1.478,341,4.673,353,2.688,391,2.53,415,4.235,471,2.491,478,5.151,512,2.072,513,2.832,578,2.362,707,3.344,735,6.231,736,4.203,758,5.008,781,5.474,802,2.431,838,3.031,840,2.727,898,4.596,1038,4.091,1169,5.008,1206,3.199,1218,3.928,1242,6.01,1254,4.235,1289,3.823,1425,4.938,1430,5.291,2250,2.707,2403,3.344,2426,4.048,2992,4.235,2993,4.701,2994,6.476,2995,6.476,2996,5.474,2997,6.476,2998,6.476,2999,6.476]],["t/383",[6,2.894,7,0.75,16,2.089,21,1.772,23,2.094,24,1.633,25,2.803,26,2.328,27,2.078,28,1.54,31,2.323,35,2.786,49,1.41,50,3.669,55,2.176,56,0.865,84,0.433,104,3.523,108,3.239,115,1.842,128,1.908,130,2.281,131,2.845,132,1.842,156,2.724,163,1.586,164,1.099,165,1.733,168,3.334,179,1.339,182,1.339,185,2.754,190,2.057,198,1.948,213,2.552,228,1.268,232,2.281,236,1.052,276,1.982,289,1.581,313,0.978,336,2.552,337,1.864,338,1.903,339,3.163,341,3.449,343,1.423,367,2.037,377,0.895,422,2.724,437,1.878,440,1.764,458,2.253,471,1.658,472,1.948,498,2.226,501,3.645,512,2.162,532,2.087,568,2.215,591,2.281,596,1.802,635,1.452,636,2.253,653,1.26,655,2.616,677,2.108,709,2.616,723,1.43,734,2.775,736,1.706,737,3.024,747,1.643,751,2.31,753,3.421,765,2.373,805,2.724,856,3.955,857,1.815,859,2.077,1022,2.201,1089,2.444,1090,1.777,1181,2.341,1262,2.213,1266,1.735,1270,3.421,1319,4.269,1583,3.191,1843,2.695,1988,2.623,2250,1.802,2403,6.865,2404,7.726,2604,4.002,2928,2.108,2967,3.645,2968,3.421,3000,4.312,3001,4.002,3002,4.312,3003,4.312,3004,4.312,3005,3.258,3006,3.191]],["t/385",[1,3.674,2,2.1,7,0.753,15,1.474,16,0.752,21,1.142,24,0.655,26,1.501,28,1.099,34,1.03,39,1.645,49,1.421,55,3.439,56,0.624,84,0.843,101,2.152,107,1.035,109,1.942,127,1.326,132,1.857,134,2.012,157,3.65,160,2.216,162,2.222,164,1.111,165,0.906,170,2.3,171,1.098,175,2.211,185,2.783,193,1.47,198,1.968,213,1.645,236,1.662,246,2.391,247,1.776,249,3.008,253,2.433,275,1.957,279,1.747,295,1.626,297,2.211,304,1.641,313,0.988,314,3.224,322,2.435,338,1.555,351,2.288,353,2.605,373,2.923,377,0.904,387,2.815,403,2.594,418,2.021,471,3.649,476,1.174,477,2.398,484,1.713,514,3.055,525,1.905,527,0.884,557,2.012,578,1.589,605,1.463,614,1.865,621,2.078,628,2.088,637,2.594,644,3.559,708,3.162,709,2.643,724,1.968,755,1.821,759,1.417,760,1.194,762,2.723,802,1.635,814,1.741,820,2.078,840,1.834,846,1.941,847,3.224,880,2.668,896,2.964,913,3.008,977,1.474,983,3.107,988,1.977,994,2.723,1038,2.752,1052,3.457,1100,1.602,1178,2.334,1206,2.152,1209,3.457,1288,2.415,1307,2.815,1326,3.682,1395,3.559,1475,4.188,1540,3.497,1558,2.276,1633,1.905,1785,3.457,1930,3.837,2010,3.919,2205,4.043,2214,2.618,2268,2.305,2322,3.008,2426,2.723,2609,3.292,2656,4.094,2967,3.682,3007,4.356,3008,2.815,3009,3.369,3010,4.356,3011,3.837,3012,3.559,3013,4.356]],["t/387",[2,1.175,4,1.352,5,1.535,6,3.151,7,0.644,15,1.589,17,1.722,22,2.704,24,1.602,26,1.618,28,1.396,33,2.484,55,3.645,56,0.977,85,2.321,103,2.242,107,2.09,108,2.251,109,1.761,125,2.484,134,2.169,135,2.484,138,1.758,139,3.664,145,1.753,160,2.349,162,1.531,164,1.197,170,3.535,183,1.871,190,2.24,193,2.437,216,2.219,217,2.484,228,1.381,229,2.585,245,2.603,279,1.883,281,2.062,283,1.26,289,3.227,303,3.409,304,0.918,313,1.065,338,1.648,351,1.577,353,1.188,359,1.922,362,3.409,365,2.208,369,2.704,383,3.034,392,2.704,407,1.97,430,2.15,437,3.146,440,2.955,447,2.062,471,3.798,496,2.662,557,2.169,565,1.423,568,1.919,608,3.478,636,3.773,677,2.296,684,2.273,757,1.074,759,3.662,760,3.215,765,2.585,801,3.243,802,2.711,859,2.262,873,3,908,2.219,945,1.442,995,2.425,1072,2.014,1078,3.151,1089,2.662,1100,1.727,1182,2.078,1212,3.726,1218,4.38,1262,2.411,1283,3.475,1332,2.905,1399,2.622,1540,2.411,1558,3.773,1754,1.852,2010,2.332,2015,2.567,2191,2.876,2290,3.837,2354,4.358,2973,4.358,3014,3.726,3015,4.696]],["t/389",[7,0.774,16,1.649,17,1.971,18,2.192,21,1.409,24,1.699,28,0.704,32,3.12,46,2.067,50,2.918,56,0.733,84,0.54,89,2.77,107,1.9,109,0.841,123,1.7,124,3.146,131,2.263,134,2.482,160,1.749,163,2.942,169,4.848,170,3.366,174,1.483,181,2.826,182,1.669,190,2.564,199,2.46,219,1.544,228,1.58,247,2.192,262,3.26,279,3.207,283,1.442,289,1.971,292,2.17,311,4.062,341,2.743,343,1.774,392,3.095,422,5.052,430,2.46,433,2.88,447,2.36,471,3.673,496,3.047,502,1.375,503,4.988,506,2.959,513,2.351,536,1.486,553,2.641,558,2.231,562,2.45,623,3.554,628,2.576,635,1.81,637,3.201,677,2.628,702,3.359,709,3.26,719,3.001,723,3.509,747,2.048,757,1.229,760,2.619,802,2.018,805,3.395,907,5.442,933,2.826,1038,3.395,1072,2.305,1083,2.418,1089,3.047,1121,3.024,1144,2.713,1159,5.919,1180,3.473,1237,3.23,1271,3.693,1467,3.12,1500,3.77,1531,3.325,1638,2.576,1787,4.543,1843,3.359,2250,2.247,2272,3.712,2300,4.734,2467,4.391,2928,2.628,3016,8.942,3017,5.375]],["t/391",[2,1.275,7,0.753,9,3.061,24,1.548,28,0.667,31,1.742,36,2.084,49,1.602,56,1.065,84,0.772,101,2.516,107,1.21,109,2.058,115,2.176,116,1.764,123,1.611,127,1.55,132,1.388,139,2.121,157,2.729,160,3.35,164,1.299,170,2.824,172,1.637,174,2.841,200,2.342,203,1.707,213,1.923,223,2.662,233,2.58,236,1.874,261,2.395,283,1.367,313,1.155,318,1.151,328,3.12,336,1.923,338,2.351,351,1.711,353,1.289,377,1.057,447,3.373,471,1.959,502,1.303,508,5.056,517,2.865,607,2.732,634,3.632,683,1.769,756,3.346,759,3.009,765,2.804,827,2.395,833,2.695,975,3.033,994,4.116,1037,2.352,1090,2.099,1108,2.418,1121,2.865,1205,4.042,1230,3.572,1235,3.466,1271,2.352,1314,3.218,1354,3.253,1508,5.964,1843,3.183,1959,2.254,2010,2.987,2082,4.161,2257,5.976,2272,3.517,2277,3.183,2322,3.517,2577,2.49,2656,3.061,3018,5.093,3019,5.093,3020,5.093,3021,5.941,3022,5.093,3023,3.517,3024,3.769]],["t/393",[7,0.453,24,1.928,29,2.004,49,1.291,56,0.567,84,1.137,107,2.107,109,1.621,115,2.645,182,1.922,226,2.786,228,1.82,233,2.079,236,1.511,246,2.172,254,3.376,275,1.778,283,1.661,318,2.341,328,3.792,353,1.567,358,4.788,372,2.15,375,3.339,376,3.275,383,4,397,2.388,426,2.122,437,2.697,471,2.381,502,1.584,527,2.102,555,2.718,578,3.234,611,2.388,634,6.323,637,3.687,663,3.509,677,3.027,683,2.15,694,3.125,760,1.697,798,3.16,804,4.912,840,2.606,945,2.723,994,2.474,1038,3.911,1100,2.277,1138,5.235,1236,3.83,1365,3.317,1540,3.178,1691,5.058,1699,3.564,1849,4.475,2254,4.25,2295,5.233,2421,3.432,2449,5.452,2575,5.745,2587,4.912,2656,6.798,3025,6.775,3026,6.191,3027,4.275,3028,6.191,3029,6.191,3030,4.678]],["t/395",[2,2.079,7,0.608,18,4.422,56,0.994,129,4.604,193,2.803,227,5.087,246,2.915,313,1.883,318,1.877,339,3.888,358,6.423,359,3.399,392,4.782,394,3.853,405,2.452,430,3.802,471,3.194,519,6.423,550,3.955,553,4.082,578,3.029,611,3.204,653,3.168,677,4.061,683,2.884,795,5.038,855,4.744,866,6.59,880,5.087,917,4.367,933,5.701,1040,4.708,1289,4.903,1331,4.946,2142,3.835,2201,6.423,2250,3.471,2980,7.708,3031,8.306,3032,8.306,3033,8.306]],["t/397",[7,0.556,16,1.313,17,2.79,23,3.17,55,3.839,84,0.765,105,2.208,113,2.574,135,4.024,164,1.939,171,1.918,196,2.798,203,1.69,213,2.872,219,2.185,225,2.523,236,1.856,275,2.939,283,2.041,313,1.724,322,2.208,323,2.847,342,4.614,398,2.486,401,2.698,421,4.415,438,3.663,479,4.915,525,3.326,543,5.346,548,4.345,552,2.498,614,1.299,703,5.629,757,1.739,828,2.943,909,5.882,937,4.217,944,2.855,1019,2.594,1080,3.927,1155,5.629,1374,4.754,1502,6.035,1507,9.366,1526,6.429,1607,4.614,1638,3.645,1775,5.882,2096,5.882,2890,5.748,3034,7.606,3035,6.035,3036,6.035]],["t/399",[2,1.714,5,1.434,7,0.697,9,2.636,15,1.484,16,1.183,18,2.793,19,1.814,21,1.15,23,2.95,24,0.659,28,0.574,49,0.915,55,2.214,56,1.048,84,0.957,88,1.833,89,1.519,105,1.273,116,1.519,127,1.335,131,1.847,156,2.771,160,1.427,164,1.118,165,1.752,171,1.727,189,2.178,196,1.613,213,1.657,219,1.26,220,3.912,221,2.348,225,1.455,232,2.844,236,1.07,239,2.335,283,2.772,284,2.742,292,1.771,306,3.472,313,0.995,316,1.814,322,1.273,338,1.001,347,2.687,348,2.612,351,1.473,353,1.733,359,2.803,366,3.976,367,2.072,377,0.91,386,3.029,393,1.999,395,1.609,397,3.674,407,1.84,408,1.47,440,1.795,447,1.926,471,2.634,482,1.82,512,1.404,530,1.82,543,3.579,552,1.441,558,1.82,611,1.692,614,1.17,636,2.292,647,1.527,684,2.123,734,1.801,736,1.736,747,1.672,790,2.834,795,2.661,796,2.134,802,2.571,807,1.573,820,2.092,840,1.847,857,2.884,859,3.299,904,8.087,907,2.985,908,2.072,909,3.392,911,3.128,933,2.306,934,3.135,995,2.265,1019,3.247,1096,3.708,1108,2.082,1109,3.481,1121,2.468,1129,3.315,1178,2.35,1219,1.814,1265,2.134,1374,4.281,1507,6.117,1540,2.252,1775,3.392,2035,2.713,2272,3.029,2329,3.481,2549,3.584,2577,2.145,2649,3.863,2977,4.071,2985,4.071,2989,3.708,3037,3.863,3038,4.387,3039,4.387,3040,4.387,3041,3.863]],["t/401",[2,2.306,7,0.848,16,1.591,26,3.174,28,1.518,55,5.85,132,3.159,162,3.005,249,6.362,342,5.589,446,5.439,467,6.688,511,2.726,684,4.459,802,4.351,813,5.537,814,4.632,1090,5.225,1100,3.389,1288,6.425,1313,4.969,1411,5.537,2401,8.551]],["t/403",[7,0.486,23,2.059,28,0.87,31,1.507,32,3.858,55,4.707,56,0.609,84,0.937,89,2.302,103,2.064,105,1.929,109,1.04,123,2.102,139,2.768,156,4.199,157,3.561,162,2.167,164,1.694,165,1.382,170,3.202,225,2.205,236,1.622,311,5.023,341,4.76,353,3.112,471,2.556,512,2.127,530,2.758,558,2.758,559,5.023,560,3.283,568,1.766,605,2.232,607,2.364,636,3.473,696,4.347,702,4.154,741,3.994,756,2.895,760,2.557,764,4.154,888,3.25,994,2.656,1175,2.84,1237,3.994,1238,4.74,1271,3.069,1332,4.111,1365,3.561,1399,3.711,1423,4.46,1540,3.412,2010,2.147,2099,3.711,2268,3.516,2322,6.439,2421,5.169,2426,4.154,2637,5.023,2960,3.858,2967,5.618,2968,5.274,2969,5.853,2992,4.347,2993,4.825,3012,5.43,3016,5.618,3042,6.646,3043,6.646,3044,5.274,3045,6.646,3046,5.853]],["t/405",[7,0.876,18,3.959,26,3.345,55,4.9,115,4.149,160,3.158,206,5.835,291,5.307,306,4.001,507,4.9,683,3.371,684,4.699,802,4.495,918,5.782,1100,3.571,1219,4.015,1477,7.508,1478,7.932,1479,6.201,1480,6.809,1481,4.821,2444,6.006,2893,7.508]],["t/407",[7,0.827,16,1.381,17,1.971,18,4.612,19,4.676,26,1.852,28,1.047,31,1.219,48,2.024,55,4.037,84,0.54,105,1.56,123,1.7,132,1.465,158,2.493,159,4.947,160,1.749,165,1.663,182,1.669,189,2.669,221,1.842,225,2.653,226,2.418,227,3.292,232,1.814,245,2.98,257,2.539,262,3.26,278,1.787,281,2.36,296,1.922,318,1.214,332,2.589,341,2.743,348,3.201,353,1.36,375,2.899,390,2.408,391,2.1,393,2.45,447,3.511,471,2.067,478,4.499,558,2.231,560,3.951,565,1.629,568,1.428,611,2.074,637,3.201,677,2.628,684,2.602,696,3.515,719,3.001,731,2.615,767,3.833,809,3.433,827,2.528,828,2.08,833,2.843,858,2.46,878,4.157,880,3.292,904,4.898,906,3.23,913,3.712,988,2.439,1121,3.024,1207,3.607,1233,4.062,1237,3.23,1238,5.703,1253,2.98,1263,3.77,1267,3.77,1269,4.543,1313,2.899,1333,3.902,1390,3.433,1482,4.988,1483,4.988,1672,4.391,1688,3.395,2250,3.992,2268,4.231,2272,5.523,2282,4.265,2580,4.543,2601,4.734,2868,2.713,2964,4.543,2966,3.978,2968,6.346,3047,5.375,3048,5.375,3049,5.375,3050,5.375,3051,4.988,3052,5.375,3053,5.375,3054,5.375,3055,5.375,3056,5.375]],["t/409",[1781,3.73]],["t/411",[7,0.807,31,1.934,56,0.782,84,0.857,123,2.698,162,2.782,216,5.213,219,2.45,275,2.45,283,2.289,306,3.515,316,3.527,318,1.927,390,3.822,463,4.763,527,1.731,555,3.745,565,2.584,567,3.174,568,2.267,604,5.224,683,2.962,692,6.312,693,4.872,734,3.503,738,6.969,798,4.354,802,3.202,814,3.409,843,4.329,850,5.276,854,5.804,856,4.992,858,3.904,868,7.21,901,3.887,993,3.202,1390,5.448,1480,5.983,2089,5.276,3057,7.512]],["t/413",[7,0.79,10,4.142,19,3.412,22,4.751,23,2.556,24,1.24,27,1.818,34,2.552,84,0.829,88,3.449,94,4.142,103,2.562,106,3.673,123,2.61,158,3.827,181,4.338,220,4.713,221,2.828,225,2.737,226,3.713,313,1.871,332,3.974,339,3.862,341,4.212,384,6.381,387,5.332,491,2.308,495,4.839,506,4.542,511,2.441,512,2.64,513,3.608,519,6.381,599,3.936,694,5.45,773,5.99,855,4.713,898,4.142,1159,6.106,1323,5.271,1454,6.381,1456,4.034,1459,5.005]],["t/415",[7,0.852,26,3.197,28,1.215,41,3.907,47,5.432,56,0.85,90,5.092,92,4.372,93,3.649,94,4.659,97,3.851,132,2.53,186,4.059,283,2.491,597,5.581,599,4.427,601,4.043,611,3.58,614,1.586,619,5.527,620,4.764,715,4.427,901,4.23,1161,6.868,1162,4.792,1368,5.527]],["t/417",[7,0.852,16,1.603,27,2.044,28,1.525,84,0.933,88,3.879,89,4.406,145,3.464,174,2.56,244,5.301,278,3.086,283,2.491,289,3.404,294,4.634,306,3.824,318,2.097,512,2.97,558,3.851,575,4.091,780,4.585,857,3.907,896,6.316,897,5.432,914,5.432,942,6.316,993,3.484,1022,4.737]],["t/419",[4,2.3,7,0.867,23,3.276,26,2.753,28,1.046,31,2.397,56,0.732,84,1.063,103,2.481,123,3.344,219,2.295,232,4.256,236,1.95,283,3.18,313,1.812,318,1.805,391,3.122,443,5.226,480,5.048,511,2.364,530,3.316,545,3.427,548,4.564,552,3.473,558,3.316,567,3.934,568,2.124,601,3.481,629,4.717,685,4.126,814,3.193,833,4.227,892,2.775,914,4.677,1262,4.102,1266,4.767,1330,4.847,1503,6.34]],["t/421",[2,2.34,7,0.684,24,1.918,84,0.94,165,1.944,248,4.073,283,2.509,285,3.585,313,2.12,397,3.607,512,2.992,536,2.584,565,2.833,567,3.479,568,2.485,757,2.919,759,3.042,760,2.564,843,4.745,1219,3.866,1285,3.7,1462,4.693,2017,3.653,2368,6.667,3058,7.639,3059,5.221]],["t/423",[7,0.739,16,1.743,24,1.843,28,1.322,56,0.925,84,1.015,132,3.343,145,3.768,228,2.968,502,2.584,565,3.059,568,2.683,683,3.506,769,3.601,1251,6.687,1329,5.241,2311,6.31]],["t/425",[7,0.721,16,1.703,27,2.172,31,2.235,40,4.821,56,0.904,96,3.223,107,2.872,109,2.046,115,4.213,123,3.118,163,3.627,278,3.279,458,5.152,527,2.001,597,4.726,635,3.32,653,2.88,814,3.94,994,3.94]],["t/427",[7,0.789,16,1.862,28,1.412,33,5.706,109,1.688,132,3.478,170,2.957,283,2.894,444,4.339,576,5.896,734,4.429]],["t/429",[23,2.854,24,1.385,25,3.444,28,1.206,32,5.348,49,1.922,56,0.844,98,3.005,132,2.511,157,4.937,165,1.915,236,2.248,276,4.236,337,3.982,339,4.313,491,2.577,502,2.966,513,4.029,544,5.348,558,3.823,565,2.792,577,5.089,757,2.107,795,5.589,796,4.482,1019,3.142,1155,6.818,2017,3.599,2217,6.57]],["t/431",[2,2.568,107,2.438,109,2.082,115,4.384,163,3.774,174,2.83,254,3.062,313,2.326,507,5.179,527,2.082,550,3.742,658,4.56,755,4.288,1099,3.526,1102,5.688,3060,9.036]],["t/433",[2,2.135,7,0.807,16,1.473,27,1.879,28,1.445,56,0.782,109,1.727,126,3.822,132,2.325,138,3.192,172,3.932,190,4.068,193,3.724,288,5.224,289,3.128,313,1.934,337,3.687,338,1.947,339,3.993,408,2.858,491,2.386,552,2.802,611,3.291,614,1.457,632,4.258,755,3.565,806,6.192,810,3.515,819,5.578,830,3.618,872,5.126,977,2.886,1070,4.512,1093,4.484,1302,4.798,2293,4.631,3061,7.21]],["t/435",[7,0.769,27,2.317,29,3.405,109,1.646,127,3.2,224,4.135,248,4.581,338,2.401,390,4.712,434,6.505,555,4.617,715,5.016,1112,5.789,1401,5.749,3062,8.344]],["t/437",[7,0.839,58,7.697,275,3.295,564,6.893,1625,8.045]],["t/439",[7,0.684,13,6.787,84,0.94,228,2.749,283,3.139,294,4.668,300,5.259,332,4.503,395,3.429,399,6.558,476,2.519,542,7.23,543,6.112,742,5.427,885,6.456,977,3.164,1035,5.183,1272,6.667,1282,7.066,1290,5.111,1291,6.667,1292,5.844,1566,6.192,1802,5.259,2598,5.341,3063,6.274,3064,6.558,3065,8.234]],["t/441",[7,0.884,11,7.297,17,3.616,18,4.021,19,4.077,28,1.291,158,5.608,332,5.822,391,3.852,914,5.771,994,3.94,1090,4.063,1310,8.55,1314,6.229,1315,8.056,1316,8.056,1317,10.219]],["t/443",[1,3.529,2,1.638,7,0.954,17,2.4,24,0.983,28,0.857,31,2.422,84,0.927,98,2.134,106,2.225,109,1.024,115,4.564,123,2.917,135,3.461,174,2.544,233,4.107,278,2.176,298,3.709,313,1.483,316,2.706,336,2.471,353,1.656,361,4.089,363,4.333,381,2.873,397,3.558,408,2.192,430,2.995,444,2.632,484,2.573,492,4.75,527,2.353,550,2.386,568,2.451,572,3.216,578,2.386,605,2.198,611,3.558,623,2.908,655,3.969,658,2.908,712,3.267,715,3.121,829,3.199,901,2.982,910,4.819,1117,4.048,1120,5.762,1167,5.346,1206,3.232,1253,3.627,1263,4.589,1264,3.969,1309,3.529,1696,3.863,2268,3.461,2361,4.75,2430,3.738,2450,4.048,2641,4.333,2642,5.531,2960,3.798,3066,6.276]],["t/445",[1781,3.73]],["t/447",[7,0.81,16,1.911,34,2.616,164,2.822,261,5.205,484,4.352,532,5.357,747,4.218,3067,9.748]],["t/449",[4,2.559,7,0.829,15,3.008,16,1.535,34,2.101,49,2.363,56,0.815,84,0.894,103,2.76,192,4.859,203,1.975,228,2.613,261,5.328,318,2.56,401,4.019,484,3.495,607,3.162,614,1.519,723,2.949,977,3.008,1076,2.971,1224,3.031,1913,4.645,2000,5.16,2013,2.748,2141,4.22,2722,5.078,2898,5.556,3068,7.829,3069,7.829,3070,7.237]],["t/451",[7,0.937,49,1.965,203,2.092,278,3.132,304,1.842,318,3.031,398,3.842,401,3.341,527,1.911,565,2.854,568,2.503,760,2.583,927,5.769,1089,5.339,1236,5.827,1297,5.288,1313,5.08,2142,5.427,2340,5.38,2388,4.582,3071,6.717]],["t/453",[7,0.881,19,3.415,21,1.472,23,2.559,24,1.92,28,1.508,34,2.846,49,1.171,84,1.158,85,1.805,88,2.346,89,2.86,103,3.043,105,1.629,116,1.944,132,2.671,162,1.831,165,1.167,196,2.065,203,1.247,228,1.65,232,3.307,236,1.37,254,1.675,261,4.608,283,1.506,285,2.152,353,2.09,372,1.949,377,1.165,397,2.166,398,2.7,471,2.159,512,2.643,527,1.676,530,2.33,536,1.551,545,3.542,565,3.489,568,1.492,575,2.475,580,2.661,613,2.355,630,1.924,757,1.284,759,3.746,760,3.157,857,3.477,916,2.922,1019,1.914,1132,3.403,1182,3.655,1251,3.718,1550,3.876,1558,2.933,1842,3.876,2099,4.612,2142,2.592,2250,2.346,2253,3.158,2312,4.745]],["t/455",[7,0.655,16,1.965,19,3.702,24,1.88,27,1.972,28,1.639,34,2.69,107,2.973,109,2.061,116,3.1,132,3.102,135,4.736,145,3.341,170,2.455,228,3.346,236,2.185,261,5.352,353,2.266,372,3.109,528,4.377,596,3.742,1182,3.962]],["t/457",[7,0.777,19,3.786,21,1.698,24,0.973,25,1.924,27,2.543,28,0.848,31,1.468,40,3.166,48,2.438,56,0.593,84,0.92,96,2.117,107,2.175,109,1.906,123,2.048,124,3.791,127,2.787,135,3.426,158,3.004,160,2.979,219,1.86,221,3.138,226,2.914,272,3.479,275,1.86,278,2.153,283,1.738,316,2.678,318,2.752,360,4.289,394,3.004,395,2.375,397,2.498,471,3.521,527,2.342,550,3.339,555,2.843,557,2.991,561,5.291,562,2.952,565,1.962,567,2.41,568,1.721,605,3.075,607,2.303,630,2.22,654,5.291,729,4.289,757,1.481,870,4.701,974,3.671,995,3.344,1141,4.289,1202,3.699,1224,2.208,1289,3.823,1367,3.791,1628,4.618,1773,4.542,1781,2.029,2625,3.448,3062,5.139,3072,5.139]],["t/459",[7,0.689,16,2.029,24,1.415,26,4.049,34,2.226,49,1.965,145,3.515,159,5.827,164,2.401,261,4.43,276,4.33,300,5.299,359,3.854,392,5.423,393,5.356,399,6.607,403,5.61,611,3.634,742,5.467,744,4.983,764,7.345,1010,5.827,1802,5.299,3063,6.321,3064,6.607]],["t/461",[7,0.832,8,5.675,133,4.513,393,5.181,434,7.032,436,7.528]],["t/463",[7,0.897,15,3.417,26,3.478,28,1.322,47,5.909,56,0.925,84,1.015,90,4.415,92,3.469,93,3.97,97,4.19,130,5.341,138,3.779,272,3.836,367,4.77,408,3.382,440,4.131,441,5.096,605,3.391]],["t/465",[7,0.716,16,1.689,88,4.089,109,1.531,133,3.884,134,4.518,189,4.859,192,5.348,194,6.977,200,4.498,229,5.386,248,4.262,283,2.625,284,6.115,353,2.476,436,6.48,545,4.197,918,5.827,1123,5.348,1206,4.833,1365,5.242,1400,4.712,1558,5.112,2151,7.24]],["t/467",[2,1.317,7,0.917,19,2.176,23,1.631,24,1.573,28,1.235,31,1.193,34,1.244,46,3.028,56,0.959,92,4.042,93,3.709,101,2.6,105,1.528,107,2.241,109,1.476,123,1.664,127,2.396,162,1.716,182,1.634,207,1.993,216,2.487,217,2.784,219,1.512,221,1.804,225,1.746,229,2.897,232,1.776,236,1.921,276,2.42,280,3.107,283,1.412,294,3.931,306,2.169,313,1.193,315,2.32,318,1.189,323,3.53,361,3.289,390,2.358,407,2.208,476,2.541,491,1.472,527,1.068,528,3.849,546,4.07,552,1.729,555,2.311,562,2.399,566,3.401,590,2.628,597,3.773,599,3.755,601,2.293,613,2.208,614,0.899,623,2.339,629,3.107,647,1.832,658,2.339,677,2.573,691,2.686,693,3.006,694,2.656,707,2.718,734,2.161,814,3.146,827,2.475,828,2.037,833,2.784,904,3.223,1124,6.655,1145,3.72,1212,4.176,1221,3.401,1330,3.193,1583,3.895,2331,3.107,2334,4.884,2483,4.884,2515,3.895,2541,4.3,2568,4.3,2763,4.635,3012,4.3,3061,4.449,3073,5.263,3074,3.977,3075,4.3,3076,5.263,3077,4.3,3078,4.449,3079,5.263,3080,5.263,3081,5.263]],["t/469",[7,0.9,21,1.299,23,1.535,24,1.894,26,1.707,27,1.657,28,1.33,30,2.875,31,1.123,40,2.422,46,4.421,56,0.454,84,0.914,85,1.592,90,2.166,92,3.125,96,2.459,97,2.056,103,1.538,107,2.413,109,1.926,115,2.117,127,1.508,154,4.363,170,1.358,171,1.249,172,2.418,179,1.538,183,1.973,193,2.539,225,1.643,236,1.209,254,2.245,256,1.72,282,2.5,372,1.72,395,1.817,437,3.277,447,2.175,455,3.744,476,2.45,491,1.386,525,2.166,552,2.987,576,2.708,597,3.605,599,4.843,602,4.047,604,3.034,611,1.911,632,2.473,638,2.766,658,2.201,671,4.187,694,2.5,707,3.884,769,2.683,814,3.006,830,3.19,838,2.319,840,2.086,944,1.86,984,2.986,993,1.86,1008,2.746,1070,2.621,1074,3.324,1075,2.41,1097,3.533,1099,3.752,1123,4.112,1124,6.358,1145,3.554,1157,5.276,1158,4.597,1210,3.596,1257,3.596,1271,2.288,1285,1.96,1806,3.24,1849,2.5,2320,3.831,2947,3.831,2951,2.727,3012,4.047,3082,4.954]],["t/471",[7,0.412,10,2.83,15,1.908,24,1.627,31,1.278,56,1.271,92,3.721,93,2.217,108,2.702,131,3.489,155,3.389,181,2.964,182,1.751,191,3.784,201,2.664,203,1.253,225,1.87,300,3.172,308,5.233,309,5.233,312,7.27,315,2.486,322,1.637,323,2.11,361,3.524,381,2.476,392,3.246,395,3.039,405,1.665,421,3.273,426,1.933,483,3.3,491,1.577,553,2.771,562,4.477,571,3.894,600,3.488,607,2.006,635,1.898,677,2.757,690,3.246,694,2.846,735,7.368,757,1.289,763,3.562,778,3.3,809,3.602,921,4.891,923,3.3,947,5.354,1163,3.524,1219,2.332,1353,3.453,1380,4.474,1382,4.474,1383,4.474,1664,3.837,1923,2.785,2272,3.894,3083,5.639,3084,5.639,3085,5.639,3086,5.639,3087,5.639,3088,4.766,3089,5.233,3090,5.639,3091,6.015,3092,5.639,3093,4.173,3094,5.233,3095,5.233,3096,4.173,3097,5.233,3098,4.766,3099,4.766,3100,4.766,3101,4.766,3102,4.766,3103,4.766,3104,5.639,3105,5.639,3106,4.966,3107,5.639,3108,4.766,3109,4.766,3110,4.766,3111,4.766]],["t/473",[0,1.919,2,0.506,7,0.353,10,2.419,15,1.212,16,0.618,17,0.742,19,0.836,21,1.528,24,1.001,25,1.979,26,0.697,28,0.469,29,0.655,31,0.812,32,2.078,34,0.478,36,2.384,39,2.2,42,1.376,48,0.761,50,1.944,56,1.324,84,0.74,90,0.884,92,3.44,95,2.062,96,0.661,98,1.168,100,0.839,106,0.688,108,0.969,109,0.316,115,0.864,123,0.64,124,1.184,126,0.906,140,1.302,153,0.96,155,1.215,157,1.083,164,0.516,165,1.002,175,2.957,176,1.528,177,2.159,183,0.805,184,1.418,186,2.548,191,3.91,197,1.481,200,0.93,203,0.795,204,3.642,213,0.764,216,0.955,220,1.155,221,1.227,225,0.671,236,0.493,262,1.227,275,0.581,278,0.672,283,0.543,304,0.395,311,1.528,312,3.566,313,0.812,315,1.578,322,0.587,339,1.676,343,0.667,353,0.906,370,1.468,381,1.572,384,1.564,395,1.313,398,1.171,405,1.423,408,0.677,413,2.511,421,1.174,426,1.652,430,0.926,432,1.184,440,1.465,446,1.194,477,1.113,483,2.096,485,1.418,491,1.63,506,1.971,508,1.105,511,1.059,553,1.76,566,1.307,568,0.537,569,1.605,589,1.709,590,1.01,593,2.667,596,1.497,600,1.251,612,1.215,628,0.969,630,0.693,650,1.264,653,0.591,664,1.376,677,0.989,698,1.357,702,2.238,703,1.496,710,7.473,723,3.827,725,0.955,735,5.775,750,1.322,757,1.332,758,1.564,773,1.468,778,1.184,779,3.566,780,0.999,784,2.437,787,2.706,790,1.307,796,0.984,824,1.376,828,0.783,921,4.703,923,2.096,934,0.926,947,3.114,977,0.684,984,1.422,1027,2.114,1083,2.621,1089,1.146,1159,2.65,1163,1.264,1174,4.163,1175,0.864,1207,1.357,1219,0.836,1222,1.442,1285,0.8,1323,1.292,1353,1.239,1380,4.623,1382,2.841,1383,1.605,1406,1.442,1409,1.239,1635,2.769,1664,2.437,1779,1.264,1860,1.877,1994,2.314,2200,2.841,2306,1.528,2328,1.709,2450,3.604,2482,1.781,2484,1.442,2485,1.877,2541,1.652,2545,1.239,2630,6.836,2672,2.403,3088,1.709,3089,3.323,3091,4.229,3093,1.496,3094,1.877,3095,1.877,3096,1.496,3097,1.877,3098,1.709,3099,1.709,3100,1.709,3101,1.709,3102,1.709,3103,1.709,3106,1.781,3108,1.709,3109,1.709,3110,3.027,3111,1.709,3112,1.877,3113,2.022,3114,1.709,3115,2.022,3116,3.581,3117,3.581,3118,2.769,3119,2.022,3120,2.022,3121,3.581,3122,5.407,3123,2.022,3124,5.826,3125,3.581,3126,2.022,3127,3.581,3128,2.022,3129,3.581,3130,3.581,3131,2.022,3132,2.022,3133,6.182,3134,3.581,3135,4.819,3136,2.022,3137,1.564,3138,2.022,3139,1.564,3140,2.022,3141,3.581,3142,2.022,3143,1.468,3144,2.022,3145,3.581,3146,3.581,3147,2.022,3148,2.022,3149,2.022,3150,2.022,3151,2.022,3152,2.022,3153,3.581,3154,2.022,3155,2.022,3156,2.022,3157,1.781,3158,2.022]],["t/475",[2,1.722,4,0.739,7,0.503,8,2.895,16,1.001,19,3.497,21,1.521,24,1.436,25,2.302,28,0.336,29,0.832,49,0.92,50,1.395,56,0.775,84,0.91,85,0.826,89,0.89,90,1.123,92,4.567,94,1.289,97,1.831,100,2.406,105,0.746,108,2.114,112,2.028,113,0.869,115,1.098,132,0.7,162,0.838,164,1.754,165,0.534,175,1.304,177,1.151,179,0.798,182,1.37,186,1.929,192,1.404,197,1.824,198,2.62,207,1.671,213,0.97,216,1.214,228,0.755,236,0.627,246,3.749,257,1.214,275,2.225,279,1.03,282,1.296,283,1.846,292,1.037,294,1.282,306,2.389,312,6.259,313,1.315,316,1.062,318,0.58,323,1.651,337,1.11,339,1.202,340,1.589,343,1.914,348,1.53,353,1.96,359,1.051,377,0.533,380,2.262,386,1.774,388,1.724,391,1.723,405,0.758,407,1.077,422,1.623,430,2.019,437,1.119,438,2.125,440,1.051,441,1.296,443,6.247,478,3.262,480,1.623,482,1.066,500,1.937,504,1.445,511,1.305,522,1.774,526,1.724,536,1.219,541,1.467,560,2.179,568,0.683,578,0.937,605,0.863,614,1.175,622,1.202,651,1.589,653,0.75,670,1.385,680,1.865,699,2.262,702,1.605,710,4.209,714,5.006,715,4.034,723,2.805,735,3.002,756,1.119,757,1.326,796,1.249,846,0.732,873,1.641,881,1.491,916,0.909,921,1.516,939,1.359,966,1.741,988,1.166,989,1.342,1087,4.135,1123,3.761,1132,1.058,1142,1.573,1145,1.214,1156,3.604,1162,1.326,1175,1.098,1215,2.384,1218,1.558,1219,1.824,1254,4.5,1265,1.249,1313,1.385,1322,1.748,1333,1.865,1390,4.948,1396,5.321,1402,2.985,1406,3.146,1458,5.272,1461,3.334,1687,1.986,2035,1.589,2190,1.334,2198,2.961,2259,2.729,2307,2.099,2353,2.262,2404,1.641,2432,2.384,2459,3.203,2584,1.68,2625,3.087,2866,2.038,2876,1.901,2884,2.038,2928,1.256,3096,1.901,3112,2.384,3159,1.986,3160,2.038,3161,1.986,3162,1.986,3163,5.816,3164,3.729,3165,3.729,3166,3.729,3167,3.412,3168,2.171,3169,2.171,3170,2.171,3171,2.171,3172,2.171,3173,2.171,3174,2.171,3175,2.171,3176,2.171,3177,2.171,3178,4.412,3179,4.094,3180,2.569,3181,2.569,3182,2.569,3183,2.569,3184,2.038,3185,2.384,3186,2.384,3187,2.569,3188,2.569,3189,3.729,3190,2.569]],["t/477",[2,0.68,5,0.487,7,0.443,8,1.873,10,1.365,14,1.028,15,0.504,17,1.698,18,0.607,19,2.229,21,0.713,24,0.996,26,0.513,27,1.02,28,0.935,29,0.482,31,0.338,32,0.864,33,0.788,34,0.352,46,1.046,56,1.195,84,0.764,92,3.514,93,0.585,96,1.226,100,1.922,105,1.756,106,0.925,107,0.891,108,1.798,109,1.038,110,0.728,112,1.25,127,1.64,128,0.659,129,0.825,132,0.406,134,0.688,135,1.438,140,0.541,156,0.94,160,1.753,162,1.223,163,0.548,165,0.565,172,0.874,177,0.667,179,0.462,181,0.783,182,1.673,185,2.396,186,0.651,189,0.739,190,0.71,191,4.06,193,0.918,194,1.062,196,1,197,1.124,199,1.244,200,0.684,213,1.027,221,0.932,224,0.585,225,3.668,228,1.103,236,1.315,241,1.081,243,0.756,246,1.625,247,0.607,254,1.119,275,0.428,277,0.71,278,1.247,279,0.597,281,0.654,282,0.751,289,0.997,292,0.601,294,1.357,297,0.756,300,0.837,311,1.125,312,5.93,313,1.372,315,2.041,316,0.616,317,0.691,323,2.481,338,0.34,339,1.756,348,1.619,353,0.949,359,1.535,360,0.986,367,0.703,372,0.517,375,1.466,377,0.564,380,1.311,381,1.194,395,0.546,397,0.574,398,0.487,407,0.625,416,1.162,418,1.261,437,0.649,440,0.609,446,0.879,474,1.125,476,0.732,479,1.757,482,0.618,483,5.986,485,1.044,489,1.682,491,0.416,495,1.218,498,0.769,500,1.194,502,0.381,511,0.44,513,1.189,524,0.85,525,0.651,531,2.605,532,1.316,541,2.143,552,0.893,555,0.654,568,0.396,572,0.732,576,0.814,593,0.681,596,0.622,597,1.303,599,0.71,600,2.321,607,0.53,613,0.625,614,0.254,628,0.714,632,0.743,636,0.778,647,0.518,653,1.096,658,0.662,662,0.803,669,1.125,690,0.857,694,0.751,702,0.93,707,0.769,710,5.186,715,1.789,716,1.216,723,1.536,726,0.903,734,0.611,735,2.553,752,1.125,755,0.622,757,0.622,762,0.93,767,1.062,803,0.778,806,1.081,809,0.951,813,0.895,814,1.851,820,0.71,830,0.631,833,0.788,838,0.697,843,0.756,845,0.879,857,0.627,880,0.912,886,1.311,887,0.921,888,0.728,912,1.151,916,1.327,923,0.871,946,1.181,947,1.757,966,0.688,974,1.541,993,0.559,1017,0.986,1039,0.912,1083,4.687,1093,0.783,1099,0.934,1107,1.044,1123,2.051,1124,1.258,1141,0.986,1144,1.372,1157,1.044,1160,1.125,1180,0.962,1202,0.85,1219,1.124,1254,0.974,1272,1.939,1285,1.484,1288,1.507,1365,0.798,1369,1.062,1370,1.102,1380,2.157,1382,2.157,1383,2.977,1409,0.912,1426,1.939,1437,1.907,1458,3.667,1461,1.125,1527,0.912,1605,1.013,1642,1.181,1664,1.013,1688,0.94,1691,1.216,1779,0.93,1781,0.467,1829,1.081,1849,0.751,1856,1.181,1923,0.735,2082,1.216,2086,1.013,2113,0.951,2169,1.258,2298,1.125,2435,2.523,2450,1.682,2484,1.062,2493,1.258,2499,1.181,2592,1.382,2612,1.311,2625,0.793,2866,1.181,2875,1.028,2898,0.93,3088,1.258,3091,2.723,3098,1.258,3099,1.258,3100,1.258,3101,1.258,3102,1.258,3103,1.258,3108,1.258,3109,1.258,3110,1.258,3111,1.258,3157,2.394,3179,1.382,3191,7.951,3192,1.489,3193,1.489,3194,1.489,3195,1.489,3196,2.523,3197,3.481,3198,1.489,3199,1.489,3200,1.489,3201,1.489,3202,1.489,3203,1.311,3204,1.489,3205,1.489,3206,1.489,3207,1.382,3208,1.489,3209,1.489,3210,1.489,3211,1.489,3212,1.489,3213,1.489,3214,1.382,3215,1.382,3216,1.382,3217,1.382,3218,1.489,3219,1.489,3220,1.489,3221,1.489,3222,1.489,3223,1.489,3224,1.489,3225,1.489,3226,1.489,3227,1.382,3228,1.489,3229,1.489,3230,1.489,3231,1.489,3232,1.489,3233,1.489,3234,1.258,3235,1.382,3236,1.489,3237,2.298,3238,3.171,3239,1.311,3240,1.489,3241,1.489,3242,7.631,3243,2.523,3244,1.382,3245,1.382,3246,1.489,3247,3.304,3248,2.523,3249,1.125,3250,1.489,3251,2.523,3252,1.382,3253,1.489,3254,1.489,3255,1.489,3256,1.382,3257,1.489,3258,1.489,3259,1.382,3260,2.523,3261,1.382,3262,1.489,3263,1.489,3264,1.311,3265,1.382,3266,1.489,3267,1.489,3268,1.382,3269,1.489,3270,1.489,3271,1.382,3272,1.489,3273,1.489,3274,2.298,3275,1.382,3276,1.489,3277,1.489,3278,1.382,3279,1.489,3280,1.382,3281,1.489,3282,1.489,3283,2.523,3284,1.489,3285,1.489,3286,1.382,3287,1.489,3288,1.489,3289,1.489,3290,1.489,3291,1.489,3292,1.382,3293,1.489,3294,1.489,3295,1.382,3296,2.977,3297,1.216,3298,1.489,3299,1.489,3300,1.216]],["t/479",[2,0.422,7,0.305,8,1.521,9,1.83,15,0.571,17,1.117,19,2.436,21,0.799,22,0.972,24,0.458,25,0.905,28,0.668,31,0.383,32,2.416,34,0.72,46,0.649,50,1.654,56,1.225,84,0.592,85,0.542,92,3.95,93,2.316,98,0.55,100,3.703,103,1.293,105,0.49,108,2.443,109,0.264,112,0.776,127,0.927,131,0.71,132,0.46,140,1.514,144,0.75,158,1.413,160,1.917,163,0.621,165,0.865,177,1.365,182,1.829,183,0.672,185,1.078,186,0.738,191,2.794,193,0.57,197,1.721,198,0.763,199,0.772,200,0.776,201,1.967,206,1.014,207,0.639,221,0.578,225,1.954,227,1.034,228,0.496,231,1.132,236,0.412,243,0.856,247,0.688,272,0.641,275,0.875,277,0.805,281,0.741,282,0.852,283,1.117,295,0.63,298,1.726,304,0.33,306,0.695,312,3.081,313,0.383,315,1.835,317,0.783,323,3.202,337,0.729,338,0.695,339,1.949,343,0.557,348,1.005,351,1.023,353,1.29,359,0.691,377,0.35,381,2.238,383,1.09,391,0.659,395,0.619,407,0.708,408,1.02,418,1.413,432,0.988,433,0.904,440,0.691,446,0.996,447,2.238,471,1.171,475,1.566,477,0.929,482,0.7,483,6.373,504,0.949,505,0.649,506,0.929,511,1.232,531,2.868,541,0.964,555,0.741,562,0.769,568,0.809,576,1.665,579,1.132,582,1.305,593,1.394,596,2.131,597,1.995,602,1.379,608,1.467,611,0.651,612,1.014,621,0.805,628,0.809,629,0.996,653,0.493,659,1.014,662,1.643,669,1.275,677,1.489,684,0.817,694,0.852,710,5.58,714,1.09,715,1.453,723,2.377,734,0.693,735,1.148,740,0.708,757,0.952,787,1.275,796,1.482,809,1.946,813,1.014,820,1.453,821,1.09,830,0.716,840,0.71,842,1.566,855,0.964,871,1.148,921,1.798,923,1.783,947,1.09,977,1.031,985,1.034,988,0.766,1017,1.118,1021,1.066,1070,0.893,1072,0.724,1083,4.636,1089,0.956,1100,0.621,1106,1.486,1121,2.342,1145,0.797,1174,2.602,1175,0.721,1178,0.904,1183,1.078,1207,2.044,1210,1.225,1214,1.339,1219,0.698,1221,1.09,1243,1.203,1253,0.935,1267,1.184,1272,1.203,1316,1.379,1375,0.949,1380,3.303,1382,3.303,1383,3.303,1390,1.946,1424,1.184,1426,1.203,1437,1.184,1458,2.833,1462,0.847,1480,1.184,1691,1.379,1733,0.935,1779,1.055,1993,1.339,2201,2.356,2298,1.275,2331,1.798,2333,1.339,2403,0.871,2421,1.689,2444,1.044,2538,1.566,2625,1.622,2644,1.566,2966,2.254,3051,1.566,3088,1.426,3098,1.426,3099,1.426,3100,1.426,3101,1.426,3102,1.426,3103,1.426,3106,1.486,3108,1.426,3109,1.426,3110,1.426,3111,1.426,3133,2.827,3189,2.575,3191,3.666,3196,1.566,3197,1.566,3207,1.566,3214,1.566,3215,1.566,3216,1.566,3217,1.566,3227,1.566,3234,1.426,3235,1.566,3237,2.575,3238,3.519,3239,3.666,3242,8.075,3243,1.566,3244,1.566,3245,1.566,3247,1.486,3248,1.566,3249,1.275,3251,1.566,3252,1.566,3256,1.566,3259,1.566,3260,2.827,3261,1.566,3264,1.486,3265,1.566,3268,1.566,3271,1.566,3274,3.519,3275,1.566,3278,1.566,3280,2.827,3283,2.827,3286,1.566,3292,1.566,3295,1.566,3300,8.896,3301,1.688,3302,3.046,3303,3.046,3304,2.575,3305,1.688,3306,1.688,3307,1.688,3308,1.688,3309,1.688,3310,1.688,3311,1.688,3312,1.688,3313,1.688,3314,1.688,3315,1.688,3316,1.688,3317,1.688,3318,1.688,3319,1.688,3320,1.688,3321,1.688,3322,1.688,3323,1.688,3324,3.046,3325,1.688,3326,1.688,3327,1.688,3328,1.688,3329,1.275,3330,1.688,3331,1.566,3332,1.688,3333,1.688,3334,1.688,3335,1.688,3336,1.688,3337,1.688,3338,1.688,3339,1.688,3340,1.688,3341,1.688,3342,1.688,3343,1.566,3344,1.688,3345,1.688,3346,1.688,3347,1.688,3348,1.688,3349,1.688,3350,1.688,3351,1.688,3352,1.688,3353,1.688,3354,1.688,3355,1.688,3356,1.688,3357,1.688,3358,1.688,3359,1.688,3360,1.688,3361,1.566,3362,1.688,3363,1.688,3364,1.688,3365,1.688,3366,1.688,3367,1.688,3368,3.046,3369,1.688,3370,1.566,3371,1.688,3372,3.046,3373,1.688,3374,1.688]],["t/481",[1,0.812,2,0.947,5,1.237,7,0.684,10,1.378,14,1.039,15,1.281,16,0.26,17,2.637,19,1.135,21,0.72,25,1.125,31,0.622,32,3.152,33,0.796,34,0.356,35,0.972,36,2.222,39,2.051,42,1.024,44,2.178,49,0.573,50,6.024,56,1.082,63,1.958,68,1.911,84,0.847,85,0.882,92,3.859,93,1.836,96,0.897,98,1.234,99,0.881,100,2.984,101,1.356,102,3.179,103,0.467,105,1.576,106,0.934,107,0.358,109,0.235,113,0.509,121,2.055,123,0.476,124,0.881,126,1.23,131,3.22,132,1.032,134,1.268,138,0.563,140,1.376,153,2.893,155,1.65,157,2.91,160,1.231,162,1.234,163,0.553,165,0.971,175,1.393,177,0.674,181,1.443,182,1.686,185,1.753,191,5.883,195,1.229,196,1.392,197,1.565,198,0.68,199,0.689,200,0.692,201,1.788,203,0.61,221,0.516,225,1.801,236,0.67,240,0.764,246,0.963,247,0.614,253,0.84,254,0.449,260,1.325,262,0.913,278,0.5,279,0.603,293,2.801,304,0.913,312,5.321,313,1.513,316,1.135,317,1.273,323,2.498,334,1.443,338,0.627,339,1.772,340,0.931,343,0.497,351,1.824,353,1.542,359,1.123,365,0.708,369,0.866,375,0.812,377,0.57,388,1.01,392,0.866,394,0.698,395,1.007,397,0.581,407,0.631,408,1.819,416,1.617,422,3.431,430,1.256,440,1.123,471,1.056,473,5.655,476,0.405,477,0.828,479,0.972,483,3.179,491,0.768,498,1.417,500,0.661,502,0.969,504,2.129,505,0.579,506,3.958,511,0.445,517,1.544,531,1.544,555,0.661,562,0.686,568,1.774,569,1.194,570,1.272,572,2.295,576,1.501,578,1.001,587,1.137,590,0.751,607,0.535,612,0.904,614,0.647,621,0.718,628,0.721,629,0.888,630,0.941,657,1.092,658,0.669,659,0.904,672,0.823,677,2.283,687,1.164,710,4.423,712,0.751,723,1.801,734,1.127,740,0.631,755,0.629,757,1.644,758,2.123,767,1.073,770,1.073,779,3.456,784,1.024,787,2.074,794,1.774,796,2.271,802,0.565,823,1.137,824,1.024,840,1.156,856,2.215,871,1.024,916,0.532,957,1.48,977,0.929,984,0.597,1072,0.645,1083,1.235,1090,0.62,1100,0.553,1108,1.797,1111,0.972,1112,0.828,1142,0.922,1174,1.716,1178,0.806,1183,0.961,1184,1.325,1251,0.997,1254,0.984,1283,2.801,1287,1.024,1312,0.961,1332,0.931,1364,0.698,1365,0.806,1426,1.073,1453,2.548,1458,2.576,1527,0.922,1531,1.698,1554,1.194,1730,1.137,1779,0.94,1930,1.325,2041,0.94,2082,1.229,2190,0.782,2201,1.164,2272,1.039,2331,0.888,2584,0.984,2631,4.889,2719,1.396,2864,1.092,2983,2.548,3027,3.224,3030,1.137,3078,1.272,3091,1.092,3122,1.396,3143,3.39,3191,4.112,3203,1.325,3234,1.272,3237,2.32,3238,1.272,3239,1.325,3242,3.947,3249,1.137,3274,1.272,3297,3.815,3300,7.413,3343,1.396,3375,1.505,3376,1.194,3377,2.745,3378,1.505,3379,1.505,3380,1.505,3381,1.505,3382,1.505,3383,1.505,3384,1.505,3385,7.19,3386,1.055,3387,1.055,3388,1.505,3389,1.505,3390,1.505,3391,1.505,3392,1.505,3393,1.505,3394,1.505,3395,1.505,3396,1.396,3397,1.092,3398,1.01,3399,1.272,3400,1.505,3401,2.745,3402,3.785,3403,2.417,3404,1.505,3405,1.396,3406,5.431,3407,2.745,3408,1.505,3409,1.505,3410,1.505,3411,1.505,3412,1.505,3413,1.505,3414,1.505,3415,1.505,3416,1.505,3417,1.505,3418,1.505,3419,1.505,3420,1.505,3421,1.505,3422,1.505,3423,1.505,3424,1.505,3425,1.505,3426,1.505,3427,1.505,3428,1.505,3429,1.505,3430,1.505,3431,1.396,3432,1.505,3433,1.505,3434,2.745,3435,1.272,3436,1.505,3437,1.505,3438,1.505,3439,1.505,3440,1.505,3441,2.123,3442,1.505,3443,1.505,3444,1.505,3445,1.505,3446,1.505,3447,1.505,3448,1.505,3449,2.745,3450,2.745,3451,1.505,3452,1.505,3453,1.229,3454,1.505,3455,1.505]],["t/483",[7,0.574,15,1.332,16,1.086,17,2.306,19,1.628,21,1.032,32,5.689,34,0.93,56,1.147,58,2.641,84,0.789,92,2.698,93,1.547,95,2.266,96,1.287,100,1.633,103,1.222,105,2.279,107,2.483,108,1.886,109,0.616,153,1.868,157,3.369,160,2.917,162,1.284,165,1.632,170,1.079,175,3.191,179,1.222,182,2.438,183,1.568,184,2.761,191,2.641,195,3.216,225,1.306,228,1.157,246,1.381,247,1.605,253,2.198,275,1.131,277,1.877,279,1.578,292,1.589,312,7.253,315,1.735,320,2.095,323,2.353,351,1.322,359,1.611,381,1.728,394,1.826,405,1.162,426,1.349,437,1.714,438,1.896,440,1.611,446,4.635,476,1.06,482,2.609,483,6.116,502,1.007,504,2.214,517,2.214,531,3.537,536,1.088,568,1.046,576,3.437,578,2.293,583,2.231,607,2.236,623,1.749,690,2.266,693,2.248,755,1.645,778,3.68,801,2.718,802,1.478,811,2.266,813,4.718,820,1.877,829,1.924,876,2.304,982,1.842,984,1.563,989,2.057,1022,2.009,1159,2.913,1174,2.46,1188,2.323,1208,2.974,1285,1.558,1322,2.678,1405,2.365,1531,2.435,1625,2.761,1759,2.514,1779,2.46,2268,3.326,2462,6.636,2548,3.044,2898,2.46,3143,2.857,3234,3.327,3237,5.315,3238,3.327,3242,8.285,3247,3.466,3264,3.466,3274,3.327,3300,6.414,3361,3.653,3431,3.653,3456,3.936,3457,3.936,3458,3.936,3459,3.936,3460,3.936,3461,6.288,3462,6.288,3463,3.936,3464,3.327,3465,3.936,3466,2.486,3467,3.936,3468,2.913,3469,3.936,3470,3.936,3471,3.936,3472,3.936,3473,3.936,3474,3.936,3475,3.936,3476,3.936,3477,3.936,3478,3.936,3479,3.936,3480,3.936,3481,3.936,3482,3.936,3483,3.936,3484,3.936,3485,3.936,3486,3.936,3487,3.936,3488,3.936]],["t/485",[5,1.283,7,0.573,9,2.358,10,1.97,16,0.678,19,2.594,21,1.029,23,1.216,26,1.352,29,1.27,31,0.89,32,6.057,46,1.509,50,5.666,56,0.575,84,0.63,92,4.223,93,1.543,101,1.938,105,1.139,106,2.665,126,2.81,127,1.194,130,2.076,131,2.641,133,2.49,138,2.347,153,1.863,156,2.479,157,2.102,160,1.276,165,0.816,175,3.183,183,1.563,185,2.506,186,1.716,191,4.209,200,1.804,207,1.486,221,2.15,225,2.599,226,1.766,227,2.403,243,1.991,247,1.6,275,1.127,280,2.316,293,2.904,294,1.959,296,1.403,306,1.617,312,2.904,315,1.73,318,0.887,320,3.339,323,1.469,339,4.189,351,1.318,353,1.587,359,2.566,360,2.599,367,1.854,375,2.116,376,3.318,379,3.034,381,1.723,398,1.283,407,1.646,416,3.348,422,4.949,430,1.796,437,1.709,476,1.057,477,3.452,501,3.317,502,2.005,504,2.207,505,1.509,506,2.16,511,1.856,527,0.796,538,3.034,555,1.723,557,1.812,568,1.667,571,2.709,591,3.318,611,3.023,623,1.744,628,1.881,637,2.337,653,1.146,670,2.116,677,1.918,683,1.362,703,2.904,710,2.848,716,5.124,723,1.302,732,2.38,770,2.798,796,4.354,799,2.752,802,1.473,805,2.479,811,2.259,820,1.871,821,2.535,825,2.798,856,2.297,920,2.904,981,2.452,1072,1.683,1088,3.456,1155,2.904,1160,4.74,1174,5.594,1202,2.241,1219,2.594,1257,2.848,1288,3.477,1437,2.752,1458,5.331,1583,2.904,1729,2.904,1789,2.965,2113,2.506,2351,5.821,2421,3.477,2450,2.427,2491,2.904,2499,3.113,2879,3.034,3027,2.709,3077,3.206,3157,3.456,3185,3.641,3186,3.641,3296,3.113,3300,5.124,3370,3.641,3489,3.924,3490,3.206,3491,3.924,3492,3.924,3493,3.034,3494,3.924]],["t/487",[1781,3.73]],["t/489",[7,0.83,16,0.936,21,1.422,24,1.596,26,2.773,27,1.194,31,2.176,34,1.281,47,3.173,56,0.738,90,2.371,116,1.877,123,3.036,124,3.173,132,1.478,145,2.023,162,2.625,165,1.673,174,1.495,189,2.692,201,2.561,215,3.743,226,4.319,228,1.594,236,1.323,240,2.751,244,3.096,245,3.005,247,2.211,256,1.882,273,4.012,275,1.557,276,4.884,278,1.803,280,3.2,283,2.576,315,2.39,316,2.242,353,1.372,359,2.218,377,1.671,390,3.606,393,2.471,409,2.549,418,2.515,430,2.481,444,2.181,458,4.206,463,5.36,467,3.935,484,2.131,493,4.774,565,2.909,568,2.551,599,2.586,604,4.93,607,1.928,611,2.091,622,2.538,628,2.598,683,2.795,718,4.301,767,3.866,802,2.035,814,2.166,821,3.503,833,4.258,848,4.582,850,3.353,851,4.192,852,4.582,855,3.096,856,4.711,859,2.611,860,4.301,1022,2.767,1119,3.935,1144,2.736,1163,3.388,1258,4.301,1289,3.2,1418,4.429,1791,4.301,2089,3.353,2431,3.59,2561,4.301,3329,4.097,3495,4.774,3496,4.774,3497,4.774,3498,4.774,3499,7.47]],["t/491",[7,0.925,15,2.721,16,1.389,24,1.786,27,2.339,28,1.39,40,3.932,84,0.808,105,2.334,107,2.523,109,1.86,127,2.447,140,2.924,145,3.001,226,3.618,243,4.081,272,3.055,276,3.697,318,1.817,394,3.73,472,3.634,527,2.154,550,3.872,565,3.217,568,3.159,597,3.854,626,6.797,628,3.854,798,4.104,855,4.593,944,3.019,1036,5.64,1289,4.747,1390,5.136,2035,4.974,2099,4.49,3500,8.042]],["t/493",[34,2.736,975,6.894,1477,8.951,1478,9.457]],["t/495",[1781,3.73]],["t/497",[7,0.624,16,1.473,18,4.5,24,1.838,28,1.117,34,2.016,56,1.011,84,0.857,98,2.782,103,2.649,140,3.102,160,3.59,162,2.782,164,2.175,165,1.773,203,1.895,225,2.829,248,3.715,254,2.545,261,4.011,285,4.231,338,1.947,353,2.159,377,1.77,512,2.729,536,2.357,759,3.979,760,2.339,857,3.591,934,5.051,935,7.21,1394,5.035,3501,7.512,3502,7.512]],["t/499",[16,1.981,24,1.724,116,3.972,807,4.113,2316,9.101]],["t/501",[7,0.763,16,2.16,24,1.88,28,1.638,165,2.168,289,3.825,830,4.423,832,6.518,977,3.529,1181,5.663,1303,6.211,2403,5.385,2422,6.211]],["t/503",[24,1.567,28,1.366,203,2.317,323,3.904,338,2.381,377,2.165,550,3.803,807,3.74,917,5.483,1955,8.065,3503,9.185,3504,8.521,3505,9.185,3506,9.185,3507,8.521,3508,9.185]],["t/505",[2,1.958,7,0.687,12,1.954,16,1.621,20,3.03,27,2.068,28,1.535,49,1.089,56,0.86,96,2.558,107,2.786,109,1.835,127,2.381,138,1.954,153,3.714,164,1.331,165,1.085,170,2.145,173,5.823,174,2.158,193,3.77,197,2.158,217,2.761,224,2.052,236,1.909,256,1.812,257,2.466,261,2.455,272,1.983,289,2.87,313,1.774,318,1.768,322,1.515,323,3.513,353,1.321,394,2.421,397,2.014,405,1.541,407,3.282,418,2.421,426,1.789,432,4.58,433,2.797,437,2.274,484,2.052,485,3.661,491,1.46,507,2.634,550,4.073,565,2.371,568,2.494,632,5.205,635,1.757,647,1.817,743,4.792,807,1.872,812,4.84,829,2.552,916,1.846,977,1.766,1007,3.137,1009,3.297,1062,2.329,1075,3.806,1094,2.421,1102,2.894,1265,2.539,1285,2.065,1303,3.109,1309,2.815,1467,3.03,1913,2.727,1991,3.229,2569,3.863,3030,3.944,3509,4.597,3510,4.142,3511,4.142]],["t/507",[2,2.149,16,2.119,18,3.502,28,1.451,56,0.787,96,2.807,97,3.564,105,2.493,107,2.633,132,3.345,145,3.205,170,3.364,193,3.741,313,1.947,318,1.94,338,1.96,405,2.535,507,4.334,550,4.728,596,3.589,989,4.487,995,4.434,1007,5.161,1061,7.259,1062,3.832,1102,4.761,1271,3.966,1467,4.985,3510,6.814,3511,6.814]],["t/509",[16,1.983,23,2.813,24,1.365,25,2.698,28,1.504,49,1.894,56,1.052,109,1.421,132,2.475,228,2.67,297,4.609,313,2.059,398,2.969,476,2.446,491,2.54,502,2.939,536,2.51,545,3.895,577,5.812,580,2.926,916,3.213,934,4.157,1040,5.147,3512,9.383,3513,7.419,3514,7.419]],["t/511",[0,3.822,12,2.67,28,0.934,49,1.488,56,1.103,107,1.695,109,1.75,112,5.801,172,4.056,193,4.259,217,3.774,391,2.787,502,1.826,525,3.12,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,934,5.511,944,4.199,1039,7.374,1040,6.824,1075,3.47,1077,3.705,1083,3.21,1175,4.779,1332,4.413,1337,6.059,3515,5.829,3516,9.138,3517,5.829,3518,5.829,3519,5.829]],["t/513",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,934,4.799,945,2.423,1039,7.685,1040,5.943,1099,4.614,1100,3.856,1112,5.771,3520,6.447,3521,6.447]],["t/515",[1781,3.73]],["t/517",[7,0.969,9,4.959,15,2.792,24,1.623,35,5.332,39,3.116,46,4.153,49,2.252,85,2.653,138,3.088,171,2.08,207,3.125,233,2.771,248,3.594,272,3.135,275,2.37,292,3.331,365,3.88,461,4.286,691,5.512,802,3.097,810,4.45,1070,4.365,1079,4.789,1089,4.677,1132,3.4,1145,3.898,1175,3.526,1400,3.974,1401,4.511,1733,4.574,2010,4.124,2545,5.054,3329,6.236,3522,6.381,3523,7.658]],["t/519",[7,0.622,48,2.202,49,1.775,56,1.268,85,1.88,98,1.907,105,2.47,113,1.979,138,2.189,171,2.778,233,1.964,254,1.745,256,2.03,278,1.944,283,1.569,304,1.144,313,1.326,322,3.546,377,1.766,401,3.558,482,2.427,524,3.34,565,1.772,653,1.708,691,2.985,736,3.97,810,4.134,828,3.293,888,2.859,1145,4.02,1638,2.802,2010,3.558,2013,3.101,2130,4.419,2388,2.844,2611,7.497,2612,5.149,3524,10.031,3525,12.217,3526,11.017,3527,11.017,3528,5.847,3529,10.031,3530,11.017,3531,5.498,3532,5.847,3533,5.847,3534,5.847,3535,5.847,3536,5.847,3537,5.847,3538,5.847]],["t/521",[1781,3.73]],["t/523",[2,1.317,4,1.515,7,1.002,15,3.192,34,1.244,49,2.338,58,6.33,68,1.852,85,1.692,97,2.184,103,2.929,113,2.664,129,2.918,160,1.712,176,5.95,178,3.821,186,3.443,228,1.547,231,3.532,244,3.006,278,1.75,296,1.882,304,1.029,313,1.193,326,3.325,334,4.959,356,4.449,381,2.311,390,2.358,392,3.03,396,3.821,398,1.72,401,4.325,416,2.249,481,2.918,484,2.069,564,3.163,575,4.158,580,1.696,607,1.872,614,0.899,636,2.75,683,1.827,684,4.565,723,1.746,736,2.083,944,1.976,1090,3.244,1100,2.896,1172,2.939,1220,2.802,1409,3.223,1411,3.163,1538,3.753,1565,5.087,1625,5.522,1923,2.6,2013,1.627,2086,3.581,2142,2.43,2450,3.256,2510,4.07,2586,3.977,2621,3.532,2637,3.977,2729,3.634,2898,3.289,3070,6.025,3304,4.449,3539,4.176,3540,4.635,3541,4.635,3542,6.934,3543,4.635,3544,4.635,3545,4.635,3546,4.635,3547,4.635,3548,4.635,3549,4.635,3550,4.635,3551,4.635,3552,4.635,3553,3.486,3554,3.486,3555,3.895,3556,4.822,3557,4.635,3558,4.635]],["t/525",[4,2.065,7,0.719,15,3.796,16,1.239,21,1.881,24,1.478,34,1.696,98,2.34,145,2.677,158,3.328,159,6.082,181,3.772,189,3.563,196,2.639,203,1.594,247,2.926,261,3.374,276,3.298,279,2.877,338,1.638,343,2.368,365,3.374,377,1.489,393,3.27,401,3.488,402,5.208,403,4.273,408,3.294,412,4.954,513,3.137,541,4.098,580,2.312,611,2.768,669,5.422,723,2.38,742,4.164,764,4.484,1072,3.077,1352,4.484,1364,3.328,1376,4.484,1454,5.548,1461,5.422,1511,4.352,1802,7.346,1923,3.544,1994,4.636,2013,2.218,2385,5.548,2778,5.208,3559,7.276,3560,6.658,3561,5.693,3562,5.693,3563,5.548,3564,6.318,3565,4.484,3566,7.175]],["t/527",[7,0.901,15,2.926,49,1.803,56,0.792,159,5.349,322,2.51,369,4.978,401,3.067,412,5.971,438,4.164,607,3.959,723,2.868,1175,3.695,1400,4.164,1601,4.793,1704,8.608,1802,6.262,1923,4.271,2013,3.441,2085,6.534,2141,4.104,2142,3.993,2500,7.064,2722,4.939,2723,5.802,2724,5.802,2726,7.064,2778,6.277,3562,6.861,3567,7.615,3568,7.615,3569,7.615,3570,8.646,3571,7.615,3572,7.615]],["t/529",[7,0.92,12,3.936,13,9.125,34,2.485,48,3.959,465,6.795,715,5.016,885,8.68,1243,7.499,1290,5.749,1291,7.499,2096,8.132]],["t/531",[7,0.7,16,1.192,31,1.564,34,1.631,48,2.598,58,4.631,59,9.29,112,3.172,116,2.39,133,2.739,160,2.245,176,10.351,178,5.009,275,1.982,283,2.569,300,6.183,324,3.391,334,7.291,351,2.317,353,1.746,372,2.396,465,4.459,483,4.039,532,3.34,580,2.224,590,4.779,604,4.226,684,3.34,720,5.833,880,4.226,1090,2.843,1729,5.106,1730,5.215,2113,4.408,2956,5.833,3063,4.631,3064,4.84,3573,6.077,3574,6.404,3575,6.077,3576,4.84,3577,6.077,3578,9.679,3579,6.404,3580,6.404,3581,6.077,3582,6.077,3583,6.077,3584,6.077,3585,6.077,3586,8.43]],["t/533",[7,0.938,491,3.043,532,6.207,683,3.777,1625,7.63,3587,11.294,3588,9.58,3589,9.58]],["t/535",[164,2.822,225,3.672,226,4.98,532,5.357,2114,9.043,3590,8.783,3591,9.748,3592,9.748,3593,9.748]],["t/537",[1781,3.73]],["t/539",[28,1.412,89,3.735,172,3.467,275,3.098,303,7.829,318,2.437,608,5.194,802,4.049,873,6.889,874,9.117,1076,3.604,2444,6.672]],["t/541",[28,1.581,49,1.732,84,0.835,132,2.264,171,2.094,172,3.882,193,3.661,203,1.845,236,2.646,254,2.478,304,1.624,318,2.45,336,3.136,338,1.896,391,3.245,401,2.946,404,3.753,408,4.045,426,3.717,471,3.194,502,2.775,552,2.728,568,2.207,614,1.419,937,4.604,1070,4.394,1073,5.501,1089,4.708,1094,3.853,1288,4.604,1377,5.735,2268,4.394,2990,4.708,3594,7.02,3595,2.839,3596,7.314]],["t/543",[27,2.417,106,3.732,304,2.146,404,4.958,408,3.676,471,4.22,614,1.875,1073,7.267,1093,5.769,3597,9.275]],["t/545",[7,0.897,56,0.925,84,1.233,164,2.574,183,4.021,303,7.329,313,2.289,471,3.883,552,3.316,608,4.863,614,1.725,725,4.77,1075,4.911,1076,3.374,1262,5.183,1399,5.638,3598,10.096,3599,9.37]],["t/548",[7,0.745,31,2.307,49,2.571,84,1.023,100,4.224,236,2.483,376,5.384,377,2.112,732,6.174,751,5.453,802,4.627,1262,5.225,1307,6.576,1689,4.951,3600,9.314,3601,5.683]],["t/550",[7,0.705,34,2.277,49,2.01,236,2.351,313,2.184,376,5.097,471,3.705,608,4.64,614,2.31,659,5.79,732,5.844,802,3.617,1093,5.065,1178,5.162,1262,4.946,1306,7.13,1307,6.226,1475,4.81,1689,4.687,3602,8.942,3603,8.942,3604,5.639,3605,9.635]],["t/552",[7,0.716,49,2.041,56,0.897,84,0.983,106,3.328,144,4.348,316,4.046,339,4.58,393,4.459,471,3.763,491,2.737,580,3.153,683,3.397,723,3.991,742,5.679,1094,6.044,1262,5.023,1369,6.977,1511,7.298,2113,6.249]],["t/554",[7,0.664,24,1.726,28,1.189,56,1.251,84,0.913,165,1.888,174,2.505,203,2.017,228,2.67,232,3.065,246,3.187,313,2.059,338,2.622,372,3.153,536,2.51,843,5.829,937,5.034,1019,3.097,1262,4.662,2000,6.666,2013,3.55,2887,4.93,2894,7.023,3606,9.081,3607,6.179]],["t/556",[2,2.135,7,0.807,29,2.762,84,1.109,88,4.612,164,2.175,182,2.649,183,3.397,300,6.88,306,3.515,313,1.934,318,2.493,418,3.957,447,3.745,471,3.28,495,3.822,511,2.524,567,4.812,577,3.745,672,6.032,846,2.43,916,3.017,1219,3.527,1262,4.379,1297,3.838,1306,8.166,1309,4.6,1411,5.126,1462,4.282,2408,7.512,3608,8.53,3609,7.916]],["t/558",[2,2.149,24,1.291,26,2.959,34,2.03,90,3.755,106,2.921,109,2.151,115,3.67,144,3.816,160,2.794,179,2.667,213,3.243,216,4.057,318,2.773,416,3.67,418,3.983,502,2.836,572,4.22,578,3.132,705,4.905,736,3.398,802,3.224,840,3.615,916,3.92,984,3.409,1132,3.539,1138,5.07,1224,2.928,1262,4.408,1298,7.259,2577,4.199,3008,5.549,3610,7.259]],["t/560",[2,2.137,7,0.808,24,0.883,34,1.388,49,1.781,56,1.075,68,2.067,84,1.269,108,2.815,123,1.858,125,3.108,140,2.136,160,1.911,171,1.481,201,2.775,203,1.305,219,3.172,279,2.355,306,3.518,313,1.936,322,1.705,365,2.762,372,3.492,437,2.559,449,3.355,471,2.259,476,1.582,479,3.796,482,2.438,491,1.643,512,1.88,527,1.732,536,3.382,554,4.965,575,4.434,605,1.973,608,2.829,614,1.004,757,1.343,762,3.671,802,2.205,818,2.737,843,2.981,865,3.671,945,1.804,988,2.666,1038,3.711,1051,2.643,1076,1.963,1212,4.661,1262,3.016,1264,3.563,1333,4.264,1375,3.305,1399,3.28,1402,4.364,1413,3.051,1616,3.033,1700,4.057,2008,4.543,2009,3.634,2013,1.816,2017,3.335,2031,4.995,2190,3.051,2259,3.634,2268,4.517,2990,3.33,3611,4.965,3612,10.058,3613,5.874,3614,4.965]],["t/562",[2,2.34,7,0.684,21,2.452,31,2.12,139,5.317,162,3.049,170,3.668,171,2.357,203,2.077,213,3.531,313,2.12,323,3.499,578,3.41,608,4.503,759,3.805,760,2.564,769,3.335,833,4.946,1083,4.207,1262,4.8,2311,5.844,3615,9.35,3616,6.682]],["t/564",[2,2.164,49,2.568,56,0.792,92,4.23,93,3.399,94,4.34,106,2.941,160,2.813,165,1.797,186,3.781,228,2.542,279,3.467,334,5.852,339,4.047,351,2.904,353,2.188,359,3.538,418,4.011,484,3.399,557,3.993,613,3.627,614,1.902,659,5.196,709,5.245,1175,3.695,1262,4.439,1309,4.663,1312,5.523,1399,4.828,1998,4.978,2390,6.277,2990,4.901,3617,8.646,3618,8.646,3619,8.646]],["t/566",[4,2.633,7,0.844,56,0.838,84,1.16,92,4.343,164,2.332,172,2.94,177,4.099,213,3.454,256,3.176,275,2.628,304,1.789,322,2.655,372,4.006,476,3.108,531,5.145,552,3.005,707,5.957,845,5.4,865,5.717,1090,3.769,1262,4.695,2268,4.839,3611,7.732,3620,6.912,3621,7.732]],["t/568",[7,0.763,49,2.175,84,1.048,171,2.63,203,2.317,232,3.52,304,2.04,313,2.365,322,3.027,527,2.116,545,5.365,554,8.816,572,5.125,1262,5.354,2887,5.663]],["t/570",[4,1.383,49,1.532,105,1.394,203,1.067,236,1.172,324,2.36,372,1.668,471,1.847,608,5.187,614,0.821,757,1.098,802,1.803,843,2.438,985,2.942,988,2.18,1093,2.525,1306,10.05,1307,9.366,1689,7.033,2000,2.788,2017,1.876,2142,2.218,2335,7.009,2722,2.744,2908,3.63,3599,4.458,3600,3.63,3602,4.458,3603,4.458,3604,4.3,3622,4.803,3623,4.06,3624,4.803,3625,4.803,3626,4.803,3627,7.347,3628,4.803,3629,8.28,3630,4.803,3631,8.922,3632,4.803,3633,4.803,3634,4.803,3635,8.28,3636,4.803,3637,4.803,3638,4.803,3639,4.803,3640,4.06,3641,4.06,3642,3.924,3643,4.06,3644,4.803,3645,3.714,3646,3.714,3647,3.714,3648,3.714,3649,4.803,3650,4.803,3651,4.803,3652,4.803,3653,4.803,3654,4.458,3655,4.803,3656,4.803,3657,4.803,3658,4.803,3659,4.803,3660,4.06,3661,4.803,3662,4.06,3663,4.803,3664,4.06]],["t/572",[7,0.781,9,3.932,28,0.857,41,2.755,49,2.419,56,0.6,84,1.074,92,2.248,105,3.1,132,1.783,163,2.407,164,2.351,165,1.36,213,3.483,296,2.34,313,1.483,359,3.774,364,4.279,377,1.358,440,2.678,476,1.763,512,3.418,522,4.518,611,2.524,631,3.969,641,5.06,658,2.908,709,3.969,723,3.059,736,2.589,795,3.969,802,2.456,816,3.44,845,3.863,865,4.089,876,3.83,888,3.199,891,5.346,977,3.121,985,4.008,1094,4.278,1105,4.75,1209,5.192,1262,4.735,1271,3.022,1288,5.113,1302,5.188,1307,7.494,1313,3.529,1511,5.595,1632,3.151,1633,2.861,1735,4.228,1799,4.518,1913,3.419,2000,3.798,2133,5.192,2388,3.183,2510,5.06,2908,4.945,3553,7.074,3554,4.333]],["t/574",[1781,3.73]],["t/576",[1,3.082,4,1.645,7,0.724,16,0.987,24,0.859,28,1.096,29,1.85,31,2.471,36,2.339,48,2.152,50,3.103,89,1.979,109,1.31,115,3.576,123,2.647,130,3.023,131,3.523,132,1.558,138,2.139,169,2.901,172,1.837,174,3.006,207,2.165,224,2.247,227,3.5,228,1.68,233,3.325,236,1.395,254,1.705,262,3.467,287,5.542,299,3.404,315,2.519,318,2.237,339,3.917,370,4.149,372,1.984,381,2.509,416,2.442,527,2.605,560,2.823,578,2.084,599,2.726,623,2.54,658,2.54,675,3.374,759,1.859,760,2.715,802,2.145,822,3.168,910,2.986,945,3.04,1090,2.355,1099,1.964,1112,3.146,1115,4.319,1117,3.535,1132,2.355,1202,3.264,1208,4.319,1219,2.363,1231,4.42,1314,5.286,1407,3.785,1423,3.835,1426,4.076,1475,2.853,1476,4.42,1629,3.693,2099,3.191,2257,3.693,2352,3.785,2361,4.149,2382,4.008,2430,3.264,2594,5.286,3066,3.889,3665,5.033,3666,4.535,3667,4.669,3668,4.149,3669,4.535,3670,4.535,3671,4.076]],["t/578",[7,0.921,15,2.401,16,1.225,24,1.066,36,2.903,39,2.679,46,2.728,84,0.713,85,2.28,86,4.481,96,2.319,108,3.4,131,2.987,145,2.647,162,2.313,174,1.957,228,2.085,233,4.37,240,3.6,247,3.979,278,2.359,283,1.904,313,1.608,318,1.603,338,1.619,377,1.472,393,3.233,398,2.319,408,3.269,504,3.991,505,3.752,576,3.878,578,2.587,590,3.542,683,2.463,757,1.622,833,3.753,856,4.152,888,3.468,908,3.352,939,3.753,977,2.401,1090,2.923,1175,4.765,1310,7.293,1314,4.481,1364,3.291,1402,4.839,1538,5.059,1829,5.15,2014,5.486,2864,5.15,3672,7.742,3673,6.247]],["t/580",[4,2.672,7,0.852,15,3.141,135,6.16,144,4.124,162,3.027,174,2.56,233,3.911,292,3.746,343,3.064,372,3.223,398,3.806,408,3.109,482,3.851,491,2.596,750,6.07,892,3.223,993,3.484,1219,3.838,1310,7.615,1367,5.432,1469,8.174,1527,5.684,2437,8.174,3674,8.174,3675,8.613]],["t/582",[1781,3.73]],["t/584",[56,0.972,220,6.057,246,4.433,247,5.152,278,4.201,353,2.684,377,2.201,794,6.852,1246,6.26,2026,6.852,2086,7.216]],["t/586",[1781,3.73]],["t/588",[7,0.716,15,3.311,34,2.312,84,0.983,196,3.599,203,2.173,233,4.041,295,3.651,381,4.296,398,3.198,416,4.181,575,4.313,580,3.153,613,4.104,684,6.306,794,6.322,1310,6.399,1411,5.88,1538,6.977,2086,6.658,3539,7.763]],["t/590",[7,0.884,15,3.337,56,0.904,84,0.991,96,3.223,159,6.099,233,4.06,412,6.809,1090,4.063,1314,6.229,1352,6.163,1802,5.547,1923,4.871,1994,6.371,2013,3.048,2778,7.158,3559,8.946,3560,9.151,3561,7.824,3676,9.86]],["t/592",[84,1.204,159,8.032,233,4.022,292,3.919,351,3.261,621,4.631,802,3.645,885,6.704,1090,4.001,1180,6.273,1290,5.307,1291,6.923,1410,6.809,1802,5.461,3559,7.185,3565,7.484,3677,8.55,3678,8.55,3679,7.508,3680,9.709]],["t/594",[7,0.776,300,5.965,580,3.417,684,5.133,3064,7.438,3681,8.414,3682,8.414,3683,8.414,3684,9.842,3685,12.633,3686,10.605,3687,9.339,3688,10.605]],["t/596",[7,0.757,233,4.18,300,5.819,532,5.007,580,3.333,684,5.007,3063,6.942,3681,8.208,3682,8.208,3683,8.208,3684,9.6,3689,12.446,3690,10.344,3691,9.11,3692,10.344]],["t/598",[7,0.731,16,1.268,34,1.735,49,2.083,56,0.915,85,3.649,96,2.4,158,3.405,174,2.755,198,3.317,203,2.219,233,4.092,254,2.191,292,2.963,296,2.625,332,3.536,398,3.265,408,2.459,491,2.794,513,3.21,525,3.21,532,3.553,614,1.254,653,2.144,769,2.618,857,3.091,888,3.589,1076,2.453,1090,4.116,1310,8.798,1398,4.689,1402,4.334,1632,4.81,1754,2.896,1913,3.836,2013,2.269,2387,5.329,2388,3.571,3672,7.925,3693,6.465,3694,6.465,3695,6.465,3696,6.205,3697,6.465,3698,5.998]],["t/600",[6,5.672,7,0.73,21,1.519,26,3.438,28,1.436,31,1.314,41,2.439,48,2.181,56,0.775,84,1.003,92,1.991,93,3.323,107,2.371,109,1.562,123,1.832,125,3.065,138,2.168,153,2.75,165,2.074,171,2.131,172,3.208,173,2.877,174,2.331,179,3.406,190,4.031,207,2.194,224,2.278,233,1.946,236,2.062,289,3.1,304,1.133,318,1.309,322,1.682,339,2.712,351,1.946,491,1.621,502,1.483,505,3.838,578,2.113,583,3.284,599,2.763,614,1.874,620,2.974,658,2.575,702,3.621,724,2.618,725,3.993,757,1.933,812,3.584,888,2.833,940,4.378,977,2.86,1035,3.212,1086,4.897,1099,3.769,1112,4.653,1179,3.584,1219,2.396,1302,3.259,1368,3.45,1737,2.028,2013,1.791,2190,3.009,2369,4.897,2388,2.818,2431,3.837,3563,6.536,3595,1.981,3669,4.597,3699,7.444,3700,5.102]],["t/602",[1781,3.73]],["t/604",[2,1.004,7,0.905,16,1.102,24,1.583,28,0.525,34,2.142,48,1.51,49,1.89,56,1.083,84,0.799,85,2.913,96,1.311,97,1.664,105,1.164,113,1.357,123,1.268,140,1.459,160,2.076,164,1.023,171,2.285,174,1.106,203,1.766,213,1.515,219,1.152,223,3.335,232,1.354,233,2.144,246,1.407,254,2.952,275,1.152,295,2.382,296,1.434,304,1.772,313,0.909,322,2.308,337,1.734,338,1.457,343,1.324,372,2.216,377,0.832,381,1.761,398,1.311,401,1.423,430,1.836,449,2.291,461,4.129,476,1.08,482,1.664,491,1.122,505,1.543,508,2.193,525,1.754,527,0.814,528,1.961,567,1.492,580,2.057,614,1.8,622,1.877,628,1.922,707,3.296,715,3.044,810,1.653,830,1.701,843,2.036,846,2.265,888,1.961,897,2.348,901,1.828,970,3.183,1076,2.133,1094,1.86,1117,6.836,1172,3.564,1210,2.912,1266,1.614,1271,2.947,1297,1.805,1353,2.457,1539,3.948,1700,2.77,1735,2.592,1737,2.234,1799,2.77,1991,2.481,2013,1.24,2017,1.567,2031,1.992,2343,4.936,2350,2.729,2362,6.656,2388,1.951,2403,2.071,2430,2.291,2656,2.41,2952,2.912,3059,2.24,3595,2.718,3701,5.065,3702,5.065,3703,3.183,3704,3.532,3705,3.532,3706,3.532,3707,5.065,3708,5.065,3709,3.532,3710,5.065,3711,5.065,3712,5.065,3713,3.532,3714,3.532,3715,3.532,3716,3.532,3717,5.924,3718,3.532,3719,3.183]],["t/606",[7,0.505,21,1.809,24,1.037,28,1.253,31,2.492,56,1.182,76,4.765,109,1.498,232,3.231,461,6.165,527,1.942,572,4.704,590,3.445,607,4.222,846,2.727,966,3.414,1090,3.944,3720,6.077,3721,6.077,3722,8.883,3723,11.33,3724,9.572,3725,9.572,3726,9.572,3727,9.572,3728,6.714,3729,6.077,3730,5.475,3731,4.84,3732,4.84,3733,4.84,3734,4.84,3735,6.077,3736,6.077,3737,4.84,3738,5.215]],["t/608",[7,0.528,21,1.892,28,1.293,31,2.551,56,1.199,76,4.983,109,1.545,381,3.168,405,2.914,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,807,3.54,916,3.492,966,3.45,1177,6.816,1689,4.802,3722,9.161,3728,6.923,3730,5.725,3731,5.061,3732,5.061,3733,5.061,3734,5.061,3737,5.061,3738,5.453,3739,6.354,3740,6.354,3741,8.012,3742,7.039,3743,6.354,3744,6.354,3745,5.58]],["t/610",[7,0.341,21,1.222,24,1.079,26,1.606,28,0.61,31,1.628,37,4.442,56,1.35,95,5.664,106,2.442,107,1.108,109,1.124,112,2.143,127,2.665,137,2.776,179,1.447,193,1.573,242,3.049,267,3.699,381,3.152,391,1.821,397,2.77,405,2.585,440,1.908,444,2.889,461,3.729,471,2.761,502,1.193,511,1.379,514,3.269,527,2.156,572,4.835,635,1.569,757,1.066,784,7.23,813,4.315,829,3.51,830,1.977,1063,3.809,1072,1.999,1100,1.715,1177,6.047,1437,6.142,1462,2.34,2031,2.315,2951,2.566,2952,5.212,3014,3.699,3066,3.172,3576,3.269,3746,6.323,3747,4.105,3748,4.105,3749,4.105,3750,4.105,3751,4.105,3752,4.105,3753,7.402,3754,6.323,3755,7.712,3756,4.105,3757,4.105,3758,4.105,3759,4.105,3760,4.105,3761,4.662,3762,4.105,3763,2.752,3764,4.105,3765,4.105,3766,6.323,3767,4.105,3768,4.105,3769,4.105,3770,4.105,3771,3.324,3772,4.105,3773,4.105,3774,4.105,3775,4.105,3776,4.105,3777,4.105,3778,4.105,3779,4.105,3780,4.105,3781,4.105]],["t/612",[7,0.427,15,3.391,16,1.274,21,0.941,24,0.877,28,0.764,56,1.369,97,1.49,107,2.019,171,0.905,186,1.57,203,1.297,228,1.055,237,2.443,322,2.142,393,1.636,527,0.728,605,1.206,683,1.246,715,2.784,784,2.443,822,1.99,843,1.822,846,1.663,890,7.817,1072,1.54,1083,5.745,1099,1.233,1117,6.199,1219,1.484,1352,2.243,1409,2.198,1567,2.56,1577,4.094,1597,2.101,1700,2.479,1802,3.284,1994,2.319,2013,1.11,2045,3.034,2419,2.776,2420,2.848,2577,1.755,3531,2.319,3701,2.848,3702,2.848,3703,2.848,3707,2.848,3708,2.848,3710,2.848,3711,2.848,3712,2.848,3763,3.446,3782,3.161,3783,5.838,3784,3.161,3785,3.161,3786,9.302,3787,9.302,3788,9.302,3789,9.302,3790,9.302,3791,8.63,3792,3.161,3793,9.302,3794,8.381,3795,7.485,3796,3.161,3797,3.161,3798,3.161,3799,6.497,3800,3.161,3801,3.161,3802,3.161]],["t/614",[7,0.695,22,5.47,39,2.578,46,3.654,49,1.424,68,2.402,85,2.194,95,3.93,98,3.098,100,3.942,116,2.364,126,3.059,160,2.221,164,1.74,171,1.721,202,6.068,207,2.585,213,2.578,247,2.784,278,2.27,337,2.95,343,2.253,351,2.292,403,4.065,404,3.084,408,2.287,434,4.222,461,3.545,506,5.229,525,4.155,550,4.689,657,4.955,706,4.521,779,7.03,1098,3.995,1113,4.521,1117,7.683,1175,2.917,1219,2.823,1336,4.141,1733,3.784,1893,8.671,1991,4.222,2350,4.645,2362,6.001,2421,3.784,2430,3.899,2448,3.962,2598,3.899,2951,3.757,3118,7.347,3143,4.955,3803,6.011,3804,4.411,3805,4.714,3806,6.011,3807,6.011,3808,6.011]],["t/616",[1781,3.73]],["t/618",[2,2.138,7,0.705,24,1.046,28,0.587,39,1.692,49,2.176,56,1.286,84,0.45,86,2.83,90,1.959,92,4.378,93,3.787,94,2.249,97,1.859,100,5.079,105,1.3,107,1.064,112,2.06,116,1.551,121,5.23,127,2.931,132,1.221,165,0.931,171,2.784,172,1.44,174,1.921,177,2.007,179,1.391,193,1.512,203,0.995,228,1.317,233,1.505,236,1.093,283,1.202,313,1.579,322,2.796,372,1.556,377,0.93,381,1.967,491,1.253,495,2.007,496,2.539,527,1.733,577,3.057,614,0.765,714,2.895,715,2.137,751,5.161,757,1.024,760,1.228,816,2.355,892,1.556,916,2.463,945,1.376,995,2.313,1037,2.069,1099,1.539,1162,2.313,1297,2.016,1329,1.914,1462,2.249,1737,2.437,1998,4.009,2013,2.152,2276,2.385,2911,2.83,3066,8.669,3398,3.006,3466,4.398,3601,4.769,3809,5.99,3810,2.6,3811,3.195,3812,6.963,3813,8.939,3814,3.464,3815,3.787,3816,4.884,3817,3.555]],["t/620",[1781,3.73]],["t/622",[7,0.624,27,2.431,31,1.934,34,2.016,106,2.901,112,3.921,131,3.591,138,3.192,172,2.742,189,4.236,190,4.068,191,5.724,193,2.879,198,3.854,219,3.17,233,2.865,278,2.836,295,3.183,318,2.493,341,4.354,353,2.159,377,1.77,477,4.695,491,2.386,498,4.404,499,5.512,500,3.745,504,4.798,512,2.729,774,4.282,1174,5.331,1175,3.645,1369,6.083,1375,4.798,1376,5.331,1419,6.192,1428,6.596,3818,5.983,3819,7.512,3820,7.512]],["t/624",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1376,6.465,3818,7.255]],["t/626",[7,0.757,106,3.518,131,4.355,138,3.872,190,4.934,219,2.971,233,3.474,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,512,3.31,536,2.859,1376,6.465,3818,7.255]],["t/628",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1376,6.465,3818,7.255]],["t/630",[7,0.832,139,4.734,233,3.818,341,5.802,759,3.698,1425,6.131]],["t/632",[7,0.782,24,1.607,88,4.47,206,6.427,233,3.592,341,5.458,552,3.513,567,3.979,723,3.547,1266,4.302,1271,4.938,3821,11.181]],["t/634",[7,0.929,29,3.462,219,3.072,233,3.592,315,4.714,318,2.416,395,3.922,1225,7.385,1314,6.756,1688,6.756,2444,6.615,3296,8.486]],["t/636",[34,2.817,42,6.556,96,3.149,127,2.932,155,5.79,253,5.38,283,2.585,318,2.177,353,2.438,377,2,381,4.23,472,4.354,502,2.465,530,3.998,698,6.466,1175,4.117,1220,5.129,1424,6.758,1868,7.645,2091,6.871,2092,6.466,2093,6.086,2094,7.998,3387,6.758]],["t/638",[7,0.751,27,2.26,34,2.425,102,6.005,172,3.298,283,2.753,318,2.318,530,4.258,1107,7.196,1175,4.384,1424,7.196,1557,5.907,2091,7.317,2092,6.885,2093,6.481,2094,8.311,3822,9.036]],["t/640",[7,0.669,29,2.961,31,2.074,39,3.454,46,3.518,50,6.264,84,0.919,98,2.983,105,2.655,123,2.893,131,3.851,207,3.465,233,3.072,248,3.984,254,2.73,278,3.042,313,2.074,318,2.067,447,4.016,572,4.495,578,3.336,580,2.947,814,3.655,1112,5.035,1115,6.912,1132,3.769,1400,4.405,1401,5,2322,6.316,2631,5.548,3670,9.155]],["t/642",[1781,3.73]],["t/644",[2,1.727,5,2.256,7,0.868,49,1.439,56,1.007,138,2.583,165,1.435,171,1.74,203,1.533,207,3.626,246,3.857,254,3.721,256,3.324,275,1.982,276,4.401,288,4.226,304,1.349,313,1.564,315,4.219,337,2.982,338,1.575,343,2.278,416,2.949,476,1.859,491,1.93,513,3.018,691,4.886,707,3.563,715,3.291,735,4.696,760,3.419,769,2.461,774,5.517,810,3.944,830,2.927,977,2.335,1079,4.005,1107,4.84,1110,4.84,1213,4.631,1285,2.731,1367,4.039,2073,5.306,2306,5.215,2594,4.359,2868,3.483,2951,3.798,3823,7.298,3824,5.475,3825,5.475]],["t/646",[7,0.646,31,2.001,84,0.887,92,3.033,93,3.471,116,3.057,139,3.676,171,2.226,174,2.435,207,3.344,233,2.965,254,2.634,272,3.354,276,4.058,283,2.369,287,5.846,304,2.206,318,1.995,343,2.914,527,1.791,653,2.579,734,3.625,759,2.872,760,3.409,810,3.637,838,4.132,945,2.71,1219,3.65,2868,4.455,3668,6.408,3823,7.667,3826,6.827]],["t/648",[4,1.748,5,3.35,7,0.64,21,2.293,26,2.092,46,2.335,49,2.339,56,1.169,85,1.952,90,2.655,92,4.696,94,5.144,105,3.449,116,2.103,133,2.411,171,1.531,174,2.412,203,1.943,236,1.482,254,1.812,275,1.744,278,2.019,283,1.629,290,4.696,337,2.624,353,1.537,359,2.485,365,2.855,405,1.793,476,2.356,496,3.442,498,3.135,505,2.335,513,2.655,531,3.416,578,2.214,601,2.645,605,2.937,614,1.037,769,2.166,774,3.048,824,4.132,845,3.584,976,4.193,1080,3.135,1108,2.882,1162,3.135,1364,2.816,1998,5.035,2594,3.836,2868,3.065,2951,3.342,2990,3.442,3466,5.524,3816,4.259,3823,5.998,3827,6.939,3828,6.133,3829,6.939,3830,4.021,3831,3.649,3832,4.818,3833,4.132]],["t/650",[49,2.01,56,1.328,171,2.429,283,2.585,297,4.89,476,2.595,810,5.571,2611,6.556,2868,4.863,3531,6.226,3823,5.639,3827,7.645,3828,8.359,3829,7.645,3834,6.556]],["t/652",[4,1.56,5,1.772,7,0.589,24,0.815,31,2.176,39,2.047,46,2.085,56,0.973,84,0.965,115,2.316,116,1.877,138,2.029,139,3.352,165,1.127,182,1.683,203,1.788,207,2.053,221,1.858,233,1.821,236,1.323,254,2.402,256,1.882,275,1.557,318,1.819,325,3.503,338,1.238,377,1.671,414,4.285,444,3.238,461,2.816,476,1.46,479,3.503,508,2.963,527,1.1,605,1.821,614,0.926,647,1.887,653,2.351,724,2.45,757,1.24,759,3.456,760,3.467,769,3.424,774,4.04,810,2.234,830,2.299,846,1.544,908,2.561,916,1.918,945,2.471,966,1.372,988,2.46,1037,2.503,1051,3.622,1070,2.868,1100,1.994,1118,2.125,1181,2.943,1224,1.848,1328,2.624,1364,2.515,1375,3.049,1456,2.65,1793,3.173,1932,3.545,1988,2.104,2017,3.144,2141,2.573,2338,2.767,2339,3.005,2340,3.096,2402,3.733,2577,2.65,2594,6.711,2625,2.886,2951,2.984,3616,4.597,3668,5.843,3823,4.711,3835,4.301,3836,4.429,3837,4.774,3838,5.401,3839,7.424,3840,4.012,3841,3.388]],["t/654",[4,2.302,5,1.757,7,0.393,10,2.698,31,2.688,39,2.03,46,2.067,56,0.969,84,0.96,116,1.861,138,2.012,139,2.238,165,1.117,182,1.669,203,1.777,207,2.036,221,1.842,254,1.604,256,1.866,275,1.544,287,6.326,302,2.297,318,1.214,325,3.473,338,1.227,377,1.66,414,4.258,461,2.792,476,1.448,527,2.491,568,1.428,592,3.201,614,0.918,647,1.871,653,2.336,757,1.229,759,3.442,760,3.25,769,3.407,774,4.015,802,2.018,810,2.215,830,2.28,840,3.367,846,1.531,908,2.539,916,1.901,945,1.65,966,1.36,988,2.439,1037,2.482,1051,3.599,1070,2.843,1100,1.977,1118,3.134,1219,3.95,1224,1.833,1328,2.602,1364,2.493,1375,3.024,1456,2.628,1601,2.98,1793,3.146,1913,2.809,1988,2.087,2017,3.124,2141,2.551,2338,2.743,2339,2.98,2340,3.07,2402,3.71,2577,2.628,2594,5.052,2625,2.861,2951,2.959,3616,4.568,3823,4.681,3826,6.185,3840,3.978,3841,4.999,3842,4.734,3843,7.043,3844,4.734]],["t/656",[1781,3.73]],["t/658",[2,2.149,24,1.291,84,0.863,221,2.943,313,1.947,343,2.835,401,3.046,527,1.742,530,3.564,560,4.242,612,5.161,653,2.509,677,4.199,683,2.982,760,3.364,876,5.026,908,4.057,944,3.224,963,4.831,976,5.93,988,3.897,1019,2.928,1144,4.334,1328,5.364,1400,5.91,1566,5.687,1718,5.312,2031,4.265,2128,5.485,2361,8.045,2621,5.763,3771,6.124,3845,7.563,3846,7.563,3847,6.814,3848,7.563]],["t/660",[1781,3.73]],["t/662",[2,1.746,5,2.281,7,0.872,49,1.455,56,1.013,138,2.611,165,1.45,171,1.759,203,1.55,207,3.653,246,3.879,254,3.559,256,3.349,275,2.004,276,4.434,288,4.273,299,7.455,304,1.364,313,1.582,315,4.252,337,3.015,338,1.593,416,2.981,476,1.879,491,1.951,513,3.051,527,2.54,691,5.642,707,3.602,715,3.328,735,4.747,757,1.595,769,2.488,774,5.549,810,2.875,830,2.959,977,2.361,1107,4.893,1110,4.893,1213,4.682,1367,4.083,1733,3.868,2073,5.347,2306,5.272,2868,3.521,2951,3.84,3666,5.536,3824,5.536,3825,5.536]],["t/664",[7,0.694,49,1.979,92,3.261,93,4.642,116,3.287,164,2.419,171,2.393,174,2.618,207,3.595,233,3.187,254,3.523,256,3.295,276,5.909,283,2.547,299,7.654,304,1.856,343,3.132,527,1.925,653,2.772,810,3.911,838,4.442,1806,6.206]],["t/666",[4,1.827,5,3.434,7,0.66,21,2.367,26,2.186,46,2.441,49,2.386,56,1.108,85,2.04,90,2.775,92,4.683,94,5.272,105,3.509,116,2.198,133,2.519,171,1.6,174,2.489,203,2.005,236,1.549,254,1.894,275,1.823,278,2.11,283,1.703,290,4.908,299,6.813,337,2.743,353,1.606,359,2.597,365,2.984,405,1.874,476,2.431,496,3.597,498,3.277,505,2.441,513,2.775,527,1.288,531,3.57,578,2.314,605,3.031,614,1.084,769,2.263,774,3.186,824,4.318,845,3.746,1080,3.277,1108,3.013,1162,3.277,1364,2.944,1998,3.654,2868,3.203,2951,3.493,2990,3.597,3466,4.009,3816,4.451,3828,4.451,3830,4.203,3831,3.814,3832,5.036,3833,4.318,3849,7.162]],["t/668",[49,2.089,56,1.309,171,2.525,283,2.688,297,5.084,299,5.965,476,2.698,810,5.425,2611,6.816,2868,5.055,3531,6.472,3828,7.025,3834,6.816,3849,7.948]],["t/670",[4,1.976,5,2.243,7,0.502,31,2.162,39,2.592,46,2.639,56,1.085,84,0.69,116,2.377,138,2.569,182,2.131,203,1.525,207,2.599,221,2.352,254,2.048,256,2.383,275,1.971,299,7.411,377,1.424,414,5.077,476,1.849,527,2.681,614,1.172,647,2.389,653,2.786,691,3.503,757,1.569,769,3.401,774,4.787,830,2.911,846,1.955,908,3.242,916,2.428,945,2.107,966,1.737,988,3.114,1051,4.291,1070,3.631,1118,2.69,1224,2.34,1328,3.322,1456,3.355,1988,2.664,2017,3.725,2141,3.258,2338,3.503,2339,3.805,2340,3.92,2402,4.423,2577,3.355,2625,3.654,2951,3.778,3616,5.447,3840,5.079,3841,4.289,3850,6.044,3851,5.446]],["t/672",[4,2.439,56,1.182,116,2.934,182,2.631,221,2.904,377,1.758,476,2.282,527,2.712,647,2.95,653,3.209,757,1.937,846,2.414,916,2.997,945,2.601,966,2.144,988,3.845,1051,4.943,1118,3.32,1224,2.889,1328,4.101,1456,4.142,1988,3.289,2017,4.292,2141,4.022,2338,4.325,2339,4.697,2340,4.84,2402,5.096,3851,6.723,3852,7.462,3853,10.737]],["t/674",[1781,3.73]],["t/676",[1781,3.73]],["t/678",[7,0.54,28,1.491,31,2.273,49,1.54,56,0.677,68,2.598,84,0.742,92,3.446,94,3.706,115,4.866,132,2.733,133,2.931,196,2.716,203,1.64,233,2.48,236,1.802,254,2.203,283,1.981,313,1.674,315,3.255,318,1.668,377,2.081,390,3.309,436,4.89,502,3.125,513,3.229,527,1.498,550,2.693,577,4.999,630,2.531,675,4.359,715,3.522,751,3.956,769,2.633,829,3.61,910,5.24,912,5.71,916,3.547,1010,4.568,1015,4.064,1040,4.185,1098,4.322,1988,2.866,2210,5.36,2382,5.179,2402,3.425,2868,3.727,3854,5.464,3855,7.384,3856,6.503,3857,6.503]],["t/680",[2,1.786,4,2.054,7,0.522,16,1.232,56,1.103,84,0.985,116,2.471,182,2.215,221,2.445,232,2.408,313,1.617,318,1.612,372,2.477,377,1.481,476,1.922,491,1.996,502,1.826,527,2.56,536,1.972,565,2.162,568,1.896,605,3.289,630,2.445,647,2.484,653,2.861,683,2.477,757,1.631,814,4.811,846,2.032,910,6.593,916,2.524,957,3.848,966,1.805,988,3.237,1051,4.407,1072,3.06,1118,2.796,1224,2.433,1266,2.87,1328,3.453,1456,3.488,1481,3.543,1988,2.769,2017,3.826,2141,3.386,2338,3.641,2339,3.955,2340,4.075,2402,4.543,3854,7.248,3858,3.436]],["t/682",[0,2.444,4,1.313,7,0.516,12,1.707,16,1.219,24,1.061,31,2.522,39,2.666,41,1.92,56,1.22,84,0.458,98,1.487,113,2.389,115,4.495,132,1.243,165,0.948,179,1.416,182,2.682,196,1.677,203,1.013,219,1.31,221,1.563,254,1.361,282,2.302,318,1.595,372,1.584,414,3.758,471,1.754,476,1.229,477,2.51,491,1.276,502,1.167,527,2.636,553,2.241,565,1.382,567,1.697,568,1.876,577,2.002,580,1.47,610,3.252,614,1.206,647,3.007,653,2.841,658,4.322,670,2.46,724,2.061,757,1.043,814,2.821,829,4.223,830,1.934,846,2.461,910,6.263,916,2.498,945,1.4,957,2.46,966,1.787,984,3.429,988,2.07,1051,3.887,1076,3.877,1118,3.385,1224,2.945,1328,2.207,1456,2.23,1671,3.603,1932,2.983,2017,2.758,2141,2.165,2196,8.134,2338,2.328,2339,2.528,2340,2.605,2402,3.275,2875,3.149,2885,3.619,2949,3.619,3858,3.4,3859,4.233,3860,6.217,3861,4.016,3862,4.016,3863,6.217]],["t/684",[4,2.243,5,2.548,16,1.346,56,1.144,116,2.699,164,1.987,182,2.42,221,2.671,254,2.326,318,1.761,372,2.706,476,2.099,502,1.994,527,2.715,647,2.713,653,3.038,757,1.782,846,2.22,910,6.524,916,2.757,957,4.203,966,1.972,988,3.537,1051,4.679,1118,3.054,1224,2.658,1328,3.772,1408,9.892,1456,3.81,1988,3.025,2017,4.062,2141,3.7,2338,3.978,2339,4.32,2340,4.452,2352,5.162,2402,4.824,3858,3.754,3864,9.158]],["t/686",[2,1.47,7,0.808,24,0.883,28,0.769,31,1.936,49,2.098,56,1.269,90,2.569,92,4.76,93,4.341,94,2.949,97,2.438,98,1.916,100,2.438,115,3.648,132,1.601,165,1.221,171,2.536,174,2.774,177,2.632,203,1.897,283,1.576,304,1.149,313,1.332,322,2.92,398,1.92,496,3.33,527,2.041,553,2.887,614,1.004,714,3.796,715,2.802,769,3.045,814,2.348,881,4.956,892,2.04,910,5.769,995,3.033,1037,2.713,1162,3.033,1264,3.563,1297,2.643,1689,2.858,1998,4.916,2013,3.109,2464,4.264,2868,2.965,3066,3.997,3466,3.711,3794,4.661,3816,5.988,3817,4.661,3865,10.058,3866,7.519,3867,5.173]],["t/688",[1781,3.73]],["t/690",[7,0.902,34,2.724,49,1.577,56,1.315,84,0.76,109,2.076,164,3.144,278,2.514,295,4.301,304,1.479,401,2.682,408,2.533,452,6.508,495,3.388,527,1.534,614,1.292,802,2.838,846,2.903,908,5.445,966,1.913,983,5.392,1035,4.191,1558,3.951,1705,4.285,2010,2.442,2015,4.133,2214,6.123,3671,5.392,3868,5.999,3869,5.392,3870,6.658]],["t/692",[7,0.525,27,2.166,34,1.696,40,3.508,56,1.028,84,0.721,98,2.34,107,1.705,109,2.208,111,4.882,127,2.992,164,1.829,170,1.967,179,3.053,193,2.422,200,3.298,203,1.594,295,2.677,313,1.627,320,3.819,322,2.083,353,2.488,447,3.15,452,4.583,505,2.759,527,1.995,580,2.312,614,2.062,683,2.491,818,3.343,846,2.801,847,5.309,892,2.491,1114,5.862,1132,2.956,1285,2.839,1693,3.895,2010,4.398,2191,4.394,3871,8.901,3872,6.064]],["t/694",[4,2.674,7,0.484,12,2.475,16,1.142,34,1.563,56,0.606,84,1.08,85,2.125,107,1.571,109,2.195,110,3.233,132,1.802,164,1.686,170,3.195,174,1.824,186,2.891,193,2.232,200,3.04,203,1.469,261,3.109,295,2.467,302,4.59,348,3.938,377,1.372,408,2.215,468,3.903,476,1.781,484,2.599,527,2.179,536,1.827,565,3.53,568,1.757,578,3.388,605,2.221,638,3.692,666,4.8,724,4.198,741,3.973,756,2.88,757,1.512,759,2.151,760,1.813,857,2.784,994,2.642,1536,4.499,1558,6.832,1913,3.455,2010,2.136,2142,3.053,2191,4.049,2333,5.246,3493,5.113,3871,5.246,3873,5.589]],["t/696",[7,0.397,24,1.443,26,1.868,27,1.194,28,0.71,56,1.04,84,1.068,107,1.912,109,2.187,110,4.693,113,1.834,162,1.768,164,2.052,165,1.127,170,1.486,174,1.495,186,2.371,189,2.692,203,1.204,216,2.561,236,2.342,257,2.561,272,2.06,278,1.803,285,2.078,296,1.939,338,1.238,372,1.882,377,1.992,408,2.696,440,2.218,452,5.141,502,1.387,527,2.816,536,1.498,565,1.643,568,1.441,583,3.073,605,1.821,607,2.863,613,3.376,653,1.584,663,3.073,724,2.45,734,2.226,759,2.618,760,1.486,837,3.049,846,2.293,938,3.096,945,3.651,957,4.341,993,2.035,1145,2.561,1287,3.689,1475,2.706,1558,2.833,2010,1.751,2015,2.963,2729,3.743,3874,4.774,3875,8.454,3876,4.429,3877,3.638,3878,4.429,3879,4.774,3880,4.429,3881,5.031]],["t/698",[49,2.23,84,1.075,109,2.119,336,4.038,391,4.178,614,2.169,993,4.014,2010,3.454,2268,5.657,2799,7.277]],["t/700",[7,0.731,25,2.181,27,2.684,28,1.308,31,1.664,56,0.673,84,1.225,107,1.744,109,1.149,123,2.322,127,3.879,132,2.722,162,2.394,165,2.076,213,2.772,219,2.109,256,2.549,281,3.223,304,1.436,318,1.659,338,1.676,536,2.029,557,3.39,565,3.026,567,2.732,568,3.017,614,1.706,615,3.986,630,2.516,635,2.472,810,4.116,900,5.998,1009,4.637,1021,4.637,1121,4.13,1144,5.041,1183,4.689,1225,5.069,1236,4.541,1274,5.433,1459,4.453,1737,2.569,1754,3.94,2076,3.959,2966,5.433,3595,2.51]],["t/702",[28,1.706,107,2.859,109,1.883,127,3.965,162,3.191,171,3.033,246,3.433,304,1.913,338,2.233,614,2.056,840,4.119,977,3.311,1008,5.424,1737,3.424,3595,3.345,3882,9.08]],["t/704",[12,2.83,24,1.531,31,1.714,34,1.787,56,0.693,84,1.024,109,1.183,113,2.558,123,2.391,125,4,139,3.149,162,2.466,164,1.928,171,1.906,174,2.085,193,2.552,203,1.68,236,2.486,275,2.172,285,2.899,297,3.837,304,1.479,338,1.726,444,3.042,527,2.797,536,2.09,565,2.291,568,2.009,614,1.969,724,5.208,755,3.16,759,3.315,945,3.539,966,1.913,1284,4.776,1737,2.646,1959,3.346,1988,3.955,2276,6.135,3595,2.585,3883,6.391,3884,6.658]],["t/706",[7,0.596,24,1.224,28,1.402,56,0.746,98,2.656,107,1.935,109,2.065,164,2.077,171,2.054,207,3.085,254,3.569,282,4.111,304,2.094,343,2.689,476,2.194,480,5.145,482,3.38,527,1.653,578,2.97,580,2.625,599,3.885,614,1.392,658,5.315,707,4.206,944,3.058,945,3.288,1099,4.535,1100,2.996,1132,4.412,1737,2.851,3595,2.784,3885,6.463,3886,7.173,3887,6.463]],["t/708",[16,2.098,28,1.301,56,0.911,132,3.311,254,2.965,304,2.376,318,2.745,338,2.269,351,3.338,502,2.543,580,3.914,734,4.081,1099,4.174,1737,3.478,3595,3.397,3888,9.938]],["t/710",[7,0.574,24,1.569,25,2.33,27,2.586,28,1.367,56,0.957,84,0.788,90,3.429,103,2.435,106,3.993,107,2.789,109,2.04,115,3.351,174,2.163,254,2.34,256,2.723,302,3.351,304,1.534,362,5.693,416,3.351,513,3.429,527,1.591,550,3.808,615,4.258,623,3.485,755,3.278,945,3.206,1062,3.499,1101,5.788,1111,6.747,1132,4.303,1364,3.638,1737,2.745,2900,4.901,3595,2.681,3885,6.222,3889,7.842,3890,6.407]],["t/712",[5,2.196,7,0.793,27,2.818,34,1.588,49,1.401,50,3.647,56,0.993,68,2.363,98,2.19,109,1.932,126,4.209,164,2.395,171,2.369,172,4.222,203,1.492,207,4.444,240,3.409,256,3.262,272,2.552,279,2.693,282,3.39,295,2.507,304,1.314,313,1.523,316,2.778,343,3.101,353,1.7,404,4.245,408,2.25,476,1.809,480,4.243,482,2.787,489,6.702,531,3.779,578,2.45,599,4.481,614,1.148,707,3.468,734,2.758,993,3.527,1083,3.022,1364,3.116,1671,3.428,2010,2.17,3604,3.931,3887,5.33,3891,6.635,3892,5.916,3893,4.638]],["t/714",[27,1.656,31,1.704,39,2.838,49,1.568,56,1.177,109,1.925,112,3.455,172,4.256,183,2.994,207,3.844,256,2.61,279,3.013,304,1.985,316,3.108,336,2.838,343,2.481,377,1.56,391,2.936,476,2.025,531,4.228,553,4.987,599,3.585,614,1.284,662,4.053,734,3.086,751,4.027,755,3.141,1377,5.19,1509,5.964,1689,4.937,1806,4.915,2010,2.428,2013,2.323,2041,4.697,2191,4.603,2293,4.081,2325,5.964,2887,4.081,3604,6.725,3891,6.216,3893,5.19,3894,6.14,3895,5.68,3896,6.14]],["t/716",[2,1.718,5,2.243,7,0.866,12,2.569,27,2.414,28,1.63,30,3.983,49,1.431,56,0.629,84,1.102,107,1.631,109,1.492,113,3.227,171,1.73,172,3.523,174,1.893,203,1.525,224,3.749,246,3.346,304,1.342,318,1.551,351,2.305,391,2.681,432,4.017,505,2.639,511,2.03,525,3.001,552,3.6,597,3.289,614,1.629,725,6.242,807,3.93,832,4.289,854,4.67,944,3.58,1075,3.338,1077,3.565,1093,3.608,1098,4.017,1181,3.726,1285,2.716,1302,3.861,1737,2.402,2904,4.606,2928,3.355,2947,5.307,3595,2.346,3604,4.017,3891,4.203,3897,4.982]],["t/718",[0,3.524,4,1.893,5,3.026,7,0.85,24,0.988,27,2.36,28,0.861,31,1.491,34,1.554,49,1.372,56,0.603,84,0.931,109,1.676,113,2.226,123,2.08,140,2.392,162,2.145,164,1.677,171,2.334,172,2.976,173,4.597,174,2.553,184,4.613,193,3.125,196,2.419,203,1.461,228,1.934,255,4.111,261,3.093,275,1.889,289,2.412,304,1.286,320,3.502,323,2.462,353,1.664,377,1.365,426,2.254,432,3.85,484,3.639,505,2.53,550,2.399,568,1.748,596,2.749,614,1.83,632,3.284,725,3.108,728,4.25,755,3.869,802,2.469,828,2.545,854,4.476,858,3.011,908,3.108,944,2.469,977,2.226,1093,6.441,1182,2.911,1737,2.302,2876,6.85,2946,4.867,3595,2.249,3604,3.85,3891,4.028,3893,4.542]],["t/720",[16,1.974,28,1.497,56,1.048,113,3.051,132,3.116,171,2.273,172,4.036,173,4.477,196,3.316,275,2.59,304,2.236,318,2.583,338,2.058,351,3.028,430,4.127,502,2.307,580,3.684,729,5.971,734,3.702,827,4.24,1737,4.001,2545,5.522,3595,3.082,3898,11.433]],["t/722",[7,0.608,12,3.109,16,1.434,24,1.248,28,1.676,56,0.761,107,2.577,109,2.131,132,2.956,165,1.727,170,2.277,304,1.624,338,1.896,372,2.884,405,2.452,408,2.782,426,2.847,527,1.685,536,2.295,670,4.479,759,2.702,831,4.861,857,3.497,936,5.191,984,3.297,1072,3.562,1475,5.414,1622,4.946,1737,2.907,2010,3.503,2191,5.087,3595,2.839,3899,6.642,3900,8.306]],["t/724",[56,0.988,107,2.563,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1132,4.444,1737,3.775,2012,7.339,3595,3.687]],["t/726",[56,0.988,127,3.282,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1132,4.444,1737,3.775,2011,7.448,3595,3.687]],["t/728",[56,1.118,84,1.007,107,2.38,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,966,2.535,993,3.76,1402,5.296,1622,5.965,2012,6.816,2960,7.085,3901,7.948]],["t/730",[56,1.118,84,1.007,127,3.048,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,966,2.535,993,3.76,1402,5.296,1622,5.965,2011,6.917,2960,7.085,3902,7.948]],["t/732",[29,2.637,56,0.981,105,2.364,109,1.872,112,3.745,127,2.479,162,2.656,203,2.379,206,4.895,295,3.04,318,1.84,322,3.933,336,3.076,391,3.182,395,2.987,398,3.5,614,2.171,828,3.152,993,3.058,1073,5.394,1093,4.282,1099,2.799,1633,4.682,2010,3.863,2013,2.518,2141,3.866,2142,4.944,2276,4.336,2722,4.652,2900,5.091,3604,4.767,3882,7.559,3891,4.989]],["t/734",[2,0.927,4,1.067,7,0.271,29,1.2,31,0.84,35,2.394,41,1.56,49,1.806,56,1.129,84,0.956,85,1.191,96,1.958,105,1.076,106,1.26,108,2.871,109,2.12,112,2.754,113,1.254,123,1.172,127,1.823,140,2.178,160,1.205,170,1.016,171,1.51,174,1.022,179,1.86,182,1.151,193,2.545,196,1.363,203,1.331,228,1.089,236,0.904,262,2.248,275,1.064,304,0.725,313,0.84,316,2.477,318,0.837,322,3.431,336,2.262,338,0.846,353,0.938,359,1.516,372,1.287,377,1.565,391,2.34,398,1.958,405,1.094,408,2.007,444,1.491,491,1.036,502,0.948,509,1.948,527,1.215,553,1.821,568,0.985,578,1.351,605,1.244,613,1.554,614,2.065,623,2.662,658,1.647,696,2.423,756,1.614,828,1.434,846,3.51,892,1.287,937,2.054,993,3.569,1073,2.454,1093,1.948,1099,2.975,1162,1.913,1284,2.341,1398,2.367,1558,3.13,1588,3.132,1693,2.012,1705,2.1,1982,2.642,2010,4.402,2013,1.852,2141,1.759,2142,2.766,2210,2.69,2214,3.6,2252,2.559,2268,1.96,2276,3.189,2625,3.189,2722,2.116,2900,3.744,3604,3.506,3839,2.865,3880,3.027,3891,3.668,3903,3.263,3904,3.263,3905,3.263,3906,2.559,3907,2.742,3908,3.263,3909,3.263,3910,3.263,3911,5.982,3912,3.705,3913,3.705,3914,3.263,3915,3.263,3916,3.263]],["t/736",[1781,3.73]],["t/738",[7,0.782,48,4.026,84,1.075,109,1.674,139,4.454,759,3.479,1132,4.407,1959,5.619,2231,6.55,3668,7.763,3826,8.27,3917,6.615]],["t/740",[7,0.829,24,1.336,28,1.164,49,1.854,56,0.815,84,0.894,107,2.112,109,1.391,139,4.719,165,1.848,171,2.241,285,3.408,502,2.275,527,2.531,568,3.011,605,2.986,614,1.519,724,4.017,729,5.887,759,2.892,944,4.253,945,3.479,966,2.867,1100,3.27,1506,5.294,1959,3.934,1988,3.451,2276,6.032]],["t/742",[5,1.938,7,0.629,19,2.452,24,1.987,28,0.776,31,1.344,39,2.239,41,3.619,68,3.558,84,0.596,96,1.938,107,1.409,109,0.928,115,2.534,133,3.413,134,2.738,139,3.58,160,1.929,164,1.512,165,1.787,200,2.726,203,1.91,226,2.668,236,2.873,288,3.632,295,2.213,318,1.34,338,1.354,353,1.501,367,2.801,414,3.157,527,2.052,568,2.284,591,3.137,605,1.991,630,2.032,638,3.311,757,1.356,759,2.796,760,2.772,769,4.199,770,4.228,774,2.976,827,5.215,938,3.387,939,3.137,1008,3.287,1111,3.831,1182,2.624,1185,4.481,1251,3.927,1322,4.035,1329,5.03,1475,2.96,1506,3.531,1733,3.287,1754,2.339,2231,6.193,2271,3.927,2434,3.746,3386,4.159,3810,3.442,3835,4.705,3893,4.095,3918,3.5,3919,4.844,3920,5.503,3921,5.503,3922,4.705]],["t/744",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1219,4.277,3823,6.054]],["t/746",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,945,3.229,3823,6.155]],["t/748",[7,0.934,56,0.857,84,0.94,109,1.463,139,3.894,145,3.489,170,2.564,203,2.599,206,5.619,246,4.105,304,1.828,614,1.597,759,3.042,760,2.564,966,2.366,1364,4.337,1481,4.643,1558,4.886,2013,2.89,3923,6.558,3924,7.066,3925,6.274,3926,7.066,3927,9.647]],["t/750",[7,0.856,56,0.857,84,0.94,109,1.463,139,3.894,145,3.489,170,2.564,203,2.599,206,5.619,246,4.105,304,1.828,614,1.597,759,3.042,760,2.564,966,2.366,1364,4.337,1481,4.643,1558,4.886,2013,2.89,3923,6.558,3925,6.274,3928,7.066,3929,7.066,3930,9.647,3931,8.234]],["t/752",[7,0.745,24,1.529,28,1.614,109,1.593,127,3.097,132,2.774,139,4.239,193,3.435,236,2.483,254,3.956,550,3.712,578,3.712,614,1.739,623,4.523,1099,3.497,1101,5.642]],["t/754",[7,0.694,84,1.187,88,3.967,89,3.287,128,5.688,140,3.451,165,1.973,182,2.947,294,4.738,575,4.183,605,3.187,635,3.195,763,5.995,828,3.673,892,3.295,1076,3.946,1100,3.49,1132,3.911,1425,5.118,1616,6.096,3932,6.889,3933,7.753,3934,7.753]],["t/756",[12,2.899,16,1.788,24,1.164,28,1.356,34,1.831,56,0.71,84,0.779,85,2.49,107,2.959,109,2.196,113,2.621,132,3.18,139,3.226,164,1.975,170,2.124,213,2.925,236,1.89,338,1.768,351,2.601,353,1.96,372,2.69,470,5.198,484,3.045,502,1.982,527,2.101,614,1.769,755,3.237,759,2.52,916,2.74,966,1.96,977,2.621,994,3.095,1008,4.294,1364,3.593,1475,5.17,1558,4.048,2010,2.502,3858,4.988,3935,7.189]],["t/758",[7,0.297,12,2.413,16,0.702,24,1.372,25,1.207,28,0.532,34,0.96,36,1.662,49,0.847,56,1.114,68,1.429,84,0.648,85,1.306,96,1.328,107,2.169,109,2.032,113,1.375,132,1.757,139,4.413,140,1.477,164,1.036,170,2.198,179,1.261,199,1.859,213,1.534,236,1.573,295,1.516,296,1.453,313,0.921,338,1.83,351,1.364,353,1.028,377,0.843,426,1.392,470,2.726,476,2.992,491,1.136,502,1.65,512,2.565,527,1.627,575,2.842,580,1.309,614,1.969,647,3.466,653,1.883,663,2.303,725,3.046,755,1.698,756,1.77,759,2.608,803,4.189,828,1.572,846,1.157,857,1.71,866,3.223,892,2.784,916,1.437,945,1.247,966,1.632,977,1.375,993,1.525,994,1.623,1008,2.252,1076,2.679,1093,3.39,1099,2.216,1118,4.651,1132,4.102,1224,2.199,1364,1.884,1475,3.219,1558,2.123,1616,3.329,1959,4.406,2010,1.312,2253,2.285,2256,7.279,2257,2.625,2900,4.03,2928,1.986,3008,2.625,3917,2.513,3935,3.77,3936,3.319,3937,5.984,3938,3.578,3939,5.678,3940,7.457,3941,5.678,3942,2.398,3943,7.099,3944,2.897]],["t/760",[7,0.84,24,1.726,28,1.189,68,3.195,132,2.475,233,3.05,313,2.059,318,2.052,328,5.562,398,2.969,426,3.112,476,2.446,502,2.324,512,2.906,527,1.843,536,2.51,565,3.48,568,2.413,578,3.312,623,4.036,647,3.998,724,4.103,916,3.213,1098,5.315,1132,3.742,1236,5.617,1849,4.583,2254,4.352,3830,6.014,3945,9.081]],["t/762",[7,0.751,24,2,29,2.478,31,2.629,39,2.89,49,1.596,125,4.048,139,4.829,165,1.591,197,3.164,203,2.753,213,3.879,236,1.867,246,4.069,272,2.907,292,3.089,304,1.496,414,4.074,491,2.14,759,3.342,760,2.816,769,4.929,811,4.406,867,5.457,908,3.615,1080,3.951,1083,3.443,2231,7.102,2887,4.155,3616,5.867,3918,4.517,3946,6.739]],["t/764",[7,0.89,16,1.116,24,1.952,25,2.387,28,1.655,29,1.32,31,2.261,34,0.963,37,3.999,39,2.441,49,0.85,56,0.736,68,2.275,85,1.31,96,1.332,103,1.266,106,1.386,112,2.972,133,1.618,134,1.882,139,1.697,140,1.482,145,1.521,160,2.975,165,0.847,171,1.028,179,1.266,203,1.436,221,1.397,223,2.13,225,1.352,228,1.198,236,1.577,257,1.926,279,1.634,292,1.645,304,1.264,313,0.924,318,0.921,323,1.525,338,0.93,343,1.345,377,0.846,394,1.89,395,2.371,397,1.572,418,1.89,426,2.754,484,1.602,500,1.789,512,1.304,527,1.855,530,1.691,560,2.013,565,1.235,568,1.718,605,1.369,609,2.472,614,0.696,630,1.397,635,1.372,712,2.035,723,1.352,747,2.463,757,2.091,759,2.614,760,2.734,769,3.556,827,3.778,857,1.716,892,1.415,938,5.223,957,2.198,977,1.379,984,1.618,1015,4.422,1019,1.39,1072,1.748,1076,2.16,1100,1.499,1181,2.213,1271,1.882,1329,5.308,1413,2.117,1605,2.773,1754,2.55,1781,1.277,2081,2.699,2101,3.016,2229,3.016,2231,6.499,2232,3.016,2293,4.362,2501,2.959,2816,4.228,2868,2.057,2928,1.993,3918,2.406,3947,3.589,3948,3.234,3949,3.589,3950,3.33,3951,3.782,3952,3.234,3953,3.782]],["t/766",[24,1.693,29,3.648,103,3.498,395,4.132,759,3.665,1530,6.291,2240,8.034]],["t/768",[7,0.843,18,4.155,24,1.136,28,0.99,56,1.18,68,3.585,84,1.159,103,2.348,105,2.195,132,2.061,160,2.46,165,1.572,196,2.781,257,3.572,279,3.031,398,3.331,527,1.534,565,2.291,568,2.009,759,3.315,760,2.794,838,3.539,857,3.183,933,3.975,957,5.495,1192,4.353,1236,4.677,1530,4.222,1633,3.306,1735,6.584,1959,3.346,2142,5.322,2227,6.584,2722,5.82,3071,5.392,3948,5.999,3950,6.177,3954,6.177,3955,5.999,3956,5.999,3957,6.391,3958,7.561,3959,7.561]],["t/770",[37,5.461,56,1.306,95,6.494,279,3.539,759,2.872,760,3.093,957,6.084,1192,5.082,1530,4.929,1735,5.704,1754,3.482,1959,4.992,2041,5.517,2142,5.209,2229,6.533,2230,7.004,2231,5.407,2232,6.533,2234,6.533,2235,6.295,2240,6.295,2245,6.671,3955,7.004,3956,7.004,3960,7.774,3961,8.192]],["t/772",[56,1.251,279,3.641,395,3.331,527,1.843,759,2.954,760,2.49,846,3.589,957,6.194,1132,4.733,1192,5.228,1275,8.882,1530,5.071,1735,5.868,1959,5.083,2142,4.194,2234,6.72,2235,6.476,2254,4.352,3955,7.206,3956,7.206,3957,7.676,3962,7.997,3963,7.997,3964,7.997]],["t/774",[16,1.862,84,1.283,254,3.218,409,5.072,760,2.957,1100,3.967,1268,7.053,1456,5.273,1632,5.194,3965,9.498,3966,8.812]],["t/776",[2,1.604,7,0.888,17,2.351,21,2.384,24,1.366,29,2.076,39,2.421,49,1.337,84,0.914,100,2.66,107,1.523,109,1.423,116,3.148,138,4.301,165,1.333,170,1.758,171,2.292,183,2.553,203,2.019,219,2.611,236,2.218,248,3.96,272,2.435,283,2.834,313,1.453,351,2.153,398,3.757,512,2.909,614,1.095,677,3.134,734,2.632,757,1.466,759,2.957,760,2.896,780,3.167,827,3.015,859,3.087,906,3.852,966,1.622,1019,3.919,1070,3.391,1219,2.651,1297,2.884,1540,3.291,1550,4.427,1558,4.75,1698,4.427,2017,2.504,2269,4.957,2271,4.246,2368,8.987,2377,7.03,2384,4.05,2448,3.721,3810,3.721,3967,5.646]],["t/778",[1781,3.73]],["t/780",[7,0.65,12,3.327,28,1.776,84,0.894,89,3.079,105,2.58,107,2.112,174,2.452,228,2.613,372,3.087,377,1.845,390,3.983,397,3.429,405,4.005,426,3.883,496,5.039,511,3.352,536,2.457,558,3.689,622,4.161,723,3.758,892,3.087,1219,3.676,2928,6.098]],["t/782",[16,2.273,27,2.206,28,1.312,102,5.863,183,3.99,405,3.887,426,3.433,495,5.469,511,2.963,670,5.402,837,5.635,871,6.816,1407,6.634,2076,5.402,3968,6.917,3969,8.821]],["t/784",[27,2.46,267,8.86,405,3.297,511,3.304,757,2.553,2226,10.419]],["t/786",[7,0.733,27,2.206,28,1.312,49,2.089,84,1.007,109,2.144,183,3.99,405,2.957,409,4.71,426,3.433,511,2.963,622,4.689,846,2.854,993,3.76,1753,7.796,2010,3.235,3970,7.143]],["t/788",[27,2.376,130,5.706,183,4.296,338,2.462,405,3.184,511,3.191,724,4.873,994,5.099,3971,7.448,3972,7.74]],["t/790",[27,2.356,163,3.933,183,4.26,277,6.056,405,3.157,511,3.164,622,5.006,723,3.547,916,4.79,3973,9.039]],["t/792",[27,2.336,28,1.389,107,3.319,109,1.659,405,3.131,426,3.635,511,3.137,820,6.436,3974,8.964]],["t/794",[16,1.689,27,2.155,34,2.312,121,5.312,254,2.92,353,3.045,397,3.774,405,2.888,408,4.554,484,3.847,511,2.895,581,6.566,712,4.885,1632,4.712,3008,6.322,3975,9.784,3976,11.474,3977,8.616]],["t/796",[16,1.786,27,2.279,343,3.414,351,3.474,405,3.054,432,7.285,476,2.787,511,3.06,583,5.863,635,3.483,757,3.053,2448,6.004,3978,7.999,3979,7.655]],["t/798",[27,2.206,49,2.089,109,1.567,130,5.299,171,2.525,172,3.924,174,2.763,183,3.99,377,2.079,405,2.957,426,3.433,505,3.852,511,2.963,580,3.933,636,5.234,724,4.526,1099,4.194,3980,8.467]],["t/800",[24,1.621,27,2.376,405,3.184,511,3.191,760,3.726,769,4.847,1696,6.367,3981,10.009]],["t/802",[24,1.635,27,2.396,405,3.211,511,3.218,690,6.263,760,2.983,769,4.864,1696,6.422,3982,9.195]],["t/804",[16,1.801,26,4.309,27,2.297,121,5.663,183,4.154,405,3.079,407,4.375,408,3.494,511,3.086,750,6.821,757,3.064,936,6.518,3753,8.816]],["t/806",[4,2.861,27,2.189,172,3.195,183,3.958,193,3.354,394,4.61,405,2.934,407,4.169,511,2.94,552,3.264,725,5.739,822,5.509,857,4.184,1077,5.162,1100,3.655,1355,5.722,3968,6.862,3983,8.4,3984,10.268,3985,8.4]],["t/808",[5,3.442,7,0.864,16,1.818,25,2.359,28,1.379,56,0.728,84,0.798,105,2.305,125,4.201,133,4.18,134,3.667,145,2.963,164,2.024,174,2.19,196,2.921,198,3.588,200,3.651,228,2.334,236,1.938,294,3.964,372,2.757,390,3.558,458,4.149,511,3.98,536,2.195,677,3.882,683,2.757,724,3.588,807,4.694,881,4.609,892,3.656,914,4.648,966,2.009,1206,3.922,1500,5.569,1779,4.963,2598,4.536,3986,6.993]],["t/810",[16,1.743,24,1.517,28,1.606,84,1.015,144,4.487,367,4.77,511,3.629,635,3.399,807,4.398,820,4.816,1236,6.246,2390,7.329,2598,5.767,3987,8.249,3988,10.096,3989,10.096,3990,10.096]],["t/812",[5,3.496,46,4.113,163,3.933,225,3.547,289,3.922,322,3.104,511,3.164,807,3.835,1406,9.054,2584,6.994,2950,7.626,3991,10.694]],["t/814",[5,3.438,28,1.377,163,3.868,287,8.904,289,3.857,511,3.111,760,3.686,807,3.771,1737,3.681,2613,7.947,3992,10.516]],["t/816",[5,3.438,28,1.377,84,1.057,89,3.642,163,3.868,289,3.857,432,7.357,511,3.111,757,2.874,807,3.771,1737,3.681,3979,7.782,3993,10.516]],["t/818",[5,3.409,16,2.313,163,3.836,289,3.825,322,3.027,405,3.955,495,5.604,511,3.086,807,3.74,3994,10.43,3995,8.521]],["t/820",[5,3.526,24,1.621,28,1.412,163,3.967,289,3.956,322,3.131,511,3.191,769,4.847,807,3.868,3996,10.785]],["t/822",[5,3.466,28,1.389,163,3.9,170,3.7,289,3.889,322,3.078,511,3.137,769,4.812,807,3.803,3997,10.605]],["t/824",[5,3.274,24,1.505,28,1.312,49,2.089,163,3.684,183,3.99,285,3.84,289,3.674,323,3.749,367,4.732,409,4.71,511,2.963,751,5.367,807,3.592,1178,5.367,1737,3.506,2422,5.965,2593,9.685,3093,7.412,3998,10.016]],["t/826",[5,3.438,27,2.317,28,1.377,163,3.868,200,4.835,240,5.337,289,3.857,322,3.053,511,3.111,694,5.308,807,3.771,816,5.529,3386,8.816,3999,10.516]],["t/828",[5,3.409,24,1.567,28,1.366,163,3.836,201,6.33,289,3.825,322,3.027,405,3.955,511,3.086,807,3.74,3995,8.521,4000,10.43]],["t/830",[5,3.149,16,1.664,28,1.561,109,1.508,163,3.544,193,4.023,224,4.686,289,4.371,322,2.797,338,2.199,511,2.85,552,3.165,632,4.81,725,4.552,807,4.274,812,5.96,1181,5.231,3897,8.652,3968,6.653,4001,9.635]],["t/832",[5,3.354,16,1.772,24,1.542,163,3.774,289,4.879,322,2.978,426,3.517,511,3.036,605,3.446,763,6.481,807,4.441,1481,6.15,3077,8.383,4002,10.26]],["t/834",[5,3.381,16,1.786,24,1.554,163,3.805,289,4.896,511,3.06,662,5.579,807,4.788,1481,6.181,1557,5.956,1737,3.62,2416,9.11]],["t/836",[5,3.249,24,2.054,28,1.301,36,4.067,49,2.073,163,3.655,289,3.645,407,4.169,408,4.395,511,2.94,527,2.016,731,4.834,807,3.564,945,3.051,1703,7.885,1737,3.478,4003,9.938]],["t/838",[16,2.181,219,3.046,318,3.049,405,3.729,426,3.635,567,3.946,723,3.518,734,4.354,2928,5.185,4004,8.664]],["t/840",[7,0.757,16,2.149,27,2.279,102,6.054,183,4.12,405,3.674,426,3.545,495,4.635,511,3.06,670,5.579,871,7.039,1407,6.851,2076,5.579,3968,7.143,4005,10.344]],["t/842",[27,2.356,109,2.119,183,4.26,405,3.157,511,3.164,622,5.006,846,3.047,1753,8.11,2010,3.454,3970,7.626]],["t/844",[7,0.776,27,2.336,163,3.9,183,4.224,277,6.025,405,3.131,511,3.137,622,4.964,723,3.518,916,4.773,3973,8.964]],["t/846",[4,3.002,7,0.763,27,2.297,405,3.079,432,7.321,476,2.81,511,3.086,632,5.207,635,3.512,757,2.86,1076,3.485,1695,8.521,3978,8.065,3979,7.718]],["t/848",[27,2.438,130,5.855,171,2.791,183,4.409,405,3.268,511,3.275,580,4.175,3980,9.356]],["t/850",[4,2.93,27,2.242,172,3.272,183,4.054,193,3.435,394,4.721,407,4.269,511,3.011,552,3.343,725,4.809,822,5.642,1077,5.286,1100,3.743,1355,5.86,3968,7.028,3983,8.603,3984,8.603,3985,8.603,4006,10.178]],["t/852",[1781,3.73]],["t/854",[7,0.563,17,2.824,34,1.82,49,1.606,56,1.138,203,1.71,225,2.554,239,4.099,248,5.412,256,2.673,285,3.954,296,2.753,304,2.43,317,3.571,351,2.586,393,3.509,398,2.517,731,3.745,734,3.161,760,2.111,966,2.61,1123,6.358,1400,5.602,1401,4.208,1467,4.469,1475,3.844,1601,5.718,1632,3.708,1737,2.694,2073,4.268,2142,4.763,2317,5.954,2327,5.818,2723,6.921,2724,6.921,2745,5.589,3595,2.632,4007,6.78,4008,6.78,4009,4.975]],["t/856",[7,0.674,10,4.625,56,1.062,84,1.165,228,2.708,248,5.524,322,2.674,511,2.726,605,3.893,892,3.199,1123,6.932,1290,5.036,1400,4.437,1565,5.953,1601,5.108,1629,5.953,1638,4.416,2013,2.848,2142,4.255,2723,6.183,2724,6.183,2799,6.27,4010,8.114,4011,8.114,4012,8.114]],["t/858",[7,0.585,15,2.704,56,1.086,133,3.172,140,2.906,162,2.606,164,2.037,165,1.661,171,2.015,196,2.939,203,1.775,248,4.605,255,4.994,322,3.069,324,3.927,347,4.894,393,3.642,614,1.806,724,3.611,731,3.887,828,3.092,977,2.704,1123,5.779,1265,3.887,1400,6.317,1402,4.587,1475,3.989,1601,5.861,1632,3.848,1633,3.494,2013,2.47,2142,3.69,2723,7.095,2724,7.095,3607,7.194,4009,5.163,4013,6.34,4014,6.34]],["t/860",[1781,3.73]],["t/862",[7,0.958,15,3.812,572,5.536,1090,5.394,3831,6.77]],["t/864",[7,0.889,27,2.189,49,2.073,56,0.911,100,4.124,243,5.044,246,3.487,277,4.74,401,3.525,405,3.586,511,2.94,723,3.296,1072,4.262,1182,4.398,1754,3.92,3556,6.086,3831,7.3,4015,10.698]],["t/866",[4,2.541,7,0.825,27,1.945,49,2.353,56,0.809,100,3.663,103,3.503,236,2.154,243,4.48,283,2.369,394,4.095,401,4.41,405,3.33,437,3.845,481,4.894,492,6.408,511,2.612,723,2.928,736,3.493,977,2.987,1923,5.572,2013,2.729,3070,8.368,3553,5.846,3554,5.846,3555,6.533,3831,6.779]],["t/868",[7,0.773,17,2.931,27,1.76,39,3.018,56,0.732,100,4.917,103,3.283,106,2.718,228,2.349,243,4.056,394,3.707,405,3.121,410,4.894,437,3.481,511,2.364,527,1.621,723,2.651,760,3.249,769,2.85,802,3,945,2.454,977,2.704,1094,3.707,1754,3.152,1759,6.753,1923,5.223,2013,3.268,2227,5.163,2228,5.163,2293,4.338,2384,5.048,3468,7.824,3831,6.354,4016,8.063,4017,9.681,4018,6.528,4019,6.528]],["t/870",[7,0.689,10,4.728,21,2.47,24,1.415,27,2.075,56,0.863,84,0.947,100,3.909,243,4.781,353,2.384,394,4.369,405,3.469,511,2.787,568,2.503,683,3.271,723,3.124,759,3.064,760,2.583,840,3.966,1754,4.635,1923,4.653,1991,5.827,2013,2.912,3831,7.062,4020,10.35]],["t/872",[1781,3.73]],["t/874",[7,0.847,49,2.414,228,3.403,278,3.849]],["t/876",[7,0.951,34,2.043,49,2.568,56,0.792,84,1.119,103,2.685,203,1.921,228,2.542,278,2.875,283,2.32,332,4.164,382,4.978,393,3.941,401,3.067,408,2.897,513,3.781,580,2.786,614,1.477,723,4.083,888,5.442,1094,6.03,1369,6.166,1504,5.655,1511,7.467,2621,5.802,4021,7.309,4022,6.277]],["t/878",[2,1.463,7,0.622,34,1.382,39,2.208,49,1.775,56,0.78,68,2.994,84,1.107,100,2.427,113,2.879,144,2.599,165,1.216,186,2.557,203,2.87,213,2.208,225,1.94,226,2.631,233,1.964,302,2.499,313,1.326,322,2.47,332,2.816,373,3.924,377,2.082,491,2.806,613,2.453,614,2.087,647,2.036,659,3.514,736,2.314,763,3.694,846,2.424,888,5.973,966,2.153,995,3.019,1035,3.241,1083,2.631,1094,2.712,1117,3.617,1119,4.245,1220,3.113,1265,2.844,1353,3.581,1375,3.289,1737,2.978,1799,6.927,1988,2.27,2000,3.394,2010,2.748,2015,3.196,2343,6.58,2362,5.375,2388,5.943,2389,4.943,2430,3.34,2625,3.113,3167,4.522,3595,1.999,3804,3.778,3858,2.816,4022,4.245,4023,5.149,4024,5.149,4025,5.149,4026,5.149,4027,5.149,4028,5.149,4029,5.149]],["t/880",[2,1.915,7,0.946,15,2.589,21,2.007,49,2.418,85,3.302,103,2.376,106,2.603,113,2.589,179,2.376,203,2.282,228,2.249,283,2.756,313,1.735,332,3.685,401,4.586,481,4.242,491,2.873,512,2.448,562,3.487,614,2.209,628,3.667,723,2.538,736,3.028,820,3.65,888,3.741,944,2.873,1206,3.78,1254,5.004,1565,4.944,1633,4.492,1923,3.78,2013,3.175,2388,3.722,3070,4.888,3553,5.068,3554,5.068,4022,5.555,4030,6.468,4031,7.102]],["t/882",[7,0.81,49,2.309,283,2.97,401,3.926,512,3.542,736,4.38,1254,7.239,3553,7.33,3554,7.33]],["t/884",[7,0.853,16,1.338,24,1.164,28,1.014,34,1.831,49,1.616,56,0.71,68,2.725,84,1.252,105,3.386,162,2.526,219,2.225,246,2.718,256,2.69,261,4.87,285,2.97,295,2.891,318,2.636,353,2.621,377,1.608,398,2.532,401,2.748,512,2.478,536,2.141,614,1.323,734,3.181,759,3.795,828,2.998,966,1.96,988,3.515,1019,2.641,1132,3.192,1236,4.792,2000,6.011,2013,2.394,2141,4.916,2142,4.782,2722,5.915,2744,5.433,3071,5.524,4032,6.547]],["t/886",[7,0.822,49,1.828,56,1.197,84,0.881,140,3.188,283,2.352,318,1.981,336,3.31,377,1.819,393,3.995,398,2.866,401,3.11,568,2.33,683,3.044,736,3.469,888,4.286,966,2.218,993,3.291,1100,3.224,1632,5.409,2000,5.088,2142,4.048,2359,8.006,2722,5.007,2744,6.148,3554,5.806,3555,8.311,4033,7.72,4034,6.956,4035,6.956,4036,7.72]],["t/888",[49,2.089,84,1.007,161,6.327,201,4.732,283,2.688,318,2.263,398,3.274,401,3.553,736,3.964,966,2.535,1109,7.948,2000,5.814,2142,4.625,2359,6.472,2451,7.948,3553,8.084,3804,6.472,4034,7.948,4035,7.948,4037,8.821]],["t/890",[7,0.824,15,3.812,49,2.35,279,4.517,395,4.132,614,1.925,828,4.36]],["t/892",[2,1.491,7,0.436,17,2.185,23,2.672,24,1.772,28,0.78,48,2.243,49,1.799,56,1.162,84,0.867,98,1.943,113,2.016,162,2.813,165,1.793,171,1.502,174,1.643,203,2.252,219,1.711,228,2.536,232,2.011,246,3.558,248,2.595,272,2.263,313,1.956,338,2.537,377,1.236,433,3.192,524,3.403,530,2.472,536,1.647,545,2.555,568,1.583,614,1.018,705,5.791,757,2.318,831,3.487,843,6.436,857,3.631,966,1.508,993,2.236,1019,2.031,1037,2.751,1100,2.191,1235,4.054,1266,2.397,2000,7.137,2017,3.37,2144,5.036,2176,4.607,2214,3.58,2515,4.409,2744,6.05,2894,4.607,3607,4.054,3858,4.883,4038,4.727,4039,5.247,4040,7.596,4041,5.247,4042,5.247,4043,5.247,4044,5.247]],["t/894",[7,0.796,56,0.997,304,2.127,397,4.197,580,3.505,651,6.729,966,2.753,989,5.684,1123,7.011,1297,4.895]],["t/896",[7,0.832,304,2.223,438,5.475,651,7.032,966,2.877,1992,7.973]],["t/898",[84,1.057,92,3.614,93,4.942,200,4.835,207,4.761,261,4.945,304,2.057,313,2.384,353,2.661,614,1.797,620,5.399,966,2.661,1182,4.654]],["t/900",[16,1.895,68,3.861,85,3.527,165,2.281,304,2.146,317,5.09,338,2.505,614,1.875,2202,7.825,2268,5.805]],["t/902",[5,2.947,21,2.364,28,1.644,36,3.69,56,0.826,96,2.947,98,2.94,106,4.27,107,2.142,109,1.789,170,3.135,186,3.943,203,2.003,256,3.97,296,3.225,304,1.763,343,2.976,368,6.324,404,4.074,513,3.943,568,2.396,606,6.43,611,3.478,694,4.551,774,4.526,858,4.127,3091,6.545,4045,6.814]],["t/904",[109,1.763,203,2.503,304,2.203,404,5.091,846,3.21,1705,6.385,3868,8.939]],["t/906",[7,0.925,15,2.721,21,2.784,24,1.786,26,2.77,36,3.291,46,5.055,56,1.089,90,3.517,98,2.622,100,3.337,106,2.735,112,4.881,144,3.574,183,3.203,186,3.517,203,1.786,217,4.254,275,2.31,279,3.224,304,2.076,359,3.291,404,3.634,408,2.694,527,2.565,607,2.86,622,3.764,719,4.49,883,6.57,945,2.469,1083,3.618,1253,4.458,1475,4.015,4045,6.077,4046,7.082]],["t/908",[24,1.594,27,2.336,28,1.389,126,4.752,128,4.693,304,2.47,367,5.01,404,4.792,650,6.628,825,7.562,3386,7.438,4047,9.339,4048,9.339]],["t/910",[7,0.716,24,1.808,28,1.281,107,2.325,109,1.531,113,3.311,162,3.924,165,2.034,171,3.033,304,1.913,527,1.985,614,1.672,759,3.183,760,2.683,945,3.004,1402,4.245,1737,3.424,1988,3.798,2215,8.27,2276,6.405]],["t/912",[7,0.762,16,1.08,21,2.98,28,1.574,32,3.629,39,3.934,56,0.818,68,3.142,100,2.594,107,1.485,127,1.903,165,1.3,181,3.287,183,2.49,197,2.585,203,1.984,207,2.368,223,3.267,253,3.491,304,1.746,315,2.756,316,2.585,336,2.361,337,2.702,339,2.927,343,3.967,379,6.906,404,2.825,430,2.862,477,3.441,480,3.949,482,2.594,502,1.6,508,3.418,555,2.745,577,4.989,613,3.746,614,1.526,623,2.778,635,2.105,690,3.6,694,3.156,723,3.769,747,3.403,757,2.042,778,3.659,838,2.927,856,6.651,984,2.482,1083,2.813,1099,2.148,1219,2.585,1402,2.713,1693,3.394,2277,3.907,2484,4.458,2887,3.394,3005,4.725,3386,4.385,4049,9.174,4050,4.725,4051,5.285]],["t/914",[7,0.91,15,3.5,20,7.224,21,2.713,50,5.616,84,1.04,106,3.518,116,3.582,304,2.434,408,3.465,580,3.333,614,1.767,796,5.032,1083,4.654]],["t/916",[7,0.839,85,3.687,278,3.814,580,3.696,1019,3.911]],["t/918",[8,4.743,16,1.179,23,2.943,56,0.871,96,2.231,140,2.482,177,3.059,179,2.12,219,2.729,275,1.961,304,2.631,318,2.147,366,3.962,393,3.111,438,5.69,447,2.997,500,2.997,545,2.928,548,5.426,552,2.242,555,2.997,558,3.942,614,1.166,802,2.562,846,2.707,1019,4.659,1266,4.753,1297,3.071,1299,4.411,1330,4.141,1413,3.545,1531,4.222,1638,3.271,1954,5.158,1989,4.955,1992,4.788,2016,5.158,2340,3.899,2887,6.743,4052,6.011,4053,8.367,4054,5.158,4055,6.011,4056,6.663,4057,5.416]],["t/920",[113,3.417,248,5.755,275,2.9,304,1.974,353,2.555,484,3.97,1019,3.443,1206,4.987,1309,5.445,1400,6.363,1401,7.222,1638,4.839,1889,6.972,2745,7.329]],["t/922",[7,0.818,34,2.058,56,1.132,84,0.875,85,3.594,97,3.613,224,3.423,254,2.598,322,2.527,353,2.203,482,3.613,495,3.901,572,4.278,580,2.805,611,3.359,614,1.487,709,5.281,762,5.441,1019,3.812,1132,4.607,1178,4.665,1220,4.635,1402,4.851,1633,3.807,1781,2.728,2359,8.71,4058,7.667,4059,7.359,4060,7.667]],["t/924",[7,0.727,84,0.999,171,2.506,246,3.487,304,1.943,491,2.78,580,3.914,614,1.698,723,3.296,1019,3.389,1094,4.61,1511,6.028,1638,4.763,1737,3.478,1923,4.909,2013,3.072,3595,3.397,3805,9.437]],["t/926",[2,1.418,7,0.414,15,1.917,16,1.436,36,2.318,48,2.132,49,2.054,56,0.903,84,1.214,133,2.249,160,1.843,165,1.177,179,1.759,197,2.342,219,1.627,224,2.227,246,2.917,248,4.29,255,3.54,275,2.388,302,2.42,313,1.885,317,2.627,318,2.611,377,1.725,438,4.744,441,2.859,495,4.862,580,1.825,614,0.968,655,3.436,724,2.559,736,2.241,744,2.996,757,1.295,802,2.126,892,2.887,981,3.54,1019,4.592,1098,4.866,1123,5.384,1172,3.163,1292,6.782,1297,2.549,1324,4.112,1400,4.744,1475,2.828,1781,1.775,2001,4.38,2016,4.28,2026,5.372,2359,6.364,2577,2.769,2598,4.749,2641,3.751,2893,4.38,3805,7.493,4009,3.66,4061,4.988,4062,4.988,4063,4.988,4064,4.988,4065,4.988,4066,4.988,4067,7.321,4068,4.988]],["t/928",[23,3.231,203,2.779,318,2.357,322,3.631,614,2.137,1019,4.265,2887,6.791,3595,4.276,4069,9.185,4070,9.185]],["t/930",[2,1.699,7,0.497,16,1.172,23,2.932,56,0.622,219,3.13,246,2.382,278,2.258,283,1.822,304,2.669,313,1.539,318,2.139,545,2.912,548,3.878,552,2.23,614,1.16,756,5.945,892,2.357,917,3.569,1019,2.315,1266,4.383,1292,4.243,1400,3.27,1413,7.089,1954,5.131,2015,7.46,2598,3.878,3805,6.536,4057,5.387,4071,5.979,4072,5.547,4073,5.979,4074,5.979,4075,5.979,4076,5.979,4077,5.979,4078,5.979,4079,5.979,4080,5.979,4081,5.979,4082,5.979,4083,5.979]],["t/932",[2,2.093,7,0.797,24,1.256,25,2.484,28,1.095,35,5.402,39,4.113,49,2.272,84,0.84,109,1.308,116,2.895,197,3.457,304,1.635,313,1.896,336,3.157,391,3.266,404,3.778,505,3.215,614,1.861,635,2.815,743,6.671,760,2.292,802,3.138,830,3.546,1403,6.187,1404,6.187,1406,5.962,1407,7.213,1671,4.267,2010,2.7,2123,7.067,2142,3.861,2584,5.468,2902,6.318,2950,5.962,3643,7.067,4084,9.592,4085,7.363,4086,7.363]],["t/935",[7,0.856,17,3.429,24,1.405,31,2.12,84,1.176,123,2.957,133,3.712,162,3.049,285,3.585,304,1.828,338,2.134,375,6.309,393,4.261,527,1.897,568,2.485,614,1.597,690,5.383,759,3.042,760,2.564,1274,6.919,1402,5.076,1409,5.726,1737,3.272,2966,6.919,3595,3.196,4087,8.234]],["t/937",[5,2.715,7,0.608,16,1.434,25,3.222,84,1.287,162,2.708,164,2.117,171,2.734,219,2.386,236,2.027,304,1.624,318,2.892,377,1.724,398,2.715,614,2.063,683,2.884,723,3.597,724,3.753,731,4.04,757,2.48,988,3.769,1019,3.698,1289,4.903,1328,4.02,1402,4.705,1475,4.147,1629,5.367,1737,2.907,2017,3.245,2609,6.277,3595,2.839,4009,5.367,4088,6.59]],["t/939",[7,0.684,49,1.95,84,1.176,171,2.357,192,5.111,248,4.073,285,4.485,304,1.828,336,3.531,536,2.584,605,3.14,614,1.597,662,5.043,731,4.548,1123,6.979,1400,6.148,1402,4.057,1471,7.639,1475,4.668,1540,4.8,1737,3.272,3595,3.196,4009,6.041,4014,7.419]],["t/941",[7,0.764,31,2.661,35,5.067,37,4.851,39,4.433,56,1.147,63,5.592,68,2.759,84,1.05,95,4.515,100,3.254,109,1.227,203,2.32,236,1.914,283,2.104,377,2.436,481,4.347,614,1.784,734,3.22,881,4.552,966,1.984,1027,4.629,1595,8.826,1689,5.71,1754,3.093,1797,5.803,2384,4.954,2459,5.693,2621,5.263,2672,5.263,3397,5.693,3398,5.263,3604,4.59,3895,5.926,4089,6.906,4090,6.906,4091,6.906,4092,6.906,4093,6.906,4094,6.906]],["t/943",[7,0.872,113,3.26,181,5.065,203,2.648,334,5.065,491,2.695,512,3.083,614,1.646,846,2.745,888,4.711,966,2.438,1119,6.994,1182,4.264,1642,7.645,1754,3.8,1799,6.653,2010,3.85,2388,4.687,3858,4.64,4095,8.485,4096,7.451,4097,7.451,4098,8.485]],["t/945",[1,3.783,5,2.293,7,0.811,25,3.294,56,0.887,84,0.973,126,3.143,131,5.281,135,3.711,157,3.759,162,2.288,165,1.458,174,2.671,203,1.558,254,2.889,314,5.191,322,2.036,341,3.581,369,4.039,370,5.093,375,3.783,449,4.007,476,1.89,500,3.08,525,3.068,532,3.395,555,3.08,590,3.502,611,2.706,614,1.199,621,3.346,623,3.118,675,4.141,677,3.43,683,2.436,830,2.975,859,3.379,1090,2.891,1112,6.103,1113,4.646,1163,4.384,1222,5.003,1230,4.92,1297,3.157,1319,4.432,1320,5.003,1527,4.297,1601,3.889,2013,2.169,2276,3.735,2384,4.432,2549,5.732,2887,5.257,3249,5.301,3493,5.425,4099,6.178,4100,5.425,4101,6.178,4102,8.527]],["t/947",[1781,3.73]],["t/949",[7,0.811,15,3.276,16,1.211,21,1.84,24,1.455,26,2.417,28,0.919,34,1.658,58,4.708,70,7.343,84,0.973,98,2.288,99,4.106,106,3.293,145,3.613,158,6.016,164,1.788,175,5.627,203,1.558,242,4.588,246,2.462,304,1.372,343,3.196,353,2.45,381,3.08,401,4.6,402,5.093,408,4.202,410,4.297,412,4.844,413,4.92,416,2.998,453,4.178,532,3.395,536,1.939,572,3.447,605,2.356,744,3.711,794,4.533,840,2.953,944,2.633,977,2.374,1132,2.891,1206,3.465,3063,4.708,3091,5.093,4103,6.178,4104,6.178]],["t/951",[0,3.177,2,1.484,7,0.74,16,1.484,24,1.292,28,0.776,33,3.137,34,1.401,41,2.496,56,0.927,84,0.596,97,3.567,98,1.934,131,2.496,134,2.738,138,4.595,153,2.815,162,1.934,175,3.009,220,3.387,221,2.032,245,3.287,304,1.681,316,2.452,332,4.14,353,2.559,359,2.426,365,5.536,394,2.75,396,6.24,403,3.531,407,3.606,408,2.88,409,2.788,438,5.912,439,5.214,440,3.517,441,5.942,442,4.585,443,3.878,444,2.385,445,3.471,446,3.5,447,2.603,448,5.012,449,4.91,450,7.266,451,4.585,452,3.787,453,5.119,454,5.012,459,4.304,484,2.331,536,1.639,580,2.77,614,1.013,637,3.531,742,4.989,1021,3.746,1123,3.241,1313,3.198,1324,4.304,1337,3.668,1355,3.414,2307,4.844,4105,4.481,4106,5.222,4107,4.705,4108,4.304]],["t/953",[7,0.653,16,1.08,21,1.639,24,1.342,26,2.154,28,0.819,29,2.024,31,1.417,44,4.961,56,0.818,84,0.898,97,3.706,105,1.815,132,1.704,138,4.253,153,2.968,175,3.173,189,3.105,199,2.862,224,4.468,239,3.328,294,3.121,340,3.867,353,2.636,365,4.199,377,1.298,394,2.9,402,7.562,403,3.723,405,2.636,438,4.301,439,5.417,441,4.507,442,4.835,443,4.089,451,4.835,452,3.993,455,8.587,456,10.159,466,4.089,496,3.544,504,3.517,557,2.887,614,1.068,742,5.183,744,3.307,757,1.43,778,3.659,807,2.242,1123,3.418,1297,2.813,1299,4.04,1355,3.6,1632,3.011,2076,3.372,3464,5.285,4105,4.725,4107,4.961,4108,4.539,4109,5.506,4110,4.385,4111,4.961,4112,5.506,4113,7.864,4114,5.506,4115,5.506,4116,5.506,4117,5.506]],["t/955",[5,3.3,24,1.843,49,2.106,70,6.687,106,4.172,286,5.679,304,1.974,351,3.391,404,4.562,408,4.109,528,4.936,536,3.39,621,4.816,744,5.341,804,8.011,4118,8.534]],["t/957",[2,2.375,24,1.774,34,2.79,106,3.228,126,4.252,175,4.817,254,3.523,256,3.295,304,1.856,353,2.402,408,4.505,409,4.463,447,4.167,460,8.022,461,4.929,462,8.022,463,5.299,464,7.53,465,6.132,466,6.206,629,5.602,2548,7.339]],["t/959",[1781,3.73]],["t/961",[1,2.758,7,0.678,12,1.914,16,0.883,28,1.009,34,1.209,49,1.607,68,1.799,84,0.932,92,3.186,93,4.353,96,1.672,98,2.513,99,2.993,109,1.451,116,1.771,127,1.556,165,1.602,171,2.338,172,4.336,173,2.54,179,1.588,193,3.129,203,1.136,207,2.919,224,2.011,228,1.503,236,1.248,256,1.776,257,2.416,272,2.927,276,3.542,296,1.829,304,1,318,1.156,323,1.914,332,3.711,336,1.931,337,2.21,343,1.688,377,1.061,439,3.102,445,2.993,476,2.076,491,1.431,500,2.245,552,4.082,568,2.048,580,1.648,601,2.228,614,0.874,620,2.625,686,3.712,707,2.641,728,4.979,734,3.164,769,1.824,802,1.92,810,2.107,816,2.689,858,2.341,894,3.587,966,1.294,1074,3.432,1075,2.488,1076,3.449,1077,4.002,1079,4.472,1108,2.428,1144,2.581,1368,3.046,1377,3.531,1398,3.267,1399,2.856,1782,4.058,1806,3.345,2010,1.652,2073,2.835,2352,3.387,2868,3.889,2965,5.959,3604,2.993,3742,3.647,3804,3.304,3891,3.132,3899,3.132,4050,3.865,4119,3.865]],["t/963",[0,2.096,17,1.435,19,1.617,56,1.245,68,2.751,84,0.629,85,1.257,100,2.596,105,1.135,109,0.612,112,5.389,123,1.237,172,3.35,193,2.112,217,3.31,225,1.297,243,1.985,304,1.224,318,0.884,322,2.593,324,3.075,377,0.812,387,2.528,391,1.528,408,2.096,491,1.75,502,1.001,552,3.594,609,2.373,641,3.025,725,2.956,726,5.42,734,2.569,747,1.491,765,2.153,802,1.468,828,2.421,829,1.912,872,6.263,921,4.616,923,3.662,944,3.668,1073,4.143,1075,5.069,1076,2.091,1077,5.904,1081,3.996,1082,2.743,1083,2.815,1142,2.396,1175,4.453,1254,2.558,1285,1.548,1319,6.583,1332,3.87,1334,3.306,1335,3.025,1336,3.795,1337,7.96,1338,3.196,1339,3.306,1340,5.288,1341,3.306,1342,3.306,1343,5.288,1344,3.306,1345,3.306,1346,3.306,1347,2.528,1348,3.306,1349,3.306,1350,3.306,1351,3.306,1352,2.445,1353,2.396,1354,3.996,1355,2.252,1356,3.306,1357,3.306,1358,3.306,1359,3.306,1360,3.306,1361,3.306,1362,3.306,1363,3.306,1737,3.127,2825,7.093,3576,2.743,3594,5.288,4120,9.636,4121,3.104]],["t/965",[0,1.482,1,1.491,2,1.533,5,0.904,7,0.723,16,0.477,17,1.014,19,1.143,21,1.607,26,1.619,27,1.939,28,1.507,31,1.389,34,0.654,49,0.98,56,0.253,68,1.653,84,0.814,85,1.51,89,2.502,92,2.105,93,2.409,98,1.532,103,1.459,105,0.803,106,0.94,107,1.923,109,1.377,116,1.627,126,1.239,134,2.17,135,1.463,138,1.035,140,2.228,144,1.229,145,1.032,153,2.23,160,0.9,163,1.728,164,0.705,165,0.575,169,1.403,171,1.185,172,2.602,173,5.295,174,1.993,179,0.859,189,1.373,190,1.319,192,1.512,193,3.8,203,1.361,207,1.78,224,2.409,232,0.933,236,0.675,257,1.306,275,0.794,276,1.271,288,1.694,289,2.969,304,0.541,313,1.389,317,2.179,318,0.625,322,1.364,323,1.035,336,1.044,337,2.648,338,1.073,339,1.294,343,1.551,351,0.929,372,0.96,377,0.574,383,1.787,391,1.836,401,0.981,426,2.1,441,1.396,458,1.445,491,0.773,499,1.787,502,0.708,504,1.556,505,1.063,509,1.454,541,1.579,552,1.543,553,2.309,558,1.148,565,1.856,568,1.628,591,1.463,597,1.325,601,1.205,611,1.067,614,1.047,621,1.319,631,1.677,632,5.142,635,0.931,638,2.624,653,0.808,667,2.435,672,1.512,691,1.411,723,0.917,741,2.824,743,1.694,750,1.808,756,1.205,772,2.878,774,1.388,795,1.677,807,2.903,810,1.139,811,1.592,812,2.907,822,1.533,827,2.881,828,2.371,829,1.352,838,1.294,845,1.632,857,1.164,858,1.266,872,2.824,917,1.454,974,1.567,977,2.739,987,1.909,995,1.428,1008,2.605,1070,2.486,1073,1.831,1075,2.98,1076,2.047,1077,4.204,1079,1.605,1093,5.415,1094,3.351,1097,1.972,1121,1.556,1145,1.306,1180,3.036,1182,1.224,1302,2.643,1467,1.605,1557,1.592,1693,1.501,1696,1.632,2010,1.518,2073,4.005,2190,1.436,2329,2.194,2331,1.632,2431,1.831,2534,2.259,2547,3.728,2569,3.477,2928,2.995,2939,2.194,2942,3.972,3030,2.09,3074,3.551,3595,2.094,3597,2.337,3810,1.605,4122,2.435,4123,2.566,4124,4.361,4125,2.259]],["t/967",[1781,3.73]],["t/969",[4,2.189,7,0.944,8,3.797,21,1.995,27,1.675,41,3.202,46,4.962,84,1.028,85,2.445,90,4.474,92,2.614,93,4.022,98,2.48,109,1.19,127,2.315,133,3.02,134,3.512,153,3.611,164,1.939,172,3.289,174,2.098,207,3.875,213,2.872,224,2.99,256,2.641,276,3.497,283,2.745,304,1.487,305,4.49,338,1.736,340,4.705,343,2.511,363,5.037,440,3.112,476,2.049,532,3.681,599,3.628,613,3.191,690,4.379,694,3.839,1099,2.614,1162,3.927,1202,4.345,1213,5.104,1267,5.335,2073,4.217,4126,6.698]],["t/971",[5,2.319,31,2.212,39,2.679,56,1.022,68,3.433,84,1.121,106,2.413,109,1.971,110,3.468,165,2.028,172,3.137,197,2.933,203,2.167,207,3.696,253,3.961,254,2.117,304,1.908,377,1.472,409,3.336,527,1.98,552,3.205,553,3.486,576,3.878,607,3.47,611,2.737,614,1.212,630,2.431,638,3.961,769,3.977,819,4.639,830,3.009,902,5.059,944,2.663,979,4.388,1075,3.451,1077,3.685,1299,4.584,1399,3.961,1689,3.451,2335,6.843,2434,4.481,3059,7.032,3387,4.976,3894,5.796,3896,5.796,4119,5.361,4127,5.25,4128,6.584]],["t/973",[7,0.818,27,2.463,46,4.75,47,5.096,84,1.124,90,3.807,92,4.74,93,3.423,96,2.846,158,4.038,172,2.799,207,3.298,256,3.023,306,3.587,332,4.193,338,1.987,476,3.012,481,4.826,597,4.173,599,5.333,707,4.495,796,4.235,810,3.587,1070,4.606,1090,3.587,1099,2.992,1806,5.694,3620,6.579]],["t/975",[5,1.485,7,0.515,16,0.785,17,1.667,24,0.683,29,1.471,31,1.596,39,1.716,49,0.948,56,1.062,68,2.477,84,1.117,92,4.461,93,3.816,106,2.395,109,1.738,110,2.222,144,2.019,165,1.464,171,1.146,172,2.771,177,2.036,197,1.879,199,2.08,203,1.564,207,3.264,253,2.537,256,2.445,278,1.511,297,2.306,304,2.456,372,1.578,377,0.943,398,2.301,409,2.137,476,1.224,527,1.749,543,3.679,552,2.831,553,2.233,576,2.484,580,1.464,607,2.504,611,1.753,614,1.472,630,1.557,638,2.537,707,2.346,755,1.899,769,3.074,819,2.972,830,1.927,873,2.903,892,1.578,902,3.241,944,1.706,963,2.556,966,2.456,979,2.811,1075,2.21,1077,2.36,1083,3.168,1100,1.671,1157,3.187,1218,2.757,1297,2.045,1299,2.936,1399,2.537,1597,2.66,1601,2.519,1671,2.319,1689,2.21,1737,3.016,1849,2.294,1998,2.616,2015,2.484,2130,3.434,2335,4.938,2376,3.434,2434,2.871,2464,3.299,2577,2.222,2887,2.467,3059,5.42,3387,3.187,3398,3.05,3595,2.946,3621,3.841,3896,3.713,3923,3.187,4119,3.434,4127,3.363,4128,4.217,4129,4.002,4130,4.002,4131,3.606,4132,4.002,4133,4.002,4134,3.841,4135,3.713]],["t/977",[7,0.953,15,3.577,21,1.209,24,1.47,25,1.37,27,1.016,28,1.139,34,1.09,40,2.254,46,4.748,47,4.167,49,0.962,56,0.652,84,1.063,85,3.744,92,3.839,93,1.813,96,2.843,97,4.833,105,2.067,106,2.958,129,2.556,135,2.439,138,1.726,140,1.677,158,3.302,164,1.175,179,2.211,196,1.696,272,1.752,278,1.533,283,1.237,296,1.649,304,0.902,313,1.045,318,1.042,377,0.957,393,2.101,395,2.611,437,3.788,438,5.381,440,1.887,441,2.327,471,1.773,491,1.29,530,1.913,557,3.288,591,2.439,597,2.21,605,1.548,614,0.788,723,1.529,730,3.484,734,1.893,744,2.439,747,3.314,757,1.054,813,5.227,820,2.199,837,4.005,838,2.158,888,2.254,1035,2.556,1206,2.278,1253,2.556,1405,4.279,1409,4.36,1597,2.699,1688,2.912,1992,6.1,1997,6.018,1998,2.654,2091,3.288,2092,3.094,2093,2.912,2094,3.094,2116,3.484,2251,3.288,2759,3.484,3556,5.327,4136,4.06,4137,6.901,4138,7.659,4139,3.658,4140,4.06,4141,4.06]],["t/979",[4,1.541,7,0.697,16,0.924,17,1.963,21,1.403,29,1.733,49,1.663,56,1.085,84,0.801,85,1.721,92,4.424,93,3.135,97,3.308,108,2.565,160,1.741,164,1.364,196,1.969,203,1.189,217,2.831,228,1.573,275,1.537,283,1.436,304,1.559,365,2.517,372,1.858,401,1.898,434,4.932,438,2.578,482,2.221,500,2.35,527,1.086,579,3.592,580,2.569,591,2.831,596,2.237,613,2.245,614,1.628,680,3.885,713,4.139,714,8.468,751,2.868,774,2.687,780,2.644,796,2.604,830,2.27,846,1.525,963,3.011,966,2.672,1087,7.528,1175,2.287,1206,2.644,1230,3.754,1290,4.358,1297,2.408,1601,2.967,1705,3.034,1733,2.967,1913,2.797,2013,2.464,2499,6.326,2584,5.214,2800,4.524,3096,3.961,3397,3.885,3556,3.278,3620,4.045,3662,8.053,3664,8.053,3877,3.592,3923,3.754,4131,4.247,4142,4.714,4143,4.714,4144,4.714,4145,9.297,4146,4.714,4147,4.714,4148,4.714,4149,4.524,4150,4.714,4151,4.714]],["t/981",[7,0.784,16,1.408,25,2.882,49,1.7,84,0.975,92,4.112,100,3.383,108,3.907,112,3.748,164,1.406,165,1.695,198,4.382,213,2.083,246,4.699,292,2.226,348,3.285,377,1.145,388,3.701,397,2.128,438,3.926,440,2.257,441,4.115,443,8.312,544,3.201,562,2.514,614,1.657,651,3.412,713,4.265,714,5.268,859,2.656,966,2.454,989,2.882,1087,3.933,1132,2.273,1156,4.506,1297,2.482,1396,8.285,1402,2.393,1633,2.412,1687,4.265,1913,2.882,2076,2.975,2251,3.933,2404,3.523,2459,5.918,3160,7.695,3161,4.265,3162,4.265,3163,9.056,3164,6.891,3165,6.891,3166,6.891,3167,6.305,3168,4.662,3169,4.662,3170,4.662,3171,4.662,3172,4.662,3173,4.662,3174,4.662,3175,4.662,3176,4.662,3177,4.662,3556,3.378,4137,4.377,4152,4.857,4153,4.857]],["t/983",[2,1.29,7,0.854,8,2.574,15,2.623,16,0.89,17,1.891,21,1.352,29,1.669,34,1.219,49,1.617,50,2.799,56,1.142,84,1.117,85,2.492,90,2.255,92,4.688,93,3.048,94,5.859,96,2.534,97,3.866,102,3.018,106,2.637,108,2.471,109,0.807,116,2.685,125,2.727,131,2.171,164,1.314,171,1.3,174,1.422,181,2.711,186,2.255,196,1.896,275,1.481,283,2.08,313,2.112,316,2.132,323,1.93,377,1.07,390,2.31,395,1.891,398,1.685,407,2.163,438,3.734,491,1.442,526,3.46,562,2.35,568,1.37,593,3.549,597,2.471,611,1.989,694,2.602,729,3.415,892,1.79,963,2.9,1111,3.331,1157,5.437,1161,3.815,1162,4.003,1178,2.762,1264,3.127,1297,2.32,1531,3.189,1597,3.018,1601,2.858,1633,2.255,1941,3.896,1998,4.463,2013,2.396,2130,5.859,2464,3.743,3006,3.815,3096,3.815,3161,3.987,3162,3.987,3329,3.896,3810,4.5,3877,3.46,4154,4.541,4155,4.541,4156,4.541,4157,4.541,4158,4.541,4159,4.541,4160,4.091,4161,4.541]],["t/985",[7,0.776,15,1.827,21,1.415,27,1.189,33,2.856,34,1.276,46,4.566,56,1.183,84,0.963,85,1.735,90,2.361,92,4.225,94,4.027,96,3.13,98,1.76,101,2.666,113,1.827,133,2.143,140,1.963,164,2.045,179,2.973,203,1.199,208,4.563,238,2.874,240,2.74,254,1.611,275,2.305,278,2.668,351,1.813,353,2.03,372,1.874,377,1.12,407,2.264,437,2.351,440,3.918,476,2.161,482,2.24,491,2.244,562,2.46,566,3.488,583,3.06,597,3.845,613,3.366,614,0.922,715,6.264,828,2.089,888,3.923,966,2.03,995,2.787,1108,2.562,1157,3.786,1162,2.787,1227,4.563,1633,2.361,2118,6.782,2128,3.448,2388,3.903,2840,4.41,3006,3.995,3660,8.094,3804,3.488,3810,3.133,3858,4.612,3877,3.622,4160,4.283,4162,7.066,4163,4.754,4164,7.066,4165,4.754,4166,4.754,4167,4.754]],["t/987",[7,0.806,15,3.73,16,1.659,20,4.027,36,2.839,49,1.447,84,0.966,92,4.641,93,3.778,94,3.483,97,2.879,98,2.263,157,3.718,164,2.45,174,2.65,177,4.305,230,5.366,236,2.345,275,2.76,278,3.195,302,2.965,315,3.058,359,2.839,407,2.91,430,3.176,440,2.839,578,2.53,621,3.309,684,3.358,712,3.464,795,4.209,816,3.648,908,3.278,971,4.721,1080,3.583,1188,4.096,1205,5.505,1218,6.686,1364,3.218,1365,3.718,1368,8.044,1374,6.005,1931,4.656,1941,5.243,1993,5.505,2588,4.209,4168,6.11]],["t/989",[7,0.796,8,2.16,15,2.826,17,3.467,18,3.406,21,1.135,24,0.65,26,2.878,28,0.567,34,1.974,46,1.664,48,1.629,56,0.765,84,0.681,90,1.892,92,2.328,93,4.473,94,3.401,96,1.414,97,3.922,103,1.343,132,1.847,165,0.899,172,1.391,181,2.275,183,1.723,185,4.328,186,1.892,201,2.044,207,1.639,213,1.634,225,1.435,228,1.992,236,2.306,244,3.871,278,1.439,283,1.161,291,2.365,292,1.746,298,2.452,306,1.783,313,0.981,315,1.907,323,1.619,338,0.988,353,1.095,369,2.491,375,2.333,377,0.898,381,2.975,393,1.972,395,1.587,440,2.773,444,1.741,463,2.416,476,1.166,505,1.664,509,2.275,590,2.16,597,2.074,611,1.669,620,7.05,676,3.535,677,2.115,680,3.141,691,2.208,693,2.471,694,3.42,696,2.829,719,2.416,740,1.815,790,2.796,794,4.378,802,1.624,892,1.502,901,3.807,910,2.261,977,1.464,1099,1.487,1132,1.783,1162,3.499,1163,2.704,1175,1.849,1188,4,1217,3.035,1287,2.944,1364,2.007,1374,4.235,1387,4.753,1389,3.27,1390,5.335,1391,7.06,1392,8.675,1393,5.728,1428,3.346,2013,1.337,2035,2.676,2217,3.085,2565,3.346,2609,3.27,3075,3.535,3159,3.346,3435,3.657,4169,3.81,4170,3.81,4171,3.81,4172,3.81,4173,3.433,4174,5.377,4175,3.81,4176,3.433,4177,3.81]],["t/991",[2,1.718,8,3.426,17,2.517,24,1.87,28,0.899,48,3.59,56,0.874,68,2.415,92,3.277,93,4.309,94,3.445,97,2.848,132,1.871,165,1.427,179,2.131,186,3.001,199,3.141,203,2.118,207,2.599,236,1.675,283,1.842,304,2.519,338,2.177,527,1.392,614,1.172,620,6.612,966,3.149,1087,4.894,1162,3.544,1188,4.051,1297,4.291,1387,4.813,1390,6.091,1402,4.138,1601,3.805,1689,3.338,1693,3.726,2247,4.488,2335,4.813,3159,5.307,3858,3.305,3894,5.607,4173,7.567,4174,7.567,4176,5.446,4178,6.044,4179,6.044,4180,6.044,4181,6.044]],["t/993",[2,1.393,7,0.908,16,2.073,21,1.459,24,1.465,27,1.226,28,0.729,41,2.343,47,3.257,56,0.986,85,3.459,90,3.588,93,3.226,97,3.405,105,1.615,109,0.871,123,1.76,124,3.257,170,1.526,172,1.789,173,2.763,174,1.535,183,2.216,200,2.558,244,3.178,256,1.932,275,1.598,283,1.493,291,3.042,295,2.076,313,1.262,323,2.083,353,1.408,408,2.749,437,4.246,438,2.68,447,2.443,471,2.14,491,2.295,502,1.424,525,2.433,580,1.793,597,4.672,599,3.914,614,0.951,615,3.021,619,7.747,627,4.415,747,2.12,901,3.74,945,1.708,989,2.907,1090,2.293,1099,1.912,1235,3.786,2214,3.344,2491,4.118,3556,5.026,3590,4.415,4139,4.415,4182,4.9,4183,10.104,4184,4.9,4185,9.374,4186,4.9,4187,4.9,4188,4.9,4189,4.9,4190,4.9,4191,4.9,4192,4.9,4193,4.9,4194,4.9,4195,7.226]],["t/995",[7,0.84,12,3.399,24,1.726,84,0.913,109,1.421,201,4.29,304,2.589,619,7.502,736,3.594,780,5.673,966,2.906,1413,4.717,1689,5.587,2015,4.964,3093,6.72,3143,6.592,3601,6.413,3640,7.676,3641,7.676,4196,7.997,4197,7.997,4198,7.997,4199,7.997]],["t/997",[1781,3.73]],["t/999",[2,1.827,7,0.96,15,2.47,16,1.26,23,2.261,24,1.495,29,2.363,46,2.807,49,1.522,56,0.669,84,0.734,108,3.498,138,2.732,145,2.724,162,2.38,165,1.517,213,2.756,228,2.146,248,3.179,256,2.534,281,3.204,313,2.256,317,3.386,377,1.515,440,2.987,441,5.021,458,3.814,481,4.046,534,4.834,536,3.359,546,5.644,581,4.898,605,2.451,613,3.062,742,4.236,744,5.263,747,2.781,798,3.725,837,4.106,840,3.073,1090,4.099,1121,4.106,1172,4.076,1268,4.773,1337,6.154,2026,4.716,2327,5.516,2434,4.611,2958,5.791,3933,5.963,4056,5.119,4200,6.428,4201,6.428,4202,6.428]],["t/1001",[7,0.572,16,1.621,24,1.176,26,1.798,56,1.289,84,0.525,114,3.863,123,2.475,232,1.762,304,1.021,306,2.151,342,4.746,405,2.31,449,4.469,513,2.283,545,2.239,552,1.715,623,2.32,780,2.578,822,2.894,989,4.905,1597,4.58,1789,3.944,1889,3.604,2025,6.393,2026,6.065,2027,5.913,2029,3.944,2030,9.45,2031,5.178,2032,5.913,2035,6.45,2037,4.036,2038,4.036,2039,4.036,2040,4.036,2041,3.262,2042,5.913,2099,2.915,2448,3.03,2672,3.503,3441,6.051,3522,4.036,4110,5.488,4111,4.142,4203,4.142,4204,5.79,4205,4.142,4206,6.209,4207,4.597,4208,6.891,4209,4.597,4210,4.597,4211,4.597,4212,4.597,4213,4.597,4214,4.597,4215,4.597,4216,4.597,4217,4.597,4218,4.597,4219,4.597,4220,4.597,4221,4.597,4222,4.597,4223,4.597,4224,4.597,4225,4.597,4226,4.597,4227,4.036,4228,4.597,4229,4.597,4230,4.597,4231,4.597,4232,6.209,4233,6.209,4234,4.597,4235,4.412,4236,4.264,4237,4.597,4238,6.891,4239,4.597]],["t/1003",[7,0.846,16,1.839,24,0.978,26,2.243,56,1.27,84,0.654,100,3.813,114,4.817,123,2.906,232,2.197,248,2.836,304,1.273,342,5.574,449,5.249,545,2.792,744,3.444,790,5.938,1268,4.257,1597,5.379,1633,2.847,1789,6.944,2026,5.938,2027,6.944,2029,4.919,2030,10.046,2031,5.746,2032,6.944,2035,7.158,2037,5.034,2038,5.034,2039,5.034,2040,5.034,2041,4.068,2042,6.944,4110,6.445,4203,5.165,4204,6.8,4205,5.165,4206,7.292,4232,7.292,4233,7.292,4240,5.733,4241,5.733,4242,5.733,4243,5.733]],["t/1005",[56,1.014,164,2.822,550,4.036,614,1.891,744,5.855,814,4.423,1704,8.56,2020,10.952]],["t/1007",[1781,3.73]],["t/1009",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,1019,4.182,1292,8.588,2013,3.121,2598,5.767]],["t/1011",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,977,2.966,1035,4.86,1112,4.825,1531,5.423,2013,2.71,2362,5.538,3642,7.162,4244,10.474,4245,7.41,4246,7.41,4247,7.41,4248,9.492]],["t/1013",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,1272,8.832,1282,9.359,1290,5.609,1292,7.741,1480,7.196,2298,7.754,2598,5.861]],["t/1015",[1781,3.73]],["t/1017",[28,1.377,109,1.967,139,4.38,170,2.883,183,4.189,395,3.857,484,4.135,760,2.883,828,4.07,1132,4.333,1558,5.495,2231,6.441,3616,6.007,3917,6.505]],["t/1019",[4,2.243,7,0.57,16,1.346,18,3.178,19,3.223,24,1.171,28,1.817,84,1.045,98,2.542,103,2.42,107,2.471,109,1.954,165,2.162,170,2.851,196,2.867,221,2.671,286,4.384,289,2.858,397,3.007,444,3.135,527,1.581,550,2.842,565,2.361,568,2.071,757,1.782,760,2.137,933,4.097,945,4.108,966,1.972,977,2.637,1108,3.7,1206,3.85,1309,4.203,1558,5.434,2081,5.162,2253,4.384,3609,7.233]],["t/1021",[4,3.272,28,1.488,107,2.701,109,1.779,505,4.372,966,2.877]],["t/1023",[12,4.179,24,1.678,109,1.747,254,3.332,512,3.573,578,4.072,614,1.908,1754,4.404]],["t/1025",[7,0.782,12,4.003,24,1.607,109,1.674,128,4.733,254,3.191,578,3.9,614,1.827,681,6.207,736,4.232,966,2.706,1425,5.768,1616,5.522]],["t/1027",[24,1.691,28,1.584,31,2.238,34,2.333,36,2.953,39,2.725,49,1.505,84,0.725,109,1.545,116,2.499,132,1.967,133,2.865,139,3.005,164,2.868,165,1.5,170,3.474,203,2.193,285,2.766,289,2.646,338,1.647,353,1.826,367,3.409,382,4.154,394,3.347,397,2.784,414,3.841,505,2.775,525,3.155,614,1.233,693,4.121,756,3.143,759,3.211,760,3.085,769,4.314,830,3.06,908,3.409,1206,3.564,1309,3.891,1329,4.808,1506,4.297,1558,3.77,1671,3.683,2231,4.419,2311,4.51,2960,4.188,3918,4.259,3922,5.725]],["t/1029",[7,0.711,21,1.85,29,2.284,31,2.849,39,2.664,49,1.471,125,3.732,139,4.631,165,1.467,170,3.774,197,2.917,203,2.662,213,3.67,236,1.721,246,4.205,272,2.68,292,2.848,304,1.38,323,2.64,338,1.61,414,3.756,491,1.973,605,2.369,614,1.205,734,2.897,759,3.162,760,3.285,769,4.909,811,4.062,867,5.031,908,3.333,1080,3.642,1083,3.174,2231,6.81,2887,3.83,3616,5.552,3918,4.164]],["t/1031",[7,0.893,28,1.312,139,4.171,145,3.738,170,2.746,203,2.225,246,4.283,285,3.84,304,1.959,614,1.711,759,3.971,828,3.876,3923,7.025,3924,9.224,3925,6.722,3926,7.569,3927,7.569]],["t/1033",[7,0.893,28,1.312,139,4.171,145,3.738,170,2.746,203,2.225,246,4.283,285,3.84,304,1.959,614,1.711,759,3.971,828,3.876,3923,7.025,3925,6.722,3928,9.224,3929,7.569,3930,7.569]],["t/1035",[7,0.596,28,1.402,49,1.699,56,0.746,84,1.202,103,2.529,109,1.676,164,2.077,170,2.233,217,4.309,279,3.266,377,1.691,398,3.5,565,2.468,760,3.28,811,4.689,945,2.501,1367,4.767,1530,4.548,1550,5.624,1558,5.595,1793,4.767,2076,4.393,2142,5.523,2227,5.263,2235,5.808,2722,4.652,3839,9.825,3911,8.496,3954,6.655,4249,12.705,4250,7.559,4251,7.173]],["t/1037",[1781,3.73]],["t/1039",[16,1.981,34,2.711,107,2.725,318,2.592,1062,5.118]],["t/1041",[2,1.45,7,0.424,16,1,24,1.5,28,0.759,34,1.369,41,2.439,49,1.763,56,1.005,84,1.003,103,2.625,107,2.771,109,1.968,110,6.147,113,2.86,127,1.763,132,2.304,140,2.107,164,1.477,165,1.204,171,2.131,179,1.799,193,1.956,203,2.217,228,1.703,272,2.201,304,2.145,313,1.314,343,1.912,377,1.754,408,1.941,527,2.615,614,1.705,658,2.575,846,3.125,859,2.79,892,2.012,945,3.064,966,1.466,977,3.947,982,2.712,1737,2.028,1779,3.621,1988,3.281,2010,3.767,2191,3.548,2274,3.888,2275,6.536,2276,3.084,2990,3.284,3595,1.981,3858,2.79,4252,7.444,4253,5.102]],["t/1043",[5,1.868,7,0.849,12,2.139,17,2.096,21,1.499,24,1.881,27,1.259,28,0.748,31,1.296,41,2.406,46,3.808,56,1.111,98,2.729,109,1.549,110,5.671,116,1.979,131,2.406,133,2.269,158,2.651,170,1.567,171,1.441,174,2.731,179,2.599,186,3.66,196,2.102,197,2.363,254,1.705,272,2.171,275,2.404,282,4.224,289,2.096,304,1.118,322,2.874,353,1.446,367,3.954,377,1.186,386,5.779,388,3.835,408,1.915,440,2.339,482,2.372,505,3.218,525,2.499,527,2.353,528,2.794,568,1.519,590,2.853,597,2.739,599,2.726,611,2.205,614,0.976,631,5.076,719,3.191,755,3.498,807,2.05,816,3.005,846,1.628,977,1.934,1080,2.951,1099,3.402,1253,3.168,1284,3.61,1318,4.42,1402,2.48,1633,2.499,1737,2,2010,1.846,2274,3.835,2275,4.42,2606,4.149,3595,1.954,4254,7.37]],["t/1045",[1781,3.73]],["t/1047",[7,0.705,15,3.26,34,2.277,56,1.092,228,2.832,322,2.797,377,2,398,3.149,401,3.418,465,6.226,539,5.738,543,5.035,607,3.427,683,3.345,747,3.671,820,4.595,1535,6.871,1536,6.556,1555,7.872,1563,7.281,1607,5.844,2013,2.978,3556,5.901,4255,7.872,4256,7.872]],["t/1049",[7,0.618,16,1.458,24,0.661,25,1.308,28,0.899,34,1.04,49,1.432,56,0.629,84,0.849,103,1.367,145,3.152,162,1.435,171,1.11,175,2.234,203,1.526,219,1.973,236,1.676,256,2.385,275,1.264,281,1.933,283,1.843,304,1.343,313,1.557,315,1.94,318,0.995,322,1.994,334,2.314,338,2.178,365,2.07,368,3.087,372,1.528,395,2.519,398,1.439,401,2.436,438,2.12,458,2.3,482,1.827,530,2.85,531,2.476,539,6.82,542,5.311,543,4.986,552,1.446,564,2.645,567,2.556,605,1.478,613,1.846,614,1.956,630,1.509,708,4.986,747,1.677,757,1.007,762,2.751,838,2.06,846,1.254,859,2.12,911,3.139,966,2.774,977,2.858,982,2.06,1013,2.555,1019,2.342,1070,2.329,1094,3.186,1141,2.915,1145,2.08,1219,1.82,1241,3.493,1297,3.8,1481,2.186,1531,2.723,1540,2.26,1550,3.04,1570,3.404,1638,2.11,1732,3.04,1737,1.541,1754,1.736,2013,1.361,2218,2.954,2577,2.152,2871,3.258,2887,3.729,3035,7.571,3036,3.493,3441,5.311,3595,3.537,3623,3.721,3719,3.493,3877,2.954,4056,4.817,4257,6.049,4258,6.049,4259,3.877,4260,6.049,4261,3.877,4262,6.049,4263,3.877,4264,6.049,4265,6.049,4266,3.721,4267,6.049,4268,3.877,4269,3.877,4270,6.049,4271,3.877,4272,3.877,4273,3.877,4274,3.877,4275,3.877]],["t/1051",[4,2.472,16,2.239,49,1.791,171,2.165,236,2.994,318,2.93,398,3.623,401,4.353,539,7.722,543,5.791,567,4.824,892,2.982,966,2.805,1094,3.983,1559,8.75,1638,5.312,3595,2.936,4276,7.563,4277,7.563,4278,7.563]],["t/1053",[4,2.391,7,0.608,16,2.085,49,1.732,85,2.67,171,2.094,236,2.947,398,3.545,401,4.283,539,7.624,543,5.667,552,3.966,567,4.763,892,2.884,966,2.744,1019,2.832,1094,3.853,1266,3.341,1559,8.611,1638,5.198,1732,7.489,3595,2.839,4279,7.314,4280,7.314,4281,7.314,4282,7.314]],["t/1055",[1781,3.73]],["t/1057",[7,0.796,24,1.635,28,1.424,219,3.684,502,2.784,536,3.006,550,3.967,565,3.296,568,2.891,1307,7.029]],["t/1059",[7,0.637,28,1.464,34,2.642,49,2.332,163,4.112,188,5.842,219,2.501,221,2.984,295,4.172,353,2.203,377,1.807,476,2.345,484,4.396,500,3.822,502,3.16,512,3.577,536,2.406,555,3.822,577,3.822,623,3.869,653,2.543,757,2.824,780,4.301,843,4.419,1019,2.969,2017,3.401,2128,5.561,2624,6.719]],["t/1061",[4,1.893,7,0.784,25,1.954,56,1.065,101,3.249,123,2.08,132,1.793,165,1.367,196,2.419,203,1.461,219,3.339,236,1.605,285,2.522,313,1.491,317,3.051,338,1.501,348,3.917,359,2.691,372,3.214,377,1.365,449,3.757,512,2.104,527,1.878,536,3.213,565,3.245,568,2.46,605,2.209,613,2.759,630,2.254,653,1.921,736,2.603,757,2.658,843,4.698,888,3.216,945,2.02,988,2.985,1035,3.646,1333,4.775,1456,3.216,1481,3.266,1700,6.392,2008,5.086,2009,4.069,2013,2.033,2017,4.541,2031,6.084,2338,3.357,2362,4.155,2388,3.199,2624,3.953,2880,7.345,2990,3.728,3804,4.25,4283,5.792,4284,8.152,4285,5.792,4286,5.792]],["t/1063",[28,1.451,34,2.03,49,1.791,84,0.863,96,2.807,165,1.785,190,4.096,219,2.467,232,2.899,295,3.205,304,1.679,398,4.011,461,4.46,536,3.709,552,2.821,567,4.124,605,2.884,614,1.467,731,4.177,757,2.534,966,2.173,1266,3.455,1394,5.07,2017,4.329,3059,7.495,3858,4.136,4287,7.563,4288,7.563]],["t/1065",[1,0.992,7,0.762,12,0.689,16,1.2,17,0.675,23,1.02,24,1.89,25,1.327,27,0.405,28,1.526,34,1.056,39,0.695,41,1.386,48,0.693,49,0.687,56,0.852,68,0.647,84,0.934,88,0.769,89,1.883,98,0.6,103,1.688,105,0.956,107,1.061,109,1.087,116,1.14,123,0.582,128,1.457,132,2.621,133,0.73,160,0.599,162,1.073,163,1.643,164,0.839,165,0.929,170,0.903,179,1.022,182,2.343,183,1.311,196,0.677,198,0.831,199,0.842,203,1.676,219,1.283,220,1.88,221,1.128,223,0.961,232,1.835,236,1.327,246,1.155,255,1.15,275,0.528,277,0.878,278,0.612,283,0.494,285,2.397,286,1.852,289,1.207,295,0.687,296,0.658,302,0.786,323,0.689,324,0.904,328,2.016,337,0.795,338,1.02,343,0.607,347,1.127,351,0.618,353,0.833,372,1.551,377,1.566,392,1.059,398,0.601,401,0.653,407,0.772,409,1.548,416,0.786,426,1.864,430,0.842,444,0.74,463,1.027,472,1.487,476,2.406,482,0.763,483,1.077,484,1.294,512,2.581,525,0.805,527,1.73,528,0.899,530,0.763,536,2.23,545,0.789,555,0.808,560,0.909,565,2.706,568,1.662,575,2.397,578,0.671,580,0.593,601,1.434,605,1.5,611,1.27,614,1.588,621,0.878,630,2.143,635,0.619,647,0.64,653,1.827,681,1.911,685,0.95,723,1.092,736,1.768,743,1.127,756,0.801,757,2.327,759,2.26,760,1.225,769,1.593,780,0.909,803,1.72,811,1.059,832,1.15,840,1.881,843,0.934,845,1.086,846,1.273,850,1.138,857,2.633,859,0.886,892,2.171,898,2.242,901,0.838,917,1.731,938,1.051,939,0.973,941,1.361,945,1.67,966,2.575,984,2.482,994,0.735,1019,0.627,1072,0.789,1076,2.984,1083,0.828,1090,1.356,1098,1.077,1100,0.677,1118,3.988,1121,1.035,1132,1.841,1192,2.572,1224,1.122,1246,1.943,1268,2.153,1285,0.728,1402,0.798,1413,1.71,1425,1.775,1481,0.914,1530,1.027,1616,1.7,1633,0.805,1671,0.939,1733,1.02,1781,0.576,1849,0.929,1850,1.503,1913,0.961,1959,0.814,1988,1.278,2000,1.068,2002,3.306,2009,3.364,2017,0.719,2073,1.02,2089,1.138,2145,1.46,2176,1.423,2254,0.882,2338,0.939,2727,1.336,2898,1.15,3600,2.488,3601,1.027,3616,1.051,3771,3.186,3858,1.585,3883,1.555,3899,1.127,3917,1.138,3918,1.086,3942,1.943,4038,1.46,4056,1.29,4289,1.62,4290,1.203,4291,1.203,4292,1.203,4293,1.203,4294,1.203,4295,1.203,4296,1.203,4297,1.312,4298,1.555,4299,1.62,4300,1.503,4301,1.62,4302,1.62,4303,2.899,4304,1.62,4305,1.62,4306,1.62,4307,1.555,4308,1.62,4309,1.62,4310,1.62,4311,1.62,4312,1.62,4313,1.62,4314,1.62,4315,1.62,4316,1.62,4317,1.62,4318,1.62]],["t/1067",[7,0.703,23,1.383,24,1.878,27,0.983,28,1.364,31,1.012,39,1.686,48,1.681,49,0.931,56,0.881,84,0.857,88,1.866,89,3.329,98,1.456,103,1.386,116,2.405,128,1.976,132,1.217,163,2.554,171,1.126,177,2,182,2.156,198,2.017,219,1.282,221,1.53,225,1.481,232,2.344,285,1.712,286,2.511,302,1.907,304,0.873,313,1.012,322,1.296,328,2.734,343,1.473,377,0.927,401,1.583,444,1.796,472,2.017,502,1.142,512,3.528,527,1.409,545,1.915,555,1.96,565,2.582,575,3.061,614,1.779,630,2.38,653,2.808,670,2.408,681,2.591,736,1.766,756,1.944,757,1.949,769,3.429,780,2.205,807,1.601,840,1.879,846,1.272,898,4.277,908,2.109,945,2.132,966,2.791,984,1.772,1072,1.915,1076,3.213,1094,2.071,1118,4.082,1132,3.511,1224,2.368,1329,4.45,1425,2.408,1557,2.57,1616,2.305,1633,1.952,1671,2.278,1754,1.761,1765,2.376,1849,3.505,2198,2.996,2254,2.139,2311,2.79,2588,2.708,3601,2.493,3616,3.966,3645,5.37,3917,2.761,3918,2.635,4290,2.919,4291,2.919,4292,2.919,4293,2.919,4294,2.919,4295,2.919,4296,2.919,4297,3.183,4300,3.647,4319,5.51,4320,3.542,4321,3.038,4322,3.647,4323,2.956]],["t/1069",[7,0.311,23,2.072,24,1.53,28,1.415,34,1.005,39,1.606,48,1.602,49,0.887,56,0.757,63,3.033,84,0.83,88,1.778,89,3.243,98,1.387,103,1.321,116,2.316,163,3.039,171,1.072,177,2.996,182,2.076,198,1.922,203,1.485,219,1.222,221,1.458,228,1.25,232,2.257,283,1.141,285,1.631,286,2.393,295,1.587,304,0.832,322,1.235,328,2.605,338,0.971,343,1.404,377,1.388,401,1.509,405,1.256,426,1.458,444,1.711,472,1.922,476,2.911,491,1.19,502,1.711,512,3.617,527,1.357,536,1.848,545,2.868,558,1.765,565,2.026,575,3.642,577,5.141,614,1.739,630,2.292,653,2.735,683,1.477,736,1.683,756,1.853,757,1.889,780,2.101,796,2.069,846,1.905,892,2.322,898,4.147,916,3.313,945,2.053,966,3.048,984,3.28,1072,1.825,1076,3.402,1118,4.235,1224,2.28,1325,2.895,1402,1.846,1616,2.196,1633,1.86,1754,1.678,1849,3.375,2251,3.033,2254,2.039,2588,2.58,2727,3.088,3601,2.375,3646,5.171,3942,3.947,4290,2.782,4291,2.782,4292,2.782,4293,2.782,4294,2.782,4295,2.782,4296,2.782,4300,3.475,4321,2.895,4323,2.817,4324,5.306,4325,3.746,4326,3.746,4327,3.746,4328,3.746]],["t/1071",[4,0.709,7,0.648,21,0.646,23,0.763,24,1.242,25,1.669,28,1.415,31,0.559,34,1.006,36,1.741,39,0.93,48,0.928,49,1.395,56,1.036,68,0.867,84,0.891,88,1.03,89,2.317,98,1.388,103,0.765,107,2.824,109,1.86,116,1.946,128,1.884,132,0.671,133,0.978,134,1.138,140,0.896,162,0.803,163,2.46,165,0.885,170,2.071,171,1.904,182,1.321,183,0.981,198,1.113,203,0.547,219,0.708,221,0.844,232,1.436,236,1.038,246,1.972,285,0.945,286,1.386,295,1.588,296,0.881,304,0.482,322,1.631,328,1.509,336,1.607,338,1.527,343,0.813,377,0.883,382,1.418,391,0.962,401,0.874,405,0.727,408,1.426,426,0.844,444,0.991,472,1.113,476,2.226,491,0.689,505,0.947,512,2.997,527,1.677,536,3.06,545,1.057,557,1.138,565,2.027,575,2.477,580,1.81,605,0.827,614,1.675,623,1.095,630,1.459,647,1.956,653,2.414,659,1.48,663,1.396,681,2.47,723,1.412,724,1.113,725,2.011,736,1.684,756,1.073,757,1.727,760,0.675,780,1.217,820,1.175,827,1.158,828,0.953,840,1.792,846,1.212,892,1.951,898,2.82,944,0.925,945,1.725,966,2.577,984,0.978,993,1.598,994,0.984,1072,1.057,1076,2.961,1093,2.237,1099,1.462,1118,4.236,1132,2.315,1219,1.019,1224,1.916,1285,0.975,1365,1.32,1402,1.069,1425,2.295,1540,1.265,1558,2.936,1616,2.197,1633,1.077,1754,0.972,1849,2.148,1913,1.287,1931,1.653,2006,5.055,2010,2.669,2012,4.552,2015,1.347,2254,1.181,2256,5.917,2257,1.592,2588,1.494,2900,2.66,2928,2.081,3595,0.842,3601,3.137,3647,5.173,3717,3.95,3763,1.454,3830,1.632,3917,1.524,3942,2.512,3943,5.386,3944,1.757,4290,1.611,4291,1.611,4292,1.611,4293,1.611,4294,1.611,4295,1.611,4296,1.611,4300,2.013,4321,1.676,4323,1.632,4329,4.948,4330,1.862,4331,3.216]],["t/1073",[7,0.549,21,0.769,23,0.908,24,0.961,25,0.871,27,0.646,28,1.381,34,0.693,36,1.2,39,1.107,41,2.079,48,1.104,49,1.03,56,1.027,84,0.496,88,1.225,89,2.598,92,1.007,98,0.956,103,0.91,105,0.851,107,0.697,109,1.867,116,1.71,127,3.908,128,1.298,132,1.346,133,1.164,140,1.066,162,0.956,163,3.08,164,0.747,165,1.026,171,1.892,172,0.943,177,2.212,179,0.91,182,1.533,183,1.967,193,1.666,198,1.325,203,1.097,219,0.842,221,1.005,232,1.666,256,1.018,257,1.385,275,0.842,279,1.176,285,1.124,286,1.649,295,1.094,304,0.573,322,2.634,328,1.796,336,1.864,338,1.127,343,0.968,351,0.985,359,1.2,377,1.327,391,1.929,394,2.29,401,1.04,405,2.215,426,1.692,444,1.18,472,1.325,476,2.842,502,0.75,512,3.088,527,1.002,536,2.668,545,1.258,565,2.537,575,2.819,605,0.985,614,1.863,615,1.592,623,1.303,630,1.692,653,2.192,681,1.702,732,1.778,736,1.16,755,1.225,756,1.277,757,2.075,780,1.448,840,2.692,843,1.488,846,1.407,898,3.21,945,1.516,966,2.669,974,1.662,984,1.164,993,1.101,1072,1.258,1076,2.798,1118,3.556,1144,1.48,1224,1.683,1284,1.852,1402,1.272,1425,1.581,1616,1.514,1633,1.282,1754,1.947,1765,1.561,1849,2.492,1991,1.814,2004,5.802,2010,3.407,2011,5.181,2190,1.523,2254,1.405,2588,1.778,3522,2.267,3595,1.002,3601,3.571,3648,5.802,3763,1.731,3942,2.915,4290,1.917,4291,1.917,4292,1.917,4293,1.917,4294,1.917,4295,1.917,4296,1.917,4300,2.395,4321,1.995,4323,1.942,4330,2.216,4331,3.731,4332,5.225,4333,2.395,4334,2.582,4335,2.17]],["t/1075",[7,0.588,27,1.771,28,1.39,56,1.158,84,0.808,89,2.785,196,2.958,405,4.125,426,4.333,495,4.757,511,3.74,512,2.573,565,2.437,568,2.822,583,4.558,647,2.8,729,5.326,757,1.839,892,3.687,984,3.193,1076,2.687,1083,3.618,1100,2.958,1329,3.436,1336,4.878,1355,4.63,1693,4.366,2672,7.125,3671,5.735,3741,5.326,4336,8.042]],["t/1077",[5,3.582,7,0.698,23,2.126,28,1.249,56,1.004,84,0.69,89,3.795,106,3.243,116,2.377,164,2.431,178,4.982,196,2.524,228,2.803,256,3.311,289,2.517,295,2.561,407,2.879,408,2.299,426,3.268,433,3.677,495,4.91,511,3.242,512,2.196,565,2.079,568,2.534,615,3.726,647,3.32,756,2.989,757,1.569,769,2.448,807,4.968,892,4.321,966,1.737,984,2.725,1100,2.524,1329,2.933,1355,3.951,1693,3.726,1737,2.402,2214,4.124,2487,8.696,3005,5.186,3671,4.894,3741,4.545,4337,5.607]],["t/1079",[7,0.807,16,0.876,24,1.151,28,1.003,56,1.257,84,0.77,88,2.12,96,1.658,107,1.205,109,0.794,127,2.331,140,1.845,162,1.654,164,1.293,165,1.592,171,1.279,177,3.432,179,2.378,219,3.169,277,2.42,296,1.814,322,2.678,377,1.053,392,2.921,491,2.142,502,1.96,536,3.631,550,1.85,565,2.321,567,4.812,577,2.227,653,1.482,672,4.187,734,2.083,798,2.589,818,3.569,846,1.445,897,4.483,918,3.021,1019,2.611,1076,2.559,1224,1.73,1309,2.736,1329,2.168,1367,2.969,1398,3.24,2002,3.754,2004,3.923,2006,3.834,2010,2.98,2011,3.503,2012,3.452,2013,3.41,2017,2.992,2035,3.138,2092,3.404,3059,2.833,3600,3.834,3601,6.16,3645,3.923,3646,3.923,3647,3.923,3648,3.923,3679,3.923,4319,4.025,4324,4.025,4338,5.668,4339,4.145,4340,4.468,4341,9.014]],["t/1081",[24,1.651,36,3.467,56,1.117,68,2.981,84,1.226,113,3.718,203,1.882,304,2.384,398,2.77,401,3.005,482,3.516,536,2.342,543,4.427,565,2.567,567,3.153,577,3.72,578,3.09,614,1.447,622,3.966,756,3.691,892,3.815,938,4.84,966,3.085,1019,2.889,1540,4.349,1607,5.139,1638,4.061,2268,4.482,3607,5.765,4342,7.462,4343,9.675,4344,7.462]],["t/1083",[7,0.852,15,3.141,16,1.603,88,3.879,103,2.882,113,3.141,199,4.248,225,3.079,226,5.239,243,4.711,292,3.746,377,1.926,491,2.596,511,2.746,536,2.565,550,3.385,565,3.528,567,4.332,593,4.248,672,5.073,719,5.182,798,4.737,855,5.301,892,3.223,1007,5.578,1253,5.145,2128,5.928]],["t/1085",[7,0.585,17,3.878,28,1.651,84,0.803,89,2.767,105,2.319,190,3.811,219,3.768,232,2.697,283,2.144,294,3.989,377,1.658,461,4.15,536,3.724,552,2.625,567,5.014,653,3.088,731,3.887,757,2.884,1266,3.215,2017,4.629,3059,6.616,4345,7.037,4346,11.105]],["t/1087",[2,1.225,4,2.146,7,0.795,23,1.517,24,1.12,25,1.455,27,1.642,28,1.182,56,1.049,84,1.233,88,2.046,89,1.696,109,0.766,113,2.523,127,1.49,132,1.335,163,1.801,165,1.55,171,1.234,174,1.351,177,2.194,182,1.52,193,1.653,219,2.142,223,2.558,232,1.653,240,2.485,275,2.142,278,1.628,283,1.314,285,1.877,294,2.444,313,1.69,322,1.421,338,1.118,447,2.15,472,4.08,476,2.719,498,2.528,502,1.253,512,3.475,525,2.141,527,0.993,536,3.163,558,3.094,565,1.484,567,2.774,568,1.301,575,5.044,614,0.836,736,3.573,755,2.046,757,1.705,765,2.695,773,3.554,796,2.382,816,2.574,857,3.801,879,3.623,892,1.7,898,4.532,977,1.657,984,1.944,1076,3.824,1080,2.528,1099,1.682,1101,2.714,1108,2.324,1132,3.072,1163,3.06,1178,2.623,1181,2.658,1224,1.67,1402,2.124,1425,2.641,1616,5.908,1913,2.558,2013,1.514,2254,2.347,2444,3.029,2624,2.942,3614,4.139,3899,2.999,3917,3.029,3942,2.89,4347,4.312,4348,4.312,4349,4.312]],["t/1089",[2,1.484,7,0.74,21,2.908,24,0.891,26,2.043,36,3.517,49,2.455,56,0.543,84,1.183,96,3.305,98,3.297,107,2.402,109,1.345,113,2.909,127,1.805,140,2.156,160,2.796,164,1.512,165,1.233,203,2.246,304,1.16,313,1.344,336,2.239,338,1.354,353,1.501,377,1.231,502,1.517,509,3.117,536,3.893,577,4.439,578,2.162,604,3.632,629,3.5,653,2.954,692,4.388,756,2.583,805,3.746,833,4.547,857,2.496,918,3.531,993,2.226,1019,3.448,1284,3.746,1402,4.812,1779,3.706,2010,3.802,2017,3.358,2257,3.831,2960,5.869,4350,8.905]],["t/1091",[4,2.752,7,0.699,48,3.6,49,1.994,56,1.087,84,0.961,140,3.477,171,2.411,203,2.124,272,3.633,295,3.568,322,2.776,536,2.643,613,4.011,614,2.027,731,5.77,802,3.589,828,4.591,2013,2.956,2431,6.333,3595,3.269,4351,7.856]],["t/1093",[2,1.03,4,1.875,12,1.54,24,1.382,29,1.332,36,1.684,49,1.359,56,1.263,68,1.448,84,0.924,96,1.345,98,2.124,105,1.195,179,1.278,203,1.447,219,1.871,254,1.228,304,1.798,322,1.195,336,1.554,372,2.807,377,1.352,391,1.608,395,1.509,398,2.643,401,1.46,444,1.656,472,1.86,482,1.708,536,3.553,543,2.15,565,1.247,567,3.008,577,1.807,623,1.829,647,2.815,653,1.202,736,1.628,757,0.941,780,4.542,838,1.926,846,3.609,892,2.262,938,2.351,966,2.929,993,1.545,1019,4.32,1037,1.9,1076,3.072,1118,2.553,1224,1.403,1329,1.758,1402,2.827,1540,2.113,1607,2.496,1616,2.125,1638,1.972,1689,2.002,1765,3.468,1849,2.077,1982,2.935,2009,2.546,2013,1.272,2017,2.545,2254,1.972,2727,2.987,2960,2.389,3059,3.638,3601,2.298,3607,2.8,3858,4.825,3906,2.842,3907,3.045,4320,3.265,4351,2.725,4352,5.506,4353,5.737,4354,3.265,4355,3.624,4356,3.265,4357,3.624,4358,3.265,4359,3.624,4360,3.265,4361,3.624,4362,3.265,4363,5.737,4364,3.265,4365,3.624,4366,3.265,4367,3.624,4368,3.479,4369,3.624,4370,3.624,4371,3.624,4372,3.624,4373,5.737,4374,3.624]],["t/1095",[1,4.256,7,0.577,24,1.77,49,1.646,56,1.23,84,1.184,171,1.99,272,2.998,322,3.043,372,2.74,377,2.176,395,2.894,397,4.045,430,3.612,482,3.275,536,3.787,550,2.878,565,2.391,611,3.044,846,2.248,901,3.596,993,2.962,1121,4.439,1192,6.037,1309,4.256,1329,4.48,1402,3.424,2013,3.241,2960,4.58,4351,6.944,4375,9.234,4376,6.949]],["t/1097",[2,1.565,7,0.457,12,2.34,36,2.558,49,1.862,56,1.245,84,1.047,107,2.475,109,1.778,127,3.658,162,2.039,165,1.3,179,1.941,183,2.49,275,1.796,296,2.236,304,1.223,313,1.417,322,3.024,336,3.934,391,4.07,536,3.454,614,1.78,647,2.177,757,1.43,827,2.94,846,2.544,993,3.352,1988,3.467,2010,4.388,2013,1.933,2990,3.544,3901,9.917,3902,9.917,4096,4.835,4097,4.835,4351,4.141,4354,4.961,4356,4.961,4360,4.961,4362,4.961]],["t/1099",[2,1.52,7,0.64,49,1.824,56,1.287,84,1.127,85,1.952,109,1.937,162,1.98,165,1.262,179,2.715,296,2.171,313,1.377,322,1.762,336,2.293,391,2.372,452,3.878,502,1.554,536,3.284,577,6.063,614,1.037,705,3.468,757,2,827,2.855,846,2.92,916,3.966,993,2.279,1037,2.804,1754,2.395,1982,4.33,2010,4.36,2013,1.877,2990,3.442,3906,4.193,3907,4.493,4351,4.021,4358,6.939,4364,6.939,4366,4.818,4377,4.818,4378,5.347,4379,5.347,4380,9.025,4381,5.347,4382,5.347,4383,5.347]],["t/1101",[7,0.682,18,5.34,19,2.748,23,2.059,24,0.999,28,1.67,31,1.507,34,2.204,48,2.502,56,1.07,84,0.937,89,3.229,96,2.173,103,2.064,105,2.707,123,2.102,133,2.639,164,2.377,201,3.14,225,3.573,226,2.991,274,5.14,318,1.502,338,1.517,372,2.308,530,2.758,558,3.87,568,1.766,593,4.268,605,2.232,683,3.238,685,5.562,892,2.308,914,5.458,933,3.494,1007,3.994,1192,3.827,1193,4.295,1194,6.168,1195,4.199,1196,4.402,1197,5.891,1198,4.523,1199,5.828,1200,5.14,1224,2.266,1266,3.751,1506,3.958,2082,5.43,2099,3.711,2250,3.898,2257,4.295,4384,6.646]],["t/1103",[1781,3.73]],["t/1105",[7,0.637,16,1.503,19,3.6,21,2.283,29,2.819,84,1.124,105,2.527,165,1.81,219,3.548,221,2.984,275,2.501,296,3.113,304,1.703,318,2.945,512,2.786,513,3.807,562,3.968,567,3.239,580,3.603,653,2.543,723,2.888,736,3.445,757,2.824,916,3.08,1019,2.969,1302,4.897,1456,4.256,2338,4.444,2402,4.038,2624,5.232,2625,4.635]],["t/1107",[2,2.212,7,0.725,15,1.601,25,1.406,29,1.532,49,1.515,56,1.035,84,0.73,98,2.368,105,2.108,113,1.601,132,1.29,165,1.51,203,1.613,219,1.359,221,1.622,228,2.135,246,1.66,255,2.957,256,1.643,283,1.27,313,2.004,316,1.956,318,2.998,364,3.094,377,0.982,395,1.735,398,3.965,471,1.82,476,1.275,482,1.963,484,1.86,491,2.032,502,1.858,512,1.514,530,1.963,543,2.472,580,1.525,605,1.589,614,1.241,615,2.569,621,2.257,647,2.528,723,3.745,728,3.057,731,2.301,757,1.661,822,4.026,831,2.769,838,2.215,840,1.992,846,1.348,857,3.058,916,3.784,963,2.661,988,4.854,995,2.443,1019,3.014,1051,5.08,1066,3.999,1108,2.246,1132,2.993,1328,3.515,1413,2.457,1461,3.575,1475,3.626,1633,2.069,1688,2.989,2013,1.463,2017,3.873,2094,3.175,2141,4.707,2339,4.026,2340,2.703,2577,3.551,2879,3.659,2887,2.569,2958,3.754,3565,2.957,3966,3.866,4009,3.057,4013,3.754,4088,3.754,4385,4.391,4386,4.167,4387,4.167]],["t/1109",[4,1.005,7,0.255,19,1.443,25,1.695,27,1.257,28,0.457,29,1.13,40,1.707,56,1.257,68,1.228,84,0.727,100,1.448,105,1.013,109,1.814,127,3.751,140,1.269,165,0.726,172,1.834,179,1.772,182,1.772,193,2.821,195,2.852,219,1.639,254,1.042,256,1.212,277,1.665,279,1.4,295,1.303,318,0.789,327,4.002,337,2.466,338,0.797,395,1.28,398,1.141,405,2.721,418,1.619,476,2.934,511,1.688,512,1.826,552,1.874,580,1.125,614,0.596,647,1.986,653,2.889,723,2.773,757,1.655,803,1.824,818,3.372,828,1.351,837,1.963,846,2.062,876,2.043,892,1.212,898,1.752,916,3.26,939,1.846,944,1.31,966,1.831,984,1.386,988,1.584,1013,4.852,1016,2.852,1051,4.451,1075,2.776,1076,2.418,1077,2.964,1100,2.099,1108,1.657,1118,3.276,1224,1.946,1456,2.79,1530,1.949,1539,2.159,1671,1.782,1693,1.895,1700,2.41,1705,4.102,1753,2.229,1765,1.858,1768,3.604,1925,5.688,2010,1.843,2141,1.657,2208,3.779,2254,1.673,2338,2.912,2339,3.163,2402,2.647,2890,2.638,3565,3.566,3899,2.138,3923,2.448,3970,2.489,4227,2.699,4335,2.583,4388,3.074,4389,3.074,4390,3.239,4391,3.074,4392,3.239,4393,3.074,4394,3.239,4395,3.074,4396,3.074,4397,3.239,4398,4.002,4399,4.002,4400,2.77,4401,3.239,4402,3.239,4403,3.239,4404,3.239,4405,3.239,4406,3.239,4407,3.239,4408,3.239,4409,3.239,4410,3.883,4411,3.94,4412,3.239,4413,3.94,4414,3.239,4415,3.94,4416,3.074,4417,3.239,4418,3.239,4419,3.239,4420,2.448]],["t/1111",[2,1.621,4,1.864,25,1.924,26,2.231,28,0.848,40,3.166,56,1.116,68,2.279,98,2.986,109,1.979,127,3.848,172,2.082,179,2.011,182,2.843,193,3.585,199,2.964,338,1.478,372,2.249,398,2.117,405,1.912,476,3.281,508,3.54,512,2.93,580,2.087,614,1.106,653,2.675,757,1.481,803,3.384,846,2.609,892,2.249,916,2.291,966,2.317,984,4.217,1013,3.759,1051,4.12,1076,3.06,1108,3.074,1118,3.588,1224,3.122,1459,6.443,1539,4.006,1765,3.448,1925,4.407,2254,3.104,2402,4.247,3565,5.723,3858,4.41,3899,3.966,3923,4.542,4335,4.793,4420,4.542,4421,6.01,4422,6.01]],["t/1113",[4,2.637,7,0.747,24,0.753,27,1.104,28,1.437,34,1.185,49,1.045,56,0.459,68,1.764,84,0.504,96,1.639,107,3.005,109,1.599,116,1.736,163,1.844,170,2.081,179,1.557,182,1.557,203,1.686,213,2.866,295,1.871,338,1.733,397,2.928,476,2.752,512,2.428,527,2.434,552,1.647,560,3.749,575,2.21,580,2.446,614,1.297,647,3.821,653,2.217,663,2.841,725,4.327,736,1.984,740,2.103,846,2.162,892,2.635,945,2.812,966,2.922,993,1.882,1051,2.255,1076,2.536,1082,3.516,1093,4.815,1099,3.147,1101,2.779,1118,4.526,1132,3.127,1224,2.588,1402,2.175,1425,4.093,1616,3.919,1622,2.985,1988,1.946,2254,2.402,2256,7.803,2402,3.521,2900,4.744,3917,3.101,3923,3.516,3942,2.959,3943,7.827,3944,3.575,4423,4.415]],["t/1115",[4,3.278,7,0.54,28,1.491,56,1.043,109,1.999,116,2.557,182,2.293,203,2.228,213,3.787,338,1.686,397,3.868,527,2.781,560,4.954,580,2.379,647,2.571,653,2.157,807,2.648,846,3.244,916,2.612,945,3.496,966,3.091,1051,4.512,1062,6.117,1118,3.93,1224,2.518,1988,2.866,2402,4.651,3923,5.179]],["t/1117",[4,3.293,5,2.95,7,0.464,28,1.498,34,1.5,56,0.963,116,2.198,182,1.971,203,2.005,213,3.408,236,1.549,239,3.379,256,2.204,289,3.31,295,2.368,338,1.449,397,3.482,511,2.67,527,2.622,560,4.458,580,2.045,647,3.983,653,2.636,807,5.072,846,2.571,892,2.204,916,2.245,945,3.711,966,3.059,1051,4.061,1076,3.016,1118,4.116,1224,3.078,1765,3.379,1988,2.464,2402,4.187,3841,3.966,3858,4.347,3923,4.451,4424,5.589]],["t/1119",[4,3.258,5,2.386,7,0.534,24,1.701,28,1.482,31,1.655,39,2.756,84,1,116,2.528,144,3.244,182,2.266,203,2.21,213,3.757,232,2.464,338,1.666,367,3.448,397,2.816,414,3.886,527,2.581,560,4.915,580,2.352,647,2.541,653,2.132,769,4.335,807,2.617,846,2.079,908,3.448,945,3.055,966,2.864,1051,3.284,1118,2.86,1224,2.489,1266,2.936,1329,5.74,1765,3.886,1988,2.833,2198,4.898,2402,4.615,3616,4.169,3841,4.562,3923,5.119]],["t/1121",[4,1.426,24,0.744,28,1.505,34,1.171,56,1.214,107,1.177,109,1.972,171,1.897,172,1.592,182,2.336,193,1.672,219,1.423,254,1.478,296,1.772,304,0.969,327,3.474,337,2.141,338,1.131,377,1.028,491,1.386,502,1.268,512,2.407,527,2.06,552,1.627,600,3.064,614,1.865,647,1.725,653,2.966,658,2.201,803,2.589,818,3.505,846,2.893,916,2.661,944,1.86,945,2.792,957,4.057,966,2.57,1051,4.092,1075,2.41,1076,3.393,1099,4.416,1118,3.564,1132,3.1,1133,5.969,1224,3.101,1705,2.808,1768,3.129,2208,3.281,2402,3.489,4398,3.474,4399,3.474,4410,3.371,4411,3.421,4413,3.421,4415,3.421,4425,4.597,4426,3.596,4427,4.597,4428,4.597,4429,6.624,4430,8.942,4431,8.942,4432,8.942,4433,6.624,4434,6.624,4435,6.624,4436,4.047,4437,4.187]],["t/1123",[4,1.855,17,2.363,28,1.195,56,1.157,106,3.918,109,2.031,137,5.433,182,2.833,219,1.851,254,1.923,327,4.519,337,2.785,338,1.471,377,1.337,444,3.67,502,1.649,527,2.149,600,3.986,614,1.101,638,3.598,653,3.094,818,3.002,846,3.018,916,3.227,945,1.978,966,1.631,1051,4.766,1076,3.049,1083,4.105,1099,3.958,1112,5.022,1118,2.525,1224,3.111,1671,4.657,1705,3.652,1768,4.07,2208,4.267,2402,4.232,2911,6.691,3871,5.113,4398,4.519,4399,4.519,4410,4.384,4411,4.449,4413,4.449,4415,4.449,4426,4.677,4438,5.446,4439,5.446,4440,6.751,4441,7.239]],["t/1125",[0,2.205,4,1.875,27,0.907,34,1.54,56,1.218,106,1.4,108,3.875,109,1.865,113,1.393,127,1.252,162,1.342,171,1.642,172,4.004,182,2.511,193,3.907,203,1.447,219,1.182,236,1.004,254,2.413,261,1.935,296,1.472,304,1.274,318,0.93,338,0.939,372,2.262,377,0.854,391,1.608,408,3.57,418,1.909,426,2.771,446,2.429,476,1.755,491,1.151,502,1.667,552,3.5,600,2.546,614,1.113,647,2.268,653,2.686,725,3.82,757,1.49,813,2.473,818,3.035,829,2.012,846,2.619,872,2.473,890,3.045,916,2.304,937,2.281,944,3.035,966,2.046,1013,3.781,1051,3.638,1075,5.631,1076,3.348,1077,5.534,1082,4.569,1118,4.176,1182,1.821,1224,3.135,1337,4.03,1411,2.473,1671,3.325,1705,2.333,1765,2.191,1768,2.6,1918,3.624,1925,4.433,2402,4.265,2875,2.842,3841,2.572,3858,1.982,4121,3.265,4398,2.886,4399,2.886,4410,2.8,4411,2.842,4413,2.842,4415,2.842,4426,2.987,4442,3.819,4443,3.819,4444,3.819,4445,3.819,4446,3.624]],["t/1127",[4,2.019,28,0.919,56,1.149,109,1.963,116,2.429,172,3.843,182,3.006,193,2.368,219,2.015,224,2.758,327,4.92,337,3.032,338,1.601,377,1.456,391,2.741,407,2.943,476,1.89,513,3.068,552,2.304,608,5.757,647,2.442,653,2.828,725,3.314,807,2.516,818,4.511,846,2.758,916,3.425,944,2.633,966,1.775,1051,4.989,1118,2.749,1224,2.392,1705,3.976,1768,4.432,1849,3.541,1988,2.723,2208,4.646,2402,4.491,2868,3.541,3897,5.093,4398,4.92,4399,4.92,4410,4.774,4411,4.844,4413,4.844,4415,4.844,4426,5.093,4438,5.93,4439,5.93,4447,9.764,4448,6.178]],["t/1129",[4,1.691,6,5.73,7,0.625,8,4.263,21,1.54,24,0.883,31,1.332,34,1.388,56,0.782,68,2.067,84,0.591,104,4.799,105,1.705,106,1.998,107,2.029,109,2.185,115,4.718,116,2.034,123,1.858,137,3.498,144,2.611,157,3.148,182,1.824,228,1.727,229,3.234,236,1.433,256,2.04,295,2.192,304,1.149,316,2.429,343,1.939,353,2.161,372,2.04,398,1.92,426,2.926,491,1.643,492,4.264,577,2.579,593,3.908,614,1.459,615,3.189,653,2.938,679,4.264,683,2.04,705,4.877,742,3.41,765,3.234,803,4.461,809,3.752,828,2.273,846,2.865,916,3.02,944,2.205,966,2.161,1051,3.842,1076,3.69,1081,3.752,1138,3.468,1224,3.43,1364,2.725,1605,3.997,1671,2.998,2402,3.96,2430,3.355,3008,3.796,3610,4.965,4440,4.347,4449,8.858]],["t/1131",[7,0.505,15,2.335,46,4.795,56,1.255,116,2.39,182,2.972,219,1.982,254,2.856,327,4.84,337,2.982,338,1.575,377,1.432,600,4.268,614,1.179,647,3.332,653,2.796,715,3.291,846,2.727,916,3.386,1051,4.307,1076,3.199,1083,5.341,1090,2.843,1132,4.891,1224,2.353,1275,5.336,1705,3.911,1768,4.359,1988,2.679,2208,4.57,3838,4.631,3858,3.323,4398,4.84,4399,4.84,4410,4.696,4411,4.765,4413,4.765,4415,4.765,4426,5.009,4440,5.106,4441,5.475,4450,6.404,4451,6.404,4452,6.077,4453,6.077,4454,6.077,4455,6.077,4456,6.077,4457,6.077,4458,6.077,4459,6.077,4460,6.077]],["t/1133",[2,2.547,7,0.745,16,1.757,21,2.669,84,1.023,278,3.384,313,2.307,339,4.764,386,7.028,490,6.656,512,3.256,544,5.907,552,3.343,677,4.976,723,3.376,736,4.027,757,2.327,1142,6.233,1174,6.361]],["t/1135",[7,0.728,19,4.114,20,4.236,25,2.169,28,1.592,96,2.386,103,2.266,219,2.858,221,2.502,225,2.421,226,5.469,243,3.704,256,2.534,278,2.427,280,4.309,296,2.61,306,3.008,318,3.089,366,4.236,472,3.298,511,2.159,512,2.335,513,3.192,562,3.326,567,2.716,607,2.596,611,2.816,615,3.963,683,2.534,757,2.588,851,5.644,853,5.791,977,2.47,988,3.312,1192,4.202,1290,3.99,1302,4.106,1335,5.644,1530,4.076,2017,2.851,2078,6.169,2140,6.169,2624,4.386,2625,3.886,4461,6.428,4462,6.428,4463,6.428,4464,6.428,4465,6.428,4466,6.428]],["t/1137",[2,1.47,7,0.924,17,2.154,25,1.745,28,0.769,34,1.388,56,0.922,84,1.23,85,1.888,88,2.455,89,2.034,103,2.651,109,0.919,127,1.788,134,2.713,171,1.481,179,1.824,182,1.824,202,3.752,219,2.889,226,2.643,232,1.983,247,2.396,272,2.232,283,1.576,296,2.101,304,1.149,306,3.518,313,1.332,318,2.923,322,1.705,338,1.341,353,1.487,397,2.266,401,2.084,418,4.665,421,3.41,438,2.829,471,2.259,476,1.582,502,1.503,511,2.526,526,3.942,534,3.89,567,4.985,575,3.764,591,3.108,605,1.973,614,1.459,672,5.498,723,3.336,734,2.412,802,2.205,828,2.273,897,3.438,1076,1.963,1224,2.911,1309,3.168,2013,1.816,2402,2.725,2886,4.264,2993,4.264,3932,4.264,4467,7.519,4468,5.173,4469,5.173]],["t/1139",[7,0.795,15,3.191,16,1.166,25,2.006,56,1.174,84,0.679,89,2.339,103,2.928,108,3.237,145,2.52,164,2.77,196,2.484,201,3.19,243,3.427,272,2.566,279,2.708,318,1.526,336,4.441,377,1.402,401,2.395,405,3.472,418,3.132,471,2.597,511,1.998,517,3.799,532,3.268,562,3.078,567,4.376,723,2.24,757,1.544,921,3.986,937,3.744,977,2.285,1039,4.136,1375,3.799,1394,3.986,1530,3.771,1797,4.997,4470,5.947,4471,5.947,4472,5.947,4473,5.947,4474,5.947,4475,5.947,4476,5.358,4477,5.947,4478,5.947,4479,5.947,4480,5.947,4481,5.947,4482,5.947,4483,5.947,4484,5.947,4485,5.947,4486,5.947,4487,5.947,4488,5.947,4489,5.947,4490,5.947,4491,5.947,4492,5.947,4493,5.947]],["t/1141",[7,0.673,15,3.471,28,1,31,1.146,48,1.902,56,1.312,85,2.455,103,1.569,105,1.467,109,1.195,127,2.324,140,1.837,161,3.192,164,1.288,171,1.274,179,1.569,225,3.653,243,2.564,300,2.842,318,1.142,336,1.908,377,1.049,392,2.909,401,1.792,405,1.492,437,2.201,472,2.283,476,1.361,511,2.259,532,2.446,562,2.303,567,3.817,601,2.201,607,1.797,670,2.725,747,1.925,829,2.47,830,2.143,921,6.501,923,5.387,1012,3.739,1013,2.933,1027,2.983,1039,3.095,1313,2.725,1352,3.158,1405,3.036,1631,3.818,1632,2.434,1633,2.21,1664,3.438,1718,3.126,2013,1.562,2253,2.842,2655,4.271,3137,3.907,3139,3.907,3763,4.507,3847,4.009,4476,6.059,4494,9.699,4495,6.724,4496,4.45,4497,4.45,4498,4.45,4499,4.45,4500,4.45,4501,4.45,4502,4.45,4503,4.45,4504,4.45,4505,4.45,4506,4.45,4507,4.45,4508,6.059,4509,4.009,4510,4.45,4511,4.009,4512,4.009,4513,3.668,4514,4.45,4515,4.009,4516,4.45,4517,4.009,4518,6.059,4519,4.009,4520,4.009,4521,4.009,4522,4.009,4523,4.009,4524,4.009]],["t/1143",[7,0.398,28,0.713,31,1.234,36,2.228,49,1.136,56,1.342,68,1.916,109,0.852,127,1.657,165,1.132,203,1.794,225,1.806,226,3.633,300,3.063,318,1.23,336,2.056,377,1.13,398,3.48,405,2.384,418,2.525,426,2.767,476,1.467,567,3.581,577,2.39,601,2.372,921,6.712,923,5.633,946,4.32,966,2.043,988,3.664,1012,4.029,1013,3.16,1027,3.214,1039,3.334,1309,2.936,1352,3.403,1475,2.718,1631,4.114,1632,2.622,1635,4.21,1664,3.705,1718,3.368,1763,4.602,1765,2.898,2253,3.063,2339,4.476,2775,4.602,3059,3.04,3137,4.21,3139,4.21,3468,5.975,3763,3.214,4420,5.663,4509,4.32,4511,4.32,4512,4.32,4513,3.952,4515,4.32,4517,4.32,4518,6.407,4519,4.32,4520,4.32,4521,4.32,4522,4.32,4523,4.32,4524,4.32,4525,4.795,4526,4.602,4527,8.476,4528,4.795,4529,4.795,4530,4.795,4531,4.795,4532,4.795,4533,4.795,4534,4.795,4535,4.795]],["t/1145",[1781,3.73]],["t/1147",[34,2.593,56,1.005,84,1.103,257,5.184,278,3.649,279,4.4,295,4.095,545,4.706,1364,5.09,4536,9.663]],["t/1149",[4,1.668,7,0.802,15,1.961,16,1.894,21,2.216,24,1.5,28,1.307,41,2.439,46,3.838,49,1.208,56,0.531,84,1.103,96,1.894,125,3.065,138,3.735,164,1.477,165,1.204,171,1.461,179,3.622,183,2.308,189,2.877,203,1.287,207,2.194,245,3.212,282,2.924,283,2.268,305,3.42,306,2.387,337,2.504,338,1.323,343,1.912,353,1.466,377,1.754,409,2.725,416,2.476,440,2.371,441,5.036,449,4.828,534,5.598,536,2.336,545,2.485,558,2.404,559,8.814,560,4.929,565,2.561,596,3.533,687,4.48,744,5.803,747,2.208,757,1.325,827,4.693,985,5.177,1179,3.584,1182,2.564,1297,3.803,1319,3.66,1389,4.378,2009,3.584,2076,3.125,2117,4.132,4110,7.693,4204,7.384,4537,5.102,4538,5.102]],["t/1151",[18,4.288,28,1.377,56,0.964,84,1.057,113,3.559,253,5.872,283,2.822,530,5.216,545,5.391,583,5.961,881,6.104,933,5.529,4539,8.132]],["t/1153",[7,0.739,21,2.648,23,3.128,84,1.015,88,4.22,89,3.497,283,2.709,353,2.555,359,4.131,391,3.944,472,4.562,476,2.72,512,3.23,545,5.261,575,4.451,614,1.725,1105,7.329,1186,7.2,3830,6.687]],["t/1156",[171,2.866,232,3.837,304,2.223,545,4.876,1737,3.978,3595,3.886]],["t/1158",[4,3.105,17,3.956,84,1.084,219,3.098,237,7.339,284,6.741,304,2.109,545,4.626,1413,5.602,2424,9.117,4540,9.498,4541,9.498]],["t/1160",[1781,3.73]],["t/1162",[2,1.397,5,1.11,7,0.602,16,1.927,17,2.046,18,3.704,19,3.4,23,1.729,24,1.726,25,1.658,27,2.373,28,1.279,29,1.807,84,0.827,88,2.332,89,3.381,105,0.986,107,1.954,109,1.421,127,2.161,132,2.241,145,3.644,153,1.612,162,1.108,165,1.709,168,2.627,169,1.724,170,3.149,172,1.794,173,2.771,174,1.539,179,1.055,193,1.147,203,0.755,225,1.127,228,1.64,232,1.147,236,2.872,275,1.603,283,1.497,289,2.604,294,1.696,313,0.77,323,2.088,336,3.687,343,1.121,365,1.597,372,1.937,391,3.814,397,1.31,405,1.647,476,1.503,491,0.95,507,1.714,510,2.627,511,1.005,512,2.631,530,1.41,545,2.393,550,1.239,558,1.41,575,2.46,577,1.491,580,1.095,591,1.797,596,1.42,647,1.183,653,0.992,723,1.127,734,2.291,736,3.254,747,1.294,748,2.382,749,2.041,757,0.777,759,1.105,760,2.677,762,2.123,769,1.99,780,1.678,807,2.001,827,3.867,828,2.159,892,1.179,916,4.063,917,1.786,944,1.275,966,0.86,977,1.888,982,1.59,988,1.541,1008,1.883,1022,3.625,1062,3.169,1098,1.988,1099,2.825,1102,1.883,1112,3.071,1132,1.4,1186,3.979,1206,1.678,1211,3.152,1329,2.384,1475,1.696,1558,2.916,1925,2.311,2017,1.327,2073,1.883,2252,2.346,2350,2.311,2422,2.023,4542,3.397]],["t/1164",[7,0.18,16,2.078,18,1.73,19,1.015,21,1.976,23,0.76,24,0.369,25,2.239,26,1.93,27,1.948,28,1.629,29,0.795,31,0.556,34,1.577,56,1.14,84,0.94,85,0.789,88,3.149,89,1.469,96,1.386,98,1.827,100,2.769,102,1.436,105,1.626,106,0.835,108,2.033,109,0.877,113,1.435,116,1.469,127,0.747,128,1.877,145,0.916,160,0.798,163,2.06,165,1.387,179,0.762,182,2.561,183,0.977,198,1.109,200,1.95,201,1.159,203,0.545,216,1.159,231,1.647,278,1.41,279,1.701,281,1.077,293,1.816,294,1.225,295,2.09,297,1.245,306,1.748,313,0.556,322,0.712,323,1.588,332,1.182,338,0.968,339,1.149,343,1.4,353,1.073,377,1.711,405,2.889,413,1.721,416,1.049,418,1.138,426,1.454,440,1.004,472,1.109,476,0.661,484,0.965,495,1.1,502,0.628,507,2.141,508,3.647,509,1.29,511,0.726,512,3.359,520,2.074,552,0.806,557,1.133,558,1.76,592,1.461,596,1.026,614,0.419,622,1.985,636,1.282,647,1.477,653,1.239,723,4.127,731,1.194,734,1.008,736,3.7,757,1.281,828,0.95,838,1.149,840,1.786,855,1.402,897,3.279,898,2.812,916,1.5,934,3.775,945,2.871,974,1.391,984,2.991,993,1.592,1015,2.335,1022,2.165,1037,2.587,1051,1.908,1074,1.647,1076,2.756,1100,1.56,1105,1.781,1132,1.011,1182,1.086,1202,1.402,1219,2.316,1224,0.837,1244,1.898,1265,2.063,1377,1.695,1394,1.449,1425,1.323,1511,3.398,1695,2.005,1705,3.175,1768,1.55,1778,1.854,2015,1.341,2076,1.323,2093,1.55,2214,1.475,2247,1.605,2252,1.695,2311,1.534,2341,4.233,2426,1.534,2545,1.503,2928,5.955,2965,1.898,2992,1.605,3072,1.947,3741,1.625,3899,1.503,4543,2.161,4544,2.161,4545,2.161,4546,2.161,4547,2.161,4548,2.161,4549,2.161,4550,2.161,4551,3.735,4552,2.161,4553,3.735,4554,2.161,4555,2.161,4556,2.161,4557,2.161,4558,2.161,4559,2.454,4560,1.75,4561,2.161]],["t/1166",[7,0.323,16,1.896,17,1.62,23,2.134,24,1.964,25,2.84,27,0.973,28,1.438,29,2.23,31,1.001,39,1.668,56,1.295,68,1.554,84,0.851,88,1.846,95,2.543,100,3.967,105,1.282,107,1.049,108,3.301,112,2.031,127,1.344,128,1.955,165,0.918,172,1.42,182,2.139,203,0.981,228,1.299,292,1.783,304,0.864,377,0.917,444,1.777,446,2.608,476,2.28,483,2.585,543,2.308,577,1.939,614,1.446,670,2.382,723,3.438,747,1.683,759,1.437,760,2.621,769,3.019,813,4.139,892,1.534,897,2.585,916,2.994,945,2.935,963,2.485,966,2.142,984,1.754,988,2.004,993,1.658,1019,2.348,1051,3.809,1062,1.971,1076,2.301,1083,3.809,1090,3.488,1099,1.518,1100,2.533,1219,1.827,1329,1.888,1437,3.098,1467,2.564,1729,3.269,1730,3.338,1733,2.449,1765,2.352,2190,2.294,2251,3.15,2293,2.398,2403,2.281,2911,2.79,2928,2.16,3093,5.097,3576,3.098,3741,2.925,3811,3.15,4562,4.417,4563,4.417,4564,6.391,4565,6.887,4566,4.417,4567,4.417,4568,4.417,4569,4.417,4570,4.417,4571,4.417,4572,3.416]],["t/1168",[12,2.718,16,1.254,24,1.091,25,2.158,27,2.797,28,1.298,34,1.716,39,1.787,48,1.781,56,0.98,98,1.543,100,1.963,103,1.469,106,1.609,107,2.682,108,3.481,109,1.898,127,1.44,170,2.719,173,3.607,182,2.255,183,2.893,193,3.968,203,1.613,244,2.703,261,2.225,272,1.797,278,2.415,295,1.766,297,4.486,320,2.519,322,1.373,347,2.898,353,1.838,418,2.195,444,1.903,502,1.211,507,2.388,550,1.725,614,2.008,632,3.626,635,3.338,653,1.382,723,2.409,769,3.536,916,1.674,917,2.487,944,2.726,945,2.714,966,1.838,974,4.116,984,1.878,1022,3.707,1051,3.268,1062,2.111,1075,3.533,1076,3.313,1077,3.772,1093,4.647,1094,2.195,1102,2.623,1182,2.094,1467,2.746,1696,2.793,1733,2.623,1737,1.656,1765,4.706,2190,2.457,2293,2.569,2928,4.848,3741,3.133,3742,3.374,3858,4.775,4573,4.391,4574,3.501]],["t/1170",[16,1.473,24,1.282,27,2.431,28,1.445,56,1.121,108,4.088,109,2.096,134,3.939,170,3.025,183,3.397,189,4.236,297,5.6,323,3.192,476,2.298,495,3.822,502,2.183,608,4.108,635,2.872,707,4.404,723,2.829,813,5.126,870,6.192,916,3.017,945,2.619,984,3.386,1022,4.354,1051,3.838,1062,4.924,1622,5.08,1931,5.724,3114,7.21,4573,7.916]],["t/1172",[2,1.38,16,1.85,24,2.092,25,2.422,28,1.699,29,1.786,31,1.251,39,3.079,49,1.15,56,0.889,68,2.869,112,2.536,124,3.228,140,2.006,165,2.016,171,1.391,199,2.525,203,1.225,256,1.915,285,2.115,289,2.023,296,1.973,302,2.357,313,1.251,322,1.601,323,2.064,367,5.062,405,1.628,426,1.89,446,3.256,511,1.632,564,3.315,596,2.305,712,2.754,723,3.554,726,3.346,748,3.869,749,3.315,755,2.305,760,3.281,769,4.268,814,2.204,820,2.631,827,3.834,831,3.228,857,2.322,916,1.951,938,3.151,945,1.694,979,3.412,1051,2.482,1053,4.662,1076,2.724,1179,3.412,1329,5.431,1397,4.506,1733,3.058,1779,3.447,2190,2.865,2293,5.817,2928,3.986,3981,5.119,4575,5.119]],["t/1174",[7,0.592,16,2.059,25,2.405,27,1.783,28,1.66,49,1.688,109,1.983,127,3.857,171,2.688,172,3.427,198,3.657,203,1.798,254,2.415,296,2.894,322,2.349,338,2.893,444,3.256,502,2.071,527,1.642,562,3.688,614,1.383,638,4.519,723,3.537,827,3.806,984,3.213,1099,4.097,1132,3.335,1557,6.865,2911,5.112,4576,8.093]],["t/1176",[2,1.306,7,0.382,16,1.351,27,2.913,28,1.229,29,1.69,56,1.253,96,1.706,107,1.24,109,1.748,127,1.589,129,2.894,163,2.878,170,1.431,171,1.973,182,3.238,183,2.079,193,3.168,196,1.92,203,1.16,278,1.736,279,2.093,296,1.867,297,2.649,313,1.183,338,1.192,360,5.182,440,2.136,476,2.809,507,5.262,596,2.182,614,0.892,653,2.742,723,1.731,944,2.937,945,1.603,979,3.229,1022,3.994,1062,4.188,1076,3.137,1078,5.251,1083,2.349,1102,4.338,1132,3.868,1220,2.779,2010,1.686,2900,3.262,2928,2.552,4437,7.934,4577,10.426,4578,5.22,4579,9.386,4580,9.386,4581,9.386,4582,9.386,4583,9.386,4584,9.386,4585,9.386,4586,5.22,4587,5.22,4588,5.22,4589,5.22]],["t/1178",[16,1.849,24,1.797,25,2.42,28,1.402,29,2.637,31,1.847,39,4.043,56,0.746,95,4.689,100,3.38,106,2.77,188,5.466,197,3.368,198,3.68,282,4.111,289,2.987,302,3.48,505,3.133,557,3.761,635,2.742,723,3.968,751,4.364,763,5.145,778,4.767,830,4.541,1181,4.422,1210,5.913,1768,5.145,2403,6.56,2404,5.203,2422,6.376,2501,5.913,2577,5.235,2928,5.235,4590,8.145,4591,8.145]],["t/1180",[5,4.209,16,1.759,23,3.157,24,1.136,28,1.509,29,2.448,56,0.934,88,3.16,145,2.821,153,3.589,162,2.466,163,2.781,165,2.396,169,3.837,190,3.606,216,3.572,217,4,239,6.566,289,4.523,322,2.195,365,3.555,367,4.814,376,4,430,3.461,433,4.051,505,2.908,512,2.419,647,3.547,732,4.586,748,5.303,751,4.051,753,5.999,807,4.423,898,3.795,966,2.578,1076,2.527,1100,3.748,1406,5.392]],["t/1182",[2,2.1,16,1.638,19,1.801,24,1.547,25,1.294,27,1.501,28,1.625,56,0.399,89,1.509,97,1.808,102,2.55,107,2.594,109,1.066,113,1.474,125,2.305,127,2.073,132,2.287,140,1.584,145,2.542,153,2.068,163,1.602,165,2.269,170,1.868,172,1.4,179,1.353,182,2.115,183,1.735,198,1.968,228,1.281,239,2.319,245,2.415,275,1.251,276,3.857,277,2.078,289,3.077,294,2.175,313,2.151,320,2.319,323,1.63,324,3.348,351,1.463,365,2.049,381,4.165,394,2.021,398,1.424,405,3.846,426,1.493,432,2.55,444,1.753,468,2.572,472,1.968,495,1.952,502,1.115,511,2.807,527,1.382,550,1.589,555,1.913,577,1.913,581,2.923,621,2.078,635,1.467,662,2.349,683,1.513,712,2.175,723,1.445,748,3.055,749,2.618,750,2.849,757,1.558,759,1.417,760,1.194,769,1.554,780,4.144,814,1.741,820,3.249,840,1.834,859,2.098,891,3.559,917,2.29,994,1.741,1015,2.398,1022,2.224,1036,3.055,1072,4.416,1099,1.497,1100,1.602,1172,2.433,1182,3.713,1183,2.783,1214,3.457,1229,4.946,1331,2.594,1407,2.885,1475,2.175,1987,2.849,2010,1.407,2253,2.451,2333,3.457,2434,2.752,2448,3.954,2479,4.946,2898,2.723,2946,5.042,3184,3.457,3972,2.643,3974,3.682,3982,3.682,4004,3.559]],["t/1184",[2,0.883,4,1.016,7,0.258,16,1.811,17,1.294,18,2.348,21,1.51,24,1.911,25,1.049,27,1.606,28,1.643,31,1.653,49,1.201,56,0.911,84,0.355,95,2.032,97,1.465,98,1.151,105,2.117,107,1.733,108,1.692,109,0.901,115,1.508,145,1.317,157,1.891,160,1.148,165,1.197,169,2.922,170,2.726,179,1.096,193,1.943,203,2.513,224,3.308,226,1.588,228,1.038,239,1.879,278,2.425,283,0.947,289,4.149,313,0.8,315,2.538,318,0.797,322,2.117,323,1.321,338,0.806,377,0.733,405,1.042,426,1.21,502,1.866,505,1.357,506,1.943,507,1.781,525,1.543,550,4.126,607,2.594,609,2.141,611,1.362,612,2.121,635,2.455,647,2.005,662,1.903,683,1.226,731,1.717,741,2.121,749,4.383,750,3.766,756,1.537,757,0.807,760,0.968,807,4.057,812,2.183,830,1.497,870,2.562,917,5.948,934,1.616,977,2.468,985,2.162,1010,2.183,1015,1.943,1022,1.801,1062,2.569,1079,2.049,1094,4.3,1102,1.957,1220,3.065,1287,2.402,1462,4.224,1467,3.342,1735,2.281,1737,2.552,1955,2.729,1969,2.983,1991,3.562,2271,3.813,2282,2.801,2313,3.275,2403,1.822,2404,2.254,2448,2.049,2501,2.562,2545,2.162,2833,2.801,2903,6.358,2928,1.726,4004,2.884,4592,3.529,4593,3.529,4594,5.758,4595,3.529,4596,3.529,4597,3.529,4598,3.275,4599,3.529,4600,3.529,4601,5.344,4602,3.529]],["t/1186",[2,1.737,4,1.997,16,2.157,24,1.656,27,1.528,28,1.735,107,2.283,109,1.504,116,2.403,127,2.112,132,3.523,145,2.589,160,2.257,162,2.263,163,2.552,164,1.769,165,1.997,170,3.022,193,2.342,199,3.176,203,1.541,289,2.545,313,1.573,336,2.62,391,2.711,394,3.218,502,2.459,507,3.502,550,4.02,596,2.9,607,2.468,613,2.91,632,3.464,740,2.91,917,5.052,1022,3.541,1062,3.096,1076,2.319,1093,3.648,1100,2.552,1102,3.846,1183,4.432,1959,4.878,2253,3.903,2868,3.502,2928,3.392,4603,11.023]],["t/1188",[16,1.354,24,1.178,27,2.3,28,1.027,39,2.961,84,0.788,107,2.481,153,4.957,170,2.15,203,1.742,224,3.083,257,3.705,279,3.144,320,4.175,322,2.276,365,3.688,407,3.29,502,2.007,527,2.119,550,3.808,747,2.988,772,6.395,807,2.812,838,3.671,916,3.694,977,2.654,1008,4.347,1062,3.499,1076,4.183,1079,4.552,1093,6.172,1271,3.621,1303,4.67,1737,2.745,2253,5.874,2311,4.901,3833,5.336,4560,5.592,4604,7.842,4605,7.842,4606,10.442,4607,7.842]],["t/1190",[5,3.126,16,1.651,23,3.675,28,1.252,48,3.6,163,3.517,165,1.988,169,4.853,239,5.09,289,4.35,367,4.518,512,3.059,527,1.94,550,3.487,647,3.329,712,4.774,724,4.321,807,3.429,966,3.002,977,3.236,1076,3.964,1100,3.517,1765,5.09]],["t/1192",[21,2.669,24,1.529,28,1.736,89,3.525,107,3.15,109,1.593,165,2.116,179,3.16,338,2.323,405,3.005,635,3.427,723,3.376,820,4.854,1229,7.388,2928,4.976]],["t/1194",[1781,3.73]],["t/1196",[2,2.507,23,3.103,24,1.834,56,0.918,179,3.11,221,3.433,313,2.767,324,4.922,512,3.205,552,3.29,723,4.049,857,4.217,1019,3.415,1224,3.415,1266,4.03,1271,4.625,1309,5.402,2624,6.019]],["t/1198",[512,3.808]],["t/1200",[7,0.664,16,1.568,24,1.726,25,2.698,27,2.916,28,1.189,31,2.059,68,3.195,84,1.155,96,2.969,132,2.475,281,3.987,512,3.675,553,4.463,635,3.867,653,2.653,898,4.558,971,6.179,995,4.689,1181,4.93,1182,4.019,1285,3.594,1622,6.84,2403,4.689,2422,5.408,2825,6.592]],["t/1203",[56,1.163,171,2.696,219,3.072,304,2.091,322,3.104,552,3.513,1019,3.647,1266,4.302,1737,3.743,2013,3.306,3595,3.656,4608,8.082]],["t/1205",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,1271,5.023,4609,6.948]],["t/1207",[7,0.806,15,2.584,16,0.873,21,1.325,24,1.541,27,2.027,29,1.636,34,1.194,49,1.054,56,1.135,84,0.508,85,1.624,103,1.569,107,1.2,109,1.44,110,3.733,127,2.324,158,2.344,160,1.644,219,2.946,221,3.516,225,1.676,322,2.216,353,1.279,395,1.853,401,1.792,410,3.095,476,1.361,527,2.081,528,2.47,552,3.618,562,3.48,564,5.531,607,1.797,613,2.12,635,1.701,755,2.112,757,1.155,759,1.644,760,2.524,769,1.802,916,1.787,977,1.71,988,2.293,1013,4.432,1019,2.604,1051,2.274,1239,4.009,1285,1.999,1328,4.965,1365,2.707,1405,5.531,1718,5.693,1732,5.273,1759,3.227,1857,3.265,1923,2.496,2013,2.36,2031,4.571,2228,3.265,2274,3.391,2293,2.743,2339,2.801,2340,2.886,2403,2.609,2569,3.739,2621,3.391,2825,3.668,3070,3.227,4016,3.438,4054,5.77,4508,4.009,4608,3.818,4610,4.45,4611,6.724,4612,4.45,4613,6.724,4614,6.724,4615,4.45,4616,4.45,4617,4.45,4618,4.45,4619,4.45,4620,5.053,4621,5.053,4622,5.053,4623,5.053,4624,5.053]],["t/1209",[105,3.329,192,6.27,277,5.471,377,2.381,530,4.76]],["t/1211",[1781,3.73]],["t/1213",[7,0.629,23,1.837,24,1.845,25,1.762,29,1.92,56,0.927,68,2.086,84,1.115,160,2.796,165,2.102,177,2.657,179,1.841,198,2.679,199,2.714,219,1.703,221,3.466,228,1.743,232,2.001,240,3.009,248,2.583,275,1.703,283,1.591,296,2.121,313,1.949,316,2.452,324,2.914,338,1.962,353,1.501,377,1.231,397,2.288,422,3.746,491,1.659,512,2.75,522,4.095,530,4.196,548,4.91,552,1.948,647,2.064,723,3.678,757,2.536,760,1.626,822,3.287,857,2.496,898,4.315,934,2.714,966,1.501,1019,2.022,1051,3.868,1193,3.831,1213,3.979,1224,2.022,1266,4.068,1271,2.738,1300,2.976,1309,3.198,1330,5.214,1413,6.376,1570,4.585,1705,3.361,1913,3.098,2402,2.75,2624,3.563,2868,2.993,3869,4.228,4625,7.57,4626,5.222,4627,5.012,4628,5.222,4629,5.222]],["t/1215",[512,3.808]],["t/1217",[7,0.71,16,1.677,24,1.459,25,2.885,27,2.139,31,2.201,84,0.976,113,3.285,281,4.263,476,3.226,550,4.367,553,4.771,635,3.269,846,2.766,971,6.607,1181,5.271,1330,5.889,1622,5.782,1988,3.769,2403,6.183,2422,5.782,4630,9.709]],["t/1220",[56,1.163,171,2.696,304,2.091,322,3.104,548,6.108,1019,3.647,1266,4.302,1330,6.487,1737,3.743,2013,3.306,3595,3.656,4297,7.626]],["t/1222",[24,1.567,84,1.048,126,4.673,128,4.616,219,3.593,221,4.287,304,2.04,338,2.381,404,4.713,453,6.211,757,2.86,1271,4.816,4609,6.662]],["t/1224",[105,3.329,192,6.27,277,5.471,377,2.381,530,4.76]],["t/1226",[1781,3.73]],["t/1228",[512,3.773,575,5.198]],["t/1231",[56,1.169,171,2.719,304,2.109,322,3.131,567,4.013,1019,3.678,1266,4.339,1737,3.775,2013,3.334,3595,3.687,4631,9.498]],["t/1233",[304,2.243,438,5.524,966,2.903,989,5.994,4632,10.102]],["t/1235",[56,1.014,304,2.165,567,4.119,651,6.847,966,2.801,989,5.784,1123,6.051,1297,4.98,1732,7.643]],["t/1237",[24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,567,3.979,635,3.601,747,4.075,4609,6.831]],["t/1239",[16,1.911,171,2.791,304,2.165,543,5.784,567,4.119,892,3.843,1266,4.453,1737,3.874,3595,3.784]],["t/1241",[56,1.042,304,2.223,401,4.032,966,2.877,1607,6.895,1638,5.448]],["t/1243",[56,1.042,304,2.223,398,3.716,401,4.032,966,2.877,1638,5.448]],["t/1245",[7,0.664,24,1.365,27,2,28,1.189,84,1.155,88,3.796,103,3.566,105,2.636,144,4.036,145,3.389,179,2.82,192,4.964,225,3.012,226,5.168,277,4.331,343,2.998,377,1.885,511,3.398,557,4.194,567,3.379,568,2.413,593,4.157,653,2.653,672,4.964,706,6.014,740,4.818,1753,5.801,2117,6.476,2128,5.801]],["t/1247",[1781,3.73]],["t/1249",[2,2.079,4,3.122,16,1.434,23,2.573,24,1.924,25,2.468,27,1.83,84,0.835,109,1.3,170,2.277,174,2.291,254,2.478,313,1.883,476,2.921,512,3.864,536,2.295,568,2.882,653,2.426,723,2.755,747,3.165,759,2.702,760,2.277,803,4.34,846,3.44,857,3.497,892,2.884,966,3.056,1076,3.624,1090,4.469,1224,2.832,1959,3.676,2472,5.735,4125,6.786]],["t/1251",[4,2.711,28,1.233,84,1.181,217,6.217,552,3.86,592,5.61,653,2.751,725,6.052,846,2.684,966,2.384,1075,5.717,1076,3.927,1077,6.104,1093,7.052,1224,3.212,1671,4.808,2190,4.892,3595,3.22]],["t/1253",[512,3.808]],["t/1255",[7,0.733,16,1.73,24,1.505,27,2.689,31,2.271,84,1.227,169,5.084,281,4.398,472,4.526,553,4.922,635,3.372,971,6.816,1181,5.438,1266,4.03,1622,5.965,1988,3.888,2179,8.467,2403,5.172,2422,5.965]],["t/1258",[23,3.342,56,1.169,171,2.719,304,2.109,322,3.131,1019,3.678,1266,4.339,1737,3.775,2013,3.334,3595,3.687,4633,8.558]],["t/1260",[23,3.313,24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,635,3.601,747,4.075,4609,6.831]],["t/1262",[7,0.855,15,3.386,16,1.898,23,2.627,24,1.833,25,1.729,27,1.868,49,1.214,56,1.118,84,1.006,109,1.327,110,2.846,127,1.771,170,1.596,171,2.138,318,1.315,321,4.082,322,1.689,338,1.329,377,1.208,395,3.11,410,3.565,437,2.535,505,2.238,527,1.721,528,4.146,532,2.817,596,2.433,635,1.96,723,1.931,747,2.218,759,1.893,760,1.596,769,2.076,830,2.469,876,3.407,977,1.97,1019,1.985,1266,2.342,1400,2.803,1401,3.182,1405,5.097,1759,3.718,1773,4.082,1923,5.43,1959,2.576,1988,2.259,2013,3.093,2076,3.139,2137,3.961,2142,2.688,2228,3.761,2274,3.906,2293,3.16,2403,3.005,2406,8.981,2410,4.755,2722,3.325,4633,4.618,4634,5.126,4635,5.126,4636,7.469,4637,5.126,4638,5.126,4639,5.126,4640,5.126,4641,5.82,4642,7.469,4643,5.82,4644,5.82,4645,5.82,4646,5.82,4647,5.82]],["t/1264",[1781,3.73]],["t/1266",[4,2.3,5,2.612,7,0.922,19,4.372,24,1.589,28,1.651,56,0.732,84,0.803,89,2.767,164,2.695,174,2.204,224,3.142,232,3.999,274,6.179,306,3.293,313,1.812,400,5.801,421,4.638,558,5.233,596,3.34,601,3.481,683,2.775,757,2.418,880,4.894,888,3.907,914,6.188,984,3.172,1022,4.079,1037,3.69,1145,3.775,1182,4.679,1508,4.102,1913,4.175,2103,6.528,2250,3.34,4648,7.037]],["t/1268",[2,1.672,4,1.923,7,0.856,16,1.154,23,2.07,24,1.406,25,1.985,27,1.472,28,1.533,31,2.122,56,0.99,84,1.086,103,2.075,113,2.261,123,2.113,165,1.389,170,1.832,173,3.318,174,1.843,216,3.157,217,3.535,219,1.919,232,3.646,247,2.725,276,3.072,289,2.45,306,2.753,318,2.441,337,2.888,363,4.425,409,3.142,491,2.618,500,2.934,502,2.395,511,1.977,513,2.922,530,2.773,548,3.817,552,2.195,558,2.773,562,3.045,565,2.024,567,2.486,568,1.776,577,2.934,632,3.336,635,2.25,663,3.787,666,4.85,683,2.32,685,3.45,814,2.67,821,4.317,914,5.478,919,4.765,977,2.261,1182,2.957,1201,5.648,1266,4.346,1287,4.547,1398,4.268,4649,5.884,4650,5.884]],["t/1270",[7,0.719,23,1.747,24,1.627,25,1.675,27,2.164,28,0.738,29,1.826,56,1.289,84,0.567,89,3.402,95,3.246,100,3.439,107,1.34,108,3.971,109,0.882,113,1.908,128,3.667,135,2.983,164,1.438,165,2.042,169,4.206,177,2.527,179,1.751,193,2.797,219,1.62,232,2.797,275,1.62,294,2.815,296,2.017,306,2.323,343,1.861,446,3.329,447,2.476,476,2.232,483,3.3,496,3.196,505,2.169,507,2.846,512,3.143,548,3.221,552,1.852,558,3.439,596,2.357,635,1.898,653,2.87,662,3.041,740,5.23,807,2.022,813,4.98,827,2.652,897,3.3,944,2.117,966,1.427,993,3.11,1062,2.516,1076,1.884,1083,2.537,1102,3.126,1121,3.172,1207,3.784,1266,4.641,1329,2.409,1437,3.955,1539,3.488,1729,4.173,2093,3.562,4564,5.233,4651,8.286]],["t/1272",[1781,3.73]],["t/1275",[23,3.37,24,1.635,27,2.396,126,4.874,128,4.815,304,2.127,338,2.483,404,4.915,453,6.478,635,3.663,4609,6.948]],["t/1277",[7,0.646,16,1.524,23,3.495,24,1.695,27,1.945,36,3.612,56,1.034,68,3.106,79,6.827,106,3.002,126,5.054,128,4.992,131,3.716,175,4.48,200,4.058,202,5.639,203,1.961,204,6.671,207,3.344,209,7.462,219,2.536,223,4.613,304,1.726,335,6.827,339,4.132,409,4.151,416,3.772,496,5.003,500,3.876,502,2.259,1083,3.972,2013,2.729,3399,7.462]],["t/1279",[7,0.664,16,1.568,23,3.558,24,1.726,36,3.716,41,3.823,56,1.052,68,3.195,79,7.023,126,4.069,128,5.083,131,3.823,201,4.29,202,7.336,203,2.017,204,6.863,207,3.44,210,7.676,219,3.299,223,4.745,304,1.776,335,7.023,339,4.251,409,4.27,496,6.51,1083,4.086,2013,2.807]],["t/1281",[7,0.739,23,3.128,24,1.517,41,4.251,98,3.292,131,4.251,201,5.795,202,6.449,207,3.824,304,1.974,334,5.308,339,4.726,613,4.235,614,1.725,651,6.246,1583,7.472,1737,3.534,3595,3.452,4652,8.892]],["t/1283",[2,1.312,7,0.688,16,0.905,19,2.167,23,4.039,24,1.179,27,2.301,28,1.368,29,1.697,49,1.637,56,0.48,68,1.844,84,1.125,100,2.175,121,2.846,126,2.349,128,2.32,131,2.207,171,2.634,174,2.165,182,1.628,183,3.126,196,1.928,201,2.476,203,1.744,207,1.985,253,2.927,262,3.179,283,2.525,302,2.24,304,1.84,313,1.188,315,2.31,316,2.167,322,1.521,336,3.553,339,2.453,344,6.228,345,7.466,346,9.678,353,1.326,371,4.159,401,1.859,437,2.283,466,3.428,479,3.387,481,2.906,489,3.242,490,3.428,491,1.466,495,2.349,498,2.706,500,3.446,544,3.042,605,1.76,614,1.341,681,4.556,756,2.283,892,1.82,899,3.879,977,1.774,993,1.968,1019,2.676,1083,3.532,1364,2.431,1374,5.881,1686,7.953,1699,3.018,4653,4.159,4654,6.912,4655,4.616,4656,4.616,4657,4.616,4658,4.616]],["t/1285",[2,1.424,7,0.722,12,2.129,16,1.44,23,2.584,24,0.855,28,1.424,29,1.842,84,1.257,88,4.839,89,4.01,109,0.89,127,1.731,128,2.518,129,3.154,132,1.551,164,1.45,165,1.183,169,4.233,179,2.59,213,2.148,219,1.634,236,1.388,241,4.13,275,1.634,280,3.359,296,2.035,304,1.113,313,1.29,315,2.508,316,4.083,318,1.286,337,2.459,397,3.218,404,2.571,422,3.594,440,2.328,458,2.973,470,3.818,472,2.571,476,2.66,512,3.705,532,2.754,544,3.302,545,2.44,562,3.801,568,2.624,575,5.104,605,3.316,622,2.663,681,3.302,736,2.251,753,4.514,816,2.991,822,3.154,857,2.395,898,4.187,899,4.21,1019,1.94,1162,2.938,1413,4.332,1430,4.648,2402,2.639,2421,3.154,2426,5.213,2545,3.485,2641,3.768,2992,5.455,2993,4.13,4659,5.01,4660,5.01,4661,5.01]],["t/1287",[2282,9.444]],["t/1289",[9,6.537,48,4.096,84,1.094,159,6.729,1223,9.195,1472,9.195,1497,8.888,1781,3.409,2651,9.58,3403,9.58,4662,10.096]],["t/1291",[1781,3.73]],["t/1293",[3,5.964,7,0.55,8,3.752,10,3.773,11,5.562,21,1.971,22,4.327,23,2.328,24,1.129,26,2.589,27,2.236,28,1.329,31,1.704,46,2.89,56,0.689,84,0.755,103,2.334,105,2.182,109,1.176,121,4.081,123,2.377,126,3.368,140,2.733,165,1.562,171,1.895,174,2.073,197,3.108,213,2.838,220,4.293,221,2.576,232,2.537,292,3.034,305,4.437,315,3.313,346,5.271,353,1.902,392,4.327,434,6.278,491,2.102,500,3.3,555,3.3,571,7.934,583,4.26,590,3.752,591,3.976,592,4.476,611,2.899,622,3.518,637,4.476,657,5.456,1224,2.563,1325,5.114,1479,4.801,1504,4.915,1754,2.964,2568,6.14,2863,6.619,2864,5.456]],["t/1295",[4,2.151,7,0.547,16,1.29,23,3.132,24,1.986,31,2.292,35,4.828,36,4.136,56,0.926,84,0.751,86,4.72,96,2.442,98,3.296,100,3.1,108,3.581,123,2.363,131,4.255,142,6.315,143,9.275,145,2.788,165,1.553,171,1.884,174,2.061,188,6.783,198,3.376,205,6.315,207,3.829,228,2.196,232,3.412,337,3.229,353,1.891,377,2.098,400,7.338,489,4.622,500,4.438,545,3.205,571,5.159,662,4.029,892,2.594,985,4.576,2864,5.424,2865,6.58,2866,5.928,2867,6.58]],["t/1297",[34,2.527,84,1.075,92,3.675,93,4.992,229,5.887,252,8.27,390,4.792,599,5.101,619,6.369,620,5.49,1118,4.191,1368,6.369]],["t/1299",[34,2.761,415,7.64,858,5.347]],["t/1301",[34,2.761,415,7.64,858,5.347]],["t/1303",[12,3.15,27,2.41,28,1.591,84,1.222,107,2.599,109,2.013,172,3.907,173,4.179,174,2.321,272,3.197,304,1.646,318,1.902,468,4.968,495,3.771,502,2.154,557,3.886,564,5.058,596,3.518,607,2.993,612,5.058,747,3.207,819,5.504,829,5.348,830,3.569,977,4.354,978,6.876,1062,3.756,1072,3.61,2218,5.648,2868,4.248]],["t/1305",[4,1.496,5,1.699,7,0.685,16,0.898,21,2.455,24,0.781,25,1.545,26,3.585,27,1.718,28,1.226,40,2.541,56,0.858,84,1.046,98,3.053,106,1.768,107,1.853,109,2.068,110,3.814,111,7.587,116,1.8,126,2.329,132,1.417,133,2.064,164,2.387,165,1.622,169,3.959,170,2.139,183,2.07,186,2.273,189,3.874,256,1.805,272,1.975,289,1.906,316,2.15,318,1.175,353,2.369,365,2.444,372,1.805,377,1.079,382,2.993,407,2.181,416,2.221,432,3.042,439,3.153,440,2.127,447,2.282,458,2.716,527,2.262,555,2.282,565,1.575,568,2.073,578,1.896,593,3.57,606,3.707,613,3.927,615,2.822,630,2.674,635,1.75,679,5.663,809,3.32,814,2.077,818,4.363,837,2.924,976,3.59,980,6.032,1081,5.98,1167,7.649,1271,2.4,1322,5.308,1323,3.32,1459,4.732,1476,4.02,1783,3.646,1987,3.4,2467,4.247,2869,4.578,2870,4.578,2871,3.847]],["t/1307",[4,1.595,5,1.811,7,0.711,15,1.875,16,0.957,27,2.803,28,1.569,56,0.891,98,1.807,105,1.608,107,1.316,109,1.68,113,1.875,127,2.489,132,1.51,165,2.021,171,1.397,172,3.126,173,2.751,182,1.72,199,2.536,228,2.404,236,1.996,257,2.617,281,2.432,289,3,292,2.236,302,2.367,320,2.949,338,1.265,343,1.829,351,1.861,365,3.846,366,3.216,369,3.19,437,2.413,445,3.242,455,6.181,458,2.895,459,4.022,490,3.623,491,1.55,502,1.418,525,2.423,550,2.02,552,1.82,568,2.174,581,5.489,604,3.393,617,4.1,633,4.022,635,2.754,636,2.895,684,2.681,734,2.275,757,1.267,798,2.828,801,3.825,815,4.526,831,3.242,908,2.617,944,3.07,945,3.297,995,2.86,1108,4.615,1138,3.27,1180,3.58,1183,3.539,1265,2.695,1285,2.192,1302,3.116,1322,3.77,1419,4.022,2199,4.526,2479,5.937,2872,4.879,2873,4.879,2874,4.284,2875,3.825,2876,6.053,2877,4.879,2878,4.879,2879,4.284]],["t/1309",[2,1.672,7,0.79,17,2.45,25,2.781,28,1.614,56,1.073,84,1.177,88,4.515,89,4.054,109,1.046,128,2.957,133,2.653,162,2.179,164,1.703,175,3.391,275,1.919,313,1.515,315,2.945,377,1.387,405,1.973,407,2.803,440,2.734,472,4.881,476,2.521,483,3.911,491,1.869,505,2.57,512,4.282,558,2.773,575,5.781,605,2.244,698,4.484,726,4.053,898,4.698,914,3.911,1080,5.578,1307,4.317,1616,3.45,1754,2.635,2547,5.302,2880,5.302,2881,5.648,2882,9.131,2883,5.459,2884,5.302]],["t/1311",[7,0.751,84,1.166,92,2.629,98,2.495,109,1.607,110,3.741,116,2.65,165,1.591,174,3.198,186,3.346,216,3.615,228,3.02,229,4.212,233,2.57,242,5.004,317,3.549,318,1.729,351,2.57,364,5.004,372,2.657,377,2.132,390,3.429,426,2.623,458,3.999,527,2.515,580,2.466,591,4.048,613,3.21,630,3.521,637,4.557,658,5.745,693,4.371,798,3.906,818,3.566,837,4.305,910,5.368,1099,3.53,1179,4.734,1367,4.479,2196,5.918,2421,4.242,2885,6.072]],["t/1313",[2,2.449,5,3.198,23,3.031,24,1.47,26,3.371,158,4.538,220,5.589,246,3.433,247,3.99,276,4.498,351,3.286,365,4.601,401,3.47,408,3.278,418,4.538,420,8.27,458,5.112,466,6.399,534,6.48,628,4.689,828,3.786,880,5.992,1336,5.935,2204,8.27]],["t/1315",[7,0.769,19,4.349,28,1.377,84,1.057,138,3.936,197,4.349,225,3.488,316,4.349,338,2.401,441,5.308,536,2.906,840,4.427,858,4.814,1118,4.121,1506,6.263]],["t/1317",[7,0.895,15,2.886,16,1.905,23,2.643,84,0.857,144,3.791,219,3.513,225,2.829,232,3.724,236,2.081,256,2.962,275,3.17,283,2.289,323,3.192,351,2.865,390,3.822,491,2.386,545,3.659,548,4.872,552,2.802,583,4.835,615,4.631,888,4.17,1118,3.343,1266,3.431,1330,5.174,1923,4.214,2013,2.637,2388,4.149,2886,8.879,2887,4.631,2888,7.512,2889,7.512,2890,6.446]],["t/1319",[7,0.505,16,1.192,17,2.531,23,2.966,55,3.483,85,2.218,105,2.003,113,2.335,116,2.39,135,3.65,164,1.759,171,1.74,196,2.538,213,2.606,219,1.982,225,2.289,232,2.329,236,2.682,272,2.622,275,3.157,283,2.949,313,1.564,322,2.003,323,2.583,342,4.186,366,4.005,398,2.256,401,2.448,421,4.005,438,3.323,471,2.654,479,4.459,491,1.93,543,5.002,548,3.941,552,2.267,614,1.179,702,4.313,703,5.106,757,1.578,828,2.67,904,5.863,909,5.336,937,3.825,1019,2.353,1080,3.563,1108,3.276,1155,5.106,1313,3.722,1374,5.982,1502,5.475,1507,9.605,1607,4.186,1638,3.307,1775,5.336,2096,5.336,2331,4.073,2890,5.215,3035,5.475,3036,5.475,4663,6.404,4664,6.404]],["t/1321",[17,3.857,23,3.258,24,1.58,84,1.057,98,3.429,160,3.421,161,6.643,219,3.021,232,3.55,275,3.021,530,4.364,607,3.74,894,7.376,977,3.559,2886,7.634]],["t/1323",[5,1.994,7,0.642,16,1.054,25,3.339,27,2.475,28,1.623,29,1.975,31,2.33,34,1.442,84,0.613,96,1.994,105,1.771,123,3.25,158,2.83,163,2.244,164,1.555,181,3.208,197,2.523,200,5.166,233,2.049,245,4.864,246,2.141,248,2.658,256,2.118,257,2.883,281,2.679,313,1.383,318,1.983,332,2.938,377,1.266,399,4.279,437,2.658,440,2.497,447,2.679,491,1.707,504,3.432,530,2.532,562,2.781,565,1.849,635,2.054,658,2.711,740,5.2,810,2.514,830,2.588,847,4.515,981,3.813,982,6.231,1035,4.864,1090,2.514,1097,4.351,1196,4.041,1198,4.152,1300,5.976,1479,3.897,2198,4.094,2586,4.611,2587,4.841,2595,3.602,2596,3.634,2606,4.429,2625,3.248,2633,6.786]],["t/1325",[7,0.897,25,3,27,2.702,28,1.73,109,1.58,272,3.836,281,4.433,318,2.281,343,3.333,565,3.059,568,2.683,596,4.22,635,3.399,2529,6.87,2633,7.808,2892,8.011]],["t/1327",[2,1.211,7,0.833,12,1.812,16,1.276,21,1.269,23,2.29,27,2.38,28,1.314,40,2.366,56,0.677,84,1.009,103,1.503,109,1.691,110,2.366,113,1.638,127,3.811,131,2.038,133,1.922,153,2.298,155,2.909,162,2.923,164,1.234,165,1.864,171,1.22,174,2.038,193,1.634,197,2.001,219,2.882,221,1.659,225,1.605,228,2.635,232,1.634,248,2.108,298,2.743,313,1.097,318,1.67,338,1.105,343,1.598,375,2.61,377,1.534,393,2.206,440,1.981,472,2.187,491,1.354,502,2.294,524,2.765,527,1.499,536,2.042,538,3.743,544,2.809,550,1.765,557,2.235,558,2.009,565,3.871,567,2.75,568,2.872,577,3.245,578,1.765,580,1.56,593,2.215,601,2.108,605,1.625,614,0.827,630,1.659,635,1.63,670,2.61,690,2.787,747,1.844,756,3.219,757,2.294,796,4.361,810,1.994,857,2.038,981,3.025,1021,4.668,1144,2.443,1320,3.451,1394,2.857,1504,3.165,1558,2.529,1754,2.915,2081,3.205,2099,2.703,2113,3.092,2133,3.84,2893,3.743,2894,3.743]],["t/1329",[2,1.664,5,2.173,17,2.438,26,3.213,31,1.507,34,1.571,41,2.798,56,0.855,84,0.937,85,2.137,101,3.283,103,2.064,107,2.922,109,2.049,125,3.516,129,3.685,170,2.557,174,1.833,200,3.056,216,3.14,228,2.741,236,1.622,257,3.14,261,4.385,278,2.21,302,2.84,353,1.682,369,3.827,398,2.173,408,3.124,444,2.674,527,2.587,536,2.577,553,3.266,565,2.014,601,2.895,607,3.317,630,2.278,663,3.767,693,3.796,757,1.52,810,2.739,846,3.069,945,3.308,957,3.585,1144,3.355,1220,3.538,1536,4.523,1558,3.473,1754,2.622,2142,3.069,2892,5.274,2895,5.853,2896,5.853,2897,5.853,2898,4.154,2899,5.853]],["t/1331",[4,2.423,27,2.41,28,1.102,31,2.48,84,1.1,96,2.751,109,1.902,123,3.459,125,4.452,138,3.15,164,2.789,171,2.758,174,3.017,175,4.272,179,2.613,278,2.799,297,4.272,347,5.155,377,1.747,416,3.596,544,4.885,568,2.237,607,2.993,614,2.076,630,2.885,635,2.834,818,3.922,1101,6.064,1182,3.725,1504,5.504,2010,3.533,2900,5.26]],["t/1333",[5,2.926,7,0.833,24,1.88,34,2.116,56,0.82,107,2.127,127,2.725,158,4.153,174,2.469,228,2.632,236,2.185,283,2.402,295,3.341,306,3.689,338,2.044,502,2.291,536,3.146,550,3.265,558,3.715,560,4.422,577,4.997,827,4.21,830,3.797,901,4.08,1007,5.38,1035,4.963,1318,6.923,1329,3.825,2816,7.443,2901,7.314]],["t/1335",[2,1.63,5,2.643,7,0.847,16,1.125,17,1.509,18,1.678,19,1.702,20,2.389,21,1.079,22,2.369,24,1.677,26,2.244,28,1.462,31,1.477,33,2.177,41,1.733,46,1.583,56,0.741,84,0.655,85,1.323,90,1.8,92,1.414,98,1.342,103,1.278,125,3.446,132,2.204,138,1.54,140,1.497,145,1.536,164,1.661,165,2.083,168,3.182,179,1.278,181,2.164,183,1.639,189,2.044,220,2.351,225,1.365,226,1.852,243,2.089,244,2.351,245,4.482,255,2.572,257,1.944,280,2.429,283,1.748,285,1.578,289,2.389,292,1.661,320,2.191,339,1.926,343,3.307,353,1.041,360,2.725,366,2.389,376,2.177,386,2.842,408,1.379,430,1.884,444,1.656,468,2.429,498,3.364,502,1.053,505,1.583,509,2.164,527,1.64,536,1.137,550,3.886,565,2.786,566,2.659,574,3.479,593,1.884,596,1.72,622,1.926,628,1.972,630,2.233,631,2.496,632,2.055,636,3.404,672,2.25,686,2.987,719,2.298,732,2.496,747,1.568,759,1.339,770,2.935,796,2.002,807,4.466,814,1.645,816,2.164,820,1.963,826,3.182,827,1.935,830,2.763,832,2.572,856,2.409,858,2.982,910,2.15,939,2.177,945,1.264,979,2.546,985,2.52,995,2.125,1005,2.496,1017,2.725,1065,3.479,1072,1.765,1114,3.362,1183,4.161,1202,2.351,1238,2.935,1253,2.281,1300,2.066,1329,1.758,1932,2.691,2254,1.972,2403,2.125,2450,2.546,2631,3.952,2816,5.288,2874,3.182,2902,3.11,2903,3.11,2904,2.762,2905,3.819,2906,3.624,2907,3.624,2908,3.11,2909,3.362]],["t/1337",[5,2.751,21,2.207,28,1.591,31,1.908,56,1.002,84,0.846,89,2.915,105,2.443,107,2.599,109,1.317,140,3.06,144,3.74,145,3.141,165,1.75,179,2.613,199,3.852,242,5.504,257,3.976,337,3.638,507,4.248,536,2.326,550,4.432,565,2.55,580,3.525,630,2.885,633,6.11,807,3.923,828,3.257,830,3.569,944,3.159,1062,3.756,1101,4.666,1102,4.666,1304,6.678,2403,4.346,2816,7.154]],["t/1339",[2,1.43,5,2.736,7,0.418,26,2.883,27,2.181,28,1.296,31,1.296,84,0.574,85,1.837,92,1.964,93,2.247,98,2.729,103,1.775,106,2.846,107,2.879,109,1.897,115,4.657,123,1.807,138,2.139,163,2.102,164,2.133,165,1.188,174,1.576,179,1.775,182,1.775,233,1.919,236,2.042,242,3.738,254,3.252,257,2.7,272,3.179,278,1.9,289,2.096,302,2.442,316,2.363,317,2.651,318,1.291,322,1.659,324,2.809,341,2.917,353,1.446,385,4.669,416,2.442,430,2.616,444,2.299,458,2.986,468,3.374,489,3.535,490,3.738,502,1.462,522,3.946,524,3.264,527,1.698,550,3.052,576,3.124,611,2.205,613,2.397,658,2.54,693,3.264,709,3.467,737,4.008,755,2.389,774,2.869,798,2.917,910,4.373,916,2.022,1036,4.008,1099,3.402,1100,2.102,1101,5.488,1110,4.008,1115,4.319,1178,3.062,1222,4.076,1494,4.535,2201,4.42,2910,4.831,2911,5.286,2912,5.033,2913,5.033,2914,5.033]],["t/1341",[7,0.761,24,1.171,25,3.09,34,1.842,84,1.045,103,2.42,107,3.09,109,1.627,113,2.637,162,2.542,164,1.987,174,2.15,224,3.064,228,2.291,236,1.902,291,4.26,433,4.176,447,3.422,476,3.504,502,1.994,517,4.384,536,2.154,550,2.842,565,2.361,568,2.764,690,4.487,757,1.782,840,3.281,857,3.281,981,4.871,995,4.024,1076,2.604,1271,4.802,1467,4.524,1781,2.442,1842,5.382,2101,5.768,2253,4.384,2254,3.735,2331,4.601,2928,3.81,3942,4.601,4665,7.233]],["t/1343",[2,2.273,7,0.921,8,5.734,21,2.381,84,0.913,92,3.121,96,3.754,98,2.961,157,4.866,165,1.888,197,3.755,203,2.017,313,2.059,336,3.429,364,5.939,375,6.194,383,5.868,444,3.653,532,4.395,582,7.023,593,4.157,605,3.05,636,4.745,764,5.676,1367,5.315,2577,5.615,4135,7.419,4666,8.428]],["t/1345",[306,4.814,975,6.957,1557,6.726]],["t/1347",[7,0.976,16,1.183,24,1.827,25,2.504,27,1.509,28,1.709,31,2.593,40,3.349,56,1.21,103,1.362,107,2.002,109,1.49,115,2.927,123,3.617,127,3.601,162,1.43,219,1.26,223,2.292,276,3.149,468,4.043,476,1.845,491,1.916,527,1.39,528,2.145,565,3.684,568,2.24,601,2.984,614,1.17,630,3.264,635,1.477,757,1.003,759,2.228,810,2.822,814,3.368,857,1.847,938,2.506,1035,2.432,1144,3.457,1163,2.742,1219,1.814,1754,3.324,2625,3.646,2799,4.661,2915,3.863,2917,3.863,2918,3.863,2919,8.387,2920,6.032,2921,3.863,2922,3.863,2923,7.421,2924,3.863,2925,3.863,2926,3.863,2927,3.863,2928,2.145,2929,6.032,2930,6.032,2931,6.032,2932,6.032,2933,6.032,2934,6.032,2935,6.032,4667,4.071]],["t/1349",[7,0.818,27,1.918,31,1.974,34,2.058,56,0.798,109,1.362,115,3.72,123,2.753,135,4.606,194,6.208,238,4.635,258,7.667,269,7.359,306,5.089,505,3.348,507,4.394,557,4.02,591,5.915,607,3.097,732,5.281,795,5.281,975,6.658,1121,4.897,1206,4.301,1325,5.924,1367,5.096,1477,6.733,1478,7.113,1479,5.561,1527,5.332,1932,5.694,3940,7.113,4668,8.08,4669,7.667,4670,8.08]],["t/1351",[1781,3.73]],["t/1353",[7,0.884,16,1.701,17,2.077,18,4.926,19,4.996,20,3.288,26,2.864,28,1.089,31,1.284,48,2.132,56,0.519,84,0.836,85,1.821,89,1.962,90,2.477,92,1.946,93,2.227,123,1.791,164,2.511,165,1.177,174,1.562,199,2.593,201,2.676,221,1.941,225,3.267,226,4.883,227,3.469,232,1.912,237,3.854,240,2.875,247,2.31,248,2.467,275,1.627,291,3.096,296,2.026,298,4.712,302,4.209,318,1.28,323,2.12,353,2.104,359,2.318,377,1.725,390,2.538,391,2.213,403,3.373,407,2.376,418,2.627,447,2.487,478,3.186,484,2.227,536,1.565,541,3.235,555,2.487,557,2.615,568,1.505,596,2.367,630,1.941,664,3.854,683,1.967,719,3.163,731,2.755,762,3.54,764,3.54,821,3.66,878,4.38,879,4.191,892,1.967,901,3.789,913,3.911,1206,2.798,1224,2.835,1243,4.039,1253,3.14,1265,2.755,1286,4.112,1390,3.618,1400,2.728,1401,3.096,2076,3.055,2250,2.367,2268,2.996,2297,4.38,2640,4.788,2653,5.256]],["t/1355",[7,0.887,19,3.001,21,1.903,34,1.715,56,0.665,84,0.729,103,2.253,156,4.584,162,3.232,174,2.002,236,1.771,249,5.011,278,2.413,296,2.595,302,3.101,306,2.99,326,4.584,338,1.657,353,2.508,375,3.914,408,2.431,430,3.322,463,7.317,685,3.747,833,3.839,850,6.13,901,6.383,1021,4.584,1313,3.914,1459,4.402,1856,5.758,2089,7.501,2543,6.391,2759,7.489,2760,8.096,2859,6.134,2860,6.391,2861,6.391,2862,5.929,4671,9.197,4672,6.735,4673,6.735,4674,6.735]],["t/1357",[1781,3.73]],["t/1359",[7,0.624,16,1.473,18,4.5,24,1.838,28,1.117,34,2.016,56,1.011,84,0.857,98,2.782,103,2.649,140,3.102,160,3.59,162,2.782,164,2.175,165,1.773,203,1.895,225,2.829,248,3.715,254,2.545,261,4.011,285,4.231,338,1.947,353,2.159,377,1.77,512,2.729,536,2.357,759,3.979,760,2.339,857,3.591,934,5.051,935,7.21,1394,5.035,3501,7.512,3502,7.512]],["t/1361",[16,1.981,24,1.724,116,3.972,807,4.113,2316,9.101]],["t/1363",[7,0.763,16,2.16,24,1.88,28,1.638,165,2.168,289,3.825,830,4.423,832,6.518,977,3.529,1181,5.663,1303,6.211,2403,5.385,2422,6.211]],["t/1365",[24,1.567,28,1.366,203,2.317,323,3.904,338,2.381,377,2.165,550,3.803,807,3.74,917,5.483,1955,8.065,3503,9.185,3504,8.521,3505,9.185,3506,9.185,3507,8.521,3508,9.185]],["t/1367",[2,1.958,7,0.687,12,1.954,16,1.621,20,3.03,27,2.068,28,1.535,49,1.089,56,0.86,96,2.558,107,2.786,109,1.835,127,2.381,138,1.954,153,3.714,164,1.331,165,1.085,170,2.145,173,5.823,174,2.158,193,3.77,197,2.158,217,2.761,224,2.052,236,1.909,256,1.812,257,2.466,261,2.455,272,1.983,289,2.87,313,1.774,318,1.768,322,1.515,323,3.513,353,1.321,394,2.421,397,2.014,405,1.541,407,3.282,418,2.421,426,1.789,432,4.58,433,2.797,437,2.274,484,2.052,485,3.661,491,1.46,507,2.634,550,4.073,565,2.371,568,2.494,632,5.205,635,1.757,647,1.817,743,4.792,807,1.872,812,4.84,829,2.552,916,1.846,977,1.766,1007,3.137,1009,3.297,1062,2.329,1075,3.806,1094,2.421,1102,2.894,1265,2.539,1285,2.065,1303,3.109,1309,2.815,1467,3.03,1913,2.727,1991,3.229,2569,3.863,3030,3.944,3509,4.597,3510,4.142,3511,4.142]],["t/1369",[2,2.149,16,2.119,18,3.502,28,1.451,56,0.787,96,2.807,97,3.564,105,2.493,107,2.633,132,3.345,145,3.205,170,3.364,193,3.741,313,1.947,318,1.94,338,1.96,405,2.535,507,4.334,550,4.728,596,3.589,989,4.487,995,4.434,1007,5.161,1061,7.259,1062,3.832,1102,4.761,1271,3.966,1467,4.985,3510,6.814,3511,6.814]],["t/1371",[16,1.983,23,2.813,24,1.365,25,2.698,28,1.504,49,1.894,56,1.052,109,1.421,132,2.475,228,2.67,297,4.609,313,2.059,398,2.969,476,2.446,491,2.54,502,2.939,536,2.51,545,3.895,577,5.812,580,2.926,916,3.213,934,4.157,1040,5.147,3512,9.383,3513,7.419,3514,7.419]],["t/1373",[0,3.822,12,2.67,28,0.934,49,1.488,56,1.103,107,1.695,109,1.75,112,5.801,172,4.056,193,4.259,217,3.774,391,2.787,502,1.826,525,3.12,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,934,5.511,944,4.199,1039,7.374,1040,6.824,1075,3.47,1077,3.705,1083,3.21,1175,4.779,1332,4.413,1337,6.059,3515,5.829,3516,9.138,3517,5.829,3518,5.829,3519,5.829]],["t/1375",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,934,4.799,945,2.423,1039,7.685,1040,5.943,1099,4.614,1100,3.856,1112,5.771,3520,6.447,3521,6.447]],["t/1377",[1781,3.73]],["t/1379",[2,1.995,7,0.922,16,1.645,24,1.198,25,2.369,27,1.179,28,1.55,29,1.733,31,1.213,56,0.49,84,0.958,89,1.854,103,1.662,105,1.554,106,1.82,109,0.838,116,1.854,123,1.693,124,3.133,127,2.9,132,1.459,160,1.741,162,1.745,165,1.113,167,3.817,170,1.468,174,2.199,175,2.716,181,2.814,193,2.691,197,2.213,225,1.775,228,1.573,275,1.537,276,2.461,313,1.213,318,1.801,334,2.814,338,1.82,343,1.767,377,1.655,390,2.398,440,3.262,471,3.664,476,2.148,490,3.5,491,1.497,502,1.37,536,2.633,550,1.952,553,2.63,558,3.308,565,3.42,568,3,576,2.926,577,2.35,580,1.725,593,3.649,601,2.331,607,1.904,663,3.034,664,3.642,670,2.887,683,1.858,734,2.198,747,2.04,757,2.414,796,2.604,810,2.205,843,2.716,865,3.345,867,3.817,901,2.439,1072,2.296,1144,2.701,1217,3.754,1255,4.139,1266,2.153,1319,3.381,1462,2.687,1754,2.111,2081,3.545,2311,3.345,2936,4.714,2937,4.967,2938,3.961]],["t/1381",[2,1.125,4,1.294,5,1.47,7,0.873,27,2.126,28,0.589,31,1.941,36,1.84,42,3.059,49,2.179,56,0.412,84,0.97,92,2.941,93,3.365,106,2.375,109,1.093,131,1.893,134,2.076,138,3.911,144,1.998,153,2.134,165,1.451,171,1.134,172,3.939,173,2.233,181,2.364,188,3.017,193,1.517,207,1.703,216,2.124,224,2.745,228,2.052,236,1.097,256,1.561,276,3.21,281,1.974,288,2.754,289,2.561,292,1.815,302,1.921,318,1.016,334,2.364,337,1.943,338,1.594,339,4.007,365,2.114,376,2.378,377,1.449,409,2.114,441,2.269,468,2.654,500,3.065,502,1.15,525,1.966,527,0.912,552,1.477,555,1.974,568,2.777,578,1.639,579,3.017,580,1.449,601,4.552,620,3.584,629,2.654,632,2.245,636,3.648,638,2.51,691,5.333,715,2.144,728,2.905,732,2.727,743,2.754,774,2.257,807,1.612,810,4.306,812,5.295,816,2.364,858,2.058,872,2.702,894,3.153,1009,2.84,1070,2.378,1072,1.928,1078,3.017,1079,4.053,1188,2.654,1271,2.076,1285,3.387,1302,2.529,1303,2.677,1387,4.897,2073,5.35,2190,2.335,2217,3.206,2431,2.977,2561,3.567,2939,3.567,2940,6.149,2941,4.172,2942,3.8,2943,3.959]],["t/1383",[2,1.29,7,0.911,21,1.352,27,2.808,28,1.455,30,4.5,31,1.169,36,2.11,49,1.075,84,0.518,92,1.772,101,2.547,109,1.458,116,1.786,126,2.31,138,2.902,153,2.447,162,1.681,163,1.896,165,1.072,171,1.955,172,3.572,173,4.627,174,2.57,184,3.616,188,3.46,193,3.145,196,1.896,223,2.694,224,3.048,228,1.516,246,1.809,282,2.602,304,1.008,313,1.169,320,2.745,353,1.305,407,2.163,416,2.203,478,2.9,502,1.319,505,1.983,525,4.074,552,1.694,568,2.476,576,2.818,578,1.88,597,3.716,599,2.459,601,2.246,608,2.483,614,0.881,620,2.647,631,3.127,632,4.651,638,4.329,658,2.291,675,3.044,725,2.436,741,3.098,807,3.341,810,2.124,818,2.402,819,3.372,830,3.951,831,3.018,832,5.823,858,2.36,977,1.745,985,3.158,1070,2.727,1075,3.771,1079,2.993,1093,4.898,1175,2.203,1181,2.799,1188,3.044,1370,3.815,1843,3.222,2257,3.331,2331,3.044,2384,3.257,2391,3.56,2434,3.257,2945,4.541,2946,3.815,2947,3.987,4675,4.785]],["t/1385",[0,6.386,7,0.872,21,3.125,27,2.851,109,2.025,127,3.627,179,3.701,193,3.252,343,3.18,437,4.197,557,4.449,631,7.229,757,2.203,816,5.065,856,5.639,2484,6.871]],["t/1387",[7,0.77,84,0.798,109,1.243,133,3.152,160,3.843,165,1.651,221,3.609,228,3.095,275,3.024,320,5.605,377,1.648,426,2.722,430,3.635,445,4.648,476,2.139,491,2.221,498,4.1,502,2.032,527,2.397,565,2.406,568,2.798,635,2.673,811,4.572,858,3.635,976,5.483,984,3.152,1036,5.569,1080,4.1,1141,8.668,1202,4.536,1229,5.764,1404,5.876,1987,5.193,2081,6.973,2101,5.876,2479,5.764,2577,3.882,2948,6.712,2949,6.301]],["t/1389",[7,0.528,21,1.892,28,1.293,31,1.636,33,3.817,37,4.463,42,4.91,46,2.775,56,0.661,84,0.725,90,3.155,92,2.479,93,2.837,106,2.454,107,2.345,109,1.893,115,4.218,126,3.233,153,3.425,165,1.5,189,3.583,216,3.409,224,3.881,272,2.741,318,1.63,353,1.826,407,4.141,426,3.383,502,1.846,513,3.155,620,3.704,629,4.259,635,2.429,658,3.207,769,2.573,858,3.303,984,2.865,1099,4.495,1122,5.895,1162,3.726,1175,3.083,1178,3.866,1180,4.662,1183,4.609,1331,5.879,1733,4,2271,4.779,2385,5.58,2434,4.558,2584,4.719,2875,4.983,2911,4.558,2950,5.145,2951,3.972,2952,5.238]],["t/1391",[1,2.815,2,1.958,7,0.817,8,2.606,28,0.683,31,1.183,33,4.139,34,1.234,36,2.136,39,1.971,41,2.198,46,2.007,49,1.089,50,2.834,56,0.478,84,0.944,90,3.422,91,4.036,92,3.583,93,3.076,94,5.886,96,2.558,97,2.166,109,0.817,130,2.761,132,1.423,144,2.32,145,2.92,164,1.995,165,1.085,172,2.515,179,1.621,186,2.283,203,1.16,207,1.977,216,2.466,224,2.052,228,1.534,236,1.274,240,2.649,272,1.983,283,2.519,292,2.107,313,1.774,343,1.723,351,1.753,390,2.339,407,2.19,408,1.749,437,2.274,439,3.166,440,3.202,441,2.634,447,2.292,476,3.009,482,3.247,568,2.771,578,1.903,580,1.682,593,2.389,599,5.965,601,2.274,619,4.66,620,4.017,658,2.32,670,2.815,675,3.081,694,2.634,715,4.477,737,3.661,757,1.194,769,1.862,810,2.151,812,3.229,822,2.894,832,3.262,858,2.389,977,1.766,1099,1.794,1162,4.846,1175,2.23,1299,3.373,1368,4.66,2073,2.894,2271,3.457,2816,3.414,2953,4.412,2954,4.036,2955,4.597]],["t/1393",[1781,3.73]],["t/1395",[7,0.804,31,1.921,56,0.776,84,0.852,123,2.679,162,2.763,215,5.851,216,4.003,219,2.434,275,2.434,283,2.274,306,3.491,318,1.914,340,5.241,390,3.796,463,4.731,467,6.151,527,1.719,555,3.72,565,2.567,567,3.153,568,2.252,604,5.189,683,2.942,692,6.27,693,4.84,734,3.479,738,6.922,798,4.325,802,3.181,814,3.386,843,4.3,850,5.241,854,5.765,856,4.959,858,3.878,868,7.162,901,3.861,993,3.181,1390,5.412,1480,5.942,2089,5.241,3057,7.462]],["t/1397",[7,0.787,10,4.115,19,3.39,22,4.72,23,2.54,24,1.232,27,1.806,34,2.541,88,3.426,94,4.115,103,2.546,106,3.657,123,2.593,158,3.803,220,4.683,221,2.81,225,2.719,226,3.689,313,1.859,332,3.948,339,3.837,341,4.184,384,6.34,387,5.297,390,3.673,391,3.203,491,2.293,495,3.673,506,4.513,511,2.425,512,2.623,513,3.585,519,6.34,599,3.91,694,5.427,773,7.806,855,4.683,898,4.115,1159,6.067,1323,5.236,1454,6.34,1455,7.22,1456,4.008,1459,4.973]],["t/1399",[7,0.881,24,1.623,26,2.843,28,1.414,34,1.95,41,3.474,46,3.173,47,4.83,56,0.99,90,4.722,92,4.136,93,3.244,94,4.142,96,2.697,97,3.424,132,2.249,186,3.608,216,3.898,272,3.135,283,2.214,353,2.088,418,3.827,491,2.308,509,4.338,568,2.193,597,5.175,599,3.936,601,3.594,611,3.183,614,1.41,619,4.914,620,4.236,715,5.15,901,3.761,1161,6.106,1162,4.261,1368,4.914,3006,6.106]],["t/1401",[7,0.818,16,1.503,27,1.918,28,1.617,31,1.974,34,2.058,84,0.875,88,3.639,89,4.277,123,2.753,145,3.249,174,2.401,232,2.939,244,4.973,254,2.598,278,2.895,283,2.336,289,3.193,294,4.346,306,3.587,318,1.967,484,3.423,485,6.106,512,2.786,558,3.613,575,3.838,780,4.301,857,3.665,896,5.924,897,5.096,942,5.924,993,3.268,1022,4.444,1172,4.861,2250,3.639]],["t/1403",[4,2.077,7,0.823,10,3.622,17,2.646,23,3.748,26,2.486,28,1.293,31,2.551,34,1.705,56,0.661,84,0.992,103,2.241,123,3.558,219,2.836,232,4.083,236,1.761,254,2.153,283,3.019,384,5.58,478,4.059,484,2.837,485,5.061,511,2.135,530,2.994,545,3.095,548,5.638,552,3.696,558,2.994,567,3.673,568,1.918,601,3.143,614,1.233,629,4.259,685,3.726,814,2.883,882,5.34,889,6.099,904,4.419,1172,4.029,1237,4.336,1266,4.526,1330,5.988,1374,4.51,1506,4.297,2250,3.016,2871,5.34]],["t/1405",[2,2.323,7,0.852,24,1.912,28,1.215,84,0.933,165,1.929,248,4.043,283,2.491,285,3.558,313,2.104,397,3.58,512,2.97,536,2.565,565,2.812,567,3.453,568,2.467,757,2.909,759,3.019,760,2.545,843,4.711,1219,3.838,1285,3.673,2017,3.626,2368,6.619,3058,7.583,3059,5.182]],["t/1407",[7,0.739,16,1.743,24,1.843,28,1.322,56,0.925,84,1.015,132,3.343,145,3.768,228,2.968,502,2.584,565,3.059,568,2.683,683,3.506,769,3.601,1251,6.687,1329,5.241,2311,6.31]],["t/1409",[7,0.751,16,1.772,27,2.26,31,2.326,40,5.016,56,0.94,107,2.943,109,2.082,123,3.245,163,3.774,527,2.082,597,4.917,635,3.455,658,4.56,814,4.1]],["t/1411",[7,0.789,16,2.203,28,1.671,109,1.688,132,3.478,170,2.957,283,2.894,1271,4.981,1481,5.356]],["t/1413",[23,2.875,24,1.395,25,3.46,28,1.215,32,5.387,49,1.936,56,0.85,98,3.027,132,2.53,157,4.973,165,1.929,236,2.265,337,4.011,339,4.344,491,2.596,502,2.98,513,4.059,544,5.387,558,3.851,565,2.812,577,5.112,757,2.122,795,5.63,796,4.515,1019,3.165,1155,6.868,2017,3.626,2217,6.619]],["t/1415",[28,1.344,107,2.438,109,2.082,115,4.384,132,2.797,174,2.83,236,2.504,254,3.062,360,6.795,507,5.179,550,3.742,635,3.455,658,4.56,1099,3.526,1102,5.688,3060,9.036]],["t/1417",[2,2.106,7,0.8,16,1.453,28,1.432,56,0.771,109,1.712,126,3.771,132,2.294,138,3.15,172,3.907,190,4.014,193,3.692,224,3.309,288,5.155,289,3.087,313,1.908,337,3.638,338,1.921,339,3.94,408,2.819,491,2.354,552,2.765,611,3.247,614,1.438,632,4.202,694,4.248,755,3.518,806,6.11,810,3.468,819,5.504,830,3.569,872,5.058,977,2.848,1070,4.452,1175,3.596,1302,4.734,2293,4.569,2391,5.812,3061,7.114]],["t/1419",[7,0.789,27,2.376,29,3.492,109,1.688,127,3.282,254,3.218,338,2.462,353,2.729,390,4.833,715,5.144,1400,5.194,1401,5.896]],["t/1421",[7,0.839,58,7.697,275,3.295,564,6.893,1625,8.045]],["t/1423",[7,0.684,13,6.787,84,0.94,228,2.749,283,3.139,294,4.668,300,5.259,332,4.503,395,3.429,399,6.558,476,2.519,542,7.23,543,6.112,742,5.427,885,6.456,977,3.164,1035,5.183,1272,6.667,1282,7.066,1290,5.111,1291,6.667,1292,5.844,1566,6.192,1802,5.259,2598,5.341,3063,6.274,3064,6.558,3065,8.234]],["t/1425",[7,0.872,11,7.13,17,3.534,18,3.929,19,4.928,28,1.262,158,5.528,318,2.177,332,5.74,391,3.764,994,3.85,1090,3.97,1290,5.267,1310,8.463,1314,6.086,1315,7.872,1316,7.872,1317,10.074,2250,4.027]],["t/1427",[7,0.973,135,5.428,174,2.83,233,4.16,278,3.412,363,6.795,444,4.128,484,4.034,578,3.742,712,5.122,1206,5.068,1253,5.688,1263,7.196,1264,6.224,1309,5.534]],["t/1429",[1781,3.73]],["t/1431",[2,1.216,4,1.399,7,0.996,15,3.041,34,1.148,48,1.829,49,2.258,56,0.445,58,6.746,68,1.71,84,0.488,85,1.562,97,2.016,103,2.79,113,2.508,129,2.693,160,1.581,164,1.239,176,5.601,178,3.527,186,3.241,231,3.26,244,2.775,275,1.396,278,1.616,296,1.738,304,0.95,313,1.102,326,3.069,334,4.724,356,4.107,381,2.133,390,2.177,392,2.797,396,3.527,398,1.588,401,4.209,416,3.167,481,2.693,484,1.91,564,2.92,575,4.432,580,1.566,590,2.426,607,1.728,614,0.83,636,2.539,683,1.687,684,4.349,723,2.459,736,1.923,944,2.782,1090,3.054,1100,2.726,1172,2.713,1220,2.587,1328,2.352,1409,2.976,1411,2.92,1538,6.408,1565,5.806,1625,6.302,1923,2.4,2013,1.502,2086,3.306,2142,2.244,2450,3.005,2510,3.757,2586,3.672,2637,3.672,2729,3.355,3046,4.279,3070,5.74,3304,4.107,3539,5.881,3540,4.279,3541,4.279,3542,6.527,3543,4.279,3544,4.279,3545,4.279,3546,4.279,3547,4.279,3548,4.279,3549,4.279,3550,4.279,3551,4.279,3552,4.279,3553,3.218,3554,3.218,3555,3.595,3556,4.539,3557,4.279,3558,4.279,4676,4.509,4677,4.279,4678,4.509,4679,4.279]],["t/1433",[4,2.065,7,0.719,15,3.796,16,1.239,21,1.881,24,1.478,34,1.696,98,2.34,145,2.677,158,3.328,159,6.082,181,3.772,189,3.563,196,2.639,203,1.594,247,2.926,261,3.374,276,3.298,279,2.877,338,1.638,343,2.368,365,3.374,377,1.489,393,3.27,401,3.488,402,5.208,403,4.273,408,3.294,412,4.954,513,3.137,541,4.098,580,2.312,611,2.768,669,5.422,723,2.38,742,4.164,764,4.484,1072,3.077,1352,4.484,1364,3.328,1376,4.484,1454,5.548,1461,5.422,1511,4.352,1802,7.346,1923,3.544,1994,4.636,2013,2.218,2385,5.548,2778,5.208,3559,7.276,3561,5.693,3562,5.693,3563,5.548,3564,6.318,3565,4.484,4680,6.064,4681,7.175]],["t/1435",[7,0.895,15,2.886,49,1.779,56,0.782,159,5.276,322,2.476,369,4.911,401,3.026,412,5.89,438,4.108,607,3.925,723,2.829,1090,3.515,1175,3.645,1400,4.108,1601,4.729,1704,8.533,1802,6.207,1923,4.214,2013,3.411,2085,6.446,2141,4.049,2142,3.939,2500,6.969,2722,4.872,2723,5.724,2724,5.724,2726,6.969,2778,6.192,3562,6.768,3567,7.512,3568,7.512,3569,7.512,3571,7.512,3572,7.512,4682,7.916,4683,8.53]],["t/1437",[7,0.92,12,3.936,13,9.125,34,2.485,48,3.959,465,6.795,715,5.016,885,8.68,1243,7.499,1290,5.749,1291,7.499,2096,8.132]],["t/1439",[7,0.687,16,1.16,31,1.523,34,1.588,48,2.529,58,4.508,59,9.917,112,3.088,116,2.326,133,2.667,160,2.185,176,10.423,178,4.876,275,1.93,283,2.521,300,6.094,324,3.301,334,7.329,351,2.256,353,1.7,372,2.332,465,4.34,483,5.498,532,3.251,580,2.164,590,4.69,604,4.114,684,3.251,720,5.678,880,4.114,1090,2.768,1729,4.971,1730,5.076,2113,4.29,2956,5.678,3063,4.508,3064,4.711,3573,5.916,3574,6.234,3575,5.916,3576,4.711,3577,5.916,3578,9.541,3579,6.234,3580,6.234,3581,5.916,3582,5.916,3583,5.916,3584,5.916,3585,5.916,3586,8.273,4684,6.717]],["t/1441",[7,0.938,491,3.043,532,6.207,683,3.777,1625,7.63,3587,11.294,3588,9.58,3589,9.58]],["t/1443",[164,2.822,225,3.672,226,4.98,532,5.357,2114,9.043,3590,8.783,3591,9.748,3592,9.748,3593,9.748]],["t/1445",[1781,3.73]],["t/1447",[7,0.81,16,1.911,34,2.616,164,2.822,261,5.205,484,4.352,532,5.357,747,4.218,3067,9.748]],["t/1449",[7,0.822,15,2.966,16,1.514,34,2.072,49,2.342,56,0.803,84,0.881,85,2.818,103,2.722,192,4.792,203,2.495,228,2.577,261,5.281,318,2.537,401,3.983,481,4.86,484,3.447,607,3.118,614,1.498,723,2.908,977,2.966,1076,2.929,1224,2.989,1913,4.581,2000,5.088,2013,2.71,2141,4.161,2722,5.007,2898,5.479,3068,7.72,3069,7.72,3070,7.173]],["t/1451",[7,0.918,49,1.881,203,2.003,278,2.998,285,3.457,304,1.763,318,2.983,398,3.737,401,3.198,527,1.829,565,2.732,568,2.396,759,2.933,760,3.135,927,5.522,1019,3.074,1089,5.11,1236,5.577,1297,5.144,1312,5.759,1313,4.863,2142,5.28,2340,5.15,2388,4.386,3071,6.43,4685,7.621]],["t/1453",[7,0.913,19,3.68,21,1.631,24,1.974,28,1.571,34,2.68,49,1.298,84,1.206,85,2,103,3.226,105,1.806,132,2.832,162,2.029,165,1.293,196,2.288,203,1.382,228,1.829,232,2.1,236,1.518,254,1.856,261,4.185,285,2.385,372,2.16,377,1.291,397,2.4,398,2.909,471,2.393,527,1.805,530,2.582,536,1.719,565,3.635,568,1.653,580,2.867,613,2.61,630,2.132,757,1.423,759,3.902,760,3.289,857,3.746,916,3.148,1019,2.121,1182,3.938,1550,4.296,1558,3.251,1842,4.296,2099,4.969,2142,2.873,2250,2.6,2253,3.5,2312,5.259]],["t/1455",[2,2.093,7,0.612,16,1.881,24,1.82,27,1.842,28,1.743,34,2.574,107,2.878,109,2.008,116,2.895,132,2.969,135,4.423,145,3.12,170,2.292,228,3.202,236,2.04,261,5.122,318,1.889,353,2.756,372,2.903,528,4.088,558,3.469,596,3.494,799,5.864,906,5.024,1182,3.7,2250,5.063]],["t/1457",[7,0.771,19,3.746,21,1.673,24,0.958,25,2.691,27,2.526,28,0.835,31,1.446,40,3.118,48,2.401,56,0.584,84,0.911,96,2.085,107,2.502,109,1.969,123,2.017,124,3.733,127,2.757,135,3.374,158,2.959,160,2.947,219,1.832,221,3.104,226,2.87,272,3.441,275,1.832,278,2.121,283,1.712,316,2.637,318,2.736,360,4.224,394,2.959,395,2.339,397,2.461,471,3.484,527,2.326,550,3.303,555,2.8,557,2.945,561,5.211,562,2.907,565,1.933,567,2.373,568,1.695,605,3.042,607,2.269,630,2.186,654,5.211,658,2.834,729,4.224,757,1.459,870,4.63,974,3.615,995,3.293,1141,4.224,1202,3.643,1224,2.175,1289,3.765,1628,4.548,1773,4.474,1781,1.999,2625,3.396,3062,5.061,3072,5.061]],["t/1459",[7,0.689,16,2.029,24,1.415,26,4.049,34,2.226,49,1.965,145,3.515,159,5.827,164,2.401,261,4.43,276,4.33,300,5.299,359,3.854,392,5.423,393,5.356,399,6.607,403,5.61,611,3.634,742,5.467,744,4.983,764,7.345,1010,5.827,1802,5.299,3063,6.321,3064,6.607]],["t/1461",[1,6.551,7,0.889,8,6.064,24,1.493,32,5.768,49,2.073,106,3.38,133,3.945,225,3.296,381,4.363,393,4.529,434,6.147,436,6.581,555,4.363,590,4.961,611,3.834,1212,7.885,2598,5.676,4686,6.862]],["t/1463",[7,0.852,15,3.141,26,3.197,28,1.215,47,5.432,56,0.85,84,0.933,90,4.059,92,3.189,93,4.578,97,3.851,130,4.91,138,3.474,229,5.109,261,4.365,272,3.526,367,4.385,408,3.109,440,3.798,441,4.684,484,3.649,580,2.991,599,4.427,605,3.117,614,1.586,619,5.527,620,4.764,828,3.592,1368,5.527]],["t/1465",[7,0.689,16,1.627,56,0.863,84,0.947,88,3.937,109,1.474,133,3.74,134,4.35,189,5.836,192,5.149,194,6.717,200,4.33,229,5.185,248,4.103,254,2.811,283,2.528,284,5.887,353,2.384,436,6.238,545,4.04,918,5.61,1100,3.464,1123,5.149,1206,4.653,1365,5.047,1400,4.537,1558,4.922,2151,6.971]],["t/1467",[1781,3.73]],["t/1469",[7,0.867,16,0.836,21,1.269,24,1.624,26,2.546,27,1.628,31,2.45,46,1.861,47,2.833,56,0.444,96,1.582,107,1.15,109,0.757,123,3.418,124,2.833,132,1.319,145,2.758,162,2.41,165,1.536,174,1.335,187,3.127,189,2.404,193,1.634,201,2.287,213,1.828,215,5.103,216,2.287,226,4.034,228,1.423,229,2.664,232,1.634,236,1.181,244,2.765,245,2.683,256,1.681,273,3.582,275,1.39,276,4.613,280,2.857,283,2.406,315,2.134,351,1.625,353,1.87,359,1.981,377,1.534,390,4.017,393,2.206,409,2.276,430,2.215,444,1.947,458,3.862,463,5.006,467,5.365,478,2.723,493,4.262,565,2.716,568,2.872,604,5.49,607,1.721,611,1.867,621,2.309,622,2.266,628,2.32,635,1.63,683,2.566,693,2.765,718,3.84,765,2.664,767,3.451,802,1.817,814,1.934,833,3.909,848,4.091,850,2.994,851,3.743,852,4.091,855,2.765,856,4.325,859,2.331,860,3.84,881,2.809,1022,2.47,1119,3.513,1144,2.443,1206,2.391,1258,3.84,1289,2.857,1313,3.986,1790,3.743,1791,3.84,2089,2.994,2431,3.205,2546,3.658,2561,3.84,3329,3.658,3495,4.262,3496,4.262,3497,4.262,3498,4.262,3499,6.858,4687,4.492,4688,4.492,4689,4.492,4690,4.492]],["t/1471",[1781,3.73]],["t/1473",[24,1.696,28,0.95,41,3.055,56,1.163,99,4.247,105,2.106,107,2.881,109,1.986,127,2.208,139,3.022,160,3.224,165,1.509,170,1.99,172,2.333,182,2.253,193,3.345,203,1.612,217,3.839,318,2.239,338,1.657,343,2.395,351,2.437,377,1.506,502,2.536,536,2.006,550,2.646,565,2.199,635,2.443,725,3.429,759,2.361,994,2.9,1076,2.425,1099,3.405,1101,4.023,1112,3.995,1219,3.001,1285,3.921,1354,4.635,1508,5.087,1959,4.386,2277,7.053,3021,7.663,3024,5.37,4574,5.37,4691,5.484]],["t/1475",[0,2.115,1,2.129,2,0.988,7,0.576,12,1.478,21,1.035,28,1.03,34,0.933,56,1.285,84,0.397,96,1.291,105,1.146,107,1.498,109,2.042,123,1.249,124,2.311,127,1.202,133,1.567,144,1.755,160,3.404,170,1.083,171,0.995,172,1.269,197,1.633,198,1.784,213,1.491,228,1.161,277,1.883,279,1.583,291,2.158,292,1.594,295,2.352,297,2.004,302,1.687,313,0.895,318,0.892,322,1.146,338,2.507,343,3.454,365,1.857,377,1.308,395,1.448,397,1.523,426,1.353,430,1.807,437,1.72,502,1.01,513,1.727,527,0.801,557,1.823,591,3.334,605,1.326,614,0.675,628,1.892,647,4.093,712,1.971,747,2.998,765,2.173,801,2.726,833,2.089,838,1.848,936,2.468,940,2.984,994,3.144,1037,5.071,1099,1.357,1100,1.452,1103,3.226,1178,2.115,1180,4.073,1235,4.289,1354,5.737,1462,1.982,1508,5.039,1693,2.144,1931,4.23,1959,4.861,2010,1.275,2256,2.494,2277,8.06,2904,2.65,2951,3.47,3021,8.092,3023,2.726,3024,5.822,3968,4.353,3971,2.726,3972,2.395,4333,3.226,4691,5.945,4692,2.866,4693,3.664,4694,3.226,4695,3.337]],["t/1477",[5,2.645,24,1.792,28,1.06,31,1.835,56,0.977,68,2.848,84,0.814,95,4.659,112,3.721,165,2.216,203,1.798,236,1.975,256,2.81,275,3.063,279,3.245,291,4.424,313,1.835,317,3.754,367,3.824,397,3.122,517,4.553,536,2.947,568,2.151,759,3.468,760,2.923,769,4.252,774,4.062,807,2.902,814,3.234,963,4.553,1329,4.556,1365,4.336,1508,6.121,1733,4.486,2577,3.957,3890,6.612,4692,5.875,4696,6.422]],["t/1479",[7,0.758,21,1.623,24,1.799,28,1.568,56,0.949,84,1.041,109,1.388,138,2.317,160,3.68,164,1.578,165,1.843,174,1.708,182,1.922,228,1.82,233,2.079,236,2.164,289,4.149,292,2.499,314,6.562,405,1.828,407,3.72,505,2.381,511,2.623,527,2.102,536,2.863,557,4.095,635,2.084,759,2.014,760,3.102,769,2.208,778,3.623,807,3.18,814,3.544,963,4.988,994,3.544,1062,3.957,1089,5.026,1285,4.1,1329,3.789,1399,4.951,1508,5.807,1557,3.564,2277,7.07,2588,5.379,2594,6.545,2898,3.869]],["t/1481",[2,2.121,56,1.225,84,1.104,96,3.985,107,2.013,109,2.091,130,4.482,160,3.574,164,2.16,196,3.116,313,1.921,338,1.934,377,1.758,395,3.107,647,3.825,653,2.475,712,4.23,846,2.414,994,3.386,1037,5.63,1235,7.476,1959,3.75,2010,2.737,2268,4.482,2277,7.62,4694,6.922]],["t/1483",[56,1.311,109,1.674,338,2.441,1037,4.938,1959,4.733,2277,6.684,3023,8.767,4697,9.039]],["t/1485",[2,2.358,12,3.525,21,2.47,56,1.308,96,3.079,109,1.839,291,5.149,313,2.136,338,2.683,653,2.751,1037,5.427,1142,5.769,1959,5.201,2277,7.345,3021,7.284,3023,6.504,3858,4.537,4697,7.962]],["t/1487",[28,1.518,29,2.983,56,1.302,109,1.442,302,3.937,324,5.696,338,2.646,395,3.379,628,4.416,712,4.6,724,5.237,994,3.682,1037,4.255,1235,6.27,1354,5.885,1959,4.078,2076,4.969,2277,5.758,2387,6.688,3024,6.818,3893,6.362,4691,6.963]],["t/1489",[2,1.915,7,0.56,56,0.941,84,0.769,107,2.441,109,1.939,127,2.329,139,4.829,170,3.649,172,2.46,182,2.376,198,3.458,203,1.7,246,2.685,253,4.273,313,1.735,338,2.829,351,2.57,392,4.406,502,1.958,527,2.084,536,2.115,583,4.337,759,2.489,760,3.398,820,3.65,994,4.634,1076,2.557,1099,2.629,1354,4.888,1508,3.928,1754,4.052,1959,3.387,2191,4.687,2259,4.734,2525,6.252,3024,5.663,4691,5.783]],["t/1491",[5,2.788,24,1.282,28,1.117,31,1.934,56,1.011,68,3.001,84,0.857,95,4.911,107,2.027,112,3.921,165,2.294,170,2.339,256,2.962,275,3.17,279,3.42,291,4.663,313,1.934,317,3.957,367,4.03,397,3.291,517,4.798,536,2.357,568,2.267,759,3.59,760,3.025,769,4.362,774,4.282,963,4.798,1062,3.806,1365,4.57,1508,5.664,1733,4.729,2577,4.17,4692,6.192,4696,6.768]],["t/1493",[7,0.734,21,1.936,28,1.491,56,0.919,84,1.008,107,1.754,109,1.156,138,2.764,160,3.704,164,1.882,165,2.085,170,2.749,174,2.037,203,1.64,228,2.171,233,2.48,236,1.802,289,2.708,292,2.981,405,2.18,505,2.84,511,3.369,527,2.31,536,2.041,635,2.486,759,2.402,760,3.122,769,2.633,772,4.522,778,4.322,807,2.648,814,2.951,963,4.154,1062,3.295,1175,3.155,1285,4.506,1329,3.155,1508,5.148,2277,6.267,2594,7.193,2898,4.615,2901,6.033,4698,9.306,4699,6.853]],["t/1495",[4,0.328,7,0.811,12,0.427,15,1.01,16,1.364,17,0.418,18,1.799,21,0.557,23,1.159,24,1.187,25,0.631,27,0.657,28,1.14,29,0.966,31,1.136,34,1.426,46,0.438,47,0.667,48,0.429,49,1.435,56,1.099,68,2.421,84,0.692,85,1.784,96,0.695,97,0.473,100,0.473,101,0.563,103,2.011,105,0.866,107,1.722,108,0.546,109,1.495,112,1.372,113,0.386,116,0.395,123,1.909,124,0.667,127,1.837,132,0.311,133,0.453,138,0.427,140,0.773,145,0.793,157,0.611,158,2.324,160,1.435,161,3.165,164,1.125,165,0.62,169,0.579,170,0.818,171,0.287,174,0.314,177,0.511,193,1.007,196,0.781,200,0.977,203,0.831,213,1.127,219,0.61,221,0.391,224,0.835,225,0.378,228,0.625,236,0.728,246,0.4,247,0.866,248,2.821,249,0.787,253,1.186,254,0.634,261,0.536,272,0.433,275,1.074,278,0.706,285,1.144,286,0.641,287,0.755,296,0.408,298,0.646,304,0.415,305,0.673,313,0.482,316,0.879,317,0.529,318,0.845,322,1.086,338,0.485,343,0.701,348,0.679,351,1.002,353,0.755,359,0.466,366,0.662,377,1.152,382,0.656,387,0.737,388,0.765,390,1.337,395,1.618,401,3,405,0.337,407,0.478,410,1.301,418,0.986,421,2.171,437,0.925,438,1.023,470,0.765,471,2.321,476,1.008,479,0.737,481,0.632,482,0.473,484,0.835,491,1.552,498,0.589,502,0.764,512,0.955,517,0.641,525,0.498,527,0.895,528,1.039,530,2.855,532,1.028,557,0.526,562,2.284,564,0.685,565,1.133,568,1.604,572,0.56,575,0.937,580,0.367,600,0.705,605,0.383,607,1.061,613,0.478,614,0.639,621,1.423,630,1.512,635,1.259,653,0.621,655,0.691,670,0.615,683,1.036,684,0.552,712,1.061,723,1.662,730,0.861,736,0.451,743,1.301,747,0.81,756,0.496,757,0.855,759,1.435,760,2.451,769,0.758,784,0.776,807,0.762,813,0.685,814,0.849,828,0.441,829,0.557,837,0.641,843,1.514,846,0.325,857,0.894,870,0.827,876,1.747,890,0.844,901,0.968,906,1.277,916,0.752,933,0.599,934,1.712,939,0.603,944,0.428,945,0.35,966,0.288,974,2.12,988,1.354,993,0.428,994,0.849,1005,0.691,1009,0.72,1010,1.314,1015,1.643,1019,0.724,1051,0.513,1062,0.509,1080,0.589,1090,0.47,1094,0.986,1100,0.419,1108,0.541,1113,2.922,1123,1.161,1160,1.606,1172,0.636,1202,0.651,1206,0.563,1207,0.765,1220,0.607,1221,0.737,1224,0.389,1225,0.787,1245,0.861,1246,2.208,1264,0.691,1265,3.151,1285,0.451,1297,0.513,1328,1.028,1329,0.487,1332,0.705,1347,3.9,1365,1.138,1400,3.491,1405,0.685,1411,0.685,1456,1.039,1523,1.004,1530,0.636,1540,0.585,1565,1.373,1566,1.407,1567,0.813,1579,0.881,1628,0.813,1632,1.437,1633,0.498,1638,0.546,1687,0.881,1754,0.838,1759,1.357,1781,0.666,1794,0.963,1796,0.904,1797,0.844,1842,0.787,1844,3.162,1853,1.643,1876,0.963,1893,1.686,1913,0.596,1931,0.765,1955,0.881,1959,0.94,1987,0.745,1988,0.442,2000,3.503,2010,0.686,2013,1.364,2017,1.166,2067,1.058,2073,1.178,2081,0.755,2128,0.728,2131,0.931,2141,1.776,2227,1.373,2228,1.373,2249,0.931,2250,0.888,2263,1.606,2268,0.603,2293,0.619,2338,0.582,2339,0.632,2340,1.214,2370,1.004,2388,0.554,2565,0.881,2623,1.004,2705,1.004,2722,2.137,2723,2.51,2724,2.002,2729,1.467,2744,2.093,2745,1.542,2799,1.446,2833,1.686,2898,0.712,2960,0.662,3070,0.728,3556,1.301,3919,0.931,3972,1.289,3987,2.438,4016,1.446,4072,3.604,4185,3.056,4700,1.058,4701,1.058,4702,2.769,4703,2.769,4704,2.769,4705,2.769,4706,2.769,4707,3.471,4708,3.471,4709,3.471,4710,3.471,4711,3.471,4712,2.769,4713,2.769,4714,2.769,4715,2.769,4716,2.769,4717,3.471,4718,3.471,4719,3.471,4720,3.471,4721,3.471,4722,1.058,4723,1.058,4724,1.058,4725,1.14,4726,1.058,4727,1.058,4728,1.972,4729,3.471,4730,3.471,4731,0.963,4732,1.058,4733,0.963,4734,1.058,4735,1.058,4736,1.058,4737,1.972,4738,1.058,4739,1.686,4740,1.058,4741,0.963,4742,0.963,4743,1.058,4744,1.14,4745,1.058,4746,1.972,4747,1.058,4748,1.058,4749,1.058,4750,1.058,4751,1.972,4752,2.769,4753,1.972,4754,1.058,4755,1.972,4756,1.972,4757,1.972,4758,1.972,4759,1.058,4760,1.058,4761,1.058,4762,1.058,4763,1.058,4764,1.058,4765,1.796,4766,1.058,4767,1.972,4768,1.058,4769,1.058,4770,1.058,4771,1.058,4772,1.058,4773,1.058,4774,1.058,4775,1.058,4776,1.058,4777,1.058,4778,1.058,4779,1.058,4780,1.058,4781,1.058,4782,1.058,4783,1.058,4784,1.058,4785,1.446]],["t/1497",[1781,3.73]],["t/1499",[2,1.058,4,1.216,7,0.9,15,1.43,16,0.73,18,4.138,21,1.108,24,1.402,28,1.694,31,0.958,48,1.591,49,1.387,55,5.439,56,1.127,84,1.083,85,2.138,89,3.732,103,2.554,109,1.287,132,1.813,160,3.301,170,1.159,171,1.065,187,2.73,200,1.943,203,0.939,223,2.208,225,1.402,272,1.605,275,1.911,278,1.405,279,1.694,283,1.134,295,1.577,296,2.379,313,0.958,318,1.503,322,1.226,342,4.035,347,2.588,359,1.729,383,2.73,393,1.926,408,2.228,416,1.805,482,1.753,527,1.35,531,2.377,605,1.419,608,3.203,614,1.405,630,1.448,684,2.045,755,2.78,760,1.159,802,2.497,814,2.658,837,2.377,845,2.494,857,1.779,881,2.453,906,2.539,913,2.918,933,2.221,944,1.586,1008,2.342,1019,2.268,1051,1.901,1072,1.812,1090,3.844,1100,1.554,1193,2.73,1195,2.669,1197,2.669,1199,2.641,1216,3.127,1218,2.563,1220,2.249,1262,3.414,1263,4.665,1288,4.56,1411,2.539,1413,2.195,1500,2.964,1558,2.208,1633,1.848,1793,2.473,2250,4.883,2491,3.127,2545,2.588,3595,1.444,4279,3.721,4786,6.173,4787,3.921,4788,3.921,4789,6.72,4790,3.921,4791,6.173,4792,3.452,4793,3.721]],["t/1501",[7,0.872,28,1.396,55,5.381,84,0.814,89,3.692,96,2.645,140,3.877,160,2.633,165,1.682,169,4.107,171,2.04,179,2.513,213,3.056,232,4.278,275,3.063,283,2.172,317,3.754,351,2.718,491,2.264,531,4.553,593,3.704,614,1.383,622,3.788,736,4.219,802,3.038,828,4.126,1080,4.179,1089,4.587,1266,5.298,1288,4.486,1313,4.365,1693,4.394,2250,3.383,2545,4.957,4789,6.612]],["t/1503",[7,0.95,34,2.295,55,6.044,219,2.789,221,3.328,254,2.897,283,2.605,303,7.048,361,6.068,391,3.793,471,4.605,478,5.461,545,4.164,548,5.546,552,3.189,558,4.029,578,3.541,684,4.699,829,4.747,1237,5.835,1286,7.048]],["t/1505",[7,0.811,16,1.483,28,1.125,33,4.543,56,0.787,84,1.114,109,1.734,134,3.966,165,2.304,245,4.761,247,3.502,320,4.572,353,2.173,366,4.985,405,2.535,418,3.983,426,2.943,432,5.026,471,3.303,509,4.515,566,5.549,628,4.116,757,1.964,859,4.136,865,5.367,896,5.844,983,7.903,987,5.93,1100,3.159,1262,4.408,1336,5.209,1527,5.26,2268,4.543,3011,7.563,3869,7.903,3978,6.641]],["t/1507",[4,2.393,7,0.72,16,0.978,21,1.485,24,0.851,28,1.089,49,1.734,55,5.833,56,0.903,84,0.569,85,3.166,107,2.34,109,1.301,139,4.813,160,1.843,162,2.711,165,1.728,170,3.169,196,2.083,203,1.847,246,1.988,272,2.152,279,2.271,283,1.52,285,2.172,313,1.284,322,1.644,326,3.578,338,1.293,353,1.433,359,2.318,416,2.42,429,4.494,430,2.593,471,2.178,482,2.35,517,3.186,527,2.202,580,2.679,608,2.728,614,1.42,623,2.517,628,2.715,703,4.191,760,3.312,790,3.66,802,2.126,831,3.315,873,3.618,944,3.697,945,3.332,994,2.263,1038,3.578,1072,3.566,1100,3.058,1108,2.689,1218,3.436,1262,2.908,1288,4.609,1305,4.788,1539,3.504,1540,2.908,1959,4.803,2010,1.829,2198,3.801,2904,3.801,3595,1.936,4785,3.854,4794,7.321]],["t/1509",[34,2.386,109,1.919,283,2.709,295,3.768,338,2.305,376,5.341,408,3.382,471,3.883,491,2.824,527,2.048,613,4.235,614,2.095,732,6.124,802,3.79,1307,6.524,2010,3.962,2211,7.472]],["t/1511",[2,2.189,7,0.75,9,3.649,10,4.389,21,1.592,28,0.795,41,2.556,48,2.286,49,2.581,55,4.414,56,0.939,84,0.61,105,2.538,109,1.368,113,2.959,131,2.556,132,1.655,160,3.334,163,2.233,165,1.262,179,1.885,190,2.896,200,2.792,211,4.193,225,2.014,236,1.482,256,2.108,296,2.171,304,1.187,313,1.377,351,2.039,353,2.213,359,3.578,393,2.767,416,2.595,418,2.816,471,2.335,476,1.636,505,2.335,524,3.468,531,3.416,611,2.342,614,1.494,677,2.969,742,5.076,747,3.332,795,3.683,802,3.283,805,3.836,840,2.556,846,1.73,873,3.878,896,4.132,994,2.426,1218,3.683,1307,7.245,1364,2.816,1394,3.584,1475,3.031,1540,4.489,1566,4.021,1632,2.924,1633,2.655,2010,3.998,2426,3.795,2444,3.756,2510,4.696,2904,4.075,3071,4.33,3490,4.961,4795,5.635]],["t/1513",[5,3.126,23,2.962,34,2.26,68,3.364,84,0.961,169,4.853,239,5.09,289,4.35,338,2.183,353,2.42,367,4.518,418,5.502,471,3.677,484,3.759,511,3.509,512,3.059,513,4.181,536,2.643,608,4.605,807,4.254,892,3.32,1094,4.435,2344,7.587]],["t/1515",[7,0.669,56,0.838,84,0.919,140,3.326,157,4.901,160,2.976,278,3.042,297,4.642,376,4.839,608,5.557,611,3.529,614,2.267,732,5.548,828,3.54,846,2.606,859,4.405,865,5.717,1038,5.778,1262,4.695,1288,5.071,1306,8.538,1307,5.91,2010,2.954,2352,6.058,3008,5.91,3596,8.055,3884,8.055,4796,8.489]],["t/1517",[2,2.487,7,0.727,24,1.493,56,0.911,103,3.086,233,3.338,318,2.245,353,2.515,471,3.822,580,3.202,790,6.421,802,3.73,846,2.831,916,4.297,944,3.73,1037,4.589,1089,5.633,1306,7.354,2268,5.257,3836,8.119,4797,9.223]],["t/1519",[1781,3.73]],["t/1521",[2,1.557,7,0.651,24,1.704,29,2.014,49,1.298,56,1.204,84,0.894,107,1.478,109,1.774,139,2.591,140,2.262,170,2.44,179,1.932,182,1.932,203,1.382,236,1.518,254,1.856,275,1.787,296,2.225,313,1.41,322,1.806,377,1.291,381,3.907,395,2.282,418,2.886,484,2.446,512,1.991,517,3.5,536,1.719,578,2.269,601,2.71,614,1.063,628,2.982,634,6.346,647,3.098,683,2.16,731,3.026,759,2.024,840,2.619,846,1.772,892,2.16,939,3.291,993,2.335,1400,5.461,1462,3.123,1605,4.233,1693,3.378,1754,2.454,1849,6.054,2010,3.875,2013,1.923,2254,4.979,2656,7.499,3025,6.794,3376,4.936,4798,8.781,4799,5.259]],["t/1523",[7,0.337,12,1.726,21,1.209,24,1.589,28,1.281,29,1.493,56,1.176,84,0.716,107,2.858,109,2.039,162,1.504,165,1.48,182,2.211,217,2.439,246,1.618,254,3.155,285,1.768,313,1.045,338,1.625,353,1.167,377,1.478,381,2.024,395,1.691,433,2.47,446,2.722,491,1.29,505,1.773,517,2.594,527,2.505,536,1.968,565,1.397,580,1.486,634,3.288,653,1.347,683,1.601,705,5.587,759,2.316,846,1.314,859,2.221,944,3.265,945,3.003,966,1.802,993,1.731,994,1.842,1037,4.517,1224,1.572,1394,2.722,1400,5.091,1735,2.979,1737,2.492,1765,2.454,1793,2.699,1849,4.937,1988,1.79,2010,1.489,2190,2.395,2252,3.184,2254,3.412,2382,3.234,2388,5.142,2625,2.454,2656,5.227,3025,4.656,3376,3.658,4420,3.234,4798,6.018,4799,6.018,4800,4.279,4801,3.897,4802,8.072,4803,6.607,4804,4.279,4805,4.279,4806,4.279,4807,6.607,4808,4.279,4809,6.607,4810,4.279,4811,4.279,4812,4.279]],["t/1525",[109,1.717,127,3.339,179,3.407,246,3.851,580,3.536,994,4.385,2388,5.337,2656,7.746,3025,7.176]],["t/1527",[109,1.717,172,3.527,179,3.407,246,3.851,580,3.536,994,4.385,2388,5.337,2656,7.746,3025,7.176]],["t/1529",[109,1.717,179,3.407,246,3.851,580,3.536,994,4.385,1099,3.77,2388,5.337,2656,7.746,3025,7.176]],["t/1531",[1781,3.73]],["t/1533",[2,0.893,7,0.892,16,1.003,24,1.584,28,0.467,34,2.362,48,1.344,49,1.767,56,1.041,84,0.739,85,2.723,96,1.167,97,1.481,105,1.036,109,0.559,113,1.208,123,1.129,140,1.298,164,0.91,170,2.015,171,2.136,174,0.984,193,1.205,203,1.633,213,1.348,219,1.025,223,4.872,232,1.205,246,1.253,254,3.144,275,1.025,295,2.743,296,1.276,304,1.657,313,0.809,322,2.901,337,1.543,338,1.326,343,1.178,372,2.018,377,0.741,381,1.567,398,1.167,401,1.266,430,1.634,449,2.039,461,5.192,476,0.962,482,1.481,491,0.998,505,1.373,508,1.951,525,2.541,527,0.724,528,2.841,567,1.328,580,2.368,613,2.437,614,1.708,622,1.671,628,1.711,707,3.795,715,2.772,740,1.497,810,1.471,830,2.464,843,1.812,846,2.094,888,1.745,897,2.089,901,1.627,970,2.832,982,1.671,1076,2.456,1094,1.656,1117,7.026,1172,3.245,1210,4.218,1266,1.436,1271,2.683,1297,1.606,1353,2.186,1539,3.594,1700,2.465,1735,2.306,1737,2.034,1799,2.465,1991,2.208,2013,1.103,2017,1.394,2031,1.772,2343,4.494,2350,2.429,2362,7.377,2388,1.736,2403,1.843,2430,2.039,2656,2.145,2952,4.218,3059,1.993,3595,2.512,3701,4.611,3702,4.611,3703,2.832,3704,3.143,3705,3.143,3706,3.143,3707,4.611,3708,4.611,3709,3.143,3710,4.611,3711,4.611,3712,4.611,3713,3.143,3714,3.143,3715,3.143,3716,3.143,3718,3.143,3719,2.832,4813,4.912,4814,4.912,4815,5.393]],["t/1535",[7,0.528,21,1.892,24,1.084,28,1.293,31,2.551,56,1.199,76,4.983,232,3.332,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,846,2.812,966,3.54,1090,4.067,2017,3.856,3059,5.512,3720,6.354,3721,6.354,3723,9.884,3728,6.923,3729,6.354,3730,5.725,3731,5.061,3732,5.061,3733,5.061,3734,5.061,3735,6.354,3736,6.354,3737,5.061,3738,5.453,4816,11.227]],["t/1537",[7,0.6,21,2.15,28,1.408,31,2.72,56,1.244,76,5.661,109,1.683,170,2.248,461,6.616,527,2.182,572,5.284,590,4.093,607,4.53,966,3.223,3723,8.785,3728,7.542,3731,5.75,3732,5.75,3733,5.75,3734,5.75,3737,5.75,3738,6.195,4817,7.608,4818,7.608,4819,6.505,4820,7.608,4821,7.608]],["t/1539",[7,0.528,21,1.892,28,1.293,31,2.551,56,1.199,76,4.983,109,1.545,381,3.168,405,2.914,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,807,3.54,916,3.492,966,3.54,1177,6.816,1689,4.802,3728,6.923,3730,5.725,3731,5.061,3732,5.061,3733,5.061,3734,5.061,3737,5.061,3738,5.453,3739,6.354,3740,6.354,3741,8.012,3742,7.039,3743,6.354,3744,6.354,3745,5.58]],["t/1541",[7,0.57,21,2.044,28,1.362,31,2.653,56,1.226,76,5.382,109,1.627,126,4.659,193,2.631,332,5.008,461,6.485,527,2.11,572,5.11,590,3.891,607,4.441,966,3.512,3728,7.293,3731,5.466,3732,5.466,3733,5.466,3734,5.466,3737,5.466,3742,7.416,3745,6.027,4819,6.184,4822,7.233,4823,7.233,4824,7.233,4825,7.233]],["t/1543",[7,0.474,21,1.698,31,2.408,56,1.191,76,4.472,174,1.786,233,2.175,236,2.234,318,1.463,461,5.997,504,3.643,509,3.405,527,1.858,572,4.5,590,3.233,607,4.106,653,2.675,740,2.717,887,5.664,910,4.784,966,3.714,982,4.286,2617,6.529,2622,7.265,2625,3.448,3728,6.422,3731,4.542,3732,4.542,3733,4.542,3734,4.542,3737,4.542,3745,5.008,3838,6.145,4819,5.139,4826,6.01,4827,6.01,4828,7.739,4829,8.497,4830,8.497,4831,6.01,4832,6.01]],["t/1545",[7,0.313,21,1.123,24,0.644,26,1.475,28,0.561,31,1.881,37,4.158,56,1.346,95,5.412,106,2.822,107,1.018,109,1.052,112,1.969,127,2.525,137,4.003,163,1.575,170,1.174,179,1.33,193,1.446,242,2.801,267,3.398,381,2.951,391,1.673,397,2.593,405,2.45,440,1.752,444,2.704,461,3.492,471,3.191,502,1.096,511,1.267,514,3.004,527,2.072,572,5.019,635,1.442,740,1.796,757,0.979,784,7.375,813,4.04,829,4.057,830,1.816,887,2.649,982,2.005,1062,1.911,1072,2.883,1100,1.575,1177,5.73,1196,2.836,1300,2.15,1437,5.82,1462,2.15,2031,2.127,2384,2.705,2951,2.357,2952,4.88,3014,3.398,3066,2.914,3249,3.236,3576,3.004,3746,5.92,3747,3.772,3748,3.772,3749,3.772,3750,3.772,3751,3.772,3752,3.772,3753,7.014,3754,7.308,3755,7.308,3756,3.772,3757,3.772,3758,3.772,3759,3.772,3760,3.772,3762,3.772,3763,2.528,3764,3.772,3765,3.772,3766,5.92,3767,3.772,3768,3.772,3769,3.772,3770,3.772,3771,3.054,3772,3.772,3773,3.772,3774,3.772,3775,3.772,3776,3.772,3777,3.772,3778,3.772,3779,3.772,3780,3.772,3781,3.772,4833,3.974,4834,3.974,4835,3.974,4836,3.974,4837,3.974]],["t/1547",[7,0.413,9,2.069,15,3.325,16,1.432,21,0.903,24,0.848,28,0.739,34,0.814,49,0.718,56,1.366,97,1.429,107,1.97,171,0.868,186,1.506,203,1.253,228,1.012,237,2.343,281,1.512,322,2.656,377,0.715,393,1.569,527,0.699,605,1.156,683,1.195,715,3.419,784,2.343,822,1.909,843,1.747,846,1.607,890,7.679,1072,1.477,1083,5.687,1099,1.183,1117,6.419,1219,1.424,1352,2.152,1409,2.109,1567,2.455,1577,3.957,1597,2.015,1700,2.377,1802,4.033,1994,2.225,2013,1.064,2045,2.91,2085,2.602,2127,3.032,2419,2.662,2420,2.732,2577,1.683,3531,2.225,3701,2.732,3702,2.732,3703,2.732,3707,2.732,3708,2.732,3710,2.732,3711,2.732,3712,2.732,3763,3.331,3782,3.032,3784,3.032,3785,3.032,3786,9.139,3787,9.139,3788,9.139,3789,9.139,3790,9.139,3791,8.478,3792,3.032,3793,9.139,3794,8.234,3795,7.301,3796,3.032,3797,3.032,3798,3.032,3799,6.313,3800,3.032,3801,3.032,3802,3.032,4813,2.91,4814,2.91,4838,5.236]],["t/1549",[7,0.845,22,5.272,39,2.446,46,4.085,49,1.351,84,0.651,85,2.082,98,2.986,100,3.8,112,2.977,126,2.902,160,2.107,171,1.633,174,1.786,201,3.06,202,5.848,207,2.453,213,2.446,233,2.175,278,2.153,316,2.678,337,2.799,343,2.138,351,2.175,404,2.926,408,2.17,434,4.006,461,3.364,476,1.745,480,4.091,482,2.688,506,5.04,511,1.916,525,4.004,550,4.611,643,5.474,657,4.701,706,4.289,779,6.776,1098,3.791,1113,4.289,1117,7.822,1175,2.767,1219,2.678,1733,3.59,1893,8.428,1991,4.006,2350,4.407,2362,5.784,2421,5.076,2430,5.23,2448,3.759,2598,3.699,2951,3.565,3118,7.081,3143,4.701,3803,5.703,3804,4.185,3805,4.472,3806,5.703,3807,5.703,3808,5.703]],["t/1551",[1781,3.73]],["t/1553",[7,0.716,15,3.311,34,2.312,84,0.983,196,3.599,203,2.173,233,4.041,295,3.651,381,4.296,398,3.198,416,4.181,575,4.313,580,3.153,613,4.104,684,6.306,794,6.322,1310,6.399,1411,5.88,1538,6.977,2086,6.658,3539,7.763]],["t/1555",[7,0.88,15,3.311,56,0.897,84,0.983,96,3.198,159,6.052,233,4.041,412,6.756,1090,4.032,1314,6.18,1352,6.115,1802,5.504,1923,4.833,1994,6.322,2013,3.024,2778,7.102,3559,8.904,3561,7.763,4680,8.27,4839,9.784,4840,9.08]],["t/1557",[84,1.198,159,8.005,233,4.003,292,3.889,351,3.236,621,4.595,802,3.617,885,6.653,1090,3.97,1180,6.226,1290,5.267,1291,6.871,1410,6.758,1802,5.42,3559,7.13,3565,7.449,3677,8.485,3678,8.485,3679,7.451,4840,8.942,4841,8.942]],["t/1559",[7,0.769,300,5.916,580,3.389,684,5.09,3064,7.376,3681,8.344,3682,8.344,3683,8.344,3687,9.261,4842,9.76,4843,9.76,4844,12.57,4845,10.516,4846,10.516]],["t/1561",[7,0.751,233,4.16,300,5.772,532,4.966,580,3.306,684,4.966,3063,6.885,3681,8.141,3682,8.141,3683,8.141,3691,9.036,4842,9.522,4843,9.522,4847,12.385,4848,10.26,4849,10.26]],["t/1563",[7,0.731,16,1.268,34,1.735,49,2.083,56,0.915,85,3.649,96,2.4,158,3.405,174,2.755,198,3.317,203,2.219,233,4.092,254,2.191,292,2.963,296,2.625,332,3.536,398,3.265,408,2.459,491,2.794,513,3.21,525,3.21,532,3.553,614,1.254,653,2.144,769,2.618,857,3.091,888,3.589,1076,2.453,1090,4.116,1310,8.798,1398,4.689,1402,4.334,1632,4.81,1754,2.896,1913,3.836,2013,2.269,2387,5.329,2388,3.571,3672,7.925,3693,6.465,3694,6.465,3695,6.465,3696,6.205,3697,6.465,3698,5.998]],["t/1565",[1781,3.73]],["t/1567",[4,1.541,21,1.403,26,1.844,28,0.701,29,1.733,49,1.663,56,1.312,84,0.801,85,2.563,105,2.314,109,2.015,113,1.811,140,1.946,144,2.379,155,3.216,160,1.741,165,1.113,174,1.476,192,2.926,219,2.29,225,2.645,254,1.597,272,2.033,295,1.997,302,2.287,313,1.213,322,1.554,353,1.354,377,1.977,395,1.963,405,1.58,426,1.834,447,2.35,463,2.989,517,3.011,614,1.628,705,6.03,755,2.237,801,3.696,824,3.642,827,2.517,846,3.216,933,2.814,934,3.649,993,3.576,1037,5.466,1162,2.764,1332,3.311,1633,2.341,1638,2.565,1693,2.906,1753,3.419,1952,4.714,1959,2.369,1978,3.817,2010,3.96,2214,3.216,2341,4.045,2388,2.604,4850,4.045,4851,4.967,4852,7.399,4853,7.399,4854,6.739,4855,7.399,4856,4.967,4857,4.967,4858,4.967]],["t/1569",[7,0.6,12,3.071,16,0.961,49,1.711,56,0.986,84,0.825,85,3.134,109,2.124,113,1.883,162,1.814,164,1.419,165,1.157,174,1.535,182,2.548,199,2.547,219,1.598,246,1.953,257,2.629,297,2.824,313,1.262,323,2.083,336,2.101,338,1.873,391,2.174,405,2.422,426,1.907,447,3.603,484,2.188,491,2.727,508,3.042,613,3.442,614,0.951,660,3.842,705,7.663,846,2.777,879,4.118,937,4.549,983,3.968,993,4.307,1037,4.968,1072,2.387,1100,2.047,1331,4.887,1399,3.107,1506,3.314,1753,3.554,1754,3.236,1978,3.968,2010,3.877,2026,3.595,2214,3.344,2388,5.581,2729,3.842,2876,4.118,2990,3.154,3023,3.842,3869,3.968,3873,4.703,3970,3.968,4695,4.703,4801,4.703,4859,5.164,4860,4.703,4861,5.164]],["t/1571",[2,1.881,48,2.83,56,1.313,109,2.118,113,2.543,186,3.287,283,2.017,377,1.56,437,3.274,527,1.525,611,2.899,705,7.563,846,2.891,993,2.821,1037,6.114,2200,5.964,2388,5.589,2402,3.486,2515,5.562,4854,8.578,4862,6.975,4863,6.975,4864,6.975,4865,6.975,4866,6.975,4867,6.975,4868,6.975,4869,6.975,4870,6.975,4871,6.975,4872,6.975,4873,6.975]],["t/1573",[7,0.733,12,3.749,84,1.007,101,4.948,109,1.91,228,2.945,233,3.364,509,5.266,578,3.653,580,3.228,634,7.143,702,6.26,705,6.972,892,3.478,2388,4.872,2656,7.335,3025,7.982]],["t/1575",[1781,3.73]],["t/1577",[7,0.721,15,2.437,21,1.227,24,1.902,29,1.515,34,1.106,56,1.238,84,0.47,89,2.494,98,1.526,103,2.236,105,1.358,108,2.242,139,1.949,160,1.522,171,1.18,182,2.726,203,1.039,219,1.344,221,3.008,232,2.963,254,1.396,318,1.057,338,1.068,353,1.184,377,1.495,395,2.641,410,2.866,426,1.604,437,2.038,471,1.799,476,2.365,491,2.014,502,1.197,512,2.809,527,2.159,528,2.287,536,1.293,564,2.812,607,2.561,630,1.604,653,1.367,747,1.783,757,1.07,759,2.856,760,2.407,769,1.669,846,2.051,916,1.655,957,4.734,988,2.123,1051,2.105,1072,2.007,1225,3.231,1328,4.771,1405,7.264,1456,2.287,1759,2.989,1849,4.43,1923,2.311,2013,1.446,2017,2.813,2031,3.576,2141,2.221,2228,3.023,2293,2.54,2338,2.388,2339,2.594,2340,2.673,3070,2.989,3936,3.823,4016,3.184,4054,5.442,4608,6.633,4874,4.342,4875,4.342,4876,8.146,4877,4.342,4878,6.683,4879,4.342,4880,4.342,4881,4.342,4882,4.342,4883,4.342,4884,6.087,4885,4.342,4886,4.342,4887,4.342,4888,4.342,4889,4.342]],["t/1579",[4,1.241,24,1.86,28,1.584,31,1.532,39,2.552,68,1.517,84,0.433,88,1.802,89,2.34,107,1.606,109,1.476,115,2.888,170,1.182,182,1.339,224,2.657,232,2.281,236,1.649,254,2.815,332,3.255,398,1.409,414,3.598,426,1.478,444,2.719,512,2.162,527,2.205,614,1.857,630,2.316,647,3.567,653,3.319,694,2.176,757,0.986,760,2.98,803,2.253,846,3.351,945,3.146,966,3.061,1051,1.94,1076,4.284,1090,1.777,1118,5.094,1138,3.989,1219,2.795,1224,3.875,1375,2.426,1849,3.411,2254,2.067,2402,3.135,2656,4.061,2843,5.952,3025,2.82,3668,4.906,3838,4.535,3841,5.209,4884,3.645,4890,4.002,4891,6.272,4892,4.002,4893,4.002,4894,4.002,4895,6.272,4896,4.002,4897,3.024,4898,6.272,4899,4.002,4900,6.272]],["t/1581",[1781,3.73]],["t/1583",[1,2.399,4,1.28,7,0.622,16,1.196,17,1.631,24,1.442,25,1.322,27,0.98,28,0.907,31,2.357,36,3.479,46,1.711,48,1.675,49,0.928,50,2.415,56,1.226,89,1.541,109,1.799,115,2.959,123,2.689,130,2.353,131,2.915,132,1.212,138,1.665,169,2.258,170,1.22,172,1.43,174,2.647,201,3.272,207,1.685,219,1.278,224,1.749,227,2.724,233,2.855,262,2.698,287,4.586,299,2.649,315,1.961,318,2.488,338,1.941,339,3.241,370,3.229,372,1.545,381,3.04,403,2.649,416,1.901,506,2.449,527,2.234,530,1.846,599,2.122,658,1.977,675,2.626,705,2.541,718,3.53,731,2.164,740,2.905,756,3.016,759,1.447,760,2.331,802,1.67,805,2.81,822,2.466,892,1.545,910,2.324,945,2.611,982,3.241,993,1.67,994,2.767,1090,1.833,1099,1.529,1112,2.449,1117,2.752,1132,1.833,1138,2.626,1145,2.102,1193,2.874,1202,2.541,1208,3.362,1219,1.839,1231,3.44,1300,2.233,1314,4.374,1400,2.142,1401,2.432,1407,2.946,1423,2.985,1426,3.172,1475,2.221,1476,3.44,1508,2.284,1906,3.917,1978,3.172,2099,2.484,2352,2.946,2361,3.229,2382,3.12,2402,2.063,2421,2.466,2430,2.541,2594,4.374,2641,2.946,2656,2.673,3025,2.909,3066,3.027,3665,3.917,3666,3.53,3667,3.634,3669,3.53,3670,3.53,3671,3.172,4901,4.128]],["t/1585",[7,0.941,15,2.31,16,1.179,18,2.784,24,1.026,36,2.793,39,2.578,46,2.625,84,0.686,85,2.194,86,4.312,96,2.231,108,3.271,112,3.138,131,2.874,145,2.547,162,2.226,174,1.883,228,2.007,233,4.43,240,3.464,247,3.874,278,2.27,283,1.832,313,1.548,318,1.542,338,1.558,377,1.417,393,3.111,398,2.231,408,3.183,504,3.84,505,3.654,576,3.731,578,2.489,590,3.408,683,2.37,757,1.561,833,3.611,856,3.995,888,3.337,908,3.225,939,3.611,977,2.31,1090,2.813,1175,4.669,1310,7.727,1314,4.312,1402,4.741,1538,4.868,1829,4.955,2014,5.279,2427,5.577,2428,6.011,3672,7.538,3673,8.367,4902,6.335]],["t/1587",[4,2.595,7,0.836,15,3.051,56,0.826,135,6.048,144,4.007,162,2.94,174,2.487,233,4.217,292,3.639,343,2.976,372,3.131,398,3.737,408,3.021,482,3.742,491,2.522,590,4.501,741,5.418,750,5.897,892,3.131,993,3.385,1219,3.728,1310,7.477,1367,5.277,1469,7.94,1527,5.522,2437,7.94,3674,7.94,3675,8.368]],["t/1589",[1781,3.73]],["t/1591",[7,0.624,27,2.431,31,1.934,34,2.016,106,2.901,112,3.921,131,3.591,138,3.192,172,2.742,189,4.236,190,4.068,191,5.724,193,2.879,198,3.854,219,3.17,233,2.865,278,2.836,295,3.183,318,2.493,341,4.354,353,2.159,377,1.77,477,4.695,491,2.386,498,4.404,499,5.512,500,3.745,504,4.798,512,2.729,774,4.282,1174,5.331,1175,3.645,1369,6.083,1375,4.798,1376,5.331,1419,6.192,1428,6.596,3818,5.983,3819,7.512,3820,7.512]],["t/1593",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1376,6.465,3818,7.255]],["t/1595",[7,0.757,106,3.518,131,4.355,138,3.872,190,4.934,219,2.971,233,3.474,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,512,3.31,536,2.859,1376,6.465,3818,7.255]],["t/1597",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1376,6.465,3818,7.255]],["t/1599",[7,0.832,139,4.734,233,3.818,341,5.802,759,3.698,1425,6.131]],["t/1601",[7,0.782,24,1.607,88,4.47,206,6.427,233,3.592,341,5.458,552,3.513,567,3.979,723,3.547,1266,4.302,1271,4.938,3821,11.181]],["t/1603",[7,0.929,29,3.462,219,3.072,233,3.592,315,4.714,318,2.416,395,3.922,1225,7.385,1314,6.756,1688,6.756,2444,6.615,3296,8.486]],["t/1605",[34,2.817,42,6.556,96,3.149,127,2.932,155,5.79,253,5.38,283,2.585,318,2.177,353,2.438,377,2,381,4.23,472,4.354,502,2.465,530,3.998,698,6.466,1175,4.117,1220,5.129,1424,6.758,1868,7.645,2091,6.871,2092,6.466,2093,6.086,2094,7.998,3387,6.758]],["t/1607",[7,0.751,27,2.26,34,2.425,102,6.005,172,3.298,283,2.753,318,2.318,530,4.258,1107,7.196,1175,4.384,1424,7.196,1557,5.907,2091,7.317,2092,6.885,2093,6.481,2094,8.311,3822,9.036]],["t/1609",[1781,3.73]],["t/1611",[7,0.675,12,3.452,16,1.13,24,1.743,25,1.944,27,2.694,28,0.857,31,1.483,36,3.774,39,3.483,50,3.553,56,1.12,70,4.333,84,0.658,98,2.134,101,3.232,102,3.83,105,1.899,106,2.225,113,2.214,121,3.553,131,2.755,165,1.917,171,2.325,197,2.706,201,3.091,240,3.321,304,1.28,339,3.063,353,1.656,377,1.914,394,3.035,404,2.957,407,2.745,408,2.192,416,2.796,502,1.674,505,2.516,508,3.577,579,4.391,614,1.118,630,2.243,734,2.687,752,4.945,779,4.842,787,4.945,840,2.755,875,4.945,1174,5.764,1633,2.861,1689,3.183,2013,2.023,2099,3.654,2331,5.445,2450,4.048,2672,4.391,3297,5.346,4850,4.945,4903,11.348,4904,6.073,4905,8.559,4906,5.531,4907,6.073]],["t/1613",[24,1.542,27,2.26,28,1.344,84,1.031,162,3.346,225,3.403,232,3.463,322,2.978,524,5.861,614,1.753,650,7.741,723,3.403,855,5.861,2928,6.055,3595,3.508,4908,9.522]],["t/1615",[7,0.751,21,2.691,27,2.26,84,1.031,100,4.258,106,4.212,169,5.207,232,3.463,304,2.007,404,4.636,507,5.179,614,1.753,752,7.754,756,4.469,1671,5.237,4045,7.754,4906,8.673]],["t/1617",[7,0.669,29,2.961,31,2.074,39,3.454,46,3.518,50,6.264,84,0.919,98,2.983,105,2.655,123,2.893,131,3.851,207,3.465,233,3.072,248,3.984,254,2.73,278,3.042,313,2.074,318,2.067,447,4.016,572,4.495,578,3.336,580,2.947,814,3.655,1112,5.035,1115,6.912,1132,3.769,1400,4.405,1401,5,2322,6.316,2631,5.548,3670,9.155]],["t/1619",[6,5.235,7,0.685,21,2.455,26,3.226,28,1.362,31,1.179,41,2.188,48,1.957,56,0.858,84,1.046,92,1.786,93,3.067,107,2.225,109,1.465,123,1.644,125,2.75,138,1.946,153,2.468,163,1.912,165,1.946,171,1.967,172,3.01,173,2.581,174,2.152,179,3.231,190,3.721,199,2.379,207,2.955,224,2.044,233,1.746,236,1.903,289,2.861,296,1.859,304,1.017,318,1.175,322,1.509,339,2.433,351,1.746,437,2.264,491,1.454,502,1.996,505,3.601,578,1.896,583,2.946,596,2.173,599,3.721,613,2.181,614,1.778,620,2.668,658,2.31,691,2.653,702,3.249,724,2.349,725,3.685,757,2.379,812,3.216,888,2.541,940,5.895,977,2.64,1035,2.882,1070,2.75,1086,4.394,1099,3.831,1112,4.294,1142,3.184,1179,3.216,1219,2.15,1302,2.924,1368,3.096,1737,1.819,2013,1.607,2190,2.7,2272,3.59,2369,4.394,2388,2.529,2431,3.443,2484,3.707,2546,3.928,3563,6.032,3595,1.777,3669,7.429,3699,6.87,3700,4.578]],["t/1621",[1781,3.73]],["t/1623",[29,3.492,101,5.328,305,6.367,318,2.437,323,4.037,395,3.956,505,4.148,655,6.542,728,6.969,740,4.524,858,4.937,982,5.049]],["t/1625",[4,2.243,7,0.57,17,2.858,25,2.316,27,1.717,40,3.81,48,2.934,85,2.505,164,1.987,203,1.731,233,2.617,236,3.047,275,2.239,281,3.422,282,3.934,323,2.917,343,2.573,405,2.301,409,3.665,565,2.361,568,2.764,608,3.754,655,4.728,683,2.706,691,5.308,740,4.909,760,3.423,798,3.978,810,3.211,830,3.305,901,4.739,982,3.648,985,4.773,1007,4.684,1145,3.682,1289,4.601,1300,5.874,1370,5.768,2577,3.81,2593,6.184,2595,4.601,2596,4.641,2614,4.821,4909,7.233]],["t/1627",[7,0.912,10,4.462,25,3.706,32,5.16,39,3.357,84,0.894,101,4.391,229,4.893,233,2.986,236,2.169,318,2.009,343,2.934,390,3.983,481,4.928,565,2.694,630,3.047,683,3.087,737,6.235,740,4.753,827,4.18,856,5.203,982,5.304,1323,5.678,2259,5.499,2421,4.928,2471,6.235,2595,5.248,2596,5.294,2614,5.499,2953,7.514,4910,8.25]],["t/1629",[7,0.644,12,1.758,21,1.894,25,2.146,27,1.034,28,0.615,31,1.065,56,0.977,84,0.726,96,1.535,106,1.597,107,1.716,109,0.735,111,3.196,115,2.007,123,1.485,132,1.968,162,1.531,165,0.976,170,2.708,179,2.242,197,2.986,213,1.773,228,2.123,233,2.955,236,1.762,238,7.053,246,1.648,247,1.915,272,3.343,276,3.32,281,2.062,296,1.679,313,1.637,318,1.988,323,1.758,324,2.308,364,3.071,365,2.208,372,1.631,382,2.704,387,3.034,390,2.104,409,2.208,482,1.949,491,1.314,502,1.202,527,2.518,541,2.682,565,1.423,568,1.248,578,1.713,607,1.67,630,1.61,632,2.345,651,5.442,658,2.087,660,3.243,685,2.425,734,1.928,814,1.877,827,2.208,833,3.82,838,2.198,846,1.338,858,2.15,927,2.876,945,1.442,977,2.444,982,3.38,994,1.877,1037,2.169,1062,2.096,1097,3.349,1108,2.229,1111,4.666,1145,3.412,1175,2.007,1178,2.516,1188,2.772,1219,1.942,1285,1.858,1300,6.361,1395,3.837,1632,2.262,2099,2.622,2259,2.905,2314,3.726,2472,3.243,2595,2.772,2596,5.882,2610,3.549,2614,6.109,2636,3.837,2638,4.136,2639,4.136]],["t/1631",[2,1.915,7,0.848,25,3.841,84,0.769,96,2.501,98,2.495,113,2.589,233,2.57,236,3.155,238,5.468,245,4.242,313,1.735,318,2.62,338,1.747,372,3.567,472,3.458,536,2.115,557,3.534,628,3.667,681,4.442,683,2.657,685,5.304,736,3.028,740,4.309,760,2.098,859,3.685,860,6.072,895,6.468,945,2.35,982,4.808,993,2.873,1108,3.632,1142,6.291,1300,3.841,2595,4.517,2596,6.117,2614,6.354,2627,6.252,2968,6.072,3830,5.068]],["t/1633",[7,0.84,30,6.666,84,1.155,163,3.34,228,2.67,233,3.857,236,2.803,281,3.987,318,2.052,361,7.873,740,5.284,982,5.896,1117,5.617,1221,5.868,2137,6.179,2322,6.271,2430,6.56,2536,7.676,2596,5.408,2641,6.014,2642,7.676,2643,7.206]],["t/1635",[7,0.869,31,1.377,34,1.435,40,2.969,48,2.286,56,0.556,84,0.61,103,1.885,107,1.443,116,2.103,132,1.655,145,2.266,179,1.885,233,2.937,236,2.501,238,3.232,247,2.476,248,2.645,313,1.377,318,2.685,372,2.108,377,1.26,426,2.081,447,2.666,465,5.65,482,2.52,502,2.869,509,3.192,525,2.655,527,1.774,530,2.52,565,2.65,568,1.614,608,2.924,614,1.037,630,2.081,635,2.044,647,2.114,685,3.135,691,4.463,740,3.668,760,2.81,857,2.556,887,3.756,927,5.356,945,2.685,982,2.842,1144,3.065,1196,4.021,1198,4.132,1200,4.696,1300,6.553,1632,2.924,2099,6.261,2210,4.408,2250,3.655,2588,5.304,2595,3.584,2596,5.208,2610,4.589,2614,3.756,2619,4.696,2641,4.021,4911,5.132,4912,5.132,4913,5.132,4914,5.132]],["t/1637",[7,0.803,21,2.877,49,2.289,56,1.005,84,1.103,233,3.685,236,2.677,305,6.477,740,4.603,1175,4.689]],["t/1639",[7,0.616,21,1.512,25,2.957,29,1.867,34,1.363,41,2.428,49,2.076,56,1.151,84,1.1,85,1.854,92,2.895,105,1.674,113,1.952,129,3.197,131,2.428,134,2.663,140,2.097,162,1.881,165,1.199,171,1.454,233,1.937,236,1.407,238,4.485,256,2.003,296,2.063,318,1.903,324,2.834,351,1.937,372,2.003,377,1.197,401,2.046,465,3.727,491,1.613,536,1.594,580,1.858,607,2.051,614,0.985,621,2.751,628,2.764,681,3.348,685,2.978,691,2.944,740,4.884,757,1.319,760,2.729,810,4.51,820,2.751,901,2.628,938,3.294,945,1.771,982,5.691,993,2.165,1145,2.725,1172,3.22,1297,2.595,1300,2.895,1323,3.684,1638,2.764,1998,3.32,2014,4.46,2421,3.197,2595,3.405,2596,7.241,2611,5.732,2614,5.211,3027,3.982,3466,3.643,3531,3.727,3834,3.924,4915,5.352,4916,5.352,4917,4.358,4918,4.576,4919,5.352,4920,5.352,4921,5.352]],["t/1641",[34,2.277,56,1.297,105,2.797,296,3.446,377,2,630,4.085,810,3.97,945,2.958,1998,5.547,2142,4.449,2614,5.96,3466,6.086,3531,6.226,3834,6.556,4917,7.281,4918,7.645,4922,8.942,4923,8.942,4924,8.942,4925,8.942]],["t/1643",[48,3.371,56,1.273,283,2.402,491,2.504,539,5.332,614,1.529,683,3.109,760,2.455,810,3.689,963,5.036,1998,5.154,2595,5.285,3466,5.655,3531,5.785,3834,6.092,4917,6.765,4918,7.104,4926,8.308,4927,8.308,4928,8.308,4929,8.308,4930,8.308,4931,8.308,4932,8.308,4933,8.308,4934,8.308,4935,8.308,4936,8.308,4937,8.308,4938,8.308,4939,8.308]],["t/1645",[7,0.776,49,2.212,106,3.607,162,3.458,256,3.682,304,2.074,608,5.107,614,1.812,814,4.238,881,6.155,1300,6.341,2325,8.414,4940,9.842]],["t/1647",[7,0.56,17,3.767,56,1.283,85,2.46,92,3.53,93,3.009,115,3.27,197,3.164,233,2.57,272,2.907,296,2.737,304,1.496,313,1.735,315,3.373,323,2.864,353,1.936,361,4.783,552,3.374,562,3.487,596,3.198,612,4.599,614,1.981,650,7.247,740,4.309,796,3.722,810,4.233,982,4.808,1017,6.803,1072,3.282,1079,4.442,1323,4.888,1423,5.135,2013,2.365,2541,6.252,2611,6.99,3531,4.944,3834,5.207,4850,5.783,4917,5.783,4941,7.102]],["t/1650",[4,2.535,68,2.157,170,1.681,182,1.904,236,2.514,238,5.996,248,2.671,254,1.829,305,5.198,324,3.013,372,2.129,398,2.004,476,1.652,527,2.091,578,3.211,653,2.572,659,3.684,740,2.572,741,3.684,757,1.402,760,2.825,814,3.519,846,1.747,887,5.447,927,5.393,945,3.458,966,3.373,977,2.075,982,2.87,1008,3.399,1038,3.873,1076,2.943,1118,4.414,1145,2.897,1196,4.06,1198,4.172,1224,3.003,1300,6.873,1754,3.473,1841,7.194,1849,3.094,2254,2.938,2595,3.619,2596,3.651,2610,6.654,2614,3.792,3059,3.423,3565,5.503,3858,2.953,4897,6.176,4942,5.182,4943,5.69,4944,5.182,4945,5.69]],["t/1652",[4,3.37,109,0.915,164,1.491,182,1.816,203,2.447,236,1.427,238,4.53,247,2.384,377,1.214,502,1.496,527,2.558,614,1.882,630,2.004,647,4.076,653,3.42,757,1.337,760,1.603,814,4.678,916,2.069,945,3.08,966,3.092,1076,3.912,1118,4.318,1138,5.922,1224,3.992,1300,6.132,1632,2.816,1849,5.063,1988,2.27,3565,6.27,4897,5.968,4912,4.943,4913,7.193,4946,7.296,4947,5.149,4948,5.847,4949,5.427,4950,7.897,4951,5.427,4952,7.897,4953,5.847,4954,8.509,4955,5.427,4956,8.509]],["t/1654",[4,2.423,133,3.341,164,2.146,182,2.613,236,2.054,247,3.432,372,3.798,502,2.154,527,2.466,608,6.197,630,2.885,647,2.93,653,2.458,691,4.296,757,1.925,760,2.999,846,2.398,945,3.731,966,2.768,1076,2.812,1138,4.968,1224,2.87,1300,6.695,1632,4.053,1988,3.267,4914,9.246,4946,7.244,4947,7.412,4957,7.412]],["t/1656",[4,2.049,98,2.836,106,1.568,182,3.469,236,2.58,238,3.79,254,2.595,322,2.525,353,1.167,372,3.88,404,2.083,408,1.545,426,1.58,506,2.538,527,1.985,580,1.486,623,4.965,653,2.541,747,1.757,757,1.628,760,2.899,776,3.565,829,2.254,846,2.028,887,6.05,927,2.824,945,3.246,966,3.339,1076,2.906,1081,4.548,1089,2.613,1094,4.537,1118,2.79,1138,6.596,1224,2.966,1300,6.764,1375,2.594,1696,5.774,1765,5.207,1781,1.445,1849,3.593,1988,1.79,2190,5.08,2218,3.094,2618,8.614,3027,3.184,3565,4.45,3858,2.221,4572,3.565,4897,4.994,4911,6.018,4942,3.897,4946,7.4,4947,4.06,4957,4.06,4958,3.658,4959,6.607,4960,7.12,4961,6.607,4962,4.06]],["t/1658",[4,2.691,182,2.903,203,2.077,238,6.796,372,3.246,482,3.88,608,4.503,647,3.255,653,2.731,757,2.138,803,4.886,945,2.871,966,3.231,1076,3.124,1118,4.584,1145,4.417,1177,6.456,1224,3.188,1300,5.872,1988,3.63,2617,6.667,4946,6.192,4963,10.856]],["t/1660",[4,2.736,98,1.738,106,1.813,109,1.244,182,3.907,236,1.939,238,5.608,254,1.59,353,1.349,372,4.251,404,2.408,408,1.785,506,2.934,527,1.081,580,1.717,623,2.368,647,2.767,653,2.776,747,2.031,757,1.219,760,2.179,776,4.122,803,2.785,829,2.606,846,1.518,887,3.297,945,3.46,966,3.313,1076,3.176,1081,3.404,1089,3.021,1094,3.686,1118,4.416,1138,7.227,1224,3.241,1300,6.572,1696,4.692,1765,4.231,1781,1.67,1988,2.069,2015,2.913,2190,4.128,2218,3.577,2617,5.668,4572,4.122,4946,8.108,4957,4.694,4958,4.229,4962,4.694,4964,7.376,4965,4.946,4966,5.33,4967,4.946,4968,4.946,4969,4.505,4970,4.946,4971,9.777]],["t/1662",[4,2.455,182,2.649,203,1.895,238,6.885,372,2.962,482,3.54,527,2.784,608,5.314,647,2.97,653,2.492,757,1.951,803,4.457,927,5.224,945,2.619,966,3.095,977,2.886,1076,2.85,1118,4.324,1145,4.03,1177,5.89,1224,2.909,1285,4.366,1300,5.539,1988,3.311,2254,4.088,2636,9.015,4946,5.649,4972,7.916]],["t/1664",[4,2.674,106,2.249,109,1.454,182,3.618,238,6.203,353,1.673,372,4.046,404,2.988,408,2.215,506,3.639,527,2.491,580,2.13,647,3.234,653,3.138,747,2.52,757,1.512,776,5.113,803,3.455,829,3.233,846,1.884,945,3.578,966,3.309,1076,3.59,1081,4.223,1089,3.747,1094,3.067,1118,4.21,1138,6.341,1224,3.663,1300,3.319,1696,3.903,1765,3.52,1781,2.072,1988,2.567,2015,3.614,2190,3.434,2218,4.437,2635,8.182,4572,5.113,4944,10.376,4946,7.114,4958,5.246,4962,5.823,4969,5.589]],["t/1666",[4,3.021,7,0.768,21,1.042,25,3.838,27,1.986,48,1.496,84,0.399,99,2.325,163,1.461,182,1.234,203,0.883,236,1.928,238,5.242,297,2.016,318,0.898,372,3.13,390,1.78,397,1.533,414,2.115,418,2.938,444,2.548,445,2.325,472,2.862,512,2.528,647,2.75,653,3.216,685,5.902,740,3.781,757,1.807,760,2.166,828,2.451,846,1.805,945,2.426,966,3.177,982,5.515,1037,1.835,1076,3.937,1118,3.859,1145,2.993,1224,3.754,1244,3.072,1300,1.994,1323,2.538,1462,1.994,1931,2.666,2017,2.475,2293,2.157,2430,2.269,2471,2.787,2595,5.813,2596,6.557,2614,6.81,2616,3.499,2617,4.517,2619,4.899,2622,5.027,3027,4.374,3616,2.269,3838,5.302,3918,2.345,4897,2.787,4946,6.521,4973,5.879,4974,5.879,4975,5.879,4976,5.879,4977,3.687,4978,3.687,4979,3.687,4980,3.687,4981,3.687,4982,3.687,4983,5.879,4984,5.879,4985,5.879]],["t/1668",[2,2.108,4,2.425,7,0.616,16,0.996,25,1.714,27,1.27,28,0.755,30,3.348,40,2.82,56,0.528,84,1.1,98,1.881,103,1.791,105,1.674,116,1.997,132,1.572,163,2.121,164,1.47,165,1.199,182,1.791,233,2.829,247,2.352,298,3.269,302,2.464,313,1.91,318,2.631,338,1.317,351,1.937,394,2.675,444,2.32,447,2.532,461,4.376,477,3.175,491,1.613,502,1.476,536,1.594,565,1.747,647,2.008,653,1.685,740,4.591,757,2.276,802,2.165,814,2.305,822,3.197,828,2.232,901,2.628,910,5.719,945,3.361,966,2.77,982,5.691,987,3.982,1019,1.967,1076,2.815,1118,3.9,1144,2.911,1179,3.568,1221,3.727,1224,1.967,1300,4.229,1313,3.11,1336,3.498,1374,3.604,1527,3.532,1605,3.924,1988,2.239,2137,5.732,2430,3.294,2617,4.113,2619,4.46,2643,8.685,3838,6.679,4828,4.875]],["t/1670",[7,0.553,31,2.31,106,2.571,137,4.503,163,2.781,224,2.973,236,3.236,278,2.514,367,3.572,405,2.232,416,3.231,434,4.677,511,2.237,572,5.664,580,2.436,650,7.203,653,2.976,740,4.834,846,2.903,870,5.488,887,4.677,982,3.539,1113,5.007,1221,4.885,1377,5.221,1467,4.388,1689,3.678,2470,6.658,2471,8.083,2598,4.319,2622,8.085,3118,5.847,4686,5.221,4694,10.076,4986,7.017,4987,7.017,4988,8.973,4989,7.017,4990,7.561,4991,7.017,4992,7.017]],["t/1672",[272,4.48,596,4.928]],["t/1674",[1781,3.73]],["t/1676",[7,0.446,12,1.416,16,0.653,24,1.317,28,1.148,31,1.381,36,3.129,39,1.429,49,1.27,56,1.334,84,0.612,86,3.847,98,1.234,99,2.214,100,2.528,106,2.072,107,1.816,109,0.592,112,1.739,113,2.061,121,5.574,123,1.926,127,1.854,132,1.031,133,1.502,137,5.219,145,1.412,163,1.391,164,0.964,165,0.786,170,1.67,171,0.954,172,1.216,179,1.175,197,1.564,207,1.433,228,2.248,246,1.327,278,1.258,289,1.387,296,1.353,304,1.495,322,1.098,323,1.416,336,1.429,338,1.39,372,1.314,381,2.674,405,2.257,408,2.936,409,1.779,433,3.263,495,1.695,505,1.455,508,2.068,527,0.768,577,2.674,578,1.38,605,1.27,614,0.646,630,1.297,635,1.274,723,3.581,749,3.66,751,5.784,757,0.865,760,1.67,838,1.771,855,2.161,916,2.155,937,3.376,977,1.28,993,3.29,1072,1.623,1099,1.3,1100,1.391,1182,1.674,1185,2.859,1236,2.34,1251,2.505,1285,1.497,1302,2.128,1329,3.267,2013,1.169,2041,3.807,2193,3.198,2250,2.546,2257,2.444,2421,2.097,2448,4.438,2864,2.746,2911,2.39,3066,5.964,3398,2.539,3601,4.894,3809,8.69,3810,3.535,3811,2.698,3814,2.925,3815,3.198,4686,2.612,4993,3.783]],["t/1678",[7,0.379,21,1.358,24,1.169,28,1.018,36,3.822,49,1.08,56,1.344,84,0.52,86,3.27,96,1.692,100,3.227,107,1.23,112,2.38,113,1.752,121,5.638,127,2.367,137,3.083,140,1.883,171,1.305,172,1.664,177,2.32,199,3.56,203,1.15,207,3.538,233,1.739,246,1.817,285,1.985,304,1.521,316,3.216,322,1.503,372,1.798,376,2.739,381,2.273,437,2.255,508,2.83,527,2.258,577,3.414,578,1.888,596,2.164,614,0.884,653,2.728,723,1.717,732,3.14,751,5.563,757,1.184,760,1.419,816,2.722,828,2.003,859,2.493,916,3.304,945,2.868,1099,1.779,1329,2.212,1988,2.01,2013,1.6,2041,3.236,2276,2.756,2421,2.87,2911,3.27,3066,8.683,3398,3.474,3601,5.215,3809,7.806,3811,3.692,3814,4.003,3815,4.376,4994,5.177]],["t/1680",[2,1.444,7,0.801,10,1.777,21,0.928,24,0.867,28,0.463,32,2.054,36,2.361,39,1.337,46,1.361,49,1.936,56,1.31,84,0.356,86,2.236,90,2.524,92,4.372,93,3.65,94,1.777,96,1.157,97,2.395,99,2.072,100,2.395,107,0.841,112,1.627,113,1.198,121,4.576,127,1.756,132,0.965,137,2.108,140,1.287,145,1.321,162,1.154,165,1.2,171,1.455,172,1.138,174,1.592,177,1.586,179,1.792,186,1.548,197,1.464,199,2.642,203,1.282,207,1.341,224,1.392,228,1.04,233,1.189,236,0.864,246,2.564,283,0.95,285,1.357,304,1.129,313,1.308,316,1.464,317,1.642,322,2.121,323,2.16,343,1.168,353,0.896,372,1.229,381,1.554,408,2.448,482,1.469,495,1.586,496,2.006,505,1.361,527,2.02,577,2.534,590,1.767,596,1.479,609,2.147,614,0.986,653,1.686,657,2.569,691,1.807,714,2.287,715,2.753,723,2.796,751,4.516,757,1.32,760,1.582,816,1.861,828,1.37,838,2.701,859,1.705,892,1.229,916,2.042,945,1.772,995,1.827,1037,1.634,1099,1.216,1162,1.827,1174,2.212,1182,1.566,1220,1.884,1284,2.236,1329,1.512,1331,2.108,1988,1.374,1998,3.323,2013,1.784,2041,2.212,2217,2.524,2276,1.884,2450,2.189,2868,1.786,2911,2.236,3066,8.112,3398,2.375,3466,3.646,3601,4.08,3809,7.679,3810,2.054,3811,2.524,3814,2.737,3815,2.992,3816,4.048,3817,2.808,4995,5.771]],["t/1682",[2,1.221,5,1.594,7,0.737,12,1.825,16,1.284,17,1.789,23,2.303,24,1.841,26,1.68,28,0.973,29,1.579,36,3.686,48,1.836,49,1.879,56,1.224,84,0.49,85,1.568,86,3.081,103,1.514,121,2.648,130,2.58,137,2.905,138,1.825,140,1.774,165,1.545,171,1.23,189,2.422,190,2.326,193,1.646,207,1.847,219,2.587,228,2.185,232,1.646,233,2.497,236,1.19,239,2.597,289,2.726,296,1.744,313,1.685,322,1.416,324,2.397,336,1.842,342,2.959,373,3.273,381,2.141,405,3.616,407,3.778,408,3.375,409,3.496,438,2.349,458,3.884,464,3.87,495,3.331,502,1.248,508,2.666,527,0.99,567,1.815,572,2.397,580,1.572,592,2.905,600,3.017,673,3.985,683,1.694,723,2.988,731,2.373,744,2.58,750,3.19,751,2.613,757,1.115,807,1.749,811,2.808,831,2.855,876,2.855,984,1.936,993,1.831,1266,1.962,1319,3.081,1475,3.711,1509,3.87,1699,2.808,2013,1.508,2250,2.039,2422,2.905,2450,3.017,2459,3.541,3066,5.058,3667,3.985,3809,7.065,3979,3.609,4127,3.609,4686,3.368,4996,4.526,4997,4.526,4998,4.123,4999,4.526,5000,4.526]],["t/1684",[1781,3.73]],["t/1686",[2,2.106,24,1.265,84,0.846,221,2.885,233,2.827,313,1.908,343,2.778,401,2.985,527,1.708,530,3.493,560,4.157,612,5.058,653,2.458,677,4.115,683,2.922,760,3.332,876,4.926,908,3.976,944,3.159,963,4.734,976,5.812,988,3.819,1019,2.87,1090,3.468,1144,4.248,1217,5.903,1328,5.294,1400,5.853,1566,5.574,1718,5.206,2031,4.179,2128,5.376,2361,7.941,2621,5.648,3771,6.002,3845,7.412,3846,7.412,3847,6.678,3848,7.412]],["t/1688",[1781,3.73]],["t/1690",[2,1.727,5,2.256,7,0.868,49,1.439,56,1.007,138,2.583,165,1.435,171,1.74,203,1.533,207,3.626,246,3.857,254,3.721,256,3.324,275,1.982,276,4.401,288,4.226,304,1.349,313,1.564,315,4.219,337,2.982,338,1.575,343,2.278,416,2.949,476,1.859,491,1.93,513,3.018,691,4.886,707,3.563,715,3.291,735,4.696,760,3.419,769,2.461,774,5.517,810,3.944,830,2.927,977,2.335,1079,4.005,1107,4.84,1110,4.84,1213,4.631,1285,2.731,1367,4.039,2073,5.306,2306,5.215,2594,4.359,2868,3.483,2951,3.798,3823,7.298,3824,5.475,3825,5.475]],["t/1692",[7,0.646,31,2.001,84,0.887,92,3.033,93,3.471,116,3.057,139,3.676,171,2.226,174,2.435,207,3.344,233,2.965,254,2.634,272,3.354,276,4.058,283,2.369,287,5.846,304,2.206,318,1.995,343,2.914,527,1.791,653,2.579,734,3.625,759,2.872,760,3.409,810,3.637,838,4.132,945,2.71,1219,3.65,2868,4.455,3668,6.408,3823,7.667,3826,6.827]],["t/1694",[4,1.748,5,3.35,7,0.64,21,2.293,26,2.092,46,2.335,49,2.339,56,1.169,85,1.952,90,2.655,92,4.696,94,5.144,105,3.449,116,2.103,133,2.411,171,1.531,174,2.412,203,1.943,236,1.482,254,1.812,275,1.744,278,2.019,283,1.629,290,4.696,337,2.624,353,1.537,359,2.485,365,2.855,405,1.793,476,2.356,496,3.442,498,3.135,505,2.335,513,2.655,531,3.416,578,2.214,601,2.645,605,2.937,614,1.037,769,2.166,774,3.048,824,4.132,845,3.584,976,4.193,1080,3.135,1108,2.882,1162,3.135,1364,2.816,1998,5.035,2594,3.836,2868,3.065,2951,3.342,2990,3.442,3466,5.524,3816,4.259,3823,5.998,3827,6.939,3828,6.133,3829,6.939,3830,4.021,3831,3.649,3832,4.818,3833,4.132]],["t/1696",[49,2.01,56,1.328,171,2.429,283,2.585,297,4.89,476,2.595,810,5.571,2611,6.556,2868,4.863,3531,6.226,3823,5.639,3827,7.645,3828,8.359,3829,7.645,3834,6.556]],["t/1699",[4,1.574,5,1.787,7,0.593,24,0.822,31,2.188,39,2.065,46,2.103,56,0.978,84,0.97,115,2.336,116,1.894,138,2.047,139,3.373,165,1.137,182,1.698,203,1.799,207,2.071,221,1.874,233,1.836,236,1.334,254,2.417,256,1.899,275,1.571,318,1.83,325,3.533,338,1.248,377,1.681,414,4.312,444,3.259,461,2.84,476,1.473,479,3.533,508,2.989,527,1.109,605,1.836,614,0.934,647,1.904,653,2.366,724,2.471,757,1.25,759,3.471,760,3.271,769,3.441,774,4.066,810,2.253,830,2.319,846,1.558,908,2.583,916,1.934,945,2.487,966,1.384,988,2.481,1037,2.525,1051,3.645,1070,2.893,1100,2.011,1118,2.143,1181,2.969,1224,1.864,1328,2.647,1364,2.536,1375,3.076,1456,2.673,1793,3.2,1932,3.576,1988,2.123,2017,3.164,2141,2.596,2338,2.791,2339,3.031,2340,3.123,2402,3.757,2577,2.673,2594,5.117,2625,2.911,2951,3.01,3616,4.627,3668,5.88,3823,4.741,3835,4.339,3836,4.467,3837,8.497,3838,5.436,3839,7.461,3840,4.046,3841,3.417]],["t/1702",[4,2.302,5,1.757,7,0.393,10,2.698,16,0.928,31,2.783,39,2.03,46,2.067,56,0.969,84,1.063,138,2.012,139,2.238,165,1.117,182,1.669,203,1.777,207,2.036,221,1.842,236,1.312,254,1.604,256,1.866,275,1.544,287,3.56,302,2.297,318,1.214,325,3.473,338,1.227,377,1.66,414,5.085,461,2.792,476,1.448,527,2.295,568,1.428,592,3.201,614,0.918,647,1.871,653,2.336,757,1.229,759,3.442,760,2.901,769,3.407,774,4.015,802,2.018,810,2.215,830,2.28,840,3.367,846,1.531,908,2.539,916,1.901,944,2.018,945,1.65,966,1.36,988,2.439,1037,2.482,1051,3.599,1070,2.843,1100,1.977,1118,2.106,1219,3.95,1224,1.833,1328,2.602,1364,2.493,1375,3.024,1456,2.628,1601,2.98,1793,3.146,1913,2.809,1988,2.087,2017,3.124,2141,2.551,2338,2.743,2339,2.98,2340,3.07,2402,3.71,2577,2.628,2594,5.052,2625,2.861,2951,2.959,3616,4.568,3823,4.681,3826,6.185,3840,3.978,3841,4.999,3842,8.412,3843,7.043,3844,4.734]],["t/1704",[1781,3.73]],["t/1706",[2,1.746,5,2.281,7,0.872,49,1.455,56,1.013,138,2.611,165,1.45,171,1.759,203,1.55,207,3.653,246,3.879,254,3.559,256,3.349,275,2.004,276,4.434,288,4.273,299,7.455,304,1.364,313,1.582,315,4.252,337,3.015,338,1.593,416,2.981,476,1.879,491,1.951,513,3.051,527,2.54,691,5.642,707,3.602,715,3.328,735,4.747,757,1.595,769,2.488,774,5.549,810,2.875,830,2.959,977,2.361,1107,4.893,1110,4.893,1213,4.682,1367,4.083,1733,3.868,2073,5.347,2306,5.272,2868,3.521,2951,3.84,3666,5.536,3824,5.536,3825,5.536]],["t/1708",[7,0.694,49,1.979,92,3.261,93,4.642,116,3.287,164,2.419,171,2.393,174,2.618,207,3.595,233,3.187,254,3.523,256,3.295,276,5.909,283,2.547,299,7.654,304,1.856,343,3.132,527,1.925,653,2.772,810,3.911,838,4.442,1806,6.206]],["t/1710",[4,1.827,5,3.434,7,0.66,21,2.367,26,2.186,46,2.441,49,2.386,56,1.108,85,2.04,90,2.775,92,4.683,94,5.272,105,3.509,116,2.198,133,2.519,171,1.6,174,2.489,203,2.005,236,1.549,254,1.894,275,1.823,278,2.11,283,1.703,290,4.908,299,6.813,337,2.743,353,1.606,359,2.597,365,2.984,405,1.874,476,2.431,496,3.597,498,3.277,505,2.441,513,2.775,527,1.288,531,3.57,578,2.314,605,3.031,614,1.084,769,2.263,774,3.186,824,4.318,845,3.746,1080,3.277,1108,3.013,1162,3.277,1364,2.944,1998,3.654,2868,3.203,2951,3.493,2990,3.597,3466,4.009,3816,4.451,3828,4.451,3830,4.203,3831,3.814,3832,5.036,3833,4.318,3849,7.162]],["t/1712",[15,3.008,49,1.854,56,1.271,85,2.858,171,2.241,283,2.385,296,3.179,297,4.512,299,5.294,476,2.395,810,5.412,1055,7.604,1207,5.966,1409,5.445,1577,6.235,1802,5.001,1994,5.744,2611,7.71,2868,4.487,3531,5.744,3828,6.235,3834,6.049,3849,7.054,5001,8.25,5002,8.25,5003,8.25,5004,8.25]],["t/1715",[4,1.997,5,2.268,7,0.508,31,2.178,39,2.62,46,2.668,56,1.09,84,0.697,116,2.403,138,2.597,182,2.154,203,1.541,207,2.628,221,2.378,254,2.07,256,2.409,275,1.993,299,6.564,377,1.44,414,5.115,476,1.869,527,2.535,614,1.185,647,2.415,653,2.807,691,3.541,757,1.587,769,3.427,774,4.823,830,2.943,846,1.977,908,3.278,916,2.454,945,2.13,966,1.756,988,3.149,1051,4.323,1070,3.67,1118,2.719,1224,2.366,1328,3.358,1456,3.392,1988,2.693,2017,3.754,2141,3.294,2338,3.541,2339,3.846,2340,3.963,2402,4.457,2577,3.392,2625,3.694,2951,3.819,3616,5.488,3840,5.135,3841,4.336,3850,9.707,3851,5.505]],["t/1717",[4,2.472,56,1.188,116,2.974,182,2.667,221,2.943,377,1.782,476,2.313,527,2.49,647,2.99,653,3.237,757,1.964,846,2.447,916,3.038,945,2.637,966,2.173,988,3.897,1051,4.987,1118,3.366,1224,2.928,1328,4.157,1456,4.199,1988,3.334,2017,4.329,2141,4.077,2338,4.383,2339,4.761,2340,4.905,2402,5.141,3851,6.814,3852,10.806,3853,7.563]],["t/1719",[1781,3.73]],["t/1721",[7,0.563,17,2.824,34,1.82,49,1.606,56,1.138,203,1.71,225,2.554,239,4.099,248,5.412,256,2.673,285,3.954,296,2.753,304,2.43,317,3.571,351,2.586,393,3.509,398,2.517,731,3.745,734,3.161,760,2.111,966,2.61,1123,6.358,1400,5.602,1401,4.208,1467,4.469,1475,3.844,1601,5.718,1632,3.708,1737,2.694,2073,4.268,2142,4.763,2317,5.954,2327,5.818,2723,6.921,2724,6.921,2745,5.589,3595,2.632,4007,6.78,4008,6.78,4009,4.975]],["t/1723",[7,0.674,10,4.625,56,1.062,84,1.165,228,2.708,248,5.524,322,2.674,511,2.726,605,3.893,892,3.199,1123,6.932,1290,5.036,1400,4.437,1565,5.953,1601,5.108,1629,5.953,1638,4.416,2013,2.848,2142,4.255,2723,6.183,2724,6.183,2799,6.27,4010,8.114,4011,8.114,4012,8.114]],["t/1725",[7,0.585,15,2.704,56,1.086,133,3.172,140,2.906,162,2.606,164,2.037,165,1.661,171,2.015,196,2.939,203,1.775,248,4.605,255,4.994,322,3.069,324,3.927,347,4.894,393,3.642,614,1.806,724,3.611,731,3.887,828,3.092,977,2.704,1123,5.779,1265,3.887,1400,6.317,1402,4.587,1475,3.989,1601,5.861,1632,3.848,1633,3.494,2013,2.47,2142,3.69,2723,7.095,2724,7.095,3607,7.194,4009,5.163,4013,6.34,4014,6.34]],["t/1727",[1781,3.73]],["t/1729",[7,0.54,28,1.491,31,2.273,49,1.54,56,0.677,68,2.598,84,0.742,92,3.446,94,3.706,115,4.866,132,2.733,133,2.931,196,2.716,203,1.64,233,2.48,236,1.802,254,2.203,283,1.981,313,1.674,315,3.255,318,1.668,377,2.081,390,3.309,436,4.89,502,3.125,513,3.229,527,1.498,550,2.693,577,4.999,630,2.531,675,4.359,715,3.522,751,3.956,769,2.633,829,3.61,910,5.24,912,5.71,916,3.547,1010,4.568,1015,4.064,1040,4.185,1098,4.322,1988,2.866,2210,5.36,2382,5.179,2402,3.425,2868,3.727,3854,5.464,3856,6.503,3857,6.503,5005,6.853]],["t/1731",[2,1.786,4,2.054,7,0.522,16,1.232,56,1.103,84,0.985,116,2.471,182,2.215,221,2.445,232,2.408,313,1.617,318,1.612,372,2.477,377,1.481,476,1.922,491,1.996,502,1.826,527,2.56,536,1.972,565,2.162,568,1.896,605,3.289,630,2.445,647,2.484,653,2.861,683,2.477,757,1.631,814,4.811,846,2.032,910,6.593,916,2.524,957,3.848,966,1.805,988,3.237,1051,4.407,1072,3.06,1118,2.796,1224,2.433,1266,2.87,1328,3.453,1456,3.488,1481,3.543,1988,2.769,2017,3.826,2141,3.386,2338,3.641,2339,3.955,2340,4.075,2402,4.543,3854,7.248,3858,3.436]],["t/1733",[0,2.444,4,1.313,7,0.516,12,1.707,16,1.219,24,1.061,31,2.522,39,2.666,41,1.92,56,1.22,84,0.458,98,1.487,113,2.389,115,4.495,132,1.243,165,0.948,179,1.416,182,2.682,196,1.677,203,1.013,219,1.31,221,1.563,254,1.361,282,2.302,318,1.595,372,1.584,414,3.758,471,1.754,476,1.229,477,2.51,491,1.276,502,1.167,527,2.636,553,2.241,565,1.382,567,1.697,568,1.876,577,2.002,580,1.47,610,3.252,614,1.206,647,3.007,653,2.841,658,4.322,670,2.46,724,2.061,757,1.043,814,2.821,829,4.223,830,1.934,846,2.461,910,6.263,916,2.498,945,1.4,957,2.46,966,1.787,984,3.429,988,2.07,1051,3.887,1076,3.877,1118,3.385,1224,2.945,1328,2.207,1456,2.23,1671,3.603,1932,2.983,2017,2.758,2141,2.165,2196,8.134,2338,2.328,2339,2.528,2340,2.605,2402,3.275,2875,3.149,2885,3.619,2949,3.619,3858,3.4,3859,4.233,3860,6.217,3861,4.016,3862,4.016,3863,6.217]],["t/1735",[4,2.243,5,2.548,16,1.346,56,1.144,116,2.699,164,1.987,182,2.42,221,2.671,254,2.326,318,1.761,372,2.706,476,2.099,502,1.994,527,2.715,647,2.713,653,3.038,757,1.782,846,2.22,910,6.524,916,2.757,957,4.203,966,1.972,988,3.537,1051,4.679,1118,3.054,1224,2.658,1328,3.772,1408,9.892,1456,3.81,1988,3.025,2017,4.062,2141,3.7,2338,3.978,2339,4.32,2340,4.452,2352,5.162,2402,4.824,3858,3.754,3864,9.158]],["t/1737",[2,1.47,7,0.808,24,0.883,28,0.769,31,1.936,49,2.098,56,1.269,90,2.569,92,4.76,93,4.341,94,2.949,97,2.438,98,1.916,100,2.438,115,2.51,132,1.601,165,1.221,171,2.536,174,2.774,177,2.632,203,1.897,283,1.576,304,1.149,313,1.332,322,2.92,398,1.92,496,3.33,527,2.24,553,2.887,614,1.004,714,3.796,715,2.802,769,3.045,814,2.348,881,4.956,892,2.04,910,5.769,995,3.033,1037,2.713,1162,3.033,1264,3.563,1297,2.643,1689,2.858,1998,4.916,2013,3.109,2464,4.264,2868,2.965,3066,3.997,3466,3.711,3794,4.661,3816,5.988,3817,4.661,3866,7.519,3867,5.173,5006,9.334]],["t/1739",[1781,3.73]],["t/1741",[7,0.693,16,0.636,24,1.726,28,1.241,29,1.193,34,0.871,49,1.8,55,1.859,56,0.791,84,1.073,85,1.184,107,3.253,109,1.925,113,1.247,127,1.121,162,1.944,165,0.766,170,2.928,171,0.929,172,1.184,193,1.243,233,2.522,236,2.106,246,2.092,278,1.225,285,2.286,295,1.375,304,1.166,338,2.164,351,2.002,376,1.949,405,1.088,408,3.398,426,1.263,429,2.923,477,2.028,484,1.448,500,1.617,527,2.495,536,3.335,557,1.701,576,2.014,593,1.686,605,1.237,611,1.421,614,1.733,621,1.757,622,1.724,647,2.075,736,1.458,755,1.54,756,1.605,757,0.842,760,3.372,765,2.028,811,2.121,828,1.426,859,2.871,936,2.302,939,3.972,944,1.383,945,1.83,984,1.462,994,3.449,1037,1.701,1099,1.266,1100,1.355,1288,2.042,1364,3.483,1462,1.849,1508,5.207,1540,3.06,1699,2.121,1754,1.453,1988,1.43,2010,3.71,2211,2.726,2259,3.688,2276,3.174,2525,3.01,2613,2.784,3971,2.544,3972,2.234,4785,5.11,5007,3.419,5008,3.114,5009,2.784]],["t/1743",[7,0.774,16,1.111,24,1.826,28,1.452,31,0.918,49,1.9,56,0.371,84,1.115,107,2.362,109,1.901,123,1.281,127,4.248,132,1.753,165,1.663,170,1.763,193,3.355,197,1.674,233,3.06,276,1.862,278,1.347,296,1.448,304,0.792,318,0.915,338,2.415,373,2.717,405,1.195,426,2.204,433,2.17,476,2.678,491,1.133,500,1.778,524,2.313,527,0.822,536,3.487,565,2.424,568,1.709,592,2.412,593,1.854,605,1.36,609,2.456,614,1.557,635,1.363,647,3.172,653,1.183,691,2.067,756,1.764,760,3.041,818,1.887,840,1.705,936,2.531,939,2.142,984,1.608,994,3.197,1009,2.558,1100,2.366,1144,2.044,1319,2.558,1462,2.033,1508,6.476,1696,2.39,1699,2.331,1754,1.597,2010,3.583,2331,2.39,2472,2.796,3009,7.046,3971,2.796,3972,2.456,5010,6.046]],["t/1745",[7,0.796,16,0.982,24,1.879,28,1.293,49,1.187,84,0.838,107,2.584,109,2.047,170,1.56,172,4.465,174,1.569,233,3.652,278,1.892,304,1.113,338,2.762,405,1.68,426,2.859,502,3.096,527,1.692,536,3.005,592,3.388,593,2.604,605,1.911,614,1.425,647,3.786,756,2.478,757,1.907,760,2.982,819,3.721,936,3.556,984,2.259,994,3.333,1100,3.068,1462,2.856,1508,6.582,1699,3.276,2010,3.512,3971,3.929,3972,3.451,5011,6.618,5012,7.05]],["t/1747",[7,0.728,16,0.996,24,1.887,28,1.303,49,1.203,84,0.847,107,2.6,109,2.088,170,1.581,233,3.342,278,1.918,304,1.128,338,2.775,405,1.703,426,2.887,502,3.111,527,1.709,536,3.025,592,3.435,593,2.64,605,1.937,614,1.439,647,3.81,756,2.512,757,1.926,760,3.001,936,3.604,984,2.29,994,3.367,1099,4.784,1100,3.098,1462,2.895,1508,6.607,1699,3.32,2010,3.535,3971,3.982,3972,3.498,5013,6.684,5014,7.121]],["t/1749",[16,1.757,28,1.614,84,1.023,109,1.593,165,2.116,170,2.791,172,3.272,322,2.954,607,3.62,614,1.739,724,4.599,725,5.823,755,4.254,1508,5.225,2010,3.287,2190,5.286,5015,7.138]],["t/1751",[16,1.743,28,1.606,84,1.015,109,1.58,165,2.099,170,2.768,172,3.246,173,5.014,322,2.931,607,3.591,614,1.725,724,4.562,755,4.22,977,3.417,1508,5.183,2010,3.261,2190,5.244,4574,7.472,5016,6.87]],["t/1753",[16,2.058,25,2.863,27,2.625,28,1.561,84,0.969,107,2.832,109,1.508,165,2.003,322,2.797,614,1.646,623,4.282,724,4.354,1099,3.311,1101,6.607,1285,3.813,1481,4.785,1508,4.946,1557,5.547,2010,3.112,2190,5.004,5017,6.871]],["t/1755",[1781,3.73]],["t/1757",[7,0.938,24,1.635,84,1.094,139,4.53,397,4.197,759,3.539,892,3.777,993,4.084,3925,7.3,3938,9.58]],["t/1759",[7,0.633,24,1.299,56,0.792,84,0.869,89,2.994,109,1.353,139,4.636,164,2.204,165,1.797,171,2.806,174,2.385,285,3.315,502,2.213,527,2.637,568,2.958,605,2.904,614,1.477,724,5.03,729,5.726,759,2.813,944,4.179,945,3.418,966,2.817,1100,3.18,1506,5.149,1959,3.827,1988,3.357,2010,2.793,2276,5.926,5018,6.277]],["t/1761",[5,1.938,7,0.629,19,2.452,24,1.987,28,0.776,31,1.344,39,2.239,41,3.619,68,3.558,84,0.596,96,1.938,107,1.409,109,0.928,115,2.534,133,3.413,134,2.738,139,3.58,160,1.929,164,1.512,165,1.787,200,2.726,203,1.91,226,2.668,236,2.873,288,3.632,295,2.213,318,1.34,338,1.354,353,1.501,367,2.801,414,3.157,527,2.052,568,2.284,591,3.137,605,1.991,630,2.032,638,3.311,757,1.356,759,2.796,760,2.772,769,4.199,770,4.228,774,2.976,827,5.215,938,3.387,939,3.137,1008,3.287,1111,3.831,1182,2.624,1185,4.481,1251,3.927,1322,4.035,1329,5.03,1475,2.96,1506,3.531,1733,3.287,1754,2.339,2231,6.193,2271,3.927,2434,3.746,3386,4.159,3810,3.442,3835,4.705,3893,4.095,3918,3.5,3919,4.844,3920,5.503,3921,5.503,3922,4.705]],["t/1763",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1219,4.277,3823,6.054]],["t/1765",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,945,3.229,3823,6.155]],["t/1767",[4,2.506,7,0.818,49,1.816,56,0.798,84,1.124,109,1.362,139,3.626,165,1.81,170,3.066,200,4.002,203,2.484,206,5.232,246,3.923,247,3.55,304,1.703,613,3.652,614,1.487,759,2.832,760,3.386,966,2.203,988,3.951,1219,3.6,1336,5.281,1364,4.038,1481,4.323,1527,5.332,2013,2.691,3924,6.579,3925,5.842,3926,6.579,3927,9.333]],["t/1769",[4,2.506,7,0.637,49,1.816,56,0.798,84,1.124,109,1.362,139,3.626,165,1.81,170,3.066,200,4.002,203,2.484,206,5.232,246,3.923,247,3.55,304,1.703,613,3.652,614,1.487,759,2.832,760,3.386,966,2.203,988,3.951,1219,3.6,1336,5.281,1364,4.038,1481,4.323,1527,5.332,2013,2.691,3925,5.842,3928,6.579,3929,6.579,3930,9.333,3931,7.667]],["t/1771",[7,0.745,24,1.529,28,1.614,109,1.593,127,3.097,132,2.774,139,4.239,193,3.435,236,2.483,254,3.956,550,3.712,578,3.712,614,1.739,623,4.523,1099,3.497,1101,5.642]],["t/1773",[7,0.694,84,1.187,88,3.967,89,3.287,128,5.688,140,3.451,165,1.973,182,2.947,294,4.738,575,4.183,605,3.187,635,3.195,763,5.995,828,3.673,892,3.295,1076,3.946,1100,3.49,1132,3.911,1425,5.118,1616,6.096,3932,6.889,3933,7.753,3934,7.753]],["t/1775",[16,2.181,28,1.389,34,2.506,107,2.52,109,1.659,132,3.443,170,2.908,353,2.684,484,4.169,614,1.812,966,2.684,3858,5.107]],["t/1777",[7,0.347,12,1.777,16,0.82,24,1.331,25,1.411,34,1.122,36,1.943,49,0.991,56,1.141,68,1.671,84,0.477,96,1.552,107,1.128,109,1.842,132,1.294,139,4.462,140,1.727,170,2.429,179,1.475,199,2.174,236,1.159,295,1.772,296,1.698,313,1.077,338,1.663,353,1.202,377,0.986,426,1.628,476,3.171,491,1.328,502,1.215,512,2.835,527,0.964,575,3.211,580,1.53,614,2.011,647,3.73,653,2.128,663,2.692,725,3.441,756,2.069,759,2.37,803,2.482,828,1.838,846,1.353,857,1.999,866,3.768,892,3.076,945,1.458,966,1.843,993,1.783,1076,2.961,1093,3.829,1099,2.503,1118,4.88,1132,4.415,1224,2.484,1616,3.761,1959,3.921,2253,2.671,2256,7.672,2257,3.069,2900,4.552,2928,2.322,3008,3.069,3917,2.938,3936,3.88,3937,6.76,3938,4.182,3939,6.414,3940,8.117,3941,6.414,3942,2.804,3943,7.641,3944,3.387]],["t/1779",[7,0.814,12,3.236,24,1.85,28,1.132,34,2.043,68,3.042,132,2.357,233,2.904,313,1.96,318,1.954,328,5.296,398,2.826,426,2.963,472,3.907,476,2.329,502,2.213,512,2.767,536,2.39,565,3.373,568,2.298,578,3.153,614,1.477,623,3.842,647,3.875,724,3.907,916,3.059,1098,5.061,1099,2.971,1101,4.793,1132,3.563,1224,2.948,1236,5.349,1849,4.364,2254,4.144,3830,5.726,4323,5.726]],["t/1781",[7,0.751,24,2,29,2.478,31,2.629,39,2.89,49,1.596,125,4.048,139,4.829,165,1.591,197,3.164,203,2.753,213,3.879,236,1.867,246,4.069,272,2.907,292,3.089,304,1.496,414,4.074,491,2.14,759,3.342,760,2.816,769,4.929,811,4.406,867,5.457,908,3.615,1080,3.951,1083,3.443,2231,7.102,2887,4.155,3616,5.867,3918,4.517,3946,6.739]],["t/1783",[7,0.89,16,1.116,24,1.952,25,2.387,28,1.655,29,1.32,31,2.261,34,0.963,37,3.999,39,2.441,49,0.85,56,0.736,68,2.275,85,1.31,96,1.332,103,1.266,106,1.386,112,2.972,133,1.618,134,1.882,139,1.697,140,1.482,145,1.521,160,2.975,165,0.847,171,1.028,179,1.266,203,1.436,221,1.397,223,2.13,225,1.352,228,1.198,236,1.577,257,1.926,279,1.634,292,1.645,304,1.264,313,0.924,318,0.921,323,1.525,338,0.93,343,1.345,377,0.846,394,1.89,395,2.371,397,1.572,418,1.89,426,2.754,484,1.602,500,1.789,512,1.304,527,1.855,530,1.691,560,2.013,565,1.235,568,1.718,605,1.369,609,2.472,614,0.696,630,1.397,635,1.372,712,2.035,723,1.352,747,2.463,757,2.091,759,2.614,760,2.734,769,3.556,827,3.778,857,1.716,892,1.415,938,5.223,957,2.198,977,1.379,984,1.618,1015,4.422,1019,1.39,1072,1.748,1076,2.16,1100,1.499,1181,2.213,1271,1.882,1329,5.308,1413,2.117,1605,2.773,1754,2.55,1781,1.277,2081,2.699,2101,3.016,2229,3.016,2231,6.499,2232,3.016,2293,4.362,2501,2.959,2816,4.228,2868,2.057,2928,1.993,3918,2.406,3947,3.589,3948,3.234,3949,3.589,3950,3.33,3951,3.782,3952,3.234,3953,3.782]],["t/1785",[24,1.693,29,3.648,103,3.498,395,4.132,759,3.665,1530,6.291,2240,8.034]],["t/1787",[7,0.797,18,3.86,24,1.422,28,1.239,31,1.539,34,1.605,56,1.176,68,3.33,84,1.095,103,2.108,105,1.971,123,2.147,132,2.58,145,2.534,160,2.209,165,1.411,196,2.497,257,3.208,275,1.95,279,2.722,318,1.534,353,1.718,398,3.094,484,2.669,527,1.377,565,2.057,568,1.804,759,3.079,760,3.399,769,2.421,838,3.178,857,2.858,906,4.08,933,3.569,957,5.104,1192,3.909,1236,4.2,1262,3.485,1530,3.791,1633,2.969,1735,6.116,1959,3.005,2142,5.032,2227,6.116,2250,2.838,2722,5.406,2833,5.387,3071,4.842,3948,5.387,3950,5.547,3954,5.547,3955,5.387,3956,5.387,3957,5.739,5019,6.301]],["t/1789",[37,5.461,56,1.306,95,6.494,279,3.539,759,2.872,760,3.093,957,6.084,1192,5.082,1530,4.929,1735,5.704,1754,3.482,1959,4.992,2041,5.517,2142,5.209,2229,6.533,2230,7.004,2231,5.407,2232,6.533,2234,6.533,2235,6.295,2240,6.295,2245,6.671,3955,7.004,3956,7.004,3960,7.774,3961,8.192]],["t/1791",[56,1.251,279,3.641,395,3.331,527,1.843,759,2.954,760,2.49,846,3.589,957,6.194,1132,4.733,1192,5.228,1275,8.882,1530,5.071,1735,5.868,1959,5.083,2142,4.194,2234,6.72,2235,6.476,2254,4.352,3955,7.206,3956,7.206,3957,7.676,3962,7.997,3963,7.997,3964,7.997]],["t/1793",[16,1.862,84,1.283,254,3.218,409,5.072,760,2.957,1100,3.967,1268,7.053,1456,5.273,1632,5.194,3965,9.498,3966,8.812]],["t/1795",[2,1.463,7,0.894,17,2.144,21,2.231,24,1.507,29,1.893,39,2.208,49,1.22,84,1.177,100,2.427,107,1.389,109,1.332,116,2.947,138,4.382,165,1.769,170,1.603,171,2.145,183,2.329,193,1.974,200,2.688,203,1.89,219,2.444,220,3.34,236,2.076,248,3.707,272,2.221,283,2.283,296,2.091,313,1.326,351,1.964,398,3.827,512,2.723,525,2.557,614,0.999,677,2.859,734,2.401,757,1.337,759,2.768,760,3.021,780,2.888,827,2.75,859,2.816,906,3.514,966,1.48,1019,3.992,1070,3.093,1219,2.418,1246,3.452,1297,2.631,1540,3.002,1550,4.038,1558,3.055,1698,5.876,2017,2.284,2269,4.522,2271,3.872,2368,8.992,2371,6.752,2377,6.58,2384,3.694,2448,3.394,2764,4.943,3810,3.394,3967,5.149]],["t/1797",[2,1.351,4,1.554,7,0.7,29,1.748,49,1.126,56,0.877,84,1.236,85,1.735,101,2.666,109,0.845,111,6.516,139,3.988,165,1.668,170,2.2,200,2.482,203,1.782,206,3.244,225,1.791,236,1.317,247,2.201,313,1.224,347,3.306,351,1.813,377,1.12,395,1.98,405,1.594,426,1.85,578,1.968,580,3.086,613,2.264,614,0.922,621,2.575,663,3.06,759,3.115,760,2.907,838,2.527,846,2.728,942,3.673,988,2.45,1019,3.615,1037,4.422,1072,2.315,1219,2.232,1285,2.136,1336,3.274,1364,2.504,1527,3.306,1633,3.509,1754,2.129,1842,3.727,2252,7.32,2368,7.56,2377,6.205,2379,4.41,2388,5.98,2515,5.937,2625,5.098,3023,5.54,3906,3.727,3924,4.079,3926,7.236,3927,4.079,3928,4.079,3929,7.236,3930,4.079,5020,5.01,5021,9.838,5022,8.887,5023,5.01,5024,5.01,5025,5.01]],["t/1799",[1781,3.73]],["t/1801",[7,0.938,84,1.094,139,4.53,170,2.983,397,4.197,759,3.539,892,3.777,993,4.084,1506,6.478,3925,7.3]],["t/1803",[24,1.75,31,2.104,56,0.85,68,3.266,113,3.141,123,2.935,139,4.849,179,2.882,183,3.697,313,2.104,527,2.707,568,2.467,632,4.634,759,3.788,760,2.545,944,3.484,945,3.575,966,2.349,1037,4.286,1100,3.414,1219,3.838,1506,5.527,1988,3.603,2276,4.941]],["t/1805",[4,3.243,28,1.475,107,2.677,109,1.763,505,4.333,760,3.089,966,2.851]],["t/1807",[12,4.179,24,1.678,109,1.747,254,3.332,512,3.573,578,4.072,614,1.908,1754,4.404]],["t/1809",[7,0.782,12,4.003,24,1.607,109,1.674,128,4.733,254,3.191,578,3.9,614,1.827,681,6.207,736,4.232,966,2.706,1425,5.768,1616,5.522]],["t/1811",[24,1.691,28,1.584,31,2.238,34,2.333,36,2.953,39,2.725,49,1.505,84,0.725,109,1.545,116,2.499,132,1.967,133,2.865,139,3.005,164,2.868,165,1.5,170,3.474,203,2.193,285,2.766,289,2.646,338,1.647,353,1.826,367,3.409,382,4.154,394,3.347,397,2.784,414,3.841,505,2.775,525,3.155,614,1.233,693,4.121,756,3.143,759,3.211,760,3.085,769,4.314,830,3.06,908,3.409,1206,3.564,1309,3.891,1329,4.808,1506,4.297,1558,3.77,1671,3.683,2231,4.419,2311,4.51,2960,4.188,3918,4.259,3922,5.725]],["t/1813",[7,0.711,21,1.85,29,2.284,31,2.849,39,2.664,49,1.471,125,3.732,139,4.631,165,1.467,170,3.774,197,2.917,203,2.662,213,3.67,236,1.721,246,4.205,272,2.68,292,2.848,304,1.38,323,2.64,338,1.61,414,3.756,491,1.973,605,2.369,614,1.205,734,2.897,759,3.162,760,3.285,769,4.909,811,4.062,867,5.031,908,3.333,1080,3.642,1083,3.174,2231,6.81,2887,3.83,3616,5.552,3918,4.164]],["t/1815",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3924,9.359,3925,6.885,3926,7.754,3927,7.754]],["t/1817",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3925,6.885,3928,9.359,3929,7.754,3930,7.754]],["t/1819",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1219,4.277,3823,6.054]],["t/1821",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,945,3.229,3823,6.155]],["t/1823",[21,1.809,56,1.212,68,3.368,107,2.274,109,2.111,139,2.874,170,1.892,254,2.059,338,1.575,353,1.746,377,1.432,596,2.884,605,2.317,614,2.261,647,3.332,653,2.016,755,2.884,760,1.892,1076,2.306,1099,4.079,1118,4.887,1224,2.353,1737,2.415,1932,6.26,2256,8.521,2900,5.982,2903,5.215,3943,9.201,3944,4.921,5015,6.714,5016,6.514,5017,6.826]],["t/1825",[7,0.624,49,1.779,56,0.782,84,1.229,103,2.649,109,1.727,164,2.175,170,2.339,217,4.512,279,3.42,377,1.77,398,3.607,527,1.731,565,2.584,760,3.354,811,4.911,945,2.619,957,5.951,1367,4.992,1530,4.763,1550,5.89,1558,4.457,1793,4.992,1959,3.775,2076,4.6,2142,5.648,2227,5.512,2235,6.083,2722,4.872,3911,8.755,3954,6.969,4250,7.916,4251,7.512]],["t/1827",[7,0.818,34,2.058,84,0.875,103,3.472,109,1.933,123,2.753,170,3.066,410,5.332,527,1.766,565,2.638,568,2.314,630,2.984,683,3.023,723,2.888,759,3.637,760,2.387,769,3.105,1027,5.139,1072,3.734,1207,5.842,1401,4.759,1566,5.766,1754,3.434,1759,5.561,2227,5.625,2228,5.625,2293,4.727,2462,7.359,4765,9.45,5026,9.45,5027,8.08,5028,8.08]],["t/1829",[1781,3.73]],["t/1831",[7,0.65,12,3.327,28,1.776,84,0.894,89,3.079,105,2.58,107,2.112,174,2.452,228,2.613,372,3.087,377,1.845,390,3.983,397,3.429,405,4.005,426,3.883,496,5.039,511,3.352,536,2.457,558,3.689,622,4.161,723,3.758,892,3.087,1219,3.676,2928,6.098]],["t/1833",[16,2.273,28,1.312,183,3.99,322,2.907,405,3.603,426,3.433,495,5.469,511,2.963,670,5.402,837,5.635,871,6.816,1407,6.634,2076,5.402,3968,6.917,3969,8.821,5029,9.296,5030,9.296]],["t/1835",[405,3.326,511,3.333,757,2.576,2226,10.457,5031,10.455]],["t/1837",[7,0.739,28,1.322,49,2.106,84,1.015,109,2.15,183,4.021,405,2.981,409,4.748,426,3.46,511,2.987,622,4.726,846,2.876,993,3.79,1753,7.834,2010,3.261,5032,9.37]],["t/1839",[130,5.755,183,4.333,338,2.483,405,3.211,511,3.218,724,4.915,994,5.125,3972,7.779,5033,10.096]],["t/1841",[163,3.967,183,4.296,277,6.086,405,3.184,511,3.191,622,5.049,723,3.578,916,4.807,5034,10.009]],["t/1843",[28,1.4,107,3.328,109,1.674,405,3.157,426,3.665,511,3.164,820,6.459,5035,9.925]],["t/1845",[16,1.703,34,2.33,121,5.353,254,2.942,353,3.059,397,3.804,405,2.911,408,4.566,484,3.877,511,2.917,581,6.617,712,4.923,1632,4.749,3008,6.371,3976,11.514,3977,8.684,5036,9.151]],["t/1847",[16,1.801,343,3.443,351,3.503,405,3.079,432,7.321,476,2.81,511,3.086,583,5.911,635,3.512,757,3.064,2448,6.054,3979,7.718,5037,9.679]],["t/1849",[49,2.106,109,1.58,130,5.341,171,2.546,172,3.943,174,2.785,183,4.021,377,2.096,405,2.981,426,3.46,505,3.883,511,2.987,580,3.952,636,5.276,724,4.562,1099,4.215,5038,9.37]],["t/1851",[24,1.554,145,3.86,228,3.041,405,3.054,511,3.06,760,3.798,769,4.761,814,4.134,1182,4.578,1696,6.106,2250,4.323,5039,9.6]],["t/1853",[24,1.649,405,3.239,511,3.246,690,6.317,760,3.009,769,4.882,1696,6.477,5040,10.183]],["t/1855",[16,1.801,26,4.309,121,5.663,183,4.154,322,3.027,405,3.079,407,4.375,408,3.494,511,3.086,750,6.821,757,2.86,936,6.518,5041,9.679,5042,9.679]],["t/1857",[4,2.883,172,3.22,183,3.99,193,3.381,394,4.646,405,2.957,407,4.202,511,2.963,552,3.29,725,5.767,822,5.553,857,4.217,1077,5.203,1100,3.684,1355,5.767,3968,6.917,3984,10.317,3985,8.467,5043,9.296]],["t/1859",[5,2.715,7,0.883,16,1.873,25,2.468,28,1.088,56,0.761,84,1.09,105,2.411,125,4.394,133,3.297,144,3.691,145,3.099,164,2.117,174,2.291,196,3.055,198,4.9,228,2.442,294,4.147,372,2.884,390,3.722,458,4.34,511,4.03,536,2.295,683,2.884,807,4.591,892,3.766,966,2.102,1206,4.103,1632,4,1779,5.191,2598,6.194,3986,7.314]],["t/1861",[5,3.056,16,2.02,24,1.918,28,1.532,84,0.94,132,2.548,134,4.318,162,3.049,239,4.977,255,5.844,367,5.527,407,3.922,511,2.766,647,3.255,807,4.797,832,5.844,1078,6.274,1182,4.138,1303,5.568,2390,6.787,5044,8.677,5045,8.677]],["t/1863",[5,1.728,16,2.108,24,1.577,25,1.57,26,2.721,27,1.164,28,1.599,31,1.198,46,2.033,49,1.102,109,0.827,163,1.944,170,2.166,193,2.666,200,2.43,201,3.731,224,2.078,225,1.753,240,2.682,281,2.32,289,3.848,336,1.996,338,1.206,405,3.097,407,2.217,495,3.539,511,3.924,552,1.736,558,2.193,568,1.405,632,2.639,635,2.659,694,2.667,723,1.753,725,2.497,757,1.209,769,3.373,807,3.762,812,3.269,816,2.779,1181,2.869,1219,5.19,1406,6.743,1481,2.625,2403,2.729,2404,5.045,2584,3.456,2950,3.769,3386,3.707,3897,3.837,3968,3.65,3995,6.453,5046,4.905,5047,7.33,5048,4.905,5049,4.905,5050,4.905,5051,7.33,5052,4.905,5053,7.33,5054,4.905,5055,7.33,5056,4.905,5057,7.33,5058,4.905,5059,4.905,5060,7.33,5061,4.905,5062,4.905,5063,7.33,5064,4.905,5065,7.33,5066,4.905]],["t/1865",[16,1.519,17,1.722,24,1.762,25,1.395,26,2.488,27,1.591,28,1.688,36,1.922,41,1.977,49,1.835,84,0.472,89,2.501,103,1.458,109,1.377,132,1.28,145,2.695,162,2.355,169,2.383,203,1.043,240,2.383,281,2.062,285,1.801,286,2.642,287,4.782,289,2.648,367,2.219,377,1.499,394,2.178,405,2.597,407,1.97,408,3.309,409,2.208,432,2.749,476,1.945,511,3.815,512,2.311,527,0.953,575,3.183,580,1.513,635,2.431,653,1.372,662,2.533,693,2.682,724,2.122,731,2.284,736,1.858,751,2.516,757,1.074,760,1.98,807,2.59,945,1.442,994,2.886,1022,2.397,1178,2.516,1219,5.332,1285,1.858,1462,2.357,1481,2.332,1557,2.704,1696,2.772,1703,5.73,2593,3.726,2613,3.549,3093,3.475,3972,2.849,3979,3.475,5067,6.702,5068,4.358,5069,6.702,5070,4.358,5071,6.702,5072,4.358,5073,6.702,5074,4.358,5075,6.702,5076,4.358,5077,6.702,5078,4.358,5079,6.702,5080,4.358,5081,4.358,5082,4.358,5083,6.702,5084,4.358,5085,8.165,5086,4.358,5087,4.358,5088,4.358,5089,6.702,5090,4.358]],["t/1867",[1781,3.73]],["t/1869",[4,1.567,7,0.779,27,1.199,28,1.057,31,1.831,34,1.287,49,1.136,56,0.74,84,0.967,96,3.146,107,2.701,109,2.261,123,2.554,127,2.457,133,2.161,160,1.771,164,1.388,170,1.493,172,1.75,174,1.502,182,1.691,193,1.838,197,2.251,203,1.209,228,1.6,254,1.625,272,3.068,295,2.032,313,1.234,323,2.038,351,1.828,391,2.127,397,2.1,405,1.607,416,2.326,439,3.303,557,2.514,580,1.754,597,2.609,607,2.872,614,1.379,621,2.597,635,1.833,658,2.419,731,2.648,747,2.075,755,2.276,827,2.56,829,2.662,846,1.551,859,2.622,937,3.018,966,2.043,977,2.732,993,3.996,1099,1.871,1102,3.018,1141,3.606,1145,2.572,1175,2.326,1364,2.525,1462,2.733,1557,3.135,1753,3.478,1754,2.147,1843,3.403,1978,3.883,2010,3.847,2191,3.334,2268,4.271,2390,3.952,2577,2.662,3970,3.883,5091,5.053,5092,5.053]],["t/1871",[7,0.907,34,2.74,49,1.596,56,1.317,84,0.769,109,2.083,164,3.16,278,2.545,295,4.327,304,1.496,408,2.564,452,6.561,495,3.429,527,1.553,614,1.307,756,3.333,846,2.926,908,5.478,966,1.936,983,5.457,1558,3.999,1705,4.337,2010,2.472,2015,4.183,2214,6.173,3671,5.457,3868,6.072,3869,5.457,3870,6.739]],["t/1873",[7,0.531,27,2.183,34,1.715,40,3.548,56,1.034,84,0.729,98,2.366,107,1.724,109,2.213,111,4.938,127,3.016,164,1.85,170,1.99,179,3.077,193,2.449,200,3.336,203,1.612,295,2.708,313,1.645,320,3.863,322,2.106,353,1.836,447,3.186,452,4.635,505,2.791,527,2.011,614,2.072,683,2.52,818,3.381,846,2.823,847,5.37,892,2.52,1114,5.929,1132,2.99,1285,2.872,1693,3.94,2010,4.41,2191,4.445,3871,8.954,3872,6.134]],["t/1875",[7,0.418,24,1.258,26,1.969,27,1.259,28,0.748,34,1.351,56,0.907,84,0.995,107,1.988,109,2.218,110,4.84,162,1.864,164,2.133,165,1.188,170,1.567,171,1.441,186,2.499,216,2.7,236,2.416,254,1.705,257,2.7,272,2.171,278,1.9,295,2.133,296,2.044,338,1.305,353,1.446,372,1.984,377,2.055,408,3.317,440,2.339,452,3.65,502,1.462,527,2.82,605,1.919,607,2.976,613,3.51,614,1.43,653,1.669,663,3.239,724,2.582,759,1.859,760,1.567,846,2.384,938,3.264,945,3.562,957,4.513,993,2.145,1145,2.7,1287,3.889,2010,3.198,2211,4.229,2729,3.946,3874,5.033,3875,5.033,3876,4.669,3877,3.835,3878,4.669,3879,5.033,3880,4.669,3881,5.304]],["t/1877",[7,0.972,28,0.996,55,5.835,56,1.133,85,3.289,101,3.757,107,2.747,109,1.935,162,3.336,165,1.581,170,3.39,171,1.918,304,1.487,338,1.736,408,2.548,429,6.035,527,1.543,611,2.934,614,1.748,622,3.56,759,2.474,760,3.17,865,4.754,939,4.024,1038,4.805,1089,4.311,1090,3.134,1216,5.629,1218,4.614,1262,3.905,1288,4.217,1365,4.075,1399,4.247,1540,3.905,1754,3,2010,2.457,4785,5.176,5008,6.429]],["t/1879",[7,0.784,27,2.039,29,2.13,41,2.769,56,0.603,96,3.8,107,2.911,109,2.193,127,2.817,160,3.782,171,1.658,179,3.326,198,4.183,246,2.308,256,3.214,293,4.867,408,2.203,444,2.646,445,3.85,527,2.578,603,5.219,615,3.571,635,2.215,734,2.701,803,3.437,838,3.079,918,3.917,945,2.02,957,3.547,966,2.711,994,2.628,1494,5.219,1693,5.026,1841,5.374,2010,3.46,2191,4.028,2444,4.069,2763,5.792,3077,5.374,3763,3.883,3876,5.374,3878,9.497,3932,4.775,5093,8.591]],["t/1881",[2,0.914,7,0.267,21,0.958,26,1.258,28,0.478,29,1.182,31,0.828,33,3.132,35,2.36,41,1.537,49,1.791,56,1.143,84,0.948,85,1.174,89,1.265,96,1.935,105,1.06,106,1.242,108,2.837,109,2.139,112,2.721,113,1.236,123,1.155,140,2.153,160,1.188,170,1.001,171,1.492,174,1.007,179,1.838,182,1.134,193,1.233,196,1.343,203,1.658,228,1.073,236,0.891,262,2.215,275,1.049,277,1.742,295,1.363,304,0.714,313,0.828,316,2.448,318,0.825,322,1.718,336,2.819,338,0.834,343,1.205,353,0.924,359,1.494,372,1.268,377,1.549,391,2.916,398,1.935,405,1.078,408,1.983,444,1.469,491,1.021,502,0.934,509,1.92,527,1.201,553,1.795,568,0.97,578,1.332,605,1.226,613,1.532,614,2.131,623,2.631,658,1.623,696,2.388,723,1.211,756,1.591,828,2.291,846,3.554,892,1.268,937,2.024,993,3.542,1073,2.418,1099,2.565,1162,1.886,1284,2.307,1398,2.333,1558,3.093,1588,3.087,1693,1.983,1705,2.07,1982,2.604,2010,4.439,2013,1.83,2141,1.733,2142,2.734,2191,2.237,2214,3.557,2252,2.522,2268,3.132,2625,3.151,2722,2.086,2799,2.485,3604,3.465,3839,2.824,3880,2.983,3891,3.626,3903,3.216,3904,3.216,3905,3.216,3906,2.522,3907,2.702,3908,3.216,3909,3.216,3910,3.216,3911,5.923,3914,3.216,3915,3.216,3916,3.216,5094,3.389,5095,3.389]],["t/1883",[49,2.23,84,1.075,109,2.119,336,4.038,391,4.178,614,2.169,993,4.014,2010,3.454,2268,5.657,2799,7.277]],["t/1885",[4,1.29,7,0.509,24,1.283,28,1.509,34,2.018,56,1.228,84,0.45,96,1.464,98,1.461,107,1.654,108,3.337,109,1.865,132,1.221,161,2.83,164,1.142,171,1.129,182,1.391,207,1.697,221,1.535,254,2.549,282,2.261,304,1.362,318,1.012,343,1.479,353,1.134,395,1.643,476,2.301,480,2.83,482,1.859,484,1.761,502,1.146,517,2.52,527,2.241,530,1.859,578,1.634,580,1.444,599,2.137,614,0.765,653,1.309,658,4.28,707,2.313,741,2.692,757,1.024,846,2.433,858,2.051,916,1.585,944,1.682,945,2.622,988,2.033,994,2.783,1051,2.016,1099,4.446,1100,1.648,1132,2.869,1328,2.168,1364,2.078,1456,2.19,1737,1.568,2010,1.447,2017,2.72,2141,2.127,2338,2.287,2339,2.483,2340,2.559,3595,1.531,3763,4.11,3771,4.965,3791,7.869,3885,3.555,3886,3.945,3887,3.555,4436,6.978,5096,6.462,5097,6.462,5098,6.462,5099,6.462,5100,6.462,5101,6.462,5102,6.462,5103,6.462,5104,6.462,5105,6.462,5106,6.462,5107,6.462,5108,6.462,5109,6.462,5110,6.462,5111,6.462,5112,4.158]],["t/1887",[12,2.073,17,2.032,24,1.229,25,1.646,27,2.731,28,1.569,30,3.216,31,1.256,33,2.931,40,2.708,56,0.891,84,0.822,85,1.781,98,1.807,103,2.54,106,3.307,107,2.721,109,1.94,115,2.367,132,1.51,138,2.073,164,1.412,165,1.152,171,1.397,174,1.528,179,1.72,182,1.72,183,2.207,197,2.291,199,2.536,236,1.352,254,2.441,256,1.924,289,2.032,302,2.367,304,1.599,313,1.256,318,1.252,369,3.19,413,5.736,416,2.367,489,3.427,502,1.418,507,2.796,513,2.423,525,2.423,527,1.124,550,3.546,590,2.766,614,0.946,615,3.008,623,2.462,631,3.36,755,2.315,809,3.539,937,3.071,945,2.511,994,2.214,1008,3.071,1062,2.472,1063,4.526,1099,2.81,1101,5.952,1102,3.071,1111,6.282,1132,3.37,1222,3.951,1364,2.57,1459,3.36,1481,2.751,1737,1.939,1785,4.396,2010,1.789,2900,5.112,2903,4.186,2951,3.049,3595,1.894,3885,4.396,3890,4.526,5017,3.951,5113,5.141]],["t/1889",[27,1.931,28,1.623,56,1.029,99,5.131,109,1.757,132,2.389,162,2.859,170,2.404,171,2.21,236,2.139,304,1.714,336,3.31,338,2.001,550,3.197,614,1.498,635,2.952,636,4.581,724,3.961,740,3.677,977,2.966,994,3.503,1099,3.012,1101,6.869,1481,4.353,1508,4.5,1737,3.068,1785,6.956,2010,3.627,2990,6.365,3595,2.997,5018,6.364]],["t/1891",[4,1.884,5,2.139,7,0.781,27,2.795,34,1.546,49,1.365,50,3.553,56,0.979,68,2.302,84,0.658,98,2.134,109,1.985,126,4.133,164,2.351,171,2.325,172,4.279,203,1.454,207,4.393,240,3.321,256,3.202,272,2.486,279,2.624,282,3.303,295,2.442,304,1.28,313,2.091,316,2.706,343,3.044,353,1.656,404,4.168,408,2.192,476,1.763,480,4.133,482,2.715,489,6.607,531,3.681,578,2.386,599,4.399,614,1.118,707,3.379,734,2.687,993,3.462,1083,2.944,1364,3.035,1671,3.34,2010,2.113,3604,3.83,3887,5.192,3891,6.542,3892,5.762,3893,4.518]],["t/1893",[7,0.505,16,0.765,24,0.666,27,1.521,28,0.904,29,1.435,31,1.005,34,1.632,39,1.674,46,1.705,49,1.44,56,1.278,84,0.694,92,2.373,93,3.335,100,2.866,102,2.594,108,4.066,109,1.721,112,2.038,132,1.208,163,1.63,165,0.922,171,1.118,172,4.254,173,2.201,179,1.376,182,1.376,183,1.766,193,1.496,207,3.213,221,1.519,254,1.323,256,1.539,261,2.085,276,3.9,279,1.777,296,1.585,304,1.351,316,1.833,318,1.002,336,1.674,343,1.463,377,1.761,391,1.732,395,1.626,408,1.485,476,2.58,502,1.134,517,2.494,531,2.494,553,3.394,578,1.616,599,2.114,614,0.757,620,2.276,653,1.295,662,2.391,728,2.864,734,1.82,741,2.664,751,2.375,755,1.853,757,1.014,818,2.065,838,2.075,913,3.061,916,1.568,988,2.012,994,2.76,1051,1.995,1075,4.659,1077,3.587,1100,1.63,1328,2.145,1368,2.64,1377,3.061,1456,2.167,1509,3.517,1689,3.359,1806,2.899,1857,4.462,2010,2.231,2013,1.37,2017,2.698,2041,2.77,2073,2.457,2141,2.104,2191,2.715,2293,2.407,2325,3.517,2338,2.263,2339,2.457,2340,2.532,2344,3.517,2868,2.237,2887,2.407,3604,4.965,3763,4.077,3891,4.23,3893,3.061,3894,3.622,3895,3.35,3896,3.622,4692,3.218,5114,4.114]],["t/1895",[2,1.512,5,1.975,7,0.868,12,2.262,27,1.92,28,1.618,30,3.508,49,1.26,56,0.799,84,1.027,107,1.436,109,1.599,113,3.459,170,2.389,171,2.197,172,3.596,203,1.342,224,3.426,236,1.475,246,3.058,304,1.182,318,1.365,336,2.282,338,1.379,351,2.029,391,2.361,432,3.537,505,2.324,511,1.788,525,2.643,552,3.357,597,2.896,599,2.882,614,1.911,636,3.158,724,2.731,725,5.838,807,3.665,832,3.777,854,4.112,944,3.271,994,2.415,1075,2.939,1077,3.139,1093,3.177,1098,3.537,1181,3.281,1285,2.391,1302,3.399,1481,4.328,1508,3.102,1737,2.115,2010,2.815,2904,4.055,2928,2.954,2947,4.673,2990,3.425,3595,2.066,3604,3.537,3891,3.701,3897,4.387,5015,7.168,5018,4.387]],["t/1897",[0,3.09,4,1.66,5,2.754,7,0.852,24,0.867,27,2.192,28,1.103,31,1.308,34,1.363,49,1.203,56,0.772,84,0.847,109,1.822,113,1.952,123,1.824,140,2.097,162,1.881,164,1.47,170,2.31,171,2.509,172,3.199,173,4.942,174,2.324,184,4.045,193,2.843,203,1.281,228,1.695,236,1.407,255,3.604,261,2.712,275,1.657,289,2.115,296,2.063,304,1.128,320,3.07,323,2.159,336,2.178,338,1.317,353,1.459,377,1.197,426,1.977,432,3.376,484,3.312,505,2.218,550,2.103,568,1.533,596,2.41,614,1.989,632,2.879,636,3.014,724,2.606,725,2.725,728,3.727,755,3.521,802,2.165,828,2.232,854,3.924,858,2.64,908,2.725,944,2.165,977,1.952,994,2.305,1093,5.232,1182,2.553,1481,4.183,1508,2.961,1737,2.018,2010,2.721,2876,6.234,2946,4.268,2990,3.269,3595,1.972,3604,3.376,3891,3.532,3893,3.982,4574,4.268,5015,4.045,5016,5.732,5018,4.187]],["t/1899",[24,1.248,28,1.088,34,1.963,56,0.994,107,2.869,109,1.697,113,2.811,193,2.803,246,3.806,285,3.184,295,3.099,304,1.624,338,1.896,351,3.642,477,4.572,527,2.2,576,4.54,605,2.789,614,1.419,621,3.961,755,4.533,765,4.572,859,5.223,944,3.118,945,2.55,994,3.319,1364,3.853,1540,4.264,1737,2.907,1988,3.224,2010,3.503,2259,5.138,2276,5.773,3595,2.839,4785,5.652,5009,6.277]],["t/1901",[28,1.389,56,0.972,107,2.52,109,1.659,304,2.074,527,2.152,755,4.432,945,3.256,1737,3.711,1988,4.117,2010,3.425,2276,5.645,3595,3.625,5009,8.014]],["t/1903",[7,0.66,16,1.096,24,0.954,25,1.886,27,1.988,28,1.375,31,2.046,34,1.5,41,2.672,56,0.827,84,1.056,107,2.496,109,1.892,113,2.148,123,2.854,127,3.679,131,2.672,132,2.863,162,2.07,165,1.319,171,1.6,193,2.142,213,2.397,225,2.105,256,2.204,295,2.368,302,2.712,304,1.241,318,1.434,377,1.317,433,3.4,472,2.868,491,1.775,524,3.625,536,1.754,557,2.931,565,3.183,614,1.794,615,3.446,628,3.042,630,3.093,635,2.137,810,2.615,845,3.746,900,5.185,1008,3.518,1009,4.009,1021,4.009,1121,3.57,1144,3.203,1225,4.382,1274,4.696,1737,2.221,1754,4.513,1857,5.832,2010,2.05,2076,3.423,2331,3.746,2966,4.696,3009,4.908,3595,2.17,5010,7.938]],["t/1905",[7,0.731,16,1.268,27,2.501,28,1.596,34,1.735,41,3.091,56,1.04,84,1.004,107,2.373,109,1.994,113,2.484,127,4.002,131,3.091,132,2.722,165,1.526,193,2.478,225,2.435,256,2.549,302,3.137,304,1.436,313,1.664,318,1.659,338,1.676,433,3.933,472,3.317,524,4.193,536,2.029,557,3.39,565,3.026,615,3.986,630,2.516,635,2.472,845,4.334,1008,4.07,1009,4.637,1021,4.637,1737,2.569,1754,4.478,1857,6.454,2331,4.334,3009,5.677,3595,2.51,5010,5.548]],["t/1907",[28,1.424,56,0.997,109,1.702,127,3.311,304,2.127,755,4.547,1737,3.807,2010,3.514,2276,5.791,3595,3.719,5115,9.195]],["t/1909",[28,1.4,56,0.98,107,2.541,109,1.987,172,3.438,304,2.091,318,2.416,338,2.441,502,2.737,1737,3.743,3595,3.656,5011,8.486]],["t/1911",[28,1.424,56,0.997,172,3.497,254,3.246,304,2.127,338,2.483,502,2.784,1737,3.807,2010,3.514,3595,3.719,5012,9.195]],["t/1913",[28,1.412,56,0.988,109,1.688,172,3.467,304,2.109,502,2.76,755,4.508,1737,3.775,2010,3.484,2276,5.742,3595,3.687,5116,9.117]],["t/1915",[28,1.4,56,0.98,107,2.541,109,1.987,254,3.191,304,2.091,338,2.441,502,2.737,1099,3.675,1737,3.743,3595,3.656,5013,8.486]],["t/1917",[28,1.424,56,0.997,254,3.246,304,2.127,338,2.483,502,2.784,1099,3.738,1737,3.807,2010,3.514,3595,3.719,5014,9.195]],["t/1919",[28,1.412,56,0.988,109,1.688,304,2.109,502,2.76,755,4.508,1099,3.706,1737,3.775,2010,3.484,2276,5.742,3595,3.687,5117,9.117]],["t/1921",[16,1.651,28,1.252,84,0.961,107,2.818,109,1.856,170,2.622,196,3.517,246,3.355,304,1.87,338,2.183,405,2.823,408,3.974,527,2.407,614,1.634,936,5.976,994,4.74,1737,3.347,2010,3.088,2211,7.076,3595,3.269,3971,6.603,3972,5.8]],["t/1923",[2,1.505,7,0.746,12,2.251,16,1.039,28,1.137,41,2.532,56,1.165,84,1.122,107,1.429,109,2.076,132,2.367,170,2.381,179,1.868,196,2.212,203,1.336,225,1.995,226,2.706,246,3.048,257,2.841,300,3.383,304,1.698,322,1.746,338,1.982,377,1.802,395,2.206,408,2.909,447,2.64,605,2.02,614,1.484,705,3.435,709,3.648,765,3.31,818,5.516,845,3.55,846,3.373,896,4.092,966,1.522,1037,2.777,1052,6.891,1539,3.72,1718,3.72,2010,4.206,2015,4.747,2169,5.084,2656,5.219,3023,7.039,3763,3.55,3906,4.153,4785,4.092,4850,4.545,4860,8.616,5118,5.581,5119,5.581,5120,5.581,5121,5.581]],["t/1925",[56,0.988,107,2.563,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1132,4.444,1737,3.775,2012,7.339,3595,3.687]],["t/1927",[56,0.988,127,3.282,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1132,4.444,1737,3.775,2011,7.448,3595,3.687]],["t/1929",[56,0.98,261,5.029,304,2.091,353,2.706,536,3.509,755,4.47,1132,4.407,1329,4.57,1737,3.743,1959,4.733,3595,3.656,5122,8.486]],["t/1931",[56,1.118,84,1.007,107,2.38,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,966,2.535,993,3.76,1402,5.296,1622,5.965,2012,6.816,2960,7.085,3901,7.948]],["t/1933",[56,1.118,84,1.007,127,3.048,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,966,2.535,993,3.76,1402,5.296,1622,5.965,2011,6.917,2960,7.085,3902,7.948]],["t/1935",[56,1.113,84,0.999,140,3.614,196,3.655,304,1.943,536,3.626,614,1.698,755,4.154,966,2.515,993,3.73,1329,4.246,1402,5.271,1622,5.918,1959,4.398,2960,7.051,5122,7.885,5123,8.4]],["t/1937",[24,0.781,26,1.791,28,0.681,29,1.683,31,1.179,48,1.957,49,1.084,56,1.113,105,1.509,107,1.853,109,1.628,112,3.586,123,1.644,127,1.582,140,1.89,162,1.695,165,1.622,172,2.508,173,2.581,174,2.152,193,1.755,203,1.733,206,3.124,318,2.116,322,4.121,336,1.963,338,1.187,377,1.079,391,2.031,395,1.906,398,3.401,502,1.33,527,1.055,593,2.379,613,2.181,614,1.778,623,2.31,755,2.173,846,1.481,993,1.951,1073,5.166,1099,3.575,1145,2.456,1394,3.069,1481,2.581,1633,4.094,2010,3.601,2013,2.411,2015,2.842,2141,3.703,2142,4.805,2268,2.75,2276,2.767,2577,2.541,2722,4.456,3604,4.566,3891,4.777,4785,5.308,5009,5.895,5010,5.895,5011,6.189,5013,6.189,5016,5.308,5115,6.593,5116,6.593,5117,6.593,5124,8.689]],["t/1939",[1781,3.73]],["t/1941",[7,0.847,49,2.414,228,3.403,278,3.849]],["t/1943",[7,0.951,34,2.043,49,2.568,56,0.792,84,1.119,103,2.685,203,1.921,228,2.542,278,2.875,283,2.32,332,4.164,382,4.978,393,3.941,401,3.067,408,2.897,513,3.781,580,2.786,614,1.477,723,4.083,888,5.442,1094,6.03,1369,6.166,1504,5.655,1511,7.467,2621,5.802,4021,7.309,4022,6.277]],["t/1945",[2,1.463,7,0.622,34,1.382,39,2.208,49,1.775,56,0.78,68,2.994,84,1.107,100,2.427,113,2.879,144,2.599,165,1.216,186,2.557,203,2.87,213,2.208,225,1.94,226,2.631,233,1.964,302,2.499,313,1.326,322,2.47,332,2.816,373,3.924,377,2.082,491,2.806,613,2.453,614,2.087,647,2.036,659,3.514,736,2.314,763,3.694,846,2.424,888,5.973,966,2.153,995,3.019,1035,3.241,1083,2.631,1094,2.712,1117,3.617,1119,4.245,1220,3.113,1265,2.844,1353,3.581,1375,3.289,1737,2.978,1799,6.927,1988,2.27,2000,3.394,2010,2.748,2015,3.196,2343,6.58,2362,5.375,2388,5.943,2389,4.943,2430,3.34,2625,3.113,3167,4.522,3595,1.999,3804,3.778,3858,2.816,4022,4.245,4023,5.149,4024,5.149,4025,5.149,4026,5.149,4027,5.149,4028,5.149,4029,5.149]],["t/1947",[2,1.915,7,0.946,15,2.589,21,2.007,49,2.418,85,3.302,103,2.376,106,2.603,113,2.589,179,2.376,203,2.282,228,2.249,283,2.756,313,1.735,332,3.685,401,4.586,481,4.242,491,2.873,512,2.448,562,3.487,614,2.209,628,3.667,723,2.538,736,3.028,820,3.65,888,3.741,944,2.873,1206,3.78,1254,5.004,1565,4.944,1633,4.492,1923,3.78,2013,3.175,2388,3.722,3070,4.888,3553,5.068,3554,5.068,4022,5.555,4030,6.468,4031,7.102]],["t/1949",[7,0.81,49,2.309,283,2.97,401,3.926,512,3.542,736,4.38,1254,7.239,3553,7.33,3554,7.33]],["t/1951",[7,0.853,16,1.338,24,1.164,28,1.014,34,1.831,49,1.616,56,0.71,68,2.725,84,1.252,105,3.386,162,2.526,219,2.225,246,2.718,256,2.69,261,4.87,285,2.97,295,2.891,318,2.636,353,2.621,377,1.608,398,2.532,401,2.748,512,2.478,536,2.141,614,1.323,734,3.181,759,3.795,828,2.998,966,1.96,988,3.515,1019,2.641,1132,3.192,1236,4.792,2000,6.011,2013,2.394,2141,4.916,2142,4.782,2722,5.915,2744,5.433,3071,5.524,4032,6.547]],["t/1953",[7,0.822,49,1.828,56,1.197,84,0.881,140,3.188,283,2.352,318,1.981,336,3.31,377,1.819,393,3.995,398,2.866,401,3.11,568,2.33,683,3.044,736,3.469,888,4.286,966,2.218,993,3.291,1100,3.224,1632,5.409,2000,5.088,2142,4.048,2359,8.006,2722,5.007,2744,6.148,3554,5.806,3555,8.311,4033,7.72,4034,6.956,4035,6.956,4036,7.72]],["t/1955",[49,2.089,84,1.007,161,6.327,201,4.732,283,2.688,318,2.263,398,3.274,401,3.553,736,3.964,966,2.535,1109,7.948,2000,5.814,2142,4.625,2359,6.472,2451,7.948,3553,8.084,3804,6.472,4034,7.948,4035,7.948,4037,8.821]],["t/1957",[7,0.824,15,3.812,49,2.35,279,4.517,395,4.132,614,1.925,828,4.36]],["t/1959",[2,1.491,7,0.436,17,2.185,23,2.672,24,1.772,28,0.78,48,2.243,49,1.799,56,1.162,84,0.867,98,1.943,113,2.016,162,2.813,165,1.793,171,1.502,174,1.643,203,2.252,219,1.711,228,2.536,232,2.011,246,3.558,248,2.595,272,2.263,313,1.956,338,2.537,377,1.236,433,3.192,524,3.403,530,2.472,536,1.647,545,2.555,568,1.583,614,1.018,705,5.791,757,2.318,831,3.487,843,6.436,857,3.631,966,1.508,993,2.236,1019,2.031,1037,2.751,1100,2.191,1235,4.054,1266,2.397,2000,7.137,2017,3.37,2144,5.036,2176,4.607,2214,3.58,2515,4.409,2744,6.05,2894,4.607,3607,4.054,3858,4.883,4038,4.727,4039,5.247,4040,7.596,4041,5.247,4042,5.247,4043,5.247,4044,5.247]],["t/1961",[7,0.796,56,0.997,304,2.127,397,4.197,580,3.505,651,6.729,966,2.753,989,5.684,1123,7.011,1297,4.895]],["t/1963",[7,0.832,304,2.223,438,5.475,651,7.032,966,2.877,1992,7.973]],["t/1965",[84,1.057,92,3.614,93,4.942,200,4.835,207,4.761,261,4.945,304,2.057,313,2.384,353,2.661,614,1.797,620,5.399,966,2.661,1182,4.654]],["t/1967",[16,1.895,68,3.861,85,3.527,165,2.281,304,2.146,317,5.09,338,2.505,614,1.875,2202,7.825,2268,5.805]],["t/1969",[5,2.947,21,2.364,28,1.644,36,3.69,56,0.826,96,2.947,98,2.94,106,4.27,107,2.142,109,1.789,170,3.135,186,3.943,203,2.003,256,3.97,296,3.225,304,1.763,343,2.976,368,6.324,404,4.074,513,3.943,568,2.396,606,6.43,611,3.478,694,4.551,774,4.526,858,4.127,3091,6.545,4045,6.814]],["t/1971",[109,1.763,203,2.503,304,2.203,404,5.091,846,3.21,1705,6.385,3868,8.939]],["t/1973",[7,0.925,15,2.721,21,2.784,24,1.786,26,2.77,36,3.291,46,5.055,56,1.089,90,3.517,98,2.622,100,3.337,106,2.735,112,4.881,144,3.574,183,3.203,186,3.517,203,1.786,217,4.254,275,2.31,279,3.224,304,2.076,359,3.291,404,3.634,408,2.694,527,2.565,607,2.86,622,3.764,719,4.49,883,6.57,945,2.469,1083,3.618,1253,4.458,1475,4.015,4045,6.077,4046,7.082]],["t/1975",[7,0.77,24,1.582,28,1.04,46,3.054,56,0.728,68,2.794,70,5.259,106,2.701,145,2.963,165,1.651,186,3.472,196,2.921,203,1.764,206,4.772,219,2.281,232,2.68,277,3.787,289,2.912,292,3.205,304,1.553,336,2.999,343,2.621,408,2.66,491,2.221,534,5.259,567,2.955,596,3.319,614,1.357,757,1.816,844,6.001,941,5.876,1074,5.329,1083,4.737,1108,3.769,1113,5.259,1219,3.283,1266,3.194,1292,4.963,1462,3.986,1632,5.071,1931,5.329,2250,3.319,2387,5.764,3027,5.483,4338,5.876,5125,7.941,5126,9.771,5127,7.369,5128,7.369]],["t/1977",[7,0.78,24,1.602,31,2.417,36,3.312,39,3.056,46,3.112,84,0.814,106,2.753,123,3.372,145,3.02,160,2.633,186,3.539,196,2.977,203,1.798,206,4.864,219,2.325,232,3.599,289,2.968,292,3.267,304,2.085,404,3.657,408,3.572,491,2.264,511,2.394,528,3.957,567,3.011,596,3.383,749,4.864,757,2.438,798,4.131,939,4.281,984,3.213,1019,2.76,1083,3.641,1108,3.842,1266,3.256,2250,3.383,2350,5.507,4236,6.612,4539,6.258,4792,6.612,5129,9.895]],["t/1979",[24,1.594,27,2.336,28,1.389,126,4.752,128,4.693,304,2.47,367,5.01,404,4.792,650,6.628,825,7.562,3386,7.438,4047,9.339,4048,9.339]],["t/1981",[7,0.762,16,1.08,21,2.98,28,1.574,32,3.629,39,3.934,56,0.818,68,3.142,100,2.594,107,1.485,127,1.903,165,1.3,181,3.287,183,2.49,197,2.585,203,1.984,207,2.368,223,3.267,253,3.491,304,1.746,315,2.756,316,2.585,336,2.361,337,2.702,339,2.927,343,3.967,379,6.906,404,2.825,430,2.862,477,3.441,480,3.949,482,2.594,502,1.6,508,3.418,555,2.745,577,4.989,613,3.746,614,1.526,623,2.778,635,2.105,690,3.6,694,3.156,723,3.769,747,3.403,757,2.042,778,3.659,838,2.927,856,6.651,984,2.482,1083,2.813,1099,2.148,1219,2.585,1402,2.713,1693,3.394,2277,3.907,2484,4.458,2887,3.394,3005,4.725,3386,4.385,4049,9.174,4050,4.725,4051,5.285]],["t/1983",[5,3.223,7,0.721,24,1.817,28,1.291,31,2.235,68,3.469,84,0.991,112,4.533,165,2.05,169,5.004,254,2.942,285,3.78,304,2.364,338,2.251,403,5.872,404,4.455,528,4.821,614,1.685,778,5.771,1100,3.627,1689,4.796]],["t/1985",[7,0.705,24,1.945,28,1.262,109,1.508,113,3.26,162,3.887,165,2.003,171,3.005,304,1.884,381,4.23,527,1.955,724,4.354,759,3.134,760,2.642,937,5.341,945,2.958,1402,4.18,1737,3.372,1988,3.74,2215,8.144,2276,6.345]],["t/1987",[7,0.934,24,1.758,41,3.936,56,0.857,113,3.164,139,3.894,203,2.077,236,2.281,239,4.977,289,3.429,304,1.828,367,4.417,430,4.28,527,1.897,614,1.597,662,5.043,759,3.042,760,2.564,939,4.946,1108,4.438,1737,3.272,1754,4.614,1959,4.138,1988,3.63,2276,4.977,4792,7.639]],["t/1989",[7,0.956,15,3.337,20,7.017,21,2.586,29,3.192,50,5.353,84,0.991,106,3.354,116,3.415,304,2.364,408,4.05,580,3.177,614,1.685,796,4.796,1083,4.437,5130,9.151,5131,9.151,5132,9.151]],["t/1991",[7,0.839,85,3.687,278,3.814,580,3.696,1019,3.911]],["t/1993",[8,4.506,16,1.096,23,2.796,56,0.827,96,2.074,140,2.308,177,2.844,179,1.971,219,3.017,275,1.823,304,2.665,318,2.039,366,3.684,393,2.892,438,5.51,447,2.786,500,2.786,545,2.722,548,6,552,2.965,555,2.786,558,4.748,614,1.084,802,2.382,846,2.571,1019,4.582,1237,5.424,1266,4.602,1297,2.856,1299,4.101,1330,5.475,1413,3.296,1531,3.926,1638,3.042,1954,4.796,1989,4.607,1992,4.451,2016,4.796,2340,3.625,2887,7.016,4052,5.589,4053,7.948,4054,4.796,4055,5.589,4056,6.33,4057,5.036,5133,5.89,5134,5.89]],["t/1995",[113,3.417,248,5.755,275,2.9,304,1.974,353,2.555,484,3.97,1019,3.443,1206,4.987,1309,5.445,1400,6.363,1401,7.222,1638,4.839,1889,6.972,2745,7.329]],["t/1997",[7,0.818,34,2.058,56,1.132,84,0.875,85,3.594,97,3.613,224,3.423,254,2.598,322,2.527,353,2.203,482,3.613,495,3.901,572,4.278,580,2.805,611,3.359,614,1.487,709,5.281,762,5.441,1019,3.812,1132,4.607,1178,4.665,1220,4.635,1402,4.851,1633,3.807,1781,2.728,2359,8.71,4058,7.667,4059,7.359,4060,7.667]],["t/1999",[7,0.727,84,0.999,171,2.506,246,3.487,304,1.943,491,2.78,580,3.914,614,1.698,723,3.296,1019,3.389,1094,4.61,1511,6.028,1638,4.763,1737,3.478,1923,4.909,2013,3.072,3595,3.397,3805,9.437]],["t/2001",[2,1.418,7,0.414,15,1.917,16,1.436,36,2.318,48,2.132,49,2.054,56,0.903,84,1.214,133,2.249,160,1.843,165,1.177,179,1.759,197,2.342,219,1.627,224,2.227,246,2.917,248,4.29,255,3.54,275,2.388,302,2.42,313,1.885,317,2.627,318,2.611,377,1.725,438,4.744,441,2.859,495,4.862,580,1.825,614,0.968,655,3.436,724,2.559,736,2.241,744,2.996,757,1.295,802,2.126,892,2.887,981,3.54,1019,4.592,1098,4.866,1123,5.384,1172,3.163,1292,6.782,1297,2.549,1324,4.112,1400,4.744,1475,2.828,1781,1.775,2001,4.38,2016,4.28,2026,5.372,2359,6.364,2577,2.769,2598,4.749,2641,3.751,2893,4.38,3805,7.493,4009,3.66,4061,4.988,4062,4.988,4063,4.988,4064,4.988,4065,4.988,4066,4.988,4067,7.321,4068,4.988]],["t/2003",[23,3.231,203,2.779,318,2.357,322,3.631,614,2.137,1019,4.265,2887,6.791,3595,4.276,4069,9.185,4070,9.185]],["t/2005",[2,1.573,7,0.46,16,1.085,23,2.776,56,0.576,219,3.272,246,2.205,278,2.089,283,1.686,304,2.691,313,1.425,318,2.025,545,2.695,548,3.589,552,2.944,614,1.073,756,5.994,892,2.182,917,3.303,1019,2.142,1266,4.202,1292,3.927,1400,3.026,1413,7.148,1954,4.748,2015,7.522,2296,5.133,2598,3.589,3805,6.188,4057,4.986,4071,5.533,4072,5.133,4073,5.533,4074,5.533,4075,5.533,4076,5.533,4077,5.533,4078,5.533,4079,5.533,4080,5.533,4081,5.533,4082,5.533,4083,5.533,5135,5.831,5136,5.533,5137,5.831,5138,5.533]],["t/2007",[2,2.093,7,0.797,24,1.256,25,2.484,28,1.095,35,5.402,39,4.113,49,2.272,84,0.84,109,1.308,116,2.895,197,3.457,304,1.635,313,1.896,336,3.157,391,3.266,404,3.778,505,3.215,614,1.861,635,2.815,743,6.671,760,2.292,802,3.138,830,3.546,1403,6.187,1404,6.187,1406,5.962,1407,7.213,1671,4.267,2010,2.7,2123,7.067,2142,3.861,2584,5.468,2902,6.318,2950,5.962,3643,7.067,4084,9.592,4085,7.363,4086,7.363]],["t/2010",[7,0.8,10,4.225,17,3.087,24,1.265,28,1.102,31,1.908,84,1.1,102,4.926,123,2.662,133,3.341,162,2.745,172,2.706,246,2.953,257,3.976,285,3.227,304,1.646,318,1.902,336,3.178,338,1.921,375,5.899,393,3.836,470,5.648,502,2.154,527,1.708,568,2.237,614,1.438,690,4.846,759,2.738,760,2.308,901,3.836,994,3.363,1099,2.892,1274,6.228,1402,4.746,1409,5.155,1733,4.666,1737,2.946,2887,4.569,2966,6.228,3595,2.877,4087,7.412]],["t/2012",[5,2.715,7,0.608,16,1.434,25,3.222,84,1.287,162,2.708,164,2.117,171,2.734,219,2.386,236,2.027,304,1.624,318,2.892,377,1.724,398,2.715,614,2.063,683,2.884,723,3.597,724,3.753,731,4.04,757,2.48,988,3.769,1019,3.698,1289,4.903,1328,4.02,1402,4.705,1475,4.147,1629,5.367,1737,2.907,2017,3.245,2609,6.277,3595,2.839,4009,5.367,4088,6.59]],["t/2014",[7,0.684,49,1.95,84,1.176,171,2.357,192,5.111,248,4.073,285,4.485,304,1.828,336,3.531,536,2.584,605,3.14,614,1.597,662,5.043,731,4.548,1123,6.979,1400,6.148,1402,4.057,1471,7.639,1475,4.668,1540,4.8,1737,3.272,3595,3.196,4009,6.041,4014,7.419]],["t/2016",[7,0.764,31,2.661,35,5.067,37,4.851,39,4.433,56,1.147,63,5.592,68,2.759,84,1.05,95,4.515,100,3.254,109,1.227,203,2.32,236,1.914,283,2.104,377,2.436,481,4.347,614,1.784,734,3.22,881,4.552,966,1.984,1027,4.629,1595,8.826,1689,5.71,1754,3.093,1797,5.803,2384,4.954,2459,5.693,2621,5.263,2672,5.263,3397,5.693,3398,5.263,3604,4.59,3895,5.926,4089,6.906,4090,6.906,4091,6.906,4092,6.906,4093,6.906,4094,6.906]],["t/2018",[7,0.872,113,3.26,181,5.065,203,2.648,334,5.065,491,2.695,512,3.083,614,1.646,846,2.745,888,4.711,966,2.438,1119,6.994,1182,4.264,1642,7.645,1754,3.8,1799,6.653,2010,3.85,2388,4.687,3858,4.64,4095,8.485,4096,7.451,4097,7.451,4098,8.485]],["t/2020",[1,3.783,5,2.293,7,0.811,25,3.294,56,0.887,84,0.973,126,3.143,131,5.281,135,3.711,157,3.759,162,2.288,165,1.458,174,2.671,203,1.558,254,2.889,314,5.191,322,2.036,341,3.581,369,4.039,370,5.093,375,3.783,449,4.007,476,1.89,500,3.08,525,3.068,532,3.395,555,3.08,590,3.502,611,2.706,614,1.199,621,3.346,623,3.118,675,4.141,677,3.43,683,2.436,830,2.975,859,3.379,1090,2.891,1112,6.103,1113,4.646,1163,4.384,1222,5.003,1230,4.92,1297,3.157,1319,4.432,1320,5.003,1527,4.297,1601,3.889,2013,2.169,2276,3.735,2384,4.432,2549,5.732,2887,5.257,3249,5.301,3493,5.425,4099,6.178,4100,5.425,4101,6.178,4102,8.527]],["t/2022",[1781,3.73]],["t/2024",[4,2.189,7,0.944,8,3.797,21,1.995,27,1.675,41,3.202,46,4.962,84,1.028,85,2.445,90,4.474,92,2.614,93,4.022,98,2.48,109,1.19,127,2.315,133,3.02,134,3.512,153,3.611,164,1.939,172,3.289,174,2.098,207,3.875,213,2.872,224,2.99,256,2.641,276,3.497,283,2.745,304,1.487,305,4.49,338,1.736,340,4.705,343,2.511,363,5.037,440,3.112,476,2.049,532,3.681,599,3.628,613,3.191,690,4.379,694,3.839,1099,2.614,1162,3.927,1202,4.345,1213,5.104,1267,5.335,2073,4.217,4126,6.698]],["t/2026",[5,2.268,7,0.703,31,2.178,49,1.447,56,0.636,68,3.878,84,1.256,105,2.014,109,1.725,110,3.392,164,1.769,165,1.442,172,3.543,203,2.134,207,3.639,253,3.874,256,3.336,304,1.357,377,1.44,404,3.135,527,1.95,552,3.156,553,3.41,576,3.793,607,3.418,611,2.677,614,1.185,623,3.083,630,2.378,638,3.874,731,3.375,769,3.427,881,4.027,902,4.948,979,4.292,1075,3.375,1077,3.604,1299,4.483,1399,3.874,1411,4.17,1423,4.656,1689,3.375,2293,3.767,2335,6.739,2469,5.505,3059,6.976,3387,4.866,3453,5.669,4119,5.243,4127,5.135,4653,5.505,5139,5.505,5140,5.669]],["t/2028",[2,2.106,7,0.8,27,2.41,46,4.674,47,4.926,84,1.1,90,3.68,92,4.783,93,3.309,96,2.751,158,3.904,172,2.706,207,3.188,256,2.922,306,3.468,313,1.908,332,4.053,338,1.921,476,2.947,481,4.666,596,3.518,597,4.034,599,5.217,604,5.155,707,4.346,796,4.094,810,3.468,1070,4.452,1090,3.468,1099,2.892,1806,5.504,3620,6.36]],["t/2030",[5,1.409,7,0.75,16,0.745,17,1.581,28,0.565,29,1.396,31,1.532,49,1.41,56,0.939,68,3.32,84,1.244,89,2.34,92,4.405,93,3.277,105,1.252,106,2.299,109,1.603,110,2.108,144,1.916,165,0.896,172,3.033,177,1.932,199,1.974,203,1.851,207,3.157,253,2.408,256,2.894,278,1.434,283,1.157,297,2.188,304,2.365,306,1.777,322,1.252,372,1.497,377,0.895,398,2.209,404,1.948,476,1.162,505,2.599,527,1.691,552,3.099,553,2.119,576,2.357,580,1.389,607,2.404,611,1.663,614,1.424,623,1.916,630,1.478,638,2.408,707,2.226,731,2.097,747,1.643,755,1.802,769,1.538,873,2.754,881,2.503,892,1.497,902,3.075,944,1.619,963,2.426,966,2.387,977,1.459,979,2.667,1075,3.287,1077,3.51,1083,1.94,1100,1.586,1144,2.176,1157,3.024,1181,2.341,1218,2.616,1297,1.94,1299,2.786,1399,2.408,1411,2.591,1481,2.141,1601,2.39,1671,2.201,1689,2.097,1737,2.365,1849,2.176,1998,2.482,2015,2.357,2130,3.258,2293,2.341,2335,5.845,2464,3.13,2469,3.421,2577,2.108,3059,5.268,3189,3.645,3387,3.024,3453,3.523,3595,2.31,3621,3.645,4119,3.258,4127,3.191,4129,3.797,4130,3.797,4131,3.421,4132,3.797,4135,3.523,5020,4.002,5139,3.421,5140,3.523,5141,3.797,5142,4.002]],["t/2032",[7,0.866,24,0.804,49,1.116,55,2.701,56,1.201,84,1.135,92,3.274,171,1.349,177,4.269,283,2.139,304,2.315,306,2.205,368,3.754,382,5.485,398,2.606,401,4.198,426,1.834,440,2.19,449,3.057,539,4.748,543,6.185,608,2.578,611,2.065,614,1.929,623,2.379,736,2.118,857,4.011,966,3.103,1108,5.011,1297,2.408,1475,2.672,1597,4.666,1607,4.836,1638,5.673,1698,3.696,1699,3.081,1737,2.79,1889,5.505,2289,3.458,2376,4.045,2887,2.906,3044,4.247,3078,8.923,3595,2.726,4100,4.139,4108,3.885,4133,4.714,4134,4.524,5143,4.967,5144,4.524,5145,4.967,5146,4.967,5147,4.967,5148,4.967,5149,4.967,5150,6.739,5151,6.739,5152,4.967,5153,4.967]],["t/2034",[7,0.953,15,3.577,21,1.209,24,1.47,25,1.37,27,1.016,28,1.139,34,1.09,40,2.254,46,4.748,47,4.167,49,0.962,56,0.652,84,1.063,85,3.744,92,3.839,93,1.813,96,2.843,97,4.833,105,2.067,106,2.958,129,2.556,135,2.439,138,1.726,140,1.677,158,3.302,164,1.175,179,2.211,196,1.696,272,1.752,278,1.533,283,1.237,296,1.649,304,0.902,313,1.045,318,1.042,377,0.957,393,2.101,395,2.611,437,3.788,438,5.381,440,1.887,441,2.327,471,1.773,491,1.29,530,1.913,557,3.288,591,2.439,597,2.21,605,1.548,614,0.788,723,1.529,730,3.484,734,1.893,744,2.439,747,3.314,757,1.054,813,5.227,820,2.199,837,4.005,838,2.158,888,2.254,1035,2.556,1206,2.278,1253,2.556,1405,4.279,1409,4.36,1597,2.699,1688,2.912,1992,6.1,1997,6.018,1998,2.654,2091,3.288,2092,3.094,2093,2.912,2094,3.094,2116,3.484,2251,3.288,2759,3.484,3556,5.327,4136,4.06,4137,6.901,4138,7.659,4139,3.658,4140,4.06,4141,4.06]],["t/2036",[4,1.515,7,0.69,8,2.628,16,0.909,17,1.93,21,1.38,29,1.704,46,2.024,49,1.642,56,1.078,84,0.791,85,1.692,92,4.404,93,3.096,97,3.267,108,3.773,160,1.712,164,1.342,196,1.936,203,1.169,217,2.784,228,1.547,275,1.512,283,1.412,304,1.54,365,2.475,372,1.827,401,1.867,434,4.87,438,2.535,482,2.184,500,2.311,527,1.068,579,3.532,580,1.696,591,2.784,596,2.2,613,2.208,614,1.612,680,3.821,713,4.07,714,8.561,751,2.82,774,2.642,780,2.6,796,2.56,813,3.163,830,2.232,846,1.499,963,2.961,966,2.649,1087,7.466,1175,2.249,1206,2.6,1230,3.691,1290,4.304,1297,2.368,1601,2.918,1705,2.983,1733,2.918,1913,2.75,2013,2.434,2499,6.247,2584,5.149,2800,4.449,3096,3.895,3397,3.821,3556,3.223,3620,3.977,3662,7.973,3664,7.973,3877,3.532,4131,4.176,4142,4.635,4143,4.635,4144,4.635,4145,9.22,4146,4.635,4147,4.635,4148,4.635,4149,4.449,4150,4.635,4151,6.934]],["t/2038",[7,0.829,24,0.811,48,2.032,49,1.126,55,2.724,56,1.203,84,1.193,92,3.29,171,1.361,177,4.291,283,2.153,304,2.322,368,3.786,382,5.513,398,2.623,401,4.211,426,1.85,449,3.083,539,4.778,543,6.204,608,2.6,614,1.936,623,2.399,714,3.488,736,2.136,857,4.031,966,3.111,1108,5.032,1297,2.429,1597,4.696,1607,4.867,1638,5.69,1698,3.727,1699,3.108,1737,2.808,1889,5.54,2289,3.488,2376,4.079,2887,2.931,3044,4.283,3078,8.961,3595,2.743,4100,4.174,4108,3.919,5144,4.563,5150,6.782,5151,6.782,5154,5.01,5155,5.01,5156,5.01,5157,5.01,5158,5.01,5159,5.01,5160,5.01,5161,5.01,5162,5.01,5163,5.01]],["t/2040",[7,0.784,16,1.408,25,2.882,49,1.7,84,0.975,92,4.112,100,3.383,108,3.907,112,3.748,164,1.406,165,1.695,198,4.382,213,2.083,246,4.699,292,2.226,348,3.285,377,1.145,388,3.701,397,2.128,438,3.926,440,2.257,441,4.115,443,8.312,544,3.201,562,2.514,614,1.657,651,3.412,713,4.265,714,5.268,859,2.656,966,2.454,989,2.882,1087,3.933,1132,2.273,1156,4.506,1297,2.482,1396,8.285,1402,2.393,1633,2.412,1687,4.265,1913,2.882,2076,2.975,2251,3.933,2404,3.523,2459,5.918,3160,7.695,3161,4.265,3162,4.265,3163,9.056,3164,6.891,3165,6.891,3166,6.891,3167,6.305,3168,4.662,3169,4.662,3170,4.662,3171,4.662,3172,4.662,3173,4.662,3174,4.662,3175,4.662,3176,4.662,3177,4.662,3556,3.378,4137,4.377,4152,4.857,4153,4.857]],["t/2042",[2,1.29,7,0.854,8,2.574,15,2.623,16,0.89,17,1.891,21,1.352,29,1.669,34,1.219,49,1.617,50,2.799,56,1.142,84,1.117,85,2.492,90,2.255,92,4.688,93,3.048,94,5.859,96,2.534,97,3.866,102,3.018,106,2.637,108,2.471,109,0.807,116,2.685,125,2.727,131,2.171,164,1.314,171,1.3,174,1.422,181,2.711,186,2.255,196,1.896,275,1.481,283,2.08,313,2.112,316,2.132,323,1.93,377,1.07,390,2.31,395,1.891,398,1.685,407,2.163,438,3.734,491,1.442,526,3.46,562,2.35,568,1.37,593,3.549,597,2.471,611,1.989,694,2.602,729,3.415,892,1.79,963,2.9,1111,3.331,1157,5.437,1161,3.815,1162,4.003,1178,2.762,1264,3.127,1297,2.32,1531,3.189,1597,3.018,1601,2.858,1633,2.255,1941,3.896,1998,4.463,2013,2.396,2130,5.859,2464,3.743,3006,3.815,3096,3.815,3161,3.987,3162,3.987,3329,3.896,3810,4.5,3877,3.46,4154,4.541,4155,4.541,4156,4.541,4157,4.541,4158,4.541,4159,4.541,4160,4.091,4161,4.541]],["t/2044",[7,0.776,15,1.827,21,1.415,27,1.189,33,2.856,34,1.276,46,4.566,56,1.183,84,0.963,85,1.735,90,2.361,92,4.225,94,4.027,96,3.13,98,1.76,101,2.666,113,1.827,133,2.143,140,1.963,164,2.045,179,2.973,203,1.199,208,4.563,238,2.874,240,2.74,254,1.611,275,2.305,278,2.668,351,1.813,353,2.03,372,1.874,377,1.12,407,2.264,437,2.351,440,3.918,476,2.161,482,2.24,491,2.244,562,2.46,566,3.488,583,3.06,597,3.845,613,3.366,614,0.922,715,6.264,828,2.089,888,3.923,966,2.03,995,2.787,1108,2.562,1157,3.786,1162,2.787,1227,4.563,1633,2.361,2118,6.782,2128,3.448,2388,3.903,2840,4.41,3006,3.995,3660,8.094,3804,3.488,3810,3.133,3858,4.612,3877,3.622,4160,4.283,4162,7.066,4163,4.754,4164,7.066,4165,4.754,4166,4.754,4167,4.754]],["t/2046",[7,0.806,15,3.73,16,1.659,20,4.027,36,2.839,49,1.447,84,0.966,92,4.641,93,3.778,94,3.483,97,2.879,98,2.263,157,3.718,164,2.45,174,2.65,177,4.305,230,5.366,236,2.345,275,2.76,278,3.195,302,2.965,315,3.058,359,2.839,407,2.91,430,3.176,440,2.839,578,2.53,621,3.309,684,3.358,712,3.464,795,4.209,816,3.648,908,3.278,971,4.721,1080,3.583,1188,4.096,1205,5.505,1218,6.686,1364,3.218,1365,3.718,1368,8.044,1374,6.005,1931,4.656,1941,5.243,1993,5.505,2588,4.209,4168,6.11]],["t/2048",[0,3.866,7,0.722,8,4.928,10,6.722,56,0.905,84,0.725,89,2.499,92,3.392,138,3.695,144,3.207,172,2.32,196,2.654,200,3.317,213,2.725,236,1.761,240,3.662,275,3.475,283,1.936,295,2.693,313,1.636,316,2.984,351,2.423,365,3.393,504,5.553,605,2.423,623,4.387,637,4.297,680,7.166,691,5.038,715,3.442,795,4.377,872,4.336,1217,5.061,1218,5.988,1231,5.58,1237,4.336,1337,4.463,1402,3.131,1426,5.145,1504,6.456,1527,4.419,1991,4.463,2372,6.354,2444,4.463,2491,5.34,5164,6.696,5165,10.442,5166,7.215]],["t/2050",[7,0.796,8,2.16,15,2.826,17,3.467,18,3.406,21,1.135,24,0.65,26,2.878,28,0.567,34,1.974,46,1.664,48,1.629,56,0.765,84,0.681,90,1.892,92,2.328,93,4.473,94,3.401,96,1.414,97,3.922,103,1.343,132,1.847,165,0.899,172,1.391,181,2.275,183,1.723,185,4.328,186,1.892,201,2.044,207,1.639,213,1.634,225,1.435,228,1.992,236,2.306,244,3.871,278,1.439,283,1.161,291,2.365,292,1.746,298,2.452,306,1.783,313,0.981,315,1.907,323,1.619,338,0.988,353,1.095,369,2.491,375,2.333,377,0.898,381,2.975,393,1.972,395,1.587,440,2.773,444,1.741,463,2.416,476,1.166,505,1.664,509,2.275,590,2.16,597,2.074,611,1.669,620,7.05,676,3.535,677,2.115,680,3.141,691,2.208,693,2.471,694,3.42,696,2.829,719,2.416,740,1.815,790,2.796,794,4.378,802,1.624,892,1.502,901,3.807,910,2.261,977,1.464,1099,1.487,1132,1.783,1162,3.499,1163,2.704,1175,1.849,1188,4,1217,3.035,1287,2.944,1364,2.007,1374,4.235,1387,4.753,1389,3.27,1390,5.335,1391,7.06,1392,8.675,1393,5.728,1428,3.346,2013,1.337,2035,2.676,2217,3.085,2565,3.346,2609,3.27,3075,3.535,3159,3.346,3435,3.657,4169,3.81,4170,3.81,4171,3.81,4172,3.81,4173,3.433,4174,5.377,4175,3.81,4176,3.433,4177,3.81]],["t/2052",[2,1.718,8,3.426,17,2.517,24,1.87,28,0.899,48,3.59,56,0.874,68,2.415,92,3.277,93,4.309,94,3.445,97,2.848,132,1.871,165,1.427,179,2.131,186,3.001,199,3.141,203,2.118,207,2.599,236,1.675,283,1.842,304,2.519,338,2.177,527,1.392,614,1.172,620,6.612,966,3.149,1087,4.894,1162,3.544,1188,4.051,1297,4.291,1387,4.813,1390,6.091,1402,4.138,1601,3.805,1689,3.338,1693,3.726,2247,4.488,2335,4.813,3159,5.307,3858,3.305,4173,7.567,4174,7.567,4176,5.446,4178,6.044,4179,6.044,4180,6.044,4181,6.044,5139,5.446]],["t/2054",[2,1.393,7,0.908,16,2.073,21,1.459,24,1.465,27,1.226,28,0.729,41,2.343,47,3.257,56,0.986,85,3.459,90,3.588,93,3.226,97,3.405,105,1.615,109,0.871,123,1.76,124,3.257,170,1.526,172,1.789,173,2.763,174,1.535,183,2.216,200,2.558,244,3.178,256,1.932,275,1.598,283,1.493,291,3.042,295,2.076,313,1.262,323,2.083,353,1.408,408,2.749,437,4.246,438,2.68,447,2.443,471,2.14,491,2.295,502,1.424,525,2.433,580,1.793,597,4.672,599,3.914,614,0.951,615,3.021,619,7.747,627,4.415,747,2.12,901,3.74,945,1.708,989,2.907,1090,2.293,1099,1.912,1235,3.786,2214,3.344,2491,4.118,3556,5.026,3590,4.415,4139,4.415,4182,4.9,4183,10.104,4184,4.9,4185,9.374,4186,4.9,4187,4.9,4188,4.9,4189,4.9,4190,4.9,4191,4.9,4192,4.9,4193,4.9,4194,4.9,4195,7.226]],["t/2056",[7,0.84,12,3.399,24,1.726,84,0.913,109,1.421,201,4.29,304,2.589,619,7.502,736,3.594,780,5.673,966,2.906,1413,4.717,1689,5.587,2015,4.964,3093,6.72,3143,6.592,3601,6.413,3640,7.676,3641,7.676,4196,7.997,4197,7.997,4198,7.997,4199,7.997]],["t/2058",[1781,3.73]],["t/2060",[7,0.958,15,3.812,572,5.536,1090,5.394,3831,6.77]],["t/2062",[4,2.489,7,0.814,27,1.905,49,2.322,56,0.792,84,0.869,100,3.588,103,3.456,144,3.842,236,2.11,243,4.388,283,2.32,394,4.011,401,4.367,405,3.286,437,3.766,481,4.793,492,6.277,511,2.558,683,3.002,723,2.868,736,3.422,977,2.926,1923,5.499,2013,2.673,3070,8.303,3553,5.726,3554,5.726,3555,6.399,3831,6.689]],["t/2064",[7,0.889,27,2.189,49,2.073,56,0.911,100,4.124,243,5.044,246,3.487,277,4.74,401,3.525,405,3.586,511,2.94,723,3.296,1072,4.262,1182,4.398,1754,3.92,3556,6.086,3831,7.3,4015,10.698]],["t/2066",[7,0.761,17,2.858,27,1.717,39,2.943,56,0.714,84,0.783,100,4.856,103,3.229,106,2.651,144,3.463,228,2.291,243,3.956,394,3.615,405,3.07,410,4.773,437,3.395,511,2.306,527,1.581,683,2.706,723,3.449,760,3.209,769,2.78,802,2.926,945,2.393,977,2.637,1094,3.615,1754,3.074,1759,6.642,1923,5.137,2013,3.214,2227,5.036,2228,5.036,2293,4.231,2384,4.923,3468,7.695,3831,6.249,4016,7.963,4017,9.561,4018,6.367,4019,6.367]],["t/2068",[7,0.739,17,2.74,27,1.646,39,2.821,56,0.685,84,0.751,100,4.754,103,3.139,106,2.541,109,1.921,144,3.32,170,2.049,228,2.196,243,3.792,394,3.466,405,2.984,410,4.576,437,3.254,511,2.21,527,1.516,683,2.594,723,3.353,760,3.141,769,2.665,802,2.805,945,2.294,977,2.528,1027,5.967,1094,3.466,1754,2.947,1759,6.456,1923,4.993,2013,3.125,2227,4.828,2228,4.828,2293,4.056,2384,4.72,3468,7.48,3831,6.074,4016,5.084,4017,9.359,4018,6.104,4019,6.104,5026,8.544]],["t/2070",[7,0.674,10,4.625,21,2.416,24,1.385,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,840,3.879,1754,4.572,1923,4.551,1991,5.699,2013,2.848,3831,6.965,4020,10.207]],["t/2072",[7,0.674,10,4.625,21,2.416,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,170,2.526,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,840,3.879,1754,4.572,1923,4.551,1991,5.699,2013,2.848,3831,6.965,5167,10.757]],["t/2074",[1781,3.73]],["t/2076",[7,0.811,15,3.276,16,1.211,21,1.84,24,1.455,26,2.417,28,0.919,34,1.658,58,4.708,70,7.343,84,0.973,98,2.288,99,4.106,106,3.293,145,3.613,158,6.016,164,1.788,175,5.627,203,1.558,242,4.588,246,2.462,304,1.372,343,3.196,353,2.45,381,3.08,401,4.6,402,5.093,408,4.202,410,4.297,412,4.844,413,4.92,416,2.998,453,4.178,532,3.395,536,1.939,572,3.447,605,2.356,744,3.711,794,4.533,840,2.953,944,2.633,977,2.374,1132,2.891,1206,3.465,3063,4.708,3091,5.093,4103,6.178,4104,6.178]],["t/2078",[0,3.177,2,1.484,7,0.74,16,1.484,24,1.292,28,0.776,33,3.137,34,1.401,41,2.496,56,0.927,84,0.596,97,3.567,98,1.934,131,2.496,134,2.738,138,4.595,153,2.815,162,1.934,175,3.009,220,3.387,221,2.032,245,3.287,304,1.681,316,2.452,332,4.14,353,2.559,359,2.426,365,5.536,394,2.75,396,6.24,403,3.531,407,3.606,408,2.88,409,2.788,438,5.912,439,5.214,440,3.517,441,5.942,442,4.585,443,3.878,444,2.385,445,3.471,446,3.5,447,2.603,448,5.012,449,4.91,450,7.266,451,4.585,452,3.787,453,5.119,454,5.012,459,4.304,484,2.331,536,1.639,580,2.77,614,1.013,637,3.531,742,4.989,1021,3.746,1123,3.241,1313,3.198,1324,4.304,1337,3.668,1355,3.414,2307,4.844,4105,4.481,4106,5.222,4107,4.705,4108,4.304]],["t/2080",[7,0.653,16,1.08,21,1.639,24,1.342,26,2.154,28,0.819,29,2.024,31,1.417,44,4.961,56,0.818,84,0.898,97,3.706,105,1.815,132,1.704,138,4.253,153,2.968,175,3.173,189,3.105,199,2.862,224,4.468,239,3.328,294,3.121,340,3.867,353,2.636,365,4.199,377,1.298,394,2.9,402,7.562,403,3.723,405,2.636,438,4.301,439,5.417,441,4.507,442,4.835,443,4.089,451,4.835,452,3.993,455,8.587,456,10.159,466,4.089,496,3.544,504,3.517,557,2.887,614,1.068,742,5.183,744,3.307,757,1.43,778,3.659,807,2.242,1123,3.418,1297,2.813,1299,4.04,1355,3.6,1632,3.011,2076,3.372,3464,5.285,4105,4.725,4107,4.961,4108,4.539,4109,5.506,4110,4.385,4111,4.961,4112,5.506,4113,7.864,4114,5.506,4115,5.506,4116,5.506,4117,5.506]],["t/2082",[5,3.3,24,1.843,49,2.106,70,6.687,106,4.172,286,5.679,304,1.974,351,3.391,404,4.562,408,4.109,528,4.936,536,3.39,621,4.816,744,5.341,804,8.011,4118,8.534]],["t/2084",[2,2.375,24,1.774,34,2.79,106,3.228,126,4.252,175,4.817,254,3.523,256,3.295,304,1.856,353,2.402,408,4.505,409,4.463,447,4.167,460,8.022,461,4.929,462,8.022,463,5.299,464,7.53,465,6.132,466,6.206,629,5.602,2548,7.339]],["t/2086",[2,1.892,7,0.843,16,1.306,24,1.136,49,2.404,84,1.024,105,2.195,106,2.571,121,4.105,153,3.589,197,3.126,228,2.223,236,1.845,278,2.514,291,4.133,302,3.231,336,2.855,372,3.538,377,1.569,408,3.413,449,4.319,461,3.927,478,4.253,532,3.659,578,3.716,596,3.16,605,2.539,614,1.292,670,4.078,731,3.678,744,4,764,4.725,828,2.926,1019,2.578,1108,4.837,1172,4.222,1297,3.402,1475,6.157,1932,4.945,2268,4,2444,4.677,2577,3.696,2954,5.847,3160,5.999,4105,5.714,4685,6.391,4998,6.391,5168,6.177,5169,7.017,5170,7.017]],["t/2088",[1781,3.73]],["t/2090",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,1019,4.182,1292,8.588,2013,3.121,2598,5.767]],["t/2092",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,977,2.966,1035,4.86,1112,4.825,1531,5.423,2013,2.71,2362,5.538,3642,7.162,4244,10.474,4245,7.41,4246,7.41,4247,7.41,4248,9.492]],["t/2094",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,1272,8.832,1282,9.359,1290,5.609,1292,7.741,1480,7.196,2298,7.754,2598,5.861]],["t/2096",[1781,3.73]],["t/2098",[1,2.672,7,0.799,12,1.854,16,0.855,27,1.091,28,0.985,34,1.171,49,1.569,68,1.743,84,0.914,92,3.125,93,4.293,98,2.453,99,2.899,109,1.589,116,1.716,127,1.508,162,1.615,165,1.564,171,2.293,172,4.352,173,3.735,179,1.538,193,1.672,203,1.1,207,2.849,224,2.957,228,1.456,236,1.209,257,2.34,272,2.858,276,4.181,296,1.772,304,0.969,318,1.119,332,3.623,336,1.871,337,2.141,343,1.635,377,1.028,439,3.005,445,2.899,476,2.026,491,1.386,502,1.268,527,1.005,552,3.776,568,1.317,580,1.596,601,2.158,607,2.675,614,0.846,620,3.861,686,3.596,707,2.558,728,5.876,734,3.089,769,1.767,802,1.86,810,2.041,816,2.604,858,2.267,894,3.474,913,3.421,966,1.254,1074,3.324,1075,2.41,1076,3.393,1077,3.907,1079,4.366,1108,2.352,1121,2.787,1144,2.5,1368,2.95,1377,3.421,1398,3.164,1399,2.766,1782,3.931,1806,3.24,2010,1.6,2073,2.746,2352,3.281,2476,4.187,2868,3.797,2965,3.831,2990,2.808,3604,2.899,3742,3.533,3804,3.201,3891,3.034,3899,3.034,4050,3.744]],["t/2100",[0,2.096,17,1.435,19,1.617,56,1.245,68,2.751,84,0.629,85,1.257,100,2.596,105,1.135,109,0.612,112,5.389,123,1.237,172,3.35,193,2.112,217,3.31,225,1.297,243,1.985,304,1.224,318,0.884,322,2.593,324,3.075,377,0.812,387,2.528,391,1.528,408,2.096,491,1.75,502,1.001,552,3.594,609,2.373,641,3.025,725,2.956,726,5.42,734,2.569,747,1.491,765,2.153,802,1.468,828,2.421,829,1.912,872,6.263,921,4.616,923,3.662,944,3.668,1073,4.143,1075,5.069,1076,2.091,1077,5.904,1081,3.996,1082,2.743,1083,2.815,1142,2.396,1175,4.453,1254,2.558,1285,1.548,1319,6.583,1332,3.87,1334,3.306,1335,3.025,1336,3.795,1337,7.96,1338,3.196,1339,3.306,1340,5.288,1341,3.306,1342,3.306,1343,5.288,1344,3.306,1345,3.306,1346,3.306,1347,2.528,1348,3.306,1349,3.306,1350,3.306,1351,3.306,1352,2.445,1353,2.396,1354,3.996,1355,2.252,1356,3.306,1357,3.306,1358,3.306,1359,3.306,1360,3.306,1361,3.306,1362,3.306,1363,3.306,1737,3.127,2825,7.093,3576,2.743,3594,5.288,4120,9.636,4121,3.104]],["t/2102",[2,1.77,5,0.725,7,0.814,8,1.107,17,0.813,21,1.359,22,1.277,24,0.584,26,1.785,27,1.37,28,1.531,31,1.41,49,1.081,56,0.356,68,1.823,84,0.711,85,0.713,89,1.794,93,0.872,94,1.113,96,0.725,98,0.723,103,0.688,105,1.127,106,2.116,107,2.114,109,1.462,116,0.768,124,1.298,126,0.993,132,0.604,134,2.392,135,1.173,138,2.328,140,1.412,144,0.985,153,1.843,163,0.816,165,1.293,169,1.125,170,1.421,171,1.306,172,2.86,173,5.158,174,1.429,179,1.206,184,1.555,185,1.416,189,1.928,190,2.471,192,1.212,193,3.827,203,0.863,224,3.059,228,1.142,232,0.748,236,1.518,256,0.77,275,0.637,288,2.378,289,2.593,292,0.895,296,0.793,304,0.434,313,1.41,317,1.028,318,0.877,322,1.127,323,1.453,336,0.837,337,1.678,338,0.886,339,1.038,340,1.372,343,1.282,347,1.358,351,0.745,372,1.348,377,0.46,383,1.433,391,2.024,395,1.424,401,0.786,426,2.132,433,1.188,441,1.119,458,1.159,491,0.62,502,0.994,505,0.853,509,1.166,513,0.97,541,1.266,550,2.268,552,3.069,553,2.546,555,0.973,557,1.024,558,0.92,565,1.885,568,1.879,597,1.063,601,1.692,610,1.581,611,1.498,614,1.329,621,1.058,622,1.038,630,0.76,632,5.033,635,1.744,636,1.159,638,2.168,647,0.772,686,1.61,691,1.982,712,1.107,719,1.238,725,1.048,741,1.332,743,2.378,756,0.966,757,0.507,769,0.791,772,4.331,774,1.113,802,0.832,806,1.61,807,2.536,810,1.6,812,2.402,816,1.166,822,1.229,827,2.436,828,1.503,829,1.084,838,1.818,840,0.933,845,1.309,858,1.015,872,2.334,917,1.166,974,2.201,977,2.105,987,1.531,994,0.886,995,1.145,1008,2.153,1070,1.173,1073,1.468,1074,2.606,1075,3.785,1076,1.298,1077,3.673,1079,1.287,1093,4.913,1094,2.403,1097,1.581,1121,1.247,1144,1.96,1145,1.048,1175,0.947,1182,0.981,1252,1.953,1285,0.877,1302,2.184,1303,1.32,1312,1.416,1331,1.32,1375,1.247,1409,1.358,1424,1.555,1467,1.287,1557,2.236,1605,1.509,1696,1.309,1699,2.236,1703,3.081,1737,1.359,2010,2.009,2073,1.229,2190,1.152,2250,2.6,2253,2.914,2269,1.715,2331,1.309,2352,1.468,2431,1.468,2534,1.812,2545,1.358,2577,1.084,2884,1.759,2928,2.533,2939,1.759,3030,1.676,3062,1.759,3595,1.328,3597,3.282,3604,1.298,3891,1.358,4122,1.953,5016,1.509,5171,2.058]],["t/2104",[1781,3.73]],["t/2106",[2,1.827,7,0.96,15,2.47,16,1.26,23,2.261,24,1.495,29,2.363,46,2.807,49,1.522,56,0.669,84,0.734,108,3.498,138,2.732,145,2.724,162,2.38,165,1.517,213,2.756,228,2.146,248,3.179,256,2.534,281,3.204,313,2.256,317,3.386,377,1.515,440,2.987,441,5.021,458,3.814,481,4.046,534,4.834,536,3.359,546,5.644,581,4.898,605,2.451,613,3.062,742,4.236,744,5.263,747,2.781,798,3.725,837,4.106,840,3.073,1090,4.099,1121,4.106,1172,4.076,1268,4.773,1337,6.154,2026,4.716,2327,5.516,2434,4.611,2958,5.791,3933,5.963,4056,5.119,4200,6.428,4201,6.428,4202,6.428]],["t/2108",[7,0.572,16,1.621,24,1.176,26,1.798,56,1.289,84,0.525,114,3.863,123,2.475,232,1.762,304,1.021,306,2.151,342,4.746,405,2.31,449,4.469,513,2.283,545,2.239,552,1.715,623,2.32,780,2.578,822,2.894,989,4.905,1597,4.58,1789,3.944,1889,3.604,2025,6.393,2026,6.065,2027,5.913,2029,3.944,2030,9.45,2031,5.178,2032,5.913,2035,6.45,2037,4.036,2038,4.036,2039,4.036,2040,4.036,2041,3.262,2042,5.913,2099,2.915,2448,3.03,2672,3.503,3441,6.051,3522,4.036,4110,5.488,4111,4.142,4203,4.142,4204,5.79,4205,4.142,4206,6.209,4207,4.597,4208,6.891,4209,4.597,4210,4.597,4211,4.597,4212,4.597,4213,4.597,4214,4.597,4215,4.597,4216,4.597,4217,4.597,4218,4.597,4219,4.597,4220,4.597,4221,4.597,4222,4.597,4223,4.597,4224,4.597,4225,4.597,4226,4.597,4227,4.036,4228,4.597,4229,4.597,4230,4.597,4231,4.597,4232,6.209,4233,6.209,4234,4.597,4235,4.412,4236,4.264,4237,4.597,4238,6.891,4239,4.597]],["t/2110",[7,0.846,16,1.839,24,0.978,26,2.243,56,1.27,84,0.654,100,3.813,114,4.817,123,2.906,232,2.197,248,2.836,304,1.273,342,5.574,449,5.249,545,2.792,744,3.444,790,5.938,1268,4.257,1597,5.379,1633,2.847,1789,6.944,2026,5.938,2027,6.944,2029,4.919,2030,10.046,2031,5.746,2032,6.944,2035,7.158,2037,5.034,2038,5.034,2039,5.034,2040,5.034,2041,4.068,2042,6.944,4110,6.445,4203,5.165,4204,6.8,4205,5.165,4206,7.292,4232,7.292,4233,7.292,4240,5.733,4241,5.733,4242,5.733,4243,5.733]],["t/2112",[56,1.014,164,2.822,550,4.036,614,1.891,744,5.855,814,4.423,1704,8.56,2020,10.952]],["t/2114",[1781,3.73]],["t/2116",[24,1.87,109,1.619,236,2.524,254,3.087,397,3.991,444,4.161,527,2.099,623,4.597,658,4.597,944,3.883,945,3.176,1099,3.555,2382,7.255,2952,7.509,3918,6.106,5172,10.344]],["t/2118",[16,1.811,34,1.865,49,1.646,56,1.256,109,1.641,164,2.012,171,2.644,193,2.663,203,1.753,221,2.705,254,2.355,318,1.783,322,3.043,397,3.044,476,2.126,491,2.207,502,2.019,614,1.348,653,2.305,658,3.507,757,1.804,822,4.374,916,2.791,988,3.581,1051,3.551,1099,4.709,1224,2.691,1328,3.819,1456,3.858,2010,3.387,2017,4.096,2338,4.028,2339,4.374,2340,4.507,3058,6.447,4400,6.261,5173,7.323]],["t/2120",[16,1.329,28,1.627,56,1.066,107,1.829,109,1.205,132,2.811,164,2.629,170,2.828,171,1.941,246,2.702,254,3.078,313,1.745,326,4.863,336,2.907,377,1.598,391,3.008,426,2.639,502,2.976,525,4.51,580,2.481,647,2.68,712,3.844,734,3.161,857,3.241,916,3.649,934,4.721,944,2.89,974,4.364,994,4.122,1040,5.846,1099,4.582,1100,2.832,2218,6.921,5174,10.314,5175,7.145,5176,7.145]],["t/2122",[16,1.389,28,1.053,31,2.407,56,0.973,84,0.808,107,1.911,109,1.258,115,3.436,123,3.358,132,2.192,140,2.924,164,2.05,170,2.205,174,2.218,179,2.497,236,1.962,256,2.792,336,3.037,338,1.836,391,3.141,502,2.058,507,5.999,527,2.154,550,3.872,658,3.574,814,3.214,828,3.112,979,4.974,1099,3.648,1102,5.886,1145,3.799,1179,6.568,1688,5.08,2137,5.472,4377,6.381,5177,7.463,5178,7.463,5179,8.974,5180,7.463,5181,7.463,5182,7.463]],["t/2124",[7,0.751,16,1.321,17,2.806,31,1.735,34,1.809,40,3.741,84,0.769,115,3.27,123,2.42,138,2.864,164,1.951,174,2.833,175,3.884,246,2.685,247,3.12,254,2.283,261,3.598,282,3.862,351,2.57,353,1.936,415,5.004,433,4.1,476,3.124,507,3.862,525,3.346,527,1.553,550,2.791,568,2.73,571,5.284,578,2.791,621,3.65,631,4.642,658,3.401,707,3.951,858,4.702,1098,4.479,1099,4.259,1102,4.242,1508,3.928,1540,5.273,2151,5.663,2529,5.207,2577,3.741,3114,6.468,3493,5.918,3934,6.252,4560,5.457,5183,6.739,5184,6.739,5185,7.102]],["t/2126",[7,0.65,31,2.569,49,1.854,84,1.139,115,3.799,123,3.583,174,3.44,236,2.169,247,3.625,351,2.986,416,3.799,476,2.395,527,1.804,568,3.011,584,6.875,585,6.235,621,4.24,658,3.951,818,4.142,876,6.632,1099,3.055,1179,5.499,1688,7.158,2137,6.049,2299,7.514,2606,6.453,2862,7.263,2954,6.875,4560,6.339]],["t/2128",[1781,3.73]],["t/2130",[16,1.981,34,2.711,107,2.725,318,2.592,1062,5.118]],["t/2132",[2,1.45,7,0.424,16,1,24,1.5,28,0.759,34,1.369,41,2.439,49,1.763,56,1.005,84,1.003,103,2.625,107,2.771,109,1.968,110,6.147,113,2.86,127,1.763,132,2.304,140,2.107,164,1.477,165,1.204,171,2.131,179,1.799,193,1.956,203,2.217,228,1.703,272,2.201,304,2.145,313,1.314,343,1.912,377,1.754,408,1.941,527,2.615,614,1.705,658,2.575,846,3.125,859,2.79,892,2.012,945,3.064,966,1.466,977,3.947,982,2.712,1737,2.028,1779,3.621,1988,3.281,2010,3.767,2191,3.548,2274,3.888,2275,6.536,2276,3.084,2990,3.284,3595,1.981,3858,2.79,4252,7.444,4253,5.102]],["t/2134",[5,1.86,7,0.847,12,2.129,17,2.087,21,1.492,24,1.879,27,1.253,28,0.745,31,1.29,41,2.395,46,3.798,56,1.109,98,2.72,109,1.545,110,5.661,116,1.97,131,2.395,133,2.259,158,2.639,170,1.56,171,1.434,174,2.724,179,2.59,186,3.647,196,2.093,197,2.353,254,1.698,272,2.161,275,2.396,282,4.21,289,2.087,304,1.113,322,2.866,353,1.44,367,3.941,377,1.181,386,5.76,388,3.818,408,1.906,440,2.328,482,2.361,505,3.208,525,2.488,527,2.349,528,2.782,568,1.512,590,2.84,597,2.727,599,2.714,611,2.195,614,0.972,631,5.059,719,3.177,755,3.486,807,2.04,816,2.991,846,1.621,977,1.925,1080,2.938,1099,3.393,1253,3.154,1284,3.594,1318,4.4,1402,2.469,1633,2.488,1737,1.991,2010,1.838,2013,1.759,2274,3.818,2275,4.4,2606,4.13,3595,1.945,4254,7.345]],["t/2136",[1781,3.73]],["t/2138",[7,0.796,24,1.635,28,1.424,219,3.684,502,2.784,536,3.006,550,3.967,565,3.296,568,2.891,1307,7.029]],["t/2140",[7,0.637,28,1.464,34,2.642,49,2.332,163,4.112,188,5.842,219,2.501,221,2.984,295,4.172,353,2.203,377,1.807,476,2.345,484,4.396,500,3.822,502,3.16,512,3.577,536,2.406,555,3.822,577,3.822,623,3.869,653,2.543,757,2.824,780,4.301,843,4.419,1019,2.969,2017,3.401,2128,5.561,2624,6.719]],["t/2142",[4,1.893,7,0.784,25,1.954,56,1.065,101,3.249,123,2.08,132,1.793,165,1.367,196,2.419,203,1.461,219,3.339,236,1.605,285,2.522,313,1.491,317,3.051,338,1.501,348,3.917,359,2.691,372,3.214,377,1.365,449,3.757,512,2.104,527,1.878,536,3.213,565,3.245,568,2.46,605,2.209,613,2.759,630,2.254,653,1.921,736,2.603,757,2.658,843,4.698,888,3.216,945,2.02,988,2.985,1035,3.646,1333,4.775,1456,3.216,1481,3.266,1700,6.392,2008,5.086,2009,4.069,2013,2.033,2017,4.541,2031,6.084,2338,3.357,2362,4.155,2388,3.199,2624,3.953,2880,7.345,2990,3.728,3804,4.25,4283,5.792,4284,8.152,4285,5.792,4286,5.792]],["t/2144",[28,1.451,34,2.03,49,1.791,84,0.863,96,2.807,165,1.785,190,4.096,219,2.467,232,2.899,295,3.205,304,1.679,398,4.011,461,4.46,536,3.709,552,2.821,567,4.124,605,2.884,614,1.467,731,4.177,757,2.534,966,2.173,1266,3.455,1394,5.07,2017,4.329,3059,7.495,3858,4.136,4287,7.563,4288,7.563]],["t/2146",[1,0.911,5,0.997,7,0.737,12,0.632,16,1.136,17,0.62,23,0.945,24,1.9,25,1.238,26,0.582,27,0.372,28,1.488,34,1.206,41,1.284,48,1.148,49,0.636,56,0.924,68,0.595,84,0.898,88,0.706,89,1.767,98,0.551,103,1.585,105,0.885,107,0.99,109,1.03,116,1.056,123,0.534,128,1.35,132,2.524,133,0.671,160,0.55,162,0.994,163,1.533,164,0.777,165,1.226,170,0.836,179,0.947,182,2.227,183,1.215,196,0.621,198,0.764,199,0.773,203,1.593,219,1.197,220,1.742,221,1.045,223,0.883,232,1.722,236,1.439,240,0.858,246,1.07,255,1.056,275,0.485,277,0.806,278,0.562,283,0.453,285,2.523,286,1.716,289,1.528,295,0.631,296,0.604,302,0.722,304,0.33,323,0.632,324,0.83,328,1.868,337,0.73,338,0.951,343,0.558,347,1.035,351,0.568,353,0.772,372,1.447,377,1.489,392,0.973,398,0.552,401,0.599,407,0.709,409,1.96,416,0.722,426,2.458,430,0.773,444,0.68,463,0.944,472,1.378,476,2.306,482,0.701,483,0.989,484,1.639,500,0.742,511,1.233,512,2.461,517,0.951,525,0.739,527,1.737,528,0.826,530,0.701,536,2.253,545,0.725,555,0.742,560,1.506,565,2.594,568,1.567,575,2.249,578,0.616,580,0.544,601,1.328,605,1.714,611,1.176,614,1.525,621,0.806,622,0.791,629,0.998,630,2.021,635,0.569,647,1.062,653,1.723,681,1.77,683,0.587,685,0.873,723,1.012,731,0.822,736,1.649,743,1.035,756,0.736,757,2.248,759,2.14,760,1.143,769,1.486,780,0.835,784,1.15,803,1.594,807,2.115,811,0.973,827,0.795,828,0.654,832,1.056,840,1.755,843,0.858,845,0.998,846,1.187,850,1.045,857,2.483,859,0.814,892,2.491,898,2.092,901,0.77,917,2.191,938,0.965,939,0.894,941,1.25,945,1.567,966,2.488,984,2.848,994,0.675,1019,0.576,1072,0.725,1076,2.861,1083,0.76,1090,1.257,1098,0.989,1100,0.621,1118,3.853,1121,0.951,1132,1.717,1172,1.703,1175,0.722,1192,2.399,1224,1.04,1246,1.8,1251,1.119,1268,1.994,1285,0.669,1402,0.733,1413,1.584,1425,1.645,1481,0.839,1530,0.944,1616,1.575,1633,0.739,1671,0.862,1689,0.822,1733,0.937,1781,0.529,1849,0.853,1850,1.381,1913,0.883,1959,0.748,1988,1.184,2000,0.981,2002,3.084,2009,3.157,2017,0.66,2073,0.937,2089,1.045,2145,1.341,2176,1.307,2190,0.878,2226,1.277,2254,0.81,2338,0.862,2727,1.227,2898,1.056,3600,2.305,3601,1.703,3616,0.965,3771,2.972,3858,1.469,3883,1.428,3899,1.035,3917,1.045,3918,0.998,3942,1.8,4038,1.341,4056,1.185,4289,1.488,4290,1.105,4291,1.105,4292,1.105,4293,1.105,4294,1.105,4295,1.105,4296,1.105,4297,1.205,4298,1.428,4299,1.488,4301,1.488,4302,1.488,4303,2.686,4304,1.488,4305,1.488,4306,1.488,4307,1.428,4308,1.488,4309,1.488,4310,1.488,4311,1.488,4312,1.488,4313,1.488,4314,1.488,4315,1.488,4316,1.488,4317,1.488,4318,1.488,4560,1.205,5186,1.568,5187,1.568]],["t/2148",[7,0.702,23,1.378,24,1.876,27,0.98,28,1.362,31,1.009,48,1.675,49,0.928,56,0.879,84,0.855,88,1.859,89,3.323,98,1.451,103,1.381,116,2.398,128,1.969,132,1.212,163,2.547,171,1.122,177,1.993,182,2.15,198,2.01,219,1.278,221,1.525,225,1.476,232,2.337,285,1.706,286,2.502,302,1.901,304,1.354,313,1.009,322,1.291,328,2.724,343,1.468,377,0.923,401,1.578,444,1.79,472,2.01,502,1.138,512,3.523,527,1.405,545,1.908,555,1.953,565,2.576,575,3.052,614,1.776,630,2.373,653,2.803,670,2.399,681,2.582,736,1.76,756,1.938,757,1.944,769,3.422,780,2.197,807,1.595,840,1.873,846,1.267,898,4.268,908,2.102,945,2.126,966,2.786,984,1.766,1072,1.908,1076,3.207,1094,2.063,1118,4.075,1132,3.503,1224,2.361,1329,4.443,1425,2.399,1557,2.561,1616,2.297,1633,1.945,1671,2.27,1689,2.164,1754,1.755,1765,2.368,1849,3.495,2198,2.985,2254,2.132,2311,2.78,2588,2.698,3601,3.867,3616,3.955,3645,5.355,3917,2.752,3918,2.626,4290,2.909,4291,2.909,4292,2.909,4293,2.909,4294,2.909,4295,2.909,4296,2.909,4297,3.172,4319,5.495,4320,3.53,4321,3.027,4322,3.634,4323,2.946]],["t/2150",[7,0.31,23,2.066,24,1.527,28,1.413,34,1.002,48,1.596,49,0.884,56,0.755,63,3.023,84,0.829,88,1.772,89,3.237,98,1.383,103,1.316,116,2.31,163,3.032,171,1.069,177,2.988,182,2.071,198,1.916,203,1.481,219,1.218,221,1.453,228,1.246,232,2.251,283,1.138,285,1.625,286,2.385,295,1.582,304,1.304,322,1.231,328,2.597,338,0.968,343,1.399,377,1.384,401,1.504,405,1.252,426,1.453,444,1.706,472,1.916,476,2.907,491,1.186,502,1.707,512,3.612,527,1.353,536,1.843,545,2.86,558,1.759,565,2.021,575,3.634,577,5.135,614,1.736,630,2.286,653,2.73,683,1.472,736,1.678,756,1.847,757,1.885,780,2.094,796,2.062,846,1.9,892,2.316,898,4.138,916,3.307,945,2.048,966,3.045,984,3.273,1072,1.818,1076,3.396,1118,4.229,1224,2.274,1325,2.885,1402,1.839,1616,2.189,1633,1.854,1689,2.062,1754,1.672,1849,3.366,2251,3.023,2254,2.032,2588,2.572,2727,3.078,3601,3.724,3646,5.157,3942,3.937,4290,2.773,4291,2.773,4292,2.773,4293,2.773,4294,2.773,4295,2.773,4296,2.773,4321,2.885,4323,2.808,4324,5.292,4325,3.734,4326,3.734,4327,3.734,4328,3.734]],["t/2152",[4,0.61,5,0.693,7,0.554,21,0.556,23,0.657,24,1.229,25,1.487,27,0.467,28,1.373,31,0.481,34,1.182,36,1.528,41,0.893,48,0.798,49,1.257,56,1.057,68,0.746,84,0.822,88,0.886,89,2.087,98,1.218,103,1.16,105,1.084,107,2.848,109,1.831,116,1.733,123,0.671,125,1.122,128,1.653,132,1.018,133,0.842,134,1.724,140,1.358,162,0.692,163,2.217,165,1.253,170,2.243,171,1.732,182,1.16,183,0.845,190,1.011,198,0.958,203,0.471,219,1.073,221,0.727,232,1.26,236,0.911,240,1.076,246,1.756,256,0.736,285,0.813,286,1.193,295,1.394,296,0.758,304,0.73,313,0.481,322,1.452,328,1.299,336,1.41,338,1.73,343,0.7,377,0.775,382,1.221,391,0.828,394,0.984,401,0.752,405,2.237,408,1.251,426,2.355,444,0.853,472,0.958,476,2.042,491,0.593,502,0.543,505,1.924,511,1.48,512,2.783,527,1.765,536,2.898,545,0.91,550,0.773,557,0.979,565,1.826,568,0.564,575,2.205,580,0.683,605,0.712,614,1.563,623,0.942,629,1.252,630,1.28,647,2.392,653,2.214,659,1.274,660,1.464,663,1.202,681,2.167,723,1.239,724,1.687,736,1.478,756,0.924,757,1.571,760,0.581,772,1.299,780,1.048,820,1.011,827,0.997,828,1.445,840,1.572,846,1.064,892,2.093,898,2.511,917,1.115,944,0.796,945,1.536,966,2.415,977,0.718,984,1.986,993,1.402,994,1.492,1062,0.946,1072,0.91,1076,2.734,1099,1.283,1118,4.109,1132,2.062,1172,1.184,1175,0.906,1219,0.877,1224,1.706,1285,0.839,1312,1.354,1355,1.221,1365,1.136,1402,0.92,1425,2.014,1481,1.053,1540,1.089,1558,2.614,1616,1.928,1633,0.927,1689,1.032,1754,0.836,1849,1.885,1913,1.108,1931,1.423,2006,4.555,2010,2.448,2012,4.101,2015,1.159,2099,1.184,2254,1.016,2256,5.779,2257,1.37,2588,1.286,2900,2.334,2928,1.826,3595,0.725,3601,3.366,3647,4.661,3763,1.252,3830,1.404,3917,1.312,3942,2.204,3943,5.405,3944,1.512,3952,3.97,3972,1.286,4290,1.387,4291,1.387,4292,1.387,4293,1.387,4294,1.387,4295,1.387,4296,1.387,4321,1.443,4323,1.404,4329,4.406,4330,1.602,4331,2.822,5015,2.619,5016,2.541,5017,2.663,5168,1.733,5188,1.602,5189,1.683]],["t/2154",[7,0.548,21,0.767,23,0.906,24,0.959,25,0.869,27,0.644,28,1.38,34,0.691,36,1.197,41,2.075,48,1.101,49,1.028,56,1.026,84,0.495,88,1.223,89,2.595,92,1.005,98,0.954,103,0.908,105,0.849,107,0.695,109,1.866,116,1.706,127,3.906,128,1.295,132,1.343,133,1.161,140,1.064,162,0.954,163,3.076,164,0.746,165,1.024,171,1.889,172,0.94,177,2.208,179,0.908,182,1.53,183,1.963,193,1.663,198,1.322,203,1.095,219,0.84,221,1.003,232,1.663,256,1.016,257,1.382,275,0.84,279,1.173,285,1.122,286,1.645,295,1.092,304,0.964,322,2.631,328,1.792,336,1.861,338,1.125,343,0.966,351,0.982,359,1.197,377,1.325,391,1.925,394,2.286,401,1.038,405,2.212,426,1.689,444,1.177,472,1.322,476,2.839,502,0.749,512,3.085,527,1,536,2.665,545,1.255,565,2.534,575,2.814,605,0.982,614,1.861,615,1.588,623,1.3,630,1.689,653,2.188,681,1.698,732,1.774,736,1.158,755,1.223,756,1.274,757,2.073,780,1.445,840,2.688,843,1.485,846,1.404,898,3.205,945,1.513,966,2.667,974,1.658,984,1.161,993,1.098,1072,1.255,1076,2.794,1118,3.552,1144,1.476,1224,1.68,1284,1.848,1402,1.269,1425,1.578,1616,1.51,1633,1.279,1689,1.423,1754,1.944,1765,1.557,1849,2.487,1991,1.809,2004,5.794,2010,3.404,2011,5.173,2190,1.519,2254,1.402,2588,1.774,3522,2.262,3595,1,3601,4.183,3648,5.794,3763,1.727,3942,2.909,4290,1.913,4291,1.913,4292,1.913,4293,1.913,4294,1.913,4295,1.913,4296,1.913,4321,1.99,4323,1.937,4330,2.211,4331,3.724,4332,5.216,4333,2.39,4334,2.576,4335,2.165]],["t/2156",[7,0.72,21,1.342,23,0.948,24,1.289,26,1.054,27,0.674,28,1.123,31,1.16,36,1.252,37,1.893,48,1.152,49,1.067,56,1.015,84,0.862,88,1.279,89,2.67,98,0.998,103,0.95,107,0.727,109,1.733,116,1.772,127,0.931,128,1.354,132,0.834,162,0.998,163,1.882,164,0.78,165,0.636,170,2.941,171,1.944,177,1.371,182,1.589,183,1.219,190,1.459,198,1.383,219,0.879,221,1.049,225,1.697,228,0.899,232,1.727,240,1.553,247,1.248,285,1.173,286,1.721,302,1.307,304,1.001,313,0.694,322,1.914,323,1.915,328,1.874,336,1.155,343,1.01,353,0.774,377,0.635,391,1.195,401,1.085,444,1.231,472,1.383,484,1.203,491,0.856,502,0.783,512,2.967,527,1.038,536,2.563,545,1.312,555,1.343,565,2.599,568,0.813,575,2.256,605,1.028,614,1.584,623,1.36,630,2.26,653,2.506,670,1.65,677,1.496,681,1.776,736,1.211,756,1.333,757,1.961,769,3.308,780,1.511,807,1.097,830,1.298,840,1.288,846,1.458,898,3.311,902,2.182,908,1.446,944,1.149,945,1.571,966,2.492,984,1.215,993,1.149,1005,1.856,1019,1.043,1072,1.312,1076,2.576,1094,1.419,1108,1.452,1118,3.362,1132,2.718,1175,1.307,1191,2.586,1224,2.249,1329,4.973,1336,1.856,1364,1.419,1425,1.65,1527,1.874,1557,1.762,1616,1.58,1633,1.338,1671,1.562,1688,1.933,1689,1.488,1754,2.018,1765,1.629,1793,1.791,1849,2.583,1959,4.569,2010,2.996,2198,2.053,2254,1.466,2311,1.912,2421,1.696,2588,1.856,2631,1.856,3184,2.428,3595,1.046,3601,2.857,3616,2.923,3917,1.893,3918,1.806,4290,2.001,4291,2.001,4292,2.001,4293,2.001,4294,2.001,4295,2.001,4296,2.001,4297,2.182,4321,2.082,4322,2.5,4323,2.026,4330,2.312,4331,3.867,4332,2.5,4792,2.5,5122,6.117,5190,7.154,5191,4.749,5192,4.749]],["t/2158",[27,1.83,28,1.42,56,1.107,84,0.835,196,3.055,405,4.096,426,4.388,495,3.722,511,3.573,512,2.657,536,2.295,565,2.517,568,2.882,583,4.708,647,2.892,729,5.501,757,1.899,892,3.766,984,3.297,1076,2.776,1100,3.055,1329,4.634,1336,5.038,1355,4.782,1693,4.509,1959,3.676,2672,5.574,3741,5.501,5188,6.277,5189,6.59]],["t/2160",[5,3.633,7,0.631,23,1.846,28,1.455,41,2.508,56,1.018,70,3.945,84,0.599,89,3.511,106,2.934,113,2.016,116,2.063,128,2.637,164,2.199,178,6.261,196,2.191,228,2.536,239,3.172,256,2.995,289,3.163,407,2.499,408,1.996,426,2.956,433,3.192,495,4.543,511,3.638,512,1.906,536,1.647,565,1.805,568,2.292,615,3.235,635,2.006,647,3.003,712,2.974,756,2.595,757,1.362,769,2.125,807,4.82,892,3.859,966,1.508,983,4.248,984,2.365,1100,2.191,1285,2.357,1329,3.686,1355,6.399,1405,3.58,1693,3.235,1959,2.637,2214,3.58,2487,8.045,3005,4.502,3671,4.248,3741,3.945,4337,8.283,4539,4.607,5193,8.005,5194,5.036]],["t/2162",[7,0.807,16,0.876,24,1.151,28,1.003,56,1.257,84,0.77,88,2.12,96,1.658,107,1.205,109,0.794,127,2.331,140,1.845,162,1.654,164,1.293,165,1.592,171,1.279,177,3.432,179,2.378,219,3.169,277,2.42,296,1.814,322,2.678,377,1.053,392,2.921,491,2.142,502,1.96,536,3.631,550,1.85,565,2.321,567,4.812,577,2.227,653,1.482,672,4.187,734,2.083,798,2.589,818,3.569,846,1.445,897,4.483,918,3.021,1019,2.611,1076,2.559,1224,1.73,1309,2.736,1329,2.168,1367,2.969,1398,3.24,2002,3.754,2004,3.923,2006,3.834,2010,2.98,2011,3.503,2012,3.452,2013,3.41,2017,2.992,2035,3.138,2092,3.404,3059,2.833,3600,3.834,3601,6.16,3645,3.923,3646,3.923,3647,3.923,3648,3.923,3679,3.923,4319,4.025,4324,4.025,4338,5.668,4339,4.145,4340,4.468,4341,9.014]],["t/2164",[24,1.651,36,3.467,56,1.117,68,2.981,84,1.226,113,3.718,203,1.882,304,2.384,398,2.77,401,3.005,482,3.516,536,2.342,543,4.427,565,2.567,567,3.153,577,3.72,578,3.09,614,1.447,622,3.966,756,3.691,892,3.815,938,4.84,966,3.085,1019,2.889,1540,4.349,1607,5.139,1638,4.061,2268,4.482,3607,5.765,4342,7.462,4343,9.675,4344,7.462]],["t/2166",[7,0.852,15,3.141,16,1.603,88,3.879,103,2.882,113,3.141,199,4.248,225,3.079,226,5.239,243,4.711,292,3.746,377,1.926,491,2.596,511,2.746,536,2.565,550,3.385,565,3.528,567,4.332,593,4.248,672,5.073,719,5.182,798,4.737,855,5.301,892,3.223,1007,5.578,1253,5.145,2128,5.928]],["t/2168",[7,0.585,17,3.878,28,1.651,84,0.803,89,2.767,105,2.319,190,3.811,219,3.768,232,2.697,283,2.144,294,3.989,377,1.658,461,4.15,536,3.724,552,2.625,567,5.014,653,3.088,731,3.887,757,2.884,1266,3.215,2017,4.629,3059,6.616,4345,7.037,4346,11.105]],["t/2170",[2,1.225,4,2.146,7,0.795,23,1.517,24,1.12,25,1.455,27,1.642,28,1.182,56,1.049,84,1.233,88,2.046,89,1.696,109,0.766,113,2.523,127,1.49,132,1.335,163,1.801,165,1.55,171,1.234,174,1.351,177,2.194,182,1.52,193,1.653,219,2.142,223,2.558,232,1.653,240,2.485,275,2.142,278,1.628,283,1.314,285,1.877,294,2.444,313,1.69,322,1.421,338,1.118,447,2.15,472,4.08,476,2.719,498,2.528,502,1.253,512,3.475,525,2.141,527,0.993,536,3.163,558,3.094,565,1.484,567,2.774,568,1.301,575,5.044,614,0.836,736,3.573,755,2.046,757,1.705,765,2.695,773,3.554,796,2.382,816,2.574,857,3.801,879,3.623,892,1.7,898,4.532,977,1.657,984,1.944,1076,3.824,1080,2.528,1099,1.682,1101,2.714,1108,2.324,1132,3.072,1163,3.06,1178,2.623,1181,2.658,1224,1.67,1402,2.124,1425,2.641,1616,5.908,1913,2.558,2013,1.514,2254,2.347,2444,3.029,2624,2.942,3614,4.139,3899,2.999,3917,3.029,3942,2.89,4347,4.312,4348,4.312,4349,4.312]],["t/2172",[2,1.505,7,0.635,21,2.927,24,0.904,26,2.072,36,3.554,49,2.469,56,0.551,84,1.19,96,3.332,98,3.324,107,2.422,109,1.359,113,2.939,127,1.83,140,2.187,160,1.957,164,1.533,165,1.25,203,2.264,304,1.176,313,1.364,336,2.271,338,1.373,353,1.522,377,1.248,502,1.539,509,3.162,536,3.902,577,4.475,578,2.193,604,3.683,629,3.55,653,2.977,692,4.451,756,2.62,805,3.799,833,4.594,857,2.532,918,3.582,993,2.258,1019,3.476,1284,3.799,1402,4.843,1779,3.759,2010,3.824,2017,3.393,2257,3.886,2960,5.916,4350,7.648]],["t/2174",[4,2.752,7,0.699,48,3.6,49,1.994,56,1.087,84,0.961,140,3.477,171,2.411,203,2.124,272,3.633,295,3.568,322,2.776,536,2.643,613,4.011,614,2.027,731,5.77,802,3.589,828,4.591,2013,2.956,2431,6.333,3595,3.269,4351,7.856]],["t/2176",[2,1.03,4,1.875,12,1.54,24,1.382,29,1.332,36,1.684,49,1.359,56,1.263,68,1.448,84,0.924,96,1.345,98,2.124,105,1.195,179,1.278,203,1.447,219,1.871,254,1.228,304,1.798,322,1.195,336,1.554,372,2.807,377,1.352,391,1.608,395,1.509,398,2.643,401,1.46,444,1.656,472,1.86,482,1.708,536,3.553,543,2.15,565,1.247,567,3.008,577,1.807,623,1.829,647,2.815,653,1.202,736,1.628,757,0.941,780,4.542,838,1.926,846,3.609,892,2.262,938,2.351,966,2.929,993,1.545,1019,4.32,1037,1.9,1076,3.072,1118,2.553,1224,1.403,1329,1.758,1402,2.827,1540,2.113,1607,2.496,1616,2.125,1638,1.972,1689,2.002,1765,3.468,1849,2.077,1982,2.935,2009,2.546,2013,1.272,2017,2.545,2254,1.972,2727,2.987,2960,2.389,3059,3.638,3601,2.298,3607,2.8,3858,4.825,3906,2.842,3907,3.045,4320,3.265,4351,2.725,4352,5.506,4353,5.737,4354,3.265,4355,3.624,4356,3.265,4357,3.624,4358,3.265,4359,3.624,4360,3.265,4361,3.624,4362,3.265,4363,5.737,4364,3.265,4365,3.624,4366,3.265,4367,3.624,4368,3.479,4369,3.624,4370,3.624,4371,3.624,4372,3.624,4373,5.737,4374,3.624]],["t/2178",[1,4.256,7,0.577,24,1.77,49,1.646,56,1.23,84,1.184,171,1.99,272,2.998,322,3.043,372,2.74,377,2.176,395,2.894,397,4.045,430,3.612,482,3.275,536,3.787,550,2.878,565,2.391,611,3.044,846,2.248,901,3.596,993,2.962,1121,4.439,1192,6.037,1309,4.256,1329,4.48,1402,3.424,2013,3.241,2960,4.58,4351,6.944,4375,9.234,4376,6.949]],["t/2180",[2,1.457,7,0.426,12,2.178,36,2.382,49,1.769,56,1.225,84,1.105,107,2.377,109,1.72,127,3.556,162,1.898,165,1.21,179,1.807,183,2.318,275,1.672,296,2.082,304,1.138,313,1.32,322,2.904,336,3.778,391,3.909,536,3.48,614,1.709,647,2.026,757,1.331,827,2.737,846,2.416,993,3.184,1329,4.275,1959,4.428,1988,3.884,2010,4.424,2013,1.799,2990,3.299,3901,9.678,3902,9.678,4096,4.501,4097,4.501,4351,3.855,4354,4.618,4356,4.618,4360,4.618,4362,4.618,5123,9.292]],["t/2182",[2,1.52,7,0.64,49,1.824,56,1.287,84,1.127,85,1.952,109,1.937,162,1.98,165,1.262,179,2.715,296,2.171,313,1.377,322,1.762,336,2.293,391,2.372,452,3.878,502,1.554,536,3.284,577,6.063,614,1.037,705,3.468,757,2,827,2.855,846,2.92,916,3.966,993,2.279,1037,2.804,1754,2.395,1982,4.33,2010,4.36,2013,1.877,2990,3.442,3906,4.193,3907,4.493,4351,4.021,4358,6.939,4364,6.939,4366,4.818,4377,4.818,4378,5.347,4379,5.347,4380,9.025,4381,5.347,4382,5.347,4383,5.347]],["t/2184",[1781,3.73]],["t/2186",[7,0.633,16,1.493,19,3.575,21,2.267,29,2.799,84,1.119,105,2.51,165,1.797,219,3.536,221,2.963,275,2.484,296,3.092,304,1.691,318,2.937,512,2.767,513,3.781,562,3.941,567,3.217,580,3.587,653,2.526,723,3.692,736,3.422,757,2.815,916,3.059,1019,2.948,1302,4.864,1456,4.227,2338,4.413,2402,4.011,2624,5.196,2625,4.603]],["t/2188",[2,2.207,7,0.724,15,1.595,25,1.401,29,1.526,49,1.51,56,1.076,84,0.728,98,2.362,105,2.102,113,1.595,132,1.285,165,1.506,203,1.609,219,1.354,221,1.616,228,2.129,246,1.654,255,2.946,256,1.637,283,1.265,313,1.999,316,1.949,318,2.996,364,3.083,377,0.978,395,1.729,398,3.961,471,1.813,476,1.27,482,1.956,484,1.853,491,2.026,502,1.853,512,1.508,530,1.956,543,2.463,580,1.519,605,1.583,614,1.237,615,2.559,621,2.248,647,2.521,723,3.739,728,3.046,731,2.293,757,1.656,822,4.015,831,2.759,838,2.206,840,1.984,846,1.343,857,3.049,916,3.778,963,2.652,988,4.846,995,2.434,1019,3.007,1051,5.072,1066,3.984,1108,2.238,1132,2.984,1328,3.505,1413,2.448,1461,3.562,1475,3.616,1633,2.061,1688,2.978,2013,1.457,2017,3.866,2094,3.163,2141,4.697,2339,4.015,2340,2.692,2577,3.541,2879,3.645,2887,2.559,2958,3.74,3565,2.946,3966,3.851,4009,3.046,4013,3.74,4088,3.74,4385,4.375,4386,4.151,4387,4.151]],["t/2190",[2,0.805,4,0.926,7,0.5,19,1.33,25,2.03,27,1.505,28,0.698,29,1.041,31,0.729,40,1.572,56,1.233,68,1.131,84,0.886,100,1.334,109,1.81,127,3.83,140,1.169,165,1.109,172,1.714,179,1.656,182,1.656,193,2.976,219,1.963,254,0.959,256,1.116,277,1.534,279,1.289,295,1.2,313,0.729,318,1.205,327,3.74,337,2.305,338,0.734,377,0.667,395,1.956,398,1.051,405,2.603,418,1.491,476,3.036,502,0.823,511,1.578,512,1.706,552,1.752,567,1.196,575,3.012,614,0.549,647,1.857,653,2.777,723,2.637,757,1.562,803,1.68,818,3.184,828,1.244,837,1.809,840,1.354,846,1.947,876,1.882,892,1.852,898,2.677,916,3.119,939,1.701,944,1.207,966,2.012,984,1.276,988,1.459,1013,4.615,1016,2.627,1051,4.278,1075,2.594,1076,2.657,1077,2.77,1100,1.961,1108,1.526,1118,3.116,1224,1.818,1425,1.734,1456,2.607,1530,1.795,1539,1.989,1616,2.754,1671,1.641,1693,1.746,1700,2.22,1705,3.873,1753,2.054,1765,1.712,1768,3.369,1925,5.41,2010,1.722,2141,1.526,2208,3.532,2254,1.541,2338,2.722,2339,2.956,2402,2.474,2890,2.43,3565,3.333,3899,1.969,3942,1.898,3970,2.293,4227,2.486,4335,2.379,4388,2.832,4389,2.832,4390,2.984,4391,2.832,4392,2.984,4393,2.832,4394,2.984,4395,2.832,4396,2.832,4397,2.984,4398,3.74,4399,3.74,4400,2.551,4401,2.984,4402,2.984,4403,2.984,4404,2.984,4405,2.984,4406,2.984,4407,2.984,4408,2.984,4409,2.984,4410,3.629,4411,3.683,4412,2.984,4413,3.683,4414,2.984,4415,3.683,4416,2.832,4417,2.984,4418,2.984,4419,2.984,4420,2.255]],["t/2192",[2,2.088,4,1.638,7,0.416,25,2.478,26,1.96,28,1.092,40,2.782,56,1.061,68,2.002,84,0.838,98,2.72,109,1.956,127,3.982,165,1.183,172,1.829,179,1.767,182,2.59,193,3.333,199,2.604,219,1.634,313,1.29,318,1.286,338,1.299,372,1.975,398,1.86,405,1.68,476,3.368,508,3.11,512,2.669,567,2.117,575,4.353,614,0.972,653,2.436,757,1.301,803,2.973,840,2.395,846,2.376,892,2.896,898,2.856,916,2.013,966,2.499,984,3.92,1013,3.302,1051,3.753,1076,3.3,1108,2.701,1118,3.269,1224,2.844,1425,3.068,1459,5.99,1539,3.519,1616,4.307,1765,3.029,1925,3.871,2254,2.727,2402,3.869,3565,5.213,3858,4.017,3899,3.485,3942,3.359,4335,4.21,4420,3.99,4421,5.28,4422,5.28]],["t/2194",[4,2.624,7,0.667,24,0.747,27,1.096,28,1.432,34,1.175,49,1.037,56,0.456,68,1.75,84,0.5,96,1.626,107,2.998,109,1.592,116,1.722,163,1.829,170,2.069,179,1.544,182,1.544,203,1.676,213,2.849,295,1.856,338,1.722,397,2.911,476,2.741,512,2.414,527,2.427,552,1.634,560,3.727,575,2.192,580,1.603,614,1.289,647,3.808,653,2.204,663,2.819,725,2.35,736,1.968,740,2.086,846,2.149,892,2.62,945,2.799,966,2.913,993,1.867,1051,2.238,1076,2.521,1082,3.488,1093,2.615,1099,3.132,1101,2.757,1118,4.688,1132,3.109,1224,2.573,1402,2.158,1425,4.069,1616,3.896,1622,2.962,1988,1.931,2254,2.384,2256,7.971,2402,3.5,2900,4.715,3917,3.076,3942,2.936,3943,8.21,3944,3.547,4423,4.38,5015,5.291,5016,5.134,5017,5.38]],["t/2196",[4,3.307,28,1.505,56,1.053,109,2.011,116,2.603,182,2.334,203,2.254,213,3.832,338,1.716,397,3.915,527,2.793,560,5.013,647,2.617,653,2.195,807,2.695,846,3.273,916,2.659,945,3.528,966,3.113,1051,4.566,1062,6.142,1118,3.977,1224,2.563,1988,2.918,2402,4.707]],["t/2198",[4,3.346,5,3.026,28,1.522,34,1.554,56,0.982,116,2.278,182,2.042,203,2.056,213,3.496,236,1.605,256,2.284,295,2.455,338,1.501,397,3.571,511,2.739,527,2.649,560,4.573,647,4.047,653,2.704,807,5.101,846,2.637,892,2.284,916,2.327,945,3.762,966,3.1,1051,4.165,1076,3.093,1118,4.198,1224,3.156,1988,2.553,2402,4.294,3841,4.111,3858,4.458,4424,5.792]],["t/2200",[4,3.288,5,2.428,24,1.716,28,1.496,31,1.684,39,2.805,84,1.012,116,2.572,144,3.301,182,2.306,203,2.236,213,3.802,232,2.507,338,1.696,367,3.509,397,2.865,414,3.954,527,2.596,560,4.973,647,2.586,653,2.17,769,4.367,807,2.664,846,2.116,908,3.509,945,3.091,966,2.89,1051,3.342,1118,2.911,1224,2.533,1266,2.988,1329,5.766,1765,3.954,1988,2.883,2198,4.984,2402,4.67,3616,4.242,3841,4.642]],["t/2202",[4,1.404,24,0.733,28,1.496,34,1.153,56,1.209,107,1.159,109,1.964,171,1.874,172,1.568,182,2.308,193,1.646,219,1.401,246,1.712,247,1.989,254,1.455,261,2.294,296,1.744,304,0.954,327,3.421,337,2.108,338,1.113,377,1.012,485,3.421,491,1.364,502,1.248,512,2.378,527,2.044,552,1.602,600,3.017,614,1.853,647,1.698,653,2.942,658,2.167,803,2.549,818,3.464,846,2.87,916,2.63,944,1.831,945,2.766,957,4.009,966,2.549,1051,4.053,1075,2.373,1076,3.366,1099,4.399,1118,3.53,1132,3.063,1133,5.898,1224,3.071,1705,2.764,1768,3.081,2208,3.23,2402,3.448,4398,3.421,4399,3.421,4410,3.319,4411,3.368,4413,3.368,4415,3.368,4425,4.526,4426,3.541,4427,4.526,4428,4.526,4429,6.546,4430,8.871,4431,8.871,4432,8.871,4433,6.546,4434,6.546,4435,6.546,4436,3.985,4437,4.123]],["t/2204",[4,1.855,17,2.363,28,1.195,56,1.157,106,3.918,109,2.031,137,5.433,182,2.833,219,1.851,254,1.923,327,4.519,337,2.785,338,1.471,377,1.337,444,3.67,502,1.649,527,2.149,600,3.986,614,1.101,638,3.598,653,3.094,818,3.002,846,3.018,916,3.227,945,1.978,966,1.631,1051,4.766,1076,3.049,1083,4.105,1099,3.958,1112,5.022,1118,2.525,1224,3.111,1671,4.657,1705,3.652,1768,4.07,2208,4.267,2402,4.232,2911,6.691,3871,5.113,4398,4.519,4399,4.519,4410,4.384,4411,4.449,4413,4.449,4415,4.449,4426,4.677,4438,5.446,4439,5.446,4440,6.751,4441,7.239]],["t/2206",[0,2.205,4,1.875,27,0.907,34,1.54,56,1.218,106,1.4,108,3.875,109,1.865,113,1.393,127,1.252,162,1.342,171,1.642,172,4.004,182,2.511,193,3.907,203,1.447,219,1.182,236,1.004,254,2.413,261,1.935,296,1.472,304,1.274,318,0.93,338,0.939,372,2.262,377,0.854,391,1.608,408,3.57,418,1.909,426,2.771,446,2.429,476,1.755,491,1.151,502,1.667,552,3.5,600,2.546,614,1.113,647,2.268,653,2.686,725,3.82,757,1.49,813,2.473,818,3.035,829,2.012,846,2.619,872,2.473,890,3.045,916,2.304,937,2.281,944,3.035,966,2.046,1013,3.781,1051,3.638,1075,5.631,1076,3.348,1077,5.534,1082,4.569,1118,4.176,1182,1.821,1224,3.135,1337,4.03,1411,2.473,1671,3.325,1705,2.333,1765,2.191,1768,2.6,1918,3.624,1925,4.433,2402,4.265,2875,2.842,3841,2.572,3858,1.982,4121,3.265,4398,2.886,4399,2.886,4410,2.8,4411,2.842,4413,2.842,4415,2.842,4426,2.987,4442,3.819,4443,3.819,4444,3.819,4445,3.819,4446,3.624]],["t/2208",[4,2.019,28,0.919,56,1.149,109,1.963,116,2.429,172,3.843,182,3.006,193,2.368,219,2.015,224,2.758,327,4.92,337,3.032,338,1.601,377,1.456,391,2.741,407,2.943,476,1.89,513,3.068,552,2.304,608,5.757,647,2.442,653,2.828,725,3.314,807,2.516,818,4.511,846,2.758,916,3.425,944,2.633,966,1.775,1051,4.989,1118,2.749,1224,2.392,1705,3.976,1768,4.432,1849,3.541,1988,2.723,2208,4.646,2402,4.491,2868,3.541,3897,5.093,4398,4.92,4399,4.92,4410,4.774,4411,4.844,4413,4.844,4415,4.844,4426,5.093,4438,5.93,4439,5.93,4447,9.764,4448,6.178]],["t/2210",[4,1.66,6,5.653,7,0.616,8,4.206,19,2.385,21,1.512,24,0.867,31,1.308,34,1.363,56,0.772,68,2.029,84,0.847,104,4.712,105,1.674,106,1.962,107,2.001,109,2.179,115,4.677,116,1.997,123,1.824,137,3.435,144,2.563,157,3.09,182,1.791,228,1.695,229,3.175,236,1.407,256,2.003,295,2.152,304,1.128,316,2.385,318,1.303,343,1.904,353,2.132,372,2.003,398,1.885,426,2.887,491,1.613,502,1.476,593,3.856,614,1.439,615,3.131,653,2.907,679,4.187,683,2.003,705,4.812,742,3.348,765,3.175,803,4.402,809,3.684,828,2.232,846,2.835,916,2.98,944,2.165,966,2.132,1037,2.663,1051,3.79,1076,3.658,1081,3.684,1138,3.405,1224,3.394,1364,2.675,1605,3.924,1671,2.944,2402,3.908,2430,3.294,3008,3.727,3610,4.875,4440,4.268,4449,8.765,5195,5.352]],["t/2212",[7,0.505,15,2.335,46,4.795,56,1.255,116,2.39,182,2.972,219,1.982,254,2.856,327,4.84,337,2.982,338,1.575,377,1.432,600,4.268,614,1.179,647,3.332,653,2.796,715,3.291,846,2.727,916,3.386,1051,4.307,1076,3.199,1083,5.341,1090,2.843,1132,4.891,1224,2.353,1275,5.336,1705,3.911,1768,4.359,1988,2.679,2208,4.57,3838,4.631,3858,3.323,4398,4.84,4399,4.84,4410,4.696,4411,4.765,4413,4.765,4415,4.765,4426,5.009,4440,5.106,4441,5.475,4450,6.404,4451,6.404,4452,6.077,4453,6.077,4454,6.077,4455,6.077,4456,6.077,4457,6.077,4458,6.077,4459,6.077,4460,6.077]],["t/2214",[2,1.939,7,0.567,10,5.198,16,1.338,21,2.031,25,2.302,56,1.141,84,1.041,108,3.712,109,1.621,127,3.152,160,2.52,196,2.849,221,2.655,278,2.576,313,1.756,339,3.626,377,1.608,386,5.349,413,5.433,476,3.355,490,5.066,512,2.478,544,4.496,552,2.544,677,3.787,723,2.569,736,3.065,757,2.368,916,2.74,988,3.515,1051,4.66,1142,4.744,1174,4.841,1328,5.012,1511,4.699,1913,4.048,2017,3.026,2141,3.677,2339,4.294,2419,5.99,2420,6.146,3869,5.524,5196,7.746,5197,7.746]],["t/2216",[7,0.728,19,4.114,20,4.236,25,2.169,28,1.592,96,2.386,103,2.266,219,2.858,221,2.502,225,2.421,226,5.469,243,3.704,256,2.534,278,2.427,280,4.309,296,2.61,306,3.008,318,3.089,366,4.236,472,3.298,511,2.159,512,2.335,513,3.192,562,3.326,567,2.716,607,2.596,611,2.816,615,3.963,683,2.534,757,2.588,851,5.644,853,5.791,977,2.47,988,3.312,1192,4.202,1290,3.99,1302,4.106,1335,5.644,1530,4.076,2017,2.851,2078,6.169,2140,6.169,2624,4.386,2625,3.886,4461,6.428,4462,6.428,4463,6.428,4464,6.428,4465,6.428,4466,6.428]],["t/2218",[2,1.47,7,0.924,17,2.154,25,1.745,28,0.769,34,1.388,56,0.922,84,1.23,85,1.888,88,2.455,89,2.034,103,2.651,109,0.919,127,1.788,134,2.713,171,1.481,179,1.824,182,1.824,202,3.752,219,2.889,226,2.643,232,1.983,247,2.396,272,2.232,283,1.576,296,2.101,304,1.149,306,3.518,313,1.332,318,2.923,322,1.705,338,1.341,353,1.487,397,2.266,401,2.084,418,4.665,421,3.41,438,2.829,471,2.259,476,1.582,502,1.503,511,2.526,526,3.942,534,3.89,567,4.985,575,3.764,591,3.108,605,1.973,614,1.459,672,5.498,723,3.336,734,2.412,802,2.205,828,2.273,897,3.438,1076,1.963,1224,2.911,1309,3.168,2013,1.816,2402,2.725,2886,4.264,2993,4.264,3932,4.264,4467,7.519,4468,5.173,4469,5.173]],["t/2220",[7,0.795,15,3.191,16,1.166,25,2.006,56,1.174,84,0.679,89,2.339,103,2.928,108,3.237,145,2.52,164,2.77,196,2.484,201,3.19,243,3.427,272,2.566,279,2.708,318,1.526,336,4.441,377,1.402,401,2.395,405,3.472,418,3.132,471,2.597,511,1.998,517,3.799,532,3.268,562,3.078,567,4.376,723,2.24,757,1.544,921,3.986,937,3.744,977,2.285,1039,4.136,1375,3.799,1394,3.986,1530,3.771,1797,4.997,4470,5.947,4471,5.947,4472,5.947,4473,5.947,4474,5.947,4475,5.947,4476,5.358,4477,5.947,4478,5.947,4479,5.947,4480,5.947,4481,5.947,4482,5.947,4483,5.947,4484,5.947,4485,5.947,4486,5.947,4487,5.947,4488,5.947,4489,5.947,4490,5.947,4491,5.947,4492,5.947,4493,5.947]],["t/2222",[7,0.673,15,3.471,28,1,31,1.146,48,1.902,56,1.312,85,2.455,103,1.569,105,1.467,109,1.195,127,2.324,140,1.837,161,3.192,164,1.288,171,1.274,179,1.569,225,3.653,243,2.564,300,2.842,318,1.142,336,1.908,377,1.049,392,2.909,401,1.792,405,1.492,437,2.201,472,2.283,476,1.361,511,2.259,532,2.446,562,2.303,567,3.817,601,2.201,607,1.797,670,2.725,747,1.925,829,2.47,830,2.143,921,6.501,923,5.387,1012,3.739,1013,2.933,1027,2.983,1039,3.095,1313,2.725,1352,3.158,1405,3.036,1631,3.818,1632,2.434,1633,2.21,1664,3.438,1718,3.126,2013,1.562,2253,2.842,2655,4.271,3137,3.907,3139,3.907,3763,4.507,3847,4.009,4476,6.059,4494,9.699,4495,6.724,4496,4.45,4497,4.45,4498,4.45,4499,4.45,4500,4.45,4501,4.45,4502,4.45,4503,4.45,4504,4.45,4505,4.45,4506,4.45,4507,4.45,4508,6.059,4509,4.009,4510,4.45,4511,4.009,4512,4.009,4513,3.668,4514,4.45,4515,4.009,4516,4.45,4517,4.009,4518,6.059,4519,4.009,4520,4.009,4521,4.009,4522,4.009,4523,4.009,4524,4.009]],["t/2224",[7,0.398,28,0.713,31,1.234,36,2.228,49,1.136,56,1.342,68,1.916,109,0.852,127,1.657,165,1.132,203,1.794,225,1.806,226,3.633,300,3.063,318,1.23,336,2.056,377,1.13,398,3.48,405,2.384,418,2.525,426,2.767,476,1.467,567,3.581,577,2.39,601,2.372,921,6.712,923,5.633,946,4.32,966,2.043,988,3.664,1012,4.029,1013,3.16,1027,3.214,1039,3.334,1309,2.936,1352,3.403,1475,2.718,1631,4.114,1632,2.622,1635,4.21,1664,3.705,1718,3.368,1763,4.602,1765,2.898,2253,3.063,2339,4.476,2775,4.602,3059,3.04,3137,4.21,3139,4.21,3468,5.975,3763,3.214,4420,5.663,4509,4.32,4511,4.32,4512,4.32,4513,3.952,4515,4.32,4517,4.32,4518,6.407,4519,4.32,4520,4.32,4521,4.32,4522,4.32,4523,4.32,4524,4.32,4525,4.795,4526,4.602,4527,8.476,4528,4.795,4529,4.795,4530,4.795,4531,4.795,4532,4.795,4533,4.795,4534,4.795,4535,4.795]],["t/2226",[1781,3.73]],["t/2228",[7,0.705,15,3.26,34,2.277,56,1.092,228,2.832,322,2.797,377,2,398,3.149,401,3.418,465,6.226,539,5.738,543,5.035,607,3.427,683,3.345,747,3.671,820,4.595,1535,6.871,1536,6.556,1555,7.872,1563,7.281,1607,5.844,2013,2.978,3556,5.901,4255,7.872,4256,7.872]],["t/2230",[7,0.618,16,1.458,24,0.661,25,1.308,28,0.899,34,1.04,49,1.432,56,0.629,84,0.849,103,1.367,145,3.152,162,1.435,171,1.11,175,2.234,203,1.526,219,1.973,236,1.676,256,2.385,275,1.264,281,1.933,283,1.843,304,1.343,313,1.557,315,1.94,318,0.995,322,1.994,334,2.314,338,2.178,365,2.07,368,3.087,372,1.528,395,2.519,398,1.439,401,2.436,438,2.12,458,2.3,482,1.827,530,2.85,531,2.476,539,6.82,542,5.311,543,4.986,552,1.446,564,2.645,567,2.556,605,1.478,613,1.846,614,1.956,630,1.509,708,4.986,747,1.677,757,1.007,762,2.751,838,2.06,846,1.254,859,2.12,911,3.139,966,2.774,977,2.858,982,2.06,1013,2.555,1019,2.342,1070,2.329,1094,3.186,1141,2.915,1145,2.08,1219,1.82,1241,3.493,1297,3.8,1481,2.186,1531,2.723,1540,2.26,1550,3.04,1570,3.404,1638,2.11,1732,3.04,1737,1.541,1754,1.736,2013,1.361,2218,2.954,2577,2.152,2871,3.258,2887,3.729,3035,7.571,3036,3.493,3441,5.311,3595,3.537,3623,3.721,3719,3.493,3877,2.954,4056,4.817,4257,6.049,4258,6.049,4259,3.877,4260,6.049,4261,3.877,4262,6.049,4263,3.877,4264,6.049,4265,6.049,4266,3.721,4267,6.049,4268,3.877,4269,3.877,4270,6.049,4271,3.877,4272,3.877,4273,3.877,4274,3.877,4275,3.877]],["t/2232",[4,2.472,16,2.239,49,1.791,171,2.165,236,2.994,318,2.93,398,3.623,401,4.353,539,7.722,543,5.791,567,4.824,892,2.982,966,2.805,1094,3.983,1559,8.75,1638,5.312,3595,2.936,4276,7.563,4277,7.563,4278,7.563]],["t/2234",[4,2.439,16,2.105,49,1.767,171,2.136,236,2.975,398,3.591,401,4.325,539,7.682,543,5.741,552,4.005,567,4.8,892,2.942,966,2.78,1019,2.889,1094,3.93,1266,3.408,1559,8.694,1638,5.265,1732,7.586,3595,2.896,4280,7.462,4281,7.462,4282,7.462]],["t/2236",[1781,3.73]],["t/2238",[7,0.528,16,1.943,18,4.933,19,4.082,24,1.084,27,1.589,28,1.754,31,1.636,56,0.905,84,0.992,88,4.126,89,4.387,116,2.499,169,3.662,198,3.26,225,2.393,240,3.662,283,1.936,292,2.913,296,2.58,302,3.083,303,5.238,332,3.475,390,3.233,391,2.819,397,2.784,491,2.018,527,1.464,608,3.475,712,3.602,723,2.393,734,2.963,740,3.027,865,4.51,873,4.609,897,5.777,908,3.409,942,4.91,993,2.709,1022,3.683,1076,2.411,1100,2.654,1188,4.259,1216,8.326,1218,4.377,1288,4,2928,3.528,2965,5.58]],["t/2240",[16,2.026,18,1.476,19,1.497,23,1.122,24,1.115,25,3.282,27,2.069,28,1.603,29,1.903,56,0.783,96,1.183,98,1.181,100,3.08,102,3.441,107,2.232,109,1.786,113,1.989,116,1.254,127,1.789,133,1.437,140,1.317,145,2.194,163,4.502,165,1.776,170,0.993,172,1.89,182,2.304,183,1.442,198,1.636,216,1.71,231,2.43,232,1.222,254,2.215,278,1.204,279,1.452,285,2.254,295,1.351,306,1.492,322,1.051,334,1.903,336,1.367,338,0.826,343,1.94,353,0.916,377,1.22,405,3.463,440,1.481,476,1.584,495,2.634,511,1.739,512,1.881,517,2.037,557,1.672,558,2.439,577,2.581,592,2.156,614,0.619,636,1.892,647,1.26,653,1.058,719,2.022,723,4.244,734,1.487,757,1.953,760,2.035,778,3.441,840,2.475,855,2.068,944,1.359,945,3.089,974,2.052,984,3.391,993,1.359,1013,2.101,1022,1.848,1037,3.427,1062,2.623,1074,2.43,1076,2.855,1099,2.55,1100,2.162,1105,2.628,1112,1.993,1182,1.602,1219,1.497,1244,2.8,1303,2.156,1329,3.171,1768,2.287,1778,2.736,2076,1.953,2247,2.368,2250,1.513,2311,2.263,2450,2.24,2911,2.287,2928,5.581,3741,2.398,3811,2.582,3899,3.6,4560,2.582,5018,2.628,5198,3.36]],["t/2242",[16,2.212,24,1.328,25,1.83,27,2.939,28,1.677,31,1.397,84,0.619,107,2.455,109,1.946,116,2.134,127,2.689,163,4.82,165,1.281,203,1.369,232,2.983,278,2.049,281,2.705,293,4.559,360,5.852,367,2.911,377,1.279,507,4.46,596,3.693,653,2.581,723,2.044,740,2.584,807,3.169,840,2.594,938,3.519,944,2.313,945,2.713,979,3.811,1022,5.274,1062,4.61,1076,2.059,1100,2.266,1102,3.415,1182,2.727,1219,2.548,1266,3.555,1329,3.776,1354,3.935,1377,4.254,2247,4.029,2928,5.518,3072,4.888,3899,3.773,5199,5.717]],["t/2244",[7,0.28,16,1.948,21,2.707,25,1.138,26,2.655,27,0.843,28,1.524,34,1.822,56,1.232,84,1.09,88,3.689,96,1.252,98,2.005,100,1.589,105,2.237,106,2.092,107,1.461,108,1.835,109,0.962,113,1.296,128,2.722,160,1.246,163,2.262,165,1.279,169,1.943,179,1.189,200,2.827,201,1.809,278,1.273,295,1.429,296,1.369,297,1.943,313,0.868,322,1.111,323,2.302,338,0.874,339,2.879,353,0.969,377,2.006,413,2.685,416,2.628,418,1.776,426,2.108,430,1.752,476,1.031,484,1.505,502,1.574,506,2.107,507,3.104,508,4.824,509,3.233,512,3.821,520,3.236,552,1.258,622,2.879,723,3.86,731,1.862,736,4.463,757,1.406,898,3.869,934,4.725,945,1.176,984,3.06,1015,3.385,1022,1.954,1076,1.279,1132,1.578,1202,2.187,1265,2.992,1394,2.26,1425,2.065,1511,4.675,1705,4.369,2015,2.093,2093,2.419,2214,2.301,2252,2.644,2341,5.825,2426,2.393,2545,2.345,2928,5.514,2992,2.504,4543,3.372,4544,3.372,4545,3.372,4546,3.372,4547,3.372,4548,3.372,4549,3.372,4550,3.372,4551,5.416,4552,3.372,4553,5.416,4554,3.372,4555,3.372,4556,3.372,4557,3.372,4558,3.372]],["t/2246",[16,2.197,24,1.912,25,3.46,28,1.525,31,2.104,39,3.505,198,4.194,282,4.684,289,3.404,302,3.966,409,4.365,505,3.569,635,3.125,723,3.079,830,3.936,1181,5.039,1210,6.737,1988,4.52,2403,6.012,2422,6.934,2501,6.737,2928,4.538]],["t/2248",[5,3.249,16,1.716,23,3.763,28,1.301,145,3.709,163,3.655,165,2.066,169,5.044,239,6.984,367,4.695,511,2.94,807,4.705,966,2.515,1100,3.655,2426,6.211,2992,6.499,3741,6.581]],["t/2250",[2,2.1,16,1.638,19,1.801,24,1.547,25,1.294,27,1.501,28,1.625,56,0.399,89,1.509,97,1.808,102,2.55,107,2.594,109,1.066,113,1.474,125,2.305,127,2.073,132,2.287,140,1.584,145,2.542,153,2.068,163,1.602,165,2.269,170,1.868,172,1.4,179,1.353,182,2.115,183,1.735,198,1.968,228,1.281,239,2.319,245,2.415,275,1.251,276,3.857,277,2.078,289,3.077,294,2.175,313,2.151,320,2.319,323,1.63,324,3.348,351,1.463,365,2.049,381,4.165,394,2.021,398,1.424,405,3.846,426,1.493,432,2.55,444,1.753,468,2.572,472,1.968,495,1.952,502,1.115,511,2.807,527,1.382,550,1.589,555,1.913,577,1.913,581,2.923,621,2.078,635,1.467,662,2.349,683,1.513,712,2.175,723,1.445,748,3.055,749,2.618,750,2.849,757,1.558,759,1.417,760,1.194,769,1.554,780,4.144,814,1.741,820,3.249,840,1.834,859,2.098,891,3.559,917,2.29,994,1.741,1015,2.398,1022,2.224,1036,3.055,1072,4.416,1099,1.497,1100,1.602,1172,2.433,1182,3.713,1183,2.783,1214,3.457,1229,4.946,1331,2.594,1407,2.885,1475,2.175,1987,2.849,2010,1.407,2253,2.451,2333,3.457,2434,2.752,2448,3.954,2479,4.946,2898,2.723,2946,5.042,3184,3.457,3972,2.643,3974,3.682,3982,3.682,4004,3.559]],["t/2252",[21,2.669,24,1.529,28,1.736,89,3.525,107,3.15,109,1.593,165,2.116,179,3.16,338,2.323,405,3.005,635,3.427,723,3.376,820,4.854,1229,7.388,2928,4.976]],["t/2254",[1781,3.73]],["t/2256",[7,0.62,18,5.45,28,1.109,34,2.002,56,1.117,89,3.805,103,2.631,201,4.003,225,3.644,226,3.812,495,3.796,530,3.516,558,3.516,605,2.845,683,3.815,685,6.295,707,4.375,799,5.942,892,2.942,914,4.959,933,4.454,1007,5.092,1192,4.878,1195,5.352,1196,5.611,1197,6.94,1198,5.765,1199,6.866,1200,6.552,1224,2.889,5200,7.863]],["t/2258",[2,2.157,7,0.768,16,1.676,18,5.121,19,1.879,25,1.35,27,1.001,28,1.129,31,1.596,34,1.074,84,1.056,88,5.249,89,3.637,123,2.227,128,3.116,132,1.239,162,1.482,164,1.159,165,1.464,169,3.573,196,2.589,197,1.879,213,1.716,225,2.335,226,2.045,275,2.476,279,1.822,283,1.219,304,1.377,306,2.901,313,1.03,351,2.364,353,1.782,440,1.86,472,3.181,483,2.66,484,1.787,495,3.155,500,1.995,510,8.121,512,4.091,557,3.251,558,1.886,575,3.103,605,1.526,628,2.178,647,3,659,2.731,734,2.891,736,3.41,762,2.84,796,2.21,828,1.759,838,2.127,857,1.913,892,2.445,897,4.121,898,3.534,899,3.363,933,5.103,1015,2.501,1017,3.01,1076,1.519,1195,2.871,1197,2.871,1199,2.84,1243,3.241,1331,2.706,1423,3.05,1424,3.187,1778,3.434,2093,2.871,2113,2.903,2151,3.363,2250,4.842,2331,2.683,2426,2.84,2883,3.713,2992,2.972,2993,5.111,3932,6.256,5201,4.217,5202,4.217,5203,4.217,5204,4.217]],["t/2260",[28,1.437,96,3.587,174,3.027,254,3.274,283,2.944,295,4.095,591,5.805,1290,5.998,2250,4.586,2444,6.788]],["t/2262",[16,1.789,23,1.918,24,1.333,28,1.357,56,1.394,132,1.687,145,2.31,170,1.697,174,1.708,203,1.375,224,2.434,225,2.054,239,3.296,289,2.27,318,1.399,506,3.408,558,2.569,614,1.058,723,2.941,740,2.597,749,3.72,760,1.697,780,3.058,802,2.324,807,2.22,933,3.255,977,2.095,1195,3.911,1197,3.911,1199,3.869,1237,3.72,1329,2.645,2247,4.049,2403,3.197,2404,3.954,2928,3.027,3810,3.593]],["t/2264",[16,1.16,28,0.88,56,1.391,109,1.051,145,2.507,170,3.601,174,1.853,203,1.492,224,2.641,225,2.228,239,3.576,614,1.148,723,2.228,740,2.818,749,4.037,760,1.842,772,4.114,802,2.522,933,3.531,977,2.273,1062,2.997,1145,3.174,1195,4.243,1197,4.243,1199,4.198,1329,2.87,1410,4.711,2247,4.393,2928,3.284,3016,5.678,3810,3.899,5205,6.234]],["t/2266",[1781,3.73]],["t/2269",[28,1.301,56,1.113,171,3.063,219,3.489,304,2.376,322,2.885,552,3.264,757,2.272,939,5.257,1019,4.142,1237,7.884,1737,3.478,2013,3.072,2986,8.752,3595,3.397]],["t/2271",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,1271,5.023,4609,6.948]],["t/2273",[1781,3.73]],["t/2275",[34,2.593,56,1.005,84,1.103,257,5.184,278,3.649,279,4.4,295,4.095,545,4.706,1364,5.09,4536,9.663]],["t/2277",[4,1.668,7,0.802,15,1.961,16,1.894,21,2.216,24,1.5,28,1.307,41,2.439,46,3.838,49,1.208,56,0.531,84,1.103,96,1.894,125,3.065,138,3.735,164,1.477,165,1.204,171,1.461,179,3.622,183,2.308,189,2.877,203,1.287,207,2.194,245,3.212,282,2.924,283,2.268,305,3.42,306,2.387,337,2.504,338,1.323,343,1.912,353,1.466,377,1.754,409,2.725,416,2.476,440,2.371,441,5.036,449,4.828,534,5.598,536,2.336,545,2.485,558,2.404,559,8.814,560,4.929,565,2.561,596,3.533,687,4.48,744,5.803,747,2.208,757,1.325,827,4.693,985,5.177,1179,3.584,1182,2.564,1297,3.803,1319,3.66,1389,4.378,2009,3.584,2076,3.125,2117,4.132,4110,7.693,4204,7.384,4537,5.102,4538,5.102]],["t/2279",[18,4.288,28,1.377,56,0.964,84,1.057,113,3.559,253,5.872,283,2.822,530,5.216,545,5.391,583,5.961,881,6.104,933,5.529,4539,8.132]],["t/2281",[7,0.739,21,2.648,23,3.128,84,1.015,88,4.22,89,3.497,283,2.709,353,2.555,359,4.131,391,3.944,472,4.562,476,2.72,512,3.23,545,5.261,575,4.451,614,1.725,1105,7.329,1186,7.2,3830,6.687]],["t/2284",[171,2.866,232,3.837,304,2.223,545,4.876,1737,3.978,3595,3.886]],["t/2286",[4,3.105,17,3.956,84,1.084,219,3.098,237,7.339,284,6.741,304,2.109,545,4.626,1413,5.602,2424,9.117,4540,9.498,4541,9.498]],["t/2288",[1781,3.73]],["t/2290",[4,2.042,5,2.319,7,0.879,16,1.225,19,4.035,23,2.198,24,1.466,28,1.573,56,0.65,84,0.713,89,2.457,145,2.647,163,2.609,164,2.487,174,1.957,219,2.038,224,2.789,232,4.054,274,5.486,275,2.038,278,2.359,284,4.434,313,1.608,361,4.434,400,5.15,421,4.118,491,2.729,545,3.043,550,2.587,552,2.33,555,3.115,558,4.984,596,2.965,601,3.09,683,2.463,757,2.231,880,4.345,888,3.468,914,5.711,984,2.816,1015,3.905,1022,3.621,1037,3.276,1145,3.352,1182,4.318,1290,3.878,1330,4.303,1508,3.642,1913,3.707,2103,5.796,2250,4.661,4648,6.247]],["t/2292",[2,1.681,4,1.934,7,0.858,16,1.16,23,2.081,24,1.412,25,1.996,27,1.48,28,1.536,31,2.13,56,0.861,84,1.089,103,2.086,113,2.273,123,2.124,165,1.396,170,1.842,173,3.336,174,1.853,216,3.174,217,3.553,219,1.93,232,3.657,247,2.739,276,3.088,289,2.463,306,2.768,318,2.448,337,2.903,363,4.449,409,3.159,491,2.628,500,2.949,502,2.404,511,1.987,513,2.937,530,2.787,552,2.206,558,2.787,562,3.061,565,2.035,567,2.499,568,1.785,577,2.949,632,3.354,635,2.262,663,3.807,666,4.876,683,2.332,685,3.468,814,2.684,821,4.34,914,5.498,919,4.79,977,2.273,1182,2.973,1201,5.678,1266,4.358,1287,4.571,1398,4.29,4649,5.916,4650,5.916,5206,6.234]],["t/2294",[7,0.644,21,1.231,24,1.691,25,1.395,27,2.734,28,0.946,29,2.338,48,1.768,56,1.25,63,3.349,84,0.472,88,1.963,89,3.692,95,4.158,106,1.597,107,2.09,108,2.251,109,1.13,112,2.159,113,1.589,128,3.196,135,3.82,164,1.197,165,1.829,169,3.665,193,3.598,203,1.043,232,2.437,294,2.345,296,2.583,306,1.935,336,1.773,343,1.55,377,0.975,391,1.835,446,4.263,447,2.062,476,1.265,483,2.749,496,2.662,505,1.806,507,4.441,512,2.311,558,1.949,635,1.581,647,1.635,653,2.109,659,2.822,672,2.567,740,5.316,767,3.349,807,3.155,873,3,897,4.227,966,1.827,993,2.711,1062,3.926,1075,2.284,1089,2.662,1090,1.935,1100,2.656,1102,4.877,1121,2.642,1132,1.935,1133,3.726,1207,3.151,1218,2.849,1266,3.973,1329,3.759,1354,3,1375,2.642,1437,5.065,1539,2.905,1622,2.797,1729,5.344,1730,3.549,2093,2.967,2191,2.876,3565,2.935,3576,3.294,3742,3.349,4897,3.294]],["t/2296",[2,1.387,7,0.838,16,0.957,24,1.613,25,3.403,27,1.802,28,1.623,39,3.089,50,3.008,84,0.822,89,1.919,105,1.608,130,2.931,131,2.332,132,1.51,133,2.199,145,2.067,153,2.63,163,2.038,164,1.412,165,1.152,168,7.519,175,2.812,190,2.642,198,3.696,232,1.87,275,2.349,304,2.24,313,1.256,323,2.073,336,2.092,337,2.394,338,1.265,339,2.593,351,1.861,353,1.402,367,3.864,483,3.242,491,1.55,544,3.216,558,4.455,591,4.327,611,2.137,614,1.397,621,2.642,635,1.865,751,2.968,765,3.049,778,3.242,780,2.737,802,3.07,830,2.35,840,2.332,859,2.668,944,2.08,977,1.875,1080,2.86,1266,2.229,1364,2.57,1403,4.1,1423,6.525,1689,3.979,2035,3.427,2202,3.951,2250,3.418,2403,6.189,2404,6.21,2422,4.871,2501,5.937,4050,4.186,5207,7.59,5208,7.59,5209,5.141]],["t/2298",[1781,3.73]],["t/2301",[28,1.301,56,1.113,171,3.063,219,2.855,304,2.376,322,2.885,548,5.676,757,2.272,939,5.257,1019,4.142,1237,7.884,1330,7.368,1737,3.478,2013,3.072,3595,3.397]],["t/2303",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,1271,4.662,1693,5.482,4609,6.449]],["t/2305",[1781,3.73]],["t/2307",[512,3.773,575,5.198]],["t/2310",[56,1.169,171,2.719,304,2.109,322,3.131,567,4.013,1019,3.678,1266,4.339,1737,3.775,2013,3.334,3595,3.687,4631,9.498]],["t/2312",[304,2.243,438,5.524,966,2.903,989,5.994,4632,10.102]],["t/2314",[56,1.014,304,2.165,567,4.119,651,6.847,966,2.801,989,5.784,1123,6.051,1297,4.98,1732,7.643]],["t/2316",[24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,567,3.979,635,3.601,747,4.075,4609,6.831]],["t/2318",[16,1.911,171,2.791,304,2.165,543,5.784,567,4.119,892,3.843,1266,4.453,1737,3.874,3595,3.784]],["t/2320",[56,1.042,304,2.223,401,4.032,966,2.877,1607,6.895,1638,5.448]],["t/2322",[56,1.042,304,2.223,398,3.716,401,4.032,966,2.877,1638,5.448]],["t/2324",[7,0.674,24,1.385,27,2.03,28,1.206,55,4.65,84,0.926,88,3.851,103,3.938,145,3.438,192,5.036,272,3.5,343,3.041,377,1.912,418,4.274,511,3.429,557,4.255,567,3.428,568,2.449,596,3.851,653,2.691,672,5.036,706,6.102,740,4.862,814,3.682,1753,5.885,2117,6.57,2128,5.885,3810,5.348]],["t/2326",[1781,3.73]],["t/2328",[2,2.507,23,3.103,24,1.834,56,0.918,179,3.11,221,3.433,313,2.767,324,4.922,512,3.205,552,3.29,723,4.049,857,4.217,1019,3.415,1224,3.415,1266,4.03,1271,4.625,1309,5.402,2624,6.019]],["t/2330",[512,3.808]],["t/2332",[7,0.664,16,1.568,24,1.726,25,2.698,27,2.916,28,1.189,31,2.059,68,3.195,84,1.155,96,2.969,132,2.475,281,3.987,512,3.675,553,4.463,635,3.867,653,2.653,898,4.558,971,6.179,995,4.689,1181,4.93,1182,4.019,1285,3.594,1622,6.84,2403,4.689,2422,5.408,2825,6.592]],["t/2335",[56,1.163,171,2.696,219,3.072,304,2.091,322,3.104,552,3.513,1019,3.647,1266,4.302,1737,3.743,2013,3.306,3595,3.656,4608,8.082]],["t/2337",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,1271,5.023,4609,6.948]],["t/2339",[7,0.836,15,2.759,16,1.408,21,1.446,24,1.457,27,2.136,29,1.786,34,1.304,49,1.15,56,1.134,84,0.554,85,1.773,103,1.713,107,1.31,109,1.518,110,3.986,127,2.481,158,2.558,160,1.794,219,2.786,221,3.324,225,1.83,322,2.366,353,1.396,395,2.023,401,1.956,410,3.378,476,1.486,527,2.174,552,3.519,562,3.715,564,4.899,596,2.305,607,1.962,613,2.314,755,2.305,757,1.261,759,1.794,760,2.235,769,1.967,916,1.951,977,1.866,988,2.503,1013,3.201,1019,1.881,1051,2.482,1239,4.377,1285,2.183,1328,4.694,1365,2.955,1405,4.899,1718,5.043,1732,5.63,1759,3.523,1857,3.564,1923,2.725,2013,2.52,2031,4.048,2228,3.564,2274,3.701,2293,2.995,2339,3.058,2340,3.151,2403,2.848,2569,4.082,2621,3.701,3070,3.523,4016,3.753,4054,4.168,4508,4.377,4608,4.168,4610,4.857,4611,7.18,4612,4.857,4613,7.18,4614,4.857,4615,4.857,4616,4.857,4617,4.857,4618,4.857,4619,4.857]],["t/2341",[55,5.686,192,6.158,272,4.28,418,5.226,530,4.675,596,4.709,814,4.502]],["t/2343",[1781,3.73]],["t/2345",[2,0.533,5,1.224,16,1.651,24,1.996,28,1.668,29,0.689,56,1.235,84,0.376,97,0.883,105,0.618,129,1.18,130,1.981,132,1.021,153,1.01,155,1.279,160,2.24,162,0.694,164,1.279,165,1.432,170,0.583,193,1.693,196,0.783,203,0.473,224,4.473,225,1.664,232,1.264,239,1.994,243,1.08,275,0.611,277,1.015,279,0.853,289,2.785,297,1.08,323,1.402,324,1.046,351,0.715,365,1.001,367,3.588,377,1.254,394,3.194,405,2.42,407,2.889,446,1.256,495,2.248,502,2.687,505,0.818,511,0.629,514,1.492,527,0.432,528,1.831,550,2.989,555,1.644,568,0.565,583,1.206,591,1.126,605,0.715,607,1.784,609,1.291,614,0.364,630,0.729,632,1.062,635,0.716,650,1.33,662,2.705,663,1.206,675,1.256,683,0.739,734,0.874,748,2.627,749,2.251,754,1.608,760,1.027,802,0.799,807,3.553,814,0.85,816,1.969,820,2.393,871,1.448,916,1.775,934,0.974,945,1.15,947,3.241,966,0.538,974,1.206,977,0.72,989,2.621,994,0.85,1005,1.291,1015,1.171,1019,1.277,1027,3.567,1055,3.366,1062,0.949,1072,1.606,1079,2.912,1093,1.119,1129,2.83,1175,1.6,1178,2.007,1195,1.344,1197,2.366,1199,1.33,1271,1.729,1285,1.482,1303,2.23,1329,1.6,1347,1.375,1353,3.072,1376,1.33,1399,2.091,1403,1.575,1405,3.015,1458,1.448,1462,1.068,1467,2.174,1633,2.194,1699,2.156,1718,3.103,1923,1.051,1941,2.83,1989,2.719,2182,4.111,2250,2.097,2253,1.197,2256,3.169,2263,2.83,2264,1.469,2390,2.719,2426,1.33,2439,1.545,2584,1.391,2613,1.608,2938,1.575,2950,1.517,2992,1.391,3027,1.469,3074,1.608,3504,3.06,3507,1.738,3810,2.174,4731,5.821,4733,4.24,5179,1.798,5210,3.476,5211,1.975,5212,1.975,5213,1.975,5214,1.975,5215,1.975,5216,1.975,5217,1.798,5218,1.798,5219,1.975,5220,3.166,5221,1.798,5222,1.798,5223,1.688,5224,1.798,5225,1.975,5226,4.656,5227,1.975,5228,3.476,5229,5.107,5230,3.166,5231,1.975,5232,1.975,5233,1.975,5234,1.975,5235,1.975,5236,1.975,5237,1.798,5238,1.798,5239,1.798,5240,1.975,5241,3.166,5242,3.98,5243,1.975,5244,1.975,5245,1.975,5246,1.975,5247,1.975,5248,1.975,5249,1.975,5250,3.476,5251,1.975,5252,1.975,5253,1.975,5254,1.975,5255,1.798,5256,1.798,5257,1.975,5258,1.798,5259,1.975,5260,1.975,5261,1.975,5262,3.476,5263,1.975,5264,1.975,5265,1.975,5266,1.975,5267,1.975,5268,1.975,5269,1.975,5270,1.975,5271,1.975]],["t/2347",[16,1.329,18,3.139,23,2.385,24,1.867,25,2.287,28,1.351,34,1.82,49,2.591,70,6.831,97,3.195,203,2.291,278,2.56,289,2.824,295,2.873,342,4.67,373,5.166,381,3.38,476,2.074,484,3.027,580,2.481,607,2.738,614,2.123,637,4.585,749,4.627,757,1.76,760,2.828,769,2.746,780,3.803,846,2.193,933,4.048,1005,4.67,1090,3.172,1181,4.18,1285,3.046,1402,3.34,2017,3.008,2202,5.49,2403,3.975,2422,4.585,2430,4.397,2631,7.055,3008,4.975,3810,4.469,4696,6.109]],["t/2349",[16,2.25,24,1.808,25,3.575,27,2.155,28,1.281,56,0.897,164,2.494,367,5.684,476,2.636,577,4.296,778,5.726,807,4.315,984,3.884,1022,4.994,1399,5.463,3899,5.992,5272,9.08,5273,9.08]],["t/2351",[7,0.638,18,1.887,24,1.753,28,1.387,31,1.049,56,1.312,84,0.465,103,2.217,132,1.946,160,3.794,163,1.702,165,1.484,183,1.843,203,1.586,221,1.586,224,1.819,236,1.129,256,1.607,283,1.242,285,1.774,296,1.655,313,1.049,377,1.482,381,2.032,405,1.366,426,3.997,502,1.184,511,1.369,527,0.939,528,2.262,565,2.163,614,0.791,653,1.352,675,2.732,683,1.607,754,3.497,759,2.836,760,3.461,769,2.546,807,1.659,814,1.849,840,1.948,910,2.418,916,1.637,934,3.268,936,2.892,966,1.171,994,1.849,1005,6.423,1182,2.048,1265,4.24,1271,4.525,1285,1.831,1329,1.977,1347,5.633,1456,2.262,1462,2.323,1629,2.99,1793,2.708,1988,1.796,1989,3.359,2017,2.789,2246,5.284,2250,4.426,2264,3.195,2338,2.362,2448,2.686,2624,2.781,2747,3.781,2835,4.075,2839,4.075,5274,4.295,5275,4.295,5276,4.295,5277,4.295,5278,3.911,5279,3.497,5280,3.911,5281,3.911,5282,4.295,5283,4.295,5284,4.295,5285,4.627]],["t/2353",[16,2.058,24,1.448,28,1.561,31,2.184,49,2.01,56,1.092,163,3.544,302,4.117,528,4.711,541,5.503,553,4.735,635,4.013,751,5.162,1285,3.813,1325,6.556,2403,6.981,2404,6.154,2422,5.738,2631,5.844]],["t/2355",[2,0.601,5,1.361,7,0.176,9,1.444,16,0.719,24,0.988,28,0.861,29,0.778,34,0.568,56,1.287,84,0.242,97,1.728,107,2.878,109,1.577,113,0.813,129,1.332,134,1.109,155,1.444,160,1.793,164,0.613,165,1.367,170,3.51,179,0.746,193,1.861,213,0.907,224,4.211,225,1.828,232,0.811,289,1.527,295,0.897,297,1.219,326,1.518,338,1.258,343,0.793,373,1.612,377,0.864,394,1.114,405,2.732,495,1.866,500,1.055,502,2.579,505,0.924,511,0.711,514,1.685,528,1.175,541,1.372,550,2.972,555,1.055,591,1.271,605,0.807,607,2.339,608,1.157,636,1.255,647,0.836,653,0.702,662,2.246,663,1.362,675,1.418,734,0.987,748,1.685,749,1.444,760,0.659,772,4.028,802,0.902,813,1.444,838,1.125,859,1.157,871,1.635,914,1.406,916,1.95,921,3.254,923,2.438,934,1.906,947,3.562,966,0.608,977,1.409,989,1.255,1015,1.323,1027,5.464,1055,4.413,1062,4.326,1079,1.395,1144,1.213,1145,1.135,1178,2.231,1195,1.518,1197,1.518,1199,1.502,1271,1.923,1285,2.181,1303,3.283,1347,1.552,1353,3.376,1376,1.502,1410,4.612,1458,2.834,1462,1.206,1467,3.199,1699,2.398,1718,2.576,1959,4.096,2145,1.906,2182,4.475,2247,1.571,2250,1.004,2253,1.352,2256,3.482,2263,3.147,2350,1.635,2391,2.876,2426,1.502,2439,1.744,2868,1.213,2938,1.778,2970,2.116,2992,1.571,3074,1.816,4513,1.744,4739,5.218,4742,2.031,5217,2.031,5223,1.906,5229,5.559,5230,3.52,5242,4.374,5279,1.816,5286,2.23,5287,2.23,5288,2.23,5289,2.23,5290,2.23,5291,2.23,5292,2.23,5293,2.23,5294,2.23,5295,2.23,5296,2.23,5297,3.52,5298,2.031,5299,2.23,5300,4.659,5301,2.23,5302,3.865,5303,2.23,5304,2.23,5305,2.23,5306,2.23,5307,2.23,5308,2.031,5309,2.031,5310,2.031,5311,3.52,5312,3.52,5313,2.23,5314,2.23,5315,2.23,5316,4.659,5317,2.23,5318,2.23,5319,2.23,5320,2.23,5321,2.23,5322,2.23,5323,2.031,5324,2.031,5325,1.963,5326,2.031,5327,2.23,5328,2.031,5329,2.23,5330,2.23,5331,3.865,5332,3.865,5333,2.23,5334,2.23,5335,2.23,5336,2.23,5337,2.23,5338,2.23,5339,2.031,5340,2.23,5341,2.23,5342,2.23,5343,2.23]],["t/2357",[4,2.138,28,1.318,84,0.747,109,1.575,130,3.929,132,2.024,133,2.949,160,2.416,164,1.894,170,3.892,236,1.812,278,2.47,295,2.772,373,4.984,377,1.542,426,3.451,491,2.078,495,3.328,566,4.799,605,2.494,608,4.849,614,1.72,622,4.713,637,4.423,820,3.543,840,3.127,872,4.464,916,2.627,927,4.549,977,2.513,1008,4.117,1010,4.594,1062,3.314,1145,3.509,1399,4.147,1410,8.587,1458,5.054,1699,4.276,1829,5.392,2250,3.104,2825,5.392,5279,5.613,5344,6.893,5345,6.893,5346,6.893,5347,6.893]],["t/2359",[2,0.881,12,1.317,16,0.992,24,1.391,27,1.265,28,1.372,29,1.14,56,1.318,68,1.238,106,1.197,107,1.996,109,1.139,113,1.191,127,2.556,129,1.951,132,0.959,160,3.011,170,2.538,172,1.131,193,3.536,196,1.295,224,1.384,225,1.167,254,1.05,275,1.011,278,1.17,279,1.411,291,3.14,292,1.421,313,0.798,318,0.795,351,1.182,377,1.192,398,1.151,472,1.59,476,1.548,481,1.951,502,1.47,505,1.354,511,1.041,512,1.126,550,1.283,552,1.156,575,1.551,596,1.471,601,1.533,607,1.252,609,2.135,614,1.435,622,1.647,636,1.839,803,1.839,840,1.482,921,5.464,923,4.26,934,2.63,947,2.274,966,1.454,989,3.803,1015,1.937,1019,1.959,1027,2.078,1028,3.1,1055,3.855,1074,2.362,1075,2.794,1076,1.176,1080,1.817,1129,4.341,1132,1.45,1195,2.223,1197,2.223,1199,2.2,1285,1.393,1347,2.274,1353,2.156,1633,3.183,1664,2.395,1959,3.718,1989,4.17,2182,3.909,2249,2.876,2253,1.98,2256,2.223,2264,2.43,2322,2.43,2391,3.967,2439,4.17,3565,3.59,4513,2.555,5218,2.975,5220,4.856,5221,2.975,5222,2.975,5223,4.558,5224,2.975,5237,2.975,5238,2.975,5239,2.975,5241,4.856,5242,2.793,5255,2.975,5256,2.975,5258,2.975,5297,4.856,5298,2.975,5300,2.975,5308,2.975,5309,2.975,5310,2.975,5311,2.975,5312,4.856,5316,2.975,5323,2.975,5324,2.975,5325,2.876,5326,2.975,5328,2.975,5339,2.975,5348,5.059,5349,3.266,5350,3.266,5351,3.266,5352,3.266,5353,3.266,5354,3.266,5355,3.266,5356,3.266,5357,3.266,5358,3.266,5359,3.266,5360,3.266,5361,3.266,5362,3.266,5363,3.266,5364,3.266,5365,3.266,5366,3.266,5367,3.266,5368,3.266,5369,3.266,5370,3.266,5371,3.266,5372,3.266]],["t/2361",[2,1.542,7,0.451,18,2.512,56,0.81,84,1.039,89,2.134,107,2.455,109,0.964,113,2.085,138,2.306,164,1.571,170,3.095,171,1.553,173,5.131,182,2.744,193,3.81,223,4.617,224,4.438,236,1.503,254,3.083,277,2.938,282,3.109,289,2.259,313,2.003,320,3.28,343,2.917,377,1.279,433,3.301,495,2.76,502,2.261,550,2.247,552,2.024,568,1.637,601,2.684,605,2.069,612,3.702,614,1.053,632,5.158,635,2.074,725,4.881,747,2.348,772,3.773,810,2.539,814,2.462,838,2.884,974,3.492,1062,2.749,1072,2.642,1076,2.059,1078,4.134,1093,5.934,1094,4.099,1175,2.632,1285,2.438,1462,3.092,1737,3.093,2190,5.366,2253,3.466,2352,4.08,2391,4.254,3595,3.021,3833,4.192,4739,4.888,5373,8.201]],["t/2363",[16,2.271,27,1.617,34,1.735,85,2.36,97,3.046,107,1.744,113,2.484,170,3.689,173,3.646,203,2.219,217,3.884,278,2.441,295,2.74,351,3.354,353,1.858,476,1.978,484,2.886,552,2.411,580,2.366,607,2.611,614,2.339,637,4.372,725,4.719,740,3.079,747,2.797,749,4.412,757,1.679,760,2.739,769,2.618,772,4.496,1005,4.453,1101,4.07,1285,2.905,1462,3.685,1481,5.638,1557,6.536,2202,5.235,2254,3.518,3810,4.261,4574,5.433,5374,6.813]],["t/2365",[24,1.317,28,1.148,56,0.803,85,2.818,106,2.982,107,2.668,109,2.199,127,2.668,170,3.583,172,2.818,254,3.351,444,3.527,476,2.362,527,1.779,658,4.991,757,2.833,1062,5.011,1099,4.258,1112,4.825,1149,7.41,5375,7.72,5376,8.136]],["t/2367",[7,0.511,28,0.914,31,1.019,56,1.307,84,0.452,103,2.168,107,2.034,109,1.635,132,1.225,160,3.598,163,1.654,165,1.78,170,3.271,183,1.791,203,1.551,221,1.541,236,1.097,256,1.561,283,1.206,296,1.608,313,1.019,377,1.449,381,1.974,405,1.327,426,3.955,502,1.15,511,1.33,527,0.912,565,2.116,614,0.768,653,1.313,683,1.561,754,3.397,759,2.785,760,3.496,769,2.49,814,1.797,840,1.893,910,2.349,916,1.59,934,3.918,936,2.81,966,1.138,988,2.04,994,1.797,1005,5.854,1062,2.006,1182,1.99,1265,4.164,1271,3.953,1285,1.779,1329,1.921,1347,6.236,1399,2.51,1456,2.198,1462,2.257,1629,2.905,1793,2.631,1959,3.09,1988,1.745,2017,2.728,2246,5.167,2250,4.367,2264,3.104,2338,2.295,2448,2.61,2624,2.702,2747,3.673,2835,3.959,2839,3.959,5274,4.172,5275,4.172,5276,4.172,5277,4.172,5278,3.8,5279,3.397,5280,3.8,5281,3.8,5282,4.172,5283,4.172,5284,4.172,5325,3.673,5377,6.48,5378,4.172,5379,4.172,5380,4.496]],["t/2369",[2,1.665,16,2.346,24,1.525,25,1.976,27,0.931,28,1.67,56,1.213,84,0.827,107,1.58,109,1.588,113,1.43,132,3.675,144,1.878,157,2.264,160,1.375,162,1.378,163,1.554,165,0.878,170,3.497,193,1.426,199,1.934,224,1.661,236,1.623,254,1.261,256,2.31,278,1.405,289,2.439,295,1.577,297,2.145,336,1.596,377,0.877,381,1.855,391,1.651,476,1.138,491,1.182,495,1.893,502,2.597,528,2.066,550,3,552,1.388,568,1.123,582,3.268,605,1.419,607,1.503,614,0.722,622,3.113,635,1.423,662,2.279,675,2.494,725,1.996,740,2.79,756,3.583,807,1.515,916,1.495,934,1.934,937,2.342,989,2.208,1005,2.563,1038,2.669,1062,1.885,1072,1.812,1079,2.453,1145,1.996,1285,3.255,1331,2.516,1397,3.452,1410,5.769,1779,2.641,2182,6.349,2194,3.452,2250,2.78,2259,2.614,2264,2.918,2266,3.721,2267,3.721,2545,2.588,2909,3.452,3611,3.572,3698,3.452,4741,5.622,5381,3.921,5382,3.921,5383,3.921,5384,3.921,5385,3.921,5386,3.921,5387,3.921,5388,3.921,5389,3.921,5390,3.921]],["t/2371",[16,2.033,26,2.719,28,1.373,84,0.793,102,6.137,132,2.858,226,3.551,275,2.267,323,2.953,390,3.536,391,3.083,405,4.045,426,3.594,511,3.973,568,2.097,612,4.742,653,2.305,751,4.228,1078,5.295,1083,4.718,1094,3.66,1100,2.902,1209,6.261,1303,4.699,1355,6.037,1407,5.226,2254,3.782,2448,4.58,3595,2.698,3671,5.627,4561,9.234,5188,5.963,5189,8.32,5391,7.323]],["t/2373",[5,2.788,16,2.112,28,1.693,56,1.011,84,0.857,107,2.027,113,2.886,132,2.325,182,3.426,224,4.338,225,2.829,323,3.192,397,3.291,426,2.923,511,3.962,596,3.565,609,5.174,772,5.224,977,2.886,1062,5.976,1355,4.911,1462,4.282,5392,10.24,5393,7.916,5394,10.24]],["t/2375",[5,2.788,16,1.473,24,1.282,25,2.534,28,1.754,84,0.857,113,2.886,132,2.325,182,2.649,224,4.338,225,2.829,289,3.128,323,3.192,397,3.291,511,3.962,609,5.174,635,2.872,807,4.638,892,3.831,977,2.886,1285,3.375,1355,6.353,2487,6.768,3987,9.993,4337,6.969,5194,7.21,5395,7.916]],["t/2377",[7,0.633,16,1.922,24,1.299,25,2.569,28,1.132,31,1.96,49,1.803,56,1.02,109,1.353,123,2.734,132,2.357,228,3.272,236,2.11,297,4.388,313,1.96,398,2.826,476,2.329,491,2.419,502,2.848,536,3.076,565,2.62,568,2.298,577,5.905,580,2.786,916,3.059,934,3.958,1040,4.901,2250,3.614,3512,9.094,3513,7.064,3514,7.064]],["t/2379",[0,3.822,12,2.67,56,1.103,109,1.75,112,5.801,171,1.799,172,4.056,193,4.259,217,3.774,236,1.741,391,2.787,502,1.826,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,916,2.524,934,5.511,944,4.199,1039,7.374,1040,6.824,1075,3.47,1077,3.705,1083,3.21,1175,4.779,1332,4.413,1337,6.059,2250,2.982,3515,5.829,3516,9.138,3517,5.829,3518,5.829,3519,5.829]],["t/2381",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,934,4.799,945,2.423,1039,7.685,1040,5.943,1099,4.614,1100,3.856,1112,5.771,3520,6.447,3521,6.447]],["t/2383",[12,2.847,16,1.767,24,1.537,27,1.675,28,1.34,56,0.697,89,2.634,116,2.634,133,3.02,145,2.838,162,2.48,170,2.085,200,3.497,224,4.022,275,2.185,281,3.339,292,3.07,299,6.093,302,3.25,318,1.719,351,2.554,381,3.339,396,5.522,405,2.245,436,5.037,470,5.104,502,2.958,513,3.326,550,2.774,568,2.021,609,4.614,635,2.561,679,5.522,694,3.839,787,5.748,802,2.855,807,2.728,811,4.379,917,3.999,944,2.855,1005,7.5,1008,4.217,1022,3.882,1188,4.49,1193,6.611,2450,4.705,2580,6.429,2902,5.748,3490,6.214,3833,5.176]],["t/2385",[1781,3.73]],["t/2387",[7,0.629,23,1.837,24,1.845,25,1.762,29,1.92,56,0.927,68,2.086,84,1.115,160,2.796,165,2.102,177,2.657,179,1.841,198,2.679,199,2.714,219,1.703,221,3.466,228,1.743,232,2.001,240,3.009,248,2.583,275,1.703,283,1.591,296,2.121,313,1.949,316,2.452,324,2.914,338,1.962,353,1.501,377,1.231,397,2.288,422,3.746,491,1.659,512,2.75,522,4.095,530,4.196,548,4.91,552,1.948,647,2.064,723,3.678,757,2.536,760,1.626,822,3.287,857,2.496,898,4.315,934,2.714,966,1.501,1019,2.022,1051,3.868,1193,3.831,1213,3.979,1224,2.022,1266,4.068,1271,2.738,1300,2.976,1309,3.198,1330,5.214,1413,6.376,1570,4.585,1705,3.361,1913,3.098,2402,2.75,2624,3.563,2868,2.993,3869,4.228,4625,7.57,4626,5.222,4627,5.012,4628,5.222,4629,5.222]],["t/2389",[512,3.808]],["t/2392",[56,1.163,171,2.696,304,2.091,322,3.104,548,6.108,1019,3.647,1266,4.302,1330,6.487,1737,3.743,2013,3.306,3595,3.656,4297,7.626]],["t/2394",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,1271,4.662,1693,5.482,4609,6.449]],["t/2396",[55,5.686,192,6.158,272,4.28,418,5.226,530,4.675,596,4.709,814,4.502]],["t/2398",[1781,3.73]],["t/2400",[2,2.079,4,3.122,16,1.434,23,2.573,24,1.924,25,2.468,27,1.83,84,0.835,109,1.3,170,2.277,174,2.291,254,2.478,313,1.883,476,2.921,512,3.864,536,2.295,568,2.882,653,2.426,723,2.755,747,3.165,759,2.702,760,2.277,803,4.34,846,3.44,857,3.497,892,2.884,966,3.056,1076,3.624,1090,4.469,1224,2.832,1959,3.676,2472,5.735,4125,6.786]],["t/2402",[4,2.711,28,1.233,84,1.181,217,6.217,552,3.86,592,5.61,653,2.751,725,6.052,846,2.684,966,2.384,1075,5.717,1076,3.927,1077,6.104,1093,7.052,1224,3.212,1671,4.808,2190,4.892,3595,3.22]],["t/2404",[512,3.808]],["t/2406",[7,0.727,16,1.716,24,1.493,27,2.189,31,2.253,56,0.911,84,1.221,165,2.066,281,4.363,512,3.18,553,4.884,635,3.346,723,3.296,971,6.762,1181,5.396,1266,3.998,1622,5.918,1988,3.858,2179,8.4,2403,5.131,2422,5.918]],["t/2409",[23,3.342,56,1.169,171,2.719,304,2.109,322,3.131,1019,3.678,1266,4.339,1737,3.775,2013,3.334,3595,3.687,4633,8.558]],["t/2411",[23,3.313,24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,635,3.601,747,4.075,4609,6.831]],["t/2413",[7,0.842,15,3.316,16,1.988,23,2.559,24,1.81,25,1.668,27,1.237,49,1.171,56,1.141,84,0.985,109,1.292,110,2.745,127,1.708,170,1.539,171,2.082,318,1.268,321,3.937,322,1.629,338,1.281,377,1.165,395,3.029,410,3.438,437,2.445,505,2.159,527,1.676,528,4.038,532,2.717,596,2.346,635,1.89,723,2.74,747,2.139,759,1.826,760,1.539,769,2.002,830,2.381,876,3.286,977,1.9,1019,1.914,1266,2.258,1400,2.704,1401,3.069,1405,4.963,1759,3.586,1773,3.937,1923,5.337,1959,2.485,1988,2.179,2013,3.029,2076,3.028,2137,3.82,2142,2.592,2228,3.627,2274,3.767,2293,3.048,2403,2.899,2406,8.828,2410,4.586,2722,3.206,4016,3.82,4633,4.454,4634,4.944,4635,4.944,4636,7.273,4637,4.944,4638,4.944,4639,4.944,4640,4.944,4642,7.273,5396,5.21,5397,5.21,5398,5.21,5399,5.21,5400,5.21,5401,5.21,5402,5.21,5403,5.21,5404,5.21,5405,5.21,5406,5.21]],["t/2415",[2282,9.444]],["t/2417",[9,6.537,48,4.096,84,1.094,159,6.729,1223,9.195,1472,9.195,1497,8.888,1781,3.409,2651,9.58,3403,9.58,4662,10.096]],["t/2419",[1781,3.73]],["t/2422",[23,3.37,24,1.635,27,2.396,126,4.874,128,4.815,304,2.127,338,2.483,404,4.915,453,6.478,635,3.663,4609,6.948]],["t/2424",[7,0.646,16,1.524,23,3.495,24,1.695,27,1.945,36,3.612,56,1.034,68,3.106,79,6.827,106,3.002,126,5.054,128,4.992,131,3.716,175,4.48,200,4.058,202,5.639,203,1.961,204,6.671,207,3.344,209,7.462,219,2.536,223,4.613,304,1.726,335,6.827,339,4.132,409,4.151,416,3.772,496,5.003,500,3.876,502,2.259,1083,3.972,2013,2.729,3399,7.462]],["t/2426",[7,0.664,16,1.568,23,3.558,24,1.726,36,3.716,41,3.823,56,1.052,68,3.195,79,7.023,126,4.069,128,5.083,131,3.823,201,4.29,202,7.336,203,2.017,204,6.863,207,3.44,210,7.676,219,3.299,223,4.745,304,1.776,335,7.023,339,4.251,409,4.27,496,6.51,1083,4.086,2013,2.807]],["t/2428",[7,0.739,23,3.128,24,1.517,41,4.251,98,3.292,131,4.251,201,5.795,202,6.449,207,3.824,304,1.974,334,5.308,339,4.726,613,4.235,614,1.725,651,6.246,1583,7.472,1737,3.534,3595,3.452,4652,8.892]],["t/2430",[2,1.312,7,0.688,16,0.905,19,2.167,23,4.039,24,1.179,27,2.301,28,1.368,29,1.697,49,1.637,56,0.48,68,1.844,84,1.125,100,2.175,121,2.846,126,2.349,128,2.32,131,2.207,171,2.634,174,2.165,182,1.628,183,3.126,196,1.928,201,2.476,203,1.744,207,1.985,253,2.927,262,3.179,283,2.525,302,2.24,304,1.84,313,1.188,315,2.31,316,2.167,322,1.521,336,3.553,339,2.453,344,6.228,345,7.466,346,9.678,353,1.326,371,4.159,401,1.859,437,2.283,466,3.428,479,3.387,481,2.906,489,3.242,490,3.428,491,1.466,495,2.349,498,2.706,500,3.446,544,3.042,605,1.76,614,1.341,681,4.556,756,2.283,892,1.82,899,3.879,977,1.774,993,1.968,1019,2.676,1083,3.532,1364,2.431,1374,5.881,1686,7.953,1699,3.018,4653,4.159,4654,6.912,4655,4.616,4656,4.616,4657,4.616,4658,4.616]],["t/2432",[2,1.424,7,0.722,12,2.129,16,1.44,23,2.584,24,0.855,28,1.424,29,1.842,84,1.257,88,4.839,89,4.01,109,0.89,127,1.731,128,2.518,129,3.154,132,1.551,164,1.45,165,1.183,169,4.233,179,2.59,213,2.148,219,1.634,236,1.388,241,4.13,275,1.634,280,3.359,296,2.035,304,1.113,313,1.29,315,2.508,316,4.083,318,1.286,337,2.459,397,3.218,404,2.571,422,3.594,440,2.328,458,2.973,470,3.818,472,2.571,476,2.66,512,3.705,532,2.754,544,3.302,545,2.44,562,3.801,568,2.624,575,5.104,605,3.316,622,2.663,681,3.302,736,2.251,753,4.514,816,2.991,822,3.154,857,2.395,898,4.187,899,4.21,1019,1.94,1162,2.938,1413,4.332,1430,4.648,2402,2.639,2421,3.154,2426,5.213,2545,3.485,2641,3.768,2992,5.455,2993,4.13,4659,5.01,4660,5.01,4661,5.01]],["t/2434",[2,0.843,4,0.97,5,1.101,7,0.662,15,2.39,16,1.219,18,2.261,23,2.535,24,0.506,27,0.742,28,0.726,31,0.764,32,1.956,34,0.796,56,1.099,68,1.186,84,0.909,85,1.083,88,1.408,89,1.167,105,1.609,116,1.167,131,2.334,138,1.261,156,2.129,160,1.096,162,1.099,164,1.413,165,1.88,171,1.398,174,0.929,186,1.474,196,1.239,201,1.592,219,1.592,228,0.991,232,2.384,239,1.794,275,0.968,283,2.427,284,2.106,292,1.36,296,1.982,304,0.659,306,2.284,313,1.601,317,1.563,318,1.252,322,2.05,347,2.064,348,2.007,351,1.862,359,1.379,361,2.106,366,1.956,372,1.17,377,0.699,391,1.316,393,2.526,395,1.236,397,2.724,405,2.085,407,1.413,408,1.129,415,2.204,440,1.379,441,1.701,466,2.204,471,1.296,512,1.078,530,1.398,536,1.952,539,5.386,543,4.276,544,1.956,552,1.107,558,2.3,572,1.656,575,1.485,614,0.576,621,2.644,712,1.682,734,1.384,747,1.284,790,3.582,795,2.044,796,1.639,802,2.081,807,1.208,843,2.813,845,1.989,857,2.334,859,2.67,904,7.881,907,2.293,908,1.592,911,5.036,915,2.967,933,1.771,934,2.537,995,1.74,1010,2.084,1019,3.084,1080,1.74,1094,1.563,1096,2.848,1129,2.546,1178,1.805,1219,1.393,1246,1.989,1265,1.639,1337,2.084,1374,4.414,1481,2.753,1502,2.674,1503,2.674,1507,7.061,1535,3.953,1536,3.772,1540,1.73,1542,6.917,1559,2.403,1560,2.848,1563,2.546,1606,2.967,1607,2.044,1608,2.967,1609,2.967,1610,2.967,1611,2.967,1612,2.967,1613,2.967,1614,2.967,1615,2.967,1616,1.74,1622,4.206,1623,4.882,1775,2.606,2013,1.042,2035,3.429,2094,2.261,2577,2.71,2588,2.044,2989,2.848,3037,2.967,3041,2.967,4255,2.753,4256,2.753,5407,2.848,5408,3.127,5409,3.127,5410,3.127,5411,3.127,5412,3.127,5413,3.127,5414,3.127,5415,3.127]],["t/2436",[7,0.976,16,1.183,24,1.827,25,2.504,27,1.509,28,1.709,31,2.593,40,3.349,56,1.21,103,1.362,107,2.002,109,1.49,115,2.927,123,3.617,127,3.601,162,1.43,219,1.26,223,2.292,276,3.149,468,4.043,476,1.845,491,1.916,527,1.39,528,2.145,565,3.684,568,2.24,601,2.984,614,1.17,630,3.264,635,1.477,757,1.003,759,2.228,810,2.822,814,3.368,857,1.847,938,2.506,1035,2.432,1144,3.457,1163,2.742,1219,1.814,1754,3.324,2625,3.646,2799,4.661,2915,3.863,2917,3.863,2918,3.863,2919,8.387,2920,6.032,2921,3.863,2922,3.863,2923,7.421,2924,3.863,2925,3.863,2926,3.863,2927,3.863,2928,2.145,2929,6.032,2930,6.032,2931,6.032,2932,6.032,2933,6.032,2934,6.032,2935,6.032,4667,4.071]],["t/2438",[1781,3.73]],["t/2440",[3,5.964,7,0.55,8,3.752,10,3.773,11,5.562,21,1.971,22,4.327,23,2.328,24,1.129,26,2.589,27,2.236,28,1.329,31,1.704,46,2.89,56,0.689,84,0.755,103,2.334,105,2.182,109,1.176,121,4.081,123,2.377,126,3.368,140,2.733,165,1.562,171,1.895,174,2.073,197,3.108,213,2.838,220,4.293,221,2.576,232,2.537,292,3.034,305,4.437,315,3.313,346,5.271,353,1.902,392,4.327,434,6.278,491,2.102,500,3.3,555,3.3,571,7.934,583,4.26,590,3.752,591,3.976,592,4.476,611,2.899,622,3.518,637,4.476,657,5.456,1224,2.563,1325,5.114,1479,4.801,1504,4.915,1754,2.964,2568,6.14,2863,6.619,2864,5.456]],["t/2442",[4,2.151,7,0.547,16,1.29,23,3.132,24,1.986,31,2.292,35,4.828,36,4.136,56,0.926,84,0.751,86,4.72,96,2.442,98,3.296,100,3.1,108,3.581,123,2.363,131,4.255,142,6.315,143,9.275,145,2.788,165,1.553,171,1.884,174,2.061,188,6.783,198,3.376,205,6.315,207,3.829,228,2.196,232,3.412,337,3.229,353,1.891,377,2.098,400,7.338,489,4.622,500,4.438,545,3.205,571,5.159,662,4.029,892,2.594,985,4.576,2864,5.424,2865,6.58,2866,5.928,2867,6.58]],["t/2444",[34,2.527,84,1.075,92,3.675,93,4.992,229,5.887,252,8.27,390,4.792,599,5.101,619,6.369,620,5.49,1118,4.191,1368,6.369]],["t/2446",[34,2.761,415,7.64,858,5.347]],["t/2448",[34,2.761,415,7.64,858,5.347]],["t/2450",[12,3.15,27,2.41,28,1.591,84,1.222,107,2.599,109,2.013,172,3.907,173,4.179,174,2.321,272,3.197,304,1.646,318,1.902,468,4.968,495,3.771,502,2.154,557,3.886,564,5.058,596,3.518,607,2.993,612,5.058,747,3.207,819,5.504,829,5.348,830,3.569,977,4.354,978,6.876,1062,3.756,1072,3.61,2218,5.648,2868,4.248]],["t/2452",[4,1.496,5,1.699,7,0.685,16,0.898,21,2.455,24,0.781,25,1.545,26,3.585,27,1.718,28,1.226,40,2.541,56,0.858,84,1.046,98,3.053,106,1.768,107,1.853,109,2.068,110,3.814,111,7.587,116,1.8,126,2.329,132,1.417,133,2.064,164,2.387,165,1.622,169,3.959,170,2.139,183,2.07,186,2.273,189,3.874,256,1.805,272,1.975,289,1.906,316,2.15,318,1.175,353,2.369,365,2.444,372,1.805,377,1.079,382,2.993,407,2.181,416,2.221,432,3.042,439,3.153,440,2.127,447,2.282,458,2.716,527,2.262,555,2.282,565,1.575,568,2.073,578,1.896,593,3.57,606,3.707,613,3.927,615,2.822,630,2.674,635,1.75,679,5.663,809,3.32,814,2.077,818,4.363,837,2.924,976,3.59,980,6.032,1081,5.98,1167,7.649,1271,2.4,1322,5.308,1323,3.32,1459,4.732,1476,4.02,1783,3.646,1987,3.4,2467,4.247,2869,4.578,2870,4.578,2871,3.847]],["t/2454",[4,1.595,5,1.811,7,0.711,15,1.875,16,0.957,27,2.803,28,1.569,56,0.891,98,1.807,105,1.608,107,1.316,109,1.68,113,1.875,127,2.489,132,1.51,165,2.021,171,1.397,172,3.126,173,2.751,182,1.72,199,2.536,228,2.404,236,1.996,257,2.617,281,2.432,289,3,292,2.236,302,2.367,320,2.949,338,1.265,343,1.829,351,1.861,365,3.846,366,3.216,369,3.19,437,2.413,445,3.242,455,6.181,458,2.895,459,4.022,490,3.623,491,1.55,502,1.418,525,2.423,550,2.02,552,1.82,568,2.174,581,5.489,604,3.393,617,4.1,633,4.022,635,2.754,636,2.895,684,2.681,734,2.275,757,1.267,798,2.828,801,3.825,815,4.526,831,3.242,908,2.617,944,3.07,945,3.297,995,2.86,1108,4.615,1138,3.27,1180,3.58,1183,3.539,1265,2.695,1285,2.192,1302,3.116,1322,3.77,1419,4.022,2199,4.526,2479,5.937,2872,4.879,2873,4.879,2874,4.284,2875,3.825,2876,6.053,2877,4.879,2878,4.879,2879,4.284]],["t/2456",[2,1.672,7,0.79,17,2.45,25,2.781,28,1.614,56,1.073,84,1.177,88,4.515,89,4.054,109,1.046,128,2.957,133,2.653,162,2.179,164,1.703,175,3.391,275,1.919,313,1.515,315,2.945,377,1.387,405,1.973,407,2.803,440,2.734,472,4.881,476,2.521,483,3.911,491,1.869,505,2.57,512,4.282,558,2.773,575,5.781,605,2.244,698,4.484,726,4.053,898,4.698,914,3.911,1080,5.578,1307,4.317,1616,3.45,1754,2.635,2547,5.302,2880,5.302,2881,5.648,2882,9.131,2883,5.459,2884,5.302]],["t/2458",[7,0.751,84,1.166,92,2.629,98,2.495,109,1.607,110,3.741,116,2.65,165,1.591,174,3.198,186,3.346,216,3.615,228,3.02,229,4.212,233,2.57,242,5.004,317,3.549,318,1.729,351,2.57,364,5.004,372,2.657,377,2.132,390,3.429,426,2.623,458,3.999,527,2.515,580,2.466,591,4.048,613,3.21,630,3.521,637,4.557,658,5.745,693,4.371,798,3.906,818,3.566,837,4.305,910,5.368,1099,3.53,1179,4.734,1367,4.479,2196,5.918,2421,4.242,2885,6.072]],["t/2460",[2,2.449,5,3.198,23,3.031,24,1.47,26,3.371,158,4.538,220,5.589,246,3.433,247,3.99,276,4.498,351,3.286,365,4.601,401,3.47,408,3.278,418,4.538,420,8.27,458,5.112,466,6.399,534,6.48,628,4.689,828,3.786,880,5.992,1336,5.935,2204,8.27]],["t/2462",[7,0.769,19,4.349,28,1.377,84,1.057,138,3.936,197,4.349,225,3.488,316,4.349,338,2.401,441,5.308,536,2.906,840,4.427,858,4.814,1118,4.121,1506,6.263]],["t/2464",[7,0.895,15,2.886,16,1.905,23,2.643,84,0.857,144,3.791,219,3.513,225,2.829,232,3.724,236,2.081,256,2.962,275,3.17,283,2.289,323,3.192,351,2.865,390,3.822,491,2.386,545,3.659,548,4.872,552,2.802,583,4.835,615,4.631,888,4.17,1118,3.343,1266,3.431,1330,5.174,1923,4.214,2013,2.637,2388,4.149,2886,8.879,2887,4.631,2888,7.512,2889,7.512,2890,6.446]],["t/2466",[7,0.505,16,1.192,17,2.531,23,2.966,55,3.483,85,2.218,105,2.003,113,2.335,116,2.39,135,3.65,164,1.759,171,1.74,196,2.538,213,2.606,219,1.982,225,2.289,232,2.329,236,2.682,272,2.622,275,3.157,283,2.949,313,1.564,322,2.003,323,2.583,342,4.186,366,4.005,398,2.256,401,2.448,421,4.005,438,3.323,471,2.654,479,4.459,491,1.93,543,5.002,548,3.941,552,2.267,614,1.179,702,4.313,703,5.106,757,1.578,828,2.67,904,5.863,909,5.336,937,3.825,1019,2.353,1080,3.563,1108,3.276,1155,5.106,1313,3.722,1374,5.982,1502,5.475,1507,9.605,1607,4.186,1638,3.307,1775,5.336,2096,5.336,2331,4.073,2890,5.215,3035,5.475,3036,5.475,4663,6.404,4664,6.404]],["t/2468",[17,3.857,23,3.258,24,1.58,84,1.057,98,3.429,160,3.421,161,6.643,219,3.021,232,3.55,275,3.021,530,4.364,607,3.74,894,7.376,977,3.559,2886,7.634]],["t/2470",[5,1.994,7,0.642,16,1.054,25,3.339,27,2.475,28,1.623,29,1.975,31,2.33,34,1.442,84,0.613,96,1.994,105,1.771,123,3.25,158,2.83,163,2.244,164,1.555,181,3.208,197,2.523,200,5.166,233,2.049,245,4.864,246,2.141,248,2.658,256,2.118,257,2.883,281,2.679,313,1.383,318,1.983,332,2.938,377,1.266,399,4.279,437,2.658,440,2.497,447,2.679,491,1.707,504,3.432,530,2.532,562,2.781,565,1.849,635,2.054,658,2.711,740,5.2,810,2.514,830,2.588,847,4.515,981,3.813,982,6.231,1035,4.864,1090,2.514,1097,4.351,1196,4.041,1198,4.152,1300,5.976,1479,3.897,2198,4.094,2586,4.611,2587,4.841,2595,3.602,2596,3.634,2606,4.429,2625,3.248,2633,6.786]],["t/2472",[7,0.897,25,3,27,2.702,28,1.73,109,1.58,272,3.836,281,4.433,318,2.281,343,3.333,565,3.059,568,2.683,596,4.22,635,3.399,2529,6.87,2633,7.808,2892,8.011]],["t/2474",[2,1.211,7,0.833,12,1.812,16,1.276,21,1.269,23,2.29,27,2.38,28,1.314,40,2.366,56,0.677,84,1.009,103,1.503,109,1.691,110,2.366,113,1.638,127,3.811,131,2.038,133,1.922,153,2.298,155,2.909,162,2.923,164,1.234,165,1.864,171,1.22,174,2.038,193,1.634,197,2.001,219,2.882,221,1.659,225,1.605,228,2.635,232,1.634,248,2.108,298,2.743,313,1.097,318,1.67,338,1.105,343,1.598,375,2.61,377,1.534,393,2.206,440,1.981,472,2.187,491,1.354,502,2.294,524,2.765,527,1.499,536,2.042,538,3.743,544,2.809,550,1.765,557,2.235,558,2.009,565,3.871,567,2.75,568,2.872,577,3.245,578,1.765,580,1.56,593,2.215,601,2.108,605,1.625,614,0.827,630,1.659,635,1.63,670,2.61,690,2.787,747,1.844,756,3.219,757,2.294,796,4.361,810,1.994,857,2.038,981,3.025,1021,4.668,1144,2.443,1320,3.451,1394,2.857,1504,3.165,1558,2.529,1754,2.915,2081,3.205,2099,2.703,2113,3.092,2133,3.84,2893,3.743,2894,3.743]],["t/2476",[2,1.664,5,2.173,17,2.438,26,3.213,31,1.507,34,1.571,41,2.798,56,0.855,84,0.937,85,2.137,101,3.283,103,2.064,107,2.922,109,2.049,125,3.516,129,3.685,170,2.557,174,1.833,200,3.056,216,3.14,228,2.741,236,1.622,257,3.14,261,4.385,278,2.21,302,2.84,353,1.682,369,3.827,398,2.173,408,3.124,444,2.674,527,2.587,536,2.577,553,3.266,565,2.014,601,2.895,607,3.317,630,2.278,663,3.767,693,3.796,757,1.52,810,2.739,846,3.069,945,3.308,957,3.585,1144,3.355,1220,3.538,1536,4.523,1558,3.473,1754,2.622,2142,3.069,2892,5.274,2895,5.853,2896,5.853,2897,5.853,2898,4.154,2899,5.853]],["t/2478",[4,2.423,27,2.41,28,1.102,31,2.48,84,1.1,96,2.751,109,1.902,123,3.459,125,4.452,138,3.15,164,2.789,171,2.758,174,3.017,175,4.272,179,2.613,278,2.799,297,4.272,347,5.155,377,1.747,416,3.596,544,4.885,568,2.237,607,2.993,614,2.076,630,2.885,635,2.834,818,3.922,1101,6.064,1182,3.725,1504,5.504,2010,3.533,2900,5.26]],["t/2480",[5,2.926,7,0.833,24,1.88,34,2.116,56,0.82,107,2.127,127,2.725,158,4.153,174,2.469,228,2.632,236,2.185,283,2.402,295,3.341,306,3.689,338,2.044,502,2.291,536,3.146,550,3.265,558,3.715,560,4.422,577,4.997,827,4.21,830,3.797,901,4.08,1007,5.38,1035,4.963,1318,6.923,1329,3.825,2816,7.443,2901,7.314]],["t/2482",[1,2.071,2,1.543,5,2.525,7,0.874,16,0.663,18,1.566,19,1.588,20,2.229,22,2.211,24,1.632,26,2.124,28,1.158,31,0.871,33,2.032,46,2.371,49,0.801,56,0.887,84,0.889,85,1.235,90,1.679,92,1.32,103,1.192,105,1.789,109,0.601,115,1.641,121,2.085,125,3.261,132,1.047,140,1.397,164,0.979,165,2.258,174,1.059,179,1.192,189,1.907,190,1.832,197,2.549,220,2.194,224,2.424,225,1.274,226,1.728,233,1.29,243,1.949,244,2.194,245,4.281,257,1.814,281,1.686,283,1.654,289,2.833,292,1.55,302,1.641,320,2.044,343,2.035,359,1.571,360,2.543,364,2.512,366,2.229,376,2.032,386,2.652,395,1.408,408,1.287,430,1.758,437,1.673,471,1.477,498,1.983,502,0.983,505,1.477,509,2.019,527,0.779,536,1.704,550,3.961,565,1.868,566,2.481,574,3.246,593,1.758,596,1.605,600,2.376,622,1.798,628,2.955,630,2.113,632,1.917,636,3.221,655,2.33,672,2.099,719,3.442,732,2.33,751,2.058,757,0.878,770,2.739,796,1.868,807,4.461,811,2.211,814,3.086,816,2.019,820,1.832,826,2.97,828,1.486,830,3.276,831,2.248,856,2.248,858,2.822,910,2.007,939,2.032,985,2.352,1005,2.33,1062,1.714,1065,3.246,1108,1.823,1183,2.453,1202,2.194,1207,2.577,1210,2.788,1253,2.129,1271,1.773,1297,1.728,1304,6.128,1322,4.195,1323,2.453,1329,2.634,1693,2.085,1913,2.007,2258,3.246,2443,6.802,2548,2.97,2631,2.33,2816,6.76,2874,2.97,2902,2.902,2903,2.902,2904,2.577,2906,3.382,2907,3.382,2908,2.902,2909,3.138,5416,6.165,5417,3.84,5418,3.84,5419,3.84,5420,3.84]],["t/2484",[5,2.751,21,2.207,28,1.591,31,1.908,56,1.002,84,0.846,89,2.915,105,2.443,107,2.599,109,1.317,140,3.06,144,3.74,145,3.141,165,1.75,179,2.613,199,3.852,242,5.504,257,3.976,337,3.638,507,4.248,536,2.326,550,4.432,565,2.55,580,3.525,630,2.885,633,6.11,807,3.923,828,3.257,830,3.569,944,3.159,1062,3.756,1101,4.666,1102,4.666,1304,6.678,2403,4.346,2816,7.154]],["t/2486",[2,1.43,5,2.736,7,0.418,26,2.883,27,2.181,28,1.296,31,1.296,84,0.574,85,1.837,92,1.964,93,2.247,98,2.729,103,1.775,106,2.846,107,2.879,109,1.897,115,4.657,123,1.807,138,2.139,163,2.102,164,2.133,165,1.188,174,1.576,179,1.775,182,1.775,233,1.919,236,2.042,242,3.738,254,3.252,257,2.7,272,3.179,278,1.9,289,2.096,302,2.442,316,2.363,317,2.651,318,1.291,322,1.659,324,2.809,341,2.917,353,1.446,385,4.669,416,2.442,430,2.616,444,2.299,458,2.986,468,3.374,489,3.535,490,3.738,502,1.462,522,3.946,524,3.264,527,1.698,550,3.052,576,3.124,611,2.205,613,2.397,658,2.54,693,3.264,709,3.467,737,4.008,755,2.389,774,2.869,798,2.917,910,4.373,916,2.022,1036,4.008,1099,3.402,1100,2.102,1101,5.488,1110,4.008,1115,4.319,1178,3.062,1222,4.076,1494,4.535,2201,4.42,2910,4.831,2911,5.286,2912,5.033,2913,5.033,2914,5.033]],["t/2488",[7,0.761,24,1.171,25,3.09,34,1.842,84,1.045,103,2.42,107,3.09,109,1.627,113,2.637,162,2.542,164,1.987,174,2.15,224,3.064,228,2.291,236,1.902,291,4.26,433,4.176,447,3.422,476,3.504,502,1.994,517,4.384,536,2.154,550,2.842,565,2.361,568,2.764,690,4.487,757,1.782,840,3.281,857,3.281,981,4.871,995,4.024,1076,2.604,1271,4.802,1467,4.524,1781,2.442,1842,5.382,2101,5.768,2253,4.384,2254,3.735,2331,4.601,2928,3.81,3942,4.601,4665,7.233]],["t/2490",[2,2.273,7,0.921,8,5.734,21,2.381,84,0.913,92,3.121,96,3.754,98,2.961,157,4.866,165,1.888,197,3.755,203,2.017,313,2.059,336,3.429,364,5.939,375,6.194,383,5.868,444,3.653,532,4.395,582,7.023,593,4.157,605,3.05,636,4.745,764,5.676,1367,5.315,2577,5.615,4135,7.419,4666,8.428]],["t/2492",[306,4.814,975,6.957,1557,6.726]],["t/2494",[7,0.825,27,1.945,31,2.001,34,2.086,109,1.381,115,3.772,123,2.792,135,4.67,194,6.295,238,4.699,258,7.774,269,7.462,306,5.123,507,5.693,591,5.967,607,3.14,732,5.355,795,5.355,975,6.718,1121,4.966,1206,4.361,1325,6.007,1367,5.167,1477,6.827,1478,7.212,1479,5.639,1932,5.773,2571,8.192,3940,7.212,4668,8.192,4669,7.774,4670,8.192]],["t/2496",[1781,3.73]],["t/2498",[7,0.804,16,2.105,19,3.504,84,0.852,144,3.765,164,2.16,201,5.19,216,4.003,226,4.943,244,4.84,247,3.455,281,3.72,313,2.491,353,2.144,391,3.31,408,3.68,511,2.507,565,3.329,596,3.541,696,5.541,712,4.23,731,4.121,827,3.984,855,4.84,858,3.878,878,8.496,901,5.007,1015,4.664,1390,7.017,1790,8.496,2297,6.552,2840,6.922]],["t/2500",[7,0.78,15,2.739,16,1.397,23,2.507,49,1.688,56,0.742,84,0.814,93,3.182,96,2.645,153,3.842,164,2.718,167,5.771,203,1.798,215,5.588,245,4.486,253,4.519,275,2.325,313,2.417,326,6.735,353,2.048,369,4.659,474,6.116,476,2.872,524,4.623,525,3.539,601,3.525,628,3.879,702,5.058,764,5.058,798,4.131,887,5.006,901,3.688,1110,5.676,1155,5.989,1175,3.458,1296,8.46,1299,5.229,1472,6.841,1535,5.771,1593,7.127,1597,4.737,1843,5.058,5421,8.093,5422,8.093,5423,8.093]],["t/2502",[7,0.944,27,1.675,28,1.34,31,1.724,56,1.059,84,0.765,89,2.634,92,4.433,109,1.19,113,2.574,123,2.405,164,1.939,172,2.445,179,2.362,185,4.858,189,3.777,199,3.482,201,4.833,229,4.187,232,2.567,236,2.496,247,3.102,256,2.641,298,4.311,323,2.847,391,2.971,416,3.25,495,3.408,511,2.25,541,4.345,555,3.339,597,4.903,658,3.38,816,3.999,821,4.915,880,4.658,901,3.466,1019,2.594,1021,4.805,1099,2.614,1202,4.345,1325,5.176,1387,5.335,1479,4.858,2206,7.059,2250,3.179,2421,4.217]],["t/2504",[4,1.63,7,0.845,17,2.077,22,3.261,24,0.851,27,2.39,28,0.742,31,2.46,41,2.385,56,0.519,84,0.569,96,1.851,109,1.698,119,4.788,123,3.115,126,2.538,130,2.996,172,2.672,197,2.342,199,3.805,207,2.145,216,4.653,224,2.227,236,2.403,238,5.243,241,4.112,253,3.163,275,1.627,276,3.822,306,3.426,319,5.256,353,1.433,408,1.897,416,2.42,439,3.436,504,3.186,527,1.687,565,1.716,568,1.505,597,2.715,601,2.467,606,5.928,607,2.015,617,8.552,630,1.941,631,3.436,659,3.404,660,3.911,667,4.988,693,4.749,740,2.376,747,2.158,756,2.467,798,4.243,807,2.031,810,2.334,816,2.978,819,3.704,832,3.54,858,3.805,918,3.373,981,3.54,1093,2.978,1098,4.866,1315,4.627,1371,4.988,1557,3.261,2099,5.5,2190,2.942,2258,4.788,2272,3.911,2434,3.578,2531,5.256,2563,5.256,2637,4.28,2816,3.704,3008,3.66,3833,3.854,5424,5.664,5425,5.664,5426,5.664]],["t/2506",[2,1.207,7,0.866,21,1.264,23,2.283,24,1.107,26,1.661,27,1.062,28,1.311,29,1.561,30,2.799,31,1.093,42,3.281,56,0.675,84,0.741,89,1.67,109,1.78,113,1.632,123,1.525,132,2.008,134,2.226,157,2.583,170,1.322,172,1.55,174,2.762,203,1.071,213,1.821,224,1.896,225,1.599,226,3.316,229,2.654,232,1.627,233,2.475,236,2.632,238,2.567,247,1.966,262,2.925,280,2.846,283,2.4,284,3.013,285,2.825,294,4.465,306,1.987,313,1.093,315,2.125,316,1.994,353,1.865,390,2.16,416,3.822,430,2.207,439,2.925,444,2.964,491,1.349,508,2.636,525,2.108,526,3.236,536,1.333,550,2.687,552,1.584,555,2.117,565,2.233,567,1.794,568,2.377,569,3.826,597,3.532,599,2.3,607,2.621,614,0.824,619,7.048,623,2.143,631,2.925,658,2.143,689,6.228,709,2.925,734,3.026,740,2.023,757,1.103,827,2.267,843,2.447,855,2.754,858,2.207,880,2.953,901,3.358,970,3.826,982,2.257,988,3.344,1070,2.551,1090,1.987,1098,2.822,1099,1.657,1117,2.983,1182,2.134,1371,4.246,2099,2.692,2113,3.08,2250,2.015,2430,2.754,2439,3.5,2476,4.075,2637,3.644,3075,3.939,3296,3.826,5427,4.246,5428,4.246,5429,4.475]],["t/2508",[1781,3.73]],["t/2510",[7,0.624,16,1.473,18,4.5,24,1.838,28,1.117,34,2.016,56,1.011,84,0.857,98,2.782,103,2.649,140,3.102,160,3.59,162,2.782,164,2.175,165,1.773,203,1.895,225,2.829,248,3.715,254,2.545,261,4.011,285,4.231,338,1.947,353,2.159,377,1.77,512,2.729,536,2.357,759,3.979,760,2.339,857,3.591,934,5.051,935,7.21,1394,5.035,3501,7.512,3502,7.512]],["t/2512",[16,1.981,24,1.724,116,3.972,807,4.113,2316,9.101]],["t/2514",[7,0.763,16,2.16,24,1.88,28,1.638,165,2.168,289,3.825,830,4.423,832,6.518,977,3.529,1181,5.663,1303,6.211,2403,5.385,2422,6.211]],["t/2516",[24,1.567,28,1.366,203,2.317,323,3.904,338,2.381,377,2.165,550,3.803,807,3.74,917,5.483,1955,8.065,3503,9.185,3504,8.521,3505,9.185,3506,9.185,3507,8.521,3508,9.185]],["t/2518",[2,1.964,7,0.688,12,1.962,16,1.625,20,3.042,27,2.073,28,1.538,49,1.093,56,0.862,96,2.566,107,2.79,109,1.838,127,2.389,138,1.962,153,3.726,164,1.336,165,1.632,170,2.152,173,5.832,174,2.165,193,3.777,217,2.773,224,2.061,236,1.915,256,1.82,257,2.476,261,2.465,272,1.991,277,2.5,289,2.879,313,1.188,318,1.773,323,3.522,353,1.326,394,2.431,397,2.022,405,1.547,407,3.292,418,2.431,426,1.796,432,4.594,433,2.808,437,2.283,484,2.061,485,3.676,491,1.466,507,2.645,550,4.08,565,2.378,568,2.501,632,5.216,635,1.765,647,1.825,743,4.807,807,1.88,812,4.855,829,2.563,916,1.854,977,1.774,1007,3.15,1009,3.311,1062,2.339,1075,3.818,1094,2.431,1102,2.906,1265,2.55,1285,2.074,1303,3.121,1309,2.827,1467,3.042,1913,2.739,1991,3.242,2569,3.879,3030,3.961,3509,4.616,3510,4.159,3511,4.159]],["t/2520",[2,2.149,16,2.119,18,3.502,28,1.451,56,0.787,96,2.807,97,3.564,105,2.493,107,2.633,132,3.345,145,3.205,170,3.364,193,3.741,313,1.947,318,1.94,338,1.96,405,2.535,507,4.334,550,4.728,596,3.589,989,4.487,995,4.434,1007,5.161,1061,7.259,1062,3.832,1102,4.761,1271,3.966,1467,4.985,3510,6.814,3511,6.814]],["t/2522",[16,1.983,23,2.813,24,1.365,25,2.698,28,1.504,49,1.894,56,1.052,109,1.421,132,2.475,228,2.67,297,4.609,313,2.059,398,2.969,476,2.446,491,2.54,502,2.939,536,2.51,545,3.895,577,5.812,580,2.926,916,3.213,934,4.157,1040,5.147,3512,9.383,3513,7.419,3514,7.419]],["t/2524",[0,3.822,12,2.67,28,0.934,49,1.488,56,1.103,107,1.695,109,1.75,112,5.801,172,4.056,193,4.259,217,3.774,391,2.787,502,1.826,525,3.12,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,934,5.511,944,4.199,1039,7.374,1040,6.824,1075,3.47,1077,3.705,1083,3.21,1175,4.779,1332,4.413,1337,6.059,3515,5.829,3516,9.138,3517,5.829,3518,5.829,3519,5.829]],["t/2526",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,934,4.799,945,2.423,1039,7.685,1040,5.943,1099,4.614,1100,3.856,1112,5.771,3520,6.447,3521,6.447]],["t/2528",[1781,3.73]],["t/2530",[2,1.4,7,0.91,16,0.966,24,1.621,25,1.662,26,1.173,27,1.814,28,1.191,29,1.103,30,1.977,31,2.342,33,1.802,34,0.805,40,2.734,41,1.434,48,1.282,56,0.946,84,0.715,98,2.32,101,1.682,103,1.737,107,1.957,109,1.53,113,1.153,115,3.04,123,3.411,127,2.506,132,0.928,140,1.239,158,1.58,160,1.819,161,3.533,162,2.32,165,1.479,172,1.798,174,2.272,190,1.624,193,1.15,225,1.13,228,1.001,232,1.15,236,1.365,275,2.044,276,2.571,278,1.86,313,1.268,318,1.264,332,1.64,337,2.417,343,1.846,365,1.602,377,0.707,409,2.63,421,1.977,476,1.506,478,1.916,502,1.431,527,2.096,530,1.413,536,2.514,550,3.767,552,1.119,553,1.674,558,2.321,565,3.384,568,2.745,572,1.674,576,1.862,577,2.455,605,1.144,607,2.531,608,1.64,610,2.429,613,2.984,615,1.849,630,3.118,635,1.147,662,1.837,664,2.318,690,1.961,698,2.286,705,1.945,706,2.256,747,1.298,757,2.08,795,2.066,796,2.72,798,1.738,810,1.403,838,1.594,843,1.729,858,1.559,867,2.429,892,1.183,901,1.552,913,2.352,919,2.429,984,1.352,1009,2.151,1071,2.783,1144,1.719,1193,2.201,1289,2.011,1300,1.71,1390,2.175,1404,2.52,1456,2.734,1459,3.392,1462,1.71,1754,2.206,1793,1.993,2035,2.107,2311,2.129,2320,2.634,2421,3.1,2545,2.086,2868,1.719,2936,2.999,2938,4.138,2948,2.879,3376,2.702,3523,3.161,3876,2.783,4338,2.52,5430,3.406,5431,3.406]],["t/2532",[2,1.09,4,1.254,5,1.424,7,0.864,27,2.09,28,0.892,31,1.902,36,1.783,42,2.964,49,2.147,56,0.624,84,1.035,92,2.883,93,3.298,106,2.317,109,1.066,131,1.834,134,2.012,138,3.854,144,1.936,153,2.068,165,1.416,171,1.098,172,3.99,173,3.383,181,2.29,188,2.923,193,1.47,207,1.65,216,2.058,224,2.679,228,1.281,236,1.063,256,1.513,276,3.132,281,1.913,288,2.668,289,2.499,292,1.758,302,1.861,318,0.984,334,2.29,337,1.883,338,1.555,339,3.189,365,2.049,376,2.305,377,1.414,404,1.968,409,2.049,441,2.199,468,2.572,500,2.991,502,1.115,525,1.905,527,0.884,552,1.431,555,1.913,568,2.521,578,1.589,579,2.923,580,1.404,601,4.485,620,3.497,629,2.572,632,2.175,636,3.56,638,2.433,691,4.842,715,2.078,728,2.815,732,2.643,743,2.668,774,2.187,807,1.562,810,4.243,812,5.19,858,1.994,872,2.618,894,3.055,974,2.469,977,1.474,1009,2.752,1070,3.604,1072,1.869,1078,2.923,1079,3.954,1094,2.021,1188,2.572,1271,2.012,1285,3.32,1302,2.451,1303,2.594,1387,4.778,2073,5.259,2190,2.263,2217,3.107,2431,2.885,2561,3.457,2626,4.043,2939,3.457,2940,6,2942,3.682,2943,3.837,3297,3.559,4124,4.043,5432,3.682,5433,4.356]],["t/2534",[2,1.527,7,0.871,27,2.731,28,1.471,30,5.093,31,1.383,84,0.613,92,2.097,101,3.014,109,0.955,126,2.734,138,2.284,153,2.896,162,1.99,163,2.244,171,2.212,172,3.612,173,4.358,174,2.834,184,4.279,193,3.469,196,2.244,224,2.399,228,1.794,246,2.141,282,3.079,304,1.193,313,1.383,351,2.049,407,2.559,416,2.607,478,3.432,502,1.561,525,4.494,552,2.004,568,1.621,578,2.225,597,4.206,599,2.91,614,1.042,620,3.132,631,3.701,632,5.13,638,3.407,658,2.711,675,3.602,725,2.883,741,3.667,807,3.147,819,3.99,830,2.588,831,3.571,832,6.422,977,2.065,985,3.737,1075,4.268,1079,3.541,1093,4.613,1175,2.607,1181,3.313,1188,3.602,1843,3.813,2331,3.602,2391,4.213,2434,3.854,2908,4.611,2945,5.373,2946,4.515,2947,4.718,4675,5.662]],["t/2536",[0,6.416,7,0.71,21,3.14,27,2.86,109,2.032,127,3.644,179,3.718,193,3.277,343,3.205,437,4.229,557,4.483,631,7.264,757,2.22,816,5.104,2484,8.539]],["t/2538",[7,0.754,84,0.774,109,1.614,133,3.056,160,3.784,165,1.6,179,2.391,221,3.535,228,3.032,275,2.963,320,5.491,377,1.598,426,2.639,430,3.524,445,4.506,476,2.778,491,2.154,498,3.975,502,1.97,527,2.36,565,2.333,568,2.741,635,3.473,811,4.432,858,3.524,976,5.316,984,3.056,1036,5.4,1080,3.975,1141,8.579,1202,4.397,1229,5.589,1404,5.697,1987,5.035,2081,6.831,2101,5.697,2479,5.589,2577,3.764,2582,7.145,2948,6.507,2949,6.109]],["t/2540",[7,0.531,21,1.903,28,1.298,31,1.645,33,3.839,37,4.489,42,4.938,46,2.791,56,0.665,84,0.729,90,3.173,92,2.494,93,2.853,106,2.468,107,2.354,109,1.898,115,4.234,126,3.252,153,3.445,165,1.509,189,3.604,216,3.429,224,3.896,272,2.757,318,1.64,353,1.836,407,4.157,426,3.396,502,1.857,513,3.173,620,3.725,629,4.284,635,2.443,658,3.225,769,2.588,858,3.322,984,2.881,1099,4.503,1122,5.929,1162,3.747,1175,3.101,1178,3.888,1180,4.689,1183,4.635,1331,5.902,1406,5.175,1733,4.023,2271,4.806,2385,5.612,2434,4.584,2875,5.011,2911,4.584,2951,3.995,2952,5.268]],["t/2542",[1,2.747,2,1.923,7,0.753,8,2.543,28,0.667,31,1.155,33,4.064,34,1.204,36,2.084,39,1.923,41,2.144,46,1.959,49,1.062,50,2.765,56,0.467,84,0.93,90,3.359,91,3.939,92,3.539,93,3.02,94,5.832,96,2.511,97,2.114,109,0.797,113,1.724,130,2.695,132,1.388,144,2.264,145,2.867,164,1.958,165,1.059,172,2.47,179,1.582,186,2.227,203,1.131,207,1.929,216,2.406,224,2.003,228,1.497,236,1.874,240,2.585,272,1.935,283,2.482,292,2.056,313,1.742,343,1.681,351,1.711,390,2.282,407,2.137,408,1.706,437,2.219,439,3.09,440,3.143,441,2.571,447,2.236,476,2.977,482,3.188,568,2.737,578,1.857,580,1.641,593,2.331,599,5.921,601,2.219,619,4.575,620,3.944,658,2.264,670,2.747,675,3.007,694,2.571,715,4.411,737,3.572,757,1.165,769,1.817,810,2.099,812,3.151,822,2.824,832,3.183,858,2.331,977,1.724,1093,2.678,1094,2.363,1099,1.75,1162,4.775,1175,2.176,1299,3.291,1368,4.575,1737,1.783,2073,2.824,2250,2.129,2271,3.373,2320,3.939,2816,3.331,2953,4.305,2954,3.939,2955,4.486]],["t/2544",[1781,3.73]],["t/2546",[7,0.885,16,1.705,17,2.087,18,4.934,19,4.788,20,3.302,26,2.874,28,1.092,31,1.29,48,2.142,56,0.521,84,0.838,85,1.829,89,1.97,90,2.488,92,1.955,93,2.237,123,1.799,164,2.517,165,1.183,174,1.569,199,2.604,201,2.688,221,1.95,225,3.275,226,4.893,227,3.485,232,1.92,237,3.871,240,2.888,247,2.32,248,2.478,275,1.634,291,3.11,296,2.035,298,4.728,302,4.219,318,1.286,323,2.129,353,1.44,359,2.328,377,1.731,390,2.549,391,2.223,403,3.388,407,2.387,418,2.639,447,2.498,478,3.2,484,2.237,536,1.572,541,3.25,555,2.498,557,2.627,568,1.512,596,2.378,630,1.95,664,3.871,683,1.975,719,3.177,731,2.768,762,3.556,764,3.556,821,3.676,878,4.4,879,4.21,892,1.975,901,3.801,913,3.929,1206,2.81,1224,2.844,1243,4.057,1253,3.154,1265,2.768,1286,4.13,1390,3.634,1400,2.74,1401,3.11,2076,3.068,2250,2.378,2268,3.01,2297,4.4,2640,4.809,5429,5.28,5434,5.689]],["t/2548",[7,0.887,19,3.001,21,1.903,34,1.715,56,0.665,84,0.729,103,2.253,156,4.584,162,3.232,174,2.002,236,1.771,249,5.011,278,2.413,296,2.595,302,3.101,306,2.99,326,4.584,338,1.657,353,2.508,375,3.914,408,2.431,430,3.322,463,7.317,685,3.747,833,3.839,850,6.13,901,6.383,1021,4.584,1313,3.914,1459,4.402,1856,5.758,2089,7.501,2543,6.391,2759,7.489,2760,8.096,2859,6.134,2860,6.391,2861,6.391,2862,5.929,4671,9.197,4672,6.735,4673,6.735,4674,6.735]],["t/2550",[1781,3.73]],["t/2552",[7,0.804,31,1.921,56,0.776,84,0.852,123,2.679,162,2.763,215,5.851,216,4.003,219,2.434,275,2.434,283,2.274,306,3.491,318,1.914,340,5.241,390,3.796,463,4.731,467,6.151,527,1.719,555,3.72,565,2.567,567,3.153,568,2.252,604,5.189,683,2.942,692,6.27,693,4.84,734,3.479,738,6.922,798,4.325,802,3.181,814,3.386,843,4.3,850,5.241,854,5.765,856,4.959,858,3.878,868,7.162,901,3.861,993,3.181,1390,5.412,1480,5.942,2089,5.241,3057,7.462]],["t/2554",[7,0.783,10,4.089,19,3.368,22,4.689,23,2.523,24,1.224,27,1.794,34,2.53,88,3.404,94,4.089,103,2.529,106,3.642,123,2.576,158,3.778,220,4.652,221,2.792,225,2.702,226,3.665,313,1.847,332,3.923,339,3.813,341,4.157,384,6.299,387,5.263,390,3.65,391,3.182,495,3.65,506,4.483,511,2.41,512,2.606,513,3.562,519,6.299,524,4.652,599,3.885,694,5.404,767,5.808,773,7.773,855,4.652,898,4.089,1159,6.028,1323,5.203,1454,6.299,1455,7.173,1456,3.982,1459,4.941]],["t/2556",[7,0.875,24,1.609,26,2.806,28,1.402,34,1.925,41,3.429,46,3.133,47,4.767,56,0.981,90,4.682,92,4.11,93,4.21,94,4.089,96,2.663,97,3.38,132,2.22,186,3.562,216,3.848,272,3.094,283,2.186,353,2.061,418,3.778,491,2.278,509,4.282,568,2.165,580,2.625,597,5.132,599,3.885,601,3.548,611,3.142,614,1.392,619,4.851,620,4.181,715,5.107,901,3.712,1161,6.028,1162,4.206,1368,4.851,3006,6.028]],["t/2558",[7,0.818,16,1.503,27,1.918,28,1.617,31,1.974,34,2.058,84,0.875,88,3.639,89,4.277,123,2.753,145,3.249,174,2.401,232,2.939,244,4.973,254,2.598,278,2.895,283,2.336,289,3.193,294,4.346,306,3.587,318,1.967,484,3.423,485,6.106,512,2.786,558,3.613,575,3.838,780,4.301,857,3.665,896,5.924,897,5.096,942,5.924,993,3.268,1022,4.444,1172,4.861,2250,3.639]],["t/2560",[4,1.944,7,0.795,10,3.39,17,2.477,23,3.643,26,2.327,28,1.235,31,2.463,34,1.596,56,0.619,84,1.092,103,2.097,123,3.436,219,2.709,232,3.969,236,1.648,254,2.015,283,3.156,318,1.526,384,5.222,478,3.799,484,2.655,485,4.736,511,1.998,530,3.913,545,2.896,548,5.386,552,3.863,558,3.913,567,3.509,568,1.795,578,2.463,593,3.091,601,2.942,614,1.154,629,3.986,685,3.487,796,3.285,814,2.699,882,4.997,889,5.708,904,4.136,1172,3.771,1237,4.058,1266,4.371,1330,5.72,1374,4.221,1506,4.022,2223,5.358,2250,2.822,2259,4.177,2426,4.221,2871,4.997]],["t/2562",[2,2.323,7,0.852,24,1.912,28,1.215,165,1.929,246,3.257,248,4.043,283,2.491,285,3.558,313,2.104,397,3.58,512,2.97,536,2.565,565,2.812,567,3.453,568,2.467,757,2.909,759,3.019,760,2.545,843,4.711,1219,3.838,1285,3.673,2017,3.626,2368,6.619,3058,7.583,3059,5.182]],["t/2564",[7,0.852,16,1.603,24,1.75,28,1.215,56,0.85,84,0.933,109,1.822,115,3.966,132,3.174,145,3.464,228,2.728,236,2.265,318,2.097,502,2.375,527,2.362,565,2.812,568,2.467,683,3.223,769,3.31,807,3.328,1062,4.141,1251,6.147,1329,4.975,2311,5.801,2641,6.147]],["t/2566",[7,0.751,16,1.772,27,2.26,31,2.326,40,5.016,56,0.94,107,2.943,109,2.082,123,3.245,163,3.774,527,2.082,597,4.917,635,3.455,658,4.56,814,4.1]],["t/2568",[7,0.789,16,2.203,28,1.671,109,1.688,132,3.478,170,2.957,283,2.894,1271,4.981,1481,5.356]],["t/2570",[23,2.985,24,1.448,25,3.541,28,1.262,49,2.01,56,0.883,98,3.142,132,2.626,286,5.42,337,4.164,339,4.51,491,2.695,502,3.05,513,4.213,544,5.592,558,3.998,565,2.919,577,5.233,757,2.203,795,5.844,796,4.687,1019,3.285,2017,3.764]],["t/2572",[28,1.344,107,2.438,109,2.082,115,4.384,132,2.797,174,2.83,236,2.504,254,3.062,360,6.795,507,5.179,550,3.742,635,3.455,658,4.56,1099,3.526,1102,5.688,3060,9.036]],["t/2574",[2,2.079,7,0.793,28,1.42,56,0.761,109,1.697,126,3.722,132,2.264,138,3.109,172,4.115,190,3.961,193,3.661,224,3.265,288,5.087,289,3.046,313,1.883,337,3.59,338,1.896,339,3.888,408,2.782,491,2.323,552,2.728,565,2.517,568,2.207,611,3.204,614,1.419,632,4.147,694,4.192,755,3.471,806,6.029,810,3.422,819,5.432,830,3.523,872,4.991,977,2.811,1070,4.394,1175,3.549,1302,4.672,2293,4.509,2391,5.735,3061,7.02]],["t/2576",[7,0.789,27,2.376,29,3.492,109,1.688,127,3.282,254,3.218,338,2.462,353,2.729,390,4.833,715,5.144,1400,5.194,1401,5.896]],["t/2578",[7,0.839,58,7.697,275,3.295,564,6.893,1625,8.045]],["t/2580",[7,0.684,13,6.787,84,0.94,283,3.139,294,4.668,300,5.259,332,4.503,395,3.429,399,6.558,476,2.519,542,7.23,543,6.112,742,5.427,885,6.456,916,3.308,977,3.164,1035,5.183,1272,6.667,1282,7.066,1290,5.111,1291,6.667,1292,5.844,1566,6.192,1802,5.259,2598,5.341,3063,6.274,3064,6.558,3065,8.234]],["t/2582",[7,0.872,11,7.13,17,3.534,18,3.929,19,4.928,28,1.262,158,5.528,318,2.177,332,5.74,391,3.764,994,3.85,1090,3.97,1290,5.267,1310,8.463,1314,6.086,1315,7.872,1316,7.872,1317,10.074,2250,4.027]],["t/2584",[7,0.973,135,5.428,174,2.83,233,4.16,236,2.504,278,3.412,363,6.795,444,4.128,484,4.034,578,3.742,1206,5.068,1253,5.688,1263,7.196,1264,6.224,1309,5.534]],["t/2586",[1781,3.73]],["t/2588",[7,0.803,16,1.895,34,2.593,164,2.797,261,5.16,359,4.49,484,4.314,532,5.311,747,4.181,3067,9.663]],["t/2590",[7,0.822,15,2.966,16,1.514,34,2.072,49,2.342,56,0.803,84,0.881,85,2.818,103,2.722,192,4.792,203,2.495,228,2.577,261,5.281,318,2.537,401,3.983,481,4.86,484,3.447,607,3.118,614,1.498,723,2.908,977,2.966,1076,2.929,1224,2.989,1913,4.581,2000,5.088,2013,2.71,2141,4.161,2722,5.007,2898,5.479,3068,7.72,3069,7.72,3070,7.173]],["t/2592",[7,0.915,49,1.867,203,1.989,216,4.23,278,2.977,285,3.432,304,1.751,318,2.975,398,3.721,401,3.176,527,1.816,565,2.713,568,2.379,759,2.912,760,3.121,927,5.483,1019,3.053,1089,5.074,1236,5.538,1297,5.121,1312,5.718,1313,4.828,2142,5.256,2340,5.114,2388,4.355,3071,6.384,4685,7.567]],["t/2594",[7,0.911,19,3.667,21,1.623,24,1.972,28,1.568,34,2.674,49,1.291,84,1.204,85,1.99,103,3.217,105,1.797,132,2.824,162,2.019,165,1.287,196,2.277,203,1.375,228,1.82,232,2.09,236,1.511,254,1.847,261,4.17,285,2.374,372,2.15,377,1.285,397,2.388,398,2.899,471,2.381,527,1.799,530,2.569,536,1.711,565,3.628,568,1.645,580,2.857,613,2.597,630,2.122,757,1.416,759,3.895,760,3.283,857,3.733,916,3.137,1019,2.111,1182,3.925,1285,2.45,1550,4.275,1558,3.235,1842,4.275,2099,4.951,2142,2.859,2250,2.588,2253,3.483,2312,5.233]],["t/2596",[2,2.093,7,0.612,16,1.881,24,1.82,27,1.842,28,1.743,34,2.574,107,2.878,109,2.008,116,2.895,132,2.969,135,4.423,145,3.12,170,2.292,228,3.202,236,2.04,261,5.122,318,1.889,353,2.756,372,2.903,528,4.088,558,3.469,596,3.494,799,5.864,906,5.024,1182,3.7,2250,5.063]],["t/2598",[7,0.768,19,3.732,21,1.664,24,0.954,25,2.682,27,2.52,28,0.831,31,1.439,40,3.103,48,2.389,56,0.582,84,0.907,96,2.074,107,2.496,109,1.966,123,2.007,124,3.714,127,2.747,135,3.357,158,2.944,160,2.936,219,1.823,221,3.093,226,2.856,272,3.429,275,1.823,278,2.11,283,1.703,316,2.624,318,2.731,360,4.203,394,2.944,395,2.327,397,2.448,471,3.471,527,2.321,550,3.291,555,2.786,557,2.931,561,5.185,562,2.892,565,1.923,567,2.361,568,1.687,605,3.031,607,2.257,630,2.175,635,2.137,654,5.185,658,2.82,729,4.203,757,1.451,870,4.607,974,3.597,995,3.277,1141,4.203,1202,3.625,1224,2.164,1289,3.746,1628,4.526,1773,4.451,1781,1.989,2625,3.379,3062,5.036,3072,5.036]],["t/2600",[7,0.689,16,2.029,24,1.415,26,4.049,34,2.226,49,1.965,145,3.515,159,5.827,164,2.401,261,4.43,276,4.33,300,5.299,359,3.854,392,5.423,393,5.356,399,6.607,403,5.61,611,3.634,742,5.467,744,4.983,764,7.345,1010,5.827,1802,5.299,3063,6.321,3064,6.607]],["t/2602",[1,6.551,7,0.889,8,6.064,24,1.493,32,5.768,49,2.073,106,3.38,133,3.945,225,3.296,381,4.363,393,4.529,434,6.147,436,6.581,555,4.363,590,4.961,611,3.834,1212,7.885,2598,5.676,4686,6.862]],["t/2604",[7,0.852,15,3.141,26,3.197,28,1.215,47,5.432,56,0.85,84,0.933,90,4.059,92,3.189,93,4.578,97,3.851,130,4.91,138,3.474,229,5.109,261,4.365,272,3.526,367,4.385,408,3.109,440,3.798,441,4.684,484,3.649,580,2.991,599,4.427,605,3.117,614,1.586,619,5.527,620,4.764,828,3.592,1368,5.527]],["t/2606",[7,0.689,16,1.627,56,0.863,84,0.947,88,3.937,109,1.474,133,3.74,134,4.35,189,5.836,192,5.149,194,6.717,200,4.33,229,5.185,248,4.103,254,2.811,283,2.528,284,5.887,353,2.384,436,6.238,545,4.04,918,5.61,1100,3.464,1123,5.149,1206,4.653,1365,5.047,1400,4.537,1558,4.922,2151,6.971]],["t/2608",[1781,3.73]],["t/2610",[7,0.827,11,3.148,16,0.735,21,1.115,23,1.318,24,1.624,26,1.465,27,1.473,31,2.563,33,2.25,46,1.636,47,2.49,84,0.428,90,1.86,96,1.391,97,1.765,105,1.235,107,1.011,109,0.666,123,3.418,132,1.159,145,1.587,162,1.387,165,0.884,170,1.166,174,1.844,180,3.948,187,2.749,193,1.436,215,4.617,216,2.01,226,3.717,228,1.25,232,1.436,236,1.632,244,2.43,245,3.707,256,1.477,275,1.222,276,4.305,280,2.511,281,1.868,283,2.217,289,1.56,348,2.533,353,2.091,359,1.741,361,4.179,377,0.883,382,2.449,390,4.196,393,1.939,408,1.425,409,2,421,2.469,430,1.947,444,1.711,458,2.223,459,3.088,463,5.229,467,4.854,478,2.393,491,1.19,511,1.258,555,1.868,565,2.503,568,1.777,604,5.06,611,1.641,622,1.991,628,2.039,635,1.432,683,2.869,693,2.43,701,3.948,718,3.375,719,2.375,767,3.033,802,1.597,814,1.7,826,3.29,833,2.25,850,2.631,851,3.29,852,3.596,855,2.43,856,3.914,859,2.049,860,3.375,881,2.469,1022,2.171,1119,3.088,1144,2.147,1145,3.159,1189,3.375,1205,3.375,1206,2.101,1233,3.215,1258,3.375,1270,3.375,1289,2.511,1296,3.375,1313,3.606,1326,3.596,1535,3.033,1597,2.49,1790,3.29,1791,3.375,2089,2.631,2110,3.746,2150,3.948,2250,1.778,2320,3.29,2342,3.948,2403,2.196,2404,2.717,2431,2.817,2529,2.895,2545,2.605,2546,3.215,3074,5.053,3329,3.215,3495,3.746,3496,3.746,3497,3.746,3498,3.746,4575,3.948,4687,3.948,4688,3.948,4689,3.948,4690,3.948,5432,6.983,5435,4.254,5436,4.254,5437,4.254]],["t/2612",[1781,3.73]],["t/2614",[2,1.221,4,1.404,7,0.996,15,3.048,34,1.153,48,1.836,49,2.262,56,0.447,58,6.76,68,1.716,84,0.49,85,1.568,97,2.024,103,2.797,113,1.65,129,2.704,160,1.587,164,1.243,176,5.617,178,3.541,186,3.251,231,3.273,244,2.786,275,1.401,278,1.622,296,1.744,304,0.954,313,1.106,326,3.081,334,4.735,356,4.123,377,1.012,381,2.141,390,2.185,392,2.808,396,3.541,398,1.594,401,4.215,416,3.176,481,2.704,484,1.918,564,2.931,575,4.44,580,1.572,590,2.435,607,1.735,614,0.833,683,1.694,684,4.36,723,2.466,736,1.93,944,2.79,1090,3.063,1100,2.734,1172,2.723,1220,2.597,1328,2.361,1409,2.987,1411,2.931,1538,6.423,1565,5.82,1625,6.317,1923,2.409,2013,1.508,2086,3.319,2142,2.252,2450,3.017,2510,3.772,2586,3.686,2637,3.686,2729,3.368,3070,5.753,3304,4.123,3539,5.898,3540,4.295,3541,4.295,3542,6.546,3543,4.295,3544,4.295,3545,4.295,3546,4.295,3547,4.295,3548,4.295,3549,4.295,3550,4.295,3551,4.295,3552,4.295,3553,3.23,3554,3.23,3555,3.609,3556,4.553,3557,4.295,3558,4.295,4676,4.526,4677,4.295,4678,4.526,4679,4.295,5438,4.877]],["t/2616",[4,2.077,7,0.722,15,3.807,16,1.246,21,1.892,24,1.483,26,2.486,34,1.705,97,2.994,98,2.353,145,2.693,158,3.347,159,6.106,196,2.654,203,1.603,247,2.942,261,3.393,276,3.317,279,2.893,321,5.061,338,1.647,343,2.382,365,3.393,393,3.288,401,3.501,402,5.238,403,4.297,408,3.307,412,4.983,513,3.155,580,2.325,611,2.784,669,5.453,723,2.393,764,4.51,1072,3.095,1308,6.696,1352,4.51,1364,3.347,1376,4.51,1454,5.58,1461,5.453,1511,4.377,1802,7.358,1923,3.564,1994,4.662,2013,2.23,2385,5.58,2778,5.238,3559,5.34,3561,5.725,3562,5.725,3563,5.58,3564,6.354,3565,4.51,4680,6.099,5439,6.696,5440,7.215]],["t/2618",[7,0.895,15,2.886,49,1.779,56,0.782,159,5.276,322,2.476,369,4.911,401,3.026,412,5.89,438,4.108,607,3.925,723,2.829,1090,3.515,1175,3.645,1400,4.108,1601,4.729,1704,8.533,1802,6.207,1923,4.214,2013,3.411,2085,6.446,2141,4.049,2142,3.939,2500,6.969,2722,4.872,2723,5.724,2724,5.724,2726,6.969,2778,6.192,3562,6.768,3567,7.512,3568,7.512,3569,7.512,3571,7.512,3572,7.512,4682,7.916,5439,7.916]],["t/2620",[7,0.92,12,3.936,13,9.125,34,2.485,48,3.959,465,6.795,715,5.016,885,8.68,1243,7.499,1290,5.749,1291,7.499,2096,8.132]],["t/2622",[7,0.725,16,1.253,31,1.645,34,1.715,48,2.732,58,4.87,59,8.376,112,3.336,116,2.513,133,2.881,160,2.361,176,10.135,178,5.268,275,2.085,283,2.659,300,6.348,324,3.566,334,7.178,351,2.437,353,1.836,372,2.52,465,4.689,532,3.512,580,2.338,590,4.947,604,4.445,684,3.512,720,6.134,880,4.445,1090,2.99,1729,5.37,2113,4.635,2956,6.134,3063,4.87,3064,5.09,3573,6.391,3575,6.391,3576,5.09,3577,6.391,3578,9.938,3581,6.391,3582,6.391,3583,8.727,3584,6.391,3585,6.391,3586,6.391,5441,7.257]],["t/2624",[7,0.938,491,3.043,532,6.207,683,3.777,1625,7.63,3587,11.294,3588,9.58,3589,9.58]],["t/2626",[164,2.822,225,3.672,226,4.98,532,5.357,2114,9.043,3590,8.783,3591,9.748,3592,9.748,3593,9.748]],["t/2628",[1781,3.73]],["t/2630",[2,1.058,4,1.216,7,0.9,15,1.43,16,0.73,18,4.138,21,1.108,24,1.402,28,1.694,31,0.958,48,1.591,49,1.387,55,5.439,56,1.127,84,1.083,85,2.138,89,3.732,103,2.554,109,1.287,132,1.813,160,3.301,170,1.159,171,1.065,187,2.73,200,1.943,203,0.939,223,2.208,225,1.402,272,1.605,275,1.911,278,1.405,279,1.694,283,1.134,295,1.577,296,2.379,313,0.958,318,1.503,322,1.226,342,4.035,347,2.588,359,1.729,383,2.73,393,1.926,408,2.228,416,1.805,482,1.753,527,1.35,531,2.377,605,1.419,608,3.203,614,1.405,630,1.448,684,2.045,755,2.78,760,1.159,802,2.497,814,2.658,837,2.377,845,2.494,857,1.779,881,2.453,906,2.539,913,2.918,933,2.221,944,1.586,1008,2.342,1019,2.268,1051,1.901,1072,1.812,1090,3.844,1100,1.554,1193,2.73,1195,2.669,1197,2.669,1199,2.641,1216,3.127,1218,2.563,1220,2.249,1262,3.414,1263,4.665,1288,4.56,1411,2.539,1413,2.195,1500,2.964,1558,2.208,1633,1.848,1793,2.473,2250,4.883,2491,3.127,2545,2.588,3595,1.444,4279,3.721,4786,6.173,4787,3.921,4788,3.921,4789,6.72,4790,3.921,4791,6.173,4792,3.452,4793,3.721]],["t/2632",[7,0.872,28,1.396,55,5.381,84,0.814,89,3.692,96,2.645,140,3.877,160,2.633,165,1.682,169,4.107,171,2.04,179,2.513,213,3.056,232,4.278,275,3.063,283,2.172,317,3.754,351,2.718,491,2.264,531,4.553,593,3.704,614,1.383,622,3.788,736,4.219,802,3.038,828,4.126,1080,4.179,1089,4.587,1266,5.298,1288,4.486,1313,4.365,1693,4.394,2250,3.383,2545,4.957,4789,6.612]],["t/2634",[7,0.95,34,2.295,55,6.044,219,2.789,221,3.328,254,2.897,283,2.605,303,7.048,361,6.068,391,3.793,471,4.605,478,5.461,545,4.164,548,5.546,552,3.189,558,4.029,578,3.541,684,4.699,829,4.747,1237,5.835,1286,7.048]],["t/2636",[7,0.811,16,1.483,28,1.125,33,4.543,56,0.787,84,1.114,109,1.734,134,3.966,165,2.304,245,4.761,247,3.502,320,4.572,353,2.173,366,4.985,405,2.535,418,3.983,426,2.943,432,5.026,471,3.303,509,4.515,566,5.549,628,4.116,757,1.964,859,4.136,865,5.367,896,5.844,983,7.903,987,5.93,1100,3.159,1262,4.408,1336,5.209,1527,5.26,2268,4.543,3011,7.563,3869,7.903,3978,6.641]],["t/2638",[4,2.393,7,0.72,16,0.978,21,1.485,24,0.851,28,1.089,49,1.734,55,5.833,56,0.903,84,0.569,85,3.166,107,2.34,109,1.301,139,4.813,160,1.843,162,2.711,165,1.728,170,3.169,196,2.083,203,1.847,246,1.988,272,2.152,279,2.271,283,1.52,285,2.172,313,1.284,322,1.644,326,3.578,338,1.293,353,1.433,359,2.318,416,2.42,429,4.494,430,2.593,471,2.178,482,2.35,517,3.186,527,2.202,580,2.679,608,2.728,614,1.42,623,2.517,628,2.715,703,4.191,760,3.312,790,3.66,802,2.126,831,3.315,873,3.618,944,3.697,945,3.332,994,2.263,1038,3.578,1072,3.566,1100,3.058,1108,2.689,1218,3.436,1262,2.908,1288,4.609,1305,4.788,1539,3.504,1540,2.908,1959,4.803,2010,1.829,2198,3.801,2904,3.801,3595,1.936,4785,3.854,4794,7.321]],["t/2640",[34,2.386,109,1.919,283,2.709,295,3.768,338,2.305,376,5.341,408,3.382,471,3.883,491,2.824,527,2.048,613,4.235,614,2.095,732,6.124,802,3.79,1307,6.524,2010,3.962,2211,7.472]],["t/2642",[2,2.189,7,0.75,9,3.649,10,4.389,21,1.592,28,0.795,41,2.556,48,2.286,49,2.581,55,4.414,56,0.939,84,0.61,105,2.538,109,1.368,113,2.959,131,2.556,132,1.655,160,3.334,163,2.233,165,1.262,179,1.885,190,2.896,200,2.792,211,4.193,225,2.014,236,1.482,256,2.108,296,2.171,304,1.187,313,1.377,351,2.039,353,2.213,359,3.578,393,2.767,416,2.595,418,2.816,471,2.335,476,1.636,505,2.335,524,3.468,531,3.416,611,2.342,614,1.494,677,2.969,742,5.076,747,3.332,795,3.683,802,3.283,805,3.836,840,2.556,846,1.73,873,3.878,896,4.132,994,2.426,1218,3.683,1307,7.245,1364,2.816,1394,3.584,1475,3.031,1540,4.489,1566,4.021,1632,2.924,1633,2.655,2010,3.998,2426,3.795,2444,3.756,2510,4.696,2904,4.075,3071,4.33,3490,4.961,4795,5.635]],["t/2644",[5,3.126,23,2.962,34,2.26,68,3.364,84,0.961,169,4.853,239,5.09,289,4.35,338,2.183,353,2.42,367,4.518,418,5.502,471,3.677,484,3.759,511,3.509,512,3.059,513,4.181,536,2.643,608,4.605,807,4.254,892,3.32,1094,4.435,2344,7.587]],["t/2646",[7,0.669,56,0.838,84,0.919,140,3.326,157,4.901,160,2.976,278,3.042,297,4.642,376,4.839,608,5.557,611,3.529,614,2.267,732,5.548,828,3.54,846,2.606,859,4.405,865,5.717,1038,5.778,1262,4.695,1288,5.071,1306,8.538,1307,5.91,2010,2.954,2352,6.058,3008,5.91,3596,8.055,3884,8.055,4796,8.489]],["t/2648",[2,2.487,7,0.727,24,1.493,56,0.911,103,3.086,233,3.338,318,2.245,353,2.515,471,3.822,580,3.202,790,6.421,802,3.73,846,2.831,916,4.297,944,3.73,1037,4.589,1089,5.633,1306,7.354,2268,5.257,3836,8.119,4797,9.223]],["t/2650",[1781,3.73]],["t/2652",[2,1.549,7,0.649,16,1.069,18,4.882,20,3.593,28,1.568,41,2.606,55,3.125,56,1.142,105,1.797,132,2.417,160,2.014,179,1.922,182,1.922,190,2.953,213,2.338,225,2.054,275,2.547,313,1.404,351,2.079,353,1.567,372,2.15,397,2.388,405,1.828,426,3.039,470,4.155,651,3.83,684,2.996,747,2.359,760,1.697,802,3.329,805,3.911,807,3.18,814,3.544,831,3.623,906,5.329,933,3.255,966,1.567,971,4.213,1015,3.408,1029,5.745,1030,5.745,1090,3.654,1182,2.74,1195,3.911,1197,3.911,1199,3.869,1257,4.494,1265,5.04,1283,4.581,1288,3.432,1375,3.483,1398,3.954,1754,2.442,1988,2.403,2246,6.562,2250,5.207,2268,3.275,2289,5.73,2292,7.809,2444,3.83,4377,4.912,4841,5.745,5442,8.867,5443,6.191,5444,6.191]],["t/2654",[7,0.91,15,3.5,46,4.787,55,6.282,56,0.948,89,3.582,179,3.212,306,4.262,513,4.524,715,4.934,873,6.607,874,8.744,2289,8.042]],["t/2656",[2,1.527,4,1.756,19,2.523,21,2.301,28,0.799,56,0.804,84,1.13,103,1.895,133,2.422,160,3.656,171,1.538,177,3.932,187,3.942,213,2.304,219,3.561,275,1.753,282,3.079,292,2.463,304,1.193,318,2.953,336,2.304,382,5.052,391,2.384,398,2.868,436,4.041,471,3.375,472,3.965,516,5.662,531,5.781,608,2.938,623,2.711,636,3.188,659,5.273,684,2.953,736,5.172,757,2.007,855,3.485,906,3.667,916,3.104,988,2.769,1051,3.948,1090,2.514,1269,5.157,1288,4.864,1309,3.291,1399,3.407,1500,4.279,1527,3.737,1638,2.924,2017,2.384,2031,3.03,2087,5.373,2289,3.942,2339,3.382,2348,5.373,2434,3.854,2438,5.373,4168,5.373,4400,6.963,5445,6.101,5446,6.101,5447,6.101]],["t/2658",[289,3.355,318,2.067,502,2.341,536,2.528,608,6.912,614,2.387,707,4.723,807,4.137,892,3.176,1094,4.243,1306,6.769,3629,8.489,3635,8.489,3654,8.489,4134,7.732,4789,7.473,4793,8.055,4794,8.055,5448,9.147,5449,9.147]],["t/2660",[7,0.88,41,4.119,55,4.938,105,2.84,183,3.897,213,3.695,277,4.667,580,3.153,684,4.735,865,6.115,1206,6.436,1262,5.023,1268,6.399,1399,5.463,1597,5.726,2086,6.658,2289,7.774,2584,6.399,2950,6.977,5450,9.784]],["t/2662",[4,0.33,7,0.812,12,0.429,15,1.014,16,1.313,17,0.42,18,1.805,21,0.559,23,1.163,24,1.19,25,0.634,27,0.66,28,1.143,29,0.97,31,1.14,34,1.431,46,0.44,47,0.67,48,0.431,49,1.439,56,1.106,68,2.427,84,0.693,85,1.79,96,0.979,97,0.475,100,0.475,101,0.566,103,2.017,105,0.87,107,1.727,108,0.549,109,1.498,112,1.377,113,0.387,116,0.397,123,1.914,124,0.67,127,1.694,132,0.312,133,0.455,138,0.429,140,0.776,145,0.796,158,2.332,160,1.44,161,3.176,164,1.129,165,0.623,169,0.581,170,0.821,171,0.289,174,0.316,177,0.513,193,1.011,196,0.785,200,0.981,203,0.834,213,1.131,219,0.613,221,0.392,224,0.839,225,0.38,228,0.627,236,0.731,246,0.402,247,0.467,248,2.829,249,0.791,253,0.639,254,0.637,261,0.538,272,0.435,275,1.078,278,0.709,285,1.149,286,0.644,287,0.758,296,0.409,298,0.649,304,0.417,305,0.676,313,0.484,316,0.882,317,0.531,318,1,322,1.09,338,0.487,343,0.704,348,0.682,351,1.006,353,0.758,359,0.469,366,0.665,377,1.155,382,0.659,387,0.74,388,0.768,390,1.342,395,1.624,398,0.374,401,3.007,405,0.338,407,0.48,410,1.307,418,0.99,421,2.179,437,0.499,438,1.028,470,0.768,471,2.328,476,1.011,479,0.74,481,0.635,482,0.475,484,0.839,491,1.557,498,0.591,502,0.767,512,0.959,525,0.501,527,0.898,528,1.043,530,2.863,532,1.033,557,0.529,562,2.291,564,0.688,565,1.138,568,1.609,572,0.563,575,0.941,580,0.369,600,0.708,605,0.385,607,1.066,613,0.48,614,0.641,621,1.429,630,1.517,635,1.264,653,0.623,655,0.695,670,0.618,683,1.04,684,0.554,712,1.065,723,1.668,736,0.453,743,1.307,747,0.813,756,0.499,757,0.859,759,1.44,760,2.456,769,0.761,784,0.779,807,0.765,813,0.688,814,0.853,828,0.443,829,0.56,843,1.52,846,0.326,857,0.898,870,0.831,876,1.753,890,0.847,901,0.972,906,1.282,916,0.405,933,0.602,934,1.718,939,0.606,944,0.43,945,0.352,966,0.29,974,1.698,988,0.968,993,0.43,994,0.853,1005,0.695,1009,0.723,1010,1.32,1015,1.649,1019,0.728,1062,0.511,1080,0.591,1090,0.472,1094,0.99,1100,0.421,1108,0.544,1113,2.932,1123,1.166,1160,1.612,1172,0.639,1202,0.654,1206,0.566,1207,0.768,1220,0.61,1221,0.74,1224,0.39,1225,0.791,1245,0.865,1246,2.216,1264,0.695,1265,3.16,1271,0.529,1285,0.453,1297,0.515,1328,1.033,1329,0.489,1332,0.708,1347,3.912,1365,1.143,1400,3.5,1405,0.688,1411,0.688,1456,1.043,1523,1.008,1530,0.639,1540,0.588,1565,1.379,1566,1.413,1567,0.817,1579,0.885,1628,0.817,1632,1.443,1633,0.501,1638,0.549,1687,0.885,1754,0.842,1759,1.363,1781,0.359,1794,0.968,1796,0.909,1797,0.847,1842,0.791,1844,3.173,1853,1.65,1876,0.968,1893,1.693,1913,0.598,1931,0.768,1955,0.885,1959,0.944,1987,0.749,1988,0.444,2000,3.514,2010,0.689,2013,1.368,2017,1.17,2031,0.569,2073,1.183,2076,0.618,2081,0.758,2092,0.768,2128,0.731,2131,0.935,2141,1.782,2227,1.379,2228,1.379,2249,0.935,2250,0.892,2263,1.612,2268,0.606,2293,0.622,2338,0.584,2340,0.654,2346,0.885,2370,1.008,2388,0.557,2565,0.885,2623,1.008,2705,1.008,2722,2.144,2723,2.519,2724,2.01,2729,1.473,2744,2.101,2745,1.549,2799,1.452,2833,1.693,2898,0.716,2960,0.665,3070,0.731,3556,1.307,3620,0.865,3919,0.935,3972,1.294,3987,2.448,4016,1.452,4072,3.617,4185,3.067,4700,1.063,4701,1.063,4702,2.78,4703,2.78,4704,2.78,4705,2.78,4706,2.78,4707,3.484,4708,3.484,4709,3.484,4710,3.484,4711,3.484,4712,2.78,4713,2.78,4714,2.78,4715,2.78,4716,2.78,4717,3.484,4718,3.484,4719,3.484,4720,3.484,4721,3.484,4722,1.063,4723,1.063,4724,1.063,4726,1.063,4727,1.063,4728,1.98,4729,3.484,4730,3.484,4731,0.968,4732,1.063,4733,0.968,4734,1.063,4735,1.063,4736,1.063,4737,1.98,4738,1.063,4739,1.693,4740,1.063,4741,0.968,4742,0.968,4743,1.063,4745,1.063,4746,1.98,4747,1.063,4748,1.063,4749,1.063,4750,1.063,4751,1.98,4752,2.78,4753,1.98,4754,1.063,4755,1.98,4756,1.98,4757,1.98,4758,1.98,4759,1.063,4760,1.063,4761,1.063,4762,1.063,4763,1.063,4764,1.063,4765,1.803,4766,1.063,4767,1.98,4768,1.063,4769,1.063,4770,1.063,4771,1.063,4772,1.063,4773,1.063,4774,1.063,4775,1.063,4776,1.063,4777,1.063,4778,1.063,4779,1.063,4780,1.063,4781,1.063,4782,1.063,4783,1.063,4784,1.063,4785,1.452,5184,1.008,5451,1.145,5452,1.145,5453,1.145]],["t/2664",[1781,3.73]],["t/2666",[24,1.696,28,0.95,41,3.055,56,1.163,99,4.247,105,2.106,107,2.881,109,1.986,127,2.208,139,3.022,160,3.224,165,1.509,170,1.99,172,2.333,182,2.253,193,3.345,203,1.612,217,3.839,318,2.239,338,1.657,343,2.395,351,2.437,377,1.506,502,2.536,536,2.006,550,2.646,565,2.199,635,2.443,725,3.429,759,2.361,994,2.9,1076,2.425,1099,3.405,1101,4.023,1112,3.995,1219,3.001,1285,3.921,1354,4.635,1508,5.087,1959,4.386,2277,7.053,3021,7.663,3024,5.37,4574,5.37,4691,5.484]],["t/2668",[0,2.115,1,2.129,2,0.988,7,0.576,12,1.478,21,1.035,28,1.03,34,0.933,56,1.285,84,0.397,96,1.291,105,1.146,107,1.498,109,2.042,123,1.249,124,2.311,127,1.202,133,1.567,144,1.755,160,3.404,170,1.083,171,0.995,172,1.269,197,1.633,198,1.784,213,1.491,228,1.161,277,1.883,279,1.583,291,2.158,292,1.594,295,2.352,297,2.004,302,1.687,313,0.895,318,0.892,322,1.146,338,2.507,343,3.454,365,1.857,377,1.308,395,1.448,397,1.523,426,1.353,430,1.807,437,1.72,502,1.01,513,1.727,527,0.801,557,1.823,591,3.334,605,1.326,614,0.675,628,1.892,647,4.093,712,1.971,747,2.998,765,2.173,801,2.726,833,2.089,838,1.848,936,2.468,940,2.984,994,3.144,1037,5.071,1099,1.357,1100,1.452,1103,3.226,1178,2.115,1180,4.073,1235,4.289,1354,5.737,1462,1.982,1508,5.039,1693,2.144,1931,4.23,1959,4.861,2010,1.275,2256,2.494,2277,8.06,2904,2.65,2951,3.47,3021,8.092,3023,2.726,3024,5.822,3968,4.353,3971,2.726,3972,2.395,4333,3.226,4691,5.945,4692,2.866,4693,3.664,4694,3.226,4695,3.337]],["t/2670",[5,2.645,24,1.792,28,1.06,31,1.835,56,0.977,68,2.848,84,0.814,95,4.659,112,3.721,165,2.216,203,1.798,236,1.975,256,2.81,275,3.063,279,3.245,291,4.424,313,1.835,317,3.754,367,3.824,397,3.122,517,4.553,536,2.947,568,2.151,759,3.468,760,2.923,769,4.252,774,4.062,807,2.902,814,3.234,963,4.553,1329,4.556,1365,4.336,1508,6.121,1733,4.486,2577,3.957,3890,6.612,4692,5.875,4696,6.422]],["t/2672",[7,0.758,21,1.623,24,1.799,28,1.568,56,0.949,84,1.041,109,1.388,138,2.317,160,3.68,164,1.578,165,1.843,174,1.708,182,1.922,228,1.82,233,2.079,236,2.164,289,4.149,292,2.499,314,6.562,405,1.828,407,3.72,505,2.381,511,2.623,527,2.102,536,2.863,557,4.095,635,2.084,759,2.014,760,3.102,769,2.208,778,3.623,807,3.18,814,3.544,963,4.988,994,3.544,1062,3.957,1089,5.026,1285,4.1,1329,3.789,1399,4.951,1508,5.807,1557,3.564,2277,7.07,2588,5.379,2594,6.545,2898,3.869]],["t/2674",[2,2.121,56,1.225,84,1.104,96,3.985,107,2.013,109,2.091,130,4.482,160,3.574,164,2.16,196,3.116,313,1.921,338,1.934,377,1.758,395,3.107,647,3.825,653,2.475,712,4.23,846,2.414,994,3.386,1037,5.63,1235,7.476,1959,3.75,2010,2.737,2268,4.482,2277,7.62,4694,6.922]],["t/2676",[56,1.311,109,1.674,338,2.441,1037,4.938,1959,4.733,2277,6.684,3023,8.767,4697,9.039]],["t/2678",[2,2.358,12,3.525,21,2.47,56,1.308,96,3.079,109,1.839,291,5.149,313,2.136,338,2.683,653,2.751,1037,5.427,1142,5.769,1959,5.201,2277,7.345,3021,7.284,3023,6.504,3858,4.537,4697,7.962]],["t/2680",[28,1.518,29,2.983,56,1.302,109,1.442,302,3.937,324,5.696,338,2.646,395,3.379,628,4.416,712,4.6,724,5.237,994,3.682,1037,4.255,1235,6.27,1354,5.885,1959,4.078,2076,4.969,2277,5.758,2387,6.688,3024,6.818,3893,6.362,4691,6.963]],["t/2682",[2,1.915,7,0.56,56,0.941,84,0.769,107,2.441,109,1.939,127,2.329,139,4.829,170,3.649,172,2.46,182,2.376,198,3.458,203,1.7,246,2.685,253,4.273,313,1.735,338,2.829,351,2.57,392,4.406,502,1.958,527,2.084,536,2.115,583,4.337,759,2.489,760,3.398,820,3.65,994,4.634,1076,2.557,1099,2.629,1354,4.888,1508,3.928,1754,4.052,1959,3.387,2191,4.687,2259,4.734,2525,6.252,3024,5.663,4691,5.783]],["t/2684",[5,2.788,24,1.282,28,1.117,31,1.934,56,1.011,68,3.001,84,0.857,95,4.911,107,2.027,112,3.921,165,2.294,170,2.339,256,2.962,275,3.17,279,3.42,291,4.663,313,1.934,317,3.957,367,4.03,397,3.291,517,4.798,536,2.357,568,2.267,759,3.59,760,3.025,769,4.362,774,4.282,963,4.798,1062,3.806,1365,4.57,1508,5.664,1733,4.729,2577,4.17,4692,6.192,4696,6.768]],["t/2686",[7,0.734,21,1.936,28,1.491,56,0.919,84,1.008,107,1.754,109,1.156,138,2.764,160,3.704,164,1.882,165,2.085,170,2.749,174,2.037,203,1.64,228,2.171,233,2.48,236,1.802,289,2.708,292,2.981,405,2.18,505,2.84,511,3.369,527,2.31,536,2.041,635,2.486,759,2.402,760,3.122,769,2.633,772,4.522,778,4.322,807,2.648,814,2.951,963,4.154,1062,3.295,1175,3.155,1285,4.506,1329,3.155,1508,5.148,2277,6.267,2594,7.193,2898,4.615,2901,6.033,4698,9.306,4699,6.853]],["t/2688",[1781,3.73]],["t/2690",[2,1.557,7,0.651,24,1.704,29,2.014,49,1.298,56,1.204,84,0.894,107,1.478,109,1.774,139,2.591,140,2.262,170,2.44,179,1.932,182,1.932,203,1.382,236,1.518,254,1.856,275,1.787,296,2.225,313,1.41,322,1.806,377,1.291,381,3.907,395,2.282,418,2.886,484,2.446,512,1.991,517,3.5,536,1.719,578,2.269,601,2.71,614,1.063,628,2.982,634,6.346,647,3.098,683,2.16,731,3.026,759,2.024,840,2.619,846,1.772,892,2.16,939,3.291,993,2.335,1400,5.461,1462,3.123,1605,4.233,1693,3.378,1754,2.454,1849,6.054,2010,3.875,2013,1.923,2254,4.979,2656,7.499,3025,6.794,3376,4.936,4798,8.781,4799,5.259]],["t/2692",[7,0.337,12,1.726,21,1.209,24,1.589,28,1.281,29,1.493,56,1.176,84,0.716,107,2.858,109,2.039,162,1.504,165,1.48,182,2.211,217,2.439,246,1.618,254,3.155,285,1.768,313,1.045,338,1.625,353,1.167,377,1.478,381,2.024,395,1.691,433,2.47,446,2.722,491,1.29,505,1.773,517,2.594,527,2.505,536,1.968,565,1.397,580,1.486,634,3.288,653,1.347,683,1.601,705,5.587,759,2.316,846,1.314,859,2.221,944,3.265,945,3.003,966,1.802,993,1.731,994,1.842,1037,4.517,1224,1.572,1394,2.722,1400,5.091,1735,2.979,1737,2.492,1765,2.454,1793,2.699,1849,4.937,1988,1.79,2010,1.489,2190,2.395,2252,3.184,2254,3.412,2382,3.234,2388,5.142,2625,2.454,2656,5.227,3025,4.656,3376,3.658,4420,3.234,4798,6.018,4799,6.018,4800,4.279,4801,3.897,4802,8.072,4803,6.607,4804,4.279,4805,4.279,4806,4.279,4807,6.607,4808,4.279,4809,6.607,4810,4.279,4811,4.279,4812,4.279]],["t/2694",[109,1.717,127,3.339,179,3.407,246,3.851,580,3.536,994,4.385,2388,5.337,2656,7.746,3025,7.176]],["t/2696",[109,1.717,172,3.527,179,3.407,246,3.851,580,3.536,994,4.385,2388,5.337,2656,7.746,3025,7.176]],["t/2698",[109,1.717,179,3.407,246,3.851,580,3.536,994,4.385,1099,3.77,2388,5.337,2656,7.746,3025,7.176]],["t/2700",[1781,3.73]],["t/2702",[2,0.893,7,0.892,16,1.003,24,1.584,28,0.467,34,2.362,48,1.344,49,1.767,56,1.041,84,0.739,85,2.723,96,1.167,97,1.481,105,1.036,109,0.559,113,1.208,123,1.129,140,1.298,164,0.91,170,2.015,171,2.136,174,0.984,193,1.205,203,1.633,213,1.348,219,1.025,223,4.872,232,1.205,246,1.253,254,3.144,275,1.025,295,2.743,296,1.276,304,1.657,313,0.809,322,2.901,337,1.543,338,1.326,343,1.178,372,2.018,377,0.741,381,1.567,398,1.167,401,1.266,430,1.634,449,2.039,461,5.192,476,0.962,482,1.481,491,0.998,505,1.373,508,1.951,525,2.541,527,0.724,528,2.841,567,1.328,580,2.368,613,2.437,614,1.708,622,1.671,628,1.711,707,3.795,715,2.772,740,1.497,810,1.471,830,2.464,843,1.812,846,2.094,888,1.745,897,2.089,901,1.627,970,2.832,982,1.671,1076,2.456,1094,1.656,1117,7.026,1172,3.245,1210,4.218,1266,1.436,1271,2.683,1297,1.606,1353,2.186,1539,3.594,1700,2.465,1735,2.306,1737,2.034,1799,2.465,1991,2.208,2013,1.103,2017,1.394,2031,1.772,2343,4.494,2350,2.429,2362,7.377,2388,1.736,2403,1.843,2430,2.039,2656,2.145,2952,4.218,3059,1.993,3595,2.512,3701,4.611,3702,4.611,3703,2.832,3704,3.143,3705,3.143,3706,3.143,3707,4.611,3708,4.611,3709,3.143,3710,4.611,3711,4.611,3712,4.611,3713,3.143,3714,3.143,3715,3.143,3716,3.143,3718,3.143,3719,2.832,4813,4.912,4814,4.912,4815,5.393]],["t/2704",[7,0.528,21,1.892,24,1.084,28,1.293,31,2.551,56,1.199,76,4.983,232,3.332,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,846,2.812,966,3.54,1090,4.067,2017,3.856,3059,5.512,3720,6.354,3721,6.354,3723,9.884,3728,6.923,3729,6.354,3730,5.725,3731,5.061,3732,5.061,3733,5.061,3734,5.061,3735,6.354,3736,6.354,3737,5.061,3738,5.453,4816,11.227]],["t/2706",[7,0.6,21,2.15,28,1.408,31,2.72,56,1.244,76,5.661,109,1.683,170,2.248,461,6.616,527,2.182,572,5.284,590,4.093,607,4.53,966,3.223,3723,8.785,3728,7.542,3731,5.75,3732,5.75,3733,5.75,3734,5.75,3737,5.75,3738,6.195,4817,7.608,4818,7.608,4819,6.505,4820,7.608,4821,7.608]],["t/2708",[7,0.528,21,1.892,28,1.293,31,2.551,56,1.199,76,4.983,109,1.545,381,3.168,405,2.914,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,807,3.54,916,3.492,966,3.54,1177,6.816,1689,4.802,3728,6.923,3730,5.725,3731,5.061,3732,5.061,3733,5.061,3734,5.061,3737,5.061,3738,5.453,3739,6.354,3740,6.354,3741,8.012,3742,7.039,3743,6.354,3744,6.354,3745,5.58]],["t/2710",[7,0.57,21,2.044,28,1.362,31,2.653,56,1.226,76,5.382,109,1.627,126,4.659,193,2.631,332,5.008,461,6.485,527,2.11,572,5.11,590,3.891,607,4.441,966,3.512,3728,7.293,3731,5.466,3732,5.466,3733,5.466,3734,5.466,3737,5.466,3742,7.416,3745,6.027,4819,6.184,4822,7.233,4823,7.233,4824,7.233,4825,7.233]],["t/2712",[7,0.474,21,1.698,31,2.408,56,1.191,76,4.472,174,1.786,233,2.175,236,2.234,318,1.463,461,5.997,504,3.643,509,3.405,527,1.858,572,4.5,590,3.233,607,4.106,653,2.675,740,2.717,887,5.664,910,4.784,966,3.714,982,4.286,2617,6.529,2622,7.265,2625,3.448,3728,6.422,3731,4.542,3732,4.542,3733,4.542,3734,4.542,3737,4.542,3745,5.008,3838,6.145,4819,5.139,4826,6.01,4827,6.01,4828,7.739,4829,8.497,4830,8.497,4831,6.01,4832,6.01]],["t/2714",[7,0.313,21,1.123,24,0.644,26,1.475,28,0.561,31,1.881,37,4.158,56,1.346,95,5.412,106,2.822,107,1.018,109,1.052,112,1.969,127,2.525,137,4.003,163,1.575,170,1.174,179,1.33,193,1.446,242,2.801,267,3.398,381,2.951,391,1.673,397,2.593,405,2.45,440,1.752,444,2.704,461,3.492,471,3.191,502,1.096,511,1.267,514,3.004,527,2.072,572,5.019,635,1.442,740,1.796,757,0.979,784,7.375,813,4.04,829,4.057,830,1.816,887,2.649,982,2.005,1062,1.911,1072,2.883,1100,1.575,1177,5.73,1196,2.836,1300,2.15,1437,5.82,1462,2.15,2031,2.127,2384,2.705,2951,2.357,2952,4.88,3014,3.398,3066,2.914,3249,3.236,3576,3.004,3746,5.92,3747,3.772,3748,3.772,3749,3.772,3750,3.772,3751,3.772,3752,3.772,3753,7.014,3754,7.308,3755,7.308,3756,3.772,3757,3.772,3758,3.772,3759,3.772,3760,3.772,3762,3.772,3763,2.528,3764,3.772,3765,3.772,3766,5.92,3767,3.772,3768,3.772,3769,3.772,3770,3.772,3771,3.054,3772,3.772,3773,3.772,3774,3.772,3775,3.772,3776,3.772,3777,3.772,3778,3.772,3779,3.772,3780,3.772,3781,3.772,4833,3.974,4834,3.974,4835,3.974,4836,3.974,4837,3.974]],["t/2716",[7,0.413,9,2.069,15,3.325,16,1.432,21,0.903,24,0.848,28,0.739,34,0.814,49,0.718,56,1.366,97,1.429,107,1.97,171,0.868,186,1.506,203,1.253,228,1.012,237,2.343,281,1.512,322,2.656,377,0.715,393,1.569,527,0.699,605,1.156,683,1.195,715,3.419,784,2.343,822,1.909,843,1.747,846,1.607,890,7.679,1072,1.477,1083,5.687,1099,1.183,1117,6.419,1219,1.424,1352,2.152,1409,2.109,1567,2.455,1577,3.957,1597,2.015,1700,2.377,1802,4.033,1994,2.225,2013,1.064,2045,2.91,2085,2.602,2127,3.032,2419,2.662,2420,2.732,2577,1.683,3531,2.225,3701,2.732,3702,2.732,3703,2.732,3707,2.732,3708,2.732,3710,2.732,3711,2.732,3712,2.732,3763,3.331,3782,3.032,3784,3.032,3785,3.032,3786,9.139,3787,9.139,3788,9.139,3789,9.139,3790,9.139,3791,8.478,3792,3.032,3793,9.139,3794,8.234,3795,7.301,3796,3.032,3797,3.032,3798,3.032,3799,6.313,3800,3.032,3801,3.032,3802,3.032,4813,2.91,4814,2.91,4838,5.236]],["t/2718",[7,0.87,22,5.532,39,2.62,46,4.239,49,1.447,84,0.697,85,2.23,98,3.133,100,3.987,112,3.19,126,3.109,160,2.257,171,1.749,174,1.914,201,3.278,202,6.137,207,2.628,213,2.62,233,2.33,316,2.869,337,2.999,351,2.33,404,3.135,408,2.324,461,3.604,476,1.869,480,4.383,482,2.879,506,5.289,511,2.053,525,4.202,550,4.713,643,5.865,657,5.037,706,4.595,779,7.111,1117,7.995,1175,2.965,1219,2.869,1733,3.846,1893,8.746,1991,4.292,2350,4.721,2362,4.383,2421,5.327,2430,5.488,2448,4.027,2951,3.819,3143,5.037,3803,6.11,3804,4.483]],["t/2720",[1,3.701,39,2.592,84,0.959,106,2.334,126,4.273,131,4.614,137,4.087,182,2.131,278,2.282,315,4.831,341,3.503,343,3.148,381,3.013,403,4.087,434,5.899,445,4.017,476,3.352,506,5.249,590,3.426,611,4.228,653,3.763,706,6.315,1008,3.805,1098,4.017,1113,4.545,1117,7.698,1179,4.245,1219,3.943,1224,3.737,1689,3.338,2362,4.335,2430,3.92,2448,3.983,2452,8.953,2598,3.92,2904,4.606,3118,7.374,3143,8.597,3398,4.606,3805,4.739,3806,6.044,3807,6.044,3808,6.044,4686,4.739,5454,6.863]],["t/2722",[7,0.808,16,0.57,25,0.98,28,0.713,29,1.764,31,0.748,34,0.78,40,3.402,41,1.389,46,1.269,49,1.136,56,1.112,84,0.812,85,1.751,92,2.776,93,2.142,95,3.136,101,1.63,107,1.294,109,1.982,121,1.791,131,1.389,160,1.073,162,1.076,163,2.559,164,0.841,165,1.447,171,2.254,174,2.228,179,1.024,190,2.598,197,1.364,203,0.733,213,1.246,224,4.34,228,0.97,233,3.421,240,1.674,244,1.884,254,0.985,256,1.146,257,1.559,277,1.574,279,2.184,281,2.392,288,2.021,289,1.21,318,0.745,322,0.958,323,2.039,343,1.089,347,2.021,351,1.108,377,0.685,393,1.504,398,1.078,401,1.17,405,0.974,433,1.768,437,1.437,471,1.269,472,2.462,476,0.889,481,1.829,484,1.297,492,2.395,500,1.448,502,1.394,506,1.816,513,1.443,536,1.923,550,4.717,565,2.448,577,1.448,579,2.214,593,1.51,607,1.173,634,2.353,635,1.111,647,1.149,650,2.062,653,0.964,672,1.803,694,2.75,704,2.695,706,2.185,747,1.257,748,2.314,749,1.983,757,0.754,772,3.337,818,2.538,829,2.663,830,1.399,840,1.389,846,0.94,917,1.734,937,1.829,974,1.87,982,2.55,985,2.021,1019,2.373,1072,2.337,1079,3.162,1099,2.776,1100,1.213,1102,1.829,1122,2.695,1174,2.062,1175,1.41,1225,2.278,1314,2.084,1365,1.768,1633,1.443,1988,1.281,2010,2.247,2013,1.02,2034,2.905,2210,3.955,2350,2.245,2434,2.084,2448,1.915,2500,2.695,2548,2.551,2577,1.613,2631,2.001,2874,2.551,2908,2.493,3001,3.062,3763,1.948,3858,1.589,4339,2.695,4601,5.056,5427,7.114,5428,7.875,5455,5.448,5456,3.299,5457,5.448,5458,2.789,5459,3.062,5460,5.448,5461,5.448,5462,3.299,5463,3.299,5464,3.299,5465,3.299,5466,3.299,5467,3.299]],["t/2724",[1781,3.73]],["t/2726",[4,1.541,21,1.403,26,1.844,28,0.701,29,1.733,49,1.663,56,1.312,84,0.801,85,2.563,105,2.314,109,2.015,113,1.811,140,1.946,144,2.379,155,3.216,160,1.741,165,1.113,174,1.476,192,2.926,219,2.29,225,2.645,254,1.597,272,2.033,295,1.997,302,2.287,313,1.213,322,1.554,353,1.354,377,1.977,395,1.963,405,1.58,426,1.834,447,2.35,463,2.989,517,3.011,614,1.628,705,6.03,755,2.237,801,3.696,824,3.642,827,2.517,846,3.216,933,2.814,934,3.649,993,3.576,1037,5.466,1162,2.764,1332,3.311,1633,2.341,1638,2.565,1693,2.906,1753,3.419,1952,4.714,1959,2.369,1978,3.817,2010,3.96,2214,3.216,2341,4.045,2388,2.604,4850,4.045,4851,4.967,4852,7.399,4853,7.399,4854,6.739,4855,7.399,4856,4.967,4857,4.967,4858,4.967]],["t/2728",[7,0.6,12,3.071,16,0.961,49,1.711,56,0.986,84,0.825,85,3.134,109,2.124,113,1.883,162,1.814,164,1.419,165,1.157,174,1.535,182,2.548,199,2.547,219,1.598,246,1.953,257,2.629,297,2.824,313,1.262,323,2.083,336,2.101,338,1.873,391,2.174,405,2.422,426,1.907,447,3.603,484,2.188,491,2.727,508,3.042,613,3.442,614,0.951,660,3.842,705,7.663,846,2.777,879,4.118,937,4.549,983,3.968,993,4.307,1037,4.968,1072,2.387,1100,2.047,1331,4.887,1399,3.107,1506,3.314,1753,3.554,1754,3.236,1978,3.968,2010,3.877,2026,3.595,2214,3.344,2388,5.581,2729,3.842,2876,4.118,2990,3.154,3023,3.842,3869,3.968,3873,4.703,3970,3.968,4695,4.703,4801,4.703,4859,5.164,4860,4.703,4861,5.164]],["t/2730",[2,1.881,48,2.83,56,1.313,109,2.118,113,2.543,186,3.287,283,2.017,377,1.56,437,3.274,527,1.525,611,2.899,705,7.563,846,2.891,993,2.821,1037,6.114,2200,5.964,2388,5.589,2402,3.486,2515,5.562,4854,8.578,4862,6.975,4863,6.975,4864,6.975,4865,6.975,4866,6.975,4867,6.975,4868,6.975,4869,6.975,4870,6.975,4871,6.975,4872,6.975,4873,6.975]],["t/2732",[7,0.733,12,3.749,84,1.007,101,4.948,109,1.91,228,2.945,233,3.364,509,5.266,578,3.653,580,3.228,634,7.143,702,6.26,705,6.972,892,3.478,2388,4.872,2656,7.335,3025,7.982]],["t/2734",[1781,3.73]],["t/2736",[7,0.675,12,3.452,16,1.13,24,1.743,25,1.944,27,2.694,28,0.857,31,1.483,36,3.774,39,3.483,50,3.553,56,1.12,70,4.333,84,0.658,98,2.134,101,3.232,102,3.83,105,1.899,106,2.225,113,2.214,121,3.553,131,2.755,165,1.917,171,2.325,197,2.706,201,3.091,240,3.321,304,1.28,339,3.063,353,1.656,377,1.914,394,3.035,404,2.957,407,2.745,408,2.192,416,2.796,502,1.674,505,2.516,508,3.577,579,4.391,614,1.118,630,2.243,734,2.687,752,4.945,779,4.842,787,4.945,840,2.755,875,4.945,1174,5.764,1633,2.861,1689,3.183,2013,2.023,2099,3.654,2331,5.445,2450,4.048,2672,4.391,3297,5.346,4850,4.945,4903,11.348,4904,6.073,4905,8.559,4906,5.531,4907,6.073]],["t/2738",[24,1.542,27,2.26,28,1.344,84,1.031,162,3.346,225,3.403,232,3.463,322,2.978,524,5.861,614,1.753,650,7.741,723,3.403,855,5.861,2928,6.055,3595,3.508,4908,9.522]],["t/2740",[7,0.751,21,2.691,27,2.26,84,1.031,100,4.258,106,4.212,169,5.207,232,3.463,304,2.007,404,4.636,507,5.179,614,1.753,752,7.754,756,4.469,1671,5.237,4045,7.754,4906,8.673]],["t/2742",[1781,3.73]],["t/2744",[1,2.975,2,2.04,7,0.784,17,2.023,31,1.848,49,2.023,56,1.227,92,1.895,121,4.426,133,2.19,165,1.147,182,3.011,183,2.197,221,1.89,233,2.738,236,1.346,246,2.861,315,2.431,318,1.246,322,1.601,338,2.732,377,1.145,398,2.665,408,2.731,422,3.484,426,1.89,447,2.422,502,1.411,511,1.632,579,3.701,590,2.754,611,2.128,630,1.89,751,2.955,757,1.864,760,2.235,838,2.582,908,2.606,979,3.412,988,2.503,1010,7.071,1076,2.724,1083,2.482,1094,2.558,1220,2.936,1328,2.67,1377,5.63,1718,3.412,1730,4.168,1734,4.857,1737,1.93,2031,2.739,2339,3.058,2450,5.999,2621,3.701,2631,3.346,2996,4.662,3397,5.918,3809,7.514,3895,4.168,3918,3.256,4149,4.662,4692,4.004,5468,5.516,5469,5.516,5470,5.516,5471,5.516,5472,9,5473,9,5474,5.516,5475,9,5476,5.516,5477,5.516,5478,5.516,5479,4.857]],["t/2746",[2,2.393,7,0.699,49,1.994,56,1.27,84,0.961,92,4.076,94,4.8,105,2.776,121,5.191,171,2.991,174,2.637,196,3.517,313,2.168,322,2.776,408,3.203,590,4.774,715,4.561,1998,5.505,2013,2.956,3466,6.04,5480,9.562]],["t/2748",[29,3.714,318,2.592,395,4.207,471,4.411,1224,3.911]],["t/2750",[4,2.577,36,3.663,84,0.9,100,3.715,121,6.18,228,2.632,236,2.777,304,1.751,338,2.044,398,4.091,653,2.615,730,6.765,751,6.705,944,3.361,966,2.266,1010,5.538,1076,2.992,1118,4.904,1224,3.053,1671,6.387,2625,4.766,3397,6.499,3809,6.279,5479,11.02]],["t/2752",[4,2.523,16,1.514,26,3.02,31,2.546,84,0.881,182,2.722,292,3.539,338,2.829,414,4.667,653,2.561,662,4.728,760,2.404,846,2.498,979,5.423,1010,5.423,1076,2.929,1077,4.553,1118,4.401,1224,2.989,1671,6.324,1765,4.667,2190,4.553,2472,7.755,3397,8.152,3809,6.148,4298,7.41,4692,6.364,5472,8.136,5473,8.136,5475,8.136,5479,7.72]],["t/2754",[1781,3.73]],["t/2756",[7,0.804,15,2.867,34,2.002,48,3.19,56,0.776,58,5.686,84,1.104,164,2.16,196,3.116,203,1.882,233,3.69,275,2.434,295,3.162,381,3.72,398,2.77,416,3.62,575,4.843,580,2.73,590,4.23,613,3.554,684,5.901,723,2.81,794,5.475,944,3.181,1310,5.541,1328,4.101,1411,5.092,1538,7.834,1565,5.475,1625,5.942,2086,5.765,3046,7.462,3539,8.717,4677,7.462,4679,7.462,5481,8.473]],["t/2758",[7,0.884,15,3.337,56,0.904,84,0.991,96,3.223,159,6.099,233,4.06,412,6.809,1090,4.063,1314,6.229,1352,6.163,1802,5.547,1923,4.871,1994,6.371,2013,3.048,2778,7.158,3559,8.946,3561,7.824,4680,8.335,5482,9.86]],["t/2760",[84,1.204,159,8.032,233,4.022,292,3.919,351,3.261,621,4.631,802,3.645,885,6.704,1090,4.001,1180,6.273,1290,5.307,1291,6.923,1410,6.809,1802,5.461,3559,7.185,3565,7.484,3677,8.55,3678,8.55,3679,7.508,5458,8.207]],["t/2762",[7,0.789,300,6.067,580,3.475,684,5.22,3064,7.565,3681,8.558,3682,8.558,3683,8.558,3687,9.498,5483,10.009,5484,10.785,5485,10.785]],["t/2764",[7,0.769,233,4.222,300,5.916,532,5.09,580,3.389,684,5.09,3063,7.057,3681,8.344,3682,8.344,3683,8.344,3691,9.261,5483,9.76,5486,10.516,5487,10.516]],["t/2766",[7,0.731,16,1.268,34,1.735,49,2.083,56,0.915,85,3.649,96,2.4,158,3.405,174,2.755,198,3.317,203,2.219,233,4.092,254,2.191,292,2.963,296,2.625,332,3.536,398,3.265,408,2.459,491,2.794,513,3.21,525,3.21,532,3.553,614,1.254,653,2.144,769,2.618,857,3.091,888,3.589,1076,2.453,1090,4.116,1310,8.798,1398,4.689,1402,4.334,1632,4.81,1754,2.896,1913,3.836,2013,2.269,2387,5.329,2388,3.571,3672,7.925,3693,6.465,3694,6.465,3695,6.465,3696,6.205,3697,6.465,3698,5.998]],["t/2768",[1781,3.73]],["t/2770",[7,0.724,15,2.451,21,1.236,24,1.867,29,1.526,34,1.114,56,1.24,84,0.474,89,2.508,98,1.537,103,2.249,105,1.368,108,2.259,139,1.963,160,1.533,171,1.188,182,2.738,203,1.047,219,1.354,221,3.022,232,2.977,254,1.407,318,1.636,338,1.076,353,1.193,377,1.503,395,2.656,398,1.541,410,2.887,426,1.616,437,2.053,471,1.813,476,2.376,491,2.026,502,1.206,512,2.822,527,2.167,528,2.305,536,1.303,564,2.833,607,2.576,630,1.616,653,1.377,747,1.796,757,1.078,759,2.869,760,2.418,769,1.681,846,2.063,957,4.756,1072,2.022,1225,3.255,1328,4.79,1405,7.281,1456,2.305,1759,3.011,1849,4.451,1923,2.328,2013,1.457,2017,2.829,2031,4.379,2141,2.238,2228,3.046,2293,2.559,2338,2.406,3070,3.011,3936,3.851,4016,3.208,4054,5.473,4608,6.664,4874,4.375,4875,4.375,4876,8.184,4877,4.375,4878,6.721,4879,4.375,4880,4.375,4881,4.375,4882,4.375,4883,4.375,4884,6.121,4885,4.375,4886,4.375,4887,4.375,4888,4.375,4889,4.375,5488,4.714]],["t/2772",[4,1.241,24,1.86,28,1.584,31,1.532,39,2.552,68,1.517,84,0.433,88,1.802,89,2.34,107,1.606,109,1.476,115,2.888,170,1.182,182,1.339,224,2.657,232,2.281,236,1.649,254,2.815,332,3.255,398,1.409,414,3.598,426,1.478,444,2.719,512,2.162,527,2.205,614,1.857,630,2.316,647,3.567,653,3.319,694,2.176,757,0.986,760,2.98,803,2.253,846,3.351,945,3.146,966,3.061,1051,1.94,1076,4.284,1090,1.777,1118,5.094,1138,3.989,1219,2.795,1224,3.875,1375,2.426,1849,3.411,2254,2.067,2402,3.135,2656,4.061,2843,5.952,3025,2.82,3668,4.906,3838,4.535,3841,5.209,4884,3.645,4890,4.002,4891,6.272,4892,4.002,4893,4.002,4894,4.002,4895,6.272,4896,4.002,4897,3.024,4898,6.272,4899,4.002,4900,6.272]],["t/2774",[4,2.176,7,0.843,39,2.855,48,2.847,56,0.934,84,0.76,109,1.804,116,2.618,165,1.572,171,2.906,197,3.126,233,3.871,236,1.845,322,2.958,480,4.776,482,3.138,580,2.436,614,1.969,747,2.881,838,3.539,937,4.191,1098,4.425,1426,5.392,1633,3.306,2010,3.723,2013,2.337,2452,6.177,2598,5.82,2607,8.973,3398,6.838,3595,2.585,4051,6.391,5458,6.391,5489,9.456,5490,9.456,5491,9.456,5492,9.456,5493,10.189,5494,10.189]],["t/2776",[1781,3.73]],["t/2778",[1,2.249,7,0.678,16,1.137,17,1.529,24,1.392,25,1.239,27,0.918,28,0.862,31,2.1,36,3.337,46,1.604,48,1.57,49,0.87,50,2.264,56,1.232,89,1.444,109,1.873,115,2.813,121,2.264,123,2.579,130,2.206,131,2.771,132,1.137,138,1.561,169,2.116,170,1.143,172,1.34,174,2.555,201,3.11,207,1.579,219,1.198,224,3.206,233,2.211,262,2.529,287,4.359,299,2.483,315,1.838,318,2.421,338,1.862,339,3.081,353,1.055,372,1.448,381,2.89,403,2.483,408,1.397,415,2.727,416,2.813,506,2.295,527,2.174,530,1.73,550,2.4,565,1.263,578,1.521,599,1.989,658,1.853,688,3.672,705,2.382,718,3.308,731,2.028,740,2.761,756,2.867,759,1.356,760,2.236,805,2.634,822,2.311,892,1.448,908,1.97,910,2.179,944,1.565,945,2.504,982,3.081,993,1.565,994,2.63,1099,1.433,1112,2.295,1117,2.579,1138,2.461,1145,1.97,1193,2.694,1202,2.382,1208,3.151,1219,1.724,1221,2.694,1231,3.224,1300,2.093,1314,2.634,1400,2.008,1401,2.279,1423,2.798,1426,4.694,1475,2.082,1476,3.224,1500,4.617,1508,2.14,1528,3.672,1906,3.672,1978,2.973,2099,2.328,2258,3.524,2361,3.027,2382,2.924,2402,1.934,2421,2.311,2430,2.382,2594,4.158,2607,5.797,2641,2.761,2649,3.672,2656,2.506,2960,2.42,3025,2.727,3331,3.87,3665,3.672,3666,3.308,3667,3.407,3669,3.308,3670,3.308,4901,3.87,5427,3.672,5428,3.672,5495,4.17]],["t/2780",[4,2.589,7,0.766,15,2.137,18,2.575,28,0.827,31,1.432,56,0.824,68,2.222,170,1.731,233,3.518,236,1.541,237,4.297,247,2.575,254,1.884,275,1.814,281,2.772,342,3.83,397,2.436,398,4.099,525,2.761,535,5.337,540,5.01,580,2.898,623,2.806,658,2.806,684,3.056,686,4.584,696,4.13,724,2.853,741,3.795,757,1.444,769,3.736,796,3.072,798,3.223,814,2.523,846,1.799,887,3.906,916,2.234,984,2.507,1090,2.602,1202,3.607,1224,2.153,1253,3.501,1258,5.01,1297,2.841,1310,7.465,1336,3.83,1367,3.696,1500,4.429,1527,3.867,1557,3.636,1733,3.501,1806,4.13,2427,5.159,2545,3.867,2631,3.83,3674,5.561,3696,7.602,4266,5.337,5496,6.315,5497,6.315,5498,6.315,5499,10.475,5500,6.315,5501,10.475,5502,6.315,5503,6.315,5504,6.315,5505,6.315,5506,6.315,5507,6.315,5508,6.315,5509,6.315]],["t/2782",[7,0.941,15,2.31,16,1.179,18,2.784,24,1.026,36,2.793,39,2.578,46,2.625,84,0.686,85,2.194,86,4.312,95,3.93,96,2.231,108,3.271,131,2.874,145,2.547,162,2.226,174,1.883,228,2.007,233,4.43,240,3.464,247,3.874,278,2.27,283,1.832,313,1.548,318,1.542,338,1.558,377,1.417,393,3.111,398,2.231,408,3.183,504,3.84,505,3.654,576,3.731,578,2.489,590,3.408,683,2.37,757,1.561,833,3.611,856,3.995,888,3.337,908,3.225,939,3.611,977,2.31,1090,2.813,1175,4.669,1310,7.727,1314,4.312,1402,4.741,1538,4.868,1829,4.955,2014,5.279,2427,5.577,2428,6.011,3672,7.538,3673,8.367,4902,6.335]],["t/2784",[1781,3.73]],["t/2786",[7,0.624,27,2.431,31,1.934,34,2.016,106,2.901,112,3.921,131,3.591,138,3.192,172,2.742,189,4.236,190,4.068,191,5.724,193,2.879,198,3.854,219,3.17,233,2.865,278,2.836,295,3.183,318,2.493,341,4.354,353,2.159,377,1.77,477,4.695,491,2.386,498,4.404,499,5.512,500,3.745,504,4.798,512,2.729,774,4.282,1174,5.331,1175,3.645,1369,6.083,1375,4.798,1376,5.331,1419,6.192,1428,6.596,3818,5.983,3819,7.512,3820,7.512]],["t/2788",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1376,6.465,3818,7.255]],["t/2790",[7,0.757,106,3.518,131,4.355,138,3.872,190,4.934,219,2.971,233,3.474,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,512,3.31,536,2.859,1376,6.465,3818,7.255]],["t/2792",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1376,6.465,3818,7.255]],["t/2794",[7,0.832,139,4.734,233,3.818,341,5.802,759,3.698,1425,6.131]],["t/2796",[7,0.782,24,1.607,88,4.47,206,6.427,233,3.592,341,5.458,552,3.513,567,3.979,723,3.547,1266,4.302,1271,4.938,3821,11.181]],["t/2798",[7,0.929,29,3.462,219,3.072,233,3.592,315,4.714,318,2.416,395,3.922,1225,7.385,1314,6.756,1688,6.756,2444,6.615,3296,8.486]],["t/2800",[34,2.817,42,6.556,96,3.149,127,2.932,155,5.79,253,5.38,283,2.585,318,2.177,353,2.438,377,2,381,4.23,472,4.354,502,2.465,530,3.998,698,6.466,1175,4.117,1220,5.129,1424,6.758,1868,7.645,2091,6.871,2092,6.466,2093,6.086,2094,7.998,3387,6.758]],["t/2802",[7,0.751,27,2.26,34,2.425,102,6.005,172,3.298,283,2.753,318,2.318,530,4.258,1107,7.196,1175,4.384,1424,7.196,1557,5.907,2091,7.317,2092,6.885,2093,6.481,2094,8.311,3822,9.036]],["t/2804",[7,0.669,29,2.961,31,2.074,39,3.454,46,3.518,50,6.264,84,0.919,98,2.983,105,2.655,123,2.893,131,3.851,207,3.465,233,3.072,248,3.984,254,2.73,278,3.042,313,2.074,318,2.067,447,4.016,572,4.495,578,3.336,580,2.947,814,3.655,1112,5.035,1115,6.912,1132,3.769,1400,4.405,1401,5,2322,6.316,2631,5.548,3670,9.155]],["t/2806",[6,5.235,7,0.685,21,2.455,26,3.226,28,1.362,31,1.179,41,2.188,48,1.957,56,0.858,84,1.046,92,1.786,93,3.067,107,2.225,109,1.465,123,1.644,125,2.75,138,1.946,153,2.468,163,1.912,165,1.946,171,1.967,172,3.01,173,2.581,174,2.152,179,3.231,190,3.721,199,2.379,207,2.955,224,2.044,233,1.746,236,1.903,289,2.861,296,1.859,304,1.017,318,1.175,322,1.509,339,2.433,351,1.746,437,2.264,491,1.454,502,1.996,505,3.601,578,1.896,583,2.946,596,2.173,599,3.721,613,2.181,614,1.778,620,2.668,658,2.31,691,2.653,702,3.249,724,2.349,725,3.685,757,2.379,812,3.216,888,2.541,940,5.895,977,2.64,1035,2.882,1070,2.75,1086,4.394,1099,3.831,1112,4.294,1142,3.184,1179,3.216,1219,2.15,1302,2.924,1368,3.096,1737,1.819,2013,1.607,2190,2.7,2272,3.59,2369,4.394,2388,2.529,2431,3.443,2484,3.707,2546,3.928,3563,6.032,3595,1.777,3669,7.429,3699,6.87,3700,4.578]],["t/2808",[1781,3.73]],["t/2810",[2,2.106,24,1.265,84,0.846,221,2.885,233,2.827,313,1.908,343,2.778,401,2.985,527,1.708,530,3.493,560,4.157,612,5.058,653,2.458,677,4.115,683,2.922,760,3.332,876,4.926,908,3.976,944,3.159,963,4.734,976,5.812,988,3.819,1019,2.87,1090,3.468,1144,4.248,1217,5.903,1328,5.294,1400,5.853,1566,5.574,1718,5.206,2031,4.179,2128,5.376,2361,7.941,2621,5.648,3771,6.002,3845,7.412,3846,7.412,3847,6.678,3848,7.412]],["t/2812",[1781,3.73]],["t/2814",[2,1.727,5,2.256,7,0.868,49,1.439,56,1.007,138,2.583,165,1.435,171,1.74,203,1.533,207,3.626,246,3.857,254,3.721,256,3.324,275,1.982,276,4.401,288,4.226,304,1.349,313,1.564,315,4.219,337,2.982,338,1.575,343,2.278,416,2.949,476,1.859,491,1.93,513,3.018,691,4.886,707,3.563,715,3.291,735,4.696,760,3.419,769,2.461,774,5.517,810,3.944,830,2.927,977,2.335,1079,4.005,1107,4.84,1110,4.84,1213,4.631,1285,2.731,1367,4.039,2073,5.306,2306,5.215,2594,4.359,2868,3.483,2951,3.798,3823,7.298,3824,5.475,3825,5.475]],["t/2816",[7,0.646,31,2.001,84,0.887,92,3.033,93,3.471,116,3.057,139,3.676,171,2.226,174,2.435,207,3.344,233,2.965,254,2.634,272,3.354,276,4.058,283,2.369,287,5.846,304,2.206,318,1.995,343,2.914,527,1.791,653,2.579,734,3.625,759,2.872,760,3.409,810,3.637,838,4.132,945,2.71,1219,3.65,2868,4.455,3668,6.408,3823,7.667,3826,6.827]],["t/2818",[4,1.748,5,3.35,7,0.64,21,2.293,26,2.092,46,2.335,49,2.339,56,1.169,85,1.952,90,2.655,92,4.696,94,5.144,105,3.449,116,2.103,133,2.411,171,1.531,174,2.412,203,1.943,236,1.482,254,1.812,275,1.744,278,2.019,283,1.629,290,4.696,337,2.624,353,1.537,359,2.485,365,2.855,405,1.793,476,2.356,496,3.442,498,3.135,505,2.335,513,2.655,531,3.416,578,2.214,601,2.645,605,2.937,614,1.037,769,2.166,774,3.048,824,4.132,845,3.584,976,4.193,1080,3.135,1108,2.882,1162,3.135,1364,2.816,1998,5.035,2594,3.836,2868,3.065,2951,3.342,2990,3.442,3466,5.524,3816,4.259,3823,5.998,3827,6.939,3828,6.133,3829,6.939,3830,4.021,3831,3.649,3832,4.818,3833,4.132]],["t/2820",[49,2.01,56,1.328,171,2.429,283,2.585,297,4.89,476,2.595,810,5.571,2611,6.556,2868,4.863,3531,6.226,3823,5.639,3827,7.645,3828,8.359,3829,7.645,3834,6.556]],["t/2823",[4,1.588,5,1.803,7,0.596,24,0.829,31,2.199,39,2.083,46,2.121,56,0.982,84,0.975,115,2.357,116,1.91,138,2.064,139,3.395,165,1.147,182,1.713,203,1.811,207,2.089,221,1.89,233,1.852,236,1.346,254,2.433,256,1.915,275,1.584,318,2.191,325,3.564,338,1.259,377,1.692,398,1.803,414,4.34,444,3.28,461,2.865,476,1.486,479,3.564,508,3.015,527,1.119,605,1.852,614,0.942,647,1.92,653,2.381,724,2.492,757,1.261,759,3.485,760,3.281,769,3.459,774,4.092,810,2.273,830,2.339,846,1.571,908,2.606,945,2.503,966,1.396,1037,2.547,1051,2.482,1070,2.918,1100,2.029,1118,2.162,1181,2.995,1224,1.881,1328,2.67,1364,2.558,1375,3.103,1456,2.697,1793,3.228,1932,3.607,1988,2.141,2017,3.185,2031,2.739,2141,2.618,2338,2.815,2402,3.782,2577,2.697,2594,5.15,2625,2.936,2951,3.036,3616,4.657,3668,5.918,3823,4.772,3835,4.377,3836,4.506,3837,7.18,3838,5.471,3839,7.5,3840,4.082,3841,3.447,5510,5.516]],["t/2826",[4,2.317,5,1.772,7,0.397,10,2.721,16,0.936,31,2.791,39,2.047,46,2.085,56,0.973,84,1.068,138,2.029,139,2.258,165,1.127,182,1.683,203,1.788,207,2.053,221,1.858,236,1.323,254,1.618,256,1.882,275,1.557,287,3.59,302,2.316,318,1.819,325,3.503,338,1.238,377,1.671,398,1.772,414,5.111,461,2.816,476,1.46,527,2.303,568,1.441,592,3.228,614,0.926,647,1.887,653,2.351,757,1.24,759,3.456,760,2.913,769,3.424,774,4.04,802,2.035,810,2.234,830,2.299,840,3.389,846,1.544,908,2.561,944,2.035,945,1.665,966,1.372,1037,2.503,1051,2.439,1070,2.868,1100,1.994,1118,2.125,1219,3.97,1224,1.848,1328,2.624,1364,2.515,1375,3.049,1456,2.65,1601,3.005,1793,3.173,1913,2.833,1988,2.104,2017,3.144,2031,2.692,2141,2.573,2338,2.767,2402,3.733,2577,2.65,2594,5.084,2625,2.886,2951,2.984,3616,4.597,3823,4.711,3826,6.224,3840,4.012,3841,5.03,3842,7.088,3843,7.088,3844,4.774,5511,5.421]],["t/2828",[1781,3.73]],["t/2830",[7,0.441,12,1.399,16,0.646,24,1.308,28,1.14,31,1.368,36,3.104,39,1.412,49,1.258,56,1.333,84,0.606,86,3.811,98,1.219,99,2.188,100,2.503,106,2.052,107,1.802,109,0.585,112,1.719,113,2.041,121,6.435,123,1.908,127,1.836,132,1.019,133,1.484,137,5.183,145,1.395,163,1.375,164,0.953,165,0.777,170,1.654,171,0.942,172,1.202,179,1.161,197,1.546,207,1.416,228,2.23,246,1.312,278,1.243,289,1.371,296,1.337,304,1.483,322,1.085,323,1.399,336,1.412,338,1.377,372,1.298,381,2.649,405,2.239,408,3.745,409,1.758,433,3.232,495,1.675,505,1.438,508,2.043,527,0.758,577,2.649,578,1.363,605,1.255,614,0.639,630,1.281,635,1.259,723,3.563,749,3.625,751,5.756,757,0.855,760,1.654,838,1.75,855,2.135,916,2.134,937,3.344,977,1.265,993,3.267,1072,1.603,1099,1.285,1100,1.375,1182,1.654,1185,2.825,1236,2.312,1251,2.476,1285,1.479,1302,2.103,1329,3.241,2013,1.156,2041,3.77,2193,3.16,2250,2.521,2257,2.415,2421,2.072,2448,4.402,2864,2.714,2911,2.361,3398,2.509,3601,4.86,3809,8.661,3810,3.502,3811,2.666,3814,2.891,3854,2.766,4686,2.581,5512,3.738]],["t/2832",[7,0.365,21,1.309,24,1.137,28,0.991,36,3.739,49,1.041,56,1.339,84,0.502,86,3.154,96,1.632,100,3.14,107,1.186,112,2.295,113,1.69,121,7.287,127,2.303,137,2.973,140,1.816,171,1.259,172,1.605,177,2.237,199,3.464,203,1.109,207,3.461,233,1.677,246,1.752,285,1.914,304,1.48,316,3.129,322,1.449,372,1.734,376,2.641,381,2.192,408,4.232,437,2.175,508,2.729,527,2.223,577,3.322,578,1.821,596,2.087,614,0.853,653,2.669,723,1.656,732,3.029,751,5.463,757,1.142,760,1.369,816,2.625,828,1.932,859,2.405,916,3.232,945,2.806,1099,1.716,1329,2.133,1988,1.938,2013,1.543,2041,3.121,2276,2.658,2421,2.768,2911,3.154,3398,3.351,3601,5.102,3809,7.684,3811,3.561,3814,3.861,3854,3.695,5513,4.993]],["t/2834",[2,1.409,7,0.792,10,1.724,21,0.9,24,0.846,28,0.45,32,1.993,36,2.304,39,1.297,46,1.321,49,1.905,56,1.306,84,0.345,86,2.169,90,2.462,92,4.34,93,3.592,94,1.724,96,1.122,97,2.336,99,2.01,100,2.336,107,0.816,112,1.579,113,1.162,121,6.858,127,1.713,132,0.936,137,2.045,140,1.249,145,1.281,162,1.12,165,1.17,171,1.419,172,1.104,174,1.553,177,1.539,179,1.748,186,1.502,197,1.42,199,2.577,203,1.251,207,1.301,224,1.35,228,1.009,233,1.153,236,0.838,246,2.511,283,0.921,285,1.316,304,1.101,313,1.276,316,1.42,317,1.593,322,2.077,323,2.107,343,1.133,353,0.869,372,1.192,381,1.507,408,4.175,482,1.425,495,1.539,496,1.946,505,1.321,527,1.991,577,2.472,590,1.714,596,1.435,609,2.083,614,0.962,653,1.644,657,2.493,691,1.753,714,2.219,715,2.685,723,2.745,751,4.434,757,1.287,760,1.544,816,1.805,828,1.329,838,2.635,859,1.654,892,1.192,916,1.992,945,1.729,995,1.773,1037,1.586,1099,1.18,1162,1.773,1174,2.146,1182,1.52,1220,1.828,1284,2.169,1329,1.467,1331,2.045,1988,1.333,1998,3.241,2013,1.74,2041,2.146,2217,2.449,2276,1.828,2450,2.124,2868,1.733,2911,2.169,3398,2.304,3466,3.556,3601,3.995,3809,7.589,3810,1.993,3811,2.449,3814,2.655,3816,3.948,3817,2.724,3854,2.541,5514,5.63]],["t/2836",[2,1.211,5,1.582,7,0.734,12,1.812,16,1.276,17,1.775,23,2.29,24,1.836,26,1.667,28,0.968,29,1.567,36,3.668,48,1.822,49,1.87,56,1.222,84,0.487,85,1.556,86,3.057,103,1.503,121,4.867,130,2.56,137,2.882,138,1.812,140,1.76,165,1.536,171,1.22,189,2.404,190,2.309,193,1.634,207,1.833,219,2.575,228,2.172,232,1.634,233,2.482,236,1.181,239,2.577,289,2.71,296,1.731,313,1.675,322,1.405,324,2.378,336,1.828,342,2.936,373,3.248,381,2.125,405,3.607,407,3.76,408,3.816,409,3.475,438,2.331,458,3.862,464,3.84,495,3.311,502,1.239,508,2.646,527,0.982,567,1.801,572,2.378,580,1.56,592,2.882,600,2.994,673,3.954,683,1.681,723,2.974,731,2.354,744,2.56,750,3.165,751,2.593,757,1.107,807,1.736,811,2.787,831,2.833,876,2.833,984,1.922,993,1.817,1266,1.947,1319,3.057,1475,3.69,1509,3.84,1699,2.787,2013,1.496,2250,2.023,2422,2.882,2450,2.994,2459,3.513,3667,3.954,3809,7.037,3979,3.582,4127,3.582,4686,3.342,4996,4.492,4997,4.492,4998,4.091,4999,4.492,5000,4.492]],["t/2838",[1781,3.73]],["t/2840",[29,3.492,101,5.328,305,6.367,318,2.437,323,4.037,395,3.956,505,4.148,655,6.542,728,6.969,740,4.524,858,4.937,982,5.049]],["t/2842",[4,2.243,7,0.57,17,2.858,25,2.316,27,1.717,40,3.81,48,2.934,85,2.505,164,1.987,203,1.731,233,2.617,236,3.047,275,2.239,281,3.422,282,3.934,323,2.917,343,2.573,405,2.301,409,3.665,565,2.361,568,2.764,608,3.754,655,4.728,683,2.706,691,5.308,740,4.909,760,3.423,798,3.978,810,3.211,830,3.305,901,4.739,982,3.648,985,4.773,1007,4.684,1145,3.682,1289,4.601,1300,5.874,1370,5.768,2577,3.81,2593,6.184,2595,4.601,2596,4.641,2614,4.821,4909,7.233]],["t/2844",[7,0.946,10,3.841,17,2.806,22,4.406,25,3.841,30,4.442,31,1.735,32,4.442,36,3.131,37,4.734,56,0.701,84,0.769,101,3.78,164,1.951,174,2.111,229,4.212,233,3.894,236,1.867,278,2.545,283,2.053,318,1.729,343,2.526,377,1.588,390,3.429,481,4.242,527,1.553,565,2.319,612,4.599,630,2.623,650,4.783,683,2.657,740,5.199,827,3.598,856,4.479,982,5.801,1323,4.888,2259,4.734,2421,4.242,2471,5.367,2595,4.517,2596,4.557,2614,4.734,2631,4.642,2632,7.102]],["t/2846",[7,0.644,12,1.758,21,1.894,25,2.146,27,1.034,28,0.615,31,1.065,56,0.977,84,0.726,96,1.535,106,1.597,107,1.716,109,0.735,111,3.196,115,2.007,123,1.485,132,1.968,162,1.531,165,0.976,170,2.708,179,2.242,197,2.986,213,1.773,228,2.123,233,2.955,236,1.762,238,7.053,246,1.648,247,1.915,272,3.343,276,3.32,281,2.062,296,1.679,313,1.637,318,1.988,323,1.758,324,2.308,364,3.071,365,2.208,372,1.631,382,2.704,387,3.034,390,2.104,409,2.208,482,1.949,491,1.314,502,1.202,527,2.518,541,2.682,565,1.423,568,1.248,578,1.713,607,1.67,630,1.61,632,2.345,651,5.442,658,2.087,660,3.243,685,2.425,734,1.928,814,1.877,827,2.208,833,3.82,838,2.198,846,1.338,858,2.15,927,2.876,945,1.442,977,2.444,982,3.38,994,1.877,1037,2.169,1062,2.096,1097,3.349,1108,2.229,1111,4.666,1145,3.412,1175,2.007,1178,2.516,1188,2.772,1219,1.942,1285,1.858,1300,6.361,1395,3.837,1632,2.262,2099,2.622,2259,2.905,2314,3.726,2472,3.243,2595,2.772,2596,5.882,2610,3.549,2614,6.109,2636,3.837,2638,4.136,2639,4.136]],["t/2848",[2,1.915,7,0.848,25,3.841,84,0.769,96,2.501,98,2.495,113,2.589,233,2.57,236,3.155,238,5.468,245,4.242,313,1.735,318,2.62,338,1.747,372,3.567,472,3.458,536,2.115,557,3.534,628,3.667,681,4.442,683,2.657,685,5.304,736,3.028,740,4.309,760,2.098,859,3.685,860,6.072,895,6.468,945,2.35,982,4.808,993,2.873,1108,3.632,1142,6.291,1300,3.841,2595,4.517,2596,6.117,2614,6.354,2627,6.252,2968,6.072,3830,5.068]],["t/2850",[7,0.84,30,6.666,84,1.155,163,3.34,228,2.67,233,3.857,236,2.803,281,3.987,318,2.052,361,7.873,740,5.284,982,5.896,1117,5.617,1221,5.868,2137,6.179,2322,6.271,2430,6.56,2536,7.676,2596,5.408,2641,6.014,2642,7.676,2643,7.206]],["t/2852",[7,0.869,31,1.377,34,1.435,40,2.969,48,2.286,56,0.556,84,0.61,103,1.885,107,1.443,116,2.103,132,1.655,145,2.266,179,1.885,233,2.937,236,2.501,238,3.232,247,2.476,248,2.645,313,1.377,318,2.685,372,2.108,377,1.26,426,2.081,447,2.666,465,5.65,482,2.52,502,2.869,509,3.192,525,2.655,527,1.774,530,2.52,565,2.65,568,1.614,608,2.924,614,1.037,630,2.081,635,2.044,647,2.114,685,3.135,691,4.463,740,3.668,760,2.81,857,2.556,887,3.756,927,5.356,945,2.685,982,2.842,1144,3.065,1196,4.021,1198,4.132,1200,4.696,1300,6.553,1632,2.924,2099,6.261,2210,4.408,2250,3.655,2588,5.304,2595,3.584,2596,5.208,2610,4.589,2614,3.756,2619,4.696,2641,4.021,4911,5.132,4912,5.132,4913,5.132,4914,5.132]],["t/2854",[7,0.803,21,2.877,49,2.289,56,1.005,84,1.103,233,3.685,236,2.677,305,6.477,740,4.603,1175,4.689]],["t/2856",[7,0.616,21,1.512,25,2.957,29,1.867,34,1.363,41,2.428,49,2.076,56,1.151,84,1.1,85,1.854,92,2.895,105,1.674,113,1.952,129,3.197,131,2.428,134,2.663,140,2.097,162,1.881,165,1.199,171,1.454,233,1.937,236,1.407,238,4.485,256,2.003,296,2.063,318,1.903,324,2.834,351,1.937,372,2.003,377,1.197,401,2.046,465,3.727,491,1.613,536,1.594,580,1.858,607,2.051,614,0.985,621,2.751,628,2.764,681,3.348,685,2.978,691,2.944,740,4.884,757,1.319,760,2.729,810,4.51,820,2.751,901,2.628,938,3.294,945,1.771,982,5.691,993,2.165,1145,2.725,1172,3.22,1297,2.595,1300,2.895,1323,3.684,1638,2.764,1998,3.32,2014,4.46,2421,3.197,2595,3.405,2596,7.241,2611,5.732,2614,5.211,3027,3.982,3466,3.643,3531,3.727,3834,3.924,4915,5.352,4916,5.352,4917,4.358,4918,4.576,4919,5.352,4920,5.352,4921,5.352]],["t/2858",[34,2.277,56,1.297,105,2.797,296,3.446,377,2,630,4.085,810,3.97,945,2.958,1998,5.547,2142,4.449,2614,5.96,3466,6.086,3531,6.226,3834,6.556,4917,7.281,4918,7.645,4922,8.942,4923,8.942,4924,8.942,4925,8.942]],["t/2860",[48,3.371,56,1.273,283,2.402,491,2.504,539,5.332,614,1.529,683,3.109,760,2.455,810,3.689,963,5.036,1998,5.154,2595,5.285,3466,5.655,3531,5.785,3834,6.092,4917,6.765,4918,7.104,4926,8.308,4927,8.308,4928,8.308,4929,8.308,4930,8.308,4931,8.308,4932,8.308,4933,8.308,4934,8.308,4935,8.308,4936,8.308,4937,8.308,4938,8.308,4939,8.308]],["t/2862",[7,0.703,15,2.348,25,2.062,32,4.027,49,1.447,84,1.108,92,3.302,93,2.728,106,2.36,123,2.194,133,2.755,162,2.263,164,1.769,171,2.423,177,3.109,203,1.541,233,2.33,236,1.693,256,2.409,304,1.879,313,1.573,322,2.014,343,2.29,387,4.483,509,3.648,578,2.53,608,3.342,614,1.883,650,4.336,683,3.336,740,4.624,757,2.197,814,2.773,881,4.027,937,3.846,982,5.16,1014,6.439,1237,4.17,1300,4.823,1632,3.342,1671,3.541,1689,3.375,2325,5.505,2471,4.866,2595,4.096,2596,4.132,2611,4.721,2614,4.292,2631,5.828,3203,6.11,3595,3.285,3833,4.721,4598,6.439,4940,6.439,5515,6.938,5516,6.938,5517,9.609,5518,6.938,5519,6.938]],["t/2864",[7,0.556,17,3.752,56,1.265,85,2.445,92,3.515,93,2.99,103,2.362,197,3.145,233,2.554,296,2.72,304,1.487,313,1.724,315,3.353,323,2.847,353,2.589,361,4.754,552,3.36,562,3.466,612,4.571,614,1.975,650,7.225,740,4.291,796,3.7,810,4.216,944,2.855,982,4.789,1017,6.775,1072,3.262,1079,4.415,1323,6.535,2013,2.351,2421,4.217,2541,6.214,2611,6.962,3531,4.915,3834,5.176,4850,5.748,4917,5.748,4941,7.059,5520,7.606]],["t/2867",[4,2.626,170,1.767,182,2.001,236,2.584,238,6.132,248,2.807,254,1.923,305,5.386,324,3.166,372,2.237,527,2.149,578,2.35,653,2.665,659,3.872,740,2.703,741,3.872,757,1.473,760,2.904,814,3.646,846,1.836,887,5.643,892,2.237,927,5.588,945,3.537,966,3.355,977,2.18,982,3.016,1008,3.572,1038,4.07,1076,3.049,1118,4.514,1145,3.044,1196,4.267,1198,4.384,1224,3.111,1300,6.863,1754,3.598,1849,3.252,2254,3.088,2595,3.804,2596,3.837,2610,6.894,2614,3.986,3059,3.598,3858,3.103,4897,4.519,4942,5.446,4943,5.98,4944,5.446,4945,5.98]],["t/2869",[4,3.286,164,1.61,182,1.961,203,2.327,238,5.576,247,2.575,351,2.121,377,1.311,381,3.949,482,2.62,502,1.616,527,2.618,608,3.041,614,1.79,630,2.164,647,3.974,653,3.334,757,1.444,814,4.562,916,2.234,945,3.216,966,3.173,1076,3.814,1118,4.473,1138,5.309,1177,4.36,1224,3.892,1300,6.056,1632,3.041,1849,5.287,1988,2.451,4897,4.429,4912,5.337,4913,7.602,4946,6.937,4949,5.86,4950,8.346,4951,5.86,4952,8.346,4955,5.86,5521,8.993]],["t/2871",[4,2.506,133,3.456,164,2.219,182,2.703,247,3.55,372,3.882,502,2.228,527,2.506,608,5.948,630,2.984,647,3.031,653,2.543,691,4.444,757,1.991,760,2.387,846,2.48,945,3.792,966,2.829,1076,2.909,1138,5.139,1224,2.969,1300,6.766,1632,4.193,1988,3.38,4914,9.45,4946,7.404]],["t/2873",[4,2.716,68,1.517,84,0.433,98,2.204,106,1.467,137,4.025,182,3.528,203,0.958,236,2.302,238,5.023,254,2.017,322,1.962,372,3.945,404,1.948,408,1.444,426,1.478,506,2.373,527,1.914,622,2.018,623,4.553,653,2.756,747,1.643,757,1.545,760,2.587,776,3.334,829,2.108,846,1.925,887,5.836,927,2.641,944,1.619,945,3.146,966,3.365,1076,3.153,1081,4.317,1089,2.444,1094,3.866,1118,4.26,1138,7.14,1220,3.598,1224,3.217,1300,6.524,1375,2.426,1671,2.201,1696,4.92,1765,4.437,1781,2.612,1849,3.411,1988,1.674,2190,4.329,2218,2.894,2618,8.308,3027,2.977,3858,2.077,4572,3.334,4686,4.667,4897,3.024,4911,5.712,4942,3.645,4946,7.198,4958,6.613,4959,6.272,4961,6.272,5522,6.272,5523,5.952,5524,3.797,5525,4.002,5526,3.797]],["t/2875",[4,2.691,182,2.903,203,2.077,238,6.796,372,3.246,482,3.88,608,4.503,647,3.255,653,2.731,757,2.138,803,4.886,945,2.871,966,3.231,1076,3.124,1118,4.584,1145,4.417,1177,6.456,1224,3.188,1300,5.872,1988,3.63,2617,6.667,4946,6.192,4963,10.856]],["t/2877",[4,2.877,68,1.69,84,0.483,98,1.566,106,1.634,137,4.376,182,3.785,203,1.067,236,1.172,238,6.046,254,1.433,372,4.232,404,2.17,408,1.609,506,2.644,527,0.975,622,2.248,623,3.265,647,3.106,653,2.919,747,1.83,757,1.098,760,1.317,776,3.714,803,2.51,829,2.348,846,1.368,887,2.971,945,3.487,966,3.328,984,1.907,1076,3.34,1081,3.068,1089,2.722,1094,3.408,1118,3.917,1132,1.979,1138,7.195,1220,3.911,1224,3.408,1300,6.399,1671,2.452,1696,4.337,1765,3.911,1781,2.796,1988,1.865,2015,2.626,2190,3.816,2218,3.223,2617,5.239,4572,3.714,4686,5.073,4946,7.522,4958,7.08,4964,6.819,4965,4.458,4967,4.458,4968,4.458,4969,4.06,4970,4.458,4971,4.458,5523,6.47,5524,4.23,5525,4.458,5526,4.23,5527,7.347,5528,8.922]],["t/2879",[4,2.455,182,2.649,203,1.895,238,6.885,372,2.962,482,3.54,527,2.784,608,5.314,647,2.97,653,2.492,757,1.951,803,4.457,927,5.224,945,2.619,966,3.095,977,2.886,1076,2.85,1118,4.324,1145,4.03,1177,5.89,1224,2.909,1285,4.366,1300,5.539,1988,3.311,2254,4.088,2636,9.015,4946,5.649,4972,7.916]],["t/2881",[4,2.958,106,2.075,109,0.955,137,3.634,182,3.489,238,6.338,372,3.902,404,2.757,408,2.044,506,3.358,527,2.415,623,2.711,647,3.577,653,3.282,747,2.325,757,1.395,776,4.718,803,3.188,829,2.983,846,1.738,945,3.45,966,3.308,984,2.422,1076,3.755,1081,3.897,1089,3.458,1094,2.83,1118,4.027,1132,2.514,1138,6.634,1224,3.832,1671,3.114,1696,3.602,1765,3.248,1781,1.912,1988,2.368,2015,3.335,2190,3.169,2218,4.094,2635,7.728,4572,4.718,4686,4.213,4944,10.063,4946,6.805,4958,6.963,4969,5.157,5523,5.373,5524,5.373,5526,5.373,5529,6.101,5530,8.775]],["t/2883",[4,2.896,5,1.183,7,0.736,17,1.328,21,0.949,25,3.801,27,1.882,31,1.333,39,1.367,48,1.363,55,1.827,68,1.274,84,0.591,99,2.119,103,1.124,163,1.332,182,1.124,196,1.332,203,1.306,236,1.811,238,3.951,297,1.837,313,0.821,318,0.818,372,2.577,390,1.622,397,2.268,414,3.129,418,2.727,433,1.94,444,2.365,445,2.119,472,2.656,512,2.374,531,2.037,614,0.619,630,1.241,647,2.584,653,3.097,685,5.703,691,1.848,740,4.633,757,1.697,760,2.035,828,2.275,846,1.675,908,1.71,945,2.279,966,2.969,982,5.619,1037,1.672,1076,3.815,1118,3.681,1138,3.47,1224,3.615,1244,2.8,1300,2.951,1323,3.755,1462,1.817,1475,1.807,1733,2.007,1931,2.43,2017,2.296,2293,1.966,2430,2.068,2471,2.539,2577,2.874,2595,5.545,2596,6.313,2614,6.557,2616,3.188,2617,2.582,2619,4.546,2622,4.664,2631,2.196,2634,3.188,3027,4.059,3616,2.068,3838,4.98,3918,2.137,4897,2.539,4946,4.915,4973,5.455,4974,5.455,4975,5.455,4976,5.455,4977,3.36,4978,3.36,4979,3.36,4980,3.36,4981,3.36,4982,5.455,4983,5.455,4984,5.455,4985,5.455,5522,3.36,5531,3.62,5532,3.36]],["t/2885",[4,2.795,68,3.416,163,3.571,182,3.015,647,3.38,653,2.836,740,5.023,757,2.22,892,3.371,966,2.457,982,5.605,1076,3.244,1118,3.805,1224,3.311,1988,3.769,2631,5.889,2634,10.546,2643,9.502,3838,8.036,5532,9.01]],["t/2887",[2,2.108,4,2.425,7,0.616,16,0.996,25,1.714,27,1.27,28,0.755,30,3.348,40,2.82,56,0.528,84,1.1,98,1.881,103,1.791,105,1.674,116,1.997,132,1.572,163,2.121,164,1.47,165,1.199,182,1.791,233,2.829,247,2.352,298,3.269,302,2.464,313,1.91,318,2.631,338,1.317,351,1.937,394,2.675,444,2.32,447,2.532,461,4.376,477,3.175,491,1.613,502,1.476,536,1.594,565,1.747,647,2.008,653,1.685,740,4.591,757,2.276,802,2.165,814,2.305,822,3.197,828,2.232,901,2.628,910,5.719,945,3.361,966,2.77,982,5.691,987,3.982,1019,1.967,1076,2.815,1118,3.9,1144,2.911,1179,3.568,1221,3.727,1224,1.967,1300,4.229,1313,3.11,1336,3.498,1374,3.604,1527,3.532,1605,3.924,1988,2.239,2137,5.732,2430,3.294,2617,4.113,2619,4.46,2643,8.685,3838,6.679,4828,4.875]],["t/2889",[7,0.434,31,1.344,39,3.246,84,0.864,99,5.031,106,2.924,121,3.219,130,3.137,131,4.669,137,3.531,138,2.219,163,2.181,224,2.331,236,2.873,278,1.972,315,3.789,341,3.027,343,3.886,403,3.531,416,3.673,434,6.255,445,3.471,580,1.911,611,4.278,650,6.931,653,3.239,740,4.652,796,2.884,887,3.668,982,4.024,1008,4.765,1113,3.927,1161,4.388,1179,5.317,1219,2.452,1221,3.831,1224,3.781,1467,4.989,1689,2.884,2421,3.287,2452,9.06,2470,5.222,2471,4.159,2529,5.849,2598,3.387,2631,3.597,2904,3.979,3118,4.585,3398,3.979,4059,5.012,4686,4.095,4986,5.503,4987,5.503,4988,7.57,4989,5.503,4991,5.503,4992,10.925,5533,8.596,5534,5.93,5535,5.93]],["t/2891",[272,4.48,596,4.928]],["t/2893",[1781,3.73]],["t/2895",[2,1.746,5,2.281,7,0.872,49,1.455,56,1.013,138,2.611,165,1.45,171,1.759,203,1.55,207,3.653,246,3.879,254,3.559,256,3.349,275,2.004,276,4.434,288,4.273,299,7.455,304,1.364,313,1.582,315,4.252,337,3.015,338,1.593,416,2.981,476,1.879,491,1.951,513,3.051,527,2.54,691,5.642,707,3.602,715,3.328,735,4.747,757,1.595,769,2.488,774,5.549,810,2.875,830,2.959,977,2.361,1107,4.893,1110,4.893,1213,4.682,1367,4.083,1733,3.868,2073,5.347,2306,5.272,2868,3.521,2951,3.84,3666,5.536,3824,5.536,3825,5.536]],["t/2897",[7,0.694,49,1.979,92,3.261,93,4.642,116,3.287,164,2.419,171,2.393,174,2.618,207,3.595,233,3.187,254,3.523,256,3.295,276,5.909,283,2.547,299,7.654,304,1.856,343,3.132,527,1.925,653,2.772,810,3.911,838,4.442,1806,6.206]],["t/2899",[4,1.827,5,3.434,7,0.66,21,2.367,26,2.186,46,2.441,49,2.386,56,1.108,85,2.04,90,2.775,92,4.683,94,5.272,105,3.509,116,2.198,133,2.519,171,1.6,174,2.489,203,2.005,236,1.549,254,1.894,275,1.823,278,2.11,283,1.703,290,4.908,299,6.813,337,2.743,353,1.606,359,2.597,365,2.984,405,1.874,476,2.431,496,3.597,498,3.277,505,2.441,513,2.775,527,1.288,531,3.57,578,2.314,605,3.031,614,1.084,769,2.263,774,3.186,824,4.318,845,3.746,1080,3.277,1108,3.013,1162,3.277,1364,2.944,1998,3.654,2868,3.203,2951,3.493,2990,3.597,3466,4.009,3816,4.451,3828,4.451,3830,4.203,3831,3.814,3832,5.036,3833,4.318,3849,7.162]],["t/2901",[15,3.008,49,1.854,56,1.271,85,2.858,171,2.241,283,2.385,296,3.179,297,4.512,299,5.294,476,2.395,810,5.412,1055,7.604,1207,5.966,1409,5.445,1577,6.235,1802,5.001,1994,5.744,2611,7.71,2868,4.487,3531,5.744,3828,6.235,3834,6.049,3849,7.054,5001,8.25,5002,8.25,5003,8.25,5004,8.25]],["t/2904",[4,2.019,5,2.293,7,0.513,31,2.195,39,2.649,46,2.698,56,1.095,84,0.705,116,2.429,138,2.626,182,2.178,203,1.558,207,2.657,221,2.404,254,2.093,256,2.436,275,2.015,299,6.603,318,1.585,377,1.456,398,2.293,414,5.154,476,1.89,527,2.545,614,1.199,647,2.442,653,2.828,691,3.581,757,1.604,769,3.453,774,4.86,830,2.975,846,1.999,908,3.314,945,2.154,966,1.775,1051,3.157,1070,3.711,1118,2.749,1224,2.392,1328,3.395,1456,3.43,1988,2.723,2017,3.782,2031,3.484,2141,3.33,2338,3.581,2402,4.491,2577,3.43,2625,3.735,2951,3.862,3616,5.53,3840,5.191,3841,4.384,3850,8.527,3851,5.566,5536,7.015]],["t/2906",[4,2.506,56,1.194,116,3.015,182,2.703,221,2.984,318,1.967,377,1.807,398,2.846,476,2.345,527,2.506,647,3.031,653,3.266,757,1.991,846,2.48,945,2.673,966,2.203,1051,3.917,1118,3.412,1224,2.969,1328,4.214,1456,4.256,1988,3.38,2017,4.368,2031,4.323,2141,4.133,2338,4.444,2402,5.186,3851,6.908,3852,9.846,3853,7.667,5537,8.706]],["t/2908",[1781,3.73]],["t/2910",[7,0.563,17,2.824,34,1.82,49,1.606,56,1.138,203,1.71,225,2.554,239,4.099,248,5.412,256,2.673,285,3.954,296,2.753,304,2.43,317,3.571,351,2.586,393,3.509,398,2.517,731,3.745,734,3.161,760,2.111,966,2.61,1123,6.358,1400,5.602,1401,4.208,1467,4.469,1475,3.844,1601,5.718,1632,3.708,1737,2.694,2073,4.268,2142,4.763,2317,5.954,2327,5.818,2723,6.921,2724,6.921,2745,5.589,3595,2.632,4007,6.78,4008,6.78,4009,4.975]],["t/2912",[7,0.674,10,4.625,56,1.062,84,1.165,228,2.708,248,5.524,322,2.674,511,2.726,605,3.893,892,3.199,1123,6.932,1290,5.036,1400,4.437,1565,5.953,1601,5.108,1629,5.953,1638,4.416,2013,2.848,2142,4.255,2723,6.183,2724,6.183,2799,6.27,4010,8.114,4011,8.114,4012,8.114]],["t/2914",[7,0.574,15,2.654,49,1.636,56,1.076,140,2.852,162,3.405,171,1.977,196,2.884,203,1.742,248,4.548,255,4.901,281,3.443,302,3.351,313,1.778,322,3.031,324,3.854,393,3.574,614,1.34,704,6.407,724,3.543,731,3.815,828,3.035,977,2.654,1123,5.708,1265,3.815,1400,6.278,1402,5.431,1475,5.213,1601,5.788,1632,3.777,2013,2.424,2142,3.621,2723,7.007,2724,7.007,3607,7.105,4009,5.067,4013,6.222,4014,6.222]],["t/2916",[1781,3.73]],["t/2918",[7,0.54,28,1.491,31,2.273,49,1.54,56,0.677,68,2.598,84,0.742,92,3.446,94,3.706,115,4.866,132,2.733,133,2.931,196,2.716,203,1.64,233,2.48,236,1.802,254,2.203,283,1.981,313,1.674,315,3.255,318,1.668,377,2.081,390,3.309,436,4.89,502,3.125,513,3.229,527,1.498,550,2.693,577,4.999,630,2.531,675,4.359,715,3.522,751,3.956,769,2.633,829,3.61,910,5.24,912,5.71,916,3.547,1010,4.568,1015,4.064,1040,4.185,1098,4.322,1988,2.866,2210,5.36,2382,5.179,2402,3.425,2868,3.727,3854,5.464,3856,6.503,3857,6.503,5005,6.853]],["t/2920",[2,1.806,4,2.077,7,0.528,16,1.246,56,1.109,84,0.992,116,2.499,182,2.241,221,2.473,232,2.435,313,1.636,318,2.23,372,2.505,377,1.498,398,2.359,476,1.944,491,2.018,502,1.846,527,2.57,536,1.994,565,2.186,568,1.918,605,3.315,630,2.473,647,2.512,653,2.883,683,2.505,757,1.65,814,4.834,846,2.056,910,6.62,957,3.891,966,1.826,1051,3.247,1072,3.095,1118,2.828,1224,2.46,1266,2.903,1328,3.492,1456,3.528,1481,3.583,1988,2.801,2017,3.856,2031,3.583,2141,3.425,2338,3.683,2402,4.579,3854,5.34,3858,3.475,5538,7.215]],["t/2922",[0,2.461,4,1.322,7,0.519,12,1.719,16,1.226,24,1.067,31,2.529,39,2.681,41,1.934,56,1.222,84,0.462,98,1.498,113,2.402,115,4.51,132,1.252,165,0.955,179,1.426,182,2.694,196,1.69,203,1.02,219,1.32,221,1.574,254,1.371,282,2.319,318,1.961,372,1.595,398,1.502,414,3.78,471,1.767,476,1.238,477,2.529,491,1.285,502,1.176,527,2.641,553,2.257,565,1.392,567,1.709,568,1.887,577,2.017,580,1.48,610,3.276,614,1.213,647,3.021,653,2.852,658,4.338,670,2.478,724,2.076,757,1.05,814,2.837,829,4.242,830,1.948,846,2.472,910,6.278,916,1.625,945,1.411,957,2.478,966,1.797,984,3.445,1051,3.195,1076,3.887,1118,3.401,1224,2.959,1328,2.223,1456,2.246,1671,3.624,1932,3.004,2017,2.774,2031,2.281,2141,2.181,2196,8.161,2338,2.345,2402,3.293,2875,3.172,2885,3.645,2949,3.645,3858,3.419,3860,6.252,3861,4.046,3862,4.046,3863,6.252,5539,4.594]],["t/2924",[4,2.271,5,2.579,16,1.363,56,1.15,116,2.733,164,2.012,182,2.45,221,2.705,254,2.355,318,2.369,372,2.74,398,2.579,476,2.126,502,2.019,527,2.724,647,2.747,653,3.063,757,1.804,846,2.248,910,6.556,957,4.256,966,1.997,1051,3.551,1118,3.093,1224,2.691,1328,3.819,1408,9.953,1456,3.858,1988,3.063,2017,4.096,2031,3.919,2141,3.746,2338,4.028,2352,5.226,2402,4.864,3858,3.8,3864,6.949,5540,7.891]],["t/2926",[2,1.47,7,0.808,24,0.883,28,0.769,31,1.936,49,2.098,56,1.269,90,2.569,92,4.76,93,4.341,94,2.949,97,2.438,98,1.916,100,2.438,115,2.51,132,1.601,165,1.221,171,2.536,174,2.774,177,2.632,203,1.897,283,1.576,304,1.149,313,1.332,322,2.92,398,1.92,496,3.33,527,2.24,553,2.887,614,1.004,714,3.796,715,2.802,769,3.045,814,2.348,881,4.956,892,2.04,910,5.769,995,3.033,1037,2.713,1162,3.033,1264,3.563,1297,2.643,1689,2.858,1998,4.916,2013,3.109,2464,4.264,2868,2.965,3066,3.997,3466,3.711,3794,4.661,3816,5.988,3817,4.661,3866,7.519,3867,5.173,5006,9.334]],["t/2928",[1781,3.73]],["t/2930",[7,0.693,16,0.636,24,1.726,28,1.241,29,1.193,34,0.871,49,1.8,55,1.859,56,0.791,84,1.073,85,1.184,107,3.253,109,1.925,113,1.247,127,1.121,162,1.944,165,0.766,170,2.928,171,0.929,172,1.184,193,1.243,233,2.522,236,2.106,246,2.092,278,1.225,285,2.286,295,1.375,304,1.166,338,2.164,351,2.002,376,1.949,405,1.088,408,3.398,426,1.263,429,2.923,477,2.028,484,1.448,500,1.617,527,2.495,536,3.335,557,1.701,576,2.014,593,1.686,605,1.237,611,1.421,614,1.733,621,1.757,622,1.724,647,2.075,736,1.458,755,1.54,756,1.605,757,0.842,760,3.372,765,2.028,811,2.121,828,1.426,859,2.871,936,2.302,939,3.972,944,1.383,945,1.83,984,1.462,994,3.449,1037,1.701,1099,1.266,1100,1.355,1288,2.042,1364,3.483,1462,1.849,1508,5.207,1540,3.06,1699,2.121,1754,1.453,1988,1.43,2010,3.71,2211,2.726,2259,3.688,2276,3.174,2525,3.01,2613,2.784,3971,2.544,3972,2.234,4785,5.11,5007,3.419,5008,3.114,5009,2.784]],["t/2932",[7,0.774,16,1.111,24,1.826,28,1.452,31,0.918,49,1.9,56,0.371,84,1.115,107,2.362,109,1.901,123,1.281,127,4.248,132,1.753,165,1.663,170,1.763,193,3.355,197,1.674,233,3.06,276,1.862,278,1.347,296,1.448,304,0.792,318,0.915,338,2.415,373,2.717,405,1.195,426,2.204,433,2.17,476,2.678,491,1.133,500,1.778,524,2.313,527,0.822,536,3.487,565,2.424,568,1.709,592,2.412,593,1.854,605,1.36,609,2.456,614,1.557,635,1.363,647,3.172,653,1.183,691,2.067,756,1.764,760,3.041,818,1.887,840,1.705,936,2.531,939,2.142,984,1.608,994,3.197,1009,2.558,1100,2.366,1144,2.044,1319,2.558,1462,2.033,1508,6.476,1696,2.39,1699,2.331,1754,1.597,2010,3.583,2331,2.39,2472,2.796,3009,7.046,3971,2.796,3972,2.456,5010,6.046]],["t/2934",[7,0.796,16,0.982,24,1.879,28,1.293,49,1.187,84,0.838,107,2.584,109,2.047,170,1.56,172,4.465,174,1.569,233,3.652,278,1.892,304,1.113,338,2.762,405,1.68,426,2.859,502,3.096,527,1.692,536,3.005,592,3.388,593,2.604,605,1.911,614,1.425,647,3.786,756,2.478,757,1.907,760,2.982,819,3.721,936,3.556,984,2.259,994,3.333,1100,3.068,1462,2.856,1508,6.582,1699,3.276,2010,3.512,3971,3.929,3972,3.451,5011,6.618,5012,7.05]],["t/2936",[7,0.728,16,0.996,24,1.887,28,1.303,49,1.203,84,0.847,107,2.6,109,2.088,170,1.581,233,3.342,278,1.918,304,1.128,338,2.775,405,1.703,426,2.887,502,3.111,527,1.709,536,3.025,592,3.435,593,2.64,605,1.937,614,1.439,647,3.81,756,2.512,757,1.926,760,3.001,936,3.604,984,2.29,994,3.367,1099,4.784,1100,3.098,1462,2.895,1508,6.607,1699,3.32,2010,3.535,3971,3.982,3972,3.498,5013,6.684,5014,7.121]],["t/2938",[16,1.757,28,1.614,84,1.023,109,1.593,165,2.116,170,2.791,172,3.272,322,2.954,607,3.62,614,1.739,724,4.599,725,5.823,755,4.254,1508,5.225,2010,3.287,2190,5.286,5015,7.138]],["t/2940",[16,1.743,28,1.606,84,1.015,109,1.58,165,2.099,170,2.768,172,3.246,173,5.014,322,2.931,607,3.591,614,1.725,724,4.562,755,4.22,977,3.417,1508,5.183,2010,3.261,2190,5.244,4574,7.472,5016,6.87]],["t/2942",[16,2.058,25,2.863,27,2.625,28,1.561,84,0.969,107,2.832,109,1.508,165,2.003,322,2.797,614,1.646,623,4.282,724,4.354,1099,3.311,1101,6.607,1285,3.813,1481,4.785,1508,4.946,1557,5.547,2010,3.112,2190,5.004,5017,6.871]],["t/2944",[1781,3.73]],["t/2946",[7,0.924,24,1.594,84,1.066,139,5.261,216,5.01,325,6.852,397,4.091,759,3.45,892,3.682,993,3.981,1506,6.315,5541,9.339]],["t/2948",[7,0.641,24,1.317,84,0.881,89,3.036,109,1.372,139,4.677,164,2.235,165,1.822,171,2.21,174,2.418,281,3.849,285,3.361,338,2.564,527,2.651,568,2.985,614,1.498,724,3.961,759,2.852,944,3.291,945,3.448,966,2.842,1100,3.224,1506,5.221,1959,3.88,1988,3.403,2010,2.831,2276,5.978,2303,7.41,3946,7.72,5018,6.364]],["t/2950",[5,1.92,7,0.625,19,2.429,24,1.982,28,0.769,31,1.332,39,2.218,68,3.539,96,1.92,107,1.396,109,0.919,139,4.189,160,1.911,182,1.824,190,2.802,203,1.897,226,2.643,236,2.083,243,2.981,277,2.802,295,2.192,313,1.332,318,1.327,338,1.341,367,2.775,414,3.127,421,3.41,441,2.965,445,3.438,484,2.31,527,2.041,553,2.887,568,2.269,605,1.973,623,3.794,638,4.767,662,3.168,757,1.343,759,2.778,760,2.758,769,4.183,796,2.858,827,4.73,938,3.355,939,3.108,944,2.205,1008,3.257,1103,4.799,1163,3.671,1182,2.6,1185,4.439,1249,4.799,1251,3.89,1264,3.563,1271,2.713,1322,3.997,1329,4.718,1369,4.189,1475,2.933,1506,3.498,1629,3.796,1733,3.257,1754,2.317,1991,3.634,2231,6.16,2434,3.711,2631,3.563,3810,3.41,3835,4.661,3918,3.468,3919,4.799,3922,6.775,5183,5.173,5542,5.874,5543,5.874]],["t/2952",[7,0.745,31,2.307,39,3.843,139,4.239,174,2.807,325,6.576,363,6.74,414,5.418,614,1.739,672,5.564,760,3.379,854,6.926,1219,4.209,1309,5.489,1642,8.076,2627,8.315,3823,5.957,5541,8.963]],["t/2954",[7,0.763,139,4.344,174,2.877,325,6.739,444,4.196,445,6.104,614,1.782,672,5.701,760,3.429,854,7.097,1309,5.625,2328,8.816,2627,8.521,3823,6.104,5541,9.185]],["t/2956",[7,0.807,24,1.282,49,2.301,56,0.782,84,0.857,109,1.335,139,3.552,164,2.175,170,3.354,174,2.353,203,2.451,206,5.126,246,4.292,247,3.478,304,1.668,613,4.629,614,1.457,759,2.775,760,3.672,966,2.159,988,3.871,1364,3.957,1481,4.236,1633,3.73,2013,2.637,3924,6.446,3925,5.724,3926,6.446,3927,9.243]],["t/2958",[7,0.624,24,1.282,49,2.301,56,0.782,84,0.857,109,1.335,139,3.552,164,2.175,170,3.354,174,2.353,203,2.451,206,5.126,246,4.292,247,3.478,304,1.668,613,4.629,614,1.457,759,2.775,760,3.672,966,2.159,988,3.871,1364,3.957,1481,4.236,1633,3.73,2013,2.637,3925,5.724,3928,6.446,3929,6.446,3930,9.243,3931,7.512]],["t/2960",[7,0.751,24,1.542,28,1.344,109,1.606,132,2.797,139,4.273,193,3.463,236,2.504,254,3.696,476,2.764,550,3.742,578,3.742,614,1.753,623,4.56,1099,3.526,1101,5.688,1699,5.907]],["t/2962",[4,2.711,7,0.689,84,0.947,88,3.937,89,3.262,128,5.669,140,3.425,182,2.925,294,4.703,557,4.35,575,4.152,763,5.95,796,4.582,828,3.645,892,3.271,898,4.728,1076,3.148,1100,3.464,1132,3.881,1285,3.727,1425,5.08,1616,6.068,3932,6.838,3934,7.696,5544,9.419,5545,9.419]],["t/2964",[16,2.033,24,1.186,28,1.542,84,0.793,107,2.798,109,1.964,132,3.42,139,4.367,170,2.875,177,3.536,196,2.902,254,2.355,277,5.001,279,3.164,296,2.822,313,1.789,318,1.783,351,2.65,353,1.997,372,2.74,421,4.58,445,4.619,484,3.102,527,1.601,614,1.348,704,6.447,966,1.997,977,2.67,981,4.932,994,3.153,1163,4.932,1206,5.179,1285,3.123,1629,5.099,3008,5.099,3833,5.37,3858,3.8,5546,7.891,5547,7.891]],["t/2966",[7,0.337,12,1.726,16,0.796,24,1.307,25,1.37,34,1.09,36,1.887,49,0.962,56,1.131,68,1.622,84,0.463,98,1.504,107,1.095,109,1.823,132,1.257,139,4.403,170,2.385,179,1.432,185,2.945,199,2.11,236,1.737,254,1.376,295,1.721,296,1.649,338,1.625,353,1.167,377,0.957,421,2.676,426,1.58,476,3.138,484,1.813,512,2.783,524,2.634,527,0.935,575,3.138,576,2.52,578,1.681,580,1.486,593,2.11,614,1.99,628,2.21,638,2.574,647,3.68,653,2.08,663,2.613,725,3.364,759,2.316,803,2.409,846,1.314,847,3.412,857,1.941,866,3.658,892,3.02,945,1.416,966,1.802,993,1.731,1076,2.906,1093,3.743,1099,2.446,1118,4.838,1132,4.03,1224,2.428,1506,2.746,1616,3.676,1843,2.882,1959,3.849,2253,2.594,2256,7.599,2257,2.979,2900,4.45,2928,2.254,2964,3.897,3008,2.979,3014,3.658,3405,4.279,3917,2.852,3936,3.767,3939,6.27,3940,7.991,3941,6.27,3942,2.722,3943,7.539,3944,3.288,5548,4.611]],["t/2968",[7,0.814,16,1.493,24,1.85,28,1.132,68,3.042,96,2.826,132,2.357,233,2.904,318,1.954,398,2.826,426,2.963,502,2.213,512,2.767,536,2.39,550,3.153,565,2.62,568,2.298,578,3.153,614,1.477,623,3.842,647,3.875,724,3.907,916,3.059,1099,2.971,1101,4.793,1132,3.563,1224,2.948,1236,5.349,1309,4.663,1843,6.957,1849,4.364,2254,4.144,2585,8.024,3059,4.828,4323,5.726,5549,8.646]],["t/2970",[7,0.758,24,2.007,31,2.824,84,1.173,139,5.187,190,3.695,203,2.301,213,3.911,246,3.634,397,2.988,414,4.124,605,2.601,613,3.249,623,3.442,759,3.369,760,2.84,769,4.943,831,4.534,908,3.66,1083,3.485,1145,3.66,1370,5.732,1696,4.573,1698,5.349,2231,7.145,2327,5.854,2344,6.146,3616,5.915,3918,4.573]],["t/2972",[7,0.849,16,0.788,24,1.889,25,1.355,28,1.377,29,2.286,31,1.601,37,2.821,39,2.666,56,0.891,68,1.605,103,1.416,112,2.097,115,1.949,138,2.642,139,3.597,145,1.702,160,3.619,179,1.416,203,1.568,225,2.342,236,2.373,248,1.987,257,2.155,279,1.829,282,2.302,285,1.749,292,1.841,304,0.892,313,1.034,338,1.041,343,2.851,409,2.145,445,2.669,484,1.793,495,2.044,525,1.994,527,0.925,530,1.893,560,2.253,562,2.078,565,2.139,628,2.186,630,1.563,635,1.536,683,1.584,723,1.513,747,1.738,757,1.043,759,2.297,760,3.447,769,3.468,814,3.452,827,4.062,830,2.994,840,1.92,858,2.088,938,4.033,957,2.46,977,1.543,984,2.803,1100,1.677,1103,3.726,1175,1.949,1219,1.886,1264,2.766,1265,2.218,1271,3.989,1285,1.805,1312,2.913,1329,3.691,1332,2.821,1404,3.375,1629,2.947,1689,2.218,1696,2.692,1754,2.785,1781,1.429,1793,2.669,2101,3.375,2142,2.106,2223,3.619,2229,3.375,2231,7.105,2232,3.375,2250,3.61,2293,3.833,2303,3.855,2501,3.311,2816,2.983,3947,4.016,3948,3.619,3949,4.016,5550,4.561,5551,4.561,5552,4.016,5553,4.233]],["t/2974",[24,1.693,29,3.648,103,3.498,395,4.132,759,3.665,1530,6.291,2240,8.034]],["t/2976",[7,0.756,18,3.603,24,1.328,28,1.157,31,1.397,34,1.456,56,1.241,68,3.109,84,1.039,103,1.913,105,1.788,123,1.948,132,2.408,145,2.299,160,3.361,165,1.281,196,2.266,257,2.911,275,1.77,279,2.47,318,1.392,353,1.559,398,2.888,484,2.422,527,1.25,565,1.867,568,1.637,601,2.684,759,2.004,760,3.277,769,2.197,838,2.884,857,2.594,906,3.702,933,3.239,934,5.167,957,4.766,1040,3.492,1192,3.547,1236,3.811,1262,3.163,1265,2.997,1530,3.44,1633,2.694,1735,3.981,1959,2.727,2142,5.212,2227,5.71,2236,4.888,2238,4.888,2239,7.011,2240,4.393,2241,4.888,2242,4.888,2244,4.888,2245,4.656,2250,2.575,2279,5.717,2722,5.047,2833,4.888,3071,4.393,3948,4.888,3950,5.033,3954,5.033,5019,5.717]],["t/2978",[37,4.974,56,1.308,95,4.63,160,3.454,279,3.224,601,3.503,760,2.911,934,5.786,957,5.726,1040,4.558,1192,4.63,1265,3.912,1530,4.49,1754,3.172,1959,4.699,2142,4.903,2229,5.951,2230,6.381,2231,4.925,2232,5.951,2234,5.951,2235,5.735,2236,6.381,2237,6.797,2238,6.381,2239,8.424,2240,7.571,2241,6.381,2242,6.381,2244,6.381,2245,6.077,3960,7.082,5554,7.463]],["t/2980",[56,1.292,160,3.425,279,3.184,395,2.912,527,1.611,601,3.459,760,2.177,846,3.365,934,5.758,957,5.678,1040,4.501,1132,4.338,1192,4.572,1265,3.863,1275,8.142,1530,4.434,1959,4.66,2142,4.862,2234,5.876,2235,5.663,2236,6.301,2237,6.712,2238,6.301,2239,8.354,2240,5.663,2241,6.301,2242,6.301,2244,6.301,2245,6.001,2254,3.806,3962,6.993,3963,6.993,3964,6.993,5554,7.369]],["t/2982",[16,1.862,84,1.283,254,3.218,409,5.072,760,2.957,1100,3.967,1268,7.053,1456,5.273,1632,5.194,3965,9.498,3966,8.812]],["t/2984",[2,1.405,7,0.934,17,2.059,21,2.166,24,1.472,29,1.817,39,2.12,48,2.114,49,1.171,84,1.158,100,2.33,107,1.334,109,1.292,116,2.86,138,4.31,165,1.717,170,1.539,171,2.082,183,2.236,193,1.895,200,2.581,203,1.835,219,2.372,220,3.206,236,2.015,248,3.598,272,2.133,283,2.216,296,2.008,313,1.273,351,1.885,398,3.937,512,2.643,525,2.455,614,0.959,653,1.64,677,2.745,734,2.305,757,1.284,759,2.687,760,3.157,780,2.773,827,2.64,859,2.704,906,3.374,966,1.421,1019,3.926,1070,2.97,1219,2.321,1246,3.314,1297,2.526,1540,2.882,1550,3.876,1558,2.933,1698,5.703,2017,2.193,2269,4.341,2271,3.718,2368,9.109,2371,6.553,2377,6.387,2384,3.546,2448,3.258,2764,4.745,3967,4.944,5555,5.614,5556,5.614,5557,4.745]],["t/2986",[2,1.351,7,0.587,24,0.811,29,1.748,49,1.673,56,0.877,84,1.193,85,1.735,101,2.666,109,0.845,111,6.516,139,3.988,164,1.376,165,1.122,170,2.626,174,1.489,203,1.782,206,3.244,225,1.791,236,1.317,246,1.894,247,2.201,313,1.224,347,3.306,351,1.813,377,1.12,395,1.98,405,1.594,426,1.85,578,1.968,580,2.585,613,3.366,614,0.922,621,2.575,663,3.06,759,3.115,760,3.255,838,2.527,846,2.728,942,3.673,988,2.45,1019,3.615,1037,4.422,1072,2.315,1285,2.136,1364,2.504,1633,4.187,1754,2.129,1842,3.727,2252,7.32,2368,7.56,2377,6.205,2379,4.41,2388,5.98,2515,5.937,2625,5.098,3023,5.54,3906,3.727,3924,4.079,3926,7.236,3927,4.079,3928,4.079,3929,7.236,3930,4.079,5021,9.838,5022,8.887,5023,5.01,5024,5.01,5025,5.01]],["t/2988",[1781,3.73]],["t/2990",[7,0.938,84,1.094,139,4.53,170,2.983,397,4.197,759,3.539,892,3.777,993,4.084,1506,6.478,3925,7.3]],["t/2992",[24,1.75,31,2.104,56,0.85,68,3.266,113,3.141,123,2.935,139,4.849,179,2.882,183,3.697,313,2.104,527,2.707,568,2.467,632,4.634,759,3.788,760,2.545,944,3.484,945,3.575,966,2.349,1037,4.286,1100,3.414,1219,3.838,1506,5.527,1988,3.603,2276,4.941]],["t/2994",[4,3.243,28,1.475,107,2.677,109,1.763,505,4.333,760,3.089,966,2.851]],["t/2996",[12,4.179,24,1.678,109,1.747,254,3.332,512,3.573,578,4.072,614,1.908,1754,4.404]],["t/2998",[7,0.782,12,4.003,24,1.607,109,1.674,128,4.733,254,3.191,578,3.9,614,1.827,681,6.207,736,4.232,966,2.706,1425,5.768,1616,5.522]],["t/3000",[24,1.691,28,1.584,31,2.238,34,2.333,36,2.953,39,2.725,49,1.505,84,0.725,109,1.545,116,2.499,132,1.967,133,2.865,139,3.005,164,2.868,165,1.5,170,3.474,203,2.193,285,2.766,289,2.646,338,1.647,353,1.826,367,3.409,382,4.154,394,3.347,397,2.784,414,3.841,505,2.775,525,3.155,614,1.233,693,4.121,756,3.143,759,3.211,760,3.085,769,4.314,830,3.06,908,3.409,1206,3.564,1309,3.891,1329,4.808,1506,4.297,1558,3.77,1671,3.683,2231,4.419,2311,4.51,2960,4.188,3918,4.259,3922,5.725]],["t/3002",[7,0.711,21,1.85,29,2.284,31,2.849,39,2.664,49,1.471,125,3.732,139,4.631,165,1.467,170,3.774,197,2.917,203,2.662,213,3.67,236,1.721,246,4.205,272,2.68,292,2.848,304,1.38,323,2.64,338,1.61,414,3.756,491,1.973,605,2.369,614,1.205,734,2.897,759,3.162,760,3.285,769,4.909,811,4.062,867,5.031,908,3.333,1080,3.642,1083,3.174,2231,6.81,2887,3.83,3616,5.552,3918,4.164]],["t/3004",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3924,9.359,3925,6.885,3926,7.754,3927,7.754]],["t/3006",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3925,6.885,3928,9.359,3929,7.754,3930,7.754]],["t/3008",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1219,4.277,3823,6.054]],["t/3010",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,945,3.229,3823,6.155]],["t/3012",[21,1.809,56,1.212,68,3.368,107,2.274,109,2.111,139,2.874,170,1.892,254,2.059,338,1.575,353,1.746,377,1.432,596,2.884,605,2.317,614,2.261,647,3.332,653,2.016,755,2.884,760,1.892,1076,2.306,1099,4.079,1118,4.887,1224,2.353,1737,2.415,1932,6.26,2256,8.521,2900,5.982,2903,5.215,3943,9.201,3944,4.921,5015,6.714,5016,6.514,5017,6.826]],["t/3014",[7,0.489,49,1.394,56,1.262,84,1.086,103,2.075,109,1.691,160,3.045,164,1.703,170,1.832,217,3.535,279,2.679,377,1.387,398,3.06,527,1.356,565,2.024,601,2.91,760,3.209,811,3.847,934,5.358,945,2.052,957,5.826,1040,3.787,1192,3.847,1265,3.25,1367,3.911,1530,5.226,1550,4.614,1558,3.491,1793,3.911,1959,4.142,2076,3.604,2142,5.405,2227,4.317,2229,4.945,2230,5.302,2231,4.092,2232,4.945,2234,4.945,2235,4.765,2236,5.302,2237,5.648,2238,5.302,2239,7.427,2240,4.765,2241,5.302,2242,5.302,2244,5.302,2245,5.049,2722,3.817,3911,8.572,4251,5.884,5558,6.682,5559,6.682]],["t/3016",[7,0.818,34,2.058,84,0.875,103,3.472,109,1.933,123,2.753,170,3.066,410,5.332,527,1.766,565,2.638,568,2.314,630,2.984,683,3.023,723,2.888,759,3.637,760,2.387,769,3.105,1027,5.139,1072,3.734,1207,5.842,1401,4.759,1566,5.766,1754,3.434,1759,5.561,2227,5.625,2228,5.625,2293,4.727,2462,7.359,4765,9.45,5026,9.45,5027,8.08,5028,8.08]],["t/3018",[7,0.577,17,2.894,24,1.576,29,2.555,49,2.187,56,0.961,84,1.184,85,2.537,103,2.45,105,2.29,170,3.229,177,4.698,322,3.043,377,1.638,541,4.507,580,2.543,613,3.31,614,2.144,760,3.682,1633,3.451,1698,5.449,2013,2.439,2015,5.732,2227,5.099,2368,5.627,2374,9.731,2375,9.731,2376,5.963,2377,6.102,2378,7.323,2379,6.447,2380,7.323,2381,7.323,3595,2.698,5560,7.891]],["t/3020",[1781,3.73]],["t/3022",[7,0.65,12,3.327,28,1.776,84,0.894,89,3.079,105,2.58,107,2.112,174,2.452,228,2.613,372,3.087,377,1.845,390,3.983,397,3.429,405,4.005,426,3.883,496,5.039,511,3.352,536,2.457,558,3.689,622,4.161,723,3.758,892,3.087,1219,3.676,2928,6.098]],["t/3024",[16,2.273,28,1.312,183,3.99,322,2.907,405,3.603,426,3.433,495,5.469,511,2.963,670,5.402,837,5.635,871,6.816,1407,6.634,2076,5.402,3968,6.917,3969,8.821,5029,9.296,5030,9.296]],["t/3026",[405,3.326,511,3.333,757,2.576,2226,10.457,5031,10.455]],["t/3028",[7,0.739,28,1.322,49,2.106,84,1.015,109,2.15,183,4.021,405,2.981,409,4.748,426,3.46,511,2.987,622,4.726,846,2.876,993,3.79,1753,7.834,2010,3.261,5032,9.37]],["t/3030",[130,5.755,183,4.333,338,2.483,405,3.211,511,3.218,724,4.915,994,5.125,3972,7.779,5033,10.096]],["t/3032",[163,3.967,183,4.296,277,6.086,405,3.184,511,3.191,622,5.049,723,3.578,916,4.807,5034,10.009]],["t/3034",[7,0.612,16,1.881,21,2.192,28,1.681,84,1.218,107,3.242,109,1.896,132,2.969,170,3.321,228,2.458,397,3.225,405,3.576,426,3.733,505,3.215,511,2.473,820,6.121,1008,4.635,1208,6.318,1407,5.537,1462,4.197,1500,5.864,2250,4.552,5035,7.759]],["t/3036",[16,1.703,34,2.33,121,5.353,254,2.942,353,3.059,397,3.804,405,2.911,408,4.566,484,3.877,511,2.917,581,6.617,712,4.923,1632,4.749,3008,6.371,3976,11.514,3977,8.684,5036,9.151]],["t/3038",[16,1.801,343,3.443,351,3.503,405,3.079,432,7.321,476,2.81,511,3.086,583,5.911,635,3.512,757,3.064,2448,6.054,3979,7.718,5037,9.679]],["t/3040",[49,2.106,109,1.58,130,5.341,171,2.546,172,3.943,174,2.785,183,4.021,377,2.096,405,2.981,426,3.46,505,3.883,511,2.987,580,3.952,636,5.276,724,4.562,1099,4.215,5038,9.37]],["t/3042",[24,1.554,145,3.86,228,3.041,405,3.054,511,3.06,760,3.798,769,4.761,814,4.134,1182,4.578,1696,6.106,2250,4.323,5039,9.6]],["t/3044",[24,1.649,405,3.239,511,3.246,690,6.317,760,3.009,769,4.882,1696,6.477,5040,10.183]],["t/3046",[16,1.801,26,4.309,121,5.663,183,4.154,322,3.027,405,3.079,407,4.375,408,3.494,511,3.086,750,6.821,757,2.86,936,6.518,5041,9.679,5042,9.679]],["t/3048",[4,2.883,172,3.22,183,3.99,193,3.381,394,4.646,405,2.957,407,4.202,511,2.963,552,3.29,725,5.767,822,5.553,857,4.217,1077,5.203,1100,3.684,1355,5.767,3968,6.917,3984,10.317,3985,8.467,5043,9.296]],["t/3050",[5,2.715,7,0.883,16,1.873,25,2.468,28,1.088,56,0.761,84,1.09,105,2.411,125,4.394,133,3.297,144,3.691,145,3.099,164,2.117,174,2.291,196,3.055,198,4.9,228,2.442,294,4.147,372,2.884,390,3.722,458,4.34,511,4.03,536,2.295,683,2.884,807,4.591,892,3.766,966,2.102,1206,4.103,1632,4,1779,5.191,2598,6.194,3986,7.314]],["t/3052",[5,3.056,16,2.02,24,1.918,28,1.532,84,0.94,132,2.548,134,4.318,162,3.049,239,4.977,255,5.844,367,5.527,407,3.922,511,2.766,647,3.255,807,4.797,832,5.844,1078,6.274,1182,4.138,1303,5.568,2390,6.787,5044,8.677,5045,8.677]],["t/3054",[5,1.728,16,2.108,24,1.577,25,1.57,26,2.721,27,1.164,28,1.599,31,1.198,46,2.033,49,1.102,109,0.827,163,1.944,170,2.166,193,2.666,200,2.43,201,3.731,224,2.078,225,1.753,240,2.682,281,2.32,289,3.848,336,1.996,338,1.206,405,3.097,407,2.217,495,3.539,511,3.924,552,1.736,558,2.193,568,1.405,632,2.639,635,2.659,694,2.667,723,1.753,725,2.497,757,1.209,769,3.373,807,3.762,812,3.269,816,2.779,1181,2.869,1219,5.19,1406,6.743,1481,2.625,2403,2.729,2404,5.045,2584,3.456,2950,3.769,3386,3.707,3897,3.837,3968,3.65,3995,6.453,5046,4.905,5047,7.33,5048,4.905,5049,4.905,5050,4.905,5051,7.33,5052,4.905,5053,7.33,5054,4.905,5055,7.33,5056,4.905,5057,7.33,5058,4.905,5059,4.905,5060,7.33,5061,4.905,5062,4.905,5063,7.33,5064,4.905,5065,7.33,5066,4.905]],["t/3056",[16,1.519,17,1.722,24,1.762,25,1.395,26,2.488,27,1.591,28,1.688,36,1.922,41,1.977,49,1.835,84,0.472,89,2.501,103,1.458,109,1.377,132,1.28,145,2.695,162,2.355,169,2.383,203,1.043,240,2.383,281,2.062,285,1.801,286,2.642,287,4.782,289,2.648,367,2.219,377,1.499,394,2.178,405,2.597,407,1.97,408,3.309,409,2.208,432,2.749,476,1.945,511,3.815,512,2.311,527,0.953,575,3.183,580,1.513,635,2.431,653,1.372,662,2.533,693,2.682,724,2.122,731,2.284,736,1.858,751,2.516,757,1.074,760,1.98,807,2.59,945,1.442,994,2.886,1022,2.397,1178,2.516,1219,5.332,1285,1.858,1462,2.357,1481,2.332,1557,2.704,1696,2.772,1703,5.73,2593,3.726,2613,3.549,3093,3.475,3972,2.849,3979,3.475,5067,6.702,5068,4.358,5069,6.702,5070,4.358,5071,6.702,5072,4.358,5073,6.702,5074,4.358,5075,6.702,5076,4.358,5077,6.702,5078,4.358,5079,6.702,5080,4.358,5081,4.358,5082,4.358,5083,6.702,5084,4.358,5085,8.165,5086,4.358,5087,4.358,5088,4.358,5089,6.702,5090,4.358]],["t/3058",[1781,3.73]],["t/3060",[7,0.784,27,1.215,28,1.068,31,1.848,49,1.15,56,0.747,84,0.975,96,2.665,107,2.843,109,2.229,123,2.578,127,2.481,160,1.794,164,1.406,171,1.391,172,1.773,174,1.521,182,1.713,193,1.862,254,1.646,272,2.095,294,2.754,323,2.064,377,1.145,381,2.422,397,2.128,405,1.628,439,3.346,447,2.422,557,2.547,566,3.564,580,1.777,593,2.525,597,2.644,607,2.9,612,3.315,614,1.393,635,1.857,658,2.451,731,2.683,741,3.315,747,2.102,757,1.261,827,2.594,829,2.697,846,2.323,937,3.058,966,2.063,977,2.759,993,3.64,995,2.848,1099,1.895,1102,3.058,1141,3.653,1145,2.606,1175,2.357,1355,3.176,1364,2.558,1462,2.769,1753,3.523,1754,2.176,1843,5.095,2010,3.865,2117,3.933,2268,4.313,2390,4.004,2529,3.753,2577,2.697,3970,3.933,4338,4.082,5091,5.119,5092,5.119,5561,5.516,5562,5.516,5563,5.516,5564,5.516]],["t/3062",[7,0.904,34,2.732,49,1.586,56,1.316,84,0.765,109,2.125,164,2.947,233,2.554,278,2.529,295,4.314,304,1.487,408,2.548,452,6.535,495,3.408,527,1.543,614,1.299,705,4.345,756,3.313,846,2.915,908,4.833,966,1.925,983,5.424,1558,3.974,1705,4.311,2010,2.457,2015,4.158,2214,6.148,3671,5.424,3868,6.035,3869,5.424,3870,6.698]],["t/3064",[7,0.516,27,2.141,40,3.449,56,0.891,84,0.977,98,2.3,107,2.309,109,2.22,111,4.8,127,3.384,165,1.467,170,1.934,179,3.453,193,2.381,200,3.243,203,1.567,211,4.871,281,3.097,295,2.633,353,1.785,452,4.506,505,2.713,527,1.972,603,5.597,614,2.047,683,2.449,818,3.287,846,2.769,847,5.22,892,2.449,966,1.785,1132,2.907,1285,2.792,1693,3.83,2010,4.38,2191,4.321,2546,5.331,3871,8.823,3872,5.963,5565,7.054,5566,7.054]],["t/3066",[2,1.457,4,1.675,7,0.426,8,2.906,24,1.274,27,1.282,34,1.376,56,0.533,84,0.852,97,2.415,107,2.015,109,2.212,110,4.891,165,1.21,170,1.596,171,1.467,186,2.545,236,1.42,254,1.737,281,3.723,295,2.172,296,2.082,313,1.32,338,1.329,353,1.473,372,2.021,397,2.245,408,3.352,421,3.378,440,2.382,445,3.407,452,3.718,527,2.779,607,3.016,613,3.557,614,1.449,653,1.7,663,3.299,724,2.63,733,4.755,759,1.893,760,1.596,846,2.85,938,3.325,945,3.375,957,4.574,1145,2.75,1629,3.761,1843,3.638,2010,3.232,2200,4.618,2211,4.307,2214,3.498,2536,4.92,2729,4.019,3074,4.398,3874,5.126,3875,5.126,3877,3.906,3878,4.755,3879,5.126,4338,4.307,4560,4.151,5567,5.82,5568,5.82,5569,5.82,5570,5.82]],["t/3068",[4,2.216,7,0.948,28,1.008,55,5.206,56,1.138,85,2.475,107,2.45,109,1.944,115,3.29,165,1.6,170,3.407,236,1.879,282,3.886,304,1.506,397,2.97,612,4.627,614,1.315,759,2.505,760,2.828,858,3.524,865,4.812,918,4.585,1089,4.364,1090,4.793,1216,5.697,1218,4.67,1288,4.268,1332,4.762,1540,3.952,1558,4.023,1754,3.037,2010,2.487,2303,6.507,2964,6.507,3005,5.818,3044,6.109,3873,6.507,4785,5.239,5008,6.507,5571,7.699,5572,7.699]],["t/3070",[5,2.161,7,0.786,27,2.047,56,0.606,96,3.511,107,2.916,109,2.175,127,2.827,160,3.79,171,1.667,179,3.335,216,3.124,246,2.32,256,2.296,302,2.825,337,2.858,408,2.215,444,2.66,445,3.87,527,2.491,566,4.272,603,5.246,615,3.59,635,2.226,734,2.715,803,3.455,918,3.938,940,4.996,945,2.03,957,3.566,966,2.718,994,2.642,1494,5.246,1693,5.044,1793,3.87,1841,5.402,1843,4.132,2010,3.47,2191,4.049,2444,4.09,3014,5.246,3077,5.402,3763,3.903,3876,5.402,3878,9.519,3932,4.8,5093,8.622,5573,6.612]],["t/3072",[7,0.409,21,0.893,26,1.173,28,0.446,29,1.103,31,1.268,33,2.958,35,2.201,41,1.434,49,1.718,56,1.122,84,0.914,85,1.798,89,1.179,96,2.326,105,0.989,106,1.158,108,2.68,109,2.148,112,2.571,113,1.153,123,1.769,140,1.239,160,1.108,170,0.934,171,1.41,174,1.542,179,1.058,182,1.058,193,1.15,196,1.253,203,1.242,228,1.644,236,1.365,262,3.392,275,0.978,277,1.624,294,1.7,295,2.087,304,0.666,316,2.312,318,0.77,322,1.623,336,2.687,338,0.777,343,1.124,353,0.862,359,1.394,372,1.183,377,1.477,391,2.779,398,1.828,405,1.005,408,1.873,444,1.37,491,1.564,502,0.872,509,1.791,527,1.135,553,1.674,568,1.486,578,1.242,605,1.144,613,1.429,614,2.154,623,2.485,658,1.514,696,3.657,723,1.13,756,2.436,828,2.164,846,3.543,937,1.888,993,3.67,1073,2.256,1099,2.445,1100,2.617,1162,1.759,1284,2.151,1398,2.175,1558,2.922,1588,2.879,1693,3.863,1705,1.93,1982,2.429,2010,4.434,2013,1.729,2141,1.617,2142,2.582,2191,2.086,2214,3.361,2252,2.352,2268,3.764,2625,2.977,2722,1.945,2799,2.318,3604,3.273,3839,2.634,3880,2.783,3891,3.425,3903,2.999,3904,2.999,3905,2.999,3906,2.352,3907,2.52,3908,2.999,3909,2.999,3910,2.999,3911,5.645,3914,2.999,3915,2.999,3916,2.999,4338,2.52,5094,3.161,5095,3.161]],["t/3074",[49,2.23,84,1.075,109,2.119,336,4.038,391,4.178,614,2.169,993,4.014,2010,3.454,2268,5.657,2799,7.277]],["t/3076",[4,1.299,7,0.512,24,1.289,28,1.513,34,2.028,56,1.23,84,0.454,96,1.475,98,1.471,107,1.663,108,3.356,109,1.869,132,1.23,161,2.85,164,1.15,171,1.138,182,1.401,207,1.709,221,1.546,254,2.56,282,2.277,304,1.369,318,1.582,343,1.489,353,1.142,395,1.655,398,1.475,476,2.311,480,2.85,482,1.872,484,1.774,502,1.155,517,2.538,527,2.247,530,1.872,578,1.645,580,1.454,599,2.152,614,0.771,653,1.318,658,4.297,707,2.33,741,2.711,757,1.032,846,2.444,858,2.065,944,1.694,945,2.634,994,2.798,1099,4.386,1100,1.659,1132,2.885,1328,2.184,1364,2.093,1456,2.206,1737,1.579,2010,1.457,2017,2.735,2031,2.241,2141,2.142,2338,2.303,3595,1.542,3763,4.133,3771,4.993,3791,7.899,3885,3.58,3886,3.973,3887,3.58,4436,7.009,5096,6.498,5097,6.498,5098,6.498,5099,6.498,5100,6.498,5101,6.498,5102,6.498,5103,6.498,5104,6.498,5105,6.498,5106,6.498,5107,6.498,5108,6.498,5109,6.498,5110,6.498,5111,6.498,5112,4.187,5574,3.973]],["t/3078",[12,2.073,17,2.032,24,1.229,25,1.646,27,2.731,28,1.569,30,3.216,31,1.256,33,2.931,40,2.708,56,0.891,84,0.822,85,1.781,98,1.807,103,2.54,106,3.307,107,2.721,109,1.94,115,2.367,132,1.51,138,2.073,164,1.412,165,1.152,171,1.397,174,1.528,179,1.72,182,1.72,183,2.207,197,2.291,199,2.536,236,1.352,254,2.441,256,1.924,289,2.032,302,2.367,304,1.599,313,1.256,318,1.252,369,3.19,413,5.736,416,2.367,489,3.427,502,1.418,507,2.796,513,2.423,525,2.423,527,1.124,550,3.546,590,2.766,614,0.946,615,3.008,623,2.462,631,3.36,755,2.315,809,3.539,937,3.071,945,2.511,994,2.214,1008,3.071,1062,2.472,1063,4.526,1099,2.81,1101,5.952,1102,3.071,1111,6.282,1132,3.37,1222,3.951,1364,2.57,1459,3.36,1481,2.751,1737,1.939,1785,4.396,2010,1.789,2900,5.112,2903,4.186,2951,3.049,3595,1.894,3885,4.396,3890,4.526,5017,3.951,5113,5.141]],["t/3080",[27,1.931,28,1.623,56,1.029,99,5.131,109,1.757,132,2.389,162,2.859,170,2.404,171,2.21,236,2.139,304,1.714,336,3.31,338,2.001,550,3.197,614,1.498,635,2.952,636,4.581,724,3.961,740,3.677,977,2.966,994,3.503,1099,3.012,1101,6.869,1481,4.353,1508,4.5,1737,3.068,1785,6.956,2010,3.627,2990,6.365,3595,2.997,5018,6.364]],["t/3082",[4,1.884,5,2.139,7,0.781,27,2.795,34,1.546,49,1.365,50,3.553,56,0.979,68,2.302,84,0.658,98,2.134,109,1.985,126,4.133,164,2.351,171,2.325,172,4.279,203,1.454,207,4.393,240,3.321,256,3.202,272,2.486,279,2.624,282,3.303,295,2.442,304,1.28,313,2.091,316,2.706,343,3.044,353,1.656,404,4.168,408,2.192,476,1.763,480,4.133,482,2.715,489,6.607,531,3.681,578,2.386,599,4.399,614,1.118,707,3.379,734,2.687,993,3.462,1083,2.944,1364,3.035,1671,3.34,2010,2.113,3604,3.83,3887,5.192,3891,6.542,3892,5.762,3893,4.518]],["t/3084",[7,0.508,16,0.771,24,0.671,27,1.53,28,0.909,29,1.445,31,1.012,34,1.641,39,1.686,46,1.717,49,1.448,56,1.279,84,0.698,92,2.386,93,3.35,100,2.881,102,2.613,108,4.084,109,1.726,112,2.052,132,1.217,163,1.642,165,0.928,171,1.126,172,4.212,173,2.217,179,1.386,182,1.386,183,1.778,193,1.507,207,3.228,221,1.53,254,1.332,256,1.55,261,2.099,276,3.918,279,1.79,296,1.596,304,1.358,316,1.846,318,1.569,336,1.686,343,1.473,377,1.769,391,1.744,395,1.637,398,1.459,408,1.495,476,2.59,502,1.142,517,2.511,531,2.511,553,3.412,578,1.628,599,2.129,614,0.763,620,2.292,653,1.304,662,2.408,728,2.884,734,1.833,741,2.683,751,2.392,755,1.866,757,1.021,818,2.08,838,2.09,913,3.083,994,2.775,1075,4.676,1077,3.607,1100,1.642,1328,2.161,1368,2.659,1377,3.083,1456,2.183,1509,3.542,1689,3.378,1806,2.919,1857,4.487,2010,2.243,2013,1.38,2017,2.713,2031,2.217,2041,2.79,2073,2.475,2141,2.119,2191,2.734,2293,2.424,2325,3.542,2338,2.278,2344,3.542,2868,2.253,2887,2.424,3604,4.988,3763,4.099,3891,4.253,3893,3.083,3894,3.647,3895,3.373,3896,3.647,4692,3.241,5114,4.143,5575,4.143]],["t/3086",[5,2.074,7,0.768,31,2.046,35,4.101,39,2.397,49,1.324,56,0.827,68,3.696,84,1.299,98,2.07,105,1.842,109,1.79,110,3.103,164,1.618,165,1.876,172,3.377,203,2.005,207,3.419,253,3.544,256,3.134,304,1.241,317,2.944,336,2.397,377,1.317,404,2.868,513,2.775,527,1.831,552,3.45,553,3.119,576,3.469,607,3.21,611,2.448,614,1.084,623,2.82,630,2.175,638,3.544,731,3.087,769,3.219,881,3.684,902,4.526,979,3.926,1075,3.087,1077,3.296,1284,4.009,1299,4.101,1399,3.544,1411,3.814,1423,4.259,1629,4.101,1689,3.087,2010,2.05,2202,4.526,2293,3.446,2335,7.367,2469,5.036,3059,6.749,3387,4.451,3895,4.796,4119,4.796,4127,4.696,4653,5.036,5140,5.185]],["t/3088",[2,1.512,5,1.975,7,0.868,12,2.262,27,1.92,28,1.618,30,3.508,49,1.26,56,0.799,84,1.027,107,1.436,109,1.599,113,3.459,170,2.389,171,2.197,172,3.596,203,1.342,224,3.426,236,1.475,246,3.058,304,1.182,318,1.365,336,2.282,338,1.379,351,2.029,391,2.361,432,3.537,505,2.324,511,1.788,525,2.643,552,3.357,597,2.896,599,2.882,614,1.911,636,3.158,724,2.731,725,5.838,807,3.665,832,3.777,854,4.112,944,3.271,994,2.415,1075,2.939,1077,3.139,1093,3.177,1098,3.537,1181,3.281,1285,2.391,1302,3.399,1481,4.328,1508,3.102,1737,2.115,2010,2.815,2904,4.055,2928,2.954,2947,4.673,2990,3.425,3595,2.066,3604,3.537,3891,3.701,3897,4.387,5015,7.168,5018,4.387]],["t/3090",[0,3.09,4,1.66,5,2.754,7,0.852,24,0.867,27,2.192,28,1.103,31,1.308,34,1.363,49,1.203,56,0.772,84,0.847,109,1.822,113,1.952,123,1.824,140,2.097,162,1.881,164,1.47,170,2.31,171,2.509,172,3.199,173,4.942,174,2.324,184,4.045,193,2.843,203,1.281,228,1.695,236,1.407,255,3.604,261,2.712,275,1.657,289,2.115,296,2.063,304,1.128,320,3.07,323,2.159,336,2.178,338,1.317,353,1.459,377,1.197,426,1.977,432,3.376,484,3.312,505,2.218,550,2.103,568,1.533,596,2.41,614,1.989,632,2.879,636,3.014,724,2.606,725,2.725,728,3.727,755,3.521,802,2.165,828,2.232,854,3.924,858,2.64,908,2.725,944,2.165,977,1.952,994,2.305,1093,5.232,1182,2.553,1481,4.183,1508,2.961,1737,2.018,2010,2.721,2876,6.234,2946,4.268,2990,3.269,3595,1.972,3604,3.376,3891,3.532,3893,3.982,4574,4.268,5015,4.045,5016,5.732,5018,4.187]],["t/3092",[24,1.248,28,1.088,34,1.963,56,0.994,107,2.869,109,1.697,113,2.811,193,2.803,246,3.806,285,3.184,295,3.099,304,1.624,338,1.896,351,3.642,477,4.572,527,2.2,576,4.54,605,2.789,614,1.419,621,3.961,755,4.533,765,4.572,859,5.223,944,3.118,945,2.55,994,3.319,1364,3.853,1540,4.264,1737,2.907,1988,3.224,2010,3.503,2259,5.138,2276,5.773,3595,2.839,4785,5.652,5009,6.277]],["t/3094",[28,1.389,56,0.972,107,2.52,109,1.659,304,2.074,527,2.152,755,4.432,945,3.256,1737,3.711,1988,4.117,2010,3.425,2276,5.645,3595,3.625,5009,8.014]],["t/3096",[7,0.66,16,1.096,24,0.954,25,1.886,27,1.988,28,1.375,31,2.046,34,1.5,41,2.672,56,0.827,84,1.056,107,2.496,109,1.892,113,2.148,123,2.854,127,3.679,131,2.672,132,2.863,162,2.07,165,1.319,171,1.6,193,2.142,213,2.397,225,2.105,256,2.204,295,2.368,302,2.712,304,1.241,318,1.434,377,1.317,433,3.4,472,2.868,491,1.775,524,3.625,536,1.754,557,2.931,565,3.183,614,1.794,615,3.446,628,3.042,630,3.093,635,2.137,810,2.615,845,3.746,900,5.185,1008,3.518,1009,4.009,1021,4.009,1121,3.57,1144,3.203,1225,4.382,1274,4.696,1737,2.221,1754,4.513,1857,5.832,2010,2.05,2076,3.423,2331,3.746,2966,4.696,3009,4.908,3595,2.17,5010,7.938]],["t/3098",[7,0.731,16,1.268,27,2.501,28,1.596,34,1.735,41,3.091,56,1.04,84,1.004,107,2.373,109,1.994,113,2.484,127,4.002,131,3.091,132,2.722,165,1.526,193,2.478,225,2.435,256,2.549,302,3.137,304,1.436,313,1.664,318,1.659,338,1.676,433,3.933,472,3.317,524,4.193,536,2.029,557,3.39,565,3.026,615,3.986,630,2.516,635,2.472,845,4.334,1008,4.07,1009,4.637,1021,4.637,1737,2.569,1754,4.478,1857,6.454,2331,4.334,3009,5.677,3595,2.51,5010,5.548]],["t/3100",[28,1.424,56,0.997,109,1.702,127,3.311,304,2.127,755,4.547,1737,3.807,2010,3.514,2276,5.791,3595,3.719,5115,9.195]],["t/3102",[28,1.4,56,0.98,107,2.541,109,1.987,172,3.438,304,2.091,318,2.416,338,2.441,502,2.737,1737,3.743,3595,3.656,5011,8.486]],["t/3104",[28,1.424,56,0.997,172,3.497,254,3.246,304,2.127,338,2.483,502,2.784,1737,3.807,2010,3.514,3595,3.719,5012,9.195]],["t/3106",[28,1.412,56,0.988,109,1.688,172,3.467,304,2.109,502,2.76,755,4.508,1737,3.775,2010,3.484,2276,5.742,3595,3.687,5116,9.117]],["t/3108",[28,1.4,56,0.98,107,2.541,109,1.987,254,3.191,304,2.091,338,2.441,502,2.737,1099,3.675,1737,3.743,3595,3.656,5013,8.486]],["t/3110",[28,1.424,56,0.997,254,3.246,304,2.127,338,2.483,502,2.784,1099,3.738,1737,3.807,2010,3.514,3595,3.719,5014,9.195]],["t/3112",[28,1.412,56,0.988,109,1.688,304,2.109,502,2.76,755,4.508,1099,3.706,1737,3.775,2010,3.484,2276,5.742,3595,3.687,5117,9.117]],["t/3114",[16,1.651,28,1.252,84,0.961,107,2.818,109,1.856,170,2.622,196,3.517,246,3.355,304,1.87,338,2.183,405,2.823,408,3.974,527,2.407,614,1.634,936,5.976,994,4.74,1737,3.347,2010,3.088,2211,7.076,3595,3.269,3971,6.603,3972,5.8]],["t/3116",[2,1.505,7,0.746,12,2.251,16,1.039,28,1.137,41,2.532,56,1.165,84,1.122,107,1.429,109,2.076,132,2.367,170,2.381,179,1.868,196,2.212,203,1.336,225,1.995,226,2.706,246,3.048,257,2.841,300,3.383,304,1.698,322,1.746,338,1.982,377,1.802,395,2.206,408,2.909,447,2.64,605,2.02,614,1.484,705,3.435,709,3.648,765,3.31,818,5.516,845,3.55,846,3.373,896,4.092,966,1.522,1037,2.777,1052,6.891,1539,3.72,1718,3.72,2010,4.206,2015,4.747,2169,5.084,2656,5.219,3023,7.039,3763,3.55,3906,4.153,4785,4.092,4850,4.545,4860,8.616,5118,5.581,5119,5.581,5120,5.581,5121,5.581]],["t/3118",[56,0.988,107,2.563,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1132,4.444,1737,3.775,2012,7.339,3595,3.687]],["t/3120",[56,0.988,127,3.282,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1132,4.444,1737,3.775,2011,7.448,3595,3.687]],["t/3122",[56,0.98,261,5.029,304,2.091,353,2.706,536,3.509,755,4.47,1132,4.407,1329,4.57,1737,3.743,1959,4.733,3595,3.656,5122,8.486]],["t/3124",[34,2.571,107,2.585,170,2.983,304,2.127,536,3.006,552,4.213,755,4.547,1737,3.807,3595,3.719,5576,9.195]],["t/3126",[56,1.118,84,1.007,107,2.38,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,966,2.535,993,3.76,1402,5.296,1622,5.965,2012,6.816,2960,7.085,3901,7.948]],["t/3128",[56,1.118,84,1.007,127,3.048,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,966,2.535,993,3.76,1402,5.296,1622,5.965,2011,6.917,2960,7.085,3902,7.948]],["t/3130",[56,1.113,84,0.999,140,3.614,196,3.655,304,1.943,536,3.626,614,1.698,755,4.154,966,2.515,993,3.73,1329,4.246,1402,5.271,1622,5.918,1959,4.398,2960,7.051,5122,7.885,5123,8.4]],["t/3132",[56,1.113,84,0.999,107,2.361,140,3.614,196,3.655,304,1.943,536,3.626,552,3.264,614,1.698,755,4.154,966,2.515,993,3.73,1402,5.271,1622,5.918,2960,7.051,5576,8.4,5577,9.223]],["t/3134",[24,0.781,26,1.791,28,0.681,29,1.683,31,1.179,48,1.957,49,1.084,56,1.113,105,1.509,107,1.853,109,1.628,112,3.586,123,1.644,127,1.582,140,1.89,162,1.695,165,1.622,172,2.508,173,2.581,174,2.152,193,1.755,203,1.733,206,3.124,318,2.116,322,4.121,336,1.963,338,1.187,377,1.079,391,2.031,395,1.906,398,3.401,502,1.33,527,1.055,593,2.379,613,2.181,614,1.778,623,2.31,755,2.173,846,1.481,993,1.951,1073,5.166,1099,3.575,1145,2.456,1394,3.069,1481,2.581,1633,4.094,2010,3.601,2013,2.411,2015,2.842,2141,3.703,2142,4.805,2268,2.75,2276,2.767,2577,2.541,2722,4.456,3604,4.566,3891,4.777,4785,5.308,5009,5.895,5010,5.895,5011,6.189,5013,6.189,5016,5.308,5115,6.593,5116,6.593,5117,6.593,5124,8.689]],["t/3136",[1781,3.73]],["t/3138",[7,0.958,15,3.812,572,5.536,1090,5.394,3831,6.77]],["t/3140",[4,2.489,7,0.814,27,1.905,49,2.322,56,0.792,84,0.869,100,3.588,103,3.456,144,3.842,236,2.11,243,4.388,283,2.32,394,4.011,401,4.367,405,3.286,437,3.766,481,4.793,492,6.277,511,2.558,683,3.002,723,2.868,736,3.422,977,2.926,1923,5.499,2013,2.673,3070,8.303,3553,5.726,3554,5.726,3555,6.399,3831,6.689]],["t/3142",[7,0.889,27,2.189,49,2.073,56,0.911,100,4.124,243,5.044,246,3.487,277,4.74,401,3.525,405,3.586,511,2.94,723,3.296,1072,4.262,1182,4.398,1754,3.92,3556,6.086,3831,7.3,4015,10.698]],["t/3144",[7,0.761,17,2.858,27,1.717,39,2.943,56,0.714,84,0.783,100,4.856,103,3.229,106,2.651,144,3.463,228,2.291,243,3.956,394,3.615,405,3.07,410,4.773,437,3.395,511,2.306,527,1.581,683,2.706,723,3.449,760,3.209,769,2.78,802,2.926,945,2.393,977,2.637,1094,3.615,1754,3.074,1759,6.642,1923,5.137,2013,3.214,2227,5.036,2228,5.036,2293,4.231,2384,4.923,3468,7.695,3831,6.249,4016,7.963,4017,9.561,4018,6.367,4019,6.367]],["t/3146",[7,0.739,17,2.74,27,1.646,39,2.821,56,0.685,84,0.751,100,4.754,103,3.139,106,2.541,109,1.921,144,3.32,170,2.049,228,2.196,243,3.792,394,3.466,405,2.984,410,4.576,437,3.254,511,2.21,527,1.516,683,2.594,723,3.353,760,3.141,769,2.665,802,2.805,945,2.294,977,2.528,1027,5.967,1094,3.466,1754,2.947,1759,6.456,1923,4.993,2013,3.125,2227,4.828,2228,4.828,2293,4.056,2384,4.72,3468,7.48,3831,6.074,4016,5.084,4017,9.359,4018,6.104,4019,6.104,5026,8.544]],["t/3148",[7,0.674,10,4.625,21,2.416,24,1.385,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,840,3.879,1754,4.572,1923,4.551,1991,5.699,2013,2.848,3831,6.965,4020,10.207]],["t/3150",[7,0.674,10,4.625,21,2.416,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,170,2.526,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,840,3.879,1754,4.572,1923,4.551,1991,5.699,2013,2.848,3831,6.965,5167,10.757]],["t/3152",[1781,3.73]],["t/3154",[7,0.745,39,3.843,49,2.571,84,1.023,228,2.992,278,3.384,401,3.61,723,3.376,888,4.976,1094,5.717,1511,6.174,2388,4.951,4021,8.603,4022,9.624,4030,8.603]],["t/3156",[7,0.948,34,2.03,49,2.559,56,0.787,84,1.233,103,2.667,203,1.908,228,2.525,278,2.856,283,2.304,382,4.944,393,3.914,401,3.046,408,2.877,513,3.755,580,2.767,614,1.467,723,4.07,888,5.418,1094,6.014,1369,6.124,1504,5.616,1511,7.443,2388,4.177,2621,5.763,4021,7.259,4022,6.234]],["t/3158",[2,1.463,7,0.622,34,1.382,39,2.208,49,1.775,56,0.78,68,2.994,84,1.107,100,2.427,113,2.879,144,2.599,165,1.216,186,2.557,203,2.87,213,2.208,225,1.94,226,2.631,233,1.964,302,2.499,313,1.326,322,2.47,332,2.816,373,3.924,377,2.082,491,2.806,613,2.453,614,2.087,647,2.036,659,3.514,736,2.314,763,3.694,846,2.424,888,5.973,966,2.153,995,3.019,1035,3.241,1083,2.631,1094,2.712,1117,3.617,1119,4.245,1220,3.113,1265,2.844,1353,3.581,1375,3.289,1737,2.978,1799,6.927,1988,2.27,2000,3.394,2010,2.748,2015,3.196,2343,6.58,2362,5.375,2388,5.943,2389,4.943,2430,3.34,2625,3.113,3167,4.522,3595,1.999,3804,3.778,3858,2.816,4022,4.245,4023,5.149,4024,5.149,4025,5.149,4026,5.149,4027,5.149,4028,5.149,4029,5.149]],["t/3160",[2,1.915,7,0.946,15,2.589,21,2.007,49,2.418,85,3.302,103,2.376,106,2.603,113,2.589,179,2.376,203,2.282,228,2.249,283,2.756,313,1.735,332,3.685,382,4.406,401,4.586,481,4.242,491,2.873,512,2.448,562,3.487,614,2.209,628,3.667,723,2.538,736,3.028,820,3.65,888,3.741,944,2.873,1206,3.78,1254,5.004,1565,4.944,1633,4.492,1923,3.78,2013,3.175,2388,3.722,3070,4.888,3553,5.068,3554,5.068,4022,5.555,4030,6.468]],["t/3162",[7,0.81,49,2.309,283,2.97,401,3.926,512,3.542,736,4.38,1254,7.239,3553,7.33,3554,7.33]],["t/3164",[7,0.853,16,1.338,24,1.164,28,1.014,34,1.831,49,1.616,56,0.71,68,2.725,84,1.252,105,3.386,162,2.526,219,2.225,246,2.718,256,2.69,261,4.87,285,2.97,295,2.891,318,2.636,353,2.621,377,1.608,398,2.532,401,2.748,512,2.478,536,2.141,614,1.323,734,3.181,759,3.795,828,2.998,966,1.96,988,3.515,1019,2.641,1132,3.192,1236,4.792,2000,6.011,2013,2.394,2141,4.916,2142,4.782,2722,5.915,2744,5.433,3071,5.524,4032,6.547]],["t/3166",[7,0.822,49,1.828,56,1.197,84,0.881,140,3.188,283,2.352,318,1.981,336,3.31,377,1.819,393,3.995,398,2.866,401,3.11,568,2.33,683,3.044,736,3.469,888,4.286,966,2.218,993,3.291,1100,3.224,1632,5.409,2000,5.088,2142,4.048,2359,8.006,2722,5.007,2744,6.148,3554,5.806,3555,8.311,4033,7.72,4034,6.956,4035,6.956,4036,7.72]],["t/3168",[49,2.089,84,1.007,161,6.327,201,4.732,283,2.688,318,2.263,398,3.274,401,3.553,736,3.964,966,2.535,1109,7.948,2000,5.814,2142,4.625,2359,6.472,2451,7.948,3553,8.084,3804,6.472,4034,7.948,4035,7.948,4037,8.821]],["t/3170",[7,0.824,15,3.812,49,2.35,279,4.517,395,4.132,614,1.925,828,4.36]],["t/3172",[2,1.491,7,0.436,17,2.185,23,2.672,24,1.772,28,0.78,48,2.243,49,1.799,56,1.162,84,0.867,98,1.943,113,2.016,162,2.813,165,1.793,171,1.502,174,1.643,203,2.252,219,1.711,228,2.536,232,2.011,246,3.558,248,2.595,272,2.263,313,1.956,338,2.537,377,1.236,433,3.192,524,3.403,530,2.472,536,1.647,545,2.555,568,1.583,614,1.018,705,5.791,757,2.318,831,3.487,843,6.436,857,3.631,966,1.508,993,2.236,1019,2.031,1037,2.751,1100,2.191,1235,4.054,1266,2.397,2000,7.137,2017,3.37,2144,5.036,2176,4.607,2214,3.58,2515,4.409,2744,6.05,2894,4.607,3607,4.054,3858,4.883,4038,4.727,4039,5.247,4040,7.596,4041,5.247,4042,5.247,4043,5.247,4044,5.247]],["t/3174",[7,0.796,56,0.997,304,2.127,397,4.197,580,3.505,651,6.729,966,2.753,989,5.684,1123,7.011,1297,4.895]],["t/3176",[7,0.832,304,2.223,438,5.475,651,7.032,966,2.877,1992,7.973]],["t/3178",[84,1.057,92,3.614,93,4.942,200,4.835,207,4.761,261,4.945,304,2.057,313,2.384,353,2.661,614,1.797,620,5.399,966,2.661,1182,4.654]],["t/3180",[16,1.895,68,3.861,85,3.527,165,2.281,304,2.146,317,5.09,338,2.505,614,1.875,2202,7.825,2268,5.805]],["t/3182",[5,2.947,21,2.364,28,1.644,36,3.69,56,0.826,96,2.947,98,2.94,106,4.27,107,2.142,109,1.789,170,3.135,186,3.943,203,2.003,256,3.97,296,3.225,304,1.763,343,2.976,368,6.324,404,4.074,513,3.943,568,2.396,606,6.43,611,3.478,694,4.551,774,4.526,858,4.127,3091,6.545,4045,6.814]],["t/3184",[109,1.763,203,2.503,304,2.203,404,5.091,846,3.21,1705,6.385,3868,8.939]],["t/3186",[7,0.925,15,2.721,21,2.784,24,1.786,26,2.77,36,3.291,46,5.055,56,1.089,90,3.517,98,2.622,100,3.337,106,2.735,112,4.881,144,3.574,183,3.203,186,3.517,203,1.786,217,4.254,275,2.31,279,3.224,304,2.076,359,3.291,404,3.634,408,2.694,527,2.565,607,2.86,622,3.764,719,4.49,883,6.57,945,2.469,1083,3.618,1253,4.458,1475,4.015,4045,6.077,4046,7.082]],["t/3188",[7,0.592,24,1.602,28,1.06,46,3.112,56,0.742,68,2.848,70,5.36,106,2.753,145,3.02,165,1.682,186,3.539,196,2.977,203,1.798,206,4.864,219,2.325,232,2.732,277,3.86,289,2.968,292,3.267,304,1.583,336,3.056,343,2.671,408,2.711,491,2.264,534,5.36,567,3.011,596,3.383,614,1.383,757,1.851,844,6.116,941,5.989,1074,5.431,1083,4.797,1113,5.36,1219,3.347,1266,3.256,1292,5.058,1462,4.062,1632,5.135,1931,5.431,2250,3.383,2387,5.875,3027,5.588,4338,5.989,5126,9.895,5127,7.511,5128,7.511]],["t/3190",[7,0.604,24,1.623,31,2.448,36,3.376,39,3.116,46,3.173,84,0.829,106,2.806,123,3.415,145,3.079,160,2.684,186,3.608,196,3.035,203,1.833,206,4.959,219,2.37,232,3.645,289,3.026,292,3.331,304,2.112,404,3.728,408,3.618,491,2.308,511,2.441,528,4.034,567,3.07,596,3.449,749,4.959,757,2.469,798,4.212,939,4.365,984,3.276,1019,2.814,1083,3.713,1266,3.32,2250,3.449,2350,5.615,4236,6.741,4539,6.381,5129,10.022]],["t/3192",[24,1.594,27,2.336,28,1.389,126,4.752,128,4.693,304,2.47,367,5.01,404,4.792,650,6.628,825,7.562,3386,7.438,4047,9.339,4048,9.339]],["t/3194",[7,0.762,16,1.08,21,2.98,28,1.574,32,3.629,39,3.934,56,0.818,68,3.142,100,2.594,107,1.485,127,1.903,165,1.3,181,3.287,183,2.49,197,2.585,203,1.984,207,2.368,223,3.267,253,3.491,304,1.746,315,2.756,316,2.585,336,2.361,337,2.702,339,2.927,343,3.967,379,6.906,404,2.825,430,2.862,477,3.441,480,3.949,482,2.594,502,1.6,508,3.418,555,2.745,577,4.989,613,3.746,614,1.526,623,2.778,635,2.105,690,3.6,694,3.156,723,3.769,747,3.403,757,2.042,778,3.659,838,2.927,856,6.651,984,2.482,1083,2.813,1099,2.148,1219,2.585,1402,2.713,1693,3.394,2277,3.907,2484,4.458,2887,3.394,3005,4.725,3386,4.385,4049,9.174,4050,4.725,4051,5.285]],["t/3196",[5,3.223,7,0.721,24,1.817,28,1.291,31,2.235,68,3.469,84,0.991,112,4.533,165,2.05,169,5.004,254,2.942,285,3.78,304,2.364,338,2.251,403,5.872,404,4.455,528,4.821,614,1.685,778,5.771,1100,3.627,1689,4.796]],["t/3198",[2,1.374,7,0.708,17,2.014,23,1.701,24,1.715,28,1.267,31,1.245,56,0.98,84,1.147,85,1.765,123,1.737,131,2.312,140,1.997,164,1.4,165,1.142,197,2.271,203,2.375,219,2.779,228,2.389,232,1.854,254,1.639,275,1.578,281,2.411,285,4.97,286,3.089,304,1.074,342,3.331,377,1.14,409,3.821,491,1.536,505,2.112,513,2.402,527,2.424,536,3.417,555,2.411,565,1.664,568,1.459,578,2.003,580,1.77,596,2.295,623,2.44,628,2.632,757,2.445,759,1.787,764,3.432,769,1.959,804,4.358,827,2.583,846,1.565,857,2.312,859,2.645,945,3.504,963,3.089,966,1.39,981,3.432,1132,2.263,1163,3.432,1540,2.819,1988,2.132,2013,1.698,2017,4.667,2031,2.727,2113,3.508,2252,3.792,2387,3.987,2529,3.737,3872,4.642,5168,4.487,5458,4.642,5578,8.126,5579,5.492]],["t/3200",[7,0.705,24,1.945,28,1.262,109,1.508,113,3.26,162,3.887,165,2.003,171,3.005,304,1.884,381,4.23,527,1.955,724,4.354,759,3.134,760,2.642,937,5.341,945,2.958,1402,4.18,1737,3.372,1988,3.74,2215,8.144,2276,6.345]],["t/3202",[7,0.868,24,1.783,41,4.026,56,0.876,113,3.236,139,3.982,203,2.124,236,2.333,239,5.09,289,3.507,304,1.87,367,4.518,430,4.377,527,1.94,614,1.634,662,5.157,759,3.111,760,2.622,939,5.058,1737,3.347,1754,4.679,1959,4.232,1988,3.712,2276,5.09]],["t/3204",[7,0.956,15,3.337,20,7.017,21,2.586,29,3.192,50,5.353,84,0.991,106,3.354,116,3.415,304,2.364,408,4.05,580,3.177,614,1.685,796,4.796,1083,4.437,5130,9.151,5131,9.151,5132,9.151]],["t/3206",[7,0.839,85,3.687,278,3.814,580,3.696,1019,3.911]],["t/3208",[8,4.506,16,1.096,23,2.796,56,0.827,96,2.074,140,2.308,177,2.844,179,1.971,219,3.017,275,1.823,304,2.665,318,2.039,366,3.684,393,2.892,438,5.51,447,2.786,500,2.786,545,2.722,548,6,552,2.965,555,2.786,558,4.748,614,1.084,802,2.382,846,2.571,1019,4.582,1237,5.424,1266,4.602,1297,2.856,1299,4.101,1330,5.475,1413,3.296,1531,3.926,1638,3.042,1954,4.796,1989,4.607,1992,4.451,2016,4.796,2340,3.625,2887,7.016,4052,5.589,4053,7.948,4054,4.796,4055,5.589,4056,6.33,4057,5.036,5133,5.89,5134,5.89]],["t/3210",[113,3.417,248,5.755,275,2.9,304,1.974,353,2.555,484,3.97,1019,3.443,1206,4.987,1309,5.445,1400,6.363,1401,7.222,1638,4.839,1889,6.972,2745,7.329]],["t/3212",[7,0.818,34,2.058,56,1.132,84,0.875,85,3.594,97,3.613,224,3.423,254,2.598,322,2.527,353,2.203,482,3.613,495,3.901,572,4.278,580,2.805,611,3.359,614,1.487,709,5.281,762,5.441,1019,3.812,1132,4.607,1178,4.665,1220,4.635,1402,4.851,1633,3.807,1781,2.728,2359,8.71,4058,7.667,4059,7.359,4060,7.667]],["t/3214",[7,0.727,84,0.999,171,2.506,246,3.487,304,1.943,491,2.78,580,3.914,614,1.698,723,3.296,1019,3.389,1094,4.61,1511,6.028,1638,4.763,1737,3.478,1923,4.909,2013,3.072,3595,3.397,3805,9.437]],["t/3216",[7,0.71,16,1.677,84,0.976,171,2.448,219,2.789,304,1.899,318,2.194,491,2.716,580,3.859,614,1.659,723,3.22,1094,4.503,1401,6.546,1511,5.889,1638,4.653,1737,3.398,1923,4.796,2013,3.001,2355,11.939,3595,3.319]],["t/3218",[2,1.345,7,0.393,15,1.819,16,1.649,36,2.199,48,2.024,49,1.992,56,0.875,84,1.234,133,2.134,160,1.749,165,1.117,179,1.669,197,2.223,219,2.297,224,2.113,246,2.807,248,4.161,255,3.359,275,2.297,302,2.297,313,1.813,317,2.493,318,2.678,377,1.66,438,4.6,441,2.713,495,4.74,572,2.641,580,1.732,614,0.918,655,3.26,724,2.429,736,2.127,744,2.843,757,1.229,802,2.018,892,2.777,981,3.359,1019,4.596,1098,4.681,1123,5.221,1172,3.001,1292,6.612,1297,2.418,1324,3.902,1400,4.6,1401,4.372,1475,2.683,1781,1.684,2001,4.157,2016,4.062,2026,5.168,2093,3.395,2355,8.412,2359,6.172,2577,2.628,2598,4.568,2641,3.56,2893,4.157,3805,7.305,4009,3.473,4061,4.734,4062,4.734,4063,4.734,4064,4.734,4065,4.734,4066,4.734,4067,7.043,4068,4.734]],["t/3220",[23,3.231,203,2.779,318,2.357,322,3.631,614,2.137,1019,4.265,2887,6.791,3595,4.276,4069,9.185,4070,9.185]],["t/3222",[2,1.52,7,0.444,16,1.049,23,2.709,56,0.556,219,3.221,246,2.131,278,2.019,283,1.629,304,2.701,313,1.377,318,1.976,545,2.604,548,3.468,552,2.872,614,1.037,756,6.016,892,2.108,917,3.192,1019,2.982,1237,5.255,1266,4.123,1292,3.795,1400,2.924,1413,7.098,1954,4.589,2015,7.549,2296,4.961,2598,3.468,3805,6.038,4057,4.818,4071,5.347,4072,4.961,4073,5.347,4074,5.347,4075,5.347,4076,5.347,4077,5.347,4078,5.347,4079,5.347,4080,5.347,4081,5.347,4082,5.347,4083,5.347,5135,5.635,5136,5.347,5137,5.635,5138,5.347,5580,6.072]],["t/3224",[2,2.093,7,0.797,24,1.256,25,2.484,28,1.095,35,5.402,39,4.113,49,2.272,84,0.84,109,1.308,116,2.895,197,3.457,304,1.635,313,1.896,336,3.157,391,3.266,404,3.778,505,3.215,614,1.861,635,2.815,743,6.671,760,2.292,802,3.138,830,3.546,1403,6.187,1404,6.187,1406,5.962,1407,7.213,1671,4.267,2010,2.7,2123,7.067,2142,3.861,2584,5.468,2902,6.318,2950,5.962,3643,7.067,4084,9.592,4085,7.363,4086,7.363]],["t/3227",[7,0.8,10,4.225,17,3.087,24,1.265,28,1.102,31,1.908,84,1.1,102,4.926,123,2.662,133,3.341,162,2.745,172,2.706,246,2.953,257,3.976,285,3.227,304,1.646,318,1.902,336,3.178,338,1.921,375,5.899,393,3.836,470,5.648,502,2.154,527,1.708,568,2.237,614,1.438,690,4.846,759,2.738,760,2.308,901,3.836,994,3.363,1099,2.892,1274,6.228,1402,4.746,1409,5.155,1733,4.666,1737,2.946,2887,4.569,2966,6.228,3595,2.877,4087,7.412]],["t/3229",[7,0.836,10,4.526,24,1.355,48,3.395,56,0.826,84,0.906,139,3.755,170,2.472,171,2.273,213,3.405,223,4.711,304,1.763,409,4.24,580,2.905,614,1.54,646,8.368,683,3.131,734,3.702,736,3.568,759,3.719,760,3.442,1539,5.577,1695,7.366,1737,3.156,2376,6.814,3008,5.826,3595,3.082,3607,6.135,4032,7.621,5581,8.368,5582,9.016]],["t/3231",[5,2.715,7,0.608,16,1.434,25,3.222,84,1.287,162,2.708,164,2.117,171,2.734,219,2.386,236,2.027,304,1.624,318,2.892,377,1.724,398,2.715,614,2.063,683,2.884,723,3.597,724,3.753,731,4.04,757,2.48,988,3.769,1019,3.698,1289,4.903,1328,4.02,1402,4.705,1475,4.147,1629,5.367,1737,2.907,2017,3.245,2609,6.277,3595,2.839,4009,5.367,4088,6.59]],["t/3233",[7,0.684,49,1.95,84,1.176,171,2.357,192,5.111,248,4.073,285,4.485,304,1.828,336,3.531,536,2.584,605,3.14,614,1.597,662,5.043,731,4.548,1123,6.979,1400,6.148,1402,4.057,1471,7.639,1475,4.668,1540,4.8,1737,3.272,3595,3.196,4009,6.041,4014,7.419]],["t/3235",[7,0.764,31,2.661,35,5.067,37,4.851,39,4.433,56,1.147,63,5.592,68,2.759,84,1.05,95,4.515,100,3.254,109,1.227,203,2.32,236,1.914,283,2.104,377,2.436,481,4.347,614,1.784,734,3.22,881,4.552,966,1.984,1027,4.629,1595,8.826,1689,5.71,1754,3.093,1797,5.803,2384,4.954,2459,5.693,2621,5.263,2672,5.263,3397,5.693,3398,5.263,3604,4.59,3895,5.926,4089,6.906,4090,6.906,4091,6.906,4092,6.906,4093,6.906,4094,6.906]],["t/3237",[7,0.872,113,3.26,181,5.065,203,2.648,334,5.065,491,2.695,512,3.083,614,1.646,846,2.745,888,4.711,966,2.438,1119,6.994,1182,4.264,1642,7.645,1754,3.8,1799,6.653,2010,3.85,2388,4.687,3858,4.64,4095,8.485,4096,7.451,4097,7.451,4098,8.485]],["t/3239",[1,3.783,5,2.293,7,0.811,25,3.294,56,0.887,84,0.973,126,3.143,131,5.281,135,3.711,157,3.759,162,2.288,165,1.458,174,2.671,203,1.558,254,2.889,314,5.191,322,2.036,341,3.581,369,4.039,370,5.093,375,3.783,449,4.007,476,1.89,500,3.08,525,3.068,532,3.395,555,3.08,590,3.502,611,2.706,614,1.199,621,3.346,623,3.118,675,4.141,677,3.43,683,2.436,830,2.975,859,3.379,1090,2.891,1112,6.103,1113,4.646,1163,4.384,1222,5.003,1230,4.92,1297,3.157,1319,4.432,1320,5.003,1527,4.297,1601,3.889,2013,2.169,2276,3.735,2384,4.432,2549,5.732,2887,5.257,3249,5.301,3493,5.425,4099,6.178,4100,5.425,4101,6.178,4102,8.527]],["t/3241",[1781,3.73]],["t/3243",[7,0.811,15,3.276,16,1.211,21,1.84,24,1.455,26,2.417,28,0.919,34,1.658,58,4.708,70,7.343,84,0.973,98,2.288,99,4.106,106,3.293,145,3.613,158,6.016,164,1.788,175,5.627,203,1.558,242,4.588,246,2.462,304,1.372,343,3.196,353,2.45,381,3.08,401,4.6,402,5.093,408,4.202,410,4.297,412,4.844,413,4.92,416,2.998,453,4.178,532,3.395,536,1.939,572,3.447,605,2.356,744,3.711,794,4.533,840,2.953,944,2.633,977,2.374,1132,2.891,1206,3.465,3063,4.708,3091,5.093,4103,6.178,4104,6.178]],["t/3245",[0,3.177,2,1.484,7,0.74,16,1.484,24,1.292,28,0.776,33,3.137,34,1.401,41,2.496,56,0.927,84,0.596,97,3.567,98,1.934,131,2.496,134,2.738,138,4.595,153,2.815,162,1.934,175,3.009,220,3.387,221,2.032,245,3.287,304,1.681,316,2.452,332,4.14,353,2.559,359,2.426,365,5.536,394,2.75,396,6.24,403,3.531,407,3.606,408,2.88,409,2.788,438,5.912,439,5.214,440,3.517,441,5.942,442,4.585,443,3.878,444,2.385,445,3.471,446,3.5,447,2.603,448,5.012,449,4.91,450,7.266,451,4.585,452,3.787,453,5.119,454,5.012,459,4.304,484,2.331,536,1.639,580,2.77,614,1.013,637,3.531,742,4.989,1021,3.746,1123,3.241,1313,3.198,1324,4.304,1337,3.668,1355,3.414,2307,4.844,4105,4.481,4106,5.222,4107,4.705,4108,4.304]],["t/3247",[7,0.653,16,1.08,21,1.639,24,1.342,26,2.154,28,0.819,29,2.024,31,1.417,44,4.961,56,0.818,84,0.898,97,3.706,105,1.815,132,1.704,138,4.253,153,2.968,175,3.173,189,3.105,199,2.862,224,4.468,239,3.328,294,3.121,340,3.867,353,2.636,365,4.199,377,1.298,394,2.9,402,7.562,403,3.723,405,2.636,438,4.301,439,5.417,441,4.507,442,4.835,443,4.089,451,4.835,452,3.993,455,8.587,456,10.159,466,4.089,496,3.544,504,3.517,557,2.887,614,1.068,742,5.183,744,3.307,757,1.43,778,3.659,807,2.242,1123,3.418,1297,2.813,1299,4.04,1355,3.6,1632,3.011,2076,3.372,3464,5.285,4105,4.725,4107,4.961,4108,4.539,4109,5.506,4110,4.385,4111,4.961,4112,5.506,4113,7.864,4114,5.506,4115,5.506,4116,5.506,4117,5.506]],["t/3249",[5,3.3,24,1.843,49,2.106,70,6.687,106,4.172,286,5.679,304,1.974,351,3.391,404,4.562,408,4.109,528,4.936,536,3.39,621,4.816,744,5.341,804,8.011,4118,8.534]],["t/3251",[2,2.375,24,1.774,34,2.79,106,3.228,126,4.252,175,4.817,254,3.523,256,3.295,304,1.856,353,2.402,408,4.505,409,4.463,447,4.167,460,8.022,461,4.929,462,8.022,463,5.299,464,7.53,465,6.132,466,6.206,629,5.602,2548,7.339]],["t/3253",[2,1.892,7,0.843,16,1.306,24,1.136,49,2.404,84,1.024,105,2.195,106,2.571,121,4.105,153,3.589,197,3.126,228,2.223,236,1.845,278,2.514,291,4.133,302,3.231,336,2.855,372,3.538,377,1.569,408,3.413,449,4.319,461,3.927,478,4.253,532,3.659,578,3.716,596,3.16,605,2.539,614,1.292,670,4.078,731,3.678,744,4,764,4.725,828,2.926,1019,2.578,1108,4.837,1172,4.222,1297,3.402,1475,6.157,1932,4.945,2268,4,2444,4.677,2577,3.696,2954,5.847,3160,5.999,4105,5.714,4685,6.391,4998,6.391,5168,6.177,5169,7.017,5170,7.017]],["t/3255",[1781,3.73]],["t/3257",[1,2.672,7,0.799,12,1.854,16,0.855,27,1.091,28,0.985,34,1.171,49,1.569,68,1.743,84,0.914,92,3.125,93,4.293,98,2.453,99,2.899,109,1.589,116,1.716,127,1.508,162,1.615,165,1.564,171,2.293,172,4.352,173,3.735,179,1.538,193,1.672,203,1.1,207,2.849,224,2.957,228,1.456,236,1.209,257,2.34,272,2.858,276,4.181,296,1.772,304,0.969,318,1.119,332,3.623,336,1.871,337,2.141,343,1.635,377,1.028,439,3.005,445,2.899,476,2.026,491,1.386,502,1.268,527,1.005,552,3.776,568,1.317,580,1.596,601,2.158,607,2.675,614,0.846,620,3.861,686,3.596,707,2.558,728,5.876,734,3.089,769,1.767,802,1.86,810,2.041,816,2.604,858,2.267,894,3.474,913,3.421,966,1.254,1074,3.324,1075,2.41,1076,3.393,1077,3.907,1079,4.366,1108,2.352,1121,2.787,1144,2.5,1368,2.95,1377,3.421,1398,3.164,1399,2.766,1782,3.931,1806,3.24,2010,1.6,2073,2.746,2352,3.281,2476,4.187,2868,3.797,2965,3.831,2990,2.808,3604,2.899,3742,3.533,3804,3.201,3891,3.034,3899,3.034,4050,3.744]],["t/3259",[0,2.096,17,1.435,19,1.617,56,1.245,68,2.751,84,0.629,85,1.257,100,2.596,105,1.135,109,0.612,112,5.389,123,1.237,172,3.35,193,2.112,217,3.31,225,1.297,243,1.985,304,1.224,318,0.884,322,2.593,324,3.075,377,0.812,387,2.528,391,1.528,408,2.096,491,1.75,502,1.001,552,3.594,609,2.373,641,3.025,725,2.956,726,5.42,734,2.569,747,1.491,765,2.153,802,1.468,828,2.421,829,1.912,872,6.263,921,4.616,923,3.662,944,3.668,1073,4.143,1075,5.069,1076,2.091,1077,5.904,1081,3.996,1082,2.743,1083,2.815,1142,2.396,1175,4.453,1254,2.558,1285,1.548,1319,6.583,1332,3.87,1334,3.306,1335,3.025,1336,3.795,1337,7.96,1338,3.196,1339,3.306,1340,5.288,1341,3.306,1342,3.306,1343,5.288,1344,3.306,1345,3.306,1346,3.306,1347,2.528,1348,3.306,1349,3.306,1350,3.306,1351,3.306,1352,2.445,1353,2.396,1354,3.996,1355,2.252,1356,3.306,1357,3.306,1358,3.306,1359,3.306,1360,3.306,1361,3.306,1362,3.306,1363,3.306,1737,3.127,2825,7.093,3576,2.743,3594,5.288,4120,9.636,4121,3.104]],["t/3261",[2,1.77,5,0.725,7,0.814,8,1.107,17,0.813,21,1.359,22,1.277,24,0.584,26,1.785,27,1.37,28,1.531,31,1.41,49,1.081,56,0.356,68,1.823,84,0.711,85,0.713,89,1.794,93,0.872,94,1.113,96,0.725,98,0.723,103,0.688,105,1.127,106,2.116,107,2.114,109,1.462,116,0.768,124,1.298,126,0.993,132,0.604,134,2.392,135,1.173,138,2.328,140,1.412,144,0.985,153,1.843,163,0.816,165,1.293,169,1.125,170,1.421,171,1.306,172,2.86,173,5.158,174,1.429,179,1.206,184,1.555,185,1.416,189,1.928,190,2.471,192,1.212,193,3.827,203,0.863,224,3.059,228,1.142,232,0.748,236,1.518,256,0.77,275,0.637,288,2.378,289,2.593,292,0.895,296,0.793,304,0.434,313,1.41,317,1.028,318,0.877,322,1.127,323,1.453,336,0.837,337,1.678,338,0.886,339,1.038,340,1.372,343,1.282,347,1.358,351,0.745,372,1.348,377,0.46,383,1.433,391,2.024,395,1.424,401,0.786,426,2.132,433,1.188,441,1.119,458,1.159,491,0.62,502,0.994,505,0.853,509,1.166,513,0.97,541,1.266,550,2.268,552,3.069,553,2.546,555,0.973,557,1.024,558,0.92,565,1.885,568,1.879,597,1.063,601,1.692,610,1.581,611,1.498,614,1.329,621,1.058,622,1.038,630,0.76,632,5.033,635,1.744,636,1.159,638,2.168,647,0.772,686,1.61,691,1.982,712,1.107,719,1.238,725,1.048,741,1.332,743,2.378,756,0.966,757,0.507,769,0.791,772,4.331,774,1.113,802,0.832,806,1.61,807,2.536,810,1.6,812,2.402,816,1.166,822,1.229,827,2.436,828,1.503,829,1.084,838,1.818,840,0.933,845,1.309,858,1.015,872,2.334,917,1.166,974,2.201,977,2.105,987,1.531,994,0.886,995,1.145,1008,2.153,1070,1.173,1073,1.468,1074,2.606,1075,3.785,1076,1.298,1077,3.673,1079,1.287,1093,4.913,1094,2.403,1097,1.581,1121,1.247,1144,1.96,1145,1.048,1175,0.947,1182,0.981,1252,1.953,1285,0.877,1302,2.184,1303,1.32,1312,1.416,1331,1.32,1375,1.247,1409,1.358,1424,1.555,1467,1.287,1557,2.236,1605,1.509,1696,1.309,1699,2.236,1703,3.081,1737,1.359,2010,2.009,2073,1.229,2190,1.152,2250,2.6,2253,2.914,2269,1.715,2331,1.309,2352,1.468,2431,1.468,2534,1.812,2545,1.358,2577,1.084,2884,1.759,2928,2.533,2939,1.759,3030,1.676,3062,1.759,3595,1.328,3597,3.282,3604,1.298,3891,1.358,4122,1.953,5016,1.509,5171,2.058]],["t/3263",[1781,3.73]],["t/3265",[4,2.189,7,0.944,8,3.797,21,1.995,27,1.675,41,3.202,46,4.962,84,1.028,85,2.445,90,4.474,92,2.614,93,4.022,98,2.48,109,1.19,127,2.315,133,3.02,134,3.512,153,3.611,164,1.939,172,3.289,174,2.098,207,3.875,213,2.872,224,2.99,256,2.641,276,3.497,283,2.745,304,1.487,305,4.49,338,1.736,340,4.705,343,2.511,363,5.037,440,3.112,476,2.049,532,3.681,599,3.628,613,3.191,690,4.379,694,3.839,1099,2.614,1162,3.927,1202,4.345,1213,5.104,1267,5.335,2073,4.217,4126,6.698]],["t/3267",[5,2.268,7,0.703,31,2.178,49,1.447,56,0.636,68,3.878,84,1.256,105,2.014,109,1.725,110,3.392,164,1.769,165,1.442,172,3.543,203,2.134,207,3.639,253,3.874,256,3.336,304,1.357,377,1.44,404,3.135,527,1.95,552,3.156,553,3.41,576,3.793,607,3.418,611,2.677,614,1.185,623,3.083,630,2.378,638,3.874,731,3.375,769,3.427,881,4.027,902,4.948,979,4.292,1075,3.375,1077,3.604,1299,4.483,1399,3.874,1411,4.17,1423,4.656,1689,3.375,2293,3.767,2335,6.739,2469,5.505,3059,6.976,3387,4.866,3453,5.669,4119,5.243,4127,5.135,4653,5.505,5139,5.505,5140,5.669]],["t/3269",[2,2.106,7,0.8,27,2.41,46,4.674,47,4.926,84,1.1,90,3.68,92,4.783,93,3.309,96,2.751,158,3.904,172,2.706,207,3.188,256,2.922,306,3.468,313,1.908,332,4.053,338,1.921,476,2.947,481,4.666,596,3.518,597,4.034,599,5.217,604,5.155,707,4.346,796,4.094,810,3.468,1070,4.452,1090,3.468,1099,2.892,1806,5.504,3620,6.36]],["t/3271",[5,1.424,7,0.694,16,0.752,17,1.598,28,0.57,29,1.41,31,1.545,49,1.421,56,0.943,68,3.338,84,1.248,89,2.359,92,4.416,93,3.298,105,1.265,106,2.317,109,1.611,110,2.13,144,1.936,165,0.906,172,3.05,177,1.952,199,1.994,203,1.864,207,3.178,253,2.433,256,2.913,278,1.449,283,1.169,297,2.211,304,2.373,306,1.795,322,1.265,372,1.513,377,0.904,398,2.227,404,1.968,476,1.174,505,2.62,527,1.702,552,3.116,553,2.141,576,2.381,607,2.423,611,1.681,614,1.433,623,1.936,630,1.493,638,2.433,707,2.249,731,2.119,747,1.66,755,1.821,769,1.554,873,2.783,881,2.529,892,1.513,902,3.107,944,1.635,963,2.451,966,2.401,977,1.474,979,2.695,1075,3.314,1077,3.539,1083,1.96,1100,1.602,1144,2.199,1157,3.055,1181,2.365,1218,2.643,1297,1.96,1299,2.815,1399,2.433,1411,2.618,1481,2.163,1601,2.415,1671,2.224,1689,2.119,1737,2.384,1849,2.199,1998,2.508,2015,2.381,2130,3.292,2293,2.365,2335,5.884,2464,3.162,2469,3.457,2577,2.13,3059,5.298,3189,3.682,3387,3.055,3453,3.559,3595,2.329,3621,3.682,4119,3.292,4127,3.224,4129,3.837,4130,3.837,4131,3.457,4132,3.837,4135,3.559,5139,3.457,5140,3.559,5141,3.837,5142,4.043]],["t/3273",[7,0.606,24,0.847,49,1.176,55,2.846,56,1.216,84,1.16,92,3.376,171,1.422,177,4.402,283,2.223,304,2.359,306,2.323,368,3.955,382,5.656,398,2.709,401,4.278,426,1.933,440,2.307,449,3.221,539,4.935,543,6.303,608,2.716,611,2.175,614,1.971,623,2.506,736,2.231,857,4.136,966,3.155,1297,2.537,1475,2.815,1597,4.85,1607,5.026,1638,5.781,1698,3.894,1699,3.246,1737,2.9,1889,5.722,2289,3.643,2376,4.261,2887,3.061,3044,4.474,3595,2.833,4100,4.36,4108,4.093,4133,4.966,4134,4.766,5143,5.233,5144,4.766,5145,5.233,5146,5.233,5147,5.233,5148,5.233,5149,5.233,5150,7.004,5151,7.004,5152,5.233,5153,5.233]],["t/3275",[7,0.953,15,3.577,21,1.209,24,1.47,25,1.37,27,1.016,28,1.139,34,1.09,40,2.254,46,4.748,47,4.167,49,0.962,56,0.652,84,1.063,85,3.744,92,3.839,93,1.813,96,2.843,97,4.833,105,2.067,106,2.958,129,2.556,135,2.439,138,1.726,140,1.677,158,3.302,164,1.175,179,2.211,196,1.696,272,1.752,278,1.533,283,1.237,296,1.649,304,0.902,313,1.045,318,1.042,377,0.957,393,2.101,395,2.611,437,3.788,438,5.381,440,1.887,441,2.327,471,1.773,491,1.29,530,1.913,557,3.288,591,2.439,597,2.21,605,1.548,614,0.788,723,1.529,730,3.484,734,1.893,744,2.439,747,3.314,757,1.054,813,5.227,820,2.199,837,4.005,838,2.158,888,2.254,1035,2.556,1206,2.278,1253,2.556,1405,4.279,1409,4.36,1597,2.699,1688,2.912,1992,6.1,1997,6.018,1998,2.654,2091,3.288,2092,3.094,2093,2.912,2094,3.094,2116,3.484,2251,3.288,2759,3.484,3556,5.327,4136,4.06,4137,6.901,4138,7.659,4139,3.658,4140,4.06,4141,4.06]],["t/3277",[4,1.515,7,0.69,8,2.628,16,0.909,17,1.93,21,1.38,29,1.704,46,2.024,49,1.642,56,1.078,84,0.791,85,1.692,92,4.404,93,3.096,97,3.267,108,3.773,160,1.712,164,1.342,196,1.936,203,1.169,217,2.784,228,1.547,275,1.512,283,1.412,304,1.54,365,2.475,372,1.827,401,1.867,434,4.87,438,2.535,482,2.184,500,2.311,527,1.068,579,3.532,580,1.696,591,2.784,596,2.2,613,2.208,614,1.612,680,3.821,713,4.07,714,8.561,751,2.82,774,2.642,780,2.6,796,2.56,813,3.163,830,2.232,846,1.499,963,2.961,966,2.649,1087,7.466,1175,2.249,1206,2.6,1230,3.691,1290,4.304,1297,2.368,1601,2.918,1705,2.983,1733,2.918,1913,2.75,2013,2.434,2499,6.247,2584,5.149,2800,4.449,3096,3.895,3397,3.821,3556,3.223,3620,3.977,3662,7.973,3664,7.973,3877,3.532,4131,4.176,4142,4.635,4143,4.635,4144,4.635,4145,9.22,4146,4.635,4147,4.635,4148,4.635,4149,4.449,4150,4.635,4151,6.934]],["t/3279",[7,0.416,24,0.855,48,2.142,49,1.187,55,2.872,56,1.219,84,1.216,92,3.393,171,1.434,177,4.425,283,2.238,304,2.366,368,3.99,382,5.685,398,2.726,401,4.292,426,1.95,449,3.25,539,4.967,543,6.323,608,2.74,614,1.978,623,2.528,714,3.676,736,2.251,857,4.157,966,3.164,1297,2.56,1597,4.882,1607,5.059,1638,5.799,1698,3.929,1699,3.276,1737,2.919,1889,5.76,2289,3.676,2376,4.299,2887,3.089,3044,4.514,3595,2.851,4100,4.4,4108,4.13,5144,4.809,5150,7.05,5151,7.05,5154,5.28,5155,5.28,5156,5.28,5157,5.28,5158,5.28,5159,5.28,5160,5.28,5161,5.28,5162,5.28,5163,5.28]],["t/3281",[7,0.784,16,1.408,25,2.882,49,1.7,84,0.975,92,4.112,100,3.383,108,3.907,112,3.748,164,1.406,165,1.695,198,4.382,213,2.083,246,4.699,292,2.226,348,3.285,377,1.145,388,3.701,397,2.128,438,3.926,440,2.257,441,4.115,443,8.312,544,3.201,562,2.514,614,1.657,651,3.412,713,4.265,714,5.268,859,2.656,966,2.454,989,2.882,1087,3.933,1132,2.273,1156,4.506,1297,2.482,1396,8.285,1402,2.393,1633,2.412,1687,4.265,1913,2.882,2076,2.975,2251,3.933,2404,3.523,2459,5.918,3160,7.695,3161,4.265,3162,4.265,3163,9.056,3164,6.891,3165,6.891,3166,6.891,3167,6.305,3168,4.662,3169,4.662,3170,4.662,3171,4.662,3172,4.662,3173,4.662,3174,4.662,3175,4.662,3176,4.662,3177,4.662,3556,3.378,4137,4.377,4152,4.857,4153,4.857]],["t/3283",[2,1.29,7,0.854,8,2.574,15,2.623,16,0.89,17,1.891,21,1.352,29,1.669,34,1.219,49,1.617,50,2.799,56,1.142,84,1.117,85,2.492,90,2.255,92,4.688,93,3.048,94,5.859,96,2.534,97,3.866,102,3.018,106,2.637,108,2.471,109,0.807,116,2.685,125,2.727,131,2.171,164,1.314,171,1.3,174,1.422,181,2.711,186,2.255,196,1.896,275,1.481,283,2.08,313,2.112,316,2.132,323,1.93,377,1.07,390,2.31,395,1.891,398,1.685,407,2.163,438,3.734,491,1.442,526,3.46,562,2.35,568,1.37,593,3.549,597,2.471,611,1.989,694,2.602,729,3.415,892,1.79,963,2.9,1111,3.331,1157,5.437,1161,3.815,1162,4.003,1178,2.762,1264,3.127,1297,2.32,1531,3.189,1597,3.018,1601,2.858,1633,2.255,1941,3.896,1998,4.463,2013,2.396,2130,5.859,2464,3.743,3006,3.815,3096,3.815,3161,3.987,3162,3.987,3329,3.896,3810,4.5,3877,3.46,4154,4.541,4155,4.541,4156,4.541,4157,4.541,4158,4.541,4159,4.541,4160,4.091,4161,4.541]],["t/3285",[7,0.776,15,1.827,21,1.415,27,1.189,33,2.856,34,1.276,46,4.566,56,1.183,84,0.963,85,1.735,90,2.361,92,4.225,94,4.027,96,3.13,98,1.76,101,2.666,113,1.827,133,2.143,140,1.963,164,2.045,179,2.973,203,1.199,208,4.563,238,2.874,240,2.74,254,1.611,275,2.305,278,2.668,351,1.813,353,2.03,372,1.874,377,1.12,407,2.264,437,2.351,440,3.918,476,2.161,482,2.24,491,2.244,562,2.46,566,3.488,583,3.06,597,3.845,613,3.366,614,0.922,715,6.264,828,2.089,888,3.923,966,2.03,995,2.787,1108,2.562,1157,3.786,1162,2.787,1227,4.563,1633,2.361,2118,6.782,2128,3.448,2388,3.903,2840,4.41,3006,3.995,3660,8.094,3804,3.488,3810,3.133,3858,4.612,3877,3.622,4160,4.283,4162,7.066,4163,4.754,4164,7.066,4165,4.754,4166,4.754,4167,4.754]],["t/3287",[2,1.727,7,0.912,8,4.779,10,6.259,16,1.653,56,0.877,84,0.962,85,2.218,89,2.39,92,4.285,93,2.713,138,3.583,217,5.064,236,1.684,275,3.706,281,3.03,283,1.852,292,3.864,313,1.564,323,3.583,365,3.245,476,1.859,504,5.385,597,3.307,614,1.635,623,4.254,670,3.722,680,6.949,707,3.563,715,3.291,1080,5.675,1218,5.806,1231,5.336,1237,4.147,1368,4.109,1402,2.994,1504,6.26,2372,6.077,2444,4.268,5141,6.077,5164,6.404,5165,10.2,5583,6.9]],["t/3289",[7,0.796,8,2.16,15,2.826,17,3.467,18,3.406,21,1.135,24,0.65,26,2.878,28,0.567,34,1.974,46,1.664,48,1.629,56,0.765,84,0.681,90,1.892,92,2.328,93,4.473,94,3.401,96,1.414,97,3.922,103,1.343,132,1.847,165,0.899,172,1.391,181,2.275,183,1.723,185,4.328,186,1.892,201,2.044,207,1.639,213,1.634,225,1.435,228,1.992,236,2.306,244,3.871,278,1.439,283,1.161,291,2.365,292,1.746,298,2.452,306,1.783,313,0.981,315,1.907,323,1.619,338,0.988,353,1.095,369,2.491,375,2.333,377,0.898,381,2.975,393,1.972,395,1.587,440,2.773,444,1.741,463,2.416,476,1.166,505,1.664,509,2.275,590,2.16,597,2.074,611,1.669,620,7.05,676,3.535,677,2.115,680,3.141,691,2.208,693,2.471,694,3.42,696,2.829,719,2.416,740,1.815,790,2.796,794,4.378,802,1.624,892,1.502,901,3.807,910,2.261,977,1.464,1099,1.487,1132,1.783,1162,3.499,1163,2.704,1175,1.849,1188,4,1217,3.035,1287,2.944,1364,2.007,1374,4.235,1387,4.753,1389,3.27,1390,5.335,1391,7.06,1392,8.675,1393,5.728,1428,3.346,2013,1.337,2035,2.676,2217,3.085,2565,3.346,2609,3.27,3075,3.535,3159,3.346,3435,3.657,4169,3.81,4170,3.81,4171,3.81,4172,3.81,4173,3.433,4174,5.377,4175,3.81,4176,3.433,4177,3.81]],["t/3291",[2,1.718,8,3.426,17,2.517,24,1.87,28,0.899,48,3.59,56,0.874,68,2.415,92,3.277,93,4.309,94,3.445,97,2.848,132,1.871,165,1.427,179,2.131,186,3.001,199,3.141,203,2.118,207,2.599,236,1.675,283,1.842,304,2.519,338,2.177,527,1.392,614,1.172,620,6.612,966,3.149,1087,4.894,1162,3.544,1188,4.051,1297,4.291,1387,4.813,1390,6.091,1402,4.138,1601,3.805,1689,3.338,1693,3.726,2247,4.488,2335,4.813,3159,5.307,3858,3.305,4173,7.567,4174,7.567,4176,5.446,4178,6.044,4179,6.044,4180,6.044,4181,6.044,5139,5.446]],["t/3293",[2,1.393,7,0.908,16,2.073,21,1.459,24,1.465,27,1.226,28,0.729,41,2.343,47,3.257,56,0.986,85,3.459,90,3.588,93,3.226,97,3.405,105,1.615,109,0.871,123,1.76,124,3.257,170,1.526,172,1.789,173,2.763,174,1.535,183,2.216,200,2.558,244,3.178,256,1.932,275,1.598,283,1.493,291,3.042,295,2.076,313,1.262,323,2.083,353,1.408,408,2.749,437,4.246,438,2.68,447,2.443,471,2.14,491,2.295,502,1.424,525,2.433,580,1.793,597,4.672,599,3.914,614,0.951,615,3.021,619,7.747,627,4.415,747,2.12,901,3.74,945,1.708,989,2.907,1090,2.293,1099,1.912,1235,3.786,2214,3.344,2491,4.118,3556,5.026,3590,4.415,4139,4.415,4182,4.9,4183,10.104,4184,4.9,4185,9.374,4186,4.9,4187,4.9,4188,4.9,4189,4.9,4190,4.9,4191,4.9,4192,4.9,4193,4.9,4194,4.9,4195,7.226]],["t/3295",[7,0.84,12,3.399,24,1.726,84,0.913,109,1.421,201,4.29,304,2.589,619,7.502,736,3.594,780,5.673,966,2.906,1413,4.717,1689,5.587,2015,4.964,3093,6.72,3143,6.592,3601,6.413,3640,7.676,3641,7.676,4196,7.997,4197,7.997,4198,7.997,4199,7.997]],["t/3297",[1781,3.73]],["t/3299",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,1019,4.182,1292,8.588,2013,3.121,2598,5.767]],["t/3301",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,977,2.966,1035,4.86,1112,4.825,1531,5.423,2013,2.71,2362,5.538,3642,7.162,4244,10.474,4245,7.41,4246,7.41,4247,7.41,4248,9.492]],["t/3303",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,1272,8.832,1282,9.359,1290,5.609,1292,7.741,1480,7.196,2298,7.754,2598,5.861]],["t/3305",[1781,3.73]],["t/3307",[2,1.827,7,0.96,15,2.47,16,1.26,23,2.261,24,1.495,29,2.363,46,2.807,49,1.522,56,0.669,84,0.734,108,3.498,138,2.732,145,2.724,162,2.38,165,1.517,213,2.756,228,2.146,248,3.179,256,2.534,281,3.204,313,2.256,317,3.386,377,1.515,440,2.987,441,5.021,458,3.814,481,4.046,534,4.834,536,3.359,546,5.644,581,4.898,605,2.451,613,3.062,742,4.236,744,5.263,747,2.781,798,3.725,837,4.106,840,3.073,1090,4.099,1121,4.106,1172,4.076,1268,4.773,1337,6.154,2026,4.716,2327,5.516,2434,4.611,2958,5.791,3933,5.963,4056,5.119,4200,6.428,4201,6.428,4202,6.428]],["t/3309",[7,0.572,16,1.621,24,1.176,26,1.798,56,1.289,84,0.525,114,3.863,123,2.475,232,1.762,304,1.021,306,2.151,342,4.746,405,2.31,449,4.469,513,2.283,545,2.239,552,1.715,623,2.32,780,2.578,822,2.894,989,4.905,1597,4.58,1789,3.944,1889,3.604,2025,6.393,2026,6.065,2027,5.913,2029,3.944,2030,9.45,2031,5.178,2032,5.913,2035,6.45,2037,4.036,2038,4.036,2039,4.036,2040,4.036,2041,3.262,2042,5.913,2099,2.915,2448,3.03,2672,3.503,3441,6.051,3522,4.036,4110,5.488,4111,4.142,4203,4.142,4204,5.79,4205,4.142,4206,6.209,4207,4.597,4208,6.891,4209,4.597,4210,4.597,4211,4.597,4212,4.597,4213,4.597,4214,4.597,4215,4.597,4216,4.597,4217,4.597,4218,4.597,4219,4.597,4220,4.597,4221,4.597,4222,4.597,4223,4.597,4224,4.597,4225,4.597,4226,4.597,4227,4.036,4228,4.597,4229,4.597,4230,4.597,4231,4.597,4232,6.209,4233,6.209,4234,4.597,4235,4.412,4236,4.264,4237,4.597,4238,6.891,4239,4.597]],["t/3311",[7,0.846,16,1.839,24,0.978,26,2.243,56,1.27,84,0.654,100,3.813,114,4.817,123,2.906,232,2.197,248,2.836,304,1.273,342,5.574,449,5.249,545,2.792,744,3.444,790,5.938,1268,4.257,1597,5.379,1633,2.847,1789,6.944,2026,5.938,2027,6.944,2029,4.919,2030,10.046,2031,5.746,2032,6.944,2035,7.158,2037,5.034,2038,5.034,2039,5.034,2040,5.034,2041,4.068,2042,6.944,4110,6.445,4203,5.165,4204,6.8,4205,5.165,4206,7.292,4232,7.292,4233,7.292,4240,5.733,4241,5.733,4242,5.733,4243,5.733]],["t/3313",[56,1.014,164,2.822,550,4.036,614,1.891,744,5.855,814,4.423,1704,8.56,2020,10.952]],["t/3315",[1781,3.73]],["t/3318",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,1019,4.182,1292,8.588,2013,3.121,2598,5.767]],["t/3320",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,977,2.966,1035,4.86,1112,4.825,1531,5.423,2013,2.71,2362,5.538,3642,7.162,4244,10.474,4245,7.41,4246,7.41,4247,7.41,4248,9.492]],["t/3322",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,1272,8.832,1282,9.359,1290,5.609,1292,7.741,1480,7.196,2298,7.754,2598,5.861]],["t/3324",[1,3.01,4,0.549,7,0.249,15,0.645,16,0.587,21,0.5,24,1.511,27,1.912,28,1.317,31,1.042,46,3.756,56,0.311,84,0.873,89,1.933,96,1.503,105,0.553,106,3.733,107,0.453,109,0.532,115,1.452,121,1.845,126,2.501,131,5.269,137,1.135,138,0.713,153,0.905,163,2.053,167,2.423,170,0.932,173,0.947,198,1.535,232,3.296,236,0.465,254,0.569,279,0.764,286,1.911,294,1.696,298,1.081,315,3.827,318,1.261,336,0.72,341,2.346,343,0.629,353,0.86,381,2.812,397,2.74,405,2.097,407,1.425,408,1.138,434,6.665,444,0.767,445,3.749,476,1.725,484,0.75,491,1.561,502,1.818,511,1.005,527,0.689,536,2.273,568,0.507,571,4.905,572,1.67,599,1.621,611,3.349,635,2.156,653,3.725,715,1.621,723,1.851,768,1.611,796,0.927,798,1.734,807,0.684,827,0.896,846,0.968,888,0.932,916,1.975,944,0.716,974,1.081,984,1.825,1008,1.884,1010,2.844,1072,0.818,1090,1.4,1093,1.786,1098,1.116,1113,6.833,1161,1.411,1177,1.316,1179,5.37,1181,1.035,1186,1.359,1219,2.308,1221,1.232,1224,4.206,1247,1.679,1265,0.927,1271,0.88,1336,2.061,1500,1.337,1689,3.455,1696,1.125,1829,1.384,1843,1.191,1987,1.247,2137,1.297,2329,1.513,2450,1.179,2452,9.505,2529,6.426,2530,5.18,2598,2.626,2606,2.467,2628,1.769,2631,1.156,2645,1.769,2904,3.085,2910,1.611,2928,0.932,2960,3.24,2969,1.679,3118,4.953,3396,1.769,3805,1.316,3813,3.154,3995,1.557,4118,1.611,4686,5.319,4988,6.256,5432,1.611,5489,1.769,5490,1.769,5491,1.769,5492,1.769,5584,1.906,5585,1.906,5586,1.906,5587,1.906,5588,1.906,5589,1.906,5590,1.906,5591,1.906,5592,1.906,5593,1.906,5594,1.906,5595,1.906,5596,3.398,5597,3.398,5598,1.906,5599,1.906,5600,1.906,5601,1.906,5602,1.906,5603,1.906,5604,1.769,5605,3.398,5606,1.906,5607,5.582,5608,5.582,5609,1.906,5610,4.597,5611,1.906,5612,1.906,5613,1.906,5614,1.906,5615,1.906]],["t/3327",[2,1.505,7,0.902,15,3.449,16,1.5,25,1.787,34,1.421,49,1.254,56,1.13,84,0.605,85,1.933,158,2.79,164,1.533,171,1.516,203,1.336,219,2.495,283,2.33,292,2.428,304,1.176,313,1.969,318,2.303,322,2.521,377,1.248,471,2.313,480,3.799,513,2.63,562,2.741,567,4.153,605,2.02,736,4.034,755,3.63,757,1.986,805,3.799,811,3.463,846,1.713,888,4.246,937,3.334,1019,2.051,1055,4.036,1401,3.288,1577,4.218,1735,3.886,1802,3.383,1994,5.612,2360,10.987,2361,8.594,2362,7.793,2364,9.46,2388,2.926,5616,6.014,5617,6.014,5618,6.014,5619,6.014,5620,6.014,5621,6.014,5622,6.014,5623,6.014,5624,6.014,5625,6.014,5626,6.014,5627,6.014,5628,6.014,5629,6.014]],["t/3329",[7,0.65,15,3.008,16,1.535,48,3.347,49,2.363,68,3.128,84,1.254,103,2.76,164,2.266,187,5.744,295,3.317,304,2.216,393,4.051,401,3.153,405,2.624,409,4.18,614,1.936,764,5.556,1132,4.669,1632,6.007,2359,5.744,2361,6.453,2387,6.453,2446,8.25,2554,7.829,2577,4.346,5604,8.25,5630,8.89,5631,8.89]],["t/3331",[1781,3.73]],["t/3333",[7,0.469,16,1.107,28,0.839,34,2.148,49,1.337,56,1.266,106,3.092,109,1.798,164,1.634,171,2.292,193,2.164,203,1.424,221,3.116,254,1.913,318,2.597,322,2.639,397,2.473,398,2.972,444,2.579,476,2.449,491,1.793,502,2.326,527,1.301,614,1.095,653,2.655,658,2.849,757,2.079,822,3.554,838,3.001,1090,2.642,1099,4.55,1100,2.358,1112,3.529,1113,4.246,1224,3.1,1328,4.4,1456,4.444,2010,3.412,2017,4.49,2031,4.514,2338,4.64,3058,5.237,4400,7.213,5173,8.436,5574,5.646,5632,6.411,5633,5.949]],["t/3335",[16,1.268,28,1.596,56,1.117,107,1.744,109,1.149,132,2.722,164,2.546,170,2.739,171,1.851,246,2.576,254,2.981,313,1.664,326,4.637,336,2.772,377,1.524,391,2.868,426,2.516,502,2.905,525,4.368,580,2.366,647,2.556,712,3.665,734,3.014,857,3.091,916,4.016,934,5.196,944,2.756,974,4.161,994,3.991,1039,6.953,1040,6.435,1099,4.518,1100,2.7,1149,6.205,2218,6.703,5175,6.813,5176,6.813,5459,6.813,5634,7.341]],["t/3337",[2,1.58,7,0.462,16,1.09,25,1.876,28,1.372,31,2.375,56,1.106,84,0.635,107,1.5,109,1.407,115,3.843,123,3.313,132,2.855,140,2.296,164,1.61,170,1.731,171,1.592,179,1.961,193,2.131,236,1.541,254,1.884,256,2.193,282,3.187,304,1.235,313,1.432,322,3.04,336,2.385,372,3.123,377,1.311,391,2.467,502,1.616,507,5.287,527,1.825,550,4.163,572,3.103,585,4.429,586,5.159,658,2.806,755,2.639,814,3.594,828,2.444,937,3.501,944,2.37,975,3.761,979,3.906,989,3.3,1099,3.599,1101,3.501,1102,4.986,1142,3.867,1145,2.983,1179,5.563,1688,3.989,2010,3.383,2137,4.297,2900,3.947,4377,5.01,5017,4.503,5177,5.86,5178,5.86,5179,7.602,5180,5.86,5181,5.86,5182,5.86]],["t/3339",[7,0.731,16,1.268,17,2.692,31,1.664,34,1.735,40,3.589,84,0.738,115,3.137,123,2.322,138,2.748,164,1.872,174,2.755,175,3.726,229,4.041,246,2.576,247,2.994,254,2.191,261,3.452,282,3.705,351,2.465,353,1.858,415,4.801,433,3.933,476,3.282,507,3.705,525,3.21,527,1.489,550,3.642,568,2.654,571,5.069,578,2.677,621,3.501,628,3.518,631,4.453,658,3.262,707,3.791,858,4.572,1098,4.297,1099,4.38,1101,4.07,1102,4.07,1298,6.205,1508,3.769,1540,5.127,2151,5.433,2529,4.995,2577,3.589,3114,6.205,3493,5.677,3934,5.998,4560,5.235,5183,6.465,5184,6.465,5185,6.813]],["t/3341",[7,0.65,31,2.569,49,1.854,84,1.139,115,3.799,123,3.583,174,3.44,236,2.169,247,3.625,351,2.986,416,3.799,476,2.395,527,1.804,568,3.011,584,6.875,585,6.235,621,4.24,658,3.951,818,4.142,876,6.632,1099,3.055,1179,5.499,1688,7.158,2137,6.049,2299,7.514,2606,6.453,2862,7.263,2954,6.875,4560,6.339]],["t/3343",[1781,3.73]],["t/3345",[4,1.42,7,0.797,10,2.477,16,0.852,24,0.742,27,1.652,31,1.7,48,1.858,56,0.831,84,1.154,90,3.28,91,3.816,101,2.438,107,1.172,109,1.174,113,1.67,115,2.108,125,2.61,127,2.282,130,5.361,131,2.077,132,1.345,133,1.959,138,2.807,164,1.912,165,1.559,170,2.779,174,1.361,175,2.504,179,1.532,182,1.532,197,2.04,198,2.23,200,2.269,202,3.152,216,2.331,228,1.451,281,2.166,292,1.992,296,1.765,315,2.175,316,2.04,318,1.115,321,3.461,351,1.657,353,2.758,359,2.019,379,3.816,382,2.841,394,2.289,397,1.904,408,1.653,409,2.32,416,2.108,418,2.289,430,2.259,433,2.644,447,2.166,476,1.329,491,1.38,501,4.171,527,1.001,536,3.172,550,1.799,552,4.038,555,2.166,565,1.495,567,2.791,570,4.171,578,1.799,593,3.433,621,2.354,629,2.913,630,2.57,637,2.939,655,2.993,675,2.913,683,1.713,690,5.223,706,3.268,734,2.026,737,3.461,765,4.128,796,2.4,805,3.117,828,1.91,844,3.729,981,3.084,1009,3.117,1010,3.052,1020,4.346,1108,2.342,1285,1.953,2210,3.582,2421,2.735,2472,3.407,2491,3.652,2908,3.729,3012,4.031,4910,4.579,5635,4.934,5636,4.934,5637,4.934,5638,4.934]],["t/3347",[2,1.078,5,1.408,7,0.814,16,0.744,24,1.341,27,1.487,28,1.583,31,0.566,34,0.59,41,1.051,56,0.938,84,1.029,85,0.803,96,2.207,103,0.776,105,0.725,107,1.979,109,1.915,113,1.457,116,0.865,123,1.362,124,1.462,127,1.311,130,2.278,132,2.076,133,0.992,140,0.908,162,0.814,163,0.919,164,1.098,165,1.404,170,3.114,171,1.086,172,1.825,173,1.24,174,1.188,179,1.337,224,0.982,225,0.828,228,0.734,236,1.385,237,2.93,246,0.876,256,0.867,257,1.18,260,2.2,272,2.565,275,1.237,280,1.474,281,1.891,285,0.958,294,1.247,302,1.067,313,0.566,316,1.033,318,1.283,343,1.421,351,1.907,364,1.633,366,1.45,367,2.682,377,0.518,394,1.159,395,0.916,397,0.964,409,1.174,416,1.84,430,1.143,433,3.042,436,1.654,440,2.763,471,0.961,476,1.529,480,1.578,491,1.205,498,1.29,508,2.354,524,1.427,527,0.507,528,3.301,536,2.606,541,2.46,550,1.57,552,4.407,565,3.441,567,2.512,572,1.227,582,1.931,583,1.416,587,1.887,593,1.143,596,1.044,599,2.054,621,1.191,630,0.856,635,1.45,637,1.487,660,1.725,690,1.438,696,1.633,702,1.561,706,1.654,757,0.985,759,0.813,765,1.375,772,1.53,807,0.896,810,1.029,822,3.147,827,1.174,828,0.967,846,0.712,857,1.051,892,1.495,901,1.138,937,1.385,944,1.617,945,0.767,963,1.405,977,0.845,994,1.721,1021,1.578,1062,1.922,1099,1.48,1101,2.387,1138,2.542,1142,1.53,1145,1.18,1155,1.848,1186,3.071,1266,1.733,1271,1.153,1285,1.704,1312,1.595,1319,1.578,1399,1.395,1459,2.612,1473,2.2,1632,3.668,1843,1.561,2010,1.834,2076,1.347,2099,2.405,2191,2.638,2250,1.044,2259,1.545,2331,3.351,2352,1.654,2439,1.813,2545,1.53,2547,1.982,2569,1.848,2588,1.515,2759,3.254,2760,3.518,2938,1.848,3897,1.813,4669,2.2,5432,2.111,5553,2.318,5639,2.498,5640,2.498,5641,6.752,5642,4.307,5643,2.498,5644,2.498,5645,2.498,5646,2.498,5647,2.498,5648,2.498]],["t/3349",[7,0.579,10,1.147,12,0.855,16,0.916,21,0.599,25,1.576,27,1.588,28,0.944,29,1.717,31,0.904,34,0.54,39,0.863,48,1.501,49,1.652,56,1.27,68,0.804,84,0.639,85,0.735,88,0.955,96,1.303,100,0.948,102,1.338,103,0.71,105,1.157,107,1.881,109,1.333,113,0.773,127,1.213,132,1.446,133,0.907,145,2.371,160,0.743,164,0.583,165,1.103,170,2.598,171,1.005,175,1.16,179,0.71,192,1.249,193,1.346,196,0.841,198,1.802,199,1.046,201,1.08,213,0.863,224,0.899,225,0.758,226,1.794,227,1.4,228,0.672,243,1.16,244,2.278,246,0.802,247,0.932,277,1.09,279,0.916,281,1.003,283,1.07,292,1.61,294,1.141,296,1.426,300,1.286,304,0.78,306,0.942,316,0.945,317,1.06,322,0.663,323,2.964,336,0.863,338,0.91,351,0.768,353,1.009,359,0.935,366,1.327,377,0.828,405,1.177,409,1.075,416,0.977,426,1.367,444,0.919,471,2.04,476,1.429,484,0.899,499,1.477,502,1.02,509,1.202,528,2.593,529,1.932,536,2.496,550,0.833,552,4.318,553,1.123,565,1.925,567,3.798,568,0.607,577,2.329,578,0.833,601,0.995,621,1.09,630,0.783,653,0.668,660,1.578,662,1.233,672,2.18,719,2.226,723,0.758,731,1.112,738,1.867,747,2.021,757,0.523,778,2.334,784,1.555,798,3.679,811,1.316,820,1.09,828,1.543,837,1.286,840,1.679,846,1.136,857,0.962,897,1.338,916,0.808,917,3.79,934,1.046,947,2.576,993,0.858,994,0.913,1012,1.691,1015,1.258,1019,1.36,1027,2.354,1100,1.467,1132,0.942,1186,1.63,1202,1.305,1220,1.217,1246,1.349,1253,1.267,1265,1.112,1266,1.604,1271,1.841,1285,0.904,1353,3.891,1354,2.547,1365,1.225,1375,1.286,1539,1.414,1554,3.164,1631,1.727,1632,1.101,1635,1.767,1664,1.555,1699,1.316,1783,1.603,1913,1.194,1959,1.011,2010,2.052,2013,1.233,2182,1.555,2246,1.691,2250,1.667,2264,1.578,2577,1.117,2928,1.117,3387,1.603,3563,1.767,3679,1.767,3811,1.63,4339,1.867,4341,3.258,4526,1.932,5348,2.013,5407,1.932,5552,2.013,5576,1.932,5649,3.7,5650,4.922,5651,3.7,5652,2.121,5653,5.304,5654,2.285,5655,3.7,5656,2.285,5657,2.121,5658,2.285,5659,2.285,5660,2.121,5661,2.285,5662,2.285,5663,2.285,5664,3.987,5665,2.285,5666,2.121,5667,2.285,5668,2.285,5669,2.121,5670,3.987,5671,2.121,5672,2.121,5673,3.7,5674,2.121,5675,2.285,5676,2.285,5677,2.285,5678,2.285,5679,2.121,5680,2.285,5681,2.121,5682,2.285,5683,2.285,5684,2.285,5685,2.285,5686,2.285,5687,2.285,5688,2.285,5689,2.285,5690,2.285,5691,2.285,5692,2.285,5693,2.285,5694,2.285,5695,2.285,5696,2.285,5697,2.285]],["t/3351",[2,0.994,7,0.659,25,1.18,27,1.986,28,1.374,48,2.385,49,0.829,56,1.291,84,0.906,85,1.277,103,1.234,107,1.505,109,0.622,140,2.304,145,2.364,164,1.013,165,1.642,170,2.7,171,1.002,193,2.138,198,1.795,199,1.819,223,2.076,225,1.318,226,1.788,281,1.744,283,1.066,304,0.777,306,1.637,322,1.839,323,1.487,338,0.907,377,0.825,405,1.87,426,1.362,433,2.129,476,2.128,482,1.649,502,1.017,536,1.098,552,4.336,557,1.835,558,4.354,565,2.394,567,1.478,576,2.172,577,1.744,578,1.449,600,2.458,635,1.338,647,1.383,693,2.269,747,2.414,757,0.909,763,2.51,796,1.933,798,2.028,801,2.744,828,2.451,833,2.102,846,1.132,858,1.819,916,1.405,917,3.33,947,4.093,1027,2.345,1094,1.843,1100,2.906,1266,3.626,1285,1.572,1353,5.521,1354,4.046,1539,2.458,1554,5.027,1664,2.704,1735,2.567,1768,2.51,1804,3.499,2010,2.046,2013,1.228,4125,5.176,4341,3.246,5375,3.499,5407,3.358,5552,3.499,5576,3.358,5649,3.687,5650,3.687,5651,3.687,5652,3.687,5655,5.879,5657,3.687,5666,3.687,5669,3.687,5671,3.687,5672,3.687,5673,5.879,5674,3.687,5679,3.687,5681,3.687,5698,6.335,5699,6.335,5700,3.973,5701,6.335,5702,3.973,5703,6.335,5704,6.335,5705,3.973,5706,3.973,5707,3.973,5708,3.973,5709,3.973,5710,3.973]],["t/3353",[7,0.563,56,1.333,96,3.372,105,2.235,107,1.829,170,2.111,179,2.391,275,2.212,322,2.235,377,1.598,536,3.434,552,3.388,653,2.249,780,5.095,846,3.54,993,2.89,1019,3.517,1402,3.34,2010,3.331,2013,2.38,2960,4.469,4096,5.954,4097,5.954,4351,5.099,4352,8.718,4368,6.507,5577,9.572,5711,7.699,5712,10.314,5713,10.314,5714,7.699]],["t/3355",[7,0.776,107,2.52,155,6.373,225,3.518,226,4.772,277,5.058,323,3.969,324,5.211,377,2.622,536,2.931,552,3.483,565,3.213,794,6.852]],["t/3357",[1781,3.73]],["t/3359",[16,1.981,34,2.711,107,2.725,318,2.592,1062,5.118]],["t/3361",[2,1.45,7,0.424,16,1,24,1.5,28,0.759,34,1.369,41,2.439,49,1.763,56,1.005,84,1.003,103,2.625,107,2.771,109,1.968,110,6.147,113,2.86,127,1.763,132,2.304,140,2.107,164,1.477,165,1.204,171,2.131,179,1.799,193,1.956,203,2.217,228,1.703,272,2.201,304,2.145,313,1.314,343,1.912,377,1.754,408,1.941,527,2.615,614,1.705,658,2.575,846,3.125,859,2.79,892,2.012,945,3.064,966,1.466,977,3.947,982,2.712,1737,2.028,1779,3.621,1988,3.281,2010,3.767,2191,3.548,2274,3.888,2275,6.536,2276,3.084,2990,3.284,3595,1.981,3858,2.79,4252,7.444,4253,5.102]],["t/3363",[5,1.86,7,0.847,12,2.129,17,2.087,21,1.492,24,1.879,27,1.253,28,0.745,31,1.29,41,2.395,46,3.798,56,1.109,98,2.72,109,1.545,110,5.661,116,1.97,131,2.395,133,2.259,158,2.639,170,1.56,171,1.434,174,2.724,179,2.59,186,3.647,196,2.093,197,2.353,254,1.698,272,2.161,275,2.396,282,4.21,289,2.087,304,1.113,322,2.866,353,1.44,367,3.941,377,1.181,386,5.76,388,3.818,408,1.906,440,2.328,482,2.361,505,3.208,525,2.488,527,2.349,528,2.782,568,1.512,590,2.84,597,2.727,599,2.714,611,2.195,614,0.972,631,5.059,719,3.177,755,3.486,807,2.04,816,2.991,846,1.621,977,1.925,1080,2.938,1099,3.393,1253,3.154,1284,3.594,1318,4.4,1402,2.469,1633,2.488,1737,1.991,2010,1.838,2013,1.759,2274,3.818,2275,4.4,2606,4.13,3595,1.945,4254,7.345]],["t/3365",[1781,3.73]],["t/3367",[7,0.796,24,1.635,28,1.424,219,3.684,502,2.784,536,3.006,550,3.967,565,3.296,568,2.891,1307,7.029]],["t/3369",[7,0.633,28,1.458,34,2.631,49,2.322,163,4.094,188,5.802,219,3.198,221,2.963,295,4.154,353,2.188,377,1.795,476,2.329,484,4.376,500,3.796,502,3.15,512,3.562,536,2.39,555,3.796,577,3.796,623,3.842,653,2.526,757,2.815,780,4.271,843,4.388,1019,2.948,2017,3.378,2128,5.523,2624,6.689]],["t/3371",[2,1.255,4,1.443,7,0.747,12,1.876,24,0.753,25,1.489,28,0.656,49,1.045,56,1.132,68,3.223,84,0.504,85,1.611,101,2.476,123,1.585,132,1.366,140,1.823,165,1.042,179,1.557,196,1.844,203,2.035,219,3.153,236,1.223,285,1.922,313,1.721,317,2.325,338,1.144,348,2.985,359,2.051,372,2.635,377,1.04,398,1.639,449,2.863,491,1.402,512,2.428,527,1.54,536,3.191,565,2.775,568,2.017,572,2.463,605,1.683,613,2.103,614,0.856,630,1.718,647,1.745,653,1.464,736,1.984,757,2.641,803,2.619,843,3.852,846,1.428,888,3.711,892,3.547,945,1.539,966,2.318,988,2.275,1035,2.779,1333,3.639,1456,2.451,1481,2.489,1700,5.241,2008,5.869,2009,3.101,2013,2.346,2017,5.036,2031,5.734,2338,2.559,2362,4.794,2388,3.692,2624,3.012,2729,5.241,2880,6.022,2990,2.841,2996,4.237,3804,3.239,4283,4.415,4284,6.684,4285,4.415,4286,4.415,5715,4.652,5716,7.59,5717,5.013,5718,5.013]],["t/3373",[28,1.451,34,2.03,49,1.791,84,0.863,96,2.807,165,1.785,190,4.096,219,2.467,232,2.899,295,3.205,304,1.679,398,4.011,461,4.46,536,3.709,552,2.821,567,4.124,605,2.884,614,1.467,731,4.177,757,2.534,966,2.173,1266,3.455,1394,5.07,2017,4.329,3059,7.495,3858,4.136,4287,7.563,4288,7.563]],["t/3375",[1,0.908,5,0.993,7,0.736,12,0.63,16,1.133,17,0.617,23,0.941,24,1.898,25,1.234,26,0.58,27,0.371,28,1.486,34,1.202,39,0.636,41,1.279,48,1.144,49,0.634,56,0.922,68,0.592,84,0.896,88,0.703,89,1.762,98,0.549,103,1.58,105,0.882,107,0.987,109,1.027,116,1.052,123,0.532,128,1.345,132,2.519,133,0.668,160,0.548,162,0.991,163,1.528,164,0.775,165,1.223,170,0.833,179,0.944,182,2.222,183,1.21,196,0.619,198,0.76,199,0.77,203,1.59,219,1.193,220,1.736,221,1.041,223,0.879,232,1.717,236,1.435,240,0.854,246,1.066,255,1.052,275,0.483,277,0.803,278,0.56,283,0.452,285,2.516,286,1.709,289,1.523,295,0.628,296,0.602,302,0.719,304,0.329,323,0.63,324,0.827,328,1.861,337,0.727,338,0.948,343,0.556,347,1.031,351,0.565,353,0.769,372,1.442,377,1.485,392,0.969,398,0.55,401,0.597,407,0.706,409,1.953,414,0.896,416,0.719,426,2.452,430,0.77,444,0.677,463,0.94,472,1.373,476,2.302,482,0.698,483,0.985,484,1.633,500,0.739,511,1.229,512,2.456,517,0.947,525,0.736,527,1.734,528,0.823,530,0.698,536,2.248,545,0.722,555,0.739,560,1.501,565,2.589,568,1.563,575,2.243,578,0.614,580,0.542,601,1.324,605,1.709,611,1.172,614,1.522,621,0.803,622,0.788,629,0.994,630,2.016,635,0.567,647,1.058,653,1.718,681,1.764,683,0.584,685,0.869,723,1.008,731,0.819,736,1.644,743,1.031,756,0.733,757,2.244,759,2.135,760,1.139,769,1.481,780,0.831,784,1.145,803,1.588,807,2.109,811,0.969,827,0.791,828,0.651,832,1.052,840,1.749,843,0.854,845,0.994,846,1.183,850,1.041,857,2.476,859,0.811,892,2.485,898,2.085,901,0.767,908,0.795,917,2.184,938,0.961,939,0.89,941,1.245,945,1.562,966,2.484,984,2.841,994,0.673,1019,0.574,1072,0.722,1076,2.855,1083,0.757,1090,1.252,1098,0.985,1100,0.619,1118,3.846,1121,0.947,1132,1.712,1172,1.697,1175,0.719,1192,2.392,1224,1.036,1246,1.794,1251,1.115,1268,1.987,1285,0.666,1402,0.73,1413,1.578,1425,1.639,1481,0.836,1530,0.94,1616,1.569,1633,0.736,1671,0.859,1689,0.819,1733,0.933,1781,0.527,1849,0.849,1850,1.375,1913,0.879,1959,0.745,1988,1.18,2000,0.977,2002,3.074,2009,3.147,2017,0.657,2073,0.933,2089,1.041,2145,1.335,2176,1.302,2190,0.874,2226,1.272,2254,0.807,2338,0.859,2727,1.222,2898,1.052,3600,2.296,3601,1.697,3616,0.961,3771,2.962,3858,1.463,3883,1.423,3899,1.031,3917,1.041,3918,0.994,3942,1.794,4038,1.335,4056,1.18,4289,1.482,4290,1.101,4291,1.101,4292,1.101,4293,1.101,4294,1.101,4295,1.101,4296,1.101,4297,1.2,4298,1.423,4299,1.482,4301,1.482,4302,1.482,4303,2.676,4304,1.482,4305,1.482,4306,1.482,4307,1.423,4308,1.482,4309,1.482,4310,1.482,4311,1.482,4312,1.482,4313,1.482,4314,1.482,4315,1.482,4316,1.482,4317,1.482,4318,1.482,4560,1.2,5186,1.562,5187,1.562]],["t/3377",[7,0.702,23,1.378,24,1.876,27,0.98,28,1.362,31,1.009,48,1.675,49,0.928,56,0.879,84,0.855,88,1.859,89,3.323,98,1.451,103,1.381,116,2.398,128,1.969,132,1.212,163,2.547,171,1.122,177,1.993,182,2.15,198,2.01,219,1.278,221,1.525,225,1.476,232,2.337,285,1.706,286,2.502,302,1.901,304,1.354,313,1.009,322,1.291,328,2.724,343,1.468,377,0.923,401,1.578,444,1.79,472,2.01,502,1.138,512,3.523,527,1.405,545,1.908,555,1.953,565,2.576,575,3.052,614,1.776,630,2.373,653,2.803,670,2.399,681,2.582,736,1.76,756,1.938,757,1.944,769,3.422,780,2.197,807,1.595,840,1.873,846,1.267,898,4.268,908,2.102,945,2.126,966,2.786,984,1.766,1072,1.908,1076,3.207,1094,2.063,1118,4.075,1132,3.503,1224,2.361,1329,4.443,1425,2.399,1557,2.561,1616,2.297,1633,1.945,1671,2.27,1689,2.164,1754,1.755,1765,2.368,1849,3.495,2198,2.985,2254,2.132,2311,2.78,2588,2.698,3601,3.867,3616,3.955,3645,5.355,3917,2.752,3918,2.626,4290,2.909,4291,2.909,4292,2.909,4293,2.909,4294,2.909,4295,2.909,4296,2.909,4297,3.172,4319,5.495,4320,3.53,4321,3.027,4322,3.634,4323,2.946]],["t/3379",[7,0.484,23,2.05,24,1.52,28,1.407,34,0.992,48,1.58,49,0.875,56,0.751,63,2.993,84,0.823,88,1.754,89,3.219,98,1.369,103,1.303,116,2.291,163,3.012,171,1.058,177,2.965,182,2.055,198,1.897,203,1.47,219,1.206,221,1.439,228,1.234,232,2.765,283,1.776,285,1.609,286,2.361,295,1.566,304,1.294,322,1.218,328,2.571,338,0.958,343,1.385,377,1.373,401,1.489,405,1.239,426,1.439,444,1.689,472,1.897,476,2.895,491,1.174,502,1.693,512,3.599,527,1.342,536,1.829,545,1.8,558,2.746,565,2.005,575,3.61,577,5.117,614,1.728,630,2.268,653,2.715,683,1.457,736,1.661,756,1.828,757,1.873,780,2.073,796,2.042,846,1.885,892,2.297,898,4.111,916,3.288,945,2.032,966,3.035,984,3.252,1072,1.8,1076,3.38,1118,4.211,1224,2.256,1325,2.856,1402,1.821,1616,2.167,1633,1.836,1689,2.042,1754,1.656,1849,3.34,2251,2.993,2254,2.012,2588,2.546,2727,3.047,3601,3.695,3646,5.117,3942,3.906,4290,2.745,4291,2.745,4292,2.745,4293,2.745,4294,2.745,4295,2.745,4296,2.745,4321,2.856,4323,2.78,4324,5.25,4325,3.696,4326,3.696,4327,3.696,4328,3.696]],["t/3381",[5,0.703,7,0.559,21,0.564,23,0.666,24,1.238,25,1.502,27,0.473,28,1.38,34,1.195,36,1.546,39,0.812,41,1.591,48,0.809,49,1.055,56,1.028,68,0.756,84,0.697,88,0.898,89,2.107,98,0.701,103,1.173,105,1.097,107,2.791,109,1.786,116,1.751,123,0.68,125,1.137,128,1.672,132,1.378,133,0.853,134,0.993,140,1.374,162,0.701,163,2.238,164,0.548,165,1.051,170,2.633,171,1.534,182,1.173,183,0.856,190,1.025,198,0.971,203,1.123,219,1.085,221,0.737,232,1.275,236,0.922,240,1.091,246,0.754,256,0.746,283,0.577,285,0.824,286,1.209,295,1.41,302,0.918,304,0.739,313,0.487,322,1.468,328,1.316,336,0.812,338,1.744,343,1.247,377,0.784,391,0.84,394,1.753,401,0.762,405,2.255,408,1.266,414,1.144,416,0.918,426,2.376,444,0.865,472,0.971,476,2.058,491,0.601,502,0.55,505,1.453,511,1.496,512,2.802,527,1.55,536,2.75,545,0.922,550,0.784,557,0.993,565,1.532,568,0.571,575,2.229,580,0.693,605,0.722,614,1.573,623,0.955,629,1.269,630,1.295,647,2.413,653,2.232,660,1.484,663,1.218,681,2.193,723,1.253,724,2.285,736,1.495,756,1.646,757,1.885,760,1.036,769,1.348,772,1.316,780,1.062,820,1.025,828,1.462,840,1.591,846,1.077,892,2.113,898,2.538,908,1.015,917,1.13,945,1.16,966,2.43,977,0.727,984,2.007,993,1.419,994,1.51,1062,0.959,1072,0.922,1076,2.754,1083,0.967,1099,1.298,1118,4.229,1132,2.508,1145,1.015,1172,1.2,1175,0.918,1224,1.724,1312,1.373,1355,1.237,1365,1.152,1402,0.933,1425,2.038,1481,1.067,1616,1.951,1633,0.94,1671,1.097,1689,1.046,1793,1.258,1849,1.907,2006,4.599,2010,2.468,2012,4.141,2099,1.2,2254,1.03,2256,5.817,2257,1.389,2588,1.304,2900,2.362,2928,1.847,3595,0.735,3601,3.398,3616,1.228,3647,4.706,3763,1.269,3830,1.423,3917,1.33,3918,1.269,3942,2.231,3943,5.448,3944,1.533,3952,4.012,3972,1.304,4290,1.406,4291,1.406,4292,1.406,4293,1.406,4294,1.406,4295,1.406,4296,1.406,4321,1.463,4323,1.423,4329,4.453,4330,1.624,4331,2.856,5015,2.65,5016,2.571,5017,2.695,5168,1.756,5188,1.624,5189,1.705,5581,1.995]],["t/3383",[7,0.548,21,0.767,23,0.906,24,0.959,25,0.869,27,0.644,28,1.38,34,0.691,36,1.197,41,2.075,48,1.101,49,1.028,56,1.026,84,0.495,88,1.223,89,2.595,92,1.005,98,0.954,103,0.908,105,0.849,107,0.695,109,1.866,116,1.706,127,3.906,128,1.295,132,1.343,133,1.161,140,1.064,162,0.954,163,3.076,164,0.746,165,1.024,171,1.889,172,0.94,177,2.208,179,0.908,182,1.53,183,1.963,193,1.663,198,1.322,203,1.095,219,0.84,221,1.003,232,1.663,256,1.016,257,1.382,275,0.84,279,1.173,285,1.122,286,1.645,295,1.092,304,0.964,322,2.631,328,1.792,336,1.861,338,1.125,343,0.966,351,0.982,359,1.197,377,1.325,391,1.925,394,2.286,401,1.038,405,2.212,426,1.689,444,1.177,472,1.322,476,2.839,502,0.749,512,3.085,527,1,536,2.665,545,1.255,565,2.534,575,2.814,605,0.982,614,1.861,615,1.588,623,1.3,630,1.689,653,2.188,681,1.698,732,1.774,736,1.158,755,1.223,756,1.274,757,2.073,780,1.445,840,2.688,843,1.485,846,1.404,898,3.205,945,1.513,966,2.667,974,1.658,984,1.161,993,1.098,1072,1.255,1076,2.794,1118,3.552,1144,1.476,1224,1.68,1284,1.848,1402,1.269,1425,1.578,1616,1.51,1633,1.279,1689,1.423,1754,1.944,1765,1.557,1849,2.487,1991,1.809,2004,5.794,2010,3.404,2011,5.173,2190,1.519,2254,1.402,2588,1.774,3522,2.262,3595,1,3601,4.183,3648,5.794,3763,1.727,3942,2.909,4290,1.913,4291,1.913,4292,1.913,4293,1.913,4294,1.913,4295,1.913,4296,1.913,4321,1.99,4323,1.937,4330,2.211,4331,3.724,4332,5.216,4333,2.39,4334,2.576,4335,2.165]],["t/3385",[7,0.679,21,1.345,23,0.95,24,0.993,26,1.057,27,0.676,28,1.125,31,1.162,36,1.255,37,1.897,39,1.158,48,1.155,49,1.069,56,1.016,84,0.863,88,1.282,89,2.674,98,1,103,0.952,107,0.729,109,1.734,116,1.776,127,0.933,128,1.357,132,0.836,162,1,163,1.886,164,0.782,165,0.638,170,3.121,171,1.947,177,1.374,182,1.592,183,1.222,190,1.463,198,1.386,219,0.881,221,1.051,225,1.701,228,0.902,232,1.731,240,1.557,247,1.251,285,1.176,286,1.725,302,1.311,304,1.003,313,0.695,322,1.918,323,1.919,328,1.878,336,1.158,343,1.012,353,0.776,377,0.637,391,1.198,401,1.088,414,1.633,444,1.234,472,1.386,484,1.206,491,0.858,502,0.785,512,2.971,527,1.04,536,2.566,545,1.316,555,1.347,565,2.602,568,0.815,575,2.26,605,1.03,614,1.586,623,1.363,630,2.264,653,2.509,670,1.654,677,1.5,681,1.78,736,1.214,756,1.336,757,1.964,769,3.312,780,1.515,807,1.1,830,1.301,840,1.291,846,1.461,898,3.316,902,2.187,908,1.449,944,1.151,945,1.574,966,2.494,984,1.218,993,1.151,1005,1.86,1019,1.046,1072,1.316,1076,2.58,1094,1.423,1118,3.366,1132,2.722,1175,1.311,1191,2.593,1224,2.253,1329,4.977,1336,1.86,1364,1.423,1425,1.654,1527,1.878,1557,1.766,1616,1.584,1633,1.341,1671,1.565,1688,1.937,1689,1.492,1754,2.022,1765,1.633,1793,1.795,1849,2.588,1959,4.573,2010,2.999,2198,2.058,2254,1.47,2311,1.917,2421,1.7,2588,1.86,2631,1.86,3184,2.434,3595,1.049,3601,2.863,3616,2.929,3917,1.897,3918,1.811,4290,2.006,4291,2.006,4292,2.006,4293,2.006,4294,2.006,4295,2.006,4296,2.006,4297,2.187,4321,2.087,4322,2.506,4323,2.031,4330,2.318,4331,3.875,4332,2.506,5122,6.126,5190,7.165,5191,4.758,5192,4.758]],["t/3387",[24,1.171,27,1.717,28,1.362,56,1.072,84,0.783,107,1.852,113,2.637,127,2.372,196,2.867,343,2.573,405,4.097,426,4.279,495,3.492,511,3.462,512,2.494,536,2.874,565,2.361,568,2.764,577,3.422,583,4.417,647,2.713,653,2.277,729,5.162,757,1.782,892,4.063,984,3.094,1076,2.604,1100,2.867,1329,4.443,1336,4.728,1355,4.487,1693,4.231,1959,3.449,2672,5.23,3741,5.162,5188,5.89,5189,6.184]],["t/3389",[5,3.555,7,0.61,23,1.763,24,0.855,28,1.424,41,2.395,56,0.997,70,3.768,84,0.572,89,3.42,106,2.837,107,1.352,113,2.822,116,1.97,127,1.731,128,2.518,164,2.126,178,6.055,196,2.093,228,2.452,239,3.029,256,2.896,289,3.059,343,1.878,407,2.387,408,1.906,426,2.859,433,3.048,495,4.425,511,3.58,512,1.82,536,2.305,565,1.724,568,2.217,577,2.498,615,3.089,635,1.916,647,2.904,653,1.662,712,2.84,756,2.478,757,1.301,769,2.029,807,4.834,892,4.02,966,1.44,983,4.057,984,2.259,1100,2.093,1285,2.251,1329,3.564,1355,6.261,1405,3.419,1693,3.089,1959,2.518,2214,3.419,2487,7.835,3005,4.299,3671,4.057,3741,3.768,4337,8.068,4539,4.4,5193,7.741,5194,4.809]],["t/3391",[7,0.807,16,0.876,24,1.151,28,1.003,56,1.257,84,0.77,88,2.12,96,1.658,107,1.205,109,0.794,127,2.331,140,1.845,162,1.654,164,1.293,165,1.592,171,1.279,177,3.432,179,2.378,219,3.169,277,2.42,296,1.814,322,2.678,377,1.053,392,2.921,491,2.142,502,1.96,536,3.631,550,1.85,565,2.321,567,4.812,577,2.227,653,1.482,672,4.187,734,2.083,798,2.589,818,3.569,846,1.445,897,4.483,918,3.021,1019,2.611,1076,2.559,1224,1.73,1309,2.736,1329,2.168,1367,2.969,1398,3.24,2002,3.754,2004,3.923,2006,3.834,2010,2.98,2011,3.503,2012,3.452,2013,3.41,2017,2.992,2035,3.138,2092,3.404,3059,2.833,3600,3.834,3601,6.16,3645,3.923,3646,3.923,3647,3.923,3648,3.923,3679,3.923,4319,4.025,4324,4.025,4338,5.668,4339,4.145,4340,4.468,4341,9.014]],["t/3393",[7,0.556,24,1.537,36,3.112,56,1.059,68,2.676,84,1.297,98,2.48,113,3.912,203,1.69,304,2.418,322,2.208,346,5.335,398,2.486,401,2.698,482,3.156,502,1.946,536,2.827,543,3.974,565,2.305,567,3.807,577,3.339,578,2.774,580,2.451,614,1.299,622,3.56,756,3.313,892,3.552,938,4.345,966,2.925,1019,2.594,1540,3.905,1607,4.614,1638,3.645,1737,2.662,1765,4.049,2268,4.024,3607,5.176,4227,5.882,4342,6.698,4343,9.01,4344,6.698,5557,6.429,5719,7.606]],["t/3395",[7,0.852,15,3.141,16,1.603,88,3.879,103,2.882,113,3.141,199,4.248,225,3.079,226,5.239,243,4.711,292,3.746,377,1.926,491,2.596,511,2.746,536,2.565,550,3.385,565,3.528,567,4.332,593,4.248,672,5.073,719,5.182,798,4.737,855,5.301,892,3.223,1007,5.578,1253,5.145,2128,5.928]],["t/3397",[7,0.585,17,3.878,28,1.651,84,0.803,89,2.767,105,2.319,190,3.811,219,3.768,232,2.697,283,2.144,294,3.989,377,1.658,461,4.15,536,3.724,552,2.625,567,5.014,653,3.088,731,3.887,757,2.884,1266,3.215,2017,4.629,3059,6.616,4345,7.037,4346,11.105]],["t/3399",[2,1.225,4,2.146,7,0.795,23,1.517,24,1.12,25,1.455,27,1.642,28,1.182,56,1.049,84,1.233,88,2.046,89,1.696,109,0.766,113,2.523,127,1.49,132,1.335,163,1.801,165,1.55,171,1.234,174,1.351,177,2.194,182,1.52,193,1.653,219,2.142,223,2.558,232,1.653,240,2.485,275,2.142,278,1.628,283,1.314,285,1.877,294,2.444,313,1.69,322,1.421,338,1.118,447,2.15,472,4.08,476,2.719,498,2.528,502,1.253,512,3.475,525,2.141,527,0.993,536,3.163,558,3.094,565,1.484,567,2.774,568,1.301,575,5.044,614,0.836,736,3.573,755,2.046,757,1.705,765,2.695,773,3.554,796,2.382,816,2.574,857,3.801,879,3.623,892,1.7,898,4.532,977,1.657,984,1.944,1076,3.824,1080,2.528,1099,1.682,1101,2.714,1108,2.324,1132,3.072,1163,3.06,1178,2.623,1181,2.658,1224,1.67,1402,2.124,1425,2.641,1616,5.908,1913,2.558,2013,1.514,2254,2.347,2444,3.029,2624,2.942,3614,4.139,3899,2.999,3917,3.029,3942,2.89,4347,4.312,4348,4.312,4349,4.312]],["t/3401",[2,1.505,7,0.635,21,2.927,24,0.904,26,2.072,36,3.554,49,2.469,56,0.551,84,1.19,96,3.332,98,3.324,107,2.422,109,1.359,113,2.939,127,1.83,140,2.187,160,1.957,164,1.533,165,1.25,203,2.264,304,1.176,313,1.364,336,2.271,338,1.373,353,1.522,377,1.248,502,1.539,509,3.162,536,3.902,577,4.475,578,2.193,604,3.683,629,3.55,653,2.977,692,4.451,756,2.62,805,3.799,833,4.594,857,2.532,918,3.582,993,2.258,1019,3.476,1284,3.799,1402,4.843,1779,3.759,2010,3.824,2017,3.393,2257,3.886,2960,5.916,4350,7.648]],["t/3403",[4,2.752,7,0.699,48,3.6,49,1.994,56,1.087,84,0.961,140,3.477,171,2.411,203,2.124,272,3.633,295,3.568,322,2.776,536,2.643,613,4.011,614,2.027,731,5.77,802,3.589,828,4.591,2013,2.956,2431,6.333,3595,3.269,4351,7.856]],["t/3405",[2,0.93,4,1.728,7,0.553,12,1.391,24,1.303,29,1.203,36,1.521,49,1.252,56,1.242,68,1.308,84,0.957,96,1.215,98,2.463,105,1.079,179,1.154,196,1.367,203,1.678,219,2.492,254,1.109,304,1.696,322,1.743,336,1.403,346,2.606,372,2.623,377,1.246,391,1.452,395,1.363,398,2.469,401,1.318,444,1.495,472,1.679,482,2.492,502,0.951,536,3.597,543,1.942,565,1.819,567,3.227,577,1.632,580,1.935,623,1.651,647,2.63,653,1.086,736,1.471,757,1.727,780,4.285,838,1.739,846,3.512,892,2.085,938,2.123,966,2.823,993,1.395,1019,4.204,1037,1.716,1076,2.899,1118,2.353,1224,1.267,1329,1.588,1402,2.605,1540,1.908,1607,2.254,1616,1.919,1638,1.781,1689,1.808,1765,4.022,1849,1.876,1982,2.65,2009,2.299,2013,1.149,2017,3.389,2254,1.781,2727,2.698,2729,2.566,2960,2.157,2996,3.141,3059,3.352,3601,2.075,3607,2.529,3858,4.585,3906,2.566,3907,2.75,4227,2.874,4320,2.949,4351,2.461,4352,5.075,4353,5.287,4354,2.949,4355,3.273,4356,2.949,4357,3.273,4358,2.949,4359,3.273,4360,2.949,4361,3.273,4362,2.949,4363,5.287,4364,2.949,4365,3.273,4366,2.949,4367,3.273,4368,3.141,4369,3.273,4370,3.273,4371,3.273,4372,3.273,4373,5.287,4374,3.273,4420,2.606,5557,3.141,5715,3.449,5720,3.716,5721,3.716]],["t/3407",[1,4.256,7,0.577,24,1.77,49,1.646,56,1.23,84,1.184,171,1.99,272,2.998,322,3.043,372,2.74,377,2.176,395,2.894,397,4.045,430,3.612,482,3.275,536,3.787,550,2.878,565,2.391,611,3.044,846,2.248,901,3.596,993,2.962,1121,4.439,1192,6.037,1309,4.256,1329,4.48,1402,3.424,2013,3.241,2960,4.58,4351,6.944,4375,9.234,4376,6.949]],["t/3409",[2,1.457,7,0.426,12,2.178,36,2.382,49,1.769,56,1.225,84,1.105,107,2.377,109,1.72,127,3.556,162,1.898,165,1.21,179,1.807,183,2.318,275,1.672,296,2.082,304,1.138,313,1.32,322,2.904,336,3.778,391,3.909,536,3.48,614,1.709,647,2.026,757,1.331,827,2.737,846,2.416,993,3.184,1329,4.275,1959,4.428,1988,3.884,2010,4.424,2013,1.799,2990,3.299,3901,9.678,3902,9.678,4096,4.501,4097,4.501,4351,3.855,4354,4.618,4356,4.618,4360,4.618,4362,4.618,5123,9.292]],["t/3411",[2,1.52,7,0.64,49,1.824,56,1.287,84,1.127,85,1.952,109,1.937,162,1.98,165,1.262,179,2.715,296,2.171,313,1.377,322,1.762,336,2.293,391,2.372,452,3.878,502,1.554,536,3.284,577,6.063,614,1.037,705,3.468,757,2,827,2.855,846,2.92,916,3.966,993,2.279,1037,2.804,1754,2.395,1982,4.33,2010,4.36,2013,1.877,2990,3.442,3906,4.193,3907,4.493,4351,4.021,4358,6.939,4364,6.939,4366,4.818,4377,4.818,4378,5.347,4379,5.347,4380,9.025,4381,5.347,4382,5.347,4383,5.347]],["t/3413",[1781,3.73]],["t/3415",[7,0.705,15,3.26,34,2.277,56,1.092,228,2.832,322,2.797,377,2,398,3.149,401,3.418,465,6.226,539,5.738,543,5.035,607,3.427,683,3.345,747,3.671,820,4.595,1535,6.871,1536,6.556,1555,7.872,1563,7.281,1607,5.844,2013,2.978,3556,5.901,4255,7.872,4256,7.872]],["t/3417",[7,0.618,16,1.458,24,0.661,25,1.308,28,0.899,34,1.04,49,1.432,56,0.629,84,0.849,103,1.367,145,3.152,162,1.435,171,1.11,175,2.234,203,1.526,219,1.973,236,1.676,256,2.385,275,1.264,281,1.933,283,1.843,304,1.343,313,1.557,315,1.94,318,0.995,322,1.994,334,2.314,338,2.178,365,2.07,368,3.087,372,1.528,395,2.519,398,1.439,401,2.436,438,2.12,458,2.3,482,1.827,530,2.85,531,2.476,539,6.82,542,5.311,543,4.986,552,1.446,564,2.645,567,2.556,605,1.478,613,1.846,614,1.956,630,1.509,708,4.986,747,1.677,757,1.007,762,2.751,838,2.06,846,1.254,859,2.12,911,3.139,966,2.774,977,2.858,982,2.06,1013,2.555,1019,2.342,1070,2.329,1094,3.186,1141,2.915,1145,2.08,1219,1.82,1241,3.493,1297,3.8,1481,2.186,1531,2.723,1540,2.26,1550,3.04,1570,3.404,1638,2.11,1732,3.04,1737,1.541,1754,1.736,2013,1.361,2218,2.954,2577,2.152,2871,3.258,2887,3.729,3035,7.571,3036,3.493,3441,5.311,3595,3.537,3623,3.721,3719,3.493,3877,2.954,4056,4.817,4257,6.049,4258,6.049,4259,3.877,4260,6.049,4261,3.877,4262,6.049,4263,3.877,4264,6.049,4265,6.049,4266,3.721,4267,6.049,4268,3.877,4269,3.877,4270,6.049,4271,3.877,4272,3.877,4273,3.877,4274,3.877,4275,3.877]],["t/3419",[4,2.472,16,2.239,49,1.791,171,2.165,236,2.994,318,2.93,398,3.623,401,4.353,539,7.722,543,5.791,567,4.824,892,2.982,966,2.805,1094,3.983,1559,8.75,1638,5.312,3595,2.936,4276,7.563,4277,7.563,4278,7.563]],["t/3421",[4,2.439,16,2.105,49,1.767,171,2.136,236,2.975,398,3.591,401,4.325,539,7.682,543,5.741,552,4.005,567,4.8,892,2.942,966,2.78,1019,2.889,1094,3.93,1266,3.408,1559,8.694,1638,5.265,1732,7.586,3595,2.896,4280,7.462,4281,7.462,4282,7.462]],["t/3423",[1781,3.73]],["t/3425",[7,0.502,10,3.445,16,1.185,19,2.838,21,1.8,26,2.364,29,2.222,56,1.004,68,2.415,84,0.959,85,2.206,105,1.992,109,1.074,127,2.089,177,3.075,219,3.402,221,3.268,254,2.048,275,1.971,295,2.561,296,2.454,304,1.342,318,3.044,377,1.424,397,2.648,398,3.117,476,2.569,484,2.698,502,1.756,512,2.196,513,3.001,562,3.128,567,2.554,580,2.211,653,2.005,707,3.544,736,2.716,757,2.708,916,3.373,920,5.079,988,3.114,1071,5.607,1267,4.813,1302,3.861,1328,3.322,1456,3.355,1638,4.57,2015,3.752,2017,3.725,2031,4.736,2141,3.258,2338,3.503,2346,5.307,2624,4.124,2625,3.654,5722,6.863]],["t/3427",[2,1.52,7,0.82,25,1.804,49,1.824,56,0.939,84,0.879,113,2.055,132,1.655,203,1.943,219,1.744,228,2.571,246,2.131,256,2.108,302,2.595,313,1.377,316,2.511,318,3.12,351,2.039,353,1.537,393,2.767,398,4.267,482,2.52,491,2.446,536,1.678,543,3.173,614,1.494,615,3.297,621,2.896,683,2.108,723,2.014,724,2.744,731,2.954,757,1.388,822,4.848,831,3.554,857,3.682,963,3.416,995,3.135,1019,3.494,1051,3.935,1066,5.132,1108,2.882,1413,3.154,1475,5.116,1540,3.117,1633,2.655,2013,1.877,2017,3.416,2031,5.089,2141,4.865,2577,4.275,2879,4.696,2887,3.297,4009,3.923,4013,4.818,4088,4.818,4386,5.347,4387,5.347,4400,6.939,5723,6.072]],["t/3429",[16,1.878,26,3.748,85,3.497,127,3.311,279,4.362,283,2.919,318,2.458,381,4.776,395,3.99,828,4.21,916,3.848]],["t/3431",[2,0.93,4,1.07,7,0.696,19,1.537,25,1.104,27,1.911,28,0.786,29,1.203,31,0.843,40,1.817,49,0.775,56,1.193,68,1.308,84,0.759,100,1.542,105,1.079,109,1.745,127,3.814,165,1.57,172,1.93,177,1.665,179,1.154,182,2.346,193,3.437,219,1.067,221,2.058,277,1.772,279,1.49,295,1.387,313,0.843,318,1.96,338,0.848,376,1.966,377,1.568,388,2.494,395,2.202,398,2.835,405,3.006,418,1.724,472,1.679,476,3.187,502,0.951,511,1.776,512,1.921,552,1.972,567,1.383,575,3.33,578,1.355,614,0.635,647,2.09,653,2.207,723,1.992,757,2.177,803,1.942,818,1.732,828,1.438,837,2.09,840,1.565,846,2.152,892,2.085,898,1.865,916,1.315,944,1.395,966,2.195,984,2.999,993,1.395,1013,3.485,1075,2.921,1076,3.581,1077,3.118,1100,2.778,1108,1.764,1118,3.399,1224,2.047,1328,2.906,1425,2.004,1456,2.935,1467,2.157,1530,2.075,1539,2.299,1616,3.1,1671,1.897,1753,2.374,1765,1.978,1925,4.085,2010,1.939,2017,2.951,2031,3.751,2141,2.85,2254,1.781,2338,1.897,2346,4.643,3565,3.752,3899,2.276,3942,2.194,3970,2.65,4307,3.141,4335,2.75,4388,3.273,4389,3.273,4391,3.273,4393,3.273,4395,3.273,4396,3.273,4410,2.529,4416,3.273,4420,2.606,5724,3.716,5725,3.716]],["t/3433",[2,2.04,4,1.588,7,0.403,25,2.422,26,1.9,28,1.068,40,2.697,56,0.889,68,1.941,84,0.82,98,2.659,109,1.937,127,3.95,165,1.147,172,1.773,179,1.713,182,2.532,193,3.273,199,2.525,219,1.584,221,1.89,313,1.251,318,1.842,338,1.259,372,1.915,398,2.665,405,1.628,476,3.424,508,3.015,512,2.608,567,2.052,575,4.275,614,0.942,653,2.381,757,2.218,803,2.882,840,2.322,846,2.323,892,2.831,898,2.769,966,2.454,984,3.85,1013,3.201,1076,3.82,1108,2.618,1118,3.195,1224,2.78,1328,2.67,1425,2.975,1459,5.883,1539,3.412,1616,4.21,1765,2.936,1925,3.753,2017,2.155,2031,2.739,2141,2.618,2254,2.644,2421,3.058,3565,5.095,3858,3.926,3899,3.378,3942,3.256,4335,4.082,4420,3.869,5726,5.516,5727,5.516,5728,5.516]],["t/3435",[4,2.568,7,0.653,16,0.829,24,0.722,27,1.058,28,1.41,34,1.135,49,1.002,56,0.44,68,1.69,84,0.483,96,1.57,107,2.968,109,1.564,115,2.052,116,1.663,132,1.309,144,2.135,163,1.767,170,2.446,179,1.491,182,1.491,196,1.767,203,1.632,213,2.775,295,1.792,313,1.089,338,1.677,397,2.834,476,2.692,512,2.351,527,2.304,552,1.578,560,4.407,575,2.117,580,1.548,614,1.255,647,3.749,653,2.146,663,4.164,725,2.269,736,1.901,740,2.015,757,1.098,846,2.093,892,2.551,945,2.74,966,2.874,993,1.803,1076,3.34,1082,3.369,1093,2.525,1099,3.066,1101,2.663,1118,4.632,1132,3.027,1224,2.505,1402,2.084,1425,3.962,1616,3.794,1622,2.861,1988,1.865,2254,2.302,2256,7.892,2382,3.369,2900,4.592,3917,2.971,3942,2.835,3943,8.099,3944,3.425,4423,4.23,5015,5.153,5016,5,5017,5.239]],["t/3437",[4,3.432,28,1.561,109,1.866,116,2.803,182,2.513,203,2.368,213,4.026,338,1.847,397,4.113,527,2.744,560,5.267,647,2.817,653,2.364,757,1.851,807,2.902,846,3.038,945,3.274,966,3.207,1062,6.153,1076,3.563,1118,4.178,1224,2.76,1988,3.142]],["t/3439",[4,3.417,5,3.129,28,1.554,34,1.631,116,2.39,182,2.143,203,2.126,213,3.615,236,1.684,256,2.396,295,2.575,338,1.575,397,3.693,511,2.832,527,2.53,560,4.728,647,4.132,653,2.796,757,1.578,807,5.105,846,2.727,892,2.396,945,3.645,966,3.155,1076,3.967,1118,4.307,1224,3.264,1988,2.679,3841,4.313,3858,4.61,4424,6.077]],["t/3441",[4,3.288,5,2.428,24,1.716,28,1.496,31,1.684,39,2.805,84,1.012,116,2.572,144,3.301,182,2.306,203,2.236,213,3.802,232,2.507,338,1.696,367,3.509,397,2.865,414,3.954,527,2.596,560,4.973,647,2.586,653,2.17,757,1.698,769,4.367,807,2.664,846,2.116,908,3.509,945,3.091,966,2.89,1076,3.364,1118,2.911,1224,2.533,1266,2.988,1329,5.766,1765,3.954,1988,2.883,2198,4.984,3616,4.242,3841,4.642]],["t/3443",[4,1.484,24,0.775,28,1.528,34,1.219,56,1.224,107,1.225,109,1.95,171,1.955,172,1.657,182,2.407,193,1.74,221,1.767,246,1.809,247,2.102,254,1.539,261,2.425,296,1.844,304,1.008,318,1.165,338,1.177,377,1.07,398,1.685,476,1.389,485,3.616,491,1.442,502,1.319,512,2.481,527,2.102,552,1.694,600,3.189,614,1.898,647,1.795,653,2.722,658,2.291,757,1.773,803,2.694,818,2.402,846,2.952,944,1.935,945,2.861,957,4.181,966,2.622,984,2.047,1075,2.508,1076,3.9,1099,4.281,1118,3.651,1132,3.195,1133,6.152,1224,3.177,1328,2.495,2017,2.014,2031,2.56,2141,2.447,4429,6.827,4430,9.126,4431,9.126,4432,9.126,4433,6.827,4434,6.827,4435,6.827,4436,4.212,4437,4.358,5574,4.541]],["t/3445",[4,1.986,17,2.531,28,1.253,56,1.182,106,4.037,109,2.019,137,5.701,182,2.972,221,2.365,254,2.059,318,1.559,338,1.575,377,1.432,398,2.256,444,3.851,476,1.859,502,1.766,527,2.23,600,4.268,614,1.179,638,3.853,653,2.796,757,2.189,818,3.215,846,3.131,945,2.119,966,1.746,984,2.739,1076,3.967,1083,4.307,1099,4.079,1112,5.269,1118,2.704,1224,3.264,1328,3.34,1671,4.886,2017,2.696,2031,3.427,2141,3.276,2911,4.359,4440,7.084,4441,7.595,5633,6.404]],["t/3447",[0,2.326,4,1.956,27,0.956,34,1.606,56,1.231,106,1.477,108,3.256,109,1.845,113,1.469,127,1.321,162,1.416,171,1.713,172,3.897,182,2.599,193,3.98,203,1.509,221,1.488,236,1.059,254,2.498,261,2.042,296,1.553,304,1.329,318,1.535,338,0.991,372,2.359,377,0.901,391,1.696,398,1.419,408,3.444,418,2.014,426,2.869,446,2.563,476,2.255,491,1.214,502,1.739,552,3.58,600,2.686,614,1.161,647,2.365,653,2.445,725,3.955,757,2.165,813,2.609,818,2.023,829,2.123,846,2.698,872,2.609,937,2.407,944,3.142,966,2.118,984,1.724,1013,3.944,1075,5.736,1076,3.807,1077,5.662,1082,4.765,1118,4.272,1182,1.921,1224,3.229,1328,2.101,1337,4.203,1411,2.609,1671,3.468,1765,2.311,1925,4.623,2017,1.696,2031,2.156,2141,2.061,2402,3.152,2875,2.998,3841,2.713,3858,2.091,4121,3.445,4446,3.823,5575,4.029]],["t/3449",[4,2.472,28,1.125,109,1.92,116,2.974,172,4.168,182,2.667,193,2.899,224,3.376,338,1.96,391,3.355,407,3.602,476,2.313,513,3.755,552,2.821,608,6.245,647,2.99,653,2.509,725,4.057,757,1.964,807,3.08,846,3.157,944,3.224,966,2.173,1076,3.703,1118,3.366,1224,2.928,1849,4.334,1988,3.334,2868,4.334,3897,6.234,4447,7.563,4448,7.563]],["t/3451",[4,1.595,6,5.489,7,0.598,8,4.083,19,2.291,21,1.453,24,0.832,31,1.256,34,1.309,56,0.984,68,1.949,84,0.822,104,4.526,105,1.608,106,1.884,107,1.943,109,2.144,115,4.588,116,1.919,123,1.752,137,3.299,144,2.462,157,2.968,182,1.72,221,1.899,228,1.629,229,3.049,236,1.352,256,1.924,295,2.067,304,1.083,316,2.291,318,1.848,343,1.829,353,2.07,372,1.924,398,2.674,426,2.803,476,1.492,491,1.55,502,1.418,593,3.744,614,1.397,615,3.008,653,2.84,679,4.022,683,1.924,705,4.672,742,3.216,757,1.87,765,3.049,803,4.274,809,3.539,828,2.144,846,2.77,916,1.96,944,2.08,966,2.07,1037,2.558,1076,4.005,1081,3.539,1138,3.27,1224,3.315,1328,2.681,1364,2.57,1605,3.77,1671,2.828,2017,2.164,2031,2.751,2141,2.63,2430,3.164,3008,3.58,3610,4.683,4440,4.1,4449,8.563,5195,5.141,5729,5.54]],["t/3453",[7,0.563,15,2.605,46,4.982,56,1.268,116,2.666,182,3.203,254,3.078,338,1.757,377,1.598,600,4.762,614,1.315,647,3.591,653,2.249,715,3.672,757,1.76,846,2.938,916,2.723,984,3.056,1076,3.447,1083,5.59,1090,3.172,1132,4.793,1224,2.625,1275,5.954,1988,2.989,3838,5.166,3858,3.708,4440,5.697,4441,6.109,4452,6.78,4453,6.78,4454,6.78,4455,6.78,4456,6.78,4457,6.78,4458,6.78,4459,6.78,4460,6.78]],["t/3455",[7,0.543,21,2.64,25,2.207,26,2.559,32,4.311,56,1.236,84,0.747,98,3.283,109,1.787,127,3.476,163,2.732,193,2.507,221,2.546,246,2.606,318,1.678,338,1.696,343,2.452,377,2.09,398,2.428,405,2.193,426,2.546,476,3.077,506,4.088,508,4.06,513,3.248,527,1.507,710,5.392,723,4.061,757,2.612,838,3.477,879,5.496,984,2.949,1159,5.496,1328,3.595,2017,2.902,2031,3.688,2141,3.526,2419,5.744,2881,6.278,2928,4.922,5730,7.427,5731,7.427,5732,7.427,5733,7.427]],["t/3457",[7,0.728,19,4.114,20,4.236,25,2.169,28,1.592,96,2.386,103,2.266,219,2.858,221,2.502,225,2.421,226,5.469,243,3.704,256,2.534,278,2.427,280,4.309,296,2.61,306,3.008,318,3.089,366,4.236,472,3.298,511,2.159,512,2.335,513,3.192,562,3.326,567,2.716,607,2.596,611,2.816,615,3.963,683,2.534,757,2.588,851,5.644,853,5.791,977,2.47,988,3.312,1192,4.202,1290,3.99,1302,4.106,1335,5.644,1530,4.076,2017,2.851,2078,6.169,2140,6.169,2624,4.386,2625,3.886,4461,6.428,4462,6.428,4463,6.428,4464,6.428,4465,6.428,4466,6.428]],["t/3459",[2,1.47,7,0.924,17,2.154,25,1.745,28,0.769,34,1.388,56,0.922,84,1.23,85,1.888,88,2.455,89,2.034,103,2.651,109,0.919,127,1.788,134,2.713,171,1.481,179,1.824,182,1.824,202,3.752,219,2.889,226,2.643,232,1.983,247,2.396,272,2.232,283,1.576,296,2.101,304,1.149,306,3.518,313,1.332,318,2.923,322,1.705,338,1.341,353,1.487,397,2.266,401,2.084,418,4.665,421,3.41,438,2.829,471,2.259,476,1.582,502,1.503,511,2.526,526,3.942,534,3.89,567,4.985,575,3.764,591,3.108,605,1.973,614,1.459,672,5.498,723,3.336,734,2.412,802,2.205,828,2.273,897,3.438,1076,1.963,1224,2.911,1309,3.168,2013,1.816,2402,2.725,2886,4.264,2993,4.264,3932,4.264,4467,7.519,4468,5.173,4469,5.173]],["t/3461",[7,0.795,15,3.191,16,1.166,25,2.006,56,1.174,84,0.679,89,2.339,103,2.928,108,3.237,145,2.52,164,2.77,196,2.484,201,3.19,243,3.427,272,2.566,279,2.708,318,1.526,336,4.441,377,1.402,401,2.395,405,3.472,418,3.132,471,2.597,511,1.998,517,3.799,532,3.268,562,3.078,567,4.376,723,2.24,757,1.544,921,3.986,937,3.744,977,2.285,1039,4.136,1375,3.799,1394,3.986,1530,3.771,1797,4.997,4470,5.947,4471,5.947,4472,5.947,4473,5.947,4474,5.947,4475,5.947,4476,5.358,4477,5.947,4478,5.947,4479,5.947,4480,5.947,4481,5.947,4482,5.947,4483,5.947,4484,5.947,4485,5.947,4486,5.947,4487,5.947,4488,5.947,4489,5.947,4490,5.947,4491,5.947,4492,5.947,4493,5.947]],["t/3463",[7,0.673,15,3.471,28,1,31,1.146,48,1.902,56,1.312,85,2.455,103,1.569,105,1.467,109,1.195,127,2.324,140,1.837,161,3.192,164,1.288,171,1.274,179,1.569,225,3.653,243,2.564,300,2.842,318,1.142,336,1.908,377,1.049,392,2.909,401,1.792,405,1.492,437,2.201,472,2.283,476,1.361,511,2.259,532,2.446,562,2.303,567,3.817,601,2.201,607,1.797,670,2.725,747,1.925,829,2.47,830,2.143,921,6.501,923,5.387,1012,3.739,1013,2.933,1027,2.983,1039,3.095,1313,2.725,1352,3.158,1405,3.036,1631,3.818,1632,2.434,1633,2.21,1664,3.438,1718,3.126,2013,1.562,2253,2.842,2655,4.271,3137,3.907,3139,3.907,3763,4.507,3847,4.009,4476,6.059,4494,9.699,4495,6.724,4496,4.45,4497,4.45,4498,4.45,4499,4.45,4500,4.45,4501,4.45,4502,4.45,4503,4.45,4504,4.45,4505,4.45,4506,4.45,4507,4.45,4508,6.059,4509,4.009,4510,4.45,4511,4.009,4512,4.009,4513,3.668,4514,4.45,4515,4.009,4516,4.45,4517,4.009,4518,6.059,4519,4.009,4520,4.009,4521,4.009,4522,4.009,4523,4.009,4524,4.009]],["t/3465",[7,0.398,28,0.713,31,1.234,36,2.228,49,1.136,56,1.342,68,1.916,109,0.852,127,1.657,165,1.132,203,1.794,225,1.806,226,3.633,300,3.063,318,1.23,336,2.056,377,1.13,398,3.48,405,2.384,418,2.525,426,2.767,476,1.467,567,3.581,577,2.39,601,2.372,921,6.712,923,5.633,946,4.32,966,2.043,988,3.664,1012,4.029,1013,3.16,1027,3.214,1039,3.334,1309,2.936,1352,3.403,1475,2.718,1631,4.114,1632,2.622,1635,4.21,1664,3.705,1718,3.368,1763,4.602,1765,2.898,2253,3.063,2339,4.476,2775,4.602,3059,3.04,3137,4.21,3139,4.21,3468,5.975,3763,3.214,4420,5.663,4509,4.32,4511,4.32,4512,4.32,4513,3.952,4515,4.32,4517,4.32,4518,6.407,4519,4.32,4520,4.32,4521,4.32,4522,4.32,4523,4.32,4524,4.32,4525,4.795,4526,4.602,4527,8.476,4528,4.795,4529,4.795,4530,4.795,4531,4.795,4532,4.795,4533,4.795,4534,4.795,4535,4.795]],["t/3467",[7,0.825,12,1.31,14,2.417,16,1.446,26,1.97,27,1.26,29,1.852,36,1.432,48,1.318,56,0.999,84,0.842,101,1.729,102,2.049,125,1.852,140,1.273,153,1.661,157,1.875,162,1.141,164,0.892,177,1.568,183,1.394,189,1.738,196,2.103,197,1.447,203,0.778,213,1.322,219,3.002,221,1.96,227,2.144,232,3.916,304,0.684,318,2.684,321,2.455,336,2.16,351,1.175,353,0.886,377,1.505,381,1.537,388,4.866,395,1.284,397,1.35,398,1.144,405,3.931,418,1.624,426,3.395,430,1.602,432,4.244,433,1.875,474,2.645,495,2.562,502,2.362,506,1.927,508,1.913,511,2.73,555,1.537,567,3.115,572,3.563,588,3.248,591,1.852,601,1.525,611,1.35,614,1.577,647,1.219,653,1.67,685,1.807,755,1.463,757,2.11,844,2.645,892,2.518,901,1.595,977,1.184,984,3.932,1037,1.616,1076,2.798,1100,2.667,1108,1.661,1182,2.531,1236,3.538,1284,2.211,1364,1.624,1394,2.066,1402,4.534,1459,2.123,1632,1.686,1705,3.241,1768,3.613,2014,5.607,2017,1.367,2031,3.601,2257,2.262,2295,2.959,2346,2.707,2448,2.032,2652,5.307,2992,2.289,3059,1.954,3978,4.422,4410,5.697,4411,2.417,4413,3.949,4415,3.949,5188,2.645,5557,2.959,5734,3.5,5735,3.5,5736,5.719,5737,5.719,5738,5.719,5739,3.5,5740,3.5,5741,3.5,5742,3.5,5743,3.5,5744,3.5,5745,3.5]],["t/3469",[56,1.326,102,4.017,103,2.131,105,1.992,160,2.233,165,2.587,219,3.7,304,1.865,318,1.551,405,3.496,426,4.415,495,3.075,513,3.001,828,2.656,937,3.805,947,4.435,1017,4.545,3841,4.289,3970,4.894,3973,5.801,3978,5.307,3980,5.801,3983,5.801,4235,8.061,5188,5.186,5746,6.863,5747,6.369,5748,6.369,5749,9.536,5750,6.369,5751,6.369,5752,6.863,5753,6.863,5754,6.369,5755,6.369,5756,6.863,5757,6.369,5758,6.863,5759,6.863,5760,6.369,5761,6.369,5762,6.863,5763,6.863,5764,6.369]],["t/3471",[56,1.295,102,4.452,103,2.362,105,2.208,160,2.474,165,2.682,304,2.001,318,1.719,405,3.65,426,4.554,495,3.408,513,3.326,828,2.943,937,4.217,947,4.915,1017,5.037,3970,5.424,3973,6.429,3978,5.882,3980,6.429,3983,6.429,5747,7.059,5748,7.059,5750,7.059,5751,7.059,5754,7.059,5755,7.059,5757,7.059,5760,7.059,5761,7.059,5764,7.059,5765,7.606,5766,7.606,5767,10.231,5768,7.606,5769,7.606,5770,7.606,5771,7.606]],["t/3473",[1781,3.73]],["t/3476",[4,2.626,7,0.775,16,1.575,18,2.628,24,1.592,25,2.711,28,1.508,34,2.156,46,2.478,56,0.836,84,0.917,101,3.183,132,1.756,145,2.404,171,2.67,196,2.37,219,3.492,232,2.175,304,1.784,322,1.87,337,2.785,351,2.164,476,1.736,478,3.624,484,2.533,502,2.335,536,1.781,552,3.479,558,4.395,580,2.076,621,3.073,664,4.384,734,2.646,756,2.807,757,2.422,805,4.07,892,2.237,933,3.387,939,3.409,1019,4.304,1180,4.163,1237,7.968,1266,2.592,1413,3.347,1737,2.255,2013,1.992,2015,4.987,2986,8.034,3595,2.203,5136,5.674]],["t/3478",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,1271,5.023,4609,6.948]],["t/3480",[1781,3.73]],["t/3482",[4,1.893,5,2.15,7,0.896,16,1.136,19,3.828,23,2.038,24,1.61,28,1.522,56,0.603,84,0.661,89,2.278,96,2.15,109,1.029,145,2.455,163,2.419,164,2.36,170,1.803,174,1.814,186,2.876,219,1.889,224,2.586,232,3.924,274,5.086,275,1.889,278,2.187,283,1.765,284,4.111,291,3.595,313,1.491,361,4.111,400,4.775,421,3.818,491,2.996,545,2.821,550,2.399,552,2.16,555,2.888,558,4.824,596,2.749,601,2.865,683,2.284,757,2.117,765,3.62,880,4.028,888,3.216,904,4.028,914,5.418,984,2.611,1015,3.62,1020,5.792,1022,3.357,1037,3.037,1145,3.108,1182,4.097,1189,5.219,1290,3.595,1330,3.99,1374,4.111,1508,3.376,1913,3.437,2103,5.374,2250,4.477,4648,5.792]],["t/3484",[2,1.621,4,1.864,7,0.845,16,1.118,23,2.006,24,1.376,25,1.924,27,1.427,28,1.512,31,2.076,56,0.839,84,1.068,89,2.243,103,2.011,113,2.192,123,2.048,165,1.346,170,1.776,173,3.216,174,1.786,216,3.06,217,3.426,219,1.86,232,3.897,247,2.641,276,2.977,289,2.375,306,2.669,318,2.4,337,2.799,353,1.639,363,4.289,409,3.045,491,2.561,500,2.843,502,2.343,511,1.916,513,2.832,530,2.688,552,2.127,558,2.688,562,2.952,565,1.962,567,2.41,568,1.721,577,2.843,580,2.087,632,3.233,635,2.18,663,3.671,666,4.701,683,2.249,685,3.344,799,4.542,814,2.588,821,4.185,914,5.359,919,4.618,977,2.192,1182,2.866,1201,5.474,1266,4.9,1287,4.407,1398,4.137,4649,5.703,4650,5.703,5206,6.01]],["t/3486",[2,1.387,7,0.838,16,0.957,24,1.613,25,3.403,27,1.802,28,1.623,39,3.089,50,3.008,84,0.822,89,1.919,105,1.608,130,2.931,131,2.332,132,1.51,133,2.199,145,2.067,153,2.63,163,2.038,164,1.412,165,1.152,168,7.519,175,2.812,190,2.642,198,3.696,232,1.87,275,2.349,304,2.24,313,1.256,323,2.073,336,2.092,337,2.394,338,1.265,339,2.593,351,1.861,353,1.402,367,3.864,483,3.242,491,1.55,544,3.216,558,4.455,591,4.327,611,2.137,614,1.397,621,2.642,635,1.865,751,2.968,765,3.049,778,3.242,780,2.737,802,3.07,830,2.35,840,2.332,859,2.668,944,2.08,977,1.875,1080,2.86,1266,2.229,1364,2.57,1403,4.1,1423,6.525,1689,3.979,2035,3.427,2202,3.951,2250,3.418,2403,6.189,2404,6.21,2422,4.871,2501,5.937,4050,4.186,5207,7.59,5208,7.59,5209,5.141]],["t/3488",[1781,3.73]],["t/3490",[7,0.51,18,5.412,28,0.914,29,2.259,34,1.649,56,1.013,89,3.828,103,2.166,105,2.025,165,1.45,201,3.296,225,3.199,226,3.139,247,2.845,248,3.039,275,2.004,282,3.521,306,3.974,332,3.36,372,2.422,376,3.691,495,3.126,511,2.064,530,2.895,558,2.895,605,2.343,622,3.266,683,3.349,685,5.708,707,3.602,799,4.893,892,2.422,914,4.083,933,3.668,975,4.155,1007,4.193,1192,4.017,1193,4.508,1195,4.407,1196,4.62,1197,6.093,1198,4.747,1199,6.028,1200,5.395,1224,2.379,1336,4.232,1479,4.456,1923,3.446,2250,4.985,2427,5.7,2859,5.897,5200,6.475,5660,6.475,5772,6.977,5773,6.977,5774,6.977]],["t/3492",[2,2.157,7,0.768,16,1.676,18,5.121,19,1.879,25,1.35,27,1.001,28,1.129,31,1.596,34,1.074,84,1.056,88,5.249,89,3.637,123,2.227,128,3.116,132,1.239,162,1.482,164,1.159,165,1.464,169,3.573,196,2.589,197,1.879,213,1.716,225,2.335,226,2.045,275,2.476,279,1.822,283,1.219,304,1.377,306,2.901,313,1.03,351,2.364,353,1.782,440,1.86,472,3.181,483,2.66,484,1.787,495,3.155,500,1.995,510,8.121,512,4.091,557,3.251,558,1.886,575,3.103,605,1.526,628,2.178,647,3,659,2.731,734,2.891,736,3.41,762,2.84,796,2.21,828,1.759,838,2.127,857,1.913,892,2.445,897,4.121,898,3.534,899,3.363,933,5.103,1015,2.501,1017,3.01,1076,1.519,1195,2.871,1197,2.871,1199,2.84,1243,3.241,1331,2.706,1423,3.05,1424,3.187,1778,3.434,2093,2.871,2113,2.903,2151,3.363,2250,4.842,2331,2.683,2426,2.84,2883,3.713,2992,2.972,2993,5.111,3932,6.256,5201,4.217,5202,4.217,5203,4.217,5204,4.217]],["t/3494",[28,1.437,96,3.587,174,3.027,254,3.274,283,2.944,295,4.095,591,5.805,1290,5.998,2250,4.586,2444,6.788]],["t/3496",[16,1.789,23,1.918,24,1.333,28,1.357,56,1.394,132,1.687,145,2.31,170,1.697,174,1.708,203,1.375,224,2.434,225,2.054,239,3.296,289,2.27,318,1.399,506,3.408,558,2.569,614,1.058,723,2.941,740,2.597,749,3.72,760,1.697,780,3.058,802,2.324,807,2.22,933,3.255,977,2.095,1195,3.911,1197,3.911,1199,3.869,1237,3.72,1329,2.645,2247,4.049,2403,3.197,2404,3.954,2928,3.027,3810,3.593]],["t/3498",[16,1.16,28,0.88,56,1.391,109,1.051,145,2.507,170,3.601,174,1.853,203,1.492,224,2.641,225,2.228,239,3.576,614,1.148,723,2.228,740,2.818,749,4.037,760,1.842,772,4.114,802,2.522,933,3.531,977,2.273,1062,2.997,1145,3.174,1195,4.243,1197,4.243,1199,4.198,1329,2.87,1410,4.711,2247,4.393,2928,3.284,3016,5.678,3810,3.899,5205,6.234]],["t/3500",[1781,3.73]],["t/3502",[34,2.593,56,1.005,84,1.103,257,5.184,278,3.649,279,4.4,295,4.095,545,4.706,1364,5.09,4536,9.663]],["t/3504",[4,1.668,7,0.802,15,1.961,16,1.894,21,2.216,24,1.5,28,1.307,41,2.439,46,3.838,49,1.208,56,0.531,84,1.103,96,1.894,125,3.065,138,3.735,164,1.477,165,1.204,171,1.461,179,3.622,183,2.308,189,2.877,203,1.287,207,2.194,245,3.212,282,2.924,283,2.268,305,3.42,306,2.387,337,2.504,338,1.323,343,1.912,353,1.466,377,1.754,409,2.725,416,2.476,440,2.371,441,5.036,449,4.828,534,5.598,536,2.336,545,2.485,558,2.404,559,8.814,560,4.929,565,2.561,596,3.533,687,4.48,744,5.803,747,2.208,757,1.325,827,4.693,985,5.177,1179,3.584,1182,2.564,1297,3.803,1319,3.66,1389,4.378,2009,3.584,2076,3.125,2117,4.132,4110,7.693,4204,7.384,4537,5.102,4538,5.102]],["t/3506",[18,4.288,28,1.377,56,0.964,84,1.057,113,3.559,253,5.872,283,2.822,530,5.216,545,5.391,583,5.961,881,6.104,933,5.529,4539,8.132]],["t/3508",[7,0.739,21,2.648,23,3.128,84,1.015,88,4.22,89,3.497,283,2.709,353,2.555,359,4.131,391,3.944,472,4.562,476,2.72,512,3.23,545,5.261,575,4.451,614,1.725,1105,7.329,1186,7.2,3830,6.687]],["t/3511",[171,2.866,232,3.837,304,2.223,545,4.876,1737,3.978,3595,3.886]],["t/3513",[4,3.105,17,3.956,84,1.084,219,3.098,237,7.339,284,6.741,304,2.109,545,4.626,1413,5.602,2424,9.117,4540,9.498,4541,9.498]],["t/3515",[1781,3.73]],["t/3518",[4,2.645,7,0.779,16,1.587,18,2.655,24,1.601,25,2.73,28,1.515,34,2.172,46,2.504,56,0.842,84,0.924,101,3.216,132,1.774,145,2.429,171,2.685,196,2.394,219,2.64,232,2.197,304,1.797,322,1.89,337,2.814,351,2.186,476,1.754,478,3.662,484,2.559,502,2.352,536,1.799,548,3.718,558,4.42,580,2.098,621,3.105,664,4.43,734,2.673,756,2.836,757,2.436,805,4.112,892,2.26,933,3.422,939,3.444,1019,4.319,1180,4.206,1237,7.989,1266,2.619,1330,7.019,1413,3.381,1737,2.278,2013,2.012,2015,5.023,2296,5.318,3595,2.225,5138,5.733]],["t/3520",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,1271,4.662,1693,5.482,4609,6.449]],["t/3522",[1781,3.73]],["t/3524",[7,0.644,21,1.231,24,1.691,25,1.395,27,2.734,28,0.946,29,2.338,48,1.768,56,1.25,63,3.349,84,0.472,88,1.963,89,3.692,95,4.158,106,1.597,107,2.09,108,2.251,109,1.13,112,2.159,113,1.589,128,3.196,135,3.82,164,1.197,165,1.829,169,3.665,193,3.598,203,1.043,232,2.437,294,2.345,296,2.583,306,1.935,336,1.773,343,1.55,377,0.975,391,1.835,446,4.263,447,2.062,476,1.265,483,2.749,496,2.662,505,1.806,507,4.441,512,2.311,558,1.949,635,1.581,647,1.635,653,2.109,659,2.822,672,2.567,740,5.316,767,3.349,807,3.155,873,3,897,4.227,966,1.827,993,2.711,1062,3.926,1075,2.284,1089,2.662,1090,1.935,1100,2.656,1102,4.877,1121,2.642,1132,1.935,1133,3.726,1207,3.151,1218,2.849,1266,3.973,1329,3.759,1354,3,1375,2.642,1437,5.065,1539,2.905,1622,2.797,1729,5.344,1730,3.549,2093,2.967,2191,2.876,3565,2.935,3576,3.294,3742,3.349,4897,3.294]],["t/3526",[1781,3.73]],["t/3528",[512,3.773,575,5.198]],["t/3531",[56,1.169,171,2.719,304,2.109,322,3.131,567,4.013,1019,3.678,1266,4.339,1737,3.775,2013,3.334,3595,3.687,4631,9.498]],["t/3533",[304,2.243,438,5.524,966,2.903,989,5.994,4632,10.102]],["t/3535",[56,1.014,304,2.165,567,4.119,651,6.847,966,2.801,989,5.784,1123,6.051,1297,4.98,1732,7.643]],["t/3537",[24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,567,3.979,635,3.601,747,4.075,4609,6.831]],["t/3539",[16,1.911,171,2.791,304,2.165,543,5.784,567,4.119,892,3.843,1266,4.453,1737,3.874,3595,3.784]],["t/3541",[56,1.042,304,2.223,401,4.032,966,2.877,1607,6.895,1638,5.448]],["t/3543",[56,1.042,304,2.223,398,3.716,401,4.032,966,2.877,1638,5.448]],["t/3545",[7,0.699,24,1.437,27,2.106,28,1.252,84,0.961,88,3.997,103,4.005,105,2.776,133,3.796,145,3.568,343,3.156,377,1.985,511,3.509,557,4.416,567,3.558,568,2.541,672,5.227,706,6.333,740,4.976,1753,6.108,2117,6.819,2128,6.108,3810,5.55]],["t/3547",[1781,3.73]],["t/3549",[7,0.528,16,1.943,18,4.933,19,4.082,24,1.084,27,1.589,28,1.754,31,1.636,56,0.905,84,0.992,88,4.126,89,4.387,116,2.499,169,3.662,198,3.26,225,2.393,240,3.662,283,1.936,292,2.913,296,2.58,302,3.083,303,5.238,332,3.475,390,3.233,391,2.819,397,2.784,491,2.018,527,1.464,608,3.475,712,3.602,723,2.393,734,2.963,740,3.027,865,4.51,873,4.609,897,5.777,908,3.409,942,4.91,993,2.709,1022,3.683,1076,2.411,1100,2.654,1188,4.259,1216,8.326,1218,4.377,1288,4,2928,3.528,2965,5.58]],["t/3551",[16,2.026,18,1.476,19,1.497,23,1.122,24,1.115,25,3.282,27,2.069,28,1.603,29,1.903,56,0.783,96,1.183,98,1.181,100,3.08,102,3.441,107,2.232,109,1.786,113,1.989,116,1.254,127,1.789,133,1.437,140,1.317,145,2.194,163,4.502,165,1.776,170,0.993,172,1.89,182,2.304,183,1.442,198,1.636,216,1.71,231,2.43,232,1.222,254,2.215,278,1.204,279,1.452,285,2.254,295,1.351,306,1.492,322,1.051,334,1.903,336,1.367,338,0.826,343,1.94,353,0.916,377,1.22,405,3.463,440,1.481,476,1.584,495,2.634,511,1.739,512,1.881,517,2.037,557,1.672,558,2.439,577,2.581,592,2.156,614,0.619,636,1.892,647,1.26,653,1.058,719,2.022,723,4.244,734,1.487,757,1.953,760,2.035,778,3.441,840,2.475,855,2.068,944,1.359,945,3.089,974,2.052,984,3.391,993,1.359,1013,2.101,1022,1.848,1037,3.427,1062,2.623,1074,2.43,1076,2.855,1099,2.55,1100,2.162,1105,2.628,1112,1.993,1182,1.602,1219,1.497,1244,2.8,1303,2.156,1329,3.171,1768,2.287,1778,2.736,2076,1.953,2247,2.368,2250,1.513,2311,2.263,2450,2.24,2911,2.287,2928,5.581,3741,2.398,3811,2.582,3899,3.6,4560,2.582,5018,2.628,5198,3.36]],["t/3553",[16,2.212,24,1.328,25,1.83,27,2.939,28,1.677,31,1.397,84,0.619,107,2.455,109,1.946,116,2.134,127,2.689,163,4.82,165,1.281,203,1.369,232,2.983,278,2.049,281,2.705,293,4.559,360,5.852,367,2.911,377,1.279,507,4.46,596,3.693,653,2.581,723,2.044,740,2.584,807,3.169,840,2.594,938,3.519,944,2.313,945,2.713,979,3.811,1022,5.274,1062,4.61,1076,2.059,1100,2.266,1102,3.415,1182,2.727,1219,2.548,1266,3.555,1329,3.776,1354,3.935,1377,4.254,2247,4.029,2928,5.518,3072,4.888,3899,3.773,5199,5.717]],["t/3555",[7,0.28,16,1.948,21,2.707,25,1.138,26,2.655,27,0.843,28,1.524,34,1.822,56,1.232,84,1.09,88,3.689,96,1.252,98,2.005,100,1.589,105,2.237,106,2.092,107,1.461,108,1.835,109,0.962,113,1.296,128,2.722,160,1.246,163,2.262,165,1.279,169,1.943,179,1.189,200,2.827,201,1.809,278,1.273,295,1.429,296,1.369,297,1.943,313,0.868,322,1.111,323,2.302,338,0.874,339,2.879,353,0.969,377,2.006,413,2.685,416,2.628,418,1.776,426,2.108,430,1.752,476,1.031,484,1.505,502,1.574,506,2.107,507,3.104,508,4.824,509,3.233,512,3.821,520,3.236,552,1.258,622,2.879,723,3.86,731,1.862,736,4.463,757,1.406,898,3.869,934,4.725,945,1.176,984,3.06,1015,3.385,1022,1.954,1076,1.279,1132,1.578,1202,2.187,1265,2.992,1394,2.26,1425,2.065,1511,4.675,1705,4.369,2015,2.093,2093,2.419,2214,2.301,2252,2.644,2341,5.825,2426,2.393,2545,2.345,2928,5.514,2992,2.504,4543,3.372,4544,3.372,4545,3.372,4546,3.372,4547,3.372,4548,3.372,4549,3.372,4550,3.372,4551,5.416,4552,3.372,4553,5.416,4554,3.372,4555,3.372,4556,3.372,4557,3.372,4558,3.372]],["t/3557",[16,2.197,24,1.912,25,3.46,28,1.525,31,2.104,39,3.505,198,4.194,282,4.684,289,3.404,302,3.966,409,4.365,505,3.569,635,3.125,723,3.079,830,3.936,1181,5.039,1210,6.737,1988,4.52,2403,6.012,2422,6.934,2501,6.737,2928,4.538]],["t/3559",[5,3.249,16,1.716,23,3.763,28,1.301,145,3.709,163,3.655,165,2.066,169,5.044,239,6.984,367,4.695,511,2.94,807,4.705,966,2.515,1100,3.655,2426,6.211,2992,6.499,3741,6.581]],["t/3561",[2,2.1,16,1.638,19,1.801,24,1.547,25,1.294,27,1.501,28,1.625,56,0.399,89,1.509,97,1.808,102,2.55,107,2.594,109,1.066,113,1.474,125,2.305,127,2.073,132,2.287,140,1.584,145,2.542,153,2.068,163,1.602,165,2.269,170,1.868,172,1.4,179,1.353,182,2.115,183,1.735,198,1.968,228,1.281,239,2.319,245,2.415,275,1.251,276,3.857,277,2.078,289,3.077,294,2.175,313,2.151,320,2.319,323,1.63,324,3.348,351,1.463,365,2.049,381,4.165,394,2.021,398,1.424,405,3.846,426,1.493,432,2.55,444,1.753,468,2.572,472,1.968,495,1.952,502,1.115,511,2.807,527,1.382,550,1.589,555,1.913,577,1.913,581,2.923,621,2.078,635,1.467,662,2.349,683,1.513,712,2.175,723,1.445,748,3.055,749,2.618,750,2.849,757,1.558,759,1.417,760,1.194,769,1.554,780,4.144,814,1.741,820,3.249,840,1.834,859,2.098,891,3.559,917,2.29,994,1.741,1015,2.398,1022,2.224,1036,3.055,1072,4.416,1099,1.497,1100,1.602,1172,2.433,1182,3.713,1183,2.783,1214,3.457,1229,4.946,1331,2.594,1407,2.885,1475,2.175,1987,2.849,2010,1.407,2253,2.451,2333,3.457,2434,2.752,2448,3.954,2479,4.946,2898,2.723,2946,5.042,3184,3.457,3972,2.643,3974,3.682,3982,3.682,4004,3.559]],["t/3563",[21,2.669,24,1.529,28,1.736,89,3.525,107,3.15,109,1.593,165,2.116,179,3.16,338,2.323,405,3.005,635,3.427,723,3.376,820,4.854,1229,7.388,2928,4.976]],["t/3565",[1781,3.73]],["t/3567",[2,2.079,4,3.122,16,1.434,23,2.573,24,1.924,25,2.468,27,1.83,84,0.835,109,1.3,170,2.277,174,2.291,254,2.478,313,1.883,476,2.921,512,3.864,536,2.295,568,2.882,653,2.426,723,2.755,747,3.165,759,2.702,760,2.277,803,4.34,846,3.44,857,3.497,892,2.884,966,3.056,1076,3.624,1090,4.469,1224,2.832,1959,3.676,2472,5.735,4125,6.786]],["t/3569",[4,2.711,28,1.233,84,1.181,217,6.217,552,3.86,592,5.61,653,2.751,725,6.052,846,2.684,966,2.384,1075,5.717,1076,3.927,1077,6.104,1093,7.052,1224,3.212,1671,4.808,2190,4.892,3595,3.22]],["t/3571",[512,3.808]],["t/3573",[7,0.727,16,1.716,24,1.493,27,2.189,31,2.253,56,0.911,84,1.221,165,2.066,281,4.363,512,3.18,553,4.884,635,3.346,723,3.296,971,6.762,1181,5.396,1266,3.998,1622,5.918,1988,3.858,2179,8.4,2403,5.131,2422,5.918]],["t/3576",[23,3.342,56,1.169,171,2.719,304,2.109,322,3.131,1019,3.678,1266,4.339,1737,3.775,2013,3.334,3595,3.687,4633,8.558]],["t/3578",[23,3.313,24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,635,3.601,747,4.075,4609,6.831]],["t/3580",[7,0.842,15,3.316,16,1.988,23,2.559,24,1.81,25,1.668,27,1.237,49,1.171,56,1.141,84,0.985,109,1.292,110,2.745,127,1.708,170,1.539,171,2.082,318,1.268,321,3.937,322,1.629,338,1.281,377,1.165,395,3.029,410,3.438,437,2.445,505,2.159,527,1.676,528,4.038,532,2.717,596,2.346,635,1.89,723,2.74,747,2.139,759,1.826,760,1.539,769,2.002,830,2.381,876,3.286,977,1.9,1019,1.914,1266,2.258,1400,2.704,1401,3.069,1405,4.963,1759,3.586,1773,3.937,1923,5.337,1959,2.485,1988,2.179,2013,3.029,2076,3.028,2137,3.82,2142,2.592,2228,3.627,2274,3.767,2293,3.048,2403,2.899,2406,8.828,2410,4.586,2722,3.206,4016,3.82,4633,4.454,4634,4.944,4635,4.944,4636,7.273,4637,4.944,4638,4.944,4639,4.944,4640,4.944,4642,7.273,5396,5.21,5397,5.21,5398,5.21,5399,5.21,5400,5.21,5401,5.21,5402,5.21,5403,5.21,5404,5.21,5405,5.21,5406,5.21]],["t/3582",[1781,3.73]],["t/3584",[2,2.507,23,3.103,24,1.834,56,0.918,179,3.11,221,3.433,313,2.767,324,4.922,512,3.205,552,3.29,723,4.049,857,4.217,1019,3.415,1224,3.415,1266,4.03,1271,4.625,1309,5.402,2624,6.019]],["t/3586",[512,3.808]],["t/3588",[7,0.664,16,1.568,24,1.726,25,2.698,27,2.916,28,1.189,31,2.059,68,3.195,84,1.155,96,2.969,132,2.475,281,3.987,512,3.675,553,4.463,635,3.867,653,2.653,898,4.558,971,6.179,995,4.689,1181,4.93,1182,4.019,1285,3.594,1622,6.84,2403,4.689,2422,5.408,2825,6.592]],["t/3591",[56,1.163,171,2.696,219,3.072,304,2.091,322,3.104,552,3.513,1019,3.647,1266,4.302,1737,3.743,2013,3.306,3595,3.656,4608,8.082]],["t/3593",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,1271,5.023,4609,6.948]],["t/3595",[7,0.841,15,2.786,16,1.422,21,1.466,24,1.469,27,2.153,29,1.809,34,1.321,49,1.166,56,1.139,84,0.562,85,1.797,103,1.735,107,1.328,109,1.529,110,4.025,127,1.701,158,2.592,160,1.818,219,2.807,221,3.35,225,1.854,322,2.39,353,1.414,395,2.05,401,1.982,410,3.423,476,1.506,527,2.188,552,3.542,562,3.752,564,4.947,596,2.336,607,1.988,613,2.344,755,2.336,757,1.278,759,1.818,760,2.257,769,1.993,977,1.891,988,2.536,1013,3.244,1019,1.906,1239,4.435,1285,2.212,1328,4.73,1365,2.994,1405,4.947,1718,5.092,1732,5.685,1759,3.57,1857,3.611,1923,2.761,2013,2.545,2031,4.088,2228,3.611,2274,3.751,2293,3.034,2339,3.098,2346,4.322,2403,2.886,2569,4.136,2621,3.751,3070,3.57,4016,3.803,4054,4.223,4508,4.435,4608,4.223,4610,4.922,4611,7.25,4612,4.922,4613,7.25,4614,4.922,4615,4.922,4616,4.922,4617,4.922,4618,4.922,4619,4.922]],["t/3597",[55,5.686,192,6.158,272,4.28,418,5.226,530,4.675,596,4.709,814,4.502]],["t/3599",[1781,3.73]],["t/3601",[7,0.629,23,1.837,24,1.845,25,1.762,29,1.92,56,0.927,68,2.086,84,1.115,160,2.796,165,2.102,177,2.657,179,1.841,198,2.679,199,2.714,219,1.703,221,3.466,228,1.743,232,2.001,240,3.009,248,2.583,275,1.703,283,1.591,296,2.121,313,1.949,316,2.452,324,2.914,338,1.962,353,1.501,377,1.231,397,2.288,422,3.746,491,1.659,512,2.75,522,4.095,530,4.196,548,4.91,552,1.948,647,2.064,723,3.678,757,2.536,760,1.626,822,3.287,857,2.496,898,4.315,934,2.714,966,1.501,1019,2.022,1051,3.868,1193,3.831,1213,3.979,1224,2.022,1266,4.068,1271,2.738,1300,2.976,1309,3.198,1330,5.214,1413,6.376,1570,4.585,1705,3.361,1913,3.098,2402,2.75,2624,3.563,2868,2.993,3869,4.228,4625,7.57,4626,5.222,4627,5.012,4628,5.222,4629,5.222]],["t/3603",[512,3.808]],["t/3606",[56,1.163,171,2.696,304,2.091,322,3.104,548,6.108,1019,3.647,1266,4.302,1330,6.487,1737,3.743,2013,3.306,3595,3.656,4297,7.626]],["t/3608",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,1271,4.662,1693,5.482,4609,6.449]],["t/3610",[272,4.48,596,4.928]],["t/3612",[2,0.803,4,0.923,5,1.048,7,0.644,15,2.308,16,1.178,18,2.17,23,2.46,24,0.482,27,0.706,28,0.697,31,0.727,32,1.862,34,0.758,56,1.138,68,1.128,84,0.885,85,1.031,88,1.34,89,1.111,105,1.545,116,1.111,131,2.24,138,1.2,156,2.026,160,1.043,162,1.046,164,1.357,165,1.83,171,1.342,174,0.885,186,1.402,196,1.18,201,1.515,219,1.529,228,0.943,232,2.302,239,1.707,275,0.921,283,2.363,284,2.004,292,1.295,296,1.903,302,1.37,304,0.627,306,2.193,313,1.546,317,1.488,318,1.202,322,1.98,347,1.964,348,1.91,351,1.787,359,1.312,361,2.004,366,1.862,367,1.515,372,1.114,377,0.666,391,1.253,393,2.425,395,1.176,397,2.631,405,2.014,407,1.345,408,1.074,415,2.097,440,1.312,441,1.619,466,2.097,471,1.233,504,1.804,512,1.026,530,2.208,536,1.885,539,5.243,543,4.148,544,1.862,550,1.17,552,1.053,558,2.208,560,1.584,572,1.576,575,1.414,614,0.548,621,2.538,712,1.601,734,1.317,743,1.964,747,1.222,790,3.439,795,1.945,796,1.56,802,1.998,807,1.15,843,2.701,845,1.893,857,2.24,859,2.563,904,7.874,907,3.621,908,1.515,911,5.661,915,2.824,933,1.686,934,2.436,977,1.085,995,1.656,1010,1.984,1019,3.002,1080,1.656,1094,1.488,1096,2.711,1108,1.522,1129,2.424,1178,1.718,1219,1.326,1246,1.893,1265,1.56,1285,1.269,1337,1.984,1374,4.263,1481,2.643,1502,2.545,1503,2.545,1507,6.893,1535,3.795,1536,3.621,1540,1.646,1542,6.71,1559,2.287,1560,2.711,1563,2.424,1606,2.824,1607,1.945,1608,2.824,1609,2.824,1610,2.824,1611,2.824,1612,2.824,1613,2.824,1614,2.824,1615,2.824,1616,1.656,1622,4.062,1623,4.687,1775,2.48,2013,0.991,2035,3.292,2094,2.152,2316,2.545,2577,2.602,2588,1.945,2989,2.711,3037,2.824,3041,2.824,4123,2.976,4255,2.62,4256,2.62,5407,2.711,5408,2.976,5409,2.976,5410,2.976,5411,2.976,5412,2.976,5413,2.976,5414,2.976,5415,2.976,5775,3.207]],["t/3614",[1781,3.73]],["t/3617",[23,3.37,24,1.635,27,2.396,126,4.874,128,4.815,304,2.127,338,2.483,404,4.915,453,6.478,635,3.663,4609,6.948]],["t/3619",[7,0.646,16,1.524,23,3.495,24,1.695,27,1.945,36,3.612,56,1.034,68,3.106,79,6.827,106,3.002,126,5.054,128,4.992,131,3.716,175,4.48,200,4.058,202,5.639,203,1.961,204,6.671,207,3.344,209,7.462,219,2.536,223,4.613,304,1.726,335,6.827,339,4.132,409,4.151,416,3.772,496,5.003,500,3.876,502,2.259,1083,3.972,2013,2.729,3399,7.462]],["t/3621",[7,0.664,16,1.568,23,3.558,24,1.726,36,3.716,41,3.823,56,1.052,68,3.195,79,7.023,126,4.069,128,5.083,131,3.823,201,4.29,202,7.336,203,2.017,204,6.863,207,3.44,210,7.676,219,3.299,223,4.745,304,1.776,335,7.023,339,4.251,409,4.27,496,6.51,1083,4.086,2013,2.807]],["t/3623",[7,0.739,23,3.128,24,1.517,41,4.251,98,3.292,131,4.251,201,5.795,202,6.449,207,3.824,304,1.974,334,5.308,339,4.726,613,4.235,614,1.725,651,6.246,1583,7.472,1737,3.534,3595,3.452,4652,8.892]],["t/3625",[2,1.312,7,0.688,16,0.905,19,2.167,23,4.039,24,1.179,27,2.301,28,1.368,29,1.697,49,1.637,56,0.48,68,1.844,84,1.125,100,2.175,121,2.846,126,2.349,128,2.32,131,2.207,171,2.634,174,2.165,182,1.628,183,3.126,196,1.928,201,2.476,203,1.744,207,1.985,253,2.927,262,3.179,283,2.525,302,2.24,304,1.84,313,1.188,315,2.31,316,2.167,322,1.521,336,3.553,339,2.453,344,6.228,345,7.466,346,9.678,353,1.326,371,4.159,401,1.859,437,2.283,466,3.428,479,3.387,481,2.906,489,3.242,490,3.428,491,1.466,495,2.349,498,2.706,500,3.446,544,3.042,605,1.76,614,1.341,681,4.556,756,2.283,892,1.82,899,3.879,977,1.774,993,1.968,1019,2.676,1083,3.532,1364,2.431,1374,5.881,1686,7.953,1699,3.018,4653,4.159,4654,6.912,4655,4.616,4656,4.616,4657,4.616,4658,4.616]],["t/3627",[2,1.424,7,0.722,12,2.129,16,1.44,23,2.584,24,0.855,28,1.424,29,1.842,84,1.257,88,4.839,89,4.01,109,0.89,127,1.731,128,2.518,129,3.154,132,1.551,164,1.45,165,1.183,169,4.233,179,2.59,213,2.148,219,1.634,236,1.388,241,4.13,275,1.634,280,3.359,296,2.035,304,1.113,313,1.29,315,2.508,316,4.083,318,1.286,337,2.459,397,3.218,404,2.571,422,3.594,440,2.328,458,2.973,470,3.818,472,2.571,476,2.66,512,3.705,532,2.754,544,3.302,545,2.44,562,3.801,568,2.624,575,5.104,605,3.316,622,2.663,681,3.302,736,2.251,753,4.514,816,2.991,822,3.154,857,2.395,898,4.187,899,4.21,1019,1.94,1162,2.938,1413,4.332,1430,4.648,2402,2.639,2421,3.154,2426,5.213,2545,3.485,2641,3.768,2992,5.455,2993,4.13,4659,5.01,4660,5.01,4661,5.01]],["t/3629",[2,1.129,5,1.475,7,0.917,16,0.779,21,1.183,23,3.243,24,1.664,25,2.08,27,1.89,28,0.917,29,1.461,31,1.023,33,2.387,34,1.066,56,0.959,84,1.113,89,2.971,100,1.872,105,1.31,107,1.072,109,1.096,127,1.373,140,1.641,155,2.711,162,1.471,163,1.659,169,3.553,174,1.244,177,2.022,182,1.401,190,2.152,193,1.523,196,1.659,198,2.039,248,1.965,273,3.339,275,1.296,278,1.5,283,1.879,302,1.928,304,1.369,321,3.164,336,2.644,347,2.763,353,1.142,365,2.122,367,2.132,377,1.453,382,2.598,393,2.056,407,2.937,502,1.155,506,2.484,511,1.335,512,2.24,555,1.981,558,2.905,560,2.229,568,1.199,628,2.162,630,1.546,685,2.33,723,2.322,736,1.785,755,1.886,763,2.85,807,1.618,827,2.122,830,2.969,838,2.112,876,4.098,945,1.385,984,1.791,1019,1.538,1181,2.45,1182,1.997,1285,1.785,1289,2.663,1302,2.538,1370,3.339,1400,2.173,1401,2.466,1409,2.763,1413,2.343,1511,2.737,1773,3.164,1843,2.82,2076,2.433,2250,2.926,2341,3.41,2359,4.524,2402,3.98,2403,5.404,2404,6.685,2405,8.973,2406,7.009,2407,4.187,2408,3.973,2409,4.187,2410,3.686,2411,4.187,2412,4.187,2413,4.187,2414,4.187,2415,4.187,2416,3.973,2417,6.498,2419,3.489,2420,3.58,2421,2.501,2422,2.687,5776,4.512]],["t/3631",[1781,3.73]],["t/3633",[2,0.533,5,1.224,16,1.651,24,1.996,28,1.668,29,0.689,56,1.235,84,0.376,97,0.883,105,0.618,129,1.18,130,1.981,132,1.021,153,1.01,155,1.279,160,2.24,162,0.694,164,1.279,165,1.432,170,0.583,193,1.693,196,0.783,203,0.473,224,4.473,225,1.664,232,1.264,239,1.994,243,1.08,275,0.611,277,1.015,279,0.853,289,2.785,297,1.08,323,1.402,324,1.046,351,0.715,365,1.001,367,3.588,377,1.254,394,3.194,405,2.42,407,2.889,446,1.256,495,2.248,502,2.687,505,0.818,511,0.629,514,1.492,527,0.432,528,1.831,550,2.989,555,1.644,568,0.565,583,1.206,591,1.126,605,0.715,607,1.784,609,1.291,614,0.364,630,0.729,632,1.062,635,0.716,650,1.33,662,2.705,663,1.206,675,1.256,683,0.739,734,0.874,748,2.627,749,2.251,754,1.608,760,1.027,802,0.799,807,3.553,814,0.85,816,1.969,820,2.393,871,1.448,916,1.775,934,0.974,945,1.15,947,3.241,966,0.538,974,1.206,977,0.72,989,2.621,994,0.85,1005,1.291,1015,1.171,1019,1.277,1027,3.567,1055,3.366,1062,0.949,1072,1.606,1079,2.912,1093,1.119,1129,2.83,1175,1.6,1178,2.007,1195,1.344,1197,2.366,1199,1.33,1271,1.729,1285,1.482,1303,2.23,1329,1.6,1347,1.375,1353,3.072,1376,1.33,1399,2.091,1403,1.575,1405,3.015,1458,1.448,1462,1.068,1467,2.174,1633,2.194,1699,2.156,1718,3.103,1923,1.051,1941,2.83,1989,2.719,2182,4.111,2250,2.097,2253,1.197,2256,3.169,2263,2.83,2264,1.469,2390,2.719,2426,1.33,2439,1.545,2584,1.391,2613,1.608,2938,1.575,2950,1.517,2992,1.391,3027,1.469,3074,1.608,3504,3.06,3507,1.738,3810,2.174,4731,5.821,4733,4.24,5179,1.798,5210,3.476,5211,1.975,5212,1.975,5213,1.975,5214,1.975,5215,1.975,5216,1.975,5217,1.798,5218,1.798,5219,1.975,5220,3.166,5221,1.798,5222,1.798,5223,1.688,5224,1.798,5225,1.975,5226,4.656,5227,1.975,5228,3.476,5229,5.107,5230,3.166,5231,1.975,5232,1.975,5233,1.975,5234,1.975,5235,1.975,5236,1.975,5237,1.798,5238,1.798,5239,1.798,5240,1.975,5241,3.166,5242,3.98,5243,1.975,5244,1.975,5245,1.975,5246,1.975,5247,1.975,5248,1.975,5249,1.975,5250,3.476,5251,1.975,5252,1.975,5253,1.975,5254,1.975,5255,1.798,5256,1.798,5257,1.975,5258,1.798,5259,1.975,5260,1.975,5261,1.975,5262,3.476,5263,1.975,5264,1.975,5265,1.975,5266,1.975,5267,1.975,5268,1.975,5269,1.975,5270,1.975,5271,1.975]],["t/3635",[16,1.329,18,3.139,23,2.385,24,1.867,25,2.287,28,1.351,34,1.82,49,2.591,70,6.831,97,3.195,203,2.291,278,2.56,289,2.824,295,2.873,342,4.67,373,5.166,381,3.38,476,2.074,484,3.027,580,2.481,607,2.738,614,2.123,637,4.585,749,4.627,757,1.76,760,2.828,769,2.746,780,3.803,846,2.193,933,4.048,1005,4.67,1090,3.172,1181,4.18,1285,3.046,1402,3.34,2017,3.008,2202,5.49,2403,3.975,2422,4.585,2430,4.397,2631,7.055,3008,4.975,3810,4.469,4696,6.109]],["t/3637",[16,2.25,24,1.808,25,3.575,27,2.155,28,1.281,56,0.897,164,2.494,367,5.684,476,2.636,577,4.296,778,5.726,807,4.315,984,3.884,1022,4.994,1399,5.463,3899,5.992,5272,9.08,5273,9.08]],["t/3639",[7,0.519,18,1.873,24,1.677,28,1.382,31,1.042,56,1.296,84,0.462,103,2.694,132,1.935,160,3.63,163,1.69,165,1.476,182,1.426,203,1.577,236,1.121,240,2.332,256,1.595,273,3.4,283,1.233,285,1.761,296,1.643,313,1.042,377,1.474,381,2.017,405,2.562,426,3.824,476,1.238,502,1.176,511,1.359,527,0.932,528,2.246,541,4.055,565,2.629,568,1.221,614,1.213,683,1.595,754,3.471,759,2.823,760,3.656,769,2.532,807,1.647,814,1.836,840,1.934,910,2.4,934,3.972,1005,5.922,1040,4.024,1182,3.142,1265,3.454,1271,4.508,1285,1.818,1329,3.708,1462,3.564,1629,2.968,1793,2.689,1988,1.783,1989,3.335,2223,3.645,2246,7.81,2247,3.004,2250,4.411,2264,3.172,2276,2.446,2448,2.666,2637,3.471,2747,3.753,3952,3.645,5278,3.883,5279,5.365,5280,3.883,5281,6.001,5777,4.594,5778,4.594,5779,4.263,5780,4.263,5781,4.263]],["t/3641",[16,2.058,24,1.448,28,1.561,31,2.184,49,2.01,56,1.092,163,3.544,302,4.117,528,4.711,541,5.503,553,4.735,635,4.013,751,5.162,1285,3.813,1325,6.556,2403,6.981,2404,6.154,2422,5.738,2631,5.844]],["t/3643",[2,0.601,5,1.361,7,0.176,9,1.444,16,0.719,24,0.988,28,0.861,29,0.778,34,0.568,56,1.287,84,0.242,97,1.728,107,2.878,109,1.577,113,0.813,129,1.332,134,1.109,155,1.444,160,1.793,164,0.613,165,1.367,170,3.51,179,0.746,193,1.861,213,0.907,224,4.211,225,1.828,232,0.811,289,1.527,295,0.897,297,1.219,326,1.518,338,1.258,343,0.793,373,1.612,377,0.864,394,1.114,405,2.732,495,1.866,500,1.055,502,2.579,505,0.924,511,0.711,514,1.685,528,1.175,541,1.372,550,2.972,555,1.055,591,1.271,605,0.807,607,2.339,608,1.157,636,1.255,647,0.836,653,0.702,662,2.246,663,1.362,675,1.418,734,0.987,748,1.685,749,1.444,760,0.659,772,4.028,802,0.902,813,1.444,838,1.125,859,1.157,871,1.635,914,1.406,916,1.95,921,3.254,923,2.438,934,1.906,947,3.562,966,0.608,977,1.409,989,1.255,1015,1.323,1027,5.464,1055,4.413,1062,4.326,1079,1.395,1144,1.213,1145,1.135,1178,2.231,1195,1.518,1197,1.518,1199,1.502,1271,1.923,1285,2.181,1303,3.283,1347,1.552,1353,3.376,1376,1.502,1410,4.612,1458,2.834,1462,1.206,1467,3.199,1699,2.398,1718,2.576,1959,4.096,2145,1.906,2182,4.475,2247,1.571,2250,1.004,2253,1.352,2256,3.482,2263,3.147,2350,1.635,2391,2.876,2426,1.502,2439,1.744,2868,1.213,2938,1.778,2970,2.116,2992,1.571,3074,1.816,4513,1.744,4739,5.218,4742,2.031,5217,2.031,5223,1.906,5229,5.559,5230,3.52,5242,4.374,5279,1.816,5286,2.23,5287,2.23,5288,2.23,5289,2.23,5290,2.23,5291,2.23,5292,2.23,5293,2.23,5294,2.23,5295,2.23,5296,2.23,5297,3.52,5298,2.031,5299,2.23,5300,4.659,5301,2.23,5302,3.865,5303,2.23,5304,2.23,5305,2.23,5306,2.23,5307,2.23,5308,2.031,5309,2.031,5310,2.031,5311,3.52,5312,3.52,5313,2.23,5314,2.23,5315,2.23,5316,4.659,5317,2.23,5318,2.23,5319,2.23,5320,2.23,5321,2.23,5322,2.23,5323,2.031,5324,2.031,5325,1.963,5326,2.031,5327,2.23,5328,2.031,5329,2.23,5330,2.23,5331,3.865,5332,3.865,5333,2.23,5334,2.23,5335,2.23,5336,2.23,5337,2.23,5338,2.23,5339,2.031,5340,2.23,5341,2.23,5342,2.23,5343,2.23]],["t/3645",[4,2.138,28,1.318,84,0.747,109,1.575,130,3.929,132,2.024,133,2.949,160,2.416,164,1.894,170,3.892,236,1.812,278,2.47,295,2.772,373,4.984,377,1.542,426,3.451,491,2.078,495,3.328,566,4.799,605,2.494,608,4.849,614,1.72,622,4.713,637,4.423,820,3.543,840,3.127,872,4.464,916,2.627,927,4.549,977,2.513,1008,4.117,1010,4.594,1062,3.314,1145,3.509,1399,4.147,1410,8.587,1458,5.054,1699,4.276,1829,5.392,2250,3.104,2825,5.392,5279,5.613,5344,6.893,5345,6.893,5346,6.893,5347,6.893]],["t/3647",[2,0.881,12,1.317,16,0.992,24,1.391,27,1.265,28,1.372,29,1.14,56,1.318,68,1.238,106,1.197,107,1.996,109,1.139,113,1.191,127,2.556,129,1.951,132,0.959,160,3.011,170,2.538,172,1.131,193,3.536,196,1.295,224,1.384,225,1.167,254,1.05,275,1.011,278,1.17,279,1.411,291,3.14,292,1.421,313,0.798,318,0.795,351,1.182,377,1.192,398,1.151,472,1.59,476,1.548,481,1.951,502,1.47,505,1.354,511,1.041,512,1.126,550,1.283,552,1.156,575,1.551,596,1.471,601,1.533,607,1.252,609,2.135,614,1.435,622,1.647,636,1.839,803,1.839,840,1.482,921,5.464,923,4.26,934,2.63,947,2.274,966,1.454,989,3.803,1015,1.937,1019,1.959,1027,2.078,1028,3.1,1055,3.855,1074,2.362,1075,2.794,1076,1.176,1080,1.817,1129,4.341,1132,1.45,1195,2.223,1197,2.223,1199,2.2,1285,1.393,1347,2.274,1353,2.156,1633,3.183,1664,2.395,1959,3.718,1989,4.17,2182,3.909,2249,2.876,2253,1.98,2256,2.223,2264,2.43,2322,2.43,2391,3.967,2439,4.17,3565,3.59,4513,2.555,5218,2.975,5220,4.856,5221,2.975,5222,2.975,5223,4.558,5224,2.975,5237,2.975,5238,2.975,5239,2.975,5241,4.856,5242,2.793,5255,2.975,5256,2.975,5258,2.975,5297,4.856,5298,2.975,5300,2.975,5308,2.975,5309,2.975,5310,2.975,5311,2.975,5312,4.856,5316,2.975,5323,2.975,5324,2.975,5325,2.876,5326,2.975,5328,2.975,5339,2.975,5348,5.059,5349,3.266,5350,3.266,5351,3.266,5352,3.266,5353,3.266,5354,3.266,5355,3.266,5356,3.266,5357,3.266,5358,3.266,5359,3.266,5360,3.266,5361,3.266,5362,3.266,5363,3.266,5364,3.266,5365,3.266,5366,3.266,5367,3.266,5368,3.266,5369,3.266,5370,3.266,5371,3.266,5372,3.266]],["t/3649",[2,1.542,7,0.451,18,2.512,56,0.81,84,1.039,89,2.134,107,2.455,109,0.964,113,2.085,138,2.306,164,1.571,170,3.095,171,1.553,173,5.131,182,2.744,193,3.81,223,4.617,224,4.438,236,1.503,254,3.083,277,2.938,282,3.109,289,2.259,313,2.003,320,3.28,343,2.917,377,1.279,433,3.301,495,2.76,502,2.261,550,2.247,552,2.024,568,1.637,601,2.684,605,2.069,612,3.702,614,1.053,632,5.158,635,2.074,725,4.881,747,2.348,772,3.773,810,2.539,814,2.462,838,2.884,974,3.492,1062,2.749,1072,2.642,1076,2.059,1078,4.134,1093,5.934,1094,4.099,1175,2.632,1285,2.438,1462,3.092,1737,3.093,2190,5.366,2253,3.466,2352,4.08,2391,4.254,3595,3.021,3833,4.192,4739,4.888,5373,8.201]],["t/3651",[16,2.271,27,1.617,34,1.735,85,2.36,97,3.046,107,1.744,113,2.484,170,3.689,173,3.646,203,2.219,217,3.884,278,2.441,295,2.74,351,3.354,353,1.858,476,1.978,484,2.886,552,2.411,580,2.366,607,2.611,614,2.339,637,4.372,725,4.719,740,3.079,747,2.797,749,4.412,757,1.679,760,2.739,769,2.618,772,4.496,1005,4.453,1101,4.07,1285,2.905,1462,3.685,1481,5.638,1557,6.536,2202,5.235,2254,3.518,3810,4.261,4574,5.433,5374,6.813]],["t/3653",[24,1.317,28,1.148,56,0.803,85,2.818,106,2.982,107,2.668,109,2.199,127,2.668,170,3.583,172,2.818,254,3.351,444,3.527,476,2.362,527,1.779,658,4.991,757,2.833,1062,5.011,1099,4.258,1112,4.825,1149,7.41,5375,7.72,5376,8.136]],["t/3655",[7,0.493,28,1.089,31,0.974,56,1.29,84,0.432,103,2.093,107,1.976,109,1.601,132,1.171,160,3.531,163,1.58,165,1.401,170,3.112,182,1.334,183,1.712,203,1.497,221,1.473,236,1.049,240,2.181,256,1.492,273,3.18,283,1.153,296,1.537,313,0.974,377,1.399,381,1.887,405,2.455,426,3.72,476,1.158,502,1.1,511,1.271,527,0.872,541,3.85,565,2.52,568,1.142,614,1.152,653,1.255,683,1.492,754,3.247,759,2.705,760,3.512,769,2.404,807,1.541,814,1.717,840,1.809,910,2.245,916,1.52,934,4.31,966,1.087,988,1.95,994,1.717,1005,5.712,1040,3.82,1062,1.917,1182,2.983,1265,3.279,1271,3.84,1285,1.7,1329,3.553,1399,2.399,1456,2.101,1462,3.383,1629,2.777,1793,2.515,1959,2.983,1988,1.668,2017,2.633,2194,3.511,2223,3.41,2246,6.154,2247,2.81,2250,4.275,2264,2.967,2338,2.193,2448,2.494,2624,2.582,2747,3.511,3952,3.41,4627,3.632,5278,3.632,5279,5.093,5280,3.632,5281,5.697,5377,6.255,5378,3.988,5379,3.988,5779,3.988,5780,3.988,5781,3.988,5782,4.297,5783,4.297,5784,4.297]],["t/3657",[2,1.665,16,2.346,24,1.525,25,1.976,27,0.931,28,1.67,56,1.213,84,0.827,107,1.58,109,1.588,113,1.43,132,3.675,144,1.878,157,2.264,160,1.375,162,1.378,163,1.554,165,0.878,170,3.497,193,1.426,199,1.934,224,1.661,236,1.623,254,1.261,256,2.31,278,1.405,289,2.439,295,1.577,297,2.145,336,1.596,377,0.877,381,1.855,391,1.651,476,1.138,491,1.182,495,1.893,502,2.597,528,2.066,550,3,552,1.388,568,1.123,582,3.268,605,1.419,607,1.503,614,0.722,622,3.113,635,1.423,662,2.279,675,2.494,725,1.996,740,2.79,756,3.583,807,1.515,916,1.495,934,1.934,937,2.342,989,2.208,1005,2.563,1038,2.669,1062,1.885,1072,1.812,1079,2.453,1145,1.996,1285,3.255,1331,2.516,1397,3.452,1410,5.769,1779,2.641,2182,6.349,2194,3.452,2250,2.78,2259,2.614,2264,2.918,2266,3.721,2267,3.721,2545,2.588,2909,3.452,3611,3.572,3698,3.452,4741,5.622,5381,3.921,5382,3.921,5383,3.921,5384,3.921,5385,3.921,5386,3.921,5387,3.921,5388,3.921,5389,3.921,5390,3.921]],["t/3659",[16,2.033,26,2.719,28,1.373,84,0.793,102,6.137,132,2.858,226,3.551,275,2.267,323,2.953,390,3.536,391,3.083,405,4.045,426,3.594,511,3.973,568,2.097,612,4.742,653,2.305,751,4.228,1078,5.295,1083,4.718,1094,3.66,1100,2.902,1209,6.261,1303,4.699,1355,6.037,1407,5.226,2254,3.782,2448,4.58,3595,2.698,3671,5.627,4561,9.234,5188,5.963,5189,8.32,5391,7.323]],["t/3661",[5,2.788,16,2.112,28,1.693,56,1.011,84,0.857,107,2.027,113,2.886,132,2.325,182,3.426,224,4.338,225,2.829,323,3.192,397,3.291,426,2.923,511,3.962,596,3.565,609,5.174,772,5.224,977,2.886,1062,5.976,1355,4.911,1462,4.282,5392,10.24,5393,7.916,5394,10.24]],["t/3663",[5,2.788,16,1.473,24,1.282,25,2.534,28,1.754,84,0.857,113,2.886,132,2.325,182,2.649,224,4.338,225,2.829,289,3.128,323,3.192,397,3.291,511,3.962,609,5.174,635,2.872,807,4.638,892,3.831,977,2.886,1285,3.375,1355,6.353,2487,6.768,3987,9.993,4337,6.969,5194,7.21,5395,7.916]],["t/3665",[7,0.633,16,1.922,24,1.299,25,2.569,28,1.132,31,1.96,49,1.803,56,1.02,109,1.353,123,2.734,132,2.357,228,3.272,236,2.11,297,4.388,313,1.96,398,2.826,476,2.329,491,2.419,502,2.848,536,3.076,565,2.62,568,2.298,577,5.905,580,2.786,916,3.059,934,3.958,1040,4.901,2250,3.614,3512,9.094,3513,7.064,3514,7.064]],["t/3667",[0,3.822,12,2.67,56,1.103,109,1.75,112,5.801,171,1.799,172,4.056,193,4.259,217,3.774,236,1.741,391,2.787,502,1.826,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,916,2.524,934,5.511,944,4.199,1039,7.374,1040,6.824,1075,3.47,1077,3.705,1083,3.21,1175,4.779,1332,4.413,1337,6.059,2250,2.982,3515,5.829,3516,9.138,3517,5.829,3518,5.829,3519,5.829]],["t/3669",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,934,4.799,945,2.423,1039,7.685,1040,5.943,1099,4.614,1100,3.856,1112,5.771,3520,6.447,3521,6.447]],["t/3671",[12,2.847,16,1.767,24,1.537,27,1.675,28,1.34,56,0.697,89,2.634,116,2.634,133,3.02,145,2.838,162,2.48,170,2.085,200,3.497,224,4.022,275,2.185,281,3.339,292,3.07,299,6.093,302,3.25,318,1.719,351,2.554,381,3.339,396,5.522,405,2.245,436,5.037,470,5.104,502,2.958,513,3.326,550,2.774,568,2.021,609,4.614,635,2.561,679,5.522,694,3.839,787,5.748,802,2.855,807,2.728,811,4.379,917,3.999,944,2.855,1005,7.5,1008,4.217,1022,3.882,1188,4.49,1193,6.611,2450,4.705,2580,6.429,2902,5.748,3490,6.214,3833,5.176]]],"invertedIndex":[["",{"_index":56,"t":{"2":{"position":[[690,1],[791,1],[811,1],[837,1],[880,1],[921,1],[962,1],[990,1],[2335,1],[3666,1],[3668,3]]},"4":{"position":[[343,1],[586,1],[883,1],[951,1],[1055,1],[1148,1]]},"6":{"position":[[85,1]]},"8":{"position":[[331,1],[386,1],[493,1],[1017,1]]},"10":{"position":[[754,1],[805,2],[818,2],[841,2],[850,1],[914,1],[1452,1],[1466,1],[1483,1]]},"12":{"position":[[109,1],[1017,1]]},"14":{"position":[[201,1],[1317,1],[1530,1]]},"16":{"position":[[1622,1],[4978,1],[6339,1],[6863,2],[6970,1]]},"18":{"position":[[337,1],[1633,1],[1843,1],[2527,2],[2536,1],[2545,1],[2557,2],[2560,1],[2578,2],[2617,1],[2632,1],[2641,1],[2653,2],[2656,1],[2674,1],[2716,1],[2718,1],[2720,2],[2781,2],[3173,1]]},"20":{"position":[[1191,1],[1593,1]]},"22":{"position":[[260,1]]},"24":{"position":[[218,1]]},"26":{"position":[[56,1],[366,1],[927,1],[1323,1],[1336,1],[1459,1]]},"28":{"position":[[8,3],[124,1],[331,1],[428,1],[1349,1],[1555,1],[1664,1],[2191,1]]},"30":{"position":[[104,1],[133,1],[564,2],[614,2],[621,1],[645,2],[664,2],[671,1],[737,1],[827,2],[1067,1],[1252,1]]},"32":{"position":[[475,1],[546,1],[553,2],[595,1],[604,2],[654,1],[696,3],[811,2],[983,1],[1192,1],[1631,1]]},"34":{"position":[[437,1],[514,1],[924,1],[1105,1],[1645,1],[1733,1],[1748,2],[1761,1],[1836,1],[1875,4],[2367,2],[2469,1],[2937,1]]},"36":{"position":[[122,1],[448,1],[695,1]]},"38":{"position":[[311,1],[352,4],[497,1],[511,2],[526,1],[598,1],[792,1]]},"40":{"position":[[463,1],[563,3],[567,2],[570,2],[634,1],[708,1],[930,1]]},"42":{"position":[[622,1],[994,1],[1029,1],[1519,1],[2251,1],[2336,2],[2339,2],[2526,1]]},"44":{"position":[[767,1],[870,2],[873,2],[1196,1],[1321,1],[1442,1],[1619,1],[1672,1],[2037,1]]},"46":{"position":[[710,1],[1312,1],[1698,1],[2045,1],[2352,1],[2401,1],[3970,1]]},"48":{"position":[[312,1],[408,1],[855,1],[1215,1]]},"50":{"position":[[39,1],[444,1],[818,1],[1361,1],[2418,1],[2555,1]]},"52":{"position":[[101,1]]},"54":{"position":[[144,1],[382,1],[1083,1],[1233,1],[1429,1]]},"56":{"position":[[166,1],[193,1]]},"62":{"position":[[805,1]]},"64":{"position":[[42,1]]},"68":{"position":[[286,1],[1504,1]]},"70":{"position":[[66,1],[332,1],[804,2],[827,2],[892,2],[1007,1],[1028,1],[1131,1],[1140,2],[1147,1],[1194,1],[1207,2],[1243,1],[1269,5],[1296,1],[1370,1],[1372,1],[1409,1],[1417,1],[1419,2],[1473,1],[1549,2],[1638,1]]},"72":{"position":[[283,1]]},"74":{"position":[[1897,1]]},"76":{"position":[[413,1]]},"78":{"position":[[328,1]]},"86":{"position":[[454,1],[715,1],[1085,1],[1478,1],[1786,1]]},"92":{"position":[[191,1]]},"94":{"position":[[235,1],[992,1],[1245,1],[1359,1],[1444,1],[1762,1],[2006,1],[2868,1]]},"96":{"position":[[152,2]]},"98":{"position":[[216,1],[862,3],[1679,4],[2074,4]]},"100":{"position":[[326,1],[361,1],[392,1],[660,1],[741,1],[784,1],[1184,2]]},"104":{"position":[[73,1],[182,1],[184,2],[251,2],[319,2],[384,2],[654,1],[804,1],[854,4],[915,1],[966,1],[975,2],[1158,2],[1168,2],[1189,2],[1196,1],[1213,2]]},"106":{"position":[[206,2],[265,1],[281,2],[356,2],[363,1],[376,1],[382,1],[394,2],[442,2],[449,1],[462,1],[537,1],[552,1],[556,1],[617,3],[643,3],[715,1],[717,2],[741,1],[1099,2],[1196,1],[1274,1],[1286,2],[1359,2],[1366,1],[1383,1],[1485,2]]},"108":{"position":[[97,1],[1496,1]]},"110":{"position":[[675,2],[845,1],[927,1],[975,1],[1003,1],[1066,1],[1068,2],[1117,2],[1307,1],[1321,2],[1374,2],[1381,1],[1407,1],[1486,2],[1528,2],[1535,1],[1561,1],[1619,2],[1666,2],[1673,1],[1699,1],[1701,2],[1731,2],[1759,2],[1766,1],[1869,1],[2025,1],[2138,1],[2516,1],[2518,1],[2520,1],[2522,1],[2524,1],[2526,1],[2528,1],[2530,1],[2532,1],[2534,1],[2536,1],[2538,1],[2540,1],[2542,1],[2544,1],[2546,1],[2548,1],[2550,1],[2552,1],[2554,1],[2556,1],[2558,1],[2560,1],[2562,1],[2564,1],[2566,1],[2568,1],[2570,1],[2572,1],[2574,1],[2576,1],[2578,2],[2590,1],[2604,2],[2607,1],[2609,1],[2611,1],[2613,1],[2615,1],[2617,1],[2619,1],[2621,1],[2623,1],[2625,1],[2627,1],[2629,1],[2631,1],[2633,1],[2635,1],[2637,1],[2639,1],[2641,1],[2643,1],[2645,1],[2647,1],[2649,1],[2651,1],[2653,1],[2655,1],[2657,1],[2659,1],[2661,1],[2663,1],[2665,1],[2667,1],[2669,2],[2678,3],[2682,4],[2687,1],[2689,1],[2691,1],[2693,1],[2695,1],[2697,1],[2699,1],[2701,1],[2703,1],[2705,1],[2707,1],[2709,1],[2711,1],[2713,1],[2715,1],[2717,1],[2719,1],[2721,1],[2723,1],[2725,1],[2727,1],[2729,1],[2731,1],[2733,1],[2735,1],[2737,1],[2739,1],[2741,1],[2743,1],[2745,1],[2747,1],[2749,1],[2980,1],[3020,2],[3081,2],[3139,2],[3152,1],[3162,1],[3170,1],[3190,2],[3244,2],[3251,1],[3260,2],[3270,1],[3277,1],[3284,1],[3291,1],[3322,1],[3324,1],[3352,1],[3413,2],[3420,1],[3451,1],[3457,2],[3493,2],[3520,2],[3527,1],[3536,2],[3546,1],[3548,2],[3596,1],[3603,1],[3610,1],[3641,1],[3643,1],[3657,2],[3672,1],[3737,1],[3745,2],[3792,1],[3850,1],[3910,1],[3996,1],[3998,1],[4125,1],[4244,1],[4253,2],[4292,2],[4299,1],[4321,1],[4374,1],[4396,1],[4458,1],[4469,2],[4483,1],[4490,2],[4495,1],[4509,1],[4511,1],[4588,2]]},"112":{"position":[[448,1],[454,1],[468,2],[518,2],[525,1],[538,1],[603,2],[635,2],[642,1],[655,1],[657,2]]},"114":{"position":[[47,1]]},"116":{"position":[[167,1],[391,1],[1052,1]]},"118":{"position":[[614,1]]},"120":{"position":[[610,1],[878,1]]},"122":{"position":[[210,1],[609,1]]},"124":{"position":[[274,1],[317,3],[333,3],[345,3],[361,3],[373,3]]},"126":{"position":[[227,1],[296,3],[570,1],[774,1],[809,1],[811,1]]},"128":{"position":[[383,1],[428,3],[438,3],[604,2],[743,2],[880,1],[943,1],[1027,1],[1042,1],[1058,3],[1066,1],[1068,2],[1105,1],[1107,2],[1129,1],[1144,1],[1179,1],[1213,1],[1223,1],[1250,1],[1266,1],[1277,1],[1279,4],[1284,2],[1706,1],[1751,3],[1761,3],[1898,1],[1924,1],[1926,1],[2281,1],[2283,1]]},"130":{"position":[[238,3],[248,3],[264,3],[458,2],[779,2],[789,2],[826,2],[853,1],[934,1],[995,1],[1061,1],[1086,1],[1109,1],[1177,1],[1210,3],[1257,1],[1293,3],[1307,1],[1339,1],[1366,1],[1388,1],[1441,1],[1589,1],[1616,3],[1685,1],[1700,1],[1716,3],[1724,1],[1726,2],[1777,1],[1813,1],[1823,1],[1858,3],[1881,1],[1883,3],[1887,1],[1889,2],[2158,1],[2183,2],[2238,1],[2294,1],[2320,1],[2322,1],[2398,6]]},"132":{"position":[[210,1],[612,1],[614,1],[697,1],[699,2],[1240,1]]},"134":{"position":[[475,1],[675,1],[734,1],[767,1],[814,1],[816,1],[1062,1],[1095,1],[1521,1],[1523,2]]},"136":{"position":[[382,1],[408,1],[410,1],[486,6],[864,1],[918,1],[988,1],[1059,1],[1153,1],[1155,1],[1167,1],[1193,1],[1195,1],[1260,1],[1339,1],[1372,1],[1401,1],[1472,1],[1539,1],[1541,1],[1553,2],[1556,1],[1623,1],[1702,1],[1735,1],[1765,1],[1838,1],[1907,1],[1909,1],[1921,2],[1924,1]]},"138":{"position":[[341,1],[1455,1]]},"142":{"position":[[346,1],[476,1]]},"144":{"position":[[493,1]]},"146":{"position":[[478,1],[665,1],[697,1]]},"148":{"position":[[507,1],[913,1],[1219,1]]},"150":{"position":[[405,1],[451,1],[511,1],[601,1],[669,1]]},"158":{"position":[[631,1]]},"160":{"position":[[173,1],[289,1],[311,2],[398,1],[409,2],[426,1],[428,1],[449,1],[451,2],[642,1],[732,1]]},"162":{"position":[[336,1],[373,2],[376,1],[385,2],[426,2],[462,1],[464,3],[468,2]]},"164":{"position":[[348,1],[380,2],[411,2],[449,2],[465,1],[508,1],[545,1],[559,1],[574,1],[627,1],[636,2],[670,2],[724,2],[743,2],[777,2]]},"166":{"position":[[597,1],[611,1],[627,3],[635,1],[671,1],[707,1],[717,1],[731,1],[784,1],[793,1],[904,4],[928,1],[930,3],[934,3],[1185,1],[1195,1],[1218,1],[1261,1],[1322,1],[1413,1],[1506,1],[1539,3],[1558,1],[1605,1]]},"168":{"position":[[435,1],[566,1]]},"170":{"position":[[328,1],[408,1]]},"172":{"position":[[623,2]]},"174":{"position":[[752,1],[754,3],[772,1],[774,1],[813,1],[815,2]]},"176":{"position":[[140,1],[156,1],[158,1],[234,1],[236,2],[364,1],[445,1],[608,1],[688,1]]},"178":{"position":[[413,1],[467,1],[511,1],[564,1],[931,1],[1240,1],[1242,1],[1306,1],[1320,1],[1402,1],[1442,1],[1457,1],[1504,1],[1526,1],[1591,2],[1620,2],[1833,1],[1877,1],[1910,1],[1924,1],[2023,1],[2224,1],[2763,1]]},"180":{"position":[[1141,1],[1231,1],[1233,1],[1297,1],[1299,2],[1661,1],[1741,1]]},"182":{"position":[[572,1],[633,1],[700,1],[1245,1],[1868,1]]},"184":{"position":[[246,1]]},"186":{"position":[[278,1],[377,1],[379,1],[418,1],[420,2]]},"188":{"position":[[284,1],[286,1],[325,1],[342,1],[344,1],[392,1],[402,1],[484,1],[525,1],[540,1],[587,1],[609,1],[653,1],[655,1],[703,1],[705,1],[707,2],[736,2]]},"192":{"position":[[199,1]]},"194":{"position":[[708,1]]},"198":{"position":[[873,1],[1074,1],[1135,1],[1137,2],[1205,2],[1353,1],[1380,2],[1479,1],[1506,2],[1605,2]]},"204":{"position":[[292,1],[335,1],[352,1],[434,1],[1430,1],[1462,1],[1479,1],[1528,1]]},"210":{"position":[[373,1],[876,1]]},"214":{"position":[[412,3],[618,2],[703,1],[816,4],[1784,1]]},"216":{"position":[[130,1],[368,1]]},"218":{"position":[[232,1],[721,1],[761,1]]},"226":{"position":[[350,1],[720,1],[967,1]]},"230":{"position":[[532,1]]},"236":{"position":[[547,1]]},"238":{"position":[[172,1],[352,1]]},"240":{"position":[[920,1]]},"242":{"position":[[284,1]]},"244":{"position":[[626,1]]},"246":{"position":[[54,1],[362,4],[2318,1],[2882,1],[4320,1]]},"250":{"position":[[2153,1]]},"252":{"position":[[334,1]]},"260":{"position":[[303,1]]},"262":{"position":[[340,1],[374,1],[378,1],[403,1],[415,1],[436,1],[438,2],[457,1],[466,1],[479,1],[540,1],[542,1],[1488,1],[2138,1]]},"264":{"position":[[733,1],[1043,1],[1256,1]]},"279":{"position":[[644,1],[783,1],[1224,1],[1349,1],[1542,1]]},"281":{"position":[[289,1]]},"283":{"position":[[161,1],[201,1],[243,1],[285,1],[317,1],[346,1],[369,1],[413,1],[420,1],[425,1],[430,1],[520,1],[537,2],[732,2],[735,1],[761,1],[794,1],[812,3],[816,1],[856,1],[896,2],[899,2],[902,1],[1933,1],[1963,1],[2006,2],[2009,1],[2033,2],[2047,2],[2068,2],[2083,1],[2104,1],[2144,1],[2151,1],[2195,4],[2406,1],[2530,2],[2533,1]]},"285":{"position":[[55,1],[216,1],[242,2],[373,1],[384,2],[496,1],[508,1],[555,1],[629,1],[724,2],[727,1],[805,2],[808,1],[894,1],[924,1],[926,1],[928,1]]},"287":{"position":[[174,1],[472,1],[743,1],[790,1],[792,1],[1027,1],[1050,1],[1398,2],[1674,2],[1677,1],[1703,1],[1724,1],[1751,1],[1753,3],[1757,1],[1787,1],[1842,1],[1844,6]]},"289":{"position":[[382,1],[447,1],[544,2],[547,2],[550,1],[556,1],[593,1],[622,1],[633,3],[637,1],[651,2],[693,1],[733,1],[742,2],[745,2],[748,3],[752,1],[764,1],[792,3]]},"291":{"position":[[248,1],[383,1]]},"297":{"position":[[89,1],[115,1],[150,1]]},"299":{"position":[[90,1],[146,1],[269,1],[318,1],[345,1],[381,1],[418,1],[724,1]]},"301":{"position":[[662,1],[686,1],[813,1],[1037,2],[1427,1],[1449,1],[1513,1],[1526,1],[1553,1],[1567,1],[1628,1],[1636,1],[1660,1],[1662,1],[1696,1],[1711,1],[1720,1],[1803,1],[1805,1],[1910,2],[1957,1],[1959,1],[1961,1],[1997,2],[2035,2],[2054,1],[2394,2],[2397,1],[2416,2],[2444,1],[2454,1],[2473,1],[2534,1],[2578,2],[2581,1],[2620,2],[2623,1],[2663,2],[2666,1],[2717,1],[2751,3],[2755,2],[2758,1],[2760,3],[2793,2],[2796,2],[2799,1],[2826,4],[3200,1],[3246,1],[3293,1],[3295,1],[3313,5],[3333,5],[3352,4],[3357,1],[3359,3],[3376,2],[3379,1],[3381,3],[3423,1],[3459,2],[3462,3],[3473,1],[3475,3],[3513,1],[3515,1],[3800,1],[3802,3],[3846,1],[3862,3],[3879,2],[3882,1],[3884,3],[3906,1],[3908,3],[3912,1],[3951,1],[4031,1],[4093,3]]},"303":{"position":[[126,1]]},"307":{"position":[[552,1]]},"309":{"position":[[384,1],[462,1]]},"319":{"position":[[339,1],[678,1],[942,1]]},"321":{"position":[[250,1],[454,1],[972,1]]},"323":{"position":[[265,1],[621,1],[712,1],[790,1]]},"339":{"position":[[670,1],[1087,1]]},"341":{"position":[[75,2],[254,2]]},"345":{"position":[[349,1]]},"347":{"position":[[1674,1],[1791,1],[1991,1]]},"349":{"position":[[155,1],[322,1]]},"355":{"position":[[39,1],[141,1],[253,1],[320,2],[387,1],[475,1],[656,1],[695,1],[779,1],[1034,1],[1088,2],[1221,1],[1476,1],[1530,2]]},"359":{"position":[[578,1]]},"361":{"position":[[1144,1]]},"369":{"position":[[539,1]]},"371":{"position":[[147,1]]},"373":{"position":[[67,1],[701,1]]},"375":{"position":[[111,1]]},"377":{"position":[[1107,1],[2113,1]]},"379":{"position":[[553,1],[833,1],[958,1],[1252,1],[1704,1],[1897,1],[2149,1],[2483,1],[2489,1],[2529,1],[2535,1],[2571,1],[2673,1],[2918,1],[3212,1],[3344,1],[3557,1],[3559,1],[3561,1],[3646,2],[3649,1],[3740,2],[3743,1],[3838,1],[4040,1]]},"381":{"position":[[614,1]]},"383":{"position":[[1375,1],[1425,3],[1454,1],[1462,2]]},"385":{"position":[[304,1],[1472,1]]},"387":{"position":[[53,1],[450,2],[477,1],[706,1],[1472,1]]},"389":{"position":[[221,1],[785,1]]},"391":{"position":[[844,1],[854,1],[856,1],[908,1],[910,2],[1380,1]]},"393":{"position":[[623,1]]},"395":{"position":[[116,1],[171,2]]},"399":{"position":[[902,1],[904,1],[906,1],[999,2],[1002,1],[1107,1],[1201,1]]},"403":{"position":[[417,1]]},"411":{"position":[[355,1]]},"415":{"position":[[190,1]]},"419":{"position":[[478,1]]},"423":{"position":[[81,1]]},"425":{"position":[[31,1]]},"429":{"position":[[209,1]]},"433":{"position":[[297,1]]},"449":{"position":[[350,1]]},"457":{"position":[[30,1]]},"463":{"position":[[85,1]]},"467":{"position":[[183,1],[463,1],[965,2],[975,1]]},"469":{"position":[[379,1]]},"471":{"position":[[561,1],[568,2],[660,1],[697,2],[700,1],[810,1],[826,1],[830,2],[851,2],[891,2],[898,1],[913,1],[925,1],[927,3]]},"473":{"position":[[702,1],[914,1],[1034,1],[1367,1],[1391,1],[1416,1],[1440,1],[1478,1],[1557,1],[1561,2],[1612,1],[1624,1],[1630,1],[1638,2],[1646,1],[1672,1],[1697,1],[1699,1],[1701,3],[1744,1],[1752,2],[1779,2],[1805,1],[1807,2],[1871,1],[1885,1],[1931,1],[1938,2],[1977,1],[1986,1],[1997,2],[2000,1],[2020,2],[2079,2],[2143,2],[2151,1],[2180,1],[2189,1],[2200,2],[2203,1],[2223,1],[2302,1],[2304,1],[2306,2],[2358,2],[2381,2],[2388,1],[2396,2],[2404,1],[2425,1],[2445,1],[2452,1],[2463,1],[2465,2],[2522,2],[2530,1],[2551,1],[2560,2],[2596,1],[2603,1],[2605,1],[2655,1],[2662,2],[2724,1],[2761,2],[2764,1],[2792,2],[2900,1],[2916,1],[2922,2],[2946,2],[2953,1],[2968,1],[2970,1],[2972,3],[3464,1],[3800,1],[3808,1],[3813,1],[3927,1],[3935,1],[3940,1],[4282,1]]},"475":{"position":[[469,1],[473,1],[475,1],[1526,1],[1616,1],[1635,1]]},"477":{"position":[[1631,1],[1826,1],[1835,2],[1929,2],[2042,1],[2058,1],[2065,2],[2137,2],[2144,1],[2166,1],[2168,1],[2170,3],[2510,1],[3305,2],[4566,1],[4678,1],[4738,1],[4749,1],[4820,1],[4861,1],[4872,1],[4927,1],[4939,1],[5024,1],[5034,1],[5092,1],[5104,1],[5156,1],[5166,1],[5218,1],[5230,1],[5284,1],[5294,1],[5379,1],[5388,1],[5446,1],[5456,1],[5508,1],[5518,1],[5566,1],[5576,1],[5630,1],[5640,1],[5776,1],[6033,1],[6374,1],[6769,1],[7056,1]]},"479":{"position":[[843,1],[857,2],[932,2],[942,2],[949,1],[964,1],[1055,1],[1071,1],[1077,2],[1138,2],[1193,2],[1200,1],[1223,1],[1225,1],[1227,3],[1596,1],[2727,1],[3115,1],[3549,1],[3661,1],[3720,1],[3731,1],[3796,1],[3801,1],[3841,1],[3852,1],[3908,1],[3920,1],[4005,1],[4014,1],[4073,1],[4085,1],[4136,1],[4146,1],[4198,1],[4210,1],[4264,1],[4274,1],[4360,1],[4369,1],[4427,1],[4437,1],[4489,1],[4498,1],[4547,1],[4556,1],[4610,1],[4619,1],[4878,1]]},"481":{"position":[[727,1],[2405,2],[2463,1],[2475,4],[2560,1],[2848,1],[2871,1],[3121,1],[3143,1],[3506,1],[4032,3],[4282,1],[4388,1],[4399,1],[4457,1],[4469,1],[4520,1],[4532,1],[4583,1],[4595,1],[4650,1],[4662,1],[4996,1],[5058,1],[5082,1],[5928,1],[6038,1],[6665,1],[6882,2]]},"483":{"position":[[644,1],[766,1],[777,1],[836,1],[848,1],[902,1],[914,1],[966,1],[977,1],[1033,1],[1044,1],[1262,1]]},"485":{"position":[[147,1],[1402,1]]},"489":{"position":[[873,1],[1297,1]]},"497":{"position":[[71,1],[284,1]]},"505":{"position":[[190,1],[1180,1],[1311,1]]},"507":{"position":[[186,1]]},"509":{"position":[[259,1],[289,1]]},"511":{"position":[[195,1],[355,1],[545,1],[729,1]]},"513":{"position":[[214,1],[311,2],[436,1]]},"519":{"position":[[48,1],[249,1],[251,3],[272,1],[384,2],[719,1],[721,3],[742,1],[934,2],[991,1],[993,3],[1014,1],[1180,2]]},"527":{"position":[[39,1]]},"545":{"position":[[158,1]]},"552":{"position":[[152,1]]},"554":{"position":[[141,1],[143,3],[321,1],[323,3],[346,6]]},"560":{"position":[[166,1],[190,1],[290,2],[967,1],[1019,1]]},"564":{"position":[[347,1]]},"566":{"position":[[112,2]]},"572":{"position":[[608,1]]},"584":{"position":[[44,1]]},"590":{"position":[[55,1]]},"598":{"position":[[381,1],[383,3]]},"600":{"position":[[999,1],[1001,3]]},"604":{"position":[[101,1],[103,3],[131,1],[168,1],[265,2],[427,1],[466,1],[468,2],[2114,1]]},"606":{"position":[[444,1],[581,1],[626,1],[633,100],[1223,1],[1312,100]]},"608":{"position":[[500,1],[636,1],[681,1],[688,255],[1489,1],[1577,255]]},"610":{"position":[[121,2],[131,2],[141,2],[151,2],[161,13],[368,1],[383,1],[414,12],[523,1],[538,1],[561,1],[593,12],[822,1],[952,1],[957,1],[994,2],[1053,20],[1228,1],[1251,2],[1354,1],[1365,2],[1371,1],[1381,2],[1386,1],[1396,2],[1401,1],[1411,2],[1416,1],[1426,2],[1431,1],[1441,2],[1446,1],[1456,2],[1461,1],[1471,2],[1476,1],[1486,2],[1491,1],[1501,23]]},"612":{"position":[[236,1],[238,3],[266,1],[303,1],[328,2],[390,3],[472,1],[497,1],[499,2],[775,2],[785,1],[990,1],[997,1],[1010,1],[1012,1],[1014,1],[1018,1],[1022,1],[1031,1],[1053,2],[1093,1],[1100,1],[1110,1],[1112,1],[1114,1],[1118,1],[1122,1],[1126,1],[1148,2],[1188,1],[1195,1],[1207,1],[1221,1],[1223,1],[1227,1],[1231,1],[1239,1],[1261,2],[1301,1],[1308,1],[1319,1],[1333,1],[1335,1],[1339,1],[1343,1],[1350,1],[1372,2],[1412,1],[1419,1],[1431,1],[1439,1],[1441,1],[1445,1],[1449,1],[1456,1],[1478,2],[1518,1],[1525,1],[1537,1],[1545,1],[1547,1],[1551,1],[1555,1],[1561,1],[1583,2],[1623,1],[1630,1],[1642,1],[1650,1],[1652,1],[1656,1],[1660,1],[1666,1],[1688,139]]},"618":{"position":[[418,1],[420,3],[444,1],[510,1],[523,1],[585,2],[595,1],[678,1],[680,1],[758,1],[760,1],[762,1],[764,1],[766,2],[1305,1],[1307,3],[1385,1],[1447,1],[1460,1],[1522,1],[1524,1],[1526,2]]},"644":{"position":[[285,1],[557,1],[667,1]]},"648":{"position":[[78,1],[80,3],[100,1],[167,2],[196,1],[263,2],[1032,1]]},"650":{"position":[[131,1],[133,3],[149,1],[211,2],[230,1],[265,2],[294,1],[329,2]]},"652":{"position":[[464,1],[509,1],[526,1],[623,1]]},"654":{"position":[[341,1],[386,1],[403,1],[533,1]]},"662":{"position":[[263,1],[534,1],[643,1]]},"666":{"position":[[76,1],[78,3],[96,1],[163,2],[890,1]]},"668":{"position":[[129,1],[131,3],[147,1],[209,2],[226,1],[261,2]]},"670":{"position":[[56,1],[101,1],[118,1],[207,1]]},"672":{"position":[[56,1],[101,1],[118,1],[184,1]]},"678":{"position":[[295,4]]},"680":{"position":[[290,1],[335,1],[352,1],[425,1]]},"682":{"position":[[197,1],[242,1],[259,1],[329,1],[394,1],[420,1],[476,1],[478,1],[480,2],[579,1],[605,1],[619,1],[621,1],[623,2]]},"684":{"position":[[155,1],[200,1],[217,1],[290,1]]},"686":{"position":[[51,1],[53,3],[80,1],[133,2],[528,1],[530,3],[611,1],[661,2],[970,1],[972,3],[999,1],[1001,3],[1030,2]]},"690":{"position":[[248,1],[250,1],[295,1],[297,1],[338,1],[340,1],[380,1],[382,1],[416,1],[418,1],[452,1],[454,1]]},"692":{"position":[[0,1],[2,1],[504,1]]},"694":{"position":[[27,1]]},"696":{"position":[[0,1],[2,1],[578,1],[606,1],[904,1]]},"700":{"position":[[33,1]]},"704":{"position":[[35,1]]},"706":{"position":[[34,1]]},"708":{"position":[[53,1]]},"710":{"position":[[36,1],[236,1]]},"712":{"position":[[34,1],[450,1],[552,1]]},"714":{"position":[[35,1],[327,1],[380,1],[557,1],[559,3]]},"716":{"position":[[34,1]]},"718":{"position":[[33,1]]},"720":{"position":[[52,1],[131,1]]},"722":{"position":[[35,1]]},"724":{"position":[[41,1]]},"726":{"position":[[39,1]]},"728":{"position":[[38,3],[42,1]]},"730":{"position":[[36,3],[40,1]]},"732":{"position":[[72,1],[357,1]]},"734":{"position":[[596,2],[726,1],[848,1],[1131,1],[1341,1],[1343,1],[1458,2],[1461,1],[1519,1],[1521,2],[2131,1],[2234,1]]},"740":{"position":[[212,1]]},"748":{"position":[[186,1]]},"750":{"position":[[182,1]]},"756":{"position":[[555,2]]},"758":{"position":[[874,2],[939,1],[941,3],[953,1],[967,1],[1012,2],[1027,1],[1072,1],[1074,2],[1826,1]]},"764":{"position":[[348,1],[399,1],[1388,1]]},"768":{"position":[[30,1],[97,1],[133,1],[156,2],[629,1]]},"770":{"position":[[88,1],[128,1],[141,1],[220,1],[256,1],[279,2],[292,1],[307,1]]},"772":{"position":[[55,1],[114,1],[193,1],[229,1],[288,2]]},"808":{"position":[[403,1]]},"854":{"position":[[32,1],[97,3],[101,1],[185,3]]},"856":{"position":[[106,1],[108,4]]},"858":{"position":[[144,1],[461,1],[463,4]]},"864":{"position":[[154,1]]},"866":{"position":[[159,1]]},"868":{"position":[[362,1]]},"870":{"position":[[219,1]]},"876":{"position":[[238,1]]},"878":{"position":[[213,1],[1100,3]]},"884":{"position":[[70,1]]},"886":{"position":[[150,1],[199,1],[236,1],[269,1]]},"892":{"position":[[683,1],[771,1],[839,1],[881,1],[908,1],[1181,1],[1183,4]]},"894":{"position":[[73,2]]},"902":{"position":[[263,1]]},"906":{"position":[[307,1],[402,1],[432,1]]},"912":{"position":[[149,1],[886,1]]},"918":{"position":[[740,1],[924,1]]},"922":{"position":[[140,1],[142,3],[210,1]]},"926":{"position":[[236,3],[948,1],[950,3]]},"930":{"position":[[136,3]]},"941":{"position":[[396,2],[421,2],[438,2],[456,2]]},"945":{"position":[[362,1],[714,1]]},"951":{"position":[[417,1],[431,1],[448,1]]},"953":{"position":[[519,1],[543,1]]},"963":{"position":[[1366,2],[1389,2],[1454,2],[1569,1],[1590,1],[1693,1],[1702,2],[1709,1],[1756,1],[1769,2],[1805,1],[1831,5],[1858,1],[1932,1],[1934,1],[1971,1],[1979,1],[1981,2],[2035,1],[2111,2]]},"965":{"position":[[2668,1]]},"971":{"position":[[286,1],[318,1],[357,1]]},"975":{"position":[[159,2],[242,2],[529,1],[624,1],[1161,1],[1193,1],[1232,1]]},"977":{"position":[[1235,1],[1401,1]]},"979":{"position":[[242,3],[379,3],[454,1],[526,2],[802,1],[804,3]]},"983":{"position":[[565,1],[567,3],[607,1],[645,2],[721,1],[723,3],[763,1],[807,2]]},"985":{"position":[[218,1],[220,3],[249,1],[301,2],[361,1],[681,1],[683,3],[712,1],[815,2]]},"989":{"position":[[1536,2],[1651,1],[1653,3]]},"991":{"position":[[581,3],[644,3]]},"993":{"position":[[112,1],[431,1],[437,1],[479,1]]},"999":{"position":[[453,1]]},"1001":{"position":[[20,1],[22,1],[167,1],[186,2],[204,2],[264,5],[312,1],[671,1],[734,1],[802,1],[980,1],[1302,1],[1313,1],[1315,1],[1347,1],[1396,1],[1408,1],[1438,2]]},"1003":{"position":[[20,1],[22,1],[167,1],[186,2],[203,1],[205,1],[207,3],[260,1],[485,1],[519,1],[876,2]]},"1005":{"position":[[71,1]]},"1009":{"position":[[86,1],[88,3]]},"1011":{"position":[[62,1],[64,3]]},"1013":{"position":[[147,1]]},"1035":{"position":[[173,1]]},"1041":{"position":[[837,3],[841,1],[999,1],[1075,1]]},"1043":{"position":[[679,1],[819,1],[821,1],[860,1],[862,2],[940,1]]},"1047":{"position":[[152,1],[154,3]]},"1049":{"position":[[87,1],[89,3]]},"1061":{"position":[[342,1],[344,3],[370,1],[470,2]]},"1065":{"position":[[54,1],[56,3],[161,1],[1855,1],[5143,1],[5168,1],[5183,1],[5185,2],[5249,1],[5329,2],[5401,2]]},"1067":{"position":[[54,1],[56,3],[161,1],[456,1]]},"1069":{"position":[[53,1],[55,3],[368,1]]},"1071":{"position":[[53,1],[55,3],[166,1],[725,1],[743,1],[1324,1],[1326,3],[1496,1],[1498,3],[1623,2],[1665,3],[1727,1],[1945,3],[3755,1]]},"1073":{"position":[[53,1],[55,3],[160,1],[1184,1],[1186,3],[1365,1],[1367,3],[1486,2],[1543,3],[1742,1],[1981,3]]},"1075":{"position":[[94,1],[105,1],[147,2],[185,2]]},"1077":{"position":[[122,1],[138,1],[204,2]]},"1079":{"position":[[309,1],[732,1],[734,3],[848,1],[850,3],[967,1],[969,3],[1097,1],[1099,3],[1199,1],[1201,1],[1258,1],[1260,2],[1332,1],[1334,3]]},"1081":{"position":[[101,3],[268,2],[373,2]]},"1087":{"position":[[625,1],[829,1],[831,3],[1150,1],[1239,1],[1278,1]]},"1089":{"position":[[913,1]]},"1091":{"position":[[252,1],[254,3]]},"1093":{"position":[[252,1],[254,3],[298,1],[300,1],[424,2],[427,1],[523,2],[526,1],[607,2],[610,1],[687,2],[690,1],[759,2],[762,1],[843,2],[846,1],[904,1],[906,1],[960,1],[962,2],[1210,2]]},"1095":{"position":[[171,1],[173,3],[217,6],[479,1],[481,3],[525,6]]},"1097":{"position":[[300,1],[302,3],[346,6],[367,1],[369,1],[474,2],[477,1],[582,1],[584,2],[949,1]]},"1099":{"position":[[87,1],[89,3],[133,6],[154,6],[179,1],[181,1],[228,2],[231,1],[277,1],[279,2],[498,1],[659,2],[695,1],[852,1]]},"1101":{"position":[[307,1],[372,1],[576,1],[621,1]]},"1107":{"position":[[126,1],[128,3],[1267,1],[1284,1],[1323,4],[1328,1]]},"1109":{"position":[[124,1],[157,1],[186,1],[204,1],[206,1],[208,1],[385,1],[387,1],[420,1],[449,1],[466,1],[478,1],[499,1],[528,1],[592,1],[594,1],[609,1],[832,1],[1151,1],[1180,1],[1198,1],[1311,3],[1538,1],[1712,1],[1760,2]]},"1111":{"position":[[72,1],[107,1],[157,1],[175,1],[177,2]]},"1113":{"position":[[1290,1]]},"1115":{"position":[[54,1],[91,1],[138,2]]},"1117":{"position":[[46,1],[82,1],[102,2]]},"1121":{"position":[[207,1],[241,1],[261,2],[349,1],[514,1],[528,1],[570,1],[635,2],[678,1],[743,1],[745,1],[747,2]]},"1123":{"position":[[132,1],[172,1],[192,2],[278,1],[442,1],[477,2]]},"1125":{"position":[[465,1],[498,1],[530,2],[614,1],[779,1],[827,1],[829,1],[839,1],[857,2],[872,2],[875,1],[885,1],[903,2],[918,1],[920,1],[922,2]]},"1127":{"position":[[172,1],[212,1],[232,2],[311,1],[475,3]]},"1129":{"position":[[77,1],[111,3]]},"1131":{"position":[[115,4],[120,1],[285,1],[296,1],[298,1],[469,2],[472,1],[474,1],[476,2]]},"1137":{"position":[[618,1],[620,3],[777,1]]},"1139":{"position":[[132,2],[460,1],[507,1],[554,1],[616,1],[694,1]]},"1141":{"position":[[545,2],[677,1],[769,1],[781,2],[841,2],[848,1],[867,1],[895,2],[940,1],[952,2],[1087,2],[1097,2],[1104,1],[1151,1],[1158,1],[1179,2],[1186,1],[1198,2],[1280,1],[1287,1],[1327,1],[1329,1],[1355,2]]},"1143":{"position":[[237,1],[337,1],[449,1],[502,1],[504,1],[513,2],[569,1],[596,1],[608,2],[712,2],[719,1],[738,1],[766,2],[808,1],[820,2],[946,2],[956,2],[963,1],[1010,1],[1017,1],[1038,2],[1045,1],[1057,2],[1139,1],[1146,1],[1186,1],[1188,1],[1214,2]]},"1147":{"position":[[70,1]]},"1149":{"position":[[646,1]]},"1151":{"position":[[42,1]]},"1164":{"position":[[952,1],[976,1],[978,3],[1008,1],[1042,1],[1095,1],[1097,1],[1375,1],[2019,1],[2035,1],[2037,3],[2062,1],[2114,1],[2154,1],[2180,1],[2192,1],[2206,1],[2260,1],[2262,1],[2915,3],[3366,1]]},"1166":{"position":[[385,1],[419,1],[445,2],[448,2],[507,1],[519,1],[540,1],[550,1],[565,1],[582,1],[595,1],[609,1],[629,1],[642,1],[652,1],[662,1],[671,1],[684,1],[702,1],[708,1],[728,1],[746,1],[1069,1],[1160,2]]},"1168":{"position":[[161,1],[195,1],[214,2],[348,1],[369,3]]},"1170":{"position":[[181,1],[215,1],[234,2]]},"1172":{"position":[[437,1],[472,1],[503,2]]},"1176":{"position":[[189,1],[372,1],[374,1],[376,2],[568,1],[739,1],[741,1],[743,2],[988,1],[1159,1],[1161,1],[1163,2],[1261,1],[1311,2]]},"1178":{"position":[[164,1]]},"1180":{"position":[[269,1],[311,1]]},"1182":{"position":[[1709,1]]},"1184":{"position":[[256,1],[317,4],[558,1],[630,1],[766,1],[1630,1]]},"1196":{"position":[[140,1]]},"1203":{"position":[[85,1],[87,3]]},"1207":{"position":[[261,1],[263,3],[423,1],[791,1],[1054,1],[1145,1],[1325,1],[1389,1]]},"1213":{"position":[[327,1],[474,1],[544,1]]},"1220":{"position":[[88,1],[90,3]]},"1231":{"position":[[75,1],[77,3]]},"1235":{"position":[[16,2]]},"1241":{"position":[[17,3]]},"1243":{"position":[[17,3]]},"1258":{"position":[[80,1],[82,3]]},"1262":{"position":[[70,1],[300,1],[460,1],[874,1],[1007,5],[1438,1]]},"1266":{"position":[[374,1]]},"1268":{"position":[[363,1],[826,1],[849,1]]},"1270":{"position":[[268,1],[284,1],[298,1],[308,1],[319,1],[336,1],[349,1],[364,1],[377,1],[393,1],[406,1],[411,1],[427,1],[447,1],[463,1],[851,1]]},"1277":{"position":[[301,1],[303,3]]},"1279":{"position":[[260,1],[262,3]]},"1283":{"position":[[497,1]]},"1293":{"position":[[552,1]]},"1295":{"position":[[384,1],[462,1]]},"1305":{"position":[[339,1],[678,1],[942,1]]},"1307":{"position":[[250,1],[454,1],[972,1]]},"1309":{"position":[[265,1],[621,1],[712,1],[790,1]]},"1327":{"position":[[670,1],[1087,1]]},"1329":{"position":[[75,2],[254,2]]},"1333":{"position":[[349,1]]},"1335":{"position":[[1678,1],[1795,1],[1995,1]]},"1337":{"position":[[155,1],[322,1]]},"1347":{"position":[[39,1],[141,1],[253,1],[320,2],[387,1],[475,1],[656,1],[695,1],[779,1],[1034,1],[1088,2],[1221,1],[1476,1],[1530,2]]},"1349":{"position":[[50,1]]},"1353":{"position":[[929,1]]},"1355":{"position":[[213,1]]},"1359":{"position":[[71,1],[284,1]]},"1367":{"position":[[190,1],[1180,1],[1311,1]]},"1369":{"position":[[186,1]]},"1371":{"position":[[259,1],[289,1]]},"1373":{"position":[[195,1],[355,1],[545,1],[729,1]]},"1375":{"position":[[214,1],[311,2],[436,1]]},"1379":{"position":[[578,1]]},"1381":{"position":[[1149,1]]},"1389":{"position":[[539,1]]},"1391":{"position":[[147,1]]},"1395":{"position":[[371,1]]},"1399":{"position":[[138,1],[364,1]]},"1403":{"position":[[588,1]]},"1407":{"position":[[81,1]]},"1409":{"position":[[31,1]]},"1413":{"position":[[198,1]]},"1417":{"position":[[233,1]]},"1431":{"position":[[818,1]]},"1435":{"position":[[39,1]]},"1449":{"position":[[350,1]]},"1457":{"position":[[27,1]]},"1463":{"position":[[85,1]]},"1465":{"position":[[233,1]]},"1469":{"position":[[899,1]]},"1473":{"position":[[214,1],[224,1],[226,1],[278,1],[280,2]]},"1475":{"position":[[184,1],[356,1],[366,1],[368,1],[409,2],[412,1],[453,2],[456,2],[605,1],[782,1],[912,1],[922,1],[924,1],[976,2],[979,1],[1037,2],[1040,2],[1246,1],[1628,1],[1638,1],[1640,1],[1681,2],[1684,1],[1749,2],[1752,2],[1897,1]]},"1477":{"position":[[17,1],[283,1]]},"1479":{"position":[[154,1],[625,1],[766,1]]},"1481":{"position":[[143,1],[153,1],[155,1],[219,1],[221,2]]},"1483":{"position":[[10,1],[20,1],[22,1],[90,1],[92,2]]},"1485":{"position":[[83,1],[93,1],[95,1],[163,1],[165,1],[209,1],[211,2]]},"1487":{"position":[[53,1],[63,1],[65,1],[79,6],[145,1],[147,2],[254,2]]},"1489":{"position":[[395,1],[503,1]]},"1491":{"position":[[17,1],[298,1]]},"1493":{"position":[[251,1],[444,1]]},"1495":{"position":[[20,2],[791,1],[1042,3],[1325,1],[1591,3],[2330,1],[2385,1],[2446,1],[2465,3],[2512,1],[2603,1],[2690,1],[2773,1],[2851,1],[2873,1],[2906,1],[2960,1],[3053,1],[3124,1],[4078,1],[4282,1],[5028,1],[5030,3],[5159,1],[5970,1],[6197,1],[6449,1],[6884,1],[6945,1],[7116,1],[7828,1],[8414,1],[8689,1],[9353,1],[9434,1],[9511,1],[9847,1],[9919,1],[9936,1],[10025,1],[10053,1],[10466,1]]},"1499":{"position":[[212,2],[236,2],[262,2],[289,2],[315,2],[374,1],[581,2],[621,2],[872,1],[1102,1]]},"1505":{"position":[[345,1]]},"1507":{"position":[[906,1],[1085,1],[1176,1]]},"1511":{"position":[[352,1],[692,1],[788,1]]},"1515":{"position":[[147,1]]},"1517":{"position":[[193,1]]},"1521":{"position":[[364,1],[380,1],[382,1],[442,1],[444,2],[836,1],[858,1],[1040,3]]},"1523":{"position":[[1154,1],[1156,3],[1188,2],[1520,1],[1536,1],[1538,1],[1602,2],[1617,2],[1631,2],[1666,1],[1668,2]]},"1533":{"position":[[101,1],[103,3],[131,1],[168,1],[265,2],[516,1],[554,1],[556,2],[2618,1]]},"1535":{"position":[[364,1],[501,1],[546,1],[553,100],[1063,1],[1152,100]]},"1537":{"position":[[418,1],[480,1],[525,1],[532,255],[1097,1],[1188,100]]},"1539":{"position":[[491,1],[627,1],[672,1],[679,255],[1471,1],[1559,255]]},"1541":{"position":[[440,1],[502,1],[547,1],[554,255],[1294,1],[1385,255]]},"1543":{"position":[[0,2],[640,1],[702,1],[747,1],[754,255],[1614,1],[1702,255]]},"1545":{"position":[[121,2],[131,2],[141,2],[151,2],[161,13],[368,1],[383,1],[414,12],[523,1],[538,1],[561,1],[593,12],[814,1],[946,1],[951,1],[988,2],[1047,20],[1222,1],[1245,2],[1348,1],[1359,2],[1365,1],[1375,2],[1380,1],[1390,2],[1395,1],[1405,2],[1410,1],[1420,2],[1425,1],[1435,2],[1440,1],[1450,2],[1455,1],[1465,2],[1470,1],[1480,2],[1485,1],[1495,23],[1657,1],[1703,1],[1730,12]]},"1547":{"position":[[307,1],[309,3],[337,1],[374,1],[399,2],[461,3],[602,1],[627,1],[629,2],[944,2],[954,1],[1159,1],[1166,1],[1179,1],[1181,1],[1183,1],[1187,1],[1191,1],[1200,1],[1222,2],[1262,1],[1269,1],[1279,1],[1281,1],[1283,1],[1287,1],[1291,1],[1295,1],[1317,2],[1357,1],[1364,1],[1376,1],[1390,1],[1392,1],[1396,1],[1400,1],[1408,1],[1430,2],[1470,1],[1477,1],[1488,1],[1502,1],[1504,1],[1508,1],[1512,1],[1519,1],[1541,2],[1581,1],[1588,1],[1600,1],[1608,1],[1610,1],[1614,1],[1618,1],[1625,1],[1647,2],[1687,1],[1694,1],[1706,1],[1714,1],[1716,1],[1720,1],[1724,1],[1730,1],[1752,2],[1792,1],[1799,1],[1811,1],[1819,1],[1821,1],[1825,1],[1829,1],[1835,1],[1857,139]]},"1555":{"position":[[55,1]]},"1563":{"position":[[381,1],[383,3]]},"1567":{"position":[[27,1],[29,2],[51,3],[81,2],[131,1],[133,1],[158,2],[191,3],[195,2],[198,1],[233,2],[266,3],[270,1],[272,2],[313,1],[412,2],[419,1],[477,1],[585,1],[1207,2],[1251,1]]},"1569":{"position":[[96,1],[200,1],[778,2],[955,1]]},"1571":{"position":[[0,1],[179,1],[198,1],[222,1],[240,1],[399,1],[429,1],[448,1],[464,1],[612,1],[639,1],[662,1]]},"1577":{"position":[[68,1],[159,1],[1156,1],[1201,1],[1218,1],[1281,1],[1321,1],[1333,1],[1350,1],[1392,1],[1469,2],[1512,1],[1589,1],[1591,1],[1593,2]]},"1583":{"position":[[85,2],[192,2],[289,3],[383,2],[480,2],[564,2],[632,1],[690,2],[800,2],[896,2],[987,2],[1073,2],[1136,2],[1214,2],[1300,2]]},"1587":{"position":[[260,1]]},"1611":{"position":[[603,1],[610,1],[797,1],[799,2],[822,3]]},"1619":{"position":[[504,1],[1281,1],[1283,3]]},"1629":{"position":[[539,1],[834,1],[871,1],[1116,1],[1329,1]]},"1635":{"position":[[227,1]]},"1637":{"position":[[73,1]]},"1639":{"position":[[535,1],[537,3],[553,1],[615,2],[640,1],[780,2],[848,1]]},"1641":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[242,2]]},"1643":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[402,2]]},"1647":{"position":[[12,2],[205,1],[207,3],[223,1],[285,2],[310,1],[340,2],[366,2],[457,1]]},"1668":{"position":[[748,1]]},"1676":{"position":[[105,1],[489,1],[890,1],[892,3],[925,1],[955,1],[968,1],[970,1],[1001,2],[1004,1],[1006,2],[1018,1],[1031,1],[1033,1],[1064,2],[1067,1],[1099,2],[1102,1],[1104,2],[1118,1],[1131,1],[1133,1],[1163,2],[1166,1],[1168,2],[1178,1],[1191,1],[1193,1],[1224,1],[1226,2],[1248,1],[1250,1],[1295,1],[1297,1],[1328,1],[1330,1],[1332,1],[1334,1],[1336,1],[1338,2],[1436,1],[1572,1],[1824,1]]},"1678":{"position":[[380,1],[788,1],[790,3],[821,1],[851,1],[864,1],[866,1],[897,2],[900,1],[902,2],[916,1],[929,1],[931,1],[961,1],[963,1],[965,2],[975,1],[988,1],[990,1],[1021,1],[1023,2],[1045,1],[1047,1],[1092,1],[1094,1],[1125,1],[1127,1],[1129,1],[1131,1],[1133,1],[1135,2]]},"1680":{"position":[[496,1],[751,1],[1228,1],[1230,3],[1267,1],[1332,1],[1345,1],[1347,1],[1378,2],[1381,1],[1383,2],[1397,1],[1410,1],[1412,1],[1442,1],[1444,1],[1446,2],[1456,1],[1469,1],[1471,1],[1502,1],[1504,2],[1526,1],[1528,1],[1573,1],[1575,1],[1606,1],[1608,1],[1610,1],[1612,1],[1614,1],[1616,2],[2015,1],[2017,3],[2108,1],[2158,3],[2162,2]]},"1682":{"position":[[1063,1],[1282,1],[1417,1],[1419,3],[1446,1],[1474,1],[1486,1],[1488,1],[1490,1],[1521,1],[1523,1],[1525,1],[1527,2]]},"1690":{"position":[[285,1],[557,1],[667,1]]},"1694":{"position":[[78,1],[80,3],[100,1],[167,2],[196,1],[263,2],[1032,1]]},"1696":{"position":[[131,1],[133,3],[149,1],[211,2],[230,1],[265,2],[294,1],[329,2]]},"1699":{"position":[[464,1],[509,1],[526,1],[623,1]]},"1702":{"position":[[341,1],[386,1],[403,1],[533,1]]},"1706":{"position":[[263,1],[534,1],[643,1]]},"1710":{"position":[[76,1],[78,3],[96,1],[163,2],[890,1]]},"1712":{"position":[[129,1],[131,3],[147,1],[209,2],[226,1],[261,2]]},"1715":{"position":[[56,1],[101,1],[118,1],[207,1]]},"1717":{"position":[[56,1],[101,1],[118,1],[184,1]]},"1721":{"position":[[32,1],[97,3],[101,1],[185,3]]},"1723":{"position":[[106,1],[108,4]]},"1725":{"position":[[144,1],[461,1],[463,4]]},"1729":{"position":[[290,4]]},"1731":{"position":[[290,1],[335,1],[352,1],[425,1]]},"1733":{"position":[[197,1],[242,1],[259,1],[329,1],[394,1],[420,1],[476,1],[478,1],[480,2],[579,1],[605,1],[619,1],[621,1],[623,2]]},"1735":{"position":[[155,1],[200,1],[217,1],[290,1]]},"1737":{"position":[[49,1],[51,3],[70,1],[123,2],[518,1],[520,3],[593,1],[643,2],[952,1],[954,3],[973,1],[975,3],[1004,2]]},"1741":{"position":[[905,2],[1261,1],[1417,1],[1938,1]]},"1743":{"position":[[332,1]]},"1759":{"position":[[212,1]]},"1767":{"position":[[154,1]]},"1769":{"position":[[150,1]]},"1777":{"position":[[466,1],[468,3],[480,1],[494,1],[539,2],[554,1],[599,1],[601,2],[1353,1]]},"1783":{"position":[[348,1],[399,1],[1388,1]]},"1787":{"position":[[30,1],[97,1],[133,1],[156,2],[627,1],[688,1]]},"1789":{"position":[[88,1],[128,1],[141,1],[220,1],[256,1],[279,2],[292,1],[307,1]]},"1791":{"position":[[55,1],[114,1],[193,1],[229,1],[288,2]]},"1797":{"position":[[731,1],[971,2],[974,3]]},"1803":{"position":[[145,1]]},"1823":{"position":[[567,1],[701,1],[703,3],[719,1],[733,1],[750,1],[752,2]]},"1825":{"position":[[147,1]]},"1859":{"position":[[403,1]]},"1869":{"position":[[550,1],[975,1]]},"1871":{"position":[[240,1],[242,1],[287,1],[289,1],[329,1],[331,1],[372,1],[374,1],[408,1],[410,1],[444,1],[446,1]]},"1873":{"position":[[0,1],[2,1],[489,1]]},"1875":{"position":[[0,1],[2,1],[597,1]]},"1877":{"position":[[159,1],[251,1],[339,1],[539,2]]},"1879":{"position":[[866,1]]},"1881":{"position":[[596,2],[726,1],[844,1],[883,1],[1162,1],[1316,1],[1318,1],[1379,2],[1382,1],[1402,1],[1404,2],[2018,1],[2121,1]]},"1885":{"position":[[34,1],[621,1],[666,1],[683,1],[754,1],[793,1],[807,1],[849,1],[947,2],[990,1],[1088,1],[1130,1],[1227,1],[1229,1],[1231,2]]},"1887":{"position":[[36,1],[239,1],[983,1]]},"1889":{"position":[[118,1],[306,1]]},"1891":{"position":[[34,1],[450,1],[552,1]]},"1893":{"position":[[35,1],[327,1],[380,1],[557,1],[559,3],[802,1],[847,1],[864,1],[948,1],[987,1],[1005,1],[1007,1],[1017,1],[1032,2],[1047,2],[1050,1],[1060,1],[1075,2],[1090,1],[1092,2],[1124,2]]},"1895":{"position":[[43,1],[988,1]]},"1897":{"position":[[40,1],[743,1]]},"1899":{"position":[[52,1],[313,1]]},"1901":{"position":[[55,1]]},"1903":{"position":[[348,1],[889,1]]},"1905":{"position":[[50,1],[179,1],[321,1]]},"1907":{"position":[[53,1]]},"1909":{"position":[[54,1]]},"1911":{"position":[[50,1]]},"1913":{"position":[[53,1]]},"1915":{"position":[[55,1]]},"1917":{"position":[[51,1]]},"1919":{"position":[[54,1]]},"1923":{"position":[[31,3],[35,1],[616,1],[632,1],[634,1],[715,1],[717,2]]},"1925":{"position":[[41,1]]},"1927":{"position":[[39,1]]},"1929":{"position":[[43,1]]},"1931":{"position":[[38,3],[42,1]]},"1933":{"position":[[36,3],[40,1]]},"1935":{"position":[[40,3],[44,1]]},"1937":{"position":[[306,1],[814,1],[1059,1],[1146,1],[1148,1],[1552,1],[1554,2]]},"1943":{"position":[[238,1]]},"1945":{"position":[[213,1],[1100,3]]},"1951":{"position":[[70,1]]},"1953":{"position":[[150,1],[199,1],[236,1],[269,1]]},"1959":{"position":[[683,1],[771,1],[839,1],[881,1],[908,1],[1181,1],[1183,4]]},"1961":{"position":[[73,2]]},"1969":{"position":[[263,1]]},"1973":{"position":[[307,1],[402,1],[432,1]]},"1975":{"position":[[58,1]]},"1981":{"position":[[149,1],[886,1]]},"1987":{"position":[[204,1]]},"1993":{"position":[[955,1],[1139,1]]},"1997":{"position":[[140,1],[142,3],[210,1]]},"2001":{"position":[[236,3],[948,1],[950,3]]},"2005":{"position":[[136,3]]},"2016":{"position":[[396,2],[421,2],[438,2],[456,2]]},"2020":{"position":[[362,1],[714,1]]},"2026":{"position":[[304,1]]},"2030":{"position":[[159,2],[208,2],[345,1],[440,1],[1347,1]]},"2032":{"position":[[305,1],[329,1],[420,2],[423,1],[616,2],[619,1],[811,2],[814,1],[1044,2],[1047,1]]},"2034":{"position":[[1235,1],[1401,1]]},"2036":{"position":[[242,3],[379,3],[454,1],[526,2],[777,1],[779,3]]},"2038":{"position":[[290,1],[323,1],[423,2],[426,1],[637,2],[640,1],[850,2],[853,1],[1101,2],[1104,1]]},"2042":{"position":[[565,1],[567,3],[607,1],[645,2],[721,1],[723,3],[763,1],[807,2]]},"2044":{"position":[[218,1],[220,3],[249,1],[301,2],[361,1],[681,1],[683,3],[712,1],[815,2]]},"2048":{"position":[[230,1],[287,1]]},"2050":{"position":[[1536,2],[1651,1],[1653,3]]},"2052":{"position":[[581,3],[644,3]]},"2054":{"position":[[112,1],[431,1],[437,1],[479,1]]},"2062":{"position":[[198,1]]},"2064":{"position":[[154,1]]},"2066":{"position":[[362,1]]},"2068":{"position":[[423,1]]},"2070":{"position":[[219,1]]},"2072":{"position":[[227,1]]},"2078":{"position":[[417,1],[431,1],[448,1]]},"2080":{"position":[[519,1],[543,1]]},"2090":{"position":[[86,1],[88,3]]},"2092":{"position":[[62,1],[64,3]]},"2094":{"position":[[147,1]]},"2100":{"position":[[1366,2],[1389,2],[1454,2],[1569,1],[1590,1],[1693,1],[1702,2],[1709,1],[1756,1],[1769,2],[1805,1],[1831,5],[1858,1],[1932,1],[1934,1],[1971,1],[1979,1],[1981,2],[2035,1],[2111,2]]},"2102":{"position":[[1500,1],[2855,1]]},"2106":{"position":[[453,1]]},"2108":{"position":[[20,1],[22,1],[167,1],[186,2],[204,2],[264,5],[312,1],[671,1],[734,1],[802,1],[980,1],[1302,1],[1313,1],[1315,1],[1347,1],[1396,1],[1408,1],[1438,2]]},"2110":{"position":[[20,1],[22,1],[167,1],[186,2],[203,1],[205,1],[207,3],[260,1],[485,1],[519,1],[876,2]]},"2112":{"position":[[71,1]]},"2118":{"position":[[126,1],[142,2],[185,3],[328,1],[376,1],[393,1],[465,1]]},"2120":{"position":[[282,1],[466,1],[548,1]]},"2122":{"position":[[176,1],[264,1]]},"2132":{"position":[[837,3],[841,1],[999,1],[1075,1]]},"2134":{"position":[[691,1],[831,1],[833,1],[872,1],[874,2],[952,1]]},"2142":{"position":[[342,1],[344,3],[370,1],[470,2]]},"2146":{"position":[[54,1],[56,3],[161,1],[1972,1],[5266,1],[5291,1],[5306,1],[5308,2],[5372,1],[5452,2],[5524,2],[6245,1],[6261,1],[6302,2],[6305,1]]},"2148":{"position":[[54,1],[56,3],[161,1],[456,1]]},"2150":{"position":[[53,1],[55,3],[368,1]]},"2152":{"position":[[53,1],[55,3],[166,1],[725,1],[743,1],[1324,1],[1326,3],[1496,1],[1498,3],[1623,2],[1665,3],[1727,1],[1945,3],[3786,1],[4399,1],[4410,1],[4456,2],[4459,1]]},"2154":{"position":[[53,1],[55,3],[160,1],[1184,1],[1186,3],[1365,1],[1367,3],[1486,2],[1543,3],[1742,1],[1981,3]]},"2156":{"position":[[81,1],[83,3],[200,1],[1445,1],[1447,3],[1590,1],[1592,3],[1684,2],[1728,3],[1831,1]]},"2158":{"position":[[94,1],[105,1],[147,2]]},"2160":{"position":[[122,1],[138,1],[184,2],[378,1]]},"2162":{"position":[[309,1],[732,1],[734,3],[848,1],[850,3],[967,1],[969,3],[1097,1],[1099,3],[1199,1],[1201,1],[1258,1],[1260,2],[1332,1],[1334,3]]},"2164":{"position":[[101,3],[268,2],[373,2]]},"2170":{"position":[[625,1],[829,1],[831,3],[1150,1],[1239,1],[1278,1]]},"2172":{"position":[[887,1]]},"2174":{"position":[[252,1],[254,3]]},"2176":{"position":[[252,1],[254,3],[298,1],[300,1],[424,2],[427,1],[523,2],[526,1],[607,2],[610,1],[687,2],[690,1],[759,2],[762,1],[843,2],[846,1],[904,1],[906,1],[960,1],[962,2],[1210,2]]},"2178":{"position":[[171,1],[173,3],[217,6],[479,1],[481,3],[525,6]]},"2180":{"position":[[337,1],[339,3],[383,6],[404,1],[406,1],[511,2],[514,1],[619,1],[621,2],[1152,1]]},"2182":{"position":[[87,1],[89,3],[133,6],[154,6],[179,1],[181,1],[228,2],[231,1],[277,1],[279,2],[498,1],[659,2],[695,1],[852,1]]},"2188":{"position":[[129,1],[131,3],[734,1],[1270,1],[1287,1],[1326,4],[1331,1]]},"2190":{"position":[[185,1],[218,1],[247,1],[265,1],[267,2],[445,1],[447,1],[480,1],[509,1],[526,1],[538,1],[559,1],[588,1],[652,1],[654,1],[669,1],[892,1],[1211,1],[1240,1],[1258,1],[1371,3],[1592,1],[1766,1],[1814,2]]},"2192":{"position":[[72,1],[107,1],[157,1],[175,1],[177,2]]},"2194":{"position":[[1354,1]]},"2196":{"position":[[54,1],[91,1],[138,2]]},"2198":{"position":[[46,1],[82,1],[102,2]]},"2202":{"position":[[250,1],[284,1],[304,2],[392,1],[557,1],[571,1],[613,1],[678,2],[721,1],[786,1],[788,1],[790,2]]},"2204":{"position":[[132,1],[172,1],[192,2],[278,1],[442,1],[477,2]]},"2206":{"position":[[465,1],[498,1],[530,2],[614,1],[779,1],[827,1],[829,1],[839,1],[857,2],[872,2],[875,1],[885,1],[903,2],[918,1],[920,1],[922,2]]},"2208":{"position":[[172,1],[212,1],[232,2],[311,1],[475,3]]},"2210":{"position":[[77,1],[111,3]]},"2212":{"position":[[115,4],[120,1],[285,1],[296,1],[298,1],[469,2],[472,1],[474,1],[476,2]]},"2214":{"position":[[241,1],[258,1],[421,1],[509,1]]},"2218":{"position":[[618,1],[620,3],[777,1]]},"2220":{"position":[[132,2],[460,1],[507,1],[554,1],[616,1],[694,1]]},"2222":{"position":[[545,2],[677,1],[769,1],[781,2],[841,2],[848,1],[867,1],[895,2],[940,1],[952,2],[1087,2],[1097,2],[1104,1],[1151,1],[1158,1],[1179,2],[1186,1],[1198,2],[1280,1],[1287,1],[1327,1],[1329,1],[1355,2]]},"2224":{"position":[[237,1],[337,1],[449,1],[502,1],[504,1],[513,2],[569,1],[596,1],[608,2],[712,2],[719,1],[738,1],[766,2],[808,1],[820,2],[946,2],[956,2],[963,1],[1010,1],[1017,1],[1038,2],[1045,1],[1057,2],[1139,1],[1146,1],[1186,1],[1188,1],[1214,2]]},"2228":{"position":[[152,1],[154,3]]},"2230":{"position":[[87,1],[89,3]]},"2238":{"position":[[139,1],[473,1]]},"2240":{"position":[[802,3],[1186,1],[1586,1],[1717,1]]},"2244":{"position":[[646,1],[670,1],[672,3],[702,1],[736,1],[789,1],[791,1],[1065,1],[1817,1],[1833,1],[1835,3],[1860,1],[1912,1],[1952,1],[1978,1],[1990,1],[2004,1],[2058,1],[2060,1]]},"2250":{"position":[[1709,1]]},"2256":{"position":[[14,1],[141,1],[186,1]]},"2262":{"position":[[57,1],[59,1],[61,1],[63,1],[65,1],[90,1],[92,1],[94,1],[96,1],[98,1],[143,1],[145,1],[147,1],[149,1],[151,1],[174,1],[176,1],[178,1],[180,1],[182,1],[198,1],[200,1],[202,1],[204,1],[206,1],[225,1],[227,1],[229,1],[231,1],[233,1],[248,1],[250,1],[252,1],[254,1],[256,1],[268,1],[270,1],[272,1],[274,1],[276,1],[303,1],[305,1],[307,1],[309,1],[311,1],[350,1],[352,1],[354,1],[356,1],[358,1],[386,1],[388,1],[390,1],[392,1],[394,1],[409,1],[445,1]]},"2264":{"position":[[59,1],[61,1],[63,1],[65,1],[67,1],[98,1],[100,1],[102,1],[104,1],[106,1],[153,1],[155,1],[157,1],[159,1],[161,1],[190,1],[192,1],[194,1],[196,1],[198,1],[227,1],[229,1],[231,1],[233,1],[235,1],[263,1],[265,1],[267,1],[269,1],[271,1],[300,1],[302,1],[304,1],[306,1],[308,1],[348,1],[350,1],[352,1],[354,1],[356,1],[387,1],[389,1],[391,1],[393,1],[395,1],[422,1]]},"2269":{"position":[[173,1],[175,3]]},"2275":{"position":[[70,1]]},"2277":{"position":[[646,1]]},"2279":{"position":[[42,1]]},"2290":{"position":[[380,1]]},"2292":{"position":[[829,1],[852,1]]},"2294":{"position":[[268,1],[285,1],[306,1],[321,1],[338,1],[367,1],[388,1],[413,1],[434,1],[462,1],[484,1],[1160,1],[1173,1],[1194,1],[1213,1],[1243,1]]},"2301":{"position":[[176,1],[178,3]]},"2310":{"position":[[75,1],[77,3]]},"2314":{"position":[[16,2]]},"2320":{"position":[[17,3]]},"2322":{"position":[[17,3]]},"2328":{"position":[[140,1]]},"2335":{"position":[[85,1],[87,3]]},"2339":{"position":[[261,1],[263,3],[423,1],[791,1],[1054,1],[1145,1],[1260,2]]},"2345":{"position":[[209,1],[1402,1],[1505,1],[1578,1],[1653,1],[1723,1],[1741,1],[1788,1],[2057,1],[2094,1],[2169,1],[2209,1],[2287,1],[2326,1],[2410,1],[2465,1],[2504,1],[2559,1],[2681,1],[2703,1],[2776,1],[2811,1],[2886,1],[2946,1],[3025,1],[3120,1],[3150,1],[3248,2],[3417,1],[3526,1],[3635,1],[3694,1],[3811,1],[3851,1],[4013,1],[4053,1],[4225,1],[4265,1],[4505,1],[5154,1]]},"2349":{"position":[[10,1]]},"2351":{"position":[[148,1],[209,1],[583,1],[836,1],[875,2],[878,1],[891,1],[967,3],[997,2],[1010,2],[1013,1],[1028,1],[1087,1],[1108,2],[1122,2],[1125,1],[1148,2],[1162,2],[1165,1],[1180,3],[1201,1],[1263,1],[1330,1],[1386,1],[1388,3],[1456,1]]},"2353":{"position":[[158,1],[178,1]]},"2355":{"position":[[99,1],[482,1],[1177,1],[1247,1],[1314,1],[1382,1],[1458,1],[1482,1],[1544,1],[1796,1],[1839,1],[1922,1],[1963,1],[2044,1],[2066,1],[2151,1],[2198,1],[2204,1],[2317,1],[2405,1],[2439,1],[2517,1],[2553,1],[2555,1],[2576,1],[2656,1],[2689,1],[2691,1],[2712,1],[2794,1],[2829,1],[2831,1],[2876,1],[2882,1],[2991,1],[3024,2],[3067,2],[3074,1],[3147,1],[3283,1],[3394,1],[3485,1],[3509,2],[3516,1],[3853,1],[3895,1],[4083,1],[4125,1],[4704,1]]},"2359":{"position":[[167,1],[759,1],[834,1],[902,1],[1015,1],[1133,1],[1207,1],[1323,1],[1373,1],[1412,1],[1467,1],[1576,1],[1619,1],[1702,1],[1738,1],[1744,1],[1857,1],[1946,1],[1984,1],[2014,1],[2125,1],[2159,1],[2239,1],[2278,1],[2280,1],[2325,1],[2331,1],[2440,1],[2488,2],[2614,2],[2657,2],[2664,1],[2737,1],[2783,1],[2807,2],[2814,1],[2840,1],[2864,2],[2871,1],[2965,1]]},"2361":{"position":[[416,1],[472,1]]},"2365":{"position":[[12,1]]},"2367":{"position":[[165,1],[203,1],[575,1],[818,1],[857,2],[860,1],[873,1],[949,3],[979,2],[992,2],[995,1],[1010,1],[1069,1],[1090,2],[1104,2],[1107,1],[1130,2],[1144,2],[1147,1],[1162,3],[1183,1],[1258,1],[1296,1],[1363,1],[1365,2],[1558,1]]},"2369":{"position":[[515,1],[618,1],[620,2],[693,2],[768,2],[889,1],[891,2],[961,2],[1038,2],[1207,1],[1209,2],[1281,2],[1462,1],[1464,2],[1731,1]]},"2373":{"position":[[111,2],[237,1]]},"2377":{"position":[[321,1],[351,1]]},"2379":{"position":[[195,1],[355,1],[545,1],[729,1]]},"2381":{"position":[[214,1],[311,2],[436,1]]},"2383":{"position":[[371,1]]},"2387":{"position":[[327,1],[474,1],[544,1]]},"2392":{"position":[[88,1],[90,3]]},"2406":{"position":[[31,3]]},"2409":{"position":[[80,1],[82,3]]},"2413":{"position":[[70,1],[300,1],[460,1],[883,1],[1133,5],[1388,3],[1517,1]]},"2424":{"position":[[301,1],[303,3]]},"2426":{"position":[[260,1],[262,3]]},"2430":{"position":[[497,1]]},"2434":{"position":[[478,1],[1153,1],[1188,1],[1219,1],[1469,1],[1619,1],[1621,1],[1623,1],[1711,2],[1714,1],[1814,1],[1908,1]]},"2436":{"position":[[39,1],[141,1],[253,1],[320,2],[387,1],[475,1],[656,1],[695,1],[779,1],[1034,1],[1088,2],[1221,1],[1476,1],[1530,2]]},"2440":{"position":[[552,1]]},"2442":{"position":[[384,1],[462,1]]},"2452":{"position":[[339,1],[678,1],[942,1]]},"2454":{"position":[[250,1],[454,1],[972,1]]},"2456":{"position":[[265,1],[621,1],[712,1],[790,1]]},"2474":{"position":[[670,1],[1087,1]]},"2476":{"position":[[75,2],[254,2]]},"2480":{"position":[[349,1]]},"2482":{"position":[[506,1],[1690,1],[1923,1],[2037,1],[2237,1]]},"2484":{"position":[[155,1],[322,1]]},"2500":{"position":[[264,1]]},"2502":{"position":[[34,1],[335,1],[447,1]]},"2504":{"position":[[897,1]]},"2506":{"position":[[887,1],[1148,1]]},"2510":{"position":[[71,1],[284,1]]},"2518":{"position":[[190,1],[1234,1],[1369,1]]},"2520":{"position":[[186,1]]},"2522":{"position":[[268,1],[298,1]]},"2524":{"position":[[195,1],[355,1],[545,1],[729,1]]},"2526":{"position":[[214,1],[311,2],[436,1]]},"2530":{"position":[[884,1],[1042,1],[1699,1],[1774,1],[1923,1],[2041,1],[2736,1]]},"2532":{"position":[[1215,1],[1710,1]]},"2540":{"position":[[544,1]]},"2542":{"position":[[151,1]]},"2546":{"position":[[982,1]]},"2548":{"position":[[213,1]]},"2552":{"position":[[375,1]]},"2556":{"position":[[140,1],[371,1]]},"2560":{"position":[[714,1]]},"2564":{"position":[[81,1]]},"2566":{"position":[[31,1]]},"2570":{"position":[[198,1]]},"2574":{"position":[[232,1]]},"2590":{"position":[[365,1]]},"2598":{"position":[[27,1]]},"2604":{"position":[[86,1]]},"2606":{"position":[[242,1]]},"2614":{"position":[[842,1]]},"2618":{"position":[[39,1]]},"2630":{"position":[[212,2],[236,2],[262,2],[289,2],[315,2],[374,1],[581,2],[621,2],[872,1],[1102,1]]},"2636":{"position":[[345,1]]},"2638":{"position":[[906,1],[1085,1],[1176,1]]},"2642":{"position":[[352,1],[692,1],[788,1]]},"2646":{"position":[[147,1]]},"2648":{"position":[[193,1]]},"2652":{"position":[[520,2],[544,2],[571,2],[599,2],[625,2],[853,1]]},"2654":{"position":[[73,1]]},"2656":{"position":[[197,1],[424,1]]},"2662":{"position":[[20,2],[806,1],[1057,3],[1340,1],[1606,3],[2345,1],[2400,1],[2461,1],[2480,3],[2527,1],[2618,1],[2705,1],[2788,1],[2866,1],[2888,1],[2921,1],[2975,1],[3068,1],[3139,1],[4136,1],[4343,1],[5090,1],[5092,3],[5221,1],[6033,1],[6266,1],[6518,1],[6965,1],[7026,1],[7197,1],[7925,1],[8515,1],[8798,1],[9500,1],[9542,1],[9590,1],[9667,1],[10003,1],[10064,1],[10081,1],[10137,1],[10173,1],[10594,1]]},"2666":{"position":[[214,1],[224,1],[226,1],[278,1],[280,2]]},"2668":{"position":[[184,1],[356,1],[366,1],[368,1],[409,2],[412,1],[453,2],[456,2],[605,1],[782,1],[912,1],[922,1],[924,1],[976,2],[979,1],[1037,2],[1040,2],[1246,1],[1628,1],[1638,1],[1640,1],[1681,2],[1684,1],[1749,2],[1752,2],[1897,1]]},"2670":{"position":[[17,1],[283,1]]},"2672":{"position":[[154,1],[625,1],[766,1]]},"2674":{"position":[[143,1],[153,1],[155,1],[219,1],[221,2]]},"2676":{"position":[[10,1],[20,1],[22,1],[90,1],[92,2]]},"2678":{"position":[[83,1],[93,1],[95,1],[163,1],[165,1],[209,1],[211,2]]},"2680":{"position":[[53,1],[63,1],[65,1],[79,6],[145,1],[147,2],[254,2]]},"2682":{"position":[[395,1],[503,1]]},"2684":{"position":[[17,1],[298,1]]},"2686":{"position":[[251,1],[444,1]]},"2690":{"position":[[364,1],[380,1],[382,1],[442,1],[444,2],[836,1],[858,1],[1040,3]]},"2692":{"position":[[1154,1],[1156,3],[1188,2],[1520,1],[1536,1],[1538,1],[1602,2],[1617,2],[1631,2],[1666,1],[1668,2]]},"2702":{"position":[[101,1],[103,3],[131,1],[168,1],[265,2],[516,1],[554,1],[556,2],[2618,1]]},"2704":{"position":[[364,1],[501,1],[546,1],[553,100],[1063,1],[1152,100]]},"2706":{"position":[[418,1],[480,1],[525,1],[532,255],[1097,1],[1188,100]]},"2708":{"position":[[491,1],[627,1],[672,1],[679,255],[1471,1],[1559,255]]},"2710":{"position":[[440,1],[502,1],[547,1],[554,255],[1294,1],[1385,255]]},"2712":{"position":[[0,2],[640,1],[702,1],[747,1],[754,255],[1614,1],[1702,255]]},"2714":{"position":[[121,2],[131,2],[141,2],[151,2],[161,13],[368,1],[383,1],[414,12],[523,1],[538,1],[561,1],[593,12],[814,1],[946,1],[951,1],[988,2],[1047,20],[1222,1],[1245,2],[1348,1],[1359,2],[1365,1],[1375,2],[1380,1],[1390,2],[1395,1],[1405,2],[1410,1],[1420,2],[1425,1],[1435,2],[1440,1],[1450,2],[1455,1],[1465,2],[1470,1],[1480,2],[1485,1],[1495,23],[1657,1],[1703,1],[1730,12]]},"2716":{"position":[[307,1],[309,3],[337,1],[374,1],[399,2],[461,3],[602,1],[627,1],[629,2],[944,2],[954,1],[1159,1],[1166,1],[1179,1],[1181,1],[1183,1],[1187,1],[1191,1],[1200,1],[1222,2],[1262,1],[1269,1],[1279,1],[1281,1],[1283,1],[1287,1],[1291,1],[1295,1],[1317,2],[1357,1],[1364,1],[1376,1],[1390,1],[1392,1],[1396,1],[1400,1],[1408,1],[1430,2],[1470,1],[1477,1],[1488,1],[1502,1],[1504,1],[1508,1],[1512,1],[1519,1],[1541,2],[1581,1],[1588,1],[1600,1],[1608,1],[1610,1],[1614,1],[1618,1],[1625,1],[1647,2],[1687,1],[1694,1],[1706,1],[1714,1],[1716,1],[1720,1],[1724,1],[1730,1],[1752,2],[1792,1],[1799,1],[1811,1],[1819,1],[1821,1],[1825,1],[1829,1],[1835,1],[1857,139]]},"2722":{"position":[[1047,1],[1049,3],[1169,1],[1171,1],[1255,1],[1257,2],[1479,1],[1491,1],[1566,2],[1638,1],[2050,1],[2582,1],[2730,1]]},"2726":{"position":[[27,1],[29,2],[51,3],[81,2],[131,1],[133,1],[158,2],[191,3],[195,2],[198,1],[233,2],[266,3],[270,1],[272,2],[313,1],[412,2],[419,1],[477,1],[585,1],[1207,2],[1251,1]]},"2728":{"position":[[96,1],[200,1],[778,2],[955,1]]},"2730":{"position":[[0,1],[179,1],[198,1],[222,1],[240,1],[399,1],[429,1],[448,1],[464,1],[612,1],[639,1],[662,1]]},"2736":{"position":[[603,1],[610,1],[797,1],[799,2],[822,3]]},"2744":{"position":[[228,1],[347,1],[381,1],[385,1],[473,1],[485,1],[521,2],[561,1],[573,1],[661,2],[1016,1]]},"2746":{"position":[[48,1],[50,3],[80,1],[133,1],[135,1]]},"2756":{"position":[[433,1]]},"2758":{"position":[[55,1]]},"2766":{"position":[[381,1],[383,3]]},"2770":{"position":[[68,1],[159,1],[1156,1],[1190,1],[1207,1],[1233,1],[1285,1],[1297,1],[1314,1],[1356,1],[1433,2],[1476,1],[1553,1],[1555,1],[1557,2]]},"2774":{"position":[[554,1],[556,3]]},"2778":{"position":[[85,2],[192,2],[289,3],[384,2],[481,2],[565,2],[633,1],[691,2],[801,2],[898,2],[994,2],[1085,2],[1171,2],[1234,2],[1312,2],[1398,2],[1480,2]]},"2780":{"position":[[125,1],[980,1]]},"2806":{"position":[[504,1],[1281,1],[1283,3]]},"2814":{"position":[[285,1],[557,1],[667,1]]},"2818":{"position":[[78,1],[80,3],[100,1],[167,2],[196,1],[263,2],[1032,1]]},"2820":{"position":[[131,1],[133,3],[149,1],[211,2],[230,1],[265,2],[294,1],[329,2]]},"2823":{"position":[[464,1],[498,1],[515,1],[574,1]]},"2826":{"position":[[341,1],[375,1],[392,1],[474,1]]},"2830":{"position":[[105,1],[492,1],[893,1],[895,3],[928,1],[958,1],[971,1],[973,1],[1004,2],[1007,1],[1009,2],[1021,1],[1034,1],[1036,1],[1067,2],[1070,1],[1102,2],[1105,1],[1107,2],[1121,1],[1134,1],[1136,1],[1166,2],[1169,1],[1171,2],[1181,1],[1194,1],[1196,1],[1227,1],[1229,2],[1251,1],[1253,1],[1298,1],[1300,1],[1331,1],[1333,1],[1335,1],[1337,1],[1339,1],[1341,2],[1439,1],[1575,1],[1827,1]]},"2832":{"position":[[380,1],[794,1],[796,3],[827,1],[857,1],[870,1],[872,1],[903,2],[906,1],[908,2],[922,1],[935,1],[937,1],[967,1],[969,1],[971,2],[981,1],[994,1],[996,1],[1027,1],[1029,2],[1051,1],[1053,1],[1098,1],[1100,1],[1131,1],[1133,1],[1135,1],[1137,1],[1139,1],[1141,2]]},"2834":{"position":[[496,1],[751,1],[1234,1],[1236,3],[1273,1],[1338,1],[1351,1],[1353,1],[1384,2],[1387,1],[1389,2],[1403,1],[1416,1],[1418,1],[1448,1],[1450,1],[1452,2],[1462,1],[1475,1],[1477,1],[1508,1],[1510,2],[1532,1],[1534,1],[1579,1],[1581,1],[1612,1],[1614,1],[1616,1],[1618,1],[1620,1],[1622,2],[2021,1],[2023,3],[2114,1],[2164,3],[2168,2]]},"2836":{"position":[[1063,1],[1282,1],[1417,1],[1419,3],[1446,1],[1474,1],[1486,1],[1488,1],[1490,1],[1521,1],[1523,1],[1525,1],[1527,2]]},"2844":{"position":[[421,1]]},"2846":{"position":[[539,1],[834,1],[871,1],[1116,1],[1329,1]]},"2852":{"position":[[227,1]]},"2854":{"position":[[73,1]]},"2856":{"position":[[535,1],[537,3],[553,1],[615,2],[640,1],[780,2],[848,1]]},"2858":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[242,2]]},"2860":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[402,2]]},"2864":{"position":[[190,1],[192,3],[208,1],[270,2],[295,1],[325,2],[351,2],[460,1]]},"2887":{"position":[[748,1]]},"2895":{"position":[[263,1],[534,1],[643,1]]},"2899":{"position":[[76,1],[78,3],[96,1],[163,2],[890,1]]},"2901":{"position":[[129,1],[131,3],[147,1],[209,2],[226,1],[261,2]]},"2904":{"position":[[56,1],[90,1],[107,1],[160,1]]},"2906":{"position":[[56,1],[90,1],[107,1],[135,1]]},"2910":{"position":[[32,1],[97,3],[101,1],[185,3]]},"2912":{"position":[[106,1],[108,4]]},"2914":{"position":[[157,1],[525,1],[527,4]]},"2918":{"position":[[290,4]]},"2920":{"position":[[290,1],[324,1],[341,1],[370,1]]},"2922":{"position":[[197,1],[231,1],[248,1],[277,1],[358,1],[384,1],[440,1],[442,1],[444,2],[543,1],[569,1],[583,1],[585,1],[587,2]]},"2924":{"position":[[155,1],[189,1],[206,1],[235,1]]},"2926":{"position":[[49,1],[51,3],[70,1],[123,2],[518,1],[520,3],[593,1],[643,2],[952,1],[954,3],[973,1],[975,3],[1004,2]]},"2930":{"position":[[905,2],[1261,1],[1417,1],[1938,1]]},"2932":{"position":[[332,1]]},"2956":{"position":[[154,1]]},"2958":{"position":[[150,1]]},"2966":{"position":[[544,1],[546,3],[558,1],[572,1],[617,2],[632,1],[677,1],[679,2],[1431,1]]},"2972":{"position":[[360,1],[411,1],[1236,1],[1325,1]]},"2976":{"position":[[30,1],[100,1],[141,1],[156,1],[203,1],[225,1],[262,2],[291,2],[771,1],[832,1]]},"2978":{"position":[[88,1],[128,1],[141,1],[223,1],[263,1],[278,1],[325,1],[347,1],[384,2],[413,2]]},"2980":{"position":[[55,1],[114,1],[196,1],[236,1],[251,1],[298,1],[320,1],[393,2],[422,2]]},"2986":{"position":[[697,1],[937,2],[940,3]]},"2992":{"position":[[145,1]]},"3012":{"position":[[567,1],[701,1],[703,3],[719,1],[733,1],[750,1],[752,2]]},"3014":{"position":[[173,1],[236,1],[249,1],[331,1],[371,1],[386,1],[433,1],[455,1],[513,2],[542,2]]},"3018":{"position":[[357,1],[689,1]]},"3050":{"position":[[403,1]]},"3060":{"position":[[624,1],[1059,1]]},"3062":{"position":[[240,1],[242,1],[287,1],[289,1],[329,1],[331,1],[372,1],[374,1],[415,1],[417,1],[451,1],[453,1]]},"3064":{"position":[[0,1],[2,1]]},"3066":{"position":[[0,1]]},"3068":{"position":[[121,1],[167,1],[198,1],[562,2]]},"3070":{"position":[[877,1]]},"3072":{"position":[[884,2],[1014,1],[1132,1],[1171,1],[1450,1],[1604,1],[1606,1],[1667,2],[1670,1],[1690,1],[1692,2],[2306,1],[2409,1]]},"3076":{"position":[[34,1],[621,1],[655,1],[672,1],[709,1],[757,1],[771,1],[813,1],[911,2],[954,1],[1052,1],[1094,1],[1191,1],[1193,1],[1195,2]]},"3078":{"position":[[36,1],[239,1],[983,1]]},"3080":{"position":[[118,1],[306,1]]},"3082":{"position":[[34,1],[450,1],[552,1]]},"3084":{"position":[[35,1],[327,1],[380,1],[557,1],[559,3],[802,1],[836,1],[853,1],[904,1],[951,1],[969,1],[971,1],[981,1],[996,2],[1011,2],[1014,1],[1024,1],[1039,2],[1054,1],[1056,2],[1088,2]]},"3086":{"position":[[28,1],[306,1]]},"3088":{"position":[[43,1],[988,1]]},"3090":{"position":[[40,1],[743,1]]},"3092":{"position":[[52,1],[313,1]]},"3094":{"position":[[55,1]]},"3096":{"position":[[348,1],[889,1]]},"3098":{"position":[[50,1],[179,1],[321,1]]},"3100":{"position":[[53,1]]},"3102":{"position":[[54,1]]},"3104":{"position":[[50,1]]},"3106":{"position":[[53,1]]},"3108":{"position":[[55,1]]},"3110":{"position":[[51,1]]},"3112":{"position":[[54,1]]},"3116":{"position":[[31,3],[35,1],[616,1],[632,1],[634,1],[715,1],[717,2]]},"3118":{"position":[[41,1]]},"3120":{"position":[[39,1]]},"3122":{"position":[[43,1]]},"3126":{"position":[[38,3],[42,1]]},"3128":{"position":[[36,3],[40,1]]},"3130":{"position":[[40,3],[44,1]]},"3132":{"position":[[45,3],[49,1]]},"3134":{"position":[[306,1],[814,1],[1059,1],[1146,1],[1148,1],[1552,1],[1554,2]]},"3140":{"position":[[198,1]]},"3142":{"position":[[154,1]]},"3144":{"position":[[362,1]]},"3146":{"position":[[423,1]]},"3148":{"position":[[219,1]]},"3150":{"position":[[227,1]]},"3156":{"position":[[238,1]]},"3158":{"position":[[213,1],[1100,3]]},"3164":{"position":[[70,1]]},"3166":{"position":[[150,1],[199,1],[236,1],[269,1]]},"3172":{"position":[[683,1],[771,1],[839,1],[881,1],[908,1],[1181,1],[1183,4]]},"3174":{"position":[[73,2]]},"3182":{"position":[[263,1]]},"3186":{"position":[[307,1],[402,1],[432,1]]},"3188":{"position":[[31,1]]},"3194":{"position":[[149,1],[886,1]]},"3198":{"position":[[17,2],[793,1],[795,3],[886,1]]},"3202":{"position":[[177,1]]},"3208":{"position":[[955,1],[1139,1]]},"3212":{"position":[[140,1],[142,3],[210,1]]},"3218":{"position":[[236,3],[1038,1],[1040,3]]},"3222":{"position":[[136,3]]},"3229":{"position":[[125,1]]},"3235":{"position":[[396,2],[421,2],[438,2],[456,2]]},"3239":{"position":[[362,1],[714,1]]},"3245":{"position":[[417,1],[431,1],[448,1]]},"3247":{"position":[[519,1],[543,1]]},"3259":{"position":[[1366,2],[1389,2],[1454,2],[1569,1],[1590,1],[1693,1],[1702,2],[1709,1],[1756,1],[1769,2],[1805,1],[1831,5],[1858,1],[1932,1],[1934,1],[1971,1],[1979,1],[1981,2],[2035,1],[2111,2]]},"3261":{"position":[[1500,1],[2855,1]]},"3267":{"position":[[304,1]]},"3271":{"position":[[159,2],[208,2],[345,1],[440,1],[1313,1]]},"3273":{"position":[[305,1],[329,1],[393,2],[396,1],[562,2],[565,1],[730,2],[733,1],[936,2],[939,1]]},"3275":{"position":[[1235,1],[1401,1]]},"3277":{"position":[[242,3],[379,3],[454,1],[526,2],[777,1],[779,3]]},"3279":{"position":[[290,1],[323,1],[396,2],[399,1],[583,2],[586,1],[769,2],[772,1],[993,2],[996,1]]},"3283":{"position":[[565,1],[567,3],[607,1],[645,2],[721,1],[723,3],[763,1],[807,2]]},"3285":{"position":[[218,1],[220,3],[249,1],[301,2],[361,1],[681,1],[683,3],[712,1],[815,2]]},"3287":{"position":[[309,1],[425,1]]},"3289":{"position":[[1536,2],[1651,1],[1653,3]]},"3291":{"position":[[581,3],[644,3]]},"3293":{"position":[[112,1],[431,1],[437,1],[479,1]]},"3299":{"position":[[86,1],[88,3]]},"3301":{"position":[[62,1],[64,3]]},"3303":{"position":[[147,1]]},"3307":{"position":[[453,1]]},"3309":{"position":[[20,1],[22,1],[167,1],[186,2],[204,2],[264,5],[312,1],[671,1],[734,1],[802,1],[980,1],[1302,1],[1313,1],[1315,1],[1347,1],[1396,1],[1408,1],[1438,2]]},"3311":{"position":[[20,1],[22,1],[167,1],[186,2],[203,1],[205,1],[207,3],[260,1],[485,1],[519,1],[876,2]]},"3313":{"position":[[71,1]]},"3318":{"position":[[86,1],[88,3]]},"3320":{"position":[[62,1],[64,3]]},"3322":{"position":[[147,1]]},"3324":{"position":[[4867,1],[5358,1]]},"3327":{"position":[[117,1],[119,3],[553,1],[586,1],[603,1],[618,1]]},"3333":{"position":[[140,1],[156,2],[199,3],[342,1],[379,1],[396,1],[434,1],[695,1],[732,1],[749,1],[787,1]]},"3335":{"position":[[274,1],[450,1],[532,1],[679,1]]},"3337":{"position":[[255,1],[271,2],[365,3],[436,1],[524,1]]},"3345":{"position":[[315,1],[1232,1],[1412,1]]},"3347":{"position":[[510,1],[919,1],[1894,1],[1945,1],[2003,1],[2099,1],[2274,1],[2439,1],[2721,1],[4046,1]]},"3349":{"position":[[496,1],[498,3],[971,1],[973,3],[1092,1],[1094,1],[1146,1],[1148,2],[1447,1],[1449,3],[1453,2],[1621,4],[1825,1],[1934,1],[2038,1],[2453,1],[2571,1],[2751,1],[2761,2],[2853,1],[2934,3],[2940,1],[3027,2],[3030,2],[3082,1],[3091,1],[3098,1],[3160,1],[3178,1],[3199,1],[3205,2],[3248,2],[3251,1],[3269,1],[3271,6],[3280,1],[3355,1],[3364,2],[3397,2],[3513,1],[4455,1],[4591,1],[4652,1],[4716,1],[4789,1],[4834,1]]},"3351":{"position":[[536,1],[765,1],[767,3],[886,1],[888,1],[986,1],[988,2],[1085,1],[1087,3],[1091,2],[1269,4],[1442,1],[1733,1],[1743,2],[1816,1],[1896,3],[1900,2],[1958,2],[2041,1],[2128,2],[2131,2],[2195,1],[2204,1],[2225,2],[2317,2],[2350,1],[2411,2]]},"3353":{"position":[[219,1],[221,3],[265,1],[267,1],[346,2],[349,1],[428,1],[430,2],[447,1],[449,1],[508,2],[511,1],[570,1],[572,2]]},"3361":{"position":[[837,3],[841,1],[999,1],[1075,1]]},"3363":{"position":[[691,1],[831,1],[833,1],[872,1],[874,2],[952,1]]},"3371":{"position":[[342,1],[344,3],[370,1],[470,2],[960,1],[962,3],[995,1],[1031,2]]},"3375":{"position":[[54,1],[56,3],[161,1],[1972,1],[5295,1],[5320,1],[5335,1],[5337,2],[5401,1],[5481,2],[5553,2],[6274,1],[6290,1],[6331,2],[6334,1]]},"3377":{"position":[[54,1],[56,3],[161,1],[456,1]]},"3379":{"position":[[53,1],[55,3],[415,1]]},"3381":{"position":[[53,1],[55,3],[166,1],[1127,1],[1129,3],[1299,1],[1301,3],[1426,2],[1468,3],[1530,1],[1748,3],[3789,1],[4402,1],[4413,1],[4459,2],[4462,1]]},"3383":{"position":[[53,1],[55,3],[160,1],[1184,1],[1186,3],[1365,1],[1367,3],[1486,2],[1543,3],[1742,1],[1981,3]]},"3385":{"position":[[54,1],[56,3],[173,1],[1418,1],[1420,3],[1563,1],[1565,3],[1657,2],[1701,3],[1804,1]]},"3387":{"position":[[94,1],[105,1],[147,2]]},"3389":{"position":[[122,1],[138,1],[184,2],[378,1]]},"3391":{"position":[[309,1],[732,1],[734,3],[848,1],[850,3],[967,1],[969,3],[1097,1],[1099,3],[1199,1],[1201,1],[1258,1],[1260,2],[1332,1],[1334,3]]},"3393":{"position":[[101,3],[268,2],[373,2]]},"3399":{"position":[[625,1],[829,1],[831,3],[1150,1],[1239,1],[1278,1]]},"3401":{"position":[[887,1]]},"3403":{"position":[[252,1],[254,3]]},"3405":{"position":[[252,1],[254,3],[298,1],[300,1],[424,2],[427,1],[523,2],[526,1],[607,2],[610,1],[687,2],[690,1],[759,2],[762,1],[843,2],[846,1],[904,1],[906,1],[960,1],[962,2],[1210,2]]},"3407":{"position":[[171,1],[173,3],[217,6],[479,1],[481,3],[525,6]]},"3409":{"position":[[337,1],[339,3],[383,6],[404,1],[406,1],[511,2],[514,1],[619,1],[621,2],[1152,1]]},"3411":{"position":[[87,1],[89,3],[133,6],[154,6],[179,1],[181,1],[228,2],[231,1],[277,1],[279,2],[498,1],[659,2],[695,1],[852,1]]},"3415":{"position":[[152,1],[154,3]]},"3417":{"position":[[87,1],[89,3]]},"3425":{"position":[[331,1],[348,1],[412,1]]},"3427":{"position":[[129,1],[131,3],[701,1]]},"3431":{"position":[[257,1],[274,1],[332,1],[433,1],[445,3],[590,1],[611,1],[653,1],[681,1],[746,1],[1057,1],[1074,1],[1140,1],[1270,1],[1281,1],[1329,2]]},"3433":{"position":[[134,1],[151,1],[224,1]]},"3435":{"position":[[1422,1]]},"3443":{"position":[[287,1],[304,1],[335,1],[390,1],[402,1],[416,1],[458,1],[523,2],[566,1],[631,1],[633,1],[635,2]]},"3445":{"position":[[169,1],[186,1],[217,1],[278,1],[290,1],[325,2]]},"3447":{"position":[[502,1],[519,1],[562,1],[616,1],[628,1],[676,1],[678,1],[688,1],[706,2],[721,2],[724,1],[734,1],[752,2],[767,1],[769,1],[771,2]]},"3451":{"position":[[114,1],[131,1],[140,4],[145,1]]},"3453":{"position":[[89,1],[101,1],[112,1],[114,1],[285,2],[288,1],[290,1],[292,2]]},"3455":{"position":[[335,1],[352,1],[417,5],[466,7],[474,1],[526,1],[607,2]]},"3459":{"position":[[618,1],[620,3],[777,1]]},"3461":{"position":[[132,2],[460,1],[507,1],[554,1],[616,1],[694,1]]},"3463":{"position":[[545,2],[677,1],[769,1],[781,2],[841,2],[848,1],[867,1],[895,2],[940,1],[952,2],[1087,2],[1097,2],[1104,1],[1151,1],[1158,1],[1179,2],[1186,1],[1198,2],[1280,1],[1287,1],[1327,1],[1329,1],[1355,2]]},"3465":{"position":[[237,1],[337,1],[449,1],[502,1],[504,1],[513,2],[569,1],[596,1],[608,2],[712,2],[719,1],[738,1],[766,2],[808,1],[820,2],[946,2],[956,2],[963,1],[1010,1],[1017,1],[1038,2],[1045,1],[1057,2],[1139,1],[1146,1],[1186,1],[1188,1],[1214,2]]},"3467":{"position":[[903,1],[910,4],[915,1],[1071,1],[1111,2],[1264,1],[1271,4],[1276,1]]},"3469":{"position":[[40,1],[58,1],[85,2],[93,1],[193,2],[206,1],[295,2],[308,1],[386,2],[394,1],[477,2],[485,1],[532,2],[582,2],[708,2],[720,2]]},"3471":{"position":[[47,1],[65,1],[92,2],[172,2],[257,2],[336,2],[392,2],[435,2],[485,2],[595,2]]},"3476":{"position":[[173,1],[175,3]]},"3482":{"position":[[415,1]]},"3484":{"position":[[829,1],[852,1]]},"3490":{"position":[[69,1],[215,1],[260,1]]},"3496":{"position":[[57,1],[59,1],[61,1],[63,1],[65,1],[90,1],[92,1],[94,1],[96,1],[98,1],[143,1],[145,1],[147,1],[149,1],[151,1],[174,1],[176,1],[178,1],[180,1],[182,1],[198,1],[200,1],[202,1],[204,1],[206,1],[225,1],[227,1],[229,1],[231,1],[233,1],[248,1],[250,1],[252,1],[254,1],[256,1],[268,1],[270,1],[272,1],[274,1],[276,1],[303,1],[305,1],[307,1],[309,1],[311,1],[350,1],[352,1],[354,1],[356,1],[358,1],[386,1],[388,1],[390,1],[392,1],[394,1],[409,1],[445,1]]},"3498":{"position":[[59,1],[61,1],[63,1],[65,1],[67,1],[98,1],[100,1],[102,1],[104,1],[106,1],[153,1],[155,1],[157,1],[159,1],[161,1],[190,1],[192,1],[194,1],[196,1],[198,1],[227,1],[229,1],[231,1],[233,1],[235,1],[263,1],[265,1],[267,1],[269,1],[271,1],[300,1],[302,1],[304,1],[306,1],[308,1],[348,1],[350,1],[352,1],[354,1],[356,1],[387,1],[389,1],[391,1],[393,1],[395,1],[422,1]]},"3502":{"position":[[70,1]]},"3504":{"position":[[646,1]]},"3506":{"position":[[42,1]]},"3518":{"position":[[176,1],[178,3]]},"3524":{"position":[[268,1],[285,1],[306,1],[321,1],[338,1],[367,1],[388,1],[413,1],[434,1],[462,1],[484,1],[1160,1],[1173,1],[1194,1],[1213,1],[1243,1]]},"3531":{"position":[[75,1],[77,3]]},"3535":{"position":[[16,2]]},"3541":{"position":[[17,3]]},"3543":{"position":[[17,3]]},"3549":{"position":[[139,1],[473,1]]},"3551":{"position":[[802,3],[1186,1],[1586,1],[1717,1]]},"3555":{"position":[[646,1],[670,1],[672,3],[702,1],[736,1],[789,1],[791,1],[1065,1],[1817,1],[1833,1],[1835,3],[1860,1],[1912,1],[1952,1],[1978,1],[1990,1],[2004,1],[2058,1],[2060,1]]},"3561":{"position":[[1709,1]]},"3573":{"position":[[31,3]]},"3576":{"position":[[80,1],[82,3]]},"3580":{"position":[[70,1],[300,1],[460,1],[883,1],[1133,5],[1388,3],[1517,1]]},"3584":{"position":[[140,1]]},"3591":{"position":[[85,1],[87,3]]},"3595":{"position":[[261,1],[263,3],[423,1],[791,1],[1062,1],[1120,1],[1235,2]]},"3601":{"position":[[327,1],[474,1],[544,1]]},"3606":{"position":[[88,1],[90,3]]},"3612":{"position":[[505,1],[1180,1],[1215,1],[1246,1],[1496,1],[1646,1],[1648,1],[1650,1],[1738,2],[1741,1],[1841,1],[1935,1],[2252,2],[2255,1],[2281,3]]},"3619":{"position":[[301,1],[303,3]]},"3621":{"position":[[260,1],[262,3]]},"3625":{"position":[[497,1]]},"3629":{"position":[[413,3],[619,2],[704,1],[817,4],[1785,1]]},"3633":{"position":[[209,1],[1402,1],[1505,1],[1578,1],[1653,1],[1723,1],[1741,1],[1788,1],[2057,1],[2094,1],[2169,1],[2209,1],[2287,1],[2326,1],[2410,1],[2465,1],[2504,1],[2559,1],[2681,1],[2703,1],[2776,1],[2811,1],[2886,1],[2946,1],[3025,1],[3120,1],[3150,1],[3248,2],[3417,1],[3526,1],[3635,1],[3694,1],[3811,1],[3851,1],[4013,1],[4053,1],[4225,1],[4265,1],[4505,1],[5154,1]]},"3637":{"position":[[10,1]]},"3639":{"position":[[148,1],[209,1],[678,1],[695,1],[704,3],[708,2],[767,1],[769,2],[814,1],[895,1],[912,3],[921,1],[923,2],[984,2],[1058,1],[1060,2],[1187,1],[1200,1],[1252,1],[1314,1],[1356,2],[1408,3],[1476,1]]},"3641":{"position":[[158,1],[178,1]]},"3643":{"position":[[99,1],[482,1],[1177,1],[1247,1],[1314,1],[1382,1],[1458,1],[1482,1],[1544,1],[1796,1],[1839,1],[1922,1],[1963,1],[2044,1],[2066,1],[2151,1],[2198,1],[2204,1],[2317,1],[2405,1],[2439,1],[2517,1],[2553,1],[2555,1],[2576,1],[2656,1],[2689,1],[2691,1],[2712,1],[2794,1],[2829,1],[2831,1],[2876,1],[2882,1],[2991,1],[3024,2],[3067,2],[3074,1],[3147,1],[3283,1],[3394,1],[3485,1],[3509,2],[3516,1],[3853,1],[3895,1],[4083,1],[4125,1],[4704,1]]},"3647":{"position":[[167,1],[759,1],[834,1],[902,1],[1015,1],[1133,1],[1207,1],[1323,1],[1373,1],[1412,1],[1467,1],[1576,1],[1619,1],[1702,1],[1738,1],[1744,1],[1857,1],[1946,1],[1984,1],[2014,1],[2125,1],[2159,1],[2239,1],[2278,1],[2280,1],[2325,1],[2331,1],[2440,1],[2488,2],[2614,2],[2657,2],[2664,1],[2737,1],[2783,1],[2807,2],[2814,1],[2840,1],[2864,2],[2871,1],[2965,1]]},"3649":{"position":[[416,1],[472,1]]},"3653":{"position":[[12,1]]},"3655":{"position":[[165,1],[203,1],[575,1],[819,1],[821,2],[866,1],[935,1],[1011,3],[1059,2],[1062,3],[1079,1],[1096,3],[1105,1],[1107,2],[1168,2],[1242,1],[1244,2],[1371,1],[1384,1],[1436,1],[1511,1],[1549,1],[1591,2],[1726,1]]},"3657":{"position":[[515,1],[618,1],[620,2],[693,2],[768,2],[889,1],[891,2],[961,2],[1038,2],[1207,1],[1209,2],[1281,2],[1462,1],[1464,2],[1731,1]]},"3661":{"position":[[111,2],[237,1]]},"3665":{"position":[[321,1],[351,1]]},"3667":{"position":[[195,1],[355,1],[545,1],[729,1]]},"3669":{"position":[[214,1],[311,2],[436,1]]},"3671":{"position":[[371,1]]}}}],["0",{"_index":1083,"t":{"42":{"position":[[1007,2]]},"110":{"position":[[977,1],[2444,1],[2471,1],[2491,1],[2511,1]]},"128":{"position":[[2608,2]]},"389":{"position":[[179,1]]},"473":{"position":[[1641,2],[2146,2],[2399,2],[2525,2]]},"477":{"position":[[2120,2],[4874,2],[5026,2],[5036,2],[5094,2],[5106,2],[5232,2],[5286,2],[5296,2],[5381,2],[5390,2],[5448,2],[5458,2],[5510,2],[5520,2],[5568,2],[5578,2],[5632,2],[5642,2]]},"479":{"position":[[4007,2],[4016,2],[4075,2],[4087,2],[4148,2],[4266,2],[4362,2],[4371,2],[4429,2],[4439,2],[4491,2],[4500,2],[4549,2],[4558,2],[4612,2],[4621,2]]},"481":{"position":[[4401,2],[4597,2]]},"511":{"position":[[412,2]]},"562":{"position":[[239,2]]},"612":{"position":[[1016,1],[1020,1],[1116,1],[1120,1],[1124,1],[1225,1],[1229,1],[1337,1],[1341,1],[1443,1],[1447,1],[1549,1],[1553,1],[1654,1],[1658,1]]},"712":{"position":[[31,2]]},"762":{"position":[[533,1]]},"878":{"position":[[516,4]]},"906":{"position":[[9,1]]},"912":{"position":[[9,1]]},"914":{"position":[[9,1]]},"963":{"position":[[190,2],[891,2]]},"975":{"position":[[305,1],[889,2]]},"1029":{"position":[[616,1]]},"1065":{"position":[[3740,1]]},"1075":{"position":[[219,1]]},"1123":{"position":[[459,2],[475,1]]},"1131":{"position":[[361,2],[379,2],[395,2],[455,2]]},"1166":{"position":[[406,2],[552,2],[798,4]]},"1176":{"position":[[1453,3]]},"1270":{"position":[[300,2]]},"1277":{"position":[[21,1]]},"1279":{"position":[[21,1]]},"1283":{"position":[[970,2],[1107,1]]},"1373":{"position":[[412,2]]},"1547":{"position":[[1185,1],[1189,1],[1285,1],[1289,1],[1293,1],[1394,1],[1398,1],[1506,1],[1510,1],[1612,1],[1616,1],[1718,1],[1722,1],[1823,1],[1827,1]]},"1781":{"position":[[533,1]]},"1813":{"position":[[616,1]]},"1891":{"position":[[31,2]]},"1945":{"position":[[516,4]]},"1973":{"position":[[9,1]]},"1975":{"position":[[36,1],[60,1]]},"1977":{"position":[[36,1]]},"1981":{"position":[[9,1]]},"1989":{"position":[[9,1]]},"2030":{"position":[[271,1]]},"2100":{"position":[[190,2],[891,2]]},"2146":{"position":[[3857,1]]},"2204":{"position":[[459,2],[475,1]]},"2212":{"position":[[361,2],[379,2],[395,2],[455,2]]},"2371":{"position":[[73,1],[354,1]]},"2379":{"position":[[412,2]]},"2424":{"position":[[21,1]]},"2426":{"position":[[21,1]]},"2430":{"position":[[970,2],[1107,1]]},"2524":{"position":[[412,2]]},"2716":{"position":[[1185,1],[1189,1],[1285,1],[1289,1],[1293,1],[1394,1],[1398,1],[1506,1],[1510,1],[1612,1],[1616,1],[1718,1],[1722,1],[1823,1],[1827,1]]},"2744":{"position":[[608,2]]},"2970":{"position":[[566,1]]},"3002":{"position":[[616,1]]},"3082":{"position":[[31,2]]},"3158":{"position":[[516,4]]},"3186":{"position":[[9,1]]},"3188":{"position":[[9,1],[33,1]]},"3190":{"position":[[9,1]]},"3194":{"position":[[9,1]]},"3204":{"position":[[9,1]]},"3259":{"position":[[190,2],[891,2]]},"3271":{"position":[[271,1]]},"3375":{"position":[[3886,1]]},"3381":{"position":[[3371,1]]},"3445":{"position":[[307,2],[323,1]]},"3453":{"position":[[177,2],[195,2],[211,2],[271,2]]},"3619":{"position":[[21,1]]},"3621":{"position":[[21,1]]},"3625":{"position":[[970,2],[1107,1]]},"3659":{"position":[[73,1],[354,1]]},"3667":{"position":[[412,2]]}}}],["0.0.0.0:4222[3569",{"_index":4191,"t":{"993":{"position":[[702,18]]},"2054":{"position":[[702,18]]},"3293":{"position":[[702,18]]}}}],["0.0.0.0:4433",{"_index":1653,"t":{"104":{"position":[[1024,15]]}}}],["0.3",{"_index":661,"t":{"16":{"position":[[3696,3]]}}}],["0.32",{"_index":148,"t":{"2":{"position":[[2280,4]]}}}],["0.36kb",{"_index":3352,"t":{"479":{"position":[[4203,6]]}}}],["0.39kb",{"_index":3347,"t":{"479":{"position":[[4078,6]]}}}],["0.52",{"_index":3420,"t":{"481":{"position":[[4475,5]]}}}],["0.56µ",{"_index":3334,"t":{"479":{"position":[[3654,6]]}}}],["0.59µ",{"_index":3371,"t":{"479":{"position":[[4880,8]]}}}],["0.x86_64.rpm",{"_index":3692,"t":{"596":{"position":[[188,12]]}}}],["0.x86_64.rpmsudo",{"_index":3690,"t":{"596":{"position":[[138,16]]}}}],["0083c8836529",{"_index":4760,"t":{"1495":{"position":[[5627,12]]},"2662":{"position":[[5689,12]]}}}],["0083c88365292022",{"_index":4759,"t":{"1495":{"position":[[5524,16]]},"2662":{"position":[[5586,16]]}}}],["01",{"_index":1687,"t":{"106":{"position":[[1734,2]]},"475":{"position":[[760,2]]},"981":{"position":[[379,2]]},"1495":{"position":[[4804,2]]},"2040":{"position":[[379,2]]},"2662":{"position":[[4866,2]]},"3281":{"position":[[379,2]]}}}],["01\"func",{"_index":1677,"t":{"106":{"position":[[1206,7]]}}}],["0120",{"_index":4713,"t":{"1495":{"position":[[942,4],[1491,4],[7994,4]]},"2662":{"position":[[957,4],[1506,4],[8091,4]]}}}],["02",{"_index":3168,"t":{"475":{"position":[[939,2]]},"981":{"position":[[487,2]]},"2040":{"position":[[487,2]]},"3281":{"position":[[487,2]]}}}],["02#section",{"_index":1673,"t":{"106":{"position":[[1158,10]]},"110":{"position":[[799,10]]}}}],["02.type",{"_index":1646,"t":{"104":{"position":[[616,7]]}}}],["0342fd2bb20c",{"_index":5106,"t":{"1885":{"position":[[975,14],[1043,14]]},"3076":{"position":[[939,14],[1007,14]]}}}],["0442",{"_index":4430,"t":{"1121":{"position":[[540,4],[592,4],[648,4],[700,4]]},"2202":{"position":[[583,4],[635,4],[691,4],[743,4]]},"3443":{"position":[[428,4],[480,4],[536,4],[588,4]]}}}],["051f8588020f",{"_index":4433,"t":{"1121":{"position":[[555,14],[607,14]]},"2202":{"position":[[598,14],[650,14]]},"3443":{"position":[[443,14],[495,14]]}}}],["051f858802da",{"_index":4435,"t":{"1121":{"position":[[663,14],[715,14]]},"2202":{"position":[[706,14],[758,14]]},"3443":{"position":[[551,14],[603,14]]}}}],["05:30:48",{"_index":4445,"t":{"1125":{"position":[[755,8]]},"2206":{"position":[[755,8]]}}}],["06",{"_index":4752,"t":{"1495":{"position":[[5398,2],[5541,2],[7225,2]]},"2662":{"position":[[5460,2],[5603,2],[7305,2]]}}}],["06a8236543f9",{"_index":4706,"t":{"1495":{"position":[[843,14],[1377,14],[7880,14]]},"2662":{"position":[[858,14],[1392,14],[7977,14]]}}}],["06f0",{"_index":5097,"t":{"1885":{"position":[[819,4],[887,4]]},"3076":{"position":[[783,4],[851,4]]}}}],["07",{"_index":3793,"t":{"612":{"position":[[1038,2],[1133,2],[1246,2],[1357,2],[1463,2],[1568,2],[1673,2]]},"1547":{"position":[[1207,2],[1302,2],[1415,2],[1526,2],[1632,2],[1737,2],[1842,2]]},"2716":{"position":[[1207,2],[1302,2],[1415,2],[1526,2],[1632,2],[1737,2],[1842,2]]}}}],["07:23:40",{"_index":5739,"t":{"3467":{"position":[[1048,8]]}}}],["07:23:59",{"_index":5743,"t":{"3467":{"position":[[1455,8]]}}}],["08:15:09",{"_index":3795,"t":{"612":{"position":[[1044,8],[1139,8],[1252,8],[1363,8]]},"1547":{"position":[[1213,8],[1308,8],[1421,8],[1532,8]]},"2716":{"position":[[1213,8],[1308,8],[1421,8],[1532,8]]}}}],["08:15:12",{"_index":3799,"t":{"612":{"position":[[1469,8],[1574,8],[1679,8]]},"1547":{"position":[[1638,8],[1743,8],[1848,8]]},"2716":{"position":[[1638,8],[1743,8],[1848,8]]}}}],["08:25:33",{"_index":2393,"t":{"212":{"position":[[914,8]]}}}],["09",{"_index":4748,"t":{"1495":{"position":[[4801,2]]},"2662":{"position":[[4863,2]]}}}],["0916",{"_index":4708,"t":{"1495":{"position":[[886,4],[1435,4],[1866,4],[7938,4]]},"2662":{"position":[[901,4],[1450,4],[1881,4],[8035,4]]}}}],["09:44:21",{"_index":4753,"t":{"1495":{"position":[[5404,8],[5547,8]]},"2662":{"position":[[5466,8],[5609,8]]}}}],["09:45:49",{"_index":4771,"t":{"1495":{"position":[[7231,8]]},"2662":{"position":[[7311,8]]}}}],["0_amd64.deb",{"_index":3688,"t":{"594":{"position":[[183,11]]}}}],["0_amd64.debsudo",{"_index":3686,"t":{"594":{"position":[[138,15]]}}}],["0a643d22e8a0",{"_index":4294,"t":{"1065":{"position":[[1891,14]]},"1067":{"position":[[492,14]]},"1069":{"position":[[404,14]]},"1071":{"position":[[1763,14]]},"1073":{"position":[[1778,14]]},"2146":{"position":[[2008,14]]},"2148":{"position":[[492,14]]},"2150":{"position":[[404,14]]},"2152":{"position":[[1763,14]]},"2154":{"position":[[1778,14]]},"2156":{"position":[[1867,14]]},"3375":{"position":[[2008,14]]},"3377":{"position":[[492,14]]},"3379":{"position":[[451,14]]},"3381":{"position":[[1566,14]]},"3383":{"position":[[1778,14]]},"3385":{"position":[[1840,14]]}}}],["0b6e",{"_index":5103,"t":{"1885":{"position":[[960,4],[1028,4]]},"3076":{"position":[[924,4],[992,4]]}}}],["0e08a686b727",{"_index":4711,"t":{"1495":{"position":[[901,14],[1450,14],[1881,13],[7953,14]]},"2662":{"position":[[916,14],[1465,14],[1896,13],[8050,14]]}}}],["0s",{"_index":3894,"t":{"714":{"position":[[31,3]]},"971":{"position":[[36,3]]},"991":{"position":[[711,3]]},"1893":{"position":[[31,3]]},"3084":{"position":[[31,3]]}}}],["0x10001",{"_index":1573,"t":{"98":{"position":[[1179,10]]}}}],["1",{"_index":100,"t":{"2":{"position":[[1282,1],[1448,1],[2216,1]]},"18":{"position":[[571,2]]},"52":{"position":[[716,1]]},"110":{"position":[[1005,4],[2446,1],[2453,1],[2473,1],[2493,1],[2513,2]]},"204":{"position":[[428,5],[1523,4]]},"214":{"position":[[802,2]]},"230":{"position":[[388,1]]},"238":{"position":[[470,1]]},"309":{"position":[[217,1]]},"473":{"position":[[1772,2]]},"475":{"position":[[471,1],[816,1],[995,1]]},"477":{"position":[[4929,2],[4941,2],[5220,2],[6025,1]]},"479":{"position":[[1401,1],[2445,2],[3663,2],[3733,2],[3798,2],[3854,2],[3910,2],[3922,2],[4200,2],[4212,2],[4276,2]]},"481":{"position":[[4390,2],[4459,2],[4471,2],[4522,2],[4534,2],[4585,2],[4652,2],[4664,2]]},"483":{"position":[[768,2]]},"548":{"position":[[110,1]]},"614":{"position":[[385,1],[454,1]]},"618":{"position":[[568,2],[583,1],[656,2],[741,2],[756,1],[857,1],[886,1],[1505,2],[1520,1]]},"686":{"position":[[951,1]]},"776":{"position":[[679,1]]},"864":{"position":[[207,2]]},"866":{"position":[[212,2]]},"868":{"position":[[170,1],[333,1],[415,2]]},"870":{"position":[[272,2]]},"878":{"position":[[482,5]]},"906":{"position":[[384,1]]},"912":{"position":[[551,1]]},"941":{"position":[[424,1]]},"963":{"position":[[357,2],[518,2]]},"981":{"position":[[435,1],[543,1]]},"1003":{"position":[[288,3],[546,2]]},"1109":{"position":[[462,3]]},"1164":{"position":[[741,2],[754,2],[806,2],[2879,2]]},"1166":{"position":[[393,2],[521,2],[567,2],[1077,2]]},"1168":{"position":[[182,2]]},"1178":{"position":[[421,1]]},"1270":{"position":[[310,2],[429,2]]},"1283":{"position":[[1404,1]]},"1295":{"position":[[217,1]]},"1495":{"position":[[9445,2]]},"1549":{"position":[[386,1],[455,1]]},"1615":{"position":[[181,1]]},"1676":{"position":[[1161,1],[1326,1]]},"1678":{"position":[[959,1],[1123,1]]},"1680":{"position":[[1440,1],[1604,1]]},"1737":{"position":[[933,1]]},"1795":{"position":[[857,1]]},"1893":{"position":[[1028,3],[1045,1]]},"1945":{"position":[[482,5]]},"1973":{"position":[[384,1]]},"1981":{"position":[[551,1]]},"2016":{"position":[[424,1]]},"2040":{"position":[[435,1],[543,1]]},"2062":{"position":[[251,2]]},"2064":{"position":[[207,2]]},"2066":{"position":[[170,1],[333,1],[415,2]]},"2068":{"position":[[220,1],[394,1],[476,2]]},"2070":{"position":[[272,2]]},"2072":{"position":[[280,2]]},"2100":{"position":[[357,2],[518,2]]},"2110":{"position":[[288,3],[546,2]]},"2190":{"position":[[522,3]]},"2240":{"position":[[684,2],[786,2],[1305,2]]},"2244":{"position":[[456,2]]},"2430":{"position":[[1404,1]]},"2442":{"position":[[217,1]]},"2662":{"position":[[9601,2]]},"2718":{"position":[[386,1],[455,1]]},"2740":{"position":[[181,1]]},"2750":{"position":[[290,1]]},"2830":{"position":[[1164,1],[1329,1]]},"2832":{"position":[[965,1],[1129,1]]},"2834":{"position":[[1446,1],[1610,1]]},"2926":{"position":[[933,1]]},"2984":{"position":[[857,1]]},"3084":{"position":[[992,3],[1009,1]]},"3140":{"position":[[251,2]]},"3142":{"position":[[207,2]]},"3144":{"position":[[170,1],[333,1],[415,2]]},"3146":{"position":[[220,1],[394,1],[476,2]]},"3148":{"position":[[272,2]]},"3150":{"position":[[280,2]]},"3158":{"position":[[482,5]]},"3186":{"position":[[384,1]]},"3194":{"position":[[551,1]]},"3235":{"position":[[424,1]]},"3259":{"position":[[357,2],[518,2]]},"3281":{"position":[[435,1],[543,1]]},"3311":{"position":[[288,3],[546,2]]},"3349":{"position":[[3068,1]]},"3431":{"position":[[666,3]]},"3551":{"position":[[684,2],[786,2],[1305,2]]},"3555":{"position":[[456,2]]},"3625":{"position":[[1404,1]]},"3629":{"position":[[803,2]]}}}],["1)type",{"_index":3123,"t":{"473":{"position":[[1418,6]]}}}],["1.00kb",{"_index":3272,"t":{"477":{"position":[[5223,6]]}}}],["1.08kb",{"_index":3266,"t":{"477":{"position":[[5097,6]]}}}],["1.1",{"_index":114,"t":{"2":{"position":[[1612,3]]},"134":{"position":[[882,4],[1161,4]]},"287":{"position":[[847,4],[1105,4]]},"1001":{"position":[[1098,4]]},"1003":{"position":[[782,4]]},"2108":{"position":[[1098,4]]},"2110":{"position":[[782,4]]},"3309":{"position":[[1098,4]]},"3311":{"position":[[782,4]]}}}],["1.17",{"_index":1460,"t":{"86":{"position":[[2116,4],[2290,5]]}}}],["1.25kb",{"_index":3271,"t":{"477":{"position":[[5211,6]]},"479":{"position":[[4191,6]]}}}],["1.27µ",{"_index":3423,"t":{"481":{"position":[[4525,6]]}}}],["1.3.0",{"_index":5583,"t":{"3287":{"position":[[586,5]]}}}],["1.3.13",{"_index":4202,"t":{"999":{"position":[[455,6]]},"2106":{"position":[[455,6]]},"3307":{"position":[[455,6]]}}}],["1.30kb",{"_index":3265,"t":{"477":{"position":[[5085,6]]},"479":{"position":[[4066,6]]}}}],["1.45µ",{"_index":3239,"t":{"477":{"position":[[4659,6]]},"479":{"position":[[3642,6],[3789,6],[4869,7]]},"481":{"position":[[4513,6]]}}}],["1.47µ",{"_index":3248,"t":{"477":{"position":[[4796,6],[4808,6]]},"479":{"position":[[3777,6]]}}}],["1.52µ",{"_index":3479,"t":{"483":{"position":[[895,6]]}}}],["1.7",{"_index":3369,"t":{"479":{"position":[[4785,3]]}}}],["1.88µ",{"_index":3240,"t":{"477":{"position":[[4671,6]]}}}],["10",{"_index":112,"t":{"2":{"position":[[1571,2],[2592,2]]},"42":{"position":[[1080,5]]},"70":{"position":[[830,4]]},"74":{"position":[[1060,3],[1628,2]]},"150":{"position":[[453,3],[513,3]]},"475":{"position":[[845,2],[1024,2]]},"477":{"position":[[4666,4],[4815,4]]},"479":{"position":[[3649,4]]},"511":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"531":{"position":[[228,2]]},"610":{"position":[[1083,2]]},"618":{"position":[[640,3]]},"622":{"position":[[217,2]]},"714":{"position":[[579,3]]},"732":{"position":[[265,3]]},"734":{"position":[[1301,3],[1415,3]]},"764":{"position":[[510,2],[584,2]]},"906":{"position":[[412,2],[469,2]]},"963":{"position":[[595,4],[615,3],[710,4],[730,3],[772,2],[871,3],[937,2],[1086,3],[1392,4]]},"981":{"position":[[464,2],[572,2]]},"1011":{"position":[[181,2],[371,4]]},"1166":{"position":[[686,3]]},"1172":{"position":[[458,3]]},"1373":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"1439":{"position":[[251,2]]},"1477":{"position":[[181,2]]},"1491":{"position":[[194,2]]},"1495":{"position":[[5401,2],[5544,2],[7228,2]]},"1545":{"position":[[1077,2]]},"1549":{"position":[[133,2]]},"1585":{"position":[[294,2]]},"1591":{"position":[[217,2]]},"1676":{"position":[[1221,2]]},"1678":{"position":[[1018,2]]},"1680":{"position":[[1499,2]]},"1783":{"position":[[510,2],[584,2]]},"1881":{"position":[[1276,3],[1353,3]]},"1893":{"position":[[579,3]]},"1937":{"position":[[413,3],[1200,3]]},"1973":{"position":[[412,2],[469,2]]},"1983":{"position":[[19,3]]},"2040":{"position":[[464,2],[572,2]]},"2092":{"position":[[181,2],[371,4]]},"2100":{"position":[[595,4],[615,3],[710,4],[730,3],[772,2],[871,3],[937,2],[1086,3],[1392,4]]},"2294":{"position":[[436,3]]},"2379":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"2524":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"2622":{"position":[[204,2]]},"2662":{"position":[[5463,2],[5606,2],[7308,2]]},"2670":{"position":[[181,2]]},"2684":{"position":[[194,2]]},"2714":{"position":[[1077,2]]},"2718":{"position":[[133,2]]},"2786":{"position":[[217,2]]},"2830":{"position":[[1224,2]]},"2832":{"position":[[1024,2]]},"2834":{"position":[[1505,2]]},"2972":{"position":[[505,2]]},"3072":{"position":[[1564,3],[1641,3]]},"3084":{"position":[[579,3]]},"3134":{"position":[[413,3],[1200,3]]},"3186":{"position":[[412,2],[469,2]]},"3196":{"position":[[19,3]]},"3259":{"position":[[595,4],[615,3],[710,4],[730,3],[772,2],[871,3],[937,2],[1086,3],[1392,4]]},"3281":{"position":[[464,2],[572,2]]},"3301":{"position":[[181,2],[371,4]]},"3320":{"position":[[181,2],[371,4]]},"3524":{"position":[[436,3]]},"3667":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]}}}],["10*60",{"_index":3949,"t":{"764":{"position":[[401,7]]},"1783":{"position":[[401,7]]},"2972":{"position":[[413,7]]}}}],["10.0",{"_index":3280,"t":{"477":{"position":[[5374,4]]},"479":{"position":[[4355,4],[4432,4]]}}}],["10.00",{"_index":3282,"t":{"477":{"position":[[5394,6]]}}}],["10.6µ",{"_index":3477,"t":{"483":{"position":[[841,6]]}}}],["10.limit",{"_index":1339,"t":{"70":{"position":[[818,8]]},"963":{"position":[[1380,8]]},"2100":{"position":[[1380,8]]},"3259":{"position":[[1380,8]]}}}],["100",{"_index":102,"t":{"2":{"position":[[1337,3],[1943,3],[2319,3]]},"481":{"position":[[3038,3],[3772,3],[4193,3],[5391,3],[5728,3]]},"638":{"position":[[121,3]]},"782":{"position":[[6,4]]},"840":{"position":[[6,4]]},"983":{"position":[[248,4]]},"1164":{"position":[[3376,4]]},"1182":{"position":[[594,5]]},"1607":{"position":[[121,3]]},"1611":{"position":[[605,4]]},"1893":{"position":[[941,6]]},"2010":{"position":[[420,4]]},"2042":{"position":[[248,4]]},"2240":{"position":[[1196,4],[1326,4]]},"2250":{"position":[[594,5]]},"2371":{"position":[[39,3],[356,3]]},"2736":{"position":[[605,4]]},"2802":{"position":[[121,3]]},"3084":{"position":[[898,5]]},"3227":{"position":[[420,4]]},"3283":{"position":[[248,4]]},"3349":{"position":[[3673,3]]},"3467":{"position":[[413,4]]},"3469":{"position":[[88,3]]},"3471":{"position":[[95,3]]},"3551":{"position":[[1196,4],[1326,4]]},"3561":{"position":[[594,5]]},"3659":{"position":[[39,3],[356,3]]}}}],["1000",{"_index":2672,"t":{"283":{"position":[[415,4]]},"287":{"position":[[1795,5]]},"289":{"position":[[735,6]]},"473":{"position":[[3802,5],[3929,5]]},"941":{"position":[[399,4]]},"1001":{"position":[[647,5]]},"1075":{"position":[[115,5],[188,5]]},"1611":{"position":[[597,5]]},"2016":{"position":[[399,4]]},"2108":{"position":[[647,5]]},"2158":{"position":[[115,5]]},"2736":{"position":[[597,5]]},"3235":{"position":[[399,4]]},"3309":{"position":[[647,5]]},"3387":{"position":[[115,5]]}}}],["10000",{"_index":4468,"t":{"1137":{"position":[[682,5]]},"2218":{"position":[[682,5]]},"3459":{"position":[[682,5]]}}}],["100000",{"_index":3116,"t":{"473":{"position":[[935,6],[1055,6]]}}}],["10000sentinel",{"_index":4148,"t":{"979":{"position":[[1057,13]]},"2036":{"position":[[1073,13]]},"3277":{"position":[[1073,13]]}}}],["1000m",{"_index":4091,"t":{"941":{"position":[[387,8]]},"2016":{"position":[[387,8]]},"3235":{"position":[[387,8]]}}}],["1009",{"_index":1439,"t":{"86":{"position":[[804,4]]}}}],["100k",{"_index":657,"t":{"16":{"position":[[3585,4]]},"307":{"position":[[348,4]]},"481":{"position":[[2352,4]]},"614":{"position":[[251,4]]},"1293":{"position":[[348,4]]},"1549":{"position":[[252,4]]},"1680":{"position":[[554,4]]},"2440":{"position":[[348,4]]},"2718":{"position":[[252,4]]},"2834":{"position":[[554,4]]}}}],["100m",{"_index":4905,"t":{"1611":{"position":[[548,5],[848,8]]},"2736":{"position":[[548,5],[848,8]]}}}],["100messag",{"_index":5029,"t":{"1833":{"position":[[6,11]]},"3024":{"position":[[6,11]]}}}],["100mk",{"_index":3436,"t":{"481":{"position":[[5797,6]]}}}],["101",{"_index":267,"t":{"4":{"position":[[1050,4]]},"172":{"position":[[619,3]]},"610":{"position":[[1049,3]]},"784":{"position":[[6,4]]},"1545":{"position":[[1043,3]]},"2714":{"position":[[1043,3]]}}}],["101messag",{"_index":5031,"t":{"1835":{"position":[[6,11]]},"3026":{"position":[[6,11]]}}}],["102",{"_index":3970,"t":{"786":{"position":[[6,4]]},"842":{"position":[[6,4]]},"1109":{"position":[[1722,4]]},"1569":{"position":[[868,4]]},"1869":{"position":[[1283,4]]},"2190":{"position":[[1776,4]]},"2728":{"position":[[868,4]]},"3060":{"position":[[1366,4]]},"3431":{"position":[[1291,4]]},"3469":{"position":[[196,4]]},"3471":{"position":[[175,4]]}}}],["1024",{"_index":411,"t":{"10":{"position":[[497,4]]}}}],["102400",{"_index":3156,"t":{"473":{"position":[[3942,6]]}}}],["1024;}error_log",{"_index":2021,"t":{"134":{"position":[[696,15]]}}}],["102messag",{"_index":5032,"t":{"1837":{"position":[[6,11]]},"3028":{"position":[[6,11]]}}}],["103",{"_index":3971,"t":{"788":{"position":[[6,4]]},"1475":{"position":[[191,3]]},"1741":{"position":[[89,4]]},"1743":{"position":[[618,4]]},"1745":{"position":[[130,4]]},"1747":{"position":[[130,4]]},"1921":{"position":[[255,4]]},"2668":{"position":[[191,3]]},"2930":{"position":[[89,4]]},"2932":{"position":[[618,4]]},"2934":{"position":[[130,4]]},"2936":{"position":[[130,4]]},"3114":{"position":[[255,4]]}}}],["103messag",{"_index":5033,"t":{"1839":{"position":[[6,11]]},"3030":{"position":[[6,11]]}}}],["104",{"_index":3973,"t":{"790":{"position":[[6,4]]},"844":{"position":[[6,4]]},"3469":{"position":[[201,3]]},"3471":{"position":[[180,3]]}}}],["1048576",{"_index":4047,"t":{"908":{"position":[[9,7]]},"1979":{"position":[[9,7]]},"3192":{"position":[[9,7]]}}}],["1048576net.ipv4.tcp_mem",{"_index":72,"t":{"2":{"position":[[813,23]]}}}],["104messag",{"_index":5034,"t":{"1841":{"position":[[6,11]]},"3032":{"position":[[6,11]]}}}],["105",{"_index":3974,"t":{"792":{"position":[[6,4]]},"1182":{"position":[[1174,4]]},"2250":{"position":[[1174,4]]},"3561":{"position":[[1174,4]]}}}],["105messag",{"_index":5035,"t":{"1843":{"position":[[6,11]]},"3034":{"position":[[6,11]]}}}],["106",{"_index":3975,"t":{"794":{"position":[[6,4]]}}}],["106messag",{"_index":5036,"t":{"1845":{"position":[[6,11]]},"3036":{"position":[[6,11]]}}}],["107",{"_index":3978,"t":{"796":{"position":[[6,4]]},"846":{"position":[[6,4]]},"1505":{"position":[[219,4]]},"2636":{"position":[[219,4]]},"3467":{"position":[[1081,4],[1477,4]]},"3469":{"position":[[298,4]]},"3471":{"position":[[260,4]]}}}],["107.2",{"_index":1450,"t":{"86":{"position":[[1290,5]]}}}],["107messag",{"_index":5037,"t":{"1847":{"position":[[6,11]]},"3038":{"position":[[6,11]]}}}],["108",{"_index":3980,"t":{"798":{"position":[[6,4]]},"848":{"position":[[6,4]]},"3469":{"position":[[303,3]]},"3471":{"position":[[265,3]]}}}],["108messag",{"_index":5038,"t":{"1849":{"position":[[6,11]]},"3040":{"position":[[6,11]]}}}],["109",{"_index":3981,"t":{"800":{"position":[[6,4]]},"1172":{"position":[[1139,5]]}}}],["109messag",{"_index":5039,"t":{"1851":{"position":[[6,11]]},"3042":{"position":[[6,11]]}}}],["10:17:33",{"_index":4749,"t":{"1495":{"position":[[4807,8]]},"2662":{"position":[[4869,8]]}}}],["10;┌─num_ops─┬─us",{"_index":3770,"t":{"610":{"position":[[1324,26]]},"1545":{"position":[[1318,26]]},"2714":{"position":[[1318,26]]}}}],["10k",{"_index":104,"t":{"2":{"position":[[1379,3]]},"383":{"position":[[1206,3]]},"1129":{"position":[[936,3]]},"2210":{"position":[[978,3]]},"3451":{"position":[[1038,3]]}}}],["10x",{"_index":1176,"t":{"46":{"position":[[4085,3]]},"74":{"position":[[970,3]]}}}],["10});console.log(resp.publ",{"_index":3518,"t":{"511":{"position":[[582,36]]},"1373":{"position":[[582,36]]},"2379":{"position":[[582,36]]},"2524":{"position":[[582,36]]},"3667":{"position":[[582,36]]}}}],["10}eof",{"_index":5471,"t":{"2744":{"position":[[450,6]]}}}],["11",{"_index":3576,"t":{"531":{"position":[[252,2]]},"610":{"position":[[1368,2]]},"963":{"position":[[1106,3]]},"1166":{"position":[[704,3]]},"1439":{"position":[[275,2]]},"1545":{"position":[[1362,2]]},"2100":{"position":[[1106,3]]},"2294":{"position":[[464,3]]},"2622":{"position":[[228,2]]},"2714":{"position":[[1362,2]]},"3259":{"position":[[1106,3]]},"3524":{"position":[[464,3]]}}}],["110",{"_index":3982,"t":{"802":{"position":[[6,4]]},"1182":{"position":[[352,3]]},"2250":{"position":[[352,3]]},"3561":{"position":[[352,3]]}}}],["11000",{"_index":4632,"t":{"1233":{"position":[[16,8]]},"2312":{"position":[[16,8]]},"3533":{"position":[[16,8]]}}}],["110messag",{"_index":5040,"t":{"1853":{"position":[[6,11]]},"3044":{"position":[[6,11]]}}}],["111",{"_index":3753,"t":{"610":{"position":[[296,5],[370,4],[525,4]]},"804":{"position":[[6,4]]},"1545":{"position":[[296,5],[370,4],[525,4]]},"2714":{"position":[[296,5],[370,4],[525,4]]}}}],["111messag",{"_index":5041,"t":{"1855":{"position":[[6,11]]},"3046":{"position":[[6,11]]}}}],["112",{"_index":3983,"t":{"806":{"position":[[6,4]]},"850":{"position":[[6,4]]},"3469":{"position":[[389,3]]},"3471":{"position":[[339,3]]}}}],["1121",{"_index":1436,"t":{"86":{"position":[[755,4],[815,4]]}}}],["112app",{"_index":2124,"t":{"150":{"position":[[504,6]]}}}],["112messag",{"_index":5043,"t":{"1857":{"position":[[6,11]]},"3048":{"position":[[6,11]]}}}],["113",{"_index":5761,"t":{"3469":{"position":[[480,3]]},"3471":{"position":[[395,3]]}}}],["114",{"_index":3451,"t":{"481":{"position":[[6160,5]]}}}],["116",{"_index":3401,"t":{"481":{"position":[[2856,3],[3213,4]]}}}],["12",{"_index":63,"t":{"2":{"position":[[735,2]]},"86":{"position":[[741,2],[801,2],[855,2],[918,2],[1111,2],[1171,2],[1225,2],[1287,2]]},"481":{"position":[[3151,2],[3272,3]]},"941":{"position":[[441,2]]},"1069":{"position":[[557,5]]},"2016":{"position":[[441,2]]},"2150":{"position":[[557,5]]},"2294":{"position":[[486,4]]},"3235":{"position":[[441,2]]},"3379":{"position":[[604,5]]},"3524":{"position":[[486,4]]}}}],["12.49",{"_index":3424,"t":{"481":{"position":[[4538,6]]}}}],["12.5µ",{"_index":3244,"t":{"477":{"position":[[4731,6]]},"479":{"position":[[3713,6]]}}}],["12000",{"_index":5684,"t":{"3349":{"position":[[3385,9]]}}}],["1204",{"_index":1381,"t":{"74":{"position":[[1448,4]]}}}],["1214",{"_index":3759,"t":{"610":{"position":[[588,4]]},"1545":{"position":[[588,4]]},"2714":{"position":[[588,4]]}}}],["123",{"_index":4802,"t":{"1523":{"position":[[315,3],[546,5],[1279,3]]},"2692":{"position":[[315,3],[546,5],[1279,3]]}}}],["123722",{"_index":4765,"t":{"1495":{"position":[[6190,6],[6285,8]]},"1827":{"position":[[139,6],[219,8]]},"2662":{"position":[[6259,6],[6354,8]]},"3016":{"position":[[139,6],[219,8]]}}}],["127.0.0.1",{"_index":2800,"t":{"301":{"position":[[473,9]]},"979":{"position":[[999,9]]},"2036":{"position":[[1015,9]]},"3277":{"position":[[1015,9]]}}}],["127.0.0.1:26379",{"_index":4144,"t":{"979":{"position":[[853,18]]},"2036":{"position":[[828,18]]},"3277":{"position":[[828,18]]}}}],["127.0.0.1:3301",{"_index":4177,"t":{"989":{"position":[[1701,17]]},"2050":{"position":[[1701,17]]},"3289":{"position":[[1701,17]]}}}],["127.0.0.1:6379",{"_index":2464,"t":{"230":{"position":[[289,15]]},"686":{"position":[[116,16]]},"975":{"position":[[86,16]]},"983":{"position":[[609,17]]},"1737":{"position":[[106,16]]},"2030":{"position":[[86,16]]},"2042":{"position":[[609,17]]},"2926":{"position":[[106,16]]},"3271":{"position":[[86,16]]},"3283":{"position":[[609,17]]}}}],["127.0.0.1:6380",{"_index":3161,"t":{"475":{"position":[[763,14]]},"981":{"position":[[382,14]]},"983":{"position":[[627,17]]},"2040":{"position":[[382,14]]},"2042":{"position":[[627,17]]},"3281":{"position":[[382,14]]},"3283":{"position":[[627,17]]}}}],["127.0.0.1:6381",{"_index":3169,"t":{"475":{"position":[[942,14]]},"981":{"position":[[490,14]]},"2040":{"position":[[490,14]]},"3281":{"position":[[490,14]]}}}],["127.0.0.1:8000",{"_index":4537,"t":{"1149":{"position":[[664,15]]},"2277":{"position":[[664,15]]},"3504":{"position":[[664,15]]}}}],["127.0.0.1:8000;}map",{"_index":4205,"t":{"1001":{"position":[[113,19]]},"1003":{"position":[[113,19]]},"2108":{"position":[[113,19]]},"2110":{"position":[[113,19]]},"3309":{"position":[[113,19]]},"3311":{"position":[[113,19]]}}}],["127.0.0.2:8000",{"_index":4538,"t":{"1149":{"position":[[687,16]]},"2277":{"position":[[687,16]]},"3504":{"position":[[687,16]]}}}],["127content",{"_index":4427,"t":{"1121":{"position":[[434,10]]},"2202":{"position":[[477,10]]}}}],["128",{"_index":3091,"t":{"471":{"position":[[593,4],[609,4]]},"473":{"position":[[3760,3],[3796,3],[3846,3],[4106,5]]},"477":{"position":[[1924,4],[4561,4],[5965,4]]},"481":{"position":[[4932,4]]},"902":{"position":[[9,3]]},"949":{"position":[[415,3]]},"1969":{"position":[[9,3]]},"2076":{"position":[[415,3]]},"3182":{"position":[[9,3]]},"3243":{"position":[[415,3]]}}}],["129content",{"_index":4442,"t":{"1125":{"position":[[699,10]]},"2206":{"position":[[699,10]]}}}],["12:25:05",{"_index":2121,"t":{"150":{"position":[[466,9],[526,9]]}}}],["12d3",{"_index":5692,"t":{"3349":{"position":[[4764,4]]}}}],["13",{"_index":1868,"t":{"128":{"position":[[1062,3]]},"130":{"position":[[1720,3]]},"166":{"position":[[631,3]]},"636":{"position":[[80,3]]},"1605":{"position":[[80,3]]},"2800":{"position":[[80,3]]}}}],["13.79",{"_index":3285,"t":{"477":{"position":[[5462,6]]}}}],["1328",{"_index":1442,"t":{"86":{"position":[[869,4],[933,4]]}}}],["1342fd2bb20c",{"_index":5111,"t":{"1885":{"position":[[1115,14],[1181,14]]},"3076":{"position":[[1079,14],[1145,14]]}}}],["136",{"_index":1449,"t":{"86":{"position":[[1240,3],[1302,3]]}}}],["14",{"_index":3446,"t":{"481":{"position":[[6048,3]]}}}],["14.0",{"_index":3287,"t":{"477":{"position":[[5513,4]]}}}],["14e8",{"_index":5110,"t":{"1885":{"position":[[1110,4],[1176,4]]},"3076":{"position":[[1074,4],[1140,4]]}}}],["15",{"_index":3819,"t":{"622":{"position":[[220,3]]},"1591":{"position":[[220,3]]},"2786":{"position":[[220,3]]}}}],["150",{"_index":151,"t":{"2":{"position":[[2337,3]]}}}],["151b",{"_index":3354,"t":{"479":{"position":[[4269,4]]}}}],["1565436268",{"_index":4322,"t":{"1067":{"position":[[636,12]]},"2148":{"position":[[636,12]]},"2156":{"position":[[2033,12]]},"3377":{"position":[[636,12]]},"3385":{"position":[[2006,12]]}}}],["15k",{"_index":4106,"t":{"951":{"position":[[645,3]]},"2078":{"position":[[645,3]]},"3245":{"position":[[645,3]]}}}],["16",{"_index":752,"t":{"18":{"position":[[583,2]]},"86":{"position":[[879,2],[943,2]]},"110":{"position":[[2585,4],[2599,4]]},"477":{"position":[[6030,2]]},"1611":{"position":[[641,3]]},"1615":{"position":[[172,3]]},"2736":{"position":[[641,3]]},"2740":{"position":[[172,3]]}}}],["16.04",{"_index":3579,"t":{"531":{"position":[[278,5]]},"1439":{"position":[[301,5]]}}}],["16.35",{"_index":3416,"t":{"481":{"position":[[4405,6]]}}}],["16.67",{"_index":3267,"t":{"477":{"position":[[5110,6]]}}}],["160",{"_index":4907,"t":{"1611":{"position":[[663,3]]},"2736":{"position":[[663,3]]}}}],["1635845022",{"_index":3844,"t":{"654":{"position":[[494,11]]},"1702":{"position":[[494,11]]},"2826":{"position":[[436,11]]}}}],["1635845122",{"_index":3840,"t":{"652":{"position":[[609,13]]},"654":{"position":[[519,13]]},"670":{"position":[[193,13]]},"1699":{"position":[[609,13]]},"1702":{"position":[[519,13]]},"1715":{"position":[[193,13]]},"2823":{"position":[[561,12]]},"2826":{"position":[[461,12]]},"2904":{"position":[[147,12]]}}}],["16379",{"_index":4152,"t":{"981":{"position":[[594,7]]},"2040":{"position":[[594,7]]},"3281":{"position":[[594,7]]}}}],["16686:16686",{"_index":5619,"t":{"3327":{"position":[[591,11]]}}}],["16777216net.core.rmem_max",{"_index":81,"t":{"2":{"position":[[936,25]]}}}],["16777216net.ipv4.tcp_wmem",{"_index":78,"t":{"2":{"position":[[895,25]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioii0miisimv4cci6mtyynzcxmzmznx0.s3eohujiybjc4u21nuhkbcwjll4um0qqgu3pf",{"_index":4875,"t":{"1577":{"position":[[235,123]]},"2770":{"position":[[235,123]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioiixmjm3mjiilcjlehaioje2ntu0ndg0mzgsimnoyw5uzwwioijjagfubmvsin0.jyri3ovnv",{"_index":5027,"t":{"1827":{"position":[[270,127]]},"3016":{"position":[[270,127]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioiixmjm3mjiilcjlehaioje2ntu0ndgyotl9.muu9s5kj3yqp",{"_index":4766,"t":{"1495":{"position":[[6314,103]]},"2662":{"position":[[6383,103]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioij0zxn0x3vzzxiilcjlehaioje2mzaxmzaxnzb9.u7anx",{"_index":4637,"t":{"1262":{"position":[[558,100]]},"2413":{"position":[[567,100]]},"3580":{"position":[[567,100]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioij1c2vymtiilcjlehaioje2mjuwnzmyodh9.bxms4r",{"_index":4612,"t":{"1207":{"position":[[511,97]]},"2339":{"position":[[511,97]]},"3595":{"position":[[511,97]]}}}],["1694627573210",{"_index":5476,"t":{"2744":{"position":[[646,14]]}}}],["1697206286533",{"_index":5465,"t":{"2722":{"position":[[1550,15]]}}}],["17",{"_index":2208,"t":{"174":{"position":[[151,5]]},"176":{"position":[[863,5],[903,4]]},"178":{"position":[[1251,5],[1560,5]]},"182":{"position":[[1254,5]]},"186":{"position":[[573,4]]},"188":{"position":[[295,5],[641,5]]},"1109":{"position":[[1275,2],[1677,2]]},"1121":{"position":[[478,2]]},"1123":{"position":[[406,2]]},"1127":{"position":[[439,2]]},"1131":{"position":[[249,2]]},"2190":{"position":[[1335,2],[1731,2]]},"2202":{"position":[[521,2]]},"2204":{"position":[[406,2]]},"2208":{"position":[[439,2]]},"2212":{"position":[[249,2]]}}}],["17.0",{"_index":3293,"t":{"477":{"position":[[5635,4]]}}}],["1717",{"_index":1441,"t":{"86":{"position":[[858,4]]}}}],["176",{"_index":3154,"t":{"473":{"position":[[3386,3]]}}}],["18",{"_index":1910,"t":{"128":{"position":[[2630,2]]}}}],["18.0",{"_index":3292,"t":{"477":{"position":[[5625,4]]},"479":{"position":[[4605,4]]}}}],["18.04",{"_index":3581,"t":{"531":{"position":[[305,5]]},"1439":{"position":[[328,5]]},"2622":{"position":[[254,5]]}}}],["18.4µ",{"_index":3252,"t":{"477":{"position":[[4854,6]]},"479":{"position":[[3834,6]]}}}],["1840758",{"_index":3152,"t":{"473":{"position":[[3366,7]]}}}],["184content",{"_index":4450,"t":{"1131":{"position":[[205,10]]},"2212":{"position":[[205,10]]}}}],["18:49:39django",{"_index":1919,"t":{"128":{"position":[[2835,14]]}}}],["19",{"_index":2652,"t":{"275":{"position":[[126,2]]},"3467":{"position":[[1036,2],[1443,2]]}}}],["19.91",{"_index":3273,"t":{"477":{"position":[[5236,6]]}}}],["192.168.1.34:6379",{"_index":4155,"t":{"983":{"position":[[765,20]]},"2042":{"position":[[765,20]]},"3283":{"position":[[765,20]]}}}],["192.168.1.35:6379",{"_index":4156,"t":{"983":{"position":[[786,20]]},"2042":{"position":[[786,20]]},"3283":{"position":[[786,20]]}}}],["1999",{"_index":5189,"t":{"2152":{"position":[[4634,5]]},"2158":{"position":[[199,6]]},"2371":{"position":[[43,5],[142,5]]},"3381":{"position":[[4637,5]]},"3387":{"position":[[199,6]]},"3659":{"position":[[43,5],[142,5]]}}}],["1b6e",{"_index":5108,"t":{"1885":{"position":[[1100,4],[1166,4]]},"3076":{"position":[[1064,4],[1130,4]]}}}],["1m",{"_index":4089,"t":{"941":{"position":[[183,4]]},"2016":{"position":[[183,4]]},"3235":{"position":[[183,4]]}}}],["1mb",{"_index":4048,"t":{"908":{"position":[[107,4]]},"1979":{"position":[[107,4]]},"3192":{"position":[[107,4]]}}}],["1s",{"_index":3601,"t":{"548":{"position":[[158,5]]},"618":{"position":[[554,5],[626,5],[1491,5]]},"995":{"position":[[238,3],[311,3]]},"1065":{"position":[[155,5]]},"1067":{"position":[[155,5]]},"1069":{"position":[[146,5]]},"1071":{"position":[[160,5],[1431,5],[1603,5]]},"1073":{"position":[[154,5],[1285,5],[1466,5]]},"1079":{"position":[[815,5],[931,5],[1042,5],[1180,4],[1419,5]]},"1093":{"position":[[1462,4]]},"1676":{"position":[[984,5],[1047,5],[1147,5],[1207,5]]},"1678":{"position":[[880,5],[945,5],[1004,5]]},"1680":{"position":[[1361,5],[1426,5],[1485,5]]},"2056":{"position":[[238,3],[311,3]]},"2146":{"position":[[155,5],[4867,3]]},"2148":{"position":[[155,5],[1737,3]]},"2150":{"position":[[146,5],[1721,3]]},"2152":{"position":[[160,5],[1431,5],[1603,5],[4018,3]]},"2154":{"position":[[154,5],[1285,5],[1466,5],[3323,3]]},"2156":{"position":[[194,5],[3208,3]]},"2162":{"position":[[815,5],[931,5],[1042,5],[1180,4],[1419,5]]},"2176":{"position":[[1462,4]]},"2830":{"position":[[987,5],[1050,5],[1150,5],[1210,5]]},"2832":{"position":[[886,5],[951,5],[1010,5]]},"2834":{"position":[[1367,5],[1432,5],[1491,5]]},"3295":{"position":[[238,3],[311,3]]},"3375":{"position":[[155,5],[4896,3]]},"3377":{"position":[[155,5],[1737,3]]},"3379":{"position":[[146,5],[1768,3]]},"3381":{"position":[[160,5],[1234,5],[1406,5],[4021,3]]},"3383":{"position":[[154,5],[1285,5],[1466,5],[3323,3]]},"3385":{"position":[[167,5],[3200,3]]},"3391":{"position":[[815,5],[931,5],[1042,5],[1180,4],[1419,5]]},"3405":{"position":[[1462,4]]}}}],["1}}}\\n{\"method",{"_index":5196,"t":{"2214":{"position":[[339,16]]}}}],["2",{"_index":108,"t":{"2":{"position":[[1510,1],[2168,2]]},"12":{"position":[[219,1]]},"18":{"position":[[574,2]]},"98":{"position":[[1731,2]]},"110":{"position":[[276,2],[2448,1],[2455,1],[2475,1],[2495,1]]},"309":{"position":[[386,1]]},"383":{"position":[[1041,1],[1409,1]]},"387":{"position":[[327,1]]},"471":{"position":[[989,1]]},"473":{"position":[[3326,1]]},"475":{"position":[[853,1],[1032,1]]},"477":{"position":[[2232,1],[4863,2],[5158,2]]},"479":{"position":[[1287,1],[2474,2],[3843,2],[4138,2]]},"483":{"position":[[1046,2]]},"560":{"position":[[873,2]]},"578":{"position":[[263,1]]},"734":{"position":[[559,1],[1075,1]]},"979":{"position":[[123,1]]},"981":{"position":[[472,1],[580,1]]},"983":{"position":[[468,1]]},"999":{"position":[[543,1]]},"1125":{"position":[[528,1],[609,4],[870,1]]},"1139":{"position":[[225,1]]},"1164":{"position":[[793,2],[2902,2]]},"1166":{"position":[[584,2],[730,2]]},"1168":{"position":[[169,2],[356,2]]},"1170":{"position":[[202,2]]},"1184":{"position":[[442,1]]},"1270":{"position":[[321,2],[449,2]]},"1295":{"position":[[386,1]]},"1495":{"position":[[10019,5]]},"1577":{"position":[[1626,1]]},"1585":{"position":[[263,1]]},"1881":{"position":[[559,1],[1106,1]]},"1885":{"position":[[409,1],[475,1]]},"1893":{"position":[[1071,3],[1088,1],[1122,1]]},"2036":{"position":[[123,1],[975,3]]},"2040":{"position":[[472,1],[580,1]]},"2042":{"position":[[468,1]]},"2106":{"position":[[543,1]]},"2206":{"position":[[528,1],[609,4],[870,1]]},"2214":{"position":[[415,5]]},"2220":{"position":[[225,1]]},"2244":{"position":[[498,2]]},"2294":{"position":[[287,2]]},"2442":{"position":[[386,1]]},"2662":{"position":[[10132,4]]},"2770":{"position":[[1590,1]]},"2782":{"position":[[263,1]]},"3072":{"position":[[847,1],[1394,1]]},"3076":{"position":[[409,1],[475,1]]},"3084":{"position":[[1035,3],[1052,1],[1086,1]]},"3277":{"position":[[123,1],[975,3]]},"3281":{"position":[[472,1],[580,1]]},"3283":{"position":[[468,1]]},"3307":{"position":[[543,1]]},"3447":{"position":[[558,3],[719,1]]},"3461":{"position":[[225,1]]},"3524":{"position":[[287,2]]},"3555":{"position":[[498,2]]}}}],["2.0",{"_index":1316,"t":{"66":{"position":[[241,3],[268,3]]},"441":{"position":[[159,5]]},"479":{"position":[[4365,3]]},"1425":{"position":[[159,5]]},"2582":{"position":[[163,5]]}}}],["2.0.0",{"_index":4571,"t":{"1166":{"position":[[1152,7]]}}}],["2.1.7[3569",{"_index":4186,"t":{"993":{"position":[[561,11]]},"2054":{"position":[[561,11]]},"3293":{"position":[[561,11]]}}}],["2.40ghz",{"_index":57,"t":{"2":{"position":[[692,7]]}}}],["2.5x",{"_index":3366,"t":{"479":{"position":[[4676,4]]}}}],["2.60ghzbenchmarkmarsh",{"_index":1434,"t":{"86":{"position":[[717,23],[1087,23]]}}}],["2.6mb",{"_index":1777,"t":{"116":{"position":[[601,7]]}}}],["2.7",{"_index":4175,"t":{"989":{"position":[[1539,4]]},"2050":{"position":[[1539,4]]},"3289":{"position":[[1539,4]]}}}],["20",{"_index":86,"t":{"2":{"position":[[1057,2],[2158,3]]},"74":{"position":[[1064,3]]},"228":{"position":[[581,2]]},"238":{"position":[[483,2]]},"240":{"position":[[781,2]]},"309":{"position":[[246,2]]},"578":{"position":[[294,2]]},"618":{"position":[[726,6]]},"1295":{"position":[[246,2]]},"1585":{"position":[[231,2]]},"1676":{"position":[[1061,2],[1311,6]]},"1678":{"position":[[1108,6]]},"1680":{"position":[[1589,6]]},"1682":{"position":[[1518,2]]},"2442":{"position":[[246,2]]},"2782":{"position":[[231,2]]},"2830":{"position":[[1064,2],[1314,6]]},"2832":{"position":[[1114,6]]},"2834":{"position":[[1595,6]]},"2836":{"position":[[1518,2]]}}}],["20*(1000/5",{"_index":2486,"t":{"240":{"position":[[908,11]]}}}],["20.04",{"_index":3583,"t":{"531":{"position":[[332,5]]},"1439":{"position":[[355,5]]},"2622":{"position":[[281,5],[313,5]]}}}],["20.69",{"_index":3291,"t":{"477":{"position":[[5582,6]]}}}],["200",{"_index":4410,"t":{"1109":{"position":[[1211,3],[1612,3]]},"1121":{"position":[[412,3]]},"1123":{"position":[[341,3]]},"1125":{"position":[[677,3]]},"1127":{"position":[[374,3]]},"1131":{"position":[[183,3]]},"2190":{"position":[[1271,3],[1666,3]]},"2202":{"position":[[455,3]]},"2204":{"position":[[341,3]]},"2206":{"position":[[677,3]]},"2208":{"position":[[374,3]]},"2212":{"position":[[183,3]]},"3431":{"position":[[1206,3]]},"3467":{"position":[[103,3],[349,3],[971,3],[1132,3]]}}}],["2000",{"_index":5392,"t":{"2373":{"position":[[79,6],[321,5]]},"3661":{"position":[[79,6],[321,5]]}}}],["2003",{"_index":4247,"t":{"1011":{"position":[[133,5]]},"2092":{"position":[[133,5]]},"3301":{"position":[[133,5]]},"3320":{"position":[[133,5]]}}}],["200k",{"_index":118,"t":{"2":{"position":[[1732,4]]}}}],["200m",{"_index":43,"t":{"2":{"position":[[557,5],[1858,5]]},"52":{"position":[[783,5]]}}}],["2017",{"_index":62,"t":{"2":{"position":[[729,5]]}}}],["2018",{"_index":327,"t":{"8":{"position":[[599,4]]},"26":{"position":[[1237,5]]},"1109":{"position":[[1282,4],[1684,4]]},"1121":{"position":[[485,4]]},"1123":{"position":[[413,4]]},"1127":{"position":[[446,4]]},"1131":{"position":[[256,4]]},"2190":{"position":[[1342,4],[1738,4]]},"2202":{"position":[[528,4]]},"2204":{"position":[[413,4]]},"2208":{"position":[[446,4]]},"2212":{"position":[[256,4]]}}}],["2019",{"_index":4328,"t":{"1069":{"position":[[621,9]]},"2150":{"position":[[621,9]]},"3379":{"position":[[668,9]]}}}],["2020",{"_index":212,"t":{"4":{"position":[[18,4]]},"14":{"position":[[8,4]]},"128":{"position":[[2828,4]]}}}],["2020/07/08",{"_index":4183,"t":{"993":{"position":[[499,10],[573,10],[633,10],[721,10],[830,10]]},"2054":{"position":[[499,10],[573,10],[633,10],[721,10],[830,10]]},"3293":{"position":[[499,10],[573,10],[633,10],[721,10],[830,10]]}}}],["2021",{"_index":890,"t":{"28":{"position":[[720,4]]},"150":{"position":[[461,4],[521,4]]},"612":{"position":[[1033,4],[1128,4],[1241,4],[1352,4],[1458,4],[1563,4],[1668,4]]},"1125":{"position":[[750,4]]},"1495":{"position":[[4796,4]]},"1547":{"position":[[1202,4],[1297,4],[1410,4],[1521,4],[1627,4],[1732,4],[1837,4]]},"2206":{"position":[[750,4]]},"2662":{"position":[[4858,4]]},"2716":{"position":[[1202,4],[1297,4],[1410,4],[1521,4],[1627,4],[1732,4],[1837,4]]}}}],["2022",{"_index":4751,"t":{"1495":{"position":[[5393,4],[7220,4]]},"2662":{"position":[[5455,4],[7300,4]]}}}],["2022.3",{"_index":5774,"t":{"3490":{"position":[[389,7]]}}}],["2023",{"_index":5738,"t":{"3467":{"position":[[1043,4],[1450,4]]}}}],["2048",{"_index":1570,"t":{"98":{"position":[[1029,4]]},"1049":{"position":[[1542,4]]},"1213":{"position":[[642,4]]},"2230":{"position":[[1542,4]]},"2387":{"position":[[642,4]]},"3417":{"position":[[1542,4]]},"3601":{"position":[[642,4]]}}}],["2048gener",{"_index":1569,"t":{"98":{"position":[[997,14]]}}}],["2048openssl",{"_index":1549,"t":{"98":{"position":[[316,11]]}}}],["20:28:44.324269",{"_index":4184,"t":{"993":{"position":[[510,15]]},"2054":{"position":[[510,15]]},"3293":{"position":[[510,15]]}}}],["20:28:44.324400",{"_index":4187,"t":{"993":{"position":[[584,15]]},"2054":{"position":[[584,15]]},"3293":{"position":[[584,15]]}}}],["20:28:44.325600",{"_index":4190,"t":{"993":{"position":[[644,15]]},"2054":{"position":[[644,15]]},"3293":{"position":[[644,15]]}}}],["20:28:44.325612",{"_index":4192,"t":{"993":{"position":[[732,15]]},"2054":{"position":[[732,15]]},"3293":{"position":[[732,15]]}}}],["20:28:44.325617",{"_index":4194,"t":{"993":{"position":[[841,15]]},"2054":{"position":[[841,15]]},"3293":{"position":[[841,15]]}}}],["20x",{"_index":2511,"t":{"246":{"position":[[736,3]]}}}],["21",{"_index":1918,"t":{"128":{"position":[[2824,3]]},"1125":{"position":[[743,2]]},"2206":{"position":[[743,2]]}}}],["2160h",{"_index":5139,"t":{"2026":{"position":[[36,5]]},"2030":{"position":[[898,5]]},"2052":{"position":[[711,6]]},"3267":{"position":[[36,5]]},"3271":{"position":[[864,5]]},"3291":{"position":[[711,6]]}}}],["217894",{"_index":3792,"t":{"612":{"position":[[1024,6]]},"1547":{"position":[[1193,6]]},"2716":{"position":[[1193,6]]}}}],["22.0",{"_index":3286,"t":{"477":{"position":[[5503,4]]},"479":{"position":[[4484,4]]}}}],["22.43",{"_index":3337,"t":{"479":{"position":[[3737,6]]}}}],["22.77",{"_index":3246,"t":{"477":{"position":[[4755,6]]}}}],["228804",{"_index":3109,"t":{"471":{"position":[[963,6]]},"473":{"position":[[3300,6]]},"477":{"position":[[2206,6]]},"479":{"position":[[1261,6]]}}}],["2292",{"_index":4718,"t":{"1495":{"position":[[993,4],[1542,4],[8045,4],[9890,4]]},"2662":{"position":[[1008,4],[1557,4],[8142,4],[10035,4]]}}}],["22:01:42",{"_index":4414,"t":{"1109":{"position":[[1287,8]]},"2190":{"position":[[1347,8]]}}}],["22:03:09",{"_index":4419,"t":{"1109":{"position":[[1689,8]]},"2190":{"position":[[1743,8]]}}}],["22:07:58",{"_index":4451,"t":{"1131":{"position":[[261,8]]},"2212":{"position":[[261,8]]}}}],["22:09:44",{"_index":4439,"t":{"1123":{"position":[[418,8]]},"1127":{"position":[[451,8]]},"2204":{"position":[[418,8]]},"2208":{"position":[[451,8]]}}}],["22:13:17",{"_index":4428,"t":{"1121":{"position":[[490,8]]},"2202":{"position":[[533,8]]}}}],["23",{"_index":64,"t":{"2":{"position":[[738,3]]}}}],["23.0",{"_index":3290,"t":{"477":{"position":[[5571,4]]}}}],["24",{"_index":3249,"t":{"477":{"position":[[4803,4]]},"479":{"position":[[3784,4]]},"481":{"position":[[5853,3]]},"945":{"position":[[67,2]]},"1545":{"position":[[1591,2]]},"2020":{"position":[[67,2]]},"2714":{"position":[[1591,2]]},"3239":{"position":[[67,2]]}}}],["24/7",{"_index":2508,"t":{"246":{"position":[[678,4]]}}}],["2400",{"_index":4291,"t":{"1065":{"position":[[1876,4]]},"1067":{"position":[[477,4]]},"1069":{"position":[[389,4]]},"1071":{"position":[[1748,4]]},"1073":{"position":[[1763,4]]},"2146":{"position":[[1993,4]]},"2148":{"position":[[477,4]]},"2150":{"position":[[389,4]]},"2152":{"position":[[1748,4]]},"2154":{"position":[[1763,4]]},"2156":{"position":[[1852,4]]},"3375":{"position":[[1993,4]]},"3377":{"position":[[477,4]]},"3379":{"position":[[436,4]]},"3381":{"position":[[1551,4]]},"3383":{"position":[[1763,4]]},"3385":{"position":[[1825,4]]}}}],["2402",{"_index":3798,"t":{"612":{"position":[[1451,4]]},"1547":{"position":[[1620,4]]},"2716":{"position":[[1620,4]]}}}],["244",{"_index":3217,"t":{"477":{"position":[[2280,3]]},"479":{"position":[[1335,3]]}}}],["24;app.use(sess",{"_index":2673,"t":{"283":{"position":[[432,21]]}}}],["24h",{"_index":3867,"t":{"686":{"position":[[1024,5]]},"1737":{"position":[[998,5]]},"2926":{"position":[[998,5]]}}}],["25",{"_index":2501,"t":{"246":{"position":[[340,2]]},"764":{"position":[[715,2]]},"1178":{"position":[[370,2]]},"1184":{"position":[[2375,2]]},"1783":{"position":[[715,2]]},"2246":{"position":[[301,2]]},"2296":{"position":[[464,2],[542,7]]},"2972":{"position":[[704,2]]},"3486":{"position":[[464,2],[542,7]]},"3557":{"position":[[301,2]]}}}],["25.0",{"_index":3284,"t":{"477":{"position":[[5451,4]]}}}],["25.83",{"_index":3270,"t":{"477":{"position":[[5172,6]]}}}],["250.6",{"_index":1447,"t":{"86":{"position":[[1174,5]]}}}],["2500",{"_index":5394,"t":{"2373":{"position":[[114,4],[239,4]]},"3661":{"position":[[114,4],[239,4]]}}}],["255",{"_index":3868,"t":{"690":{"position":[[82,3]]},"904":{"position":[[9,3]]},"1871":{"position":[[84,3]]},"1971":{"position":[[9,3]]},"3062":{"position":[[84,3]]},"3184":{"position":[[9,3]]}}}],["256",{"_index":410,"t":{"10":{"position":[[493,3]]},"86":{"position":[[596,3]]},"178":{"position":[[1137,3]]},"868":{"position":[[113,3]]},"949":{"position":[[419,3]]},"1207":{"position":[[467,3]]},"1262":{"position":[[511,3]]},"1495":{"position":[[6268,3],[6761,3]]},"1577":{"position":[[195,3]]},"1827":{"position":[[202,3]]},"2066":{"position":[[113,3]]},"2068":{"position":[[130,3]]},"2076":{"position":[[419,3]]},"2339":{"position":[[467,3]]},"2413":{"position":[[520,3]]},"2662":{"position":[[6337,3],[6842,3]]},"2770":{"position":[[195,3]]},"3016":{"position":[[202,3]]},"3144":{"position":[[113,3]]},"3146":{"position":[[130,3]]},"3243":{"position":[[419,3]]},"3580":{"position":[[520,3]]},"3595":{"position":[[467,3]]}}}],["25600",{"_index":3155,"t":{"473":{"position":[[3815,5]]}}}],["26379",{"_index":3463,"t":{"483":{"position":[[437,6]]}}}],["26379sentinel",{"_index":4146,"t":{"979":{"position":[[968,13]]},"2036":{"position":[[984,13]]},"3277":{"position":[[984,13]]}}}],["2640",{"_index":54,"t":{"2":{"position":[[682,4]]}}}],["2654",{"_index":4880,"t":{"1577":{"position":[[1362,4]]},"2770":{"position":[[1326,4]]}}}],["268444",{"_index":3215,"t":{"477":{"position":[[2262,6]]},"479":{"position":[[1317,6]]}}}],["26883",{"_index":1379,"t":{"74":{"position":[[1436,5]]}}}],["2694",{"_index":3791,"t":{"612":{"position":[[992,4],[1095,4],[1190,4],[1303,4],[1414,4],[1520,4],[1625,4]]},"1547":{"position":[[1161,4],[1264,4],[1359,4],[1472,4],[1583,4],[1689,4],[1794,4]]},"1885":{"position":[[426,4],[466,4],[859,7],[1000,7]]},"2716":{"position":[[1161,4],[1264,4],[1359,4],[1472,4],[1583,4],[1689,4],[1794,4]]},"3076":{"position":[[426,4],[466,4],[823,7],[964,7]]}}}],["2695",{"_index":3851,"t":{"670":{"position":[[172,7]]},"672":{"position":[[174,9]]},"1715":{"position":[[172,7]]},"1717":{"position":[[174,9]]},"2904":{"position":[[126,7]]},"2906":{"position":[[126,8]]}}}],["27",{"_index":141,"t":{"2":{"position":[[2181,2]]},"94":{"position":[[2909,3]]}}}],["271974003db9",{"_index":4459,"t":{"1131":{"position":[[430,14]]},"2212":{"position":[[430,14]]},"3453":{"position":[[246,14]]}}}],["28",{"_index":117,"t":{"2":{"position":[[1656,2]]},"74":{"position":[[1458,2]]}}}],["28282",{"_index":4017,"t":{"868":{"position":[[67,5],[146,5],[276,5]]},"2066":{"position":[[67,5],[146,5],[276,5]]},"2068":{"position":[[73,5],[196,5],[326,5]]},"3144":{"position":[[67,5],[146,5],[276,5]]},"3146":{"position":[[73,5],[196,5],[326,5]]}}}],["29",{"_index":1533,"t":{"94":{"position":[[2852,2]]}}}],["29.0",{"_index":3283,"t":{"477":{"position":[[5441,4],[5561,4]]},"479":{"position":[[4422,4],[4542,4]]}}}],["29.32",{"_index":3241,"t":{"477":{"position":[[4683,7]]}}}],["2908591",{"_index":3322,"t":{"479":{"position":[[1374,7]]}}}],["298.70",{"_index":3481,"t":{"483":{"position":[[919,8]]}}}],["2999",{"_index":5393,"t":{"2373":{"position":[[86,6]]},"3661":{"position":[[86,6]]}}}],["2s",{"_index":3163,"t":{"475":{"position":[[806,2],[824,2],[985,2],[1003,2]]},"981":{"position":[[425,2],[443,2],[533,2],[551,2]]},"2040":{"position":[[425,2],[443,2],[533,2],[551,2]]},"3281":{"position":[[425,2],[443,2],[533,2],[551,2]]}}}],["2sentinel",{"_index":4147,"t":{"979":{"position":[[1014,9]]},"2036":{"position":[[1030,9]]},"3277":{"position":[[1030,9]]}}}],["2x",{"_index":3822,"t":{"638":{"position":[[88,2]]},"1607":{"position":[[88,2]]},"2802":{"position":[[88,2]]}}}],["3",{"_index":813,"t":{"20":{"position":[[503,1]]},"86":{"position":[[1135,1],[1195,1],[1249,1],[1311,1]]},"110":{"position":[[2457,1],[2477,1],[2497,1]]},"401":{"position":[[75,1]]},"477":{"position":[[4751,2]]},"479":{"position":[[2511,2]]},"483":{"position":[[779,2],[838,2],[968,2]]},"610":{"position":[[1474,1],[1489,1]]},"977":{"position":[[86,1],[232,1],[1085,1]]},"1125":{"position":[[916,1]]},"1166":{"position":[[597,2],[748,3]]},"1170":{"position":[[189,2]]},"1270":{"position":[[338,2],[465,3]]},"1495":{"position":[[4437,1]]},"1545":{"position":[[1468,1],[1483,1]]},"2034":{"position":[[86,1],[232,1],[1085,1]]},"2036":{"position":[[941,1]]},"2206":{"position":[[916,1]]},"2355":{"position":[[555,1]]},"2662":{"position":[[4499,1]]},"2714":{"position":[[1468,1],[1483,1]]},"3275":{"position":[[86,1],[232,1],[1085,1]]},"3277":{"position":[[941,1]]},"3447":{"position":[[765,1]]},"3643":{"position":[[555,1]]}}}],["3+deb9u1",{"_index":61,"t":{"2":{"position":[[720,8]]}}}],["3,499",{"_index":5496,"t":{"2780":{"position":[[77,5]]}}}],["3.0",{"_index":3364,"t":{"479":{"position":[[4615,3]]}}}],["3.1.0",{"_index":4744,"t":{"1495":{"position":[[3260,5]]}}}],["3.1.2",{"_index":1920,"t":{"128":{"position":[[2858,6]]}}}],["3.1const",{"_index":1674,"t":{"106":{"position":[[1169,8]]}}}],["3.2",{"_index":1697,"t":{"110":{"position":[[21,3]]}}}],["3.2.2",{"_index":3689,"t":{"596":{"position":[[132,5],[182,5]]}}}],["3.2const",{"_index":1706,"t":{"110":{"position":[[810,8]]}}}],["3.33µ",{"_index":3429,"t":{"481":{"position":[[4655,6]]}}}],["3.34",{"_index":3344,"t":{"479":{"position":[[3926,5]]}}}],["3.37",{"_index":3263,"t":{"477":{"position":[[5039,6]]}}}],["3.40µ",{"_index":3257,"t":{"477":{"position":[[4932,6]]}}}],["3.60µ",{"_index":3343,"t":{"479":{"position":[[3913,6]]},"481":{"position":[[4643,6]]}}}],["3.72µ",{"_index":3256,"t":{"477":{"position":[[4920,6]]},"479":{"position":[[3901,6]]}}}],["3.9\"servic",{"_index":4682,"t":{"1435":{"position":[[211,14]]},"2618":{"position":[[211,14]]}}}],["30",{"_index":35,"t":{"2":{"position":[[427,2]]},"110":{"position":[[2450,2]]},"309":{"position":[[181,2]]},"383":{"position":[[1255,3]]},"481":{"position":[[6017,3]]},"517":{"position":[[243,2]]},"734":{"position":[[1320,6]]},"932":{"position":[[348,2]]},"941":{"position":[[459,2]]},"1295":{"position":[[181,2]]},"1881":{"position":[[1295,6]]},"2007":{"position":[[348,2]]},"2016":{"position":[[459,2]]},"2442":{"position":[[181,2]]},"3072":{"position":[[1583,6]]},"3086":{"position":[[867,2]]},"3224":{"position":[[348,2]]},"3235":{"position":[[459,2]]}}}],["30*60",{"_index":2278,"t":{"188":{"position":[[327,6]]}}}],["30*60}token",{"_index":2233,"t":{"178":{"position":[[1308,11]]}}}],["300",{"_index":1073,"t":{"42":{"position":[[483,4]]},"88":{"position":[[409,5]]},"541":{"position":[[331,5]]},"543":{"position":[[136,4]]},"732":{"position":[[284,7]]},"734":{"position":[[1434,7]]},"963":{"position":[[324,3],[485,3]]},"965":{"position":[[1794,3]]},"1881":{"position":[[1372,6]]},"1937":{"position":[[432,7],[1219,7]]},"2100":{"position":[[324,3],[485,3]]},"2102":{"position":[[1966,3]]},"3072":{"position":[[1660,6]]},"3134":{"position":[[432,7],[1219,7]]},"3259":{"position":[[324,3],[485,3]]},"3261":{"position":[[1966,3]]}}}],["3000",{"_index":3987,"t":{"810":{"position":[[6,5]]},"1495":{"position":[[4398,4],[4473,4],[4573,5]]},"2375":{"position":[[94,6],[169,4],[319,5]]},"2662":{"position":[[4460,4],[4535,4],[4635,5]]},"3663":{"position":[[94,6],[169,4],[319,5]]}}}],["3000;app.use(express.json());const",{"_index":2670,"t":{"283":{"position":[[371,34]]}}}],["3000reason",{"_index":5044,"t":{"1861":{"position":[[6,11]]},"3052":{"position":[[6,11]]}}}],["3001",{"_index":3991,"t":{"812":{"position":[[6,5]]}}}],["3001reason",{"_index":5046,"t":{"1863":{"position":[[66,11]]},"3054":{"position":[[66,11]]}}}],["3002",{"_index":3992,"t":{"814":{"position":[[6,5]]}}}],["3003",{"_index":3993,"t":{"816":{"position":[[6,5]]}}}],["3004",{"_index":3994,"t":{"818":{"position":[[6,5]]}}}],["3004reason",{"_index":5048,"t":{"1863":{"position":[[183,11]]},"3054":{"position":[[183,11]]}}}],["3005",{"_index":3996,"t":{"820":{"position":[[6,5]]}}}],["3005reason",{"_index":5050,"t":{"1863":{"position":[[313,11]]},"3054":{"position":[[313,11]]}}}],["3006",{"_index":3997,"t":{"822":{"position":[[6,5]]}}}],["3006reason",{"_index":5052,"t":{"1863":{"position":[[374,11]]},"3054":{"position":[[374,11]]}}}],["3007",{"_index":3998,"t":{"824":{"position":[[6,5]]}}}],["3008",{"_index":3999,"t":{"826":{"position":[[6,5]]}}}],["3008reason",{"_index":5054,"t":{"1863":{"position":[[493,11]]},"3054":{"position":[[493,11]]}}}],["3009",{"_index":4000,"t":{"828":{"position":[[6,5]]}}}],["3009reason",{"_index":5056,"t":{"1863":{"position":[[607,11]]},"3054":{"position":[[607,11]]}}}],["3010",{"_index":4001,"t":{"830":{"position":[[6,5]]}}}],["3010reason",{"_index":5058,"t":{"1863":{"position":[[755,11]]},"3054":{"position":[[755,11]]}}}],["3011",{"_index":4002,"t":{"832":{"position":[[6,5]]}}}],["3011reason",{"_index":5061,"t":{"1863":{"position":[[997,11]]},"3054":{"position":[[997,11]]}}}],["3012",{"_index":2416,"t":{"214":{"position":[[953,5]]},"834":{"position":[[6,5]]},"3629":{"position":[[954,5]]}}}],["3012reason",{"_index":5064,"t":{"1863":{"position":[[1162,11]]},"3054":{"position":[[1162,11]]}}}],["3013",{"_index":4003,"t":{"836":{"position":[[6,5]]}}}],["3013reason",{"_index":5066,"t":{"1863":{"position":[[1387,11]]},"3054":{"position":[[1387,11]]}}}],["3086496",{"_index":73,"t":{"2":{"position":[[839,7]]}}}],["30k",{"_index":2865,"t":{"309":{"position":[[249,3]]},"1295":{"position":[[249,3]]},"2442":{"position":[[249,3]]}}}],["30kb",{"_index":147,"t":{"2":{"position":[[2256,4]]},"52":{"position":[[620,4]]}}}],["30x",{"_index":1427,"t":{"86":{"position":[[474,3]]}}}],["31",{"_index":3794,"t":{"612":{"position":[[1041,2],[1136,2],[1249,2],[1360,2],[1466,2],[1571,2],[1676,2]]},"686":{"position":[[930,2]]},"1547":{"position":[[1210,2],[1305,2],[1418,2],[1529,2],[1635,2],[1740,2],[1845,2]]},"1737":{"position":[[912,2]]},"2716":{"position":[[1210,2],[1305,2],[1418,2],[1529,2],[1635,2],[1740,2],[1845,2]]},"2926":{"position":[[912,2]]}}}],["31.5µ",{"_index":3473,"t":{"483":{"position":[[759,6]]}}}],["31200",{"_index":4837,"t":{"1545":{"position":[[1724,5]]},"2714":{"position":[[1724,5]]}}}],["32",{"_index":178,"t":{"2":{"position":[[2884,3]]},"523":{"position":[[305,2]]},"531":{"position":[[474,2]]},"1077":{"position":[[532,2]]},"1431":{"position":[[315,2]]},"1439":{"position":[[497,2]]},"2160":{"position":[[682,2],[705,2]]},"2614":{"position":[[330,2]]},"2622":{"position":[[433,2]]},"3389":{"position":[[682,2],[705,2]]}}}],["3276750fs.nr_open",{"_index":71,"t":{"2":{"position":[[793,17]]}}}],["33554432",{"_index":83,"t":{"2":{"position":[[992,8]]}}}],["33554432net.core.wmem_max",{"_index":82,"t":{"2":{"position":[[964,25]]}}}],["3499",{"_index":5395,"t":{"2375":{"position":[[174,5]]},"3663":{"position":[[174,5]]}}}],["34d2",{"_index":4616,"t":{"1207":{"position":[[1182,4]]},"2339":{"position":[[1182,4]]},"3595":{"position":[[1157,4]]}}}],["35",{"_index":3403,"t":{"481":{"position":[[2876,2],[3148,2]]},"1289":{"position":[[130,2]]},"2417":{"position":[[130,2]]}}}],["3500reason",{"_index":5068,"t":{"1865":{"position":[[79,11]]},"3056":{"position":[[79,11]]}}}],["3501reason",{"_index":5070,"t":{"1865":{"position":[[202,11]]},"3056":{"position":[[202,11]]}}}],["3502reason",{"_index":5072,"t":{"1865":{"position":[[323,11]]},"3056":{"position":[[323,11]]}}}],["3503reason",{"_index":5074,"t":{"1865":{"position":[[492,11]]},"3056":{"position":[[492,11]]}}}],["3504reason",{"_index":5076,"t":{"1865":{"position":[[661,11]]},"3056":{"position":[[661,11]]}}}],["3505reason",{"_index":5078,"t":{"1865":{"position":[[859,11]]},"3056":{"position":[[859,11]]}}}],["3506reason",{"_index":5080,"t":{"1865":{"position":[[1022,11]]},"3056":{"position":[[1022,11]]}}}],["3507reason",{"_index":5084,"t":{"1865":{"position":[[1369,11]]},"3056":{"position":[[1369,11]]}}}],["3508reason",{"_index":5086,"t":{"1865":{"position":[[1540,11]]},"3056":{"position":[[1540,11]]}}}],["3509reason",{"_index":5090,"t":{"1865":{"position":[[1812,11]]},"3056":{"position":[[1812,11]]}}}],["350f",{"_index":4774,"t":{"1495":{"position":[[7309,4]]},"2662":{"position":[[7389,4]]}}}],["3539",{"_index":3797,"t":{"612":{"position":[[1345,4]]},"1547":{"position":[[1514,4]]},"2716":{"position":[[1514,4]]}}}],["36",{"_index":3402,"t":{"481":{"position":[[2873,2],[3145,2],[6125,3]]}}}],["36.36",{"_index":3288,"t":{"477":{"position":[[5524,6]]}}}],["3600",{"_index":4019,"t":{"868":{"position":[[285,4]]},"2066":{"position":[[285,4]]},"2068":{"position":[[346,4]]},"3144":{"position":[[285,4]]},"3146":{"position":[[346,4]]}}}],["3600}token",{"_index":5558,"t":{"3014":{"position":[[238,10]]}}}],["360b",{"_index":3350,"t":{"479":{"position":[[4141,4]]}}}],["365",{"_index":1561,"t":{"98":{"position":[[619,3],[2111,3]]}}}],["36content",{"_index":5742,"t":{"3467":{"position":[[1400,9]]}}}],["399",{"_index":5391,"t":{"2371":{"position":[[75,3]]},"3659":{"position":[[75,3]]}}}],["3content",{"_index":4412,"t":{"1109":{"position":[[1233,8]]},"2190":{"position":[[1293,8]]}}}],["3s",{"_index":5651,"t":{"3349":{"position":[[597,5],[1072,5]]},"3351":{"position":[[866,5]]}}}],["3x",{"_index":573,"t":{"14":{"position":[[1501,2]]},"86":{"position":[[1907,2]]}}}],["4",{"_index":446,"t":{"10":{"position":[[1428,1]]},"14":{"position":[[1586,1]]},"18":{"position":[[577,2]]},"74":{"position":[[968,1]]},"86":{"position":[[373,1]]},"110":{"position":[[2459,1],[2479,1],[2499,1]]},"184":{"position":[[243,2]]},"401":{"position":[[88,2]]},"473":{"position":[[3395,1]]},"477":{"position":[[4680,2]]},"479":{"position":[[1394,1]]},"483":{"position":[[850,2],[979,2],[1035,2]]},"951":{"position":[[393,1]]},"1125":{"position":[[808,2]]},"1166":{"position":[[611,2]]},"1172":{"position":[[445,2]]},"1270":{"position":[[351,2]]},"1523":{"position":[[870,4]]},"2078":{"position":[[393,1]]},"2206":{"position":[[808,2]]},"2294":{"position":[[308,2],[1175,2]]},"2345":{"position":[[22,1]]},"2692":{"position":[[870,4]]},"3245":{"position":[[393,1]]},"3447":{"position":[[657,2]]},"3524":{"position":[[308,2],[1175,2]]},"3633":{"position":[[22,1]]}}}],["4.0.0",{"_index":4847,"t":{"1561":{"position":[[140,5],[196,5]]}}}],["4.9.65",{"_index":60,"t":{"2":{"position":[[713,6]]}}}],["40",{"_index":136,"t":{"2":{"position":[[2088,2]]}}}],["400",{"_index":5188,"t":{"2152":{"position":[[4628,5]]},"2158":{"position":[[193,5]]},"2371":{"position":[[136,5]]},"3381":{"position":[[4631,5]]},"3387":{"position":[[193,5]]},"3467":{"position":[[1369,3]]},"3469":{"position":[[315,3]]},"3659":{"position":[[136,5]]}}}],["4000",{"_index":2487,"t":{"240":{"position":[[922,4]]},"1077":{"position":[[148,5],[249,4],[329,4]]},"2160":{"position":[[229,4],[283,6],[468,4]]},"2375":{"position":[[180,4]]},"3389":{"position":[[229,4],[283,6],[468,4]]},"3663":{"position":[[180,4]]}}}],["403",{"_index":3952,"t":{"764":{"position":[[1896,3]]},"1783":{"position":[[1896,3]]},"2152":{"position":[[4420,4],[4505,3],[4784,4]]},"3381":{"position":[[4423,4],[4508,3],[4787,4]]},"3639":{"position":[[916,4]]},"3655":{"position":[[1100,4]]}}}],["404",{"_index":5752,"t":{"3469":{"position":[[213,3]]}}}],["409",{"_index":5762,"t":{"3469":{"position":[[492,3]]}}}],["4094",{"_index":4704,"t":{"1495":{"position":[[833,4],[1367,4],[7870,4]]},"2662":{"position":[[848,4],[1382,4],[7967,4]]}}}],["4096",{"_index":79,"t":{"2":{"position":[[923,4]]},"1277":{"position":[[118,4]]},"1279":{"position":[[119,4]]},"2424":{"position":[[118,4]]},"2426":{"position":[[119,4]]},"3619":{"position":[[118,4]]},"3621":{"position":[[119,4]]}}}],["4115330",{"_index":74,"t":{"2":{"position":[[847,7]]}}}],["412",{"_index":3802,"t":{"612":{"position":[[1662,3]]},"1547":{"position":[[1831,3]]},"2716":{"position":[[1831,3]]}}}],["416",{"_index":5758,"t":{"3469":{"position":[[401,3]]}}}],["418.5",{"_index":3323,"t":{"479":{"position":[[1382,5]]}}}],["4194394",{"_index":80,"t":{"2":{"position":[[928,7]]}}}],["42",{"_index":957,"t":{"32":{"position":[[589,5]]},"38":{"position":[[576,9]]},"341":{"position":[[361,2]]},"481":{"position":[[2879,2],[3260,3]]},"680":{"position":[[415,9]]},"682":{"position":[[319,9]]},"684":{"position":[[280,9]]},"696":{"position":[[243,2],[941,2]]},"764":{"position":[[369,5]]},"768":{"position":[[51,6],[151,4]]},"770":{"position":[[98,5],[274,4]]},"772":{"position":[[65,5],[247,5]]},"1121":{"position":[[630,4],[738,4]]},"1329":{"position":[[361,2]]},"1577":{"position":[[64,3],[212,2],[1273,7]]},"1731":{"position":[[415,9]]},"1733":{"position":[[319,9]]},"1735":{"position":[[280,9]]},"1783":{"position":[[369,5]]},"1787":{"position":[[51,6],[151,4]]},"1789":{"position":[[98,5],[274,4]]},"1791":{"position":[[65,5],[247,5]]},"1825":{"position":[[99,2],[169,5]]},"1875":{"position":[[243,2],[634,2]]},"1879":{"position":[[485,2]]},"2202":{"position":[[673,4],[781,4]]},"2476":{"position":[[361,2]]},"2770":{"position":[[64,3],[212,2],[1226,6]]},"2920":{"position":[[361,8]]},"2922":{"position":[[268,8]]},"2924":{"position":[[226,8]]},"2972":{"position":[[381,5]]},"2976":{"position":[[51,6],[257,4]]},"2978":{"position":[[98,5],[379,4]]},"2980":{"position":[[65,5],[352,5]]},"3014":{"position":[[99,2],[183,5],[487,5]]},"3066":{"position":[[258,2],[648,2]]},"3070":{"position":[[499,2]]},"3443":{"position":[[518,4],[626,4]]}}}],["421bf374",{"_index":4566,"t":{"1166":{"position":[[1101,9]]}}}],["421d",{"_index":4757,"t":{"1495":{"position":[[5514,4],[5617,4]]},"2662":{"position":[[5576,4],[5679,4]]}}}],["4254",{"_index":4645,"t":{"1262":{"position":[[1055,4]]}}}],["426614174000",{"_index":5694,"t":{"3349":{"position":[[4774,14]]}}}],["4283",{"_index":4881,"t":{"1577":{"position":[[1367,4]]},"2770":{"position":[[1331,4]]}}}],["42hmac",{"_index":4874,"t":{"1577":{"position":[[184,6]]},"2770":{"position":[[184,6]]}}}],["43",{"_index":3879,"t":{"696":{"position":[[961,2]]},"1875":{"position":[[654,2]]},"3066":{"position":[[655,2]]}}}],["4318:4318",{"_index":5620,"t":{"3327":{"position":[[608,9]]}}}],["4375",{"_index":5098,"t":{"1885":{"position":[[824,4],[892,4]]},"3076":{"position":[[788,4],[856,4]]}}}],["439d",{"_index":4887,"t":{"1577":{"position":[[1487,4]]},"2770":{"position":[[1451,4]]}}}],["43content",{"_index":4438,"t":{"1123":{"position":[[363,9]]},"1127":{"position":[[396,9]]},"2204":{"position":[[363,9]]},"2208":{"position":[[396,9]]}}}],["44",{"_index":3439,"t":{"481":{"position":[[5870,4]]}}}],["443",{"_index":4211,"t":{"1001":{"position":[[369,3]]},"2108":{"position":[[369,3]]},"3309":{"position":[[369,3]]}}}],["447f",{"_index":4775,"t":{"1495":{"position":[[7314,4]]},"2662":{"position":[[7394,4]]}}}],["4499",{"_index":5194,"t":{"2160":{"position":[[290,5]]},"2375":{"position":[[185,4]]},"3389":{"position":[[290,5]]},"3663":{"position":[[185,4]]}}}],["44ec",{"_index":5403,"t":{"2413":{"position":[[1300,4]]},"3580":{"position":[[1300,4]]}}}],["45",{"_index":3445,"t":{"481":{"position":[[6034,3]]}}}],["4500",{"_index":5193,"t":{"2160":{"position":[[148,5],[346,6]]},"3389":{"position":[[148,5],[346,6]]}}}],["4501",{"_index":5187,"t":{"2146":{"position":[[6271,5]]},"3375":{"position":[[6300,5]]}}}],["4502",{"_index":3757,"t":{"610":{"position":[[409,4]]},"1545":{"position":[[409,4]]},"2714":{"position":[[409,4]]}}}],["4561",{"_index":3216,"t":{"477":{"position":[[2269,4]]},"479":{"position":[[1324,4]]}}}],["45c8",{"_index":5404,"t":{"2413":{"position":[[1305,4]]},"3580":{"position":[[1305,4]]}}}],["461.3",{"_index":1445,"t":{"86":{"position":[[1114,5]]}}}],["4648",{"_index":3110,"t":{"471":{"position":[[970,4]]},"473":{"position":[[3307,4],[3438,4]]},"477":{"position":[[2213,4]]},"479":{"position":[[1268,4]]}}}],["468n",{"_index":3415,"t":{"481":{"position":[[4393,5]]}}}],["46content",{"_index":5735,"t":{"3467":{"position":[[993,9]]}}}],["476.5",{"_index":1448,"t":{"86":{"position":[[1228,5]]}}}],["4791",{"_index":5400,"t":{"2413":{"position":[[1175,4]]},"3580":{"position":[[1175,4]]}}}],["47cb",{"_index":3788,"t":{"612":{"position":[[967,4],[1070,4],[1165,4],[1278,4],[1389,4],[1495,4],[1600,4]]},"1547":{"position":[[1136,4],[1239,4],[1334,4],[1447,4],[1558,4],[1664,4],[1769,4]]},"2716":{"position":[[1136,4],[1239,4],[1334,4],[1447,4],[1558,4],[1664,4],[1769,4]]}}}],["48.3µ",{"_index":3482,"t":{"483":{"position":[[959,6]]}}}],["4815",{"_index":4457,"t":{"1131":{"position":[[420,4]]},"2212":{"position":[[420,4]]},"3453":{"position":[[236,4]]}}}],["483b",{"_index":3261,"t":{"477":{"position":[[5019,4]]},"479":{"position":[[4000,4]]}}}],["49",{"_index":4269,"t":{"1049":{"position":[[1374,2]]},"2230":{"position":[[1374,2]]},"3417":{"position":[[1374,2]]}}}],["490b22231e74",{"_index":4584,"t":{"1176":{"position":[[308,14],[675,14],[1095,14]]}}}],["4911",{"_index":4719,"t":{"1495":{"position":[[998,4],[1547,4],[8050,4],[9895,4]]},"2662":{"position":[[1013,4],[1562,4],[8147,4],[10040,4]]}}}],["492.2",{"_index":1444,"t":{"86":{"position":[[921,5]]}}}],["4971",{"_index":4617,"t":{"1207":{"position":[[1187,4]]},"2339":{"position":[[1187,4]]},"3595":{"position":[[1162,4]]}}}],["4999",{"_index":4337,"t":{"1077":{"position":[[254,4]]},"2160":{"position":[[234,4],[353,5],[473,4]]},"2375":{"position":[[101,6]]},"3389":{"position":[[234,4],[353,5],[473,4]]},"3663":{"position":[[101,6]]}}}],["499a",{"_index":4431,"t":{"1121":{"position":[[545,4],[597,4],[653,4],[705,4]]},"2202":{"position":[[588,4],[640,4],[696,4],[748,4]]},"3443":{"position":[[433,4],[485,4],[541,4],[593,4]]}}}],["499b",{"_index":3262,"t":{"477":{"position":[[5029,4]]}}}],["4ad6",{"_index":2413,"t":{"214":{"position":[[864,4]]},"3629":{"position":[[865,4]]}}}],["4bc3ca70",{"_index":4885,"t":{"1577":{"position":[[1472,9]]},"2770":{"position":[[1436,9]]}}}],["4c49",{"_index":4709,"t":{"1495":{"position":[[891,4],[1440,4],[1871,4],[7943,4]]},"2662":{"position":[[906,4],[1455,4],[1886,4],[8040,4]]}}}],["4c7b",{"_index":4644,"t":{"1262":{"position":[[1050,4]]}}}],["4cfe",{"_index":5104,"t":{"1885":{"position":[[965,4],[1033,4]]},"3076":{"position":[[929,4],[997,4]]}}}],["4d0f",{"_index":4622,"t":{"1207":{"position":[[1576,4]]}}}],["4ddd",{"_index":4714,"t":{"1495":{"position":[[947,4],[1496,4],[7999,4]]},"2662":{"position":[[962,4],[1511,4],[8096,4]]}}}],["4e9eafcf",{"_index":4712,"t":{"1495":{"position":[[932,9],[1481,9],[7984,9]]},"2662":{"position":[[947,9],[1496,9],[8081,9]]}}}],["4ebc",{"_index":4292,"t":{"1065":{"position":[[1881,4]]},"1067":{"position":[[482,4]]},"1069":{"position":[[394,4]]},"1071":{"position":[[1753,4]]},"1073":{"position":[[1768,4]]},"2146":{"position":[[1998,4]]},"2148":{"position":[[482,4]]},"2150":{"position":[[394,4]]},"2152":{"position":[[1753,4]]},"2154":{"position":[[1768,4]]},"2156":{"position":[[1857,4]]},"3375":{"position":[[1998,4]]},"3377":{"position":[[482,4]]},"3379":{"position":[[441,4]]},"3381":{"position":[[1556,4]]},"3383":{"position":[[1768,4]]},"3385":{"position":[[1830,4]]}}}],["4f82",{"_index":4568,"t":{"1166":{"position":[[1116,4]]}}}],["4fa8",{"_index":4582,"t":{"1176":{"position":[[298,4],[665,4],[1085,4]]}}}],["4x",{"_index":1451,"t":{"86":{"position":[[1513,2]]}}}],["5",{"_index":95,"t":{"2":{"position":[[1224,1],[2632,2]]},"52":{"position":[[419,1]]},"86":{"position":[[2143,3]]},"110":{"position":[[2461,1],[2481,1],[2501,1]]},"146":{"position":[[367,2]]},"200":{"position":[[127,2]]},"473":{"position":[[3810,2],[3937,2]]},"483":{"position":[[904,2]]},"610":{"position":[[675,1],[1429,1],[1444,1],[1459,1]]},"614":{"position":[[133,1]]},"770":{"position":[[35,1],[305,1]]},"941":{"position":[[123,1]]},"1166":{"position":[[631,2]]},"1178":{"position":[[423,1]]},"1184":{"position":[[2461,1]]},"1270":{"position":[[366,2]]},"1477":{"position":[[179,1]]},"1491":{"position":[[192,1]]},"1545":{"position":[[675,1],[1423,1],[1438,1],[1453,1]]},"1789":{"position":[[35,1],[305,1]]},"2016":{"position":[[123,1]]},"2294":{"position":[[323,2],[1196,2]]},"2670":{"position":[[179,1]]},"2684":{"position":[[192,1]]},"2714":{"position":[[675,1],[1423,1],[1438,1],[1453,1]]},"2722":{"position":[[2042,1],[2137,1]]},"2782":{"position":[[294,1]]},"2978":{"position":[[35,1]]},"3235":{"position":[[123,1]]},"3524":{"position":[[323,2],[1196,2]]}}}],["5*60}token",{"_index":3960,"t":{"770":{"position":[[130,10]]},"1789":{"position":[[130,10]]},"2978":{"position":[[130,10]]}}}],["5.0.1",{"_index":3620,"t":{"566":{"position":[[115,5]]},"973":{"position":[[343,5]]},"979":{"position":[[529,5]]},"2028":{"position":[[343,5]]},"2036":{"position":[[529,5]]},"2662":{"position":[[3275,5]]},"3269":{"position":[[343,5]]},"3277":{"position":[[529,5]]}}}],["5.1.1.x86_64.rpm",{"_index":5487,"t":{"2764":{"position":[[180,16]]}}}],["5.1.1.x86_64.rpmsudo",{"_index":5486,"t":{"2764":{"position":[[132,20]]}}}],["5.56",{"_index":3294,"t":{"477":{"position":[[5646,5]]}}}],["5.6µ",{"_index":3474,"t":{"483":{"position":[[771,5]]}}}],["5.85µ",{"_index":3427,"t":{"481":{"position":[[4588,6]]}}}],["5.8µ",{"_index":3486,"t":{"483":{"position":[[1038,5]]}}}],["50",{"_index":2864,"t":{"307":{"position":[[345,2]]},"309":{"position":[[184,2]]},"481":{"position":[[5968,3]]},"578":{"position":[[231,2]]},"1293":{"position":[[345,2]]},"1295":{"position":[[184,2]]},"1676":{"position":[[1096,2]]},"2440":{"position":[[345,2]]},"2442":{"position":[[184,2]]},"2830":{"position":[[1099,2]]}}}],["500",{"_index":4235,"t":{"1001":{"position":[[1360,3]]},"2108":{"position":[[1360,3]]},"3309":{"position":[[1360,3]]},"3469":{"position":[[100,3],[716,3]]}}}],["5000",{"_index":2757,"t":{"289":{"position":[[796,6]]}}}],["500k",{"_index":38,"t":{"2":{"position":[[458,5],[1694,4]]},"52":{"position":[[743,4]]}}}],["500m",{"_index":4352,"t":{"1093":{"position":[[388,8],[515,7]]},"2176":{"position":[[388,8],[515,7]]},"3353":{"position":[[337,8],[419,8]]},"3405":{"position":[[388,8],[515,7]]}}}],["502",{"_index":4111,"t":{"953":{"position":[[418,3]]},"1001":{"position":[[1364,3]]},"2080":{"position":[[418,3]]},"2108":{"position":[[1364,3]]},"3247":{"position":[[418,3]]},"3309":{"position":[[1364,3]]}}}],["503",{"_index":4236,"t":{"1001":{"position":[[1368,3]]},"1977":{"position":[[272,3]]},"2108":{"position":[[1368,3]]},"3190":{"position":[[245,3]]},"3309":{"position":[[1368,3]]}}}],["504",{"_index":4237,"t":{"1001":{"position":[[1372,3]]},"2108":{"position":[[1372,3]]},"3309":{"position":[[1372,3]]}}}],["50for",{"_index":783,"t":{"18":{"position":[[2530,5]]}}}],["50k",{"_index":3434,"t":{"481":{"position":[[5641,4],[5759,3]]}}}],["50x.html",{"_index":4238,"t":{"1001":{"position":[[1376,10],[1398,9]]},"2108":{"position":[[1376,10],[1398,9]]},"3309":{"position":[[1376,10],[1398,9]]}}}],["51",{"_index":3449,"t":{"481":{"position":[[6095,3],[6112,4]]}}}],["512",{"_index":204,"t":{"2":{"position":[[3519,3],[3702,4],[3738,4]]},"473":{"position":[[1393,3],[3923,3],[4091,3]]},"1277":{"position":[[337,4]]},"1279":{"position":[[297,4]]},"2424":{"position":[[337,4]]},"2426":{"position":[[297,4]]},"3619":{"position":[[337,4]]},"3621":{"position":[[297,4]]}}}],["52.3µ",{"_index":3485,"t":{"483":{"position":[[1026,6]]}}}],["5208",{"_index":5474,"t":{"2744":{"position":[[625,5]]}}}],["5289",{"_index":2412,"t":{"214":{"position":[[859,4]]},"3629":{"position":[[860,4]]}}}],["5432:5432",{"_index":5003,"t":{"1712":{"position":[[387,9]]},"2901":{"position":[[387,9]]}}}],["55",{"_index":3443,"t":{"481":{"position":[[5955,4]]}}}],["559n",{"_index":3414,"t":{"481":{"position":[[4382,5]]}}}],["55content",{"_index":4418,"t":{"1109":{"position":[[1634,9]]},"2190":{"position":[[1688,9]]}}}],["56",{"_index":3771,"t":{"610":{"position":[[1351,2]]},"658":{"position":[[280,6]]},"1065":{"position":[[2017,6],[4941,4],[5178,4]]},"1545":{"position":[[1345,2]]},"1686":{"position":[[280,6]]},"1885":{"position":[[435,3],[1140,5]]},"2146":{"position":[[2134,6],[5064,4],[5301,4]]},"2714":{"position":[[1345,2]]},"2810":{"position":[[280,6]]},"3076":{"position":[[435,3],[1104,5]]},"3375":{"position":[[2134,6],[5093,4],[5330,4]]}}}],["563",{"_index":1386,"t":{"74":{"position":[[1619,3]]}}}],["567",{"_index":2896,"t":{"341":{"position":[[381,4]]},"1329":{"position":[[381,4]]},"2476":{"position":[[381,4]]}}}],["5883",{"_index":1435,"t":{"86":{"position":[[744,4]]}}}],["59",{"_index":3407,"t":{"481":{"position":[[3129,2],[3221,3]]}}}],["59.66",{"_index":3351,"t":{"479":{"position":[[4152,6]]}}}],["5edf",{"_index":4456,"t":{"1131":{"position":[[415,4]]},"2212":{"position":[[415,4]]},"3453":{"position":[[231,4]]}}}],["5kb",{"_index":1429,"t":{"86":{"position":[[513,3]]}}}],["5m",{"_index":2485,"t":{"240":{"position":[[835,3]]},"473":{"position":[[3699,3]]}}}],["5s",{"_index":2459,"t":{"228":{"position":[[563,2]]},"475":{"position":[[837,2],[1016,2]]},"941":{"position":[[146,5]]},"981":{"position":[[456,2],[564,2]]},"1682":{"position":[[1504,5]]},"2016":{"position":[[146,5]]},"2040":{"position":[[456,2],[564,2]]},"2836":{"position":[[1504,5]]},"3235":{"position":[[146,5]]},"3281":{"position":[[456,2],[564,2]]}}}],["5x",{"_index":676,"t":{"16":{"position":[[4413,2]]},"86":{"position":[[375,2]]},"989":{"position":[[926,2]]},"2050":{"position":[[926,2]]},"3289":{"position":[[926,2]]}}}],["6",{"_index":1437,"t":{"86":{"position":[[765,1],[825,1]]},"110":{"position":[[2463,1],[2483,1],[2503,1]]},"477":{"position":[[4740,2],[5168,2]]},"479":{"position":[[3722,2]]},"485":{"position":[[1844,1]]},"610":{"position":[[1384,1],[1399,1],[1414,1]]},"1166":{"position":[[644,2]]},"1270":{"position":[[379,2]]},"1545":{"position":[[1378,1],[1393,1],[1408,1]]},"2294":{"position":[[340,2],[1215,2]]},"2714":{"position":[[1378,1],[1393,1],[1408,1]]},"3524":{"position":[[340,2],[1215,2]]}}}],["6.0",{"_index":3359,"t":{"479":{"position":[[4494,3]]}}}],["6.05µ",{"_index":3480,"t":{"483":{"position":[[907,6]]}}}],["6.25µ",{"_index":3426,"t":{"481":{"position":[[4576,6]]}}}],["6.2µ",{"_index":3341,"t":{"479":{"position":[[3846,5]]}}}],["6.32",{"_index":3428,"t":{"481":{"position":[[4601,5]]}}}],["6.3µ",{"_index":3253,"t":{"477":{"position":[[4866,5]]}}}],["60",{"_index":2041,"t":{"134":{"position":[[1228,4]]},"246":{"position":[[3602,2]]},"283":{"position":[[422,2],[427,2]]},"481":{"position":[[6204,3]]},"714":{"position":[[598,6]]},"770":{"position":[[309,2]]},"1001":{"position":[[1074,4]]},"1003":{"position":[[648,4]]},"1676":{"position":[[998,2],[1081,6]]},"1678":{"position":[[894,2]]},"1680":{"position":[[1375,2]]},"1789":{"position":[[309,2]]},"1893":{"position":[[598,6]]},"2108":{"position":[[1074,4]]},"2110":{"position":[[648,4]]},"2830":{"position":[[1001,2],[1084,6]]},"2832":{"position":[[900,2]]},"2834":{"position":[[1381,2]]},"3084":{"position":[[598,6]]},"3309":{"position":[[1074,4]]},"3311":{"position":[[648,4]]}}}],["60000",{"_index":4149,"t":{"979":{"position":[[1097,5]]},"2036":{"position":[[1113,5]]},"2744":{"position":[[435,6]]},"3277":{"position":[[1113,5]]}}}],["604.7",{"_index":3153,"t":{"473":{"position":[[3374,5],[3452,5]]}}}],["61.53",{"_index":3335,"t":{"479":{"position":[[3667,6]]}}}],["615c0ed3eebb",{"_index":4721,"t":{"1495":{"position":[[1008,14],[1557,14],[8060,14],[9905,13]]},"2662":{"position":[[1023,14],[1572,14],[8157,14],[10050,13]]}}}],["6172992net.ipv4.tcp_rmem",{"_index":75,"t":{"2":{"position":[[855,24]]}}}],["62",{"_index":3111,"t":{"471":{"position":[[981,2]]},"473":{"position":[[3318,2]]},"477":{"position":[[2224,2]]},"479":{"position":[[1279,2]]}}}],["62.8µ",{"_index":3476,"t":{"483":{"position":[[829,6]]}}}],["6292",{"_index":1385,"t":{"74":{"position":[[1608,4]]}}}],["634",{"_index":3800,"t":{"612":{"position":[[1557,3]]},"1547":{"position":[[1726,3]]},"2716":{"position":[[1726,3]]}}}],["6379",{"_index":3096,"t":{"471":{"position":[[688,8]]},"473":{"position":[[2752,8]]},"475":{"position":[[1046,6]]},"979":{"position":[[1009,4]]},"983":{"position":[[512,4]]},"2036":{"position":[[1025,4]]},"2042":{"position":[[512,4]]},"3277":{"position":[[1025,4]]},"3283":{"position":[[512,4]]}}}],["6379:6379",{"_index":1995,"t":{"132":{"position":[[143,9]]}}}],["6380",{"_index":3162,"t":{"475":{"position":[[789,4]]},"981":{"position":[[408,4]]},"983":{"position":[[526,4]]},"2040":{"position":[[408,4]]},"2042":{"position":[[526,4]]},"3281":{"position":[[408,4]]},"3283":{"position":[[526,4]]}}}],["6381",{"_index":3170,"t":{"475":{"position":[[968,4]]},"981":{"position":[[516,4]]},"2040":{"position":[[516,4]]},"3281":{"position":[[516,4]]}}}],["64",{"_index":176,"t":{"2":{"position":[[2869,3]]},"473":{"position":[[1065,2]]},"523":{"position":[[278,2],[334,2]]},"531":{"position":[[190,2],[214,2],[238,2],[264,2],[291,2],[318,2],[350,2],[366,2],[539,2]]},"1431":{"position":[[288,2],[344,2]]},"1439":{"position":[[190,2],[213,2],[237,2],[261,2],[287,2],[314,2],[341,2],[373,2],[389,2],[562,2]]},"2614":{"position":[[303,2],[359,2]]},"2622":{"position":[[190,2],[214,2],[240,2],[267,2],[299,2],[325,2],[498,2]]}}}],["64kb",{"_index":4609,"t":{"1205":{"position":[[15,6]]},"1222":{"position":[[15,6]]},"1237":{"position":[[15,6]]},"1260":{"position":[[15,6]]},"1275":{"position":[[15,6]]},"2271":{"position":[[15,6]]},"2303":{"position":[[15,6]]},"2316":{"position":[[15,6]]},"2337":{"position":[[15,6]]},"2394":{"position":[[15,6]]},"2411":{"position":[[15,6]]},"2422":{"position":[[15,6]]},"3478":{"position":[[15,6]]},"3520":{"position":[[15,6]]},"3537":{"position":[[15,6]]},"3578":{"position":[[15,6]]},"3593":{"position":[[15,6]]},"3608":{"position":[[15,6]]},"3617":{"position":[[15,6]]}}}],["65",{"_index":2039,"t":{"134":{"position":[[1205,3]]},"1001":{"position":[[1051,3]]},"1003":{"position":[[625,3]]},"2108":{"position":[[1051,3]]},"2110":{"position":[[625,3]]},"3309":{"position":[[1051,3]]},"3311":{"position":[[625,3]]}}}],["65.52",{"_index":3358,"t":{"479":{"position":[[4443,6]]}}}],["65.78",{"_index":3254,"t":{"477":{"position":[[4878,6]]}}}],["65500",{"_index":425,"t":{"10":{"position":[[844,5]]}}}],["65535",{"_index":1704,"t":{"110":{"position":[[718,5],[847,7]]},"527":{"position":[[399,5],[411,5]]},"1005":{"position":[[92,7]]},"1435":{"position":[[423,5],[435,5]]},"2112":{"position":[[92,7]]},"2618":{"position":[[423,5],[435,5]]},"3313":{"position":[[92,7]]}}}],["65535if",{"_index":423,"t":{"10":{"position":[[821,7]]}}}],["65536",{"_index":453,"t":{"10":{"position":[[1549,5]]},"949":{"position":[[599,6]]},"951":{"position":[[519,5],[594,5]]},"1205":{"position":[[9,5]]},"1222":{"position":[[9,5]]},"1237":{"position":[[9,5]]},"1260":{"position":[[9,5]]},"1275":{"position":[[9,5]]},"2076":{"position":[[599,6]]},"2078":{"position":[[519,5],[594,5]]},"2271":{"position":[[9,5]]},"2303":{"position":[[9,5]]},"2316":{"position":[[9,5]]},"2337":{"position":[[9,5]]},"2394":{"position":[[9,5]]},"2411":{"position":[[9,5]]},"2422":{"position":[[9,5]]},"3243":{"position":[[599,6]]},"3245":{"position":[[519,5],[594,5]]},"3478":{"position":[[9,5]]},"3520":{"position":[[9,5]]},"3537":{"position":[[9,5]]},"3578":{"position":[[9,5]]},"3593":{"position":[[9,5]]},"3608":{"position":[[9,5]]},"3617":{"position":[[9,5]]}}}],["65537",{"_index":1572,"t":{"98":{"position":[[1173,5]]}}}],["66.08",{"_index":3342,"t":{"479":{"position":[[3858,6]]}}}],["662b",{"_index":3269,"t":{"477":{"position":[[5161,4]]}}}],["666880.00",{"_index":3120,"t":{"473":{"position":[[1110,9]]}}}],["66fdf8d1",{"_index":5096,"t":{"1885":{"position":[[809,9],[877,9]]},"3076":{"position":[[773,9],[841,9]]}}}],["67",{"_index":3442,"t":{"481":{"position":[[5938,3]]}}}],["69",{"_index":3452,"t":{"481":{"position":[[6174,3]]}}}],["696c5294af25",{"_index":5406,"t":{"2413":{"position":[[1315,15]]},"3580":{"position":[[1315,15]]}}}],["6a36",{"_index":4756,"t":{"1495":{"position":[[5509,4],[5612,4]]},"2662":{"position":[[5571,4],[5674,4]]}}}],["6mf7i",{"_index":4876,"t":{"1577":{"position":[[359,5],[765,7],[1038,7]]},"2770":{"position":[[359,5],[765,7],[1038,7]]}}}],["7",{"_index":1729,"t":{"110":{"position":[[2465,1],[2485,1],[2505,1]]},"485":{"position":[[1795,1]]},"531":{"position":[[364,1]]},"1166":{"position":[[654,2]]},"1270":{"position":[[395,2]]},"1439":{"position":[[387,1]]},"2294":{"position":[[369,2],[1245,3]]},"2622":{"position":[[339,1]]},"3524":{"position":[[369,2],[1245,3]]}}}],["7.0",{"_index":3362,"t":{"479":{"position":[[4552,3]]}}}],["7.3µ",{"_index":3483,"t":{"483":{"position":[[971,5]]}}}],["7.52",{"_index":3430,"t":{"481":{"position":[[4668,5]]}}}],["70",{"_index":656,"t":{"16":{"position":[[3540,2]]}}}],["70.08",{"_index":3348,"t":{"479":{"position":[[4091,6]]}}}],["700k",{"_index":3367,"t":{"479":{"position":[[4746,4]]}}}],["71.52",{"_index":3353,"t":{"479":{"position":[[4216,6]]}}}],["72.73",{"_index":3360,"t":{"479":{"position":[[4504,6]]}}}],["75",{"_index":3438,"t":{"481":{"position":[[5840,4]]}}}],["75.86",{"_index":3363,"t":{"479":{"position":[[4562,6]]}}}],["7519",{"_index":5541,"t":{"2946":{"position":[[72,5]]},"2952":{"position":[[101,5]]},"2954":{"position":[[74,5]]}}}],["8",{"_index":483,"t":{"12":{"position":[[221,1]]},"18":{"position":[[580,2]]},"110":{"position":[[2467,1],[2487,1],[2507,1]]},"128":{"position":[[601,2]]},"130":{"position":[[455,2]]},"172":{"position":[[599,1]]},"323":{"position":[[494,1]]},"471":{"position":[[961,1]]},"473":{"position":[[3298,1],[3364,1]]},"477":{"position":[[2204,1],[2260,1],[2289,1],[4657,1],[4729,1],[4794,1],[4852,1],[4918,1],[5017,1],[5083,1],[5149,1],[5209,1],[5277,1],[5372,1],[5439,1],[5501,1],[5559,1],[5623,1]]},"479":{"position":[[1259,1],[1315,1],[1344,1],[1372,1],[3640,1],[3711,1],[3775,1],[3832,1],[3899,1],[3998,1],[4064,1],[4129,1],[4189,1],[4257,1],[4353,1],[4420,1],[4482,1],[4540,1],[4603,1]]},"481":{"position":[[4380,1],[4448,1],[4511,1],[4574,1],[4641,1]]},"483":{"position":[[757,1],[827,1],[893,1],[916,2],[957,1],[1024,1]]},"531":{"position":[[380,1]]},"1065":{"position":[[5133,4]]},"1166":{"position":[[664,2]]},"1270":{"position":[[408,2]]},"1309":{"position":[[494,1]]},"1439":{"position":[[204,1],[403,1]]},"2146":{"position":[[5256,4]]},"2258":{"position":[[1526,1]]},"2294":{"position":[[390,2]]},"2296":{"position":[[672,1]]},"2456":{"position":[[494,1]]},"3375":{"position":[[5285,4]]},"3486":{"position":[[672,1]]},"3492":{"position":[[1526,1]]},"3524":{"position":[[390,2]]}}}],["8.65",{"_index":2095,"t":{"146":{"position":[[131,5]]}}}],["8.74",{"_index":3258,"t":{"477":{"position":[[4945,5]]}}}],["8.82",{"_index":3277,"t":{"477":{"position":[[5300,5]]}}}],["80",{"_index":3441,"t":{"481":{"position":[[5924,3],[6004,4]]},"1001":{"position":[[214,4],[357,3]]},"1049":{"position":[[300,6],[1051,4]]},"2108":{"position":[[214,4],[357,3]]},"2230":{"position":[[300,6],[1051,4]]},"3309":{"position":[[214,4],[357,3]]},"3417":{"position":[[300,6],[1051,4]]}}}],["80.00",{"_index":3357,"t":{"479":{"position":[[4375,6]]}}}],["8000",{"_index":1992,"t":{"130":{"position":[[3237,4]]},"132":{"position":[[967,7]]},"160":{"position":[[183,5]]},"896":{"position":[[47,8]]},"918":{"position":[[696,5]]},"977":{"position":[[720,4],[890,5],[1125,5]]},"1963":{"position":[[47,8]]},"1993":{"position":[[911,5]]},"2034":{"position":[[720,4],[890,5],[1125,5]]},"3176":{"position":[[47,8]]},"3208":{"position":[[911,5]]},"3275":{"position":[[720,4],[890,5],[1125,5]]}}}],["8000:8000",{"_index":2778,"t":{"299":{"position":[[80,9]]},"525":{"position":[[185,9]]},"527":{"position":[[366,9]]},"590":{"position":[[168,9]]},"1433":{"position":[[187,9]]},"1435":{"position":[[390,9]]},"1555":{"position":[[170,9]]},"2616":{"position":[[187,9]]},"2618":{"position":[[390,9]]},"2758":{"position":[[170,9]]}}}],["8001",{"_index":1997,"t":{"132":{"position":[[220,5],[914,6]]},"977":{"position":[[705,6],[1131,5]]},"2034":{"position":[[705,6],[1131,5]]},"3275":{"position":[[705,6],[1131,5]]}}}],["8002",{"_index":4141,"t":{"977":{"position":[[1137,4]]},"2034":{"position":[[1137,4]]},"3275":{"position":[[1137,4]]}}}],["800k",{"_index":1166,"t":{"46":{"position":[[3444,4]]}}}],["8080:8080",{"_index":2766,"t":{"297":{"position":[[79,9]]}}}],["80content",{"_index":4406,"t":{"1109":{"position":[[1042,9]]},"2190":{"position":[[1102,9]]}}}],["81.16",{"_index":3346,"t":{"479":{"position":[[4020,6]]}}}],["814a",{"_index":4758,"t":{"1495":{"position":[[5519,4],[5622,4]]},"2662":{"position":[[5581,4],[5684,4]]}}}],["8192",{"_index":76,"t":{"2":{"position":[[882,4]]},"606":{"position":[[628,4]]},"608":{"position":[[683,4]]},"1535":{"position":[[548,4]]},"1537":{"position":[[527,4]]},"1539":{"position":[[674,4]]},"1541":{"position":[[549,4]]},"1543":{"position":[[749,4]]},"2704":{"position":[[548,4]]},"2706":{"position":[[527,4]]},"2708":{"position":[[674,4]]},"2710":{"position":[[549,4]]},"2712":{"position":[[749,4]]}}}],["82.26",{"_index":3475,"t":{"483":{"position":[[783,6]]}}}],["827b",{"_index":3276,"t":{"477":{"position":[[5289,4]]}}}],["82e1",{"_index":4720,"t":{"1495":{"position":[[1003,4],[1552,4],[8055,4],[9900,4]]},"2662":{"position":[[1018,4],[1567,4],[8152,4],[10045,4]]}}}],["83.05",{"_index":3478,"t":{"483":{"position":[[854,6]]}}}],["83.33",{"_index":3365,"t":{"479":{"position":[[4625,6]]}}}],["83.34",{"_index":3355,"t":{"479":{"position":[[4280,6]]}}}],["8388608",{"_index":77,"t":{"2":{"position":[[887,7]]}}}],["83ac",{"_index":4621,"t":{"1207":{"position":[[1571,4]]}}}],["84",{"_index":3448,"t":{"481":{"position":[[6082,4]]}}}],["84.80",{"_index":3484,"t":{"483":{"position":[[983,6]]}}}],["8447f007083e\",\"version\":\"0.0.0\",\"ping\":25,\"pong\":tru",{"_index":2415,"t":{"214":{"position":[[874,56]]},"3629":{"position":[[875,56]]}}}],["84674.01",{"_index":3119,"t":{"473":{"position":[[984,8]]}}}],["84e8",{"_index":5105,"t":{"1885":{"position":[[970,4],[1038,4]]},"3076":{"position":[[934,4],[1002,4]]}}}],["85",{"_index":1534,"t":{"94":{"position":[[2870,2],[2964,2]]},"96":{"position":[[155,2]]}}}],["851a",{"_index":4882,"t":{"1577":{"position":[[1372,4]]},"2770":{"position":[[1336,4]]}}}],["851c656e81dc",{"_index":4619,"t":{"1207":{"position":[[1197,13]]},"2339":{"position":[[1197,13]]},"3595":{"position":[[1172,13]]}}}],["87334",{"_index":4252,"t":{"1041":{"position":[[446,5],[522,6]]},"2132":{"position":[[446,5],[522,6]]},"3361":{"position":[[446,5],[522,6]]}}}],["88.94",{"_index":3487,"t":{"483":{"position":[[1050,6]]}}}],["892b",{"_index":3268,"t":{"477":{"position":[[5151,4]]},"479":{"position":[[4131,4]]}}}],["8c31697e956f",{"_index":4570,"t":{"1166":{"position":[[1126,14]]}}}],["8c50",{"_index":4293,"t":{"1065":{"position":[[1886,4]]},"1067":{"position":[[487,4]]},"1069":{"position":[[399,4]]},"1071":{"position":[[1758,4]]},"1073":{"position":[[1773,4]]},"2146":{"position":[[2003,4]]},"2148":{"position":[[487,4]]},"2150":{"position":[[399,4]]},"2152":{"position":[[1758,4]]},"2154":{"position":[[1773,4]]},"2156":{"position":[[1862,4]]},"3375":{"position":[[2003,4]]},"3377":{"position":[[487,4]]},"3379":{"position":[[446,4]]},"3381":{"position":[[1561,4]]},"3383":{"position":[[1773,4]]},"3385":{"position":[[1835,4]]}}}],["8dc40072c78e",{"_index":4716,"t":{"1495":{"position":[[957,14],[1506,14],[8009,14]]},"2662":{"position":[[972,14],[1521,14],[8106,14]]}}}],["8s",{"_index":5209,"t":{"2296":{"position":[[776,6]]},"3486":{"position":[[776,6]]}}}],["9",{"_index":1730,"t":{"110":{"position":[[2469,1],[2489,1],[2509,1]]},"481":{"position":[[5883,2]]},"531":{"position":[[204,1]]},"1166":{"position":[[673,2]]},"1439":{"position":[[227,1]]},"2294":{"position":[[415,2]]},"2744":{"position":[[519,1]]},"3524":{"position":[[415,2]]}}}],["9.0",{"_index":3281,"t":{"477":{"position":[[5384,3]]}}}],["9.67µ",{"_index":3419,"t":{"481":{"position":[[4462,6]]}}}],["9.72µ",{"_index":3418,"t":{"481":{"position":[[4450,6]]}}}],["9.7µ",{"_index":3245,"t":{"477":{"position":[[4743,5]]},"479":{"position":[[3725,5]]}}}],["90",{"_index":3453,"t":{"481":{"position":[[6191,4]]},"2026":{"position":[[42,3]]},"2030":{"position":[[904,3]]},"3267":{"position":[[42,3]]},"3271":{"position":[[870,3]]}}}],["9000",{"_index":2016,"t":{"134":{"position":[[149,5],[776,5],[1744,4]]},"287":{"position":[[752,5]]},"918":{"position":[[750,5]]},"926":{"position":[[971,5]]},"1993":{"position":[[965,5]]},"2001":{"position":[[971,5]]},"3208":{"position":[[965,5]]},"3218":{"position":[[1061,5]]}}}],["9000:9000",{"_index":2045,"t":{"134":{"position":[[1652,9]]},"612":{"position":[[588,9]]},"1547":{"position":[[718,9]]},"2716":{"position":[[718,9]]}}}],["907b",{"_index":3275,"t":{"477":{"position":[[5279,4]]},"479":{"position":[[4259,4]]}}}],["91b",{"_index":3345,"t":{"479":{"position":[[4010,3]]}}}],["9230f514",{"_index":4615,"t":{"1207":{"position":[[1173,8]]},"2339":{"position":[[1173,8]]},"3595":{"position":[[1148,8]]}}}],["92714",{"_index":3796,"t":{"612":{"position":[[1233,5]]},"1547":{"position":[[1402,5]]},"2716":{"position":[[1402,5]]}}}],["928",{"_index":1446,"t":{"86":{"position":[[1126,3],[1186,3]]}}}],["93fc",{"_index":5405,"t":{"2413":{"position":[[1310,4]]},"3580":{"position":[[1310,4]]}}}],["95",{"_index":2983,"t":{"379":{"position":[[2998,3]]},"481":{"position":[[2850,2],[3123,2]]}}}],["9733d7f7b61b\",\"version\":\"dev\",\"subs\":{\"#user12\":{}}}}nullnullnullnullnull{\"channel\":\"#user12\",\"data\":{\"data\":{\"input",{"_index":4624,"t":{"1207":{"position":[[1586,118]]}}}],["9750h",{"_index":1433,"t":{"86":{"position":[[705,5],[1075,5]]}}}],["9759",{"_index":4705,"t":{"1495":{"position":[[838,4],[1372,4],[7875,4]]},"2662":{"position":[[853,4],[1387,4],[7972,4]]}}}],["979f",{"_index":4710,"t":{"1495":{"position":[[896,4],[1445,4],[1876,4],[7948,4]]},"2662":{"position":[[911,4],[1460,4],[1891,4],[8045,4]]}}}],["99",{"_index":44,"t":{"2":{"position":[[566,2],[1884,2]]},"52":{"position":[[809,2]]},"481":{"position":[[2853,2],[3126,2]]},"953":{"position":[[328,4]]},"2080":{"position":[[328,4]]},"3247":{"position":[[328,4]]}}}],["999",{"_index":4336,"t":{"1075":{"position":[[221,3]]}}}],["9_",{"_index":1983,"t":{"130":{"position":[[2395,2]]},"136":{"position":[[483,2]]}}}],["9_.]{2",{"_index":4366,"t":{"1093":{"position":[[1221,9]]},"1099":{"position":[[670,9]]},"2176":{"position":[[1221,9]]},"2182":{"position":[[670,9]]},"3405":{"position":[[1221,9]]},"3411":{"position":[[670,9]]}}}],["9_]{2",{"_index":3908,"t":{"734":{"position":[[607,10]]},"1881":{"position":[[607,10]]},"3072":{"position":[[895,10]]}}}],["9aa7",{"_index":2414,"t":{"214":{"position":[[869,4]]},"3629":{"position":[[870,4]]}}}],["9d65",{"_index":4646,"t":{"1262":{"position":[[1060,4]]}}}],["9def",{"_index":4569,"t":{"1166":{"position":[[1121,4]]}}}],["9fac",{"_index":5099,"t":{"1885":{"position":[[829,4],[897,4]]},"3076":{"position":[[793,4],[861,4]]}}}],["9ff3",{"_index":4776,"t":{"1495":{"position":[[7319,4]]},"2662":{"position":[[7399,4]]}}}],["_",{"_index":1353,"t":{"70":{"position":[[1200,2]]},"106":{"position":[[554,1]]},"110":{"position":[[4464,2]]},"112":{"position":[[596,2]]},"471":{"position":[[844,2]]},"473":{"position":[[2553,2]]},"604":{"position":[[864,1]]},"878":{"position":[[1004,1]]},"963":{"position":[[1762,2]]},"1533":{"position":[[952,1]]},"1945":{"position":[[1004,1]]},"2100":{"position":[[1762,2]]},"2345":{"position":[[2137,1],[2256,1],[2376,1]]},"2355":{"position":[[1889,1],[2012,1],[2117,1]]},"2359":{"position":[[1669,1]]},"2702":{"position":[[952,1]]},"3158":{"position":[[1004,1]]},"3259":{"position":[[1762,2]]},"3349":{"position":[[2938,1],[3278,1],[3362,1],[3511,1]]},"3351":{"position":[[1956,1],[2039,1],[2202,1],[2348,1]]},"3633":{"position":[[2137,1],[2256,1],[2376,1]]},"3643":{"position":[[1889,1],[2012,1],[2117,1]]},"3647":{"position":[[1669,1]]}}}],["__dirnam",{"_index":2682,"t":{"283":{"position":[[802,9],[864,9]]}}}],["__init__.pi",{"_index":1812,"t":{"124":{"position":[[321,11]]},"126":{"position":[[246,14],[300,14],[589,14]]},"128":{"position":[[402,14],[1725,14]]},"130":{"position":[[212,14]]}}}],["__main__",{"_index":4316,"t":{"1065":{"position":[[5404,11]]},"2146":{"position":[[5527,11]]},"3375":{"position":[[5556,11]]}}}],["__name__",{"_index":4315,"t":{"1065":{"position":[[5392,8]]},"2146":{"position":[[5515,8]]},"3375":{"position":[[5544,8]]}}}],["_exampl",{"_index":1240,"t":{"56":{"position":[[24,9]]}}}],["a253",{"_index":4703,"t":{"1495":{"position":[[828,4],[1362,4],[7865,4]]},"2662":{"position":[[843,4],[1377,4],[7962,4]]}}}],["a456",{"_index":5693,"t":{"3349":{"position":[[4769,4]]}}}],["a4cc",{"_index":3789,"t":{"612":{"position":[[972,4],[1075,4],[1170,4],[1283,4],[1394,4],[1500,4],[1605,4]]},"1547":{"position":[[1141,4],[1244,4],[1339,4],[1452,4],[1563,4],[1669,4],[1774,4]]},"2716":{"position":[[1141,4],[1244,4],[1339,4],[1452,4],[1563,4],[1669,4],[1774,4]]}}}],["a70c",{"_index":4432,"t":{"1121":{"position":[[550,4],[602,4],[658,4],[710,4]]},"2202":{"position":[[593,4],[645,4],[701,4],[753,4]]},"3443":{"position":[[438,4],[490,4],[546,4],[598,4]]}}}],["a78ae18e31c7",{"_index":4889,"t":{"1577":{"position":[[1497,14]]},"2770":{"position":[[1461,14]]}}}],["a9b2",{"_index":4583,"t":{"1176":{"position":[[303,4],[670,4],[1090,4]]}}}],["ab61c9341ef",{"_index":4777,"t":{"1495":{"position":[[7324,12]]},"2662":{"position":[[7404,12]]}}}],["abc",{"_index":4642,"t":{"1262":{"position":[[999,7],[1377,3]]},"2413":{"position":[[1125,7],[1456,3]]},"3580":{"position":[[1125,7],[1456,3]]}}}],["abil",{"_index":2322,"t":{"200":{"position":[[198,7],[1237,7]]},"266":{"position":[[26,7]]},"379":{"position":[[1087,7]]},"385":{"position":[[1053,7]]},"391":{"position":[[1285,7]]},"403":{"position":[[462,7],[549,7]]},"640":{"position":[[60,7]]},"1617":{"position":[[60,7]]},"1633":{"position":[[39,7]]},"2359":{"position":[[56,7]]},"2804":{"position":[[60,7]]},"2850":{"position":[[39,7]]},"3647":{"position":[[56,7]]}}}],["abli",{"_index":2110,"t":{"148":{"position":[[349,5]]},"256":{"position":[[77,4]]},"2610":{"position":[[1958,5]]}}}],["ably.com",{"_index":1295,"t":{"62":{"position":[[1705,8]]}}}],["abnorm",{"_index":2945,"t":{"363":{"position":[[221,8]]},"1383":{"position":[[224,8]]},"2534":{"position":[[236,8]]}}}],["abov",{"_index":140,"t":{"2":{"position":[[2152,5]]},"16":{"position":[[2346,5]]},"34":{"position":[[2664,5]]},"42":{"position":[[402,6],[503,7]]},"46":{"position":[[63,5]]},"54":{"position":[[1173,5]]},"72":{"position":[[277,5]]},"74":{"position":[[813,5]]},"78":{"position":[[737,6]]},"102":{"position":[[162,5]]},"106":{"position":[[1558,6]]},"166":{"position":[[431,6]]},"186":{"position":[[43,5],[543,5]]},"204":{"position":[[1819,6]]},"214":{"position":[[1540,5]]},"250":{"position":[[1416,6]]},"301":{"position":[[551,6]]},"307":{"position":[[531,5]]},"347":{"position":[[1861,6]]},"349":{"position":[[254,7]]},"379":{"position":[[3942,5]]},"473":{"position":[[3892,5],[4045,5]]},"477":{"position":[[6424,5]]},"479":{"position":[[800,6],[2170,5],[5465,6]]},"481":{"position":[[484,5],[1004,6],[4113,5]]},"491":{"position":[[13,6]]},"497":{"position":[[246,5]]},"560":{"position":[[394,5]]},"604":{"position":[[831,6]]},"718":{"position":[[294,6]]},"728":{"position":[[144,5]]},"730":{"position":[[138,5]]},"734":{"position":[[442,6],[1803,6]]},"754":{"position":[[284,6]]},"758":{"position":[[293,5]]},"764":{"position":[[8,5]]},"858":{"position":[[411,6]]},"886":{"position":[[294,5]]},"918":{"position":[[833,5]]},"965":{"position":[[2120,6],[3489,5],[3746,6]]},"977":{"position":[[505,6]]},"985":{"position":[[1197,5]]},"1041":{"position":[[615,6]]},"1071":{"position":[[992,5]]},"1073":{"position":[[2948,6]]},"1079":{"position":[[359,6]]},"1089":{"position":[[69,6]]},"1091":{"position":[[195,5]]},"1109":{"position":[[243,5]]},"1141":{"position":[[650,7]]},"1172":{"position":[[605,5]]},"1182":{"position":[[420,5]]},"1293":{"position":[[531,5]]},"1335":{"position":[[1865,6]]},"1337":{"position":[[254,7]]},"1359":{"position":[[246,5]]},"1495":{"position":[[3464,5],[10145,6]]},"1501":{"position":[[238,5],[436,5]]},"1515":{"position":[[244,5]]},"1521":{"position":[[914,5]]},"1533":{"position":[[919,6]]},"1567":{"position":[[782,6]]},"1639":{"position":[[13,5]]},"1678":{"position":[[568,6]]},"1680":{"position":[[1016,6]]},"1682":{"position":[[0,5]]},"1725":{"position":[[411,6]]},"1773":{"position":[[284,6]]},"1777":{"position":[[293,5]]},"1783":{"position":[[8,5]]},"1881":{"position":[[442,6],[1690,6]]},"1897":{"position":[[423,6]]},"1931":{"position":[[144,5]]},"1933":{"position":[[138,5]]},"1935":{"position":[[150,5]]},"1937":{"position":[[1016,5]]},"1953":{"position":[[294,5]]},"1993":{"position":[[1048,5]]},"2034":{"position":[[505,6]]},"2044":{"position":[[1197,5]]},"2102":{"position":[[4633,5],[4891,6]]},"2122":{"position":[[426,7]]},"2132":{"position":[[615,6]]},"2152":{"position":[[992,5],[4689,6]]},"2154":{"position":[[2948,6]]},"2162":{"position":[[359,6]]},"2172":{"position":[[43,6]]},"2174":{"position":[[195,5]]},"2190":{"position":[[303,5]]},"2222":{"position":[[650,7]]},"2240":{"position":[[903,7]]},"2250":{"position":[[420,5]]},"2440":{"position":[[531,5]]},"2482":{"position":[[2107,6]]},"2484":{"position":[[254,7]]},"2510":{"position":[[246,5]]},"2530":{"position":[[1934,5]]},"2632":{"position":[[238,5],[436,5]]},"2646":{"position":[[244,5]]},"2662":{"position":[[3487,6],[10265,6]]},"2690":{"position":[[914,5]]},"2702":{"position":[[919,6]]},"2726":{"position":[[782,6]]},"2832":{"position":[[574,6]]},"2834":{"position":[[1022,6]]},"2836":{"position":[[0,5]]},"2856":{"position":[[13,5]]},"2914":{"position":[[475,6]]},"2962":{"position":[[302,6]]},"3072":{"position":[[1978,6]]},"3090":{"position":[[423,6]]},"3126":{"position":[[144,5]]},"3128":{"position":[[138,5]]},"3130":{"position":[[150,5]]},"3132":{"position":[[165,5]]},"3134":{"position":[[1016,5]]},"3166":{"position":[[294,5]]},"3198":{"position":[[1023,6]]},"3208":{"position":[[1048,5]]},"3261":{"position":[[4633,5],[4891,6]]},"3275":{"position":[[505,6]]},"3285":{"position":[[1197,5]]},"3337":{"position":[[686,7]]},"3347":{"position":[[1472,5]]},"3351":{"position":[[438,6],[1487,6]]},"3361":{"position":[[615,6]]},"3371":{"position":[[1419,6]]},"3381":{"position":[[795,5],[4692,6]]},"3383":{"position":[[2948,6]]},"3391":{"position":[[359,6]]},"3401":{"position":[[43,6]]},"3403":{"position":[[195,5]]},"3463":{"position":[[650,7]]},"3467":{"position":[[275,5]]},"3551":{"position":[[903,7]]},"3561":{"position":[[420,5]]},"3629":{"position":[[1541,5]]}}}],["absolut",{"_index":646,"t":{"16":{"position":[[3037,10]]},"3229":{"position":[[234,10]]}}}],["abstract",{"_index":919,"t":{"30":{"position":[[261,9]]},"46":{"position":[[514,9]]},"68":{"position":[[799,10]]},"108":{"position":[[368,11]]},"166":{"position":[[1757,9]]},"198":{"position":[[948,8]]},"283":{"position":[[1803,9]]},"1268":{"position":[[417,9]]},"2292":{"position":[[420,9]]},"2530":{"position":[[39,9]]},"3484":{"position":[[420,9]]}}}],["abus",{"_index":3667,"t":{"576":{"position":[[561,7]]},"1583":{"position":[[669,7]]},"1682":{"position":[[198,7]]},"2778":{"position":[[670,7]]},"2836":{"position":[[198,7]]}}}],["abv8vxcmzcd556o2f2mnl1uou58gnr",{"_index":5028,"t":{"1827":{"position":[[398,30]]},"3016":{"position":[[398,30]]}}}],["accept",{"_index":939,"t":{"30":{"position":[[1319,6]]},"32":{"position":[[92,6]]},"106":{"position":[[57,6],[155,7],[173,6],[484,9]]},"108":{"position":[[1335,9],[1423,9]]},"110":{"position":[[415,6],[1188,6],[1432,9],[2200,6],[2299,9]]},"112":{"position":[[23,9],[347,6],[559,9]]},"130":{"position":[[3021,6]]},"136":{"position":[[1974,6]]},"146":{"position":[[199,6]]},"236":{"position":[[1318,10]]},"347":{"position":[[1690,10]]},"475":{"position":[[2318,7]]},"578":{"position":[[654,6]]},"742":{"position":[[520,8]]},"1065":{"position":[[3606,9]]},"1109":{"position":[[946,9]]},"1335":{"position":[[1694,10]]},"1495":{"position":[[8755,9]]},"1521":{"position":[[982,8]]},"1585":{"position":[[703,6]]},"1741":{"position":[[628,9],[1170,8],[1587,9]]},"1743":{"position":[[1492,8]]},"1761":{"position":[[520,8]]},"1877":{"position":[[375,10]]},"1977":{"position":[[178,6]]},"1987":{"position":[[82,6]]},"2146":{"position":[[3723,9]]},"2190":{"position":[[1006,9]]},"2269":{"position":[[116,6]]},"2301":{"position":[[119,6]]},"2482":{"position":[[1939,10]]},"2662":{"position":[[8872,9]]},"2690":{"position":[[982,8]]},"2782":{"position":[[702,6]]},"2930":{"position":[[628,9],[1170,8],[1587,9]]},"2932":{"position":[[1492,8]]},"2950":{"position":[[556,8]]},"3190":{"position":[[151,6]]},"3202":{"position":[[55,6]]},"3375":{"position":[[3723,9]]},"3476":{"position":[[116,6]]},"3518":{"position":[[119,6]]}}}],["acceptstream",{"_index":1769,"t":{"112":{"position":[[312,13]]}}}],["acceptunistream",{"_index":1728,"t":{"110":{"position":[[2252,16]]}}}],["access",{"_index":724,"t":{"16":{"position":[[6740,6]]},"20":{"position":[[1309,6]]},"40":{"position":[[810,6]]},"162":{"position":[[673,6]]},"178":{"position":[[3082,6]]},"188":{"position":[[953,6]]},"198":{"position":[[429,7]]},"206":{"position":[[381,6]]},"246":{"position":[[683,6]]},"256":{"position":[[1143,6]]},"293":{"position":[[492,6]]},"299":{"position":[[1087,6]]},"385":{"position":[[1693,6]]},"600":{"position":[[462,6]]},"652":{"position":[[127,6]]},"682":{"position":[[31,6]]},"694":{"position":[[226,6],[900,7]]},"696":{"position":[[1209,6]]},"704":{"position":[[72,6],[350,6],[386,6]]},"740":{"position":[[294,7]]},"760":{"position":[[147,10]]},"788":{"position":[[76,6]]},"798":{"position":[[142,6]]},"808":{"position":[[472,6]]},"858":{"position":[[371,6]]},"926":{"position":[[695,6]]},"937":{"position":[[181,6]]},"1071":{"position":[[289,6]]},"1190":{"position":[[184,6]]},"1487":{"position":[[155,6],[202,6]]},"1619":{"position":[[744,6]]},"1699":{"position":[[127,6]]},"1725":{"position":[[371,6]]},"1733":{"position":[[31,6]]},"1749":{"position":[[191,6]]},"1751":{"position":[[196,6]]},"1753":{"position":[[255,6]]},"1759":{"position":[[294,7],[392,6]]},"1779":{"position":[[187,10]]},"1839":{"position":[[73,6]]},"1849":{"position":[[139,6]]},"1865":{"position":[[1463,9]]},"1875":{"position":[[902,6]]},"1889":{"position":[[342,6]]},"1895":{"position":[[1024,6]]},"1897":{"position":[[779,6]]},"1985":{"position":[[187,6]]},"2001":{"position":[[695,6]]},"2012":{"position":[[181,6]]},"2152":{"position":[[289,6],[4262,6]]},"2680":{"position":[[155,6],[202,6]]},"2780":{"position":[[237,6]]},"2806":{"position":[[744,6]]},"2823":{"position":[[127,6]]},"2914":{"position":[[429,6]]},"2922":{"position":[[31,6]]},"2938":{"position":[[191,6]]},"2940":{"position":[[196,6]]},"2942":{"position":[[255,6]]},"2948":{"position":[[282,7]]},"2968":{"position":[[200,10]]},"3030":{"position":[[73,6]]},"3040":{"position":[[139,6]]},"3056":{"position":[[1463,9]]},"3066":{"position":[[899,6]]},"3080":{"position":[[342,6]]},"3088":{"position":[[1024,6]]},"3090":{"position":[[779,6]]},"3200":{"position":[[187,6]]},"3218":{"position":[[785,6]]},"3231":{"position":[[181,6]]},"3381":{"position":[[289,6],[494,6],[4265,6]]},"3427":{"position":[[726,6]]}}}],["access_log",{"_index":2024,"t":{"134":{"position":[[736,10]]}}}],["accoci",{"_index":4943,"t":{"1650":{"position":[[562,10]]},"2867":{"position":[[562,10]]}}}],["accommod",{"_index":2342,"t":{"204":{"position":[[649,13]]},"2610":{"position":[[1344,11]]}}}],["accomplish",{"_index":1701,"t":{"110":{"position":[[197,12]]}}}],["accord",{"_index":1375,"t":{"74":{"position":[[914,9],[1074,9]]},"104":{"position":[[545,11]]},"110":{"position":[[0,9],[2365,9]]},"178":{"position":[[957,9]]},"180":{"position":[[1440,9]]},"289":{"position":[[322,10]]},"479":{"position":[[147,9]]},"560":{"position":[[836,9]]},"622":{"position":[[174,9]]},"652":{"position":[[388,10]]},"654":{"position":[[265,10]]},"878":{"position":[[351,9]]},"1139":{"position":[[53,9]]},"1579":{"position":[[45,9]]},"1591":{"position":[[174,9]]},"1656":{"position":[[47,9]]},"1699":{"position":[[388,10]]},"1702":{"position":[[265,10]]},"1945":{"position":[[351,9]]},"2102":{"position":[[3876,9]]},"2220":{"position":[[53,9]]},"2294":{"position":[[1069,9]]},"2652":{"position":[[478,9]]},"2772":{"position":[[45,9]]},"2786":{"position":[[174,9]]},"2823":{"position":[[388,10]]},"2826":{"position":[[265,10]]},"2873":{"position":[[47,9]]},"3158":{"position":[[351,9]]},"3261":{"position":[[3876,9]]},"3349":{"position":[[1383,9]]},"3461":{"position":[[53,9]]},"3524":{"position":[[1069,9]]}}}],["accordingli",{"_index":2473,"t":{"236":{"position":[[442,11]]}}}],["account",{"_index":732,"t":{"16":{"position":[[7165,7]]},"198":{"position":[[291,7]]},"250":{"position":[[1361,7]]},"347":{"position":[[1837,7]]},"361":{"position":[[1170,7]]},"485":{"position":[[1586,7]]},"548":{"position":[[230,8]]},"550":{"position":[[264,8]]},"1073":{"position":[[972,7]]},"1180":{"position":[[439,8]]},"1335":{"position":[[1841,7]]},"1349":{"position":[[397,7]]},"1381":{"position":[[1175,7]]},"1509":{"position":[[191,8]]},"1515":{"position":[[266,8]]},"1678":{"position":[[294,8]]},"2154":{"position":[[972,7]]},"2482":{"position":[[2083,7]]},"2494":{"position":[[397,7]]},"2532":{"position":[[1241,7]]},"2640":{"position":[[191,8]]},"2646":{"position":[[266,8]]},"2832":{"position":[[294,8]]},"3383":{"position":[[972,7]]}}}],["accur",{"_index":3184,"t":{"475":{"position":[[2570,8]]},"1182":{"position":[[1489,8]]},"2156":{"position":[[1284,8]]},"2250":{"position":[[1489,8]]},"3385":{"position":[[1257,8]]},"3561":{"position":[[1489,8]]}}}],["ace2",{"_index":4618,"t":{"1207":{"position":[[1192,4]]},"2339":{"position":[[1192,4]]},"3595":{"position":[[1167,4]]}}}],["achiev",{"_index":101,"t":{"2":{"position":[[1297,7],[1438,9],[2128,8],[2996,7]]},"14":{"position":[[845,7]]},"16":{"position":[[4240,7]]},"28":{"position":[[523,7]]},"36":{"position":[[127,7]]},"46":{"position":[[1481,7],[3234,7]]},"48":{"position":[[1035,9]]},"50":{"position":[[753,9]]},"52":{"position":[[181,8]]},"86":{"position":[[247,7]]},"134":{"position":[[587,7]]},"172":{"position":[[335,7]]},"192":{"position":[[469,7]]},"228":{"position":[[193,7]]},"244":{"position":[[387,7]]},"246":{"position":[[4066,7]]},"254":{"position":[[1736,7]]},"341":{"position":[[26,7]]},"363":{"position":[[143,7]]},"381":{"position":[[482,7]]},"385":{"position":[[1788,8]]},"391":{"position":[[523,7]]},"467":{"position":[[1203,8]]},"481":{"position":[[3164,7],[3731,7]]},"485":{"position":[[971,7]]},"985":{"position":[[59,8]]},"1061":{"position":[[161,9]]},"1329":{"position":[[26,7]]},"1383":{"position":[[146,7]]},"1495":{"position":[[8329,7]]},"1573":{"position":[[53,8]]},"1611":{"position":[[500,9]]},"1623":{"position":[[136,8]]},"1627":{"position":[[312,8]]},"1797":{"position":[[311,7]]},"1877":{"position":[[33,7]]},"2044":{"position":[[59,8]]},"2142":{"position":[[161,9]]},"2476":{"position":[[26,7]]},"2530":{"position":[[2364,7]]},"2534":{"position":[[149,7]]},"2662":{"position":[[8432,7]]},"2722":{"position":[[1662,9]]},"2732":{"position":[[53,8]]},"2736":{"position":[[500,9]]},"2840":{"position":[[136,8]]},"2844":{"position":[[312,8]]},"2986":{"position":[[277,7]]},"3285":{"position":[[59,8]]},"3345":{"position":[[1202,7]]},"3371":{"position":[[161,9]]},"3467":{"position":[[2166,7]]},"3476":{"position":[[637,9]]},"3518":{"position":[[617,9]]}}}],["ack",{"_index":2633,"t":{"262":{"position":[[2905,3]]},"335":{"position":[[635,3],[807,3]]},"337":{"position":[[140,3]]},"1323":{"position":[[712,3],[884,3]]},"1325":{"position":[[140,3]]},"2470":{"position":[[712,3],[884,3]]},"2472":{"position":[[140,3]]}}}],["acknowledg",{"_index":1694,"t":{"108":{"position":[[1215,17]]}}}],["acl",{"_index":4131,"t":{"975":{"position":[[262,3]]},"979":{"position":[[623,3]]},"2030":{"position":[[228,3]]},"2036":{"position":[[598,3]]},"3271":{"position":[[228,3]]},"3277":{"position":[[598,3]]}}}],["acm",{"_index":3035,"t":{"397":{"position":[[498,4]]},"1049":{"position":[[397,4],[776,4],[912,4],[1024,4]]},"1319":{"position":[[675,4]]},"2230":{"position":[[397,4],[776,4],[912,4],[1024,4]]},"2466":{"position":[[675,4]]},"3417":{"position":[[397,4],[776,4],[912,4],[1024,4]]}}}],["acquir",{"_index":3113,"t":{"473":{"position":[[94,8]]}}}],["act",{"_index":4338,"t":{"1079":{"position":[[112,4],[151,4]]},"1975":{"position":[[161,4]]},"2162":{"position":[[112,4],[151,4]]},"2530":{"position":[[206,4]]},"3060":{"position":[[86,4]]},"3066":{"position":[[49,4]]},"3072":{"position":[[373,4]]},"3188":{"position":[[134,4]]},"3391":{"position":[[112,4],[151,4]]}}}],["action",{"_index":1010,"t":{"34":{"position":[[2317,7]]},"78":{"position":[[1079,7]]},"164":{"position":[[37,7]]},"254":{"position":[[745,7],[849,6]]},"459":{"position":[[236,7]]},"678":{"position":[[168,6]]},"1184":{"position":[[1519,7]]},"1459":{"position":[[236,7]]},"1495":{"position":[[9265,7],[9297,7]]},"1729":{"position":[[168,6]]},"2357":{"position":[[725,6]]},"2434":{"position":[[1352,7]]},"2600":{"position":[[244,7]]},"2662":{"position":[[9404,7],[9440,7]]},"2744":{"position":[[735,6],[801,7],[898,6],[1096,6],[1153,6]]},"2750":{"position":[[248,7]]},"2752":{"position":[[70,6]]},"2918":{"position":[[168,6]]},"3324":{"position":[[613,6],[662,7],[730,8]]},"3345":{"position":[[759,6]]},"3612":{"position":[[1379,7]]},"3645":{"position":[[725,6]]}}}],["action=\"/login",{"_index":2690,"t":{"283":{"position":[[1049,15]]}}}],["activ",{"_index":115,"t":{"2":{"position":[[1624,6]]},"6":{"position":[[169,6]]},"8":{"position":[[463,8],[1100,8]]},"16":{"position":[[1070,6],[5898,6]]},"18":{"position":[[132,6],[3304,6]]},"20":{"position":[[778,6]]},"44":{"position":[[137,6],[1177,6],[1600,6],[1854,6],[2018,6]]},"46":{"position":[[1362,6],[3449,6]]},"122":{"position":[[381,8]]},"178":{"position":[[2537,7]]},"222":{"position":[[194,6]]},"230":{"position":[[497,6]]},"246":{"position":[[2522,8],[3557,6]]},"252":{"position":[[845,8]]},"264":{"position":[[1298,6]]},"351":{"position":[[71,6],[120,6],[529,6],[898,6]]},"355":{"position":[[1185,6],[1627,6]]},"369":{"position":[[425,6],[566,6]]},"383":{"position":[[676,6]]},"391":{"position":[[1075,7]]},"393":{"position":[[869,6]]},"405":{"position":[[213,7]]},"425":{"position":[[142,6]]},"431":{"position":[[100,6]]},"443":{"position":[[513,6],[552,6],[677,8]]},"469":{"position":[[1210,6]]},"473":{"position":[[625,6]]},"475":{"position":[[3559,8]]},"513":{"position":[[171,6]]},"558":{"position":[[323,6]]},"576":{"position":[[326,8],[476,6]]},"652":{"position":[[250,6]]},"678":{"position":[[192,6],[457,6],[539,6]]},"682":{"position":[[676,6],[865,6],[1030,7],[1402,6],[1426,6]]},"686":{"position":[[16,6],[683,6]]},"710":{"position":[[438,6]]},"742":{"position":[[309,8]]},"1129":{"position":[[16,6],[50,6],[607,6],[940,6]]},"1184":{"position":[[2288,6]]},"1339":{"position":[[71,6],[120,6],[529,6],[898,6]]},"1347":{"position":[[1185,6],[1627,6]]},"1349":{"position":[[112,7]]},"1375":{"position":[[171,6]]},"1389":{"position":[[425,6],[566,6]]},"1415":{"position":[[64,6]]},"1579":{"position":[[26,6],[336,6]]},"1583":{"position":[[528,8],[612,6]]},"1629":{"position":[[1371,6]]},"1647":{"position":[[538,6]]},"1699":{"position":[[250,6]]},"1729":{"position":[[192,6],[452,6],[534,6]]},"1733":{"position":[[676,6],[865,6],[1030,7],[1402,6],[1426,6]]},"1737":{"position":[[665,6]]},"1761":{"position":[[309,8]]},"1887":{"position":[[821,6]]},"2122":{"position":[[511,8]]},"2124":{"position":[[400,8]]},"2126":{"position":[[139,9]]},"2210":{"position":[[16,6],[50,6],[654,6],[982,6]]},"2381":{"position":[[171,6]]},"2436":{"position":[[1185,6],[1627,6]]},"2482":{"position":[[1386,6]]},"2486":{"position":[[71,6],[120,6],[529,6],[898,6]]},"2494":{"position":[[117,7]]},"2526":{"position":[[171,6]]},"2530":{"position":[[1174,6],[2022,6],[2081,6]]},"2540":{"position":[[430,6],[571,6]]},"2564":{"position":[[277,6]]},"2572":{"position":[[68,6]]},"2772":{"position":[[26,6],[336,6]]},"2778":{"position":[[529,8],[613,6]]},"2823":{"position":[[250,6]]},"2846":{"position":[[1371,6]]},"2918":{"position":[[192,6],[452,6],[534,6]]},"2922":{"position":[[640,6],[829,6],[994,7],[1366,6],[1390,6]]},"2926":{"position":[[665,6]]},"2972":{"position":[[229,8]]},"3068":{"position":[[442,10]]},"3078":{"position":[[821,6]]},"3324":{"position":[[1224,6],[1911,8]]},"3337":{"position":[[771,8],[903,6]]},"3339":{"position":[[400,8]]},"3341":{"position":[[139,9]]},"3345":{"position":[[520,6]]},"3435":{"position":[[29,6]]},"3451":{"position":[[16,6],[50,6],[714,6],[1042,6]]},"3669":{"position":[[171,6]]}}}],["active\":1627107289",{"_index":3861,"t":{"682":{"position":[[435,20]]},"1733":{"position":[[435,20]]},"2922":{"position":[[399,20]]}}}],["actual",{"_index":134,"t":{"2":{"position":[[2031,8]]},"8":{"position":[[560,8]]},"10":{"position":[[1589,8]]},"12":{"position":[[892,8]]},"14":{"position":[[92,8]]},"16":{"position":[[290,8],[1267,8],[1890,9],[5520,9]]},"18":{"position":[[444,9]]},"20":{"position":[[344,7],[456,6],[591,6],[1496,8]]},"34":{"position":[[2012,9]]},"42":{"position":[[1996,6]]},"50":{"position":[[1850,8]]},"82":{"position":[[101,10]]},"84":{"position":[[109,11]]},"86":{"position":[[178,8]]},"92":{"position":[[74,6]]},"94":{"position":[[1506,9],[2722,9]]},"112":{"position":[[254,9]]},"116":{"position":[[489,8]]},"122":{"position":[[690,9]]},"130":{"position":[[3314,9]]},"172":{"position":[[590,8]]},"198":{"position":[[349,9],[1386,6],[1611,6]]},"204":{"position":[[1322,6]]},"236":{"position":[[749,8],[1175,6]]},"246":{"position":[[2940,8]]},"252":{"position":[[51,9]]},"301":{"position":[[2086,8]]},"361":{"position":[[96,6]]},"385":{"position":[[1526,8]]},"387":{"position":[[959,8]]},"389":{"position":[[901,8]]},"465":{"position":[[41,8]]},"477":{"position":[[6722,9]]},"481":{"position":[[379,11],[2518,8]]},"742":{"position":[[549,6]]},"764":{"position":[[1512,6]]},"808":{"position":[[426,8]]},"951":{"position":[[555,8]]},"965":{"position":[[328,6],[3534,6]]},"969":{"position":[[347,9]]},"1071":{"position":[[643,9]]},"1137":{"position":[[360,8]]},"1170":{"position":[[237,8]]},"1381":{"position":[[96,6]]},"1465":{"position":[[41,8]]},"1505":{"position":[[110,8]]},"1639":{"position":[[787,9]]},"1761":{"position":[[549,6]]},"1783":{"position":[[1512,6]]},"1861":{"position":[[308,6]]},"2024":{"position":[[347,9]]},"2078":{"position":[[555,8]]},"2102":{"position":[[328,6],[3417,8],[4678,6]]},"2152":{"position":[[643,9],[4509,9]]},"2218":{"position":[[360,8]]},"2355":{"position":[[682,6]]},"2506":{"position":[[468,8]]},"2532":{"position":[[100,6]]},"2606":{"position":[[45,8]]},"2636":{"position":[[110,8]]},"2856":{"position":[[787,9]]},"3052":{"position":[[308,6]]},"3245":{"position":[[555,8]]},"3261":{"position":[[328,6],[3417,8],[4678,6]]},"3265":{"position":[[347,9]]},"3381":{"position":[[4512,9]]},"3459":{"position":[[360,8]]},"3643":{"position":[[682,6]]}}}],["ad",{"_index":1108,"t":{"44":{"position":[[1413,6]]},"56":{"position":[[5,6]]},"74":{"position":[[724,5],[1760,6]]},"242":{"position":[[304,6]]},"262":{"position":[[1137,5]]},"264":{"position":[[1220,5]]},"270":{"position":[[654,5]]},"301":{"position":[[3012,5]]},"321":{"position":[[575,6],[756,5],[1258,6]]},"375":{"position":[[374,5]]},"391":{"position":[[212,6]]},"399":{"position":[[455,5]]},"481":{"position":[[3994,6],[4128,5],[5622,5]]},"648":{"position":[[470,6]]},"666":{"position":[[366,6]]},"961":{"position":[[1026,5]]},"985":{"position":[[963,6]]},"1019":{"position":[[402,5]]},"1087":{"position":[[467,5]]},"1107":{"position":[[91,5]]},"1109":{"position":[[2068,6]]},"1111":{"position":[[405,6]]},"1307":{"position":[[575,6],[756,5],[1258,6]]},"1319":{"position":[[14,5]]},"1495":{"position":[[1253,6]]},"1507":{"position":[[401,6]]},"1629":{"position":[[1293,5]]},"1631":{"position":[[454,5]]},"1694":{"position":[[470,6]]},"1710":{"position":[[366,6]]},"1975":{"position":[[0,5]]},"1977":{"position":[[0,5]]},"1987":{"position":[[0,5]]},"2032":{"position":[[377,5],[573,5],[768,5],[1001,5]]},"2038":{"position":[[380,5],[594,5],[807,5],[1058,5]]},"2044":{"position":[[963,6]]},"2086":{"position":[[25,6],[506,6]]},"2098":{"position":[[1092,5]]},"2156":{"position":[[0,5]]},"2170":{"position":[[467,5]]},"2188":{"position":[[94,5]]},"2190":{"position":[[2122,6]]},"2192":{"position":[[405,6]]},"2454":{"position":[[575,6],[756,5],[1258,6]]},"2466":{"position":[[14,5]]},"2482":{"position":[[262,6]]},"2638":{"position":[[401,6]]},"2662":{"position":[[1268,6]]},"2818":{"position":[[470,6]]},"2846":{"position":[[1293,5]]},"2848":{"position":[[454,5]]},"2899":{"position":[[366,6]]},"3253":{"position":[[25,6],[506,6]]},"3257":{"position":[[1092,5]]},"3285":{"position":[[963,6]]},"3345":{"position":[[858,6]]},"3399":{"position":[[467,5]]},"3427":{"position":[[94,5]]},"3431":{"position":[[1630,6]]},"3433":{"position":[[480,6]]},"3467":{"position":[[1841,6]]},"3612":{"position":[[2209,7]]}}}],["adad13b1",{"_index":4434,"t":{"1121":{"position":[[638,9],[690,9]]},"2202":{"position":[[681,9],[733,9]]},"3443":{"position":[[526,9],[578,9]]}}}],["adapt",{"_index":1170,"t":{"46":{"position":[[3516,7]]},"58":{"position":[[655,7]]},"64":{"position":[[800,5]]}}}],["add",{"_index":482,"t":{"12":{"position":[[209,4]]},"46":{"position":[[430,3]]},"54":{"position":[[111,3]]},"126":{"position":[[710,3]]},"128":{"position":[[2053,3]]},"134":{"position":[[615,3],[1664,3]]},"138":{"position":[[325,3]]},"156":{"position":[[443,3]]},"164":{"position":[[811,3]]},"166":{"position":[[2049,3],[2100,3]]},"168":{"position":[[180,3],[437,3]]},"204":{"position":[[139,3]]},"228":{"position":[[64,3],[206,3]]},"246":{"position":[[4096,3]]},"260":{"position":[[360,3]]},"264":{"position":[[488,3]]},"266":{"position":[[384,3]]},"270":{"position":[[793,3]]},"283":{"position":[[2749,3]]},"301":{"position":[[558,3]]},"371":{"position":[[1006,3],[1081,3]]},"399":{"position":[[192,3]]},"475":{"position":[[2618,4]]},"477":{"position":[[3691,3]]},"479":{"position":[[5480,3]]},"483":{"position":[[109,3],[533,3]]},"519":{"position":[[194,3]]},"560":{"position":[[127,3]]},"580":{"position":[[249,3]]},"604":{"position":[[38,3]]},"706":{"position":[[383,4]]},"712":{"position":[[653,4]]},"912":{"position":[[844,4]]},"922":{"position":[[112,3]]},"979":{"position":[[40,3]]},"985":{"position":[[611,3]]},"1043":{"position":[[1046,3]]},"1049":{"position":[[46,3]]},"1065":{"position":[[3291,3]]},"1081":{"position":[[288,3]]},"1093":{"position":[[1949,3]]},"1095":{"position":[[448,3]]},"1107":{"position":[[412,3]]},"1391":{"position":[[1006,3],[1081,3]]},"1495":{"position":[[8804,3]]},"1499":{"position":[[1104,3]]},"1507":{"position":[[175,3]]},"1533":{"position":[[38,3]]},"1549":{"position":[[664,3]]},"1587":{"position":[[272,3]]},"1629":{"position":[[662,3]]},"1635":{"position":[[0,3]]},"1658":{"position":[[143,3]]},"1662":{"position":[[265,3]]},"1680":{"position":[[378,4]]},"1885":{"position":[[1397,4]]},"1891":{"position":[[653,4]]},"1981":{"position":[[844,4]]},"1997":{"position":[[112,3]]},"2036":{"position":[[40,3]]},"2044":{"position":[[611,3]]},"2134":{"position":[[1058,3]]},"2146":{"position":[[3408,3]]},"2164":{"position":[[288,3]]},"2176":{"position":[[1949,3]]},"2178":{"position":[[448,3]]},"2188":{"position":[[415,3]]},"2230":{"position":[[46,3]]},"2542":{"position":[[1021,3],[1100,3]]},"2630":{"position":[[1104,3]]},"2638":{"position":[[175,3]]},"2662":{"position":[[8922,3]]},"2702":{"position":[[38,3]]},"2718":{"position":[[664,3]]},"2774":{"position":[[363,3]]},"2834":{"position":[[378,4]]},"2846":{"position":[[662,3]]},"2852":{"position":[[0,3]]},"2869":{"position":[[714,4]]},"2875":{"position":[[143,3]]},"2879":{"position":[[265,3]]},"3076":{"position":[[1361,4]]},"3082":{"position":[[653,4]]},"3194":{"position":[[844,4]]},"3212":{"position":[[112,3]]},"3277":{"position":[[40,3]]},"3285":{"position":[[611,3]]},"3351":{"position":[[675,3]]},"3363":{"position":[[1058,3]]},"3375":{"position":[[3408,3]]},"3393":{"position":[[288,3]]},"3405":{"position":[[1614,3],[2161,3]]},"3407":{"position":[[448,3]]},"3417":{"position":[[46,3]]},"3427":{"position":[[324,3]]}}}],["addeventlistener(\"unload",{"_index":5775,"t":{"3612":{"position":[[2217,26]]}}}],["addit",{"_index":578,"t":{"14":{"position":[[1694,10]]},"28":{"position":[[1267,10]]},"56":{"position":[[212,10]]},"62":{"position":[[709,9],[940,8],[1316,8]]},"76":{"position":[[131,8]]},"88":{"position":[[778,10]]},"100":{"position":[[552,10]]},"116":{"position":[[634,10]]},"138":{"position":[[1097,10]]},"148":{"position":[[483,10]]},"162":{"position":[[1468,10]]},"174":{"position":[[1311,10]]},"178":{"position":[[2566,10]]},"182":{"position":[[1906,10]]},"188":{"position":[[174,10]]},"194":{"position":[[1160,9]]},"204":{"position":[[147,10]]},"206":{"position":[[4,10]]},"208":{"position":[[13,8]]},"226":{"position":[[620,10]]},"228":{"position":[[72,10]]},"246":{"position":[[2044,10]]},"264":{"position":[[384,10]]},"297":{"position":[[557,10]]},"301":{"position":[[184,10]]},"303":{"position":[[265,10]]},"319":{"position":[[1254,10]]},"339":{"position":[[222,10]]},"361":{"position":[[634,8]]},"363":{"position":[[843,10]]},"371":{"position":[[1010,10]]},"373":{"position":[[1153,9]]},"377":{"position":[[138,10]]},"379":{"position":[[1968,10]]},"381":{"position":[[819,10]]},"385":{"position":[[477,10]]},"393":{"position":[[8,8],[224,10]]},"395":{"position":[[94,10]]},"443":{"position":[[798,10]]},"475":{"position":[[2863,10]]},"481":{"position":[[1704,10],[6848,8]]},"483":{"position":[[410,10],[1303,10]]},"558":{"position":[[112,10]]},"562":{"position":[[77,10]]},"576":{"position":[[502,10]]},"578":{"position":[[468,10]]},"600":{"position":[[40,10]]},"640":{"position":[[27,8]]},"648":{"position":[[448,10]]},"666":{"position":[[344,10]]},"694":{"position":[[425,10],[728,10]]},"706":{"position":[[440,10]]},"712":{"position":[[732,10]]},"734":{"position":[[1077,10]]},"752":{"position":[[33,10]]},"760":{"position":[[11,10]]},"987":{"position":[[125,10]]},"1023":{"position":[[10,10]]},"1025":{"position":[[10,10]]},"1065":{"position":[[1593,10]]},"1081":{"position":[[5,10]]},"1089":{"position":[[881,8]]},"1305":{"position":[[1254,10]]},"1327":{"position":[[222,10]]},"1381":{"position":[[634,8]]},"1383":{"position":[[863,10]]},"1391":{"position":[[1010,10]]},"1427":{"position":[[128,10]]},"1503":{"position":[[194,10]]},"1521":{"position":[[239,10]]},"1573":{"position":[[0,10]]},"1585":{"position":[[468,10]]},"1617":{"position":[[27,8]]},"1619":{"position":[[40,10]]},"1629":{"position":[[381,10]]},"1650":{"position":[[689,10],[774,10]]},"1676":{"position":[[414,9]]},"1678":{"position":[[584,10]]},"1694":{"position":[[448,10]]},"1710":{"position":[[344,10]]},"1771":{"position":[[33,10]]},"1779":{"position":[[11,10]]},"1797":{"position":[[348,10]]},"1807":{"position":[[10,10]]},"1809":{"position":[[10,10]]},"1881":{"position":[[1108,10]]},"1885":{"position":[[1454,10]]},"1891":{"position":[[732,10]]},"1893":{"position":[[1251,10]]},"2046":{"position":[[125,10]]},"2086":{"position":[[32,10],[231,10]]},"2124":{"position":[[116,8]]},"2146":{"position":[[1710,10]]},"2164":{"position":[[5,10]]},"2172":{"position":[[855,8]]},"2452":{"position":[[1254,10]]},"2474":{"position":[[222,10]]},"2532":{"position":[[671,8]]},"2534":{"position":[[901,10]]},"2542":{"position":[[1028,10]]},"2560":{"position":[[403,8]]},"2584":{"position":[[131,10]]},"2634":{"position":[[194,10]]},"2690":{"position":[[239,10]]},"2732":{"position":[[0,10]]},"2778":{"position":[[1432,10]]},"2782":{"position":[[467,10]]},"2804":{"position":[[27,8]]},"2806":{"position":[[40,10]]},"2818":{"position":[[448,10]]},"2830":{"position":[[417,9]]},"2832":{"position":[[590,10]]},"2846":{"position":[[381,10]]},"2862":{"position":[[353,10]]},"2867":{"position":[[689,10]]},"2899":{"position":[[344,10]]},"2960":{"position":[[29,10]]},"2966":{"position":[[204,10]]},"2968":{"position":[[11,10]]},"2986":{"position":[[314,10]]},"2996":{"position":[[10,10]]},"2998":{"position":[[10,10]]},"3072":{"position":[[1396,10]]},"3076":{"position":[[1418,10]]},"3082":{"position":[[732,10]]},"3084":{"position":[[1215,10]]},"3198":{"position":[[930,10]]},"3253":{"position":[[32,10],[231,10]]},"3339":{"position":[[116,8]]},"3345":{"position":[[157,10]]},"3349":{"position":[[103,10]]},"3351":{"position":[[3,8]]},"3375":{"position":[[1710,10]]},"3393":{"position":[[5,10]]},"3401":{"position":[[855,8]]},"3431":{"position":[[455,10]]}}}],["addition",{"_index":2259,"t":{"180":{"position":[[977,12]]},"204":{"position":[[485,12]]},"262":{"position":[[1590,12]]},"264":{"position":[[564,12]]},"295":{"position":[[91,12]]},"299":{"position":[[885,12]]},"475":{"position":[[265,12],[1320,12]]},"560":{"position":[[369,12]]},"1489":{"position":[[279,12]]},"1627":{"position":[[0,13]]},"1629":{"position":[[738,12]]},"1741":{"position":[[679,12],[2142,12]]},"1899":{"position":[[148,12]]},"2369":{"position":[[1814,13]]},"2560":{"position":[[335,13]]},"2682":{"position":[[279,12]]},"2844":{"position":[[0,13]]},"2846":{"position":[[738,12]]},"2930":{"position":[[679,12],[2142,12]]},"3092":{"position":[[148,12]]},"3347":{"position":[[1767,12]]},"3657":{"position":[[1814,13]]}}}],["addmessage(ctx.data",{"_index":2187,"t":{"166":{"position":[[1560,21]]}}}],["addpresence(ch",{"_index":1150,"t":{"46":{"position":[[1800,14]]}}}],["addr",{"_index":3206,"t":{"477":{"position":[[1888,6]]}}}],["address",{"_index":1297,"t":{"62":{"position":[[1866,9]]},"98":{"position":[[1957,7]]},"104":{"position":[[206,7]]},"132":{"position":[[1137,7]]},"204":{"position":[[832,9],[908,10]]},"212":{"position":[[496,10]]},"244":{"position":[[654,10]]},"246":{"position":[[2778,7]]},"379":{"position":[[4178,8]]},"451":{"position":[[169,8],[198,7]]},"556":{"position":[[20,9]]},"604":{"position":[[943,9]]},"618":{"position":[[1261,7]]},"686":{"position":[[484,7]]},"776":{"position":[[297,9]]},"894":{"position":[[45,7]]},"918":{"position":[[823,9]]},"926":{"position":[[1013,8]]},"945":{"position":[[160,10]]},"953":{"position":[[357,8]]},"975":{"position":[[118,8]]},"979":{"position":[[281,9]]},"981":{"position":[[253,7]]},"983":{"position":[[900,7]]},"991":{"position":[[88,7],[257,8]]},"1049":{"position":[[482,8],[768,7],[995,7]]},"1149":{"position":[[774,7],[961,7]]},"1235":{"position":[[46,7]]},"1451":{"position":[[236,8],[265,7]]},"1495":{"position":[[3569,7]]},"1533":{"position":[[1031,9]]},"1639":{"position":[[285,7]]},"1737":{"position":[[474,7]]},"1795":{"position":[[297,9]]},"1961":{"position":[[45,7]]},"1993":{"position":[[1038,9]]},"2001":{"position":[[1013,8]]},"2020":{"position":[[160,10]]},"2030":{"position":[[118,8]]},"2032":{"position":[[1199,8]]},"2036":{"position":[[281,9]]},"2038":{"position":[[1256,8]]},"2040":{"position":[[253,7]]},"2042":{"position":[[900,7]]},"2052":{"position":[[88,7],[257,8]]},"2080":{"position":[[357,8]]},"2086":{"position":[[412,8]]},"2230":{"position":[[482,8],[768,7],[995,7]]},"2277":{"position":[[774,7],[961,7]]},"2314":{"position":[[46,7]]},"2482":{"position":[[1155,10]]},"2592":{"position":[[257,8],[286,7]]},"2662":{"position":[[3602,7]]},"2702":{"position":[[1031,9]]},"2780":{"position":[[703,8]]},"2856":{"position":[[285,7]]},"2926":{"position":[[474,7]]},"2984":{"position":[[297,9]]},"3174":{"position":[[45,7]]},"3208":{"position":[[1038,9]]},"3218":{"position":[[1103,8]]},"3239":{"position":[[160,10]]},"3247":{"position":[[357,8]]},"3253":{"position":[[412,8]]},"3271":{"position":[[118,8]]},"3273":{"position":[[1091,8]]},"3277":{"position":[[281,9]]},"3279":{"position":[[1148,8]]},"3281":{"position":[[253,7]]},"3283":{"position":[[900,7]]},"3291":{"position":[[88,7],[257,8]]},"3417":{"position":[[482,8],[768,7],[995,7]]},"3504":{"position":[[774,7],[961,7]]},"3535":{"position":[[46,7]]}}}],["addroomlastmessage(ctx.data);});centrifuge.connect",{"_index":2189,"t":{"166":{"position":[[1607,53]]}}}],["adit",{"_index":2976,"t":{"379":{"position":[[843,9]]}}}],["adjust",{"_index":2292,"t":{"194":{"position":[[103,9]]},"198":{"position":[[24,9]]},"2652":{"position":[[37,8],[491,8]]}}}],["admin",{"_index":1400,"t":{"78":{"position":[[979,5]]},"124":{"position":[[157,5]]},"128":{"position":[[2735,6]]},"206":{"position":[[711,6]]},"214":{"position":[[1352,5]]},"277":{"position":[[605,5]]},"285":{"position":[[92,8]]},"289":{"position":[[922,5]]},"299":{"position":[[1027,5]]},"465":{"position":[[134,5]]},"517":{"position":[[42,5]]},"527":{"position":[[160,8]]},"640":{"position":[[117,5]]},"658":{"position":[[32,5],[75,5],[331,5]]},"854":{"position":[[0,5],[51,5],[134,5]]},"856":{"position":[[113,8]]},"858":{"position":[[54,5],[286,5],[381,5],[437,5],[468,8]]},"920":{"position":[[0,5],[104,5],[131,5]]},"926":{"position":[[38,6],[206,5],[980,5]]},"930":{"position":[[153,5]]},"939":{"position":[[124,5],[214,5],[307,5]]},"1262":{"position":[[1428,5]]},"1353":{"position":[[662,5]]},"1419":{"position":[[9,5]]},"1435":{"position":[[160,8]]},"1465":{"position":[[134,5]]},"1495":{"position":[[1112,5],[1169,5],[1200,5],[1235,5],[1260,8],[1392,8],[1757,5],[1944,5],[7895,8],[9152,5],[9191,5]]},"1521":{"position":[[392,8],[418,8],[847,10],[890,5]]},"1523":{"position":[[658,10],[1008,5],[1062,5],[1199,7],[1642,7]]},"1583":{"position":[[1357,5]]},"1617":{"position":[[117,5]]},"1686":{"position":[[32,5],[75,5],[331,5]]},"1721":{"position":[[0,5],[51,5],[134,5]]},"1723":{"position":[[113,8]]},"1725":{"position":[[54,5],[286,5],[381,5],[437,5],[468,8]]},"1995":{"position":[[0,5],[104,5],[131,5]]},"2001":{"position":[[38,6],[206,5],[980,5]]},"2005":{"position":[[153,5]]},"2014":{"position":[[124,5],[214,5],[307,5]]},"2413":{"position":[[1507,5]]},"2546":{"position":[[707,5]]},"2576":{"position":[[13,5]]},"2606":{"position":[[143,5]]},"2618":{"position":[[160,8]]},"2662":{"position":[[1127,5],[1184,5],[1215,5],[1250,5],[1275,8],[1407,8],[1772,5],[1959,5],[7992,8],[9283,5],[9326,5]]},"2690":{"position":[[392,8],[418,8],[847,10],[890,5]]},"2692":{"position":[[658,10],[1008,5],[1062,5],[1199,7],[1642,7]]},"2778":{"position":[[1537,5]]},"2804":{"position":[[117,5]]},"2810":{"position":[[32,5],[75,5],[331,5]]},"2910":{"position":[[0,5],[51,5],[134,5]]},"2912":{"position":[[113,8]]},"2914":{"position":[[54,5],[324,5],[443,5],[501,5],[532,8]]},"3210":{"position":[[0,5],[104,5],[131,5]]},"3218":{"position":[[38,6],[206,5],[1070,5]]},"3222":{"position":[[153,5]]},"3233":{"position":[[124,5],[214,5],[307,5]]},"3580":{"position":[[1507,5]]},"3629":{"position":[[1353,5]]}}}],["admin.pi",{"_index":1822,"t":{"126":{"position":[[261,11]]}}}],["admin.site.url",{"_index":1902,"t":{"128":{"position":[[2337,18]]}}}],["admin/api",{"_index":4062,"t":{"926":{"position":[[253,11]]},"2001":{"position":[[253,11]]},"3218":{"position":[[253,11]]}}}],["admin/auth",{"_index":4061,"t":{"926":{"position":[[240,12]]},"2001":{"position":[[240,12]]},"3218":{"position":[[240,12]]}}}],["admin_auth_token",{"_index":3846,"t":{"658":{"position":[[229,19]]},"1686":{"position":[[229,19]]},"2810":{"position":[[229,19]]}}}],["admin_handler_prefix",{"_index":4071,"t":{"930":{"position":[[106,20]]},"2005":{"position":[[106,20]]},"3222":{"position":[[106,20]]}}}],["admin_insecur",{"_index":4014,"t":{"858":{"position":[[483,17]]},"939":{"position":[[50,14]]},"1725":{"position":[[483,17]]},"2014":{"position":[[50,14]]},"2914":{"position":[[547,17]]},"3233":{"position":[[50,14]]}}}],["admin_password",{"_index":2723,"t":{"285":{"position":[[107,17]]},"527":{"position":[[104,17]]},"854":{"position":[[64,14],[272,14]]},"856":{"position":[[128,17]]},"858":{"position":[[102,14],[507,17]]},"1435":{"position":[[104,17]]},"1495":{"position":[[858,17],[1407,17],[1780,14],[7910,17]]},"1721":{"position":[[64,14],[272,14]]},"1723":{"position":[[128,17]]},"1725":{"position":[[102,14],[507,17]]},"2618":{"position":[[104,17]]},"2662":{"position":[[873,17],[1422,17],[1795,14],[8007,17]]},"2910":{"position":[[64,14],[272,14]]},"2912":{"position":[[128,17]]},"2914":{"position":[[104,14],[571,17]]}}}],["admin_secret",{"_index":2724,"t":{"285":{"position":[[137,15]]},"527":{"position":[[134,15]]},"854":{"position":[[154,12],[291,12]]},"856":{"position":[[160,15]]},"858":{"position":[[121,12],[539,15]]},"1435":{"position":[[134,15]]},"1495":{"position":[[916,15],[1465,15],[7968,15]]},"1721":{"position":[[154,12],[291,12]]},"1723":{"position":[[160,15]]},"1725":{"position":[[121,12],[539,15]]},"2618":{"position":[[134,15]]},"2662":{"position":[[931,15],[1480,15],[8065,15]]},"2910":{"position":[[154,12],[291,12]]},"2912":{"position":[[160,15]]},"2914":{"position":[[123,12],[603,15]]}}}],["admin_storag",{"_index":3524,"t":{"519":{"position":[[255,16],[725,16],[997,16]]}}}],["admin_web_path",{"_index":4011,"t":{"856":{"position":[[188,17]]},"1723":{"position":[[188,17]]},"2912":{"position":[[188,17]]}}}],["administr",{"_index":3062,"t":{"435":{"position":[[9,14]]},"457":{"position":[[375,13]]},"1457":{"position":[[372,13]]},"2102":{"position":[[4019,14]]},"2598":{"position":[[374,13]]},"3261":{"position":[[4019,14]]}}}],["administrative/debug",{"_index":3610,"t":{"558":{"position":[[381,20]]},"1129":{"position":[[844,20]]},"2210":{"position":[[886,20]]},"3451":{"position":[[946,20]]}}}],["adminurlpattern",{"_index":1898,"t":{"128":{"position":[[2264,16]]}}}],["adopt",{"_index":1263,"t":{"60":{"position":[[295,8],[664,9]]},"62":{"position":[[976,8]]},"68":{"position":[[2297,9]]},"74":{"position":[[1722,9]]},"88":{"position":[[189,9]]},"116":{"position":[[404,8]]},"407":{"position":[[583,5]]},"443":{"position":[[114,9]]},"1427":{"position":[[114,9]]},"1499":{"position":[[766,5],[1020,5]]},"2584":{"position":[[117,9]]},"2630":{"position":[[766,5],[1020,5]]}}}],["advanc",{"_index":718,"t":{"16":{"position":[[6577,8]]},"489":{"position":[[973,8]]},"1469":{"position":[[1040,8]]},"1583":{"position":[[1246,8]]},"2610":{"position":[[1025,8]]},"2778":{"position":[[1344,8]]}}}],["advantag",{"_index":363,"t":{"8":{"position":[[1262,10]]},"76":{"position":[[402,10]]},"88":{"position":[[821,10]]},"180":{"position":[[922,9]]},"200":{"position":[[24,10]]},"379":{"position":[[4307,10]]},"443":{"position":[[90,10]]},"969":{"position":[[200,11]]},"1268":{"position":[[122,9]]},"1427":{"position":[[90,10]]},"2024":{"position":[[200,11]]},"2292":{"position":[[122,9]]},"2584":{"position":[[93,10]]},"2952":{"position":[[141,12]]},"3265":{"position":[[200,11]]},"3484":{"position":[[122,9]]}}}],["advic",{"_index":239,"t":{"4":{"position":[[403,7]]},"12":{"position":[[1005,6]]},"16":{"position":[[3143,7]]},"18":{"position":[[3083,6]]},"22":{"position":[[5,7]]},"44":{"position":[[1309,6]]},"192":{"position":[[233,7]]},"399":{"position":[[1650,6]]},"854":{"position":[[512,6]]},"953":{"position":[[662,7]]},"1117":{"position":[[707,6]]},"1180":{"position":[[32,7],[82,6],[365,6],[427,6]]},"1182":{"position":[[22,7]]},"1184":{"position":[[14,7]]},"1190":{"position":[[262,7]]},"1513":{"position":[[65,6]]},"1682":{"position":[[695,6]]},"1721":{"position":[[512,6]]},"1861":{"position":[[147,6]]},"1987":{"position":[[245,6]]},"2080":{"position":[[662,7]]},"2160":{"position":[[311,6]]},"2248":{"position":[[32,7],[82,6],[134,6]]},"2250":{"position":[[22,7]]},"2262":{"position":[[331,6]]},"2264":{"position":[[329,6]]},"2345":{"position":[[743,6],[1138,6]]},"2434":{"position":[[2473,6]]},"2644":{"position":[[65,6]]},"2836":{"position":[[695,6]]},"2910":{"position":[[512,6]]},"3052":{"position":[[147,6]]},"3202":{"position":[[218,6]]},"3247":{"position":[[662,7]]},"3389":{"position":[[311,6]]},"3496":{"position":[[331,6]]},"3498":{"position":[[329,6]]},"3559":{"position":[[32,7],[82,6],[134,6]]},"3561":{"position":[[22,7]]},"3612":{"position":[[2672,6]]},"3633":{"position":[[743,6],[1138,6]]}}}],["af14",{"_index":4888,"t":{"1577":{"position":[[1492,4]]},"2770":{"position":[[1456,4]]}}}],["affect",{"_index":206,"t":{"2":{"position":[[3559,6]]},"64":{"position":[[416,6]]},"182":{"position":[[1514,6],[1647,6]]},"204":{"position":[[2344,8]]},"377":{"position":[[2317,6]]},"405":{"position":[[48,6]]},"479":{"position":[[5290,6]]},"632":{"position":[[99,7]]},"732":{"position":[[378,6]]},"748":{"position":[[261,6]]},"750":{"position":[[249,6]]},"1601":{"position":[[99,7]]},"1767":{"position":[[229,6]]},"1769":{"position":[[217,6]]},"1797":{"position":[[1228,6]]},"1937":{"position":[[835,6]]},"1975":{"position":[[518,6]]},"1977":{"position":[[455,6]]},"2796":{"position":[[99,7]]},"2956":{"position":[[229,6]]},"2958":{"position":[[217,6]]},"2986":{"position":[[1194,6]]},"3134":{"position":[[835,6]]},"3188":{"position":[[491,6]]},"3190":{"position":[[428,6]]}}}],["afford",{"_index":817,"t":{"20":{"position":[[859,6]]}}}],["afraid",{"_index":2869,"t":{"319":{"position":[[101,6]]},"1305":{"position":[[101,6]]},"2452":{"position":[[101,6]]}}}],["again",{"_index":662,"t":{"16":{"position":[[3818,5],[5077,6]]},"20":{"position":[[656,5]]},"114":{"position":[[41,5]]},"136":{"position":[[2307,6]]},"180":{"position":[[465,5]]},"260":{"position":[[1208,6]]},"287":{"position":[[1897,6]]},"301":{"position":[[4444,6]]},"309":{"position":[[456,5]]},"477":{"position":[[4109,5]]},"479":{"position":[[721,6],[2485,5]]},"714":{"position":[[374,5]]},"834":{"position":[[157,6]]},"939":{"position":[[145,5]]},"1182":{"position":[[494,6]]},"1184":{"position":[[1203,5]]},"1270":{"position":[[844,6]]},"1295":{"position":[[456,5]]},"1865":{"position":[[621,6]]},"1893":{"position":[[374,5]]},"1987":{"position":[[269,6]]},"2014":{"position":[[145,5]]},"2250":{"position":[[494,6]]},"2345":{"position":[[889,6],[936,7],[5352,5]]},"2355":{"position":[[5002,5],[5035,6]]},"2369":{"position":[[1725,5]]},"2442":{"position":[[456,5]]},"2530":{"position":[[2730,5]]},"2752":{"position":[[237,5]]},"2950":{"position":[[565,5]]},"3056":{"position":[[621,6]]},"3084":{"position":[[374,5]]},"3202":{"position":[[242,6]]},"3233":{"position":[[145,5]]},"3349":{"position":[[4917,6]]},"3561":{"position":[[494,6]]},"3633":{"position":[[889,6],[936,7],[5352,5]]},"3643":{"position":[[5002,5],[5035,6]]},"3657":{"position":[[1725,5]]}}}],["against",{"_index":2515,"t":{"246":{"position":[[806,7]]},"467":{"position":[[1298,7]]},"892":{"position":[[356,7]]},"1571":{"position":[[66,8]]},"1797":{"position":[[418,7],[588,7]]},"1959":{"position":[[356,7]]},"2730":{"position":[[66,8]]},"2986":{"position":[[384,7],[554,7]]},"3172":{"position":[[356,7]]}}}],["agent",{"_index":1700,"t":{"110":{"position":[[138,5]]},"560":{"position":[[208,7]]},"604":{"position":[[435,7]]},"612":{"position":[[490,6]]},"1061":{"position":[[388,7],[615,5]]},"1109":{"position":[[1099,6]]},"1533":{"position":[[524,7]]},"1547":{"position":[[620,6]]},"2142":{"position":[[388,7],[615,5]]},"2190":{"position":[[1159,6]]},"2702":{"position":[[524,7]]},"2716":{"position":[[620,6]]},"3371":{"position":[[388,7],[615,5]]}}}],["aggreg",{"_index":4248,"t":{"1011":{"position":[[165,10],[296,11]]},"2092":{"position":[[165,10],[296,11]]},"3301":{"position":[[165,10],[296,11]]},"3320":{"position":[[165,10],[296,11]]}}}],["agnost",{"_index":2297,"t":{"194":{"position":[[771,8]]},"277":{"position":[[125,8]]},"279":{"position":[[66,8]]},"373":{"position":[[764,8]]},"1353":{"position":[[97,8]]},"2498":{"position":[[345,8]]},"2546":{"position":[[105,8]]}}}],["ago",{"_index":4688,"t":{"1469":{"position":[[29,3]]},"2610":{"position":[[39,3]]}}}],["agpl",{"_index":1311,"t":{"66":{"position":[[65,4]]}}}],["agreement",{"_index":4902,"t":{"1585":{"position":[[529,9]]},"2782":{"position":[[528,9]]}}}],["agress",{"_index":3382,"t":{"481":{"position":[[1075,9]]}}}],["aid",{"_index":5454,"t":{"2720":{"position":[[543,6]]}}}],["aim",{"_index":2082,"t":{"138":{"position":[[1578,3]]},"391":{"position":[[88,5]]},"477":{"position":[[6364,5]]},"481":{"position":[[2569,3]]},"1101":{"position":[[239,3]]}}}],["ajax",{"_index":2081,"t":{"138":{"position":[[819,4]]},"166":{"position":[[387,4]]},"168":{"position":[[1034,4]]},"339":{"position":[[450,4]]},"359":{"position":[[451,4]]},"367":{"position":[[186,4],[354,4]]},"764":{"position":[[1176,4]]},"1019":{"position":[[411,4]]},"1327":{"position":[[450,4]]},"1379":{"position":[[451,4]]},"1387":{"position":[[186,4],[354,4]]},"1495":{"position":[[5960,4]]},"1783":{"position":[[1176,4]]},"2474":{"position":[[450,4]]},"2538":{"position":[[186,4],[360,4]]},"2662":{"position":[[6023,4]]}}}],["ajax/http",{"_index":4665,"t":{"1341":{"position":[[476,9]]},"2488":{"position":[[476,9]]}}}],["aka",{"_index":549,"t":{"14":{"position":[[742,3]]}}}],["alert",{"_index":2590,"t":{"254":{"position":[[641,6]]},"268":{"position":[[183,6]]}}}],["alex",{"_index":5101,"t":{"1885":{"position":[[939,7],[1080,7]]},"3076":{"position":[[903,7],[1044,7]]}}}],["alexand",{"_index":1275,"t":{"62":{"position":[[454,9]]},"772":{"position":[[88,10],[268,10]]},"1131":{"position":[[308,11]]},"1791":{"position":[[88,10],[268,10]]},"2212":{"position":[[308,11]]},"2980":{"position":[[88,10],[373,10]]},"3453":{"position":[[124,11]]}}}],["alg",{"_index":2239,"t":{"178":{"position":[[1500,3],[1616,3]]},"188":{"position":[[583,3],[732,3]]},"2976":{"position":[[199,3],[287,3]]},"2978":{"position":[[321,3],[409,3]]},"2980":{"position":[[294,3],[418,3]]},"3014":{"position":[[429,3],[538,3]]}}}],["algorithm",{"_index":3810,"t":{"618":{"position":[[199,9]]},"742":{"position":[[252,9]]},"776":{"position":[[772,9]]},"965":{"position":[[3400,9]]},"983":{"position":[[1214,10],[1263,9]]},"985":{"position":[[1177,9]]},"1676":{"position":[[205,9],[1488,10]]},"1680":{"position":[[886,9]]},"1761":{"position":[[252,9]]},"1795":{"position":[[950,9]]},"2042":{"position":[[1214,10],[1263,9]]},"2044":{"position":[[1177,9]]},"2262":{"position":[[133,9]]},"2264":{"position":[[143,9]]},"2324":{"position":[[69,9]]},"2345":{"position":[[554,9],[972,11]]},"2347":{"position":[[277,9]]},"2363":{"position":[[312,9]]},"2830":{"position":[[205,9],[1491,10]]},"2834":{"position":[[889,9]]},"2950":{"position":[[295,9]]},"3283":{"position":[[1214,10],[1263,9]]},"3285":{"position":[[1177,9]]},"3496":{"position":[[133,9]]},"3498":{"position":[[143,9]]},"3545":{"position":[[48,9]]},"3633":{"position":[[554,9],[972,11]]},"3635":{"position":[[277,9]]},"3651":{"position":[[312,9]]}}}],["algorithm=\"hs256\").decode()print(token",{"_index":2235,"t":{"178":{"position":[[1351,39]]},"188":{"position":[[433,39]]},"770":{"position":[[172,39]]},"772":{"position":[[145,39]]},"1035":{"position":[[253,39]]},"1789":{"position":[[172,39]]},"1791":{"position":[[145,39]]},"1825":{"position":[[209,39]]},"2978":{"position":[[172,39]]},"2980":{"position":[[145,39]]},"3014":{"position":[[280,39]]}}}],["algorithm=\"hs256\")print(token",{"_index":5551,"t":{"2972":{"position":[[431,30]]}}}],["alia",{"_index":5144,"t":{"2032":{"position":[[283,5]]},"2038":{"position":[[268,5]]},"3273":{"position":[[283,5]]},"3279":{"position":[[268,5]]}}}],["alic",{"_index":5112,"t":{"1885":{"position":[[1218,8]]},"3076":{"position":[[1182,8]]}}}],["align",{"_index":2345,"t":{"204":{"position":[[1202,5]]},"212":{"position":[[818,5]]}}}],["aliv",{"_index":1210,"t":{"50":{"position":[[1759,6]]},"469":{"position":[[1262,5]]},"479":{"position":[[3352,6]]},"604":{"position":[[1308,6]]},"1178":{"position":[[23,5]]},"1533":{"position":[[1396,6],[1670,6]]},"2246":{"position":[[23,5]]},"2482":{"position":[[1806,6]]},"2702":{"position":[[1396,6],[1670,6]]},"3557":{"position":[[23,5]]}}}],["alivecont",{"_index":4405,"t":{"1109":{"position":[[1021,12]]},"2190":{"position":[[1081,12]]}}}],["alloc",{"_index":1458,"t":{"86":{"position":[[2016,9]]},"130":{"position":[[3259,9]]},"132":{"position":[[935,9]]},"475":{"position":[[2628,10],[2672,11],[2734,10],[2945,11],[3324,11]]},"477":{"position":[[915,8],[1697,12],[2356,9],[2384,11],[6106,11]]},"479":{"position":[[5069,10],[5215,11],[5257,10]]},"481":{"position":[[202,9],[2944,10],[6869,5]]},"485":{"position":[[399,10],[598,9],[634,8]]},"2345":{"position":[[158,9]]},"2355":{"position":[[200,9],[311,9]]},"2357":{"position":[[136,9]]},"3633":{"position":[[158,9]]},"3643":{"position":[[200,9],[311,9]]},"3645":{"position":[[136,9]]}}}],["alloc/op",{"_index":3260,"t":{"477":{"position":[[4977,8],[4990,8]]},"479":{"position":[[3958,8],[3971,8]]}}}],["allocs/op",{"_index":1383,"t":{"74":{"position":[[1461,9],[1631,9]]},"86":{"position":[[946,9],[1313,9]]},"471":{"position":[[991,9]]},"473":{"position":[[3397,9]]},"477":{"position":[[2291,9],[5330,9],[5344,9]]},"479":{"position":[[1403,9],[4311,9],[4325,9]]}}}],["allocs/opbenchmarkgoredi",{"_index":3214,"t":{"477":{"position":[[2234,25]]},"479":{"position":[[1289,25]]}}}],["allocs/opbenchmarkmarshalparallel",{"_index":1438,"t":{"86":{"position":[[767,33],[1137,33]]}}}],["allocs/opbenchmarkredigopipelininig",{"_index":3151,"t":{"473":{"position":[[3328,35]]}}}],["allocs/opbenchmarkrueidi",{"_index":3321,"t":{"479":{"position":[[1346,25]]}}}],["allocs/opbenchmarkunmarsh",{"_index":1440,"t":{"86":{"position":[[827,27],[1197,27]]}}}],["allocs/opbenchmarkunmarshalparallel",{"_index":1443,"t":{"86":{"position":[[882,35],[1251,35]]}}}],["allow",{"_index":338,"t":{"8":{"position":[[730,6]]},"12":{"position":[[1323,6]]},"16":{"position":[[4751,5],[6537,6]]},"18":{"position":[[1426,6],[2967,6]]},"28":{"position":[[1594,5]]},"34":{"position":[[1476,6]]},"46":{"position":[[2989,5],[3188,6]]},"48":{"position":[[1029,5]]},"50":{"position":[[746,6]]},"54":{"position":[[20,5]]},"62":{"position":[[1268,7]]},"68":{"position":[[499,5]]},"72":{"position":[[305,5]]},"74":{"position":[[62,5],[478,5]]},"78":{"position":[[86,6]]},"86":{"position":[[320,7]]},"88":{"position":[[579,5]]},"100":{"position":[[85,5]]},"104":{"position":[[421,7]]},"106":{"position":[[785,6]]},"110":{"position":[[95,7]]},"112":{"position":[[926,6]]},"122":{"position":[[616,6],[760,5]]},"132":{"position":[[1116,6],[1220,5],[1566,6],[1705,6]]},"134":{"position":[[114,7]]},"136":{"position":[[1379,8],[1742,8]]},"138":{"position":[[262,5],[1158,5]]},"156":{"position":[[433,6]]},"158":{"position":[[675,6]]},"160":{"position":[[649,6],[919,7]]},"162":{"position":[[1201,5]]},"164":{"position":[[284,6]]},"176":{"position":[[883,6]]},"178":{"position":[[2742,5],[2902,6]]},"180":{"position":[[889,7]]},"188":{"position":[[0,6],[375,8],[686,8]]},"194":{"position":[[987,6]]},"196":{"position":[[554,6]]},"198":{"position":[[515,5]]},"200":{"position":[[436,6]]},"210":{"position":[[459,6]]},"226":{"position":[[33,6]]},"246":{"position":[[2023,7],[2889,7],[4053,6]]},"254":{"position":[[1791,6]]},"262":{"position":[[609,6]]},"266":{"position":[[347,6]]},"281":{"position":[[227,5]]},"299":{"position":[[551,6]]},"321":{"position":[[712,5]]},"329":{"position":[[47,5]]},"339":{"position":[[1255,6]]},"345":{"position":[[14,6]]},"359":{"position":[[171,5],[237,5]]},"361":{"position":[[403,6],[732,6]]},"373":{"position":[[980,6]]},"377":{"position":[[2120,6]]},"381":{"position":[[742,7]]},"383":{"position":[[656,7],[1531,6],[1751,6]]},"385":{"position":[[898,5],[1464,7]]},"387":{"position":[[150,7],[582,7]]},"391":{"position":[[334,6],[891,8],[1347,5],[1382,8]]},"399":{"position":[[23,7]]},"433":{"position":[[22,6]]},"435":{"position":[[31,6]]},"477":{"position":[[6490,7]]},"479":{"position":[[652,7],[2273,5]]},"481":{"position":[[1385,6],[2328,5]]},"497":{"position":[[23,6]]},"503":{"position":[[27,5]]},"507":{"position":[[72,6]]},"525":{"position":[[260,6]]},"541":{"position":[[247,7]]},"554":{"position":[[53,7],[79,7]]},"578":{"position":[[321,7]]},"604":{"position":[[1908,6],[2155,5]]},"644":{"position":[[499,6]]},"652":{"position":[[0,6]]},"654":{"position":[[0,6]]},"662":{"position":[[476,6]]},"696":{"position":[[1057,7]]},"700":{"position":[[43,6]]},"702":{"position":[[235,8]]},"704":{"position":[[523,6]]},"708":{"position":[[55,6]]},"720":{"position":[[54,6]]},"722":{"position":[[153,5]]},"734":{"position":[[110,6]]},"742":{"position":[[370,6]]},"756":{"position":[[381,5]]},"758":{"position":[[76,6],[706,5],[1437,6]]},"764":{"position":[[46,6]]},"788":{"position":[[99,8]]},"830":{"position":[[178,6]]},"892":{"position":[[12,6],[39,7],[488,9],[1050,5]]},"900":{"position":[[14,5]]},"935":{"position":[[51,6]]},"965":{"position":[[129,6],[402,6]]},"969":{"position":[[327,5]]},"973":{"position":[[128,6]]},"989":{"position":[[173,6]]},"991":{"position":[[585,6],[648,6]]},"1027":{"position":[[44,6]]},"1029":{"position":[[693,6]]},"1049":{"position":[[1288,5],[1603,6],[1770,6],[1881,6]]},"1061":{"position":[[154,6]]},"1065":{"position":[[2038,6],[3295,7],[4239,6]]},"1069":{"position":[[229,6]]},"1071":{"position":[[265,6],[1925,8],[3366,6],[3840,5]]},"1073":{"position":[[1961,8],[3179,5]]},"1087":{"position":[[1163,6]]},"1089":{"position":[[954,6]]},"1101":{"position":[[41,5]]},"1109":{"position":[[16,6]]},"1111":{"position":[[23,6]]},"1113":{"position":[[10,6],[927,6]]},"1115":{"position":[[12,6]]},"1117":{"position":[[11,6]]},"1119":{"position":[[8,6]]},"1121":{"position":[[9,6]]},"1123":{"position":[[15,6]]},"1125":{"position":[[8,6]]},"1127":{"position":[[15,6]]},"1131":{"position":[[12,6]]},"1137":{"position":[[305,6]]},"1149":{"position":[[387,8]]},"1164":{"position":[[445,6],[2736,6]]},"1174":{"position":[[112,6],[268,6],[332,6],[415,6]]},"1176":{"position":[[490,6]]},"1184":{"position":[[1581,5]]},"1192":{"position":[[25,5]]},"1205":{"position":[[30,7]]},"1213":{"position":[[41,5],[862,5]]},"1222":{"position":[[30,7]]},"1237":{"position":[[30,7]]},"1260":{"position":[[30,7]]},"1262":{"position":[[75,6]]},"1275":{"position":[[30,7]]},"1307":{"position":[[712,5]]},"1315":{"position":[[47,5]]},"1327":{"position":[[1255,6]]},"1333":{"position":[[14,6]]},"1355":{"position":[[267,5]]},"1359":{"position":[[23,6]]},"1365":{"position":[[27,5]]},"1369":{"position":[[72,6]]},"1379":{"position":[[171,5],[237,5]]},"1381":{"position":[[403,6],[741,6]]},"1417":{"position":[[22,6]]},"1419":{"position":[[18,6]]},"1433":{"position":[[262,6]]},"1473":{"position":[[261,8]]},"1475":{"position":[[392,8],[436,8],[959,8],[1006,8],[1664,8],[1711,8],[1920,5]]},"1481":{"position":[[202,8]]},"1483":{"position":[[73,8]]},"1485":{"position":[[146,8],[192,8]]},"1487":{"position":[[18,5],[107,8]]},"1489":{"position":[[62,5],[101,6],[382,5],[397,8]]},"1495":{"position":[[5080,7],[7671,5]]},"1507":{"position":[[862,5]]},"1509":{"position":[[43,7]]},"1513":{"position":[[121,7]]},"1523":{"position":[[1002,5],[1052,5]]},"1533":{"position":[[2443,6],[2659,5]]},"1569":{"position":[[385,6],[1156,7]]},"1577":{"position":[[428,5]]},"1583":{"position":[[113,6],[507,6],[916,5]]},"1585":{"position":[[321,7]]},"1631":{"position":[[391,8]]},"1668":{"position":[[81,6]]},"1676":{"position":[[71,7],[435,6]]},"1690":{"position":[[499,6]]},"1699":{"position":[[0,6]]},"1702":{"position":[[0,6]]},"1706":{"position":[[476,6]]},"1741":{"position":[[838,5],[1776,6],[2155,5],[2367,5],[2508,5]]},"1743":{"position":[[1071,6],[1226,6],[1731,5],[1882,5],[2025,5],[2174,5]]},"1745":{"position":[[313,6],[522,6],[719,5],[873,5],[1032,5],[1197,5]]},"1747":{"position":[[331,5],[458,6],[671,6],[870,5],[1026,5],[1171,5]]},"1761":{"position":[[370,6]]},"1777":{"position":[[76,6],[964,6]]},"1783":{"position":[[46,6]]},"1811":{"position":[[44,6]]},"1813":{"position":[[693,6]]},"1823":{"position":[[52,6]]},"1839":{"position":[[96,8]]},"1863":{"position":[[907,6]]},"1875":{"position":[[750,7]]},"1877":{"position":[[472,5]]},"1881":{"position":[[110,6]]},"1889":{"position":[[406,7]]},"1895":{"position":[[1087,7]]},"1897":{"position":[[842,7]]},"1899":{"position":[[161,5]]},"1905":{"position":[[60,6]]},"1909":{"position":[[56,6]]},"1911":{"position":[[52,6]]},"1915":{"position":[[57,6]]},"1917":{"position":[[53,6]]},"1921":{"position":[[55,6]]},"1923":{"position":[[91,7],[552,5]]},"1937":{"position":[[153,8]]},"1959":{"position":[[12,6],[39,7],[488,9],[1050,5]]},"1967":{"position":[[14,5]]},"1983":{"position":[[35,6]]},"2010":{"position":[[51,6]]},"2024":{"position":[[327,5]]},"2028":{"position":[[128,6]]},"2050":{"position":[[173,6]]},"2052":{"position":[[585,6],[648,6]]},"2102":{"position":[[129,6],[402,6]]},"2122":{"position":[[29,6]]},"2142":{"position":[[154,6]]},"2146":{"position":[[2155,6],[3412,7],[4356,6]]},"2150":{"position":[[229,6]]},"2152":{"position":[[265,6],[1925,8],[3298,6],[3871,5],[4048,7],[4276,8]]},"2154":{"position":[[1961,8],[3179,5]]},"2170":{"position":[[1163,6]]},"2172":{"position":[[928,6]]},"2190":{"position":[[16,6]]},"2192":{"position":[[23,6]]},"2194":{"position":[[10,6],[892,6]]},"2196":{"position":[[12,6]]},"2198":{"position":[[11,6]]},"2200":{"position":[[8,6]]},"2202":{"position":[[9,6]]},"2204":{"position":[[15,6]]},"2206":{"position":[[8,6]]},"2208":{"position":[[15,6]]},"2212":{"position":[[12,6]]},"2218":{"position":[[305,6]]},"2230":{"position":[[1288,5],[1603,6],[1770,6],[1881,6]]},"2240":{"position":[[564,6]]},"2244":{"position":[[160,6]]},"2252":{"position":[[25,5]]},"2271":{"position":[[30,7]]},"2277":{"position":[[387,8]]},"2296":{"position":[[158,6]]},"2303":{"position":[[30,7]]},"2316":{"position":[[30,7]]},"2337":{"position":[[30,7]]},"2355":{"position":[[7,6],[335,6],[429,5]]},"2387":{"position":[[41,5],[862,5]]},"2394":{"position":[[30,7]]},"2411":{"position":[[30,7]]},"2413":{"position":[[75,6]]},"2422":{"position":[[30,7]]},"2454":{"position":[[712,5]]},"2462":{"position":[[47,5]]},"2474":{"position":[[1255,6]]},"2480":{"position":[[14,6]]},"2510":{"position":[[23,6]]},"2516":{"position":[[27,5]]},"2520":{"position":[[72,6]]},"2532":{"position":[[428,6],[782,6]]},"2548":{"position":[[267,5]]},"2574":{"position":[[23,6]]},"2576":{"position":[[22,6]]},"2616":{"position":[[265,6]]},"2638":{"position":[[862,5]]},"2640":{"position":[[43,7]]},"2644":{"position":[[121,7]]},"2662":{"position":[[5142,7],[7759,5]]},"2666":{"position":[[261,8]]},"2668":{"position":[[392,8],[436,8],[959,8],[1006,8],[1664,8],[1711,8],[1920,5]]},"2674":{"position":[[202,8]]},"2676":{"position":[[73,8]]},"2678":{"position":[[146,8],[192,8]]},"2680":{"position":[[18,5],[107,8]]},"2682":{"position":[[62,5],[101,6],[382,5],[397,8]]},"2692":{"position":[[1002,5],[1052,5]]},"2702":{"position":[[2443,6],[2659,5]]},"2728":{"position":[[385,6],[1156,7]]},"2744":{"position":[[281,7],[487,10],[575,10],[764,7],[780,5],[913,8]]},"2750":{"position":[[174,7]]},"2752":{"position":[[37,7],[80,7],[229,7]]},"2770":{"position":[[428,5]]},"2778":{"position":[[113,6],[508,6],[1014,5]]},"2782":{"position":[[320,7]]},"2814":{"position":[[499,6]]},"2823":{"position":[[0,6]]},"2826":{"position":[[0,6]]},"2830":{"position":[[71,7],[438,6]]},"2848":{"position":[[391,8]]},"2887":{"position":[[81,6]]},"2895":{"position":[[476,6]]},"2930":{"position":[[838,5],[1776,6],[2155,5],[2367,5],[2508,5]]},"2932":{"position":[[1071,6],[1226,6],[1731,5],[1882,5],[2025,5],[2174,5]]},"2934":{"position":[[313,6],[522,6],[719,5],[873,5],[1032,5],[1197,5]]},"2936":{"position":[[331,5],[458,6],[671,6],[870,5],[1026,5],[1171,5]]},"2948":{"position":[[157,5],[379,5]]},"2950":{"position":[[424,6]]},"2966":{"position":[[157,6],[1042,6]]},"2972":{"position":[[670,6]]},"3000":{"position":[[44,6]]},"3002":{"position":[[693,6]]},"3012":{"position":[[52,6]]},"3030":{"position":[[96,8]]},"3054":{"position":[[907,6]]},"3066":{"position":[[762,7]]},"3072":{"position":[[11,6]]},"3080":{"position":[[406,7]]},"3088":{"position":[[1087,7]]},"3090":{"position":[[842,7]]},"3092":{"position":[[161,5]]},"3098":{"position":[[60,6]]},"3102":{"position":[[56,6]]},"3104":{"position":[[52,6]]},"3108":{"position":[[57,6]]},"3110":{"position":[[53,6]]},"3114":{"position":[[55,6]]},"3116":{"position":[[91,7],[552,5]]},"3134":{"position":[[153,8]]},"3172":{"position":[[12,6],[39,7],[488,9],[1050,5]]},"3180":{"position":[[14,5]]},"3196":{"position":[[35,6]]},"3227":{"position":[[51,6]]},"3261":{"position":[[129,6],[402,6]]},"3265":{"position":[[327,5]]},"3269":{"position":[[128,6]]},"3289":{"position":[[173,6]]},"3291":{"position":[[585,6],[648,6]]},"3349":{"position":[[1480,6],[3634,7]]},"3351":{"position":[[1117,6]]},"3371":{"position":[[154,6]]},"3375":{"position":[[2155,6],[3412,7],[4385,6]]},"3379":{"position":[[229,6]]},"3381":{"position":[[265,6],[1728,8],[3101,6],[3874,5],[4051,7],[4279,8]]},"3383":{"position":[[1961,8],[3179,5]]},"3399":{"position":[[1163,6]]},"3401":{"position":[[928,6]]},"3417":{"position":[[1288,5],[1603,6],[1770,6],[1881,6]]},"3431":{"position":[[15,6]]},"3433":{"position":[[36,6]]},"3435":{"position":[[10,6],[960,6]]},"3437":{"position":[[12,6]]},"3439":{"position":[[11,6]]},"3441":{"position":[[8,6]]},"3443":{"position":[[9,6]]},"3445":{"position":[[15,6]]},"3447":{"position":[[8,6]]},"3449":{"position":[[15,6]]},"3453":{"position":[[12,6]]},"3455":{"position":[[6,6]]},"3459":{"position":[[305,6]]},"3478":{"position":[[30,7]]},"3486":{"position":[[158,6]]},"3504":{"position":[[387,8]]},"3520":{"position":[[30,7]]},"3537":{"position":[[30,7]]},"3551":{"position":[[564,6]]},"3555":{"position":[[160,6]]},"3563":{"position":[[25,5]]},"3578":{"position":[[30,7]]},"3580":{"position":[[75,6]]},"3593":{"position":[[30,7]]},"3601":{"position":[[41,5],[862,5]]},"3608":{"position":[[30,7]]},"3617":{"position":[[30,7]]},"3643":{"position":[[7,6],[335,6],[429,5]]}}}],["allow_anonymous_connect_without_token",{"_index":4796,"t":{"1515":{"position":[[109,37]]},"2646":{"position":[[109,37]]}}}],["allow_history_for_anonym",{"_index":5116,"t":{"1913":{"position":[[0,27]]},"1937":{"position":[[652,30],[1439,30]]},"3106":{"position":[[0,27]]},"3134":{"position":[[652,30],[1439,30]]}}}],["allow_history_for_cli",{"_index":5012,"t":{"1745":{"position":[[454,24],[497,24]]},"1911":{"position":[[0,24]]},"2934":{"position":[[454,24],[497,24]]},"3104":{"position":[[0,24]]}}}],["allow_history_for_subscrib",{"_index":5011,"t":{"1745":{"position":[[237,28],[284,28]]},"1909":{"position":[[0,28]]},"1937":{"position":[[614,31],[1401,31]]},"2934":{"position":[[237,28],[284,28]]},"3102":{"position":[[0,28]]},"3134":{"position":[[614,31],[1401,31]]}}}],["allow_presence_for_anonym",{"_index":5117,"t":{"1919":{"position":[[0,28]]},"1937":{"position":[[728,31],[1515,31]]},"3112":{"position":[[0,28]]},"3134":{"position":[[728,31],[1515,31]]}}}],["allow_presence_for_cli",{"_index":5014,"t":{"1747":{"position":[[601,25],[645,25]]},"1917":{"position":[[0,25]]},"2936":{"position":[[601,25],[645,25]]},"3110":{"position":[[0,25]]}}}],["allow_presence_for_subscrib",{"_index":5013,"t":{"1747":{"position":[[380,29],[428,29]]},"1915":{"position":[[0,29]]},"1937":{"position":[[689,32],[1476,32]]},"2936":{"position":[[380,29],[428,29]]},"3108":{"position":[[0,29]]},"3134":{"position":[[689,32],[1476,32]]}}}],["allow_publish_for_anonym",{"_index":5115,"t":{"1907":{"position":[[0,27]]},"1937":{"position":[[577,30],[1364,30]]},"3100":{"position":[[0,27]]},"3134":{"position":[[577,30],[1364,30]]}}}],["allow_publish_for_cli",{"_index":3009,"t":{"385":{"position":[[1255,27]]},"1743":{"position":[[799,24],[1003,24],[1046,24],[1590,26]]},"1903":{"position":[[740,24]]},"1905":{"position":[[0,24]]},"2932":{"position":[[799,24],[1003,24],[1046,24],[1590,26]]},"3096":{"position":[[740,24]]},"3098":{"position":[[0,24]]}}}],["allow_publish_for_subscrib",{"_index":5010,"t":{"1743":{"position":[[749,28],[1150,28],[1197,28]]},"1903":{"position":[[0,28],[65,28],[707,28]]},"1905":{"position":[[150,28]]},"1937":{"position":[[539,31],[1326,31]]},"2932":{"position":[[749,28],[1150,28],[1197,28]]},"3096":{"position":[[0,28],[65,28],[707,28]]},"3098":{"position":[[150,28]]},"3134":{"position":[[539,31],[1326,31]]}}}],["allow_subscribe_for_anonym",{"_index":5009,"t":{"1741":{"position":[[2213,29]]},"1899":{"position":[[204,29]]},"1901":{"position":[[0,29]]},"1937":{"position":[[500,32],[1287,32]]},"2930":{"position":[[2213,29]]},"3092":{"position":[[204,29]]},"3094":{"position":[[0,29]]},"3134":{"position":[[500,32],[1287,32]]}}}],["allow_subscribe_for_cli",{"_index":4785,"t":{"1495":{"position":[[7781,26],[8121,29]]},"1507":{"position":[[991,26]]},"1741":{"position":[[781,26],[1704,26],[1749,26]]},"1877":{"position":[[410,26]]},"1899":{"position":[[0,26]]},"1923":{"position":[[652,29]]},"1937":{"position":[[464,29],[1251,29]]},"2638":{"position":[[991,26]]},"2662":{"position":[[7878,26],[8218,29]]},"2930":{"position":[[781,26],[1704,26],[1749,26]]},"3068":{"position":[[405,26]]},"3092":{"position":[[0,26]]},"3116":{"position":[[652,29]]},"3134":{"position":[[464,29],[1251,29]]}}}],["allow_user_limited_channel",{"_index":2211,"t":{"176":{"position":[[104,27],[198,30],[768,27]]},"1509":{"position":[[92,28]]},"1741":{"position":[[1664,27]]},"1875":{"position":[[1059,27]]},"1921":{"position":[[0,27]]},"2640":{"position":[[92,28]]},"2930":{"position":[[1664,27]]},"3066":{"position":[[1063,27]]},"3114":{"position":[[0,27]]}}}],["allowed_in",{"_index":5473,"t":{"2744":{"position":[[611,13],[837,10],[1005,10]]},"2752":{"position":[[167,10]]}}}],["allowed_origin",{"_index":2000,"t":{"132":{"position":[[288,18],[1145,15]]},"160":{"position":[[270,18]]},"285":{"position":[[197,18]]},"449":{"position":[[442,15]]},"554":{"position":[[147,18],[327,18]]},"570":{"position":[[103,15]]},"572":{"position":[[770,16]]},"878":{"position":[[589,15]]},"884":{"position":[[72,18],[534,15]]},"886":{"position":[[133,16]]},"888":{"position":[[43,16]]},"892":{"position":[[272,15],[614,15],[664,18],[752,18],[862,18],[1162,18]]},"1065":{"position":[[443,15]]},"1449":{"position":[[460,15]]},"1495":{"position":[[1023,18],[1572,18],[4868,15],[4944,15],[4989,15],[5034,18],[5243,15],[8075,18]]},"1945":{"position":[[589,15]]},"1951":{"position":[[72,18],[534,15]]},"1953":{"position":[[133,16]]},"1955":{"position":[[43,16]]},"1959":{"position":[[272,15],[614,15],[664,18],[752,18],[862,18],[1162,18]]},"2146":{"position":[[443,15]]},"2590":{"position":[[475,15]]},"2662":{"position":[[1038,18],[1587,18],[4930,15],[5006,15],[5051,15],[5096,18],[5305,15],[8172,18]]},"3158":{"position":[[589,15]]},"3164":{"position":[[72,18],[534,15]]},"3166":{"position":[[133,16]]},"3168":{"position":[[43,16]]},"3172":{"position":[[272,15],[614,15],[664,18],[752,18],[862,18],[1162,18]]},"3375":{"position":[[443,15]]}}}],["allowedorigin",{"_index":1641,"t":{"104":{"position":[[387,14],[454,14],[1093,15]]}}}],["alo",{"_index":1801,"t":{"122":{"position":[[470,5]]}}}],["along",{"_index":2301,"t":{"196":{"position":[[119,5]]},"250":{"position":[[1521,5]]}}}],["alpn",{"_index":1685,"t":{"106":{"position":[[1583,4]]}}}],["alpnquictransport",{"_index":1675,"t":{"106":{"position":[[1178,17]]}}}],["alreadi",{"_index":820,"t":{"20":{"position":[[1150,7]]},"28":{"position":[[1770,7]]},"42":{"position":[[1416,7]]},"50":{"position":[[2289,7],[2466,7]]},"94":{"position":[[2967,7]]},"108":{"position":[[898,7]]},"116":{"position":[[887,7]]},"118":{"position":[[851,7]]},"122":{"position":[[23,7],[296,8],[519,8],[912,7]]},"130":{"position":[[3246,7]]},"138":{"position":[[148,7]]},"152":{"position":[[14,7]]},"162":{"position":[[661,7]]},"170":{"position":[[139,7]]},"212":{"position":[[151,7]]},"224":{"position":[[164,7]]},"248":{"position":[[156,7]]},"250":{"position":[[747,7],[1931,7]]},"262":{"position":[[2406,7]]},"270":{"position":[[106,7],[429,7],[646,7],[733,7]]},"347":{"position":[[984,7]]},"385":{"position":[[1481,7]]},"399":{"position":[[365,7]]},"477":{"position":[[2826,7]]},"479":{"position":[[1776,7],[2192,7]]},"483":{"position":[[1701,7]]},"485":{"position":[[2012,7]]},"792":{"position":[[20,8],[48,7],[121,7]]},"810":{"position":[[186,7]]},"880":{"position":[[466,7]]},"977":{"position":[[728,7]]},"1047":{"position":[[17,7]]},"1071":{"position":[[433,7]]},"1172":{"position":[[1080,7]]},"1182":{"position":[[392,7],[1149,7]]},"1192":{"position":[[126,7]]},"1335":{"position":[[988,7]]},"1489":{"position":[[134,7]]},"1639":{"position":[[1108,7]]},"1843":{"position":[[18,8],[45,7],[118,7]]},"1947":{"position":[[466,7]]},"2034":{"position":[[728,7]]},"2152":{"position":[[433,7]]},"2228":{"position":[[17,7]]},"2250":{"position":[[392,7],[1149,7]]},"2252":{"position":[[126,7]]},"2345":{"position":[[3900,7],[4102,7],[4320,7]]},"2357":{"position":[[227,7]]},"2482":{"position":[[1052,7]]},"2682":{"position":[[134,7]]},"2856":{"position":[[1108,7]]},"3034":{"position":[[18,8],[45,7],[137,7],[464,7]]},"3160":{"position":[[470,7]]},"3275":{"position":[[728,7]]},"3349":{"position":[[1750,7]]},"3381":{"position":[[468,7]]},"3415":{"position":[[17,7]]},"3561":{"position":[[392,7],[1149,7]]},"3563":{"position":[[126,7]]},"3633":{"position":[[3900,7],[4102,7],[4320,7]]},"3645":{"position":[[227,7]]}}}],["alter",{"_index":5435,"t":{"2610":{"position":[[262,8]]}}}],["altern",{"_index":348,"t":{"8":{"position":[[911,11]]},"28":{"position":[[1683,11],[1957,11]]},"46":{"position":[[3776,11]]},"58":{"position":[[35,11]]},"94":{"position":[[569,11]]},"100":{"position":[[155,11]]},"158":{"position":[[576,11]]},"287":{"position":[[545,14]]},"379":{"position":[[1879,11]]},"399":{"position":[[1955,11]]},"407":{"position":[[888,11]]},"475":{"position":[[3065,13]]},"477":{"position":[[17,11],[6403,11]]},"479":{"position":[[2969,11]]},"694":{"position":[[691,11]]},"981":{"position":[[0,14]]},"1061":{"position":[[473,14]]},"1495":{"position":[[4403,14]]},"2040":{"position":[[0,14]]},"2142":{"position":[[473,14]]},"2434":{"position":[[2807,11]]},"2610":{"position":[[1914,11]]},"2662":{"position":[[4465,14]]},"3281":{"position":[[0,14]]},"3371":{"position":[[473,14]]},"3612":{"position":[[3006,11]]}}}],["although",{"_index":2327,"t":{"200":{"position":[[603,8]]},"854":{"position":[[455,8]]},"999":{"position":[[0,8]]},"1721":{"position":[[455,8]]},"2106":{"position":[[0,8]]},"2910":{"position":[[455,8]]},"2970":{"position":[[0,8]]},"3307":{"position":[[0,8]]}}}],["alway",{"_index":433,"t":{"10":{"position":[[1004,6]]},"12":{"position":[[504,6]]},"42":{"position":[[1967,6]]},"379":{"position":[[2425,6]]},"389":{"position":[[579,6]]},"479":{"position":[[5832,6]]},"505":{"position":[[582,6]]},"892":{"position":[[481,6]]},"1077":{"position":[[692,6]]},"1180":{"position":[[550,6]]},"1341":{"position":[[291,6]]},"1367":{"position":[[582,6]]},"1523":{"position":[[497,6]]},"1676":{"position":[[610,6],[670,6]]},"1743":{"position":[[1534,6]]},"1903":{"position":[[519,6]]},"1905":{"position":[[492,6]]},"1959":{"position":[[481,6]]},"2102":{"position":[[4398,6]]},"2124":{"position":[[302,6]]},"2160":{"position":[[892,6]]},"2361":{"position":[[1014,6]]},"2488":{"position":[[291,6]]},"2518":{"position":[[609,6]]},"2692":{"position":[[497,6]]},"2722":{"position":[[2184,6]]},"2830":{"position":[[613,6],[673,6]]},"2883":{"position":[[1515,6]]},"2932":{"position":[[1534,6]]},"3096":{"position":[[519,6]]},"3098":{"position":[[492,6]]},"3172":{"position":[[481,6]]},"3261":{"position":[[4398,6]]},"3339":{"position":[[302,6]]},"3345":{"position":[[359,6]]},"3347":{"position":[[3403,6],[4059,6],[4139,6]]},"3351":{"position":[[1917,6]]},"3389":{"position":[[892,6]]},"3467":{"position":[[88,6]]},"3649":{"position":[[1014,6]]}}}],["amaz",{"_index":1415,"t":{"84":{"position":[[65,7]]}}}],["amazon",{"_index":4065,"t":{"926":{"position":[[795,6]]},"2001":{"position":[[795,6]]},"3218":{"position":[[885,6]]}}}],["amongst",{"_index":2483,"t":{"240":{"position":[[416,7]]},"467":{"position":[[520,7]]}}}],["amount",{"_index":489,"t":{"12":{"position":[[349,6]]},"18":{"position":[[852,6]]},"44":{"position":[[1892,6]]},"46":{"position":[[466,6]]},"52":{"position":[[851,6],[916,6]]},"196":{"position":[[592,6]]},"246":{"position":[[3914,6]]},"309":{"position":[[53,6]]},"351":{"position":[[1076,6]]},"477":{"position":[[119,6],[3195,6]]},"712":{"position":[[49,7],[208,6],[304,6]]},"1283":{"position":[[199,6]]},"1295":{"position":[[53,6]]},"1339":{"position":[[1076,6]]},"1887":{"position":[[1190,6]]},"1891":{"position":[[49,7],[208,6],[304,6]]},"2430":{"position":[[199,6]]},"2442":{"position":[[53,6]]},"2486":{"position":[[1076,6]]},"3078":{"position":[[1190,6]]},"3082":{"position":[[49,7],[208,6],[304,6]]},"3625":{"position":[[199,6]]}}}],["analog",{"_index":4377,"t":{"1099":{"position":[[0,9]]},"2122":{"position":[[384,10]]},"2182":{"position":[[0,9]]},"2652":{"position":[[167,9]]},"3337":{"position":[[644,10]]},"3411":{"position":[[0,9]]}}}],["analogu",{"_index":879,"t":{"28":{"position":[[168,9]]},"1087":{"position":[[1042,10]]},"1353":{"position":[[456,8]]},"1569":{"position":[[667,8]]},"2170":{"position":[[1042,10]]},"2546":{"position":[[466,8]]},"2728":{"position":[[667,8]]},"3399":{"position":[[1042,10]]},"3455":{"position":[[228,8]]}}}],["analysi",{"_index":639,"t":{"16":{"position":[[2755,8]]}}}],["analyt",{"_index":2430,"t":{"218":{"position":[[359,9]]},"254":{"position":[[1574,9]]},"262":{"position":[[1874,10]]},"266":{"position":[[86,10],[137,10],[506,10]]},"270":{"position":[[310,9]]},"443":{"position":[[289,10]]},"576":{"position":[[208,9]]},"604":{"position":[[486,9]]},"614":{"position":[[16,9]]},"878":{"position":[[894,10]]},"1129":{"position":[[1026,9]]},"1533":{"position":[[574,9]]},"1549":{"position":[[16,9],[634,9]]},"1583":{"position":[[205,9]]},"1633":{"position":[[99,10],[145,10]]},"1666":{"position":[[1177,9]]},"1668":{"position":[[264,9]]},"1945":{"position":[[894,10]]},"2210":{"position":[[1068,9]]},"2347":{"position":[[492,9]]},"2506":{"position":[[1027,9]]},"2702":{"position":[[574,9]]},"2718":{"position":[[16,9],[634,9]]},"2720":{"position":[[234,10]]},"2778":{"position":[[205,9]]},"2850":{"position":[[99,10],[145,10]]},"2883":{"position":[[480,9]]},"2887":{"position":[[264,9]]},"3158":{"position":[[894,10]]},"3451":{"position":[[1128,9]]},"3635":{"position":[[492,9]]}}}],["analyz",{"_index":2642,"t":{"266":{"position":[[253,7]]},"443":{"position":[[366,7]]},"1633":{"position":[[261,7]]},"2850":{"position":[[261,7]]}}}],["and/or",{"_index":4795,"t":{"1511":{"position":[[854,6]]},"2642":{"position":[[854,6]]}}}],["android",{"_index":1198,"t":{"50":{"position":[[1158,7]]},"254":{"position":[[338,8]]},"256":{"position":[[293,8]]},"260":{"position":[[551,8]]},"335":{"position":[[219,7]]},"1101":{"position":[[634,7]]},"1323":{"position":[[219,7]]},"1635":{"position":[[170,8]]},"1650":{"position":[[376,8]]},"2256":{"position":[[199,7]]},"2470":{"position":[[219,7]]},"2852":{"position":[[170,8]]},"2867":{"position":[[376,8]]},"3490":{"position":[[273,7]]}}}],["anewsubscript",{"_index":5287,"t":{"2355":{"position":[[159,16]]},"3643":{"position":[[159,16]]}}}],["anf",{"_index":5171,"t":{"2102":{"position":[[2655,3]]},"3261":{"position":[[2655,3]]}}}],["angl",{"_index":3115,"t":{"473":{"position":[[749,5]]}}}],["annot",{"_index":3936,"t":{"758":{"position":[[153,9]]},"1577":{"position":[[1682,9]]},"1777":{"position":[[153,9]]},"2770":{"position":[[1646,9]]},"2966":{"position":[[172,10]]}}}],["announc",{"_index":1256,"t":{"60":{"position":[[69,8]]},"248":{"position":[[27,9]]},"373":{"position":[[24,8]]}}}],["annoy",{"_index":2972,"t":{"379":{"position":[[330,9]]}}}],["anomali",{"_index":5585,"t":{"3324":{"position":[[276,9]]}}}],["anonym",{"_index":2276,"t":{"186":{"position":[[456,9]]},"198":{"position":[[419,9],[1188,9]]},"618":{"position":[[123,9]]},"704":{"position":[[0,9],[57,9],[530,9]]},"732":{"position":[[145,12]]},"734":{"position":[[1211,12],[1380,12]]},"740":{"position":[[284,9],[320,9]]},"910":{"position":[[150,9],[226,9]]},"945":{"position":[[30,9]]},"1041":{"position":[[317,9]]},"1678":{"position":[[429,9]]},"1680":{"position":[[800,9]]},"1741":{"position":[[1805,9],[2176,9]]},"1759":{"position":[[284,9],[424,9]]},"1803":{"position":[[239,9]]},"1899":{"position":[[70,9],[167,9]]},"1901":{"position":[[68,9]]},"1907":{"position":[[66,9]]},"1913":{"position":[[66,9]]},"1919":{"position":[[67,9]]},"1937":{"position":[[196,9]]},"1985":{"position":[[150,9],[249,9]]},"1987":{"position":[[106,9]]},"2020":{"position":[[30,9]]},"2132":{"position":[[317,9]]},"2832":{"position":[[429,9]]},"2834":{"position":[[800,9]]},"2930":{"position":[[1805,9],[2176,9]]},"2948":{"position":[[272,9],[407,9]]},"2992":{"position":[[239,9]]},"3092":{"position":[[70,9],[167,9]]},"3094":{"position":[[68,9]]},"3100":{"position":[[66,9]]},"3106":{"position":[[66,9]]},"3112":{"position":[[67,9]]},"3134":{"position":[[196,9]]},"3200":{"position":[[150,9],[249,9]]},"3202":{"position":[[79,9]]},"3239":{"position":[[30,9]]},"3361":{"position":[[317,9]]},"3639":{"position":[[750,9]]}}}],["anoth",{"_index":437,"t":{"10":{"position":[[1151,7],[1169,7]]},"18":{"position":[[3075,7]]},"32":{"position":[[934,7],[1595,7]]},"44":{"position":[[0,7]]},"48":{"position":[[202,7]]},"50":{"position":[[1288,7]]},"88":{"position":[[630,7]]},"120":{"position":[[966,8]]},"128":{"position":[[216,7]]},"148":{"position":[[1316,8]]},"178":{"position":[[91,7]]},"200":{"position":[[968,7]]},"208":{"position":[[0,7]]},"212":{"position":[[216,7]]},"238":{"position":[[0,7],[160,7]]},"262":{"position":[[38,7],[558,7],[2198,7]]},"279":{"position":[[1433,8]]},"287":{"position":[[634,7]]},"321":{"position":[[338,7]]},"335":{"position":[[294,7],[910,7]]},"365":{"position":[[105,7]]},"371":{"position":[[1281,7]]},"383":{"position":[[1598,7]]},"387":{"position":[[661,7],[1734,7]]},"393":{"position":[[0,7]]},"469":{"position":[[514,7],[528,7]]},"475":{"position":[[2194,7]]},"477":{"position":[[7040,7]]},"483":{"position":[[339,7]]},"485":{"position":[[1025,7]]},"505":{"position":[[0,7]]},"560":{"position":[[513,7]]},"866":{"position":[[0,7]]},"868":{"position":[[0,7]]},"977":{"position":[[522,7],[549,7],[685,7]]},"985":{"position":[[650,7]]},"993":{"position":[[994,7],[1046,7],[1068,7]]},"1141":{"position":[[250,7]]},"1262":{"position":[[442,7]]},"1283":{"position":[[912,7]]},"1307":{"position":[[338,7]]},"1323":{"position":[[987,7]]},"1367":{"position":[[0,7]]},"1385":{"position":[[105,7]]},"1391":{"position":[[1281,7]]},"1475":{"position":[[888,7]]},"1495":{"position":[[6465,7],[9210,7]]},"1571":{"position":[[251,7]]},"1577":{"position":[[797,7]]},"1619":{"position":[[496,7]]},"1678":{"position":[[0,7]]},"2034":{"position":[[522,7],[549,7],[685,7]]},"2044":{"position":[[650,7]]},"2054":{"position":[[994,7],[1046,7],[1068,7]]},"2062":{"position":[[0,7]]},"2066":{"position":[[0,7]]},"2068":{"position":[[0,7]]},"2222":{"position":[[250,7]]},"2413":{"position":[[442,7]]},"2430":{"position":[[912,7]]},"2454":{"position":[[338,7]]},"2470":{"position":[[987,7]]},"2482":{"position":[[588,7]]},"2518":{"position":[[0,7]]},"2536":{"position":[[106,7]]},"2542":{"position":[[1302,7]]},"2662":{"position":[[9345,7]]},"2668":{"position":[[888,7]]},"2722":{"position":[[741,7]]},"2730":{"position":[[251,7]]},"2770":{"position":[[797,7]]},"2806":{"position":[[496,7]]},"2832":{"position":[[0,7]]},"3140":{"position":[[0,7]]},"3144":{"position":[[0,7]]},"3146":{"position":[[0,7]]},"3275":{"position":[[522,7],[549,7],[685,7]]},"3285":{"position":[[650,7]]},"3293":{"position":[[994,7],[1046,7],[1068,7]]},"3463":{"position":[[250,7]]},"3580":{"position":[[442,7]]},"3625":{"position":[[912,7]]}}}],["answer",{"_index":1325,"t":{"68":{"position":[[1294,6]]},"134":{"position":[[1936,6]]},"200":{"position":[[1074,7]]},"307":{"position":[[203,6]]},"1069":{"position":[[610,10]]},"1293":{"position":[[203,6]]},"1349":{"position":[[221,7]]},"2150":{"position":[[610,10]]},"2353":{"position":[[40,6]]},"2440":{"position":[[203,6]]},"2494":{"position":[[223,7]]},"2502":{"position":[[91,6]]},"3379":{"position":[[657,10]]},"3641":{"position":[[40,6]]}}}],["anton",{"_index":1482,"t":{"90":{"position":[[443,5]]},"407":{"position":[[482,5]]}}}],["anymor",{"_index":1399,"t":{"78":{"position":[[934,7]]},"92":{"position":[[81,8]]},"204":{"position":[[1655,7]]},"387":{"position":[[793,8]]},"403":{"position":[[536,8]]},"545":{"position":[[150,7]]},"560":{"position":[[504,8]]},"564":{"position":[[339,7]]},"961":{"position":[[1336,8]]},"971":{"position":[[349,7]]},"975":{"position":[[1224,7]]},"1479":{"position":[[294,7],[518,7]]},"1569":{"position":[[192,7]]},"1877":{"position":[[243,7]]},"2026":{"position":[[335,8]]},"2030":{"position":[[1378,8]]},"2098":{"position":[[1411,8]]},"2345":{"position":[[4484,9],[5541,8]]},"2349":{"position":[[109,7]]},"2357":{"position":[[622,8]]},"2367":{"position":[[637,7]]},"2656":{"position":[[653,8]]},"2660":{"position":[[155,8]]},"2672":{"position":[[294,7],[518,7]]},"2728":{"position":[[192,7]]},"3086":{"position":[[337,8]]},"3257":{"position":[[1411,8]]},"3267":{"position":[[335,8]]},"3271":{"position":[[1344,8]]},"3347":{"position":[[435,8]]},"3633":{"position":[[4484,9],[5541,8]]},"3637":{"position":[[109,7]]},"3645":{"position":[[622,8]]},"3655":{"position":[[637,7]]}}}],["anyon",{"_index":4013,"t":{"858":{"position":[[337,6]]},"1107":{"position":[[744,6]]},"1725":{"position":[[337,6]]},"2188":{"position":[[747,6]]},"2914":{"position":[[390,6]]},"3427":{"position":[[714,6]]}}}],["anyth",{"_index":2554,"t":{"246":{"position":[[3756,8]]},"375":{"position":[[544,11]]},"3329":{"position":[[157,8]]}}}],["anyway",{"_index":729,"t":{"16":{"position":[[7050,7]]},"108":{"position":[[1489,6]]},"236":{"position":[[1513,7]]},"252":{"position":[[1143,7]]},"457":{"position":[[453,7]]},"720":{"position":[[281,6]]},"740":{"position":[[205,6]]},"983":{"position":[[997,7]]},"1075":{"position":[[450,8]]},"1457":{"position":[[452,7]]},"1759":{"position":[[205,6]]},"2042":{"position":[[997,7]]},"2158":{"position":[[409,8]]},"2598":{"position":[[452,7]]},"3283":{"position":[[997,7]]},"3387":{"position":[[409,8]]}}}],["anywher",{"_index":2510,"t":{"246":{"position":[[708,8]]},"523":{"position":[[1328,9]]},"572":{"position":[[327,9]]},"1431":{"position":[[1561,9]]},"1511":{"position":[[925,9]]},"2614":{"position":[[1601,9]]},"2642":{"position":[[925,9]]}}}],["aof",{"_index":2942,"t":{"361":{"position":[[1281,4]]},"965":{"position":[[2688,3],[2831,3]]},"1381":{"position":[[1286,4]]},"2532":{"position":[[1344,3]]}}}],["apach",{"_index":1315,"t":{"66":{"position":[[234,6],[261,6]]},"441":{"position":[[151,7]]},"1425":{"position":[[151,7]]},"2504":{"position":[[57,6]]},"2582":{"position":[[155,7]]}}}],["api",{"_index":318,"t":{"8":{"position":[[382,3],[698,3],[1184,3],[1566,3]]},"18":{"position":[[192,4]]},"26":{"position":[[1538,3]]},"44":{"position":[[1786,3]]},"56":{"position":[[104,3]]},"58":{"position":[[309,4]]},"60":{"position":[[584,3]]},"64":{"position":[[445,3],[978,5]]},"68":{"position":[[847,4],[1530,4],[1850,3],[1977,3]]},"70":{"position":[[62,3],[221,3],[674,3],[1569,3]]},"72":{"position":[[263,3]]},"78":{"position":[[39,3],[75,3],[559,4],[572,3],[689,3],[1028,3],[1066,3]]},"82":{"position":[[179,4]]},"84":{"position":[[270,3]]},"88":{"position":[[953,3]]},"94":{"position":[[30,3],[924,3]]},"138":{"position":[[662,3],[861,3],[2061,4]]},"158":{"position":[[306,3],[658,3],[712,4]]},"160":{"position":[[231,3]]},"164":{"position":[[276,4]]},"176":{"position":[[1097,3]]},"178":{"position":[[729,4]]},"180":{"position":[[420,4]]},"192":{"position":[[527,5]]},"194":{"position":[[98,4]]},"204":{"position":[[35,3],[51,4],[128,3],[232,3],[693,3],[1027,3],[1217,3],[1255,4],[1444,3],[1594,4],[1681,3],[1708,3],[1925,3],[2040,5],[2180,3],[2278,3],[2306,3]]},"206":{"position":[[117,3],[718,4]]},"208":{"position":[[83,3],[217,3]]},"216":{"position":[[499,3]]},"218":{"position":[[390,4]]},"222":{"position":[[34,4],[93,3],[328,3]]},"224":{"position":[[59,3]]},"228":{"position":[[185,4],[522,3],[629,4]]},"230":{"position":[[480,4]]},"234":{"position":[[161,4]]},"236":{"position":[[1260,3]]},"238":{"position":[[150,4]]},"240":{"position":[[1084,5]]},"244":{"position":[[350,4]]},"248":{"position":[[440,4]]},"252":{"position":[[33,3],[1574,3]]},"260":{"position":[[204,3],[448,3]]},"262":{"position":[[46,3],[120,3],[273,4],[732,3],[857,3],[1164,5]]},"264":{"position":[[395,3],[589,3],[810,4],[1251,4],[1482,3]]},"266":{"position":[[333,4],[343,3]]},"270":{"position":[[99,3],[537,3],[683,4]]},"277":{"position":[[594,3]]},"289":{"position":[[282,3],[336,3],[1020,3]]},"291":{"position":[[141,4]]},"301":{"position":[[3629,3]]},"317":{"position":[[410,3]]},"319":{"position":[[1164,3]]},"325":{"position":[[443,3]]},"335":{"position":[[270,4]]},"337":{"position":[[46,4]]},"339":{"position":[[635,4],[881,4]]},"351":{"position":[[296,3]]},"359":{"position":[[472,5],[640,4]]},"361":{"position":[[664,3]]},"369":{"position":[[655,3]]},"375":{"position":[[236,4]]},"377":{"position":[[1252,4],[1282,3],[1392,3],[2389,3]]},"379":{"position":[[2043,3]]},"381":{"position":[[13,3]]},"391":{"position":[[1321,3]]},"393":{"position":[[62,4],[142,3],[800,3]]},"395":{"position":[[254,3]]},"407":{"position":[[337,3]]},"411":{"position":[[401,3]]},"417":{"position":[[286,3]]},"419":{"position":[[492,4]]},"449":{"position":[[229,3],[237,3]]},"451":{"position":[[115,3],[165,3],[194,3],[275,3]]},"457":{"position":[[599,4],[642,3],[775,3],[804,3],[879,4]]},"467":{"position":[[208,3]]},"475":{"position":[[3358,3]]},"485":{"position":[[499,4]]},"491":{"position":[[390,4]]},"505":{"position":[[37,3],[561,4]]},"507":{"position":[[68,3]]},"539":{"position":[[86,3]]},"541":{"position":[[16,3],[224,4]]},"556":{"position":[[238,3],[444,3]]},"558":{"position":[[32,3],[75,3],[235,3]]},"576":{"position":[[401,3],[455,3],[540,3]]},"578":{"position":[[297,3]]},"600":{"position":[[543,3]]},"622":{"position":[[76,4],[283,4]]},"624":{"position":[[80,4]]},"628":{"position":[[80,4]]},"634":{"position":[[89,3]]},"636":{"position":[[137,3]]},"638":{"position":[[158,4]]},"640":{"position":[[268,4]]},"646":{"position":[[219,6]]},"652":{"position":[[204,3],[305,3]]},"654":{"position":[[173,3]]},"678":{"position":[[56,3]]},"680":{"position":[[70,3]]},"682":{"position":[[48,3],[125,3]]},"684":{"position":[[98,3]]},"700":{"position":[[306,3]]},"708":{"position":[[117,4],[200,5]]},"716":{"position":[[553,4]]},"720":{"position":[[109,4],[198,5]]},"732":{"position":[[135,3]]},"734":{"position":[[1201,3]]},"742":{"position":[[145,3]]},"760":{"position":[[294,3]]},"764":{"position":[[2165,3]]},"838":{"position":[[7,3],[55,3],[119,6]]},"884":{"position":[[190,3],[405,3],[471,4]]},"886":{"position":[[245,3]]},"888":{"position":[[151,3]]},"918":{"position":[[617,3],[994,4]]},"926":{"position":[[33,4],[162,3],[175,6],[193,3],[1153,3]]},"928":{"position":[[87,3]]},"930":{"position":[[728,7],[754,3]]},"937":{"position":[[116,3],[191,4],[279,4],[386,3]]},"961":{"position":[[1486,4]]},"963":{"position":[[1236,3]]},"965":{"position":[[3732,3]]},"977":{"position":[[1200,3]]},"1039":{"position":[[37,3]]},"1049":{"position":[[1801,3]]},"1051":{"position":[[67,3],[126,3],[260,3],[347,3]]},"1101":{"position":[[327,5]]},"1105":{"position":[[12,3],[25,4],[293,3],[330,3]]},"1107":{"position":[[5,3],[150,3],[166,3],[290,3],[431,3],[459,3],[503,3],[586,3],[687,3],[817,3],[831,3],[1028,3],[1452,3]]},"1109":{"position":[[907,4]]},"1125":{"position":[[331,3]]},"1135":{"position":[[11,3],[204,3],[395,3],[443,3],[483,3],[527,3],[603,3],[648,3]]},"1137":{"position":[[30,4],[214,3],[301,3],[351,3],[388,3],[473,3],[565,3],[832,3]]},"1139":{"position":[[846,3]]},"1141":{"position":[[180,3]]},"1143":{"position":[[83,3]]},"1184":{"position":[[42,4]]},"1262":{"position":[[1415,3]]},"1268":{"position":[[243,4],[746,4],[803,3]]},"1285":{"position":[[223,3]]},"1303":{"position":[[410,3]]},"1305":{"position":[[1164,3]]},"1311":{"position":[[443,3]]},"1323":{"position":[[270,4],[319,3]]},"1325":{"position":[[46,4]]},"1327":{"position":[[635,4],[881,4]]},"1339":{"position":[[296,3]]},"1353":{"position":[[651,3]]},"1367":{"position":[[37,3],[561,4]]},"1369":{"position":[[68,3]]},"1379":{"position":[[472,5],[640,4]]},"1381":{"position":[[664,3]]},"1389":{"position":[[655,3]]},"1395":{"position":[[417,3]]},"1401":{"position":[[283,3]]},"1425":{"position":[[198,3]]},"1449":{"position":[[229,3],[237,3]]},"1451":{"position":[[171,3],[223,3],[261,3],[342,3]]},"1455":{"position":[[309,3]]},"1457":{"position":[[595,4],[638,3],[771,3],[800,3],[875,4]]},"1473":{"position":[[501,4],[573,4]]},"1475":{"position":[[1442,4]]},"1495":{"position":[[8878,3],[9046,3],[9085,3],[9763,3]]},"1499":{"position":[[1041,3],[1076,3]]},"1517":{"position":[[50,3]]},"1543":{"position":[[74,4]]},"1577":{"position":[[1104,4]]},"1583":{"position":[[351,3],[404,3],[495,3],[579,3],[648,3],[723,3]]},"1585":{"position":[[297,3]]},"1591":{"position":[[76,4],[283,4]]},"1593":{"position":[[80,4]]},"1597":{"position":[[80,4]]},"1603":{"position":[[89,3]]},"1605":{"position":[[137,3]]},"1607":{"position":[[158,4]]},"1617":{"position":[[268,4]]},"1619":{"position":[[825,3]]},"1623":{"position":[[52,4]]},"1627":{"position":[[180,3]]},"1629":{"position":[[392,3],[763,3],[1324,4]]},"1631":{"position":[[68,4],[88,4],[386,4]]},"1633":{"position":[[341,4]]},"1635":{"position":[[311,3],[401,3],[711,3],[834,4],[966,4]]},"1639":{"position":[[881,3],[1212,4]]},"1666":{"position":[[229,4]]},"1668":{"position":[[5,3],[191,3],[401,3],[588,3],[701,3]]},"1692":{"position":[[219,6]]},"1699":{"position":[[204,3],[305,3]]},"1702":{"position":[[173,3]]},"1729":{"position":[[56,3]]},"1731":{"position":[[70,3]]},"1733":{"position":[[48,3],[125,3]]},"1735":{"position":[[98,3]]},"1743":{"position":[[116,5]]},"1761":{"position":[[145,3]]},"1779":{"position":[[367,3]]},"1783":{"position":[[2165,3]]},"1787":{"position":[[851,3]]},"1881":{"position":[[1232,3]]},"1885":{"position":[[1251,3]]},"1887":{"position":[[669,5]]},"1893":{"position":[[1143,3]]},"1895":{"position":[[645,4]]},"1903":{"position":[[601,3]]},"1905":{"position":[[574,3]]},"1909":{"position":[[115,3]]},"1937":{"position":[[274,4],[369,3],[1122,3]]},"1951":{"position":[[190,3],[405,3],[471,4]]},"1953":{"position":[[245,3]]},"1955":{"position":[[151,3]]},"1993":{"position":[[832,3],[1209,4]]},"2001":{"position":[[33,4],[162,3],[175,6],[193,3],[1153,3]]},"2003":{"position":[[87,3]]},"2005":{"position":[[917,7],[943,3]]},"2010":{"position":[[203,3]]},"2012":{"position":[[116,3],[191,4],[279,4],[386,3]]},"2034":{"position":[[1200,3]]},"2098":{"position":[[1561,4]]},"2100":{"position":[[1236,3]]},"2102":{"position":[[1561,4],[4877,3]]},"2118":{"position":[[521,4]]},"2130":{"position":[[37,3]]},"2186":{"position":[[12,3],[25,4],[302,3],[339,3]]},"2188":{"position":[[5,3],[153,3],[169,3],[293,3],[434,3],[462,3],[506,3],[589,3],[690,3],[820,3],[834,3],[1031,3],[1455,3]]},"2190":{"position":[[967,4],[2432,3]]},"2192":{"position":[[726,3]]},"2206":{"position":[[331,3]]},"2210":{"position":[[830,3]]},"2216":{"position":[[11,3],[204,3],[395,3],[443,3],[483,3],[527,3],[603,3],[648,3]]},"2218":{"position":[[30,4],[214,3],[301,3],[351,3],[388,3],[473,3],[565,3],[832,3]]},"2220":{"position":[[846,3]]},"2222":{"position":[[180,3]]},"2224":{"position":[[83,3]]},"2230":{"position":[[1801,3]]},"2232":{"position":[[67,3],[126,3],[260,3],[347,3]]},"2262":{"position":[[405,3]]},"2292":{"position":[[243,4],[749,4],[806,3]]},"2359":{"position":[[595,5]]},"2383":{"position":[[361,3]]},"2413":{"position":[[1494,3]]},"2432":{"position":[[223,3]]},"2434":{"position":[[32,3],[705,4]]},"2450":{"position":[[410,3]]},"2452":{"position":[[1164,3]]},"2458":{"position":[[443,3]]},"2470":{"position":[[270,4],[319,3]]},"2472":{"position":[[46,4]]},"2474":{"position":[[635,4],[881,4]]},"2486":{"position":[[296,3]]},"2518":{"position":[[37,3],[583,4]]},"2520":{"position":[[68,3]]},"2530":{"position":[[916,3],[1107,4]]},"2532":{"position":[[705,3]]},"2540":{"position":[[665,3]]},"2546":{"position":[[696,3]]},"2552":{"position":[[421,3]]},"2558":{"position":[[284,3]]},"2560":{"position":[[225,5]]},"2564":{"position":[[206,4]]},"2582":{"position":[[202,3]]},"2590":{"position":[[236,3],[248,3]]},"2592":{"position":[[180,3],[244,3],[282,3],[367,3]]},"2596":{"position":[[318,3]]},"2598":{"position":[[608,4],[652,3],[790,3],[819,3],[896,4]]},"2630":{"position":[[1041,3],[1076,3]]},"2648":{"position":[[50,3]]},"2656":{"position":[[22,3],[154,3],[264,3],[373,3],[523,4],[751,3],[777,3],[965,3]]},"2658":{"position":[[85,3]]},"2662":{"position":[[8998,3],[9170,3],[9216,3],[9919,3],[10017,3]]},"2666":{"position":[[501,4],[573,4]]},"2668":{"position":[[1442,4]]},"2712":{"position":[[74,4]]},"2722":{"position":[[2726,3]]},"2744":{"position":[[697,3]]},"2748":{"position":[[18,3]]},"2770":{"position":[[1104,4],[1170,3]]},"2778":{"position":[[352,3],[405,3],[496,3],[580,3],[649,3],[724,3]]},"2782":{"position":[[296,3]]},"2786":{"position":[[76,4],[283,4]]},"2788":{"position":[[80,4]]},"2792":{"position":[[80,4]]},"2798":{"position":[[89,3]]},"2800":{"position":[[137,3]]},"2802":{"position":[[158,4]]},"2804":{"position":[[268,4]]},"2806":{"position":[[825,3]]},"2816":{"position":[[219,6]]},"2823":{"position":[[204,3],[305,3],[478,3]]},"2826":{"position":[[173,3],[355,3]]},"2840":{"position":[[52,4]]},"2844":{"position":[[180,3]]},"2846":{"position":[[392,3],[763,3],[1324,4]]},"2848":{"position":[[68,4],[88,4],[386,4]]},"2850":{"position":[[341,4]]},"2852":{"position":[[311,3],[401,3],[711,3],[834,4],[966,4]]},"2856":{"position":[[881,3],[1212,4]]},"2883":{"position":[[229,4]]},"2887":{"position":[[5,3],[191,3],[401,3],[588,3],[701,3]]},"2904":{"position":[[70,3]]},"2906":{"position":[[70,3]]},"2918":{"position":[[56,3]]},"2920":{"position":[[70,3],[304,3]]},"2922":{"position":[[48,3],[125,3],[211,3]]},"2924":{"position":[[98,3],[169,3]]},"2932":{"position":[[116,5]]},"2950":{"position":[[178,3]]},"2964":{"position":[[512,3]]},"2968":{"position":[[403,3]]},"2976":{"position":[[995,3]]},"3072":{"position":[[1520,3]]},"3076":{"position":[[635,3],[1215,3]]},"3078":{"position":[[669,5]]},"3084":{"position":[[816,3],[1107,3]]},"3088":{"position":[[645,4]]},"3096":{"position":[[601,3]]},"3098":{"position":[[574,3]]},"3102":{"position":[[115,3]]},"3134":{"position":[[274,4],[369,3],[1122,3]]},"3164":{"position":[[190,3],[405,3],[471,4]]},"3166":{"position":[[245,3]]},"3168":{"position":[[151,3]]},"3208":{"position":[[832,3],[1209,4]]},"3216":{"position":[[83,4]]},"3218":{"position":[[33,4],[162,3],[175,6],[193,3],[592,3],[1243,3]]},"3220":{"position":[[87,3]]},"3222":{"position":[[1004,7],[1030,3]]},"3227":{"position":[[203,3]]},"3231":{"position":[[116,3],[191,4],[279,4],[386,3]]},"3257":{"position":[[1561,4]]},"3259":{"position":[[1236,3]]},"3261":{"position":[[1561,4],[4877,3]]},"3275":{"position":[[1200,3]]},"3324":{"position":[[5886,3],[5931,3],[6106,3],[6151,3]]},"3327":{"position":[[68,3],[454,3],[808,3]]},"3333":{"position":[[356,3],[499,4],[709,3],[864,4]]},"3345":{"position":[[422,3]]},"3347":{"position":[[711,3],[1098,4],[2702,3]]},"3359":{"position":[[37,3]]},"3417":{"position":[[1801,3]]},"3419":{"position":[[67,3],[126,3],[260,3],[347,3]]},"3425":{"position":[[12,3],[25,4],[142,3],[189,3],[311,3],[495,3],[532,3],[625,3]]},"3427":{"position":[[5,3],[169,3],[206,3],[236,3],[287,3],[371,3],[415,3],[432,3],[512,3],[647,3],[799,4],[804,3]]},"3429":{"position":[[7,3]]},"3431":{"position":[[237,3],[723,3],[1037,3],[1940,3]]},"3433":{"position":[[114,3],[801,3]]},"3443":{"position":[[267,3]]},"3445":{"position":[[149,3]]},"3447":{"position":[[331,3],[482,3]]},"3451":{"position":[[94,3],[890,3]]},"3455":{"position":[[315,3]]},"3457":{"position":[[11,3],[204,3],[395,3],[443,3],[483,3],[527,3],[603,3],[648,3]]},"3459":{"position":[[30,4],[214,3],[301,3],[351,3],[388,3],[473,3],[565,3],[832,3]]},"3461":{"position":[[846,3]]},"3463":{"position":[[180,3]]},"3465":{"position":[[83,3]]},"3467":{"position":[[30,3],[119,3],[212,3],[310,3],[333,3],[598,3],[849,3],[1615,3],[1683,3],[1997,4],[2226,3]]},"3469":{"position":[[624,3]]},"3471":{"position":[[527,3]]},"3484":{"position":[[243,4],[749,4],[806,3]]},"3496":{"position":[[405,3]]},"3580":{"position":[[1494,3]]},"3612":{"position":[[32,3],[732,4]]},"3627":{"position":[[223,3]]},"3647":{"position":[[595,5]]},"3671":{"position":[[361,3]]}}}],["api.app",{"_index":2930,"t":{"355":{"position":[[1017,7],[1459,7]]},"1347":{"position":[[1017,7],[1459,7]]},"2436":{"position":[[1017,7],[1459,7]]}}}],["api.proto",{"_index":4476,"t":{"1139":{"position":[[170,9]]},"1141":{"position":[[496,9],[548,9]]},"2220":{"position":[[170,9]]},"2222":{"position":[[496,9],[548,9]]},"3461":{"position":[[170,9]]},"3463":{"position":[[496,9],[548,9]]}}}],["api/login",{"_index":2692,"t":{"283":{"position":[[1080,9]]}}}],["ha",{"_index":713,"t":{"16":{"position":[[6486,2]]},"979":{"position":[[295,3]]},"981":{"position":[[280,2]]},"2036":{"position":[[295,3]]},"2040":{"position":[[280,2]]},"3277":{"position":[[295,3]]},"3281":{"position":[[280,2]]}}}],["hand",{"_index":2087,"t":{"142":{"position":[[729,6]]},"291":{"position":[[585,6]]},"2656":{"position":[[402,4]]}}}],["handi",{"_index":900,"t":{"28":{"position":[[1201,5]]},"254":{"position":[[29,6]]},"700":{"position":[[647,5]]},"1903":{"position":[[923,5]]},"3096":{"position":[[923,5]]}}}],["handl",{"_index":145,"t":{"2":{"position":[[2209,6],[2495,6]]},"46":{"position":[[296,6]]},"54":{"position":[[1385,8]]},"68":{"position":[[926,8],[945,8],[993,9]]},"104":{"position":[[508,6]]},"120":{"position":[[481,6]]},"132":{"position":[[1065,6]]},"138":{"position":[[1183,6]]},"142":{"position":[[452,6]]},"148":{"position":[[780,6]]},"150":{"position":[[553,6]]},"168":{"position":[[643,6]]},"178":{"position":[[339,6],[369,7]]},"182":{"position":[[967,6]]},"200":{"position":[[159,8]]},"242":{"position":[[72,9],[95,6],[249,6]]},"244":{"position":[[617,8]]},"254":{"position":[[539,6],[733,6],[1065,6],[1317,6]]},"279":{"position":[[1062,6]]},"291":{"position":[[359,6]]},"309":{"position":[[233,6]]},"347":{"position":[[1081,6]]},"349":{"position":[[97,7]]},"371":{"position":[[158,6],[1241,6]]},"377":{"position":[[523,9],[1964,8],[2133,6]]},"387":{"position":[[1410,6]]},"417":{"position":[[198,6]]},"423":{"position":[[55,6]]},"455":{"position":[[167,6]]},"459":{"position":[[301,8]]},"489":{"position":[[656,6]]},"491":{"position":[[77,8]]},"507":{"position":[[3,6]]},"525":{"position":[[344,6]]},"578":{"position":[[217,7]]},"748":{"position":[[0,7]]},"750":{"position":[[0,7]]},"764":{"position":[[1055,6]]},"808":{"position":[[302,8]]},"949":{"position":[[90,6],[321,7]]},"965":{"position":[[2133,7]]},"999":{"position":[[177,8]]},"1031":{"position":[[0,7]]},"1033":{"position":[[0,7]]},"1049":{"position":[[382,8],[897,6],[1007,8]]},"1139":{"position":[[828,6]]},"1162":{"position":[[826,6],[874,6],[920,6],[1396,6],[1819,6],[1851,6]]},"1164":{"position":[[3447,8]]},"1180":{"position":[[14,6]]},"1182":{"position":[[39,8],[130,7]]},"1184":{"position":[[2665,6]]},"1186":{"position":[[142,6]]},"1245":{"position":[[253,6]]},"1295":{"position":[[233,6]]},"1335":{"position":[[1085,6]]},"1337":{"position":[[97,7]]},"1369":{"position":[[3,6]]},"1391":{"position":[[158,6],[1241,6]]},"1401":{"position":[[194,6]]},"1407":{"position":[[55,6]]},"1433":{"position":[[346,6]]},"1455":{"position":[[176,6]]},"1459":{"position":[[301,8]]},"1469":{"position":[[664,6],[1310,6]]},"1495":{"position":[[3933,6],[5579,8]]},"1585":{"position":[[217,7]]},"1635":{"position":[[253,7]]},"1676":{"position":[[1776,7]]},"1680":{"position":[[537,6]]},"1783":{"position":[[1055,6]]},"1787":{"position":[[785,8]]},"1851":{"position":[[104,6]]},"1859":{"position":[[302,8]]},"1865":{"position":[[1143,6],[1320,6]]},"1975":{"position":[[150,7]]},"1977":{"position":[[323,6]]},"2076":{"position":[[90,6],[321,7]]},"2106":{"position":[[177,8]]},"2220":{"position":[[828,6]]},"2230":{"position":[[382,8],[897,6],[1007,8]]},"2240":{"position":[[1114,6],[1398,8]]},"2248":{"position":[[14,6]]},"2250":{"position":[[39,8],[130,7]]},"2262":{"position":[[313,6]]},"2264":{"position":[[310,6]]},"2290":{"position":[[462,6]]},"2296":{"position":[[298,6]]},"2324":{"position":[[237,6]]},"2383":{"position":[[424,6]]},"2442":{"position":[[233,6]]},"2484":{"position":[[97,7]]},"2520":{"position":[[3,6]]},"2542":{"position":[[162,6],[1261,6]]},"2558":{"position":[[195,6]]},"2564":{"position":[[55,6]]},"2596":{"position":[[181,6]]},"2600":{"position":[[309,8]]},"2610":{"position":[[479,8]]},"2616":{"position":[[349,6]]},"2662":{"position":[[3991,6],[5641,8]]},"2782":{"position":[[217,7]]},"2830":{"position":[[1779,7]]},"2834":{"position":[[537,6]]},"2852":{"position":[[253,7]]},"2972":{"position":[[1003,6]]},"2976":{"position":[[929,8]]},"3042":{"position":[[104,6]]},"3050":{"position":[[302,8]]},"3056":{"position":[[1143,6],[1320,6]]},"3188":{"position":[[123,7]]},"3190":{"position":[[296,6]]},"3243":{"position":[[90,6],[321,7]]},"3307":{"position":[[177,8]]},"3349":{"position":[[1487,8],[2265,6],[2403,8],[3596,6]]},"3351":{"position":[[1124,8],[1590,8]]},"3417":{"position":[[382,8],[897,6],[1007,8]]},"3461":{"position":[[828,6]]},"3476":{"position":[[608,7]]},"3482":{"position":[[497,6]]},"3486":{"position":[[298,6]]},"3496":{"position":[[313,6]]},"3498":{"position":[[310,6]]},"3518":{"position":[[588,7]]},"3545":{"position":[[212,6]]},"3551":{"position":[[1114,6],[1398,8]]},"3559":{"position":[[14,6]]},"3561":{"position":[[39,8],[130,7]]},"3671":{"position":[[424,6]]}}}],["handler",{"_index":917,"t":{"30":{"position":[[145,9],[729,7]]},"32":{"position":[[760,7]]},"34":{"position":[[1184,8],[1212,7],[1311,7],[1456,7],[2079,8]]},"36":{"position":[[390,8]]},"38":{"position":[[382,7]]},"44":{"position":[[962,7]]},"110":{"position":[[399,7],[1232,8]]},"136":{"position":[[266,8],[877,7],[1273,7],[1638,7]]},"138":{"position":[[415,9]]},"160":{"position":[[823,8]]},"166":{"position":[[1166,8],[1950,7],[2431,8]]},"180":{"position":[[1132,8]]},"184":{"position":[[174,7]]},"254":{"position":[[489,7]]},"283":{"position":[[1883,7],[2461,7]]},"287":{"position":[[1599,7]]},"395":{"position":[[335,8]]},"503":{"position":[[41,8]]},"930":{"position":[[39,7]]},"965":{"position":[[3285,7]]},"1065":{"position":[[4801,7],[5480,7]]},"1162":{"position":[[693,8]]},"1168":{"position":[[987,7]]},"1182":{"position":[[637,7]]},"1184":{"position":[[450,8],[1961,9],[1971,7],[2030,7],[2052,7],[2066,7],[2098,7],[2124,7],[2271,8]]},"1186":{"position":[[318,8],[404,8]]},"1365":{"position":[[41,8]]},"2005":{"position":[[39,7]]},"2102":{"position":[[3001,7]]},"2146":{"position":[[1044,8],[4924,7],[5603,7]]},"2152":{"position":[[4127,7]]},"2250":{"position":[[637,7]]},"2383":{"position":[[63,9]]},"2516":{"position":[[41,8]]},"2722":{"position":[[1457,7]]},"3222":{"position":[[39,7]]},"3261":{"position":[[3001,7]]},"3349":{"position":[[2090,7],[2221,8],[2253,8],[2363,7],[4486,7]]},"3351":{"position":[[1052,8],[1513,7]]},"3375":{"position":[[1044,8],[4953,7],[5632,7]]},"3381":{"position":[[4130,7]]},"3561":{"position":[[637,7]]},"3671":{"position":[[63,9]]}}}],["handlesession(sess",{"_index":1669,"t":{"106":{"position":[[682,18]]},"110":{"position":[[1274,18]]}}}],["handshak",{"_index":368,"t":{"8":{"position":[[1339,9]]},"106":{"position":[[1639,10]]},"902":{"position":[[288,9]]},"1049":{"position":[[1640,9]]},"1969":{"position":[[288,9]]},"2032":{"position":[[1143,9]]},"2038":{"position":[[1200,9]]},"2230":{"position":[[1640,9]]},"3182":{"position":[[288,9]]},"3273":{"position":[[1035,9]]},"3279":{"position":[[1092,9]]},"3417":{"position":[[1640,9]]}}}],["happen",{"_index":394,"t":{"10":{"position":[[131,10],[1269,7]]},"38":{"position":[[132,7]]},"40":{"position":[[664,6]]},"64":{"position":[[592,9]]},"108":{"position":[[227,7]]},"130":{"position":[[2784,8]]},"220":{"position":[[37,8]]},"236":{"position":[[136,6]]},"395":{"position":[[226,9]]},"457":{"position":[[319,8]]},"481":{"position":[[4078,8]]},"483":{"position":[[1189,8]]},"491":{"position":[[289,7]]},"505":{"position":[[789,7]]},"764":{"position":[[132,7]]},"806":{"position":[[178,6]]},"850":{"position":[[176,6]]},"866":{"position":[[152,6]]},"868":{"position":[[355,6]]},"870":{"position":[[191,6]]},"951":{"position":[[39,6]]},"953":{"position":[[144,6]]},"1027":{"position":[[651,7]]},"1073":{"position":[[249,7],[627,8]]},"1182":{"position":[[86,6]]},"1186":{"position":[[651,6]]},"1367":{"position":[[789,7]]},"1457":{"position":[[316,8]]},"1611":{"position":[[333,7]]},"1668":{"position":[[48,6]]},"1783":{"position":[[132,7]]},"1811":{"position":[[651,7]]},"1857":{"position":[[175,6]]},"1865":{"position":[[1183,7]]},"2062":{"position":[[191,6]]},"2066":{"position":[[355,6]]},"2068":{"position":[[416,6]]},"2070":{"position":[[191,6]]},"2072":{"position":[[199,6]]},"2078":{"position":[[39,6]]},"2080":{"position":[[144,6]]},"2152":{"position":[[4310,6]]},"2154":{"position":[[249,7],[627,8]]},"2250":{"position":[[86,6]]},"2345":{"position":[[1033,7],[1117,6],[1194,8],[4878,9],[4972,9]]},"2355":{"position":[[4491,9]]},"2518":{"position":[[820,7]]},"2598":{"position":[[319,8]]},"2736":{"position":[[333,7]]},"2887":{"position":[[48,6]]},"3000":{"position":[[651,7]]},"3048":{"position":[[175,6]]},"3056":{"position":[[1183,7]]},"3140":{"position":[[191,6]]},"3144":{"position":[[355,6]]},"3146":{"position":[[416,6]]},"3148":{"position":[[191,6]]},"3150":{"position":[[199,6]]},"3245":{"position":[[39,6]]},"3247":{"position":[[144,6]]},"3345":{"position":[[444,8]]},"3347":{"position":[[53,9]]},"3381":{"position":[[569,7],[4313,6]]},"3383":{"position":[[249,7],[627,8]]},"3561":{"position":[[86,6]]},"3633":{"position":[[1033,7],[1117,6],[1194,8],[4878,9],[4972,9]]},"3643":{"position":[[4491,9]]}}}],["happi",{"_index":1239,"t":{"54":{"position":[[1345,5]]},"60":{"position":[[60,5]]},"248":{"position":[[83,5]]},"1207":{"position":[[1767,5]]},"2339":{"position":[[1309,5]]},"3595":{"position":[[1284,5]]}}}],["haproxi",{"_index":3160,"t":{"475":{"position":[[699,7]]},"981":{"position":[[27,7],[337,7],[892,8]]},"2040":{"position":[[27,7],[337,7],[892,8]]},"2086":{"position":[[571,7]]},"3253":{"position":[[571,7]]},"3281":{"position":[[27,7],[337,7],[892,8]]}}}],["hard",{"_index":369,"t":{"8":{"position":[[1364,4]]},"12":{"position":[[1426,4]]},"26":{"position":[[311,4],[1380,4]]},"46":{"position":[[24,4]]},"54":{"position":[[1136,4]]},"74":{"position":[[277,4]]},"192":{"position":[[206,4]]},"270":{"position":[[64,4]]},"283":{"position":[[2430,4]]},"321":{"position":[[819,4]]},"341":{"position":[[577,4]]},"377":{"position":[[965,4],[990,4]]},"387":{"position":[[1374,4]]},"481":{"position":[[5217,4]]},"527":{"position":[[405,5]]},"945":{"position":[[599,5]]},"989":{"position":[[461,4]]},"1307":{"position":[[819,4]]},"1329":{"position":[[577,4]]},"1435":{"position":[[429,5]]},"1887":{"position":[[940,4]]},"2020":{"position":[[599,5]]},"2050":{"position":[[461,4]]},"2454":{"position":[[819,4]]},"2476":{"position":[[577,4]]},"2500":{"position":[[392,4]]},"2618":{"position":[[429,5]]},"3078":{"position":[[940,4]]},"3239":{"position":[[599,5]]},"3289":{"position":[[461,4]]}}}],["hardcod",{"_index":2719,"t":{"283":{"position":[[2223,9]]},"481":{"position":[[5381,9]]}}}],["harder",{"_index":633,"t":{"16":{"position":[[2506,7]]},"46":{"position":[[212,7]]},"50":{"position":[[683,6]]},"218":{"position":[[923,6]]},"321":{"position":[[909,6]]},"349":{"position":[[148,6]]},"1307":{"position":[[909,6]]},"1337":{"position":[[148,6]]},"2454":{"position":[[909,6]]},"2484":{"position":[[148,6]]}}}],["hardest",{"_index":2601,"t":{"256":{"position":[[330,7]]},"377":{"position":[[92,7]]},"407":{"position":[[97,7]]}}}],["hardli",{"_index":2573,"t":{"252":{"position":[[137,6]]}}}],["hardwar",{"_index":3,"t":{"2":{"position":[[48,8]]},"52":{"position":[[225,9],[326,8],[842,8]]},"138":{"position":[[1230,8]]},"307":{"position":[[58,9]]},"1293":{"position":[[58,9]]},"2440":{"position":[[58,9]]}}}],["has_mor",{"_index":4962,"t":{"1656":{"position":[[1021,8]]},"1660":{"position":[[969,8]]},"1664":{"position":[[518,8]]}}}],["hash",{"_index":1157,"t":{"46":{"position":[[2684,4]]},"469":{"position":[[857,4],[1547,4]]},"477":{"position":[[3759,5]]},"975":{"position":[[1016,4]]},"983":{"position":[[1206,7],[1258,4]]},"985":{"position":[[1169,7]]},"2030":{"position":[[1085,4]]},"2042":{"position":[[1206,7],[1258,4]]},"2044":{"position":[[1169,7]]},"3271":{"position":[[1051,4]]},"3283":{"position":[[1206,7],[1258,4]]},"3285":{"position":[[1169,7]]}}}],["hasn't",{"_index":3004,"t":{"383":{"position":[[878,6]]}}}],["have",{"_index":636,"t":{"16":{"position":[[2677,7],[3431,6]]},"20":{"position":[[923,6]]},"70":{"position":[[1601,6]]},"176":{"position":[[601,6]]},"182":{"position":[[1681,6]]},"279":{"position":[[1521,6]]},"321":{"position":[[664,6]]},"347":{"position":[[1726,6],[2092,6]]},"361":{"position":[[36,6],[552,6]]},"379":{"position":[[2758,6]]},"383":{"position":[[1479,6]]},"387":{"position":[[128,6],[386,6]]},"399":{"position":[[0,6]]},"403":{"position":[[636,6]]},"477":{"position":[[702,6]]},"523":{"position":[[1076,6]]},"798":{"position":[[209,6]]},"1164":{"position":[[2726,6]]},"1307":{"position":[[664,6]]},"1335":{"position":[[1730,6],[2096,6]]},"1343":{"position":[[317,6]]},"1381":{"position":[[36,6],[552,6]]},"1431":{"position":[[1309,6]]},"1849":{"position":[[206,6]]},"1889":{"position":[[370,6]]},"1895":{"position":[[1051,6]]},"1897":{"position":[[806,6]]},"2102":{"position":[[4040,6]]},"2240":{"position":[[554,6]]},"2355":{"position":[[290,6]]},"2359":{"position":[[28,6]]},"2454":{"position":[[664,6]]},"2482":{"position":[[1972,6],[2338,6]]},"2490":{"position":[[317,6]]},"2532":{"position":[[36,6],[589,6]]},"2656":{"position":[[566,6]]},"3040":{"position":[[206,6]]},"3080":{"position":[[370,6]]},"3088":{"position":[[1051,6]]},"3090":{"position":[[806,6]]},"3261":{"position":[[4040,6]]},"3551":{"position":[[554,6]]},"3643":{"position":[[290,6]]},"3647":{"position":[[28,6]]}}}],["haven’t",{"_index":1929,"t":{"128":{"position":[[3233,7]]},"130":{"position":[[2991,7]]}}}],["head",{"_index":1510,"t":{"94":{"position":[[718,4]]}}}],["headach",{"_index":3496,"t":{"489":{"position":[[195,8]]},"1469":{"position":[[208,8]]},"2610":{"position":[[225,9]]}}}],["header",{"_index":2017,"t":{"134":{"position":[[194,7]]},"136":{"position":[[2208,7]]},"138":{"position":[[176,6]]},"160":{"position":[[589,6]]},"162":{"position":[[549,6]]},"204":{"position":[[252,6],[296,6],[1390,6],[1434,6],[1689,6]]},"228":{"position":[[511,8]]},"287":{"position":[[204,6]]},"289":{"position":[[438,8]]},"301":{"position":[[1728,8],[2038,9]]},"421":{"position":[[195,7]]},"429":{"position":[[185,7]]},"560":{"position":[[61,7],[325,7]]},"570":{"position":[[179,6]]},"604":{"position":[[1567,7]]},"652":{"position":[[424,6],[468,6]]},"654":{"position":[[301,6],[345,6]]},"670":{"position":[[16,6],[60,6]]},"672":{"position":[[16,6],[60,6]]},"680":{"position":[[250,6],[294,6]]},"682":{"position":[[157,6],[201,6]]},"684":{"position":[[115,6],[159,6]]},"776":{"position":[[180,6]]},"892":{"position":[[425,6],[944,6]]},"937":{"position":[[469,6]]},"1059":{"position":[[154,7]]},"1061":{"position":[[98,7],[285,7],[510,7],[700,6]]},"1063":{"position":[[291,7],[404,7]]},"1065":{"position":[[3552,6]]},"1079":{"position":[[516,7],[568,7]]},"1085":{"position":[[239,7],[290,7],[413,7]]},"1089":{"position":[[266,7],[757,7]]},"1093":{"position":[[1508,7],[1540,7]]},"1107":{"position":[[215,6],[397,7],[532,6],[1226,6]]},"1135":{"position":[[162,7]]},"1162":{"position":[[503,7]]},"1405":{"position":[[208,7]]},"1413":{"position":[[174,7]]},"1495":{"position":[[9807,6],[9851,6],[10079,6]]},"1533":{"position":[[2133,7]]},"1535":{"position":[[264,9],[963,9]]},"1577":{"position":[[1116,6],[1160,6]]},"1666":{"position":[[1764,7],[1800,7]]},"1699":{"position":[[424,6],[468,6]]},"1702":{"position":[[301,6],[345,6]]},"1715":{"position":[[16,6],[60,6]]},"1717":{"position":[[16,6],[60,6]]},"1731":{"position":[[250,6],[294,6]]},"1733":{"position":[[157,6],[201,6]]},"1735":{"position":[[115,6],[159,6]]},"1795":{"position":[[180,6]]},"1885":{"position":[[581,6],[625,6]]},"1893":{"position":[[762,6],[806,6]]},"1959":{"position":[[425,6],[944,6]]},"2012":{"position":[[469,6]]},"2118":{"position":[[288,6],[332,6]]},"2140":{"position":[[154,7]]},"2142":{"position":[[98,7],[285,7],[510,7],[700,6]]},"2144":{"position":[[291,7],[404,7]]},"2146":{"position":[[3669,6]]},"2162":{"position":[[516,7],[568,7]]},"2168":{"position":[[239,7],[290,7],[413,7]]},"2172":{"position":[[240,7],[731,7]]},"2176":{"position":[[1508,7],[1540,7]]},"2188":{"position":[[218,6],[400,7],[535,6],[1229,6]]},"2214":{"position":[[200,6]]},"2216":{"position":[[162,7]]},"2347":{"position":[[386,7]]},"2351":{"position":[[909,8],[922,9]]},"2367":{"position":[[891,8],[904,9]]},"2562":{"position":[[216,7]]},"2570":{"position":[[174,7]]},"2656":{"position":[[800,6]]},"2662":{"position":[[9963,6],[10007,6],[10199,6]]},"2702":{"position":[[2133,7]]},"2704":{"position":[[264,9],[963,9]]},"2770":{"position":[[1116,6],[1160,6]]},"2823":{"position":[[424,6],[468,6]]},"2826":{"position":[[301,6],[345,6]]},"2883":{"position":[[2043,7],[2079,7]]},"2904":{"position":[[16,6],[60,6]]},"2906":{"position":[[16,6],[60,6]]},"2920":{"position":[[250,6],[294,6]]},"2922":{"position":[[157,6],[201,6]]},"2924":{"position":[[115,6],[159,6]]},"2984":{"position":[[180,6]]},"3076":{"position":[[581,6],[625,6]]},"3084":{"position":[[762,6],[806,6]]},"3172":{"position":[[425,6],[944,6]]},"3198":{"position":[[246,8],[418,6],[524,6],[626,7],[1113,6],[1266,6]]},"3231":{"position":[[469,6]]},"3333":{"position":[[302,6],[346,6],[655,6],[699,6]]},"3369":{"position":[[159,7]]},"3371":{"position":[[98,7],[285,7],[510,7],[700,6],[897,7],[1007,8],[1233,8],[1266,7],[1298,6],[1353,6]]},"3373":{"position":[[291,7],[404,7]]},"3375":{"position":[[3669,6]]},"3391":{"position":[[516,7],[568,7]]},"3397":{"position":[[239,7],[290,7],[413,7]]},"3401":{"position":[[240,7],[731,7]]},"3405":{"position":[[1508,7],[1540,7],[1603,7],[1653,7]]},"3425":{"position":[[197,6],[301,6]]},"3427":{"position":[[214,6],[440,6]]},"3431":{"position":[[227,6],[670,10],[1027,6]]},"3433":{"position":[[104,6]]},"3443":{"position":[[257,6]]},"3445":{"position":[[139,6]]},"3447":{"position":[[472,6]]},"3451":{"position":[[84,6]]},"3455":{"position":[[305,6]]},"3457":{"position":[[162,7]]},"3467":{"position":[[1755,6]]},"3635":{"position":[[386,7]]},"3655":{"position":[[953,8],[966,9]]}}}],["headers.key",{"_index":3724,"t":{"606":{"position":[[299,13],[1078,13]]}}}],["headers.valu",{"_index":3725,"t":{"606":{"position":[[328,15],[1107,15]]}}}],["headers/metadata",{"_index":4345,"t":{"1085":{"position":[[161,16]]},"2168":{"position":[[161,16]]},"3397":{"position":[[161,16]]}}}],["headers=headers)print(resp.json",{"_index":4396,"t":{"1109":{"position":[[674,34]]},"2190":{"position":[[734,34]]},"3431":{"position":[[819,34]]}}}],["health",{"_index":3805,"t":{"614":{"position":[[685,7]]},"924":{"position":[[4,6],[59,6],[113,8],[189,6]]},"926":{"position":[[52,7],[376,6],[398,9],[421,6]]},"930":{"position":[[882,10],[906,6]]},"1549":{"position":[[761,7]]},"1999":{"position":[[4,6],[59,6],[113,8],[189,6]]},"2001":{"position":[[52,7],[376,6],[398,9],[421,6]]},"2005":{"position":[[1071,10],[1095,6]]},"2720":{"position":[[54,7]]},"3214":{"position":[[4,6],[59,6],[113,8],[189,6]]},"3218":{"position":[[52,7],[376,6],[398,9],[421,6]]},"3222":{"position":[[1158,10],[1182,6]]},"3324":{"position":[[2564,6]]}}}],["health_handler_prefix",{"_index":4083,"t":{"930":{"position":[[851,21]]},"2005":{"position":[[1040,21]]},"3222":{"position":[[1127,21]]}}}],["healthi",{"_index":697,"t":{"16":{"position":[[5346,7]]}}}],["heard",{"_index":877,"t":{"28":{"position":[[102,5]]},"94":{"position":[[112,5]]},"252":{"position":[[216,5]]},"373":{"position":[[387,5]]}}}],["heart",{"_index":834,"t":{"24":{"position":[[52,5]]}}}],["heavi",{"_index":679,"t":{"16":{"position":[[4498,5]]},"250":{"position":[[1241,5]]},"319":{"position":[[363,5],[394,5]]},"1129":{"position":[[640,5]]},"1305":{"position":[[363,5],[394,5]]},"2210":{"position":[[687,5]]},"2383":{"position":[[251,5]]},"2452":{"position":[[363,5],[394,5]]},"3451":{"position":[[747,5]]},"3671":{"position":[[251,5]]}}}],["heavili",{"_index":499,"t":{"12":{"position":[[781,7]]},"16":{"position":[[5419,7]]},"86":{"position":[[1963,7]]},"246":{"position":[[1661,7]]},"622":{"position":[[140,7]]},"624":{"position":[[144,7]]},"626":{"position":[[142,7]]},"628":{"position":[[144,7]]},"965":{"position":[[2068,7]]},"1591":{"position":[[140,7]]},"1593":{"position":[[144,7]]},"1595":{"position":[[142,7]]},"1597":{"position":[[144,7]]},"2786":{"position":[[140,7]]},"2788":{"position":[[144,7]]},"2790":{"position":[[142,7]]},"2792":{"position":[[144,7]]},"3349":{"position":[[653,7]]}}}],["hello",{"_index":1013,"t":{"34":{"position":[[2457,11]]},"36":{"position":[[320,10],[607,11]]},"42":{"position":[[149,11]]},"130":{"position":[[2751,7]]},"136":{"position":[[2523,7],[2567,7]]},"262":{"position":[[507,8]]},"1049":{"position":[[1650,6]]},"1109":{"position":[[196,7],[820,11],[1190,7],[1526,11]]},"1111":{"position":[[167,7]]},"1125":{"position":[[849,7],[895,7]]},"1141":{"position":[[1065,6]]},"1143":{"position":[[924,6]]},"1207":{"position":[[1133,11],[1705,10]]},"2190":{"position":[[257,7],[880,11],[1250,7],[1580,11]]},"2192":{"position":[[167,7]]},"2206":{"position":[[849,7],[895,7]]},"2222":{"position":[[1065,6]]},"2224":{"position":[[924,6]]},"2230":{"position":[[1650,6]]},"2240":{"position":[[1630,5]]},"2339":{"position":[[1133,11]]},"3417":{"position":[[1650,6]]},"3431":{"position":[[321,10],[1129,10]]},"3433":{"position":[[213,10]]},"3447":{"position":[[698,7],[744,7]]},"3463":{"position":[[1065,6]]},"3465":{"position":[[924,6]]},"3551":{"position":[[1630,5]]},"3595":{"position":[[1109,10]]}}}],["helm",{"_index":1291,"t":{"62":{"position":[[1482,4]]},"206":{"position":[[588,4]]},"439":{"position":[[103,4]]},"529":{"position":[[28,4]]},"592":{"position":[[25,4]]},"1423":{"position":[[103,4]]},"1437":{"position":[[28,4]]},"1557":{"position":[[25,4]]},"2580":{"position":[[112,4]]},"2620":{"position":[[28,4]]},"2760":{"position":[[25,4]]}}}],["help",{"_index":611,"t":{"16":{"position":[[1777,4]]},"22":{"position":[[61,4]]},"26":{"position":[[398,4],[734,4]]},"28":{"position":[[1259,4]]},"44":{"position":[[1716,4]]},"46":{"position":[[2892,4]]},"50":{"position":[[2048,4]]},"64":{"position":[[737,4]]},"70":{"position":[[467,4]]},"90":{"position":[[467,4],[587,4]]},"94":{"position":[[1150,4]]},"98":{"position":[[778,4]]},"116":{"position":[[789,4]]},"118":{"position":[[349,4]]},"128":{"position":[[771,6]]},"142":{"position":[[145,4]]},"144":{"position":[[268,4]]},"158":{"position":[[61,5]]},"172":{"position":[[455,4]]},"188":{"position":[[901,4],[1021,4]]},"204":{"position":[[1861,4]]},"212":{"position":[[783,4]]},"218":{"position":[[182,4]]},"226":{"position":[[582,4]]},"244":{"position":[[224,7]]},"246":{"position":[[267,6]]},"250":{"position":[[61,4]]},"270":{"position":[[754,7]]},"291":{"position":[[46,4]]},"307":{"position":[[294,4]]},"351":{"position":[[319,7]]},"393":{"position":[[321,4]]},"395":{"position":[[443,4]]},"399":{"position":[[544,4]]},"407":{"position":[[502,6]]},"415":{"position":[[42,4]]},"433":{"position":[[189,4]]},"443":{"position":[[313,4],[707,4]]},"459":{"position":[[92,4]]},"469":{"position":[[230,5]]},"479":{"position":[[5167,6]]},"485":{"position":[[963,4],[1708,7],[1975,4]]},"489":{"position":[[23,4]]},"525":{"position":[[628,4]]},"572":{"position":[[76,4]]},"902":{"position":[[275,4]]},"922":{"position":[[314,7]]},"945":{"position":[[433,4]]},"965":{"position":[[937,5]]},"971":{"position":[[691,5]]},"975":{"position":[[1573,5]]},"983":{"position":[[293,4]]},"989":{"position":[[793,4]]},"1043":{"position":[[552,4]]},"1065":{"position":[[5443,4],[5785,4]]},"1095":{"position":[[407,4]]},"1135":{"position":[[246,4]]},"1184":{"position":[[116,4]]},"1293":{"position":[[294,4]]},"1339":{"position":[[319,7]]},"1399":{"position":[[47,4]]},"1417":{"position":[[189,4]]},"1433":{"position":[[630,4]]},"1459":{"position":[[92,4]]},"1461":{"position":[[88,4]]},"1469":{"position":[[36,4]]},"1511":{"position":[[608,4]]},"1515":{"position":[[343,4]]},"1571":{"position":[[30,5]]},"1741":{"position":[[961,4]]},"1877":{"position":[[610,4]]},"1969":{"position":[[275,4]]},"1997":{"position":[[314,7]]},"2020":{"position":[[433,4]]},"2026":{"position":[[622,4]]},"2030":{"position":[[1665,5]]},"2032":{"position":[[17,4]]},"2042":{"position":[[293,4]]},"2050":{"position":[[793,4]]},"2102":{"position":[[930,5],[4194,5]]},"2134":{"position":[[552,4]]},"2146":{"position":[[5566,4],[5908,4]]},"2178":{"position":[[407,4]]},"2216":{"position":[[246,4]]},"2296":{"position":[[896,4]]},"2440":{"position":[[294,4]]},"2486":{"position":[[319,7]]},"2556":{"position":[[49,4]]},"2574":{"position":[[188,4]]},"2600":{"position":[[93,4]]},"2602":{"position":[[92,4]]},"2610":{"position":[[46,4]]},"2616":{"position":[[635,4]]},"2642":{"position":[[608,4]]},"2646":{"position":[[343,4]]},"2720":{"position":[[245,7],[467,5],[814,7]]},"2730":{"position":[[30,5]]},"2744":{"position":[[874,4]]},"2889":{"position":[[252,5],[575,7],[783,5],[1102,7]]},"2930":{"position":[[961,4]]},"3086":{"position":[[624,4]]},"3182":{"position":[[275,4]]},"3212":{"position":[[314,7]]},"3239":{"position":[[433,4]]},"3261":{"position":[[930,5],[4194,5]]},"3267":{"position":[[622,4]]},"3271":{"position":[[1631,5]]},"3273":{"position":[[17,4]]},"3283":{"position":[[293,4]]},"3289":{"position":[[793,4]]},"3324":{"position":[[460,5],[1078,5],[1675,5],[2091,5],[3005,5],[4129,5],[5210,5],[5907,5],[6127,5]]},"3363":{"position":[[552,4]]},"3375":{"position":[[5595,4],[5937,4]]},"3407":{"position":[[407,4]]},"3457":{"position":[[246,4]]},"3467":{"position":[[1961,4]]},"3486":{"position":[[896,4]]}}}],["helper",{"_index":4701,"t":{"1495":{"position":[[682,6]]},"2662":{"position":[[697,6]]}}}],["henc",{"_index":2334,"t":{"200":{"position":[[1355,6]]},"467":{"position":[[296,5]]}}}],["here",{"_index":105,"t":{"2":{"position":[[1413,4],[1957,4],[2081,6]]},"4":{"position":[[411,4]]},"10":{"position":[[682,4]]},"12":{"position":[[1012,4]]},"16":{"position":[[625,4],[787,4],[1943,4],[4295,4],[6858,4]]},"20":{"position":[[0,4]]},"22":{"position":[[19,4],[232,4]]},"28":{"position":[[2197,4]]},"34":{"position":[[1413,4],[2370,4]]},"44":{"position":[[583,4],[1316,4]]},"46":{"position":[[2475,4]]},"50":{"position":[[0,4],[617,4],[2053,5]]},"56":{"position":[[141,5]]},"58":{"position":[[550,5]]},"62":{"position":[[409,4]]},"70":{"position":[[580,4]]},"76":{"position":[[377,5]]},"86":{"position":[[614,4]]},"88":{"position":[[619,4],[716,4]]},"90":{"position":[[64,5],[755,4]]},"92":{"position":[[62,4]]},"94":{"position":[[315,4],[1177,4]]},"98":{"position":[[800,4]]},"108":{"position":[[1510,4]]},"110":{"position":[[338,4]]},"112":{"position":[[15,4],[111,4]]},"116":{"position":[[169,4]]},"132":{"position":[[802,5],[1339,4]]},"134":{"position":[[469,5]]},"136":{"position":[[920,4],[1341,4],[1704,4]]},"138":{"position":[[102,4]]},"142":{"position":[[0,4],[341,4]]},"146":{"position":[[596,4]]},"156":{"position":[[36,5]]},"158":{"position":[[560,5]]},"166":{"position":[[528,4]]},"168":{"position":[[13,4]]},"170":{"position":[[403,4]]},"174":{"position":[[199,4]]},"176":{"position":[[488,4]]},"178":{"position":[[1082,4],[2219,4]]},"198":{"position":[[1399,5],[1625,5]]},"210":{"position":[[748,4]]},"214":{"position":[[1164,4]]},"226":{"position":[[245,5],[356,4]]},"228":{"position":[[16,4]]},"232":{"position":[[0,4]]},"238":{"position":[[426,4]]},"244":{"position":[[252,4]]},"246":{"position":[[1453,4]]},"250":{"position":[[1121,4]]},"256":{"position":[[387,4]]},"262":{"position":[[550,4]]},"264":{"position":[[1504,5]]},"270":{"position":[[203,5],[595,5]]},"281":{"position":[[44,4],[217,4]]},"283":{"position":[[1792,4],[2441,5]]},"287":{"position":[[652,4]]},"291":{"position":[[243,4]]},"295":{"position":[[141,4]]},"299":{"position":[[507,5]]},"301":{"position":[[2877,4]]},"307":{"position":[[299,5]]},"321":{"position":[[967,4]]},"335":{"position":[[340,4]]},"349":{"position":[[280,4]]},"359":{"position":[[1240,4]]},"379":{"position":[[3076,4]]},"397":{"position":[[595,5]]},"399":{"position":[[1196,4]]},"403":{"position":[[14,4]]},"407":{"position":[[756,4]]},"453":{"position":[[256,5]]},"467":{"position":[[189,5]]},"475":{"position":[[3093,4]]},"477":{"position":[[1540,4],[3336,4],[4348,4],[6172,4],[6787,4],[6945,4]]},"479":{"position":[[3415,4]]},"481":{"position":[[2928,4],[3546,4],[4215,4],[4714,8],[6502,5]]},"483":{"position":[[183,4],[1097,8],[1864,4]]},"485":{"position":[[1539,5]]},"491":{"position":[[550,4]]},"507":{"position":[[385,4]]},"519":{"position":[[692,5],[964,5]]},"570":{"position":[[0,4]]},"572":{"position":[[0,4],[795,4],[829,4]]},"604":{"position":[[652,4]]},"618":{"position":[[221,4]]},"640":{"position":[[131,4]]},"648":{"position":[[724,4],[840,4],[937,4],[970,4],[1093,5]]},"666":{"position":[[587,4],[703,4],[800,4],[833,4],[951,5]]},"732":{"position":[[309,4]]},"734":{"position":[[983,4]]},"768":{"position":[[264,4]]},"780":{"position":[[235,4]]},"808":{"position":[[86,4]]},"884":{"position":[[0,4],[164,7],[198,7]]},"953":{"position":[[670,4]]},"963":{"position":[[1142,4]]},"965":{"position":[[3631,5]]},"977":{"position":[[1434,4],[1615,4]]},"993":{"position":[[92,5]]},"1065":{"position":[[4757,4],[4921,4]]},"1073":{"position":[[397,4]]},"1085":{"position":[[120,4]]},"1093":{"position":[[220,4]]},"1101":{"position":[[174,4],[352,5]]},"1105":{"position":[[177,4]]},"1107":{"position":[[1179,4],[1356,4]]},"1109":{"position":[[1372,5]]},"1129":{"position":[[1055,5]]},"1141":{"position":[[0,4]]},"1162":{"position":[[2354,4]]},"1164":{"position":[[851,4],[1228,4],[1876,4]]},"1166":{"position":[[755,4]]},"1184":{"position":[[0,4],[56,4],[156,4]]},"1209":{"position":[[37,5]]},"1224":{"position":[[37,5]]},"1245":{"position":[[29,5]]},"1293":{"position":[[299,5]]},"1307":{"position":[[967,4]]},"1319":{"position":[[786,5]]},"1323":{"position":[[417,4]]},"1337":{"position":[[280,4]]},"1369":{"position":[[385,4]]},"1379":{"position":[[1240,4]]},"1453":{"position":[[257,5]]},"1473":{"position":[[120,4]]},"1475":{"position":[[459,4]]},"1495":{"position":[[7641,4],[8925,4],[10513,5]]},"1511":{"position":[[943,4],[977,4]]},"1533":{"position":[[740,4]]},"1567":{"position":[[186,4],[261,4]]},"1577":{"position":[[1596,4]]},"1611":{"position":[[817,4]]},"1617":{"position":[[131,4]]},"1639":{"position":[[843,4]]},"1641":{"position":[[294,5]]},"1668":{"position":[[55,5]]},"1694":{"position":[[724,4],[840,4],[937,4],[970,4],[1093,5]]},"1710":{"position":[[587,4],[703,4],[800,4],[833,4],[951,5]]},"1787":{"position":[[264,4]]},"1831":{"position":[[235,4]]},"1859":{"position":[[86,4]]},"1881":{"position":[[1014,4]]},"1937":{"position":[[766,4]]},"1951":{"position":[[0,4],[164,7],[198,7]]},"2026":{"position":[[699,4]]},"2030":{"position":[[1689,4]]},"2034":{"position":[[1434,4],[1615,4]]},"2054":{"position":[[92,5]]},"2080":{"position":[[670,4]]},"2086":{"position":[[421,4]]},"2100":{"position":[[1142,4]]},"2102":{"position":[[3506,4],[4776,5]]},"2146":{"position":[[4880,4],[5044,4]]},"2152":{"position":[[4558,4],[4653,5]]},"2154":{"position":[[397,4]]},"2168":{"position":[[120,4]]},"2176":{"position":[[220,4]]},"2186":{"position":[[177,4]]},"2188":{"position":[[1182,4],[1359,4]]},"2210":{"position":[[1097,5]]},"2222":{"position":[[0,4]]},"2244":{"position":[[534,4],[918,4],[1674,4]]},"2296":{"position":[[1133,4]]},"2345":{"position":[[1223,4]]},"2434":{"position":[[1308,4],[1903,4]]},"2440":{"position":[[299,5]]},"2454":{"position":[[967,4]]},"2466":{"position":[[786,5]]},"2470":{"position":[[417,4]]},"2482":{"position":[[56,4],[2484,4]]},"2484":{"position":[[280,4]]},"2520":{"position":[[385,4]]},"2594":{"position":[[261,5]]},"2610":{"position":[[551,5]]},"2642":{"position":[[943,4],[977,4]]},"2652":{"position":[[436,4]]},"2660":{"position":[[246,5]]},"2662":{"position":[[7729,4],[9045,4],[10636,5]]},"2666":{"position":[[120,4]]},"2668":{"position":[[459,4]]},"2702":{"position":[[740,4]]},"2726":{"position":[[186,4],[261,4]]},"2736":{"position":[[817,4]]},"2746":{"position":[[235,4]]},"2770":{"position":[[1560,4]]},"2804":{"position":[[131,4]]},"2818":{"position":[[724,4],[840,4],[937,4],[970,4],[1093,5]]},"2856":{"position":[[843,4]]},"2858":{"position":[[294,5]]},"2887":{"position":[[55,5]]},"2899":{"position":[[587,4],[703,4],[800,4],[833,4],[951,5]]},"2976":{"position":[[408,4]]},"3018":{"position":[[229,4]]},"3022":{"position":[[235,4]]},"3050":{"position":[[86,4]]},"3072":{"position":[[1302,4]]},"3086":{"position":[[701,4]]},"3134":{"position":[[766,4]]},"3164":{"position":[[0,4],[164,7],[198,7]]},"3247":{"position":[[670,4]]},"3253":{"position":[[421,4]]},"3259":{"position":[[1142,4]]},"3261":{"position":[[3506,4],[4776,5]]},"3267":{"position":[[699,4]]},"3271":{"position":[[1655,4]]},"3275":{"position":[[1434,4],[1615,4]]},"3293":{"position":[[92,5]]},"3324":{"position":[[0,4]]},"3347":{"position":[[0,4]]},"3349":{"position":[[697,5],[1980,4]]},"3353":{"position":[[108,4]]},"3375":{"position":[[4909,4],[5073,4]]},"3381":{"position":[[4561,4],[4656,5]]},"3383":{"position":[[397,4]]},"3397":{"position":[[120,4]]},"3405":{"position":[[220,4]]},"3425":{"position":[[247,4]]},"3431":{"position":[[164,4]]},"3451":{"position":[[1157,5]]},"3463":{"position":[[0,4]]},"3469":{"position":[[664,5]]},"3471":{"position":[[567,5]]},"3486":{"position":[[1133,4]]},"3490":{"position":[[0,4]]},"3545":{"position":[[22,4]]},"3555":{"position":[[534,4],[918,4],[1674,4]]},"3612":{"position":[[1335,4],[1930,4]]},"3629":{"position":[[1165,4]]},"3633":{"position":[[1223,4]]}}}],["here'",{"_index":5550,"t":{"2972":{"position":[[117,6]]}}}],["here>\"api_key",{"_index":4034,"t":{"886":{"position":[[222,13]]},"888":{"position":[[129,14]]},"1953":{"position":[[222,13]]},"1955":{"position":[[129,14]]},"3166":{"position":[[222,13]]},"3168":{"position":[[129,14]]}}}],["here>\"log_level",{"_index":4035,"t":{"886":{"position":[[253,15]]},"888":{"position":[[159,16]]},"1953":{"position":[[253,15]]},"1955":{"position":[[159,16]]},"3166":{"position":[[253,15]]},"3168":{"position":[[159,16]]}}}],["hey",{"_index":2505,"t":{"246":{"position":[[371,3]]}}}],["high",{"_index":579,"t":{"16":{"position":[[271,4],[3275,4]]},"46":{"position":[[3250,4]]},"246":{"position":[[854,4]]},"256":{"position":[[1547,4]]},"361":{"position":[[1320,4]]},"479":{"position":[[2373,4]]},"979":{"position":[[44,4]]},"1381":{"position":[[1325,4]]},"1611":{"position":[[270,4]]},"2036":{"position":[[44,4]]},"2532":{"position":[[1412,4]]},"2722":{"position":[[1674,4]]},"2736":{"position":[[270,4]]},"2744":{"position":[[42,4]]},"3277":{"position":[[44,4]]}}}],["higher",{"_index":420,"t":{"10":{"position":[[665,7]]},"327":{"position":[[85,7]]},"1313":{"position":[[85,7]]},"2460":{"position":[[85,7]]}}}],["highest",{"_index":4021,"t":{"876":{"position":[[320,7]]},"1943":{"position":[[320,7]]},"3154":{"position":[[71,8]]},"3156":{"position":[[328,7]]}}}],["highli",{"_index":2954,"t":{"371":{"position":[[655,6]]},"1391":{"position":[[655,6]]},"2086":{"position":[[305,6]]},"2126":{"position":[[47,6]]},"2542":{"position":[[668,6]]},"3253":{"position":[[305,6]]},"3341":{"position":[[47,6]]}}}],["highlight",{"_index":1418,"t":{"84":{"position":[[187,10]]},"204":{"position":[[1171,11]]},"234":{"position":[[66,9]]},"244":{"position":[[515,10]]},"489":{"position":[[1312,10]]}}}],["hijack",{"_index":2144,"t":{"160":{"position":[[999,10]]},"892":{"position":[[146,9]]},"1959":{"position":[[146,9]]},"3172":{"position":[[146,9]]}}}],["himself",{"_index":4607,"t":{"1188":{"position":[[478,7]]}}}],["histogram",{"_index":5610,"t":{"3324":{"position":[[4869,9],[5360,9],[6035,9]]}}}],["histori",{"_index":172,"t":{"2":{"position":[[2786,7],[2798,7]]},"22":{"position":[[475,7]]},"42":{"position":[[76,7],[534,7],[1101,7],[2403,7]]},"46":{"position":[[1439,7],[1525,7],[2570,7],[4022,7]]},"48":{"position":[[493,7],[906,7],[953,7]]},"54":{"position":[[236,8]]},"64":{"position":[[437,7]]},"70":{"position":[[54,7],[139,7],[184,8],[1175,8]]},"72":{"position":[[94,8],[245,7]]},"74":{"position":[[125,7],[354,7],[443,7],[863,7]]},"78":{"position":[[657,7],[681,7]]},"82":{"position":[[171,7]]},"84":{"position":[[214,7]]},"138":{"position":[[462,7]]},"142":{"position":[[591,7]]},"148":{"position":[[1029,7]]},"156":{"position":[[550,7]]},"166":{"position":[[2071,7]]},"168":{"position":[[360,8],[739,7]]},"178":{"position":[[2811,7]]},"194":{"position":[[202,7]]},"200":{"position":[[107,8],[168,7],[218,7],[278,7],[616,7],[692,7],[881,8],[1271,7]]},"246":{"position":[[2874,7]]},"291":{"position":[[497,7]]},"317":{"position":[[180,7],[402,7],[482,7]]},"321":{"position":[[386,7],[497,7],[901,7]]},"361":{"position":[[174,8],[607,7],[646,7],[709,7],[863,7],[948,7],[1026,7],[1508,7],[1621,7]]},"363":{"position":[[75,7],[182,7],[1195,7]]},"371":{"position":[[741,9],[920,7]]},"375":{"position":[[218,7]]},"391":{"position":[[1313,7]]},"433":{"position":[[40,7],[95,7],[165,8]]},"469":{"position":[[592,7],[904,7]]},"477":{"position":[[3475,7],[3619,7]]},"511":{"position":[[44,7],[94,7],[307,7],[494,7],[650,7]]},"539":{"position":[[78,7]]},"541":{"position":[[8,7],[79,7],[216,7]]},"566":{"position":[[59,7]]},"576":{"position":[[798,7]]},"600":{"position":[[190,8],[469,7],[535,7]]},"618":{"position":[[299,7]]},"622":{"position":[[381,8]]},"638":{"position":[[150,7]]},"712":{"position":[[36,7],[108,7],[238,7],[395,7],[437,7],[532,7],[645,7]]},"714":{"position":[[71,7],[121,7],[226,7],[298,7],[353,7],[393,7]]},"716":{"position":[[545,7],[637,7],[738,7]]},"718":{"position":[[379,7],[478,7]]},"720":{"position":[[68,7],[138,7],[209,7]]},"798":{"position":[[149,7],[216,7]]},"806":{"position":[[154,7]]},"850":{"position":[[152,7]]},"961":{"position":[[0,7],[62,7],[367,7],[480,7],[551,8],[589,7],[682,7],[758,7],[802,7],[973,7],[1035,7],[1349,7],[1439,7]]},"963":{"position":[[0,7],[231,7],[398,7],[553,7],[652,7],[1737,8]]},"965":{"position":[[479,7],[1379,7],[2152,7],[3155,7],[3714,7]]},"969":{"position":[[492,7],[573,7]]},"971":{"position":[[83,7],[661,7]]},"973":{"position":[[295,7]]},"975":{"position":[[698,7],[915,7],[1543,7]]},"989":{"position":[[900,7]]},"993":{"position":[[302,8]]},"1073":{"position":[[3109,7]]},"1109":{"position":[[2090,7],[2396,7]]},"1111":{"position":[[438,7]]},"1121":{"position":[[1019,7]]},"1125":{"position":[[0,7],[31,7],[151,7],[323,7],[375,7],[477,10],[559,10],[925,7],[1035,7],[1487,7],[1657,7]]},"1127":{"position":[[55,8],[479,7],[598,7],[606,7]]},"1162":{"position":[[1136,7],[1331,7]]},"1166":{"position":[[634,7]]},"1174":{"position":[[260,7],[303,7]]},"1182":{"position":[[1438,7]]},"1303":{"position":[[180,7],[402,7],[482,7]]},"1307":{"position":[[386,7],[497,7],[901,7]]},"1373":{"position":[[44,7],[94,7],[307,7],[494,7],[650,7]]},"1381":{"position":[[174,8],[607,7],[646,7],[718,7],[868,7],[953,7],[1031,7],[1513,7],[1626,7]]},"1383":{"position":[[75,7],[185,7],[330,7],[1059,8],[1346,7]]},"1391":{"position":[[741,9],[920,7]]},"1417":{"position":[[40,7],[95,7],[165,8]]},"1473":{"position":[[565,7]]},"1475":{"position":[[1421,7]]},"1489":{"position":[[368,7]]},"1527":{"position":[[48,8]]},"1583":{"position":[[1176,7]]},"1591":{"position":[[381,8]]},"1607":{"position":[[150,7]]},"1619":{"position":[[190,8],[751,7],[817,7]]},"1676":{"position":[[321,7]]},"1678":{"position":[[651,7]]},"1680":{"position":[[1091,7]]},"1741":{"position":[[707,7]]},"1745":{"position":[[37,7],[66,7],[212,7],[320,7],[437,7],[529,7],[605,7],[735,7],[757,7],[889,7],[913,7],[1050,7],[1081,7],[1219,7]]},"1749":{"position":[[201,7]]},"1751":{"position":[[206,7]]},"1849":{"position":[[146,7],[213,7]]},"1857":{"position":[[151,7]]},"1869":{"position":[[273,7]]},"1891":{"position":[[36,7],[108,7],[238,7],[395,7],[437,7],[532,7],[645,7],[817,7]]},"1893":{"position":[[71,7],[121,7],[226,7],[298,7],[353,7],[393,7],[640,7],[719,7],[885,10],[1135,7],[1221,7],[1301,7],[1335,7],[1444,7]]},"1895":{"position":[[637,7],[738,7],[839,7],[1039,7]]},"1897":{"position":[[515,7],[614,7],[794,7]]},"1909":{"position":[[107,7]]},"1911":{"position":[[79,7]]},"1913":{"position":[[107,7]]},"1937":{"position":[[100,7],[256,8]]},"2010":{"position":[[232,7]]},"2024":{"position":[[492,7],[573,7]]},"2026":{"position":[[85,7],[134,7],[751,7]]},"2028":{"position":[[295,7]]},"2030":{"position":[[514,7],[947,7],[1182,7],[1741,7]]},"2048":{"position":[[514,7]]},"2050":{"position":[[900,7]]},"2054":{"position":[[302,8]]},"2098":{"position":[[0,7],[62,7],[189,7],[409,7],[522,7],[591,7],[637,7],[738,7],[814,7],[858,7],[1029,7],[1101,7],[1424,7],[1514,7]]},"2100":{"position":[[0,7],[231,7],[398,7],[553,7],[652,7],[1737,8]]},"2102":{"position":[[479,7],[747,7],[1379,7],[1553,7],[3195,7],[3241,7],[3853,7],[4859,7]]},"2154":{"position":[[3109,7]]},"2190":{"position":[[2144,7],[2680,7]]},"2192":{"position":[[438,7]]},"2202":{"position":[[1062,7]]},"2206":{"position":[[0,7],[31,7],[151,7],[323,7],[375,7],[477,10],[559,10],[925,7],[1035,7],[1487,7],[1657,7]]},"2208":{"position":[[55,8],[479,7],[598,7],[606,7]]},"2240":{"position":[[1983,7],[2009,7]]},"2250":{"position":[[1438,7]]},"2359":{"position":[[346,7]]},"2365":{"position":[[189,7]]},"2379":{"position":[[42,7],[92,7],[307,7],[494,7],[650,7]]},"2450":{"position":[[180,7],[402,7],[482,7]]},"2454":{"position":[[386,7],[497,7],[901,7]]},"2502":{"position":[[244,8]]},"2504":{"position":[[134,7],[582,8]]},"2506":{"position":[[1196,7]]},"2524":{"position":[[44,7],[94,7],[307,7],[494,7],[650,7]]},"2530":{"position":[[2160,7],[2239,7]]},"2532":{"position":[[178,8],[644,7],[687,7],[759,7],[924,7],[1014,7],[1093,7],[1510,7],[1673,7],[1800,7]]},"2534":{"position":[[77,7],[194,7],[343,7],[1112,8]]},"2542":{"position":[[754,9],[934,7]]},"2574":{"position":[[41,7],[96,7],[164,8],[398,7]]},"2666":{"position":[[565,7]]},"2668":{"position":[[1421,7]]},"2682":{"position":[[368,7]]},"2696":{"position":[[48,8]]},"2778":{"position":[[1274,7]]},"2786":{"position":[[381,8]]},"2802":{"position":[[150,7]]},"2806":{"position":[[190,8],[751,7],[817,7]]},"2830":{"position":[[324,7]]},"2832":{"position":[[657,7]]},"2834":{"position":[[1097,7]]},"2930":{"position":[[707,7]]},"2934":{"position":[[37,7],[66,7],[212,7],[320,7],[437,7],[529,7],[605,7],[735,7],[757,7],[889,7],[913,7],[1050,7],[1081,7],[1219,7]]},"2938":{"position":[[201,7]]},"2940":{"position":[[206,7]]},"3040":{"position":[[146,7],[213,7]]},"3048":{"position":[[151,7]]},"3060":{"position":[[309,8]]},"3082":{"position":[[36,7],[108,7],[238,7],[395,7],[437,7],[532,7],[645,7],[817,7]]},"3084":{"position":[[71,7],[121,7],[226,7],[298,7],[353,7],[393,7],[640,7],[719,7],[1099,7],[1185,7],[1265,7],[1299,7],[1408,7]]},"3086":{"position":[[45,7],[119,7],[753,7]]},"3088":{"position":[[637,7],[738,7],[839,7],[1039,7]]},"3090":{"position":[[515,7],[614,7],[794,7]]},"3102":{"position":[[107,7]]},"3104":{"position":[[79,7]]},"3106":{"position":[[107,7]]},"3134":{"position":[[100,7],[256,8]]},"3227":{"position":[[232,7]]},"3257":{"position":[[0,7],[62,7],[189,7],[409,7],[522,7],[591,7],[637,7],[738,7],[814,7],[858,7],[1029,7],[1101,7],[1424,7],[1514,7]]},"3259":{"position":[[0,7],[231,7],[398,7],[553,7],[652,7],[1737,8]]},"3261":{"position":[[479,7],[747,7],[1379,7],[1553,7],[3195,7],[3241,7],[3853,7],[4859,7]]},"3265":{"position":[[492,7],[573,7]]},"3267":{"position":[[85,7],[134,7],[751,7]]},"3269":{"position":[[295,7]]},"3271":{"position":[[514,7],[913,7],[1148,7],[1707,7]]},"3289":{"position":[[900,7]]},"3293":{"position":[[302,8]]},"3347":{"position":[[3144,7],[3245,7],[3499,7]]},"3383":{"position":[[3109,7]]},"3431":{"position":[[1652,7],[2188,7]]},"3433":{"position":[[513,7]]},"3443":{"position":[[900,7]]},"3447":{"position":[[0,7],[31,7],[151,7],[323,7],[375,7],[774,7],[877,7],[1329,7],[1499,7]]},"3449":{"position":[[55,8],[172,7],[284,7],[292,7]]},"3551":{"position":[[1983,7],[2009,7]]},"3561":{"position":[[1438,7]]},"3647":{"position":[[346,7]]},"3653":{"position":[[189,7]]},"3667":{"position":[[42,7],[92,7],[307,7],[494,7],[650,7]]}}}],["history(ch",{"_index":1137,"t":{"46":{"position":[[1019,10]]}}}],["history(channel",{"_index":5388,"t":{"2369":{"position":[[1946,16]]},"3657":{"position":[[1946,16]]}}}],["history(limit",{"_index":4120,"t":{"963":{"position":[[175,14],[341,14],[502,14],[600,14],[715,14],[856,14],[1071,14]]},"2100":{"position":[[175,14],[341,14],[502,14],[600,14],[715,14],[856,14],[1071,14]]},"3259":{"position":[[175,14],[341,14],[502,14],[600,14],[715,14],[856,14],[1071,14]]}}}],["history(opt",{"_index":5376,"t":{"2365":{"position":[[141,16]]},"3653":{"position":[[141,16]]}}}],["history/pres",{"_index":4159,"t":{"983":{"position":[[1133,16]]},"2042":{"position":[[1133,16]]},"3283":{"position":[[1133,16]]}}}],["history_disable_for_cli",{"_index":3898,"t":{"720":{"position":[[0,26],[296,26]]}}}],["history_lifetim",{"_index":3603,"t":{"550":{"position":[[64,16]]},"570":{"position":[[357,16]]}}}],["history_meta_ttl",{"_index":2335,"t":{"200":{"position":[[1362,16]]},"570":{"position":[[780,16],[831,16],[878,16],[1203,16]]},"971":{"position":[[0,17],[40,16]]},"975":{"position":[[774,17],[846,16]]},"991":{"position":[[675,17]]},"2026":{"position":[[0,17],[53,16]]},"2030":{"position":[[862,17],[915,16],[1037,16]]},"2052":{"position":[[675,17]]},"3086":{"position":[[0,16],[841,16],[943,16]]},"3267":{"position":[[0,17],[53,16]]},"3271":{"position":[[828,17],[881,16],[1003,16]]},"3291":{"position":[[675,17]]}}}],["history_recov",{"_index":3602,"t":{"550":{"position":[[17,15]]},"570":{"position":[[397,15]]}}}],["history_remov",{"_index":4447,"t":{"1127":{"position":[[0,14],[184,17],[261,17]]},"2208":{"position":[[0,14],[184,17],[261,17]]},"3449":{"position":[[0,14]]}}}],["history_s",{"_index":3891,"t":{"712":{"position":[[0,12],[271,12],[495,12]]},"714":{"position":[[434,12],[563,15]]},"716":{"position":[[664,12]]},"718":{"position":[[404,12]]},"732":{"position":[[249,15]]},"734":{"position":[[1285,15],[1399,15]]},"961":{"position":[[75,12]]},"1881":{"position":[[1260,15],[1337,15]]},"1891":{"position":[[0,12],[271,12],[495,12]]},"1893":{"position":[[434,12],[563,15]]},"1895":{"position":[[765,12]]},"1897":{"position":[[540,12]]},"1937":{"position":[[397,15],[1184,15]]},"2098":{"position":[[75,12]]},"2102":{"position":[[3787,12]]},"3072":{"position":[[1548,15],[1625,15]]},"3082":{"position":[[0,12],[271,12],[495,12]]},"3084":{"position":[[434,12],[563,15]]},"3088":{"position":[[765,12]]},"3090":{"position":[[540,12]]},"3134":{"position":[[397,15],[1184,15]]},"3257":{"position":[[75,12]]},"3261":{"position":[[3787,12]]}}}],["history_ttl",{"_index":3604,"t":{"550":{"position":[[92,11]]},"570":{"position":[[385,11],[1159,11]]},"712":{"position":[[588,11]]},"714":{"position":[[0,11],[451,11],[583,14]]},"716":{"position":[[681,11]]},"718":{"position":[[421,11]]},"732":{"position":[[269,14]]},"734":{"position":[[1305,14],[1419,14]]},"941":{"position":[[258,11]]},"961":{"position":[[92,11]]},"1881":{"position":[[1280,14],[1357,14]]},"1891":{"position":[[588,11]]},"1893":{"position":[[0,11],[451,11],[583,14]]},"1895":{"position":[[782,11]]},"1897":{"position":[[557,11]]},"1937":{"position":[[417,14],[1204,14]]},"2016":{"position":[[258,11]]},"2098":{"position":[[92,11]]},"2102":{"position":[[3889,11]]},"3072":{"position":[[1568,14],[1645,14]]},"3082":{"position":[[588,11]]},"3084":{"position":[[0,11],[451,11],[583,14]]},"3088":{"position":[[782,11]]},"3090":{"position":[[557,11]]},"3134":{"position":[[417,14],[1204,14]]},"3235":{"position":[[258,11]]},"3257":{"position":[[92,11]]},"3261":{"position":[[3889,11]]}}}],["historyfilt",{"_index":1139,"t":{"46":{"position":[[1045,14]]}}}],["historyresult",{"_index":1345,"t":{"70":{"position":[[1009,14]]},"963":{"position":[[1571,14]]},"2100":{"position":[[1571,14]]},"3259":{"position":[[1571,14]]}}}],["historyresult.epoch",{"_index":1361,"t":{"70":{"position":[[1349,20]]},"963":{"position":[[1911,20]]},"2100":{"position":[[1911,20]]},"3259":{"position":[[1911,20]]}}}],["historyresult.publ",{"_index":1356,"t":{"70":{"position":[[1216,26]]},"963":{"position":[[1778,26]]},"2100":{"position":[[1778,26]]},"3259":{"position":[[1778,26]]}}}],["hit",{"_index":1185,"t":{"50":{"position":[[158,4]]},"162":{"position":[[224,3]]},"166":{"position":[[405,4]]},"742":{"position":[[964,3]]},"1676":{"position":[[1922,3]]},"1761":{"position":[[964,3]]},"2830":{"position":[[1925,3]]},"2950":{"position":[[1012,4]]}}}],["hm",{"_index":2614,"t":{"260":{"position":[[752,3],[783,5]]},"262":{"position":[[751,3]]},"264":{"position":[[8,3],[119,5],[156,3],[355,3]]},"1625":{"position":[[124,4]]},"1627":{"position":[[248,4]]},"1629":{"position":[[8,3],[119,5],[156,3],[352,3]]},"1631":{"position":[[310,3],[341,4]]},"1635":{"position":[[215,3]]},"1639":{"position":[[957,4],[987,3]]},"1641":{"position":[[163,8]]},"1650":{"position":[[243,4]]},"1666":{"position":[[881,3],[929,3],[980,3],[1378,3],[1422,3],[1666,3],[1691,3]]},"2842":{"position":[[124,4]]},"2844":{"position":[[248,4]]},"2846":{"position":[[8,3],[119,5],[156,3],[352,3]]},"2848":{"position":[[310,3],[341,4]]},"2852":{"position":[[215,3]]},"2856":{"position":[[957,4],[987,3]]},"2858":{"position":[[163,8]]},"2862":{"position":[[602,3]]},"2867":{"position":[[243,4]]},"2883":{"position":[[1078,3],[1126,3],[1177,3],[1657,3],[1701,3],[1945,3],[1970,3]]}}}],["hmac",{"_index":2227,"t":{"178":{"position":[[1128,4],[1174,4]]},"210":{"position":[[815,4]]},"768":{"position":[[359,4],[467,4]]},"868":{"position":[[104,4]]},"1035":{"position":[[375,4]]},"1495":{"position":[[6259,4],[6752,4]]},"1787":{"position":[[359,4],[467,4]]},"1825":{"position":[[331,4]]},"1827":{"position":[[193,4]]},"2066":{"position":[[104,4]]},"2068":{"position":[[121,4]]},"2662":{"position":[[6328,4],[6833,4]]},"2976":{"position":[[503,4],[611,4]]},"3014":{"position":[[691,4]]},"3016":{"position":[[193,4]]},"3018":{"position":[[296,4]]},"3144":{"position":[[104,4]]},"3146":{"position":[[121,4]]}}}],["hms_app_id",{"_index":4922,"t":{"1641":{"position":[[172,13]]},"2858":{"position":[[172,13]]}}}],["hms_app_secret",{"_index":4924,"t":{"1641":{"position":[[203,17]]},"2858":{"position":[[203,17]]}}}],["hms_condit",{"_index":4980,"t":{"1666":{"position":[[946,13]]},"2883":{"position":[[1143,13]]}}}],["hms_token",{"_index":4978,"t":{"1666":{"position":[[833,10]]},"2883":{"position":[[1030,10]]}}}],["hms_topic",{"_index":4979,"t":{"1666":{"position":[[899,9]]},"2883":{"position":[[1096,9]]}}}],["hmspushnotif",{"_index":4984,"t":{"1666":{"position":[[1382,19],[1589,20]]},"2883":{"position":[[1661,19],[1868,20]]}}}],["hold",{"_index":1845,"t":{"128":{"position":[[298,4]]}}}],["homogen",{"_index":2555,"t":{"246":{"position":[[3879,11]]}}}],["honest",{"_index":2565,"t":{"250":{"position":[[1114,6]]},"281":{"position":[[27,7]]},"989":{"position":[[762,6]]},"1495":{"position":[[10306,7]]},"2050":{"position":[[762,6]]},"2662":{"position":[[10425,7]]},"3289":{"position":[[762,6]]}}}],["hood",{"_index":4123,"t":{"965":{"position":[[2041,5]]},"3612":{"position":[[135,6]]}}}],["hook",{"_index":2816,"t":{"301":{"position":[[1322,5],[1348,4],[2358,4]]},"345":{"position":[[270,5],[343,5]]},"347":{"position":[[39,5],[968,4],[2110,5]]},"349":{"position":[[181,4],[239,4]]},"371":{"position":[[880,5]]},"764":{"position":[[1996,4],[2112,5]]},"1333":{"position":[[270,5],[343,5]]},"1335":{"position":[[39,5],[972,4],[2114,5]]},"1337":{"position":[[181,4],[239,4]]},"1391":{"position":[[880,5]]},"1783":{"position":[[1996,4],[2112,5]]},"2480":{"position":[[270,5],[343,5]]},"2482":{"position":[[139,5],[274,5],[581,6],[1036,4],[2356,5],[2530,6]]},"2484":{"position":[[181,4],[239,4]]},"2504":{"position":[[617,5]]},"2542":{"position":[[894,5]]},"2972":{"position":[[1102,4]]}}}],["hope",{"_index":823,"t":{"22":{"position":[[0,4]]},"90":{"position":[[143,4]]},"142":{"position":[[736,4]]},"148":{"position":[[1402,4]]},"170":{"position":[[256,4]]},"192":{"position":[[698,4]]},"224":{"position":[[146,4]]},"481":{"position":[[6508,4]]}}}],["hopefulli",{"_index":2351,"t":{"204":{"position":[[2319,9]]},"485":{"position":[[899,9],[2055,9]]}}}],["horizont",{"_index":91,"t":{"2":{"position":[[1172,12]]},"16":{"position":[[226,10]]},"168":{"position":[[479,10]]},"371":{"position":[[21,12]]},"1391":{"position":[[21,12]]},"2542":{"position":[[21,12]]},"3345":{"position":[[840,12]]}}}],["host",{"_index":1597,"t":{"98":{"position":[[1928,4]]},"134":{"position":[[904,4],[909,6],[1329,4],[1334,6],[1812,4],[1912,4]]},"148":{"position":[[230,7],[960,6]]},"287":{"position":[[869,4],[874,6],[1206,4],[1211,6]]},"299":{"position":[[1094,4]]},"612":{"position":[[737,4]]},"975":{"position":[[467,4]]},"977":{"position":[[1552,6]]},"983":{"position":[[702,6]]},"1001":{"position":[[942,4],[1196,4]]},"1003":{"position":[[372,4],[746,4]]},"1547":{"position":[[906,4]]},"2032":{"position":[[236,4],[1172,7]]},"2034":{"position":[[1552,6]]},"2038":{"position":[[221,4],[1229,7]]},"2042":{"position":[[702,6]]},"2108":{"position":[[942,4],[1196,4]]},"2110":{"position":[[372,4],[746,4]]},"2500":{"position":[[49,6]]},"2610":{"position":[[1907,6]]},"2660":{"position":[[239,6]]},"2716":{"position":[[906,4]]},"3273":{"position":[[236,4],[1064,7]]},"3275":{"position":[[1552,6]]},"3279":{"position":[[221,4],[1121,7]]},"3283":{"position":[[702,6]]},"3309":{"position":[[942,4],[1196,4]]},"3311":{"position":[[372,4],[746,4]]}}}],["host.docker.intern",{"_index":2048,"t":{"134":{"position":[[1791,20]]},"299":{"position":[[1063,20]]}}}],["host/dir/with/config/file:/centrifugo",{"_index":3561,"t":{"525":{"position":[[143,38]]},"590":{"position":[[126,38]]},"1433":{"position":[[145,38]]},"1555":{"position":[[128,38]]},"2616":{"position":[[145,38]]},"2758":{"position":[[128,38]]}}}],["host=host.docker.internal:host",{"_index":2046,"t":{"134":{"position":[[1668,30]]}}}],["host=localhost",{"_index":3532,"t":{"519":{"position":[[806,15]]}}}],["hostnam",{"_index":4100,"t":{"945":{"position":[[171,10]]},"2020":{"position":[[171,10]]},"2032":{"position":[[1068,8]]},"2038":{"position":[[1125,8]]},"3239":{"position":[[171,10]]},"3273":{"position":[[960,8]]},"3279":{"position":[[1017,8]]}}}],["hot",{"_index":2626,"t":{"262":{"position":[[1543,3]]},"2532":{"position":[[223,3]]}}}],["hour",{"_index":2384,"t":{"212":{"position":[[286,5]]},"246":{"position":[[3652,6]]},"363":{"position":[[1031,4]]},"776":{"position":[[595,5]]},"868":{"position":[[335,5]]},"941":{"position":[[363,8]]},"945":{"position":[[70,7]]},"1383":{"position":[[1182,4]]},"1545":{"position":[[1594,6]]},"1795":{"position":[[773,5]]},"2016":{"position":[[363,8]]},"2020":{"position":[[70,7]]},"2066":{"position":[[335,5]]},"2068":{"position":[[396,5]]},"2714":{"position":[[1594,6]]},"2984":{"position":[[773,5]]},"3144":{"position":[[335,5]]},"3146":{"position":[[396,5]]},"3235":{"position":[[363,8]]},"3239":{"position":[[70,7]]}}}],["hours\"720h",{"_index":4094,"t":{"941":{"position":[[444,11]]},"2016":{"position":[[444,11]]},"3235":{"position":[[444,11]]}}}],["href=\"#\">joinclick",{"_index":2708,"t":{"283":{"position":[[1664,20]]}}}],["hs256",{"_index":2240,"t":{"178":{"position":[[1506,7]]},"188":{"position":[[589,7]]},"766":{"position":[[41,5]]},"770":{"position":[[0,5]]},"1785":{"position":[[41,5]]},"1789":{"position":[[0,5]]},"2974":{"position":[[41,5]]},"2976":{"position":[[205,7]]},"2978":{"position":[[0,5],[327,7]]},"2980":{"position":[[300,7]]},"3014":{"position":[[435,7]]}}}],["hst",{"_index":3024,"t":{"391":{"position":[[1399,7]]},"1473":{"position":[[554,3]]},"1475":{"position":[[1023,6],[1097,6],[1735,6]]},"1487":{"position":[[131,6]]},"1489":{"position":[[414,6]]},"2666":{"position":[[554,3]]},"2668":{"position":[[1023,6],[1097,6],[1735,6]]},"2680":{"position":[[131,6]]},"2682":{"position":[[414,6]]}}}],["html",{"_index":4746,"t":{"1495":{"position":[[3767,4],[4238,4]]},"2662":{"position":[[3807,4],[4299,4]]}}}],["html>[a",{"_index":5023,"t":{"1797":{"position":[[755,45]]},"2986":{"position":[[721,45]]}}}],["https://example.com/openid",{"_index":2378,"t":{"210":{"position":[[908,27]]},"3018":{"position":[[389,27]]}}}],["https://foo.example.com",{"_index":4043,"t":{"892":{"position":[[956,23]]},"1959":{"position":[[956,23]]},"3172":{"position":[[956,23]]}}}],["https://github.com/centrifugal/centrifugo",{"_index":3683,"t":{"594":{"position":[[46,41]]},"596":{"position":[[46,41]]},"1559":{"position":[[46,41]]},"1561":{"position":[[46,41]]},"2762":{"position":[[46,41]]},"2764":{"position":[[46,41]]}}}],["https://github.com/centrifugal/centrifugo.gitcd",{"_index":3591,"t":{"535":{"position":[[42,47]]},"1443":{"position":[[42,47]]},"2626":{"position":[[48,47]]}}}],["https://github.com/fzambia/pipelin",{"_index":3388,"t":{"481":{"position":[[1886,37]]}}}],["https://github.com/fzambia/redigo",{"_index":797,"t":{"18":{"position":[[3025,33]]}}}],["https://googlechrome.github.io",{"_index":1702,"t":{"110":{"position":[[573,30]]}}}],["https://googlechrome.github.io/samples/quictransport/client.html",{"_index":1627,"t":{"100":{"position":[[987,64]]}}}],["https://hpbn.co/websocket",{"_index":259,"t":{"4":{"position":[[924,26]]}}}],["https://keycloak:443/{{realm}}/protocol/openid",{"_index":5025,"t":{"1797":{"position":[[838,47]]},"2986":{"position":[[804,47]]}}}],["https://laravel.com/docs/8.x/authent",{"_index":2134,"t":{"156":{"position":[[181,44]]}}}],["https://localhost:4433/path",{"_index":1522,"t":{"94":{"position":[[1873,29]]}}}],["https://localhost:8000/connection/webtransport",{"_index":5414,"t":{"2434":{"position":[[1662,48]]},"3612":{"position":[[1689,48]]}}}],["https://lucumr.pocoo.org/2012/9/24/websocket",{"_index":266,"t":{"4":{"position":[[1004,45]]}}}],["https://mysite.com",{"_index":3606,"t":{"554":{"position":[[166,23]]}}}],["https://mysite2.example.com",{"_index":4028,"t":{"878":{"position":[[773,28]]},"1945":{"position":[[773,28]]},"3158":{"position":[[773,28]]}}}],["https://play.golang.org/p/u7sagolmdk",{"_index":793,"t":{"18":{"position":[[2831,38]]}}}],["https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto",{"_index":4505,"t":{"1141":{"position":[[401,91]]},"2222":{"position":[[401,91]]},"3463":{"position":[[401,91]]}}}],["https://robohash.org",{"_index":2105,"t":{"146":{"position":[[876,21]]}}}],["https://tools.ietf.org/html/draft",{"_index":1644,"t":{"104":{"position":[[560,33]]},"106":{"position":[[1102,33]]},"110":{"position":[[743,33]]}}}],["https://www.flaticon.com/packs/web",{"_index":2651,"t":{"275":{"position":[[79,34]]},"1289":{"position":[[83,34]]},"2417":{"position":[[83,34]]}}}],["https://www.google.com/intl/en/chrome/canari",{"_index":1537,"t":{"96":{"position":[[6,46]]}}}],["https://www.w3.org/tr/trac",{"_index":2367,"t":{"208":{"position":[[435,27]]}}}],["https://your_centrifugo.com/connection/http_stream",{"_index":2987,"t":{"379":{"position":[[3687,52]]}}}],["https://your_centrifugo.com/connection/ss",{"_index":2988,"t":{"379":{"position":[[3773,44]]}}}],["https://your_centrifugo.com/connection/webtransport",{"_index":3040,"t":{"399":{"position":[[945,53]]}}}],["huawei",{"_index":2615,"t":{"260":{"position":[[758,6]]}}}],["hub",{"_index":1454,"t":{"86":{"position":[[1767,3],[1917,3]]},"413":{"position":[[253,3]]},"525":{"position":[[57,4]]},"1397":{"position":[[252,3]]},"1433":{"position":[[57,4]]},"2554":{"position":[[261,3]]},"2616":{"position":[[57,4]]}}}],["huge",{"_index":809,"t":{"20":{"position":[[168,4]]},"24":{"position":[[194,4]]},"62":{"position":[[160,4]]},"74":{"position":[[834,4]]},"76":{"position":[[272,4]]},"226":{"position":[[226,5]]},"319":{"position":[[1218,4]]},"379":{"position":[[605,4]]},"407":{"position":[[422,4]]},"471":{"position":[[121,4]]},"477":{"position":[[114,4]]},"479":{"position":[[389,6],[2140,4]]},"1129":{"position":[[993,4]]},"1305":{"position":[[1218,4]]},"1887":{"position":[[736,4]]},"2210":{"position":[[1035,4]]},"2452":{"position":[[1218,4]]},"3078":{"position":[[736,4]]},"3451":{"position":[[1095,4]]}}}],["human",{"_index":2992,"t":{"381":{"position":[[116,5]]},"403":{"position":[[184,5]]},"1164":{"position":[[1196,5]]},"1285":{"position":[[105,5],[461,5]]},"2244":{"position":[[886,5]]},"2248":{"position":[[166,5]]},"2258":{"position":[[528,5]]},"2345":{"position":[[4611,5]]},"2355":{"position":[[4228,5]]},"2432":{"position":[[105,5],[461,5]]},"3467":{"position":[[751,6]]},"3492":{"position":[[528,5]]},"3555":{"position":[[886,5]]},"3559":{"position":[[166,5]]},"3627":{"position":[[105,5],[461,5]]},"3633":{"position":[[4611,5]]},"3643":{"position":[[4228,5]]}}}],["hundr",{"_index":2953,"t":{"371":{"position":[[165,8]]},"1391":{"position":[[165,8]]},"1627":{"position":[[321,8]]},"2542":{"position":[[169,8]]}}}],["hup",{"_index":4084,"t":{"932":{"position":[[22,3],[80,3]]},"2007":{"position":[[22,3],[80,3]]},"3224":{"position":[[22,3],[80,3]]}}}],["hurt",{"_index":1190,"t":{"50":{"position":[[827,5]]}}}],["hyphen",{"_index":3904,"t":{"734":{"position":[[534,7]]},"1881":{"position":[[534,7]]},"3072":{"position":[[822,7]]}}}],["i'd",{"_index":222,"t":{"4":{"position":[[140,3]]},"138":{"position":[[1546,3]]},"192":{"position":[[300,3]]}}}],["i'll",{"_index":235,"t":{"4":{"position":[[345,4]]},"16":{"position":[[2129,4],[2947,4]]},"18":{"position":[[1001,4]]},"24":{"position":[[13,4]]},"108":{"position":[[1373,4]]}}}],["i.",{"_index":113,"t":{"2":{"position":[[1594,4],[2245,4]]},"18":{"position":[[536,4]]},"34":{"position":[[2162,5]]},"46":{"position":[[2208,5]]},"74":{"position":[[1237,5]]},"132":{"position":[[1300,4]]},"146":{"position":[[667,4]]},"262":{"position":[[2030,5]]},"285":{"position":[[387,5]]},"321":{"position":[[974,4]]},"339":{"position":[[1323,5]]},"397":{"position":[[518,4]]},"475":{"position":[[477,4]]},"481":{"position":[[6413,4]]},"519":{"position":[[50,4]]},"523":{"position":[[82,5],[1033,4]]},"604":{"position":[[838,4]]},"682":{"position":[[626,4],[949,4]]},"696":{"position":[[540,4]]},"704":{"position":[[79,5]]},"716":{"position":[[234,5],[659,4]]},"718":{"position":[[399,4]]},"720":{"position":[[133,4]]},"734":{"position":[[576,4]]},"756":{"position":[[535,5]]},"758":{"position":[[854,5]]},"878":{"position":[[82,5],[396,4]]},"880":{"position":[[218,5]]},"892":{"position":[[475,5]]},"910":{"position":[[160,5]]},"920":{"position":[[53,4]]},"943":{"position":[[185,4]]},"985":{"position":[[1203,5]]},"1041":{"position":[[713,5],[843,4]]},"1081":{"position":[[271,5],[376,5]]},"1083":{"position":[[172,5]]},"1087":{"position":[[318,4],[588,5]]},"1089":{"position":[[160,5],[497,5]]},"1107":{"position":[[63,4]]},"1125":{"position":[[219,4]]},"1151":{"position":[[44,4]]},"1164":{"position":[[218,4],[629,4]]},"1182":{"position":[[1400,4]]},"1217":{"position":[[63,4]]},"1268":{"position":[[807,5]]},"1270":{"position":[[136,4]]},"1307":{"position":[[974,4]]},"1319":{"position":[[709,4]]},"1327":{"position":[[1323,5]]},"1341":{"position":[[447,5]]},"1431":{"position":[[76,5],[1266,4]]},"1495":{"position":[[6861,5]]},"1511":{"position":[[354,4],[694,4]]},"1533":{"position":[[926,4]]},"1567":{"position":[[569,4]]},"1569":{"position":[[1194,4]]},"1571":{"position":[[309,4]]},"1611":{"position":[[658,4]]},"1631":{"position":[[474,6]]},"1639":{"position":[[1021,4]]},"1676":{"position":[[107,4],[1536,5]]},"1678":{"position":[[382,4]]},"1680":{"position":[[753,4]]},"1733":{"position":[[626,4],[949,4]]},"1741":{"position":[[2040,4]]},"1803":{"position":[[254,5]]},"1881":{"position":[[576,4]]},"1895":{"position":[[154,4],[326,5],[760,4]]},"1897":{"position":[[535,4]]},"1899":{"position":[[415,4]]},"1903":{"position":[[582,5]]},"1905":{"position":[[555,5]]},"1945":{"position":[[82,5],[396,4]]},"1947":{"position":[[218,5]]},"1959":{"position":[[475,5]]},"1985":{"position":[[160,5]]},"1987":{"position":[[157,4]]},"1995":{"position":[[53,4]]},"2018":{"position":[[185,4]]},"2044":{"position":[[1203,5]]},"2132":{"position":[[713,5],[843,4]]},"2160":{"position":[[699,5]]},"2164":{"position":[[271,5],[376,5]]},"2166":{"position":[[172,5]]},"2170":{"position":[[318,4],[588,5]]},"2172":{"position":[[134,5],[471,5]]},"2188":{"position":[[66,4]]},"2206":{"position":[[219,4]]},"2240":{"position":[[91,4],[1256,4]]},"2244":{"position":[[344,4]]},"2250":{"position":[[1400,4]]},"2279":{"position":[[44,4]]},"2292":{"position":[[810,5]]},"2294":{"position":[[136,4]]},"2355":{"position":[[3602,5]]},"2359":{"position":[[91,5]]},"2361":{"position":[[418,4]]},"2363":{"position":[[379,5]]},"2369":{"position":[[188,5]]},"2373":{"position":[[190,5]]},"2375":{"position":[[190,5]]},"2454":{"position":[[974,4]]},"2466":{"position":[[709,4]]},"2474":{"position":[[1323,5]]},"2488":{"position":[[447,5]]},"2502":{"position":[[380,5]]},"2506":{"position":[[531,5]]},"2530":{"position":[[886,5]]},"2542":{"position":[[1385,6]]},"2614":{"position":[[79,6]]},"2642":{"position":[[354,4],[694,4]]},"2662":{"position":[[6942,5]]},"2702":{"position":[[926,4]]},"2726":{"position":[[569,4]]},"2728":{"position":[[1194,4]]},"2730":{"position":[[309,4]]},"2736":{"position":[[658,4]]},"2830":{"position":[[107,4],[1539,5]]},"2832":{"position":[[382,4]]},"2834":{"position":[[753,4]]},"2848":{"position":[[474,6]]},"2856":{"position":[[1021,4]]},"2922":{"position":[[590,4],[913,4]]},"2930":{"position":[[2040,4]]},"2992":{"position":[[254,5]]},"3072":{"position":[[864,4]]},"3088":{"position":[[154,4],[326,5],[760,4]]},"3090":{"position":[[535,4]]},"3092":{"position":[[415,4]]},"3096":{"position":[[582,5]]},"3098":{"position":[[555,5]]},"3158":{"position":[[82,5],[396,4]]},"3160":{"position":[[218,5]]},"3172":{"position":[[475,5]]},"3200":{"position":[[160,5]]},"3202":{"position":[[130,4]]},"3210":{"position":[[53,4]]},"3237":{"position":[[185,4]]},"3285":{"position":[[1203,5]]},"3345":{"position":[[553,4]]},"3347":{"position":[[2576,4],[3822,5]]},"3349":{"position":[[2168,4]]},"3361":{"position":[[713,5],[843,4]]},"3387":{"position":[[418,4]]},"3389":{"position":[[699,5],[947,4]]},"3393":{"position":[[271,5],[376,5],[484,5]]},"3395":{"position":[[172,5]]},"3399":{"position":[[318,4],[588,5]]},"3401":{"position":[[134,5],[471,5]]},"3427":{"position":[[66,4]]},"3447":{"position":[[219,4]]},"3484":{"position":[[810,5]]},"3506":{"position":[[44,4]]},"3524":{"position":[[136,4]]},"3551":{"position":[[91,4],[1256,4]]},"3555":{"position":[[344,4]]},"3561":{"position":[[1400,4]]},"3643":{"position":[[3602,5]]},"3647":{"position":[[91,5]]},"3649":{"position":[[418,4]]},"3651":{"position":[[379,5]]},"3657":{"position":[[188,5]]},"3661":{"position":[[190,5]]},"3663":{"position":[[190,5]]}}}],["i7",{"_index":1432,"t":{"86":{"position":[[702,2],[1072,2]]}}}],["iat",{"_index":3826,"t":{"646":{"position":[[173,3]]},"654":{"position":[[217,5],[809,3]]},"738":{"position":[[57,4]]},"1692":{"position":[[173,3]]},"1702":{"position":[[217,5],[813,3]]},"2816":{"position":[[173,3]]},"2826":{"position":[[217,5],[777,3]]}}}],["ic",{"_index":1515,"t":{"94":{"position":[[1085,4]]}}}],["icon",{"_index":1497,"t":{"90":{"position":[[744,5]]},"192":{"position":[[893,5]]},"275":{"position":[[57,5]]},"1289":{"position":[[57,5]]},"2417":{"position":[[57,5]]}}}],["id",{"_index":945,"t":{"32":{"position":[[411,2]]},"44":{"position":[[442,3],[451,4]]},"110":{"position":[[1442,3]]},"134":{"position":[[466,2]]},"136":{"position":[[947,2],[2013,2],[2127,2]]},"162":{"position":[[410,4],[456,5],[770,2]]},"164":{"position":[[396,4],[727,4],[759,4]]},"174":{"position":[[148,2],[236,3],[593,2]]},"176":{"position":[[304,2],[828,2],[900,2]]},"178":{"position":[[767,2]]},"186":{"position":[[69,2],[520,4]]},"204":{"position":[[2191,2]]},"214":{"position":[[795,6]]},"260":{"position":[[473,3],[1004,2]]},"264":{"position":[[873,2]]},"321":{"position":[[111,2],[122,3],[353,2],[364,3]]},"341":{"position":[[358,2],[378,2],[639,3]]},"347":{"position":[[1129,2]]},"387":{"position":[[1294,2]]},"393":{"position":[[194,3],[308,2]]},"513":{"position":[[299,3]]},"560":{"position":[[286,3]]},"576":{"position":[[194,3],[578,3],[640,2]]},"618":{"position":[[105,3]]},"646":{"position":[[52,2]]},"652":{"position":[[385,2],[751,2]]},"654":{"position":[[664,2]]},"670":{"position":[[326,2]]},"672":{"position":[[305,2]]},"682":{"position":[[1399,2]]},"696":{"position":[[240,2],[299,2],[693,2],[849,3],[938,2],[958,2]]},"704":{"position":[[115,4],[222,2],[465,3]]},"706":{"position":[[212,3],[221,3]]},"710":{"position":[[245,3],[254,3]]},"740":{"position":[[51,2],[252,2]]},"746":{"position":[[23,3]]},"758":{"position":[[341,2]]},"836":{"position":[[169,2]]},"868":{"position":[[143,2]]},"906":{"position":[[74,3]]},"910":{"position":[[182,3]]},"993":{"position":[[761,2]]},"1019":{"position":[[17,2],[117,2],[162,2],[235,2],[331,2],[543,2]]},"1035":{"position":[[95,2]]},"1041":{"position":[[245,2],[349,3],[443,2]]},"1061":{"position":[[466,3]]},"1065":{"position":[[2081,2],[2440,2],[3498,2],[4954,4]]},"1067":{"position":[[736,2],[1049,2]]},"1069":{"position":[[714,2],[1019,2]]},"1071":{"position":[[513,3],[2038,2],[2343,2]]},"1073":{"position":[[2072,2],[2375,2]]},"1113":{"position":[[138,2],[427,2],[532,4]]},"1115":{"position":[[134,3],[233,2],[342,2]]},"1117":{"position":[[42,3],[98,3],[196,2],[246,2],[464,3]]},"1119":{"position":[[187,2],[225,2]]},"1121":{"position":[[958,2],[1113,2],[1136,2]]},"1123":{"position":[[127,4]]},"1164":{"position":[[734,6],[2389,2],[2733,2],[2872,6],[2892,9],[2927,2],[3533,2],[3550,2]]},"1166":{"position":[[387,5],[516,2],[1071,5],[1228,2]]},"1168":{"position":[[67,2],[163,5],[350,5]]},"1170":{"position":[[183,5]]},"1172":{"position":[[439,5]]},"1176":{"position":[[450,2]]},"1307":{"position":[[111,2],[122,3],[353,2],[364,3]]},"1329":{"position":[[358,2],[378,2],[639,3]]},"1335":{"position":[[1133,2]]},"1375":{"position":[[299,3]]},"1495":{"position":[[6223,3]]},"1507":{"position":[[56,3],[338,2],[437,3],[528,2]]},"1523":{"position":[[140,3],[312,2],[579,2],[1276,2]]},"1579":{"position":[[177,2],[384,2],[741,2],[1387,2],[1557,2]]},"1583":{"position":[[175,2],[686,3],[747,2]]},"1629":{"position":[[986,2]]},"1631":{"position":[[509,3]]},"1635":{"position":[[584,2],[892,2]]},"1639":{"position":[[1169,3]]},"1641":{"position":[[276,2]]},"1650":{"position":[[98,2],[111,2],[885,2],[910,2]]},"1652":{"position":[[103,2],[178,3],[208,3]]},"1654":{"position":[[188,3],[228,3],[293,3]]},"1656":{"position":[[140,3],[178,3],[547,2],[1134,2],[1157,3]]},"1658":{"position":[[125,3]]},"1660":{"position":[[134,3],[648,2],[1093,2],[1107,2],[1155,2]]},"1662":{"position":[[247,3]]},"1664":{"position":[[304,2],[634,2],[648,2],[685,2]]},"1666":{"position":[[572,3],[1145,3],[1927,3]]},"1668":{"position":[[521,3],[889,3],[1021,2],[1049,2]]},"1678":{"position":[[59,2],[286,2],[407,2]]},"1680":{"position":[[72,2],[778,2]]},"1692":{"position":[[52,2]]},"1699":{"position":[[385,2],[751,2]]},"1702":{"position":[[664,2]]},"1715":{"position":[[326,2]]},"1717":{"position":[[305,2]]},"1733":{"position":[[1399,2]]},"1741":{"position":[[1552,2],[2064,4]]},"1759":{"position":[[51,2],[252,2]]},"1765":{"position":[[23,3]]},"1777":{"position":[[341,2]]},"1803":{"position":[[51,2],[276,4]]},"1821":{"position":[[23,3]]},"1825":{"position":[[93,2]]},"1865":{"position":[[803,2]]},"1875":{"position":[[240,2],[299,2],[542,3],[631,2],[651,2]]},"1879":{"position":[[482,2]]},"1885":{"position":[[227,3],[236,3],[423,2]]},"1887":{"position":[[248,3],[257,3]]},"1899":{"position":[[439,4]]},"1901":{"position":[[103,3]]},"1973":{"position":[[74,3]]},"1985":{"position":[[182,4]]},"2054":{"position":[[761,2]]},"2066":{"position":[[143,2]]},"2068":{"position":[[193,2]]},"2116":{"position":[[147,2]]},"2132":{"position":[[245,2],[349,3],[443,2]]},"2142":{"position":[[466,3]]},"2146":{"position":[[2198,2],[2557,2],[3615,2],[5077,4]]},"2148":{"position":[[736,2],[1049,2]]},"2150":{"position":[[714,2],[1019,2]]},"2152":{"position":[[513,3],[2038,2],[2343,2]]},"2154":{"position":[[2072,2],[2375,2]]},"2156":{"position":[[2137,2],[2450,2]]},"2194":{"position":[[138,2],[427,2],[532,4]]},"2196":{"position":[[134,3],[233,2],[342,2]]},"2198":{"position":[[42,3],[98,3],[196,2],[246,2],[430,3]]},"2200":{"position":[[187,2],[225,2]]},"2202":{"position":[[1001,2],[1156,2],[1179,2]]},"2204":{"position":[[127,4]]},"2240":{"position":[[216,2],[561,2],[677,6],[779,6],[1298,6],[2146,2]]},"2242":{"position":[[70,2],[87,2]]},"2244":{"position":[[449,6]]},"2345":{"position":[[2311,4],[3555,2]]},"2381":{"position":[[299,3]]},"2454":{"position":[[111,2],[122,3],[353,2],[364,3]]},"2476":{"position":[[358,2],[378,2],[639,3]]},"2526":{"position":[[299,3]]},"2638":{"position":[[56,3],[338,2],[437,3],[528,2]]},"2662":{"position":[[6292,3]]},"2692":{"position":[[140,3],[312,2],[579,2],[1276,2]]},"2772":{"position":[[177,2],[384,2],[741,2],[1387,2],[1557,2]]},"2778":{"position":[[175,2],[687,3],[748,2]]},"2816":{"position":[[52,2]]},"2823":{"position":[[385,2],[715,2]]},"2826":{"position":[[628,2]]},"2832":{"position":[[59,2],[286,2],[407,2]]},"2834":{"position":[[72,2],[778,2]]},"2846":{"position":[[986,2]]},"2848":{"position":[[509,3]]},"2852":{"position":[[584,2],[892,2]]},"2856":{"position":[[1169,3]]},"2858":{"position":[[276,2]]},"2867":{"position":[[98,2],[111,2],[792,2],[817,2]]},"2869":{"position":[[103,2],[178,3],[208,3]]},"2871":{"position":[[188,3],[228,3],[293,3]]},"2873":{"position":[[238,2],[666,3],[704,3],[1325,2],[1352,3]]},"2875":{"position":[[125,3]]},"2877":{"position":[[132,3],[207,2],[589,3],[1311,2],[1325,2],[1378,2]]},"2879":{"position":[[247,3]]},"2881":{"position":[[170,2],[789,2],[803,2],[840,2]]},"2883":{"position":[[448,3],[816,3],[2206,3]]},"2887":{"position":[[521,3],[889,3],[1021,2],[1049,2]]},"2904":{"position":[[290,2]]},"2906":{"position":[[269,2]]},"2922":{"position":[[1363,2]]},"2930":{"position":[[1552,2],[2064,4]]},"2948":{"position":[[41,2],[234,2]]},"2966":{"position":[[420,2]]},"2992":{"position":[[51,2],[276,4]]},"3010":{"position":[[23,3]]},"3014":{"position":[[93,2]]},"3056":{"position":[[803,2]]},"3066":{"position":[[255,2],[323,2],[553,3],[644,3]]},"3070":{"position":[[496,2]]},"3076":{"position":[[227,3],[236,3],[423,2]]},"3078":{"position":[[248,3],[257,3]]},"3092":{"position":[[439,4]]},"3094":{"position":[[103,3]]},"3144":{"position":[[143,2]]},"3146":{"position":[[193,2]]},"3186":{"position":[[74,3]]},"3198":{"position":[[206,2],[407,2],[590,2],[837,4],[1283,3]]},"3200":{"position":[[182,4]]},"3293":{"position":[[761,2]]},"3347":{"position":[[1678,2]]},"3361":{"position":[[245,2],[349,3],[443,2]]},"3371":{"position":[[466,3]]},"3375":{"position":[[2198,2],[2557,2],[3615,2],[5106,4]]},"3377":{"position":[[736,2],[1049,2]]},"3379":{"position":[[761,2],[1066,2]]},"3381":{"position":[[1841,2],[2146,2]]},"3383":{"position":[[2072,2],[2375,2]]},"3385":{"position":[[2110,2],[2423,2]]},"3435":{"position":[[206,2],[495,2],[600,4]]},"3437":{"position":[[139,2],[248,2]]},"3439":{"position":[[42,3],[130,2],[180,2],[364,3]]},"3441":{"position":[[180,2],[218,2]]},"3443":{"position":[[839,2],[994,2],[1017,2]]},"3445":{"position":[[127,4]]},"3551":{"position":[[216,2],[561,2],[677,6],[779,6],[1298,6],[2146,2]]},"3553":{"position":[[70,2],[87,2]]},"3555":{"position":[[449,6]]},"3629":{"position":[[796,6]]},"3633":{"position":[[2311,4],[3555,2]]},"3669":{"position":[[299,3]]}}}],["id\":1,\"connect\":{\"client\":\"9ac9de4",{"_index":2411,"t":{"214":{"position":[[821,37]]},"3629":{"position":[[822,37]]}}}],["id\":32",{"_index":4590,"t":{"1178":{"position":[[166,8]]}}}],["id,]);$room",{"_index":2157,"t":{"164":{"position":[[452,12]]}}}],["id;}$thi",{"_index":2161,"t":{"164":{"position":[[582,10]]}}}],["id=\"chat",{"_index":1940,"t":{"130":{"position":[[610,8],[687,8]]}}}],["id=\"count",{"_index":4724,"t":{"1495":{"position":[[2183,13]]},"2662":{"position":[[2198,13]]}}}],["id=\"log\">')centrifuge.connect",{"_index":3502,"t":{"497":{"position":[[372,27]]},"1359":{"position":[[372,27]]},"2510":{"position":[[372,27]]}}}],["jwt>'}));const",{"_index":4628,"t":{"1213":{"position":[[445,16]]},"2387":{"position":[[445,16]]},"3601":{"position":[[445,16]]}}}],["jwtclaim",{"_index":3962,"t":{"772":{"position":[[45,9]]},"1791":{"position":[[45,9]]},"2980":{"position":[[45,9]]}}}],["jwtimport",{"_index":2229,"t":{"178":{"position":[[1219,9]]},"188":{"position":[[263,9]]},"764":{"position":[[328,9]]},"770":{"position":[[67,9]]},"1783":{"position":[[328,9]]},"1789":{"position":[[67,9]]},"2972":{"position":[[340,9]]},"2978":{"position":[[67,9]]},"3014":{"position":[[152,9]]}}}],["jwtnote:th",{"_index":2917,"t":{"355":{"position":[[55,11]]},"1347":{"position":[[55,11]]},"2436":{"position":[[55,11]]}}}],["jwttoken",{"_index":3954,"t":{"768":{"position":[[21,8]]},"1035":{"position":[[164,8]]},"1787":{"position":[[21,8]]},"1825":{"position":[[138,8]]},"2976":{"position":[[21,8]]}}}],["kafka",{"_index":617,"t":{"16":{"position":[[1998,5],[3732,5],[3867,5],[4024,5],[4178,5],[4356,5],[4985,5]]},"222":{"position":[[460,6]]},"238":{"position":[[580,5],[602,5]]},"240":{"position":[[622,5],[696,5]]},"244":{"position":[[438,6]]},"321":{"position":[[961,5]]},"1307":{"position":[[961,5]]},"2454":{"position":[[961,5]]},"2504":{"position":[[64,5],[253,5],[291,5],[715,5],[984,5]]}}}],["kafka'",{"_index":5424,"t":{"2504":{"position":[[110,7]]}}}],["kamardin",{"_index":331,"t":{"8":{"position":[[627,8]]}}}],["kb",{"_index":205,"t":{"2":{"position":[[3523,2]]},"309":{"position":[[187,2]]},"1295":{"position":[[187,2]]},"2442":{"position":[[187,2]]}}}],["keep",{"_index":256,"t":{"4":{"position":[[830,4]]},"10":{"position":[[1212,4],[1868,5]]},"14":{"position":[[947,5]]},"16":{"position":[[1725,4],[3640,7],[3927,7],[6691,4]]},"20":{"position":[[995,4],[1210,4],[1558,4]]},"70":{"position":[[97,7]]},"72":{"position":[[78,7]]},"74":{"position":[[113,4]]},"94":{"position":[[2197,4]]},"136":{"position":[[967,4]]},"142":{"position":[[578,4]]},"146":{"position":[[731,4]]},"156":{"position":[[536,5]]},"160":{"position":[[256,4]]},"238":{"position":[[490,4]]},"246":{"position":[[3951,4]]},"250":{"position":[[489,7]]},"260":{"position":[[1159,4]]},"291":{"position":[[484,4]]},"319":{"position":[[153,4]]},"331":{"position":[[169,4]]},"335":{"position":[[524,4]]},"361":{"position":[[739,7]]},"469":{"position":[[560,7]]},"489":{"position":[[846,4]]},"505":{"position":[[926,5]]},"519":{"position":[[444,4]]},"566":{"position":[[54,4]]},"644":{"position":[[427,4],[462,7]]},"652":{"position":[[997,4]]},"654":{"position":[[1056,4]]},"662":{"position":[[388,4],[440,7]]},"664":{"position":[[119,4]]},"670":{"position":[[513,4]]},"700":{"position":[[123,4]]},"710":{"position":[[301,4]]},"712":{"position":[[98,5],[344,4]]},"714":{"position":[[58,4]]},"854":{"position":[[315,4]]},"884":{"position":[[476,4]]},"902":{"position":[[201,4],[280,7]]},"957":{"position":[[171,5]]},"961":{"position":[[178,4]]},"969":{"position":[[90,7]]},"973":{"position":[[277,4]]},"975":{"position":[[690,7],[724,7]]},"993":{"position":[[49,4]]},"999":{"position":[[104,4]]},"1049":{"position":[[1816,4],[1934,4]]},"1073":{"position":[[1547,4]]},"1077":{"position":[[380,4],[487,4]]},"1107":{"position":[[473,4]]},"1109":{"position":[[1016,4]]},"1117":{"position":[[471,4]]},"1129":{"position":[[540,4]]},"1135":{"position":[[565,4]]},"1172":{"position":[[103,4]]},"1305":{"position":[[153,4]]},"1317":{"position":[[229,4]]},"1323":{"position":[[601,4]]},"1367":{"position":[[926,5]]},"1381":{"position":[[748,7]]},"1469":{"position":[[872,4]]},"1477":{"position":[[101,4]]},"1491":{"position":[[114,4]]},"1511":{"position":[[323,4]]},"1639":{"position":[[1116,4]]},"1645":{"position":[[90,4]]},"1690":{"position":[[427,4],[462,7]]},"1699":{"position":[[997,4]]},"1702":{"position":[[1102,4]]},"1706":{"position":[[388,4],[440,7]]},"1708":{"position":[[119,4]]},"1715":{"position":[[513,4]]},"1721":{"position":[[315,4]]},"1879":{"position":[[0,4],[613,7]]},"1887":{"position":[[683,4]]},"1891":{"position":[[98,5],[344,4]]},"1893":{"position":[[58,4]]},"1903":{"position":[[269,4]]},"1905":{"position":[[246,4]]},"1951":{"position":[[476,4]]},"1969":{"position":[[201,4],[280,7]]},"2024":{"position":[[90,7]]},"2026":{"position":[[167,5],[710,4]]},"2028":{"position":[[277,4]]},"2030":{"position":[[506,7],[540,7],[1700,4]]},"2054":{"position":[[49,4]]},"2084":{"position":[[171,5]]},"2102":{"position":[[4093,4]]},"2106":{"position":[[104,4]]},"2122":{"position":[[492,4]]},"2152":{"position":[[4611,4]]},"2154":{"position":[[1547,4]]},"2160":{"position":[[530,4],[637,4]]},"2188":{"position":[[476,4]]},"2190":{"position":[[1076,4]]},"2198":{"position":[[437,4]]},"2210":{"position":[[587,4]]},"2216":{"position":[[565,4]]},"2230":{"position":[[1816,4],[1934,4]]},"2351":{"position":[[318,4]]},"2367":{"position":[[317,4]]},"2369":{"position":[[282,5],[1005,7]]},"2452":{"position":[[153,4]]},"2464":{"position":[[229,4]]},"2470":{"position":[[601,4]]},"2502":{"position":[[220,7]]},"2518":{"position":[[964,5]]},"2532":{"position":[[793,7]]},"2610":{"position":[[862,4]]},"2642":{"position":[[323,4]]},"2670":{"position":[[101,4]]},"2684":{"position":[[114,4]]},"2722":{"position":[[1733,4]]},"2814":{"position":[[427,4],[462,7]]},"2823":{"position":[[961,4]]},"2826":{"position":[[1066,4]]},"2856":{"position":[[1116,4]]},"2862":{"position":[[98,4]]},"2895":{"position":[[388,4],[440,7]]},"2897":{"position":[[119,4]]},"2904":{"position":[[477,4]]},"2910":{"position":[[315,4]]},"3070":{"position":[[627,7]]},"3078":{"position":[[683,4]]},"3082":{"position":[[98,5],[344,4]]},"3084":{"position":[[58,4]]},"3086":{"position":[[152,5],[712,4]]},"3096":{"position":[[269,4]]},"3098":{"position":[[246,4]]},"3164":{"position":[[476,4]]},"3182":{"position":[[201,4],[280,7]]},"3251":{"position":[[171,5]]},"3261":{"position":[[4093,4]]},"3265":{"position":[[90,7]]},"3267":{"position":[[167,5],[710,4]]},"3269":{"position":[[277,4]]},"3271":{"position":[[506,7],[540,7],[1666,4]]},"3293":{"position":[[49,4]]},"3307":{"position":[[104,4]]},"3337":{"position":[[752,4]]},"3347":{"position":[[673,7]]},"3381":{"position":[[4614,4]]},"3383":{"position":[[1547,4]]},"3389":{"position":[[530,4],[637,4]]},"3417":{"position":[[1816,4],[1934,4]]},"3427":{"position":[[385,4]]},"3439":{"position":[[371,4]]},"3451":{"position":[[647,4]]},"3457":{"position":[[565,4]]},"3639":{"position":[[318,4]]},"3655":{"position":[[317,4]]},"3657":{"position":[[282,5],[1005,7]]}}}],["keepalive_timeout",{"_index":2038,"t":{"134":{"position":[[1187,17]]},"1001":{"position":[[1033,17]]},"1003":{"position":[[607,17]]},"2108":{"position":[[1033,17]]},"2110":{"position":[[607,17]]},"3309":{"position":[[1033,17]]},"3311":{"position":[[607,17]]}}}],["kenshinstock",{"_index":3056,"t":{"407":{"position":[[1386,13]]}}}],["kept",{"_index":2868,"t":{"317":{"position":[[247,4]]},"407":{"position":[[1248,4]]},"644":{"position":[[89,4]]},"646":{"position":[[260,4]]},"648":{"position":[[23,4]]},"650":{"position":[[23,4]]},"662":{"position":[[99,4]]},"666":{"position":[[21,4]]},"668":{"position":[[21,4]]},"678":{"position":[[561,4]]},"686":{"position":[[838,4]]},"764":{"position":[[618,4]]},"961":{"position":[[813,4],[984,4]]},"1127":{"position":[[102,4]]},"1186":{"position":[[259,4]]},"1213":{"position":[[627,4]]},"1303":{"position":[[247,4]]},"1680":{"position":[[307,4]]},"1690":{"position":[[89,4]]},"1692":{"position":[[260,4]]},"1694":{"position":[[23,4]]},"1696":{"position":[[23,4]]},"1706":{"position":[[99,4]]},"1710":{"position":[[21,4]]},"1712":{"position":[[21,4]]},"1729":{"position":[[556,4]]},"1737":{"position":[[820,4]]},"1783":{"position":[[618,4]]},"1893":{"position":[[1460,4]]},"2098":{"position":[[869,4],[1040,4]]},"2208":{"position":[[102,4]]},"2355":{"position":[[4905,4]]},"2387":{"position":[[627,4]]},"2450":{"position":[[247,4]]},"2530":{"position":[[2216,4]]},"2814":{"position":[[89,4]]},"2816":{"position":[[260,4]]},"2818":{"position":[[23,4]]},"2820":{"position":[[23,4]]},"2834":{"position":[[307,4]]},"2895":{"position":[[99,4]]},"2899":{"position":[[21,4]]},"2901":{"position":[[21,4]]},"2918":{"position":[[556,4]]},"2926":{"position":[[820,4]]},"3084":{"position":[[1424,4]]},"3257":{"position":[[869,4],[1040,4]]},"3449":{"position":[[102,4]]},"3601":{"position":[[627,4]]},"3643":{"position":[[4905,4]]}}}],["kerismak",{"_index":4662,"t":{"1289":{"position":[[71,11]]},"2417":{"position":[[71,11]]}}}],["kernel",{"_index":3378,"t":{"481":{"position":[[807,6]]}}}],["key",{"_index":398,"t":{"10":{"position":[[179,3]]},"94":{"position":[[374,3]]},"98":{"position":[[418,3],[561,3],[1024,4],[1283,4],[1356,3],[2219,4]]},"104":{"position":[[351,4]]},"110":{"position":[[514,5],[2581,3],[2823,3],[3176,3],[3231,5],[3970,4],[3984,4]]},"160":{"position":[[235,3]]},"178":{"position":[[1186,3]]},"200":{"position":[[20,3]]},"204":{"position":[[1448,4],[1650,4],[1685,3],[1712,3]]},"210":{"position":[[123,4],[132,3]]},"212":{"position":[[273,3],[572,4],[937,3],[1021,3],[1101,3],[1182,3]]},"228":{"position":[[341,4],[526,4]]},"240":{"position":[[460,3]]},"262":{"position":[[367,6]]},"295":{"position":[[122,3]]},"299":{"position":[[590,3]]},"341":{"position":[[626,3]]},"397":{"position":[[572,3]]},"451":{"position":[[119,3],[279,3]]},"453":{"position":[[401,3],[513,5]]},"473":{"position":[[946,3],[1072,3]]},"477":{"position":[[6659,4]]},"485":{"position":[[1061,3]]},"509":{"position":[[261,7]]},"513":{"position":[[306,4]]},"523":{"position":[[703,5]]},"578":{"position":[[87,3]]},"580":{"position":[[56,4],[102,4]]},"588":{"position":[[196,3]]},"598":{"position":[[144,3],[244,3]]},"604":{"position":[[1656,4]]},"686":{"position":[[825,4]]},"732":{"position":[[110,5],[139,5]]},"734":{"position":[[1176,5],[1205,5]]},"760":{"position":[[39,7]]},"768":{"position":[[371,3],[479,3]]},"776":{"position":[[29,3],[392,3],[558,4],[622,4]]},"854":{"position":[[208,3]]},"884":{"position":[[194,3]]},"886":{"position":[[249,3]]},"888":{"position":[[155,3]]},"937":{"position":[[120,3]]},"975":{"position":[[569,4],[998,3]]},"983":{"position":[[1150,4]]},"1035":{"position":[[435,4],[490,3]]},"1047":{"position":[[39,3]]},"1049":{"position":[[1555,5]]},"1051":{"position":[[173,3],[334,3]]},"1053":{"position":[[221,3],[389,3]]},"1063":{"position":[[223,4],[266,4],[340,4]]},"1065":{"position":[[4525,7]]},"1081":{"position":[[299,3]]},"1093":{"position":[[1604,4],[1642,4],[1960,3]]},"1107":{"position":[[154,6],[170,3],[257,5],[294,3],[435,4],[507,3],[590,3],[835,3]]},"1109":{"position":[[2186,4]]},"1111":{"position":[[559,4]]},"1129":{"position":[[339,3]]},"1143":{"position":[[92,4],[148,3],[183,6],[339,3]]},"1182":{"position":[[1821,3]]},"1243":{"position":[[29,3]]},"1319":{"position":[[763,3]]},"1329":{"position":[[626,3]]},"1371":{"position":[[261,7]]},"1375":{"position":[[306,4]]},"1431":{"position":[[936,5]]},"1451":{"position":[[175,3],[346,3]]},"1453":{"position":[[402,3],[514,5]]},"1533":{"position":[[2222,4]]},"1553":{"position":[[196,3]]},"1563":{"position":[[144,3],[244,3]]},"1579":{"position":[[370,3]]},"1585":{"position":[[87,3]]},"1587":{"position":[[56,4],[102,4]]},"1650":{"position":[[729,3]]},"1721":{"position":[[208,3]]},"1737":{"position":[[807,4]]},"1779":{"position":[[39,7]]},"1787":{"position":[[371,3],[479,3]]},"1795":{"position":[[29,3],[392,3],[657,3],[736,4],[800,4]]},"1825":{"position":[[391,4],[446,3]]},"1881":{"position":[[1207,5],[1236,5]]},"1937":{"position":[[344,5],[373,5],[1097,5],[1126,5]]},"1951":{"position":[[194,3]]},"1953":{"position":[[249,3]]},"1955":{"position":[[155,3]]},"2012":{"position":[[120,3]]},"2030":{"position":[[385,4],[1067,3]]},"2032":{"position":[[633,3],[670,3]]},"2038":{"position":[[654,3],[691,3]]},"2042":{"position":[[1150,4]]},"2144":{"position":[[223,4],[266,4],[340,4]]},"2146":{"position":[[4642,7]]},"2164":{"position":[[299,3]]},"2176":{"position":[[1604,4],[1642,4],[1960,3]]},"2188":{"position":[[157,6],[173,3],[260,5],[297,3],[438,4],[510,3],[593,3],[838,3]]},"2190":{"position":[[2240,4]]},"2192":{"position":[[534,4]]},"2210":{"position":[[386,3]]},"2224":{"position":[[92,4],[148,3],[183,6],[339,3]]},"2228":{"position":[[39,3]]},"2230":{"position":[[1555,5]]},"2232":{"position":[[173,3],[334,3]]},"2234":{"position":[[189,3],[357,3]]},"2250":{"position":[[1821,3]]},"2322":{"position":[[29,3]]},"2359":{"position":[[440,4]]},"2377":{"position":[[323,7]]},"2381":{"position":[[306,4]]},"2466":{"position":[[763,3]]},"2476":{"position":[[626,3]]},"2522":{"position":[[270,7]]},"2526":{"position":[[306,4]]},"2592":{"position":[[184,3],[371,3]]},"2594":{"position":[[415,3],[532,5]]},"2614":{"position":[[960,5]]},"2656":{"position":[[648,4],[781,4]]},"2662":{"position":[[10021,4]]},"2702":{"position":[[2222,4]]},"2722":{"position":[[774,4]]},"2744":{"position":[[374,6],[397,6]]},"2750":{"position":[[32,3],[47,3],[84,4]]},"2756":{"position":[[196,3]]},"2766":{"position":[[144,3],[244,3]]},"2770":{"position":[[1174,4]]},"2772":{"position":[[370,3]]},"2780":{"position":[[34,3],[70,3],[98,3],[168,3],[311,4],[548,4]]},"2782":{"position":[[87,3]]},"2823":{"position":[[482,4]]},"2826":{"position":[[359,4]]},"2904":{"position":[[74,4]]},"2906":{"position":[[74,4]]},"2910":{"position":[[208,3]]},"2920":{"position":[[308,4]]},"2922":{"position":[[215,4]]},"2924":{"position":[[173,4]]},"2926":{"position":[[807,4]]},"2968":{"position":[[41,7]]},"2976":{"position":[[515,3],[623,3]]},"2984":{"position":[[29,3],[392,3],[657,3],[736,4],[800,4],[975,3]]},"3014":{"position":[[751,4],[806,3]]},"3072":{"position":[[1495,5],[1524,5]]},"3076":{"position":[[639,4]]},"3084":{"position":[[820,4]]},"3134":{"position":[[344,5],[373,5],[1097,5],[1126,5]]},"3164":{"position":[[194,3]]},"3166":{"position":[[249,3]]},"3168":{"position":[[155,3]]},"3231":{"position":[[120,3]]},"3271":{"position":[[385,4],[1033,3]]},"3273":{"position":[[579,3],[616,3]]},"3279":{"position":[[600,3],[637,3]]},"3283":{"position":[[1150,4]]},"3333":{"position":[[360,4],[713,4]]},"3371":{"position":[[1081,4]]},"3373":{"position":[[223,4],[266,4],[340,4]]},"3375":{"position":[[4671,7]]},"3393":{"position":[[299,3]]},"3405":{"position":[[1816,4],[1854,4],[2172,3]]},"3415":{"position":[[39,3]]},"3417":{"position":[[1555,5]]},"3419":{"position":[[173,3],[334,3]]},"3421":{"position":[[189,3],[357,3]]},"3425":{"position":[[193,3],[315,4]]},"3427":{"position":[[173,3],[210,3],[240,4],[291,3],[419,3],[436,3],[516,3],[808,3]]},"3431":{"position":[[241,4],[727,5],[1041,4],[1748,4]]},"3433":{"position":[[118,4],[609,4]]},"3443":{"position":[[271,4]]},"3445":{"position":[[153,4]]},"3447":{"position":[[486,4]]},"3451":{"position":[[98,4],[446,3]]},"3455":{"position":[[319,4]]},"3465":{"position":[[92,4],[148,3],[183,6],[339,3]]},"3467":{"position":[[1864,3]]},"3543":{"position":[[29,3]]},"3561":{"position":[[1821,3]]},"3647":{"position":[[440,4]]},"3665":{"position":[[323,7]]},"3669":{"position":[[306,4]]}}}],["key\"http/1.1",{"_index":4426,"t":{"1121":{"position":[[399,12]]},"1123":{"position":[[328,12]]},"1125":{"position":[[664,12]]},"1127":{"position":[[361,12]]},"1131":{"position":[[170,12]]},"2202":{"position":[[442,12]]},"2204":{"position":[[328,12]]},"2206":{"position":[[664,12]]},"2208":{"position":[[361,12]]},"2212":{"position":[[170,12]]}}}],["key=centrifugo_addr",{"_index":2400,"t":{"212":{"position":[[1211,21]]}}}],["key=granulr_proxy_mode08:25:33",{"_index":2399,"t":{"212":{"position":[[1137,30]]}}}],["key=typ",{"_index":2397,"t":{"212":{"position":[[1051,8]]}}}],["key=watch",{"_index":2395,"t":{"212":{"position":[[971,9]]}}}],["keyauth",{"_index":4527,"t":{"1143":{"position":[[322,7],[358,8],[528,8]]},"2224":{"position":[[322,7],[358,8],[528,8]]},"3465":{"position":[[322,7],[358,8],[528,8]]}}}],["keycloak",{"_index":2371,"t":{"210":{"position":[[210,9]]},"293":{"position":[[241,9],[355,8],[518,8]]},"297":{"position":[[11,8],[210,9]]},"301":{"position":[[273,8],[764,8],[778,9],[819,10],[1111,8],[1148,8],[1288,8],[1638,9],[1750,8],[2831,10],[2923,8],[4375,8]]},"1795":{"position":[[586,8],[694,9]]},"2984":{"position":[[586,8],[694,9]]}}}],["keycloak'",{"_index":2787,"t":{"299":{"position":[[635,10]]}}}],["keycloak.authent",{"_index":2826,"t":{"301":{"position":[[1779,23],[2419,24]]}}}],["keycloak.login",{"_index":2832,"t":{"301":{"position":[[2000,18]]}}}],["keycloak.logout",{"_index":2831,"t":{"301":{"position":[[1913,19],[2732,18]]}}}],["keycloak.token",{"_index":2834,"t":{"301":{"position":[[2543,15]]}}}],["keycloak.tokenparsed?.preferred_username}log",{"_index":2827,"t":{"301":{"position":[[1813,9]]}}}],["paceto",{"_index":967,"t":{"32":{"position":[[1223,7]]}}}],["pack",{"_index":4901,"t":{"1583":{"position":[[18,6]]},"2778":{"position":[[18,6]]}}}],["packag",{"_index":300,"t":{"8":{"position":[[113,7],[296,7]]},"12":{"position":[[1248,7],[1309,7]]},"58":{"position":[[595,7]]},"104":{"position":[[54,7]]},"150":{"position":[[931,8]]},"301":{"position":[[195,8],[1197,7]]},"439":{"position":[[78,9]]},"459":{"position":[[162,9]]},"471":{"position":[[37,7]]},"477":{"position":[[57,8]]},"531":{"position":[[66,8],[409,8],[516,9]]},"556":{"position":[[45,7],[242,7],[267,7]]},"594":{"position":[[4,7]]},"596":{"position":[[4,7]]},"1141":{"position":[[658,7]]},"1143":{"position":[[218,7]]},"1423":{"position":[[78,9]]},"1439":{"position":[[66,8],[432,8],[539,9]]},"1459":{"position":[[162,9]]},"1559":{"position":[[4,7]]},"1561":{"position":[[4,7]]},"1923":{"position":[[1071,7]]},"2222":{"position":[[658,7]]},"2224":{"position":[[218,7]]},"2580":{"position":[[85,9]]},"2600":{"position":[[166,9]]},"2622":{"position":[[66,8],[368,8],[475,9]]},"2762":{"position":[[4,7]]},"2764":{"position":[[4,7]]},"3116":{"position":[[1071,7]]},"3349":{"position":[[2434,7]]},"3463":{"position":[[658,7]]},"3465":{"position":[[218,7]]}}}],["packagecloud.io",{"_index":3573,"t":{"531":{"position":[[110,16]]},"1439":{"position":[[110,16]]},"2622":{"position":[[110,16]]}}}],["page",{"_index":1781,"t":{"118":{"position":[[131,6],[140,4]]},"128":{"position":[[3284,5],[3307,5]]},"130":{"position":[[2561,5],[2654,4]]},"136":{"position":[[36,4],[2382,4],[2473,5]]},"166":{"position":[[28,4],[141,4]]},"168":{"position":[[90,4]]},"236":{"position":[[623,4]]},"275":{"position":[[51,5]]},"289":{"position":[[880,5]]},"301":{"position":[[3179,5]]},"305":{"position":[[8,4]]},"357":{"position":[[8,4]]},"409":{"position":[[8,4]]},"445":{"position":[[8,4]]},"457":{"position":[[61,4]]},"477":{"position":[[6828,4]]},"487":{"position":[[8,4]]},"495":{"position":[[8,4]]},"515":{"position":[[8,4]]},"521":{"position":[[8,4]]},"537":{"position":[[8,4]]},"574":{"position":[[8,4]]},"582":{"position":[[8,4]]},"586":{"position":[[8,4]]},"602":{"position":[[8,4]]},"616":{"position":[[8,4]]},"620":{"position":[[8,4]]},"642":{"position":[[8,4]]},"656":{"position":[[8,4]]},"660":{"position":[[8,4]]},"674":{"position":[[8,4]]},"676":{"position":[[8,4]]},"688":{"position":[[8,4]]},"736":{"position":[[8,4]]},"764":{"position":[[1487,4]]},"778":{"position":[[8,4]]},"852":{"position":[[8,4]]},"860":{"position":[[8,4]]},"872":{"position":[[8,4]]},"922":{"position":[[353,4]]},"926":{"position":[[1056,4]]},"947":{"position":[[8,4]]},"959":{"position":[[8,4]]},"967":{"position":[[8,4]]},"997":{"position":[[8,4]]},"1007":{"position":[[8,4]]},"1015":{"position":[[8,4]]},"1037":{"position":[[8,4]]},"1045":{"position":[[8,4]]},"1055":{"position":[[8,4]]},"1065":{"position":[[1454,4]]},"1103":{"position":[[8,4]]},"1145":{"position":[[8,4]]},"1160":{"position":[[8,4]]},"1194":{"position":[[8,4]]},"1211":{"position":[[8,4]]},"1226":{"position":[[8,4]]},"1247":{"position":[[8,4]]},"1264":{"position":[[8,4]]},"1272":{"position":[[8,4]]},"1289":{"position":[[51,5]]},"1291":{"position":[[8,4]]},"1341":{"position":[[553,6]]},"1351":{"position":[[8,4]]},"1357":{"position":[[8,4]]},"1377":{"position":[[8,4]]},"1393":{"position":[[8,4]]},"1429":{"position":[[8,4]]},"1445":{"position":[[8,4]]},"1457":{"position":[[58,4]]},"1467":{"position":[[8,4]]},"1471":{"position":[[8,4]]},"1495":{"position":[[3762,4],[3801,4]]},"1497":{"position":[[8,4]]},"1519":{"position":[[8,4]]},"1531":{"position":[[8,4]]},"1551":{"position":[[8,4]]},"1565":{"position":[[8,4]]},"1575":{"position":[[8,4]]},"1581":{"position":[[8,4]]},"1589":{"position":[[8,4]]},"1609":{"position":[[8,4]]},"1621":{"position":[[8,4]]},"1656":{"position":[[585,6]]},"1660":{"position":[[686,6]]},"1664":{"position":[[342,6]]},"1674":{"position":[[8,4]]},"1684":{"position":[[8,4]]},"1688":{"position":[[8,4]]},"1704":{"position":[[8,4]]},"1719":{"position":[[8,4]]},"1727":{"position":[[8,4]]},"1739":{"position":[[8,4]]},"1755":{"position":[[8,4]]},"1783":{"position":[[1487,4]]},"1799":{"position":[[8,4]]},"1829":{"position":[[8,4]]},"1867":{"position":[[8,4]]},"1939":{"position":[[8,4]]},"1997":{"position":[[353,4]]},"2001":{"position":[[1056,4]]},"2022":{"position":[[8,4]]},"2058":{"position":[[8,4]]},"2074":{"position":[[8,4]]},"2088":{"position":[[8,4]]},"2096":{"position":[[8,4]]},"2104":{"position":[[8,4]]},"2114":{"position":[[8,4]]},"2128":{"position":[[8,4]]},"2136":{"position":[[8,4]]},"2146":{"position":[[1571,4]]},"2184":{"position":[[8,4]]},"2226":{"position":[[8,4]]},"2236":{"position":[[8,4]]},"2254":{"position":[[8,4]]},"2266":{"position":[[8,4]]},"2273":{"position":[[8,4]]},"2288":{"position":[[8,4]]},"2298":{"position":[[8,4]]},"2305":{"position":[[8,4]]},"2326":{"position":[[8,4]]},"2343":{"position":[[8,4]]},"2385":{"position":[[8,4]]},"2398":{"position":[[8,4]]},"2417":{"position":[[51,5]]},"2419":{"position":[[8,4]]},"2438":{"position":[[8,4]]},"2488":{"position":[[553,6]]},"2496":{"position":[[8,4]]},"2508":{"position":[[8,4]]},"2528":{"position":[[8,4]]},"2544":{"position":[[8,4]]},"2550":{"position":[[8,4]]},"2586":{"position":[[8,4]]},"2598":{"position":[[58,4]]},"2608":{"position":[[8,4]]},"2612":{"position":[[8,4]]},"2628":{"position":[[8,4]]},"2650":{"position":[[8,4]]},"2662":{"position":[[3848,4]]},"2664":{"position":[[8,4]]},"2688":{"position":[[8,4]]},"2700":{"position":[[8,4]]},"2724":{"position":[[8,4]]},"2734":{"position":[[8,4]]},"2742":{"position":[[8,4]]},"2754":{"position":[[8,4]]},"2768":{"position":[[8,4]]},"2776":{"position":[[8,4]]},"2784":{"position":[[8,4]]},"2808":{"position":[[8,4]]},"2812":{"position":[[8,4]]},"2828":{"position":[[8,4]]},"2838":{"position":[[8,4]]},"2873":{"position":[[276,6],[1165,5],[1197,4]]},"2877":{"position":[[245,6],[1151,5],[1183,4]]},"2881":{"position":[[208,6]]},"2893":{"position":[[8,4]]},"2908":{"position":[[8,4]]},"2916":{"position":[[8,4]]},"2928":{"position":[[8,4]]},"2944":{"position":[[8,4]]},"2972":{"position":[[1406,4]]},"2988":{"position":[[8,4]]},"3020":{"position":[[8,4]]},"3058":{"position":[[8,4]]},"3136":{"position":[[8,4]]},"3152":{"position":[[8,4]]},"3212":{"position":[[353,4]]},"3218":{"position":[[1146,4]]},"3241":{"position":[[8,4]]},"3255":{"position":[[8,4]]},"3263":{"position":[[8,4]]},"3297":{"position":[[8,4]]},"3305":{"position":[[8,4]]},"3315":{"position":[[8,4]]},"3331":{"position":[[8,4]]},"3343":{"position":[[8,4]]},"3357":{"position":[[8,4]]},"3365":{"position":[[8,4]]},"3375":{"position":[[1571,4]]},"3413":{"position":[[8,4]]},"3423":{"position":[[8,4]]},"3473":{"position":[[8,4]]},"3480":{"position":[[8,4]]},"3488":{"position":[[8,4]]},"3500":{"position":[[8,4]]},"3515":{"position":[[8,4]]},"3522":{"position":[[8,4]]},"3526":{"position":[[8,4]]},"3547":{"position":[[8,4]]},"3565":{"position":[[8,4]]},"3582":{"position":[[8,4]]},"3599":{"position":[[8,4]]},"3614":{"position":[[8,4]]},"3631":{"position":[[8,4]]}}}],["page'",{"_index":5452,"t":{"2662":{"position":[[3800,6]]}}}],["pagin",{"_index":1081,"t":{"42":{"position":[[912,8],[2528,10]]},"70":{"position":[[835,8],[1443,10]]},"162":{"position":[[1306,10]]},"168":{"position":[[333,10]]},"319":{"position":[[732,12],[850,11],[930,11]]},"963":{"position":[[1397,8],[2005,10]]},"1129":{"position":[[708,8]]},"1305":{"position":[[732,12],[850,11],[930,11]]},"1656":{"position":[[10,9],[523,10]]},"1660":{"position":[[624,10]]},"1664":{"position":[[287,10]]},"2100":{"position":[[1397,8],[2005,10]]},"2210":{"position":[[755,8]]},"2452":{"position":[[732,12],[850,11],[930,11]]},"2873":{"position":[[10,9],[214,10]]},"2877":{"position":[[183,10]]},"2881":{"position":[[153,10]]},"3259":{"position":[[1397,8],[2005,10]]},"3451":{"position":[[815,8]]}}}],["paginating.var",{"_index":1342,"t":{"70":{"position":[[963,14]]},"963":{"position":[[1525,14]]},"2100":{"position":[[1525,14]]},"3259":{"position":[[1525,14]]}}}],["paid",{"_index":2111,"t":{"148":{"position":[[359,4]]}}}],["pain",{"_index":595,"t":{"16":{"position":[[778,8]]},"20":{"position":[[760,7]]},"64":{"position":[[275,7]]}}}],["pair",{"_index":1731,"t":{"110":{"position":[[2833,5]]}}}],["panel",{"_index":4072,"t":{"930":{"position":[[159,5]]},"1495":{"position":[[1767,6],[1950,6],[8597,6],[9162,5],[9201,5]]},"2005":{"position":[[159,5]]},"2662":{"position":[[1782,6],[1965,6],[8698,6],[9293,5],[9336,5]]},"3222":{"position":[[159,5]]}}}],["paper",{"_index":4161,"t":{"983":{"position":[[1278,5]]},"2042":{"position":[[1278,5]]},"3283":{"position":[[1278,5]]}}}],["paradigm",{"_index":2937,"t":{"359":{"position":[[875,8]]},"1379":{"position":[[875,8]]}}}],["paralel",{"_index":3157,"t":{"473":{"position":[[4208,9]]},"477":{"position":[[4549,11],[6013,11]]},"485":{"position":[[1613,12]]}}}],["parallel",{"_index":2484,"t":{"240":{"position":[[715,11],[822,8]]},"365":{"position":[[155,8]]},"473":{"position":[[4074,12]]},"477":{"position":[[5953,11]]},"912":{"position":[[602,9]]},"1385":{"position":[[155,8]]},"1619":{"position":[[441,8]]},"1981":{"position":[[602,9]]},"2536":{"position":[[156,8],[276,9]]},"2806":{"position":[[441,8]]},"3194":{"position":[[602,9]]}}}],["param",{"_index":1051,"t":{"38":{"position":[[748,6]]},"82":{"position":[[259,5]]},"204":{"position":[[384,9],[821,7],[1643,6]]},"289":{"position":[[614,7]]},"560":{"position":[[704,5]]},"652":{"position":[[563,9],[664,7]]},"654":{"position":[[450,9],[584,7]]},"670":{"position":[[153,9],[246,7]]},"672":{"position":[[155,9],[225,7]]},"680":{"position":[[395,9],[472,7]]},"682":{"position":[[299,9],[1061,7],[1304,6]]},"684":{"position":[[260,9],[337,7]]},"1107":{"position":[[313,6],[446,5],[994,7],[1058,6],[1131,6],[1313,9]]},"1109":{"position":[[147,9],[410,9],[773,9],[1141,9],[1471,9],[1876,7]]},"1111":{"position":[[97,9],[190,7]]},"1113":{"position":[[58,7]]},"1115":{"position":[[81,9],[153,7]]},"1117":{"position":[[72,9],[116,7]]},"1119":{"position":[[107,7]]},"1121":{"position":[[231,9],[317,9],[759,7]]},"1123":{"position":[[162,9],[239,9],[495,7]]},"1125":{"position":[[488,9],[570,9],[933,7]]},"1127":{"position":[[202,9],[279,9],[494,7]]},"1129":{"position":[[101,9],[124,7]]},"1131":{"position":[[105,9],[484,7]]},"1164":{"position":[[757,9],[809,9]]},"1166":{"position":[[409,9],[739,6],[803,6]]},"1168":{"position":[[185,9],[243,6]]},"1170":{"position":[[205,9]]},"1172":{"position":[[462,9]]},"1207":{"position":[[1082,9]]},"1213":{"position":[[141,7],[190,5]]},"1495":{"position":[[9968,9]]},"1499":{"position":[[1136,5]]},"1577":{"position":[[1254,9]]},"1579":{"position":[[83,7]]},"1699":{"position":[[563,9],[664,7]]},"1702":{"position":[[450,9],[584,7]]},"1715":{"position":[[153,9],[246,7]]},"1717":{"position":[[155,9],[225,7]]},"1731":{"position":[[395,9],[472,7]]},"1733":{"position":[[299,9],[1061,7],[1304,6]]},"1735":{"position":[[260,9],[337,7]]},"1885":{"position":[[716,9]]},"1893":{"position":[[896,9]]},"2118":{"position":[[426,9]]},"2188":{"position":[[316,6],[449,5],[997,7],[1061,6],[1134,6],[1316,9]]},"2190":{"position":[[208,9],[470,9],[833,9],[1201,9],[1525,9],[1930,7]]},"2192":{"position":[[97,9],[190,7]]},"2194":{"position":[[58,7]]},"2196":{"position":[[81,9],[153,7]]},"2198":{"position":[[72,9],[116,7]]},"2200":{"position":[[107,7]]},"2202":{"position":[[274,9],[360,9],[802,7]]},"2204":{"position":[[162,9],[239,9],[495,7]]},"2206":{"position":[[488,9],[570,9],[933,7]]},"2208":{"position":[[202,9],[279,9],[494,7]]},"2210":{"position":[[101,9],[124,7]]},"2212":{"position":[[105,9],[484,7]]},"2214":{"position":[[291,9],[367,9]]},"2339":{"position":[[1082,9]]},"2387":{"position":[[141,7],[190,5]]},"2630":{"position":[[1136,5]]},"2656":{"position":[[584,6],[641,6]]},"2772":{"position":[[83,7]]},"2823":{"position":[[628,7]]},"2826":{"position":[[548,7]]},"2904":{"position":[[210,7]]},"2906":{"position":[[189,7]]},"2920":{"position":[[436,7]]},"2922":{"position":[[1025,7],[1268,6]]},"2924":{"position":[[301,7]]},"3427":{"position":[[310,6],[358,5]]},"3601":{"position":[[141,7],[190,5]]}}}],["paramet",{"_index":2402,"t":{"214":{"position":[[37,9],[982,9],[1774,9]]},"238":{"position":[[457,9]]},"652":{"position":[[672,9],[687,9]]},"654":{"position":[[592,9],[607,9]]},"670":{"position":[[254,9],[269,9]]},"672":{"position":[[233,9],[248,9]]},"678":{"position":[[106,10]]},"680":{"position":[[480,9],[495,9]]},"682":{"position":[[1069,9],[1084,9]]},"684":{"position":[[345,9],[360,9]]},"1105":{"position":[[220,10]]},"1109":{"position":[[1884,9],[1899,9]]},"1111":{"position":[[198,9],[213,9]]},"1113":{"position":[[66,9],[81,9]]},"1115":{"position":[[161,9],[176,9]]},"1117":{"position":[[124,9],[139,9]]},"1119":{"position":[[115,9],[130,9]]},"1121":{"position":[[767,9],[782,9]]},"1123":{"position":[[503,9],[518,9]]},"1125":{"position":[[126,9],[303,10],[941,9],[956,9]]},"1127":{"position":[[502,9],[517,9]]},"1129":{"position":[[132,9],[147,9]]},"1137":{"position":[[501,9]]},"1213":{"position":[[1066,11]]},"1285":{"position":[[667,9]]},"1571":{"position":[[81,10]]},"1579":{"position":[[91,9],[106,9]]},"1583":{"position":[[975,11]]},"1699":{"position":[[672,9],[687,9]]},"1702":{"position":[[592,9],[607,9]]},"1715":{"position":[[254,9],[269,9]]},"1717":{"position":[[233,9],[248,9]]},"1729":{"position":[[106,10]]},"1731":{"position":[[480,9],[495,9]]},"1733":{"position":[[1069,9],[1084,9]]},"1735":{"position":[[345,9],[360,9]]},"2186":{"position":[[228,11]]},"2190":{"position":[[1938,9],[1953,9]]},"2192":{"position":[[198,9],[213,9]]},"2194":{"position":[[66,9],[81,9]]},"2196":{"position":[[161,9],[176,9]]},"2198":{"position":[[124,9],[139,9]]},"2200":{"position":[[115,9],[130,9]]},"2202":{"position":[[810,9],[825,9]]},"2204":{"position":[[503,9],[518,9]]},"2206":{"position":[[126,9],[303,10],[941,9],[956,9]]},"2208":{"position":[[502,9],[517,9]]},"2210":{"position":[[132,9],[147,9]]},"2218":{"position":[[501,9]]},"2387":{"position":[[1066,11]]},"2432":{"position":[[667,9]]},"2730":{"position":[[81,10]]},"2772":{"position":[[91,9],[106,9]]},"2778":{"position":[[1073,11]]},"2823":{"position":[[636,9],[651,9]]},"2826":{"position":[[556,9],[571,9]]},"2904":{"position":[[218,9],[233,9]]},"2906":{"position":[[197,9],[212,9]]},"2918":{"position":[[106,10]]},"2920":{"position":[[444,9],[459,9]]},"2922":{"position":[[1033,9],[1048,9]]},"2924":{"position":[[309,9],[324,9]]},"3447":{"position":[[126,9],[303,10]]},"3459":{"position":[[501,9]]},"3601":{"position":[[1066,11]]},"3627":{"position":[[667,9]]},"3629":{"position":[[34,9],[983,9],[1775,9]]}}}],["paramount",{"_index":2535,"t":{"246":{"position":[[2166,9]]}}}],["pars",{"_index":1695,"t":{"108":{"position":[[1463,7]]},"110":{"position":[[2766,6]]},"846":{"position":[[82,5]]},"1164":{"position":[[3295,5]]},"3229":{"position":[[331,6]]}}}],["parsebool",{"_index":4025,"t":{"878":{"position":[[376,9]]},"1945":{"position":[[376,9]]},"3158":{"position":[[376,9]]}}}],["parser",{"_index":2657,"t":{"283":{"position":[[96,6]]}}}],["parser\");const",{"_index":2664,"t":{"283":{"position":[[219,14]]}}}],["part",{"_index":447,"t":{"10":{"position":[[1430,4],[1851,4]]},"16":{"position":[[1873,4],[4924,4]]},"20":{"position":[[19,4]]},"26":{"position":[[1725,6]]},"28":{"position":[[2143,6]]},"46":{"position":[[2563,6]]},"74":{"position":[[2153,5]]},"86":{"position":[[1459,4]]},"112":{"position":[[10,4]]},"118":{"position":[[544,5]]},"144":{"position":[[423,5]]},"146":{"position":[[809,6]]},"152":{"position":[[191,6]]},"166":{"position":[[2484,5]]},"170":{"position":[[204,4],[375,4]]},"176":{"position":[[831,4]]},"202":{"position":[[566,4]]},"242":{"position":[[203,4]]},"256":{"position":[[338,5]]},"277":{"position":[[999,4]]},"279":{"position":[[574,5]]},"283":{"position":[[2435,5]]},"301":{"position":[[2292,4]]},"319":{"position":[[500,4]]},"371":{"position":[[1168,4]]},"377":{"position":[[21,4],[100,4]]},"387":{"position":[[1030,4]]},"389":{"position":[[1317,4]]},"391":{"position":[[50,4],[107,4]]},"399":{"position":[[373,4]]},"407":{"position":[[105,4],[774,4]]},"469":{"position":[[542,4]]},"479":{"position":[[1561,4],[2440,4],[2469,4],[2506,4]]},"556":{"position":[[197,4]]},"640":{"position":[[255,4]]},"692":{"position":[[411,4]]},"918":{"position":[[888,5]]},"951":{"position":[[395,4]]},"957":{"position":[[154,4]]},"993":{"position":[[155,4]]},"1087":{"position":[[416,4]]},"1270":{"position":[[161,4]]},"1305":{"position":[[500,4]]},"1323":{"position":[[326,4]]},"1341":{"position":[[234,4]]},"1353":{"position":[[1105,4]]},"1391":{"position":[[1168,4]]},"1567":{"position":[[472,4]]},"1569":{"position":[[144,5],[1016,5]]},"1617":{"position":[[255,4]]},"1635":{"position":[[238,4]]},"1668":{"position":[[573,4]]},"1873":{"position":[[396,4]]},"1923":{"position":[[760,4]]},"1993":{"position":[[1103,5]]},"2054":{"position":[[155,4]]},"2078":{"position":[[395,4]]},"2084":{"position":[[154,4]]},"2170":{"position":[[416,4]]},"2294":{"position":[[161,4]]},"2452":{"position":[[500,4]]},"2470":{"position":[[326,4]]},"2488":{"position":[[234,4]]},"2542":{"position":[[1188,4]]},"2546":{"position":[[1165,5]]},"2726":{"position":[[472,4]]},"2728":{"position":[[144,5],[1016,5]]},"2744":{"position":[[256,4]]},"2804":{"position":[[255,4]]},"2852":{"position":[[238,4]]},"2887":{"position":[[573,4]]},"3060":{"position":[[861,4]]},"3116":{"position":[[760,4]]},"3208":{"position":[[1103,5]]},"3245":{"position":[[395,4]]},"3251":{"position":[[154,4]]},"3293":{"position":[[155,4]]},"3345":{"position":[[131,5]]},"3399":{"position":[[416,4]]},"3524":{"position":[[161,4]]}}}],["parti",{"_index":2760,"t":{"293":{"position":[[191,5]]},"303":{"position":[[110,5]]},"1355":{"position":[[197,5],[356,5]]},"2548":{"position":[[197,5],[356,5]]},"3347":{"position":[[166,5],[1054,5]]}}}],["particip",{"_index":2197,"t":{"168":{"position":[[235,12]]}}}],["participant'",{"_index":2084,"t":{"138":{"position":[[1988,13]]}}}],["particular",{"_index":1932,"t":{"130":{"position":[[87,10],[2796,11]]},"192":{"position":[[183,10]]},"264":{"position":[[1031,11]]},"347":{"position":[[1630,10]]},"652":{"position":[[140,10]]},"682":{"position":[[81,10]]},"1335":{"position":[[1634,10]]},"1349":{"position":[[364,11]]},"1699":{"position":[[140,10]]},"1733":{"position":[[81,10]]},"1823":{"position":[[94,10],[665,10]]},"2086":{"position":[[398,10]]},"2494":{"position":[[362,11]]},"2823":{"position":[[140,10]]},"2922":{"position":[[81,10]]},"3012":{"position":[[94,10],[665,10]]},"3253":{"position":[[398,10]]}}}],["particularli",{"_index":2324,"t":{"200":{"position":[[504,12]]}}}],["partit",{"_index":678,"t":{"16":{"position":[[4469,10]]},"238":{"position":[[616,12]]},"240":{"position":[[628,13],[668,11],[750,11]]}}}],["pass",{"_index":857,"t":{"26":{"position":[[985,7]]},"28":{"position":[[1110,4]]},"32":{"position":[[1350,4]]},"38":{"position":[[165,7],[688,4]]},"86":{"position":[[2197,7]]},"178":{"position":[[225,4]]},"190":{"position":[[68,6]]},"339":{"position":[[1291,4]]},"355":{"position":[[104,6]]},"383":{"position":[[141,6]]},"399":{"position":[[1481,4],[1624,7]]},"417":{"position":[[226,8]]},"453":{"position":[[572,4],[626,5]]},"477":{"position":[[1636,4]]},"497":{"position":[[185,4]]},"598":{"position":[[303,4]]},"694":{"position":[[167,4]]},"722":{"position":[[434,6]]},"758":{"position":[[1224,4]]},"764":{"position":[[653,6]]},"768":{"position":[[511,4]]},"806":{"position":[[204,7]]},"892":{"position":[[440,7],[1012,4]]},"965":{"position":[[3201,6]]},"1065":{"position":[[737,4],[902,6],[955,4],[1238,4],[5048,4]]},"1087":{"position":[[647,4],[1170,7],[1422,4]]},"1089":{"position":[[765,6]]},"1107":{"position":[[285,4],[491,7]]},"1172":{"position":[[987,6]]},"1196":{"position":[[17,4]]},"1213":{"position":[[100,4]]},"1249":{"position":[[360,4]]},"1285":{"position":[[846,4]]},"1327":{"position":[[1291,4]]},"1341":{"position":[[494,7]]},"1347":{"position":[[104,6]]},"1359":{"position":[[185,4]]},"1401":{"position":[[222,8]]},"1453":{"position":[[573,4],[627,5]]},"1495":{"position":[[3516,7],[5893,6]]},"1499":{"position":[[1680,4]]},"1563":{"position":[[303,4]]},"1635":{"position":[[560,4]]},"1777":{"position":[[751,4]]},"1783":{"position":[[653,6]]},"1787":{"position":[[511,4]]},"1857":{"position":[[201,7]]},"1959":{"position":[[440,7],[1012,4]]},"2032":{"position":[[462,7],[657,7],[879,7]]},"2038":{"position":[[465,7],[678,7],[918,7]]},"2120":{"position":[[528,6]]},"2146":{"position":[[737,4],[902,6],[1072,4],[1355,4],[5171,4]]},"2170":{"position":[[647,4],[1170,7],[1422,4]]},"2172":{"position":[[739,6]]},"2188":{"position":[[288,4],[494,7]]},"2258":{"position":[[898,4]]},"2328":{"position":[[17,4]]},"2387":{"position":[[100,4]]},"2400":{"position":[[360,4]]},"2432":{"position":[[846,4]]},"2434":{"position":[[2312,4],[2447,7]]},"2436":{"position":[[104,6]]},"2474":{"position":[[1291,4]]},"2488":{"position":[[494,7]]},"2510":{"position":[[185,4]]},"2558":{"position":[[223,8]]},"2594":{"position":[[595,4],[649,5]]},"2630":{"position":[[1680,4]]},"2662":{"position":[[3543,7],[5952,6]]},"2766":{"position":[[303,4]]},"2852":{"position":[[560,4]]},"2966":{"position":[[829,4]]},"2976":{"position":[[655,4]]},"3048":{"position":[[201,7]]},"3172":{"position":[[440,7],[1012,4]]},"3198":{"position":[[196,4]]},"3273":{"position":[[435,7],[603,7],[798,7]]},"3279":{"position":[[438,7],[624,7],[837,7]]},"3335":{"position":[[512,6]]},"3347":{"position":[[1390,4]]},"3349":{"position":[[4217,7]]},"3375":{"position":[[737,4],[902,6],[1072,4],[1355,4],[5200,4]]},"3399":{"position":[[647,4],[1170,7],[1422,4]]},"3401":{"position":[[739,6]]},"3427":{"position":[[282,4],[403,7]]},"3492":{"position":[[898,4]]},"3567":{"position":[[360,4]]},"3584":{"position":[[17,4]]},"3601":{"position":[[100,4]]},"3612":{"position":[[2511,4],[2646,7]]},"3627":{"position":[[846,4]]}}}],["pass'app.post('/login",{"_index":2714,"t":{"283":{"position":[[1971,23]]}}}],["pass)/limit",{"_index":4104,"t":{"949":{"position":[[229,18]]},"2076":{"position":[[229,18]]},"3243":{"position":[[229,18]]}}}],["proce",{"_index":273,"t":{"4":{"position":[[1157,8]]},"150":{"position":[[675,7]]},"194":{"position":[[1267,7]]},"214":{"position":[[1330,7]]},"489":{"position":[[772,7]]},"1469":{"position":[[784,7]]},"3629":{"position":[[1331,7]]},"3639":{"position":[[954,7]]},"3655":{"position":[[1138,7]]}}}],["procec",{"_index":4990,"t":{"1670":{"position":[[495,10]]}}}],["procedur",{"_index":717,"t":{"16":{"position":[[6556,10]]}}}],["proceed",{"_index":2608,"t":{"256":{"position":[[1615,10]]}}}],["process",{"_index":343,"t":{"8":{"position":[[820,8]]},"10":{"position":[[382,7],[736,7]]},"14":{"position":[[981,7]]},"16":{"position":[[107,7],[369,7],[2935,8],[4513,7]]},"18":{"position":[[2144,8],[2170,7],[2378,7]]},"34":{"position":[[2168,7]]},"42":{"position":[[2572,8]]},"44":{"position":[[1960,7]]},"46":{"position":[[90,8]]},"98":{"position":[[792,7]]},"110":{"position":[[3565,7]]},"118":{"position":[[958,8]]},"166":{"position":[[548,9],[959,9]]},"186":{"position":[[106,8]]},"194":{"position":[[173,8]]},"222":{"position":[[436,10]]},"226":{"position":[[76,7],[636,10]]},"228":{"position":[[88,10]]},"236":{"position":[[181,7]]},"238":{"position":[[344,7]]},"240":{"position":[[307,7]]},"244":{"position":[[141,7]]},"256":{"position":[[940,7]]},"262":{"position":[[1922,7]]},"287":{"position":[[356,7],[1866,7]]},"293":{"position":[[291,7]]},"297":{"position":[[601,8]]},"321":{"position":[[423,7]]},"337":{"position":[[211,9]]},"339":{"position":[[492,9]]},"347":{"position":[[116,7],[813,7],[947,9],[1022,10],[1744,10]]},"359":{"position":[[516,7]]},"365":{"position":[[264,9]]},"371":{"position":[[943,7]]},"383":{"position":[[386,7]]},"389":{"position":[[709,8]]},"473":{"position":[[490,9]]},"475":{"position":[[219,7],[2745,7],[2774,10]]},"479":{"position":[[5687,7]]},"481":{"position":[[6837,7]]},"525":{"position":[[679,8]]},"580":{"position":[[143,7]]},"604":{"position":[[1939,8]]},"614":{"position":[[677,7]]},"644":{"position":[[99,7]]},"646":{"position":[[270,7]]},"658":{"position":[[145,11]]},"664":{"position":[[99,7]]},"706":{"position":[[472,7]]},"712":{"position":[[128,7],[764,7]]},"714":{"position":[[143,7]]},"764":{"position":[[1113,8]]},"796":{"position":[[78,7]]},"902":{"position":[[298,7]]},"912":{"position":[[92,9],[162,9],[301,7],[575,9],[823,10]]},"949":{"position":[[58,7],[210,7]]},"961":{"position":[[703,7]]},"965":{"position":[[2522,7],[3325,7]]},"969":{"position":[[128,7]]},"1041":{"position":[[541,7]]},"1065":{"position":[[1644,7]]},"1067":{"position":[[1083,7]]},"1069":{"position":[[1053,7]]},"1071":{"position":[[2377,7]]},"1073":{"position":[[2409,7]]},"1129":{"position":[[829,7]]},"1149":{"position":[[367,7]]},"1162":{"position":[[666,7]]},"1164":{"position":[[3132,10],[3257,9]]},"1245":{"position":[[205,7]]},"1270":{"position":[[571,7]]},"1307":{"position":[[423,7]]},"1325":{"position":[[211,9]]},"1327":{"position":[[492,9]]},"1335":{"position":[[116,7],[817,7],[951,9],[1026,10],[1748,10]]},"1379":{"position":[[516,7]]},"1385":{"position":[[264,9]]},"1391":{"position":[[943,7]]},"1433":{"position":[[681,8]]},"1473":{"position":[[424,7]]},"1475":{"position":[[11,9],[139,10],[859,9],[1182,9],[1333,10],[1759,10]]},"1495":{"position":[[3667,7],[10549,7]]},"1533":{"position":[[2474,8]]},"1549":{"position":[[753,7]]},"1587":{"position":[[143,7]]},"1625":{"position":[[337,7]]},"1627":{"position":[[224,7]]},"1680":{"position":[[409,11]]},"1686":{"position":[[145,11]]},"1690":{"position":[[99,7]]},"1692":{"position":[[270,7]]},"1708":{"position":[[99,7]]},"1783":{"position":[[1113,8]]},"1847":{"position":[[75,7]]},"1881":{"position":[[950,10]]},"1885":{"position":[[1486,7]]},"1891":{"position":[[128,7],[764,7]]},"1893":{"position":[[143,7]]},"1969":{"position":[[298,7]]},"1975":{"position":[[201,10]]},"1981":{"position":[[92,9],[162,9],[301,7],[575,9],[823,10]]},"2024":{"position":[[128,7]]},"2076":{"position":[[58,7],[210,7]]},"2098":{"position":[[759,7]]},"2102":{"position":[[2170,7],[3483,8]]},"2132":{"position":[[541,7]]},"2146":{"position":[[1761,7]]},"2148":{"position":[[1083,7]]},"2150":{"position":[[1053,7]]},"2152":{"position":[[2377,7]]},"2154":{"position":[[2409,7]]},"2156":{"position":[[2484,7]]},"2210":{"position":[[871,7]]},"2240":{"position":[[951,10],[1076,9]]},"2277":{"position":[[367,7]]},"2294":{"position":[[899,7]]},"2324":{"position":[[189,7]]},"2355":{"position":[[689,7]]},"2361":{"position":[[401,7],[649,7]]},"2454":{"position":[[423,7]]},"2472":{"position":[[211,9]]},"2474":{"position":[[492,9]]},"2482":{"position":[[1015,9],[1990,10]]},"2530":{"position":[[980,7],[1234,7]]},"2536":{"position":[[263,9]]},"2542":{"position":[[958,7]]},"2616":{"position":[[689,8]]},"2662":{"position":[[3700,7],[10678,7]]},"2666":{"position":[[424,7]]},"2668":{"position":[[11,9],[139,10],[859,9],[1182,9],[1333,10],[1759,10]]},"2702":{"position":[[2474,8]]},"2720":{"position":[[46,7],[289,11]]},"2722":{"position":[[1956,9]]},"2810":{"position":[[145,11]]},"2814":{"position":[[99,7]]},"2816":{"position":[[270,7]]},"2834":{"position":[[409,11]]},"2842":{"position":[[337,7]]},"2844":{"position":[[224,7]]},"2862":{"position":[[282,7]]},"2889":{"position":[[543,10],[634,11],[821,10],[1011,10],[1090,11]]},"2897":{"position":[[99,7]]},"2972":{"position":[[147,7],[1066,8],[1117,7]]},"3038":{"position":[[75,7]]},"3072":{"position":[[1238,10]]},"3076":{"position":[[1450,7]]},"3082":{"position":[[128,7],[764,7]]},"3084":{"position":[[143,7]]},"3182":{"position":[[298,7]]},"3188":{"position":[[174,10]]},"3194":{"position":[[92,9],[162,9],[301,7],[575,9],[823,10]]},"3243":{"position":[[58,7],[210,7]]},"3257":{"position":[[759,7]]},"3261":{"position":[[2170,7],[3483,8]]},"3265":{"position":[[128,7]]},"3324":{"position":[[4496,10]]},"3347":{"position":[[204,8],[1727,7]]},"3361":{"position":[[541,7]]},"3375":{"position":[[1761,7]]},"3377":{"position":[[1083,7]]},"3379":{"position":[[1100,7]]},"3381":{"position":[[602,10],[2180,7]]},"3383":{"position":[[2409,7]]},"3385":{"position":[[2457,7]]},"3387":{"position":[[444,9]]},"3389":{"position":[[978,9]]},"3451":{"position":[[931,7]]},"3455":{"position":[[60,9]]},"3504":{"position":[[367,7]]},"3524":{"position":[[899,7]]},"3545":{"position":[[168,7]]},"3551":{"position":[[951,10],[1076,9]]},"3643":{"position":[[689,7]]},"3649":{"position":[[401,7],[649,7]]}}}],["processor",{"_index":3233,"t":{"477":{"position":[[4525,9]]}}}],["produc",{"_index":3203,"t":{"477":{"position":[[1677,8]]},"481":{"position":[[2072,7]]},"2862":{"position":[[344,8]]}}}],["product",{"_index":393,"t":{"10":{"position":[[91,10]]},"46":{"position":[[2307,10]]},"94":{"position":[[2384,10]]},"116":{"position":[[287,10]]},"138":{"position":[[537,10]]},"142":{"position":[[117,10]]},"168":{"position":[[104,10]]},"170":{"position":[[217,10]]},"212":{"position":[[459,11]]},"214":{"position":[[1705,10]]},"218":{"position":[[238,7]]},"246":{"position":[[2259,11],[3415,10]]},"252":{"position":[[1435,8]]},"339":{"position":[[148,10]]},"399":{"position":[[1873,10]]},"407":{"position":[[294,10]]},"459":{"position":[[21,10],[70,10]]},"461":{"position":[[29,10]]},"489":{"position":[[322,10]]},"525":{"position":[[563,11]]},"552":{"position":[[63,10]]},"578":{"position":[[425,10]]},"612":{"position":[[41,10]]},"854":{"position":[[568,11]]},"858":{"position":[[258,10]]},"876":{"position":[[215,10]]},"886":{"position":[[406,10]]},"918":{"position":[[759,10]]},"935":{"position":[[291,11]]},"977":{"position":[[1472,10]]},"989":{"position":[[441,10]]},"1327":{"position":[[148,10]]},"1433":{"position":[[565,11]]},"1459":{"position":[[21,10],[70,10]]},"1461":{"position":[[50,10]]},"1469":{"position":[[328,10]]},"1499":{"position":[[438,10]]},"1511":{"position":[[777,10]]},"1547":{"position":[[41,10]]},"1585":{"position":[[425,10]]},"1721":{"position":[[568,11]]},"1725":{"position":[[258,10]]},"1943":{"position":[[215,10]]},"1953":{"position":[[406,10]]},"1993":{"position":[[974,10]]},"2010":{"position":[[399,10]]},"2034":{"position":[[1472,10]]},"2050":{"position":[[441,10]]},"2434":{"position":[[262,10],[2725,10]]},"2474":{"position":[[148,10]]},"2600":{"position":[[21,11],[71,10]]},"2602":{"position":[[54,10]]},"2610":{"position":[[327,10]]},"2616":{"position":[[569,11]]},"2630":{"position":[[438,10]]},"2642":{"position":[[777,10]]},"2716":{"position":[[41,10]]},"2722":{"position":[[408,11]]},"2782":{"position":[[424,10]]},"2910":{"position":[[568,11]]},"2914":{"position":[[287,10]]},"3156":{"position":[[215,10]]},"3166":{"position":[[406,10]]},"3208":{"position":[[974,10]]},"3227":{"position":[[399,10]]},"3275":{"position":[[1472,10]]},"3289":{"position":[[441,10]]},"3329":{"position":[[191,11]]},"3427":{"position":[[690,10]]},"3612":{"position":[[289,10],[2924,10]]},"3629":{"position":[[1706,10]]}}}],["profil",{"_index":501,"t":{"12":{"position":[[810,8],[992,9]]},"383":{"position":[[1813,8]]},"485":{"position":[[1604,8]]},"3345":{"position":[[1132,8]]}}}],["program",{"_index":243,"t":{"4":{"position":[[475,11]]},"70":{"position":[[599,7]]},"76":{"position":[[578,11]]},"120":{"position":[[990,11]]},"148":{"position":[[1340,11]]},"279":{"position":[[1457,11]]},"347":{"position":[[1211,7]]},"377":{"position":[[668,11]]},"477":{"position":[[1742,7]]},"479":{"position":[[756,7]]},"485":{"position":[[1033,11]]},"491":{"position":[[188,11]]},"864":{"position":[[156,7]]},"866":{"position":[[161,7]]},"868":{"position":[[364,7]]},"870":{"position":[[221,7]]},"963":{"position":[[1161,7]]},"1083":{"position":[[125,11]]},"1135":{"position":[[67,11]]},"1139":{"position":[[314,7]]},"1141":{"position":[[1387,7]]},"1335":{"position":[[1215,7]]},"2062":{"position":[[200,7]]},"2064":{"position":[[156,7]]},"2066":{"position":[[364,7]]},"2068":{"position":[[425,7]]},"2070":{"position":[[221,7]]},"2072":{"position":[[229,7]]},"2100":{"position":[[1161,7]]},"2166":{"position":[[125,11]]},"2216":{"position":[[67,11]]},"2220":{"position":[[314,7]]},"2222":{"position":[[1387,7]]},"2345":{"position":[[1233,7]]},"2482":{"position":[[1245,7]]},"2950":{"position":[[145,11]]},"3140":{"position":[[200,7]]},"3142":{"position":[[156,7]]},"3144":{"position":[[364,7]]},"3146":{"position":[[425,7]]},"3148":{"position":[[221,7]]},"3150":{"position":[[229,7]]},"3259":{"position":[[1161,7]]},"3349":{"position":[[1863,11]]},"3395":{"position":[[125,11]]},"3457":{"position":[[67,11]]},"3461":{"position":[[314,7]]},"3463":{"position":[[1387,7]]},"3633":{"position":[[1233,7]]}}}],["programm",{"_index":5082,"t":{"1865":{"position":[[1281,10]]},"3056":{"position":[[1281,10]]}}}],["programmat",{"_index":3986,"t":{"808":{"position":[[275,16]]},"1859":{"position":[[275,16]]},"3050":{"position":[[275,16]]}}}],["progress",{"_index":1528,"t":{"94":{"position":[[2261,9]]},"114":{"position":[[104,9]]},"2778":{"position":[[1670,8]]}}}],["prohibit",{"_index":5572,"t":{"3068":{"position":[[464,9]]}}}],["project",{"_index":375,"t":{"8":{"position":[[1474,8],[1688,8]]},"26":{"position":[[225,7],[1167,8]]},"50":{"position":[[76,7],[1082,7],[2198,8]]},"84":{"position":[[35,7]]},"88":{"position":[[288,7]]},"110":{"position":[[4574,8]]},"120":{"position":[[48,7],[187,7],[955,7]]},"124":{"position":[[29,8]]},"126":{"position":[[633,7]]},"128":{"position":[[2662,7]]},"148":{"position":[[49,7],[1305,7],[1486,8]]},"194":{"position":[[354,7]]},"246":{"position":[[1140,8]]},"250":{"position":[[1136,9]]},"256":{"position":[[362,8]]},"279":{"position":[[316,7],[1422,7]]},"303":{"position":[[191,8]]},"339":{"position":[[1500,9]]},"377":{"position":[[40,7]]},"393":{"position":[[643,8]]},"407":{"position":[[456,9]]},"477":{"position":[[132,8],[340,8]]},"481":{"position":[[869,8]]},"485":{"position":[[319,8]]},"935":{"position":[[191,8],[227,9]]},"945":{"position":[[399,7]]},"989":{"position":[[676,7]]},"1327":{"position":[[1500,9]]},"1343":{"position":[[45,8],[134,8]]},"1355":{"position":[[32,8]]},"2010":{"position":[[280,8],[335,9]]},"2020":{"position":[[399,7]]},"2050":{"position":[[676,7]]},"2474":{"position":[[1500,9]]},"2490":{"position":[[45,8],[134,8]]},"2548":{"position":[[32,8]]},"3227":{"position":[[280,8],[335,9]]},"3239":{"position":[[399,7]]},"3289":{"position":[[676,7]]}}}],["prolong",{"_index":3920,"t":{"742":{"position":[[406,10]]},"1761":{"position":[[406,10]]}}}],["prometheu",{"_index":1292,"t":{"62":{"position":[[1516,10]]},"206":{"position":[[699,11]]},"439":{"position":[[240,10]]},"926":{"position":[[64,10],[289,10],[358,10],[1161,10]]},"930":{"position":[[829,10]]},"1009":{"position":[[10,10],[52,10],[92,13],[199,10]]},"1013":{"position":[[52,10],[128,10]]},"1423":{"position":[[240,10]]},"1975":{"position":[[338,10]]},"2001":{"position":[[64,10],[289,10],[358,10],[1161,10]]},"2005":{"position":[[1018,10]]},"2090":{"position":[[10,10],[52,10],[92,13],[199,10]]},"2094":{"position":[[52,10],[128,10]]},"2580":{"position":[[257,10]]},"3188":{"position":[[311,10]]},"3218":{"position":[[64,10],[289,10],[358,10],[1251,10]]},"3222":{"position":[[1105,10]]},"3299":{"position":[[10,10],[52,10],[92,13],[199,10]]},"3303":{"position":[[52,10],[128,10]]},"3318":{"position":[[10,10],[52,10],[92,13],[199,10]]},"3322":{"position":[[52,10],[128,10]]}}}],["prometheus/graphit",{"_index":3065,"t":{"439":{"position":[[173,19]]},"1423":{"position":[[173,19]]},"2580":{"position":[[182,19]]}}}],["prometheus_handler_prefix",{"_index":4082,"t":{"930":{"position":[[769,25]]},"2005":{"position":[[958,25]]},"3222":{"position":[[1045,25]]}}}],["promis",{"_index":1993,"t":{"132":{"position":[[3,8],[987,8]]},"168":{"position":[[3,9]]},"252":{"position":[[1632,8]]},"479":{"position":[[2611,9]]},"987":{"position":[[179,8]]},"2046":{"position":[[179,8]]}}}],["promise((resolv",{"_index":2835,"t":{"301":{"position":[[2594,17]]},"2351":{"position":[[849,17]]},"2367":{"position":[[831,17]]}}}],["prompt",{"_index":3079,"t":{"467":{"position":[[1096,8]]}}}],["prop",{"_index":3085,"t":{"471":{"position":[[126,5]]}}}],["propag",{"_index":3522,"t":{"517":{"position":[[192,9]]},"1001":{"position":[[771,11]]},"1073":{"position":[[656,9]]},"2108":{"position":[[771,11]]},"2154":{"position":[[656,9]]},"3309":{"position":[[771,11]]},"3383":{"position":[[656,9]]}}}],["proper",{"_index":366,"t":{"8":{"position":[[1314,6]]},"50":{"position":[[1525,6]]},"52":{"position":[[84,6]]},"198":{"position":[[1212,6]]},"236":{"position":[[1469,6]]},"321":{"position":[[1235,6]]},"347":{"position":[[2179,6]]},"379":{"position":[[2944,6]]},"399":{"position":[[1617,6],[2049,6]]},"918":{"position":[[792,6]]},"1135":{"position":[[284,6]]},"1307":{"position":[[1235,6]]},"1319":{"position":[[301,6]]},"1335":{"position":[[2183,6]]},"1495":{"position":[[6679,6]]},"1505":{"position":[[390,6]]},"1993":{"position":[[1007,6]]},"2216":{"position":[[284,6]]},"2434":{"position":[[2440,6]]},"2454":{"position":[[1235,6]]},"2466":{"position":[[301,6]]},"2482":{"position":[[2425,6]]},"2636":{"position":[[390,6]]},"2662":{"position":[[6760,6]]},"3208":{"position":[[1007,6]]},"3347":{"position":[[950,6]]},"3349":{"position":[[4957,6]]},"3457":{"position":[[284,6]]},"3612":{"position":[[2639,6]]}}}],["properli",{"_index":1913,"t":{"128":{"position":[[2683,8]]},"134":{"position":[[1995,8]]},"160":{"position":[[943,8]]},"204":{"position":[[114,8]]},"212":{"position":[[423,8]]},"449":{"position":[[423,8]]},"505":{"position":[[898,8]]},"572":{"position":[[756,8]]},"598":{"position":[[435,8]]},"654":{"position":[[819,8]]},"694":{"position":[[106,8]]},"979":{"position":[[1123,8]]},"981":{"position":[[74,8]]},"1065":{"position":[[423,8]]},"1071":{"position":[[767,8]]},"1087":{"position":[[638,8]]},"1213":{"position":[[277,8]]},"1266":{"position":[[422,8]]},"1367":{"position":[[898,8]]},"1449":{"position":[[441,8]]},"1495":{"position":[[9005,9]]},"1563":{"position":[[435,8]]},"1702":{"position":[[823,8]]},"2036":{"position":[[1139,8]]},"2040":{"position":[[74,8]]},"2146":{"position":[[423,8]]},"2152":{"position":[[767,8]]},"2170":{"position":[[638,8]]},"2214":{"position":[[514,8]]},"2290":{"position":[[428,8]]},"2387":{"position":[[277,8]]},"2482":{"position":[[1146,8]]},"2518":{"position":[[932,8]]},"2590":{"position":[[456,8]]},"2662":{"position":[[9125,9]]},"2766":{"position":[[435,8]]},"2826":{"position":[[787,8]]},"3277":{"position":[[1139,8]]},"3281":{"position":[[74,8]]},"3349":{"position":[[4169,8]]},"3375":{"position":[[423,8]]},"3399":{"position":[[638,8]]},"3482":{"position":[[463,8]]},"3601":{"position":[[277,8]]}}}],["properti",{"_index":728,"t":{"16":{"position":[[6959,10]]},"74":{"position":[[1675,10]]},"252":{"position":[[493,10]]},"361":{"position":[[1216,10]]},"718":{"position":[[250,10]]},"961":{"position":[[8,10],[863,10]]},"1107":{"position":[[971,11]]},"1381":{"position":[[1221,10]]},"1623":{"position":[[109,10]]},"1893":{"position":[[1355,10]]},"1897":{"position":[[370,10]]},"2098":{"position":[[8,10],[623,10],[919,10]]},"2188":{"position":[[974,11]]},"2532":{"position":[[1287,10]]},"2840":{"position":[[109,10]]},"3084":{"position":[[1319,10]]},"3090":{"position":[[370,10]]},"3257":{"position":[[8,10],[623,10],[919,10]]}}}],["proposit",{"_index":2572,"t":{"250":{"position":[[2085,11]]}}}],["proprietari",{"_index":1248,"t":{"58":{"position":[[347,11]]}}}],["protect",{"_index":1475,"t":{"88":{"position":[[933,7]]},"162":{"position":[[487,9]]},"200":{"position":[[1204,9]]},"385":{"position":[[349,12],[392,10],[722,9]]},"550":{"position":[[161,9]]},"576":{"position":[[386,7]]},"696":{"position":[[708,9]]},"722":{"position":[[0,9],[414,9]]},"742":{"position":[[787,7]]},"756":{"position":[[414,7],[569,9]]},"758":{"position":[[739,7],[888,9]]},"854":{"position":[[525,7]]},"858":{"position":[[276,9]]},"906":{"position":[[247,7]]},"926":{"position":[[530,7]]},"937":{"position":[[296,9]]},"939":{"position":[[200,9]]},"1107":{"position":[[9,9],[675,7]]},"1143":{"position":[[70,7]]},"1162":{"position":[[1537,10]]},"1182":{"position":[[1738,10]]},"1511":{"position":[[725,11]]},"1583":{"position":[[317,7]]},"1682":{"position":[[60,7],[1158,7]]},"1721":{"position":[[525,7]]},"1725":{"position":[[276,9]]},"1761":{"position":[[787,7]]},"1973":{"position":[[247,7]]},"2001":{"position":[[530,7]]},"2012":{"position":[[296,9]]},"2014":{"position":[[200,9]]},"2032":{"position":[[42,9]]},"2086":{"position":[[43,10],[181,10],[256,10],[468,7]]},"2188":{"position":[[12,9],[678,7]]},"2224":{"position":[[70,7]]},"2250":{"position":[[1738,10]]},"2642":{"position":[[725,11]]},"2778":{"position":[[318,7]]},"2836":{"position":[[60,7],[1158,7]]},"2883":{"position":[[1556,7]]},"2910":{"position":[[525,7]]},"2914":{"position":[[310,9],[378,11]]},"2950":{"position":[[847,7]]},"3186":{"position":[[247,7]]},"3218":{"position":[[620,7]]},"3231":{"position":[[296,9]]},"3233":{"position":[[200,9]]},"3253":{"position":[[43,10],[181,10],[256,10],[468,7]]},"3273":{"position":[[42,9]]},"3427":{"position":[[12,9],[635,7],[879,10]]},"3465":{"position":[[70,7]]},"3561":{"position":[[1738,10]]}}}],["proto",{"_index":2034,"t":{"134":{"position":[[1047,5],[1472,5]]},"287":{"position":[[1012,5],[1349,5]]},"2722":{"position":[[1323,5]]}}}],["protobuf",{"_index":88,"t":{"2":{"position":[[1144,8]]},"12":{"position":[[1341,8],[1400,8]]},"14":{"position":[[1537,9]]},"28":{"position":[[1076,8],[1130,8]]},"34":{"position":[[2637,8],[2823,8]]},"50":{"position":[[357,8]]},"62":{"position":[[1252,8]]},"76":{"position":[[541,8]]},"86":{"position":[[1338,8]]},"196":{"position":[[655,8]]},"323":{"position":[[367,8],[445,8],[573,8]]},"379":{"position":[[1374,8],[2083,8]]},"399":{"position":[[1540,8]]},"413":{"position":[[296,8]]},"417":{"position":[[36,8]]},"453":{"position":[[1096,8]]},"465":{"position":[[172,8]]},"556":{"position":[[61,8],[404,8]]},"624":{"position":[[32,8]]},"628":{"position":[[32,8]]},"632":{"position":[[32,8]]},"754":{"position":[[24,8]]},"1065":{"position":[[2629,8]]},"1067":{"position":[[922,8]]},"1069":{"position":[[892,8]]},"1071":{"position":[[2216,8]]},"1073":{"position":[[2248,8]]},"1079":{"position":[[412,8]]},"1083":{"position":[[189,8]]},"1087":{"position":[[156,8]]},"1137":{"position":[[424,8]]},"1153":{"position":[[115,8]]},"1162":{"position":[[359,8],[2270,8]]},"1164":{"position":[[31,8],[1259,8],[1481,8],[1838,8],[2285,8]]},"1166":{"position":[[474,8]]},"1180":{"position":[[224,8]]},"1245":{"position":[[114,8]]},"1285":{"position":[[264,8],[326,8],[398,8],[509,8],[1025,8]]},"1309":{"position":[[367,8],[445,8],[573,8]]},"1397":{"position":[[300,8]]},"1401":{"position":[[36,8]]},"1465":{"position":[[172,8]]},"1579":{"position":[[703,9]]},"1593":{"position":[[32,8]]},"1597":{"position":[[32,8]]},"1601":{"position":[[32,8]]},"1773":{"position":[[24,8]]},"2146":{"position":[[2746,8]]},"2148":{"position":[[922,8]]},"2150":{"position":[[892,8]]},"2152":{"position":[[2216,8]]},"2154":{"position":[[2248,8]]},"2156":{"position":[[2323,8]]},"2162":{"position":[[412,8]]},"2166":{"position":[[189,8]]},"2170":{"position":[[156,8]]},"2218":{"position":[[424,8]]},"2238":{"position":[[293,8],[357,8]]},"2244":{"position":[[949,8],[1171,8],[1534,8],[2083,8]]},"2258":{"position":[[82,9],[153,8],[180,8],[278,8],[704,8],[972,8],[1078,8],[1222,8],[1432,9],[1647,8]]},"2281":{"position":[[115,8]]},"2294":{"position":[[511,8]]},"2324":{"position":[[98,8]]},"2432":{"position":[[264,8],[326,8],[398,8],[509,8],[1025,8]]},"2434":{"position":[[2371,8]]},"2456":{"position":[[367,8],[445,8],[573,8]]},"2554":{"position":[[309,8]]},"2558":{"position":[[36,8]]},"2606":{"position":[[181,8]]},"2772":{"position":[[703,9]]},"2788":{"position":[[32,8]]},"2792":{"position":[[32,8]]},"2796":{"position":[[32,8]]},"2962":{"position":[[31,8]]},"3349":{"position":[[1918,8]]},"3375":{"position":[[2746,8]]},"3377":{"position":[[922,8]]},"3379":{"position":[[939,8]]},"3381":{"position":[[2019,8]]},"3383":{"position":[[2248,8]]},"3385":{"position":[[2296,8]]},"3391":{"position":[[412,8]]},"3395":{"position":[[189,8]]},"3399":{"position":[[156,8]]},"3459":{"position":[[424,8]]},"3492":{"position":[[82,9],[153,8],[180,8],[278,8],[704,8],[972,8],[1078,8],[1222,8],[1432,9],[1647,8]]},"3508":{"position":[[115,8]]},"3524":{"position":[[511,8]]},"3545":{"position":[[77,8]]},"3549":{"position":[[293,8],[357,8]]},"3555":{"position":[[949,8],[1171,8],[1534,8],[2083,8]]},"3612":{"position":[[2570,8]]},"3627":{"position":[[264,8],[326,8],[398,8],[509,8],[1025,8]]}}}],["protobuf.j",{"_index":5201,"t":{"2258":{"position":[[656,11]]},"3492":{"position":[[656,11]]}}}],["protoc",{"_index":4494,"t":{"1141":{"position":[[84,7],[92,6],[110,6],[510,6],[535,6]]},"2222":{"position":[[84,7],[92,6],[110,6],[510,6],[535,6]]},"3463":{"position":[[84,7],[92,6],[110,6],[510,6],[535,6]]}}}],["protocol",{"_index":89,"t":{"2":{"position":[[1153,9]]},"12":{"position":[[1044,8],[1167,9]]},"16":{"position":[[31,9],[6293,8],[6626,8]]},"28":{"position":[[833,8],[924,8],[1047,8],[1815,8],[2154,8]]},"32":{"position":[[1154,9]]},"34":{"position":[[2619,8],[2777,8],[2845,8]]},"42":{"position":[[877,8],[2511,8]]},"50":{"position":[[313,9],[327,8],[592,9],[630,8],[669,9],[1269,8]]},"58":{"position":[[745,8]]},"60":{"position":[[476,8]]},"62":{"position":[[1222,8],[1296,8]]},"64":{"position":[[102,8]]},"68":{"position":[[529,8]]},"82":{"position":[[126,9]]},"86":{"position":[[69,8],[1347,8]]},"94":{"position":[[1528,8],[2113,8],[2319,8]]},"106":{"position":[[1607,8]]},"166":{"position":[[1799,8]]},"194":{"position":[[46,8],[1147,9]]},"196":{"position":[[110,8],[247,8],[383,9],[545,8]]},"202":{"position":[[248,8],[373,8],[517,10]]},"204":{"position":[[867,8],[967,8]]},"214":{"position":[[256,8],[424,8],[1582,8]]},"277":{"position":[[750,8]]},"279":{"position":[[1107,8]]},"283":{"position":[[1853,9]]},"323":{"position":[[68,8],[376,8],[454,8],[767,8]]},"349":{"position":[[55,9]]},"359":{"position":[[129,8]]},"373":{"position":[[150,8],[1140,9]]},"375":{"position":[[253,9],[395,8]]},"377":{"position":[[318,8],[369,8],[809,8],[2097,8],[2494,8]]},"379":{"position":[[853,8],[1383,8]]},"381":{"position":[[78,8],[228,8],[302,8],[541,8],[881,8]]},"389":{"position":[[111,9],[570,8]]},"399":{"position":[[1490,8]]},"403":{"position":[[131,8]]},"417":{"position":[[45,8],[105,8],[188,9]]},"453":{"position":[[1105,9],[1195,10]]},"475":{"position":[[3523,8]]},"539":{"position":[[7,8]]},"576":{"position":[[141,8]]},"754":{"position":[[33,8]]},"780":{"position":[[124,8]]},"816":{"position":[[108,8]]},"965":{"position":[[682,8],[832,8],[1863,9],[3084,8]]},"1065":{"position":[[1931,11],[2568,8],[2587,8],[2668,8]]},"1067":{"position":[[532,11],[865,8],[884,8],[961,8]]},"1069":{"position":[[444,11],[831,8],[850,8],[931,8]]},"1071":{"position":[[1803,11],[2155,8],[2174,8],[2255,8]]},"1073":{"position":[[1818,11],[2187,8],[2206,8],[2287,8]]},"1075":{"position":[[261,9]]},"1077":{"position":[[370,9],[415,8],[460,8]]},"1085":{"position":[[207,8]]},"1087":{"position":[[165,8]]},"1101":{"position":[[210,8],[699,8]]},"1137":{"position":[[836,8]]},"1139":{"position":[[911,8]]},"1153":{"position":[[124,8]]},"1162":{"position":[[280,8],[368,8],[1697,8],[2049,8],[2238,8],[2410,8]]},"1164":{"position":[[11,8],[2609,8]]},"1182":{"position":[[936,8]]},"1192":{"position":[[7,8]]},"1266":{"position":[[255,9]]},"1270":{"position":[[119,8],[187,8],[909,10]]},"1285":{"position":[[48,8],[280,8],[518,8],[1034,8],[1106,8]]},"1309":{"position":[[68,8],[376,8],[454,8],[767,8]]},"1337":{"position":[[55,9]]},"1353":{"position":[[803,8]]},"1379":{"position":[[129,8]]},"1401":{"position":[[45,8],[105,8],[184,9]]},"1499":{"position":[[25,8],[365,8],[1214,8],[1282,8],[1404,8],[1839,8]]},"1501":{"position":[[7,8],[301,8]]},"1577":{"position":[[1450,11],[1570,11]]},"1579":{"position":[[648,8],[685,8]]},"1583":{"position":[[136,8]]},"1759":{"position":[[402,8]]},"1773":{"position":[[33,8]]},"1831":{"position":[[124,8]]},"1865":{"position":[[283,8],[1049,9]]},"1881":{"position":[[961,8]]},"2030":{"position":[[704,8],[819,8]]},"2048":{"position":[[167,9]]},"2102":{"position":[[679,8],[825,8],[2035,9]]},"2146":{"position":[[2048,11],[2685,8],[2704,8],[2785,8]]},"2148":{"position":[[532,11],[865,8],[884,8],[961,8]]},"2150":{"position":[[444,11],[831,8],[850,8],[931,8]]},"2152":{"position":[[1803,11],[2155,8],[2174,8],[2255,8]]},"2154":{"position":[[1818,11],[2187,8],[2206,8],[2287,8]]},"2156":{"position":[[1907,11],[2266,8],[2285,8],[2362,8]]},"2160":{"position":[[520,9],[565,8],[610,8]]},"2168":{"position":[[207,8]]},"2170":{"position":[[165,8]]},"2218":{"position":[[836,8]]},"2220":{"position":[[911,8]]},"2238":{"position":[[268,8],[424,8],[527,8],[550,8],[570,8]]},"2250":{"position":[[936,8]]},"2252":{"position":[[7,8]]},"2256":{"position":[[264,8],[360,8]]},"2258":{"position":[[124,8],[420,8],[713,8],[794,8],[1260,8]]},"2281":{"position":[[124,8]]},"2290":{"position":[[261,9]]},"2294":{"position":[[119,8],[187,8],[589,8],[1082,8],[1261,8]]},"2296":{"position":[[248,8]]},"2361":{"position":[[533,8]]},"2383":{"position":[[93,8]]},"2432":{"position":[[48,8],[280,8],[518,8],[1034,8],[1106,8]]},"2434":{"position":[[2321,8]]},"2456":{"position":[[68,8],[376,8],[454,8],[767,8]]},"2484":{"position":[[55,9]]},"2502":{"position":[[484,9]]},"2506":{"position":[[204,9]]},"2546":{"position":[[854,8]]},"2558":{"position":[[45,9],[106,8],[185,9]]},"2630":{"position":[[25,8],[365,8],[1214,8],[1282,8],[1404,8],[1839,8]]},"2632":{"position":[[7,8],[301,8]]},"2654":{"position":[[132,10]]},"2770":{"position":[[1414,11],[1534,11]]},"2772":{"position":[[648,8],[685,8]]},"2778":{"position":[[136,8]]},"2948":{"position":[[385,8]]},"2962":{"position":[[40,8]]},"3022":{"position":[[124,8]]},"3056":{"position":[[283,8],[1049,9]]},"3072":{"position":[[1249,8]]},"3261":{"position":[[679,8],[825,8],[2035,9]]},"3271":{"position":[[670,8],[785,8]]},"3287":{"position":[[182,8]]},"3324":{"position":[[4887,9],[5118,9],[5833,9],[6053,9]]},"3375":{"position":[[2048,11],[2685,8],[2704,8],[2785,8]]},"3377":{"position":[[532,11],[865,8],[884,8],[961,8]]},"3379":{"position":[[491,11],[878,8],[897,8],[978,8]]},"3381":{"position":[[1606,11],[1958,8],[1977,8],[2058,8]]},"3383":{"position":[[1818,11],[2187,8],[2206,8],[2287,8]]},"3385":{"position":[[1880,11],[2239,8],[2258,8],[2335,8]]},"3389":{"position":[[520,9],[565,8],[610,8]]},"3397":{"position":[[207,8]]},"3399":{"position":[[165,8]]},"3459":{"position":[[836,8]]},"3461":{"position":[[911,8]]},"3482":{"position":[[296,9]]},"3484":{"position":[[899,8]]},"3486":{"position":[[248,8]]},"3490":{"position":[[425,8],[521,8],[584,8]]},"3492":{"position":[[124,8],[420,8],[713,8],[794,8],[1260,8]]},"3508":{"position":[[124,8]]},"3524":{"position":[[119,8],[187,8],[589,8],[1082,8],[1261,8]]},"3549":{"position":[[268,8],[424,8],[527,8],[550,8],[570,8]]},"3561":{"position":[[936,8]]},"3563":{"position":[[7,8]]},"3612":{"position":[[2520,8]]},"3627":{"position":[[48,8],[280,8],[518,8],[1034,8],[1106,8]]},"3629":{"position":[[253,8],[425,8],[1583,8]]},"3649":{"position":[[533,8]]},"3671":{"position":[[93,8]]}}}],["protocol/network",{"_index":2273,"t":{"182":{"position":[[1917,16]]}}}],["prototyp",{"_index":2966,"t":{"377":{"position":[[1607,10]]},"407":{"position":[[533,10]]},"479":{"position":[[2570,10],[2693,11]]},"700":{"position":[[673,11]]},"935":{"position":[[262,12]]},"1903":{"position":[[949,11]]},"2010":{"position":[[370,12]]},"3096":{"position":[[949,11]]},"3227":{"position":[[370,12]]}}}],["prove",{"_index":912,"t":{"28":{"position":[[1850,5]]},"46":{"position":[[3564,6]]},"252":{"position":[[530,6]]},"477":{"position":[[2979,6]]},"678":{"position":[[180,6]]},"1729":{"position":[[180,6]]},"2918":{"position":[[180,6]]}}}],["proven",{"_index":2974,"t":{"379":{"position":[[572,7]]}}}],["provid",{"_index":236,"t":{"4":{"position":[[350,7]]},"8":{"position":[[671,8]]},"14":{"position":[[543,8]]},"16":{"position":[[4573,7],[4994,8],[6085,8]]},"26":{"position":[[629,7],[940,8]]},"28":{"position":[[975,8],[2248,8]]},"30":{"position":[[1221,7],[1284,8]]},"36":{"position":[[206,9]]},"40":{"position":[[255,7]]},"42":{"position":[[61,7],[459,8],[886,8],[2118,7]]},"46":{"position":[[1243,8]]},"58":{"position":[[603,8]]},"60":{"position":[[621,8]]},"64":{"position":[[481,7],[684,7]]},"68":{"position":[[190,8]]},"74":{"position":[[306,7],[823,8],[1655,7]]},"76":{"position":[[335,7]]},"86":{"position":[[287,8]]},"88":{"position":[[140,8],[807,7]]},"94":{"position":[[437,7],[614,8],[1295,8]]},"108":{"position":[[325,7],[571,8]]},"120":{"position":[[564,8]]},"128":{"position":[[3071,7]]},"148":{"position":[[867,8]]},"158":{"position":[[125,9]]},"166":{"position":[[1826,9]]},"174":{"position":[[106,7]]},"178":{"position":[[264,7],[1761,7],[2260,8],[2409,7],[2558,7]]},"180":{"position":[[990,7]]},"182":{"position":[[254,7],[413,7]]},"184":{"position":[[313,9]]},"186":{"position":[[220,8]]},"188":{"position":[[166,7],[994,9]]},"190":{"position":[[176,7]]},"198":{"position":[[1140,7]]},"210":{"position":[[195,8]]},"222":{"position":[[11,8]]},"226":{"position":[[472,8],[601,8]]},"238":{"position":[[69,7]]},"246":{"position":[[464,7]]},"250":{"position":[[1573,7],[1776,7],[2060,9]]},"252":{"position":[[4,7],[1501,7]]},"254":{"position":[[1804,7],[1855,9]]},"256":{"position":[[5,9],[578,8],[849,7],[1058,10],[1186,9],[1534,7]]},"258":{"position":[[145,8]]},"260":{"position":[[195,8],[460,7],[590,9],[704,10]]},"262":{"position":[[30,7],[93,7],[203,7],[657,8],[944,10],[1146,9],[1244,8],[1603,7],[1680,8],[2019,8],[2893,8]]},"264":{"position":[[577,8],[1415,7]]},"266":{"position":[[102,9],[167,8]]},"268":{"position":[[24,8],[113,7]]},"270":{"position":[[323,9]]},"279":{"position":[[1178,8]]},"293":{"position":[[206,10],[396,9]]},"301":{"position":[[1300,9]]},"303":{"position":[[205,7]]},"321":{"position":[[49,7],[92,9]]},"341":{"position":[[135,7]]},"345":{"position":[[96,8]]},"351":{"position":[[637,8],[733,8]]},"361":{"position":[[1417,8]]},"371":{"position":[[871,8]]},"377":{"position":[[382,8]]},"379":{"position":[[1867,7],[2259,7]]},"383":{"position":[[1788,9]]},"385":{"position":[[1064,7],[1325,8]]},"391":{"position":[[659,8],[1165,7]]},"393":{"position":[[391,7]]},"397":{"position":[[546,7]]},"399":{"position":[[2039,7]]},"403":{"position":[[213,8]]},"419":{"position":[[165,8]]},"429":{"position":[[262,8]]},"453":{"position":[[282,7]]},"455":{"position":[[150,7]]},"467":{"position":[[1000,8],[1150,8]]},"469":{"position":[[647,7]]},"473":{"position":[[3591,7]]},"475":{"position":[[1373,8]]},"477":{"position":[[534,8],[1099,7],[1218,8],[3004,7],[6326,7]]},"479":{"position":[[2330,8]]},"481":{"position":[[1019,8],[5225,7]]},"489":{"position":[[959,8]]},"505":{"position":[[611,8],[1078,8]]},"541":{"position":[[187,7],[340,7]]},"548":{"position":[[167,7]]},"550":{"position":[[199,7]]},"570":{"position":[[56,7]]},"576":{"position":[[104,8]]},"600":{"position":[[28,8],[924,7]]},"618":{"position":[[30,8]]},"648":{"position":[[508,8]]},"652":{"position":[[178,8]]},"666":{"position":[[404,8]]},"678":{"position":[[15,8]]},"696":{"position":[[147,7],[318,7],[828,7]]},"704":{"position":[[254,8],[312,7]]},"734":{"position":[[192,8]]},"742":{"position":[[134,7],[180,8],[235,8],[572,8],[825,10]]},"752":{"position":[[92,8]]},"756":{"position":[[142,9]]},"758":{"position":[[326,7],[468,9]]},"762":{"position":[[253,8]]},"764":{"position":[[277,9],[769,7]]},"776":{"position":[[102,9],[226,9]]},"808":{"position":[[464,7]]},"866":{"position":[[301,7]]},"937":{"position":[[108,7]]},"941":{"position":[[93,9]]},"961":{"position":[[295,8]]},"965":{"position":[[3468,9]]},"987":{"position":[[108,8],[591,7]]},"989":{"position":[[578,9],[635,9],[842,8],[1027,10]]},"991":{"position":[[538,10]]},"1029":{"position":[[317,8]]},"1049":{"position":[[402,9],[781,8]]},"1051":{"position":[[8,7],[207,8],[295,8]]},"1053":{"position":[[40,7],[255,8],[350,8]]},"1061":{"position":[[252,7]]},"1065":{"position":[[2799,8],[2914,8],[3019,8],[4246,9]]},"1071":{"position":[[441,9],[2659,8]]},"1117":{"position":[[508,7]]},"1125":{"position":[[289,7]]},"1129":{"position":[[691,7]]},"1162":{"position":[[809,7],[953,7],[999,7],[1042,7],[1082,7],[1128,7],[1167,7],[1203,7],[1243,7],[1280,7],[1323,7]]},"1285":{"position":[[653,7]]},"1307":{"position":[[49,7],[92,9]]},"1317":{"position":[[43,8]]},"1319":{"position":[[83,8],[272,8],[737,7]]},"1329":{"position":[[135,7]]},"1333":{"position":[[96,8]]},"1339":{"position":[[637,8],[733,8]]},"1341":{"position":[[85,8]]},"1355":{"position":[[46,7]]},"1367":{"position":[[611,8],[1078,8]]},"1381":{"position":[[1422,8]]},"1391":{"position":[[871,8]]},"1403":{"position":[[101,8]]},"1413":{"position":[[251,8]]},"1415":{"position":[[37,8]]},"1453":{"position":[[283,7]]},"1455":{"position":[[159,7]]},"1469":{"position":[[1026,8]]},"1477":{"position":[[411,7]]},"1479":{"position":[[156,7],[384,7]]},"1493":{"position":[[141,7]]},"1495":{"position":[[3635,8],[5783,7],[7597,7]]},"1511":{"position":[[562,7]]},"1521":{"position":[[663,8]]},"1543":{"position":[[430,10],[1404,10]]},"1579":{"position":[[514,8],[580,8]]},"1619":{"position":[[28,8],[1206,7]]},"1625":{"position":[[101,9],[283,8],[348,9],[558,8]]},"1627":{"position":[[29,8]]},"1629":{"position":[[751,8],[1488,7]]},"1631":{"position":[[46,8],[108,8],[161,10],[242,9],[423,8]]},"1633":{"position":[[110,9],[175,8]]},"1635":{"position":[[4,8],[46,8],[466,7]]},"1637":{"position":[[45,8]]},"1639":{"position":[[390,8]]},"1650":{"position":[[145,8],[173,8],[193,8]]},"1652":{"position":[[316,8]]},"1654":{"position":[[359,8]]},"1656":{"position":[[201,9],[251,9],[323,8],[1161,8],[1196,9]]},"1660":{"position":[[214,9],[293,8]]},"1666":{"position":[[71,8],[220,8],[1964,8]]},"1670":{"position":[[179,9],[202,9],[384,8],[526,8],[602,8],[667,8]]},"1680":{"position":[[449,7]]},"1682":{"position":[[410,8]]},"1694":{"position":[[508,8]]},"1699":{"position":[[178,8]]},"1702":{"position":[[852,8]]},"1710":{"position":[[404,8]]},"1729":{"position":[[15,8]]},"1741":{"position":[[194,7],[448,7],[489,7],[579,8]]},"1761":{"position":[[134,7],[180,8],[235,8],[572,8],[825,10]]},"1771":{"position":[[92,8]]},"1777":{"position":[[326,7]]},"1781":{"position":[[253,8]]},"1783":{"position":[[277,9],[769,7]]},"1795":{"position":[[102,9],[226,9]]},"1797":{"position":[[335,8]]},"1813":{"position":[[317,8]]},"1875":{"position":[[147,7],[318,7],[521,7]]},"1881":{"position":[[192,8]]},"1887":{"position":[[576,7]]},"1889":{"position":[[249,7]]},"1895":{"position":[[931,7]]},"1897":{"position":[[686,7]]},"1987":{"position":[[135,8]]},"2012":{"position":[[108,7]]},"2016":{"position":[[93,9]]},"2046":{"position":[[108,8],[591,7]]},"2048":{"position":[[619,8]]},"2050":{"position":[[578,9],[635,9],[842,8],[1027,10]]},"2052":{"position":[[538,10]]},"2062":{"position":[[340,7]]},"2086":{"position":[[102,8]]},"2098":{"position":[[354,8]]},"2102":{"position":[[2630,8],[2768,8],[3606,8],[4612,9]]},"2116":{"position":[[16,8]]},"2122":{"position":[[447,7]]},"2126":{"position":[[97,8]]},"2142":{"position":[[252,7]]},"2146":{"position":[[987,7],[2916,8],[3031,8],[3136,8],[4363,9]]},"2152":{"position":[[441,9],[2659,8]]},"2198":{"position":[[474,7]]},"2206":{"position":[[289,7]]},"2210":{"position":[[738,7]]},"2230":{"position":[[402,9],[781,8]]},"2232":{"position":[[8,7],[207,8],[295,8]]},"2234":{"position":[[8,7],[223,8],[318,8]]},"2351":{"position":[[1420,9]]},"2357":{"position":[[15,8]]},"2361":{"position":[[542,8]]},"2367":{"position":[[1522,9]]},"2369":{"position":[[68,7],[1745,7]]},"2377":{"position":[[7,8]]},"2379":{"position":[[4,8]]},"2432":{"position":[[653,7]]},"2454":{"position":[[49,7],[92,9]]},"2464":{"position":[[43,8]]},"2466":{"position":[[83,8],[272,8],[737,7]]},"2476":{"position":[[135,7]]},"2480":{"position":[[96,8]]},"2486":{"position":[[637,8],[733,8]]},"2488":{"position":[[85,8]]},"2502":{"position":[[52,7],[423,8]]},"2504":{"position":[[28,8],[402,8],[601,8]]},"2506":{"position":[[303,7],[653,9],[826,8],[1222,8],[1368,8]]},"2518":{"position":[[642,8],[1128,8]]},"2530":{"position":[[2389,8],[2619,8]]},"2532":{"position":[[1554,8]]},"2542":{"position":[[885,8],[1416,8]]},"2548":{"position":[[46,7]]},"2560":{"position":[[102,8]]},"2564":{"position":[[197,8]]},"2572":{"position":[[41,8]]},"2584":{"position":[[73,7]]},"2594":{"position":[[287,7]]},"2596":{"position":[[164,7]]},"2610":{"position":[[568,8],[1533,7]]},"2642":{"position":[[562,7]]},"2662":{"position":[[3668,8],[5842,7],[7683,7]]},"2670":{"position":[[411,7]]},"2672":{"position":[[156,7],[384,7]]},"2686":{"position":[[141,7]]},"2690":{"position":[[663,8]]},"2712":{"position":[[430,10],[1404,10]]},"2744":{"position":[[1035,7]]},"2750":{"position":[[191,8],[263,8]]},"2772":{"position":[[514,8],[580,8]]},"2774":{"position":[[102,8]]},"2780":{"position":[[353,8]]},"2806":{"position":[[28,8],[1206,7]]},"2818":{"position":[[508,8]]},"2823":{"position":[[178,8]]},"2826":{"position":[[816,8]]},"2834":{"position":[[449,7]]},"2836":{"position":[[410,8]]},"2842":{"position":[[101,9],[283,8],[348,9],[558,8]]},"2844":{"position":[[29,8]]},"2846":{"position":[[751,8],[1488,7]]},"2848":{"position":[[46,8],[108,8],[161,10],[242,9],[423,8]]},"2850":{"position":[[110,9],[175,8]]},"2852":{"position":[[4,8],[46,8],[466,7]]},"2854":{"position":[[45,8]]},"2856":{"position":[[390,8]]},"2862":{"position":[[606,9]]},"2867":{"position":[[145,8],[173,8],[193,8]]},"2873":{"position":[[727,9],[777,9],[1356,8],[1395,9]]},"2877":{"position":[[669,9]]},"2883":{"position":[[71,8],[220,8],[2243,8]]},"2889":{"position":[[143,9],[326,9],[440,9],[707,9],[956,9]]},"2899":{"position":[[404,8]]},"2918":{"position":[[15,8]]},"2930":{"position":[[194,7],[448,7],[489,7],[579,8]]},"2950":{"position":[[167,7],[615,8]]},"2960":{"position":[[20,8]]},"2966":{"position":[[55,9],[405,7]]},"2972":{"position":[[287,9],[1080,7],[1129,7],[1354,7]]},"2984":{"position":[[102,9],[226,9]]},"2986":{"position":[[301,8]]},"3002":{"position":[[317,8]]},"3066":{"position":[[358,8]]},"3068":{"position":[[596,9]]},"3072":{"position":[[200,8],[473,8]]},"3078":{"position":[[576,7]]},"3080":{"position":[[249,7]]},"3088":{"position":[[931,7]]},"3090":{"position":[[686,7]]},"3140":{"position":[[340,7]]},"3202":{"position":[[108,8]]},"3231":{"position":[[108,7]]},"3235":{"position":[[93,9]]},"3253":{"position":[[102,8]]},"3257":{"position":[[354,8]]},"3261":{"position":[[2630,8],[2768,8],[3606,8],[4612,9]]},"3287":{"position":[[691,8]]},"3289":{"position":[[578,9],[635,9],[842,8],[1027,10]]},"3291":{"position":[[538,10]]},"3324":{"position":[[1628,8]]},"3337":{"position":[[707,7]]},"3341":{"position":[[97,8]]},"3347":{"position":[[182,9],[690,8],[1313,7]]},"3371":{"position":[[252,7]]},"3375":{"position":[[987,7],[2916,8],[3031,8],[3136,8],[4392,9]]},"3381":{"position":[[476,9],[2462,8]]},"3417":{"position":[[402,9],[781,8]]},"3419":{"position":[[8,7],[207,8],[295,8]]},"3421":{"position":[[8,7],[223,8],[318,8]]},"3439":{"position":[[408,7]]},"3447":{"position":[[289,7]]},"3451":{"position":[[798,7]]},"3627":{"position":[[653,7]]},"3639":{"position":[[1440,9]]},"3645":{"position":[[15,8]]},"3649":{"position":[[542,8]]},"3655":{"position":[[1690,9]]},"3657":{"position":[[68,7],[1745,7]]},"3665":{"position":[[7,8]]},"3667":{"position":[[4,8]]}}}],["provider_token",{"_index":4947,"t":{"1652":{"position":[[267,15]]},"1654":{"position":[[325,15]]},"1656":{"position":[[280,15]]}}}],["providertoken",{"_index":4957,"t":{"1654":{"position":[[341,14]]},"1656":{"position":[[305,14]]},"1660":{"position":[[275,14]]}}}],["provinc",{"_index":1590,"t":{"98":{"position":[[1761,8]]}}}],["proxi",{"_index":536,"t":{"14":{"position":[[278,8],[366,5],[1705,5]]},"18":{"position":[[3334,7],[3457,5]]},"28":{"position":[[1278,9]]},"60":{"position":[[577,6]]},"62":{"position":[[1011,5]]},"68":{"position":[[1813,5],[1996,5]]},"76":{"position":[[152,5]]},"84":{"position":[[278,5]]},"132":{"position":[[1392,5],[1677,5],[1712,8],[1775,5]]},"134":{"position":[[181,5],[2004,5]]},"136":{"position":[[174,5],[1047,5],[1111,5],[1460,5],[1524,5],[1826,5],[1892,5],[2184,5]]},"138":{"position":[[156,8],[769,6],[924,5],[1108,5],[1391,5],[1430,5],[1514,5]]},"142":{"position":[[227,5],[648,6]]},"160":{"position":[[491,5],[540,7],[576,5],[817,5]]},"162":{"position":[[78,5],[240,5],[534,7]]},"168":{"position":[[781,5],[953,5]]},"174":{"position":[[408,5],[493,6]]},"180":{"position":[[657,5],[693,5],[951,5],[1467,5],[1515,5]]},"184":{"position":[[91,5],[168,5]]},"190":{"position":[[100,5],[142,5]]},"212":{"position":[[1038,5]]},"246":{"position":[[2427,6],[2606,5]]},"277":{"position":[[619,5]]},"279":{"position":[[162,5]]},"281":{"position":[[330,5]]},"287":{"position":[[276,5],[331,5],[678,5],[1593,5]]},"291":{"position":[[116,5]]},"329":{"position":[[66,5]]},"339":{"position":[[656,5],[1073,5]]},"341":{"position":[[486,5],[545,5]]},"345":{"position":[[0,5],[85,6]]},"347":{"position":[[1539,5]]},"349":{"position":[[124,6]]},"359":{"position":[[993,7],[1095,8],[1311,5]]},"375":{"position":[[211,6],[444,5]]},"379":{"position":[[160,8],[242,7],[2690,5]]},"389":{"position":[[1212,8]]},"421":{"position":[[172,5]]},"453":{"position":[[936,8]]},"475":{"position":[[595,5],[641,7]]},"483":{"position":[[399,5]]},"497":{"position":[[508,5]]},"509":{"position":[[224,5]]},"554":{"position":[[294,5]]},"560":{"position":[[16,5],[81,6],[556,7],[591,6],[609,5],[921,6]]},"626":{"position":[[76,6]]},"680":{"position":[[161,5]]},"694":{"position":[[837,5]]},"696":{"position":[[787,5]]},"700":{"position":[[343,6]]},"704":{"position":[[293,7]]},"722":{"position":[[235,5]]},"724":{"position":[[62,6],[82,5]]},"726":{"position":[[58,6],[78,5]]},"728":{"position":[[63,5],[83,5],[173,5]]},"730":{"position":[[59,5],[79,5],[167,5]]},"760":{"position":[[200,5]]},"780":{"position":[[300,5]]},"808":{"position":[[149,5]]},"884":{"position":[[330,5]]},"892":{"position":[[1146,5]]},"939":{"position":[[338,5]]},"949":{"position":[[675,5]]},"951":{"position":[[180,5]]},"955":{"position":[[0,7],[206,5]]},"999":{"position":[[61,5],[142,5],[323,5],[504,5]]},"1057":{"position":[[5,5]]},"1059":{"position":[[4,5]]},"1061":{"position":[[37,5],[68,7],[308,8],[742,5]]},"1063":{"position":[[83,5],[242,8],[302,5],[349,8],[412,7]]},"1065":{"position":[[207,7],[385,7],[1067,7],[2193,5],[2362,5],[3133,5],[3431,5],[5645,5]]},"1069":{"position":[[193,7],[1280,5]]},"1071":{"position":[[223,7],[338,5],[353,5],[599,5],[698,5],[796,5],[834,5],[947,5],[970,5],[1008,5],[1176,5],[1234,5],[2823,5]]},"1073":{"position":[[201,7],[705,6],[720,5],[888,5],[996,5],[1084,5],[2577,5]]},"1079":{"position":[[20,5],[264,5],[303,5],[371,5],[447,5],[615,8],[713,6],[829,6],[948,6],[1060,5],[1277,5]]},"1081":{"position":[[46,5]]},"1083":{"position":[[183,5]]},"1085":{"position":[[155,5],[221,5],[276,5],[319,5],[355,5],[399,5]]},"1087":{"position":[[85,5],[559,5],[784,5],[1091,5],[1144,5],[1225,5]]},"1089":{"position":[[43,5],[105,5],[154,5],[182,6],[207,6],[277,5],[310,5],[448,5],[586,5],[622,5],[696,5],[779,5],[802,5],[938,5],[977,7]]},"1091":{"position":[[159,5]]},"1093":{"position":[[20,5],[73,9],[114,5],[134,5],[287,10],[996,5],[1043,5],[1056,9],[1147,5],[1306,5],[1431,5],[1519,6],[1612,6]]},"1095":{"position":[[33,7],[92,5],[152,6],[206,10],[334,5],[460,6],[514,10]]},"1097":{"position":[[22,5],[335,10],[690,5],[823,5],[1047,7],[1066,6]]},"1099":{"position":[[122,10],[395,5],[467,5],[930,7],[949,6]]},"1149":{"position":[[739,5],[977,8]]},"1249":{"position":[[187,5]]},"1315":{"position":[[66,5]]},"1327":{"position":[[656,5],[1073,5]]},"1329":{"position":[[486,5],[545,5]]},"1333":{"position":[[0,5],[85,6]]},"1335":{"position":[[1543,5]]},"1337":{"position":[[124,6]]},"1341":{"position":[[172,5]]},"1353":{"position":[[676,5]]},"1359":{"position":[[508,5]]},"1371":{"position":[[224,5]]},"1379":{"position":[[993,7],[1095,8],[1311,5]]},"1405":{"position":[[185,5]]},"1453":{"position":[[937,8]]},"1473":{"position":[[81,5]]},"1477":{"position":[[272,5],[397,5]]},"1479":{"position":[[148,5],[184,5],[619,5]]},"1489":{"position":[[88,5]]},"1491":{"position":[[287,5]]},"1493":{"position":[[245,5]]},"1513":{"position":[[189,5]]},"1521":{"position":[[647,5]]},"1523":{"position":[[614,5],[764,5]]},"1577":{"position":[[1824,5]]},"1595":{"position":[[76,6]]},"1631":{"position":[[133,5]]},"1639":{"position":[[1059,5]]},"1668":{"position":[[622,5]]},"1731":{"position":[[161,5]]},"1741":{"position":[[241,5],[400,5],[1041,6],[1110,5],[1134,5],[1227,6],[1278,5],[1364,6],[1434,5],[2435,6],[2470,5]]},"1743":{"position":[[388,7],[739,5],[955,5],[993,5],[1367,6],[1433,5],[1457,5],[1528,5],[1952,6],[1987,5],[2099,6],[2136,5]]},"1745":{"position":[[945,6],[994,5],[1113,5],[1159,5]]},"1747":{"position":[[267,5],[293,5],[1098,6],[1133,5]]},"1779":{"position":[[239,5]]},"1831":{"position":[[300,5]]},"1859":{"position":[[149,5]]},"1903":{"position":[[677,5]]},"1905":{"position":[[650,5]]},"1925":{"position":[[62,6],[82,5]]},"1927":{"position":[[58,6],[78,5]]},"1929":{"position":[[66,6],[86,5]]},"1931":{"position":[[63,5],[83,5],[173,5]]},"1933":{"position":[[59,5],[79,5],[167,5]]},"1935":{"position":[[67,5],[87,5],[179,5]]},"1951":{"position":[[330,5]]},"1959":{"position":[[1146,5]]},"2014":{"position":[[338,5]]},"2076":{"position":[[675,5]]},"2078":{"position":[[180,5]]},"2082":{"position":[[0,7],[206,5]]},"2106":{"position":[[61,5],[142,5],[323,5],[504,5]]},"2138":{"position":[[5,5]]},"2140":{"position":[[4,5]]},"2142":{"position":[[37,5],[68,7],[308,8],[742,5]]},"2144":{"position":[[83,5],[242,8],[302,5],[349,8],[412,7]]},"2146":{"position":[[207,7],[385,7],[1038,5],[1184,7],[2310,5],[2479,5],[3250,5],[3548,5],[5768,5]]},"2150":{"position":[[193,7],[1280,5]]},"2152":{"position":[[223,7],[338,5],[353,5],[599,5],[698,5],[796,5],[834,5],[947,5],[970,5],[1008,5],[1176,5],[1234,5],[2789,5]]},"2154":{"position":[[201,7],[705,6],[720,5],[888,5],[996,5],[1084,5],[2577,5]]},"2156":{"position":[[450,5],[475,5],[651,5],[769,5],[797,5],[1353,5]]},"2158":{"position":[[328,5]]},"2160":{"position":[[808,5]]},"2162":{"position":[[20,5],[264,5],[303,5],[371,5],[447,5],[615,8],[713,6],[829,6],[948,6],[1060,5],[1277,5]]},"2164":{"position":[[46,5]]},"2166":{"position":[[183,5]]},"2168":{"position":[[155,5],[221,5],[276,5],[319,5],[355,5],[399,5]]},"2170":{"position":[[85,5],[559,5],[784,5],[1091,5],[1144,5],[1225,5]]},"2172":{"position":[[17,5],[79,5],[128,5],[156,6],[181,6],[251,5],[284,5],[422,5],[560,5],[596,5],[670,5],[753,5],[776,5],[912,5],[951,7]]},"2174":{"position":[[159,5]]},"2176":{"position":[[20,5],[73,9],[114,5],[134,5],[287,10],[996,5],[1043,5],[1056,9],[1147,5],[1306,5],[1431,5],[1519,6],[1612,6]]},"2178":{"position":[[33,7],[92,5],[152,6],[206,10],[334,5],[460,6],[514,10]]},"2180":{"position":[[35,7],[372,10],[727,5],[860,5],[1005,5],[1263,7],[1282,6]]},"2182":{"position":[[122,10],[395,5],[467,5],[930,7],[949,6]]},"2277":{"position":[[739,5],[977,8]]},"2377":{"position":[[226,5],[258,7]]},"2400":{"position":[[187,5]]},"2434":{"position":[[2152,5],[2186,5],[2196,5]]},"2462":{"position":[[66,5]]},"2474":{"position":[[656,5],[1073,5]]},"2476":{"position":[[486,5],[545,5]]},"2480":{"position":[[0,5],[85,6]]},"2482":{"position":[[1684,5],[1700,5]]},"2484":{"position":[[124,6]]},"2488":{"position":[[172,5]]},"2506":{"position":[[1255,5]]},"2510":{"position":[[508,5]]},"2522":{"position":[[230,5]]},"2530":{"position":[[2404,5],[2547,7],[2591,7],[2613,5],[2718,7]]},"2546":{"position":[[721,6]]},"2562":{"position":[[193,5]]},"2594":{"position":[[984,8]]},"2644":{"position":[[189,5]]},"2658":{"position":[[98,5]]},"2666":{"position":[[81,5]]},"2670":{"position":[[272,5],[397,5]]},"2672":{"position":[[148,5],[184,5],[619,5]]},"2682":{"position":[[88,5]]},"2684":{"position":[[287,5]]},"2686":{"position":[[245,5]]},"2690":{"position":[[647,5]]},"2692":{"position":[[614,5],[764,5]]},"2722":{"position":[[707,5],[1264,5],[1313,5]]},"2770":{"position":[[1788,5]]},"2790":{"position":[[76,6]]},"2848":{"position":[[133,5]]},"2856":{"position":[[1059,5]]},"2887":{"position":[[622,5]]},"2920":{"position":[[161,5]]},"2930":{"position":[[241,5],[400,5],[1041,6],[1110,5],[1134,5],[1227,6],[1278,5],[1364,6],[1434,5],[2435,6],[2470,5]]},"2932":{"position":[[388,7],[739,5],[955,5],[993,5],[1367,6],[1433,5],[1457,5],[1528,5],[1952,6],[1987,5],[2099,6],[2136,5]]},"2934":{"position":[[945,6],[994,5],[1113,5],[1159,5]]},"2936":{"position":[[267,5],[293,5],[1098,6],[1133,5]]},"2968":{"position":[[253,5]]},"3022":{"position":[[300,5]]},"3050":{"position":[[149,5]]},"3096":{"position":[[677,5]]},"3098":{"position":[[650,5]]},"3118":{"position":[[62,6],[82,5]]},"3120":{"position":[[58,6],[78,5]]},"3122":{"position":[[66,6],[86,5]]},"3124":{"position":[[76,6]]},"3126":{"position":[[63,5],[83,5],[173,5]]},"3128":{"position":[[59,5],[79,5],[167,5]]},"3130":{"position":[[67,5],[87,5],[179,5]]},"3132":{"position":[[77,5],[97,5],[194,5]]},"3164":{"position":[[330,5]]},"3172":{"position":[[1146,5]]},"3198":{"position":[[148,6],[344,5],[374,5],[429,5],[1078,5],[1096,5],[1247,5]]},"3233":{"position":[[338,5]]},"3243":{"position":[[675,5]]},"3245":{"position":[[180,5]]},"3249":{"position":[[0,7],[206,5]]},"3307":{"position":[[61,5],[142,5],[323,5],[504,5]]},"3324":{"position":[[4940,5],[5006,5],[5190,5],[5249,5],[5436,5],[5528,5],[5666,5],[5754,5]]},"3345":{"position":[[6,5],[301,5],[599,5],[665,5],[795,5],[1169,5]]},"3347":{"position":[[1516,5],[1751,7],[2373,5],[2816,5],[2933,5],[3156,5],[3199,5],[3301,5]]},"3349":{"position":[[122,6],[338,5],[393,6],[448,5],[1204,5],[1246,5],[2272,5],[4430,5]]},"3351":{"position":[[580,5]]},"3353":{"position":[[9,5],[95,5],[173,7],[254,10]]},"3355":{"position":[[32,5]]},"3367":{"position":[[5,5]]},"3369":{"position":[[9,5]]},"3371":{"position":[[37,5],[68,7],[308,8],[742,5],[932,5],[1343,5]]},"3373":{"position":[[83,5],[242,8],[302,5],[349,8],[412,7]]},"3375":{"position":[[207,7],[385,7],[1038,5],[1184,7],[2310,5],[2479,5],[3250,5],[3548,5],[5797,5]]},"3379":{"position":[[193,7],[1327,5]]},"3381":{"position":[[223,7],[339,5],[354,5],[520,5],[613,5],[660,5],[773,5],[811,5],[979,5],[1037,5],[2592,5]]},"3383":{"position":[[201,7],[705,6],[720,5],[888,5],[996,5],[1084,5],[2577,5]]},"3385":{"position":[[423,5],[448,5],[624,5],[742,5],[770,5],[1326,5]]},"3387":{"position":[[328,5],[494,5]]},"3389":{"position":[[808,5],[1028,5]]},"3391":{"position":[[20,5],[264,5],[303,5],[371,5],[447,5],[615,8],[713,6],[829,6],[948,6],[1060,5],[1277,5]]},"3393":{"position":[[46,5],[558,5]]},"3395":{"position":[[183,5]]},"3397":{"position":[[155,5],[221,5],[276,5],[319,5],[355,5],[399,5]]},"3399":{"position":[[85,5],[559,5],[784,5],[1091,5],[1144,5],[1225,5]]},"3401":{"position":[[17,5],[79,5],[128,5],[156,6],[181,6],[251,5],[284,5],[422,5],[560,5],[596,5],[670,5],[753,5],[776,5],[912,5],[951,7]]},"3403":{"position":[[159,5]]},"3405":{"position":[[20,5],[73,9],[114,5],[134,5],[287,10],[996,5],[1043,5],[1056,9],[1147,5],[1306,5],[1431,5],[1519,6],[1626,5],[1683,5],[1824,6],[2356,5]]},"3407":{"position":[[33,7],[92,5],[152,6],[206,10],[334,5],[460,6],[514,10]]},"3409":{"position":[[35,7],[372,10],[727,5],[860,5],[1005,5],[1263,7],[1282,6]]},"3411":{"position":[[122,10],[395,5],[467,5],[930,7],[949,6]]},"3427":{"position":[[663,5]]},"3476":{"position":[[555,5]]},"3504":{"position":[[739,5],[977,8]]},"3518":{"position":[[535,5]]},"3567":{"position":[[187,5]]},"3612":{"position":[[2351,5],[2385,5],[2395,5]]},"3665":{"position":[[226,5],[258,7]]}}}],["proxy.proto",{"_index":4339,"t":{"1079":{"position":[[242,12]]},"2162":{"position":[[242,12]]},"2722":{"position":[[1366,11]]},"3349":{"position":[[1694,11]]},"3391":{"position":[[242,12]]}}}],["proxy/load",{"_index":4064,"t":{"926":{"position":[[742,10]]},"2001":{"position":[[742,10]]},"3218":{"position":[[832,10]]}}}],["proxy_add_x_forwarded_for",{"_index":2033,"t":{"134":{"position":[[990,27],[1415,27]]},"287":{"position":[[955,27],[1292,27]]}}}],["proxy_binary_encod",{"_index":3614,"t":{"560":{"position":[[794,24]]},"1087":{"position":[[835,24]]},"2170":{"position":[[835,24]]},"3399":{"position":[[835,24]]}}}],["proxy_buff",{"_index":2037,"t":{"134":{"position":[[1166,15]]},"1001":{"position":[[1012,15]]},"1003":{"position":[[586,15]]},"2108":{"position":[[1012,15]]},"2110":{"position":[[586,15]]},"3309":{"position":[[1012,15]]},"3311":{"position":[[586,15]]}}}],["proxy_cache_bypass",{"_index":2043,"t":{"134":{"position":[[1487,18]]},"287":{"position":[[1364,18]]}}}],["proxy_channel_state_endpoint",{"_index":5461,"t":{"2722":{"position":[[1004,30],[1072,31]]}}}],["proxy_connect_endpoint",{"_index":2002,"t":{"132":{"position":[[332,25]]},"160":{"position":[[314,25]]},"285":{"position":[[281,25]]},"1065":{"position":[[60,25],[218,22],[1800,22]]},"1079":{"position":[[738,25]]},"2146":{"position":[[60,25],[218,22],[1917,22]]},"2162":{"position":[[738,25]]},"3375":{"position":[[60,25],[218,22],[1917,22]]},"3391":{"position":[[738,25]]}}}],["proxy_connect_timeout",{"_index":3600,"t":{"548":{"position":[[85,24],[133,24]]},"570":{"position":[[1511,21]]},"1065":{"position":[[130,24],[4633,21]]},"1079":{"position":[[790,24]]},"2146":{"position":[[130,24],[4750,21]]},"2162":{"position":[[790,24]]},"3375":{"position":[[130,24],[4779,21]]},"3391":{"position":[[790,24]]}}}],["proxy_extra_http_head",{"_index":3612,"t":{"560":{"position":[[339,24],[417,25],[463,24]]}}}],["proxy_grpc_cert_fil",{"_index":4342,"t":{"1081":{"position":[[62,21]]},"2164":{"position":[[62,21]]},"3393":{"position":[[62,21]]}}}],["proxy_grpc_compress",{"_index":5719,"t":{"3393":{"position":[[440,23]]}}}],["proxy_grpc_credentials_key",{"_index":4343,"t":{"1081":{"position":[[224,27],[412,27]]},"2164":{"position":[[224,27],[412,27]]},"3393":{"position":[[224,27],[412,27]]}}}],["proxy_grpc_credentials_valu",{"_index":4344,"t":{"1081":{"position":[[327,29]]},"2164":{"position":[[327,29]]},"3393":{"position":[[327,29]]}}}],["proxy_grpc_metadata",{"_index":4287,"t":{"1063":{"position":[[159,19]]},"2144":{"position":[[159,19]]},"3373":{"position":[[159,19]]}}}],["proxy_http_head",{"_index":2008,"t":{"132":{"position":[[564,21]]},"160":{"position":[[376,21]]},"285":{"position":[[351,21]]},"560":{"position":[[168,21]]},"1061":{"position":[[348,21]]},"2142":{"position":[[348,21]]},"3371":{"position":[[348,21],[1386,18]]}}}],["proxy_http_vers",{"_index":2029,"t":{"134":{"position":[[863,18],[1142,18]]},"287":{"position":[[828,18],[1086,18]]},"1001":{"position":[[1079,18]]},"1003":{"position":[[763,18]]},"2108":{"position":[[1079,18]]},"2110":{"position":[[763,18]]},"3309":{"position":[[1079,18]]},"3311":{"position":[[763,18]]}}}],["proxy_include_connection_meta",{"_index":4323,"t":{"1067":{"position":[[1161,32]]},"1069":{"position":[[1361,32]]},"1071":{"position":[[2519,32]]},"1073":{"position":[[2658,32]]},"1779":{"position":[[299,29]]},"2148":{"position":[[1161,32]]},"2150":{"position":[[1361,32]]},"2152":{"position":[[2519,32]]},"2154":{"position":[[2658,32]]},"2156":{"position":[[2630,32]]},"2968":{"position":[[322,29]]},"3377":{"position":[[1161,32]]},"3379":{"position":[[1408,32]]},"3381":{"position":[[2322,32]]},"3383":{"position":[[2658,32]]},"3385":{"position":[[2603,32]]}}}],["proxy_name=connect08:25:33",{"_index":2398,"t":{"212":{"position":[[1060,26]]}}}],["proxy_next_upstream",{"_index":4231,"t":{"1001":{"position":[[822,19]]},"2108":{"position":[[822,19]]},"3309":{"position":[[822,19]]}}}],["proxy_pass",{"_index":2027,"t":{"134":{"position":[[818,10],[1097,10]]},"287":{"position":[[794,10],[1052,10]]},"1001":{"position":[[982,10],[1317,10]]},"1003":{"position":[[299,10],[556,10]]},"2108":{"position":[[982,10],[1317,10]]},"2110":{"position":[[299,10],[556,10]]},"3309":{"position":[[982,10],[1317,10]]},"3311":{"position":[[299,10],[556,10]]}}}],["proxy_pass_head",{"_index":4241,"t":{"1003":{"position":[[329,17]]},"2110":{"position":[[329,17]]},"3311":{"position":[[329,17]]}}}],["proxy_publish",{"_index":2011,"t":{"132":{"position":[[650,16]]},"726":{"position":[[0,13]]},"730":{"position":[[109,13]]},"1073":{"position":[[853,13],[1128,13],[1308,16],[1521,16]]},"1079":{"position":[[1236,16]]},"1927":{"position":[[0,13]]},"1933":{"position":[[109,13]]},"2154":{"position":[[853,13],[1128,13],[1308,16],[1521,16]]},"2162":{"position":[[1236,16]]},"3120":{"position":[[0,13]]},"3128":{"position":[[109,13]]},"3383":{"position":[[853,13],[1128,13],[1308,16],[1521,16]]},"3391":{"position":[[1236,16]]}}}],["proxy_publish_endpoint",{"_index":2004,"t":{"132":{"position":[[408,25]]},"1073":{"position":[[59,25],[212,23],[1190,25],[1371,25]]},"1079":{"position":[[1103,25]]},"2154":{"position":[[59,25],[212,23],[1190,25],[1371,25]]},"2162":{"position":[[1103,25]]},"3383":{"position":[[59,25],[212,23],[1190,25],[1371,25]]},"3391":{"position":[[1103,25]]}}}],["proxy_publish_timeout",{"_index":3648,"t":{"570":{"position":[[1725,21]]},"1073":{"position":[[129,24],[1260,24],[1441,24],[3206,21]]},"1079":{"position":[[1155,24]]},"2154":{"position":[[129,24],[1260,24],[1441,24],[3206,21]]},"2162":{"position":[[1155,24]]},"3383":{"position":[[129,24],[1260,24],[1441,24],[3206,21]]},"3391":{"position":[[1155,24]]}}}],["proxy_read_timeout",{"_index":2040,"t":{"134":{"position":[[1209,18]]},"1001":{"position":[[1055,18]]},"1003":{"position":[[629,18]]},"2108":{"position":[[1055,18]]},"2110":{"position":[[629,18]]},"3309":{"position":[[1055,18]]},"3311":{"position":[[629,18]]}}}],["proxy_redirect",{"_index":4242,"t":{"1003":{"position":[[389,14]]},"2110":{"position":[[389,14]]},"3311":{"position":[[389,14]]}}}],["proxy_refresh_endpoint",{"_index":4319,"t":{"1067":{"position":[[60,25],[184,22]]},"1079":{"position":[[854,25]]},"2148":{"position":[[60,25],[184,22]]},"2162":{"position":[[854,25]]},"3377":{"position":[[60,25],[184,22]]},"3391":{"position":[[854,25]]}}}],["proxy_refresh_timeout",{"_index":3645,"t":{"570":{"position":[[1565,21]]},"1067":{"position":[[130,24],[1620,21]]},"1079":{"position":[[906,24]]},"2148":{"position":[[130,24],[1620,21]]},"2162":{"position":[[906,24]]},"3377":{"position":[[130,24],[1620,21]]},"3391":{"position":[[906,24]]}}}],["proxy_rpc_endpoint",{"_index":4324,"t":{"1069":{"position":[[59,21],[204,19]]},"1079":{"position":[[973,21]]},"2150":{"position":[[59,21],[204,19]]},"2162":{"position":[[973,21]]},"3379":{"position":[[59,21],[204,19]]},"3391":{"position":[[973,21]]}}}],["proxy_rpc_timeout",{"_index":3646,"t":{"570":{"position":[[1619,17]]},"1069":{"position":[[125,20],[1608,17]]},"1079":{"position":[[1021,20]]},"2150":{"position":[[125,20],[1608,17]]},"2162":{"position":[[1021,20]]},"3379":{"position":[[125,20],[1655,17]]},"3391":{"position":[[1021,20]]}}}],["proxy_set_head",{"_index":2030,"t":{"134":{"position":[[887,16],[916,16],[957,16],[1018,16],[1233,16],[1273,16],[1312,16],[1341,16],[1382,16],[1443,16]]},"287":{"position":[[852,16],[881,16],[922,16],[983,16],[1110,16],[1150,16],[1189,16],[1218,16],[1259,16],[1320,16]]},"1001":{"position":[[849,16],[890,16],[925,16],[1103,16],[1144,16],[1179,16],[1213,16],[1253,16]]},"1003":{"position":[[355,16],[409,16],[450,16],[653,16],[694,16],[729,16],[787,16],[827,16]]},"2108":{"position":[[849,16],[890,16],[925,16],[1103,16],[1144,16],[1179,16],[1213,16],[1253,16]]},"2110":{"position":[[355,16],[409,16],[450,16],[653,16],[694,16],[729,16],[787,16],[827,16]]},"3309":{"position":[[849,16],[890,16],[925,16],[1103,16],[1144,16],[1179,16],[1213,16],[1253,16]]},"3311":{"position":[[355,16],[409,16],[450,16],[653,16],[694,16],[729,16],[787,16],[827,16]]}}}],["proxy_static_http_head",{"_index":5716,"t":{"3371":{"position":[[966,28],[1034,25]]}}}],["proxy_sub_refresh",{"_index":5122,"t":{"1929":{"position":[[0,17]]},"1935":{"position":[[117,17]]},"2156":{"position":[[608,17],[1397,17],[1529,20],[1702,20]]},"3122":{"position":[[0,17]]},"3130":{"position":[[117,17]]},"3385":{"position":[[581,17],[1370,17],[1502,20],[1675,20]]}}}],["proxy_sub_refresh_endpoint",{"_index":5190,"t":{"2156":{"position":[[87,29],[223,26],[1451,29],[1596,29]]},"3385":{"position":[[60,29],[196,26],[1424,29],[1569,29]]}}}],["proxy_sub_refresh_timeout",{"_index":5192,"t":{"2156":{"position":[[165,28],[3087,25]]},"3385":{"position":[[138,28],[3079,25]]}}}],["proxy_subscrib",{"_index":2012,"t":{"132":{"position":[[673,18]]},"180":{"position":[[1273,18]]},"724":{"position":[[0,15]]},"728":{"position":[[113,15]]},"1071":{"position":[[1137,15],[1278,15],[1437,18],[1641,18]]},"1079":{"position":[[1425,18]]},"1925":{"position":[[0,15]]},"1931":{"position":[[113,15]]},"2152":{"position":[[1137,15],[1278,15],[1437,18],[1641,18]]},"2162":{"position":[[1425,18]]},"3118":{"position":[[0,15]]},"3126":{"position":[[113,15]]},"3381":{"position":[[940,15],[1081,15],[1240,18],[1444,18]]},"3391":{"position":[[1425,18]]}}}],["proxy_subscribe_endpoint",{"_index":2006,"t":{"132":{"position":[[484,27]]},"180":{"position":[[1143,27]]},"1071":{"position":[[59,27],[234,25],[1330,27],[1502,27]]},"1079":{"position":[[1338,27]]},"2152":{"position":[[59,27],[234,25],[1330,27],[1502,27]]},"2162":{"position":[[1338,27]]},"3381":{"position":[[59,27],[234,25],[1133,27],[1305,27]]},"3391":{"position":[[1338,27]]}}}],["proxy_subscribe_stream",{"_index":5576,"t":{"3124":{"position":[[0,22]]},"3132":{"position":[[127,22]]},"3349":{"position":[[1115,25]]},"3351":{"position":[[909,25]]}}}],["proxy_subscribe_stream_bidirect",{"_index":5698,"t":{"3351":{"position":[[679,36],[941,39]]}}}],["proxy_subscribe_stream_endpoint",{"_index":5649,"t":{"3349":{"position":[[502,34],[977,34]]},"3351":{"position":[[771,34]]}}}],["proxy_subscribe_stream_timeout",{"_index":5650,"t":{"3349":{"position":[[563,33],[716,30],[1038,33]]},"3351":{"position":[[832,33]]}}}],["proxy_subscribe_timeout",{"_index":3647,"t":{"570":{"position":[[1669,23]]},"1071":{"position":[[133,26],[1404,26],[1576,26],[3868,23]]},"1079":{"position":[[1392,26]]},"2152":{"position":[[133,26],[1404,26],[1576,26],[3899,23]]},"2162":{"position":[[1392,26]]},"3381":{"position":[[133,26],[1207,26],[1379,26],[3902,23]]},"3391":{"position":[[1392,26]]}}}],["pub",{"_index":1354,"t":{"70":{"position":[[1203,3],[1430,5]]},"391":{"position":[[1391,7]]},"963":{"position":[[1765,3],[1992,5]]},"1473":{"position":[[364,3]]},"1475":{"position":[[401,7],[1015,7],[1090,6],[1728,6]]},"1487":{"position":[[124,6]]},"1489":{"position":[[406,7]]},"2100":{"position":[[1765,3],[1992,5]]},"2242":{"position":[[409,3]]},"2294":{"position":[[302,3]]},"2666":{"position":[[364,3]]},"2668":{"position":[[401,7],[1015,7],[1090,6],[1728,6]]},"2680":{"position":[[124,6]]},"2682":{"position":[[406,7]]},"3259":{"position":[[1765,3],[1992,5]]},"3349":{"position":[[3201,3],[3335,5]]},"3351":{"position":[[2313,3],[2405,5]]},"3524":{"position":[[302,3]]},"3553":{"position":[[409,3]]}}}],["pub.offset",{"_index":1360,"t":{"70":{"position":[[1330,11]]},"963":{"position":[[1892,11]]},"2100":{"position":[[1892,11]]},"3259":{"position":[[1892,11]]}}}],["pub/sub",{"_index":597,"t":{"16":{"position":[[798,8],[869,7],[2306,7],[3171,7],[5630,7],[5798,7],[6070,7],[6165,7],[7139,7]]},"22":{"position":[[412,7]]},"34":{"position":[[2569,7]]},"46":{"position":[[655,7],[1426,7],[1561,7],[3864,7]]},"48":{"position":[[298,7],[422,7],[990,7]]},"62":{"position":[[685,7]]},"74":{"position":[[100,8],[328,7],[423,7],[1002,7]]},"142":{"position":[[508,7]]},"194":{"position":[[732,7]]},"202":{"position":[[91,8]]},"250":{"position":[[735,7]]},"291":{"position":[[414,7]]},"363":{"position":[[826,7]]},"373":{"position":[[725,7]]},"415":{"position":[[50,7],[76,7]]},"425":{"position":[[16,7]]},"467":{"position":[[324,7],[734,7]]},"469":{"position":[[423,8],[783,7]]},"477":{"position":[[3542,7],[3788,7]]},"479":{"position":[[2718,8],[3206,7],[3318,7]]},"491":{"position":[[520,7]]},"716":{"position":[[124,7]]},"965":{"position":[[3430,7]]},"973":{"position":[[241,7]]},"977":{"position":[[1271,7]]},"983":{"position":[[940,7]]},"985":{"position":[[895,7],[983,7]]},"989":{"position":[[986,7]]},"993":{"position":[[33,7],[147,7],[277,8]]},"1043":{"position":[[1218,7]]},"1383":{"position":[[820,7],[967,7]]},"1399":{"position":[[55,7],[250,7]]},"1409":{"position":[[16,7]]},"1869":{"position":[[16,7]]},"1895":{"position":[[218,7]]},"2028":{"position":[[241,7]]},"2034":{"position":[[1271,7]]},"2042":{"position":[[940,7]]},"2044":{"position":[[895,7],[983,7]]},"2050":{"position":[[986,7]]},"2054":{"position":[[33,7],[147,7],[277,8]]},"2102":{"position":[[4574,7]]},"2134":{"position":[[1230,7]]},"2502":{"position":[[78,8],[199,7]]},"2504":{"position":[[425,7]]},"2506":{"position":[[515,7],[542,7]]},"2534":{"position":[[858,7],[1009,7]]},"2556":{"position":[[57,7],[252,7]]},"2566":{"position":[[16,7]]},"3060":{"position":[[25,7]]},"3088":{"position":[[218,7]]},"3261":{"position":[[4574,7]]},"3269":{"position":[[241,7]]},"3275":{"position":[[1271,7]]},"3283":{"position":[[940,7]]},"3285":{"position":[[895,7],[983,7]]},"3287":{"position":[[252,7]]},"3289":{"position":[[986,7]]},"3293":{"position":[[33,7],[147,7],[277,8]]},"3363":{"position":[[1230,7]]}}}],["pubin",{"_index":1611,"t":{"100":{"position":[[342,5]]},"2434":{"position":[[1169,5]]},"3612":{"position":[[1196,5]]}}}],["pubkey",{"_index":1608,"t":{"100":{"position":[[312,6]]},"2434":{"position":[[1139,6]]},"3612":{"position":[[1166,6]]}}}],["public",{"_index":193,"t":{"2":{"position":[[3214,12]]},"16":{"position":[[5973,12]]},"42":{"position":[[254,11],[318,11],[2476,12]]},"46":{"position":[[1060,16]]},"48":{"position":[[650,11]]},"68":{"position":[[840,6]]},"136":{"position":[[1297,11],[1388,12]]},"162":{"position":[[310,6]]},"176":{"position":[[565,12]]},"182":{"position":[[121,12],[515,12],[912,11],[930,11]]},"200":{"position":[[574,12]]},"214":{"position":[[1410,12]]},"297":{"position":[[643,6]]},"339":{"position":[[1148,11]]},"359":{"position":[[311,12],[1059,12]]},"361":{"position":[[764,12]]},"363":{"position":[[300,11],[854,11]]},"365":{"position":[[164,12]]},"385":{"position":[[1686,6]]},"387":{"position":[[184,6],[255,6]]},"395":{"position":[[247,6]]},"433":{"position":[[52,12],[83,11]]},"469":{"position":[[580,11],[797,11]]},"477":{"position":[[3697,11],[4062,11]]},"479":{"position":[[4692,11]]},"505":{"position":[[665,12],[691,12],[942,11],[981,12],[1155,12]]},"507":{"position":[[10,12],[89,12]]},"511":{"position":[[32,11],[126,12],[289,12],[476,12],[632,12]]},"541":{"position":[[61,12],[147,13]]},"610":{"position":[[196,11]]},"618":{"position":[[859,11]]},"622":{"position":[[260,12]]},"692":{"position":[[383,7]]},"694":{"position":[[367,7]]},"704":{"position":[[343,6]]},"718":{"position":[[86,12],[784,12]]},"734":{"position":[[783,6],[1353,9],[1609,6]]},"752":{"position":[[207,11]]},"806":{"position":[[128,12]]},"830":{"position":[[147,11],[210,12]]},"850":{"position":[[126,12]]},"961":{"position":[[183,12],[500,11],[1009,11]]},"963":{"position":[[775,12],[940,12]]},"965":{"position":[[177,12],[457,12],[750,11],[788,12],[1456,12],[1587,12],[1664,12],[2235,11],[2350,11],[3137,12],[3180,12],[3273,11]]},"1041":{"position":[[558,12]]},"1073":{"position":[[410,11],[3082,11]]},"1087":{"position":[[476,11]]},"1109":{"position":[[2075,11],[2141,11],[2223,11],[2381,11]]},"1111":{"position":[[412,12],[489,11],[596,11]]},"1121":{"position":[[1004,11]]},"1125":{"position":[[256,12],[811,15],[1086,13],[1204,13],[1259,12],[1546,12],[1568,11],[1600,12]]},"1127":{"position":[[31,12]]},"1162":{"position":[[846,12]]},"1168":{"position":[[761,11],[863,12],[905,12],[969,11],[1110,12],[1178,12],[1462,11]]},"1176":{"position":[[176,12],[379,11],[1419,11]]},"1184":{"position":[[35,6],[1983,11]]},"1186":{"position":[[609,12]]},"1270":{"position":[[286,11],[651,11]]},"1327":{"position":[[1148,11]]},"1367":{"position":[[665,12],[691,12],[942,11],[981,12],[1155,12]]},"1369":{"position":[[10,12],[89,12]]},"1373":{"position":[[32,11],[126,12],[289,12],[476,12],[632,12]]},"1379":{"position":[[311,12],[1059,12]]},"1381":{"position":[[773,12]]},"1383":{"position":[[303,11],[874,11],[940,11]]},"1385":{"position":[[164,12]]},"1417":{"position":[[52,12],[83,11]]},"1469":{"position":[[1422,13]]},"1473":{"position":[[343,12],[436,11]]},"1495":{"position":[[210,12],[3703,15],[3740,11]]},"1533":{"position":[[1938,12]]},"1541":{"position":[[1344,15]]},"1545":{"position":[[196,11]]},"1591":{"position":[[260,12]]},"1682":{"position":[[980,6]]},"1741":{"position":[[1931,6]]},"1743":{"position":[[308,11],[424,12],[1078,12],[1233,12],[1472,11]]},"1771":{"position":[[207,11]]},"1795":{"position":[[650,6]]},"1857":{"position":[[125,12]]},"1863":{"position":[[876,11],[938,12]]},"1869":{"position":[[91,13]]},"1873":{"position":[[368,7]]},"1881":{"position":[[783,6]]},"1893":{"position":[[989,15]]},"1897":{"position":[[194,12],[1137,12]]},"1899":{"position":[[306,6]]},"1903":{"position":[[441,12]]},"1905":{"position":[[414,12]]},"1937":{"position":[[126,11]]},"2098":{"position":[[1075,11]]},"2100":{"position":[[775,12],[940,12]]},"2102":{"position":[[177,12],[457,12],[781,12],[1628,12],[1759,12],[1836,12],[2422,11],[2509,11],[2721,12],[2873,12],[2983,11],[3052,12],[3078,12],[3336,12],[3683,12],[3766,12]]},"2118":{"position":[[158,9]]},"2132":{"position":[[558,12]]},"2154":{"position":[[410,11],[3082,11]]},"2170":{"position":[[476,11]]},"2190":{"position":[[76,11],[2129,11],[2195,11],[2277,11],[2665,11]]},"2192":{"position":[[412,12],[489,11],[571,11]]},"2202":{"position":[[1047,11]]},"2206":{"position":[[256,12],[811,15],[1086,13],[1204,13],[1259,12],[1546,12],[1568,11],[1600,12]]},"2208":{"position":[[31,12]]},"2294":{"position":[[290,11],[715,11],[1034,11],[1098,11],[1148,11]]},"2345":{"position":[[2715,6],[2823,6],[2958,6]]},"2355":{"position":[[2451,6],[2588,6],[2724,6]]},"2359":{"position":[[78,12],[191,13],[214,11],[261,11],[550,11],[926,11],[2171,6]]},"2361":{"position":[[113,11],[186,12],[442,12],[895,12]]},"2369":{"position":[[1492,11]]},"2379":{"position":[[30,11],[124,12],[289,12],[476,12],[632,12]]},"2474":{"position":[[1148,11]]},"2518":{"position":[[696,12],[722,12],[984,11],[1023,12],[1209,12]]},"2520":{"position":[[10,12],[89,12]]},"2524":{"position":[[32,11],[126,12],[289,12],[476,12],[632,12]]},"2530":{"position":[[2793,11]]},"2532":{"position":[[820,12]]},"2534":{"position":[[316,11],[912,11],[978,11]]},"2536":{"position":[[165,12]]},"2574":{"position":[[53,12],[84,11]]},"2610":{"position":[[1455,13]]},"2662":{"position":[[211,12],[3736,15],[3773,12]]},"2666":{"position":[[343,12],[436,11]]},"2702":{"position":[[1938,12]]},"2710":{"position":[[1344,15]]},"2714":{"position":[[196,11]]},"2786":{"position":[[260,12]]},"2836":{"position":[[980,6]]},"2930":{"position":[[1931,6]]},"2932":{"position":[[308,11],[424,12],[1078,12],[1233,12],[1472,11]]},"2960":{"position":[[193,13]]},"2984":{"position":[[650,6]]},"3048":{"position":[[125,12]]},"3054":{"position":[[876,11],[938,12]]},"3060":{"position":[[108,13]]},"3064":{"position":[[368,7]]},"3072":{"position":[[1071,6]]},"3084":{"position":[[953,15]]},"3090":{"position":[[194,12],[1137,12]]},"3092":{"position":[[306,6]]},"3096":{"position":[[441,12]]},"3098":{"position":[[414,12]]},"3134":{"position":[[126,11]]},"3257":{"position":[[1075,11]]},"3259":{"position":[[775,12],[940,12]]},"3261":{"position":[[177,12],[457,12],[781,12],[1628,12],[1759,12],[1836,12],[2422,11],[2509,11],[2721,12],[2873,12],[2983,11],[3052,12],[3078,12],[3336,12],[3683,12],[3766,12]]},"3333":{"position":[[172,9]]},"3337":{"position":[[287,9]]},"3349":{"position":[[4275,14],[4858,11]]},"3351":{"position":[[1558,12],[2165,12]]},"3361":{"position":[[558,12]]},"3383":{"position":[[410,11],[3082,11]]},"3399":{"position":[[476,11]]},"3431":{"position":[[75,11],[865,11],[1637,11],[1703,11],[1785,11],[2173,11]]},"3433":{"position":[[487,12],[564,11],[646,11]]},"3443":{"position":[[885,11]]},"3447":{"position":[[256,12],[660,15],[928,13],[1046,13],[1101,12],[1388,12],[1410,11],[1442,12]]},"3449":{"position":[[31,12]]},"3455":{"position":[[269,12]]},"3524":{"position":[[290,11],[715,11],[1034,11],[1098,11],[1148,11]]},"3629":{"position":[[1411,12]]},"3633":{"position":[[2715,6],[2823,6],[2958,6]]},"3643":{"position":[[2451,6],[2588,6],[2724,6]]},"3647":{"position":[[78,12],[191,13],[214,11],[261,11],[550,11],[926,11],[2171,6]]},"3649":{"position":[[113,11],[186,12],[442,12],[895,12]]},"3657":{"position":[[1492,11]]},"3667":{"position":[[30,11],[124,12],[289,12],[476,12],[632,12]]}}}],["public:chat",{"_index":3871,"t":{"692":{"position":[[277,11],[492,11],[545,11]]},"694":{"position":[[305,12]]},"1123":{"position":[[261,16]]},"1873":{"position":[[262,11],[477,11],[530,11]]},"2204":{"position":[[261,16]]},"3064":{"position":[[262,11],[504,12],[555,11]]}}}],["public:messag",{"_index":3909,"t":{"734":{"position":[[744,15]]},"1881":{"position":[[744,15]]},"3072":{"position":[[1032,15]]}}}],["public:new",{"_index":3912,"t":{"734":{"position":[[1588,11]]}}}],["public:test",{"_index":5173,"t":{"2118":{"position":[[448,16]]},"3333":{"position":[[418,15],[771,15]]}}}],["publicationev",{"_index":5362,"t":{"2359":{"position":[[2215,16]]},"3647":{"position":[[2215,16]]}}}],["publications/sec",{"_index":3368,"t":{"479":{"position":[[4751,17],[4797,16]]}}}],["publish",{"_index":127,"t":{"2":{"position":[[1915,9]]},"16":{"position":[[636,7],[1010,7]]},"30":{"position":[[155,7],[429,9]]},"34":{"position":[[2343,9],[2389,7],[2673,9]]},"40":{"position":[[829,9]]},"42":{"position":[[21,9],[475,7]]},"46":{"position":[[1314,9],[1544,10]]},"48":{"position":[[161,7],[977,9]]},"74":{"position":[[402,7]]},"78":{"position":[[564,7]]},"92":{"position":[[265,7]]},"94":{"position":[[205,9]]},"118":{"position":[[408,9]]},"132":{"position":[[633,10],[1412,7],[1945,7],[1982,7]]},"136":{"position":[[1265,7],[1422,7]]},"138":{"position":[[407,7],[677,7],[761,7],[776,7],[868,7],[1939,9]]},"158":{"position":[[682,10]]},"164":{"position":[[107,7],[132,7],[291,10],[834,9]]},"166":{"position":[[795,12],[1152,7],[1936,7],[2417,7]]},"168":{"position":[[1007,7]]},"176":{"position":[[995,7],[1052,7],[1089,7]]},"178":{"position":[[2797,8]]},"204":{"position":[[373,10]]},"214":{"position":[[1366,7]]},"222":{"position":[[43,10],[78,7],[683,7],[915,10]]},"228":{"position":[[165,7],[616,7]]},"230":{"position":[[451,9]]},"234":{"position":[[27,10],[136,10]]},"236":{"position":[[536,10]]},"238":{"position":[[114,9]]},"240":{"position":[[802,7],[900,7],[1027,7]]},"242":{"position":[[5,10]]},"244":{"position":[[22,7],[155,7],[480,10]]},"289":{"position":[[24,7],[274,7],[603,10]]},"301":{"position":[[4198,10],[4270,9]]},"321":{"position":[[129,7],[1097,9]]},"339":{"position":[[17,7],[77,7],[581,9],[842,7],[1065,7],[1118,7],[1262,10],[1334,7],[1384,10]]},"345":{"position":[[158,9]]},"355":{"position":[[794,7],[835,7],[979,9],[1048,7],[1236,7],[1277,7],[1421,9],[1490,7]]},"359":{"position":[[589,7],[1252,7],[1295,7]]},"365":{"position":[[65,7],[116,7]]},"385":{"position":[[1235,10]]},"391":{"position":[[1296,7]]},"399":{"position":[[133,9]]},"435":{"position":[[38,10]]},"457":{"position":[[689,7],[899,9]]},"467":{"position":[[216,10],[354,9]]},"469":{"position":[[441,9]]},"477":{"position":[[1380,7],[3419,7],[3506,7],[3572,7],[3777,7]]},"479":{"position":[[4843,7],[5709,7]]},"485":{"position":[[1529,9]]},"491":{"position":[[321,7]]},"505":{"position":[[110,9],[491,9]]},"610":{"position":[[540,10],[1105,7],[1230,10]]},"618":{"position":[[291,7],[512,10],[821,7],[1449,10]]},"636":{"position":[[97,7]]},"692":{"position":[[523,7],[648,7]]},"700":{"position":[[0,7],[61,7],[239,9],[335,7],[507,7]]},"702":{"position":[[57,7],[94,7],[256,8]]},"726":{"position":[[50,7]]},"730":{"position":[[51,7]]},"732":{"position":[[164,10]]},"734":{"position":[[1230,10],[1363,10]]},"752":{"position":[[229,9]]},"912":{"position":[[1013,8]]},"961":{"position":[[515,9]]},"969":{"position":[[423,7]]},"1041":{"position":[[1169,7]]},"1073":{"position":[[162,7],[277,9],[351,7],[488,7],[517,7],[712,7],[880,7],[1043,7],[1076,7],[1146,7],[1291,10],[1504,10],[1592,7],[1725,7],[1950,7],[1985,7],[2468,7],[2697,7],[3010,10],[3185,11]]},"1079":{"position":[[1052,7],[1219,10]]},"1087":{"position":[[513,9]]},"1089":{"position":[[688,7]]},"1097":{"position":[[14,7],[424,10],[532,10],[815,7],[968,7]]},"1109":{"position":[[0,7],[23,10],[136,10],[258,7],[399,10],[762,10],[1130,10],[1394,10],[1460,10],[1868,7],[1973,7],[2019,7],[2293,7]]},"1111":{"position":[[11,7],[299,7],[353,7],[721,7],[772,7],[805,7]]},"1125":{"position":[[74,9]]},"1137":{"position":[[248,7]]},"1141":{"position":[[1315,11],[1419,9]]},"1143":{"position":[[1174,11]]},"1162":{"position":[[961,7],[1175,7],[1465,7]]},"1164":{"position":[[3749,9]]},"1166":{"position":[[587,7]]},"1168":{"position":[[1401,9]]},"1174":{"position":[[96,7],[122,7],[177,7],[197,7]]},"1176":{"position":[[404,9]]},"1182":{"position":[[1534,7],[1637,7]]},"1186":{"position":[[363,8]]},"1207":{"position":[[971,7],[1071,10]]},"1262":{"position":[[1342,7]]},"1285":{"position":[[189,7]]},"1307":{"position":[[129,7],[1097,9]]},"1327":{"position":[[17,7],[77,7],[581,9],[842,7],[1065,7],[1118,7],[1262,10],[1334,7],[1384,10]]},"1333":{"position":[[158,9]]},"1347":{"position":[[794,7],[835,7],[979,9],[1048,7],[1236,7],[1277,7],[1421,9],[1490,7]]},"1367":{"position":[[110,9],[491,9]]},"1379":{"position":[[589,7],[1252,7],[1295,7]]},"1385":{"position":[[65,7],[116,7]]},"1419":{"position":[[25,10]]},"1457":{"position":[[685,7],[895,9]]},"1473":{"position":[[370,7]]},"1475":{"position":[[1399,8]]},"1489":{"position":[[332,10]]},"1495":{"position":[[233,9],[8936,7],[9289,7],[9342,7],[9454,7],[9666,7],[9714,7],[9957,10]]},"1525":{"position":[[39,7]]},"1545":{"position":[[540,10],[1099,7],[1224,10]]},"1605":{"position":[[97,7]]},"1676":{"position":[[313,7],[1107,10]]},"1678":{"position":[[643,7],[905,10]]},"1680":{"position":[[1083,7],[1386,10]]},"1741":{"position":[[698,8]]},"1743":{"position":[[52,9],[201,10],[251,10],[380,7],[556,7],[700,7],[731,7],[841,7],[882,7],[923,7],[961,7],[1330,7],[1359,7],[1384,9],[1425,7],[1520,7],[1617,7],[1747,7],[1768,7],[1898,7],[1920,7],[2038,7],[2067,7],[2191,7]]},"1771":{"position":[[229,9]]},"1869":{"position":[[40,10],[174,9]]},"1873":{"position":[[508,7],[633,7]]},"1879":{"position":[[254,9],[702,7]]},"1903":{"position":[[123,7],[315,7],[560,9],[608,7],[669,7]]},"1905":{"position":[[78,7],[230,8],[288,7],[533,9],[581,7],[642,7]]},"1907":{"position":[[102,7]]},"1927":{"position":[[50,7]]},"1933":{"position":[[51,7]]},"1937":{"position":[[247,8]]},"1981":{"position":[[1013,8]]},"2024":{"position":[[423,7]]},"2098":{"position":[[550,9]]},"2132":{"position":[[1169,7]]},"2154":{"position":[[162,7],[277,9],[351,7],[488,7],[517,7],[712,7],[880,7],[1043,7],[1076,7],[1146,7],[1291,10],[1504,10],[1592,7],[1725,7],[1950,7],[1985,7],[2468,7],[2697,7],[3010,10],[3185,11]]},"2156":{"position":[[442,7]]},"2162":{"position":[[1052,7],[1219,10]]},"2170":{"position":[[513,9]]},"2172":{"position":[[662,7]]},"2180":{"position":[[11,7],[461,10],[569,10],[852,7],[1171,8]]},"2190":{"position":[[0,7],[23,10],[197,10],[318,7],[459,10],[822,10],[1190,10],[1452,7],[1514,10],[1922,7],[2027,7],[2073,7],[2362,7],[2508,11],[2544,7],[2577,7]]},"2192":{"position":[[11,7],[299,7],[353,7],[656,7],[802,11],[838,7],[951,7],[1002,7],[1035,7]]},"2206":{"position":[[74,9]]},"2214":{"position":[[280,10],[356,10]]},"2218":{"position":[[248,7]]},"2222":{"position":[[1315,11],[1419,9]]},"2224":{"position":[[1174,11]]},"2240":{"position":[[1799,7],[1817,7]]},"2242":{"position":[[295,9],[426,9]]},"2250":{"position":[[1534,7],[1637,7]]},"2339":{"position":[[971,7],[1071,10]]},"2359":{"position":[[106,9],[519,9],[585,9],[670,9]]},"2365":{"position":[[104,7]]},"2413":{"position":[[1421,7]]},"2432":{"position":[[189,7]]},"2436":{"position":[[794,7],[835,7],[979,9],[1048,7],[1236,7],[1277,7],[1421,9],[1490,7]]},"2454":{"position":[[129,7],[1097,9]]},"2474":{"position":[[17,7],[77,7],[581,9],[842,7],[1065,7],[1118,7],[1262,10],[1334,7],[1384,10]]},"2480":{"position":[[158,9]]},"2518":{"position":[[110,9],[505,9]]},"2530":{"position":[[1053,7],[1978,9],[2678,10],[2710,7]]},"2536":{"position":[[64,7],[117,7]]},"2576":{"position":[[29,10]]},"2598":{"position":[[699,7],[921,9]]},"2662":{"position":[[234,9],[9056,7],[9432,7],[9489,7],[9610,7],[9822,7],[9870,7]]},"2666":{"position":[[370,7]]},"2668":{"position":[[1399,8]]},"2682":{"position":[[332,10]]},"2694":{"position":[[39,7]]},"2714":{"position":[[540,10],[1099,7],[1224,10]]},"2800":{"position":[[97,7]]},"2830":{"position":[[316,7],[1110,10]]},"2832":{"position":[[649,7],[911,10]]},"2834":{"position":[[1089,7],[1392,10]]},"2930":{"position":[[698,8]]},"2932":{"position":[[52,9],[201,10],[251,10],[380,7],[556,7],[700,7],[731,7],[841,7],[882,7],[923,7],[961,7],[1330,7],[1359,7],[1384,9],[1425,7],[1520,7],[1617,7],[1747,7],[1768,7],[1898,7],[1920,7],[2038,7],[2067,7],[2191,7]]},"3060":{"position":[[48,10],[195,9]]},"3064":{"position":[[531,7],[666,7],[794,7]]},"3070":{"position":[[262,9],[716,7]]},"3096":{"position":[[123,7],[315,7],[560,9],[608,7],[669,7]]},"3098":{"position":[[78,7],[230,8],[288,7],[533,9],[581,7],[642,7]]},"3100":{"position":[[102,7]]},"3120":{"position":[[50,7]]},"3128":{"position":[[51,7]]},"3134":{"position":[[247,8]]},"3194":{"position":[[1013,8]]},"3257":{"position":[[550,9]]},"3265":{"position":[[423,7]]},"3345":{"position":[[366,10],[414,7]]},"3347":{"position":[[2474,9],[2710,10]]},"3349":{"position":[[1183,8],[3037,7]]},"3361":{"position":[[1169,7]]},"3383":{"position":[[162,7],[277,9],[351,7],[488,7],[517,7],[712,7],[880,7],[1043,7],[1076,7],[1146,7],[1291,10],[1504,10],[1592,7],[1725,7],[1950,7],[1985,7],[2468,7],[2697,7],[3010,10],[3185,11]]},"3385":{"position":[[415,7]]},"3387":{"position":[[478,7]]},"3389":{"position":[[1012,7]]},"3391":{"position":[[1052,7],[1219,10]]},"3399":{"position":[[513,9]]},"3401":{"position":[[662,7]]},"3409":{"position":[[11,7],[461,10],[569,10],[852,7],[1171,8]]},"3425":{"position":[[278,7]]},"3429":{"position":[[87,7]]},"3431":{"position":[[0,7],[22,10],[186,10],[390,7],[500,7],[945,7],[1437,7],[1535,7],[1581,7],[1870,7],[2016,11],[2052,7],[2085,7]]},"3433":{"position":[[24,7],[374,7],[428,7],[731,7],[877,11],[913,7],[1026,7],[1077,7],[1110,7]]},"3447":{"position":[[74,9]]},"3455":{"position":[[375,12],[423,11],[540,15]]},"3459":{"position":[[248,7]]},"3463":{"position":[[1315,11],[1419,9]]},"3465":{"position":[[1174,11]]},"3551":{"position":[[1799,7],[1817,7]]},"3553":{"position":[[295,9],[426,9]]},"3561":{"position":[[1534,7],[1637,7]]},"3580":{"position":[[1421,7]]},"3595":{"position":[[971,7]]},"3627":{"position":[[189,7]]},"3629":{"position":[[1367,7]]},"3647":{"position":[[106,9],[519,9],[585,9],[670,9]]},"3653":{"position":[[104,7]]}}}],["publish');┌─count",{"_index":3756,"t":{"610":{"position":[[385,23]]},"1545":{"position":[[385,23]]},"2714":{"position":[[385,23]]}}}],["publish(ch",{"_index":1128,"t":{"46":{"position":[[798,10]]}}}],["publish(channel",{"_index":5387,"t":{"2369":{"position":[[1923,16]]},"3657":{"position":[[1923,16]]}}}],["publish(ctx",{"_index":3201,"t":{"477":{"position":[[1416,11]]}}}],["publish(data",{"_index":5375,"t":{"2365":{"position":[[88,13]]},"3351":{"position":[[287,14]]},"3653":{"position":[[88,13]]}}}],["publish(request",{"_index":2066,"t":{"136":{"position":[[1242,17]]}}}],["publish1",{"_index":4356,"t":{"1093":{"position":[[620,11]]},"1097":{"position":[[463,10]]},"2176":{"position":[[620,11]]},"2180":{"position":[[500,10]]},"3405":{"position":[[620,11]]},"3409":{"position":[[500,10]]}}}],["publish2",{"_index":4362,"t":{"1093":{"position":[[856,11]]},"1097":{"position":[[571,10]]},"2176":{"position":[[856,11]]},"2180":{"position":[[608,10]]},"3405":{"position":[[856,11]]},"3409":{"position":[[608,10]]}}}],["publish_proxy_nam",{"_index":3902,"t":{"730":{"position":[[0,18]]},"1097":{"position":[[89,18],[441,21],[549,21],[753,20],[777,20],[903,18]]},"1933":{"position":[[0,18]]},"2180":{"position":[[101,18],[478,21],[586,21],[790,20],[814,20],[1082,19]]},"3128":{"position":[[0,18]]},"3409":{"position":[[101,18],[478,21],[586,21],[790,20],[814,20],[1082,19]]}}}],["publishcontrol(data",{"_index":1135,"t":{"46":{"position":[[970,19]]}}}],["publisheddata",{"_index":2852,"t":{"301":{"position":[[3812,15]]}}}],["publishjoin(ch",{"_index":1131,"t":{"46":{"position":[[875,14]]}}}],["publishleave(ch",{"_index":1134,"t":{"46":{"position":[[922,15]]}}}],["publishopt",{"_index":1130,"t":{"46":{"position":[[835,15]]}}}],["publishrequest",{"_index":4535,"t":{"1143":{"position":[[860,16]]},"2224":{"position":[[860,16]]},"3465":{"position":[[860,16]]}}}],["pubnub",{"_index":5437,"t":{"2610":{"position":[[1967,7]]}}}],["puctur",{"_index":2107,"t":{"146":{"position":[[930,8]]}}}],["pull",{"_index":669,"t":{"16":{"position":[[4044,4]]},"50":{"position":[[1692,4]]},"108":{"position":[[917,4]]},"477":{"position":[[3083,4]]},"479":{"position":[[2951,4]]},"525":{"position":[[69,4]]},"1433":{"position":[[69,4]]},"2616":{"position":[[69,4]]}}}],["pulsar",{"_index":618,"t":{"16":{"position":[[2007,6],[3742,6]]}}}],["purchas",{"_index":5499,"t":{"2780":{"position":[[148,9],[527,8],[799,8]]}}}],["pure",{"_index":2866,"t":{"309":{"position":[[408,4]]},"379":{"position":[[1111,4]]},"475":{"position":[[416,4]]},"477":{"position":[[5718,4]]},"1295":{"position":[[408,4]]},"2442":{"position":[[408,4]]}}}],["purpos",{"_index":3008,"t":{"385":{"position":[[1191,7]]},"558":{"position":[[402,9]]},"758":{"position":[[428,7]]},"794":{"position":[[220,9]]},"1129":{"position":[[865,9]]},"1515":{"position":[[179,7]]},"1777":{"position":[[428,7]]},"1845":{"position":[[217,9]]},"2210":{"position":[[907,9]]},"2347":{"position":[[502,7]]},"2504":{"position":[[325,9]]},"2646":{"position":[[179,7]]},"2964":{"position":[[339,8]]},"2966":{"position":[[501,8]]},"3036":{"position":[[217,9]]},"3229":{"position":[[304,9]]},"3451":{"position":[[967,9]]},"3635":{"position":[[502,7]]}}}],["push",{"_index":740,"t":{"18":{"position":[[235,4]]},"218":{"position":[[372,4]]},"222":{"position":[[629,4]]},"230":{"position":[[261,4]]},"232":{"position":[[71,4]]},"240":{"position":[[512,4]]},"248":{"position":[[421,4]]},"250":{"position":[[2191,4]]},"252":{"position":[[14,4],[84,4],[315,4],[507,4],[565,4],[1208,4],[1452,4],[1610,4]]},"254":{"position":[[0,4],[183,4],[274,4],[520,4],[555,4],[1447,4],[1812,4]]},"256":{"position":[[19,4],[470,4],[1595,4]]},"258":{"position":[[85,4]]},"260":{"position":[[75,4],[571,4],[611,4],[686,4],[789,4],[811,4],[1115,4],[1300,4]]},"262":{"position":[[8,4],[211,4],[713,4],[799,4],[852,4],[888,4],[939,4],[1159,4],[1186,4],[1222,4],[1613,4],[1647,4],[1824,4],[1856,4],[1970,4],[2179,6],[2257,4],[2562,4],[2631,4],[2660,4],[2829,4],[2939,4]]},"264":{"position":[[999,4]]},"266":{"position":[[50,4],[97,4],[189,4],[261,4],[404,4]]},"268":{"position":[[165,4]]},"270":{"position":[[25,4],[147,4],[268,4],[305,4],[373,4],[519,4]]},"335":{"position":[[76,4],[156,4],[265,4],[430,4],[859,4]]},"479":{"position":[[4979,6]]},"481":{"position":[[2116,7]]},"989":{"position":[[801,4]]},"1011":{"position":[[229,6]]},"1113":{"position":[[738,5]]},"1186":{"position":[[678,4]]},"1245":{"position":[[213,4],[241,4]]},"1270":{"position":[[58,4],[89,4],[230,4],[263,4],[519,7],[557,4],[792,4]]},"1323":{"position":[[76,4],[156,4],[265,4],[300,4],[507,4],[936,4]]},"1533":{"position":[[2044,4]]},"1543":{"position":[[56,4]]},"1545":{"position":[[1540,4]]},"1583":{"position":[[386,4],[460,4]]},"1623":{"position":[[34,4]]},"1625":{"position":[[20,4],[266,4],[292,4]]},"1627":{"position":[[91,4],[346,6]]},"1631":{"position":[[55,4],[381,4]]},"1633":{"position":[[63,4],[197,4],[269,4]]},"1635":{"position":[[98,4],[747,4]]},"1637":{"position":[[40,4]]},"1639":{"position":[[75,4],[248,4],[487,4],[862,4],[1069,4]]},"1647":{"position":[[85,4],[545,4]]},"1650":{"position":[[272,4]]},"1666":{"position":[[5,4],[344,4],[396,4],[1695,4]]},"1668":{"position":[[107,4],[225,4],[323,4],[949,4]]},"1670":{"position":[[65,4],[152,4],[506,7]]},"1889":{"position":[[200,7]]},"2050":{"position":[[801,4]]},"2092":{"position":[[229,6]]},"2194":{"position":[[703,5]]},"2238":{"position":[[242,7]]},"2242":{"position":[[907,4]]},"2262":{"position":[[241,6]]},"2264":{"position":[[256,6]]},"2294":{"position":[[58,4],[89,4],[230,4],[263,4],[657,7],[671,4],[800,6],[847,4],[893,5],[1002,6]]},"2324":{"position":[[197,4],[225,4]]},"2363":{"position":[[609,4]]},"2369":{"position":[[771,4],[1248,4]]},"2470":{"position":[[76,4],[156,4],[265,4],[300,4],[507,4],[936,4]]},"2504":{"position":[[383,4]]},"2506":{"position":[[989,4]]},"2702":{"position":[[2044,4]]},"2712":{"position":[[56,4]]},"2714":{"position":[[1540,4]]},"2778":{"position":[[387,4],[461,4]]},"2840":{"position":[[34,4]]},"2842":{"position":[[20,4],[266,4],[292,4]]},"2844":{"position":[[91,4],[338,6],[394,4],[432,4]]},"2848":{"position":[[55,4],[381,4]]},"2850":{"position":[[63,4],[197,4],[269,4]]},"2852":{"position":[[98,4],[747,4]]},"2854":{"position":[[40,4]]},"2856":{"position":[[75,4],[248,4],[487,4],[862,4],[1069,4]]},"2862":{"position":[[298,4],[408,4],[569,4]]},"2864":{"position":[[70,4],[572,4]]},"2867":{"position":[[272,4]]},"2883":{"position":[[5,4],[344,4],[396,4],[511,5],[627,4],[646,4],[1582,4],[1974,4]]},"2885":{"position":[[15,4],[159,4]]},"2887":{"position":[[107,4],[225,4],[323,4],[949,4]]},"2889":{"position":[[65,4],[225,4],[301,4],[1006,4]]},"3080":{"position":[[200,7]]},"3289":{"position":[[801,4]]},"3301":{"position":[[229,6]]},"3320":{"position":[[229,6]]},"3435":{"position":[[771,5]]},"3496":{"position":[[241,6]]},"3498":{"position":[[256,6]]},"3524":{"position":[[58,4],[89,4],[230,4],[263,4],[657,7],[671,4],[800,6],[847,4],[893,5],[1002,6]]},"3545":{"position":[[176,4],[204,7]]},"3549":{"position":[[242,7]]},"3553":{"position":[[907,4]]},"3651":{"position":[[609,4]]},"3657":{"position":[[771,4],[1248,4]]}}}],["push_notif",{"_index":4917,"t":{"1639":{"position":[[618,21]]},"1641":{"position":[[83,21]]},"1643":{"position":[[83,21]]},"1647":{"position":[[288,21]]},"2856":{"position":[[618,21]]},"2858":{"position":[[83,21]]},"2860":{"position":[[83,21]]},"2864":{"position":[[273,21]]}}}],["push_notifications.apns_cert_p12_b64",{"_index":4938,"t":{"1643":{"position":[[514,36]]},"2860":{"position":[[514,36]]}}}],["push_notifications.apns_cert_p12_password",{"_index":4939,"t":{"1643":{"position":[[551,41]]},"2860":{"position":[[551,41]]}}}],["push_notifications.apns_cert_p12_path",{"_index":4937,"t":{"1643":{"position":[[476,37]]},"2860":{"position":[[476,37]]}}}],["push_notifications.dry_run",{"_index":5517,"t":{"2862":{"position":[[486,27],[738,26]]}}}],["push_notifications.dry_run_lat",{"_index":5518,"t":{"2862":{"position":[[669,35]]}}}],["push_notifications.enable_redis_delayed_schedul",{"_index":5515,"t":{"2862":{"position":[[183,50]]}}}],["push_notifications.enabled_provid",{"_index":4916,"t":{"1639":{"position":[[167,36]]},"2856":{"position":[[167,36]]}}}],["push_notifications.max_inactive_device_day",{"_index":4940,"t":{"1645":{"position":[[0,44]]},"2862":{"position":[[0,44]]}}}],["pusher",{"_index":459,"t":{"10":{"position":[[1740,6]]},"148":{"position":[[337,7]]},"256":{"position":[[67,6]]},"321":{"position":[[42,6]]},"951":{"position":[[934,6]]},"1307":{"position":[[42,6]]},"2078":{"position":[[934,6]]},"2454":{"position":[[42,6]]},"2610":{"position":[[1950,7]]},"3245":{"position":[[934,6]]}}}],["pushnotif",{"_index":4976,"t":{"1666":{"position":[[375,16],[1069,17]]},"2883":{"position":[[375,16],[1266,17]]}}}],["pushrecipi",{"_index":4975,"t":{"1666":{"position":[[313,13],[422,13]]},"2883":{"position":[[313,13],[683,13]]}}}],["pushtyp",{"_index":4651,"t":{"1270":{"position":[[275,8],[413,8]]}}}],["put",{"_index":392,"t":{"10":{"position":[[81,3],[1138,3]]},"126":{"position":[[8,3]]},"128":{"position":[[465,3],[1355,3]]},"138":{"position":[[1405,3]]},"198":{"position":[[832,7]]},"307":{"position":[[331,3]]},"387":{"position":[[760,3]]},"389":{"position":[[485,3]]},"395":{"position":[[90,3]]},"459":{"position":[[3,3]]},"471":{"position":[[334,7]]},"481":{"position":[[2210,3]]},"523":{"position":[[1259,3]]},"1065":{"position":[[1088,3]]},"1079":{"position":[[504,4]]},"1141":{"position":[[585,3]]},"1293":{"position":[[331,3]]},"1431":{"position":[[1492,3]]},"1459":{"position":[[3,3]]},"1489":{"position":[[429,7]]},"2146":{"position":[[1205,3]]},"2162":{"position":[[504,4]]},"2222":{"position":[[585,3]]},"2440":{"position":[[331,3]]},"2600":{"position":[[3,3]]},"2614":{"position":[[1532,3]]},"2682":{"position":[[429,7]]},"3375":{"position":[[1205,3]]},"3391":{"position":[[504,4]]},"3463":{"position":[[585,3]]}}}],["puzrin",{"_index":3048,"t":{"407":{"position":[[158,6]]}}}],["python",{"_index":1530,"t":{"94":{"position":[[2487,6]]},"116":{"position":[[910,6]]},"128":{"position":[[2775,7]]},"178":{"position":[[1198,6]]},"188":{"position":[[242,6]]},"766":{"position":[[54,7]]},"768":{"position":[[0,6]]},"770":{"position":[[46,6]]},"772":{"position":[[24,6]]},"1035":{"position":[[71,6]]},"1065":{"position":[[4820,6]]},"1109":{"position":[[343,7]]},"1135":{"position":[[324,6]]},"1139":{"position":[[16,6]]},"1495":{"position":[[4430,6]]},"1785":{"position":[[54,7]]},"1787":{"position":[[0,6]]},"1789":{"position":[[46,6]]},"1791":{"position":[[24,6]]},"1825":{"position":[[71,6]]},"2146":{"position":[[4943,6]]},"2190":{"position":[[403,7]]},"2216":{"position":[[324,6]]},"2220":{"position":[[16,6]]},"2662":{"position":[[4492,6]]},"2974":{"position":[[54,7]]},"2976":{"position":[[0,6]]},"2978":{"position":[[46,6]]},"2980":{"position":[[24,6]]},"3014":{"position":[[71,6],[131,6]]},"3375":{"position":[[4972,6]]},"3431":{"position":[[548,7]]},"3457":{"position":[[324,6]]},"3461":{"position":[[16,6]]}}}],["python3",{"_index":1796,"t":{"122":{"position":[[414,7]]},"126":{"position":[[133,7]]},"128":{"position":[[2423,7]]},"130":{"position":[[2462,7]]},"1495":{"position":[[4450,7]]},"2662":{"position":[[4512,7]]}}}],["python_out",{"_index":4474,"t":{"1139":{"position":[[137,12]]},"2220":{"position":[[137,12]]},"3461":{"position":[[137,12]]}}}],["pythonista",{"_index":1786,"t":{"118":{"position":[[839,11]]}}}],["p}func",{"_index":3132,"t":{"473":{"position":[[1712,6]]}}}],["q",{"_index":2504,"t":{"246":{"position":[[367,3],[1090,3],[1508,3],[3106,3],[3385,3],[3752,3]]}}}],["qfhv",{"_index":4446,"t":{"1125":{"position":[[790,7]]},"2206":{"position":[[790,7]]},"3447":{"position":[[639,7]]}}}],["qualifi",{"_index":1596,"t":{"98":{"position":[[1918,9]]}}}],["quay.io/keycloak/keycloak:21.0.1",{"_index":2769,"t":{"297":{"position":[[152,32]]}}}],["queri",{"_index":822,"t":{"20":{"position":[[1472,8]]},"266":{"position":[[466,7]]},"371":{"position":[[390,7]]},"576":{"position":[[462,5]]},"612":{"position":[[766,8]]},"806":{"position":[[162,6]]},"850":{"position":[[160,6]]},"965":{"position":[[289,5]]},"1001":{"position":[[783,8]]},"1107":{"position":[[307,5],[440,5]]},"1213":{"position":[[611,5]]},"1285":{"position":[[661,5]]},"1391":{"position":[[390,7]]},"1547":{"position":[[935,8]]},"1583":{"position":[[586,6]]},"1668":{"position":[[358,8]]},"1857":{"position":[[159,6]]},"2102":{"position":[[289,5]]},"2108":{"position":[[783,8]]},"2118":{"position":[[217,5]]},"2188":{"position":[[310,5],[443,5]]},"2387":{"position":[[611,5]]},"2432":{"position":[[661,5]]},"2542":{"position":[[396,7]]},"2716":{"position":[[935,8]]},"2778":{"position":[[587,6]]},"2887":{"position":[[358,8]]},"3048":{"position":[[159,6]]},"3261":{"position":[[289,5]]},"3309":{"position":[[783,8]]},"3333":{"position":[[231,5]]},"3347":{"position":[[1151,5],[1395,5],[1498,6]]},"3427":{"position":[[304,5],[352,5]]},"3601":{"position":[[611,5]]},"3627":{"position":[[661,5]]}}}],["question",{"_index":1479,"t":{"90":{"position":[[294,9]]},"120":{"position":[[126,8]]},"142":{"position":[[854,9]]},"148":{"position":[[126,9]]},"170":{"position":[[304,9]]},"200":{"position":[[1031,10]]},"220":{"position":[[373,9]]},"279":{"position":[[356,8]]},"307":{"position":[[218,8]]},"335":{"position":[[331,8]]},"405":{"position":[[115,9]]},"1293":{"position":[[218,8]]},"1323":{"position":[[408,8]]},"1349":{"position":[[232,9]]},"2440":{"position":[[218,8]]},"2470":{"position":[[408,8]]},"2494":{"position":[[234,9]]},"2502":{"position":[[10,8]]},"3490":{"position":[[571,8]]}}}],["question—what",{"_index":2579,"t":{"252":{"position":[[748,13]]}}}],["queu",{"_index":1323,"t":{"68":{"position":[[967,7]]},"236":{"position":[[244,6]]},"262":{"position":[[1268,6],[1315,7],[1449,6]]},"319":{"position":[[1265,7]]},"413":{"position":[[163,7]]},"473":{"position":[[438,6]]},"1305":{"position":[[1265,7]]},"1397":{"position":[[162,7]]},"1627":{"position":[[61,7]]},"1639":{"position":[[240,7]]},"1647":{"position":[[165,8]]},"1666":{"position":[[142,6]]},"2452":{"position":[[1265,7]]},"2482":{"position":[[907,7]]},"2554":{"position":[[166,7]]},"2844":{"position":[[61,7]]},"2856":{"position":[[240,7]]},"2864":{"position":[[150,8],[565,6]]},"2883":{"position":[[142,6],[659,6]]}}}],["queue",{"_index":650,"t":{"16":{"position":[[3288,5],[3450,6]]},"222":{"position":[[654,6]]},"236":{"position":[[86,5]]},"240":{"position":[[380,5],[445,6],[574,6],[680,6]]},"244":{"position":[[330,5]]},"262":{"position":[[1340,5],[1934,5],[2654,5]]},"473":{"position":[[384,5]]},"908":{"position":[[40,5]]},"1613":{"position":[[69,5],[192,6]]},"1647":{"position":[[68,5],[373,5],[451,5]]},"1670":{"position":[[322,7],[405,6],[547,6]]},"1979":{"position":[[40,5]]},"2345":{"position":[[5462,7]]},"2722":{"position":[[1790,6]]},"2738":{"position":[[69,5],[192,6]]},"2844":{"position":[[426,5]]},"2862":{"position":[[427,5]]},"2864":{"position":[[53,5],[358,5],[454,5]]},"2889":{"position":[[450,5],[469,5],[568,6],[717,5]]},"3192":{"position":[[40,5]]},"3633":{"position":[[5462,7]]}}}],["queue_engin",{"_index":4941,"t":{"1647":{"position":[[312,15]]},"2864":{"position":[[297,15]]}}}],["queued/rat",{"_index":2905,"t":{"347":{"position":[[887,11]]},"1335":{"position":[[891,11]]}}}],["queueing/timeouts/error",{"_index":4648,"t":{"1266":{"position":[[467,25]]},"2290":{"position":[[477,25]]},"3482":{"position":[[512,25]]}}}],["quic",{"_index":907,"t":{"28":{"position":[[1659,4]]},"94":{"position":[[536,4],[973,4],[1354,4],[1381,4],[1555,5],[1683,5],[2108,4],[2292,4],[2314,4],[2816,4],[2841,4]]},"98":{"position":[[59,4]]},"100":{"position":[[761,4]]},"102":{"position":[[89,4]]},"104":{"position":[[528,4],[611,4]]},"106":{"position":[[64,4],[780,4],[1153,4]]},"108":{"position":[[320,4],[718,4],[800,4],[883,5],[1119,4],[1133,4],[1188,4],[1287,4]]},"110":{"position":[[28,4],[794,4]]},"116":{"position":[[152,4]]},"389":{"position":[[106,4],[196,5]]},"399":{"position":[[111,4]]},"2434":{"position":[[899,4]]},"3612":{"position":[[120,4],[926,4]]}}}],["quic.listen",{"_index":1657,"t":{"106":{"position":[[118,13]]}}}],["quic.listenaddr(s.config.listenaddr",{"_index":1658,"t":{"106":{"position":[[284,36]]}}}],["quic.receivestream",{"_index":1724,"t":{"110":{"position":[[1823,19],[2934,19]]}}}],["quic.sess",{"_index":1670,"t":{"106":{"position":[[701,13]]},"110":{"position":[[1293,13],[2118,13],[2279,13]]},"112":{"position":[[428,13]]}}}],["quick",{"_index":1225,"t":{"52":{"position":[[570,5]]},"232":{"position":[[10,5]]},"634":{"position":[[14,5]]},"700":{"position":[[667,5]]},"1495":{"position":[[2145,5]]},"1577":{"position":[[18,5]]},"1603":{"position":[[14,5]]},"1903":{"position":[[943,5]]},"2662":{"position":[[2160,5]]},"2722":{"position":[[2281,5]]},"2770":{"position":[[18,5]]},"2798":{"position":[[14,5]]},"3096":{"position":[[943,5]]}}}],["quickli",{"_index":1207,"t":{"50":{"position":[[1640,7]]},"122":{"position":[[325,7]]},"407":{"position":[[575,7]]},"473":{"position":[[772,7]]},"479":{"position":[[2750,7],[5927,7]]},"1270":{"position":[[977,7]]},"1495":{"position":[[6070,7]]},"1712":{"position":[[271,7]]},"1827":{"position":[[27,7]]},"2294":{"position":[[1383,7]]},"2482":{"position":[[947,7]]},"2662":{"position":[[6135,7]]},"2901":{"position":[[271,7]]},"3016":{"position":[[27,7]]},"3524":{"position":[[1383,7]]}}}],["quickstart",{"_index":1794,"t":{"122":{"position":[[229,10]]},"224":{"position":[[207,10]]},"1495":{"position":[[0,10]]},"2662":{"position":[[0,10]]}}}],["quictransport",{"_index":1501,"t":{"92":{"position":[[150,13]]},"94":{"position":[[191,13],[2419,13],[2884,13]]}}}],["quictransport('qu",{"_index":1520,"t":{"94":{"position":[[1768,19]]}}}],["quit",{"_index":321,"t":{"8":{"position":[[511,5]]},"98":{"position":[[1573,5]]},"206":{"position":[[162,5]]},"214":{"position":[[788,6]]},"246":{"position":[[3149,5]]},"1262":{"position":[[958,6]]},"2413":{"position":[[967,6]]},"2616":{"position":[[330,5]]},"3345":{"position":[[210,5]]},"3467":{"position":[[2012,5]]},"3580":{"position":[[967,6]]},"3629":{"position":[[789,6]]}}}],["quit\\r\\n",{"_index":3176,"t":{"475":{"position":[[1248,8]]},"981":{"position":[[797,8]]},"2040":{"position":[[797,8]]},"3281":{"position":[[797,8]]}}}],["quorum",{"_index":4151,"t":{"979":{"position":[[1315,6]]},"2036":{"position":[[968,6],[1331,6]]},"3277":{"position":[[968,6],[1331,6]]}}}],["r",{"_index":952,"t":{"32":{"position":[[529,1],[652,1],[693,2]]}}}],["r'/centrifugo/connect",{"_index":4311,"t":{"1065":{"position":[[5277,24]]},"2146":{"position":[[5400,24]]},"3375":{"position":[[5429,24]]}}}],["r.withcontext(newctx",{"_index":960,"t":{"32":{"position":[[656,21]]}}}],["rabbitmq",{"_index":616,"t":{"16":{"position":[[1989,8],[3158,8],[3464,10],[3516,8],[4344,8]]},"222":{"position":[[490,9]]},"244":{"position":[[445,9]]}}}],["rabbitx",{"_index":2495,"t":{"246":{"position":[[152,7],[274,7],[495,7],[518,7],[592,7],[3121,7]]}}}],["raci",{"_index":2906,"t":{"347":{"position":[[1043,4]]},"1335":{"position":[[1047,4]]},"2482":{"position":[[1083,4]]}}}],["radic",{"_index":2431,"t":{"218":{"position":[[563,7]]},"361":{"position":[[413,9]]},"489":{"position":[[702,7]]},"600":{"position":[[304,9]]},"965":{"position":[[498,9]]},"1091":{"position":[[27,7]]},"1381":{"position":[[413,9]]},"1469":{"position":[[710,7]]},"1619":{"position":[[586,9]]},"2102":{"position":[[498,9]]},"2174":{"position":[[27,7]]},"2532":{"position":[[441,7]]},"2610":{"position":[[695,7]]},"2806":{"position":[[586,9]]},"3261":{"position":[[498,9]]},"3403":{"position":[[27,7]]}}}],["raft",{"_index":4174,"t":{"989":{"position":[[1513,5],[1936,4]]},"991":{"position":[[386,4],[433,4]]},"2050":{"position":[[1513,5],[1936,4]]},"2052":{"position":[[386,4],[433,4]]},"3289":{"position":[[1513,5],[1936,4]]},"3291":{"position":[[386,4],[433,4]]}}}],["rail",{"_index":852,"t":{"26":{"position":[[552,6]]},"489":{"position":[[415,6]]},"1469":{"position":[[421,6]]},"2610":{"position":[[421,6]]}}}],["rais",{"_index":406,"t":{"10":{"position":[[434,5]]}}}],["ram",{"_index":143,"t":{"2":{"position":[[2190,3],[2261,3]]},"8":{"position":[[746,3]]},"12":{"position":[[609,3]]},"14":{"position":[[1599,3]]},"52":{"position":[[628,3]]},"309":{"position":[[63,3],[193,4],[225,3],[399,3]]},"1295":{"position":[[63,3],[193,4],[225,3],[399,3]]},"2442":{"position":[[63,3],[193,4],[225,3],[399,3]]}}}],["ran",{"_index":1930,"t":{"128":{"position":[[3342,3]]},"385":{"position":[[551,3]]},"481":{"position":[[276,3]]}}}],["random",{"_index":753,"t":{"18":{"position":[[604,6]]},"383":{"position":[[1641,10]]},"1180":{"position":[[480,6]]},"1285":{"position":[[924,6]]},"2432":{"position":[[924,6]]},"3627":{"position":[[924,6]]}}}],["randomli",{"_index":4768,"t":{"1495":{"position":[[6512,8]]},"2662":{"position":[[6589,8]]}}}],["rang",{"_index":1355,"t":{"70":{"position":[[1210,5]]},"110":{"position":[[4472,5]]},"806":{"position":[[119,5]]},"850":{"position":[[117,5]]},"951":{"position":[[747,5]]},"953":{"position":[[753,5]]},"963":{"position":[[1772,5]]},"1075":{"position":[[213,5]]},"1077":{"position":[[243,5]]},"1857":{"position":[[116,5]]},"2078":{"position":[[747,5]]},"2080":{"position":[[753,5]]},"2100":{"position":[[1772,5]]},"2152":{"position":[[4622,5]]},"2158":{"position":[[187,5]]},"2160":{"position":[[223,5],[277,5],[340,5],[478,5]]},"2371":{"position":[[33,5],[130,5]]},"2373":{"position":[[73,5]]},"2375":{"position":[[88,5],[163,5]]},"3048":{"position":[[116,5]]},"3060":{"position":[[565,5]]},"3245":{"position":[[747,5]]},"3247":{"position":[[753,5]]},"3259":{"position":[[1772,5]]},"3381":{"position":[[4625,5]]},"3387":{"position":[[187,5]]},"3389":{"position":[[223,5],[277,5],[340,5],[478,5]]},"3659":{"position":[[33,5],[130,5]]},"3661":{"position":[[73,5]]},"3663":{"position":[[88,5],[163,5]]}}}],["rare",{"_index":2548,"t":{"246":{"position":[[2978,4]]},"483":{"position":[[1790,4]]},"957":{"position":[[5,4]]},"2084":{"position":[[5,4]]},"2482":{"position":[[483,4]]},"2722":{"position":[[2535,4]]},"3251":{"position":[[5,4]]}}}],["rate",{"_index":121,"t":{"2":{"position":[[1776,4]]},"16":{"position":[[3280,4]]},"18":{"position":[[635,4],[799,6]]},"254":{"position":[[1492,6],[1513,6]]},"307":{"position":[[76,5]]},"481":{"position":[[2252,4],[2334,4],[5628,4]]},"618":{"position":[[560,7],[632,7],[733,7],[1497,7]]},"794":{"position":[[206,4]]},"804":{"position":[[107,4]]},"1283":{"position":[[525,4]]},"1293":{"position":[[76,5]]},"1611":{"position":[[283,4]]},"1676":{"position":[[990,7],[1053,7],[1088,7],[1153,7],[1213,7],[1318,7]]},"1678":{"position":[[886,7],[951,7],[1010,7],[1115,7]]},"1680":{"position":[[1367,7],[1432,7],[1491,7],[1596,7]]},"1682":{"position":[[1510,7]]},"1845":{"position":[[203,4]]},"1855":{"position":[[119,4]]},"2086":{"position":[[591,4]]},"2430":{"position":[[525,4]]},"2440":{"position":[[76,5]]},"2482":{"position":[[915,4]]},"2722":{"position":[[1757,4]]},"2736":{"position":[[283,4]]},"2744":{"position":[[23,4],[442,7]]},"2746":{"position":[[22,4]]},"2750":{"position":[[157,4],[182,4]]},"2778":{"position":[[303,4]]},"2830":{"position":[[10,4],[161,4],[263,4],[993,7],[1056,7],[1091,7],[1156,7],[1216,7],[1321,7],[1862,4]]},"2832":{"position":[[16,4],[72,4],[100,4],[218,4],[264,4],[316,4],[454,4],[512,4],[553,4],[892,7],[957,7],[1016,7],[1121,7]]},"2834":{"position":[[17,4],[75,4],[333,4],[687,4],[825,4],[947,4],[1001,4],[1373,7],[1438,7],[1497,7],[1602,7],[1649,4],[1820,4],[1954,4],[2184,4]]},"2836":{"position":[[35,4],[334,4],[1510,7]]},"2889":{"position":[[293,4]]},"3036":{"position":[[203,4]]},"3046":{"position":[[119,4]]},"3253":{"position":[[591,4]]},"3324":{"position":[[256,4],[504,4]]},"3625":{"position":[[525,4]]}}}],["rate/s",{"_index":3432,"t":{"481":{"position":[[5283,10]]}}}],["rate_limit",{"_index":5477,"t":{"2744":{"position":[[686,10]]}}}],["rate_limit_test",{"_index":5470,"t":{"2744":{"position":[[404,18]]}}}],["ratelimit.new(100",{"_index":3392,"t":{"481":{"position":[[2408,18]]}}}],["ratelimit.new(100000",{"_index":3395,"t":{"481":{"position":[[2538,21]]}}}],["ratelimit.per(time.millisecond))for",{"_index":3393,"t":{"481":{"position":[[2427,35]]}}}],["raw",{"_index":681,"t":{"16":{"position":[[4555,3]]},"262":{"position":[[631,3],[2281,3]]},"1025":{"position":[[128,3]]},"1065":{"position":[[3897,3],[4155,3]]},"1067":{"position":[[1562,3]]},"1071":{"position":[[3066,3],[3283,3]]},"1073":{"position":[[2974,3]]},"1283":{"position":[[28,3],[595,3]]},"1285":{"position":[[542,3]]},"1631":{"position":[[332,3]]},"1639":{"position":[[967,3]]},"1809":{"position":[[128,3]]},"2146":{"position":[[4014,3],[4272,3]]},"2148":{"position":[[1562,3]]},"2152":{"position":[[2998,3],[3215,3]]},"2154":{"position":[[2974,3]]},"2156":{"position":[[3029,3]]},"2430":{"position":[[28,3],[595,3]]},"2432":{"position":[[542,3]]},"2848":{"position":[[332,3]]},"2856":{"position":[[967,3]]},"2998":{"position":[[128,3]]},"3375":{"position":[[4043,3],[4301,3]]},"3377":{"position":[[1562,3]]},"3381":{"position":[[2801,3],[3018,3]]},"3383":{"position":[[2974,3]]},"3385":{"position":[[3021,3]]},"3625":{"position":[[28,3],[595,3]]},"3627":{"position":[[542,3]]}}}],["rawpixel.com",{"_index":2284,"t":{"192":{"position":[[847,12]]}}}],["rbac",{"_index":3029,"t":{"393":{"position":[[567,4]]}}}],["rc.2",{"_index":3229,"t":{"477":{"position":[[4465,5]]}}}],["rdb",{"_index":2941,"t":{"361":{"position":[[1273,3]]},"1381":{"position":[[1278,3]]}}}],["re",{"_index":541,"t":{"14":{"position":[[376,2]]},"20":{"position":[[89,2]]},"42":{"position":[[1779,2]]},"198":{"position":[[587,2]]},"222":{"position":[[680,2]]},"244":{"position":[[152,2]]},"264":{"position":[[333,2]]},"277":{"position":[[550,2]]},"283":{"position":[[727,4],[2001,4],[2525,4]]},"287":{"position":[[1669,4]]},"475":{"position":[[1785,2]]},"477":{"position":[[1164,2],[3944,2],[6614,2]]},"479":{"position":[[1134,3]]},"525":{"position":[[669,2]]},"965":{"position":[[1399,2]]},"1353":{"position":[[607,2]]},"1433":{"position":[[671,2]]},"1629":{"position":[[333,2]]},"2102":{"position":[[1571,2]]},"2353":{"position":[[228,2]]},"2355":{"position":[[3656,2]]},"2502":{"position":[[494,2]]},"2546":{"position":[[648,2]]},"2846":{"position":[[333,2]]},"3018":{"position":[[633,2]]},"3261":{"position":[[1571,2]]},"3347":{"position":[[3410,2],[3443,2]]},"3639":{"position":[[810,3],[1117,2]]},"3641":{"position":[[228,2]]},"3643":{"position":[[3656,2]]},"3655":{"position":[[862,3],[1301,2]]}}}],["re_path('room/(?p[a",{"_index":1981,"t":{"130":{"position":[[2361,30]]},"136":{"position":[[449,30]]}}}],["re_pathfrom",{"_index":1980,"t":{"130":{"position":[[2282,11]]},"136":{"position":[[370,11]]}}}],["reach",{"_index":1336,"t":{"70":{"position":[[693,8],[1511,8]]},"216":{"position":[[783,5]]},"327":{"position":[[39,8]]},"614":{"position":[[584,5]]},"963":{"position":[[1255,8],[2073,8]]},"1075":{"position":[[437,5]]},"1313":{"position":[[39,8]]},"1505":{"position":[[281,5]]},"1668":{"position":[[757,5]]},"1767":{"position":[[323,5]]},"1769":{"position":[[311,5]]},"1797":{"position":[[1322,5]]},"2100":{"position":[[1255,8],[2073,8]]},"2156":{"position":[[1206,5]]},"2158":{"position":[[396,5]]},"2460":{"position":[[39,8]]},"2636":{"position":[[281,5]]},"2780":{"position":[[560,5]]},"2887":{"position":[[757,5]]},"3259":{"position":[[1255,8],[2073,8]]},"3324":{"position":[[937,8],[3360,8]]},"3385":{"position":[[1179,5]]},"3387":{"position":[[396,5]]},"3490":{"position":[[642,5]]}}}],["reached\")}conns.add(conn",{"_index":428,"t":{"10":{"position":[[888,25]]}}}],["react",{"_index":1007,"t":{"34":{"position":[[1915,8]]},"50":{"position":[[958,5]]},"168":{"position":[[142,5]]},"180":{"position":[[1537,5]]},"246":{"position":[[3243,5]]},"293":{"position":[[442,5]]},"299":{"position":[[726,5]]},"301":{"position":[[24,5],[212,5],[242,6],[598,5],[637,6],[693,7],[1177,6],[1213,5],[1420,6],[1574,7],[4425,5]]},"345":{"position":[[114,5]]},"505":{"position":[[335,5]]},"507":{"position":[[238,5]]},"1083":{"position":[[72,5]]},"1101":{"position":[[398,5]]},"1333":{"position":[[114,5]]},"1367":{"position":[[335,5]]},"1369":{"position":[[238,5]]},"1625":{"position":[[443,6]]},"2166":{"position":[[72,5]]},"2256":{"position":[[40,5]]},"2480":{"position":[[114,5]]},"2518":{"position":[[340,5]]},"2520":{"position":[[238,5]]},"2842":{"position":[[443,6]]},"3395":{"position":[[72,5]]},"3490":{"position":[[97,5]]}}}],["react';import",{"_index":2820,"t":{"301":{"position":[[1456,14]]}}}],["react'import",{"_index":2803,"t":{"301":{"position":[[609,13]]}}}],["react.strictmod",{"_index":2815,"t":{"301":{"position":[[1013,18],[1040,19]]}}}],["react/vue/whatev",{"_index":2098,"t":{"146":{"position":[[419,18]]}}}],["reactcd",{"_index":2796,"t":{"301":{"position":[[119,7]]}}}],["reactdom",{"_index":2804,"t":{"301":{"position":[[623,8]]}}}],["reaction",{"_index":3072,"t":{"457":{"position":[[291,8]]},"1164":{"position":[[3632,8]]},"1457":{"position":[[288,8]]},"2242":{"position":[[171,8]]},"2598":{"position":[[291,8]]},"3553":{"position":[[171,8]]}}}],["reactkeycloakprovid",{"_index":2806,"t":{"301":{"position":[[664,21],[961,22],[1060,26],[1228,21]]}}}],["read",{"_index":200,"t":{"2":{"position":[[3454,4]]},"4":{"position":[[838,8]]},"8":{"position":[[963,4]]},"12":{"position":[[254,4],[952,4]]},"24":{"position":[[211,6]]},"34":{"position":[[1391,4]]},"84":{"position":[[8,7]]},"94":{"position":[[153,4],[2920,4]]},"108":{"position":[[274,7]]},"110":{"position":[[2328,4],[3023,4]]},"112":{"position":[[71,7]]},"122":{"position":[[182,4]]},"142":{"position":[[875,7]]},"168":{"position":[[192,4],[248,4],[273,4]]},"170":{"position":[[320,7]]},"172":{"position":[[540,7]]},"270":{"position":[[630,7]]},"335":{"position":[[600,5],[684,4],[790,4],[802,4]]},"341":{"position":[[188,4]]},"391":{"position":[[1407,4]]},"465":{"position":[[220,4]]},"473":{"position":[[2468,4]]},"477":{"position":[[2633,7]]},"479":{"position":[[2477,7]]},"481":{"position":[[6390,4]]},"485":{"position":[[2107,4]]},"692":{"position":[[184,4]]},"694":{"position":[[471,4]]},"742":{"position":[[1115,4]]},"808":{"position":[[482,7]]},"826":{"position":[[86,4]]},"898":{"position":[[85,4]]},"993":{"position":[[184,4]]},"1164":{"position":[[500,7],[1891,4]]},"1277":{"position":[[55,4]]},"1323":{"position":[[677,5],[761,4],[867,4],[879,4]]},"1329":{"position":[[188,4]]},"1465":{"position":[[220,4]]},"1495":{"position":[[3355,7],[10396,4]]},"1499":{"position":[[639,4]]},"1511":{"position":[[141,4]]},"1761":{"position":[[1115,4]]},"1767":{"position":[[303,4]]},"1769":{"position":[[291,4]]},"1795":{"position":[[530,4]]},"1797":{"position":[[1302,4]]},"1863":{"position":[[552,4]]},"1873":{"position":[[184,4]]},"1965":{"position":[[85,4]]},"2048":{"position":[[239,4]]},"2054":{"position":[[184,4]]},"2244":{"position":[[215,7],[1689,4]]},"2383":{"position":[[174,4]]},"2424":{"position":[[55,4]]},"2470":{"position":[[677,5],[761,4],[867,4],[879,4]]},"2476":{"position":[[188,4]]},"2606":{"position":[[229,4]]},"2630":{"position":[[639,4]]},"2642":{"position":[[141,4]]},"2662":{"position":[[3372,7],[10521,4]]},"2984":{"position":[[530,4]]},"3054":{"position":[[552,4]]},"3064":{"position":[[184,4]]},"3178":{"position":[[85,4]]},"3293":{"position":[[184,4]]},"3345":{"position":[[223,4]]},"3555":{"position":[[215,7],[1689,4]]},"3619":{"position":[[55,4]]},"3671":{"position":[[174,4]]}}}],["read/writ",{"_index":3377,"t":{"481":{"position":[[776,10],[6703,10]]}}}],["readabl",{"_index":2426,"t":{"216":{"position":[[508,8]]},"381":{"position":[[122,8]]},"385":{"position":[[1312,8]]},"403":{"position":[[190,8]]},"1164":{"position":[[1202,9]]},"1285":{"position":[[111,8],[467,8]]},"1511":{"position":[[577,8]]},"2244":{"position":[[892,9]]},"2248":{"position":[[172,8]]},"2258":{"position":[[534,8]]},"2345":{"position":[[4617,8]]},"2355":{"position":[[4234,8]]},"2432":{"position":[[111,8],[467,8]]},"2560":{"position":[[200,8]]},"2642":{"position":[[577,8]]},"3492":{"position":[[534,8]]},"3555":{"position":[[892,9]]},"3559":{"position":[[172,8]]},"3627":{"position":[[111,8],[467,8]]},"3633":{"position":[[4617,8]]},"3643":{"position":[[4234,8]]}}}],["readablestream",{"_index":2979,"t":{"379":{"position":[[2028,14],[2320,15]]}}}],["reader",{"_index":825,"t":{"22":{"position":[[45,6]]},"50":{"position":[[31,7]]},"108":{"position":[[1395,6]]},"110":{"position":[[3074,6]]},"146":{"position":[[758,6]]},"246":{"position":[[3793,7]]},"281":{"position":[[62,6]]},"485":{"position":[[909,7]]},"908":{"position":[[74,6]]},"1979":{"position":[[74,6]]},"3192":{"position":[[74,6]]}}}],["reader.read(buf",{"_index":1745,"t":{"110":{"position":[[3496,16]]}}}],["readi",{"_index":244,"t":{"4":{"position":[[536,5]]},"14":{"position":[[468,5]]},"44":{"position":[[1361,5]]},"58":{"position":[[458,5]]},"76":{"position":[[212,5]]},"116":{"position":[[321,5]]},"150":{"position":[[544,5]]},"222":{"position":[[605,5]]},"347":{"position":[[2137,5]]},"379":{"position":[[1858,5]]},"417":{"position":[[154,5]]},"489":{"position":[[333,5]]},"523":{"position":[[560,5]]},"989":{"position":[[645,5],[1750,5]]},"993":{"position":[[873,5]]},"1168":{"position":[[76,5]]},"1335":{"position":[[2141,5]]},"1401":{"position":[[156,5]]},"1431":{"position":[[570,5]]},"1469":{"position":[[339,5]]},"2050":{"position":[[645,5],[1750,5]]},"2054":{"position":[[873,5]]},"2482":{"position":[[2383,5]]},"2498":{"position":[[88,5]]},"2558":{"position":[[157,5]]},"2610":{"position":[[338,5]]},"2614":{"position":[[585,5]]},"2722":{"position":[[389,5]]},"3289":{"position":[[645,5],[1750,5]]},"3293":{"position":[[873,5]]},"3349":{"position":[[1948,5],[4346,5]]}}}],["readm",{"_index":1243,"t":{"56":{"position":[[186,6]]},"158":{"position":[[209,6]]},"277":{"position":[[442,6]]},"479":{"position":[[335,6]]},"529":{"position":[[82,6]]},"1353":{"position":[[499,6]]},"1437":{"position":[[82,6]]},"2258":{"position":[[998,7]]},"2546":{"position":[[532,6]]},"2620":{"position":[[82,6]]},"3492":{"position":[[998,7]]}}}],["real",{"_index":123,"t":{"2":{"position":[[1793,4]]},"4":{"position":[[598,4],[795,4]]},"16":{"position":[[322,4],[1393,4],[2379,4],[3188,4],[3562,4],[3975,4],[4374,4],[5190,4],[7309,4]]},"22":{"position":[[106,4]]},"24":{"position":[[89,4]]},"26":{"position":[[637,4],[1279,4]]},"28":{"position":[[39,4],[405,4],[430,4],[490,4],[1019,4],[1324,4],[2226,4]]},"34":{"position":[[418,4],[543,4]]},"44":{"position":[[2077,4]]},"58":{"position":[[172,4],[712,4]]},"60":{"position":[[314,4],[454,4]]},"62":{"position":[[369,4],[489,4],[549,4],[1600,4],[1684,4],[1740,4]]},"68":{"position":[[223,4],[1107,4]]},"70":{"position":[[783,4]]},"94":{"position":[[1130,4]]},"120":{"position":[[76,4],[652,4],[795,4]]},"132":{"position":[[1539,4]]},"134":{"position":[[935,4],[1360,4]]},"136":{"position":[[2037,4],[2103,4]]},"144":{"position":[[97,4],[297,4]]},"148":{"position":[[77,4],[300,4],[967,4]]},"160":{"position":[[863,4]]},"162":{"position":[[1083,4]]},"172":{"position":[[48,4],[279,4]]},"174":{"position":[[170,4],[258,4],[851,4]]},"176":{"position":[[342,4]]},"194":{"position":[[460,4],[850,4],[1033,4]]},"200":{"position":[[39,4]]},"218":{"position":[[349,4]]},"222":{"position":[[949,4]]},"236":{"position":[[296,4],[989,4],[1062,4],[1182,4],[1268,4]]},"244":{"position":[[675,4]]},"246":{"position":[[1155,4],[1373,4],[1889,4],[3460,4]]},"250":{"position":[[76,4],[466,4]]},"252":{"position":[[61,4],[361,4],[542,4],[1341,4]]},"256":{"position":[[1438,4]]},"262":{"position":[[2861,4],[2912,4]]},"266":{"position":[[548,4]]},"277":{"position":[[272,4]]},"279":{"position":[[25,4],[418,4],[1266,4]]},"281":{"position":[[492,4]]},"283":{"position":[[2289,4]]},"287":{"position":[[900,4],[1237,4]]},"301":{"position":[[4325,4]]},"307":{"position":[[30,4]]},"309":{"position":[[276,4]]},"335":{"position":[[53,4],[112,4],[389,4]]},"343":{"position":[[61,4],[119,4]]},"351":{"position":[[666,4]]},"355":{"position":[[347,4],[958,4],[1116,4],[1164,4],[1400,4],[1558,4],[1606,4]]},"359":{"position":[[755,4]]},"373":{"position":[[444,4],[843,4],[1026,4]]},"375":{"position":[[667,4]]},"377":{"position":[[725,4],[1031,4]]},"379":{"position":[[610,4],[2439,4]]},"389":{"position":[[1010,4]]},"391":{"position":[[419,4]]},"403":{"position":[[707,4]]},"407":{"position":[[1267,4]]},"411":{"position":[[425,4]]},"413":{"position":[[424,4]]},"419":{"position":[[262,4],[403,4]]},"425":{"position":[[72,4]]},"443":{"position":[[211,4],[268,4]]},"457":{"position":[[208,4]]},"467":{"position":[[101,4]]},"473":{"position":[[3115,4]]},"481":{"position":[[3824,4]]},"489":{"position":[[190,4],[522,4],[634,4]]},"560":{"position":[[246,4]]},"576":{"position":[[198,4],[841,4]]},"600":{"position":[[266,4]]},"604":{"position":[[456,4]]},"640":{"position":[[192,4]]},"700":{"position":[[685,4]]},"704":{"position":[[320,4]]},"718":{"position":[[532,4]]},"734":{"position":[[314,4]]},"935":{"position":[[240,4]]},"963":{"position":[[1345,4]]},"993":{"position":[[1021,4]]},"1001":{"position":[[868,4],[1122,4]]},"1003":{"position":[[428,4],[672,4]]},"1061":{"position":[[426,4]]},"1065":{"position":[[4853,4]]},"1101":{"position":[[7,4]]},"1129":{"position":[[1016,4]]},"1268":{"position":[[329,4]]},"1293":{"position":[[30,4]]},"1295":{"position":[[276,4]]},"1323":{"position":[[53,4],[112,4],[466,4]]},"1331":{"position":[[61,4],[119,4]]},"1339":{"position":[[666,4]]},"1347":{"position":[[347,4],[958,4],[1116,4],[1164,4],[1400,4],[1558,4],[1606,4]]},"1349":{"position":[[319,4]]},"1353":{"position":[[244,4]]},"1379":{"position":[[755,4]]},"1395":{"position":[[441,4]]},"1397":{"position":[[428,4]]},"1401":{"position":[[339,4]]},"1403":{"position":[[381,4],[522,4],[678,4]]},"1409":{"position":[[72,4]]},"1457":{"position":[[205,4]]},"1469":{"position":[[203,4],[534,4],[642,4],[967,4],[1501,4]]},"1475":{"position":[[706,4]]},"1495":{"position":[[383,4],[3381,4],[3684,4],[4183,4],[6708,4],[8250,4],[8782,4],[10193,4]]},"1533":{"position":[[545,4]]},"1583":{"position":[[181,4],[195,4],[341,4]]},"1617":{"position":[[192,4]]},"1619":{"position":[[266,4]]},"1629":{"position":[[469,4]]},"1676":{"position":[[135,4],[1356,4]]},"1743":{"position":[[215,4]]},"1787":{"position":[[837,4]]},"1803":{"position":[[167,4]]},"1827":{"position":[[439,4]]},"1869":{"position":[[155,4],[1049,4]]},"1881":{"position":[[314,4]]},"1897":{"position":[[885,4]]},"1903":{"position":[[194,4],[961,4]]},"1937":{"position":[[966,4]]},"1977":{"position":[[127,4],[200,4]]},"2010":{"position":[[348,4]]},"2054":{"position":[[1021,4]]},"2100":{"position":[[1345,4]]},"2108":{"position":[[868,4],[1122,4]]},"2110":{"position":[[428,4],[672,4]]},"2122":{"position":[[36,4],[455,4]]},"2124":{"position":[[257,4]]},"2126":{"position":[[70,4],[360,4]]},"2142":{"position":[[426,4]]},"2146":{"position":[[4976,4]]},"2152":{"position":[[4522,4]]},"2210":{"position":[[1058,4]]},"2258":{"position":[[11,4],[1020,4]]},"2292":{"position":[[333,4]]},"2377":{"position":[[173,4]]},"2436":{"position":[[347,4],[958,4],[1116,4],[1164,4],[1400,4],[1558,4],[1606,4]]},"2440":{"position":[[30,4]]},"2442":{"position":[[276,4]]},"2470":{"position":[[53,4],[112,4],[466,4]]},"2478":{"position":[[61,4],[119,4]]},"2486":{"position":[[666,4]]},"2494":{"position":[[317,4]]},"2502":{"position":[[454,4]]},"2504":{"position":[[351,4],[503,4],[1133,4]]},"2506":{"position":[[1017,4]]},"2530":{"position":[[307,4],[420,4],[458,4],[1346,4],[1477,4],[1598,4],[1819,4],[2746,4]]},"2546":{"position":[[252,4]]},"2552":{"position":[[445,4]]},"2554":{"position":[[444,4]]},"2558":{"position":[[336,4]]},"2560":{"position":[[496,4],[648,4],[800,4]]},"2566":{"position":[[72,4]]},"2598":{"position":[[208,4]]},"2610":{"position":[[220,4],[524,4],[630,4],[958,4],[1512,4],[1809,4]]},"2662":{"position":[[388,4],[3400,4],[3717,4],[4243,4],[6789,4],[8352,4],[8899,4],[10313,4]]},"2668":{"position":[[706,4]]},"2702":{"position":[[545,4]]},"2778":{"position":[[181,4],[195,4],[342,4]]},"2804":{"position":[[192,4]]},"2806":{"position":[[266,4]]},"2830":{"position":[[135,4],[1359,4]]},"2846":{"position":[[469,4]]},"2862":{"position":[[830,4]]},"2932":{"position":[[215,4]]},"2976":{"position":[[981,4]]},"2992":{"position":[[167,4]]},"3016":{"position":[[439,4]]},"3060":{"position":[[176,4],[1134,4]]},"3072":{"position":[[322,4],[594,4]]},"3090":{"position":[[885,4]]},"3096":{"position":[[194,4],[961,4]]},"3134":{"position":[[966,4]]},"3190":{"position":[[100,4],[173,4]]},"3198":{"position":[[663,4]]},"3227":{"position":[[348,4]]},"3259":{"position":[[1345,4]]},"3293":{"position":[[1021,4]]},"3309":{"position":[[868,4],[1122,4]]},"3311":{"position":[[428,4],[672,4]]},"3337":{"position":[[29,4],[715,4],[996,4]]},"3339":{"position":[[257,4]]},"3341":{"position":[[70,4],[358,4]]},"3347":{"position":[[631,4],[1164,4]]},"3371":{"position":[[426,4]]},"3375":{"position":[[5005,4]]},"3381":{"position":[[4525,4]]},"3451":{"position":[[1118,4]]},"3484":{"position":[[333,4]]},"3492":{"position":[[11,4],[1020,4]]},"3665":{"position":[[173,4]]}}}],["realist",{"_index":5638,"t":{"3345":{"position":[[1417,9]]}}}],["realiti",{"_index":1457,"t":{"86":{"position":[[1940,7]]},"218":{"position":[[908,7]]},"246":{"position":[[4209,7]]}}}],["realli",{"_index":765,"t":{"18":{"position":[[1392,6]]},"20":{"position":[[753,6]]},"50":{"position":[[2036,6]]},"70":{"position":[[766,6]]},"170":{"position":[[116,6]]},"188":{"position":[[878,6]]},"216":{"position":[[333,6]]},"246":{"position":[[4046,6]]},"270":{"position":[[3,6]]},"379":{"position":[[323,6]]},"383":{"position":[[1094,6]]},"387":{"position":[[298,6]]},"391":{"position":[[194,6]]},"963":{"position":[[1328,6]]},"1087":{"position":[[1106,6]]},"1129":{"position":[[633,6]]},"1469":{"position":[[456,6]]},"1475":{"position":[[726,6]]},"1741":{"position":[[2087,6]]},"1899":{"position":[[462,6]]},"1923":{"position":[[271,6]]},"2100":{"position":[[1328,6]]},"2170":{"position":[[1106,6]]},"2210":{"position":[[680,6]]},"2296":{"position":[[847,6]]},"2668":{"position":[[726,6]]},"2930":{"position":[[2087,6]]},"3092":{"position":[[462,6]]},"3116":{"position":[[271,6]]},"3259":{"position":[[1328,6]]},"3345":{"position":[[289,6],[636,6]]},"3347":{"position":[[3282,6]]},"3399":{"position":[[1106,6]]},"3451":{"position":[[740,6]]},"3482":{"position":[[139,6]]},"3486":{"position":[[847,6]]}}}],["realm",{"_index":2772,"t":{"297":{"position":[[312,5]]},"301":{"position":[[860,6]]}}}],["reason",{"_index":5,"t":{"2":{"position":[[74,10]]},"16":{"position":[[1449,10],[2818,10]]},"18":{"position":[[1602,10]]},"28":{"position":[[890,6]]},"34":{"position":[[2026,6]]},"52":{"position":[[195,10]]},"74":{"position":[[1788,6]]},"94":{"position":[[2785,6]]},"106":{"position":[[874,7]]},"138":{"position":[[1213,10]]},"146":{"position":[[719,8]]},"178":{"position":[[46,10],[1074,7]]},"182":{"position":[[1740,10]]},"198":{"position":[[223,7]]},"200":{"position":[[648,10]]},"214":{"position":[[959,7]]},"287":{"position":[[251,6],[1801,7]]},"319":{"position":[[208,10]]},"321":{"position":[[556,7]]},"327":{"position":[[17,6]]},"335":{"position":[[320,10]]},"341":{"position":[[836,10]]},"345":{"position":[[359,9]]},"347":{"position":[[499,6],[1265,10],[1780,10]]},"349":{"position":[[213,7]]},"351":{"position":[[44,10],[692,10]]},"361":{"position":[[561,10]]},"387":{"position":[[921,6]]},"399":{"position":[[1944,10]]},"477":{"position":[[7030,6]]},"481":{"position":[[1751,10],[5235,10],[6441,10]]},"485":{"position":[[689,10]]},"644":{"position":[[444,10]]},"648":{"position":[[382,6],[644,10],[893,6]]},"652":{"position":[[917,10]]},"654":{"position":[[976,10]]},"662":{"position":[[422,10]]},"666":{"position":[[280,6],[507,10],[756,6]]},"670":{"position":[[477,10]]},"684":{"position":[[54,6]]},"712":{"position":[[253,10]]},"716":{"position":[[607,10]]},"718":{"position":[[134,6],[349,10]]},"742":{"position":[[838,10]]},"808":{"position":[[78,7],[527,8]]},"812":{"position":[[12,7]]},"814":{"position":[[12,7]]},"816":{"position":[[12,7]]},"818":{"position":[[12,7]]},"820":{"position":[[12,7]]},"822":{"position":[[12,7]]},"824":{"position":[[12,7]]},"826":{"position":[[12,7]]},"828":{"position":[[12,7]]},"830":{"position":[[12,7]]},"832":{"position":[[12,7]]},"834":{"position":[[12,7]]},"836":{"position":[[12,7]]},"902":{"position":[[246,10]]},"937":{"position":[[265,10]]},"945":{"position":[[326,7]]},"955":{"position":[[129,10]]},"965":{"position":[[733,10]]},"971":{"position":[[599,10]]},"975":{"position":[[1481,10]]},"1043":{"position":[[472,6]]},"1077":{"position":[[174,9],[503,6],[739,7]]},"1117":{"position":[[643,6],[672,6]]},"1119":{"position":[[468,6]]},"1162":{"position":[[775,6]]},"1180":{"position":[[100,6],[139,6],[271,9],[332,6],[529,6]]},"1190":{"position":[[121,6]]},"1266":{"position":[[269,6]]},"1305":{"position":[[208,10]]},"1307":{"position":[[556,7]]},"1313":{"position":[[17,6]]},"1323":{"position":[[397,10]]},"1329":{"position":[[836,10]]},"1333":{"position":[[359,9]]},"1335":{"position":[[503,6],[1269,10],[1784,10]]},"1337":{"position":[[213,7]]},"1339":{"position":[[44,10],[692,10]]},"1381":{"position":[[561,10]]},"1477":{"position":[[106,10]]},"1491":{"position":[[119,10]]},"1513":{"position":[[177,7]]},"1682":{"position":[[361,7]]},"1690":{"position":[[444,10]]},"1694":{"position":[[382,6],[644,10],[893,6]]},"1699":{"position":[[917,10]]},"1702":{"position":[[1022,10]]},"1706":{"position":[[422,10]]},"1710":{"position":[[280,6],[507,10],[756,6]]},"1715":{"position":[[477,10]]},"1735":{"position":[[54,6]]},"1761":{"position":[[838,10]]},"1859":{"position":[[78,7]]},"1861":{"position":[[315,6]]},"1863":{"position":[[1103,6]]},"1891":{"position":[[253,10]]},"1895":{"position":[[708,10]]},"1897":{"position":[[254,6],[485,10]]},"1969":{"position":[[246,10]]},"1983":{"position":[[224,10]]},"2012":{"position":[[265,10]]},"2020":{"position":[[326,7]]},"2026":{"position":[[586,10]]},"2030":{"position":[[1629,10]]},"2082":{"position":[[129,10]]},"2102":{"position":[[730,10]]},"2134":{"position":[[472,6]]},"2146":{"position":[[6277,9],[6352,6]]},"2152":{"position":[[4527,6]]},"2160":{"position":[[154,9],[176,7],[653,6],[939,7]]},"2198":{"position":[[609,6],[638,6]]},"2200":{"position":[[434,6]]},"2248":{"position":[[188,7]]},"2290":{"position":[[275,6]]},"2345":{"position":[[4633,6],[4701,6]]},"2355":{"position":[[4250,6],[4318,6]]},"2373":{"position":[[364,8]]},"2375":{"position":[[361,8]]},"2434":{"position":[[2796,10]]},"2452":{"position":[[208,10]]},"2454":{"position":[[556,7]]},"2460":{"position":[[17,6]]},"2470":{"position":[[397,10]]},"2476":{"position":[[836,10]]},"2480":{"position":[[359,9]]},"2482":{"position":[[596,6],[1299,10],[2026,10]]},"2484":{"position":[[213,7]]},"2486":{"position":[[44,10],[692,10]]},"2532":{"position":[[598,10]]},"2644":{"position":[[177,7]]},"2670":{"position":[[106,10]]},"2684":{"position":[[119,10]]},"2814":{"position":[[444,10]]},"2818":{"position":[[382,6],[644,10],[893,6]]},"2823":{"position":[[881,10]]},"2826":{"position":[[986,10]]},"2836":{"position":[[361,7]]},"2883":{"position":[[1536,10]]},"2895":{"position":[[422,10]]},"2899":{"position":[[280,6],[507,10],[756,6]]},"2904":{"position":[[441,10]]},"2924":{"position":[[54,6]]},"2950":{"position":[[893,10]]},"3050":{"position":[[78,7]]},"3052":{"position":[[315,6]]},"3054":{"position":[[1103,6]]},"3070":{"position":[[781,9]]},"3082":{"position":[[253,10]]},"3086":{"position":[[588,10]]},"3088":{"position":[[708,10]]},"3090":{"position":[[254,6],[485,10]]},"3182":{"position":[[246,10]]},"3196":{"position":[[224,10]]},"3231":{"position":[[265,10]]},"3239":{"position":[[326,7]]},"3249":{"position":[[129,10]]},"3261":{"position":[[730,10]]},"3267":{"position":[[586,10]]},"3271":{"position":[[1595,10]]},"3347":{"position":[[2040,6],[2167,6]]},"3363":{"position":[[472,6]]},"3375":{"position":[[6306,9],[6381,6]]},"3381":{"position":[[4530,6]]},"3389":{"position":[[154,9],[176,7],[653,6],[939,7]]},"3439":{"position":[[543,6],[572,6]]},"3441":{"position":[[427,6]]},"3482":{"position":[[310,6]]},"3559":{"position":[[188,7]]},"3612":{"position":[[2995,10]]},"3629":{"position":[[960,7]]},"3633":{"position":[[4633,6],[4701,6]]},"3643":{"position":[[4250,6],[4318,6]]},"3661":{"position":[[364,8]]},"3663":{"position":[[361,8]]}}}],["reason=\"invalid",{"_index":4764,"t":{"1495":{"position":[[5695,15]]},"2662":{"position":[[5757,15]]}}}],["receipt",{"_index":5544,"t":{"2962":{"position":[[233,7]]}}}],["receiv",{"_index":635,"t":{"16":{"position":[[2631,7]]},"44":{"position":[[387,7]]},"68":{"position":[[1099,7]]},"76":{"position":[[36,8]]},"138":{"position":[[1881,7]]},"162":{"position":[[1075,7]]},"166":{"position":[[1051,7]]},"172":{"position":[[250,7]]},"182":{"position":[[727,8],[902,9]]},"188":{"position":[[49,9]]},"254":{"position":[[175,7],[957,8]]},"260":{"position":[[55,8]]},"319":{"position":[[865,7]]},"321":{"position":[[288,7],[1075,8]]},"335":{"position":[[811,10]]},"337":{"position":[[97,8]]},"339":{"position":[[796,9]]},"343":{"position":[[343,7]]},"355":{"position":[[338,8]]},"367":{"position":[[263,7]]},"369":{"position":[[691,9]]},"383":{"position":[[253,7]]},"389":{"position":[[344,9]]},"425":{"position":[[64,7]]},"471":{"position":[[380,9]]},"505":{"position":[[89,7]]},"610":{"position":[[252,9]]},"700":{"position":[[169,7]]},"754":{"position":[[174,9]]},"764":{"position":[[855,8]]},"796":{"position":[[86,8]]},"810":{"position":[[149,8]]},"846":{"position":[[88,8]]},"912":{"position":[[181,8]]},"932":{"position":[[286,9]]},"965":{"position":[[2931,8]]},"1065":{"position":[[527,9]]},"1168":{"position":[[327,8],[935,8],[1308,8],[1365,7]]},"1170":{"position":[[67,9]]},"1178":{"position":[[251,7]]},"1182":{"position":[[664,8]]},"1184":{"position":[[1995,8],[2449,8],[2688,8]]},"1192":{"position":[[118,7]]},"1200":{"position":[[190,10],[280,8]]},"1207":{"position":[[1239,8]]},"1217":{"position":[[184,8]]},"1237":{"position":[[70,8]]},"1255":{"position":[[173,8]]},"1260":{"position":[[70,8]]},"1262":{"position":[[1282,7]]},"1268":{"position":[[321,7]]},"1270":{"position":[[503,7]]},"1275":{"position":[[56,8]]},"1305":{"position":[[865,7]]},"1307":{"position":[[288,7],[1075,8]]},"1323":{"position":[[888,10]]},"1325":{"position":[[97,8]]},"1327":{"position":[[796,9]]},"1331":{"position":[[343,7]]},"1347":{"position":[[338,8]]},"1367":{"position":[[89,7]]},"1387":{"position":[[263,7]]},"1389":{"position":[[691,9]]},"1409":{"position":[[64,7]]},"1415":{"position":[[174,8]]},"1469":{"position":[[1406,7]]},"1473":{"position":[[335,7]]},"1479":{"position":[[887,7]]},"1493":{"position":[[604,7]]},"1495":{"position":[[188,9],[3724,9],[9547,8],[9633,7]]},"1545":{"position":[[252,9]]},"1635":{"position":[[653,8]]},"1676":{"position":[[1403,8]]},"1743":{"position":[[300,7]]},"1773":{"position":[[174,9]]},"1783":{"position":[[855,8]]},"1847":{"position":[[83,8]]},"1863":{"position":[[28,9],[1279,8]]},"1865":{"position":[[28,9],[1215,7]]},"1869":{"position":[[143,7]]},"1879":{"position":[[237,7]]},"1889":{"position":[[49,7]]},"1903":{"position":[[416,7]]},"1905":{"position":[[389,7]]},"1981":{"position":[[181,8]]},"2007":{"position":[[286,9]]},"2102":{"position":[[2264,8],[2413,8],[2925,8]]},"2146":{"position":[[527,9]]},"2160":{"position":[[408,9]]},"2246":{"position":[[169,7]]},"2250":{"position":[[664,8]]},"2252":{"position":[[118,7]]},"2292":{"position":[[325,7]]},"2294":{"position":[[641,7]]},"2296":{"position":[[143,9]]},"2316":{"position":[[70,8]]},"2332":{"position":[[190,10],[280,8]]},"2345":{"position":[[750,8]]},"2353":{"position":[[63,9],[100,7]]},"2361":{"position":[[125,8]]},"2369":{"position":[[1560,7]]},"2375":{"position":[[145,9]]},"2383":{"position":[[111,8]]},"2406":{"position":[[160,8]]},"2411":{"position":[[70,8]]},"2413":{"position":[[1351,7]]},"2422":{"position":[[56,8]]},"2436":{"position":[[338,8]]},"2452":{"position":[[865,7]]},"2454":{"position":[[288,7],[1075,8]]},"2470":{"position":[[888,10]]},"2472":{"position":[[97,8]]},"2474":{"position":[[796,9]]},"2478":{"position":[[343,7]]},"2518":{"position":[[89,7]]},"2530":{"position":[[1333,8]]},"2538":{"position":[[265,7],[550,7]]},"2540":{"position":[[701,9]]},"2566":{"position":[[64,7]]},"2572":{"position":[[178,8]]},"2598":{"position":[[499,9]]},"2610":{"position":[[1443,7]]},"2662":{"position":[[189,9],[3757,9],[9703,8],[9789,7]]},"2666":{"position":[[335,7]]},"2672":{"position":[[887,7]]},"2686":{"position":[[604,7]]},"2714":{"position":[[252,9]]},"2722":{"position":[[1470,8]]},"2830":{"position":[[1406,8]]},"2852":{"position":[[653,8]]},"2932":{"position":[[300,7]]},"2972":{"position":[[844,8]]},"3038":{"position":[[83,8]]},"3054":{"position":[[28,9],[1279,8]]},"3056":{"position":[[28,9],[1215,7]]},"3060":{"position":[[160,7]]},"3070":{"position":[[245,7]]},"3080":{"position":[[49,7]]},"3096":{"position":[[416,7]]},"3098":{"position":[[389,7]]},"3194":{"position":[[181,8]]},"3224":{"position":[[286,9]]},"3261":{"position":[[2264,8],[2413,8],[2925,8]]},"3324":{"position":[[427,8],[533,9],[4357,8],[4483,8],[4658,8]]},"3347":{"position":[[1222,8],[3094,7]]},"3351":{"position":[[2006,10]]},"3375":{"position":[[527,9]]},"3389":{"position":[[408,9]]},"3484":{"position":[[325,7]]},"3486":{"position":[[143,9]]},"3524":{"position":[[641,7]]},"3537":{"position":[[70,8]]},"3557":{"position":[[169,7]]},"3561":{"position":[[664,8]]},"3563":{"position":[[118,7]]},"3573":{"position":[[160,8]]},"3578":{"position":[[70,8]]},"3580":{"position":[[1351,7]]},"3588":{"position":[[190,10],[280,8]]},"3617":{"position":[[56,8]]},"3633":{"position":[[750,8]]},"3641":{"position":[[63,9],[100,7]]},"3649":{"position":[[125,8]]},"3657":{"position":[[1560,7]]},"3663":{"position":[[145,9]]},"3671":{"position":[[111,8]]}}}],["receiveclientindication(stream",{"_index":1720,"t":{"110":{"position":[[1489,31],[1792,30],[2903,30]]}}}],["recent",{"_index":682,"t":{"16":{"position":[[4609,8]]},"68":{"position":[[58,6]]},"94":{"position":[[215,8]]},"118":{"position":[[495,8]]},"144":{"position":[[374,8]]},"146":{"position":[[339,8]]},"279":{"position":[[525,8]]}}}],["recipi",{"_index":2622,"t":{"262":{"position":[[390,12]]},"1543":{"position":[[464,11],[1438,11]]},"1666":{"position":[[303,9],[331,9]]},"1670":{"position":[[212,9],[680,9]]},"2712":{"position":[[464,11],[1438,11]]},"2883":{"position":[[303,9],[331,9]]}}}],["recipient_typ",{"_index":5533,"t":{"2889":{"position":[[153,15],[966,14]]}}}],["recommend",{"_index":2577,"t":{"252":{"position":[[452,11],[978,11]]},"367":{"position":[[6,11]]},"391":{"position":[[444,11]]},"399":{"position":[[1854,11]]},"558":{"position":[[365,11]]},"612":{"position":[[4,11]]},"652":{"position":[[898,9]]},"654":{"position":[[957,9]]},"670":{"position":[[458,9]]},"926":{"position":[[12,9]]},"975":{"position":[[711,12]]},"1049":{"position":[[543,11]]},"1107":{"position":[[544,11],[894,11]]},"1178":{"position":[[341,11],[382,11]]},"1343":{"position":[[58,11],[307,9]]},"1387":{"position":[[6,11]]},"1477":{"position":[[154,9]]},"1491":{"position":[[167,9]]},"1547":{"position":[[4,11]]},"1625":{"position":[[567,16]]},"1699":{"position":[[898,9]]},"1702":{"position":[[1003,9]]},"1715":{"position":[[458,9]]},"1869":{"position":[[940,11]]},"1937":{"position":[[909,11]]},"2001":{"position":[[12,9]]},"2030":{"position":[[527,12]]},"2086":{"position":[[312,12]]},"2102":{"position":[[4372,9]]},"2124":{"position":[[158,11]]},"2188":{"position":[[547,11],[897,11]]},"2230":{"position":[[543,11]]},"2434":{"position":[[246,11],[2709,11]]},"2490":{"position":[[58,11],[307,9]]},"2538":{"position":[[6,11]]},"2670":{"position":[[154,9]]},"2684":{"position":[[167,9]]},"2716":{"position":[[4,11]]},"2722":{"position":[[2632,9]]},"2823":{"position":[[862,9]]},"2826":{"position":[[967,9]]},"2842":{"position":[[567,16]]},"2883":{"position":[[520,9],[1502,9]]},"2904":{"position":[[422,9]]},"3060":{"position":[[1019,11]]},"3134":{"position":[[909,11]]},"3218":{"position":[[12,9]]},"3253":{"position":[[312,12]]},"3261":{"position":[[4372,9]]},"3271":{"position":[[527,12]]},"3329":{"position":[[147,9]]},"3339":{"position":[[158,11]]},"3349":{"position":[[5001,9]]},"3417":{"position":[[543,11]]},"3427":{"position":[[452,11],[867,11]]},"3612":{"position":[[273,11],[2908,11]]}}}],["reconnect",{"_index":289,"t":{"6":{"position":[[347,9]]},"16":{"position":[[1801,9]]},"18":{"position":[[396,13],[541,9],[941,9],[1074,12],[1874,12]]},"20":{"position":[[398,10],[485,11],[723,9],[1390,10],[1521,9]]},"42":{"position":[[1392,12],[1589,9],[1685,10]]},"48":{"position":[[870,9]]},"138":{"position":[[1256,10]]},"180":{"position":[[212,9]]},"182":{"position":[[1559,11]]},"200":{"position":[[490,13]]},"250":{"position":[[888,9]]},"287":{"position":[[1825,10]]},"319":{"position":[[400,10]]},"321":{"position":[[189,9],[444,9]]},"347":{"position":[[670,12],[992,11]]},"351":{"position":[[1153,9]]},"361":{"position":[[278,9],[479,9]]},"377":{"position":[[443,12]]},"383":{"position":[[1849,9]]},"387":{"position":[[1124,9],[1169,10],[1530,9]]},"389":{"position":[[699,9]]},"417":{"position":[[245,10]]},"433":{"position":[[214,9]]},"477":{"position":[[4008,9],[4160,9]]},"501":{"position":[[33,9]]},"505":{"position":[[814,9],[1018,13]]},"600":{"position":[[436,9],[761,9]]},"718":{"position":[[114,10]]},"812":{"position":[[32,10]]},"814":{"position":[[37,10]]},"816":{"position":[[35,10]]},"818":{"position":[[45,10]]},"820":{"position":[[31,10]]},"822":{"position":[[44,10]]},"824":{"position":[[29,10]]},"826":{"position":[[28,10]]},"828":{"position":[[35,10]]},"830":{"position":[[42,10],[226,10]]},"832":{"position":[[27,11],[39,10],[73,9]]},"834":{"position":[[40,10],[78,9],[147,9]]},"836":{"position":[[40,10]]},"965":{"position":[[357,9],[626,12],[962,9],[996,9],[3566,10]]},"1019":{"position":[[199,10]]},"1027":{"position":[[314,10]]},"1043":{"position":[[514,11]]},"1065":{"position":[[1477,10],[1634,9]]},"1077":{"position":[[154,12]]},"1117":{"position":[[679,9],[697,9]]},"1162":{"position":[[550,9],[656,9],[1760,9]]},"1172":{"position":[[1194,9]]},"1178":{"position":[[330,10]]},"1180":{"position":[[293,12],[375,9],[417,9],[564,9]]},"1182":{"position":[[271,9],[552,9],[1230,9]]},"1184":{"position":[[699,9],[735,12],[900,9],[1057,12],[1095,12],[1160,13],[1374,12],[1849,9],[2534,9]]},"1186":{"position":[[561,9]]},"1190":{"position":[[141,9],[252,9]]},"1268":{"position":[[645,10]]},"1305":{"position":[[400,10]]},"1307":{"position":[[189,9],[444,9]]},"1335":{"position":[[674,12],[996,11]]},"1339":{"position":[[1153,9]]},"1363":{"position":[[33,9]]},"1367":{"position":[[814,9],[1018,13]]},"1381":{"position":[[278,9],[479,9]]},"1401":{"position":[[241,11]]},"1417":{"position":[[214,9]]},"1479":{"position":[[672,9],[724,13],[843,9],[864,12]]},"1493":{"position":[[336,9]]},"1513":{"position":[[0,9],[55,9]]},"1619":{"position":[[718,9],[1043,9]]},"1676":{"position":[[1453,9]]},"1682":{"position":[[709,10],[858,14]]},"1811":{"position":[[314,10]]},"1863":{"position":[[12,9],[954,10],[1016,10],[1127,10]]},"1865":{"position":[[13,9],[611,9]]},"1887":{"position":[[1159,9]]},"1897":{"position":[[234,10]]},"1975":{"position":[[383,13]]},"1977":{"position":[[348,13]]},"1987":{"position":[[259,9]]},"2102":{"position":[[357,9],[626,9],[955,9],[989,9],[4228,9]]},"2134":{"position":[[514,11]]},"2146":{"position":[[1594,10],[1751,9],[6139,9]]},"2160":{"position":[[321,9],[393,9]]},"2246":{"position":[[255,10]]},"2250":{"position":[[271,9],[552,9],[1230,9]]},"2262":{"position":[[110,9]]},"2292":{"position":[[648,10]]},"2345":{"position":[[733,9],[839,13],[926,9],[3989,12],[4200,13],[5019,10]]},"2347":{"position":[[259,9]]},"2355":{"position":[[374,12],[4041,11]]},"2361":{"position":[[227,9]]},"2369":{"position":[[939,11],[1343,10]]},"2375":{"position":[[129,10]]},"2452":{"position":[[400,10]]},"2454":{"position":[[189,9],[444,9]]},"2482":{"position":[[767,12],[955,11],[1060,12]]},"2486":{"position":[[1153,9]]},"2514":{"position":[[33,9]]},"2518":{"position":[[845,9],[1064,13]]},"2532":{"position":[[300,9],[515,9]]},"2558":{"position":[[242,11]]},"2574":{"position":[[213,9]]},"2610":{"position":[[1603,9]]},"2644":{"position":[[0,9],[55,9]]},"2658":{"position":[[46,9]]},"2672":{"position":[[672,9],[724,13],[843,9],[864,12]]},"2686":{"position":[[336,9]]},"2722":{"position":[[2287,9]]},"2806":{"position":[[718,9],[1043,9]]},"2830":{"position":[[1456,9]]},"2836":{"position":[[709,10],[858,14]]},"3000":{"position":[[314,10]]},"3054":{"position":[[12,9],[954,10],[1016,10],[1127,10]]},"3056":{"position":[[13,9],[611,9]]},"3078":{"position":[[1159,9]]},"3090":{"position":[[234,10]]},"3188":{"position":[[356,13]]},"3190":{"position":[[321,13]]},"3202":{"position":[[232,9]]},"3261":{"position":[[357,9],[626,9],[955,9],[989,9],[4228,9]]},"3363":{"position":[[514,11]]},"3375":{"position":[[1594,10],[1751,9],[6168,9]]},"3389":{"position":[[321,9],[393,9]]},"3484":{"position":[[648,10]]},"3496":{"position":[[110,9]]},"3557":{"position":[[255,10]]},"3561":{"position":[[271,9],[552,9],[1230,9]]},"3633":{"position":[[733,9],[839,13],[926,9],[3989,12],[4200,13],[5019,10]]},"3635":{"position":[[259,9]]},"3643":{"position":[[374,12],[4041,11]]},"3649":{"position":[[227,9]]},"3657":{"position":[[939,11],[1343,10]]},"3663":{"position":[[129,10]]}}}],["record",{"_index":2523,"t":{"246":{"position":[[1469,9]]}}}],["recov",{"_index":1093,"t":{"42":{"position":[[2130,7],[2321,8]]},"48":{"position":[[884,7]]},"74":{"position":[[1215,7]]},"182":{"position":[[1485,7]]},"363":{"position":[[267,7],[693,7],[1285,10]]},"433":{"position":[[260,7]]},"477":{"position":[[4044,10]]},"543":{"position":[[43,9]]},"550":{"position":[[48,8]]},"570":{"position":[[424,7]]},"716":{"position":[[505,7]]},"718":{"position":[[0,7],[71,7],[301,7],[489,7],[774,9]]},"732":{"position":[[292,10]]},"734":{"position":[[1442,10]]},"758":{"position":[[1753,7],[1784,7]]},"965":{"position":[[1084,7],[1218,7],[1533,9],[1613,9],[1711,7],[2593,9],[2882,9],[2980,7],[3170,9]]},"1071":{"position":[[3682,7],[3713,7]]},"1113":{"position":[[886,7],[1217,7],[1248,7]]},"1168":{"position":[[696,9],[1041,9],[1136,9]]},"1186":{"position":[[594,7]]},"1188":{"position":[[28,7],[331,9],[546,7]]},"1251":{"position":[[43,7],[88,7],[172,7],[237,7]]},"1383":{"position":[[270,7],[706,7],[1436,10]]},"1777":{"position":[[1280,7],[1311,7]]},"1895":{"position":[[597,7]]},"1897":{"position":[[179,7],[625,7],[1127,9]]},"2102":{"position":[[1077,7],[1705,9],[1785,9],[1883,7],[2832,7],[2934,10],[3104,9],[3565,7],[3615,10]]},"2194":{"position":[[851,7]]},"2345":{"position":[[821,9]]},"2361":{"position":[[171,7],[455,9],[817,9],[928,9]]},"2402":{"position":[[43,7],[88,7],[172,7],[237,7]]},"2504":{"position":[[661,7]]},"2534":{"position":[[283,7],[733,7]]},"2542":{"position":[[1401,9]]},"2966":{"position":[[1358,7],[1389,7]]},"3088":{"position":[[597,7]]},"3090":{"position":[[179,7],[625,7],[1127,9]]},"3261":{"position":[[1077,7],[1705,9],[1785,9],[1883,7],[2832,7],[2934,10],[3104,9],[3565,7],[3615,10]]},"3324":{"position":[[2924,9],[2968,7]]},"3435":{"position":[[919,7]]},"3569":{"position":[[43,7],[88,7],[172,7],[237,7]]},"3633":{"position":[[821,9]]},"3649":{"position":[[171,7],[455,9],[817,9],[928,9]]}}}],["recover",{"_index":4574,"t":{"1168":{"position":[[655,11]]},"1473":{"position":[[615,11]]},"1751":{"position":[[135,11]]},"1897":{"position":[[130,12]]},"2363":{"position":[[545,11]]},"2666":{"position":[[615,11]]},"2940":{"position":[[135,11]]},"3090":{"position":[[130,12]]},"3651":{"position":[[545,11]]}}}],["recover_sinc",{"_index":4423,"t":{"1113":{"position":[[828,13]]},"2194":{"position":[[793,13]]},"3435":{"position":[[861,13]]}}}],["recoveri",{"_index":173,"t":{"2":{"position":[[2814,8]]},"42":{"position":[[1285,8],[1313,8],[1844,8],[2071,9],[2101,8],[2360,8],[2448,8],[2563,8],[2597,8]]},"54":{"position":[[1224,8]]},"70":{"position":[[507,8]]},"84":{"position":[[226,8]]},"317":{"position":[[447,8]]},"321":{"position":[[414,8]]},"361":{"position":[[1521,8]]},"363":{"position":[[379,8],[667,8],[951,8]]},"505":{"position":[[630,8],[780,8],[874,8],[1035,8],[1294,8],[1378,8]]},"600":{"position":[[565,8]]},"718":{"position":[[194,8],[943,8]]},"720":{"position":[[217,8]]},"961":{"position":[[465,9]]},"965":{"position":[[72,8],[385,8],[673,8],[1178,8],[1821,8],[2016,8],[2051,8],[2513,8],[3391,8],[3602,8]]},"993":{"position":[[333,8]]},"1162":{"position":[[1919,8],[1976,8]]},"1168":{"position":[[631,9],[1245,8]]},"1268":{"position":[[633,8]]},"1303":{"position":[[447,8]]},"1307":{"position":[[414,8]]},"1367":{"position":[[630,8],[780,8],[874,8],[1035,8],[1294,8],[1378,8]]},"1381":{"position":[[1526,8]]},"1383":{"position":[[392,8],[680,8],[1102,8]]},"1619":{"position":[[847,8]]},"1751":{"position":[[37,8]]},"1893":{"position":[[1313,8]]},"1897":{"position":[[314,8],[647,8],[1296,8]]},"1937":{"position":[[138,9]]},"2054":{"position":[[333,8]]},"2098":{"position":[[507,9],[1402,8]]},"2102":{"position":[[72,8],[385,8],[670,8],[1171,8],[1433,8],[1993,8],[2161,8],[2244,8],[3474,8],[4073,8],[4539,8],[4747,8]]},"2292":{"position":[[636,8]]},"2361":{"position":[[31,8],[392,8],[611,8]]},"2363":{"position":[[393,8]]},"2450":{"position":[[447,8]]},"2454":{"position":[[414,8]]},"2518":{"position":[[661,8],[811,8],[905,8],[1081,8],[1352,8],[1436,8]]},"2532":{"position":[[1533,9],[1686,8]]},"2534":{"position":[[405,8],[707,8]]},"2806":{"position":[[847,8]]},"2940":{"position":[[37,8]]},"3084":{"position":[[1277,8]]},"3090":{"position":[[314,8],[647,8],[1296,8]]},"3134":{"position":[[138,9]]},"3257":{"position":[[507,9],[1402,8]]},"3261":{"position":[[72,8],[385,8],[670,8],[1171,8],[1433,8],[1993,8],[2161,8],[2244,8],[3474,8],[4073,8],[4539,8],[4747,8]]},"3293":{"position":[[333,8]]},"3324":{"position":[[3051,8]]},"3347":{"position":[[3257,8]]},"3484":{"position":[[636,8]]},"3649":{"position":[[31,8],[392,8],[611,8]]},"3651":{"position":[[393,8]]}}}],["redeploy",{"_index":1308,"t":{"64":{"position":[[855,8]]},"2616":{"position":[[676,12]]}}}],["redi",{"_index":92,"t":{"2":{"position":[[1193,5],[1236,5],[1258,5],[2304,5],[2653,5],[2852,5]]},"8":{"position":[[324,6]]},"16":{"position":[[2047,5],[3634,5],[6051,6],[6064,5],[6177,5],[6240,5],[6459,5],[6519,5],[6682,5]]},"18":{"position":[[2441,5],[2478,5],[2942,5]]},"20":{"position":[[1173,5],[1227,5],[1241,5]]},"22":{"position":[[406,5]]},"46":{"position":[[2490,5],[2504,5],[2526,5],[2593,5],[2609,5],[2776,5],[2936,5],[2959,5],[3034,5],[3109,5],[3300,5],[3332,5],[3385,5],[3535,5],[4059,5]]},"48":{"position":[[416,5]]},"52":{"position":[[421,5],[632,5]]},"62":{"position":[[608,5],[627,5]]},"72":{"position":[[17,5],[35,5],[113,5],[291,5]]},"74":{"position":[[192,5],[907,6],[1054,5],[1364,5],[1378,5],[1399,5]]},"78":{"position":[[964,5]]},"120":{"position":[[337,5],[380,5]]},"122":{"position":[[566,6],[580,5],[672,5]]},"132":{"position":[[40,5],[113,6],[236,8],[1009,5],[1131,5]]},"136":{"position":[[77,5]]},"148":{"position":[[636,5],[679,5]]},"168":{"position":[[552,5],[617,5]]},"222":{"position":[[483,6]]},"228":{"position":[[34,5],[138,5]]},"230":{"position":[[107,5],[130,5],[171,5],[200,5],[213,5],[241,5],[247,5],[277,5],[425,5],[601,5]]},"232":{"position":[[90,5]]},"240":{"position":[[354,5],[434,5]]},"244":{"position":[[319,5]]},"262":{"position":[[1297,5]]},"277":{"position":[[840,5]]},"279":{"position":[[810,5],[853,5]]},"311":{"position":[[44,6]]},"325":{"position":[[421,6]]},"347":{"position":[[1399,5]]},"351":{"position":[[429,5]]},"361":{"position":[[999,5],[1267,5],[1343,5]]},"363":{"position":[[807,7]]},"369":{"position":[[136,5]]},"371":{"position":[[56,7],[448,6],[477,5],[556,5]]},"415":{"position":[[121,6],[176,5],[204,5]]},"463":{"position":[[169,5]]},"467":{"position":[[423,5],[626,5],[750,5],[930,5],[1118,5],[1260,5]]},"469":{"position":[[777,5],[881,5],[1489,5]]},"471":{"position":[[78,5],[203,6],[297,5],[406,6]]},"473":{"position":[[153,5],[212,5],[484,5],[595,6],[827,5],[853,5],[886,5],[916,5],[1036,5],[2119,5],[3551,5],[3719,6]]},"475":{"position":[[56,5],[108,5],[137,5],[182,5],[241,5],[354,5],[375,5],[516,5],[629,5],[667,5],[741,5],[754,5],[933,5],[1409,5],[1483,5],[1746,5],[1841,5],[1852,5],[1940,5],[1964,5],[2172,5],[2285,5],[2543,6],[2960,5],[3121,5],[3171,5],[3248,5],[3405,5],[3486,5]]},"477":{"position":[[401,5],[837,6],[1032,5],[1207,5],[1315,5],[2350,5],[2994,5],[3235,5],[3500,5],[3536,5],[3671,5],[3863,5],[4323,5],[4497,5],[5770,5],[6517,5],[6544,5],[6707,5]]},"479":{"position":[[81,5],[192,5],[479,5],[489,5],[540,6],[556,5],[566,5],[794,5],[1590,5],[1655,5],[1742,5],[1996,5],[2219,5],[2866,5],[4955,6],[4997,5],[5239,5],[5376,5],[5451,5],[5535,5],[5641,5],[5717,5],[5847,5]]},"481":{"position":[[490,5],[711,5],[838,5],[919,5],[952,5],[1825,5],[2197,6],[2860,5],[3132,5],[3280,6],[3860,5],[3936,6],[5845,5],[5875,5],[5930,5],[5960,5],[6009,5],[6040,5],[6087,5],[6117,5],[6166,5],[6196,5],[6423,5],[6653,5]]},"483":{"position":[[99,6],[393,5],[1677,5]]},"485":{"position":[[124,5],[230,5],[865,5],[1008,5],[1076,5],[1175,5],[1789,5],[1838,5],[1953,5],[1983,5],[2041,5]]},"564":{"position":[[0,5],[56,5],[204,5]]},"566":{"position":[[23,5],[106,5],[183,5]]},"572":{"position":[[673,5]]},"600":{"position":[[888,7]]},"618":{"position":[[55,6],[175,5],[915,5],[977,5],[1069,6],[1076,5],[1092,5],[1173,5],[1255,5],[1321,8],[1576,5]]},"646":{"position":[[353,5]]},"648":{"position":[[31,6],[124,8],[220,8],[291,5],[459,5],[489,5],[711,5],[820,5],[914,5],[950,5],[1018,5]]},"664":{"position":[[267,5]]},"666":{"position":[[29,6],[120,8],[191,5],[355,5],[385,5],[574,5],[683,5],[777,5],[813,5],[876,5]]},"678":{"position":[[478,5],[569,5]]},"686":{"position":[[10,5],[136,5],[199,5],[291,6],[298,5],[314,5],[396,5],[478,5],[544,8],[677,5],[720,5],[762,5],[819,5]]},"898":{"position":[[24,5]]},"961":{"position":[[789,5],[821,5],[898,5]]},"965":{"position":[[2655,5],[2736,5],[2766,5]]},"969":{"position":[[170,5]]},"973":{"position":[[0,5],[115,5],[198,5],[235,5],[311,6],[326,5]]},"975":{"position":[[41,5],[105,5],[164,5],[247,5],[319,5],[379,5],[457,5],[577,6],[641,5],[819,5],[953,5],[1005,5]]},"977":{"position":[[58,5],[147,6],[177,5],[398,5],[1265,5],[1706,6]]},"979":{"position":[[65,5],[73,5],[125,5],[393,5],[511,5],[617,5],[705,5],[818,8],[1196,5]]},"981":{"position":[[58,5],[102,5],[247,5],[360,5],[373,5],[481,5]]},"983":{"position":[[24,5],[81,5],[138,5],[231,5],[400,5],[470,5],[581,8],[673,5],[737,8],[855,5],[934,5],[1026,5],[1057,5],[1171,5]]},"985":{"position":[[24,5],[180,5],[331,5],[584,5],[845,5],[916,5],[1118,5]]},"987":{"position":[[24,5],[98,5],[292,5],[402,5],[481,5],[650,7],[713,5],[762,6]]},"989":{"position":[[954,5],[1014,5]]},"991":{"position":[[525,5],[752,5]]},"1073":{"position":[[528,5]]},"1297":{"position":[[44,6]]},"1311":{"position":[[421,6]]},"1335":{"position":[[1403,5]]},"1339":{"position":[[429,5]]},"1343":{"position":[[249,5]]},"1353":{"position":[[887,5]]},"1381":{"position":[[1004,5],[1272,5],[1348,5]]},"1383":{"position":[[835,7]]},"1389":{"position":[[136,5]]},"1391":{"position":[[56,7],[448,6],[477,5],[556,5]]},"1399":{"position":[[295,6],[350,5],[378,5]]},"1463":{"position":[[169,5]]},"1619":{"position":[[1170,7]]},"1639":{"position":[[230,5],[279,5]]},"1647":{"position":[[39,5],[388,5]]},"1680":{"position":[[91,5],[315,6],[352,5],[518,5],[619,5],[660,5],[1619,5],[1681,5],[1773,6],[1780,5],[1796,5],[1877,5],[1959,5],[2031,8],[2212,5]]},"1692":{"position":[[353,5]]},"1694":{"position":[[31,6],[124,8],[220,8],[291,5],[459,5],[489,5],[711,5],[820,5],[914,5],[950,5],[1018,5]]},"1708":{"position":[[267,5]]},"1710":{"position":[[29,6],[120,8],[191,5],[355,5],[385,5],[574,5],[683,5],[777,5],[813,5],[876,5]]},"1729":{"position":[[473,5],[564,5]]},"1737":{"position":[[10,5],[126,5],[189,5],[281,6],[288,5],[304,5],[386,5],[468,5],[534,8],[659,5],[702,5],[744,5],[801,5]]},"1893":{"position":[[1498,5],[1547,5]]},"1965":{"position":[[24,5]]},"2024":{"position":[[170,5]]},"2028":{"position":[[0,5],[115,5],[198,5],[235,5],[311,6],[326,5],[354,5]]},"2030":{"position":[[41,5],[105,5],[164,5],[213,5],[285,5],[393,6],[457,5],[736,6],[755,5],[813,5],[1010,5],[1074,5]]},"2032":{"position":[[89,6],[139,5],[226,5]]},"2034":{"position":[[58,5],[147,6],[177,5],[398,5],[1265,5],[1706,6]]},"2036":{"position":[[65,5],[73,5],[125,5],[393,5],[511,5],[592,5],[680,5],[793,8],[1212,5]]},"2038":{"position":[[21,5],[115,5],[211,5]]},"2040":{"position":[[58,5],[102,5],[247,5],[360,5],[373,5],[481,5]]},"2042":{"position":[[24,5],[81,5],[138,5],[231,5],[400,5],[470,5],[581,8],[673,5],[737,8],[855,5],[934,5],[1026,5],[1057,5],[1171,5]]},"2044":{"position":[[24,5],[180,5],[331,5],[584,5],[845,5],[916,5],[1118,5]]},"2046":{"position":[[24,5],[98,5],[292,5],[402,5],[481,5],[650,7],[713,5],[762,6]]},"2048":{"position":[[161,5],[590,5]]},"2050":{"position":[[954,5],[1014,5]]},"2052":{"position":[[525,5],[755,5]]},"2098":{"position":[[845,5],[877,5],[954,5]]},"2154":{"position":[[528,5]]},"2444":{"position":[[44,6]]},"2458":{"position":[[421,6]]},"2482":{"position":[[1452,5]]},"2486":{"position":[[429,5]]},"2490":{"position":[[249,5]]},"2502":{"position":[[72,5],[121,5],[178,5],[329,5],[552,5]]},"2532":{"position":[[1065,5],[1338,5],[1435,5]]},"2534":{"position":[[873,7]]},"2540":{"position":[[136,5]]},"2542":{"position":[[56,7],[454,6],[484,5],[563,5]]},"2546":{"position":[[940,5]]},"2556":{"position":[[302,6],[357,5],[385,5]]},"2604":{"position":[[174,5]]},"2722":{"position":[[535,5],[768,5],[1063,8],[2607,6]]},"2744":{"position":[[78,5]]},"2746":{"position":[[222,5],[247,5]]},"2806":{"position":[[1170,7]]},"2816":{"position":[[353,5]]},"2818":{"position":[[31,6],[124,8],[220,8],[291,5],[459,5],[489,5],[711,5],[820,5],[914,5],[950,5],[1018,5]]},"2834":{"position":[[91,5],[315,6],[352,5],[518,5],[619,5],[660,5],[1625,5],[1687,5],[1779,6],[1786,5],[1802,5],[1883,5],[1965,5],[2037,8],[2218,5]]},"2856":{"position":[[230,5],[279,5]]},"2862":{"position":[[263,5],[376,6]]},"2864":{"position":[[24,5],[373,5]]},"2897":{"position":[[267,5]]},"2899":{"position":[[29,6],[120,8],[191,5],[355,5],[385,5],[574,5],[683,5],[777,5],[813,5],[876,5]]},"2918":{"position":[[473,5],[564,5]]},"2926":{"position":[[10,5],[126,5],[189,5],[281,6],[288,5],[304,5],[386,5],[468,5],[534,8],[659,5],[702,5],[744,5],[801,5]]},"3084":{"position":[[1462,5],[1511,5]]},"3178":{"position":[[24,5]]},"3257":{"position":[[845,5],[877,5],[954,5]]},"3265":{"position":[[170,5]]},"3269":{"position":[[0,5],[115,5],[198,5],[235,5],[311,6],[326,5],[354,5]]},"3271":{"position":[[41,5],[105,5],[164,5],[213,5],[285,5],[393,6],[457,5],[702,6],[721,5],[779,5],[976,5],[1040,5]]},"3273":{"position":[[89,6],[139,5],[226,5]]},"3275":{"position":[[58,5],[147,6],[177,5],[398,5],[1265,5],[1706,6]]},"3277":{"position":[[65,5],[73,5],[125,5],[393,5],[511,5],[592,5],[680,5],[793,8],[1212,5]]},"3279":{"position":[[21,5],[115,5],[211,5]]},"3281":{"position":[[58,5],[102,5],[247,5],[360,5],[373,5],[481,5]]},"3283":{"position":[[24,5],[81,5],[138,5],[231,5],[400,5],[470,5],[581,8],[673,5],[737,8],[855,5],[934,5],[1026,5],[1057,5],[1171,5]]},"3285":{"position":[[24,5],[180,5],[331,5],[584,5],[845,5],[916,5],[1118,5]]},"3287":{"position":[[11,5],[70,5],[106,5],[176,5],[662,5]]},"3289":{"position":[[954,5],[1014,5]]},"3291":{"position":[[525,5],[755,5]]},"3383":{"position":[[528,5]]}}}],["redigo",{"_index":312,"t":{"8":{"position":[[289,6]]},"471":{"position":[[168,6],[479,6],[866,9]]},"473":{"position":[[2055,9],[2264,9],[4325,6]]},"475":{"position":[[421,6],[1571,6],[1609,6],[2216,6],[2291,6],[3058,6]]},"477":{"position":[[32,6],[855,7],[1196,7],[1283,7],[2332,7],[4401,6],[6297,6],[6396,6],[6762,6],[6857,6]]},"479":{"position":[[780,6],[3278,6],[3468,6]]},"481":{"position":[[227,6],[414,6],[1273,6],[2004,7],[2801,6],[3074,6],[3470,6],[3643,6]]},"483":{"position":[[607,6],[1250,6],[1374,6],[1387,6],[1912,6]]},"485":{"position":[[15,6]]}}}],["redigo'",{"_index":3112,"t":{"473":{"position":[[57,8]]},"475":{"position":[[1434,8]]}}}],["redigo.conn",{"_index":3094,"t":{"471":{"position":[[639,13]]},"473":{"position":[[2703,13]]}}}],["redigo.dial(\"tcp",{"_index":3095,"t":{"471":{"position":[[669,18]]},"473":{"position":[[2733,18]]}}}],["redigo.pool",{"_index":3089,"t":{"471":{"position":[[571,12]]},"473":{"position":[[1536,12],[2665,12]]}}}],["redigo.pool}func",{"_index":3128,"t":{"473":{"position":[[1504,16]]}}}],["redigo_latency_p128.txt",{"_index":3471,"t":{"483":{"position":[[656,23]]}}}],["redigo_p128.txt",{"_index":3235,"t":{"477":{"position":[[4578,15]]},"479":{"position":[[3561,15]]}}}],["redirect",{"_index":1927,"t":{"128":{"position":[[3156,10]]},"130":{"position":[[2631,10]]},"297":{"position":[[379,8]]}}}],["redis'",{"_index":1164,"t":{"46":{"position":[[3242,7]]}}}],["redis.newuniversalclient(&redis.universalopt",{"_index":3205,"t":{"477":{"position":[[1838,49]]}}}],["redis/redi",{"_index":3191,"t":{"477":{"position":[[45,11],[311,11],[434,11],[784,11],[1758,11],[3178,12],[4445,11],[6310,11],[6478,11],[6602,11],[6871,11],[6889,11],[6998,11]]},"479":{"position":[[32,11],[411,11],[628,12]]},"481":{"position":[[2024,11],[2811,11],[3084,11],[3484,11]]}}}],["redis://127.0.0.1:6379",{"_index":2455,"t":{"228":{"position":[[316,24]]}}}],["redis://localhost:6379",{"_index":1999,"t":{"132":{"position":[[262,25]]}}}],["redis:6",{"_index":1996,"t":{"132":{"position":[[153,7]]}}}],["redis:7",{"_index":2461,"t":{"230":{"position":[[177,7]]}}}],["redis_active_statu",{"_index":3865,"t":{"686":{"position":[[57,22],[588,22],[976,22]]}}}],["redis_address",{"_index":1998,"t":{"132":{"position":[[245,16],[1102,13]]},"564":{"position":[[380,13]]},"618":{"position":[[464,16],[1330,16]]},"648":{"position":[[133,16],[229,16]]},"666":{"position":[[129,16]]},"686":{"position":[[99,16],[553,16]]},"975":{"position":[[55,14]]},"977":{"position":[[469,13]]},"983":{"position":[[590,16],[746,16]]},"1639":{"position":[[642,16]]},"1641":{"position":[[107,16]]},"1643":{"position":[[107,16]]},"1680":{"position":[[1286,16],[2040,16]]},"1694":{"position":[[133,16],[229,16]]},"1710":{"position":[[129,16]]},"1737":{"position":[[89,16],[543,16]]},"2030":{"position":[[55,14]]},"2034":{"position":[[469,13]]},"2042":{"position":[[590,16],[746,16]]},"2746":{"position":[[99,16]]},"2818":{"position":[[133,16],[229,16]]},"2834":{"position":[[1292,16],[2046,16]]},"2856":{"position":[[642,16]]},"2858":{"position":[[107,16]]},"2860":{"position":[[107,16]]},"2899":{"position":[[129,16]]},"2926":{"position":[[89,16],[543,16]]},"3271":{"position":[[55,14]]},"3275":{"position":[[469,13]]},"3283":{"position":[[590,16],[746,16]]}}}],["redis_address=\"localhost:16379",{"_index":4153,"t":{"981":{"position":[[950,31]]},"2040":{"position":[[950,31]]},"3281":{"position":[[950,31]]}}}],["redis_address=127.0.0.1:6379",{"_index":4138,"t":{"977":{"position":[[361,28],[639,28],[1043,28]]},"2034":{"position":[[361,28],[639,28],[1043,28]]},"3275":{"position":[[361,28],[639,28],[1043,28]]}}}],["redis_cluster_addr",{"_index":3659,"t":{"570":{"position":[[2470,19]]}}}],["redis_cluster_address",{"_index":3660,"t":{"570":{"position":[[2501,21]]},"985":{"position":[[74,21],[224,24],[687,24]]},"2044":{"position":[[74,21],[224,24],[687,24]]},"3285":{"position":[[74,21],[224,24],[687,24]]}}}],["redis_connect_timeout",{"_index":3656,"t":{"570":{"position":[[2313,21]]}}}],["redis_db",{"_index":4132,"t":{"975":{"position":[[278,9]]},"2030":{"position":[[244,9]]},"3271":{"position":[[244,9]]}}}],["redis_force_resp2",{"_index":5141,"t":{"2030":{"position":[[590,18]]},"3271":{"position":[[590,18]]},"3287":{"position":[[601,17]]}}}],["redis_history_meta_ttl",{"_index":3637,"t":{"570":{"position":[[797,22]]}}}],["redis_host",{"_index":3618,"t":{"564":{"position":[[306,10]]}}}],["redis_idle_timeout",{"_index":3655,"t":{"570":{"position":[[2262,18]]}}}],["redis_list",{"_index":2454,"t":{"228":{"position":[[299,11]]}}}],["redis_master_nam",{"_index":3663,"t":{"570":{"position":[[2573,17]]}}}],["redis_password",{"_index":4129,"t":{"975":{"position":[[127,15]]},"2030":{"position":[[127,15]]},"3271":{"position":[[127,15]]}}}],["redis_port",{"_index":3619,"t":{"564":{"position":[[321,10]]}}}],["redis_prefix",{"_index":4135,"t":{"975":{"position":[[486,13]]},"1343":{"position":[[286,13]]},"2030":{"position":[[302,13]]},"2490":{"position":[[286,13]]},"3271":{"position":[[302,13]]}}}],["redis_presence_ttl",{"_index":3639,"t":{"570":{"position":[[895,18]]}}}],["redis_pubsub_num_work",{"_index":3624,"t":{"570":{"position":[[261,24]]}}}],["redis_read_timeout",{"_index":3657,"t":{"570":{"position":[[2367,18]]}}}],["redis_sentinel",{"_index":3661,"t":{"570":{"position":[[2523,15]]}}}],["redis_sentinel_address",{"_index":3662,"t":{"570":{"position":[[2550,22]]},"979":{"position":[[147,22],[202,22],[827,25]]},"2036":{"position":[[147,22],[202,22],[802,25]]},"3277":{"position":[[147,22],[202,22],[802,25]]}}}],["redis_sentinel_master_nam",{"_index":3664,"t":{"570":{"position":[[2602,26]]},"979":{"position":[[174,27],[335,26],[872,29]]},"2036":{"position":[[174,27],[335,26],[847,29]]},"3277":{"position":[[174,27],[335,26],[847,29]]}}}],["redis_sentinel_password",{"_index":4142,"t":{"979":{"position":[[430,23]]},"2036":{"position":[[430,23]]},"3277":{"position":[[430,23]]}}}],["redis_sentinel_tl",{"_index":5154,"t":{"2038":{"position":[[63,19]]},"3279":{"position":[[63,19]]}}}],["redis_sentinel_tls_cert",{"_index":5157,"t":{"2038":{"position":[[355,24]]},"3279":{"position":[[355,24]]}}}],["redis_sentinel_tls_cert_pem",{"_index":5158,"t":{"2038":{"position":[[534,27]]},"3279":{"position":[[507,27]]}}}],["redis_sentinel_tls_insecure_skip_verifi",{"_index":5155,"t":{"2038":{"position":[[137,40]]},"3279":{"position":[[137,40]]}}}],["redis_sentinel_tls_key",{"_index":5159,"t":{"2038":{"position":[[570,23]]},"3279":{"position":[[543,23]]}}}],["redis_sentinel_tls_key_pem",{"_index":5160,"t":{"2038":{"position":[[744,26]]},"3279":{"position":[[690,26]]}}}],["redis_sentinel_tls_root_ca",{"_index":5161,"t":{"2038":{"position":[[779,27]]},"3279":{"position":[[725,27]]}}}],["redis_sentinel_tls_root_ca_pem",{"_index":5162,"t":{"2038":{"position":[[987,30]]},"3279":{"position":[[906,30]]}}}],["redis_sentinel_tls_server_nam",{"_index":5163,"t":{"2038":{"position":[[1026,31]]},"3279":{"position":[[945,31]]}}}],["redis_sentinel_tls_skip_verifi",{"_index":5156,"t":{"2038":{"position":[[292,30]]},"3279":{"position":[[292,30]]}}}],["redis_sentinel_us",{"_index":4143,"t":{"979":{"position":[[535,19]]},"2036":{"position":[[535,19]]},"3277":{"position":[[535,19]]}}}],["redis_sequence_ttl",{"_index":3638,"t":{"570":{"position":[[848,18]]}}}],["redis_stream",{"_index":3622,"t":{"570":{"position":[[208,13]]}}}],["redis_throttl",{"_index":3812,"t":{"618":{"position":[[424,19],[1365,19]]}}}],["redis_tl",{"_index":4133,"t":{"975":{"position":[[336,10]]},"2032":{"position":[[96,10]]},"3273":{"position":[[96,10]]}}}],["redis_tls_cert",{"_index":5145,"t":{"2032":{"position":[[361,15]]},"3273":{"position":[[361,15]]}}}],["redis_tls_cert_pem",{"_index":5146,"t":{"2032":{"position":[[531,18]]},"3273":{"position":[[504,18]]}}}],["redis_tls_insecure_skip_verifi",{"_index":5143,"t":{"2032":{"position":[[161,31]]},"3273":{"position":[[161,31]]}}}],["redis_tls_key",{"_index":5147,"t":{"2032":{"position":[[558,14]]},"3273":{"position":[[531,14]]}}}],["redis_tls_key_pem",{"_index":5148,"t":{"2032":{"position":[[723,17]]},"3273":{"position":[[669,17]]}}}],["redis_tls_root_ca",{"_index":5149,"t":{"2032":{"position":[[749,18]]},"3273":{"position":[[695,18]]}}}],["redis_tls_root_ca_pem",{"_index":5152,"t":{"2032":{"position":[[948,21]]},"3273":{"position":[[867,21]]}}}],["redis_tls_server_nam",{"_index":5153,"t":{"2032":{"position":[[978,22]]},"3273":{"position":[[897,22]]}}}],["redis_tls_skip_verifi",{"_index":4134,"t":{"975":{"position":[[401,22]]},"2032":{"position":[[307,21]]},"2658":{"position":[[231,21]]},"3273":{"position":[[307,21]]}}}],["redis_us",{"_index":4130,"t":{"975":{"position":[[180,11]]},"2030":{"position":[[180,11]]},"3271":{"position":[[180,11]]}}}],["redis_use_list",{"_index":3621,"t":{"566":{"position":[[209,18]]},"975":{"position":[[584,16]]},"2030":{"position":[[400,16]]},"3271":{"position":[[400,16]]}}}],["redis_user_command_rate_limit",{"_index":5514,"t":{"2834":{"position":[[1240,32],[2081,32]]}}}],["redis_user_command_throttl",{"_index":4995,"t":{"1680":{"position":[[1234,32],[2075,32]]}}}],["redis_write_timeout",{"_index":3658,"t":{"570":{"position":[[2418,19]]}}}],["redisaddpres",{"_index":3226,"t":{"477":{"position":[[4329,18]]}}}],["redisai",{"_index":3309,"t":{"479":{"position":[[305,8]]}}}],["redisbloom",{"_index":3307,"t":{"479":{"position":[[281,11]]}}}],["redisearch",{"_index":3308,"t":{"479":{"position":[[293,11]]}}}],["redisgear",{"_index":3310,"t":{"479":{"position":[[314,11]]}}}],["redisjson",{"_index":3306,"t":{"479":{"position":[[270,10]]}}}],["redispublish",{"_index":3222,"t":{"477":{"position":[[3557,14]]}}}],["redispublish_histori",{"_index":3223,"t":{"477":{"position":[[3796,22]]}}}],["redisrecov",{"_index":3225,"t":{"477":{"position":[[4178,15]]}}}],["redissubscrib",{"_index":3224,"t":{"477":{"position":[[4027,16]]}}}],["reduc",{"_index":339,"t":{"8":{"position":[[737,8]]},"12":{"position":[[339,6]]},"18":{"position":[[1436,6],[3391,6]]},"70":{"position":[[536,6]]},"86":{"position":[[1629,8],[1800,6]]},"88":{"position":[[967,6]]},"168":{"position":[[814,6],[881,6]]},"246":{"position":[[2146,8]]},"347":{"position":[[782,6]]},"361":{"position":[[214,6],[423,6],[1693,6]]},"383":{"position":[[412,6],[1244,7]]},"395":{"position":[[451,6]]},"413":{"position":[[204,6]]},"429":{"position":[[216,8]]},"433":{"position":[[304,8]]},"473":{"position":[[602,8],[3425,7]]},"475":{"position":[[2936,8]]},"477":{"position":[[992,7],[3252,8],[6226,7]]},"479":{"position":[[666,6],[5056,8],[5749,7]]},"481":{"position":[[1797,6],[4943,6],[4977,7]]},"485":{"position":[[714,6],[1121,6],[1149,6],[1314,6]]},"552":{"position":[[101,7]]},"564":{"position":[[142,6]]},"576":{"position":[[723,6],[809,6]]},"600":{"position":[[314,6]]},"912":{"position":[[738,6]]},"965":{"position":[[508,8]]},"1133":{"position":[[151,6]]},"1164":{"position":[[452,8]]},"1277":{"position":[[167,6]]},"1279":{"position":[[168,6]]},"1281":{"position":[[98,6]]},"1283":{"position":[[189,6]]},"1335":{"position":[[786,6]]},"1381":{"position":[[214,6],[423,6],[1698,6]]},"1397":{"position":[[203,6]]},"1413":{"position":[[205,8]]},"1417":{"position":[[306,8]]},"1583":{"position":[[1098,6],[1187,6]]},"1611":{"position":[[216,6]]},"1619":{"position":[[596,6]]},"1981":{"position":[[738,6]]},"2102":{"position":[[508,8]]},"2214":{"position":[[151,6]]},"2244":{"position":[[3,6],[167,8]]},"2296":{"position":[[874,8]]},"2424":{"position":[[167,6]]},"2426":{"position":[[168,6]]},"2428":{"position":[[98,6]]},"2430":{"position":[[189,6]]},"2532":{"position":[[236,6],[1872,6]]},"2554":{"position":[[207,6]]},"2570":{"position":[[205,8]]},"2574":{"position":[[308,8]]},"2736":{"position":[[216,6]]},"2778":{"position":[[1196,6],[1285,6]]},"2806":{"position":[[596,6]]},"3194":{"position":[[738,6]]},"3261":{"position":[[508,8]]},"3486":{"position":[[874,8]]},"3555":{"position":[[3,6],[167,8]]},"3619":{"position":[[167,6]]},"3621":{"position":[[168,6]]},"3623":{"position":[[98,6]]},"3625":{"position":[[189,6]]}}}],["reduct",{"_index":3297,"t":{"477":{"position":[[6093,9]]},"481":{"position":[[159,9],[3188,10],[3357,9],[6369,9]]},"1611":{"position":[[323,9]]},"2532":{"position":[[449,9]]},"2736":{"position":[[323,9]]}}}],["refactor",{"_index":1269,"t":{"62":{"position":[[165,11]]},"194":{"position":[[186,11]]},"407":{"position":[[4,11]]},"2656":{"position":[[436,11]]}}}],["refer",{"_index":1309,"t":{"64":{"position":[[1002,5]]},"74":{"position":[[2304,5]]},"443":{"position":[[818,5]]},"505":{"position":[[1313,5]]},"556":{"position":[[426,5]]},"564":{"position":[[191,5]]},"920":{"position":[[118,5]]},"1019":{"position":[[468,5]]},"1027":{"position":[[679,5]]},"1079":{"position":[[322,5]]},"1095":{"position":[[49,9]]},"1137":{"position":[[455,5]]},"1143":{"position":[[1237,5]]},"1196":{"position":[[94,5]]},"1213":{"position":[[498,5]]},"1367":{"position":[[1313,5]]},"1427":{"position":[[148,5]]},"1811":{"position":[[679,5]]},"1995":{"position":[[118,5]]},"2162":{"position":[[322,5]]},"2178":{"position":[[49,9]]},"2218":{"position":[[455,5]]},"2224":{"position":[[1237,5]]},"2328":{"position":[[94,5]]},"2387":{"position":[[498,5]]},"2518":{"position":[[1371,5]]},"2584":{"position":[[151,5]]},"2656":{"position":[[669,5]]},"2952":{"position":[[70,5]]},"2954":{"position":[[43,5]]},"2968":{"position":[[308,6]]},"3000":{"position":[[679,5]]},"3210":{"position":[[118,5]]},"3391":{"position":[[322,5]]},"3407":{"position":[[49,9]]},"3459":{"position":[[455,5]]},"3465":{"position":[[1237,5]]},"3584":{"position":[[94,5]]},"3601":{"position":[[498,5]]}}}],["referenc",{"_index":4365,"t":{"1093":{"position":[[1162,11]]},"2176":{"position":[[1162,11]]},"3405":{"position":[[1162,11]]}}}],["refin",{"_index":2599,"t":{"254":{"position":[[1645,6]]}}}],["reflect",{"_index":514,"t":{"12":{"position":[[1240,7]]},"204":{"position":[[2133,7]]},"220":{"position":[[92,7]]},"377":{"position":[[1436,11]]},"385":{"position":[[1179,7]]},"610":{"position":[[832,9]]},"1545":{"position":[[824,9]]},"2345":{"position":[[4914,9]]},"2355":{"position":[[4543,9]]},"2714":{"position":[[824,9]]},"3633":{"position":[[4914,9]]},"3643":{"position":[[4543,9]]}}}],["refresh",{"_index":1329,"t":{"68":{"position":[[1962,7],[1988,7]]},"178":{"position":[[352,8],[2119,7]]},"220":{"position":[[200,9]]},"301":{"position":[[2972,7]]},"345":{"position":[[262,7]]},"347":{"position":[[1531,7]]},"377":{"position":[[550,8],[1487,7],[1844,7]]},"423":{"position":[[73,7],[101,7]]},"618":{"position":[[331,7]]},"742":{"position":[[333,7],[352,7],[428,7],[941,7],[996,7]]},"764":{"position":[[741,7],[979,7],[1105,7],[1232,7],[1612,9],[1692,7],[1845,7],[1913,7],[2008,7],[2058,11],[2121,7]]},"1027":{"position":[[618,10],[634,7],[736,7]]},"1067":{"position":[[225,7],[326,7],[398,7],[649,7],[1200,7]]},"1075":{"position":[[368,7]]},"1077":{"position":[[607,7]]},"1079":{"position":[[821,7]]},"1093":{"position":[[437,10]]},"1095":{"position":[[452,7],[587,10]]},"1119":{"position":[[0,7],[15,10],[99,7],[193,7],[231,7],[316,7],[561,7]]},"1162":{"position":[[1609,7],[1672,7]]},"1166":{"position":[[676,7]]},"1172":{"position":[[308,10],[371,7],[398,7],[483,11],[543,9],[880,7],[963,7],[1172,7]]},"1270":{"position":[[398,7]]},"1333":{"position":[[262,7]]},"1335":{"position":[[1535,7]]},"1407":{"position":[[73,7],[103,7]]},"1477":{"position":[[365,7],[389,7]]},"1479":{"position":[[130,8],[176,7]]},"1493":{"position":[[132,8]]},"1495":{"position":[[8816,7]]},"1676":{"position":[[353,7],[1627,7],[1652,7]]},"1678":{"position":[[683,7]]},"1680":{"position":[[1123,7]]},"1761":{"position":[[333,7],[352,7],[428,7],[941,7],[996,7]]},"1783":{"position":[[741,7],[979,7],[1105,7],[1232,7],[1612,9],[1692,7],[1845,7],[1913,7],[2008,7],[2058,11],[2121,7]]},"1811":{"position":[[618,10],[634,7],[736,7]]},"1929":{"position":[[58,7]]},"1935":{"position":[[59,7]]},"2148":{"position":[[225,7],[326,7],[398,7],[649,7],[1200,7]]},"2156":{"position":[[268,7],[386,7],[467,7],[643,7],[761,7],[789,7],[958,7],[1305,7],[1345,7],[1771,7],[2050,7],[2673,7]]},"2158":{"position":[[304,7],[320,7]]},"2160":{"position":[[784,7],[800,7]]},"2162":{"position":[[821,7]]},"2176":{"position":[[437,10]]},"2178":{"position":[[452,7],[587,10]]},"2180":{"position":[[27,7],[997,7],[1198,7]]},"2200":{"position":[[0,7],[15,10],[99,7],[193,7],[231,7],[316,7],[527,7]]},"2240":{"position":[[2298,7],[2316,7],[2363,7]]},"2242":{"position":[[957,7],[991,9]]},"2262":{"position":[[295,7]]},"2264":{"position":[[292,7]]},"2294":{"position":[[468,7],[476,7],[787,7]]},"2345":{"position":[[4419,7],[5047,7]]},"2351":{"position":[[333,10]]},"2367":{"position":[[328,10]]},"2480":{"position":[[262,7]]},"2482":{"position":[[1692,7],[1769,7]]},"2564":{"position":[[73,7],[103,7]]},"2662":{"position":[[8934,7]]},"2670":{"position":[[365,7],[389,7]]},"2672":{"position":[[130,8],[176,7]]},"2686":{"position":[[132,8]]},"2830":{"position":[[356,7],[1630,7],[1655,7]]},"2832":{"position":[[689,7]]},"2834":{"position":[[1129,7]]},"2950":{"position":[[383,7],[406,7],[467,7],[1022,7]]},"2972":{"position":[[734,7],[949,7],[1058,7]]},"3000":{"position":[[618,10],[634,7],[736,7]]},"3122":{"position":[[58,7]]},"3130":{"position":[[59,7]]},"3377":{"position":[[225,7],[326,7],[398,7],[649,7],[1200,7]]},"3385":{"position":[[241,7],[359,7],[440,7],[616,7],[734,7],[762,7],[931,7],[1278,7],[1318,7],[1744,7],[2023,7],[2646,7]]},"3387":{"position":[[304,7],[320,7]]},"3389":{"position":[[784,7],[800,7]]},"3391":{"position":[[821,7]]},"3405":{"position":[[437,10]]},"3407":{"position":[[452,7],[587,10]]},"3409":{"position":[[27,7],[997,7],[1198,7]]},"3441":{"position":[[0,7],[15,10],[99,7],[186,7],[224,7],[309,7],[520,7]]},"3496":{"position":[[295,7]]},"3498":{"position":[[292,7]]},"3524":{"position":[[468,7],[476,7],[787,7]]},"3551":{"position":[[2298,7],[2316,7],[2363,7]]},"3553":{"position":[[957,7],[991,9]]},"3633":{"position":[[4419,7],[5047,7]]},"3639":{"position":[[333,10],[973,10],[1109,7]]},"3655":{"position":[[328,10],[1157,10],[1293,7]]}}}],["refresh_proxy_nam",{"_index":4376,"t":{"1095":{"position":[[565,21]]},"2178":{"position":[[565,21]]},"3407":{"position":[[565,21]]}}}],["refus",{"_index":5605,"t":{"3324":{"position":[[3172,7],[3333,7]]}}}],["regard",{"_index":2444,"t":{"222":{"position":[[938,7]]},"246":{"position":[[2345,9]]},"405":{"position":[[135,9]]},"479":{"position":[[3392,9]]},"539":{"position":[[55,9]]},"634":{"position":[[74,9]]},"1087":{"position":[[1201,9]]},"1511":{"position":[[491,9]]},"1603":{"position":[[74,9]]},"1879":{"position":[[582,9]]},"2048":{"position":[[504,9]]},"2086":{"position":[[164,9]]},"2170":{"position":[[1201,9]]},"2260":{"position":[[34,9]]},"2642":{"position":[[491,9]]},"2652":{"position":[[49,7]]},"2798":{"position":[[74,9]]},"3070":{"position":[[596,9]]},"3253":{"position":[[164,9]]},"3287":{"position":[[493,9]]},"3399":{"position":[[1201,9]]},"3494":{"position":[[34,9]]}}}],["regardless",{"_index":4689,"t":{"1469":{"position":[[1521,10]]},"2610":{"position":[[1830,10]]}}}],["regener",{"_index":3609,"t":{"556":{"position":[[326,10]]},"1019":{"position":[[165,11]]}}}],["regex",{"_index":3023,"t":{"391":{"position":[[1121,5]]},"1475":{"position":[[1882,5]]},"1483":{"position":[[3,6],[64,8]]},"1485":{"position":[[137,8]]},"1569":{"position":[[962,5]]},"1797":{"position":[[431,6],[601,6]]},"1923":{"position":[[768,6],[909,5],[1029,6]]},"2668":{"position":[[1882,5]]},"2676":{"position":[[3,6],[64,8]]},"2678":{"position":[[137,8]]},"2728":{"position":[[962,5]]},"2986":{"position":[[397,6],[567,6]]},"3116":{"position":[[768,6],[909,5],[1029,6]]}}}],["regexp",{"_index":3906,"t":{"734":{"position":[[589,6]]},"1093":{"position":[[1203,6]]},"1099":{"position":[[680,6]]},"1797":{"position":[[106,6]]},"1881":{"position":[[589,6]]},"1923":{"position":[[1064,6]]},"2176":{"position":[[1203,6]]},"2182":{"position":[[680,6]]},"2986":{"position":[[72,6]]},"3072":{"position":[[877,6]]},"3116":{"position":[[1064,6]]},"3405":{"position":[[1203,6]]},"3411":{"position":[[680,6]]}}}],["regist",{"_index":927,"t":{"30":{"position":[[764,11]]},"32":{"position":[[727,10]]},"86":{"position":[[2242,9]]},"150":{"position":[[619,8]]},"218":{"position":[[139,10]]},"260":{"position":[[919,11]]},"264":{"position":[[714,9]]},"268":{"position":[[64,10]]},"451":{"position":[[54,8]]},"1451":{"position":[[54,8]]},"1629":{"position":[[852,9]]},"1635":{"position":[[408,8],[534,10]]},"1650":{"position":[[0,9],[134,10]]},"1656":{"position":[[28,10]]},"1662":{"position":[[107,12]]},"2357":{"position":[[659,10]]},"2592":{"position":[[55,8]]},"2846":{"position":[[852,9]]},"2852":{"position":[[408,8],[534,10]]},"2867":{"position":[[0,9],[134,10]]},"2873":{"position":[[28,10]]},"2879":{"position":[[107,12]]},"3645":{"position":[[659,10]]}}}],["registered/upd",{"_index":4945,"t":{"1650":{"position":[[922,19]]},"2867":{"position":[[829,19]]}}}],["registri",{"_index":1410,"t":{"80":{"position":[[253,9]]},"592":{"position":[[152,9]]},"1557":{"position":[[152,9]]},"2264":{"position":[[378,8]]},"2355":{"position":[[280,9],[299,8],[4922,9],[5087,8]]},"2357":{"position":[[63,8],[172,8],[374,8],[474,9]]},"2369":{"position":[[326,8],[1041,8],[1310,8]]},"2760":{"position":[[152,9]]},"3498":{"position":[[378,8]]},"3643":{"position":[[280,9],[299,8],[4922,9],[5087,8]]},"3645":{"position":[[63,8],[172,8],[374,8],[474,9]]},"3657":{"position":[[326,8],[1041,8],[1310,8]]}}}],["regular",{"_index":1052,"t":{"38":{"position":[[806,7]]},"42":{"position":[[1876,7]]},"379":{"position":[[2558,7]]},"385":{"position":[[1074,7]]},"1923":{"position":[[59,7],[1083,7]]},"3116":{"position":[[59,7],[1083,7]]}}}],["regularli",{"_index":2592,"t":{"254":{"position":[[987,9]]},"477":{"position":[[80,9]]}}}],["reject",{"_index":936,"t":{"30":{"position":[[1176,8]]},"34":{"position":[[1554,6]]},"301":{"position":[[2612,7]]},"722":{"position":[[338,8]]},"804":{"position":[[83,8]]},"1475":{"position":[[639,8]]},"1741":{"position":[[63,8]]},"1743":{"position":[[592,8]]},"1745":{"position":[[104,8]]},"1747":{"position":[[104,8]]},"1855":{"position":[[95,8]]},"1921":{"position":[[229,7]]},"2351":{"position":[[867,7]]},"2367":{"position":[[849,7]]},"2668":{"position":[[639,8]]},"2930":{"position":[[63,8]]},"2932":{"position":[[592,8]]},"2934":{"position":[[104,8]]},"2936":{"position":[[104,8]]},"3046":{"position":[[95,8]]},"3114":{"position":[[229,7]]}}}],["reject(err",{"_index":2839,"t":{"301":{"position":[[2719,12]]},"2351":{"position":[[1167,12]]},"2367":{"position":[[1149,12]]}}}],["rel",{"_index":2332,"t":{"200":{"position":[[1254,10]]}}}],["rel=\"stylesheet",{"_index":2706,"t":{"283":{"position":[[1502,16]]}}}],["relat",{"_index":297,"t":{"6":{"position":[[515,6]]},"34":{"position":[[521,7],[1938,7]]},"132":{"position":[[1655,7]]},"138":{"position":[[1898,7]]},"156":{"position":[[414,8]]},"246":{"position":[[3479,7]]},"250":{"position":[[1742,7]]},"252":{"position":[[596,7]]},"254":{"position":[[856,7]]},"343":{"position":[[453,7]]},"385":{"position":[[1143,7]]},"477":{"position":[[2747,7]]},"509":{"position":[[61,7]]},"650":{"position":[[35,10]]},"668":{"position":[[33,10]]},"704":{"position":[[478,7]]},"975":{"position":[[30,7]]},"1164":{"position":[[1731,6]]},"1168":{"position":[[415,6],[613,6],[1254,7]]},"1170":{"position":[[128,7],[414,7]]},"1176":{"position":[[102,6]]},"1331":{"position":[[453,7]]},"1371":{"position":[[61,7]]},"1475":{"position":[[289,7]]},"1515":{"position":[[28,7]]},"1569":{"position":[[159,7]]},"1666":{"position":[[1295,6]]},"1696":{"position":[[35,10]]},"1712":{"position":[[33,10]]},"2030":{"position":[[30,7]]},"2244":{"position":[[1427,6]]},"2345":{"position":[[5055,7]]},"2355":{"position":[[4631,7]]},"2369":{"position":[[1903,7]]},"2377":{"position":[[73,7]]},"2478":{"position":[[453,7]]},"2522":{"position":[[66,7]]},"2646":{"position":[[28,7]]},"2668":{"position":[[289,7]]},"2728":{"position":[[159,7]]},"2820":{"position":[[35,10]]},"2883":{"position":[[1454,6]]},"2901":{"position":[[33,10]]},"3271":{"position":[[30,7]]},"3555":{"position":[[1427,6]]},"3633":{"position":[[5055,7]]},"3643":{"position":[[4631,7]]},"3657":{"position":[[1903,7]]},"3665":{"position":[[73,7]]}}}],["releas",{"_index":684,"t":{"16":{"position":[[4634,8]]},"26":{"position":[[1245,8]]},"50":{"position":[[1648,9],[2392,8],[2546,8]]},"60":{"position":[[93,7]]},"62":{"position":[[197,8],[335,7]]},"66":{"position":[[83,8]]},"82":{"position":[[23,7]]},"88":{"position":[[1027,8]]},"90":{"position":[[104,7]]},"94":{"position":[[2975,8],[3036,7]]},"118":{"position":[[504,8]]},"144":{"position":[[383,8]]},"146":{"position":[[348,8]]},"196":{"position":[[59,8]]},"212":{"position":[[11,8],[851,7]]},"216":{"position":[[87,7],[163,8],[711,7]]},"220":{"position":[[393,8]]},"248":{"position":[[10,7],[108,8]]},"279":{"position":[[534,8]]},"321":{"position":[[1135,8]]},"373":{"position":[[88,7]]},"375":{"position":[[55,8],[698,8],[837,8]]},"377":{"position":[[190,7],[266,8]]},"387":{"position":[[907,9]]},"399":{"position":[[440,8]]},"401":{"position":[[130,8]]},"405":{"position":[[4,7]]},"407":{"position":[[127,8]]},"479":{"position":[[2792,7]]},"523":{"position":[[74,7],[134,8],[180,7]]},"531":{"position":[[36,7]]},"588":{"position":[[22,8],[93,9],[119,7]]},"594":{"position":[[25,7]]},"596":{"position":[[25,7]]},"987":{"position":[[244,8]]},"1307":{"position":[[1135,8]]},"1431":{"position":[[68,7],[144,8],[190,7]]},"1439":{"position":[[36,7]]},"1495":{"position":[[476,7]]},"1499":{"position":[[122,7]]},"1503":{"position":[[70,7]]},"1553":{"position":[[22,8],[93,9],[119,7]]},"1559":{"position":[[25,7]]},"1561":{"position":[[25,7]]},"2046":{"position":[[244,8]]},"2454":{"position":[[1135,8]]},"2614":{"position":[[71,7],[150,8],[204,7]]},"2622":{"position":[[36,7]]},"2630":{"position":[[122,7]]},"2634":{"position":[[70,7]]},"2652":{"position":[[821,7]]},"2656":{"position":[[311,8]]},"2660":{"position":[[19,7]]},"2662":{"position":[[482,7]]},"2756":{"position":[[22,8],[93,9],[119,7]]},"2762":{"position":[[25,7]]},"2764":{"position":[[25,7]]},"2780":{"position":[[268,8]]}}}],["released#new",{"_index":5447,"t":{"2656":{"position":[[733,12]]}}}],["relev",{"_index":2597,"t":{"254":{"position":[[1530,8]]}}}],["reli",{"_index":811,"t":{"20":{"position":[[271,4]]},"54":{"position":[[1195,4]]},"210":{"position":[[182,4]]},"238":{"position":[[594,4]]},"367":{"position":[[236,4]]},"483":{"position":[[1587,6]]},"485":{"position":[[1478,4]]},"762":{"position":[[286,6]]},"965":{"position":[[2076,6]]},"1029":{"position":[[363,6]]},"1035":{"position":[[409,6]]},"1065":{"position":[[770,4]]},"1387":{"position":[[236,4]]},"1682":{"position":[[804,4]]},"1741":{"position":[[1304,7]]},"1781":{"position":[[286,6]]},"1813":{"position":[[363,6]]},"1825":{"position":[[365,6]]},"2146":{"position":[[770,4]]},"2383":{"position":[[293,4]]},"2482":{"position":[[562,4]]},"2538":{"position":[[236,4]]},"2836":{"position":[[804,4]]},"2930":{"position":[[1304,7]]},"3002":{"position":[[363,6]]},"3014":{"position":[[725,6]]},"3327":{"position":[[315,7]]},"3349":{"position":[[661,7]]},"3375":{"position":[[770,4]]},"3671":{"position":[[293,4]]}}}],["reliabl",{"_index":2329,"t":{"200":{"position":[[907,8]]},"222":{"position":[[548,8]]},"256":{"position":[[972,12],[1104,9]]},"399":{"position":[[1446,8]]},"965":{"position":[[2863,11]]},"3324":{"position":[[5234,11]]}}}],["reload",{"_index":743,"t":{"18":{"position":[[297,6],[3205,6]]},"42":{"position":[[1749,6]]},"74":{"position":[[1351,8]]},"180":{"position":[[323,7]]},"236":{"position":[[608,6]]},"361":{"position":[[336,7]]},"505":{"position":[[771,8],[854,8]]},"932":{"position":[[50,6],[122,6]]},"965":{"position":[[1030,7]]},"1065":{"position":[[1459,7]]},"1367":{"position":[[771,8],[854,8]]},"1381":{"position":[[336,7]]},"1495":{"position":[[5295,6],[7092,6]]},"2007":{"position":[[50,6],[122,6]]},"2102":{"position":[[1023,7],[4490,7]]},"2146":{"position":[[1576,7]]},"2518":{"position":[[802,8],[885,8]]},"2532":{"position":[[360,7]]},"2662":{"position":[[5357,6],[7173,6]]},"3224":{"position":[[50,6],[122,6]]},"3261":{"position":[[1023,7],[4490,7]]},"3375":{"position":[[1576,7]]},"3612":{"position":[[2189,7]]}}}],["remain",{"_index":1103,"t":{"44":{"position":[[516,9]]},"1475":{"position":[[150,9]]},"2668":{"position":[[150,9]]},"2950":{"position":[[745,6]]},"2972":{"position":[[913,9]]}}}],["rememb",{"_index":1109,"t":{"44":{"position":[[1493,8]]},"377":{"position":[[1543,8]]},"399":{"position":[[74,8]]},"888":{"position":[[192,8]]},"1955":{"position":[[192,8]]},"3168":{"position":[[192,8]]}}}],["remot",{"_index":2369,"t":{"210":{"position":[[93,6]]},"600":{"position":[[874,6]]},"1619":{"position":[[1156,6]]},"2806":{"position":[[1156,6]]}}}],["remote_addr",{"_index":2032,"t":{"134":{"position":[[943,13],[1368,13]]},"287":{"position":[[908,13],[1245,13]]},"1001":{"position":[[876,13],[1130,13]]},"1003":{"position":[[436,13],[680,13]]},"2108":{"position":[[876,13],[1130,13]]},"2110":{"position":[[436,13],[680,13]]},"3309":{"position":[[876,13],[1130,13]]},"3311":{"position":[[436,13],[680,13]]}}}],["remov",{"_index":608,"t":{"16":{"position":[[1681,7]]},"26":{"position":[[1694,6]]},"42":{"position":[[766,7]]},"64":{"position":[[672,8]]},"70":{"position":[[1582,6]]},"126":{"position":[[446,6],[500,8]]},"196":{"position":[[516,8]]},"202":{"position":[[276,8]]},"212":{"position":[[159,8]]},"216":{"position":[[106,8]]},"254":{"position":[[1022,6]]},"363":{"position":[[1161,7]]},"387":{"position":[[885,7],[934,8]]},"479":{"position":[[2514,6],[3065,6]]},"539":{"position":[[94,8]]},"545":{"position":[[23,7]]},"550":{"position":[[148,8]]},"560":{"position":[[456,6]]},"562":{"position":[[30,8]]},"570":{"position":[[200,7],[222,7],[253,7],[286,7],[309,7]]},"1127":{"position":[[22,8],[487,6],[591,6],[614,6]]},"1170":{"position":[[317,6]]},"1383":{"position":[[1312,7]]},"1499":{"position":[[1432,6],[1530,6]]},"1507":{"position":[[552,6]]},"1513":{"position":[[46,8]]},"1515":{"position":[[65,8],[319,8]]},"1625":{"position":[[477,7]]},"1635":{"position":[[933,6]]},"1645":{"position":[[150,6]]},"1654":{"position":[[0,7],[238,7],[318,6],[378,6]]},"1658":{"position":[[150,6]]},"1662":{"position":[[124,7],[272,6]]},"2032":{"position":[[346,7]]},"2038":{"position":[[340,7]]},"2208":{"position":[[22,8],[487,6],[591,6],[614,6]]},"2238":{"position":[[619,7]]},"2355":{"position":[[5053,6]]},"2357":{"position":[[439,7],[540,8]]},"2530":{"position":[[1709,8]]},"2630":{"position":[[1432,6],[1530,6]]},"2638":{"position":[[552,6]]},"2644":{"position":[[46,8]]},"2646":{"position":[[65,8],[319,8]]},"2656":{"position":[[208,6]]},"2658":{"position":[[38,7],[61,7],[181,7],[223,7],[260,7],[342,7],[385,7],[424,7]]},"2842":{"position":[[477,7]]},"2852":{"position":[[933,6]]},"2862":{"position":[[158,6]]},"2869":{"position":[[719,6]]},"2871":{"position":[[0,7],[238,7],[318,6]]},"2875":{"position":[[150,6]]},"2879":{"position":[[124,7],[272,6]]},"3273":{"position":[[346,7]]},"3279":{"position":[[340,7]]},"3449":{"position":[[22,8],[180,6],[277,6],[300,6]]},"3549":{"position":[[619,7]]},"3643":{"position":[[5053,6]]},"3645":{"position":[[439,7],[540,8]]}}}],["removed/switch",{"_index":2638,"t":{"264":{"position":[[939,17]]},"1629":{"position":[[1052,17]]},"2846":{"position":[[1052,17]]}}}],["removehistory(ch",{"_index":1140,"t":{"46":{"position":[[1100,16]]}}}],["removepresence(ch",{"_index":1153,"t":{"46":{"position":[[1886,17]]}}}],["removesubscription(sub",{"_index":5346,"t":{"2357":{"position":[[415,23]]},"3645":{"position":[[415,23]]}}}],["renam",{"_index":1306,"t":{"64":{"position":[[653,8]]},"550":{"position":[[81,7]]},"556":{"position":[[129,6],[165,6]]},"570":{"position":[[324,7],[374,7],[413,7],[462,7],[526,7],[590,7],[649,7],[721,7],[769,7],[820,7],[867,7],[914,7],[2490,7],[2539,7],[2591,7]]},"1515":{"position":[[54,7],[98,7]]},"1517":{"position":[[109,7]]},"2646":{"position":[[54,7],[98,7]]},"2648":{"position":[[109,7]]},"2658":{"position":[[281,7]]}}}],["render",{"_index":2101,"t":{"146":{"position":[[586,9],[614,7]]},"367":{"position":[[377,6]]},"764":{"position":[[1473,9]]},"1341":{"position":[[528,9]]},"1387":{"position":[[377,6]]},"1783":{"position":[[1473,9]]},"2488":{"position":[[528,9]]},"2538":{"position":[[383,6]]},"2972":{"position":[[1425,9]]}}}],["render(request",{"_index":1880,"t":{"128":{"position":[[1472,15]]},"130":{"position":[[2053,15],[2124,15]]}}}],["renderdef",{"_index":1878,"t":{"128":{"position":[[1439,9]]},"130":{"position":[[2020,9]]}}}],["renew",{"_index":4268,"t":{"1049":{"position":[[1252,8]]},"2230":{"position":[[1252,8]]},"3417":{"position":[[1252,8]]}}}],["repartit",{"_index":5426,"t":{"2504":{"position":[[925,14]]}}}],["repeat",{"_index":4946,"t":{"1652":{"position":[[182,8],[228,8],[283,8],[922,8]]},"1654":{"position":[[192,8],[252,8]]},"1656":{"position":[[144,8],[211,8],[296,8],[368,8],[437,8],[983,8]]},"1658":{"position":[[171,8]]},"1660":{"position":[[100,8],[174,8],[266,8],[345,8],[421,8],[487,8],[910,8]]},"1662":{"position":[[293,8]]},"1664":{"position":[[91,8],[150,8],[471,8]]},"1666":{"position":[[528,8],[607,8],[676,8],[844,8],[1013,8]]},"2869":{"position":[[182,8],[228,8],[740,8]]},"2871":{"position":[[192,8],[252,8]]},"2873":{"position":[[670,8],[737,8],[816,8],[885,8],[951,8],[1067,8]]},"2875":{"position":[[171,8]]},"2877":{"position":[[555,8],[629,8],[715,8],[791,8],[857,8],[1034,8]]},"2879":{"position":[[293,8]]},"2881":{"position":[[427,8],[486,8],[654,8]]},"2883":{"position":[[873,8],[1041,8],[1210,8]]}}}],["replac",{"_index":659,"t":{"16":{"position":[[3616,9]]},"46":{"position":[[3758,7]]},"64":{"position":[[940,11]]},"134":{"position":[[1538,8]]},"138":{"position":[[75,7],[479,7],[908,7]]},"168":{"position":[[765,7]]},"228":{"position":[[634,7]]},"379":{"position":[[1804,9]]},"479":{"position":[[2854,7]]},"481":{"position":[[6228,8]]},"550":{"position":[[183,12]]},"564":{"position":[[359,8]]},"878":{"position":[[983,7]]},"1071":{"position":[[610,11]]},"1650":{"position":[[527,8]]},"1945":{"position":[[983,7]]},"2152":{"position":[[610,11]]},"2258":{"position":[[297,8]]},"2294":{"position":[[1286,8]]},"2504":{"position":[[259,12]]},"2656":{"position":[[497,7],[545,7]]},"2867":{"position":[[527,8]]},"3158":{"position":[[983,7]]},"3492":{"position":[[297,8]]},"3524":{"position":[[1286,8]]}}}],["repli",{"_index":2928,"t":{"355":{"position":[[713,9]]},"383":{"position":[[1369,5]]},"389":{"position":[[375,5]]},"475":{"position":[[2457,6]]},"716":{"position":[[455,5]]},"758":{"position":[[1336,5]]},"764":{"position":[[889,6]]},"780":{"position":[[61,7],[186,5],[358,7]]},"838":{"position":[[69,7]]},"965":{"position":[[1496,6],[2623,6],[3237,5]]},"1071":{"position":[[3174,6],[3257,6]]},"1164":{"position":[[201,5],[310,5],[433,6],[1345,5],[1746,5],[1938,7],[2027,7],[2200,5],[2271,9],[2474,7],[2526,5],[2754,5],[2930,5],[3072,5],[3204,5],[3483,5],[3505,6],[3517,7],[3582,7]]},"1166":{"position":[[1043,6]]},"1168":{"position":[[336,5],[1168,5],[1330,5],[1386,5]]},"1172":{"position":[[574,5],[721,5]]},"1176":{"position":[[520,5]]},"1178":{"position":[[124,7],[264,5]]},"1184":{"position":[[2680,7]]},"1186":{"position":[[511,5]]},"1192":{"position":[[154,5]]},"1341":{"position":[[145,5]]},"1347":{"position":[[713,9]]},"1613":{"position":[[79,7],[110,7]]},"1777":{"position":[[863,5]]},"1783":{"position":[[889,6]]},"1831":{"position":[[61,7],[186,5],[358,7]]},"1895":{"position":[[547,5]]},"2102":{"position":[[1668,6],[2283,5],[2908,5]]},"2152":{"position":[[3106,6],[3189,6]]},"2238":{"position":[[202,7]]},"2240":{"position":[[72,5],[183,5],[301,7],[353,5],[582,5],[806,5],[911,5],[1023,5],[1261,5]]},"2242":{"position":[[20,5],[42,6],[54,7],[119,7]]},"2244":{"position":[[148,6],[1035,5],[1442,5],[1585,5],[1736,7],[1825,7],[1998,5],[2069,9]]},"2246":{"position":[[129,7]]},"2252":{"position":[[156,5]]},"2262":{"position":[[192,5]]},"2264":{"position":[[221,5]]},"2436":{"position":[[713,9]]},"2488":{"position":[[145,5]]},"2738":{"position":[[79,7],[110,7]]},"2966":{"position":[[941,5]]},"3022":{"position":[[61,7],[186,5],[358,7]]},"3088":{"position":[[547,5]]},"3261":{"position":[[1668,6],[2283,5],[2908,5]]},"3324":{"position":[[2297,7]]},"3349":{"position":[[4039,8]]},"3381":{"position":[[2909,6],[2992,6]]},"3455":{"position":[[151,6],[528,11]]},"3496":{"position":[[192,5]]},"3498":{"position":[[221,5]]},"3549":{"position":[[202,7]]},"3551":{"position":[[72,5],[183,5],[301,7],[353,5],[582,5],[806,5],[911,5],[1023,5],[1261,5]]},"3553":{"position":[[20,5],[42,6],[54,7],[119,7]]},"3555":{"position":[[148,6],[1035,5],[1442,5],[1585,5],[1736,7],[1825,7],[1998,5],[2069,9]]},"3557":{"position":[[129,7]]},"3563":{"position":[[156,5]]}}}],["replic",{"_index":1387,"t":{"74":{"position":[[1704,13]]},"250":{"position":[[1906,9]]},"361":{"position":[[1296,11],[1378,11]]},"989":{"position":[[1501,11],[1959,12]]},"991":{"position":[[444,13]]},"1381":{"position":[[1301,11],[1383,11]]},"2050":{"position":[[1501,11],[1959,12]]},"2052":{"position":[[444,13]]},"2502":{"position":[[386,9]]},"2532":{"position":[[1396,11],[1471,11]]},"3289":{"position":[[1501,11],[1959,12]]},"3291":{"position":[[444,13]]}}}],["replica",{"_index":3159,"t":{"475":{"position":[[153,7]]},"989":{"position":[[1315,7]]},"991":{"position":[[356,9]]},"2050":{"position":[[1315,7]]},"2052":{"position":[[356,9]]},"3289":{"position":[[1315,7]]},"3291":{"position":[[356,9]]}}}],["replica(",{"_index":3188,"t":{"475":{"position":[[3222,10]]}}}],["replicatedmergetree('/clickhouse/tables/{cluster}/{shard}/connect",{"_index":3729,"t":{"606":{"position":[[446,71]]},"1535":{"position":[[366,71]]},"2704":{"position":[[366,71]]}}}],["replicatedmergetree('/clickhouse/tables/{cluster}/{shard}/oper",{"_index":3743,"t":{"608":{"position":[[502,70]]},"1539":{"position":[[493,70]]},"2708":{"position":[[493,70]]}}}],["replication\\r\\n",{"_index":3174,"t":{"475":{"position":[[1181,15]]},"981":{"position":[[730,15]]},"2040":{"position":[[730,15]]},"3281":{"position":[[730,15]]}}}],["replica}')partit",{"_index":3730,"t":{"606":{"position":[[518,21]]},"608":{"position":[[573,21]]},"1535":{"position":[[438,21]]},"1539":{"position":[[564,21]]},"2704":{"position":[[438,21]]},"2708":{"position":[[564,21]]}}}],["replies.push(repli",{"_index":4558,"t":{"1164":{"position":[[2239,20]]},"2244":{"position":[[2037,20]]},"3555":{"position":[[2037,20]]}}}],["reply.app",{"_index":2926,"t":{"355":{"position":[[637,9]]},"1347":{"position":[[637,9]]},"2436":{"position":[[637,9]]}}}],["reply=\"id:2",{"_index":4781,"t":{"1495":{"position":[[7393,11]]},"2662":{"position":[[7473,11]]}}}],["replycentrifugo",{"_index":2927,"t":{"355":{"position":[[678,15]]},"1347":{"position":[[678,15]]},"2436":{"position":[[678,15]]}}}],["repo",{"_index":794,"t":{"18":{"position":[[2884,4]]},"56":{"position":[[55,5]]},"150":{"position":[[128,5]]},"216":{"position":[[201,4]]},"281":{"position":[[619,4]]},"377":{"position":[[1151,4]]},"481":{"position":[[1880,5],[1929,4]]},"584":{"position":[[61,5]]},"588":{"position":[[80,4]]},"949":{"position":[[543,4]]},"989":{"position":[[1741,4],[1847,4]]},"1553":{"position":[[80,4]]},"2050":{"position":[[1741,4],[1847,4]]},"2076":{"position":[[543,4]]},"2756":{"position":[[80,4]]},"3243":{"position":[[543,4]]},"3289":{"position":[[1741,4],[1847,4]]},"3355":{"position":[[126,5]]}}}],["report",{"_index":1231,"t":{"54":{"position":[[178,6]]},"266":{"position":[[517,8]]},"576":{"position":[[268,9]]},"1583":{"position":[[265,9]]},"2048":{"position":[[296,8]]},"2778":{"position":[[265,9]]},"3287":{"position":[[318,8]]}}}],["repositori",{"_index":3679,"t":{"592":{"position":[[172,11]]},"1079":{"position":[[230,11]]},"1557":{"position":[[172,11]]},"2162":{"position":[[230,11]]},"2760":{"position":[[172,11]]},"3349":{"position":[[1682,11]]},"3391":{"position":[[230,11]]}}}],["repres",{"_index":1642,"t":{"104":{"position":[[402,10]]},"477":{"position":[[552,10]]},"943":{"position":[[268,10]]},"2018":{"position":[[268,10]]},"2952":{"position":[[5,10]]},"3237":{"position":[[268,10]]}}}],["represent",{"_index":3932,"t":{"754":{"position":[[138,14]]},"1137":{"position":[[118,14]]},"1773":{"position":[[138,14]]},"1879":{"position":[[65,15]]},"2218":{"position":[[118,14]]},"2258":{"position":[[243,14],[803,15],[1335,14]]},"2962":{"position":[[148,14]]},"3070":{"position":[[65,15]]},"3459":{"position":[[118,14]]},"3492":{"position":[[243,14],[803,15],[1335,14]]}}}],["reproduc",{"_index":208,"t":{"2":{"position":[[3616,9]]},"985":{"position":[[1219,9]]},"2044":{"position":[[1219,9]]},"3285":{"position":[[1219,9]]}}}],["req",{"_index":1554,"t":{"98":{"position":[[408,3],[601,3],[1346,3],[2093,3]]},"283":{"position":[[721,5],[1995,5],[2519,5]]},"287":{"position":[[1663,5]]},"481":{"position":[[5763,3]]},"3349":{"position":[[2661,3],[2835,4]]},"3351":{"position":[[1951,4],[2197,4]]}}}],["req.body.password",{"_index":2716,"t":{"283":{"position":[[2050,17]]}}}],["req.body.usernam",{"_index":2715,"t":{"283":{"position":[[2014,18],[2106,18]]}}}],["req.publication.data",{"_index":5707,"t":{"3351":{"position":[[2228,20]]}}}],["req.session.destroy",{"_index":2721,"t":{"283":{"position":[[2535,22]]}}}],["req.session.userid",{"_index":2680,"t":{"283":{"position":[[740,20],[2085,18]]},"287":{"position":[[1682,20],[1732,18]]}}}],["req.subscriberequest",{"_index":5706,"t":{"3351":{"position":[[2017,21]]}}}],["request",{"_index":757,"t":{"18":{"position":[[862,8],[1269,7]]},"38":{"position":[[33,7],[240,7],[740,7],[819,7]]},"44":{"position":[[1570,7]]},"50":{"position":[[1697,7]]},"68":{"position":[[903,8]]},"78":{"position":[[480,7]]},"108":{"position":[[922,7]]},"134":{"position":[[2010,8]]},"136":{"position":[[1309,7],[1680,7]]},"138":{"position":[[1114,8]]},"146":{"position":[[853,10]]},"158":{"position":[[310,8]]},"160":{"position":[[622,8]]},"162":{"position":[[180,7],[590,7]]},"166":{"position":[[392,7]]},"168":{"position":[[1039,8]]},"174":{"position":[[400,7]]},"178":{"position":[[651,7]]},"180":{"position":[[112,10],[773,7]]},"202":{"position":[[161,9]]},"204":{"position":[[73,8],[167,7],[339,7],[508,7],[583,7],[1466,7]]},"208":{"position":[[87,9],[221,7]]},"222":{"position":[[143,7]]},"224":{"position":[[63,8]]},"226":{"position":[[536,8]]},"240":{"position":[[171,8],[1035,8]]},"246":{"position":[[2704,7]]},"254":{"position":[[139,7]]},"262":{"position":[[124,7],[1031,8],[2001,8]]},"281":{"position":[[347,8]]},"283":{"position":[[2482,8]]},"287":{"position":[[337,8],[532,8]]},"289":{"position":[[286,8]]},"297":{"position":[[682,7]]},"321":{"position":[[137,8]]},"339":{"position":[[460,7],[765,7],[811,7],[1160,7]]},"341":{"position":[[179,8]]},"355":{"position":[[496,7]]},"359":{"position":[[365,8],[929,8],[972,7],[1303,7]]},"365":{"position":[[136,8]]},"371":{"position":[[190,8]]},"377":{"position":[[393,7]]},"379":{"position":[[1638,8],[2509,8],[2713,8],[4214,10]]},"387":{"position":[[1185,8]]},"389":{"position":[[1223,7]]},"397":{"position":[[295,7]]},"421":{"position":[[94,7],[187,7],[206,7]]},"429":{"position":[[201,7]]},"453":{"position":[[671,7]]},"457":{"position":[[808,8]]},"471":{"position":[[286,7]]},"473":{"position":[[993,8],[1120,8],[3821,8],[3949,8]]},"475":{"position":[[2040,8],[2074,7],[2152,8]]},"477":{"position":[[1136,8],[3088,7]]},"479":{"position":[[1492,7],[1545,7],[2956,7]]},"481":{"position":[[1730,9],[2091,9],[2180,8],[2263,8],[2489,8],[2610,8],[5089,8],[5275,7]]},"560":{"position":[[278,7]]},"570":{"position":[[158,8]]},"578":{"position":[[301,8]]},"600":{"position":[[178,8],[279,8]]},"610":{"position":[[271,8]]},"618":{"position":[[109,8]]},"652":{"position":[[513,7]]},"654":{"position":[[390,7]]},"662":{"position":[[49,8]]},"670":{"position":[[105,7]]},"672":{"position":[[105,7]]},"680":{"position":[[339,7]]},"682":{"position":[[246,7]]},"684":{"position":[[204,7]]},"694":{"position":[[388,7]]},"742":{"position":[[1004,9]]},"764":{"position":[[1186,7],[1270,7],[1746,7],[1921,7]]},"776":{"position":[[666,7]]},"784":{"position":[[65,7]]},"796":{"position":[[25,9],[45,7],[134,7]]},"804":{"position":[[30,10],[56,8],[92,7]]},"816":{"position":[[25,9],[68,7]]},"846":{"position":[[25,9],[45,7]]},"892":{"position":[[316,7],[401,8],[923,8]]},"912":{"position":[[259,8],[771,9]]},"926":{"position":[[197,8]]},"937":{"position":[[132,9],[479,9]]},"953":{"position":[[347,9]]},"977":{"position":[[1204,8]]},"1019":{"position":[[421,7]]},"1049":{"position":[[1175,7]]},"1059":{"position":[[30,8],[130,8],[194,7]]},"1061":{"position":[[90,7],[111,7],[458,7],[748,8]]},"1063":{"position":[[119,8],[308,8]]},"1065":{"position":[[174,8],[393,8],[486,7],[545,7],[1053,7],[1125,7],[1352,7],[1604,8],[2270,7],[2368,8],[3437,8],[3537,7],[3632,8],[4719,7]]},"1067":{"position":[[406,7],[657,7],[1706,7]]},"1069":{"position":[[359,8],[635,7],[1690,7]]},"1071":{"position":[[178,8],[1718,8],[1959,7],[2619,7],[3956,7]]},"1073":{"position":[[241,7],[496,7],[1002,8],[1733,8],[1993,7],[3292,7]]},"1075":{"position":[[376,7]]},"1077":{"position":[[615,7]]},"1085":{"position":[[282,7],[325,7],[361,7],[405,7]]},"1087":{"position":[[600,8],[932,8]]},"1093":{"position":[[1437,7]]},"1097":{"position":[[990,8]]},"1099":{"position":[[339,8],[411,8]]},"1105":{"position":[[69,7],[106,7],[365,9]]},"1107":{"position":[[193,7],[1271,7]]},"1109":{"position":[[304,7],[322,8],[2107,7]]},"1111":{"position":[[455,7]]},"1125":{"position":[[143,7],[1114,7]]},"1133":{"position":[[51,7]]},"1135":{"position":[[15,7],[110,7],[296,8]]},"1139":{"position":[[362,8]]},"1149":{"position":[[233,8]]},"1162":{"position":[[1446,8]]},"1164":{"position":[[330,7],[1367,7],[2699,7]]},"1182":{"position":[[751,8],[1128,7]]},"1184":{"position":[[2606,8]]},"1205":{"position":[[66,7]]},"1207":{"position":[[720,8]]},"1213":{"position":[[52,8],[801,9],[872,8],[1025,7]]},"1222":{"position":[[66,7],[105,8]]},"1266":{"position":[[399,8],[459,7]]},"1307":{"position":[[137,8]]},"1319":{"position":[[486,7]]},"1327":{"position":[[460,7],[765,7],[811,7],[1160,7]]},"1329":{"position":[[179,8]]},"1341":{"position":[[310,7]]},"1347":{"position":[[496,7]]},"1379":{"position":[[365,8],[929,8],[972,7],[1303,7]]},"1385":{"position":[[136,8]]},"1391":{"position":[[190,8]]},"1405":{"position":[[94,7],[200,7],[219,7]]},"1413":{"position":[[190,7]]},"1453":{"position":[[672,7]]},"1457":{"position":[[804,8]]},"1495":{"position":[[4822,7],[5121,7],[9066,7],[9923,7]]},"1505":{"position":[[228,7]]},"1545":{"position":[[271,8]]},"1577":{"position":[[1205,7]]},"1579":{"position":[[62,8]]},"1585":{"position":[[301,8]]},"1619":{"position":[[178,8],[279,8],[450,8],[472,8]]},"1639":{"position":[[266,9]]},"1650":{"position":[[57,8]]},"1652":{"position":[[137,8]]},"1654":{"position":[[142,8]]},"1656":{"position":[[60,7],[99,8]]},"1658":{"position":[[56,8]]},"1660":{"position":[[48,8]]},"1662":{"position":[[185,8]]},"1664":{"position":[[44,8]]},"1666":{"position":[[126,7],[257,8],[1946,7]]},"1668":{"position":[[628,8],[718,8],[816,8]]},"1676":{"position":[[1587,7]]},"1678":{"position":[[415,8]]},"1680":{"position":[[571,8],[786,8]]},"1682":{"position":[[1028,7]]},"1699":{"position":[[513,7]]},"1702":{"position":[[390,7]]},"1706":{"position":[[49,8]]},"1715":{"position":[[105,7]]},"1717":{"position":[[105,7]]},"1731":{"position":[[339,7]]},"1733":{"position":[[246,7]]},"1735":{"position":[[204,7]]},"1741":{"position":[[531,8]]},"1745":{"position":[[328,8],[537,8]]},"1747":{"position":[[474,8],[687,8]]},"1761":{"position":[[1004,9]]},"1783":{"position":[[1186,7],[1270,7],[1746,7],[1921,7]]},"1795":{"position":[[844,7]]},"1835":{"position":[[62,7]]},"1847":{"position":[[23,8],[42,7],[131,7]]},"1855":{"position":[[68,8],[104,7]]},"1863":{"position":[[1409,9]]},"1865":{"position":[[219,8]]},"1885":{"position":[[670,7]]},"1893":{"position":[[851,7]]},"1959":{"position":[[316,7],[401,8],[923,8]]},"1975":{"position":[[212,7]]},"1977":{"position":[[99,8],[236,8]]},"1981":{"position":[[259,8],[771,9]]},"2001":{"position":[[197,8]]},"2012":{"position":[[132,9],[479,9]]},"2034":{"position":[[1204,8]]},"2080":{"position":[[347,9]]},"2102":{"position":[[4271,8]]},"2118":{"position":[[380,7]]},"2140":{"position":[[30,8],[130,8],[194,7]]},"2142":{"position":[[90,7],[111,7],[458,7],[748,8]]},"2144":{"position":[[119,8],[308,8]]},"2146":{"position":[[174,8],[393,8],[486,7],[545,7],[1170,7],[1242,7],[1469,7],[1721,8],[2387,7],[2485,8],[3554,8],[3654,7],[3749,8],[4827,7]]},"2148":{"position":[[406,7],[657,7],[1697,7]]},"2150":{"position":[[359,8],[635,7],[1681,7]]},"2152":{"position":[[178,8],[1718,8],[1959,7],[2619,7],[3978,7]]},"2154":{"position":[[241,7],[496,7],[1002,8],[1733,8],[1993,7],[3283,7]]},"2156":{"position":[[966,8],[1293,8],[1779,7],[2058,7],[3168,7]]},"2158":{"position":[[334,8]]},"2160":{"position":[[814,8]]},"2168":{"position":[[282,7],[325,7],[361,7],[405,7]]},"2170":{"position":[[600,8],[932,8]]},"2176":{"position":[[1437,7]]},"2180":{"position":[[1206,8]]},"2182":{"position":[[339,8],[411,8]]},"2186":{"position":[[69,7],[106,7],[374,9]]},"2188":{"position":[[196,7],[1274,7]]},"2190":{"position":[[364,7],[382,8],[2161,7]]},"2192":{"position":[[455,7]]},"2206":{"position":[[143,7],[1114,7]]},"2214":{"position":[[51,7],[245,7]]},"2216":{"position":[[15,7],[110,7],[296,8]]},"2220":{"position":[[362,8]]},"2230":{"position":[[1175,7]]},"2240":{"position":[[527,7],[1864,7],[1933,7],[2001,7]]},"2244":{"position":[[37,7],[1057,7]]},"2250":{"position":[[751,8],[1128,7]]},"2269":{"position":[[138,8]]},"2271":{"position":[[66,7]]},"2277":{"position":[[233,8]]},"2290":{"position":[[405,8],[469,7]]},"2301":{"position":[[141,8]]},"2303":{"position":[[66,7],[104,8]]},"2337":{"position":[[66,7]]},"2339":{"position":[[720,8]]},"2347":{"position":[[423,7]]},"2363":{"position":[[266,8]]},"2365":{"position":[[160,7],[210,7],[285,7]]},"2387":{"position":[[52,8],[801,9],[872,8],[1025,7]]},"2394":{"position":[[66,7],[104,8]]},"2436":{"position":[[496,7]]},"2454":{"position":[[137,8]]},"2466":{"position":[[486,7]]},"2474":{"position":[[460,7],[765,7],[811,7],[1160,7]]},"2476":{"position":[[179,8]]},"2482":{"position":[[1777,8]]},"2488":{"position":[[310,7]]},"2506":{"position":[[34,8]]},"2530":{"position":[[798,8],[2336,8],[2461,7],[2479,8],[2526,7]]},"2536":{"position":[[137,8]]},"2542":{"position":[[194,8]]},"2562":{"position":[[100,7],[208,7],[227,7]]},"2570":{"position":[[190,7]]},"2594":{"position":[[700,7]]},"2598":{"position":[[823,8]]},"2636":{"position":[[228,7]]},"2656":{"position":[[415,8],[505,7]]},"2662":{"position":[[4884,7],[5183,7],[9193,7],[10068,7]]},"2714":{"position":[[271,8]]},"2722":{"position":[[1449,7]]},"2744":{"position":[[264,7],[988,9]]},"2770":{"position":[[1194,7]]},"2772":{"position":[[62,8]]},"2780":{"position":[[972,7]]},"2782":{"position":[[300,8]]},"2806":{"position":[[178,8],[279,8],[450,8],[472,8]]},"2823":{"position":[[502,7]]},"2826":{"position":[[379,7]]},"2830":{"position":[[1590,7]]},"2832":{"position":[[415,8]]},"2834":{"position":[[571,8],[786,8]]},"2836":{"position":[[1028,7]]},"2856":{"position":[[266,9]]},"2862":{"position":[[364,8],[779,7]]},"2867":{"position":[[57,8]]},"2869":{"position":[[137,8]]},"2871":{"position":[[142,8]]},"2873":{"position":[[60,7],[99,8]]},"2875":{"position":[[56,8]]},"2877":{"position":[[48,8]]},"2879":{"position":[[185,8]]},"2881":{"position":[[44,8]]},"2883":{"position":[[126,7],[257,8],[2225,7]]},"2885":{"position":[[96,8]]},"2887":{"position":[[628,8],[718,8],[816,8]]},"2895":{"position":[[49,8]]},"2904":{"position":[[94,7]]},"2906":{"position":[[94,7]]},"2920":{"position":[[328,7]]},"2922":{"position":[[235,7]]},"2924":{"position":[[193,7]]},"2930":{"position":[[531,8]]},"2934":{"position":[[328,8],[537,8]]},"2936":{"position":[[474,8],[687,8]]},"2950":{"position":[[1030,9]]},"2972":{"position":[[1284,7]]},"2984":{"position":[[844,7]]},"3026":{"position":[[62,7]]},"3038":{"position":[[23,8],[42,7],[131,7]]},"3046":{"position":[[68,8],[104,7]]},"3054":{"position":[[1409,9]]},"3056":{"position":[[219,8]]},"3060":{"position":[[246,7]]},"3076":{"position":[[659,7]]},"3084":{"position":[[840,7]]},"3172":{"position":[[316,7],[401,8],[923,8]]},"3188":{"position":[[185,7]]},"3190":{"position":[[72,8],[209,8]]},"3194":{"position":[[259,8],[771,9]]},"3198":{"position":[[238,7],[435,8],[762,9],[1187,8]]},"3218":{"position":[[197,8]]},"3231":{"position":[[132,9],[479,9]]},"3247":{"position":[[347,9]]},"3261":{"position":[[4271,8]]},"3275":{"position":[[1204,8]]},"3327":{"position":[[72,8],[812,8]]},"3333":{"position":[[383,7],[736,7]]},"3347":{"position":[[501,8],[977,9]]},"3349":{"position":[[2825,9]]},"3351":{"position":[[1998,7]]},"3369":{"position":[[35,8],[135,8],[199,7]]},"3371":{"position":[[90,7],[111,7],[458,7],[748,8],[938,9],[1328,7]]},"3373":{"position":[[119,8],[308,8]]},"3375":{"position":[[174,8],[393,8],[486,7],[545,7],[1170,7],[1242,7],[1469,7],[1721,8],[2387,7],[2485,8],[3554,8],[3654,7],[3749,8],[4856,7]]},"3377":{"position":[[406,7],[657,7],[1697,7]]},"3379":{"position":[[406,8],[682,7],[1728,7]]},"3381":{"position":[[178,8],[619,8],[666,8],[1521,8],[1762,7],[2422,7],[3981,7]]},"3383":{"position":[[241,7],[496,7],[1002,8],[1733,8],[1993,7],[3283,7]]},"3385":{"position":[[939,8],[1266,8],[1752,7],[2031,7],[3160,7]]},"3387":{"position":[[334,8]]},"3389":{"position":[[814,8]]},"3397":{"position":[[282,7],[325,7],[361,7],[405,7]]},"3399":{"position":[[600,8],[932,8]]},"3405":{"position":[[1437,7],[1632,9],[1689,8]]},"3409":{"position":[[1206,8]]},"3411":{"position":[[339,8],[411,8]]},"3417":{"position":[[1175,7]]},"3425":{"position":[[60,7],[113,7],[335,7],[567,8]]},"3427":{"position":[[196,7]]},"3431":{"position":[[261,7],[527,8],[1061,7],[1445,8],[1669,7]]},"3433":{"position":[[138,7],[272,8],[530,7]]},"3435":{"position":[[133,8]]},"3437":{"position":[[66,8]]},"3439":{"position":[[57,8]]},"3441":{"position":[[107,8]]},"3443":{"position":[[291,7],[647,8]]},"3445":{"position":[[173,7],[343,8]]},"3447":{"position":[[143,7],[506,7],[782,8],[956,7]]},"3449":{"position":[[187,8]]},"3451":{"position":[[118,7],[191,8]]},"3453":{"position":[[300,8]]},"3455":{"position":[[42,8],[289,8],[339,7]]},"3457":{"position":[[15,7],[110,7],[296,8]]},"3461":{"position":[[362,8]]},"3467":{"position":[[820,7],[1102,8],[1208,7],[1498,9],[1722,7]]},"3476":{"position":[[138,8],[379,8],[780,8]]},"3478":{"position":[[66,7]]},"3482":{"position":[[440,8],[504,7]]},"3504":{"position":[[233,8]]},"3518":{"position":[[141,8],[358,8],[760,8]]},"3520":{"position":[[66,7],[104,8]]},"3551":{"position":[[527,7],[1864,7],[1933,7],[2001,7]]},"3555":{"position":[[37,7],[1057,7]]},"3561":{"position":[[751,8],[1128,7]]},"3593":{"position":[[66,7]]},"3595":{"position":[[720,8]]},"3601":{"position":[[52,8],[801,9],[872,8],[1025,7]]},"3608":{"position":[[66,7],[104,8]]},"3635":{"position":[[423,7]]},"3651":{"position":[[266,8]]},"3653":{"position":[[160,7],[210,7],[285,7]]}}}],["request.what",{"_index":1581,"t":{"98":{"position":[[1480,12]]}}}],["request/result",{"_index":4347,"t":{"1087":{"position":[[47,14]]},"2170":{"position":[[47,14]]},"3399":{"position":[[47,14]]}}}],["requesta",{"_index":1600,"t":{"98":{"position":[[2046,8]]}}}],["requestcont",{"_index":5741,"t":{"3467":{"position":[[1377,14]]}}}],["requestdata[\"messag",{"_index":2155,"t":{"164":{"position":[[414,24]]}}}],["requestnote:cli",{"_index":2923,"t":{"355":{"position":[[409,18],[802,18],[1244,18]]},"1347":{"position":[[409,18],[802,18],[1244,18]]},"2436":{"position":[[409,18],[802,18],[1244,18]]}}}],["requests\"temporari",{"_index":5042,"t":{"1855":{"position":[[28,19]]},"3046":{"position":[[28,19]]}}}],["requests.post(\"https://centrifuge.example.com/api",{"_index":4394,"t":{"1109":{"position":[[611,51]]},"2190":{"position":[[671,51]]}}}],["requests.post(\"https://centrifuge.example.com/api/publish",{"_index":5725,"t":{"3431":{"position":[[748,59]]}}}],["requests/sec",{"_index":3820,"t":{"622":{"position":[[229,12]]},"1591":{"position":[[229,12]]},"2786":{"position":[[229,12]]}}}],["requestsapi_key",{"_index":5724,"t":{"3431":{"position":[[574,15]]}}}],["requestscentrifugo",{"_index":2922,"t":{"355":{"position":[[299,18]]},"1347":{"position":[[299,18]]},"2436":{"position":[[299,18]]}}}],["requestscommand",{"_index":4390,"t":{"1109":{"position":[[369,15]]},"2190":{"position":[[429,15]]}}}],["requir",{"_index":4,"t":{"2":{"position":[[57,12]]},"16":{"position":[[1363,12],[5667,7],[7036,13]]},"18":{"position":[[1503,8]]},"30":{"position":[[699,8]]},"50":{"position":[[1983,8]]},"52":{"position":[[335,12]]},"54":{"position":[[625,8],[1246,8]]},"58":{"position":[[676,12]]},"74":{"position":[[800,12]]},"82":{"position":[[222,8],[397,9]]},"94":{"position":[[1304,12],[2031,13]]},"132":{"position":[[1587,8]]},"142":{"position":[[84,8]]},"148":{"position":[[475,7]]},"182":{"position":[[1834,7]]},"216":{"position":[[596,11],[754,8]]},"244":{"position":[[698,7]]},"246":{"position":[[835,7]]},"299":{"position":[[690,8],[820,8]]},"301":{"position":[[2995,9]]},"309":{"position":[[67,8]]},"319":{"position":[[291,8]]},"321":{"position":[[655,8]]},"343":{"position":[[143,8]]},"361":{"position":[[27,8]]},"379":{"position":[[2649,7],[2852,12],[4636,8]]},"387":{"position":[[1021,8]]},"419":{"position":[[439,7]]},"449":{"position":[[406,8]]},"475":{"position":[[2760,8]]},"523":{"position":[[653,8]]},"525":{"position":[[447,8]]},"566":{"position":[[97,8]]},"570":{"position":[[126,8]]},"576":{"position":[[1023,8]]},"580":{"position":[[37,8]]},"648":{"position":[[1062,7]]},"652":{"position":[[702,8]]},"654":{"position":[[622,8],[800,8]]},"666":{"position":[[920,7]]},"670":{"position":[[284,8]]},"672":{"position":[[263,8]]},"680":{"position":[[510,8]]},"682":{"position":[[1099,8]]},"684":{"position":[[375,8]]},"694":{"position":[[416,8],[717,7]]},"718":{"position":[[549,7]]},"734":{"position":[[953,9]]},"806":{"position":[[110,8]]},"846":{"position":[[139,8]]},"850":{"position":[[108,8]]},"866":{"position":[[109,8]]},"969":{"position":[[273,7]]},"979":{"position":[[325,9]]},"1019":{"position":[[0,9]]},"1021":{"position":[[0,9]]},"1051":{"position":[[138,8]]},"1053":{"position":[[186,8]]},"1061":{"position":[[240,8]]},"1071":{"position":[[534,7]]},"1087":{"position":[[1113,8],[1269,8]]},"1091":{"position":[[38,8]]},"1093":{"position":[[184,8],[1095,8]]},"1109":{"position":[[1914,8]]},"1111":{"position":[[228,8]]},"1113":{"position":[[96,8],[455,8],[604,8]]},"1115":{"position":[[191,8],[372,8],[460,8]]},"1117":{"position":[[154,8],[275,8],[363,8],[593,8]]},"1119":{"position":[[145,8],[251,8],[336,8]]},"1121":{"position":[[797,8]]},"1123":{"position":[[533,8]]},"1125":{"position":[[971,8],[1398,8]]},"1127":{"position":[[532,8]]},"1129":{"position":[[162,8]]},"1149":{"position":[[476,8]]},"1158":{"position":[[101,8]]},"1184":{"position":[[1807,8]]},"1186":{"position":[[299,8]]},"1249":{"position":[[98,8],[155,8]]},"1251":{"position":[[22,8]]},"1266":{"position":[[381,8]]},"1268":{"position":[[173,7]]},"1295":{"position":[[67,8]]},"1305":{"position":[[291,8]]},"1307":{"position":[[655,8]]},"1331":{"position":[[143,8]]},"1381":{"position":[[27,8]]},"1403":{"position":[[558,7]]},"1431":{"position":[[886,8]]},"1433":{"position":[[449,8]]},"1495":{"position":[[598,8]]},"1499":{"position":[[34,8]]},"1507":{"position":[[23,8],[110,8]]},"1567":{"position":[[84,8]]},"1579":{"position":[[121,8]]},"1583":{"position":[[1507,8]]},"1587":{"position":[[37,8]]},"1625":{"position":[[166,8]]},"1650":{"position":[[77,8],[864,8]]},"1652":{"position":[[157,8],[630,8],[708,8],[799,8],[894,8]]},"1654":{"position":[[167,8]]},"1656":{"position":[[119,8],[956,8]]},"1658":{"position":[[76,8]]},"1660":{"position":[[68,8],[883,8],[1072,8]]},"1662":{"position":[[205,8]]},"1664":{"position":[[64,8],[613,8]]},"1666":{"position":[[282,8],[496,8],[1098,8],[1509,8],[1621,8],[1743,8]]},"1668":{"position":[[784,8],[836,8]]},"1694":{"position":[[1062,7]]},"1699":{"position":[[702,8]]},"1702":{"position":[[622,8],[804,8]]},"1710":{"position":[[920,7]]},"1715":{"position":[[284,8]]},"1717":{"position":[[263,8]]},"1731":{"position":[[510,8]]},"1733":{"position":[[1099,8]]},"1735":{"position":[[375,8]]},"1767":{"position":[[350,8]]},"1769":{"position":[[338,8]]},"1797":{"position":[[1349,8]]},"1805":{"position":[[0,9]]},"1857":{"position":[[107,8]]},"1869":{"position":[[1025,8]]},"1885":{"position":[[1576,9]]},"1891":{"position":[[849,9]]},"1897":{"position":[[902,7]]},"2024":{"position":[[273,7]]},"2036":{"position":[[325,9]]},"2062":{"position":[[109,8]]},"2142":{"position":[[240,8]]},"2152":{"position":[[534,7]]},"2170":{"position":[[1113,8],[1269,8]]},"2174":{"position":[[38,8]]},"2176":{"position":[[184,8],[1095,8]]},"2190":{"position":[[1968,8]]},"2192":{"position":[[228,8]]},"2194":{"position":[[96,8],[455,8],[604,8]]},"2196":{"position":[[191,8],[372,8],[460,8]]},"2198":{"position":[[154,8],[275,8],[363,8],[559,8]]},"2200":{"position":[[145,8],[251,8],[336,8]]},"2202":{"position":[[840,8]]},"2204":{"position":[[533,8]]},"2206":{"position":[[971,8],[1398,8]]},"2208":{"position":[[532,8]]},"2210":{"position":[[162,8]]},"2232":{"position":[[138,8]]},"2234":{"position":[[154,8]]},"2277":{"position":[[476,8]]},"2286":{"position":[[101,8]]},"2290":{"position":[[387,8]]},"2292":{"position":[[173,7]]},"2357":{"position":[[735,8]]},"2400":{"position":[[98,8],[155,8]]},"2402":{"position":[[22,8]]},"2434":{"position":[[622,9]]},"2442":{"position":[[67,8]]},"2452":{"position":[[291,8]]},"2454":{"position":[[655,8]]},"2478":{"position":[[143,8]]},"2504":{"position":[[955,7]]},"2532":{"position":[[27,8]]},"2560":{"position":[[684,7]]},"2614":{"position":[[910,8]]},"2616":{"position":[[453,8]]},"2630":{"position":[[34,8]]},"2638":{"position":[[23,8],[110,8]]},"2656":{"position":[[451,9]]},"2662":{"position":[[613,8]]},"2726":{"position":[[84,8]]},"2750":{"position":[[11,8]]},"2752":{"position":[[16,8]]},"2772":{"position":[[121,8]]},"2774":{"position":[[431,8]]},"2780":{"position":[[15,8],[427,9]]},"2818":{"position":[[1062,7]]},"2823":{"position":[[666,8]]},"2826":{"position":[[586,8],[768,8]]},"2842":{"position":[[166,8]]},"2867":{"position":[[77,8],[771,8]]},"2869":{"position":[[157,8],[489,8],[567,8],[660,8]]},"2871":{"position":[[167,8]]},"2873":{"position":[[119,8],[645,8],[1040,8],[1304,8]]},"2875":{"position":[[76,8]]},"2877":{"position":[[68,8],[523,8],[1007,8],[1290,8]]},"2879":{"position":[[205,8]]},"2881":{"position":[[64,8],[400,8],[768,8]]},"2883":{"position":[[282,8],[757,8],[1295,8],[1788,8],[1900,8],[2022,8]]},"2885":{"position":[[116,8]]},"2887":{"position":[[784,8],[836,8]]},"2899":{"position":[[920,7]]},"2904":{"position":[[248,8]]},"2906":{"position":[[227,8]]},"2920":{"position":[[474,8]]},"2922":{"position":[[1063,8]]},"2924":{"position":[[339,8]]},"2962":{"position":[[53,9]]},"2994":{"position":[[0,9]]},"3048":{"position":[[107,8]]},"3066":{"position":[[152,9]]},"3068":{"position":[[200,8]]},"3076":{"position":[[1540,9]]},"3082":{"position":[[849,9]]},"3090":{"position":[[902,7]]},"3140":{"position":[[109,8]]},"3265":{"position":[[273,7]]},"3277":{"position":[[325,9]]},"3324":{"position":[[1334,8]]},"3345":{"position":[[1271,7]]},"3371":{"position":[[240,8]]},"3399":{"position":[[1113,8],[1269,8]]},"3403":{"position":[[38,8]]},"3405":{"position":[[184,8],[1095,8]]},"3419":{"position":[[138,8]]},"3421":{"position":[[154,8]]},"3431":{"position":[[1476,8]]},"3433":{"position":[[303,8]]},"3435":{"position":[[164,8],[523,8],[672,8]]},"3437":{"position":[[97,8],[278,8],[366,8]]},"3439":{"position":[[88,8],[209,8],[297,8],[493,8]]},"3441":{"position":[[138,8],[244,8],[329,8]]},"3443":{"position":[[678,8]]},"3445":{"position":[[374,8]]},"3447":{"position":[[813,8],[1240,8]]},"3449":{"position":[[218,8]]},"3451":{"position":[[222,8]]},"3476":{"position":[[419,8],[818,9]]},"3482":{"position":[[422,8]]},"3484":{"position":[[173,7]]},"3504":{"position":[[476,8]]},"3513":{"position":[[101,8]]},"3518":{"position":[[398,8],[798,9]]},"3567":{"position":[[98,8],[155,8]]},"3569":{"position":[[22,8]]},"3612":{"position":[[649,9]]},"3645":{"position":[[735,8]]}}}],["require(\"cooki",{"_index":2663,"t":{"283":{"position":[[203,15]]}}}],["require('axios');const",{"_index":2668,"t":{"283":{"position":[[319,22]]}}}],["require('express",{"_index":2665,"t":{"283":{"position":[[245,16]]}}}],["require('express');const",{"_index":2661,"t":{"283":{"position":[[163,24]]}}}],["require('jose')(async",{"_index":2237,"t":{"178":{"position":[[1404,21]]},"2978":{"position":[[225,21]]},"2980":{"position":[[198,21]]},"3014":{"position":[[333,21]]}}}],["require('jose');(async",{"_index":2279,"t":{"188":{"position":[[486,22]]},"2976":{"position":[[102,22]]}}}],["require('jsonwebtoken');var",{"_index":3955,"t":{"768":{"position":[[99,27]]},"770":{"position":[[222,27]]},"772":{"position":[[195,27]]},"1787":{"position":[[99,27]]},"1789":{"position":[[222,27]]},"1791":{"position":[[195,27]]}}}],["require('morgan');const",{"_index":2667,"t":{"283":{"position":[[287,23]]}}}],["requiretransportsecur",{"_index":4531,"t":{"1143":{"position":[[537,26]]},"2224":{"position":[[537,26]]},"3465":{"position":[[537,26]]}}}],["res.error",{"_index":3319,"t":{"479":{"position":[[1181,11]]}}}],["res.json",{"_index":2747,"t":{"287":{"position":[[1705,10],[1764,10]]},"2351":{"position":[[1096,11]]},"2367":{"position":[[1078,11]]},"3639":{"position":[[1208,11]]},"3655":{"position":[[1392,11]]}}}],["res.ok",{"_index":5278,"t":{"2351":{"position":[[1018,9]]},"2367":{"position":[[1000,9]]},"3639":{"position":[[885,9]]},"3655":{"position":[[1069,9]]}}}],["res.redirect",{"_index":2717,"t":{"283":{"position":[[2125,18],[2558,21]]}}}],["res.send('invalid",{"_index":2718,"t":{"283":{"position":[[2153,17]]}}}],["res.sendfile('views/app.html",{"_index":2681,"t":{"283":{"position":[[763,30]]}}}],["res.sendfile('views/login.html",{"_index":2683,"t":{"283":{"position":[[823,32]]}}}],["res.statu",{"_index":5281,"t":{"2351":{"position":[[1070,16]]},"2367":{"position":[[1052,16]]},"3639":{"position":[[900,11],[1170,16]]},"3655":{"position":[[1084,11],[1354,16]]}}}],["resav",{"_index":2677,"t":{"283":{"position":[[540,7]]}}}],["research",{"_index":486,"t":{"12":{"position":[[278,8]]}}}],["reserv",{"_index":3671,"t":{"576":{"position":[[926,7]]},"690":{"position":[[208,8]]},"1075":{"position":[[229,8]]},"1077":{"position":[[338,8]]},"1583":{"position":[[1410,7]]},"1871":{"position":[[200,8]]},"2160":{"position":[[488,8]]},"2371":{"position":[[79,8]]},"3062":{"position":[[200,8]]},"3389":{"position":[[488,8]]},"3659":{"position":[[79,8]]}}}],["reset",{"_index":2313,"t":{"198":{"position":[[899,5],[1509,5]]},"1184":{"position":[[1436,5]]}}}],["reshard",{"_index":2955,"t":{"371":{"position":[[954,10]]},"1391":{"position":[[954,10]]},"2542":{"position":[[969,10]]}}}],["resili",{"_index":2969,"t":{"377":{"position":[[2220,10]]},"403":{"position":[[227,9]]},"3324":{"position":[[3036,10]]}}}],["resolut",{"_index":3398,"t":{"481":{"position":[[2652,11]]},"618":{"position":[[368,11]]},"941":{"position":[[169,10]]},"975":{"position":[[980,12]]},"1676":{"position":[[399,11]]},"1678":{"position":[[729,11]]},"1680":{"position":[[1169,11]]},"2016":{"position":[[169,10]]},"2720":{"position":[[582,11]]},"2774":{"position":[[129,10],[348,10]]},"2830":{"position":[[402,11]]},"2832":{"position":[[735,11]]},"2834":{"position":[[1175,11]]},"2889":{"position":[[1142,11]]},"3235":{"position":[[169,10]]}}}],["resolv",{"_index":3329,"t":{"479":{"position":[[2758,8]]},"489":{"position":[[229,8]]},"517":{"position":[[473,9]]},"983":{"position":[[53,8]]},"1469":{"position":[[242,8]]},"2042":{"position":[[53,8]]},"2610":{"position":[[243,10]]},"3283":{"position":[[53,8]]}}}],["resolve(data.token",{"_index":5283,"t":{"2351":{"position":[[1127,20]]},"2367":{"position":[[1109,20]]}}}],["resolve(keycloak.token",{"_index":2837,"t":{"301":{"position":[[2668,24]]}}}],["resourc",{"_index":130,"t":{"2":{"position":[[1991,8],[2066,8],[2916,9]]},"4":{"position":[[373,9]]},"12":{"position":[[613,8]]},"14":{"position":[[1375,8]]},"16":{"position":[[3379,9],[4259,8],[4416,8]]},"28":{"position":[[1458,8]]},"88":{"position":[[981,8]]},"168":{"position":[[888,9]]},"174":{"position":[[1329,9]]},"188":{"position":[[219,8],[1055,9]]},"371":{"position":[[284,8]]},"383":{"position":[[426,8]]},"463":{"position":[[36,8]]},"485":{"position":[[1415,8]]},"576":{"position":[[730,8]]},"788":{"position":[[86,8]]},"798":{"position":[[68,8]]},"848":{"position":[[68,8]]},"1391":{"position":[[284,8]]},"1463":{"position":[[36,8]]},"1481":{"position":[[42,8]]},"1583":{"position":[[1105,8]]},"1682":{"position":[[75,9]]},"1839":{"position":[[83,8]]},"1849":{"position":[[65,8]]},"2296":{"position":[[997,8]]},"2345":{"position":[[168,9],[5438,9]]},"2357":{"position":[[566,9]]},"2504":{"position":[[973,10]]},"2542":{"position":[[287,8]]},"2604":{"position":[[36,8]]},"2674":{"position":[[42,8]]},"2778":{"position":[[1203,8]]},"2836":{"position":[[75,9]]},"2889":{"position":[[854,9]]},"3030":{"position":[[83,8]]},"3040":{"position":[[65,8]]},"3345":{"position":[[43,8],[954,9],[1032,8],[1284,9]]},"3347":{"position":[[373,9],[987,8]]},"3486":{"position":[[997,8]]},"3633":{"position":[[168,9],[5438,9]]},"3645":{"position":[[566,9]]}}}],["resp",{"_index":1039,"t":{"38":{"position":[[306,4]]},"42":{"position":[[1024,4]]},"477":{"position":[[2060,4]]},"511":{"position":[[190,4],[350,4],[540,4],[724,4]]},"513":{"position":[[209,4],[249,4],[431,4],[476,4]]},"1139":{"position":[[549,4]]},"1141":{"position":[[942,5]]},"1143":{"position":[[810,5]]},"1373":{"position":[[190,4],[350,4],[540,4],[724,4]]},"1375":{"position":[[209,4],[249,4],[431,4],[476,4]]},"2220":{"position":[[549,4]]},"2222":{"position":[[942,5]]},"2224":{"position":[[810,5]]},"2379":{"position":[[190,4],[350,4],[540,4],[724,4]]},"2381":{"position":[[209,4],[249,4],[431,4],[476,4]]},"2524":{"position":[[190,4],[350,4],[540,4],[724,4]]},"2526":{"position":[[209,4],[249,4],[431,4],[476,4]]},"3335":{"position":[[269,4],[445,4],[674,4]]},"3461":{"position":[[549,4]]},"3463":{"position":[[942,5]]},"3465":{"position":[[810,5]]},"3667":{"position":[[190,4],[350,4],[540,4],[724,4]]},"3669":{"position":[[209,4],[249,4],[431,4],[476,4]]}}}],["resp.err",{"_index":3212,"t":{"477":{"position":[[2126,10]]}}}],["resp.error.cod",{"_index":4490,"t":{"1139":{"position":[[677,16]]},"2220":{"position":[[677,16]]},"3461":{"position":[[677,16]]}}}],["resp.error.messag",{"_index":4492,"t":{"1139":{"position":[[750,19]]},"2220":{"position":[[750,19]]},"3461":{"position":[[750,19]]}}}],["resp.geterror",{"_index":4518,"t":{"1141":{"position":[[1163,15],[1201,15]]},"1143":{"position":[[1022,15],[1060,15]]},"2222":{"position":[[1163,15],[1201,15]]},"2224":{"position":[[1022,15],[1060,15]]},"3463":{"position":[[1163,15],[1201,15]]},"3465":{"position":[[1022,15],[1060,15]]}}}],["resp2",{"_index":3189,"t":{"475":{"position":[[3394,5],[3517,5]]},"479":{"position":[[2824,5],[2925,5]]},"2030":{"position":[[698,5]]},"3271":{"position":[[664,5]]}}}],["resp3",{"_index":5142,"t":{"2030":{"position":[[849,5]]},"3271":{"position":[[815,5]]}}}],["respect",{"_index":4668,"t":{"1349":{"position":[[159,10]]},"2494":{"position":[[160,10]]}}}],["resperror",{"_index":4519,"t":{"1141":{"position":[[1188,9]]},"1143":{"position":[[1047,9]]},"2222":{"position":[[1188,9]]},"2224":{"position":[[1047,9]]},"3463":{"position":[[1188,9]]},"3465":{"position":[[1047,9]]}}}],["resperror.cod",{"_index":4521,"t":{"1141":{"position":[[1245,15]]},"1143":{"position":[[1104,15]]},"2222":{"position":[[1245,15]]},"2224":{"position":[[1104,15]]},"3463":{"position":[[1245,15]]},"3465":{"position":[[1104,15]]}}}],["resperror.messag",{"_index":4522,"t":{"1141":{"position":[[1261,18]]},"1143":{"position":[[1120,18]]},"2222":{"position":[[1261,18]]},"2224":{"position":[[1120,18]]},"3463":{"position":[[1261,18]]},"3465":{"position":[[1120,18]]}}}],["respond",{"_index":3001,"t":{"383":{"position":[[335,7]]},"2722":{"position":[[1868,7]]}}}],["respons",{"_index":984,"t":{"34":{"position":[[697,11]]},"38":{"position":[[85,8],[223,8]]},"44":{"position":[[1690,8]]},"46":{"position":[[639,11],[1611,11]]},"68":{"position":[[915,10]]},"76":{"position":[[469,11]]},"112":{"position":[[95,9]]},"136":{"position":[[1144,8],[1530,8],[1898,8]]},"162":{"position":[[722,8]]},"200":{"position":[[1095,8]]},"214":{"position":[[460,8]]},"242":{"position":[[211,9]]},"246":{"position":[[2612,9]]},"254":{"position":[[1615,8]]},"262":{"position":[[1474,8],[1765,9]]},"293":{"position":[[167,14]]},"367":{"position":[[364,8]]},"369":{"position":[[595,8]]},"377":{"position":[[401,8]]},"469":{"position":[[116,11],[1137,11]]},"473":{"position":[[539,8],[2473,9]]},"481":{"position":[[6429,8]]},"483":{"position":[[1851,9]]},"558":{"position":[[4,8]]},"682":{"position":[[374,8],[551,8],[663,8]]},"722":{"position":[[241,9]]},"764":{"position":[[1323,8]]},"912":{"position":[[420,8]]},"1065":{"position":[[369,8],[1979,8],[2029,8],[4015,9],[4103,8]]},"1067":{"position":[[593,8]]},"1069":{"position":[[572,8],[1469,8],[1565,8]]},"1071":{"position":[[1889,8]]},"1073":{"position":[[1930,8]]},"1075":{"position":[[356,8]]},"1077":{"position":[[595,8]]},"1087":{"position":[[433,9]]},"1109":{"position":[[1332,8]]},"1111":{"position":[[702,9],[729,9],[742,9]]},"1164":{"position":[[368,8],[1759,8],[1903,8],[2707,8],[2829,8]]},"1166":{"position":[[992,8]]},"1168":{"position":[[296,8]]},"1170":{"position":[[253,8]]},"1174":{"position":[[74,8]]},"1266":{"position":[[411,10]]},"1387":{"position":[[364,8]]},"1389":{"position":[[595,8]]},"1682":{"position":[[317,9]]},"1733":{"position":[[374,8],[551,8],[663,8]]},"1741":{"position":[[1140,8]]},"1743":{"position":[[1463,8]]},"1745":{"position":[[1119,9]]},"1747":{"position":[[273,9]]},"1783":{"position":[[1323,8]]},"1977":{"position":[[304,9]]},"1981":{"position":[[420,8]]},"2146":{"position":[[369,8],[2096,8],[2146,8],[4132,9],[4220,8],[6047,9],[6235,9]]},"2148":{"position":[[593,8]]},"2150":{"position":[[572,8],[1469,8],[1565,8]]},"2152":{"position":[[1889,8],[4135,9],[4680,8]]},"2154":{"position":[[1930,8]]},"2156":{"position":[[1990,8]]},"2158":{"position":[[291,8]]},"2160":{"position":[[771,8]]},"2170":{"position":[[433,9]]},"2190":{"position":[[1392,8]]},"2192":{"position":[[932,9],[959,9],[972,9]]},"2240":{"position":[[535,8],[769,9],[2176,8],[2288,9]]},"2244":{"position":[[79,8],[1455,8],[1701,8]]},"2290":{"position":[[417,10]]},"2349":{"position":[[220,8]]},"2530":{"position":[[2469,9]]},"2538":{"position":[[370,8]]},"2540":{"position":[[601,8]]},"2780":{"position":[[955,8]]},"2836":{"position":[[317,9]]},"2877":{"position":[[483,9]]},"2881":{"position":[[362,9]]},"2922":{"position":[[338,8],[515,8],[627,8]]},"2930":{"position":[[1140,8]]},"2932":{"position":[[1463,8]]},"2934":{"position":[[1119,9]]},"2936":{"position":[[273,9]]},"2972":{"position":[[880,9],[1296,8]]},"3190":{"position":[[277,9]]},"3194":{"position":[[420,8]]},"3324":{"position":[[2845,9],[5967,10],[6187,10]]},"3375":{"position":[[369,8],[2096,8],[2146,8],[4161,9],[4249,8],[6076,9],[6264,9]]},"3377":{"position":[[593,8]]},"3379":{"position":[[619,8],[1516,8],[1612,8]]},"3381":{"position":[[1692,8],[4138,9],[4683,8]]},"3383":{"position":[[1930,8]]},"3385":{"position":[[1963,8]]},"3387":{"position":[[291,8]]},"3389":{"position":[[771,8]]},"3399":{"position":[[433,9]]},"3431":{"position":[[413,8],[884,8],[1179,8]]},"3433":{"position":[[1007,9],[1034,9],[1047,9]]},"3443":{"position":[[380,9]]},"3445":{"position":[[268,9]]},"3447":{"position":[[606,9]]},"3453":{"position":[[79,9]]},"3455":{"position":[[516,9]]},"3467":{"position":[[253,8],[560,9],[877,8],[1151,8],[1216,8],[2153,9]]},"3482":{"position":[[452,10]]},"3551":{"position":[[535,8],[769,9],[2176,8],[2288,9]]},"3555":{"position":[[79,8],[1455,8],[1701,8]]},"3629":{"position":[[461,8]]},"3637":{"position":[[220,8]]}}}],["rest",{"_index":4850,"t":{"1567":{"position":[[32,4]]},"1611":{"position":[[802,4]]},"1647":{"position":[[343,4]]},"1923":{"position":[[860,4]]},"2726":{"position":[[32,4]]},"2736":{"position":[[802,4]]},"2864":{"position":[[328,4]]},"3116":{"position":[[860,4]]}}}],["restart",{"_index":2073,"t":{"136":{"position":[[2271,7]]},"287":{"position":[[1851,7]]},"289":{"position":[[809,10]]},"361":{"position":[[355,8],[985,8],[1058,8],[1136,7]]},"371":{"position":[[1113,7]]},"644":{"position":[[176,8],[353,9]]},"662":{"position":[[156,8],[314,9]]},"854":{"position":[[355,7]]},"961":{"position":[[744,9]]},"965":{"position":[[2177,9],[2389,7],[2444,7],[2742,8]]},"969":{"position":[[612,8]]},"1065":{"position":[[1506,7]]},"1162":{"position":[[599,8]]},"1381":{"position":[[355,8],[990,8],[1063,8],[1141,7]]},"1391":{"position":[[1113,7]]},"1495":{"position":[[5208,7],[8395,7]]},"1690":{"position":[[176,8],[353,9]]},"1706":{"position":[[156,8],[314,9]]},"1721":{"position":[[355,7]]},"1893":{"position":[[1486,8]]},"2024":{"position":[[612,9]]},"2098":{"position":[[800,9]]},"2102":{"position":[[3968,9]]},"2146":{"position":[[1623,7]]},"2532":{"position":[[379,8],[1051,8],[1125,9],[1207,7]]},"2542":{"position":[[1132,7]]},"2662":{"position":[[5270,7],[8496,7]]},"2814":{"position":[[176,8],[353,9]]},"2895":{"position":[[156,8],[314,9]]},"2910":{"position":[[355,7]]},"3084":{"position":[[1450,8]]},"3257":{"position":[[800,9]]},"3261":{"position":[[3968,9]]},"3265":{"position":[[612,9]]},"3375":{"position":[[1623,7]]}}}],["restor",{"_index":812,"t":{"20":{"position":[[444,7],[1408,7]]},"48":{"position":[[1243,7]]},"200":{"position":[[465,7]]},"361":{"position":[[1483,8],[1549,7],[1667,7]]},"371":{"position":[[1299,7]]},"505":{"position":[[650,7],[965,8]]},"600":{"position":[[495,7]]},"830":{"position":[[195,7]]},"965":{"position":[[162,7],[442,7]]},"1184":{"position":[[2177,7]]},"1367":{"position":[[650,7],[965,8]]},"1381":{"position":[[1488,8],[1554,7],[1672,7]]},"1391":{"position":[[1299,7]]},"1619":{"position":[[777,7]]},"1863":{"position":[[923,7]]},"2102":{"position":[[162,7],[442,7]]},"2518":{"position":[[681,7],[1007,8]]},"2532":{"position":[[1648,8],[1727,7],[1846,7]]},"2542":{"position":[[1320,7]]},"2806":{"position":[[777,7]]},"3054":{"position":[[923,7]]},"3261":{"position":[[162,7],[442,7]]}}}],["restrict",{"_index":3873,"t":{"694":{"position":[[888,11]]},"1569":{"position":[[20,12]]},"2728":{"position":[[20,12]]},"3068":{"position":[[611,11]]}}}],["restructur",{"_index":2999,"t":{"381":{"position":[[670,13]]}}}],["resubscrib",{"_index":772,"t":{"18":{"position":[[1898,11]]},"180":{"position":[[160,12],[276,11],[442,11]]},"377":{"position":[[1900,11],[1932,11]]},"965":{"position":[[204,11],[850,13]]},"1188":{"position":[[387,11],[466,11]]},"1493":{"position":[[299,11]]},"2102":{"position":[[204,11],[843,13],[2599,13],[4164,11],[4710,11]]},"2152":{"position":[[4808,11]]},"2264":{"position":[[118,11]]},"2355":{"position":[[356,12],[961,11],[3733,11],[4057,13]]},"2361":{"position":[[204,11]]},"2363":{"position":[[292,11]]},"2373":{"position":[[170,11]]},"2686":{"position":[[299,11]]},"2722":{"position":[[2003,12],[2118,11]]},"3261":{"position":[[204,11],[843,13],[2599,13],[4164,11],[4710,11]]},"3347":{"position":[[2197,11]]},"3381":{"position":[[4811,11]]},"3498":{"position":[[118,11]]},"3643":{"position":[[356,12],[961,11],[3733,11],[4057,13]]},"3649":{"position":[[204,11]]},"3651":{"position":[[292,11]]},"3661":{"position":[[170,11]]}}}],["resubscribe/reconnect",{"_index":4699,"t":{"1493":{"position":[[533,21]]},"2686":{"position":[[533,21]]}}}],["resubscript",{"_index":2970,"t":{"377":{"position":[[2246,16]]},"2355":{"position":[[4939,14]]},"3643":{"position":[[4939,14]]}}}],["resubscription/reconnect",{"_index":4698,"t":{"1493":{"position":[[389,28],[566,27]]},"2686":{"position":[[389,28],[566,27]]}}}],["result",{"_index":182,"t":{"2":{"position":[[2956,7],[3445,8]]},"18":{"position":[[3277,9]]},"38":{"position":[[772,7]]},"86":{"position":[[585,6],[2132,7],[2228,7]]},"118":{"position":[[677,6]]},"136":{"position":[[1017,6],[1157,9],[1430,6],[1543,9],[1796,6],[1911,9]]},"138":{"position":[[1044,6],[1436,6]]},"146":{"position":[[4,6]]},"150":{"position":[[68,6]]},"162":{"position":[[364,8]]},"180":{"position":[[1094,6],[1419,6]]},"182":{"position":[[1547,6]]},"184":{"position":[[182,7]]},"190":{"position":[[106,7]]},"194":{"position":[[234,6]]},"198":{"position":[[172,8],[684,6]]},"200":{"position":[[923,7]]},"204":{"position":[[706,8]]},"212":{"position":[[372,6]]},"214":{"position":[[544,7]]},"281":{"position":[[210,6]]},"287":{"position":[[1716,7]]},"293":{"position":[[119,7]]},"321":{"position":[[520,6]]},"351":{"position":[[990,6]]},"377":{"position":[[890,6],[1403,6]]},"383":{"position":[[1030,7]]},"389":{"position":[[1073,9]]},"393":{"position":[[809,6]]},"407":{"position":[[402,6]]},"467":{"position":[[1181,7]]},"471":{"position":[[394,6]]},"475":{"position":[[1720,8],[1872,7]]},"477":{"position":[[2301,6],[3354,7],[4371,7],[5923,7],[6252,7]]},"479":{"position":[[6,7],[372,7],[3438,7],[5417,7],[5457,7]]},"481":{"position":[[759,6],[3750,7],[4224,7],[5479,7],[6609,7]]},"483":{"position":[[587,7],[1471,7],[1665,6]]},"556":{"position":[[82,8]]},"652":{"position":[[1157,7]]},"654":{"position":[[1226,7]]},"670":{"position":[[666,7]]},"672":{"position":[[332,7]]},"680":{"position":[[612,7]]},"682":{"position":[[396,10],[581,10],[1195,7]]},"684":{"position":[[477,7]]},"734":{"position":[[1715,6]]},"754":{"position":[[247,6]]},"1065":{"position":[[1329,6],[1997,10],[2146,7],[3315,6],[3330,6],[3399,6],[5158,9]]},"1067":{"position":[[611,10],[1208,6]]},"1069":{"position":[[590,10],[1404,6]]},"1071":{"position":[[1934,10],[2888,6]]},"1073":{"position":[[1970,10],[2705,6]]},"1087":{"position":[[1231,7]]},"1109":{"position":[[1301,9],[2301,7]]},"1111":{"position":[[651,7],[813,7]]},"1113":{"position":[[1323,7]]},"1115":{"position":[[527,7]]},"1117":{"position":[[725,7]]},"1119":{"position":[[569,7]]},"1121":{"position":[[504,9],[884,7]]},"1123":{"position":[[432,9],[626,7]]},"1125":{"position":[[769,9],[1184,6],[1495,7]]},"1127":{"position":[[465,9],[621,7]]},"1129":{"position":[[237,7]]},"1131":{"position":[[275,9],[525,7]]},"1137":{"position":[[519,6]]},"1164":{"position":[[2882,9],[2905,9],[2985,6],[3003,6],[3143,8],[3301,6]]},"1166":{"position":[[1080,10],[1163,6]]},"1168":{"position":[[359,9],[373,6]]},"1176":{"position":[[191,10],[570,10],[990,10],[1263,10]]},"1182":{"position":[[249,6],[530,6]]},"1283":{"position":[[430,6]]},"1307":{"position":[[520,6]]},"1339":{"position":[[990,6]]},"1473":{"position":[[87,6]]},"1479":{"position":[[190,7]]},"1489":{"position":[[94,6]]},"1521":{"position":[[653,6]]},"1523":{"position":[[620,7],[770,7]]},"1569":{"position":[[439,6],[1232,6]]},"1577":{"position":[[1313,7],[1323,9],[1830,8]]},"1579":{"position":[[244,7]]},"1650":{"position":[[840,7]]},"1652":{"position":[[972,7]]},"1654":{"position":[[399,7]]},"1656":{"position":[[192,8],[271,8],[349,8],[422,8],[487,8],[932,7]]},"1658":{"position":[[226,7]]},"1660":{"position":[[148,8],[234,8],[319,8],[399,8],[471,8],[531,8],[588,8],[859,7]]},"1662":{"position":[[346,7]]},"1664":{"position":[[134,8],[194,8],[251,8],[429,7]]},"1666":{"position":[[1868,7]]},"1668":{"position":[[1071,7]]},"1699":{"position":[[1157,7]]},"1702":{"position":[[1272,7]]},"1715":{"position":[[666,7]]},"1717":{"position":[[332,7]]},"1731":{"position":[[612,7]]},"1733":{"position":[[396,10],[581,10],[1195,7]]},"1735":{"position":[[477,7]]},"1773":{"position":[[247,6]]},"1869":{"position":[[1271,6]]},"1881":{"position":[[1598,6]]},"1885":{"position":[[783,9]]},"1887":{"position":[[1101,6]]},"1893":{"position":[[977,9]]},"2146":{"position":[[1446,6],[2114,10],[2263,7],[3432,6],[3447,6],[3516,6],[5281,9]]},"2148":{"position":[[611,10],[1208,6]]},"2150":{"position":[[590,10],[1404,6]]},"2152":{"position":[[1934,10],[2820,6]]},"2154":{"position":[[1970,10],[2705,6]]},"2156":{"position":[[2008,10],[2681,6]]},"2170":{"position":[[1231,7]]},"2190":{"position":[[1361,9],[2585,7]]},"2192":{"position":[[881,7],[1043,7]]},"2194":{"position":[[1387,7]]},"2196":{"position":[[493,7]]},"2198":{"position":[[656,7]]},"2200":{"position":[[535,7]]},"2202":{"position":[[547,9],[927,7]]},"2204":{"position":[[432,9],[626,7]]},"2206":{"position":[[769,9],[1184,6],[1495,7]]},"2208":{"position":[[465,9],[621,7]]},"2210":{"position":[[284,7]]},"2212":{"position":[[275,9],[525,7]]},"2218":{"position":[[519,6]]},"2240":{"position":[[872,6],[962,8],[1121,6]]},"2250":{"position":[[249,6],[530,6]]},"2361":{"position":[[409,6],[657,8]]},"2373":{"position":[[148,6],[244,6]]},"2375":{"position":[[242,6]]},"2430":{"position":[[430,6]]},"2454":{"position":[[520,6]]},"2486":{"position":[[990,6]]},"2652":{"position":[[234,6]]},"2666":{"position":[[87,6]]},"2672":{"position":[[190,7]]},"2682":{"position":[[94,6]]},"2690":{"position":[[653,6]]},"2692":{"position":[[620,7],[770,7]]},"2720":{"position":[[394,6]]},"2728":{"position":[[439,6],[1232,6]]},"2744":{"position":[[465,7],[475,9],[563,9]]},"2752":{"position":[[326,6]]},"2770":{"position":[[1277,7],[1287,9],[1794,8]]},"2772":{"position":[[244,7]]},"2823":{"position":[[1121,7]]},"2826":{"position":[[1236,7]]},"2867":{"position":[[747,7]]},"2869":{"position":[[799,7]]},"2871":{"position":[[339,7]]},"2873":{"position":[[178,7],[718,8],[797,8],[870,8],[935,8],[995,8],[1016,7]]},"2875":{"position":[[226,7]]},"2877":{"position":[[146,8],[603,8],[689,8],[769,8],[841,8],[901,8],[956,8],[983,7]]},"2879":{"position":[[346,7]]},"2881":{"position":[[470,8],[530,8],[587,8],[612,7]]},"2883":{"position":[[2147,7]]},"2885":{"position":[[206,7]]},"2887":{"position":[[1071,7]]},"2904":{"position":[[630,7]]},"2906":{"position":[[296,7]]},"2920":{"position":[[576,7]]},"2922":{"position":[[360,10],[545,10],[1159,7]]},"2924":{"position":[[441,7]]},"2950":{"position":[[1069,6]]},"2962":{"position":[[261,6]]},"3060":{"position":[[1356,6]]},"3072":{"position":[[1886,6]]},"3076":{"position":[[747,9]]},"3078":{"position":[[1101,6]]},"3084":{"position":[[941,9]]},"3345":{"position":[[936,7]]},"3375":{"position":[[1446,6],[2114,10],[2263,7],[3432,6],[3447,6],[3516,6],[5310,9]]},"3377":{"position":[[611,10],[1208,6]]},"3379":{"position":[[637,10],[1451,6]]},"3381":{"position":[[1737,10],[2623,6]]},"3383":{"position":[[1970,10],[2705,6]]},"3385":{"position":[[1981,10],[2654,6]]},"3399":{"position":[[1231,7]]},"3431":{"position":[[435,9],[1262,7],[2093,7]]},"3433":{"position":[[956,7],[1118,7]]},"3435":{"position":[[1455,7]]},"3437":{"position":[[399,7]]},"3439":{"position":[[590,7]]},"3441":{"position":[[528,7]]},"3443":{"position":[[392,9],[765,7]]},"3445":{"position":[[280,9],[467,7]]},"3447":{"position":[[618,9],[1026,6],[1337,7]]},"3449":{"position":[[307,7]]},"3451":{"position":[[344,7]]},"3453":{"position":[[91,9],[342,7]]},"3459":{"position":[[519,6]]},"3551":{"position":[[872,6],[962,8],[1121,6]]},"3561":{"position":[[249,6],[530,6]]},"3625":{"position":[[430,6]]},"3629":{"position":[[545,7]]},"3639":{"position":[[1091,6]]},"3649":{"position":[[409,6],[657,8]]},"3655":{"position":[[1275,6]]},"3661":{"position":[[148,6],[244,6]]},"3663":{"position":[[242,6]]}}}],["retain",{"_index":2323,"t":{"200":{"position":[[322,7]]}}}],["retent",{"_index":819,"t":{"20":{"position":[[1065,9],[1748,10]]},"22":{"position":[[308,9],[499,9]]},"42":{"position":[[365,9]]},"317":{"position":[[277,9]]},"363":{"position":[[190,9]]},"433":{"position":[[126,9]]},"971":{"position":[[669,9]]},"975":{"position":[[1551,9]]},"1303":{"position":[[277,9]]},"1383":{"position":[[193,9]]},"1417":{"position":[[126,9]]},"1745":{"position":[[74,9]]},"2450":{"position":[[277,9]]},"2504":{"position":[[161,9]]},"2534":{"position":[[202,9]]},"2574":{"position":[[127,9]]},"2934":{"position":[[74,9]]}}}],["rethinkdb",{"_index":5645,"t":{"3347":{"position":[[1191,11]]}}}],["retreiv",{"_index":5525,"t":{"2873":{"position":[[1145,10]]},"2877":{"position":[[1131,10]]}}}],["retri",{"_index":2448,"t":{"226":{"position":[[592,8]]},"254":{"position":[[1382,5]]},"614":{"position":[[328,7]]},"776":{"position":[[704,5]]},"796":{"position":[[125,8]]},"1001":{"position":[[678,5]]},"1182":{"position":[[1109,5],[1631,5]]},"1184":{"position":[[1422,5]]},"1549":{"position":[[329,7]]},"1676":{"position":[[1603,7],[1673,7],[1740,7]]},"1795":{"position":[[882,5]]},"1847":{"position":[[122,8]]},"2108":{"position":[[678,5]]},"2250":{"position":[[1109,5],[1631,5]]},"2351":{"position":[[750,5]]},"2367":{"position":[[736,5]]},"2371":{"position":[[329,6]]},"2718":{"position":[[329,7]]},"2720":{"position":[[385,8]]},"2722":{"position":[[2384,7]]},"2830":{"position":[[1606,7],[1676,7],[1743,7]]},"2984":{"position":[[882,5]]},"3038":{"position":[[122,8]]},"3309":{"position":[[678,5]]},"3467":{"position":[[673,8]]},"3561":{"position":[[1109,5],[1631,5]]},"3639":{"position":[[594,5]]},"3655":{"position":[[736,5]]},"3659":{"position":[[329,6]]}}}],["retriev",{"_index":2218,"t":{"178":{"position":[[286,8]]},"317":{"position":[[356,8]]},"1049":{"position":[[1156,9]]},"1303":{"position":[[356,8]]},"1656":{"position":[[636,9]]},"1660":{"position":[[737,9]]},"1664":{"position":[[403,9]]},"2120":{"position":[[31,8],[230,9]]},"2230":{"position":[[1156,9]]},"2450":{"position":[[356,8]]},"2873":{"position":[[327,9]]},"2877":{"position":[[296,9]]},"2881":{"position":[[269,9]]},"3335":{"position":[[31,8],[230,9]]},"3417":{"position":[[1156,9]]}}}],["return",{"_index":426,"t":{"10":{"position":[[852,6]]},"32":{"position":[[477,6],[1443,6]]},"34":{"position":[[1829,6]]},"38":{"position":[[591,6],[755,6]]},"40":{"position":[[465,6]]},"44":{"position":[[1630,7]]},"70":{"position":[[158,6]]},"78":{"position":[[580,7],[765,7]]},"104":{"position":[[806,6],[917,6]]},"106":{"position":[[365,6],[451,6],[993,8],[1385,6]]},"110":{"position":[[1400,6],[1554,6],[1692,6],[1871,6],[2027,6],[2140,6],[3293,6],[3422,6],[3612,6],[3674,6],[4000,6],[4301,6],[4376,6],[4398,6],[4497,6],[4513,6]]},"112":{"position":[[527,6],[644,6]]},"128":{"position":[[1078,6],[1215,7],[1465,6]]},"130":{"position":[[1736,6],[1815,7],[2046,6],[2117,6]]},"136":{"position":[[928,6],[1197,6],[1349,6],[1558,6],[1712,6],[1926,6],[2001,6],[2096,6]]},"162":{"position":[[338,6],[887,9]]},"166":{"position":[[709,7]]},"178":{"position":[[933,6],[990,6],[1969,6]]},"180":{"position":[[1412,6]]},"198":{"position":[[117,9],[612,9],[771,9],[1163,7]]},"242":{"position":[[191,8]]},"260":{"position":[[964,7]]},"262":{"position":[[1747,7]]},"301":{"position":[[1698,6],[1713,6],[2446,7],[2583,6],[2786,6],[3466,6]]},"325":{"position":[[452,7]]},"367":{"position":[[327,6]]},"369":{"position":[[78,6],[483,7]]},"393":{"position":[[93,6]]},"471":{"position":[[662,6]]},"473":{"position":[[1705,6],[1878,6],[2726,6]]},"483":{"position":[[1601,8]]},"505":{"position":[[280,7]]},"541":{"position":[[50,6],[97,7]]},"718":{"position":[[766,7]]},"722":{"position":[[219,8]]},"758":{"position":[[1290,6]]},"760":{"position":[[328,7]]},"764":{"position":[[1349,6],[1423,6],[1889,6]]},"780":{"position":[[37,8],[168,8]]},"782":{"position":[[83,8]]},"786":{"position":[[117,8]]},"792":{"position":[[67,8]]},"798":{"position":[[118,8]]},"832":{"position":[[138,6]]},"838":{"position":[[41,8]]},"840":{"position":[[82,8]]},"965":{"position":[[1442,6],[1516,6],[2571,9]]},"1065":{"position":[[326,6],[3370,7],[3573,6],[4934,6]]},"1069":{"position":[[1739,6]]},"1071":{"position":[[3798,6]]},"1073":{"position":[[548,8],[3137,6]]},"1075":{"position":[[24,6],[69,6],[315,9],[408,9]]},"1077":{"position":[[24,6],[549,9]]},"1125":{"position":[[174,6],[1077,8],[1252,6]]},"1129":{"position":[[9,6],[595,7]]},"1143":{"position":[[451,6],[571,6]]},"1164":{"position":[[1099,6],[2264,6]]},"1172":{"position":[[1119,8]]},"1182":{"position":[[1379,8]]},"1184":{"position":[[1732,8]]},"1311":{"position":[[452,7]]},"1367":{"position":[[280,7]]},"1387":{"position":[[327,6]]},"1389":{"position":[[78,6],[483,7]]},"1475":{"position":[[213,8]]},"1505":{"position":[[250,8]]},"1567":{"position":[[1126,9]]},"1569":{"position":[[856,7]]},"1577":{"position":[[1794,9]]},"1579":{"position":[[0,7]]},"1635":{"position":[[524,7]]},"1656":{"position":[[0,7]]},"1741":{"position":[[2480,6]]},"1743":{"position":[[1997,6],[2146,6]]},"1745":{"position":[[1004,6],[1169,6]]},"1747":{"position":[[303,6],[1143,6]]},"1777":{"position":[[817,6]]},"1779":{"position":[[401,7]]},"1783":{"position":[[1349,6],[1423,6],[1889,6]]},"1797":{"position":[[1128,8]]},"1831":{"position":[[37,8],[168,8]]},"1833":{"position":[[95,8]]},"1837":{"position":[[114,8]]},"1843":{"position":[[64,8]]},"1849":{"position":[[115,8]]},"1897":{"position":[[1119,7]]},"2032":{"position":[[1084,8]]},"2038":{"position":[[1141,8]]},"2102":{"position":[[1614,6],[1688,6],[2886,8],[3091,8]]},"2120":{"position":[[568,6]]},"2146":{"position":[[326,6],[3487,7],[3690,6],[5057,6],[6015,6],[6061,6],[6417,6]]},"2150":{"position":[[1745,6]]},"2152":{"position":[[3829,6],[4092,6],[4149,6],[4388,10],[4670,9]]},"2154":{"position":[[548,8],[3137,6]]},"2158":{"position":[[24,6],[69,6],[250,9],[367,9]]},"2160":{"position":[[24,6],[725,9]]},"2206":{"position":[[174,6],[1077,8],[1252,6]]},"2210":{"position":[[9,6],[642,7]]},"2224":{"position":[[451,6],[571,6]]},"2244":{"position":[[793,6],[2062,6]]},"2250":{"position":[[1379,8]]},"2351":{"position":[[416,6],[489,8],[559,7],[715,8],[838,6],[1089,6],[1332,6]]},"2357":{"position":[[318,7],[647,7]]},"2367":{"position":[[409,6],[479,8],[551,7],[701,8],[820,6],[1071,6],[1416,6]]},"2371":{"position":[[11,6],[155,8]]},"2373":{"position":[[11,6]]},"2458":{"position":[[452,7]]},"2518":{"position":[[285,7]]},"2538":{"position":[[329,6]]},"2540":{"position":[[78,6],[488,7]]},"2636":{"position":[[250,8]]},"2652":{"position":[[84,9],[321,9]]},"2668":{"position":[[213,8]]},"2726":{"position":[[1126,9]]},"2728":{"position":[[856,7]]},"2744":{"position":[[829,7]]},"2770":{"position":[[1758,9]]},"2772":{"position":[[0,7]]},"2852":{"position":[[524,7]]},"2873":{"position":[[0,7]]},"2930":{"position":[[2480,6]]},"2932":{"position":[[1997,6],[2146,6]]},"2934":{"position":[[1004,6],[1169,6]]},"2936":{"position":[[303,6],[1143,6]]},"2966":{"position":[[895,6]]},"2968":{"position":[[419,7]]},"2986":{"position":[[1094,8]]},"3022":{"position":[[37,8],[168,8]]},"3024":{"position":[[95,8]]},"3028":{"position":[[114,8]]},"3034":{"position":[[64,8],[388,8]]},"3040":{"position":[[115,8]]},"3090":{"position":[[1119,7]]},"3261":{"position":[[1614,6],[1688,6],[2886,8],[3091,8]]},"3273":{"position":[[976,8]]},"3279":{"position":[[1033,8]]},"3335":{"position":[[552,6]]},"3349":{"position":[[1579,7],[3125,6]]},"3351":{"position":[[1227,7]]},"3375":{"position":[[326,6],[3487,7],[3690,6],[5086,6],[6044,6],[6090,6],[6446,6]]},"3379":{"position":[[1792,6]]},"3381":{"position":[[3832,6],[4095,6],[4152,6],[4391,10],[4673,9]]},"3383":{"position":[[548,8],[3137,6]]},"3387":{"position":[[24,6],[69,6],[250,9],[367,9]]},"3389":{"position":[[24,6],[725,9]]},"3447":{"position":[[174,6],[919,8],[1094,6]]},"3451":{"position":[[9,6],[702,7]]},"3455":{"position":[[142,8]]},"3465":{"position":[[451,6],[571,6]]},"3467":{"position":[[40,7],[95,7],[133,7],[181,7],[341,7],[1124,7]]},"3469":{"position":[[104,6],[217,6],[319,6],[405,6],[496,6],[670,6]]},"3471":{"position":[[99,6],[184,6],[269,6],[343,6],[399,6],[573,6]]},"3555":{"position":[[793,6],[2062,6]]},"3561":{"position":[[1379,8]]},"3639":{"position":[[416,6],[489,8],[559,8],[697,6],[926,6],[1220,6]]},"3645":{"position":[[318,7],[647,7]]},"3655":{"position":[[409,6],[479,8],[551,7],[701,8],[1110,6],[1404,6]]},"3659":{"position":[[11,6],[155,8]]},"3661":{"position":[[11,6]]}}}],["reus",{"_index":496,"t":{"12":{"position":[[709,7]]},"94":{"position":[[813,5]]},"138":{"position":[[221,5]]},"387":{"position":[[1502,5]]},"389":{"position":[[750,6]]},"618":{"position":[[1156,5]]},"648":{"position":[[942,7]]},"666":{"position":[[805,7]]},"686":{"position":[[379,5]]},"780":{"position":[[369,5]]},"953":{"position":[[228,6]]},"1270":{"position":[[152,6]]},"1277":{"position":[[49,5]]},"1279":{"position":[[49,5],[239,8]]},"1680":{"position":[[1860,5]]},"1694":{"position":[[942,7]]},"1710":{"position":[[805,7]]},"1737":{"position":[[369,5]]},"1831":{"position":[[369,5]]},"2080":{"position":[[228,6]]},"2294":{"position":[[152,6]]},"2424":{"position":[[49,5]]},"2426":{"position":[[49,5],[239,8]]},"2818":{"position":[[942,7]]},"2834":{"position":[[1866,5]]},"2899":{"position":[[805,7]]},"2926":{"position":[[369,5]]},"3022":{"position":[[369,5]]},"3247":{"position":[[228,6]]},"3524":{"position":[[152,6]]},"3619":{"position":[[49,5]]},"3621":{"position":[[49,5],[239,8]]}}}],["reveal",{"_index":3071,"t":{"451":{"position":[[251,6]]},"768":{"position":[[456,6]]},"884":{"position":[[510,6]]},"1451":{"position":[[318,6]]},"1511":{"position":[[616,6]]},"1787":{"position":[[456,6]]},"1951":{"position":[[510,6]]},"2592":{"position":[[339,6]]},"2642":{"position":[[616,6]]},"2976":{"position":[[600,6]]},"3164":{"position":[[510,6]]}}}],["revers",{"_index":1337,"t":{"70":{"position":[[742,8],[847,8],[1465,7],[1475,8]]},"287":{"position":[[268,7]]},"511":{"position":[[686,8],[770,8]]},"951":{"position":[[172,7]]},"963":{"position":[[53,7],[206,8],[373,8],[534,8],[632,8],[684,8],[747,8],[911,8],[1027,8],[1127,8],[1304,8],[1409,8],[2027,7],[2037,8]]},"999":{"position":[[53,7],[134,7]]},"1125":{"position":[[1292,7],[1319,8]]},"1373":{"position":[[686,8],[770,8]]},"2048":{"position":[[535,8]]},"2078":{"position":[[172,7]]},"2100":{"position":[[53,7],[206,8],[373,8],[534,8],[632,8],[684,8],[747,8],[911,8],[1027,8],[1127,8],[1304,8],[1409,8],[2027,7],[2037,8]]},"2106":{"position":[[53,7],[134,7]]},"2206":{"position":[[1292,7],[1319,8]]},"2379":{"position":[[686,8],[770,8]]},"2434":{"position":[[2144,7]]},"2524":{"position":[[686,8],[770,8]]},"3245":{"position":[[172,7]]},"3259":{"position":[[53,7],[206,8],[373,8],[534,8],[632,8],[684,8],[747,8],[911,8],[1027,8],[1127,8],[1304,8],[1409,8],[2027,7],[2037,8]]},"3307":{"position":[[53,7],[134,7]]},"3447":{"position":[[1134,7],[1161,8]]},"3612":{"position":[[2343,7]]},"3667":{"position":[[686,8],[770,8]]}}}],["revis",{"_index":2967,"t":{"377":{"position":[[1706,7]]},"383":{"position":[[474,7]]},"385":{"position":[[296,7]]},"403":{"position":[[264,7]]}}}],["revoc",{"_index":3823,"t":{"644":{"position":[[36,11],[124,10],[213,10],[306,10],[680,10]]},"646":{"position":[[6,10],[32,10],[208,10],[237,10]]},"648":{"position":[[0,10],[361,10],[420,10]]},"650":{"position":[[0,10]]},"652":{"position":[[827,10],[1017,11]]},"654":{"position":[[886,10],[1076,11]]},"744":{"position":[[152,10]]},"746":{"position":[[126,10]]},"1690":{"position":[[36,11],[124,10],[213,10],[306,10],[680,10]]},"1692":{"position":[[6,10],[32,10],[208,10],[237,10]]},"1694":{"position":[[0,10],[361,10],[420,10]]},"1696":{"position":[[0,10]]},"1699":{"position":[[827,10],[1017,11]]},"1702":{"position":[[932,10],[1122,11]]},"1763":{"position":[[152,10]]},"1765":{"position":[[126,10]]},"1819":{"position":[[152,10]]},"1821":{"position":[[126,10]]},"2814":{"position":[[36,11],[124,10],[213,10],[306,10],[680,10]]},"2816":{"position":[[6,10],[32,10],[208,10],[237,10]]},"2818":{"position":[[0,10],[361,10],[420,10]]},"2820":{"position":[[0,10]]},"2823":{"position":[[791,10],[981,11]]},"2826":{"position":[[896,10],[1086,11]]},"2952":{"position":[[197,10]]},"2954":{"position":[[168,10]]},"3008":{"position":[[152,10]]},"3010":{"position":[[126,10]]}}}],["revok",{"_index":2594,"t":{"254":{"position":[[1125,7]]},"576":{"position":[[586,8],[615,8]]},"644":{"position":[[478,7]]},"648":{"position":[[617,7]]},"652":{"position":[[7,8],[120,6],[651,6],[1144,6]]},"654":{"position":[[7,8],[771,7]]},"1479":{"position":[[29,6],[768,6],[915,7]]},"1493":{"position":[[29,6],[446,6],[632,7]]},"1583":{"position":[[697,8],[730,6]]},"1690":{"position":[[478,7]]},"1694":{"position":[[617,7]]},"1699":{"position":[[7,8],[120,6]]},"1702":{"position":[[7,8],[775,7]]},"2672":{"position":[[29,6],[768,6],[915,7]]},"2686":{"position":[[29,6],[446,6],[632,7]]},"2778":{"position":[[698,8],[731,6]]},"2814":{"position":[[478,7]]},"2818":{"position":[[617,7]]},"2823":{"position":[[7,8],[120,6]]},"2826":{"position":[[7,8],[739,7]]}}}],["revoke_token",{"_index":3837,"t":{"652":{"position":[[547,15]]},"1699":{"position":[[547,15],[651,12],[1144,12]]},"2823":{"position":[[615,12],[1108,12]]}}}],["revolut",{"_index":2961,"t":{"375":{"position":[[566,13],[580,12]]}}}],["rewrit",{"_index":1789,"t":{"120":{"position":[[749,7]]},"485":{"position":[[64,9]]},"1001":{"position":[[256,7]]},"1003":{"position":[[262,7],[521,7]]},"2108":{"position":[[256,7]]},"2110":{"position":[[262,7],[521,7]]},"3309":{"position":[[256,7]]},"3311":{"position":[[262,7],[521,7]]}}}],["rewritten",{"_index":1417,"t":{"84":{"position":[[135,10]]},"220":{"position":[[157,10]]}}}],["rfc",{"_index":325,"t":{"8":{"position":[[552,4]]},"652":{"position":[[402,5]]},"654":{"position":[[279,5]]},"744":{"position":[[71,4]]},"746":{"position":[[45,4]]},"1699":{"position":[[402,5]]},"1702":{"position":[[279,5]]},"1763":{"position":[[71,4]]},"1765":{"position":[[45,4]]},"1819":{"position":[[71,4]]},"1821":{"position":[[45,4]]},"2823":{"position":[[402,5]]},"2826":{"position":[[279,5]]},"2946":{"position":[[68,3]]},"2952":{"position":[[97,3]]},"2954":{"position":[[70,3]]},"3008":{"position":[[71,4]]},"3010":{"position":[[45,4]]}}}],["rfc7519",{"_index":3925,"t":{"748":{"position":[[83,8]]},"750":{"position":[[81,8]]},"1031":{"position":[[83,8]]},"1033":{"position":[[81,8]]},"1757":{"position":[[71,8]]},"1767":{"position":[[51,8]]},"1769":{"position":[[49,8]]},"1801":{"position":[[69,8]]},"1815":{"position":[[51,8]]},"1817":{"position":[[49,8]]},"2956":{"position":[[51,8]]},"2958":{"position":[[49,8]]},"2990":{"position":[[69,8]]},"3004":{"position":[[51,8]]},"3006":{"position":[[49,8]]}}}],["riak",{"_index":417,"t":{"10":{"position":[[614,4]]}}}],["rid",{"_index":1509,"t":{"94":{"position":[[711,3]]},"714":{"position":[[215,3]]},"1682":{"position":[[778,3]]},"1893":{"position":[[215,3]]},"2836":{"position":[[778,3]]},"3084":{"position":[[215,3]]}}}],["right",{"_index":370,"t":{"8":{"position":[[1375,5]]},"26":{"position":[[1497,5]]},"148":{"position":[[539,6]]},"254":{"position":[[80,6]]},"473":{"position":[[3474,6]]},"576":{"position":[[936,5]]},"945":{"position":[[556,5]]},"1583":{"position":[[1420,5]]},"2020":{"position":[[556,5]]},"3239":{"position":[[556,5]]}}}],["rigor",{"_index":2605,"t":{"256":{"position":[[1001,10]]}}}],["rise",{"_index":3166,"t":{"475":{"position":[[840,4],[1019,4]]},"981":{"position":[[459,4],[567,4]]},"2040":{"position":[[459,4],[567,4]]},"3281":{"position":[[459,4],[567,4]]}}}],["risk",{"_index":429,"t":{"10":{"position":[[937,4]]},"1507":{"position":[[1069,4]]},"1741":{"position":[[1014,6]]},"1877":{"position":[[663,6]]},"2638":{"position":[[1069,4]]},"2930":{"position":[[1014,6]]}}}],["rl",{"_index":3391,"t":{"481":{"position":[[2402,2]]}}}],["rl.take",{"_index":3394,"t":{"481":{"position":[[2465,9]]}}}],["rm",{"_index":1577,"t":{"98":{"position":[[1318,2]]},"132":{"position":[[137,2]]},"134":{"position":[[1598,2]]},"230":{"position":[[157,2]]},"297":{"position":[[69,2]]},"299":{"position":[[70,2]]},"612":{"position":[[543,2],[676,2]]},"1547":{"position":[[673,2],[810,2]]},"1712":{"position":[[329,2]]},"2716":{"position":[[673,2],[810,2]]},"2901":{"position":[[329,2]]},"3327":{"position":[[532,2]]}}}],["rnning",{"_index":5131,"t":{"1989":{"position":[[114,6]]},"3204":{"position":[[114,6]]}}}],["road",{"_index":1318,"t":{"68":{"position":[[124,4]]},"345":{"position":[[185,4]]},"1043":{"position":[[52,4]]},"1333":{"position":[[185,4]]},"2134":{"position":[[52,4]]},"2480":{"position":[[185,4]]},"3363":{"position":[[52,4]]}}}],["roadrunn",{"_index":4671,"t":{"1355":{"position":[[613,10],[656,10]]},"2548":{"position":[[613,10],[656,10]]}}}],["robot",{"_index":2106,"t":{"146":{"position":[[924,5],[990,6]]}}}],["robust",{"_index":826,"t":{"22":{"position":[[81,6]]},"74":{"position":[[321,6]]},"262":{"position":[[1393,6]]},"347":{"position":[[1430,6]]},"1335":{"position":[[1434,6]]},"2482":{"position":[[1575,6]]},"2610":{"position":[[1596,6]]}}}],["role",{"_index":3376,"t":{"481":{"position":[[687,4]]},"1521":{"position":[[838,8]]},"1523":{"position":[[648,9]]},"2530":{"position":[[1588,4]]},"2690":{"position":[[838,8]]},"2692":{"position":[[648,9]]}}}],["role:mast",{"_index":3175,"t":{"475":{"position":[[1221,11]]},"981":{"position":[[770,11]]},"2040":{"position":[[770,11]]},"3281":{"position":[[770,11]]}}}],["roll",{"_index":2300,"t":{"196":{"position":[[71,6]]},"256":{"position":[[1196,4]]},"389":{"position":[[1366,4]]}}}],["ronach",{"_index":271,"t":{"4":{"position":[[1102,8]]}}}],["room",{"_index":975,"t":{"34":{"position":[[99,5]]},"44":{"position":[[1987,5]]},"118":{"position":[[183,4],[199,4],[253,4],[274,4],[425,4]]},"128":{"position":[[87,4],[785,4],[1344,4],[3081,4],[3115,4],[3174,4],[3253,4]]},"130":{"position":[[38,4],[103,5],[334,4],[1949,4],[2211,4],[2590,4],[2649,4],[2862,4]]},"132":{"position":[[624,8],[1255,6],[1322,5]]},"136":{"position":[[2377,4],[2468,4]]},"138":{"position":[[296,6],[310,4],[473,5],[1757,5],[1803,4]]},"144":{"position":[[41,4],[193,6],[214,5],[253,5]]},"146":{"position":[[646,4]]},"150":{"position":[[695,6]]},"156":{"position":[[310,5],[355,5],[395,5],[458,5],[511,4],[561,6]]},"162":{"position":[[1110,6],[1169,4],[1296,4],[1421,4]]},"164":{"position":[[50,6],[68,5],[81,5],[101,5],[155,5],[230,4],[521,6]]},"166":{"position":[[174,6],[206,4],[309,6],[355,4],[522,5],[774,9],[2002,4],[2086,5],[2124,4],[2181,5]]},"168":{"position":[[384,4],[415,5],[465,4]]},"220":{"position":[[355,5]]},"353":{"position":[[21,6]]},"391":{"position":[[250,4]]},"493":{"position":[[8,5]]},"1345":{"position":[[21,6]]},"1349":{"position":[[84,5],[202,5]]},"2492":{"position":[[21,6]]},"2494":{"position":[[89,5],[203,6]]},"3337":{"position":[[980,4]]},"3490":{"position":[[669,6]]}}}],["room(request",{"_index":1975,"t":{"130":{"position":[[2091,13]]}}}],["room.html",{"_index":1934,"t":{"130":{"position":[[268,12]]}}}],["room::with('us",{"_index":2158,"t":{"164":{"position":[[467,19]]}}}],["roomcentrifugo",{"_index":4722,"t":{"1495":{"position":[[2127,17]]},"2662":{"position":[[2142,17]]}}}],["title>chat",{"_index":1935,"t":{"130":{"position":[[461,11]]}}}],["title>select",{"_index":1851,"t":{"128":{"position":[[607,13]]}}}],["tl",{"_index":543,"t":{"14":{"position":[[392,3]]},"94":{"position":[[3164,3]]},"98":{"position":[[147,3],[507,3]]},"106":{"position":[[975,3]]},"379":{"position":[[305,3]]},"397":{"position":[[55,3],[512,3]]},"399":{"position":[[1270,3],[1333,3]]},"439":{"position":[[137,3],[160,4]]},"975":{"position":[[385,3],[463,3]]},"1047":{"position":[[158,6]]},"1049":{"position":[[934,3],[1316,3],[1788,3],[1899,3]]},"1051":{"position":[[54,3],[113,3]]},"1053":{"position":[[86,3],[165,3]]},"1081":{"position":[[134,3]]},"1093":{"position":[[1828,3]]},"1107":{"position":[[930,4]]},"1166":{"position":[[192,4]]},"1239":{"position":[[38,3]]},"1319":{"position":[[75,3],[689,3]]},"1423":{"position":[[137,3],[160,4]]},"2032":{"position":[[38,3],[145,3],[232,3],[433,3],[629,3],[824,3]]},"2038":{"position":[[13,3],[121,3],[217,3],[436,3],[650,3],[863,3]]},"2164":{"position":[[134,3]]},"2176":{"position":[[1828,3]]},"2188":{"position":[[933,4]]},"2228":{"position":[[158,6]]},"2230":{"position":[[934,3],[1316,3],[1788,3],[1899,3]]},"2232":{"position":[[54,3],[113,3]]},"2234":{"position":[[54,3],[133,3]]},"2318":{"position":[[38,3]]},"2434":{"position":[[495,6],[615,3],[1977,3],[2021,3]]},"2466":{"position":[[75,3],[689,3]]},"2580":{"position":[[146,3],[169,4]]},"3273":{"position":[[38,3],[145,3],[232,3],[406,3],[575,3],[743,3]]},"3279":{"position":[[13,3],[121,3],[217,3],[409,3],[596,3],[782,3]]},"3393":{"position":[[134,3]]},"3405":{"position":[[2040,3]]},"3415":{"position":[[158,6]]},"3417":{"position":[[934,3],[1316,3],[1788,3],[1899,3]]},"3419":{"position":[[54,3],[113,3]]},"3421":{"position":[[54,3],[133,3]]},"3427":{"position":[[906,4]]},"3539":{"position":[[38,3]]},"3612":{"position":[[522,6],[642,3],[2004,3],[2048,3]]}}}],["tls.certificate{cert",{"_index":1682,"t":{"106":{"position":[[1419,24]]}}}],["tls.config",{"_index":1679,"t":{"106":{"position":[[1262,11],[1392,12],[1768,12]]}}}],["tls.loadx509keypair(s.config.tlscertpath",{"_index":1680,"t":{"106":{"position":[[1289,41]]}}}],["tls_autocert",{"_index":4257,"t":{"1049":{"position":[[93,15],[307,12]]},"2230":{"position":[[93,15],[307,12]]},"3417":{"position":[[93,15],[307,12]]}}}],["tls_autocert_cache_dir",{"_index":4260,"t":{"1049":{"position":[[165,25],[575,22]]},"2230":{"position":[[165,25],[575,22]]},"3417":{"position":[[165,25],[575,22]]}}}],["tls_autocert_email",{"_index":4262,"t":{"1049":{"position":[[205,21],[712,18]]},"2230":{"position":[[205,21],[712,18]]},"3417":{"position":[[205,21],[712,18]]}}}],["tls_autocert_force_rsa",{"_index":3623,"t":{"570":{"position":[[230,22]]},"1049":{"position":[[1406,22]]},"2230":{"position":[[1406,22]]},"3417":{"position":[[1406,22]]}}}],["tls_autocert_host_whitelist",{"_index":4258,"t":{"1049":{"position":[[115,30],[412,27]]},"2230":{"position":[[115,30],[412,27]]},"3417":{"position":[[115,30],[412,27]]}}}],["tls_autocert_http",{"_index":4264,"t":{"1049":{"position":[[247,20],[853,17]]},"2230":{"position":[[247,20],[853,17]]},"3417":{"position":[[247,20],[853,17]]}}}],["tls_autocert_http_addr",{"_index":4265,"t":{"1049":{"position":[[274,25],[944,22]]},"2230":{"position":[[274,25],[944,22]]},"3417":{"position":[[274,25],[944,22]]}}}],["tls_autocert_server_nam",{"_index":4272,"t":{"1049":{"position":[[1561,24]]},"2230":{"position":[[1561,24]]},"3417":{"position":[[1561,24]]}}}],["tls_cert",{"_index":4256,"t":{"1047":{"position":[[196,11]]},"2228":{"position":[[196,11]]},"2434":{"position":[[508,11]]},"3415":{"position":[[196,11]]},"3612":{"position":[[535,11]]}}}],["tls_key",{"_index":4255,"t":{"1047":{"position":[[171,10]]},"2228":{"position":[[171,10]]},"2434":{"position":[[535,10]]},"3415":{"position":[[171,10]]},"3612":{"position":[[562,10]]}}}],["tlscertpath",{"_index":1637,"t":{"104":{"position":[[254,11],[300,11],[1040,12]]}}}],["tlskeypath",{"_index":1640,"t":{"104":{"position":[[322,10],[366,10],[1067,11]]}}}],["tlsv1",{"_index":4214,"t":{"1001":{"position":[[393,5]]},"2108":{"position":[[393,5]]},"3309":{"position":[[393,5]]}}}],["tlsv1.1",{"_index":4215,"t":{"1001":{"position":[[399,7]]},"2108":{"position":[[399,7]]},"3309":{"position":[[399,7]]}}}],["tlsv1.2",{"_index":4216,"t":{"1001":{"position":[[407,8]]},"2108":{"position":[[407,8]]},"3309":{"position":[[407,8]]}}}],["tmp/cert",{"_index":4261,"t":{"1049":{"position":[[191,13]]},"2230":{"position":[[191,13]]},"3417":{"position":[[191,13]]}}}],["tmp/clickhouse:/var/lib/clickhous",{"_index":3782,"t":{"612":{"position":[[549,35]]},"1547":{"position":[[679,35]]},"2716":{"position":[[679,35]]}}}],["to/from",{"_index":1064,"t":{"40":{"position":[[1067,7]]}}}],["todatetimestr",{"_index":2165,"t":{"164":{"position":[[694,20]]}}}],["today",{"_index":2956,"t":{"373":{"position":[[0,5]]},"531":{"position":[[577,6]]},"1439":{"position":[[600,6]]},"2622":{"position":[[536,6]]}}}],["today'",{"_index":1395,"t":{"76":{"position":[[222,7]]},"264":{"position":[[272,7]]},"385":{"position":[[58,7]]},"1629":{"position":[[272,7]]},"2846":{"position":[[272,7]]}}}],["togeth",{"_index":509,"t":{"12":{"position":[[1121,9]]},"18":{"position":[[2928,8]]},"46":{"position":[[1988,8],[3872,8]]},"216":{"position":[[837,9]]},"262":{"position":[[2847,8]]},"347":{"position":[[2195,9]]},"734":{"position":[[974,8]]},"744":{"position":[[117,8]]},"746":{"position":[[91,8]]},"965":{"position":[[719,8]]},"989":{"position":[[1066,9]]},"1089":{"position":[[915,8]]},"1164":{"position":[[699,8]]},"1335":{"position":[[2199,9]]},"1399":{"position":[[216,8]]},"1505":{"position":[[406,9]]},"1543":{"position":[[42,8]]},"1573":{"position":[[62,8]]},"1635":{"position":[[631,8]]},"1763":{"position":[[117,8]]},"1765":{"position":[[91,8]]},"1819":{"position":[[117,8]]},"1821":{"position":[[91,8]]},"1881":{"position":[[1005,8]]},"2050":{"position":[[1066,9]]},"2102":{"position":[[716,8]]},"2172":{"position":[[889,8]]},"2244":{"position":[[414,8],[1637,8]]},"2482":{"position":[[2441,9]]},"2556":{"position":[[218,8]]},"2636":{"position":[[406,9]]},"2712":{"position":[[42,8]]},"2732":{"position":[[62,8]]},"2852":{"position":[[631,8]]},"2862":{"position":[[724,8]]},"3008":{"position":[[117,8]]},"3010":{"position":[[91,8]]},"3072":{"position":[[1293,8]]},"3261":{"position":[[716,8]]},"3289":{"position":[[1066,9]]},"3349":{"position":[[1225,8]]},"3401":{"position":[[889,8]]},"3555":{"position":[[414,8],[1637,8]]}}}],["toggl",{"_index":3713,"t":{"604":{"position":[[561,6]]},"1533":{"position":[[649,6]]},"2702":{"position":[[649,6]]}}}],["tointervalday(1)set",{"_index":3733,"t":{"606":{"position":[[583,24]]},"608":{"position":[[638,24]]},"1535":{"position":[[503,24]]},"1537":{"position":[[482,24]]},"1539":{"position":[[629,24]]},"1541":{"position":[[504,24]]},"1543":{"position":[[704,24]]},"2704":{"position":[[503,24]]},"2706":{"position":[[482,24]]},"2708":{"position":[[629,24]]},"2710":{"position":[[504,24]]},"2712":{"position":[[704,24]]}}}],["tointervalhour(24",{"_index":4835,"t":{"1545":{"position":[[1668,20]]},"2714":{"position":[[1668,20]]}}}],["tointervalminute(1)))group",{"_index":3767,"t":{"610":{"position":[[1263,26]]},"1545":{"position":[[1257,26]]},"2714":{"position":[[1257,26]]}}}],["tointervalminute(5)));┌─uniqexact(us",{"_index":3764,"t":{"610":{"position":[[1006,42]]},"1545":{"position":[[1000,42]]},"2714":{"position":[[1000,42]]}}}],["token",{"_index":760,"t":{"18":{"position":[[974,6]]},"32":{"position":[[1040,5],[1175,5],[1268,7]]},"166":{"position":[[839,7]]},"178":{"position":[[248,6],[308,5],[346,5],[385,5],[407,5],[513,6],[1520,5],[1738,6],[1918,5],[1976,6],[2140,5],[2206,5],[2319,5],[2417,5],[2757,5],[2891,5],[3012,6]]},"180":{"position":[[173,5],[459,5],[479,5]]},"182":{"position":[[635,6]]},"188":{"position":[[72,6],[394,7],[603,5],[1104,5]]},"194":{"position":[[113,5]]},"198":{"position":[[87,5],[136,5],[319,5],[402,5],[491,5],[909,5],[1515,5]]},"200":{"position":[[1135,5],[1144,6],[1221,6]]},"210":{"position":[[78,6],[342,6],[437,7],[618,5],[667,6],[798,7],[856,7]]},"254":{"position":[[368,6],[391,5],[931,6],[1053,7],[1097,5]]},"260":{"position":[[80,6],[173,6],[296,6],[942,6],[1120,5],[1262,6]]},"262":{"position":[[2293,7],[2359,5]]},"293":{"position":[[499,5]]},"297":{"position":[[690,5]]},"299":{"position":[[615,7]]},"301":{"position":[[2536,6],[2902,7],[2962,5],[2984,5]]},"377":{"position":[[544,5],[1481,5],[1838,5]]},"385":{"position":[[935,5]]},"387":{"position":[[787,5],[1215,6],[1264,5],[1521,5],[1576,5],[1615,6],[1656,5]]},"389":{"position":[[445,5],[623,5],[737,5]]},"393":{"position":[[908,6]]},"403":{"position":[[387,6],[497,6]]},"421":{"position":[[60,6]]},"451":{"position":[[258,5]]},"453":{"position":[[303,5],[350,5],[468,6],[582,5],[754,5]]},"497":{"position":[[204,6]]},"562":{"position":[[159,6]]},"576":{"position":[[624,6],[634,5],[673,6]]},"618":{"position":[[186,5]]},"644":{"position":[[30,5],[118,5],[300,5],[486,6],[674,5]]},"646":{"position":[[0,5],[46,5],[64,5]]},"652":{"position":[[27,7],[72,5],[151,7],[239,6],[379,5],[658,5],[738,5],[1151,5]]},"654":{"position":[[20,6],[245,5],[577,6],[681,6],[718,6],[1219,6]]},"658":{"position":[[89,6],[223,5],[342,5]]},"694":{"position":[[752,5]]},"696":{"position":[[170,6]]},"742":{"position":[[42,5],[599,6],[811,5]]},"744":{"position":[[25,5],[146,5]]},"746":{"position":[[10,5],[120,5]]},"748":{"position":[[281,6]]},"750":{"position":[[269,6]]},"762":{"position":[[163,5],[585,5]]},"764":{"position":[[40,5],[570,6],[789,5],[1390,8],[1399,6]]},"768":{"position":[[127,5],[521,5]]},"770":{"position":[[6,5],[250,5]]},"772":{"position":[[223,5]]},"774":{"position":[[60,7]]},"776":{"position":[[353,6],[473,6],[866,7]]},"800":{"position":[[20,6],[43,5],[83,5]]},"802":{"position":[[83,5]]},"814":{"position":[[29,7],[74,5],[115,6]]},"854":{"position":[[231,5]]},"868":{"position":[[123,5],[194,5],[309,5]]},"870":{"position":[[69,7]]},"910":{"position":[[125,5]]},"932":{"position":[[129,5]]},"935":{"position":[[95,6]]},"1017":{"position":[[29,5]]},"1019":{"position":[[273,6]]},"1027":{"position":[[88,5],[566,5],[591,5]]},"1029":{"position":[[66,5],[225,5],[670,5],[737,6]]},"1035":{"position":[[30,5],[380,6],[457,6]]},"1065":{"position":[[987,5],[1404,5],[1742,5]]},"1071":{"position":[[555,7]]},"1162":{"position":[[397,5],[439,5],[1530,6],[1564,5],[1603,5],[1666,5]]},"1166":{"position":[[421,8],[834,5],[853,6],[878,5]]},"1172":{"position":[[474,8],[495,7],[971,5],[1052,6],[1096,5],[1184,5]]},"1182":{"position":[[386,5]]},"1184":{"position":[[337,5]]},"1207":{"position":[[405,5],[796,10],[1394,10]]},"1213":{"position":[[436,8]]},"1249":{"position":[[119,5]]},"1262":{"position":[[965,9]]},"1359":{"position":[[204,6]]},"1405":{"position":[[60,6]]},"1451":{"position":[[93,5],[325,5]]},"1453":{"position":[[304,5],[351,5],[469,6],[583,5],[755,5]]},"1477":{"position":[[54,5],[123,5]]},"1479":{"position":[[415,6],[755,5],[775,5],[909,5]]},"1489":{"position":[[128,5],[224,5],[472,5],[535,5]]},"1491":{"position":[[67,5],[136,5]]},"1493":{"position":[[174,6],[453,5],[626,5]]},"1495":{"position":[[2448,6],[2455,9],[5438,5],[5459,6],[5466,5],[5711,6],[5813,6],[5849,5],[6098,5],[6473,5],[6631,5],[6686,5],[6821,7],[6947,6],[8771,7],[8810,5],[8855,7]]},"1499":{"position":[[663,5]]},"1507":{"position":[[13,5],[457,7],[493,6],[691,5],[804,7],[916,5]]},"1577":{"position":[[100,5],[638,10],[911,10]]},"1579":{"position":[[993,5],[1084,6],[1381,5],[1439,5],[1551,5],[1609,5]]},"1583":{"position":[[425,6],[737,6],[772,6]]},"1625":{"position":[[197,6],[379,5],[422,5],[542,6]]},"1631":{"position":[[351,6]]},"1635":{"position":[[103,5],[333,6],[647,5]]},"1639":{"position":[[50,5],[971,7],[1121,6]]},"1643":{"position":[[260,8]]},"1650":{"position":[[216,5],[255,5],[290,5]]},"1652":{"position":[[325,6]]},"1654":{"position":[[110,5],[368,6]]},"1656":{"position":[[245,5],[332,6],[1190,5],[1206,5],[1232,6]]},"1660":{"position":[[208,5],[302,6]]},"1666":{"position":[[724,6],[892,6],[1062,6]]},"1676":{"position":[[192,5],[686,5]]},"1678":{"position":[[169,5]]},"1680":{"position":[[279,6],[873,5]]},"1686":{"position":[[89,6],[223,5],[342,5]]},"1690":{"position":[[30,5],[118,5],[300,5],[486,6],[674,5]]},"1692":{"position":[[0,5],[46,5],[64,5]]},"1699":{"position":[[27,7],[72,5],[151,7],[239,6],[379,5],[738,5]]},"1702":{"position":[[20,6],[245,5],[681,6],[717,6]]},"1721":{"position":[[231,5]]},"1741":{"position":[[215,5],[360,5],[469,6],[512,5],[562,6],[596,5],[669,5],[928,6],[1255,5],[1315,5],[2288,6],[2326,5]]},"1743":{"position":[[876,5],[917,5],[1581,5],[1652,6],[1690,5],[1803,6],[1841,5]]},"1745":{"position":[[640,6],[678,5],[794,6],[832,5]]},"1747":{"position":[[791,6],[829,5],[947,6],[985,5]]},"1761":{"position":[[42,5],[599,6],[811,5]]},"1763":{"position":[[25,5],[146,5]]},"1765":{"position":[[10,5],[120,5]]},"1767":{"position":[[249,6],[274,5],[399,7]]},"1769":{"position":[[237,6],[262,5],[387,7]]},"1781":{"position":[[163,5],[585,5]]},"1783":{"position":[[40,5],[570,6],[789,5],[1390,8],[1399,6]]},"1787":{"position":[[127,5],[521,5],[690,6],[774,6],[794,5]]},"1789":{"position":[[6,5],[250,5]]},"1791":{"position":[[223,5]]},"1793":{"position":[[60,7]]},"1795":{"position":[[353,6],[473,6],[624,6],[1042,7]]},"1797":{"position":[[229,5],[1248,6],[1273,5],[1398,7]]},"1803":{"position":[[222,5]]},"1805":{"position":[[62,5]]},"1811":{"position":[[88,5],[566,5],[591,5]]},"1813":{"position":[[66,5],[225,5],[670,5],[737,6]]},"1819":{"position":[[25,5],[146,5]]},"1821":{"position":[[10,5],[120,5]]},"1823":{"position":[[154,6]]},"1825":{"position":[[30,5],[336,6],[413,6]]},"1827":{"position":[[63,5]]},"1851":{"position":[[18,6],[40,5],[80,5],[143,6]]},"1853":{"position":[[80,5]]},"1865":{"position":[[100,6],[167,6]]},"1875":{"position":[[170,6]]},"1877":{"position":[[310,5],[324,5],[577,6]]},"1985":{"position":[[125,5]]},"1987":{"position":[[165,5]]},"2007":{"position":[[129,5]]},"2010":{"position":[[95,6]]},"2066":{"position":[[123,5],[194,5],[309,5]]},"2068":{"position":[[153,5],[244,5],[370,5]]},"2070":{"position":[[69,7]]},"2072":{"position":[[75,7]]},"2146":{"position":[[1104,5],[1521,5],[1859,5]]},"2152":{"position":[[555,7]]},"2240":{"position":[[1681,5],[2335,5],[2392,5]]},"2250":{"position":[[386,5]]},"2262":{"position":[[289,5]]},"2264":{"position":[[286,5]]},"2339":{"position":[[405,5],[796,10]]},"2345":{"position":[[4413,5],[5041,5]]},"2347":{"position":[[99,5],[136,5]]},"2351":{"position":[[71,5],[211,6],[260,5],[327,5],[429,6],[445,5],[1265,6],[1407,5],[1509,5],[1583,5]]},"2355":{"position":[[4625,5]]},"2363":{"position":[[107,5],[146,5]]},"2367":{"position":[[83,5],[205,6],[266,5],[322,5],[422,6],[436,5],[1298,6],[1404,5],[1509,5],[1611,5],[1713,5]]},"2387":{"position":[[436,8]]},"2400":{"position":[[119,5]]},"2413":{"position":[[974,9]]},"2510":{"position":[[204,6]]},"2562":{"position":[[66,6]]},"2592":{"position":[[98,5],[350,5]]},"2594":{"position":[[308,5],[360,5],[486,7],[605,5],[793,5]]},"2630":{"position":[[663,5]]},"2638":{"position":[[13,5],[457,7],[493,6],[691,5],[804,7],[916,5]]},"2652":{"position":[[76,7]]},"2662":{"position":[[2463,6],[2470,9],[5500,5],[5521,6],[5528,5],[5773,6],[5872,6],[5908,5],[6163,5],[6546,5],[6712,5],[6767,5],[6902,7],[7028,6],[8888,7],[8928,5],[8975,7]]},"2670":{"position":[[54,5],[123,5]]},"2672":{"position":[[415,6],[755,5],[775,5],[909,5]]},"2682":{"position":[[128,5],[224,5],[472,5],[535,5]]},"2684":{"position":[[67,5],[136,5]]},"2686":{"position":[[174,6],[453,5],[626,5]]},"2744":{"position":[[90,5],[536,6]]},"2752":{"position":[[143,6]]},"2770":{"position":[[100,5],[638,10],[911,10]]},"2772":{"position":[[993,5],[1084,6],[1381,5],[1439,5],[1551,5],[1609,5]]},"2778":{"position":[[426,6],[738,6],[773,6]]},"2810":{"position":[[89,6],[223,5],[342,5]]},"2814":{"position":[[30,5],[118,5],[300,5],[486,6],[674,5]]},"2816":{"position":[[0,5],[46,5],[64,5]]},"2823":{"position":[[27,7],[72,5],[151,7],[239,6],[379,5],[702,5]]},"2826":{"position":[[20,6],[245,5],[645,6],[681,6]]},"2830":{"position":[[192,5],[689,5]]},"2832":{"position":[[169,5]]},"2834":{"position":[[279,6],[876,5]]},"2842":{"position":[[197,6],[379,5],[422,5],[542,6]]},"2848":{"position":[[351,6]]},"2852":{"position":[[103,5],[333,6],[647,5]]},"2856":{"position":[[50,5],[971,7],[1121,6]]},"2860":{"position":[[260,8]]},"2867":{"position":[[216,5],[255,5],[290,5]]},"2871":{"position":[[110,5]]},"2873":{"position":[[771,5],[1389,5],[1405,5],[1435,6]]},"2877":{"position":[[663,5]]},"2883":{"position":[[921,6],[1089,6],[1259,6]]},"2910":{"position":[[231,5]]},"2930":{"position":[[215,5],[360,5],[469,6],[512,5],[562,6],[596,5],[669,5],[928,6],[1255,5],[1315,5],[2288,6],[2326,5]]},"2932":{"position":[[876,5],[917,5],[1581,5],[1652,6],[1690,5],[1803,6],[1841,5]]},"2934":{"position":[[640,6],[678,5],[794,6],[832,5]]},"2936":{"position":[[791,6],[829,5],[947,6],[985,5]]},"2950":{"position":[[62,5],[642,6],[866,5]]},"2952":{"position":[[39,5],[191,5]]},"2954":{"position":[[36,6],[162,5]]},"2956":{"position":[[249,6],[274,5],[331,5],[368,5],[420,5]]},"2958":{"position":[[237,6],[262,5],[319,5],[356,5],[408,5]]},"2970":{"position":[[136,5],[568,6]]},"2972":{"position":[[44,5],[561,6],[775,5],[1159,6],[1213,5],[1327,8],[1336,6],[1499,5],[1620,5],[1783,6]]},"2976":{"position":[[219,5],[665,5],[834,6],[918,6],[938,5]]},"2978":{"position":[[6,5],[341,5]]},"2980":{"position":[[314,5]]},"2982":{"position":[[60,7]]},"2984":{"position":[[353,6],[473,6],[624,6],[1000,7],[1109,7]]},"2986":{"position":[[195,5],[1214,6],[1239,5],[1296,5],[1333,5],[1385,5]]},"2992":{"position":[[222,5]]},"2994":{"position":[[62,5]]},"3000":{"position":[[88,5],[566,5],[591,5]]},"3002":{"position":[[66,5],[225,5],[670,5],[737,6]]},"3008":{"position":[[25,5],[146,5]]},"3010":{"position":[[10,5],[120,5]]},"3012":{"position":[[154,6]]},"3014":{"position":[[30,5],[449,5],[696,6],[773,6]]},"3016":{"position":[[63,5]]},"3018":{"position":[[99,5],[148,6],[279,7],[337,7],[606,5],[669,5]]},"3042":{"position":[[18,6],[40,5],[80,5],[143,6]]},"3044":{"position":[[80,5]]},"3056":{"position":[[100,6],[167,6]]},"3066":{"position":[[177,6]]},"3068":{"position":[[364,6],[587,5]]},"3144":{"position":[[123,5],[194,5],[309,5]]},"3146":{"position":[[153,5],[244,5],[370,5]]},"3148":{"position":[[69,7]]},"3150":{"position":[[75,7]]},"3200":{"position":[[125,5]]},"3202":{"position":[[138,5]]},"3224":{"position":[[129,5]]},"3227":{"position":[[95,6]]},"3229":{"position":[[218,7],[314,5],[352,5]]},"3375":{"position":[[1104,5],[1521,5],[1859,5]]},"3381":{"position":[[379,5],[732,6]]},"3496":{"position":[[289,5]]},"3498":{"position":[[286,5]]},"3551":{"position":[[1681,5],[2335,5],[2392,5]]},"3561":{"position":[[386,5]]},"3567":{"position":[[119,5]]},"3580":{"position":[[974,9]]},"3595":{"position":[[405,5],[796,10]]},"3601":{"position":[[436,8]]},"3633":{"position":[[4413,5],[5041,5]]},"3635":{"position":[[99,5],[136,5]]},"3639":{"position":[[71,5],[211,6],[260,5],[327,5],[429,6],[445,5],[717,5],[740,5],[967,5],[1103,5],[1316,6],[1427,5],[1529,5],[1603,5]]},"3643":{"position":[[4625,5]]},"3651":{"position":[[107,5],[146,5]]},"3655":{"position":[[83,5],[205,6],[266,5],[322,5],[422,6],[436,5],[1151,5],[1287,5],[1551,6],[1677,5],[1779,5],[1881,5]]}}}],["token/no",{"_index":2309,"t":{"198":{"position":[[482,8]]}}}],["token/top",{"_index":4921,"t":{"1639":{"position":[[1189,11]]},"2856":{"position":[[1189,11]]}}}],["token_",{"_index":5560,"t":{"3018":{"position":[[744,7]]}}}],["token_audi",{"_index":3927,"t":{"748":{"position":[[144,14],[188,17],[236,14]]},"1031":{"position":[[119,14]]},"1767":{"position":[[112,14],[156,17],[204,14]]},"1797":{"position":[[1060,14]]},"1815":{"position":[[87,14]]},"2956":{"position":[[112,14],[156,17],[204,14]]},"2986":{"position":[[1026,14]]},"3004":{"position":[[87,14]]}}}],["token_audience_regex",{"_index":5022,"t":{"1797":{"position":[[534,20],[1012,20],[1197,20]]},"2986":{"position":[[500,20],[978,20],[1163,20]]}}}],["token_hmac_secret_key",{"_index":2722,"t":{"285":{"position":[[57,24]]},"449":{"position":[[37,21]]},"527":{"position":[[41,24]]},"570":{"position":[[335,21]]},"732":{"position":[[74,24]]},"734":{"position":[[1133,24]]},"768":{"position":[[219,21],[283,21]]},"884":{"position":[[118,24],[206,21]]},"886":{"position":[[176,22]]},"1035":{"position":[[315,21]]},"1262":{"position":[[302,24]]},"1435":{"position":[[41,24]]},"1449":{"position":[[37,21]]},"1495":{"position":[[793,24],[1327,24],[6531,21],[7830,24]]},"1787":{"position":[[219,21],[283,21]]},"1825":{"position":[[271,21]]},"1881":{"position":[[1164,24]]},"1937":{"position":[[308,24],[1061,24]]},"1951":{"position":[[118,24],[206,21]]},"1953":{"position":[[176,22]]},"2413":{"position":[[302,24]]},"2590":{"position":[[39,21]]},"2618":{"position":[[41,24]]},"2662":{"position":[[808,24],[1342,24],[6608,21],[7927,24]]},"2976":{"position":[[363,21],[427,21]]},"3014":{"position":[[631,21]]},"3072":{"position":[[1452,24]]},"3134":{"position":[[308,24],[1061,24]]},"3164":{"position":[[118,24],[206,21]]},"3166":{"position":[[176,22]]},"3580":{"position":[[302,24]]}}}],["token_hmac_secret_key/token_rsa_public_key",{"_index":3069,"t":{"449":{"position":[[161,42]]},"1449":{"position":[[161,42]]},"2590":{"position":[[164,42]]}}}],["token_issu",{"_index":3930,"t":{"750":{"position":[[142,12],[184,15],[226,12]]},"1033":{"position":[[117,12]]},"1769":{"position":[[110,12],[152,15],[194,12]]},"1797":{"position":[[1043,12]]},"1817":{"position":[[85,12]]},"2958":{"position":[[110,12],[152,15],[194,12]]},"2986":{"position":[[1009,12]]},"3006":{"position":[[85,12]]}}}],["token_issuer_regex",{"_index":5021,"t":{"1797":{"position":[[368,18],[733,21],[989,18],[1174,18]]},"2986":{"position":[[334,18],[699,21],[955,18],[1140,18]]}}}],["token_jwks_public_endpoint",{"_index":2377,"t":{"210":{"position":[[878,29]]},"776":{"position":[[236,26],[318,26]]},"1795":{"position":[[236,26],[318,26]]},"1797":{"position":[[808,29],[922,26]]},"2984":{"position":[[236,26],[318,26]]},"2986":{"position":[[774,29],[888,26]]},"3018":{"position":[[359,29]]}}}],["token_revok",{"_index":3827,"t":{"648":{"position":[[84,15],[521,12]]},"650":{"position":[[214,15]]},"1694":{"position":[[84,15],[521,12]]},"1696":{"position":[[214,15]]},"2818":{"position":[[84,15],[521,12]]},"2820":{"position":[[214,15]]}}}],["token_rsa_public_key",{"_index":3068,"t":{"449":{"position":[[63,21]]},"1449":{"position":[[63,21]]},"2590":{"position":[[65,21]]}}}],["tokens_left",{"_index":5472,"t":{"2744":{"position":[[504,14],[593,14],[962,11]]},"2752":{"position":[[110,11]]}}}],["token});centrifuge.connect",{"_index":5019,"t":{"1787":{"position":[[697,29]]},"2976":{"position":[[841,29]]}}}],["token});sub.on('publ",{"_index":2221,"t":{"178":{"position":[[520,29]]}}}],["told",{"_index":2072,"t":{"136":{"position":[[2165,4]]}}}],["toler",{"_index":2946,"t":{"363":{"position":[[601,8]]},"718":{"position":[[694,8]]},"1182":{"position":[[184,8],[1140,8]]},"1383":{"position":[[614,8]]},"1897":{"position":[[1047,8]]},"2250":{"position":[[184,8],[1140,8]]},"2534":{"position":[[638,8]]},"3090":{"position":[[1047,8]]},"3561":{"position":[[184,8],[1140,8]]}}}],["toml",{"_index":3554,"t":{"523":{"position":[[1020,4]]},"572":{"position":[[169,4]]},"866":{"position":[[262,4]]},"880":{"position":[[139,5]]},"882":{"position":[[69,5]]},"886":{"position":[[25,4]]},"1431":{"position":[[1253,4]]},"1947":{"position":[[139,5]]},"1949":{"position":[[69,5]]},"1953":{"position":[[25,4]]},"2062":{"position":[[301,4]]},"2614":{"position":[[1286,4]]},"3140":{"position":[[301,4]]},"3160":{"position":[[139,5]]},"3162":{"position":[[69,5]]},"3166":{"position":[[25,4]]}}}],["ton",{"_index":385,"t":{"8":{"position":[[1680,4]]},"42":{"position":[[1355,4],[2664,4]]},"351":{"position":[[112,4]]},"1339":{"position":[[112,4]]},"2486":{"position":[[112,4]]}}}],["tool",{"_index":876,"t":{"26":{"position":[[1685,4]]},"120":{"position":[[902,4]]},"148":{"position":[[1243,4]]},"194":{"position":[[895,6]]},"214":{"position":[[270,5],[579,5]]},"222":{"position":[[749,4]]},"226":{"position":[[22,4],[610,5]]},"244":{"position":[[232,5]]},"279":{"position":[[408,5],[1373,4]]},"301":{"position":[[58,5]]},"373":{"position":[[888,6]]},"483":{"position":[[169,4]]},"572":{"position":[[579,4]]},"658":{"position":[[371,6]]},"1109":{"position":[[739,5]]},"1262":{"position":[[65,4]]},"1495":{"position":[[4697,5],[8562,5],[9794,5]]},"1682":{"position":[[832,5]]},"1686":{"position":[[371,6]]},"2126":{"position":[[61,4],[222,6]]},"2190":{"position":[[799,5]]},"2413":{"position":[[65,4]]},"2662":{"position":[[4759,5],[8659,5],[9950,5]]},"2810":{"position":[[371,6]]},"2836":{"position":[[832,5]]},"3341":{"position":[[61,4],[220,6]]},"3580":{"position":[[65,4]]},"3629":{"position":[[267,5],[580,5]]}}}],["toolspython",{"_index":4472,"t":{"1139":{"position":[[96,11]]},"2220":{"position":[[96,11]]},"3461":{"position":[[96,11]]}}}],["top",{"_index":391,"t":{"10":{"position":[[55,3]]},"16":{"position":[[5648,3],[6158,3]]},"26":{"position":[[242,3]]},"28":{"position":[[845,3],[936,3],[1652,3]]},"50":{"position":[[295,3]]},"62":{"position":[[229,3],[801,3]]},"78":{"position":[[600,3]]},"88":{"position":[[113,3]]},"94":{"position":[[1548,3]]},"192":{"position":[[742,3]]},"204":{"position":[[767,3]]},"246":{"position":[[3236,3]]},"250":{"position":[[174,3]]},"256":{"position":[[1570,3]]},"277":{"position":[[51,3]]},"281":{"position":[[179,3]]},"303":{"position":[[293,3]]},"377":{"position":[[357,3]]},"381":{"position":[[399,3]]},"407":{"position":[[665,3]]},"419":{"position":[[81,3]]},"441":{"position":[[21,3]]},"475":{"position":[[1427,3],[1564,3]]},"479":{"position":[[97,3]]},"511":{"position":[[170,3]]},"541":{"position":[[418,3]]},"610":{"position":[[1079,3]]},"698":{"position":[[107,3]]},"714":{"position":[[488,3]]},"716":{"position":[[413,3]]},"732":{"position":[[347,3]]},"734":{"position":[[1037,3],[2178,3]]},"932":{"position":[[163,4]]},"963":{"position":[[153,3]]},"965":{"position":[[1289,3],[3413,3]]},"1071":{"position":[[1299,3]]},"1073":{"position":[[1159,3],[1662,3]]},"1093":{"position":[[48,3]]},"1097":{"position":[[204,3],[239,3],[939,3]]},"1099":{"position":[[842,3]]},"1125":{"position":[[1643,3]]},"1127":{"position":[[72,3]]},"1153":{"position":[[136,3]]},"1162":{"position":[[1193,3],[1233,3],[1270,3],[1313,3],[1349,3],[2392,3]]},"1186":{"position":[[330,3]]},"1353":{"position":[[23,3]]},"1373":{"position":[[170,3]]},"1397":{"position":[[58,3]]},"1425":{"position":[[21,3]]},"1503":{"position":[[151,3]]},"1545":{"position":[[1073,3]]},"1569":{"position":[[679,3]]},"1869":{"position":[[1091,3]]},"1881":{"position":[[1068,3],[2065,3],[2288,3]]},"1883":{"position":[[107,3]]},"1893":{"position":[[488,3]]},"1895":{"position":[[505,3]]},"1937":{"position":[[804,3]]},"2007":{"position":[[163,4]]},"2100":{"position":[[153,3]]},"2102":{"position":[[1289,3],[2346,3],[4557,3]]},"2120":{"position":[[349,3]]},"2122":{"position":[[331,3]]},"2152":{"position":[[1299,3]]},"2154":{"position":[[1159,3],[1662,3]]},"2156":{"position":[[1420,3]]},"2176":{"position":[[48,3]]},"2180":{"position":[[241,3],[276,3],[1142,3]]},"2182":{"position":[[842,3]]},"2206":{"position":[[1643,3]]},"2208":{"position":[[72,3]]},"2238":{"position":[[23,3]]},"2281":{"position":[[136,3]]},"2294":{"position":[[852,3]]},"2369":{"position":[[1847,3]]},"2371":{"position":[[193,3]]},"2379":{"position":[[170,3]]},"2434":{"position":[[100,3]]},"2498":{"position":[[61,3]]},"2502":{"position":[[322,3]]},"2524":{"position":[[170,3]]},"2546":{"position":[[23,3]]},"2554":{"position":[[62,3]]},"2582":{"position":[[21,3]]},"2634":{"position":[[151,3]]},"2656":{"position":[[594,3]]},"2714":{"position":[[1073,3]]},"2728":{"position":[[679,3]]},"3072":{"position":[[1356,3],[2353,3],[2576,3]]},"3074":{"position":[[107,3]]},"3084":{"position":[[488,3]]},"3088":{"position":[[505,3]]},"3134":{"position":[[804,3]]},"3224":{"position":[[163,4]]},"3259":{"position":[[153,3]]},"3261":{"position":[[1289,3],[2346,3],[4557,3]]},"3335":{"position":[[341,3]]},"3337":{"position":[[591,3]]},"3381":{"position":[[1102,3]]},"3383":{"position":[[1159,3],[1662,3]]},"3385":{"position":[[1393,3]]},"3405":{"position":[[48,3]]},"3409":{"position":[[241,3],[276,3],[1142,3]]},"3411":{"position":[[842,3]]},"3447":{"position":[[1485,3]]},"3449":{"position":[[72,3]]},"3508":{"position":[[136,3]]},"3524":{"position":[[852,3]]},"3549":{"position":[[23,3]]},"3612":{"position":[[100,3]]},"3657":{"position":[[1847,3]]},"3659":{"position":[[193,3]]},"3667":{"position":[[170,3]]}}}],["topic",{"_index":238,"t":{"4":{"position":[[391,6]]},"16":{"position":[[960,6],[1036,5],[1530,6],[1561,7],[1580,5],[1624,6],[2601,6],[2696,5],[3859,7],[3919,7],[4076,7],[4878,7],[6437,6],[7465,5]]},"18":{"position":[[1935,6]]},"22":{"position":[[281,6]]},"34":{"position":[[108,5]]},"260":{"position":[[485,6]]},"262":{"position":[[417,9]]},"264":{"position":[[88,6],[160,6],[210,5],[359,6],[433,7],[463,5],[523,5],[647,7],[782,6],[897,6],[1161,6],[1206,5]]},"268":{"position":[[202,7]]},"287":{"position":[[624,5]]},"985":{"position":[[1078,6]]},"1349":{"position":[[339,6]]},"1629":{"position":[[88,6],[160,6],[210,5],[356,6],[430,7],[512,6],[637,5],[697,5],[821,7],[920,7],[1010,6],[1234,6],[1279,5]]},"1631":{"position":[[314,6],[529,8]]},"1635":{"position":[[482,6]]},"1639":{"position":[[998,6],[1177,7]]},"1650":{"position":[[439,6],[473,5],[544,6],[595,6]]},"1652":{"position":[[580,6],[915,6]]},"1656":{"position":[[704,6],[1327,6]]},"1658":{"position":[[28,7],[164,6],[198,7]]},"1660":{"position":[[15,5],[480,6],[514,6],[1158,5]]},"1662":{"position":[[18,6],[48,6],[286,6],[320,7]]},"1664":{"position":[[13,5],[143,6],[177,6],[688,5]]},"1666":{"position":[[53,7],[600,6],[634,6],[772,5],[940,5]]},"2044":{"position":[[1078,6]]},"2494":{"position":[[337,6]]},"2504":{"position":[[118,7],[844,6],[1043,6]]},"2506":{"position":[[1059,6]]},"2846":{"position":[[88,6],[160,6],[210,5],[356,6],[430,7],[512,6],[637,5],[697,5],[821,7],[920,7],[1010,6],[1234,6],[1279,5]]},"2848":{"position":[[314,6],[529,8]]},"2852":{"position":[[482,6]]},"2856":{"position":[[998,6],[1177,7]]},"2867":{"position":[[439,6],[473,5],[544,6],[595,6]]},"2869":{"position":[[439,6],[733,6],[760,6]]},"2873":{"position":[[494,6],[944,6],[978,6],[1537,6]]},"2875":{"position":[[28,7],[164,6],[198,7]]},"2877":{"position":[[15,5],[850,6],[884,6],[933,5],[1381,5],[1398,5]]},"2879":{"position":[[18,6],[48,6],[286,6],[320,7]]},"2881":{"position":[[13,5],[479,6],[513,6],[843,5],[860,5]]},"2883":{"position":[[53,7],[969,5],[1137,5]]},"3285":{"position":[[1078,6]]}}}],["topic_prefix",{"_index":4969,"t":{"1660":{"position":[[540,12]]},"1664":{"position":[[203,12]]},"2877":{"position":[[910,12]]},"2881":{"position":[[539,12]]}}}],["topics_upd",{"_index":4955,"t":{"1652":{"position":[[533,13]]},"2869":{"position":[[394,13]]}}}],["topolog",{"_index":4172,"t":{"989":{"position":[[1114,10]]},"2050":{"position":[[1114,10]]},"3289":{"position":[[1114,10]]}}}],["tornado",{"_index":4301,"t":{"1065":{"position":[[4812,7]]},"2146":{"position":[[4935,7]]},"3375":{"position":[[4964,7]]}}}],["tornado.ioloop.ioloop.instance().start()if",{"_index":4314,"t":{"1065":{"position":[[5349,42]]},"2146":{"position":[[5472,42]]},"3375":{"position":[[5501,42]]}}}],["tornado.web.appl",{"_index":4310,"t":{"1065":{"position":[[5251,25]]},"2146":{"position":[[5374,25]]},"3375":{"position":[[5403,25]]}}}],["total",{"_index":137,"t":{"2":{"position":[[2095,5],[2599,5]]},"88":{"position":[[418,5]]},"610":{"position":[[180,5]]},"1123":{"position":[[700,5],[756,5]]},"1129":{"position":[[470,5]]},"1545":{"position":[[180,5],[1524,5]]},"1670":{"position":[[137,5]]},"1676":{"position":[[483,5],[514,5],[697,5],[1009,8]]},"1678":{"position":[[611,5]]},"1680":{"position":[[1050,6]]},"1682":{"position":[[1465,8]]},"2204":{"position":[[700,5],[756,5]]},"2210":{"position":[[517,5]]},"2714":{"position":[[180,5],[1524,5]]},"2720":{"position":[[146,5]]},"2830":{"position":[[486,5],[517,5],[700,5],[1012,8]]},"2832":{"position":[[617,5]]},"2834":{"position":[[1056,6]]},"2836":{"position":[[1465,8]]},"2873":{"position":[[400,5],[1232,5]]},"2877":{"position":[[463,5],[1218,5]]},"2881":{"position":[[342,5]]},"2889":{"position":[[210,5]]},"3324":{"position":[[1420,5]]},"3445":{"position":[[541,5],[597,5]]},"3451":{"position":[[577,5]]}}}],["total_count",{"_index":5526,"t":{"2873":{"position":[[1209,11]]},"2877":{"position":[[1195,11]]},"2881":{"position":[[723,11]]}}}],["touch",{"_index":2203,"t":{"170":{"position":[[362,7]]}}}],["toward",{"_index":2331,"t":{"200":{"position":[[1116,7]]},"204":{"position":[[1352,7]]},"246":{"position":[[3625,7]]},"363":{"position":[[473,7]]},"467":{"position":[[232,7]]},"479":{"position":[[4777,7],[4989,7]]},"481":{"position":[[2135,7]]},"965":{"position":[[806,7]]},"1319":{"position":[[291,7]]},"1341":{"position":[[46,7]]},"1383":{"position":[[486,7]]},"1611":{"position":[[160,7],[464,7]]},"1743":{"position":[[225,7]]},"1903":{"position":[[481,7]]},"1905":{"position":[[454,7]]},"2102":{"position":[[799,7]]},"2258":{"position":[[1466,7]]},"2466":{"position":[[291,7]]},"2488":{"position":[[46,7]]},"2534":{"position":[[502,7]]},"2736":{"position":[[160,7],[464,7]]},"2932":{"position":[[225,7]]},"3096":{"position":[[481,7]]},"3098":{"position":[[454,7]]},"3261":{"position":[[799,7]]},"3347":{"position":[[760,7],[1270,7],[3665,7]]},"3492":{"position":[[1466,7]]}}}],["toxic",{"_index":3462,"t":{"483":{"position":[[387,5],[527,5]]}}}],["toxic_redi",{"_index":3470,"t":{"483":{"position":[[561,11]]}}}],["toxic_redistoxiproxi",{"_index":3467,"t":{"483":{"position":[[502,20]]}}}],["toxiproxi",{"_index":3461,"t":{"483":{"position":[[363,9],[444,9]]}}}],["toxyproxi",{"_index":3460,"t":{"483":{"position":[[310,9]]}}}],["toy",{"_index":1774,"t":{"114":{"position":[[59,3]]}}}],["toyyyymmdd(time)ord",{"_index":3731,"t":{"606":{"position":[[543,21]]},"608":{"position":[[598,21]]},"1535":{"position":[[463,21]]},"1537":{"position":[[442,21]]},"1539":{"position":[[589,21]]},"1541":{"position":[[464,21]]},"1543":{"position":[[664,21]]},"2704":{"position":[[463,21]]},"2706":{"position":[[442,21]]},"2708":{"position":[[589,21]]},"2710":{"position":[[464,21]]},"2712":{"position":[[664,21]]}}}],["trace",{"_index":2361,"t":{"208":{"position":[[42,7],[168,7],[238,6],[253,7],[401,5]]},"443":{"position":[[161,5]]},"576":{"position":[[96,7]]},"658":{"position":[[38,7],[110,7]]},"1583":{"position":[[105,7]]},"1686":{"position":[[38,7],[110,7]]},"2778":{"position":[[105,7]]},"2810":{"position":[[38,7],[110,7]]},"3327":{"position":[[36,6],[225,7],[458,7],[862,6],[914,6]]},"3329":{"position":[[89,5]]}}}],["trace.txt",{"_index":3848,"t":{"658":{"position":[[290,9]]},"1686":{"position":[[290,9]]},"2810":{"position":[[290,9]]}}}],["traceid",{"_index":4285,"t":{"1061":{"position":[[626,7]]},"2142":{"position":[[626,7]]},"3371":{"position":[[626,7]]}}}],["track",{"_index":1179,"t":{"48":{"position":[[709,5]]},"254":{"position":[[1413,5]]},"262":{"position":[[1818,5]]},"325":{"position":[[366,6]]},"600":{"position":[[671,6]]},"1149":{"position":[[1141,5]]},"1172":{"position":[[108,5]]},"1311":{"position":[[366,6]]},"1619":{"position":[[953,6]]},"1668":{"position":[[88,8]]},"2122":{"position":[[46,8],[497,5]]},"2126":{"position":[[256,8]]},"2277":{"position":[[1141,5]]},"2458":{"position":[[366,6]]},"2720":{"position":[[186,8]]},"2806":{"position":[[953,6]]},"2887":{"position":[[88,8]]},"2889":{"position":[[261,8],[792,8]]},"3324":{"position":[[141,6],[712,8],[1684,8],[2482,6],[3014,8],[3749,6],[5706,8],[5863,6],[6083,6]]},"3337":{"position":[[39,8],[757,5]]},"3341":{"position":[[254,8]]},"3504":{"position":[[1141,5]]}}}],["tracker",{"_index":3220,"t":{"477":{"position":[[2786,8]]}}}],["trade",{"_index":770,"t":{"18":{"position":[[1706,5]]},"148":{"position":[[529,5]]},"172":{"position":[[378,5]]},"246":{"position":[[322,7],[1495,7],[1829,7]]},"347":{"position":[[1701,5]]},"481":{"position":[[3387,5]]},"485":{"position":[[1392,5]]},"742":{"position":[[1104,5]]},"1335":{"position":[[1705,5]]},"1761":{"position":[[1104,5]]},"2482":{"position":[[1817,5]]}}}],["trader",{"_index":2514,"t":{"246":{"position":[[792,7],[3575,7]]}}}],["tradingview",{"_index":2550,"t":{"246":{"position":[[3269,11]]}}}],["tradit",{"_index":849,"t":{"26":{"position":[[403,11]]},"250":{"position":[[137,11]]}}}],["tradition",{"_index":2936,"t":{"359":{"position":[[727,13]]},"1379":{"position":[[727,13]]},"2530":{"position":[[1449,13]]}}}],["traffic",{"_index":544,"t":{"14":{"position":[[396,8],[455,8]]},"44":{"position":[[1136,7]]},"188":{"position":[[1039,7]]},"287":{"position":[[305,7]]},"339":{"position":[[995,7]]},"343":{"position":[[397,7]]},"379":{"position":[[270,8]]},"429":{"position":[[234,7]]},"981":{"position":[[91,7]]},"1133":{"position":[[158,7]]},"1283":{"position":[[209,7]]},"1285":{"position":[[307,7]]},"1327":{"position":[[995,7]]},"1331":{"position":[[397,7]]},"1413":{"position":[[223,7]]},"2040":{"position":[[91,7]]},"2214":{"position":[[158,7]]},"2296":{"position":[[981,7]]},"2430":{"position":[[209,7]]},"2432":{"position":[[307,7]]},"2434":{"position":[[2209,7]]},"2474":{"position":[[995,7]]},"2478":{"position":[[397,7]]},"2570":{"position":[[223,8]]},"3281":{"position":[[91,7]]},"3486":{"position":[[981,7]]},"3612":{"position":[[2408,7]]},"3625":{"position":[[209,7]]},"3627":{"position":[[307,7]]}}}],["transact",{"_index":2440,"t":{"222":{"position":[[516,13]]}}}],["transfer",{"_index":1186,"t":{"50":{"position":[[406,9]]},"226":{"position":[[391,8]]},"244":{"position":[[296,8]]},"379":{"position":[[1243,8],[1471,9],[2101,9]]},"1153":{"position":[[196,12]]},"1162":{"position":[[2134,8],[2483,8]]},"2281":{"position":[[196,12]]},"3324":{"position":[[4201,9]]},"3347":{"position":[[1255,9],[2452,9]]},"3349":{"position":[[861,8]]},"3508":{"position":[[196,12]]}}}],["transform",{"_index":1394,"t":{"76":{"position":[[19,9]]},"162":{"position":[[158,11]]},"168":{"position":[[68,9]]},"170":{"position":[[187,11]]},"226":{"position":[[651,16]]},"339":{"position":[[1176,11]]},"497":{"position":[[252,10]]},"1063":{"position":[[271,11]]},"1139":{"position":[[875,11]]},"1164":{"position":[[1430,11]]},"1327":{"position":[[1176,11]]},"1359":{"position":[[252,10]]},"1511":{"position":[[242,9]]},"1523":{"position":[[1412,11]]},"1937":{"position":[[1033,9]]},"2144":{"position":[[271,11]]},"2220":{"position":[[875,11]]},"2244":{"position":[[1120,11]]},"2474":{"position":[[1176,11]]},"2510":{"position":[[252,10]]},"2642":{"position":[[242,9]]},"2692":{"position":[[1412,11]]},"3134":{"position":[[1033,9]]},"3373":{"position":[[271,11]]},"3461":{"position":[[875,11]]},"3467":{"position":[[1232,11]]},"3555":{"position":[[1120,11]]}}}],["transit",{"_index":2263,"t":{"182":{"position":[[446,12]]},"377":{"position":[[1468,12],[1749,12],[1790,12]]},"1495":{"position":[[3953,11],[4026,11]]},"2345":{"position":[[3765,10],[4830,12]]},"2355":{"position":[[3807,10],[4448,12]]},"2662":{"position":[[4011,11],[4084,11]]},"3633":{"position":[[3765,10],[4830,12]]},"3643":{"position":[[3807,10],[4448,12]]}}}],["translat",{"_index":2133,"t":{"156":{"position":[[94,10]]},"180":{"position":[[759,10]]},"339":{"position":[[743,10]]},"572":{"position":[[84,9]]},"1327":{"position":[[743,10]]},"2474":{"position":[[743,10]]}}}],["transmit",{"_index":3073,"t":{"467":{"position":[[345,8]]}}}],["transpar",{"_index":2880,"t":{"323":{"position":[[25,13]]},"1061":{"position":[[54,13],[171,11]]},"1309":{"position":[[25,13]]},"2142":{"position":[[54,13],[171,11]]},"2456":{"position":[[25,13]]},"3371":{"position":[[54,13],[171,11]]}}}],["transport",{"_index":232,"t":{"4":{"position":[[319,9]]},"14":{"position":[[561,9],[686,10],[823,10]]},"26":{"position":[[502,12]]},"28":{"position":[[510,9],[954,9]]},"54":{"position":[[589,9],[751,11]]},"60":{"position":[[464,11]]},"68":{"position":[[233,10],[403,10],[488,10],[1278,11],[1379,11],[1665,10],[2108,11],[2247,10]]},"92":{"position":[[180,10]]},"94":{"position":[[517,10],[1320,9],[1392,9],[1479,9],[1715,9],[1752,9],[1841,9],[1906,9],[1957,10]]},"98":{"position":[[43,10]]},"116":{"position":[[157,9],[1013,9]]},"142":{"position":[[290,10]]},"144":{"position":[[307,10]]},"160":{"position":[[68,10]]},"182":{"position":[[1070,10]]},"192":{"position":[[376,10]]},"194":{"position":[[583,10],[1043,9]]},"216":{"position":[[268,10]]},"242":{"position":[[311,9]]},"252":{"position":[[371,9],[1321,9]]},"277":{"position":[[1035,9]]},"291":{"position":[[193,10]]},"307":{"position":[[40,9]]},"309":{"position":[[13,9],[286,11]]},"331":{"position":[[297,10],[327,11]]},"333":{"position":[[18,9]]},"339":{"position":[[921,9]]},"373":{"position":[[590,10],[1036,9]]},"375":{"position":[[133,10],[197,9]]},"379":{"position":[[674,10],[751,11],[1126,9],[1324,9],[1979,11],[2213,9],[3065,10],[3334,9],[3513,10],[3546,10],[3563,10],[3651,10],[3745,10],[3915,9],[3977,9],[4081,11],[4332,9]]},"383":{"position":[[639,11],[1586,11]]},"399":{"position":[[891,10],[908,10],[1004,10]]},"407":{"position":[[1277,9]]},"419":{"position":[[9,9],[68,9],[247,10],[370,9]]},"453":{"position":[[1017,11],[1158,9],[1233,9]]},"467":{"position":[[111,10]]},"554":{"position":[[108,9]]},"568":{"position":[[61,10]]},"604":{"position":[[1722,10]]},"606":{"position":[[253,11],[1032,11]]},"680":{"position":[[230,12]]},"892":{"position":[[211,10]]},"965":{"position":[[1978,11]]},"1001":{"position":[[58,9]]},"1003":{"position":[[58,9]]},"1063":{"position":[[52,9]]},"1065":{"position":[[652,9],[1173,12],[2496,9],[2516,9]]},"1067":{"position":[[792,9],[812,9]]},"1069":{"position":[[770,9],[790,9]]},"1071":{"position":[[2094,9],[2114,9]]},"1073":{"position":[[2128,9],[2148,9]]},"1085":{"position":[[47,10]]},"1087":{"position":[[210,11]]},"1119":{"position":[[77,10]]},"1137":{"position":[[186,9]]},"1156":{"position":[[40,10]]},"1162":{"position":[[2082,9]]},"1213":{"position":[[1135,9]]},"1266":{"position":[[14,10],[77,10],[133,10]]},"1268":{"position":[[15,10],[155,10],[497,10]]},"1270":{"position":[[26,10],[954,9]]},"1293":{"position":[[40,9]]},"1295":{"position":[[13,9],[286,11]]},"1317":{"position":[[357,10],[387,11]]},"1319":{"position":[[411,9]]},"1321":{"position":[[18,9]]},"1327":{"position":[[921,9]]},"1353":{"position":[[1141,9]]},"1401":{"position":[[349,10]]},"1403":{"position":[[9,9],[366,10],[489,9],[688,10]]},"1453":{"position":[[1018,11]]},"1469":{"position":[[987,9]]},"1501":{"position":[[55,11],[153,11],[273,10],[475,9]]},"1533":{"position":[[2288,10]]},"1535":{"position":[[244,11],[943,11]]},"1577":{"position":[[401,9],[1418,12],[1538,12]]},"1579":{"position":[[600,9],[638,9]]},"1613":{"position":[[137,9]]},"1615":{"position":[[143,9]]},"1682":{"position":[[902,10]]},"1731":{"position":[[230,12]]},"1959":{"position":[[211,10]]},"1975":{"position":[[569,10]]},"1977":{"position":[[210,9],[506,10]]},"2102":{"position":[[2145,11]]},"2108":{"position":[[58,9]]},"2110":{"position":[[58,9]]},"2144":{"position":[[52,9]]},"2146":{"position":[[652,9],[1290,12],[2613,9],[2633,9]]},"2148":{"position":[[792,9],[812,9]]},"2150":{"position":[[770,9],[790,9]]},"2152":{"position":[[2094,9],[2114,9]]},"2154":{"position":[[2128,9],[2148,9]]},"2156":{"position":[[2193,9],[2213,9]]},"2168":{"position":[[47,10]]},"2170":{"position":[[210,11]]},"2200":{"position":[[77,10]]},"2218":{"position":[[186,9]]},"2240":{"position":[[436,9]]},"2242":{"position":[[942,9],[1046,11]]},"2284":{"position":[[40,10]]},"2290":{"position":[[14,10],[77,10],[133,10],[610,10]]},"2292":{"position":[[15,10],[155,10],[500,10]]},"2294":{"position":[[26,10],[1360,9]]},"2296":{"position":[[114,11]]},"2345":{"position":[[4955,9],[5002,9]]},"2355":{"position":[[3608,10]]},"2387":{"position":[[1135,9]]},"2434":{"position":[[1608,10],[1625,10],[1716,10]]},"2440":{"position":[[40,9]]},"2442":{"position":[[13,9],[286,11]]},"2464":{"position":[[357,10],[387,11]]},"2466":{"position":[[411,9]]},"2468":{"position":[[18,9]]},"2474":{"position":[[921,9]]},"2502":{"position":[[515,9]]},"2506":{"position":[[780,11]]},"2530":{"position":[[1608,9]]},"2546":{"position":[[1202,9]]},"2558":{"position":[[346,10]]},"2560":{"position":[[9,9],[429,11],[615,9],[810,10]]},"2594":{"position":[[1062,11]]},"2610":{"position":[[978,9]]},"2632":{"position":[[55,11],[153,11],[273,10],[475,9]]},"2702":{"position":[[2288,10]]},"2704":{"position":[[244,11],[943,11]]},"2738":{"position":[[137,9]]},"2740":{"position":[[143,9]]},"2770":{"position":[[401,9],[1382,12],[1502,12]]},"2772":{"position":[[600,9],[638,9]]},"2836":{"position":[[902,10]]},"2920":{"position":[[230,12]]},"3172":{"position":[[211,10]]},"3188":{"position":[[542,10]]},"3190":{"position":[[183,9],[479,10]]},"3198":{"position":[[673,10]]},"3261":{"position":[[2145,11]]},"3309":{"position":[[58,9]]},"3311":{"position":[[58,9]]},"3324":{"position":[[3449,9],[3519,11],[3581,10],[3695,10],[3820,11],[3910,11],[3985,10],[4110,11],[4273,10],[4404,11],[4574,10],[4705,11]]},"3373":{"position":[[52,9]]},"3375":{"position":[[652,9],[1290,12],[2613,9],[2633,9]]},"3377":{"position":[[792,9],[812,9]]},"3379":{"position":[[308,9],[817,9],[837,9]]},"3381":{"position":[[1897,9],[1917,9]]},"3383":{"position":[[2128,9],[2148,9]]},"3385":{"position":[[2166,9],[2186,9]]},"3397":{"position":[[47,10]]},"3399":{"position":[[210,11]]},"3441":{"position":[[77,10]]},"3459":{"position":[[186,9]]},"3467":{"position":[[48,9],[146,9],[490,9],[1187,9],[1508,9],[1580,11],[1648,11],[1787,9],[1893,9],[1931,9]]},"3476":{"position":[[471,9]]},"3482":{"position":[[14,10],[77,10],[168,10],[734,10]]},"3484":{"position":[[15,10],[155,10],[500,10],[937,11]]},"3486":{"position":[[114,11]]},"3511":{"position":[[40,10]]},"3518":{"position":[[451,9]]},"3524":{"position":[[26,10],[1360,9]]},"3551":{"position":[[436,9]]},"3553":{"position":[[942,9],[1046,11]]},"3601":{"position":[[1135,9]]},"3612":{"position":[[1635,10],[1652,10],[1743,10]]},"3633":{"position":[[4955,9],[5002,9]]},"3643":{"position":[[3608,10]]}}}],["transport\":\"websocket",{"_index":4295,"t":{"1065":{"position":[[1906,24]]},"1067":{"position":[[507,24]]},"1069":{"position":[[419,24]]},"1071":{"position":[[1778,24]]},"1073":{"position":[[1793,24]]},"2146":{"position":[[2023,24]]},"2148":{"position":[[507,24]]},"2150":{"position":[[419,24]]},"2152":{"position":[[1778,24]]},"2154":{"position":[[1793,24]]},"2156":{"position":[[1882,24]]},"3375":{"position":[[2023,24]]},"3377":{"position":[[507,24]]},"3379":{"position":[[466,24]]},"3381":{"position":[[1581,24]]},"3383":{"position":[[1793,24]]},"3385":{"position":[[1855,24]]}}}],["transport\"http/1.1",{"_index":5740,"t":{"3467":{"position":[[1350,18]]}}}],["transport/endpoint",{"_index":2991,"t":{"379":{"position":[[4237,18]]}}}],["transport/featur",{"_index":2867,"t":{"309":{"position":[[523,18]]},"1295":{"position":[[523,18]]},"2442":{"position":[[523,18]]}}}],["transport/proxi",{"_index":4288,"t":{"1063":{"position":[[423,15]]},"2144":{"position":[[423,15]]},"3373":{"position":[[423,15]]}}}],["transport://localhost:4433/path",{"_index":1521,"t":{"94":{"position":[[1788,34]]}}}],["travel",{"_index":490,"t":{"12":{"position":[[364,9]]},"16":{"position":[[5991,6]]},"42":{"position":[[1924,7]]},"52":{"position":[[699,9]]},"321":{"position":[[213,7]]},"351":{"position":[[1095,9]]},"359":{"position":[[324,6]]},"1133":{"position":[[166,9]]},"1283":{"position":[[217,10]]},"1307":{"position":[[213,7]]},"1339":{"position":[[1095,9]]},"1379":{"position":[[324,6]]},"2214":{"position":[[166,9]]},"2430":{"position":[[217,10]]},"2454":{"position":[[213,7]]},"2486":{"position":[[1095,9]]},"3625":{"position":[[217,10]]}}}],["treat",{"_index":2215,"t":{"176":{"position":[[718,7]]},"910":{"position":[[139,7]]},"1985":{"position":[[139,7]]},"3200":{"position":[[139,7]]}}}],["tree",{"_index":1809,"t":{"124":{"position":[[276,4]]},"126":{"position":[[229,4],[572,4]]},"128":{"position":[[385,4],[1708,4]]}}}],["trello",{"_index":674,"t":{"16":{"position":[[4315,6]]}}}],["trend",{"_index":3665,"t":{"576":{"position":[[282,9]]},"1583":{"position":[[279,9]]},"2778":{"position":[[279,9]]}}}],["tri",{"_index":505,"t":{"12":{"position":[[1019,3],[1453,3]]},"16":{"position":[[2134,3],[2520,3],[2952,3],[6975,3],[7095,3]]},"18":{"position":[[1006,3],[2269,3]]},"20":{"position":[[389,5],[605,5]]},"24":{"position":[[18,3]]},"46":{"position":[[2721,5]]},"94":{"position":[[1615,3]]},"100":{"position":[[225,5]]},"108":{"position":[[185,3]]},"130":{"position":[[2875,6]]},"132":{"position":[[1973,5]]},"136":{"position":[[17,3],[165,5],[2290,3]]},"148":{"position":[[1466,3]]},"168":{"position":[[928,3]]},"218":{"position":[[643,6],[735,4]]},"230":{"position":[[81,5]]},"250":{"position":[[1814,3],[1896,6]]},"287":{"position":[[1878,3]]},"289":{"position":[[552,3]]},"301":{"position":[[4194,3]]},"323":{"position":[[735,3]]},"347":{"position":[[2163,3]]},"363":{"position":[[501,5]]},"377":{"position":[[126,3],[1348,3]]},"479":{"position":[[734,3]]},"481":{"position":[[2982,3]]},"485":{"position":[[673,5]]},"578":{"position":[[8,3],[376,6]]},"600":{"position":[[155,3],[455,3],[780,3]]},"604":{"position":[[1839,5]]},"648":{"position":[[692,3]]},"666":{"position":[[555,3]]},"692":{"position":[[638,6]]},"716":{"position":[[79,5]]},"718":{"position":[[64,3]]},"798":{"position":[[132,6]]},"932":{"position":[[202,5]]},"965":{"position":[[3114,3]]},"989":{"position":[[549,5]]},"1021":{"position":[[30,5]]},"1027":{"position":[[307,3]]},"1043":{"position":[[278,3],[493,3]]},"1071":{"position":[[895,5]]},"1178":{"position":[[323,3]]},"1180":{"position":[[557,3]]},"1184":{"position":[[2527,3]]},"1262":{"position":[[1335,3]]},"1270":{"position":[[925,3]]},"1309":{"position":[[735,3]]},"1335":{"position":[[2167,3]]},"1349":{"position":[[140,6]]},"1383":{"position":[[514,5]]},"1479":{"position":[[932,3]]},"1493":{"position":[[649,3]]},"1511":{"position":[[314,5]]},"1523":{"position":[[819,5]]},"1533":{"position":[[2374,5]]},"1585":{"position":[[8,3],[376,6]]},"1611":{"position":[[74,3]]},"1619":{"position":[[155,3],[737,3],[1062,3]]},"1623":{"position":[[3,5]]},"1676":{"position":[[1446,3]]},"1680":{"position":[[440,5]]},"1694":{"position":[[692,3]]},"1710":{"position":[[555,3]]},"1805":{"position":[[30,5]]},"1811":{"position":[[307,3]]},"1849":{"position":[[129,6]]},"1873":{"position":[[623,6]]},"1895":{"position":[[175,3]]},"1897":{"position":[[172,3]]},"2007":{"position":[[202,5]]},"2030":{"position":[[787,5],[842,6]]},"2050":{"position":[[549,5]]},"2102":{"position":[[2692,5]]},"2134":{"position":[[278,3],[493,3]]},"2152":{"position":[[895,5],[4223,3],[4339,3]]},"2246":{"position":[[248,3]]},"2294":{"position":[[1331,3]]},"2345":{"position":[[917,5]]},"2355":{"position":[[2206,3]]},"2359":{"position":[[1746,3]]},"2413":{"position":[[1414,3]]},"2456":{"position":[[735,3]]},"2482":{"position":[[2409,3]]},"2642":{"position":[[314,5]]},"2672":{"position":[[932,3]]},"2686":{"position":[[649,3]]},"2692":{"position":[[819,5]]},"2702":{"position":[[2374,5]]},"2736":{"position":[[74,3]]},"2782":{"position":[[8,3],[375,6]]},"2806":{"position":[[155,3],[737,3],[1062,3]]},"2818":{"position":[[692,3]]},"2830":{"position":[[1449,3]]},"2834":{"position":[[440,5]]},"2840":{"position":[[3,5]]},"2899":{"position":[[555,3]]},"2994":{"position":[[30,5]]},"3000":{"position":[[307,3]]},"3034":{"position":[[425,5]]},"3040":{"position":[[129,6]]},"3064":{"position":[[659,3]]},"3088":{"position":[[175,3]]},"3090":{"position":[[172,3]]},"3198":{"position":[[552,3]]},"3224":{"position":[[202,5]]},"3261":{"position":[[2692,5]]},"3271":{"position":[[753,5],[808,6]]},"3289":{"position":[[549,5]]},"3363":{"position":[[278,3],[493,3]]},"3381":{"position":[[4226,3],[4342,3]]},"3524":{"position":[[1331,3]]},"3557":{"position":[[248,3]]},"3580":{"position":[[1414,3]]},"3633":{"position":[[917,5]]},"3643":{"position":[[2206,3]]},"3647":{"position":[[1746,3]]}}}],["trial",{"_index":1519,"t":{"94":{"position":[[1594,5]]}}}],["trick",{"_index":1242,"t":{"56":{"position":[[168,6]]},"381":{"position":[[843,7]]}}}],["tricki",{"_index":2353,"t":{"206":{"position":[[168,6]]},"250":{"position":[[1671,6]]},"475":{"position":[[1739,6]]}}}],["trickier",{"_index":903,"t":{"28":{"position":[[1502,8]]}}}],["trigger",{"_index":3890,"t":{"710":{"position":[[217,9]]},"1477":{"position":[[373,9]]},"1887":{"position":[[220,9]]},"2670":{"position":[[373,9]]},"3078":{"position":[[220,9]]}}}],["trip",{"_index":2482,"t":{"240":{"position":[[281,4]]},"246":{"position":[[2069,6]]},"473":{"position":[[323,5]]}}}],["trivial",{"_index":2560,"t":{"250":{"position":[[251,7],[809,7]]},"377":{"position":[[598,7]]}}}],["troubleshoot",{"_index":4059,"t":{"922":{"position":[[327,16]]},"1997":{"position":[[327,16]]},"2889":{"position":[[366,16]]},"3212":{"position":[[327,16]]}}}],["true",{"_index":322,"t":{"8":{"position":[[517,5]]},"42":{"position":[[2330,5]]},"44":{"position":[[847,5],[864,5]]},"70":{"position":[[895,6]]},"110":{"position":[[3279,4],[3598,4],[4504,4]]},"132":{"position":[[644,5],[667,5],[692,4]]},"174":{"position":[[808,4]]},"176":{"position":[[192,5],[229,4]]},"180":{"position":[[1267,5],[1292,4]]},"186":{"position":[[310,5],[413,4]]},"206":{"position":[[430,4]]},"210":{"position":[[577,4],[990,5]]},"283":{"position":[[506,5],[620,4]]},"285":{"position":[[101,5],[275,5]]},"351":{"position":[[802,4]]},"385":{"position":[[362,4],[1246,4],[1283,5]]},"397":{"position":[[83,4]]},"399":{"position":[[713,4]]},"471":{"position":[[620,5]]},"473":{"position":[[2684,5]]},"505":{"position":[[589,5]]},"519":{"position":[[285,5],[379,4],[755,5],[929,4],[1027,5],[1175,4]]},"527":{"position":[[169,5]]},"560":{"position":[[819,4]]},"566":{"position":[[228,4]]},"568":{"position":[[143,4]]},"600":{"position":[[1025,5]]},"604":{"position":[[144,5],[371,5],[398,5]]},"612":{"position":[[279,5],[416,5],[443,5]]},"618":{"position":[[536,5],[608,5],[1430,5],[1473,5]]},"686":{"position":[[93,5],[624,5],[655,5]]},"692":{"position":[[751,5]]},"732":{"position":[[158,5],[175,5],[205,5],[223,5],[243,5],[303,5]]},"734":{"position":[[1224,5],[1241,5],[1259,5],[1279,5],[1374,5],[1393,5],[1453,4],[1494,5],[1514,4],[2168,4]]},"812":{"position":[[43,5]]},"818":{"position":[[56,5]]},"820":{"position":[[42,5]]},"822":{"position":[[55,5]]},"826":{"position":[[39,5]]},"828":{"position":[[46,5]]},"830":{"position":[[53,5]]},"832":{"position":[[50,5]]},"856":{"position":[[122,5]]},"858":{"position":[[477,5],[501,5]]},"878":{"position":[[408,4],[430,6]]},"922":{"position":[[155,5]]},"928":{"position":[[70,5],[134,5]]},"945":{"position":[[739,5]]},"963":{"position":[[543,5],[756,5],[1136,5],[1457,6]]},"965":{"position":[[1258,4],[2995,4]]},"1009":{"position":[[106,5]]},"1011":{"position":[[80,5]]},"1043":{"position":[[711,5],[752,5],[855,4]]},"1047":{"position":[[165,5]]},"1049":{"position":[[109,5],[268,5]]},"1067":{"position":[[1194,5]]},"1069":{"position":[[1394,5]]},"1071":{"position":[[1456,5],[1660,4],[2552,5]]},"1073":{"position":[[1302,5],[1325,5],[1515,5],[1538,4],[2691,5],[3055,4]]},"1079":{"position":[[1230,5],[1253,4],[1444,5]]},"1087":{"position":[[860,5]]},"1091":{"position":[[281,5]]},"1093":{"position":[[281,5]]},"1095":{"position":[[200,5],[508,5]]},"1097":{"position":[[329,5],[435,5],[543,5]]},"1099":{"position":[[116,5]]},"1137":{"position":[[636,5]]},"1164":{"position":[[1250,4]]},"1168":{"position":[[1070,4]]},"1172":{"position":[[730,5]]},"1174":{"position":[[255,4]]},"1180":{"position":[[306,4]]},"1184":{"position":[[726,4],[940,5],[1128,4]]},"1188":{"position":[[379,4]]},"1203":{"position":[[110,5]]},"1207":{"position":[[286,5],[322,5]]},"1220":{"position":[[105,5]]},"1231":{"position":[[93,5]]},"1258":{"position":[[103,5]]},"1262":{"position":[[389,5]]},"1283":{"position":[[647,4]]},"1319":{"position":[[101,4]]},"1339":{"position":[[802,4]]},"1367":{"position":[[589,5]]},"1435":{"position":[[169,5]]},"1475":{"position":[[1160,4]]},"1495":{"position":[[1269,4],[1401,5],[7904,5],[8151,5]]},"1499":{"position":[[1907,5]]},"1507":{"position":[[237,4]]},"1521":{"position":[[952,4]]},"1533":{"position":[[144,5],[371,5],[401,5],[428,5],[457,5],[487,5]]},"1547":{"position":[[350,5],[487,5],[517,5],[546,5],[573,5]]},"1567":{"position":[[75,5]]},"1613":{"position":[[105,4]]},"1619":{"position":[[1307,5]]},"1656":{"position":[[1387,4],[1451,4],[1515,4]]},"1676":{"position":[[938,5]]},"1678":{"position":[[834,5]]},"1680":{"position":[[1280,5],[2121,5],[2152,5]]},"1682":{"position":[[1459,5]]},"1723":{"position":[[122,5]]},"1725":{"position":[[477,5],[501,5]]},"1737":{"position":[[83,5],[606,5],[637,5]]},"1749":{"position":[[98,4]]},"1751":{"position":[[102,4]]},"1753":{"position":[[135,4]]},"1833":{"position":[[52,4]]},"1855":{"position":[[48,4]]},"1873":{"position":[[736,5]]},"1881":{"position":[[1254,5],[2055,4]]},"1923":{"position":[[682,5]]},"1937":{"position":[[391,5],[458,5],[494,5],[533,5],[571,5],[608,5],[646,5],[683,5],[722,5],[760,5],[1178,5],[1245,5],[1281,5],[1320,5],[1358,5],[1395,5],[1433,5],[1470,5],[1509,5],[1547,4]]},"1945":{"position":[[408,4],[430,6]]},"1997":{"position":[[155,5]]},"2003":{"position":[[70,5],[134,5]]},"2020":{"position":[[739,5]]},"2030":{"position":[[677,4]]},"2090":{"position":[[106,5]]},"2092":{"position":[[80,5]]},"2100":{"position":[[543,5],[756,5],[1136,5],[1457,6]]},"2102":{"position":[[1258,4],[2945,4]]},"2118":{"position":[[66,4],[180,4]]},"2134":{"position":[[723,5],[764,5],[867,4]]},"2148":{"position":[[1194,5]]},"2150":{"position":[[1394,5]]},"2152":{"position":[[1456,5],[1660,4],[2552,5]]},"2154":{"position":[[1302,5],[1325,5],[1515,5],[1538,4],[2691,5],[3055,4]]},"2156":{"position":[[1550,5],[1723,4],[2663,5]]},"2162":{"position":[[1230,5],[1253,4],[1444,5]]},"2170":{"position":[[860,5]]},"2174":{"position":[[281,5]]},"2176":{"position":[[281,5]]},"2178":{"position":[[200,5],[508,5]]},"2180":{"position":[[366,5],[472,5],[580,5]]},"2182":{"position":[[116,5]]},"2218":{"position":[[636,5]]},"2228":{"position":[[165,5]]},"2230":{"position":[[109,5],[268,5]]},"2240":{"position":[[1250,5]]},"2244":{"position":[[940,4]]},"2269":{"position":[[194,5]]},"2301":{"position":[[189,5]]},"2310":{"position":[[93,5]]},"2335":{"position":[[110,5]]},"2339":{"position":[[286,5],[322,5]]},"2392":{"position":[[105,5]]},"2409":{"position":[[103,5]]},"2413":{"position":[[389,5]]},"2430":{"position":[[647,4]]},"2434":{"position":[[489,5],[502,5],[577,5]]},"2466":{"position":[[101,4]]},"2486":{"position":[[802,4]]},"2618":{"position":[[169,5]]},"2630":{"position":[[1907,5]]},"2638":{"position":[[237,4]]},"2662":{"position":[[1284,4],[1416,5],[8001,5],[8248,5]]},"2668":{"position":[[1160,4]]},"2690":{"position":[[952,4]]},"2702":{"position":[[144,5],[371,5],[401,5],[428,5],[457,5],[487,5]]},"2716":{"position":[[350,5],[487,5],[517,5],[546,5],[573,5]]},"2722":{"position":[[1201,5]]},"2726":{"position":[[75,5]]},"2738":{"position":[[105,4]]},"2744":{"position":[[498,5]]},"2746":{"position":[[93,5]]},"2774":{"position":[[609,5],[668,5]]},"2806":{"position":[[1307,5]]},"2830":{"position":[[941,5]]},"2832":{"position":[[840,5]]},"2834":{"position":[[1286,5],[2127,5],[2158,5]]},"2836":{"position":[[1459,5]]},"2862":{"position":[[535,4]]},"2873":{"position":[[1600,4],[1667,4]]},"2912":{"position":[[122,5]]},"2914":{"position":[[541,5],[565,5]]},"2926":{"position":[[83,5],[606,5],[637,5]]},"2938":{"position":[[98,4]]},"2940":{"position":[[102,4]]},"2942":{"position":[[135,4]]},"3018":{"position":[[58,4],[471,5]]},"3024":{"position":[[52,4]]},"3046":{"position":[[48,4]]},"3072":{"position":[[1542,5],[2343,4]]},"3116":{"position":[[682,5]]},"3134":{"position":[[391,5],[458,5],[494,5],[533,5],[571,5],[608,5],[646,5],[683,5],[722,5],[760,5],[1178,5],[1245,5],[1281,5],[1320,5],[1358,5],[1395,5],[1433,5],[1470,5],[1509,5],[1547,4]]},"3158":{"position":[[408,4],[430,6]]},"3212":{"position":[[155,5]]},"3220":{"position":[[70,5],[134,5]]},"3239":{"position":[[739,5]]},"3259":{"position":[[543,5],[756,5],[1136,5],[1457,6]]},"3261":{"position":[[1258,4],[2945,4]]},"3271":{"position":[[643,4]]},"3299":{"position":[[106,5]]},"3301":{"position":[[80,5]]},"3318":{"position":[[106,5]]},"3320":{"position":[[80,5]]},"3327":{"position":[[140,5],[167,5]]},"3333":{"position":[[66,4],[194,4]]},"3337":{"position":[[309,5],[329,5],[360,4]]},"3349":{"position":[[1141,4]]},"3351":{"position":[[935,5],[981,4]]},"3353":{"position":[[248,5]]},"3363":{"position":[[723,5],[764,5],[867,4]]},"3377":{"position":[[1194,5]]},"3379":{"position":[[1441,5]]},"3381":{"position":[[1259,5],[1463,4],[2355,5]]},"3383":{"position":[[1302,5],[1325,5],[1515,5],[1538,4],[2691,5],[3055,4]]},"3385":{"position":[[1523,5],[1696,4],[2636,5]]},"3391":{"position":[[1230,5],[1253,4],[1444,5]]},"3393":{"position":[[504,4]]},"3399":{"position":[[860,5]]},"3403":{"position":[[281,5]]},"3405":{"position":[[281,5],[2302,4]]},"3407":{"position":[[200,5],[508,5]]},"3409":{"position":[[366,5],[472,5],[580,5]]},"3411":{"position":[[116,5]]},"3415":{"position":[[165,5]]},"3417":{"position":[[109,5],[268,5]]},"3459":{"position":[[636,5]]},"3476":{"position":[[194,5]]},"3518":{"position":[[189,5]]},"3531":{"position":[[93,5]]},"3551":{"position":[[1250,5]]},"3555":{"position":[[940,4]]},"3576":{"position":[[103,5]]},"3580":{"position":[[389,5]]},"3591":{"position":[[110,5]]},"3595":{"position":[[286,5],[322,5]]},"3606":{"position":[[105,5]]},"3612":{"position":[[516,5],[529,5],[604,5]]},"3625":{"position":[[647,4]]}}}],["true/fals",{"_index":3944,"t":{"758":{"position":[[1837,11]]},"1071":{"position":[[3766,11]]},"1113":{"position":[[1301,11]]},"1777":{"position":[[1364,11]]},"1823":{"position":[[578,11]]},"2152":{"position":[[3797,11]]},"2194":{"position":[[1365,11]]},"2966":{"position":[[1442,11]]},"3012":{"position":[[578,11]]},"3381":{"position":[[3800,11]]},"3435":{"position":[[1433,11]]}}}],["true});console.log(resp.publ",{"_index":3519,"t":{"511":{"position":[[779,38]]},"1373":{"position":[[779,38]]},"2379":{"position":[[779,38]]},"2524":{"position":[[779,38]]},"3667":{"position":[[779,38]]}}}],["trust",{"_index":538,"t":{"14":{"position":[[308,7]]},"94":{"position":[[3201,5]]},"100":{"position":[[97,5],[185,5],[587,5]]},"339":{"position":[[1568,5]]},"485":{"position":[[1195,5]]},"1327":{"position":[[1568,5]]},"2474":{"position":[[1568,5]]}}}],["truth",{"_index":2965,"t":{"377":{"position":[[1318,5]]},"961":{"position":[[449,5],[1330,5]]},"1164":{"position":[[79,6]]},"2098":{"position":[[491,5]]},"2238":{"position":[[332,6]]},"3257":{"position":[[491,5]]},"3549":{"position":[[332,6]]}}}],["ttl",{"_index":2293,"t":{"194":{"position":[[215,3]]},"200":{"position":[[231,3],[291,3],[629,3],[700,5],[1284,3]]},"433":{"position":[[143,6]]},"714":{"position":[[306,3]]},"764":{"position":[[868,3],[901,3],[1067,3]]},"868":{"position":[[200,3]]},"1166":{"position":[[1389,3]]},"1168":{"position":[[537,3]]},"1172":{"position":[[632,4],[786,3],[907,3],[983,3]]},"1207":{"position":[[507,3]]},"1262":{"position":[[554,3]]},"1417":{"position":[[143,6]]},"1495":{"position":[[6310,3]]},"1577":{"position":[[231,3]]},"1666":{"position":[[1318,3]]},"1783":{"position":[[868,3],[901,3],[1067,3]]},"1827":{"position":[[266,3]]},"1893":{"position":[[306,3]]},"2026":{"position":[[759,3]]},"2030":{"position":[[1749,3]]},"2066":{"position":[[200,3]]},"2068":{"position":[[250,3]]},"2339":{"position":[[507,3]]},"2413":{"position":[[563,3]]},"2574":{"position":[[144,6]]},"2662":{"position":[[6379,3]]},"2770":{"position":[[231,3]]},"2883":{"position":[[1477,3]]},"2972":{"position":[[855,3],[1015,3]]},"3016":{"position":[[266,3]]},"3084":{"position":[[306,3]]},"3086":{"position":[[761,3]]},"3144":{"position":[[200,3]]},"3146":{"position":[[250,3]]},"3267":{"position":[[759,3]]},"3271":{"position":[[1715,3]]},"3580":{"position":[[563,3]]},"3595":{"position":[[507,3]]}}}],["tunabl",{"_index":3433,"t":{"481":{"position":[[5547,7]]}}}],["tune",{"_index":403,"t":{"10":{"position":[[330,4],[2052,4]]},"16":{"position":[[41,4]]},"200":{"position":[[209,4]]},"277":{"position":[[1025,6]]},"385":{"position":[[1015,4]]},"459":{"position":[[212,6]]},"525":{"position":[[502,6]]},"614":{"position":[[612,4]]},"951":{"position":[[756,6]]},"953":{"position":[[762,6]]},"1353":{"position":[[1131,6]]},"1433":{"position":[[504,6]]},"1459":{"position":[[212,6]]},"1583":{"position":[[1255,6]]},"1983":{"position":[[42,6]]},"2078":{"position":[[756,6]]},"2080":{"position":[[762,6]]},"2546":{"position":[[1192,6]]},"2600":{"position":[[220,6]]},"2616":{"position":[[508,6]]},"2720":{"position":[[565,6]]},"2778":{"position":[[1353,6]]},"2889":{"position":[[1125,6]]},"3196":{"position":[[42,6]]},"3245":{"position":[[756,6]]},"3247":{"position":[[762,6]]}}}],["tupl",{"_index":448,"t":{"10":{"position":[[1435,6]]},"951":{"position":[[400,6]]},"2078":{"position":[[400,6]]},"3245":{"position":[[400,6]]}}}],["ture",{"_index":3026,"t":{"393":{"position":[[454,6]]}}}],["turn",{"_index":755,"t":{"18":{"position":[[619,4],[709,6]]},"44":{"position":[[263,4]]},"78":{"position":[[520,4]]},"174":{"position":[[1237,4]]},"214":{"position":[[81,5]]},"289":{"position":[[161,5]]},"351":{"position":[[848,4]]},"377":{"position":[[1618,6]]},"385":{"position":[[1376,6]]},"431":{"position":[[17,4]]},"433":{"position":[[29,7]]},"477":{"position":[[2480,6]]},"481":{"position":[[241,6]]},"483":{"position":[[1332,6]]},"704":{"position":[[411,4]]},"710":{"position":[[392,6]]},"714":{"position":[[385,4]]},"718":{"position":[[570,6],[636,6]]},"724":{"position":[[43,5]]},"726":{"position":[[41,5]]},"728":{"position":[[44,5]]},"730":{"position":[[42,5]]},"756":{"position":[[561,4]]},"758":{"position":[[880,4]]},"975":{"position":[[626,5]]},"1043":{"position":[[237,5],[630,6]]},"1073":{"position":[[1031,4]]},"1087":{"position":[[768,4]]},"1172":{"position":[[17,4]]},"1207":{"position":[[157,4]]},"1339":{"position":[[848,4]]},"1417":{"position":[[29,7]]},"1499":{"position":[[811,4],[1823,4]]},"1567":{"position":[[96,4]]},"1741":{"position":[[1880,7]]},"1749":{"position":[[19,4]]},"1751":{"position":[[19,4]]},"1823":{"position":[[618,4]]},"1869":{"position":[[980,4]]},"1887":{"position":[[775,6]]},"1893":{"position":[[385,4]]},"1897":{"position":[[923,6],[989,6]]},"1899":{"position":[[196,4],[255,7]]},"1901":{"position":[[57,4]]},"1907":{"position":[[55,4]]},"1913":{"position":[[55,4]]},"1919":{"position":[[56,4]]},"1925":{"position":[[43,5]]},"1927":{"position":[[41,5]]},"1929":{"position":[[45,5]]},"1931":{"position":[[44,5]]},"1933":{"position":[[42,5]]},"1935":{"position":[[46,5]]},"1937":{"position":[[79,7]]},"2030":{"position":[[442,5]]},"2134":{"position":[[237,5],[630,6]]},"2154":{"position":[[1031,4]]},"2170":{"position":[[768,4]]},"2339":{"position":[[157,4]]},"2486":{"position":[[848,4]]},"2574":{"position":[[30,7]]},"2630":{"position":[[811,4],[1823,4]]},"2726":{"position":[[96,4]]},"2930":{"position":[[1880,7]]},"2938":{"position":[[19,4]]},"2940":{"position":[[19,4]]},"3012":{"position":[[618,4]]},"3078":{"position":[[775,6]]},"3084":{"position":[[385,4]]},"3090":{"position":[[923,6],[989,6]]},"3092":{"position":[[196,4],[255,7]]},"3094":{"position":[[57,4]]},"3100":{"position":[[55,4]]},"3106":{"position":[[55,4]]},"3112":{"position":[[56,4]]},"3118":{"position":[[43,5]]},"3120":{"position":[[41,5]]},"3122":{"position":[[45,5]]},"3124":{"position":[[50,5]]},"3126":{"position":[[44,5]]},"3128":{"position":[[42,5]]},"3130":{"position":[[46,5]]},"3132":{"position":[[51,5]]},"3134":{"position":[[79,7]]},"3271":{"position":[[442,5]]},"3327":{"position":[[206,6],[1001,4]]},"3337":{"position":[[222,6]]},"3363":{"position":[[237,5],[630,6]]},"3383":{"position":[[1031,4]]},"3399":{"position":[[768,4]]},"3467":{"position":[[1536,6]]},"3595":{"position":[[157,4]]},"3629":{"position":[[78,5]]}}}],["tutori",{"_index":1246,"t":{"58":{"position":[[275,10]]},"118":{"position":[[8,9],[452,8],[558,8],[743,8],[783,8]]},"122":{"position":[[110,8],[152,9],[240,9],[533,8]]},"126":{"position":[[358,9]]},"134":{"position":[[417,8],[444,8]]},"136":{"position":[[972,8],[1177,9],[2019,8],[2141,8]]},"138":{"position":[[83,8]]},"142":{"position":[[758,9]]},"144":{"position":[[8,9],[331,8],[437,8]]},"170":{"position":[[278,9]]},"172":{"position":[[440,8]]},"224":{"position":[[218,9]]},"256":{"position":[[886,10]]},"279":{"position":[[88,8],[482,8],[588,8],[1560,8]]},"281":{"position":[[568,8]]},"287":{"position":[[642,9]]},"584":{"position":[[105,10]]},"1065":{"position":[[5564,8],[5772,8]]},"1495":{"position":[[11,8],[31,8],[3368,9],[6617,9]]},"1795":{"position":[[537,8]]},"2146":{"position":[[5687,8],[5895,8]]},"2434":{"position":[[1324,8]]},"2662":{"position":[[11,8],[31,9],[3385,9],[6694,9]]},"2984":{"position":[[537,8]]},"3349":{"position":[[1844,9]]},"3375":{"position":[[5716,8],[5924,8]]},"3612":{"position":[[1351,8]]}}}],["tweak",{"_index":2202,"t":{"170":{"position":[[248,7]]},"900":{"position":[[20,8]]},"1967":{"position":[[20,8]]},"2296":{"position":[[862,5]]},"2347":{"position":[[248,6]]},"2363":{"position":[[286,5]]},"3086":{"position":[[934,8]]},"3180":{"position":[[20,8]]},"3486":{"position":[[862,5]]},"3635":{"position":[[248,6]]},"3651":{"position":[[286,5]]}}}],["twemproxi",{"_index":3379,"t":{"481":{"position":[[883,9]]}}}],["twice",{"_index":1229,"t":{"54":{"position":[[58,5]]},"367":{"position":[[499,5]]},"1182":{"position":[[1622,5],[1773,5]]},"1192":{"position":[[86,6]]},"1387":{"position":[[499,5]]},"2250":{"position":[[1622,5],[1773,5]]},"2252":{"position":[[86,6]]},"2538":{"position":[[510,5]]},"3561":{"position":[[1622,5],[1773,5]]},"3563":{"position":[[86,6]]}}}],["twitter",{"_index":4669,"t":{"1349":{"position":[[389,7]]},"2494":{"position":[[389,7]]},"3347":{"position":[[1080,7]]}}}],["two",{"_index":838,"t":{"24":{"position":[[265,4]]},"32":{"position":[[133,3]]},"44":{"position":[[1826,3]]},"46":{"position":[[563,3]]},"110":{"position":[[492,3]]},"118":{"position":[[127,3]]},"214":{"position":[[1287,3]]},"264":{"position":[[1048,3]]},"379":{"position":[[1964,3]]},"381":{"position":[[428,3]]},"469":{"position":[[1485,3]]},"477":{"position":[[5749,3]]},"646":{"position":[[296,3]]},"664":{"position":[[210,3]]},"768":{"position":[[337,3]]},"912":{"position":[[251,3]]},"965":{"position":[[1845,3]]},"977":{"position":[[1661,3]]},"1049":{"position":[[1271,3]]},"1093":{"position":[[180,3]]},"1107":{"position":[[967,3]]},"1164":{"position":[[2963,3]]},"1188":{"position":[[149,3]]},"1475":{"position":[[472,3]]},"1629":{"position":[[1121,3]]},"1676":{"position":[[451,3]]},"1680":{"position":[[211,3],[275,3]]},"1692":{"position":[[296,3]]},"1708":{"position":[[210,3]]},"1787":{"position":[[337,3]]},"1797":{"position":[[344,3]]},"1879":{"position":[[316,3]]},"1893":{"position":[[678,3]]},"1981":{"position":[[251,3]]},"2034":{"position":[[1661,3]]},"2102":{"position":[[2017,3],[2310,3]]},"2176":{"position":[[180,3]]},"2188":{"position":[[970,3]]},"2230":{"position":[[1271,3]]},"2258":{"position":[[37,3]]},"2355":{"position":[[444,3]]},"2361":{"position":[[551,3]]},"2530":{"position":[[606,3]]},"2668":{"position":[[472,3]]},"2744":{"position":[[928,3]]},"2774":{"position":[[474,3]]},"2816":{"position":[[296,3]]},"2830":{"position":[[454,3]]},"2834":{"position":[[211,3],[275,3]]},"2846":{"position":[[1121,3]]},"2897":{"position":[[210,3]]},"2976":{"position":[[481,3]]},"2986":{"position":[[310,3]]},"3084":{"position":[[678,3]]},"3194":{"position":[[251,3]]},"3261":{"position":[[2017,3],[2310,3]]},"3275":{"position":[[1661,3]]},"3333":{"position":[[560,3]]},"3405":{"position":[[180,3]]},"3417":{"position":[[1271,3]]},"3455":{"position":[[265,3]]},"3492":{"position":[[37,3]]},"3629":{"position":[[1288,3]]},"3643":{"position":[[444,3]]},"3649":{"position":[[551,3]]}}}],["tx",{"_index":152,"t":{"2":{"position":[[2350,2]]}}}],["type",{"_index":653,"t":{"16":{"position":[[3310,4]]},"46":{"position":[[688,4],[1667,4],[2023,4]]},"108":{"position":[[1655,4]]},"118":{"position":[[159,4]]},"126":{"position":[[114,4]]},"128":{"position":[[63,4],[778,4],[3092,4]]},"130":{"position":[[2567,4],[2734,4]]},"132":{"position":[[1787,5]]},"136":{"position":[[2506,4]]},"204":{"position":[[99,6],[268,5],[1406,5]]},"254":{"position":[[711,4]]},"289":{"position":[[494,6]]},"383":{"position":[[123,5]]},"395":{"position":[[270,6],[363,4]]},"425":{"position":[[190,5]]},"473":{"position":[[3173,5]]},"475":{"position":[[3346,4]]},"477":{"position":[[1051,5],[1232,4],[1566,5]]},"479":{"position":[[5914,6]]},"485":{"position":[[481,4]]},"519":{"position":[[581,4]]},"598":{"position":[[536,4]]},"646":{"position":[[300,5]]},"652":{"position":[[440,5],[697,4]]},"654":{"position":[[317,5],[617,4]]},"658":{"position":[[252,9]]},"664":{"position":[[214,5]]},"670":{"position":[[32,5],[279,4]]},"672":{"position":[[32,5],[258,4]]},"680":{"position":[[266,5],[505,4]]},"682":{"position":[[173,5],[1094,4],[1220,4],[1353,4]]},"684":{"position":[[131,5],[370,4]]},"696":{"position":[[1269,4]]},"758":{"position":[[1103,4],[1600,4]]},"965":{"position":[[3076,4]]},"1059":{"position":[[275,4]]},"1061":{"position":[[695,4]]},"1065":{"position":[[2383,4],[2596,4],[2686,4],[3452,4],[5094,6]]},"1067":{"position":[[679,4],[893,4],[979,4],[1229,4]]},"1069":{"position":[[657,4],[859,4],[949,4],[1425,4]]},"1071":{"position":[[976,5],[1981,4],[2183,4],[2273,4],[2909,4],[3529,4]]},"1073":{"position":[[2015,4],[2215,4],[2305,4],[2726,4]]},"1079":{"position":[[377,5]]},"1085":{"position":[[216,4],[227,4]]},"1089":{"position":[[146,4],[316,5],[808,5]]},"1093":{"position":[[1090,4]]},"1105":{"position":[[144,4]]},"1109":{"position":[[540,6],[1052,5],[1242,5],[1644,5],[1909,4],[2326,4]]},"1111":{"position":[[223,4],[676,4]]},"1113":{"position":[[91,4],[1064,4]]},"1115":{"position":[[186,4]]},"1117":{"position":[[149,4],[588,4]]},"1119":{"position":[[140,4]]},"1121":{"position":[[445,5],[792,4],[909,4],[1063,4]]},"1123":{"position":[[373,5],[528,4],[651,4]]},"1125":{"position":[[710,5],[966,4],[1393,4],[1520,4]]},"1127":{"position":[[406,5],[527,4]]},"1129":{"position":[[157,4],[262,4],[421,4]]},"1131":{"position":[[216,5],[550,4]]},"1162":{"position":[[2206,5]]},"1164":{"position":[[3475,4],[3816,5]]},"1168":{"position":[[1455,6]]},"1176":{"position":[[18,5],[1361,5],[1403,4]]},"1200":{"position":[[39,5]]},"1245":{"position":[[246,6]]},"1249":{"position":[[93,4]]},"1251":{"position":[[17,4]]},"1270":{"position":[[422,4],[562,4],[663,6]]},"1481":{"position":[[235,4]]},"1485":{"position":[[45,5]]},"1495":{"position":[[8241,5],[9823,5]]},"1523":{"position":[[409,4]]},"1543":{"position":[[449,6],[1423,6]]},"1563":{"position":[[536,4]]},"1577":{"position":[[1132,5]]},"1579":{"position":[[116,4],[269,4],[448,4],[828,4],[1212,4],[1333,4],[1503,4]]},"1650":{"position":[[72,4],[859,4]]},"1652":{"position":[[152,4],[625,4],[703,4],[794,4],[889,4]]},"1654":{"position":[[162,4]]},"1656":{"position":[[114,4],[951,4],[1117,4]]},"1658":{"position":[[71,4]]},"1660":{"position":[[63,4],[878,4],[1067,4]]},"1662":{"position":[[200,4]]},"1664":{"position":[[59,4],[448,4],[608,4]]},"1666":{"position":[[277,4],[491,4],[1093,4],[1504,4],[1616,4],[1738,4],[1887,4]]},"1668":{"position":[[831,4]]},"1670":{"position":[[222,5],[690,5]]},"1678":{"position":[[8,4],[256,4],[308,4]]},"1680":{"position":[[9,4],[679,4]]},"1686":{"position":[[252,9]]},"1692":{"position":[[300,5]]},"1699":{"position":[[440,5],[697,4]]},"1702":{"position":[[317,5],[617,4]]},"1708":{"position":[[214,5]]},"1715":{"position":[[32,5],[279,4]]},"1717":{"position":[[32,5],[258,4]]},"1731":{"position":[[266,5],[505,4]]},"1733":{"position":[[173,5],[1094,4],[1220,4],[1353,4]]},"1735":{"position":[[131,5],[370,4]]},"1743":{"position":[[476,6]]},"1777":{"position":[[630,4],[1127,4]]},"1823":{"position":[[167,4]]},"1865":{"position":[[1649,5]]},"1875":{"position":[[962,4]]},"1885":{"position":[[597,5]]},"1893":{"position":[[778,5]]},"2118":{"position":[[304,5]]},"2140":{"position":[[275,4]]},"2142":{"position":[[695,4]]},"2146":{"position":[[2500,4],[2713,4],[2803,4],[3569,4],[5217,6]]},"2148":{"position":[[679,4],[893,4],[979,4],[1229,4]]},"2150":{"position":[[657,4],[859,4],[949,4],[1425,4]]},"2152":{"position":[[976,5],[1981,4],[2183,4],[2273,4],[2841,4],[3461,4]]},"2154":{"position":[[2015,4],[2215,4],[2305,4],[2726,4]]},"2156":{"position":[[456,6],[2080,4],[2294,4],[2380,4],[2702,4]]},"2162":{"position":[[377,5]]},"2168":{"position":[[216,4],[227,4]]},"2172":{"position":[[120,4],[290,5],[782,5]]},"2176":{"position":[[1090,4]]},"2186":{"position":[[144,4]]},"2190":{"position":[[600,6],[1112,5],[1302,5],[1698,5],[1963,4],[2610,4]]},"2192":{"position":[[223,4],[906,4]]},"2194":{"position":[[91,4],[1029,4]]},"2196":{"position":[[186,4]]},"2198":{"position":[[149,4],[554,4]]},"2200":{"position":[[140,4]]},"2202":{"position":[[488,5],[835,4],[952,4],[1106,4]]},"2204":{"position":[[373,5],[528,4],[651,4]]},"2206":{"position":[[710,5],[966,4],[1393,4],[1520,4]]},"2208":{"position":[[406,5],[527,4]]},"2210":{"position":[[157,4],[309,4],[468,4]]},"2212":{"position":[[216,5],[550,4]]},"2240":{"position":[[1457,5]]},"2242":{"position":[[12,4],[337,5]]},"2294":{"position":[[1118,4],[1301,4]]},"2324":{"position":[[230,6]]},"2332":{"position":[[39,5]]},"2351":{"position":[[941,6]]},"2355":{"position":[[925,4]]},"2367":{"position":[[923,6]]},"2371":{"position":[[514,5]]},"2400":{"position":[[93,4]]},"2402":{"position":[[17,4]]},"2662":{"position":[[8343,5],[9979,5]]},"2674":{"position":[[235,4]]},"2678":{"position":[[45,5]]},"2692":{"position":[[409,4]]},"2712":{"position":[[449,6],[1423,6]]},"2720":{"position":[[106,5],[128,4],[357,5],[379,5],[638,5],[660,4]]},"2722":{"position":[[1519,7]]},"2750":{"position":[[6,4]]},"2752":{"position":[[11,4]]},"2766":{"position":[[536,4]]},"2770":{"position":[[1132,5]]},"2772":{"position":[[116,4],[269,4],[448,4],[828,4],[1212,4],[1333,4],[1503,4]]},"2810":{"position":[[252,9]]},"2816":{"position":[[300,5]]},"2823":{"position":[[440,5],[661,4]]},"2826":{"position":[[317,5],[581,4]]},"2832":{"position":[[8,4],[256,4],[308,4]]},"2834":{"position":[[9,4],[679,4]]},"2867":{"position":[[72,4],[766,4]]},"2869":{"position":[[152,4],[484,4],[562,4],[655,4]]},"2871":{"position":[[162,4]]},"2873":{"position":[[114,4],[640,4],[1035,4],[1299,4]]},"2875":{"position":[[71,4]]},"2877":{"position":[[63,4],[518,4],[1002,4],[1285,4]]},"2879":{"position":[[200,4]]},"2881":{"position":[[59,4],[395,4],[631,4],[763,4]]},"2883":{"position":[[277,4],[752,4],[1290,4],[1783,4],[1895,4],[2017,4],[2166,4]]},"2885":{"position":[[111,4]]},"2887":{"position":[[831,4]]},"2889":{"position":[[121,5],[420,5],[687,5],[934,5]]},"2897":{"position":[[214,5]]},"2904":{"position":[[32,5],[243,4]]},"2906":{"position":[[32,5],[222,4]]},"2920":{"position":[[266,5],[469,4]]},"2922":{"position":[[173,5],[1058,4],[1184,4],[1317,4]]},"2924":{"position":[[131,5],[334,4]]},"2932":{"position":[[476,6]]},"2966":{"position":[[708,4],[1205,4]]},"2984":{"position":[[979,5]]},"3012":{"position":[[167,4]]},"3056":{"position":[[1649,5]]},"3066":{"position":[[955,4]]},"3076":{"position":[[597,5]]},"3084":{"position":[[778,5]]},"3324":{"position":[[101,5],[123,4],[355,5],[377,4],[591,5],[789,5],[987,5],[1175,5],[1385,5],[1587,5],[1793,5],[1990,5],[2218,5],[2442,5],[2684,5],[2902,5],[3122,5],[3427,5],[3673,5],[3963,5],[4251,5],[4552,5],[4853,5],[4897,4],[5096,5],[5128,4],[5344,5],[5378,5],[5580,5],[5602,5],[5811,5],[6029,5]]},"3333":{"position":[[318,5],[671,5]]},"3349":{"position":[[114,4]]},"3353":{"position":[[101,6]]},"3369":{"position":[[280,4]]},"3371":{"position":[[695,4]]},"3375":{"position":[[2500,4],[2713,4],[2803,4],[3569,4],[5246,6]]},"3377":{"position":[[679,4],[893,4],[979,4],[1229,4]]},"3379":{"position":[[704,4],[906,4],[996,4],[1472,4]]},"3381":{"position":[[779,5],[1784,4],[1986,4],[2076,4],[2644,4],[3464,4]]},"3383":{"position":[[2015,4],[2215,4],[2305,4],[2726,4]]},"3385":{"position":[[429,6],[2053,4],[2267,4],[2353,4],[2675,4]]},"3387":{"position":[[500,6]]},"3389":{"position":[[1034,6]]},"3391":{"position":[[377,5]]},"3397":{"position":[[216,4],[227,4]]},"3401":{"position":[[120,4],[290,5],[782,5]]},"3405":{"position":[[1090,4]]},"3425":{"position":[[181,5]]},"3431":{"position":[[693,6],[1471,4],[2118,4]]},"3433":{"position":[[298,4],[981,4]]},"3435":{"position":[[159,4],[1097,4]]},"3437":{"position":[[92,4]]},"3439":{"position":[[83,4],[488,4]]},"3441":{"position":[[133,4]]},"3443":{"position":[[673,4],[790,4],[944,4]]},"3445":{"position":[[369,4],[492,4]]},"3447":{"position":[[808,4],[1235,4],[1362,4]]},"3449":{"position":[[213,4]]},"3451":{"position":[[217,4],[369,4],[528,4]]},"3453":{"position":[[367,4]]},"3467":{"position":[[1003,5],[1410,5]]},"3524":{"position":[[1118,4],[1301,4]]},"3551":{"position":[[1457,5]]},"3553":{"position":[[12,4],[337,5]]},"3567":{"position":[[93,4]]},"3569":{"position":[[17,4]]},"3588":{"position":[[39,5]]},"3643":{"position":[[925,4]]},"3655":{"position":[[985,6]]},"3659":{"position":[[514,5]]}}}],["type\":1",{"_index":4587,"t":{"1176":{"position":[[581,9]]}}}],["type\":2",{"_index":4588,"t":{"1176":{"position":[[1001,9]]}}}],["type\":3",{"_index":4589,"t":{"1176":{"position":[[1274,9]]}}}],["type\":6,\"data\":{\"client\":\"8ceaa299",{"_index":4643,"t":{"1262":{"position":[[1013,36]]}}}],["type=\"button",{"_index":2829,"t":{"301":{"position":[[1884,13],[1971,13]]}}}],["type=\"password",{"_index":2697,"t":{"283":{"position":[[1283,15]]}}}],["type=\"submit",{"_index":2700,"t":{"283":{"position":[[1373,13]]}}}],["type=\"text",{"_index":1858,"t":{"128":{"position":[[710,11]]},"130":{"position":[[738,11]]},"283":{"position":[[1171,11]]}}}],["type=\"text/javascript",{"_index":4726,"t":{"1495":{"position":[[2290,23]]},"2662":{"position":[[2305,23]]}}}],["typescript",{"_index":3031,"t":{"395":{"position":[[160,10]]}}}],["typic",{"_index":1370,"t":{"74":{"position":[[547,7]]},"214":{"position":[[656,7]]},"363":{"position":[[1042,7]]},"477":{"position":[[3395,7]]},"1383":{"position":[[1193,7]]},"1625":{"position":[[156,9]]},"2842":{"position":[[156,9]]},"2970":{"position":[[20,9]]},"3629":{"position":[[657,7]]}}}],["typo",{"_index":2383,"t":{"212":{"position":[[247,4],[361,4],[869,5]]}}}],["u",{"_index":1759,"t":{"110":{"position":[[4246,2]]},"483":{"position":[[485,1]]},"868":{"position":[[65,1],[274,1]]},"1207":{"position":[[450,1]]},"1262":{"position":[[491,1]]},"1495":{"position":[[6188,1],[6206,1]]},"1577":{"position":[[182,1]]},"1827":{"position":[[137,1]]},"2066":{"position":[[65,1],[274,1]]},"2068":{"position":[[71,1],[324,1]]},"2339":{"position":[[450,1]]},"2413":{"position":[[500,1]]},"2662":{"position":[[6257,1],[6275,1]]},"2770":{"position":[[182,1]]},"3016":{"position":[[137,1]]},"3144":{"position":[[65,1],[274,1]]},"3146":{"position":[[71,1],[324,1]]},"3580":{"position":[[500,1]]},"3595":{"position":[[450,1]]}}}],["uber",{"_index":3389,"t":{"481":{"position":[[2285,4]]}}}],["ubuntu",{"_index":3578,"t":{"531":{"position":[[271,6],[298,6],[325,6]]},"1439":{"position":[[294,6],[321,6],[348,6]]},"2622":{"position":[[247,6],[274,6],[306,6]]}}}],["ucubp27tybebk7z0oenwdskwcmre46fuejjnzi",{"_index":5397,"t":{"2413":{"position":[[1076,40]]},"3580":{"position":[[1076,40]]}}}],["udp",{"_index":1512,"t":{"94":{"position":[[934,3],[981,3]]},"108":{"position":[[759,3],[1018,3],[1110,3]]},"116":{"position":[[764,3]]}}}],["ui",{"_index":1401,"t":{"78":{"position":[[989,2]]},"206":{"position":[[365,2],[501,2]]},"208":{"position":[[271,3]]},"214":{"position":[[1362,3]]},"246":{"position":[[3162,2]]},"268":{"position":[[46,2]]},"270":{"position":[[291,3],[347,2]]},"277":{"position":[[615,3]]},"289":{"position":[[932,2]]},"299":{"position":[[1037,2]]},"301":{"position":[[4262,3]]},"435":{"position":[[28,2]]},"517":{"position":[[179,2]]},"640":{"position":[[127,3]]},"854":{"position":[[61,2]]},"920":{"position":[[10,2],[114,3],[141,2]]},"1262":{"position":[[1434,3]]},"1353":{"position":[[672,3]]},"1419":{"position":[[15,2]]},"1583":{"position":[[1363,3]]},"1617":{"position":[[127,3]]},"1721":{"position":[[61,2]]},"1827":{"position":[[429,2]]},"1995":{"position":[[10,2],[114,3],[141,2]]},"2413":{"position":[[1513,3]]},"2546":{"position":[[717,3]]},"2576":{"position":[[19,2]]},"2778":{"position":[[1543,3]]},"2804":{"position":[[127,3]]},"2910":{"position":[[61,2]]},"3016":{"position":[[429,2]]},"3210":{"position":[[10,2],[114,3],[141,2]]},"3216":{"position":[[64,2],[88,2]]},"3218":{"position":[[514,2],[573,2]]},"3327":{"position":[[879,3]]},"3580":{"position":[[1513,3]]},"3629":{"position":[[1363,3]]}}}],["uid",{"_index":3838,"t":{"652":{"position":[[573,7],[723,3]]},"1131":{"position":[[398,6]]},"1543":{"position":[[416,5],[1390,5]]},"1579":{"position":[[1359,3],[1529,3]]},"1666":{"position":[[1119,3],[1904,3],[1939,3]]},"1668":{"position":[[433,3],[857,3],[872,3]]},"1699":{"position":[[573,7],[723,3]]},"2212":{"position":[[398,6]]},"2712":{"position":[[416,5],[1390,5]]},"2772":{"position":[[1359,3],[1529,3]]},"2823":{"position":[[524,8],[687,3]]},"2883":{"position":[[422,3],[2183,3],[2218,3]]},"2885":{"position":[[137,3],[152,3]]},"2887":{"position":[[433,3],[857,3],[872,3]]},"3453":{"position":[[214,6]]}}}],["uint32",{"_index":3741,"t":{"608":{"position":[[429,7],[450,7],[1418,7],[1439,7]]},"1075":{"position":[[291,6]]},"1077":{"position":[[296,6]]},"1164":{"position":[[2422,6]]},"1166":{"position":[[509,6]]},"1168":{"position":[[530,6]]},"1539":{"position":[[420,7],[441,7],[1400,7],[1421,7]]},"2158":{"position":[[226,6]]},"2160":{"position":[[430,6]]},"2240":{"position":[[249,6]]},"2248":{"position":[[150,6]]},"2708":{"position":[[420,7],[441,7],[1400,7],[1421,7]]},"3387":{"position":[[226,6]]},"3389":{"position":[[430,6]]},"3551":{"position":[[249,6]]},"3559":{"position":[[150,6]]}}}],["uint64",{"_index":3742,"t":{"608":{"position":[[469,7],[1458,7]]},"961":{"position":[[1087,6]]},"1168":{"position":[[737,6]]},"1539":{"position":[[460,7],[1440,7]]},"1541":{"position":[[377,7],[1231,7]]},"2098":{"position":[[1153,6]]},"2294":{"position":[[1199,6]]},"2708":{"position":[[460,7],[1440,7]]},"2710":{"position":[[377,7],[1231,7]]},"3257":{"position":[[1153,6]]},"3524":{"position":[[1199,6]]}}}],["ul",{"_index":1939,"t":{"130":{"position":[[606,3]]}}}],["ulimit",{"_index":412,"t":{"10":{"position":[[523,6],[808,6]]},"525":{"position":[[114,6]]},"527":{"position":[[376,8]]},"590":{"position":[[97,6]]},"949":{"position":[[156,6]]},"1433":{"position":[[114,6]]},"1435":{"position":[[400,8]]},"1555":{"position":[[97,6]]},"2076":{"position":[[156,6]]},"2616":{"position":[[114,6]]},"2618":{"position":[[400,8]]},"2758":{"position":[[97,6]]},"3243":{"position":[[156,6]]}}}],["unabl",{"_index":869,"t":{"26":{"position":[[1467,6]]}}}],["unaccess",{"_index":3935,"t":{"756":{"position":[[236,12]]},"758":{"position":[[561,12]]}}}],["unappli",{"_index":1911,"t":{"128":{"position":[[2633,9]]}}}],["unari",{"_index":4340,"t":{"1079":{"position":[[476,5]]},"2162":{"position":[[476,5]]},"3391":{"position":[[476,5]]}}}],["unauthent",{"_index":3883,"t":{"704":{"position":[[370,15]]},"1065":{"position":[[3616,15]]},"2146":{"position":[[3733,15]]},"3375":{"position":[[3733,15]]}}}],["unauthenticated/unauthor",{"_index":5186,"t":{"2146":{"position":[[5960,28]]},"3375":{"position":[[5989,28]]}}}],["unauthor",{"_index":2226,"t":{"178":{"position":[[1061,12]]},"198":{"position":[[210,12]]},"287":{"position":[[1809,15]]},"784":{"position":[[20,15],[42,12],[76,13]]},"1835":{"position":[[18,14],[39,12],[73,13]]},"2146":{"position":[[6287,14]]},"3026":{"position":[[18,14],[39,12],[73,13]]},"3375":{"position":[[6316,14]]}}}],["unavail",{"_index":2350,"t":{"204":{"position":[[2076,11]]},"236":{"position":[[189,15]]},"604":{"position":[[2102,11]]},"614":{"position":[[425,11]]},"1162":{"position":[[608,11]]},"1533":{"position":[[2606,11]]},"1549":{"position":[[426,11]]},"1977":{"position":[[284,11]]},"2355":{"position":[[3627,12]]},"2702":{"position":[[2606,11]]},"2718":{"position":[[426,11]]},"2722":{"position":[[2421,11]]},"3190":{"position":[[257,11]]},"3643":{"position":[[3627,12]]}}}],["unbind",{"_index":652,"t":{"16":{"position":[[3303,6]]}}}],["unblock",{"_index":3853,"t":{"672":{"position":[[212,7],[311,7],[319,7]]},"1717":{"position":[[311,7]]},"2906":{"position":[[275,7]]}}}],["unblock_us",{"_index":3852,"t":{"672":{"position":[[139,15]]},"1717":{"position":[[139,15],[212,12],[319,12]]},"2906":{"position":[[176,12],[283,12]]}}}],["uncom",{"_index":4203,"t":{"1001":{"position":[[24,9]]},"1003":{"position":[[24,9]]},"2108":{"position":[[24,9]]},"2110":{"position":[[24,9]]},"3309":{"position":[[24,9]]},"3311":{"position":[[24,9]]}}}],["uncov",{"_index":4690,"t":{"1469":{"position":[[1621,8]]},"2610":{"position":[[1701,8]]}}}],["undefin",{"_index":5564,"t":{"3060":{"position":[[1331,9]]}}}],["under",{"_index":504,"t":{"12":{"position":[[921,5]]},"475":{"position":[[2805,5]]},"479":{"position":[[4940,5]]},"481":{"position":[[434,5],[1182,5],[4847,5]]},"483":{"position":[[1942,5]]},"485":{"position":[[243,5]]},"578":{"position":[[544,5]]},"622":{"position":[[110,5]]},"624":{"position":[[114,5]]},"626":{"position":[[112,5]]},"628":{"position":[[114,5]]},"953":{"position":[[89,5]]},"965":{"position":[[2031,5]]},"1323":{"position":[[366,5]]},"1543":{"position":[[23,5]]},"1585":{"position":[[593,5]]},"1591":{"position":[[110,5]]},"1593":{"position":[[114,5]]},"1595":{"position":[[112,5]]},"1597":{"position":[[114,5]]},"2048":{"position":[[385,5],[715,5]]},"2080":{"position":[[89,5]]},"2470":{"position":[[366,5]]},"2504":{"position":[[1073,5]]},"2712":{"position":[[23,5]]},"2782":{"position":[[592,5]]},"2786":{"position":[[110,5]]},"2788":{"position":[[114,5]]},"2790":{"position":[[112,5]]},"2792":{"position":[[114,5]]},"3247":{"position":[[89,5]]},"3287":{"position":[[407,5],[787,5]]},"3612":{"position":[[125,5]]}}}],["underli",{"_index":894,"t":{"28":{"position":[[943,10]]},"68":{"position":[[85,10],[810,10]]},"94":{"position":[[1946,10]]},"333":{"position":[[7,10]]},"361":{"position":[[844,10]]},"961":{"position":[[1193,10]]},"1321":{"position":[[7,10]]},"1381":{"position":[[849,10]]},"2098":{"position":[[1259,10]]},"2468":{"position":[[7,10]]},"2532":{"position":[[904,10]]},"3257":{"position":[[1259,10]]}}}],["underscor",{"_index":3903,"t":{"734":{"position":[[518,12]]},"1881":{"position":[[518,12]]},"3072":{"position":[[806,12]]}}}],["understand",{"_index":1,"t":{"2":{"position":[[19,13]]},"10":{"position":[[2113,10],[2202,13]]},"16":{"position":[[6379,13]]},"50":{"position":[[1821,10]]},"142":{"position":[[150,10]]},"146":{"position":[[515,10]]},"174":{"position":[[636,11]]},"254":{"position":[[1587,10]]},"291":{"position":[[51,13]]},"371":{"position":[[909,10]]},"385":{"position":[[1343,13],[1572,10]]},"443":{"position":[[323,13]]},"481":{"position":[[6517,10]]},"576":{"position":[[315,10]]},"945":{"position":[[441,10]]},"961":{"position":[[344,10]]},"965":{"position":[[2495,10]]},"1065":{"position":[[5794,10]]},"1095":{"position":[[416,10]]},"1391":{"position":[[909,10]]},"1461":{"position":[[100,10],[150,10]]},"1475":{"position":[[836,13]]},"1583":{"position":[[514,13]]},"2020":{"position":[[441,10]]},"2098":{"position":[[385,10]]},"2146":{"position":[[5917,10]]},"2178":{"position":[[416,10]]},"2482":{"position":[[163,10]]},"2542":{"position":[[923,10]]},"2602":{"position":[[104,10],[159,10]]},"2668":{"position":[[836,13]]},"2720":{"position":[[746,13]]},"2744":{"position":[[879,13]]},"2778":{"position":[[515,13]]},"3239":{"position":[[441,10]]},"3257":{"position":[[385,10]]},"3324":{"position":[[469,13],[1087,13],[3853,13],[4973,13]]},"3375":{"position":[[5946,10]]},"3407":{"position":[[416,10]]}}}],["understood",{"_index":218,"t":{"4":{"position":[[100,10]]}}}],["undesir",{"_index":5647,"t":{"3347":{"position":[[3571,9]]}}}],["unexpect",{"_index":2385,"t":{"212":{"position":[[382,10]]},"369":{"position":[[196,10]]},"525":{"position":[[642,10]]},"1389":{"position":[[196,10]]},"1433":{"position":[[644,10]]},"2540":{"position":[[196,10]]},"2616":{"position":[[649,10]]}}}],["unexpectedli",{"_index":2874,"t":{"321":{"position":[[306,12]]},"347":{"position":[[376,12]]},"1307":{"position":[[306,12]]},"1335":{"position":[[380,12]]},"2454":{"position":[[306,12]]},"2482":{"position":[[341,12]]},"2722":{"position":[[2507,13]]}}}],["unfortun",{"_index":522,"t":{"12":{"position":[[1381,13]]},"50":{"position":[[1565,12]]},"108":{"position":[[703,14]]},"116":{"position":[[822,14]]},"216":{"position":[[172,14]]},"351":{"position":[[1244,14]]},"475":{"position":[[396,14]]},"572":{"position":[[344,14]]},"1213":{"position":[[0,13]]},"1339":{"position":[[1244,14]]},"2387":{"position":[[0,13]]},"2486":{"position":[[1244,14]]},"3601":{"position":[[0,13]]}}}],["uni",{"_index":1732,"t":{"110":{"position":[[2844,3]]},"1049":{"position":[[1912,3]]},"1053":{"position":[[308,3],[402,3]]},"1207":{"position":[[165,3],[673,3]]},"1235":{"position":[[62,3]]},"2230":{"position":[[1912,3]]},"2234":{"position":[[276,3],[370,3]]},"2314":{"position":[[62,3]]},"2339":{"position":[[165,3],[673,3]]},"3417":{"position":[[1912,3]]},"3421":{"position":[[276,3],[370,3]]},"3535":{"position":[[62,3]]},"3595":{"position":[[165,3],[673,3]]}}}],["uni_grpc",{"_index":4631,"t":{"1231":{"position":[[81,11]]},"2310":{"position":[[81,11]]},"3531":{"position":[[81,11]]}}}],["uni_grpc_tl",{"_index":4280,"t":{"1053":{"position":[[131,12]]},"2234":{"position":[[99,12]]},"3421":{"position":[[99,12]]}}}],["uni_grpc_tls_cert",{"_index":4281,"t":{"1053":{"position":[[230,17]]},"2234":{"position":[[198,17]]},"3421":{"position":[[198,17]]}}}],["uni_grpc_tls_dis",{"_index":4275,"t":{"1049":{"position":[[1847,20]]},"2230":{"position":[[1847,20]]},"3417":{"position":[[1847,20]]}}}],["uni_grpc_tls_key",{"_index":4282,"t":{"1053":{"position":[[326,16]]},"2234":{"position":[[294,16]]},"3421":{"position":[[294,16]]}}}],["uni_http_stream",{"_index":4608,"t":{"1203":{"position":[[91,18]]},"1207":{"position":[[267,18]]},"1577":{"position":[[385,15],[1431,18],[1551,18]]},"2335":{"position":[[91,18]]},"2339":{"position":[[267,18]]},"2770":{"position":[[385,15],[1395,18],[1515,18]]},"3591":{"position":[[91,18]]},"3595":{"position":[[267,18]]}}}],["uni_http_stream_handler_prefix",{"_index":4077,"t":{"930":{"position":[[459,30]]},"2005":{"position":[[648,30]]},"3222":{"position":[[735,30]]}}}],["uni_ss",{"_index":4297,"t":{"1065":{"position":[[2555,7]]},"1067":{"position":[[851,7]]},"1220":{"position":[[94,10]]},"2146":{"position":[[2672,7]]},"2148":{"position":[[851,7]]},"2156":{"position":[[2252,7]]},"2392":{"position":[[94,10]]},"3375":{"position":[[2672,7]]},"3377":{"position":[[851,7]]},"3385":{"position":[[2225,7]]},"3606":{"position":[[94,10]]}}}],["uni_sse_handler_prefix",{"_index":4075,"t":{"930":{"position":[[353,22]]},"2005":{"position":[[542,22]]},"3222":{"position":[[629,22]]}}}],["uni_websocket",{"_index":4633,"t":{"1258":{"position":[[86,16]]},"1262":{"position":[[172,13]]},"2409":{"position":[[86,16]]},"2413":{"position":[[172,13]]},"3576":{"position":[[86,16]]},"3580":{"position":[[172,13]]}}}],["uni_websocket\":tru",{"_index":4634,"t":{"1262":{"position":[[337,21]]},"2413":{"position":[[337,21]]},"3580":{"position":[[337,21]]}}}],["uni_websocket_handler_prefix",{"_index":4079,"t":{"930":{"position":[[584,28]]},"2005":{"position":[[773,28]]},"3222":{"position":[[860,28]]}}}],["uni_websocket_ping_interv",{"_index":5449,"t":{"2658":{"position":[[350,27]]}}}],["unidirect",{"_index":1266,"t":{"60":{"position":[[439,14]]},"68":{"position":[[135,14],[208,14],[1263,14],[1364,14],[1650,14],[1898,14],[2093,14],[2153,14],[2178,14],[2232,14]]},"78":{"position":[[409,14]]},"108":{"position":[[414,14],[653,14],[1248,14],[1345,14],[1437,14]]},"110":{"position":[[315,14],[424,14],[1195,14],[2209,14]]},"112":{"position":[[280,14]]},"142":{"position":[[264,14]]},"182":{"position":[[1055,14]]},"192":{"position":[[361,14]]},"194":{"position":[[1192,14]]},"291":{"position":[[167,14]]},"331":{"position":[[312,14]]},"359":{"position":[[41,14]]},"373":{"position":[[1185,14]]},"375":{"position":[[118,14]]},"379":{"position":[[1743,14]]},"383":{"position":[[1571,14]]},"419":{"position":[[232,14],[326,14],[355,14]]},"604":{"position":[[1707,14]]},"632":{"position":[[66,14]]},"680":{"position":[[215,14]]},"892":{"position":[[196,14]]},"918":{"position":[[184,14],[284,14],[395,14],[499,14]]},"930":{"position":[[421,14],[543,14],[664,14]]},"1053":{"position":[[99,14]]},"1063":{"position":[[10,14]]},"1085":{"position":[[96,14]]},"1101":{"position":[[126,14],[250,14]]},"1119":{"position":[[62,14]]},"1196":{"position":[[163,14]]},"1203":{"position":[[33,14]]},"1213":{"position":[[567,14],[754,14],[1105,14]]},"1220":{"position":[[33,14]]},"1231":{"position":[[33,14]]},"1239":{"position":[[46,14]]},"1255":{"position":[[75,14]]},"1258":{"position":[[33,14]]},"1262":{"position":[[19,14]]},"1268":{"position":[[0,14],[140,14],[482,14]]},"1270":{"position":[[11,14],[200,14],[472,14],[685,14],[939,14]]},"1317":{"position":[[372,14]]},"1379":{"position":[[41,14]]},"1403":{"position":[[351,14],[445,14],[474,14]]},"1501":{"position":[[40,14],[138,14],[258,14],[460,14],[519,14]]},"1533":{"position":[[2273,14]]},"1601":{"position":[[66,14]]},"1682":{"position":[[921,14]]},"1731":{"position":[[215,14]]},"1959":{"position":[[196,14]]},"1975":{"position":[[554,14]]},"1977":{"position":[[491,14]]},"1993":{"position":[[399,14],[499,14],[610,14],[714,14]]},"2005":{"position":[[610,14],[732,14],[853,14]]},"2144":{"position":[[10,14]]},"2168":{"position":[[96,14]]},"2200":{"position":[[62,14]]},"2234":{"position":[[67,14]]},"2242":{"position":[[927,14],[1031,14]]},"2292":{"position":[[0,14],[140,14],[485,14]]},"2294":{"position":[[11,14],[200,14],[610,14],[1345,14]]},"2296":{"position":[[1191,14]]},"2310":{"position":[[33,14]]},"2318":{"position":[[46,14]]},"2328":{"position":[[163,14]]},"2335":{"position":[[33,14]]},"2387":{"position":[[567,14],[754,14],[1105,14]]},"2392":{"position":[[33,14]]},"2406":{"position":[[62,14]]},"2409":{"position":[[33,14]]},"2413":{"position":[[19,14]]},"2464":{"position":[[372,14]]},"2560":{"position":[[468,14],[565,14],[600,14]]},"2632":{"position":[[40,14],[138,14],[258,14],[460,14],[519,14]]},"2702":{"position":[[2273,14]]},"2796":{"position":[[66,14]]},"2836":{"position":[[921,14]]},"2920":{"position":[[215,14]]},"3172":{"position":[[196,14]]},"3188":{"position":[[527,14]]},"3190":{"position":[[464,14]]},"3208":{"position":[[399,14],[499,14],[610,14],[714,14]]},"3222":{"position":[[697,14],[819,14],[940,14]]},"3347":{"position":[[849,14],[1918,14]]},"3349":{"position":[[1496,14],[2003,14]]},"3351":{"position":[[15,14],[402,14],[497,14],[1457,14]]},"3373":{"position":[[10,14]]},"3397":{"position":[[96,14]]},"3421":{"position":[[67,14]]},"3441":{"position":[[62,14]]},"3476":{"position":[[456,14]]},"3484":{"position":[[0,14],[140,14],[485,14],[884,14],[922,14]]},"3486":{"position":[[1191,14]]},"3518":{"position":[[436,14]]},"3524":{"position":[[11,14],[200,14],[610,14],[1345,14]]},"3531":{"position":[[33,14]]},"3539":{"position":[[46,14]]},"3553":{"position":[[927,14],[1031,14]]},"3573":{"position":[[62,14]]},"3576":{"position":[[33,14]]},"3580":{"position":[[19,14]]},"3584":{"position":[[163,14]]},"3591":{"position":[[33,14]]},"3601":{"position":[[567,14],[754,14],[1105,14]]},"3606":{"position":[[33,14]]}}}],["unifi",{"_index":2968,"t":{"377":{"position":[[1973,7]]},"383":{"position":[[1538,8]]},"403":{"position":[[55,7]]},"407":{"position":[[47,7],[815,7]]},"1631":{"position":[[80,7]]},"2848":{"position":[[80,7]]}}}],["uniqu",{"_index":444,"t":{"10":{"position":[[1391,8]]},"18":{"position":[[42,6]]},"42":{"position":[[634,6]]},"44":{"position":[[1902,6]]},"48":{"position":[[668,6]]},"60":{"position":[[630,6]]},"74":{"position":[[626,6]]},"88":{"position":[[154,6]]},"120":{"position":[[629,7]]},"146":{"position":[[917,6]]},"148":{"position":[[932,7]]},"218":{"position":[[315,6]]},"250":{"position":[[573,6],[2072,6]]},"254":{"position":[[1746,6]]},"262":{"position":[[1640,6],[1691,6],[1779,6]]},"279":{"position":[[1243,7]]},"341":{"position":[[679,6]]},"347":{"position":[[1115,6]]},"351":{"position":[[378,6]]},"427":{"position":[[14,6]]},"443":{"position":[[47,6]]},"489":{"position":[[986,6]]},"513":{"position":[[397,6],[526,6]]},"610":{"position":[[5,6],[621,6]]},"652":{"position":[[372,6],[744,6]]},"704":{"position":[[210,6]]},"734":{"position":[[456,6]]},"746":{"position":[[16,6]]},"951":{"position":[[356,8]]},"989":{"position":[[216,6]]},"1019":{"position":[[103,6]]},"1065":{"position":[[2426,6]]},"1067":{"position":[[722,6]]},"1069":{"position":[[700,6]]},"1071":{"position":[[2024,6]]},"1073":{"position":[[2058,6]]},"1093":{"position":[[1132,6]]},"1123":{"position":[[99,6],[772,6]]},"1166":{"position":[[1203,6]]},"1168":{"position":[[49,6]]},"1174":{"position":[[470,6]]},"1182":{"position":[[1814,6]]},"1329":{"position":[[679,6]]},"1335":{"position":[[1119,6]]},"1339":{"position":[[378,6]]},"1343":{"position":[[279,6]]},"1375":{"position":[[397,6],[526,6]]},"1427":{"position":[[47,6]]},"1469":{"position":[[1053,6]]},"1545":{"position":[[5,6],[621,6]]},"1579":{"position":[[1374,6],[1544,6]]},"1666":{"position":[[1133,6],[1915,6]]},"1668":{"position":[[876,7]]},"1699":{"position":[[372,6],[744,6]]},"1765":{"position":[[16,6]]},"1821":{"position":[[16,6]]},"1879":{"position":[[31,8]]},"1881":{"position":[[456,6]]},"2050":{"position":[[216,6]]},"2078":{"position":[[356,8]]},"2116":{"position":[[140,6]]},"2146":{"position":[[2543,6]]},"2148":{"position":[[722,6]]},"2150":{"position":[[700,6]]},"2152":{"position":[[2024,6]]},"2154":{"position":[[2058,6]]},"2156":{"position":[[2123,6]]},"2176":{"position":[[1132,6]]},"2204":{"position":[[99,6],[772,6]]},"2250":{"position":[[1814,6]]},"2365":{"position":[[382,6]]},"2381":{"position":[[397,6],[526,6]]},"2476":{"position":[[679,6]]},"2486":{"position":[[378,6]]},"2490":{"position":[[279,6]]},"2506":{"position":[[840,6],[1560,6]]},"2526":{"position":[[397,6],[526,6]]},"2584":{"position":[[47,6]]},"2610":{"position":[[1038,6]]},"2714":{"position":[[5,6],[621,6]]},"2772":{"position":[[1374,6],[1544,6]]},"2823":{"position":[[372,6],[708,6]]},"2883":{"position":[[436,6],[2194,6]]},"2887":{"position":[[876,7]]},"2954":{"position":[[10,6]]},"3010":{"position":[[16,6]]},"3070":{"position":[[31,8]]},"3072":{"position":[[744,6]]},"3245":{"position":[[356,8]]},"3289":{"position":[[216,6]]},"3324":{"position":[[1035,6]]},"3333":{"position":[[607,6]]},"3349":{"position":[[5017,6]]},"3375":{"position":[[2543,6]]},"3377":{"position":[[722,6]]},"3379":{"position":[[747,6]]},"3381":{"position":[[1827,6]]},"3383":{"position":[[2058,6]]},"3385":{"position":[[2096,6]]},"3405":{"position":[[1132,6]]},"3445":{"position":[[99,6],[613,6]]},"3561":{"position":[[1814,6]]},"3653":{"position":[[382,6]]},"3669":{"position":[[397,6],[526,6]]}}}],["unit",{"_index":1595,"t":{"98":{"position":[[1868,4]]},"941":{"position":[[79,4],[298,5]]},"2016":{"position":[[79,4],[298,5]]},"3235":{"position":[[79,4],[298,5]]}}}],["uniti",{"_index":5773,"t":{"3490":{"position":[[383,5]]}}}],["univers",{"_index":1790,"t":{"120":{"position":[[892,9]]},"148":{"position":[[1233,9]]},"192":{"position":[[223,9]]},"279":{"position":[[1363,9]]},"1469":{"position":[[1491,9]]},"2498":{"position":[[325,10],[440,10]]},"2610":{"position":[[1799,9]]}}}],["universalcli",{"_index":3197,"t":{"477":{"position":[[450,16],[478,15],[976,15]]},"479":{"position":[[606,15]]}}}],["unix",{"_index":414,"t":{"10":{"position":[[536,6]]},"652":{"position":[[798,4],[864,5]]},"654":{"position":[[857,4],[923,5]]},"670":{"position":[[355,4],[424,5]]},"682":{"position":[[1438,5],[1489,5]]},"742":{"position":[[10,4]]},"744":{"position":[[10,4]]},"762":{"position":[[401,4]]},"1027":{"position":[[113,4]]},"1029":{"position":[[480,4]]},"1119":{"position":[[492,4]]},"1579":{"position":[[1419,5],[1589,5]]},"1666":{"position":[[1206,4]]},"1699":{"position":[[798,4],[864,5]]},"1702":{"position":[[746,4],[903,4],[969,5]]},"1715":{"position":[[355,4],[424,5]]},"1733":{"position":[[1438,5],[1489,5]]},"1761":{"position":[[10,4]]},"1763":{"position":[[10,4]]},"1781":{"position":[[401,4]]},"1811":{"position":[[113,4]]},"1813":{"position":[[480,4]]},"1819":{"position":[[10,4]]},"2200":{"position":[[458,4]]},"2752":{"position":[[281,4]]},"2772":{"position":[[1419,5],[1589,5]]},"2823":{"position":[[762,4],[828,5]]},"2826":{"position":[[710,4],[867,4],[933,5]]},"2883":{"position":[[577,4],[1335,4]]},"2904":{"position":[[319,4],[388,5]]},"2922":{"position":[[1402,5],[1453,5]]},"2950":{"position":[[25,4]]},"2952":{"position":[[20,4]]},"2970":{"position":[[388,4]]},"3000":{"position":[[113,4]]},"3002":{"position":[[480,4]]},"3008":{"position":[[10,4]]},"3375":{"position":[[3792,5]]},"3381":{"position":[[3275,5]]},"3385":{"position":[[2843,5]]},"3441":{"position":[[451,4]]}}}],["unknown",{"_index":1753,"t":{"110":{"position":[[3944,7]]},"212":{"position":[[564,7],[929,7],[1013,7],[1093,7],[1174,7]]},"379":{"position":[[787,8]]},"786":{"position":[[20,8],[45,7]]},"842":{"position":[[20,8],[45,7]]},"1109":{"position":[[1423,7]]},"1245":{"position":[[233,7]]},"1567":{"position":[[1096,7]]},"1569":{"position":[[873,7]]},"1837":{"position":[[18,8],[42,7]]},"1869":{"position":[[1288,7]]},"2190":{"position":[[1478,7]]},"2324":{"position":[[217,7]]},"2726":{"position":[[1096,7]]},"2728":{"position":[[873,7]]},"3028":{"position":[[18,8],[42,7]]},"3060":{"position":[[1371,7]]},"3431":{"position":[[959,7]]},"3545":{"position":[[196,7]]}}}],["unknown:chat",{"_index":4416,"t":{"1109":{"position":[[1493,15]]},"2190":{"position":[[1547,15]]},"3431":{"position":[[1096,15]]}}}],["unless",{"_index":3044,"t":{"403":{"position":[[675,6]]},"2032":{"position":[[1180,6]]},"2038":{"position":[[1237,6]]},"3068":{"position":[[565,6]]},"3273":{"position":[[1072,6]]},"3279":{"position":[[1129,6]]}}}],["unlik",{"_index":3830,"t":{"648":{"position":[[273,6]]},"666":{"position":[[173,6]]},"760":{"position":[[96,6]]},"1071":{"position":[[963,6]]},"1153":{"position":[[150,7]]},"1631":{"position":[[0,6]]},"1694":{"position":[[273,6]]},"1710":{"position":[[173,6]]},"1779":{"position":[[96,6]]},"2152":{"position":[[963,6]]},"2281":{"position":[[150,7]]},"2818":{"position":[[273,6]]},"2848":{"position":[[0,6]]},"2899":{"position":[[173,6]]},"3381":{"position":[[766,6]]},"3508":{"position":[[150,7]]}}}],["unlimit",{"_index":4045,"t":{"902":{"position":[[156,9]]},"906":{"position":[[110,10]]},"1615":{"position":[[187,9]]},"1969":{"position":[[156,9]]},"1973":{"position":[[110,10]]},"2740":{"position":[[187,9]]},"3182":{"position":[[156,9]]},"3186":{"position":[[110,10]]}}}],["unlock",{"_index":3697,"t":{"598":{"position":[[589,8]]},"1563":{"position":[[589,8]]},"2766":{"position":[[589,8]]}}}],["unmap",{"_index":5764,"t":{"3469":{"position":[[565,8]]},"3471":{"position":[[468,8]]}}}],["unmarsh",{"_index":1422,"t":{"86":{"position":[[163,14]]}}}],["unmarshal",{"_index":521,"t":{"12":{"position":[[1366,14]]}}}],["unnecessari",{"_index":704,"t":{"16":{"position":[[5731,11]]},"126":{"position":[[509,11]]},"2722":{"position":[[2256,11]]},"2914":{"position":[[85,11]]},"2964":{"position":[[476,11]]}}}],["unpack",{"_index":3539,"t":{"523":{"position":[[215,6]]},"588":{"position":[[154,6]]},"1431":{"position":[[225,6],[712,6]]},"1553":{"position":[[154,6]]},"2614":{"position":[[239,6],[732,6]]},"2756":{"position":[[154,6],[323,6]]}}}],["unprotect",{"_index":4386,"t":{"1107":{"position":[[794,11]]},"2188":{"position":[[797,11]]},"3427":{"position":[[776,11]]}}}],["unrecover",{"_index":3984,"t":{"806":{"position":[[20,14],[52,13]]},"850":{"position":[[20,14]]},"1857":{"position":[[18,14],[49,13]]},"3048":{"position":[[18,14],[49,13]]}}}],["unreli",{"_index":627,"t":{"16":{"position":[[2282,10]]},"94":{"position":[[906,10]]},"108":{"position":[[850,10],[1007,10]]},"993":{"position":[[253,10]]},"2054":{"position":[[253,10]]},"3293":{"position":[[253,10]]}}}],["unset",{"_index":4029,"t":{"878":{"position":[[1094,5]]},"1945":{"position":[[1094,5]]},"3158":{"position":[[1094,5]]}}}],["unstabl",{"_index":3494,"t":{"485":{"position":[[2032,8]]}}}],["unsubscrib",{"_index":1062,"t":{"40":{"position":[[998,11]]},"44":{"position":[[478,12]]},"178":{"position":[[1037,12]]},"180":{"position":[[372,12]]},"264":{"position":[[1117,11]]},"317":{"position":[[146,12]]},"349":{"position":[[169,11]]},"505":{"position":[[444,11]]},"507":{"position":[[332,11]]},"710":{"position":[[122,13]]},"1039":{"position":[[18,11]]},"1115":{"position":[[0,11],[19,13],[66,14],[141,11],[239,11],[289,11],[348,11],[515,11]]},"1162":{"position":[[927,11],[1007,11],[1211,11]]},"1166":{"position":[[570,11]]},"1168":{"position":[[1509,11]]},"1170":{"position":[[21,11],[160,11]]},"1176":{"position":[[959,13],[1174,11],[1217,12]]},"1184":{"position":[[2106,11],[2259,11]]},"1186":{"position":[[385,12]]},"1188":{"position":[[495,11]]},"1270":{"position":[[324,11]]},"1303":{"position":[[146,12]]},"1337":{"position":[[169,11]]},"1367":{"position":[[444,11]]},"1369":{"position":[[332,11]]},"1479":{"position":[[230,11],[454,11]]},"1491":{"position":[[336,11]]},"1493":{"position":[[261,11]]},"1495":{"position":[[4038,14]]},"1545":{"position":[[781,11]]},"1629":{"position":[[1190,11]]},"1887":{"position":[[125,13]]},"2130":{"position":[[18,11]]},"2152":{"position":[[4711,12]]},"2196":{"position":[[0,11],[19,13],[66,14],[141,11],[239,11],[289,11],[348,11],[481,11]]},"2240":{"position":[[1750,11],[1772,11]]},"2242":{"position":[[551,13],[580,11],[619,12]]},"2264":{"position":[[317,11]]},"2294":{"position":[[343,11],[355,11],[738,11]]},"2345":{"position":[[5599,12]]},"2355":{"position":[[565,12],[646,12],[1012,12],[3709,12],[3829,12],[4186,12],[4374,12],[4764,11],[4796,14]]},"2357":{"position":[[514,12]]},"2361":{"position":[[265,13]]},"2365":{"position":[[45,13],[61,11]]},"2367":{"position":[[670,13]]},"2369":{"position":[[1236,11]]},"2373":{"position":[[18,11],[44,11],[93,11],[265,12],[352,11]]},"2450":{"position":[[146,12]]},"2482":{"position":[[2512,11]]},"2484":{"position":[[169,11]]},"2518":{"position":[[449,11]]},"2520":{"position":[[332,11]]},"2564":{"position":[[232,11]]},"2662":{"position":[[4096,14]]},"2672":{"position":[[230,11],[454,11]]},"2684":{"position":[[336,11]]},"2686":{"position":[[261,11]]},"2714":{"position":[[781,11]]},"2846":{"position":[[1190,11]]},"3078":{"position":[[125,13]]},"3347":{"position":[[1864,12],[2122,11]]},"3359":{"position":[[18,11]]},"3381":{"position":[[4714,12]]},"3437":{"position":[[0,11],[19,13],[54,11],[145,11],[195,11],[254,11],[387,11]]},"3498":{"position":[[317,11]]},"3524":{"position":[[343,11],[355,11],[738,11]]},"3551":{"position":[[1750,11],[1772,11]]},"3553":{"position":[[551,13],[580,11],[619,12]]},"3633":{"position":[[5599,12]]},"3643":{"position":[[565,12],[646,12],[1012,12],[3709,12],[3829,12],[4186,12],[4374,12],[4764,11],[4796,14]]},"3645":{"position":[[514,12]]},"3649":{"position":[[265,13]]},"3653":{"position":[[45,13],[61,11]]},"3655":{"position":[[670,13]]},"3657":{"position":[[1236,11]]},"3661":{"position":[[18,11],[44,11],[93,11],[265,12],[352,11]]}}}],["unsubscribe(ch",{"_index":1127,"t":{"46":{"position":[[769,14]]}}}],["unsubscribe/disconnect",{"_index":2901,"t":{"345":{"position":[[320,22]]},"1333":{"position":[[320,22]]},"1493":{"position":[[488,22]]},"2480":{"position":[[320,22]]},"2686":{"position":[[488,22]]}}}],["unsubscribe/remov",{"_index":5347,"t":{"2357":{"position":[[770,18]]},"3645":{"position":[[770,18]]}}}],["unsubscribedev",{"_index":5321,"t":{"2355":{"position":[[2769,17]]},"3643":{"position":[[2769,17]]}}}],["unsuccess",{"_index":1092,"t":{"42":{"position":[[2058,12]]}}}],["unsuit",{"_index":3327,"t":{"479":{"position":[[1889,10]]}}}],["unsur",{"_index":5438,"t":{"2614":{"position":[[623,6]]}}}],["until",{"_index":1733,"t":{"110":{"position":[[2855,5]]},"116":{"position":[[243,5]]},"128":{"position":[[2692,5]]},"180":{"position":[[473,5]]},"369":{"position":[[321,6]]},"479":{"position":[[2778,5]]},"517":{"position":[[439,5]]},"614":{"position":[[503,5]]},"662":{"position":[[139,5]]},"742":{"position":[[529,5]]},"979":{"position":[[1266,5]]},"1065":{"position":[[1652,6]]},"1166":{"position":[[1411,5]]},"1168":{"position":[[561,5]]},"1172":{"position":[[818,5]]},"1389":{"position":[[321,6]]},"1477":{"position":[[308,5]]},"1491":{"position":[[323,5]]},"1549":{"position":[[504,5]]},"1706":{"position":[[139,5]]},"1761":{"position":[[529,5]]},"2010":{"position":[[410,5]]},"2036":{"position":[[1282,5]]},"2146":{"position":[[1769,6]]},"2540":{"position":[[324,6]]},"2670":{"position":[[308,5]]},"2684":{"position":[[323,5]]},"2718":{"position":[[504,5]]},"2780":{"position":[[407,5]]},"2883":{"position":[[666,5]]},"2895":{"position":[[139,5]]},"2950":{"position":[[571,5]]},"3227":{"position":[[410,5]]},"3277":{"position":[[1282,5]]},"3375":{"position":[[1769,6]]}}}],["untouch",{"_index":4448,"t":{"1127":{"position":[[107,9]]},"2208":{"position":[[107,9]]},"3449":{"position":[[107,9]]}}}],["unus",{"_index":3896,"t":{"714":{"position":[[243,6]]},"971":{"position":[[711,6]]},"975":{"position":[[1593,6]]},"1893":{"position":[[243,6]]},"3084":{"position":[[243,6]]}}}],["unusu",{"_index":3015,"t":{"387":{"position":[[1364,9]]}}}],["unwant",{"_index":4127,"t":{"971":{"position":[[567,8]]},"975":{"position":[[1449,8]]},"1682":{"position":[[1182,8]]},"2026":{"position":[[552,8]]},"2030":{"position":[[1595,8]]},"2836":{"position":[[1182,8]]},"3086":{"position":[[554,8]]},"3267":{"position":[[552,8]]},"3271":{"position":[[1561,8]]}}}],["unwrap",{"_index":2997,"t":{"381":{"position":[[450,6]]}}}],["up",{"_index":1175,"t":{"46":{"position":[[4078,3]]},"74":{"position":[[962,2]]},"86":{"position":[[337,2],[367,2],[423,2],[468,2]]},"92":{"position":[[282,2]]},"140":{"position":[[66,2]]},"150":{"position":[[205,2],[666,2]]},"212":{"position":[[622,3],[794,2],[1306,2]]},"246":{"position":[[3540,2]]},"254":{"position":[[1003,2]]},"256":{"position":[[1394,3]]},"270":{"position":[[834,2]]},"281":{"position":[[676,2]]},"293":{"position":[[310,2],[420,2]]},"301":{"position":[[4372,2]]},"369":{"position":[[299,2]]},"371":{"position":[[649,2]]},"377":{"position":[[1730,3]]},"379":{"position":[[3492,2]]},"403":{"position":[[10,3]]},"473":{"position":[[3790,2]]},"475":{"position":[[898,2]]},"479":{"position":[[2151,2]]},"511":{"position":[[280,2],[467,2],[623,2]]},"517":{"position":[[237,2]]},"527":{"position":[[442,2]]},"564":{"position":[[103,2]]},"578":{"position":[[225,2],[257,2],[288,2]]},"614":{"position":[[378,3]]},"622":{"position":[[292,2]]},"636":{"position":[[90,2]]},"638":{"position":[[105,2]]},"963":{"position":[[271,2],[432,2],[588,3],[703,3],[766,2],[931,2]]},"979":{"position":[[1136,2]]},"989":{"position":[[919,3]]},"1373":{"position":[[280,2],[467,2],[623,2]]},"1383":{"position":[[1030,2]]},"1389":{"position":[[299,2]]},"1391":{"position":[[649,2]]},"1417":{"position":[[267,2]]},"1435":{"position":[[466,2]]},"1493":{"position":[[381,2]]},"1549":{"position":[[379,3]]},"1585":{"position":[[225,2],[257,2],[288,2]]},"1591":{"position":[[292,2]]},"1605":{"position":[[90,2]]},"1607":{"position":[[105,2]]},"1629":{"position":[[604,2]]},"1637":{"position":[[90,2]]},"1869":{"position":[[769,3]]},"2036":{"position":[[1152,2]]},"2050":{"position":[[919,3]]},"2100":{"position":[[271,2],[432,2],[588,3],[703,3],[766,2],[931,2]]},"2102":{"position":[[4160,3]]},"2146":{"position":[[6362,2]]},"2152":{"position":[[4573,2]]},"2156":{"position":[[1021,2]]},"2345":{"position":[[155,2],[5508,2]]},"2361":{"position":[[376,2]]},"2379":{"position":[[280,2],[467,2],[623,2]]},"2500":{"position":[[413,2]]},"2524":{"position":[[280,2],[467,2],[623,2]]},"2534":{"position":[[1077,2]]},"2540":{"position":[[302,2]]},"2542":{"position":[[660,2]]},"2574":{"position":[[266,2]]},"2618":{"position":[[466,2]]},"2686":{"position":[[381,2]]},"2718":{"position":[[379,3]]},"2722":{"position":[[1738,2]]},"2782":{"position":[[225,2],[257,2],[288,2]]},"2786":{"position":[[292,2]]},"2800":{"position":[[90,2]]},"2802":{"position":[[105,2]]},"2846":{"position":[[604,2]]},"2854":{"position":[[90,2]]},"2972":{"position":[[1171,2]]},"3060":{"position":[[840,3]]},"3259":{"position":[[271,2],[432,2],[588,3],[703,3],[766,2],[931,2]]},"3261":{"position":[[4160,3]]},"3277":{"position":[[1152,2]]},"3289":{"position":[[919,3]]},"3375":{"position":[[6391,2]]},"3381":{"position":[[4576,2]]},"3385":{"position":[[994,2]]},"3633":{"position":[[155,2],[5508,2]]},"3649":{"position":[[376,2]]},"3667":{"position":[[280,2],[467,2],[623,2]]}}}],["upcom",{"_index":688,"t":{"16":{"position":[[4889,8]]},"248":{"position":[[41,8]]},"2778":{"position":[[1629,8]]}}}],["updat",{"_index":814,"t":{"20":{"position":[[529,7]]},"34":{"position":[[595,8],[984,8],[1404,8]]},"64":{"position":[[184,6],[287,6]]},"68":{"position":[[1117,7]]},"78":{"position":[[1001,7]]},"92":{"position":[[0,7]]},"162":{"position":[[1093,7]]},"176":{"position":[[1003,7]]},"198":{"position":[[1659,7]]},"204":{"position":[[2122,7]]},"220":{"position":[[67,7]]},"236":{"position":[[934,7]]},"246":{"position":[[1322,8],[1347,8]]},"256":{"position":[[1034,7],[1276,8]]},"260":{"position":[[1095,6]]},"262":{"position":[[1082,6]]},"264":{"position":[[861,6]]},"266":{"position":[[354,8]]},"289":{"position":[[853,7]]},"319":{"position":[[873,7]]},"347":{"position":[[1350,6]]},"355":{"position":[[357,7],[968,6],[1410,6]]},"385":{"position":[[1873,7]]},"401":{"position":[[258,8],[289,6]]},"411":{"position":[[435,8]]},"419":{"position":[[272,8]]},"425":{"position":[[82,8]]},"467":{"position":[[900,7],[1111,6]]},"469":{"position":[[1284,6],[1423,7]]},"477":{"position":[[95,8],[3735,6],[4194,8],[4271,6]]},"489":{"position":[[644,7]]},"640":{"position":[[176,7]]},"680":{"position":[[174,7],[453,6],[575,6],[593,6]]},"682":{"position":[[521,6],[808,7]]},"686":{"position":[[892,7]]},"1005":{"position":[[21,6]]},"1172":{"position":[[1040,7]]},"1182":{"position":[[440,6]]},"1268":{"position":[[339,7]]},"1305":{"position":[[873,7]]},"1335":{"position":[[1354,6]]},"1347":{"position":[[357,7],[968,6],[1410,6]]},"1395":{"position":[[451,8]]},"1403":{"position":[[391,8]]},"1409":{"position":[[82,8]]},"1469":{"position":[[652,7]]},"1477":{"position":[[422,7]]},"1479":{"position":[[214,6],[438,6]]},"1493":{"position":[[197,6]]},"1495":{"position":[[333,6],[3755,6]]},"1499":{"position":[[1699,6],[1727,7]]},"1617":{"position":[[176,7]]},"1629":{"position":[[974,6]]},"1645":{"position":[[110,8]]},"1650":{"position":[[13,7],[162,10]]},"1652":{"position":[[20,6],[385,6],[452,6],[519,6],[587,6]]},"1668":{"position":[[207,7]]},"1731":{"position":[[174,7],[453,6],[575,6],[593,6]]},"1733":{"position":[[521,6],[808,7]]},"1737":{"position":[[874,7]]},"1851":{"position":[[134,8]]},"2112":{"position":[[21,6]]},"2122":{"position":[[465,7]]},"2250":{"position":[[440,6]]},"2292":{"position":[[343,7]]},"2324":{"position":[[37,6]]},"2341":{"position":[[37,6]]},"2345":{"position":[[1309,7]]},"2351":{"position":[[456,7]]},"2361":{"position":[[151,7]]},"2367":{"position":[[447,7]]},"2396":{"position":[[37,6]]},"2436":{"position":[[357,7],[968,6],[1410,6]]},"2452":{"position":[[873,7]]},"2482":{"position":[[0,6],[1397,6],[1530,7]]},"2552":{"position":[[455,8]]},"2560":{"position":[[506,8]]},"2566":{"position":[[82,8]]},"2610":{"position":[[640,7]]},"2630":{"position":[[1699,6],[1727,7]]},"2652":{"position":[[374,7],[661,8]]},"2662":{"position":[[334,6],[3789,6]]},"2670":{"position":[[422,7]]},"2672":{"position":[[214,6],[438,6]]},"2686":{"position":[[197,6]]},"2780":{"position":[[247,7]]},"2804":{"position":[[176,7]]},"2846":{"position":[[974,6]]},"2862":{"position":[[118,8]]},"2867":{"position":[[13,7],[162,10]]},"2869":{"position":[[20,6],[313,6],[380,6],[446,6]]},"2887":{"position":[[207,7]]},"2920":{"position":[[174,7],[417,6],[539,6],[557,6]]},"2922":{"position":[[485,6],[772,7]]},"2926":{"position":[[874,7]]},"2972":{"position":[[795,7],[1447,7],[1775,7]]},"3042":{"position":[[134,8]]},"3313":{"position":[[21,6]]},"3337":{"position":[[725,7],[985,7]]},"3484":{"position":[[343,7]]},"3561":{"position":[[440,6]]},"3597":{"position":[[37,6]]},"3633":{"position":[[1309,7]]},"3639":{"position":[[456,7]]},"3649":{"position":[[151,7]]},"3655":{"position":[[447,7]]}}}],["update_push_statu",{"_index":2643,"t":{"266":{"position":[[314,18]]},"1633":{"position":[[322,18]]},"1668":{"position":[[172,18],[382,18],[797,18],[1052,18]]},"2850":{"position":[[322,18]]},"2885":{"position":[[77,18],[187,18]]},"2887":{"position":[[172,18],[382,18],[797,18],[1052,18]]}}}],["update_user_statu",{"_index":3854,"t":{"678":{"position":[[67,19]]},"680":{"position":[[27,18],[373,21]]},"1729":{"position":[[67,19]]},"1731":{"position":[[27,18],[373,21]]},"2830":{"position":[[1265,21]]},"2832":{"position":[[1065,21]]},"2834":{"position":[[1546,21]]},"2918":{"position":[[67,19]]},"2920":{"position":[[27,18]]}}}],["updateactivestatu",{"_index":3815,"t":{"618":{"position":[[692,21]]},"1676":{"position":[[1262,21]]},"1678":{"position":[[1059,21]]},"1680":{"position":[[1540,21]]}}}],["upgrad",{"_index":342,"t":{"8":{"position":[[812,7]]},"86":{"position":[[2102,7]]},"134":{"position":[[1250,7],[1301,10]]},"160":{"position":[[614,7]]},"162":{"position":[[582,7]]},"287":{"position":[[524,7],[1127,7],[1178,10]]},"397":{"position":[[287,7]]},"401":{"position":[[38,7]]},"1001":{"position":[[177,8],[1230,7]]},"1003":{"position":[[177,8],[804,7]]},"1319":{"position":[[478,7]]},"1499":{"position":[[474,7],[722,7]]},"1682":{"position":[[1049,7]]},"2108":{"position":[[177,8],[1230,7]]},"2110":{"position":[[177,8],[804,7]]},"2347":{"position":[[415,7]]},"2466":{"position":[[478,7]]},"2630":{"position":[[474,7],[722,7]]},"2780":{"position":[[416,7]]},"2836":{"position":[[1049,7]]},"3198":{"position":[[754,7]]},"3309":{"position":[[177,8],[1230,7]]},"3311":{"position":[[177,8],[804,7]]},"3635":{"position":[[415,7]]}}}],["upload",{"_index":720,"t":{"16":{"position":[[6605,8]]},"531":{"position":[[47,6]]},"1439":{"position":[[47,6]]},"2622":{"position":[[47,6]]}}}],["upon",{"_index":1285,"t":{"62":{"position":[[909,4]]},"70":{"position":[[688,4]]},"80":{"position":[[95,5]]},"156":{"position":[[506,4]]},"166":{"position":[[234,4]]},"180":{"position":[[1390,4]]},"182":{"position":[[897,4],[1289,4]]},"198":{"position":[[927,4]]},"200":{"position":[[485,4]]},"212":{"position":[[604,4]]},"214":{"position":[[634,4]]},"222":{"position":[[572,4]]},"250":{"position":[[1997,4]]},"264":{"position":[[1168,4]]},"321":{"position":[[439,4]]},"361":{"position":[[969,4],[1123,4],[1516,4]]},"421":{"position":[[130,4]]},"469":{"position":[[699,4]]},"473":{"position":[[3508,4]]},"477":{"position":[[2436,4],[3991,4],[5837,4]]},"483":{"position":[[56,4]]},"505":{"position":[[1013,4]]},"644":{"position":[[160,4]]},"692":{"position":[[723,4]]},"716":{"position":[[523,4]]},"963":{"position":[[1250,4]]},"1065":{"position":[[522,4]]},"1071":{"position":[[829,4]]},"1200":{"position":[[185,4]]},"1207":{"position":[[235,4]]},"1307":{"position":[[439,4]]},"1367":{"position":[[1013,4]]},"1381":{"position":[[974,4],[1128,4],[1521,4]]},"1405":{"position":[[130,4]]},"1473":{"position":[[537,4],[627,4]]},"1479":{"position":[[125,4],[719,4],[859,4]]},"1493":{"position":[[127,4],[384,4],[561,4]]},"1495":{"position":[[3719,4]]},"1629":{"position":[[1241,4]]},"1662":{"position":[[102,4],[144,4]]},"1676":{"position":[[1702,4]]},"1690":{"position":[[160,4]]},"1753":{"position":[[203,4]]},"1797":{"position":[[224,4]]},"1865":{"position":[[23,4]]},"1873":{"position":[[708,4]]},"1895":{"position":[[615,4]]},"2100":{"position":[[1250,4]]},"2102":{"position":[[2594,4]]},"2146":{"position":[[522,4]]},"2152":{"position":[[829,4]]},"2160":{"position":[[403,4]]},"2332":{"position":[[185,4]]},"2339":{"position":[[235,4]]},"2345":{"position":[[4195,4],[5618,4]]},"2347":{"position":[[142,4]]},"2351":{"position":[[105,4]]},"2353":{"position":[[58,4]]},"2355":{"position":[[369,4],[1031,5],[3640,4]]},"2359":{"position":[[3059,4]]},"2361":{"position":[[199,4]]},"2363":{"position":[[152,4]]},"2367":{"position":[[125,4]]},"2369":{"position":[[226,4],[671,4],[1331,4]]},"2375":{"position":[[140,4]]},"2454":{"position":[[439,4]]},"2518":{"position":[[1059,4]]},"2532":{"position":[[1035,4],[1194,4],[1681,4]]},"2562":{"position":[[136,4]]},"2594":{"position":[[320,4]]},"2662":{"position":[[3752,4]]},"2666":{"position":[[537,4],[627,4]]},"2672":{"position":[[125,4],[719,4],[859,4]]},"2686":{"position":[[127,4],[384,4],[561,4]]},"2814":{"position":[[160,4]]},"2830":{"position":[[1705,4]]},"2846":{"position":[[1241,4]]},"2879":{"position":[[102,4],[144,4]]},"2942":{"position":[[203,4]]},"2962":{"position":[[228,4]]},"2964":{"position":[[449,4]]},"2972":{"position":[[808,4]]},"2986":{"position":[[190,4]]},"3056":{"position":[[23,4]]},"3064":{"position":[[744,4]]},"3088":{"position":[[615,4]]},"3259":{"position":[[1250,4]]},"3261":{"position":[[2594,4]]},"3345":{"position":[[744,4]]},"3347":{"position":[[1333,4],[3438,4]]},"3349":{"position":[[3842,4]]},"3351":{"position":[[81,4]]},"3375":{"position":[[522,4]]},"3389":{"position":[[403,4]]},"3588":{"position":[[185,4]]},"3595":{"position":[[235,4]]},"3612":{"position":[[2176,4]]},"3629":{"position":[[635,4]]},"3633":{"position":[[4195,4],[5618,4]]},"3635":{"position":[[142,4]]},"3639":{"position":[[105,4]]},"3641":{"position":[[58,4]]},"3643":{"position":[[369,4],[1031,5],[3640,4]]},"3647":{"position":[[3059,4]]},"3649":{"position":[[199,4]]},"3651":{"position":[[152,4]]},"3655":{"position":[[125,4]]},"3657":{"position":[[226,4],[671,4],[1331,4]]},"3663":{"position":[[140,4]]}}}],["uppercas",{"_index":4024,"t":{"878":{"position":[[132,11]]},"1945":{"position":[[132,11]]},"3158":{"position":[[132,11]]}}}],["uprad",{"_index":4790,"t":{"1499":{"position":[[951,6]]},"2630":{"position":[[951,6]]}}}],["upstream",{"_index":4110,"t":{"953":{"position":[[386,8]]},"1001":{"position":[[0,8],[78,8]]},"1003":{"position":[[0,8],[78,8]]},"1149":{"position":[[616,9],[626,8],[794,8],[1151,8]]},"2080":{"position":[[386,8]]},"2108":{"position":[[0,8],[78,8]]},"2110":{"position":[[0,8],[78,8]]},"2277":{"position":[[616,9],[626,8],[794,8],[1151,8]]},"3247":{"position":[[386,8]]},"3309":{"position":[[0,8],[78,8]]},"3311":{"position":[[0,8],[78,8]]},"3504":{"position":[[616,9],[626,8],[794,8],[1151,8]]}}}],["uptim",{"_index":4460,"t":{"1131":{"position":[[445,9]]},"2212":{"position":[[445,9]]},"3453":{"position":[[261,9]]}}}],["uri",{"_index":2775,"t":{"297":{"position":[[388,4]]},"1143":{"position":[[407,3]]},"2224":{"position":[[407,3]]},"3465":{"position":[[407,3]]}}}],["url",{"_index":1413,"t":{"82":{"position":[[255,3]]},"94":{"position":[[1864,3],[2002,3]]},"100":{"position":[[1052,3]]},"128":{"position":[[1548,3]]},"130":{"position":[[2939,3]]},"134":{"position":[[135,3]]},"146":{"position":[[658,6]]},"158":{"position":[[439,3]]},"166":{"position":[[1181,3]]},"214":{"position":[[33,3]]},"228":{"position":[[311,4],[459,4]]},"287":{"position":[[394,3]]},"301":{"position":[[830,4]]},"560":{"position":[[700,3]]},"764":{"position":[[1306,3]]},"918":{"position":[[926,3]]},"930":{"position":[[165,3],[258,3],[342,3],[448,3],[573,3],[689,3],[758,3],[840,3],[919,3]]},"995":{"position":[[60,3]]},"1065":{"position":[[241,3],[1843,3]]},"1107":{"position":[[303,3]]},"1158":{"position":[[88,3]]},"1213":{"position":[[137,3],[186,3],[299,4],[323,3],[607,3],[1062,3]]},"1285":{"position":[[697,4],[711,3]]},"1499":{"position":[[1132,3]]},"1783":{"position":[[1306,3]]},"1993":{"position":[[1141,3]]},"2005":{"position":[[165,3],[258,3],[360,3],[447,3],[531,3],[637,3],[762,3],[878,3],[947,3],[1029,3],[1108,3]]},"2056":{"position":[[60,3]]},"2146":{"position":[[241,3],[1960,3]]},"2188":{"position":[[306,3]]},"2286":{"position":[[88,3]]},"2387":{"position":[[137,3],[186,3],[299,4],[323,3],[607,3],[1062,3]]},"2432":{"position":[[697,4],[711,3]]},"2630":{"position":[[1132,3]]},"3208":{"position":[[1141,3]]},"3222":{"position":[[165,3],[258,3],[360,3],[447,3],[618,3],[724,3],[849,3],[965,3],[1034,3],[1116,3],[1195,3]]},"3295":{"position":[[60,3]]},"3375":{"position":[[241,3],[1960,3]]},"3427":{"position":[[300,3]]},"3476":{"position":[[880,3]]},"3513":{"position":[[88,3]]},"3518":{"position":[[849,3]]},"3601":{"position":[[137,3],[186,3],[299,4],[323,3],[607,3],[1062,3]]},"3627":{"position":[[697,4],[711,3]]},"3629":{"position":[[30,3]]}}}],["url('http://localhost:8000/connection/uni_sse');url.searchparams.append(\"cf_connect",{"_index":4626,"t":{"1213":{"position":[[333,85]]},"2387":{"position":[[333,85]]},"3601":{"position":[[333,85]]}}}],["url.parse(indication.origin",{"_index":1760,"t":{"110":{"position":[[4256,28]]}}}],["urlconf",{"_index":1882,"t":{"128":{"position":[[1578,8],[1599,7],[2001,7]]}}}],["urlpattern",{"_index":1894,"t":{"128":{"position":[[2127,11]]}}}],["urls.pi",{"_index":1815,"t":{"124":{"position":[[365,7]]},"128":{"position":[[1651,8],[1791,7]]},"130":{"position":[[281,10]]}}}],["us",{"_index":84,"t":{"2":{"position":[[1012,4],[1127,5],[1188,4],[1332,4],[2194,4],[2364,4],[3161,3]]},"4":{"position":[[545,3]]},"8":{"position":[[64,4],[345,5],[940,6]]},"10":{"position":[[394,4],[1075,5]]},"12":{"position":[[472,5],[1026,3],[1131,3],[1183,3],[1282,3]]},"14":{"position":[[160,3],[669,3],[892,3]]},"16":{"position":[[94,3],[3154,3],[3359,3],[6276,5],[6678,3],[7353,3],[7507,3]]},"18":{"position":[[230,4],[485,3],[954,5],[1037,7],[1564,5],[1963,3],[3481,5]]},"22":{"position":[[32,6]]},"26":{"position":[[601,4],[799,4],[1038,7],[1143,4]]},"28":{"position":[[371,4],[688,3],[992,6],[1234,4],[1311,5],[1950,3],[2008,3]]},"30":{"position":[[118,6],[985,6]]},"32":{"position":[[1214,3]]},"34":{"position":[[2040,3]]},"36":{"position":[[254,3],[539,3]]},"42":{"position":[[1200,4],[1330,6],[1446,6],[1865,6]]},"44":{"position":[[173,6],[1035,5],[1329,5]]},"46":{"position":[[2094,4],[2126,3],[2177,5],[2662,4],[2785,5],[3499,5],[3826,3]]},"50":{"position":[[523,3]]},"52":{"position":[[447,4]]},"54":{"position":[[478,3],[527,3]]},"58":{"position":[[699,3]]},"64":{"position":[[433,3],[875,5]]},"68":{"position":[[383,5],[754,3],[1218,5],[1347,5],[1511,3],[1615,5],[1644,5]]},"70":{"position":[[660,6],[773,6]]},"72":{"position":[[30,4],[185,4]]},"74":{"position":[[93,6],[570,3]]},"78":{"position":[[394,6],[958,5]]},"82":{"position":[[355,5],[407,5]]},"86":{"position":[[190,3],[2236,5]]},"88":{"position":[[904,6]]},"90":{"position":[[750,4]]},"94":{"position":[[462,3],[504,5],[749,5],[928,5],[1860,3],[2374,3],[2525,3],[2603,4],[3053,3]]},"96":{"position":[[97,3]]},"98":{"position":[[163,5]]},"102":{"position":[[176,3]]},"106":{"position":[[145,5],[1578,4]]},"110":{"position":[[2248,3]]},"116":{"position":[[146,5],[904,5]]},"118":{"position":[[53,5],[289,3],[637,4]]},"120":{"position":[[1027,4]]},"122":{"position":[[547,4],[576,3],[666,5],[711,4]]},"128":{"position":[[2865,5]]},"130":{"position":[[2929,5]]},"132":{"position":[[20,3],[797,4],[1003,5],[1249,5],[1305,5],[1486,3]]},"134":{"position":[[404,4],[435,3],[489,6],[538,5],[1766,3],[1787,3]]},"136":{"position":[[2072,3]]},"138":{"position":[[97,4],[637,3],[755,5],[1375,5],[1462,5],[1776,5],[1843,3]]},"142":{"position":[[260,3],[336,4],[381,5],[547,3],[606,3]]},"144":{"position":[[58,5],[516,4]]},"146":{"position":[[108,5],[333,5],[553,5]]},"148":{"position":[[1377,4]]},"150":{"position":[[628,6]]},"156":{"position":[[50,3],[248,5]]},"158":{"position":[[24,5],[293,4],[619,3]]},"160":{"position":[[147,3],[656,5],[832,5]]},"162":{"position":[[3,3],[63,3],[835,6],[945,5],[1026,5]]},"164":{"position":[[235,5],[866,4]]},"166":{"position":[[91,3],[283,6],[339,6],[1668,5],[2394,3]]},"168":{"position":[[100,3],[498,6],[932,5],[970,3]]},"172":{"position":[[144,4]]},"174":{"position":[[325,5],[362,5],[383,5],[719,4]]},"176":{"position":[[1076,5]]},"178":{"position":[[122,5],[314,7],[2361,5],[2645,6],[2736,5],[2993,5]]},"180":{"position":[[407,5],[643,3],[677,5],[935,5],[1064,5],[1505,5]]},"182":{"position":[[1038,5],[1396,5]]},"184":{"position":[[108,5],[136,5]]},"188":{"position":[[7,5],[851,5],[919,5]]},"190":{"position":[[128,5]]},"192":{"position":[[312,5],[357,3],[500,5]]},"194":{"position":[[791,4],[1227,3]]},"196":{"position":[[410,5]]},"198":{"position":[[396,5]]},"200":{"position":[[517,6],[801,3],[842,5],[1004,3],[1248,3]]},"202":{"position":[[76,5]]},"204":{"position":[[591,5],[2295,3]]},"206":{"position":[[567,5]]},"208":{"position":[[108,5]]},"210":{"position":[[85,5],[323,5],[383,5],[417,4],[678,4],[774,3]]},"214":{"position":[[90,5],[202,4],[1028,3],[1677,4],[1723,5],[1746,5]]},"216":{"position":[[479,6],[772,3]]},"222":{"position":[[123,3],[510,5]]},"226":{"position":[[284,4],[523,4]]},"230":{"position":[[206,6],[595,5]]},"236":{"position":[[884,5],[1242,5],[1333,3]]},"240":{"position":[[348,5],[653,5],[690,5],[1058,6]]},"244":{"position":[[59,5],[218,5],[288,4]]},"246":{"position":[[430,3],[927,6],[1270,3],[1533,5],[1669,3],[2531,5],[3315,5]]},"250":{"position":[[701,5],[996,6],[1340,3],[1423,5]]},"252":{"position":[[297,5],[344,5],[854,5],[1411,5]]},"254":{"position":[[434,4],[1570,3]]},"256":{"position":[[558,3]]},"260":{"position":[[162,5],[269,3]]},"262":{"position":[[251,5],[970,3],[1293,3],[1810,4],[1989,5],[2345,3],[2439,5],[2526,3]]},"264":{"position":[[1068,3],[1226,6]]},"266":{"position":[[69,5],[304,5],[529,3]]},"270":{"position":[[174,3]]},"275":{"position":[[31,4]]},"277":{"position":[[338,5],[570,5]]},"279":{"position":[[146,5],[667,4],[1494,4]]},"281":{"position":[[422,4]]},"283":{"position":[[2219,3],[2647,5]]},"287":{"position":[[22,3],[574,3],[664,5]]},"289":{"position":[[247,3],[957,5]]},"291":{"position":[[22,6],[163,3],[238,4],[282,5],[453,3]]},"293":{"position":[[349,5],[486,5]]},"297":{"position":[[20,5]]},"299":{"position":[[21,5],[502,4],[577,3],[1057,5]]},"301":{"position":[[34,5],[454,3],[494,4],[1173,3],[1315,6],[1337,5],[2199,3],[2917,5]]},"307":{"position":[[407,3]]},"309":{"position":[[23,4]]},"311":{"position":[[20,5]]},"317":{"position":[[380,5],[427,5],[475,4]]},"319":{"position":[[535,5],[756,5],[1008,5],[1139,3]]},"323":{"position":[[279,3],[354,5],[560,5],[648,5]]},"325":{"position":[[131,3],[230,5],[336,5]]},"329":{"position":[[102,6]]},"331":{"position":[[192,5]]},"333":{"position":[[51,3]]},"335":{"position":[[892,5]]},"339":{"position":[[297,5],[414,5],[605,5],[1471,6]]},"341":{"position":[[38,3],[472,3]]},"343":{"position":[[92,5],[427,4]]},"347":{"position":[[1394,4],[2056,3]]},"349":{"position":[[108,5]]},"351":{"position":[[566,5]]},"359":{"position":[[108,4],[243,5],[1289,5]]},"361":{"position":[[684,3],[814,4],[1339,3],[1636,4]]},"363":{"position":[[837,5]]},"367":{"position":[[112,3]]},"369":{"position":[[130,5]]},"371":{"position":[[336,3],[473,3],[1040,5]]},"373":{"position":[[784,4],[1220,3]]},"377":{"position":[[286,4]]},"379":{"position":[[478,3],[592,4],[934,5],[1040,5],[1098,3],[1151,4],[1278,4],[1368,5],[1509,3],[2006,6],[2415,5],[3045,5],[3279,3],[3400,5],[3448,3],[3900,5]]},"381":{"position":[[578,3]]},"383":{"position":[[265,6]]},"385":{"position":[[123,5],[345,3],[963,5]]},"389":{"position":[[933,3]]},"391":{"position":[[618,4],[1008,5]]},"393":{"position":[[513,4],[682,3],[830,6],[915,4]]},"397":{"position":[[266,3]]},"399":{"position":[[599,3],[785,5],[849,3],[1175,5]]},"403":{"position":[[367,3],[560,3]]},"407":{"position":[[1317,4]]},"411":{"position":[[41,4]]},"413":{"position":[[56,4]]},"417":{"position":[[163,3]]},"419":{"position":[[349,5],[447,5]]},"421":{"position":[[40,5]]},"423":{"position":[[83,5]]},"439":{"position":[[52,5]]},"443":{"position":[[474,6],[569,6]]},"449":{"position":[[302,5]]},"453":{"position":[[377,5],[658,3],[745,3],[906,5],[990,5]]},"457":{"position":[[577,5],[721,5]]},"463":{"position":[[163,5]]},"469":{"position":[[791,5],[841,4],[1480,4]]},"473":{"position":[[51,5],[281,5],[649,5],[1161,5],[1305,5],[4013,5]]},"475":{"position":[[285,4],[1521,4],[1788,3],[1896,4],[1958,5],[2006,4],[2223,4]]},"477":{"position":[[104,4],[303,4],[642,6],[885,3],[950,3],[2834,4],[3046,3],[5949,3],[6513,3]]},"479":{"position":[[1915,3],[1947,3],[2200,4],[3022,4],[5569,5]]},"481":{"position":[[1514,4],[1740,5],[1969,3],[2279,5],[2634,5],[3508,5],[3766,5],[4916,3],[5186,3],[5453,3],[6384,5]]},"483":{"position":[[162,3],[258,5],[358,4]]},"485":{"position":[[344,5],[1566,3]]},"491":{"position":[[373,5]]},"497":{"position":[[494,5]]},"541":{"position":[[497,3]]},"545":{"position":[[46,4],[169,4]]},"548":{"position":[[57,5]]},"552":{"position":[[185,6]]},"554":{"position":[[280,5]]},"556":{"position":[[186,4],[387,5]]},"560":{"position":[[5,5],[333,5],[499,4],[632,3],[678,5],[766,3],[896,4]]},"566":{"position":[[19,3],[205,3]]},"568":{"position":[[50,3]]},"572":{"position":[[147,5],[194,3],[451,5]]},"578":{"position":[[417,4]]},"588":{"position":[[65,3]]},"590":{"position":[[15,4]]},"592":{"position":[[8,3],[78,3]]},"600":{"position":[[529,5],[834,5],[953,5]]},"604":{"position":[[581,5],[680,3],[735,3]]},"618":{"position":[[181,4]]},"640":{"position":[[20,6]]},"646":{"position":[[204,3]]},"652":{"position":[[60,6],[335,5],[911,3]]},"654":{"position":[[104,6],[203,5],[970,3]]},"658":{"position":[[65,5]]},"670":{"position":[[471,3]]},"678":{"position":[[161,6]]},"680":{"position":[[46,5],[209,5]]},"682":{"position":[[996,5]]},"690":{"position":[[27,4]]},"692":{"position":[[52,4]]},"694":{"position":[[139,3],[637,5],[792,5]]},"696":{"position":[[457,3],[665,3],[804,5],[1015,6]]},"698":{"position":[[36,5]]},"700":{"position":[[208,3],[289,5],[329,5],[535,6]]},"704":{"position":[[279,5],[435,3]]},"710":{"position":[[532,5]]},"716":{"position":[[539,5],[582,4],[725,4]]},"718":{"position":[[324,4],[465,4]]},"728":{"position":[[97,5]]},"730":{"position":[[93,5]]},"734":{"position":[[90,7],[633,3],[1542,3],[1605,3],[1661,3]]},"738":{"position":[[11,4]]},"740":{"position":[[222,3]]},"742":{"position":[[614,3]]},"744":{"position":[[110,6]]},"746":{"position":[[84,6]]},"748":{"position":[[288,5]]},"750":{"position":[[276,5]]},"754":{"position":[[11,5],[80,3]]},"756":{"position":[[487,3]]},"758":{"position":[[190,5],[812,3]]},"768":{"position":[[202,3],[551,3],[589,5]]},"774":{"position":[[8,3],[107,6]]},"776":{"position":[[377,5],[455,3]]},"780":{"position":[[332,3]]},"786":{"position":[[138,4]]},"808":{"position":[[181,3]]},"810":{"position":[[103,6]]},"816":{"position":[[93,4]]},"856":{"position":[[15,3],[240,6]]},"870":{"position":[[141,5]]},"876":{"position":[[133,5],[171,5]]},"878":{"position":[[337,5],[426,3],[501,3],[928,3]]},"884":{"position":[[228,4],[316,5],[349,3],[385,4]]},"886":{"position":[[351,6]]},"888":{"position":[[204,3]]},"892":{"position":[[176,4],[1132,5]]},"898":{"position":[[10,3]]},"914":{"position":[[133,3]]},"922":{"position":[[222,6]]},"924":{"position":[[0,3]]},"926":{"position":[[267,4],[322,4],[410,4],[468,4],[789,5],[922,3]]},"932":{"position":[[378,5]]},"935":{"position":[[175,6],[281,3]]},"937":{"position":[[25,5],[147,5],[344,6],[413,5]]},"939":{"position":[[25,5],[263,3]]},"941":{"position":[[40,5],[142,3]]},"945":{"position":[[463,3],[503,4]]},"949":{"position":[[218,6],[528,5]]},"951":{"position":[[889,3]]},"953":{"position":[[283,5],[895,3]]},"961":{"position":[[584,4],[783,5],[1308,4]]},"963":{"position":[[1222,6],[1335,6]]},"965":{"position":[[714,4],[2684,3],[2756,5],[2791,3],[3704,5]]},"969":{"position":[[0,4],[181,5]]},"971":{"position":[[344,4],[405,7],[473,5]]},"973":{"position":[[57,4],[194,3]]},"975":{"position":[[331,4],[548,3],[635,5],[1219,4],[1287,7],[1355,5]]},"977":{"position":[[48,5],[681,3],[865,3],[1391,3],[1587,3]]},"979":{"position":[[603,5],[679,3]]},"981":{"position":[[23,3],[243,3],[310,3]]},"983":{"position":[[531,3],[884,3],[1041,5],[1187,5],[1238,3]]},"985":{"position":[[68,5],[1144,4],[1264,3]]},"987":{"position":[[381,3],[709,3]]},"989":{"position":[[654,3],[1759,3]]},"995":{"position":[[169,4]]},"999":{"position":[[26,3]]},"1001":{"position":[[45,5]]},"1003":{"position":[[45,5]]},"1011":{"position":[[317,3]]},"1019":{"position":[[219,3],[291,5]]},"1027":{"position":[[378,5]]},"1035":{"position":[[44,3],[371,3],[476,3]]},"1041":{"position":[[741,6],[777,3],[1144,3]]},"1049":{"position":[[391,5],[983,4],[1669,6]]},"1063":{"position":[[35,4]]},"1065":{"position":[[636,5],[1110,3],[1545,4],[2181,3],[2601,4],[2691,4],[3147,5],[3928,5],[5522,5],[5632,4],[5744,5]]},"1067":{"position":[[898,4],[984,4],[1593,5]]},"1069":{"position":[[864,4],[954,4],[1124,3]]},"1071":{"position":[[585,3],[688,5],[737,5],[2188,4],[2278,4],[2837,6],[3097,5]]},"1073":{"position":[[2220,4],[2310,4]]},"1075":{"position":[[169,3]]},"1077":{"position":[[224,3]]},"1079":{"position":[[656,3],[1263,3]]},"1081":{"position":[[218,5],[281,6],[386,6]]},"1085":{"position":[[147,4]]},"1087":{"position":[[103,3],[288,3],[810,6],[911,3],[1075,6],[1133,5],[1246,3],[1330,5]]},"1089":{"position":[[297,4],[354,3],[424,3],[508,5],[736,3]]},"1091":{"position":[[155,3]]},"1093":{"position":[[5,5],[1153,4],[1671,3],[1912,5]]},"1095":{"position":[[77,3],[320,4],[366,5]]},"1097":{"position":[[704,4],[837,4],[1055,5]]},"1099":{"position":[[387,3],[459,3],[493,4],[938,5]]},"1101":{"position":[[246,3],[314,3]]},"1105":{"position":[[263,3],[310,3]]},"1107":{"position":[[375,3],[619,5]]},"1109":{"position":[[94,3],[312,5],[718,5]]},"1113":{"position":[[271,4]]},"1119":{"position":[[50,6],[92,6]]},"1129":{"position":[[805,4]]},"1133":{"position":[[85,3]]},"1137":{"position":[[90,5],[149,3],[226,6],[583,5],[710,5],[986,3]]},"1139":{"position":[[327,4]]},"1147":{"position":[[57,5]]},"1149":{"position":[[26,3],[497,3],[1061,5],[1108,4]]},"1151":{"position":[[95,3]]},"1153":{"position":[[100,3]]},"1158":{"position":[[150,4]]},"1162":{"position":[[131,3],[269,5],[353,5],[2256,5]]},"1164":{"position":[[531,4],[708,5],[912,5],[1154,3],[1497,4],[1852,4],[1984,5],[3019,6]]},"1166":{"position":[[186,5],[767,5],[899,5]]},"1184":{"position":[[355,5]]},"1188":{"position":[[577,4]]},"1200":{"position":[[119,4],[233,3]]},"1207":{"position":[[53,5]]},"1213":{"position":[[728,3],[831,6],[909,6],[1045,5]]},"1217":{"position":[[137,3]]},"1222":{"position":[[89,5]]},"1245":{"position":[[38,4],[161,3]]},"1249":{"position":[[169,5]]},"1251":{"position":[[183,4],[248,4]]},"1255":{"position":[[11,4],[126,3]]},"1262":{"position":[[53,5],[1390,6],[1422,5]]},"1266":{"position":[[185,3]]},"1268":{"position":[[47,3],[224,3],[770,5]]},"1270":{"position":[[929,5]]},"1283":{"position":[[369,4],[854,3],[1029,3],[1381,4],[1463,3]]},"1285":{"position":[[23,3],[66,4],[253,3],[625,3],[825,3],[1019,5],[1097,3]]},"1289":{"position":[[31,4]]},"1293":{"position":[[407,3]]},"1295":{"position":[[23,4]]},"1297":{"position":[[20,5]]},"1303":{"position":[[380,5],[427,5],[475,4]]},"1305":{"position":[[535,5],[756,5],[1008,5],[1139,3]]},"1309":{"position":[[279,3],[354,5],[560,5],[648,5]]},"1311":{"position":[[131,3],[230,5],[336,5]]},"1315":{"position":[[102,6]]},"1317":{"position":[[252,5]]},"1321":{"position":[[51,3]]},"1323":{"position":[[969,5]]},"1327":{"position":[[297,5],[414,5],[605,5],[1471,6]]},"1329":{"position":[[38,3],[472,3]]},"1331":{"position":[[92,5],[427,4]]},"1335":{"position":[[1398,4],[2060,3]]},"1337":{"position":[[108,5]]},"1339":{"position":[[566,5]]},"1341":{"position":[[156,5],[453,5]]},"1343":{"position":[[15,3]]},"1353":{"position":[[310,5],[627,5]]},"1355":{"position":[[273,5]]},"1359":{"position":[[494,5]]},"1379":{"position":[[108,4],[243,5],[1289,5]]},"1381":{"position":[[684,3],[819,4],[1344,3],[1641,4]]},"1383":{"position":[[857,5]]},"1387":{"position":[[112,3]]},"1389":{"position":[[130,5]]},"1391":{"position":[[336,3],[473,3],[1040,5]]},"1395":{"position":[[41,4]]},"1401":{"position":[[165,3]]},"1403":{"position":[[468,5],[566,5]]},"1405":{"position":[[40,5]]},"1407":{"position":[[83,5]]},"1423":{"position":[[52,5]]},"1431":{"position":[[670,3]]},"1449":{"position":[[302,5]]},"1453":{"position":[[378,5],[659,3],[746,3],[907,5],[991,5]]},"1457":{"position":[[573,5],[717,5]]},"1463":{"position":[[163,5]]},"1465":{"position":[[254,6]]},"1475":{"position":[[1863,5]]},"1477":{"position":[[164,5]]},"1479":{"position":[[142,5],[605,5],[749,5]]},"1481":{"position":[[17,3],[243,4]]},"1489":{"position":[[376,5]]},"1491":{"position":[[177,5]]},"1493":{"position":[[229,5],[429,5]]},"1495":{"position":[[456,5],[1227,5],[3240,5],[3315,3],[4319,3],[5945,5],[6115,5],[7654,3],[9783,5],[10292,7]]},"1499":{"position":[[43,5],[543,5],[1232,4],[1396,3],[1621,5],[1720,3]]},"1501":{"position":[[555,5]]},"1505":{"position":[[336,3],[366,3]]},"1507":{"position":[[976,5]]},"1511":{"position":[[203,3]]},"1513":{"position":[[141,5]]},"1515":{"position":[[335,4]]},"1521":{"position":[[60,3],[469,5]]},"1523":{"position":[[37,3],[951,5]]},"1533":{"position":[[669,5],[768,3],[823,3]]},"1549":{"position":[[574,4]]},"1553":{"position":[[65,3]]},"1555":{"position":[[15,4]]},"1557":{"position":[[8,3],[78,3]]},"1567":{"position":[[532,4],[1164,3]]},"1569":{"position":[[65,5],[345,5]]},"1573":{"position":[[23,5]]},"1577":{"position":[[126,4]]},"1579":{"position":[[1091,4]]},"1585":{"position":[[417,4]]},"1611":{"position":[[749,3]]},"1613":{"position":[[58,3]]},"1615":{"position":[[176,3]]},"1617":{"position":[[20,6]]},"1619":{"position":[[307,5],[811,5],[1116,5],[1235,5]]},"1627":{"position":[[271,5]]},"1629":{"position":[[1141,3],[1299,6]]},"1631":{"position":[[358,5]]},"1633":{"position":[[82,5],[312,5]]},"1635":{"position":[[910,5]]},"1637":{"position":[[57,3]]},"1639":{"position":[[30,4],[225,4],[858,3],[1026,5]]},"1666":{"position":[[1149,4]]},"1668":{"position":[[168,3],[427,5],[483,5],[775,3]]},"1676":{"position":[[187,4],[819,4]]},"1678":{"position":[[154,4]]},"1680":{"position":[[868,4]]},"1682":{"position":[[1080,3]]},"1686":{"position":[[65,5]]},"1692":{"position":[[204,3]]},"1699":{"position":[[60,6],[335,5],[911,3]]},"1702":{"position":[[104,6],[203,5],[868,4],[1016,3]]},"1715":{"position":[[471,3]]},"1723":{"position":[[15,3],[240,6]]},"1729":{"position":[[161,6]]},"1731":{"position":[[46,5],[209,5]]},"1733":{"position":[[996,5]]},"1741":{"position":[[247,3],[273,3],[1291,5],[1447,4],[1458,3],[1658,5],[1700,3]]},"1743":{"position":[[28,3],[374,5],[745,3],[795,3],[999,3],[1146,3],[1541,7]]},"1745":{"position":[[233,3],[450,3]]},"1747":{"position":[[376,3],[597,3]]},"1749":{"position":[[71,5]]},"1751":{"position":[[78,5]]},"1753":{"position":[[104,5]]},"1757":{"position":[[30,4]]},"1759":{"position":[[222,3]]},"1761":{"position":[[614,3]]},"1763":{"position":[[110,6]]},"1765":{"position":[[84,6]]},"1767":{"position":[[256,5],[341,3]]},"1769":{"position":[[244,5],[329,3]]},"1773":{"position":[[11,5],[80,3]]},"1777":{"position":[[190,5]]},"1787":{"position":[[202,3],[551,3],[589,5]]},"1793":{"position":[[8,3],[107,6]]},"1795":{"position":[[377,5],[455,3],[564,5],[644,5],[992,4]]},"1797":{"position":[[97,5],[132,3],[906,3],[983,5],[1079,4],[1255,5],[1340,3]]},"1801":{"position":[[32,4]]},"1811":{"position":[[378,5]]},"1819":{"position":[[110,6]]},"1821":{"position":[[84,6]]},"1825":{"position":[[44,3],[327,3],[432,3]]},"1827":{"position":[[69,5]]},"1831":{"position":[[332,3]]},"1837":{"position":[[135,4]]},"1859":{"position":[[181,3],[459,6]]},"1861":{"position":[[96,4]]},"1865":{"position":[[268,4]]},"1869":{"position":[[485,5],[918,5],[1123,5]]},"1871":{"position":[[27,4]]},"1873":{"position":[[52,4]]},"1875":{"position":[[457,3],[708,6],[1053,5]]},"1881":{"position":[[90,7],[633,3],[1425,3],[1488,3],[1544,3]]},"1883":{"position":[[36,5]]},"1885":{"position":[[1538,3]]},"1887":{"position":[[517,5],[915,5]]},"1891":{"position":[[813,3]]},"1893":{"position":[[1400,5],[1424,5]]},"1895":{"position":[[631,5],[683,4],[826,4]]},"1897":{"position":[[460,4],[601,4]]},"1903":{"position":[[588,5],[663,5],[802,6]]},"1905":{"position":[[561,5],[636,5]]},"1921":{"position":[[62,5]]},"1923":{"position":[[417,3],[462,6],[970,5],[1047,4]]},"1931":{"position":[[97,5]]},"1933":{"position":[[93,5]]},"1935":{"position":[[101,5]]},"1943":{"position":[[133,5],[171,5]]},"1945":{"position":[[337,5],[426,3],[501,3],[928,3]]},"1951":{"position":[[228,4],[316,5],[349,3],[385,4]]},"1953":{"position":[[351,6]]},"1955":{"position":[[204,3]]},"1959":{"position":[[176,4],[1132,5]]},"1965":{"position":[[10,3]]},"1977":{"position":[[386,5]]},"1983":{"position":[[244,3]]},"1989":{"position":[[210,3]]},"1997":{"position":[[222,6]]},"1999":{"position":[[0,3]]},"2001":{"position":[[267,4],[322,4],[410,4],[468,4],[789,5],[922,3]]},"2007":{"position":[[378,5]]},"2010":{"position":[[264,6],[389,3]]},"2012":{"position":[[25,5],[147,5],[344,6],[413,5]]},"2014":{"position":[[25,5],[263,3]]},"2016":{"position":[[40,5],[142,3]]},"2020":{"position":[[463,3],[503,4]]},"2024":{"position":[[0,4],[181,5]]},"2026":{"position":[[126,5],[330,4],[390,7],[458,5],[763,4]]},"2028":{"position":[[57,4],[194,3]]},"2030":{"position":[[297,4],[364,3],[451,5],[692,5],[768,4],[1174,5],[1373,4],[1433,7],[1501,5],[1753,4]]},"2032":{"position":[[527,3],[719,3],[860,4],[944,3],[1049,4]]},"2034":{"position":[[48,5],[681,3],[865,3],[1391,3],[1587,3]]},"2036":{"position":[[578,5],[654,3]]},"2038":{"position":[[36,3],[530,3],[740,3],[899,4],[983,3],[1106,4]]},"2040":{"position":[[23,3],[243,3],[310,3]]},"2042":{"position":[[531,3],[884,3],[1041,5],[1187,5],[1238,3]]},"2044":{"position":[[68,5],[1144,4],[1264,3]]},"2046":{"position":[[381,3],[709,3]]},"2048":{"position":[[195,3]]},"2050":{"position":[[654,3],[1759,3]]},"2056":{"position":[[169,4]]},"2062":{"position":[[153,6]]},"2066":{"position":[[441,6]]},"2068":{"position":[[502,6]]},"2070":{"position":[[141,5],[290,6]]},"2072":{"position":[[149,5],[298,6]]},"2076":{"position":[[218,6],[528,5]]},"2078":{"position":[[889,3]]},"2080":{"position":[[283,5],[895,3]]},"2086":{"position":[[461,3],[549,5]]},"2092":{"position":[[317,3]]},"2098":{"position":[[681,5],[839,5],[1374,4]]},"2100":{"position":[[1222,6],[1335,6]]},"2102":{"position":[[711,4],[3173,6],[3459,3],[3926,4],[4849,5]]},"2106":{"position":[[26,3]]},"2108":{"position":[[45,5]]},"2110":{"position":[[45,5]]},"2122":{"position":[[484,4]]},"2124":{"position":[[173,3]]},"2126":{"position":[[54,6],[301,3]]},"2132":{"position":[[741,6],[777,3],[1144,3]]},"2144":{"position":[[35,4]]},"2146":{"position":[[636,5],[1227,3],[1662,4],[2298,3],[2718,4],[2808,4],[3264,5],[4045,5],[5645,5],[5755,4],[5867,5]]},"2148":{"position":[[898,4],[984,4],[1593,5]]},"2150":{"position":[[864,4],[954,4],[1124,3]]},"2152":{"position":[[585,3],[688,5],[737,5],[2188,4],[2278,4],[2803,6],[3029,5]]},"2154":{"position":[[2220,4],[2310,4]]},"2156":{"position":[[810,4],[1255,3],[2299,4],[2385,4],[3060,5]]},"2158":{"position":[[168,3]]},"2160":{"position":[[204,3]]},"2162":{"position":[[656,3],[1263,3]]},"2164":{"position":[[218,5],[281,6],[386,6]]},"2168":{"position":[[147,4]]},"2170":{"position":[[103,3],[288,3],[810,6],[911,3],[1075,6],[1133,5],[1246,3],[1330,5]]},"2172":{"position":[[271,4],[328,3],[398,3],[482,5],[710,3]]},"2174":{"position":[[155,3]]},"2176":{"position":[[5,5],[1153,4],[1671,3],[1912,5]]},"2178":{"position":[[77,3],[320,4],[366,5]]},"2180":{"position":[[741,4],[874,4],[1019,4],[1271,5]]},"2182":{"position":[[387,3],[459,3],[493,4],[938,5]]},"2186":{"position":[[272,3],[319,3]]},"2188":{"position":[[378,3],[622,5]]},"2190":{"position":[[143,3],[372,5],[778,5],[2423,3],[2559,5]]},"2192":{"position":[[717,3],[853,5]]},"2194":{"position":[[271,4]]},"2200":{"position":[[50,6],[92,6]]},"2210":{"position":[[236,5],[847,4]]},"2214":{"position":[[85,3],[505,3]]},"2218":{"position":[[90,5],[149,3],[226,6],[583,5],[710,5],[986,3]]},"2220":{"position":[[327,4]]},"2230":{"position":[[391,5],[983,4],[1669,6]]},"2238":{"position":[[76,4],[409,4]]},"2242":{"position":[[1020,7]]},"2244":{"position":[[246,4],[423,5],[606,5],[843,4],[1187,4],[1548,4],[1782,5]]},"2258":{"position":[[31,5],[392,4],[610,3],[949,3],[1211,5]]},"2275":{"position":[[57,5]]},"2277":{"position":[[26,3],[497,3],[1061,5],[1108,4]]},"2279":{"position":[[95,3]]},"2281":{"position":[[100,3]]},"2286":{"position":[[150,4]]},"2290":{"position":[[185,3]]},"2292":{"position":[[47,3],[224,3],[773,5]]},"2294":{"position":[[1335,5]]},"2296":{"position":[[496,5],[731,5]]},"2303":{"position":[[88,5],[138,5]]},"2324":{"position":[[145,3]]},"2332":{"position":[[119,4],[233,3]]},"2339":{"position":[[53,5]]},"2345":{"position":[[261,4],[5102,5]]},"2351":{"position":[[1505,3]]},"2355":{"position":[[4655,5]]},"2357":{"position":[[549,3]]},"2361":{"position":[[163,4],[624,4],[696,6]]},"2367":{"position":[[1607,3]]},"2369":{"position":[[13,5],[158,3],[461,3]]},"2371":{"position":[[367,4]]},"2373":{"position":[[311,3]]},"2375":{"position":[[309,3]]},"2387":{"position":[[728,3],[831,6],[909,6],[1045,5]]},"2394":{"position":[[88,5],[138,5]]},"2400":{"position":[[169,5]]},"2402":{"position":[[183,4],[248,4]]},"2406":{"position":[[11,4],[113,3]]},"2413":{"position":[[53,5],[1469,6],[1501,5]]},"2417":{"position":[[31,4]]},"2430":{"position":[[369,4],[854,3],[1029,3],[1381,4],[1463,3]]},"2432":{"position":[[23,3],[66,4],[253,3],[625,3],[825,3],[1019,5],[1097,3]]},"2434":{"position":[[119,5],[366,3],[1497,5],[1566,3],[1882,5]]},"2440":{"position":[[407,3]]},"2442":{"position":[[23,4]]},"2444":{"position":[[20,5]]},"2450":{"position":[[380,5],[427,5],[475,4]]},"2452":{"position":[[535,5],[756,5],[1008,5],[1139,3]]},"2456":{"position":[[279,3],[354,5],[560,5],[648,5]]},"2458":{"position":[[131,3],[230,5],[336,5]]},"2462":{"position":[[102,6]]},"2464":{"position":[[252,5]]},"2468":{"position":[[51,3]]},"2470":{"position":[[969,5]]},"2474":{"position":[[297,5],[414,5],[605,5],[1471,6]]},"2476":{"position":[[38,3],[472,3]]},"2478":{"position":[[92,5],[427,4]]},"2482":{"position":[[191,6],[207,3],[1446,5],[2302,3]]},"2484":{"position":[[108,5]]},"2486":{"position":[[566,5]]},"2488":{"position":[[156,5],[453,5]]},"2490":{"position":[[15,3]]},"2498":{"position":[[97,3]]},"2500":{"position":[[138,5]]},"2502":{"position":[[173,4]]},"2504":{"position":[[231,4]]},"2506":{"position":[[477,4],[1180,5]]},"2510":{"position":[[494,5]]},"2530":{"position":[[1083,5],[2154,5],[2703,6]]},"2532":{"position":[[725,3],[870,4],[1431,3],[1504,5],[1815,4]]},"2534":{"position":[[895,5]]},"2538":{"position":[[112,3]]},"2540":{"position":[[130,5]]},"2542":{"position":[[340,3],[480,3],[1058,5]]},"2546":{"position":[[319,5],[668,5]]},"2548":{"position":[[273,5]]},"2552":{"position":[[41,4]]},"2558":{"position":[[166,3]]},"2560":{"position":[[183,6],[515,5],[692,5]]},"2564":{"position":[[83,5]]},"2580":{"position":[[59,5]]},"2590":{"position":[[313,5]]},"2594":{"position":[[387,5],[685,3],[784,3],[950,5],[1035,5]]},"2598":{"position":[[582,5],[731,5]]},"2604":{"position":[[164,5]]},"2606":{"position":[[265,6]]},"2610":{"position":[[813,5]]},"2614":{"position":[[690,3]]},"2630":{"position":[[43,5],[543,5],[1232,4],[1396,3],[1621,5],[1720,3]]},"2632":{"position":[[555,5]]},"2636":{"position":[[336,3],[366,3]]},"2638":{"position":[[976,5]]},"2642":{"position":[[203,3]]},"2644":{"position":[[141,5]]},"2646":{"position":[[335,4]]},"2656":{"position":[[7,5],[242,5],[396,5],[769,5]]},"2662":{"position":[[462,5],[1242,5],[3255,5],[3332,3],[4381,3],[6006,5],[6180,5],[7742,3],[9939,5],[10411,7]]},"2668":{"position":[[1863,5]]},"2670":{"position":[[164,5]]},"2672":{"position":[[142,5],[605,5],[749,5]]},"2674":{"position":[[17,3],[243,4]]},"2682":{"position":[[376,5]]},"2684":{"position":[[177,5]]},"2686":{"position":[[229,5],[429,5]]},"2690":{"position":[[60,3],[469,5]]},"2692":{"position":[[37,3],[951,5]]},"2702":{"position":[[669,5],[768,3],[823,3]]},"2718":{"position":[[574,4]]},"2720":{"position":[[175,6],[735,6]]},"2722":{"position":[[531,3],[735,5],[2713,5],[2752,3]]},"2726":{"position":[[532,4],[1164,3]]},"2728":{"position":[[65,5],[345,5]]},"2732":{"position":[[23,5]]},"2736":{"position":[[749,3]]},"2738":{"position":[[58,3]]},"2740":{"position":[[176,3]]},"2746":{"position":[[243,3]]},"2750":{"position":[[295,4]]},"2752":{"position":[[308,4]]},"2756":{"position":[[65,3],[281,3]]},"2758":{"position":[[15,4]]},"2760":{"position":[[8,3],[78,3]]},"2770":{"position":[[126,4]]},"2772":{"position":[[1091,4]]},"2774":{"position":[[468,5]]},"2782":{"position":[[416,4]]},"2804":{"position":[[20,6]]},"2806":{"position":[[307,5],[811,5],[1116,5],[1235,5]]},"2810":{"position":[[65,5]]},"2816":{"position":[[204,3]]},"2823":{"position":[[60,6],[335,5],[875,3]]},"2826":{"position":[[104,6],[203,5],[832,4],[980,3]]},"2830":{"position":[[187,4],[822,4]]},"2832":{"position":[[154,4]]},"2834":{"position":[[871,4]]},"2836":{"position":[[1080,3]]},"2844":{"position":[[271,5]]},"2846":{"position":[[1141,3],[1299,6]]},"2848":{"position":[[358,5]]},"2850":{"position":[[82,5],[312,5]]},"2852":{"position":[[910,5]]},"2854":{"position":[[57,3]]},"2856":{"position":[[30,4],[225,4],[858,3],[1026,5]]},"2862":{"position":[[388,5],[645,6],[850,6]]},"2873":{"position":[[1274,5]]},"2877":{"position":[[1260,5]]},"2883":{"position":[[452,4],[530,5]]},"2887":{"position":[[168,3],[427,5],[483,5],[775,3]]},"2889":{"position":[[508,6],[1045,6]]},"2904":{"position":[[435,3]]},"2912":{"position":[[15,3],[240,6]]},"2918":{"position":[[161,6]]},"2920":{"position":[[46,5],[209,5]]},"2922":{"position":[[960,5]]},"2930":{"position":[[247,3],[273,3],[1291,5],[1447,4],[1458,3],[1658,5],[1700,3]]},"2932":{"position":[[28,3],[374,5],[745,3],[795,3],[999,3],[1146,3],[1541,7]]},"2934":{"position":[[233,3],[450,3]]},"2936":{"position":[[376,3],[597,3]]},"2938":{"position":[[71,5]]},"2940":{"position":[[78,5]]},"2942":{"position":[[104,5]]},"2946":{"position":[[31,4]]},"2948":{"position":[[217,4]]},"2956":{"position":[[256,5]]},"2958":{"position":[[244,5]]},"2962":{"position":[[113,5]]},"2964":{"position":[[357,5]]},"2966":{"position":[[397,4]]},"2970":{"position":[[30,4],[256,4],[352,5]]},"2976":{"position":[[346,3],[695,3],[733,5]]},"2982":{"position":[[8,3],[107,6]]},"2984":{"position":[[377,5],[455,3],[564,5],[644,5],[1059,4]]},"2986":{"position":[[63,5],[98,3],[872,3],[949,5],[1045,4],[1221,5]]},"2990":{"position":[[32,4]]},"3000":{"position":[[378,5]]},"3008":{"position":[[110,6]]},"3010":{"position":[[84,6]]},"3014":{"position":[[44,3],[687,3],[792,3]]},"3016":{"position":[[69,5]]},"3018":{"position":[[159,4],[255,3],[636,4]]},"3022":{"position":[[332,3]]},"3028":{"position":[[135,4]]},"3034":{"position":[[240,5],[329,4],[489,5]]},"3050":{"position":[[181,3],[459,6]]},"3052":{"position":[[96,4]]},"3056":{"position":[[268,4]]},"3060":{"position":[[557,5],[995,5],[1260,5]]},"3062":{"position":[[27,4]]},"3064":{"position":[[52,4],[841,4]]},"3066":{"position":[[464,3],[1057,5]]},"3072":{"position":[[93,7],[921,3],[1713,3],[1776,3],[1832,3]]},"3074":{"position":[[36,5]]},"3076":{"position":[[1502,3]]},"3078":{"position":[[517,5],[915,5]]},"3082":{"position":[[813,3]]},"3084":{"position":[[1364,5],[1388,5]]},"3086":{"position":[[111,5],[332,4],[392,7],[460,5],[765,4],[827,4],[915,3]]},"3088":{"position":[[631,5],[683,4],[826,4]]},"3090":{"position":[[460,4],[601,4]]},"3096":{"position":[[588,5],[663,5],[802,6]]},"3098":{"position":[[561,5],[636,5]]},"3114":{"position":[[62,5]]},"3116":{"position":[[417,3],[462,6],[970,5],[1047,4]]},"3126":{"position":[[97,5]]},"3128":{"position":[[93,5]]},"3130":{"position":[[101,5]]},"3132":{"position":[[111,5]]},"3140":{"position":[[153,6]]},"3144":{"position":[[441,6]]},"3146":{"position":[[502,6]]},"3148":{"position":[[141,5],[290,6]]},"3150":{"position":[[149,5],[298,6]]},"3154":{"position":[[46,5]]},"3156":{"position":[[133,5],[171,5],[247,5]]},"3158":{"position":[[337,5],[426,3],[501,3],[928,3]]},"3164":{"position":[[228,4],[316,5],[349,3],[385,4]]},"3166":{"position":[[351,6]]},"3168":{"position":[[204,3]]},"3172":{"position":[[176,4],[1132,5]]},"3178":{"position":[[10,3]]},"3190":{"position":[[359,5]]},"3196":{"position":[[244,3]]},"3198":{"position":[[114,3],[847,5],[981,5],[1052,5],[1128,5]]},"3204":{"position":[[210,3]]},"3212":{"position":[[222,6]]},"3214":{"position":[[0,3]]},"3216":{"position":[[0,3]]},"3218":{"position":[[267,4],[322,4],[410,4],[468,4],[539,4],[879,5],[1012,3]]},"3224":{"position":[[378,5]]},"3227":{"position":[[264,6],[389,3]]},"3229":{"position":[[271,4]]},"3231":{"position":[[25,5],[147,5],[344,6],[413,5]]},"3233":{"position":[[25,5],[263,3]]},"3235":{"position":[[40,5],[142,3]]},"3239":{"position":[[463,3],[503,4]]},"3243":{"position":[[218,6],[528,5]]},"3245":{"position":[[889,3]]},"3247":{"position":[[283,5],[895,3]]},"3253":{"position":[[461,3],[549,5]]},"3257":{"position":[[681,5],[839,5],[1374,4]]},"3259":{"position":[[1222,6],[1335,6]]},"3261":{"position":[[711,4],[3173,6],[3459,3],[3926,4],[4849,5]]},"3265":{"position":[[0,4],[181,5]]},"3267":{"position":[[126,5],[330,4],[390,7],[458,5],[763,4]]},"3269":{"position":[[57,4],[194,3]]},"3271":{"position":[[297,4],[364,3],[451,5],[658,5],[734,4],[1140,5],[1339,4],[1399,7],[1467,5],[1719,4]]},"3273":{"position":[[500,3],[665,3],[779,4],[863,3],[941,4]]},"3275":{"position":[[48,5],[681,3],[865,3],[1391,3],[1587,3]]},"3277":{"position":[[578,5],[654,3]]},"3279":{"position":[[36,3],[503,3],[686,3],[818,4],[902,3],[998,4]]},"3281":{"position":[[23,3],[243,3],[310,3]]},"3283":{"position":[[531,3],[884,3],[1041,5],[1187,5],[1238,3]]},"3285":{"position":[[68,5],[1144,4],[1264,3]]},"3287":{"position":[[5,5],[238,4]]},"3289":{"position":[[654,3],[1759,3]]},"3295":{"position":[[169,4]]},"3301":{"position":[[317,3]]},"3307":{"position":[[26,3]]},"3309":{"position":[[45,5]]},"3311":{"position":[[45,5]]},"3320":{"position":[[317,3]]},"3324":{"position":[[208,3],[701,6],[1253,3],[1885,6],[2540,3],[3240,6],[3538,3],[4724,3],[5456,3]]},"3327":{"position":[[961,3]]},"3329":{"position":[[26,5],[183,4],[355,5]]},"3337":{"position":[[744,4]]},"3339":{"position":[[173,3]]},"3341":{"position":[[54,6],[299,3]]},"3345":{"position":[[0,5],[324,5],[595,3],[703,6],[917,5],[1332,3]]},"3347":{"position":[[68,5],[641,3],[2309,4],[2691,3],[2810,5],[2973,3],[3015,3],[3623,3],[4095,5],[4146,3]]},"3349":{"position":[[189,4],[206,5],[1168,3],[5011,5]]},"3351":{"position":[[281,5],[492,4],[570,3],[647,3]]},"3361":{"position":[[741,6],[777,3],[1144,3]]},"3371":{"position":[[1151,5]]},"3373":{"position":[[35,4]]},"3375":{"position":[[636,5],[1227,3],[1662,4],[2298,3],[2718,4],[2808,4],[3264,5],[4074,5],[5674,5],[5784,4],[5896,5]]},"3377":{"position":[[898,4],[984,4],[1593,5]]},"3379":{"position":[[911,4],[1001,4],[1171,3]]},"3381":{"position":[[715,3],[1991,4],[2081,4],[2606,6],[2832,5]]},"3383":{"position":[[2220,4],[2310,4]]},"3385":{"position":[[783,4],[1228,3],[2272,4],[2358,4],[3052,5]]},"3387":{"position":[[168,3]]},"3389":{"position":[[204,3]]},"3391":{"position":[[656,3],[1263,3]]},"3393":{"position":[[218,5],[281,6],[386,6],[494,6],[539,4]]},"3397":{"position":[[147,4]]},"3399":{"position":[[103,3],[288,3],[810,6],[911,3],[1075,6],[1133,5],[1246,3],[1330,5]]},"3401":{"position":[[271,4],[328,3],[398,3],[482,5],[710,3]]},"3403":{"position":[[155,3]]},"3405":{"position":[[5,5],[1153,4],[1883,3],[2124,5],[2337,4]]},"3407":{"position":[[77,3],[320,4],[366,5]]},"3409":{"position":[[741,4],[874,4],[1019,4],[1271,5]]},"3411":{"position":[[387,3],[459,3],[493,4],[938,5]]},"3417":{"position":[[391,5],[983,4],[1669,6]]},"3425":{"position":[[465,3],[512,3]]},"3427":{"position":[[553,3],[592,3]]},"3431":{"position":[[142,3],[1931,3],[2067,5]]},"3433":{"position":[[792,3],[928,5]]},"3435":{"position":[[339,4]]},"3441":{"position":[[50,6],[92,6]]},"3451":{"position":[[296,5],[907,4]]},"3455":{"position":[[158,6]]},"3459":{"position":[[90,5],[149,3],[226,6],[583,5],[710,5],[986,3]]},"3461":{"position":[[327,4]]},"3467":{"position":[[486,3],[1556,5],[1619,5],[1710,4]]},"3476":{"position":[[332,4],[502,3]]},"3482":{"position":[[220,3]]},"3484":{"position":[[47,3],[224,3],[773,5]]},"3486":{"position":[[496,5],[731,5]]},"3492":{"position":[[31,5],[392,4],[610,3],[949,3],[1211,5]]},"3502":{"position":[[57,5]]},"3504":{"position":[[26,3],[497,3],[1061,5],[1108,4]]},"3506":{"position":[[95,3]]},"3508":{"position":[[100,3]]},"3513":{"position":[[150,4]]},"3518":{"position":[[311,4],[482,3]]},"3520":{"position":[[88,5],[138,5]]},"3524":{"position":[[1335,5]]},"3545":{"position":[[124,3]]},"3549":{"position":[[76,4],[409,4]]},"3553":{"position":[[1020,7]]},"3555":{"position":[[246,4],[423,5],[606,5],[843,4],[1187,4],[1548,4],[1782,5]]},"3567":{"position":[[169,5]]},"3569":{"position":[[183,4],[248,4]]},"3573":{"position":[[11,4],[113,3]]},"3580":{"position":[[53,5],[1469,6],[1501,5]]},"3588":{"position":[[119,4],[233,3]]},"3595":{"position":[[53,5]]},"3601":{"position":[[728,3],[831,6],[909,6],[1045,5]]},"3608":{"position":[[88,5],[138,5]]},"3612":{"position":[[146,5],[393,3],[1524,5],[1593,3],[1909,5]]},"3625":{"position":[[369,4],[854,3],[1029,3],[1381,4],[1463,3]]},"3627":{"position":[[23,3],[66,4],[253,3],[625,3],[825,3],[1019,5],[1097,3]]},"3629":{"position":[[87,5],[199,4],[1029,3],[1678,4],[1724,5],[1747,5]]},"3633":{"position":[[261,4],[5102,5]]},"3639":{"position":[[1525,3]]},"3643":{"position":[[4655,5]]},"3645":{"position":[[549,3]]},"3649":{"position":[[163,4],[624,4],[696,6]]},"3655":{"position":[[1775,3]]},"3657":{"position":[[13,5],[158,3],[461,3]]},"3659":{"position":[[367,4]]},"3661":{"position":[[311,3]]},"3663":{"position":[[309,3]]}}}],["usabl",{"_index":5271,"t":{"2345":{"position":[[5650,6]]},"3633":{"position":[[5650,6]]}}}],["usag",{"_index":131,"t":{"2":{"position":[[2000,5],[2075,5],[2289,5],[2862,6],[2926,5],[3573,7]]},"8":{"position":[[750,5]]},"12":{"position":[[622,5],[1231,5]]},"16":{"position":[[4268,5],[4425,5]]},"54":{"position":[[1077,5],[1109,5]]},"70":{"position":[[1688,6]]},"86":{"position":[[1986,5]]},"88":{"position":[[990,6]]},"116":{"position":[[298,6]]},"250":{"position":[[611,5]]},"309":{"position":[[309,5],[549,5]]},"339":{"position":[[159,5]]},"361":{"position":[[21,5]]},"383":{"position":[[435,6],[1234,5]]},"389":{"position":[[1095,6]]},"399":{"position":[[1884,5]]},"471":{"position":[[219,5],[470,5]]},"479":{"position":[[5317,6]]},"481":{"position":[[721,5],[962,5],[2735,5],[3064,5],[3182,5],[3461,5],[6484,5],[6636,5],[6824,5]]},"485":{"position":[[570,5],[1424,6]]},"576":{"position":[[739,5],[870,5]]},"578":{"position":[[152,5]]},"622":{"position":[[158,5]]},"624":{"position":[[162,5]]},"626":{"position":[[160,5]]},"628":{"position":[[162,5]]},"640":{"position":[[95,5]]},"945":{"position":[[40,5],[269,6],[298,5],[421,5],[658,5]]},"951":{"position":[[624,5]]},"983":{"position":[[257,5]]},"1043":{"position":[[0,5]]},"1277":{"position":[[181,5]]},"1279":{"position":[[182,5]]},"1281":{"position":[[112,5]]},"1283":{"position":[[491,5]]},"1295":{"position":[[309,5],[549,5]]},"1327":{"position":[[159,5]]},"1381":{"position":[[21,5]]},"1511":{"position":[[838,5]]},"1583":{"position":[[1114,5],[1322,5]]},"1585":{"position":[[152,5]]},"1591":{"position":[[158,5]]},"1593":{"position":[[162,5]]},"1595":{"position":[[160,5]]},"1597":{"position":[[162,5]]},"1611":{"position":[[227,5]]},"1617":{"position":[[95,5]]},"1639":{"position":[[1240,6]]},"1903":{"position":[[699,7]]},"1905":{"position":[[672,7]]},"2020":{"position":[[40,5],[269,6],[298,5],[421,5],[658,5]]},"2042":{"position":[[257,5]]},"2078":{"position":[[624,5]]},"2134":{"position":[[0,5]]},"2296":{"position":[[1006,5]]},"2424":{"position":[[181,5]]},"2426":{"position":[[182,5]]},"2428":{"position":[[112,5]]},"2430":{"position":[[491,5]]},"2434":{"position":[[273,6],[2736,5]]},"2442":{"position":[[309,5],[549,5]]},"2474":{"position":[[159,5]]},"2532":{"position":[[21,5]]},"2642":{"position":[[838,5]]},"2720":{"position":[[168,6],[460,6],[728,6]]},"2722":{"position":[[399,5]]},"2736":{"position":[[227,5]]},"2778":{"position":[[1212,5],[1502,5]]},"2782":{"position":[[152,5]]},"2786":{"position":[[158,5]]},"2788":{"position":[[162,5]]},"2790":{"position":[[160,5]]},"2792":{"position":[[162,5]]},"2804":{"position":[[95,5]]},"2856":{"position":[[1240,6]]},"2889":{"position":[[245,6],[501,6],[776,6],[1038,6]]},"3096":{"position":[[699,7]]},"3098":{"position":[[672,7]]},"3198":{"position":[[1215,6]]},"3239":{"position":[[40,5],[269,6],[298,5],[421,5],[658,5]]},"3245":{"position":[[624,5]]},"3283":{"position":[[257,5]]},"3324":{"position":[[201,6],[453,6],[694,6],[739,5],[873,6],[1071,6],[1246,6],[1458,6],[1668,6],[1878,6],[2084,6],[2322,6],[2533,6],[2781,6],[2998,6],[3233,6],[3531,6],[3562,5],[3832,6],[4122,6],[4171,5],[4416,6],[4717,6],[4953,6],[5203,6],[5449,6],[5679,6],[5900,6],[6120,6]]},"3345":{"position":[[52,5]]},"3363":{"position":[[0,5]]},"3486":{"position":[[1006,5]]},"3612":{"position":[[300,6],[2935,5]]},"3619":{"position":[[181,5]]},"3621":{"position":[[182,5]]},"3623":{"position":[[112,5]]},"3625":{"position":[[491,5]]}}}],["usage_stats_dis",{"_index":4102,"t":{"945":{"position":[[674,19],[716,22]]},"2020":{"position":[[674,19],[716,22]]},"3239":{"position":[[674,19],[716,22]]}}}],["usd",{"_index":2502,"t":{"246":{"position":[[351,3]]}}}],["use_client_protocol_v1_by_default",{"_index":4789,"t":{"1499":{"position":[[819,34],[1305,33],[1439,33]]},"1501":{"position":[[187,33]]},"2630":{"position":[[819,34],[1305,33],[1439,33]]},"2632":{"position":[[187,33]]},"2658":{"position":[[140,33]]}}}],["use_redis_from_engin",{"_index":3816,"t":{"618":{"position":[[1197,21],[1405,24]]},"648":{"position":[[859,21]]},"666":{"position":[[722,21]]},"686":{"position":[[420,21],[630,24]]},"1680":{"position":[[1901,21],[2127,24]]},"1694":{"position":[[859,21]]},"1710":{"position":[[722,21]]},"1737":{"position":[[410,21],[612,24]]},"2818":{"position":[[859,21]]},"2834":{"position":[[1907,21],[2133,24]]},"2899":{"position":[[722,21]]},"2926":{"position":[[410,21],[612,24]]}}}],["use_singleflight",{"_index":3699,"t":{"600":{"position":[[66,16],[1005,19]]},"1619":{"position":[[66,16],[1287,19]]},"2806":{"position":[[66,16],[1287,19]]}}}],["use_unlimited_history_by_default",{"_index":3596,"t":{"541":{"position":[[363,32]]},"1515":{"position":[[275,32]]},"2646":{"position":[[275,32]]}}}],["useeffect",{"_index":2819,"t":{"301":{"position":[[1439,9],[2348,9],[2381,12],[3363,12],[3866,12]]}}}],["usekeycloak",{"_index":2823,"t":{"301":{"position":[[1555,11],[1664,13]]}}}],["useless",{"_index":4158,"t":{"983":{"position":[[989,7]]},"2042":{"position":[[989,7]]},"3283":{"position":[[989,7]]}}}],["user",{"_index":527,"t":{"14":{"position":[[29,5],[148,5],[255,5]]},"16":{"position":[[600,5],[1651,4],[1695,4],[2227,4]]},"18":{"position":[[384,5],[884,6]]},"26":{"position":[[713,6]]},"32":{"position":[[65,4],[357,4],[406,4]]},"34":{"position":[[945,5]]},"42":{"position":[[1919,4]]},"44":{"position":[[446,4],[1909,5]]},"46":{"position":[[476,5]]},"50":{"position":[[1815,5],[2323,6]]},"54":{"position":[[173,4],[865,5]]},"58":{"position":[[578,5]]},"62":{"position":[[860,4],[914,4]]},"64":{"position":[[423,5]]},"68":{"position":[[741,5],[1080,4]]},"74":{"position":[[599,5],[2034,5]]},"76":{"position":[[78,5],[371,5]]},"110":{"position":[[133,4]]},"120":{"position":[[832,6]]},"132":{"position":[[1827,4],[1907,4],[1968,4]]},"134":{"position":[[453,4],[461,4]]},"136":{"position":[[942,4],[1169,7],[1187,5],[1333,5],[1662,4],[2008,4],[2028,5],[2122,4],[2150,5],[2230,4]]},"138":{"position":[[92,4],[282,5],[329,5],[630,6],[700,4],[1202,5],[1282,6],[1873,4],[1893,4]]},"144":{"position":[[139,5]]},"146":{"position":[[833,4]]},"148":{"position":[[1066,4]]},"156":{"position":[[149,4],[378,4],[426,6],[447,5]]},"160":{"position":[[5,4],[757,4]]},"162":{"position":[[22,4],[98,4],[378,6],[709,5],[787,4],[806,4],[842,4],[934,4],[1067,4],[1174,4],[1394,4]]},"164":{"position":[[92,5],[210,5],[528,6],[538,6],[576,5]]},"166":{"position":[[185,4],[255,5],[500,5],[2011,4],[2280,4],[2310,4]]},"168":{"position":[[305,4],[313,4]]},"172":{"position":[[153,4],[194,4]]},"174":{"position":[[88,4],[231,4],[609,5],[737,4],[993,5],[1163,4]]},"176":{"position":[[318,4],[658,4],[823,4],[839,4],[890,4],[952,5]]},"178":{"position":[[781,4],[869,4],[3118,4]]},"180":{"position":[[1385,4]]},"182":{"position":[[1316,4]]},"186":{"position":[[79,4],[247,4],[466,5],[515,4],[568,4]]},"192":{"position":[[98,4]]},"194":{"position":[[265,4],[552,5],[720,4]]},"198":{"position":[[521,5],[932,4],[1198,6],[1241,6]]},"200":{"position":[[188,5],[443,5]]},"204":{"position":[[531,5],[2329,5]]},"210":{"position":[[172,5]]},"212":{"position":[[335,6]]},"218":{"position":[[411,4]]},"222":{"position":[[276,5]]},"228":{"position":[[29,4]]},"246":{"position":[[84,5],[1212,5],[1392,4],[3071,5]]},"252":{"position":[[819,4]]},"254":{"position":[[167,4],[615,5],[740,4],[1140,5],[1598,4]]},"260":{"position":[[46,4],[468,4],[530,4]]},"262":{"position":[[2737,4]]},"264":{"position":[[626,4],[709,4],[826,4],[868,4],[892,4],[1062,5],[1198,4],[1293,4]]},"268":{"position":[[193,5]]},"270":{"position":[[333,4]]},"279":{"position":[[194,4]]},"281":{"position":[[245,4],[516,4]]},"283":{"position":[[1106,5],[2270,5],[2328,4]]},"287":{"position":[[1726,5]]},"289":{"position":[[234,5]]},"293":{"position":[[9,4],[540,5]]},"297":{"position":[[472,4]]},"299":{"position":[[913,4]]},"301":{"position":[[3762,4],[4226,4]]},"319":{"position":[[488,4],[705,4],[944,4],[1049,4],[1187,6]]},"325":{"position":[[170,5],[280,5],[399,5],[502,6]]},"339":{"position":[[343,4],[1579,5]]},"341":{"position":[[93,4],[226,4],[348,4],[368,4],[634,4],[729,5]]},"347":{"position":[[979,4],[1357,4],[1621,4]]},"351":{"position":[[385,6],[744,4]]},"355":{"position":[[95,4],[609,4]]},"361":{"position":[[272,5]]},"367":{"position":[[57,5],[156,4],[397,4]]},"373":{"position":[[559,5],[713,4]]},"377":{"position":[[1122,5],[1511,5]]},"379":{"position":[[116,5],[466,5],[1065,5],[2912,5],[3015,5],[4283,5]]},"385":{"position":[[630,5]]},"393":{"position":[[132,5],[189,4],[303,4]]},"411":{"position":[[413,5]]},"425":{"position":[[33,5]]},"431":{"position":[[166,4]]},"443":{"position":[[176,4],[508,4],[657,5],[752,4]]},"451":{"position":[[291,6]]},"453":{"position":[[9,5],[703,4]]},"457":{"position":[[45,4],[229,5],[418,4],[554,6]]},"467":{"position":[[1052,6]]},"485":{"position":[[524,5]]},"491":{"position":[[126,6],[508,4]]},"513":{"position":[[404,5],[533,6]]},"560":{"position":[[202,5],[890,5]]},"568":{"position":[[37,5]]},"576":{"position":[[91,4],[189,4],[292,4],[355,6],[438,4],[483,4],[526,4],[569,5]]},"604":{"position":[[429,5]]},"606":{"position":[[205,6],[984,6]]},"608":{"position":[[358,6],[1347,6]]},"610":{"position":[[12,5],[451,5],[555,5],[628,5],[1086,5]]},"612":{"position":[[484,5]]},"618":{"position":[[100,4],[133,5],[909,5]]},"646":{"position":[[59,4]]},"652":{"position":[[257,5]]},"654":{"position":[[33,4],[117,4],[460,8],[572,4],[643,4],[659,4],[1214,4]]},"658":{"position":[[262,7]]},"662":{"position":[[30,4],[118,4],[416,5],[464,5],[650,4]]},"664":{"position":[[0,4]]},"666":{"position":[[491,5]]},"670":{"position":[[163,8],[241,4],[305,4],[321,4],[384,4],[541,5],[661,4]]},"672":{"position":[[165,8],[220,4],[284,4],[300,4],[327,4]]},"678":{"position":[[145,4]]},"680":{"position":[[405,9],[460,4],[531,5],[566,5],[600,4]]},"682":{"position":[[92,6],[309,9],[777,4],[856,4],[959,4],[1049,4],[1120,5],[1155,5],[1183,4],[1296,4],[1379,4],[1394,4]]},"684":{"position":[[21,4],[270,9],[325,4],[396,5],[431,5],[465,4]]},"686":{"position":[[160,4],[332,4],[852,5]]},"690":{"position":[[346,4]]},"692":{"position":[[442,4],[588,5]]},"694":{"position":[[561,4],[616,6],[645,4]]},"696":{"position":[[9,4],[84,5],[104,4],[230,4],[294,4],[401,4],[584,4],[703,4],[844,4],[928,4],[948,4],[1065,6],[1095,4],[1172,6],[1250,5]]},"704":{"position":[[67,4],[91,4],[110,4],[182,5],[197,4],[217,4],[460,4],[540,5]]},"706":{"position":[[216,4]]},"710":{"position":[[249,4]]},"722":{"position":[[184,4]]},"734":{"position":[[1865,4],[2005,4]]},"740":{"position":[[81,4],[104,4],[247,4]]},"742":{"position":[[683,5],[800,5],[1064,4]]},"756":{"position":[[258,6],[271,5]]},"758":{"position":[[336,4],[583,6],[596,5]]},"760":{"position":[[353,4]]},"764":{"position":[[1463,4],[1678,4],[1818,5],[1877,4]]},"768":{"position":[[491,6]]},"772":{"position":[[13,4]]},"836":{"position":[[164,4]]},"868":{"position":[[133,4]]},"906":{"position":[[52,4],[69,4],[302,4],[441,4]]},"910":{"position":[[177,4]]},"935":{"position":[[128,4]]},"971":{"position":[[450,4],[500,4]]},"975":{"position":[[253,4],[1332,4],[1382,4]]},"979":{"position":[[598,4]]},"991":{"position":[[602,5]]},"1019":{"position":[[356,4]]},"1041":{"position":[[43,4],[240,4],[295,4],[327,5],[344,4],[433,4],[1077,4],[1214,5]]},"1043":{"position":[[131,4],[328,4],[368,4],[430,4],[908,4]]},"1061":{"position":[[382,5],[610,4]]},"1065":{"position":[[812,5],[1319,4],[2008,8],[2089,5],[3478,4],[3493,4],[4894,4],[4949,4],[5170,7]]},"1067":{"position":[[1016,4],[1044,4]]},"1069":{"position":[[986,4],[1014,4]]},"1071":{"position":[[371,4],[471,5],[508,4],[890,4],[2310,4],[2338,4]]},"1073":{"position":[[2342,4],[2370,4]]},"1087":{"position":[[282,5]]},"1113":{"position":[[29,4],[117,4],[133,4],[202,4],[443,5],[493,4],[592,5]]},"1115":{"position":[[33,4],[120,7],[128,5],[212,4],[228,4],[301,4],[360,5],[448,5]]},"1117":{"position":[[34,4],[84,7],[92,5],[175,4],[191,4],[263,5],[351,5]]},"1119":{"position":[[26,4],[166,4],[182,4],[239,5],[324,5]]},"1121":{"position":[[622,7],[730,7],[1116,4],[1131,4]]},"1123":{"position":[[106,5],[122,4],[779,5]]},"1174":{"position":[[477,5]]},"1182":{"position":[[1405,4],[1664,5]]},"1188":{"position":[[257,4],[451,4]]},"1190":{"position":[[178,5]]},"1207":{"position":[[209,5],[417,5],[479,4],[1001,4]]},"1262":{"position":[[258,6],[523,4]]},"1305":{"position":[[488,4],[705,4],[944,4],[1049,4],[1187,6]]},"1311":{"position":[[170,5],[280,5],[399,5],[502,6]]},"1327":{"position":[[343,4],[1579,5]]},"1329":{"position":[[93,4],[226,4],[348,4],[368,4],[634,4],[729,5]]},"1335":{"position":[[983,4],[1361,4],[1625,4]]},"1339":{"position":[[385,6],[744,4]]},"1347":{"position":[[95,4],[609,4]]},"1375":{"position":[[404,5],[533,6]]},"1381":{"position":[[272,5]]},"1387":{"position":[[57,5],[156,4],[397,4]]},"1395":{"position":[[429,5]]},"1409":{"position":[[33,5]]},"1451":{"position":[[358,6]]},"1453":{"position":[[9,5],[704,4]]},"1457":{"position":[[42,4],[226,5],[415,4],[550,6]]},"1475":{"position":[[1354,4]]},"1479":{"position":[[648,4],[821,4],[877,4]]},"1489":{"position":[[192,5],[323,4]]},"1493":{"position":[[275,4],[511,4],[594,4]]},"1495":{"position":[[105,5],[5718,5],[6110,4],[6218,4],[6280,4]]},"1499":{"position":[[587,4],[683,4]]},"1507":{"position":[[51,4],[333,4],[432,4],[523,4]]},"1509":{"position":[[0,4]]},"1523":{"position":[[135,4],[307,4],[534,4],[574,4],[861,8],[1068,4],[1191,4],[1271,4],[1634,4]]},"1533":{"position":[[518,5]]},"1535":{"position":[[196,6],[895,6]]},"1537":{"position":[[354,6],[1033,6]]},"1539":{"position":[[349,6],[1329,6]]},"1541":{"position":[[402,6],[1256,6]]},"1543":{"position":[[523,6],[1497,6]]},"1545":{"position":[[12,5],[451,5],[555,5],[628,5],[1080,5]]},"1547":{"position":[[614,5]]},"1571":{"position":[[233,6]]},"1577":{"position":[[59,4],[115,4],[207,4],[1264,8],[1617,4]]},"1579":{"position":[[142,4],[172,4],[226,5],[343,4],[713,4],[736,4]]},"1583":{"position":[[100,4],[170,4],[483,4],[557,6],[634,4],[677,5]]},"1629":{"position":[[585,6],[800,4],[847,4],[939,4],[981,4],[1005,4],[1135,5],[1271,4],[1366,4]]},"1635":{"position":[[844,4],[887,4]]},"1650":{"position":[[391,4],[406,4],[590,4]]},"1652":{"position":[[53,4],[98,4],[222,5],[254,5],[380,4],[651,4],[667,4]]},"1654":{"position":[[58,4],[246,5],[288,4]]},"1656":{"position":[[431,5],[471,5],[1278,4],[1294,4]]},"1660":{"position":[[455,5]]},"1662":{"position":[[30,6],[43,4],[89,4],[161,5],[226,4],[242,4]]},"1664":{"position":[[5,4],[85,5],[118,5],[664,4],[680,4]]},"1676":{"position":[[1898,5]]},"1678":{"position":[[54,4],[281,4],[374,5],[402,4],[439,5]]},"1680":{"position":[[67,4],[197,4],[745,5],[773,4],[810,5],[982,4]]},"1682":{"position":[[163,5]]},"1686":{"position":[[262,7]]},"1692":{"position":[[59,4]]},"1699":{"position":[[257,5]]},"1702":{"position":[[33,4],[117,4],[460,8],[643,4],[659,4]]},"1706":{"position":[[30,4],[118,4],[416,5],[464,5],[650,4]]},"1708":{"position":[[0,4]]},"1710":{"position":[[491,5]]},"1715":{"position":[[163,8],[305,4],[321,4],[384,4],[541,5]]},"1717":{"position":[[165,8],[284,4],[300,4]]},"1729":{"position":[[145,4]]},"1731":{"position":[[405,9],[460,4],[531,5],[566,5],[600,4]]},"1733":{"position":[[92,6],[309,9],[777,4],[856,4],[959,4],[1049,4],[1120,5],[1155,5],[1183,4],[1296,4],[1379,4],[1394,4]]},"1735":{"position":[[21,4],[270,9],[325,4],[396,5],[431,5],[465,4]]},"1737":{"position":[[16,4],[150,4],[322,4],[834,5]]},"1741":{"position":[[251,4],[966,5],[1238,4],[1375,4],[1396,4],[1462,4],[1511,4],[1547,4],[1605,4],[2059,4],[2373,4],[2514,4]]},"1743":{"position":[[1558,4]]},"1745":{"position":[[725,4],[879,4]]},"1747":{"position":[[876,4],[1032,4]]},"1759":{"position":[[81,4],[104,4],[247,4],[434,6]]},"1761":{"position":[[683,5],[800,5],[1064,4]]},"1777":{"position":[[336,4]]},"1783":{"position":[[1463,4],[1678,4],[1818,5],[1877,4]]},"1787":{"position":[[491,6]]},"1791":{"position":[[13,4]]},"1803":{"position":[[81,4],[122,4],[249,4],[271,4]]},"1825":{"position":[[88,4]]},"1827":{"position":[[214,4]]},"1865":{"position":[[798,4]]},"1871":{"position":[[295,4]]},"1873":{"position":[[427,4],[573,5]]},"1875":{"position":[[9,4],[84,5],[104,4],[230,4],[294,4],[401,4],[537,4],[621,4],[641,4],[758,6],[788,4],[865,6],[943,5],[991,4]]},"1877":{"position":[[615,5]]},"1879":{"position":[[196,4],[226,4],[393,4],[429,4],[472,4],[661,4]]},"1881":{"position":[[1752,4],[1892,4]]},"1885":{"position":[[231,4],[411,5],[461,4],[851,7],[992,7],[1132,7]]},"1887":{"position":[[252,4]]},"1899":{"position":[[177,5],[434,4]]},"1901":{"position":[[98,4]]},"1921":{"position":[[68,4],[171,4]]},"1937":{"position":[[206,6]]},"1973":{"position":[[52,4],[69,4],[302,4],[441,4]]},"1985":{"position":[[177,4]]},"1987":{"position":[[116,5]]},"2010":{"position":[[128,4]]},"2026":{"position":[[435,4],[485,4]]},"2030":{"position":[[219,4],[1478,4],[1528,4]]},"2036":{"position":[[573,4]]},"2052":{"position":[[602,5]]},"2066":{"position":[[133,4]]},"2068":{"position":[[183,4]]},"2098":{"position":[[291,6]]},"2116":{"position":[[54,5]]},"2122":{"position":[[58,5],[506,4]]},"2124":{"position":[[395,4]]},"2126":{"position":[[134,4]]},"2132":{"position":[[43,4],[240,4],[295,4],[327,5],[344,4],[433,4],[1077,4],[1214,5]]},"2134":{"position":[[131,4],[328,4],[368,4],[430,4],[920,4]]},"2142":{"position":[[382,5],[610,4]]},"2146":{"position":[[812,5],[995,4],[1436,4],[2125,8],[2206,5],[3595,4],[3610,4],[5017,4],[5072,4],[5293,7]]},"2148":{"position":[[1016,4],[1044,4]]},"2150":{"position":[[986,4],[1014,4]]},"2152":{"position":[[371,4],[471,5],[508,4],[890,4],[2310,4],[2338,4],[4330,4],[4590,4]]},"2154":{"position":[[2342,4],[2370,4]]},"2156":{"position":[[2417,4],[2445,4]]},"2170":{"position":[[282,5]]},"2194":{"position":[[29,4],[117,4],[133,4],[202,4],[443,5],[493,4],[592,5]]},"2196":{"position":[[33,4],[120,7],[128,5],[212,4],[228,4],[301,4],[360,5],[448,5]]},"2198":{"position":[[34,4],[84,7],[92,5],[175,4],[191,4],[263,5],[351,5]]},"2200":{"position":[[26,4],[166,4],[182,4],[239,5],[324,5]]},"2202":{"position":[[665,7],[773,7],[1159,4],[1174,4]]},"2204":{"position":[[106,5],[122,4],[779,5]]},"2238":{"position":[[682,5]]},"2250":{"position":[[1405,4],[1664,5]]},"2339":{"position":[[209,5],[417,5],[479,4],[1001,4]]},"2345":{"position":[[4450,4]]},"2351":{"position":[[600,4]]},"2365":{"position":[[389,5]]},"2367":{"position":[[588,4]]},"2381":{"position":[[404,5],[533,6]]},"2413":{"position":[[258,6],[532,4]]},"2436":{"position":[[95,4],[609,4]]},"2452":{"position":[[488,4],[705,4],[944,4],[1049,4],[1187,6]]},"2458":{"position":[[170,5],[280,5],[399,5],[502,6]]},"2474":{"position":[[343,4],[1579,5]]},"2476":{"position":[[93,4],[226,4],[348,4],[368,4],[634,4],[729,5]]},"2482":{"position":[[1047,4]]},"2486":{"position":[[385,6],[744,4]]},"2504":{"position":[[801,6],[857,5]]},"2526":{"position":[[404,5],[533,6]]},"2530":{"position":[[385,4],[411,5],[436,4],[512,4],[597,4],[812,5],[1905,4]]},"2532":{"position":[[294,5]]},"2538":{"position":[[57,5],[156,4],[408,4]]},"2552":{"position":[[433,5]]},"2564":{"position":[[225,6],[244,5]]},"2566":{"position":[[33,5]]},"2592":{"position":[[383,6]]},"2594":{"position":[[9,5],[738,4]]},"2598":{"position":[[42,4],[229,5],[415,4],[559,6]]},"2630":{"position":[[587,4],[683,4]]},"2638":{"position":[[51,4],[333,4],[432,4],[523,4]]},"2640":{"position":[[0,4]]},"2662":{"position":[[106,5],[5780,5],[6175,4],[6287,4],[6349,4]]},"2668":{"position":[[1354,4]]},"2672":{"position":[[648,4],[821,4],[877,4]]},"2682":{"position":[[192,5],[323,4]]},"2686":{"position":[[275,4],[511,4],[594,4]]},"2692":{"position":[[135,4],[307,4],[534,4],[574,4],[861,8],[1068,4],[1191,4],[1271,4],[1634,4]]},"2702":{"position":[[518,5]]},"2704":{"position":[[196,6],[895,6]]},"2706":{"position":[[354,6],[1033,6]]},"2708":{"position":[[349,6],[1329,6]]},"2710":{"position":[[402,6],[1256,6]]},"2712":{"position":[[523,6],[1497,6]]},"2714":{"position":[[12,5],[451,5],[555,5],[628,5],[1080,5]]},"2716":{"position":[[614,5]]},"2730":{"position":[[233,6]]},"2770":{"position":[[59,4],[115,4],[207,4],[1216,9],[1581,4]]},"2772":{"position":[[142,4],[172,4],[226,5],[343,4],[713,4],[736,4]]},"2778":{"position":[[100,4],[170,4],[484,4],[558,6],[635,4],[678,5]]},"2810":{"position":[[262,7]]},"2816":{"position":[[59,4]]},"2823":{"position":[[257,5]]},"2826":{"position":[[33,4],[117,4],[401,9],[607,4],[623,4]]},"2830":{"position":[[1901,5]]},"2832":{"position":[[54,4],[281,4],[374,5],[402,4],[439,5]]},"2834":{"position":[[67,4],[197,4],[745,5],[773,4],[810,5],[988,4]]},"2836":{"position":[[163,5]]},"2844":{"position":[[509,4]]},"2846":{"position":[[585,6],[800,4],[847,4],[939,4],[981,4],[1005,4],[1135,5],[1271,4],[1366,4]]},"2852":{"position":[[844,4],[887,4]]},"2867":{"position":[[391,4],[406,4],[590,4]]},"2869":{"position":[[53,4],[98,4],[222,5],[254,5],[308,4],[510,4],[526,4]]},"2871":{"position":[[58,4],[246,5],[288,4]]},"2873":{"position":[[879,5],[919,5],[1485,4],[1504,4]]},"2877":{"position":[[825,5]]},"2879":{"position":[[30,6],[43,4],[89,4],[161,5],[226,4],[242,4]]},"2881":{"position":[[5,4],[421,5],[454,5],[819,4],[835,4]]},"2895":{"position":[[30,4],[118,4],[416,5],[464,5],[650,4]]},"2897":{"position":[[0,4]]},"2899":{"position":[[491,5]]},"2904":{"position":[[116,9],[269,4],[285,4],[348,4],[505,5]]},"2906":{"position":[[116,9],[248,4],[264,4]]},"2918":{"position":[[145,4]]},"2920":{"position":[[350,10],[424,4],[495,5],[530,5],[564,4]]},"2922":{"position":[[92,6],[257,10],[741,4],[820,4],[923,4],[1013,4],[1084,5],[1119,5],[1147,4],[1260,4],[1343,4],[1358,4]]},"2924":{"position":[[21,4],[215,10],[289,4],[360,5],[395,5],[429,4]]},"2926":{"position":[[16,4],[150,4],[322,4],[834,5]]},"2930":{"position":[[251,4],[966,5],[1238,4],[1375,4],[1396,4],[1462,4],[1511,4],[1547,4],[1605,4],[2059,4],[2373,4],[2514,4]]},"2932":{"position":[[1558,4]]},"2934":{"position":[[725,4],[879,4]]},"2936":{"position":[[876,4],[1032,4]]},"2948":{"position":[[71,4],[96,4],[229,4],[417,6]]},"2950":{"position":[[736,5],[855,5],[1087,4]]},"2964":{"position":[[276,5]]},"2966":{"position":[[415,4]]},"2972":{"position":[[1648,4]]},"2976":{"position":[[635,6]]},"2980":{"position":[[13,4]]},"2992":{"position":[[81,4],[122,4],[249,4],[271,4]]},"3014":{"position":[[88,4]]},"3016":{"position":[[214,4]]},"3056":{"position":[[798,4]]},"3062":{"position":[[295,4]]},"3064":{"position":[[446,4],[607,5]]},"3066":{"position":[[23,4],[122,4],[245,4],[318,4],[402,4],[548,4],[633,5],[770,6],[828,4],[871,6],[943,6],[995,4]]},"3070":{"position":[[202,4],[407,4],[443,4],[486,4],[675,4]]},"3072":{"position":[[2040,4],[2180,4]]},"3076":{"position":[[231,4],[411,5],[461,4],[815,7],[956,7],[1096,7]]},"3078":{"position":[[252,4]]},"3086":{"position":[[437,4],[487,4]]},"3092":{"position":[[177,5],[434,4]]},"3094":{"position":[[98,4]]},"3114":{"position":[[68,4],[171,4]]},"3134":{"position":[[206,6]]},"3144":{"position":[[133,4]]},"3146":{"position":[[183,4]]},"3186":{"position":[[52,4],[69,4],[302,4],[441,4]]},"3198":{"position":[[201,4],[402,4],[585,4],[832,4],[866,4],[1278,4]]},"3200":{"position":[[177,4]]},"3202":{"position":[[89,5]]},"3227":{"position":[[128,4]]},"3257":{"position":[[291,6]]},"3267":{"position":[[435,4],[485,4]]},"3271":{"position":[[219,4],[1444,4],[1494,4]]},"3277":{"position":[[573,4]]},"3291":{"position":[[602,5]]},"3324":{"position":[[1042,5],[1101,4]]},"3333":{"position":[[614,5]]},"3337":{"position":[[51,5],[766,4]]},"3339":{"position":[[395,4]]},"3341":{"position":[[134,4]]},"3345":{"position":[[754,4]]},"3347":{"position":[[1684,4]]},"3361":{"position":[[43,4],[240,4],[295,4],[327,5],[344,4],[433,4],[1077,4],[1214,5]]},"3363":{"position":[[131,4],[328,4],[368,4],[430,4],[920,4]]},"3371":{"position":[[382,5],[610,4]]},"3375":{"position":[[812,5],[995,4],[1436,4],[2125,8],[2206,5],[3595,4],[3610,4],[5046,4],[5101,4],[5322,7]]},"3377":{"position":[[1016,4],[1044,4]]},"3379":{"position":[[1033,4],[1061,4]]},"3381":{"position":[[406,4],[743,4],[2113,4],[2141,4],[4333,4],[4593,4]]},"3383":{"position":[[2342,4],[2370,4]]},"3385":{"position":[[2390,4],[2418,4]]},"3399":{"position":[[282,5]]},"3435":{"position":[[185,4],[201,4],[270,4],[511,5],[561,4],[660,5]]},"3437":{"position":[[33,4],[118,4],[134,4],[207,4],[266,5],[354,5]]},"3439":{"position":[[34,4],[109,4],[125,4],[197,5],[285,5]]},"3441":{"position":[[26,4],[159,4],[175,4],[232,5],[317,5]]},"3443":{"position":[[510,7],[618,7],[997,4],[1012,4]]},"3445":{"position":[[106,5],[122,4],[620,5]]},"3455":{"position":[[98,5]]},"3549":{"position":[[682,5]]},"3561":{"position":[[1405,4],[1664,5]]},"3580":{"position":[[258,6],[532,4]]},"3595":{"position":[[209,5],[417,5],[479,4],[1001,4]]},"3633":{"position":[[4450,4]]},"3639":{"position":[[760,6]]},"3653":{"position":[[389,5]]},"3655":{"position":[[588,4]]},"3669":{"position":[[404,5],[533,6]]}}}],["user\":\"2694",{"_index":4579,"t":{"1176":{"position":[[259,14],[626,14],[1046,14]]}}}],["user\":\"42",{"_index":3860,"t":{"682":{"position":[[422,12],[607,11]]},"1733":{"position":[[422,12],[607,11]]},"2922":{"position":[[386,12],[571,11]]}}}],["user\":\"56",{"_index":4321,"t":{"1067":{"position":[[571,12]]},"1069":{"position":[[483,12]]},"1071":{"position":[[1842,12]]},"1073":{"position":[[1857,12]]},"2148":{"position":[[571,12]]},"2150":{"position":[[483,12]]},"2152":{"position":[[1842,12]]},"2154":{"position":[[1857,12]]},"2156":{"position":[[1946,12]]},"3377":{"position":[[571,12]]},"3379":{"position":[[530,12]]},"3381":{"position":[[1645,12]]},"3383":{"position":[[1857,12]]},"3385":{"position":[[1919,12]]}}}],["user'",{"_index":2382,"t":{"212":{"position":[[189,6]]},"254":{"position":[[115,6],[684,6]]},"576":{"position":[[666,6]]},"678":{"position":[[445,6]]},"1523":{"position":[[1034,6]]},"1583":{"position":[[765,6]]},"1729":{"position":[[440,6]]},"2116":{"position":[[133,6]]},"2692":{"position":[[1034,6]]},"2778":{"position":[[766,6]]},"2918":{"position":[[440,6]]},"3435":{"position":[[36,6]]}}}],["user'const",{"_index":2712,"t":{"283":{"position":[[1941,10]]}}}],["user12",{"_index":4611,"t":{"1207":{"position":[[484,6],[1104,10]]},"2339":{"position":[[484,6],[1104,10]]},"3595":{"position":[[484,6],[1080,10]]}}}],["user12hmac",{"_index":4610,"t":{"1207":{"position":[[452,10]]},"2339":{"position":[[452,10]]},"3595":{"position":[[452,10]]}}}],["user:1",{"_index":5726,"t":{"3433":{"position":[[174,10]]}}}],["user:2",{"_index":5727,"t":{"3433":{"position":[[185,10]]}}}],["user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parsetime=true&loc=loc",{"_index":3538,"t":{"519":{"position":[[1073,80]]}}}],["user=123722",{"_index":4784,"t":{"1495":{"position":[[7453,11]]},"2662":{"position":[[7533,11]]}}}],["user=postgr",{"_index":3533,"t":{"519":{"position":[[822,13]]}}}],["user@example.com",{"_index":4263,"t":{"1049":{"position":[[227,19]]},"2230":{"position":[[227,19]]},"3417":{"position":[[227,19]]}}}],["user_1",{"_index":3748,"t":{"610":{"position":[[114,6]]},"1545":{"position":[[114,6]]},"2714":{"position":[[114,6]]}}}],["user_2",{"_index":3749,"t":{"610":{"position":[[124,6]]},"1545":{"position":[[124,6]]},"2714":{"position":[[124,6]]}}}],["user_200",{"_index":3772,"t":{"610":{"position":[[1356,8]]},"1545":{"position":[[1350,8]]},"2714":{"position":[[1350,8]]}}}],["user_200');┌─count",{"_index":3758,"t":{"610":{"position":[[563,24]]},"1545":{"position":[[563,24]]},"2714":{"position":[[563,24]]}}}],["user_28",{"_index":3777,"t":{"610":{"position":[[1433,7]]},"1545":{"position":[[1427,7]]},"2714":{"position":[[1427,7]]}}}],["user_3",{"_index":3750,"t":{"610":{"position":[[134,6]]},"1545":{"position":[[134,6]]},"2714":{"position":[[134,6]]}}}],["user_32",{"_index":3780,"t":{"610":{"position":[[1478,7]]},"1545":{"position":[[1472,7]]},"2714":{"position":[[1472,7]]}}}],["user_39",{"_index":3776,"t":{"610":{"position":[[1418,7]]},"1545":{"position":[[1412,7]]},"2714":{"position":[[1412,7]]}}}],["user_4",{"_index":3751,"t":{"610":{"position":[[144,6]]},"1545":{"position":[[144,6]]},"2714":{"position":[[144,6]]}}}],["user_42",{"_index":3021,"t":{"391":{"position":[[879,11],[1000,7]]},"1473":{"position":[[193,7],[249,11]]},"1475":{"position":[[947,11],[993,12],[1138,8],[1268,7],[1556,7],[1698,12]]},"1485":{"position":[[179,12]]},"2666":{"position":[[193,7],[249,11]]},"2668":{"position":[[947,11],[993,12],[1138,8],[1268,7],[1556,7],[1698,12]]},"2678":{"position":[[179,12]]}}}],["user_5",{"_index":3752,"t":{"610":{"position":[[154,6]]},"1545":{"position":[[154,6]]},"2714":{"position":[[154,6]]}}}],["user_52",{"_index":3781,"t":{"610":{"position":[[1493,7]]},"1545":{"position":[[1487,7]]},"2714":{"position":[[1487,7]]}}}],["user_63",{"_index":3778,"t":{"610":{"position":[[1448,7]]},"1545":{"position":[[1442,7]]},"2714":{"position":[[1442,7]]}}}],["user_65",{"_index":3775,"t":{"610":{"position":[[1403,7]]},"1545":{"position":[[1397,7]]},"2714":{"position":[[1397,7]]}}}],["user_75",{"_index":3773,"t":{"610":{"position":[[1373,7]]},"1545":{"position":[[1367,7]]},"2714":{"position":[[1367,7]]}}}],["user_87",{"_index":3774,"t":{"610":{"position":[[1388,7]]},"1545":{"position":[[1382,7]]},"2714":{"position":[[1382,7]]}}}],["user_89",{"_index":3779,"t":{"610":{"position":[[1463,7]]},"1545":{"position":[[1457,7]]},"2714":{"position":[[1457,7]]}}}],["user_block",{"_index":3849,"t":{"666":{"position":[[82,13],[417,10]]},"668":{"position":[[212,13]]},"1710":{"position":[[82,13],[417,10]]},"1712":{"position":[[212,13]]},"2899":{"position":[[82,13],[417,10]]},"2901":{"position":[[212,13]]}}}],["user_command_rate_limit",{"_index":5513,"t":{"2832":{"position":[[800,26]]}}}],["user_command_throttl",{"_index":4994,"t":{"1678":{"position":[[794,26]]}}}],["user_connect",{"_index":3836,"t":{"652":{"position":[[187,16]]},"1517":{"position":[[33,16]]},"1699":{"position":[[187,16]]},"2648":{"position":[[33,16]]},"2823":{"position":[[187,16]]}}}],["user_personal_channel_namespac",{"_index":2275,"t":{"186":{"position":[[316,34]]},"1041":{"position":[[781,31],[943,31]]},"1043":{"position":[[758,34]]},"2132":{"position":[[781,31],[943,31]]},"2134":{"position":[[770,34]]},"3361":{"position":[[781,31],[943,31]]},"3363":{"position":[[770,34]]}}}],["user_personal_single_connect",{"_index":4254,"t":{"1043":{"position":[[174,31],[717,34]]},"2134":{"position":[[174,31],[729,34]]},"3363":{"position":[[174,31],[729,34]]}}}],["user_statu",{"_index":5006,"t":{"1737":{"position":[[55,14],[578,14],[958,14]]},"2926":{"position":[[55,14],[578,14],[958,14]]}}}],["user_subscribe_to_person",{"_index":2274,"t":{"186":{"position":[[280,29]]},"285":{"position":[[245,29]]},"289":{"position":[[121,26]]},"1041":{"position":[[121,26]]},"1043":{"position":[[681,29]]},"1207":{"position":[[292,29]]},"1262":{"position":[[359,29]]},"2132":{"position":[[121,26]]},"2134":{"position":[[693,29]]},"2339":{"position":[[292,29]]},"2413":{"position":[[359,29]]},"3361":{"position":[[121,26]]},"3363":{"position":[[693,29]]},"3580":{"position":[[359,29]]},"3595":{"position":[[292,29]]}}}],["user_tokens_invalid",{"_index":3829,"t":{"648":{"position":[[170,25],[538,22]]},"650":{"position":[[268,25]]},"1694":{"position":[[170,25],[538,22]]},"1696":{"position":[[268,25]]},"2818":{"position":[[170,25],[538,22]]},"2820":{"position":[[268,25]]}}}],["user_topic_list",{"_index":2635,"t":{"264":{"position":[[659,15]]},"1664":{"position":[[28,15],[413,15]]},"2881":{"position":[[28,15],[596,15]]}}}],["user_topic_upd",{"_index":2636,"t":{"264":{"position":[[679,18],[1233,17]]},"1629":{"position":[[1306,17]]},"1662":{"position":[[167,17],[328,17]]},"2846":{"position":[[1306,17]]},"2879":{"position":[[167,17],[328,17]]}}}],["user_upd",{"_index":4949,"t":{"1652":{"position":[[339,11]]},"2869":{"position":[[267,11]]}}}],["userchannel",{"_index":2854,"t":{"301":{"position":[[3894,11]]}}}],["userfrom",{"_index":3746,"t":{"610":{"position":[[56,8],[1176,8]]},"1545":{"position":[[56,8],[1170,8]]},"2714":{"position":[[56,8],[1170,8]]}}}],["userid",{"_index":956,"t":{"32":{"position":[[581,7]]}}}],["usernam",{"_index":2108,"t":{"146":{"position":[[966,9]]},"283":{"position":[[1090,10],[1232,10],[2171,8],[2233,8]]}}}],["userord",{"_index":3768,"t":{"610":{"position":[[1293,9]]},"1545":{"position":[[1287,9]]},"2714":{"position":[[1287,9]]}}}],["users/4",{"_index":4803,"t":{"1523":{"position":[[343,8],[793,10]]},"2692":{"position":[[343,8],[793,10]]}}}],["users/:nam",{"_index":4852,"t":{"1567":{"position":[[143,14],[850,12]]},"2726":{"position":[[143,14],[850,12]]}}}],["users/:name\":/users/mari",{"_index":4862,"t":{"1571":{"position":[[152,26]]},"2730":{"position":[[152,26]]}}}],["users/:us",{"_index":4804,"t":{"1523":{"position":[[386,13]]},"2692":{"position":[[386,13]]}}}],["users/:user/:us",{"_index":4861,"t":{"1569":{"position":[[1208,18]]},"2728":{"position":[[1208,18]]}}}],["users/mario",{"_index":4855,"t":{"1567":{"position":[[554,12],[803,13]]},"2726":{"position":[[554,12],[803,13]]}}}],["userstatu",{"_index":3863,"t":{"682":{"position":[[1264,10],[1324,11]]},"1733":{"position":[[1264,10],[1324,11]]},"2922":{"position":[[1228,10],[1288,11]]}}}],["users—ref",{"_index":5567,"t":{"3066":{"position":[[101,14]]}}}],["usertop",{"_index":4944,"t":{"1650":{"position":[[613,9]]},"1664":{"position":[[382,9],[480,9],[500,9],[591,10],[654,9]]},"2867":{"position":[[613,9]]},"2881":{"position":[[248,9],[663,9],[683,9],[746,10],[809,9]]}}}],["usertopicfilt",{"_index":5530,"t":{"2881":{"position":[[91,15],[372,16]]}}}],["usest",{"_index":2818,"t":{"301":{"position":[[1429,9],[3848,13]]}}}],["usestate(\"disconnect",{"_index":2845,"t":{"301":{"position":[[3248,25]]}}}],["usr/share/nginx/html",{"_index":4239,"t":{"1001":{"position":[[1415,22]]},"2108":{"position":[[1415,22]]},"3309":{"position":[[1415,22]]}}}],["usual",{"_index":409,"t":{"10":{"position":[[485,7]]},"34":{"position":[[2584,5]]},"42":{"position":[[1702,5]]},"200":{"position":[[637,7]]},"220":{"position":[[409,6]]},"244":{"position":[[112,7]]},"256":{"position":[[1251,7]]},"264":{"position":[[294,7]]},"361":{"position":[[523,7]]},"489":{"position":[[209,7]]},"774":{"position":[[86,7]]},"786":{"position":[[101,7]]},"824":{"position":[[161,7]]},"951":{"position":[[630,8]]},"957":{"position":[[34,7]]},"971":{"position":[[631,8]]},"975":{"position":[[1513,8]]},"1065":{"position":[[1414,7],[1582,7]]},"1149":{"position":[[146,7]]},"1268":{"position":[[84,7]]},"1277":{"position":[[109,8]]},"1279":{"position":[[110,8]]},"1381":{"position":[[523,7]]},"1469":{"position":[[222,7]]},"1625":{"position":[[70,7]]},"1629":{"position":[[294,7]]},"1676":{"position":[[1833,7]]},"1682":{"position":[[951,7],[1306,7]]},"1793":{"position":[[86,7]]},"1837":{"position":[[98,7]]},"1865":{"position":[[1257,7]]},"2078":{"position":[[630,8]]},"2084":{"position":[[34,7]]},"2146":{"position":[[1531,7],[1699,7],[6156,8]]},"2246":{"position":[[266,7]]},"2277":{"position":[[146,7]]},"2292":{"position":[[84,7]]},"2424":{"position":[[109,8]]},"2426":{"position":[[110,8]]},"2530":{"position":[[335,8],[1374,7]]},"2532":{"position":[[559,7]]},"2610":{"position":[[235,7]]},"2830":{"position":[[1836,7]]},"2836":{"position":[[951,7],[1306,7]]},"2842":{"position":[[70,7]]},"2846":{"position":[[294,7]]},"2972":{"position":[[1261,7]]},"2982":{"position":[[86,7]]},"3028":{"position":[[98,7]]},"3056":{"position":[[1257,7]]},"3198":{"position":[[43,7],[263,7]]},"3229":{"position":[[341,5]]},"3245":{"position":[[630,8]]},"3251":{"position":[[34,7]]},"3329":{"position":[[241,7]]},"3345":{"position":[[341,5]]},"3347":{"position":[[2896,5]]},"3349":{"position":[[3654,7]]},"3375":{"position":[[1531,7],[1699,7],[6185,8]]},"3484":{"position":[[84,7]]},"3504":{"position":[[146,7]]},"3557":{"position":[[266,7]]},"3619":{"position":[[109,8]]},"3621":{"position":[[110,8]]}}}],["utf",{"_index":2883,"t":{"323":{"position":[[490,3]]},"1309":{"position":[[490,3]]},"2258":{"position":[[1522,3]]},"2456":{"position":[[490,3]]},"3492":{"position":[[1522,3]]}}}],["util",{"_index":796,"t":{"18":{"position":[[2983,7]]},"20":{"position":[[1712,9]]},"44":{"position":[[1112,11]]},"46":{"position":[[2517,8],[2736,7]]},"50":{"position":[[790,7]]},"52":{"position":[[649,11]]},"54":{"position":[[1054,11]]},"68":{"position":[[1757,7]]},"78":{"position":[[165,9]]},"86":{"position":[[82,8]]},"339":{"position":[[640,7],[903,7],[1053,7]]},"347":{"position":[[1512,7]]},"359":{"position":[[1183,9]]},"399":{"position":[[1420,7]]},"429":{"position":[[14,7]]},"473":{"position":[[144,8]]},"475":{"position":[[2710,11]]},"479":{"position":[[1603,9],[4946,8]]},"481":{"position":[[1835,12],[3345,11],[3687,12],[5603,7]]},"485":{"position":[[174,11],[215,11],[1160,11],[1364,11]]},"914":{"position":[[111,7]]},"973":{"position":[[224,10]]},"979":{"position":[[115,7]]},"1069":{"position":[[251,7]]},"1087":{"position":[[945,7]]},"1327":{"position":[[640,7],[903,7],[1053,7]]},"1335":{"position":[[1516,7]]},"1379":{"position":[[1183,9]]},"1413":{"position":[[14,7]]},"1647":{"position":[[30,8]]},"1989":{"position":[[188,7]]},"2028":{"position":[[224,10]]},"2036":{"position":[[115,7]]},"2150":{"position":[[251,7]]},"2170":{"position":[[945,7]]},"2258":{"position":[[1289,7]]},"2434":{"position":[[2255,7]]},"2474":{"position":[[640,7],[903,7],[1053,7]]},"2482":{"position":[[1657,7]]},"2530":{"position":[[2278,7],[2637,7]]},"2560":{"position":[[588,9]]},"2570":{"position":[[14,7]]},"2780":{"position":[[771,7]]},"2864":{"position":[[15,8]]},"2889":{"position":[[874,8]]},"2950":{"position":[[692,8]]},"2962":{"position":[[10,9]]},"3204":{"position":[[188,7]]},"3269":{"position":[[224,10]]},"3277":{"position":[[115,7]]},"3324":{"position":[[1924,11]]},"3345":{"position":[[964,8]]},"3351":{"position":[[188,9]]},"3379":{"position":[[251,7]]},"3399":{"position":[[945,7]]},"3492":{"position":[[1289,7]]},"3612":{"position":[[2454,7]]}}}],["uuid",{"_index":5531,"t":{"2883":{"position":[[536,4]]}}}],["ux",{"_index":2477,"t":{"236":{"position":[[860,2]]}}}],["v",{"_index":1352,"t":{"70":{"position":[[1184,4]]},"110":{"position":[[1594,5]]},"134":{"position":[[1602,1]]},"525":{"position":[[141,1]]},"590":{"position":[[124,1]]},"612":{"position":[[547,1]]},"963":{"position":[[1746,4]]},"1141":{"position":[[1141,4]]},"1143":{"position":[[1000,4]]},"1433":{"position":[[143,1]]},"1547":{"position":[[677,1]]},"1555":{"position":[[126,1]]},"2100":{"position":[[1746,4]]},"2222":{"position":[[1141,4]]},"2224":{"position":[[1000,4]]},"2616":{"position":[[143,1]]},"2716":{"position":[[677,1]]},"2758":{"position":[[126,1]]},"3259":{"position":[[1746,4]]},"3463":{"position":[[1141,4]]},"3465":{"position":[[1000,4]]}}}],["v0.0.80",{"_index":3330,"t":{"479":{"position":[[2784,7]]}}}],["v0.0.90",{"_index":3332,"t":{"479":{"position":[[3517,9]]}}}],["v0.10.0",{"_index":5442,"t":{"2652":{"position":[[547,7],[574,7]]}}}],["v0.2.0",{"_index":4788,"t":{"1499":{"position":[[318,6]]},"2630":{"position":[[318,6]]}}}],["v0.3.0",{"_index":5444,"t":{"2652":{"position":[[628,6]]}}}],["v0.5.0",{"_index":4787,"t":{"1499":{"position":[[292,6]]},"2630":{"position":[[292,6]]}}}],["v0.6.0",{"_index":5443,"t":{"2652":{"position":[[602,6]]}}}],["v0.9.0",{"_index":4786,"t":{"1499":{"position":[[239,6],[265,6]]},"2630":{"position":[[239,6],[265,6]]}}}],["v1",{"_index":1216,"t":{"50":{"position":[[2389,2]]},"62":{"position":[[180,3],[1442,2]]},"64":{"position":[[350,2]]},"1499":{"position":[[1848,2]]},"1877":{"position":[[116,3]]},"2238":{"position":[[433,3],[559,3],[579,2]]},"2630":{"position":[[1848,2]]},"3068":{"position":[[147,3]]},"3549":{"position":[[433,3],[559,3],[579,2]]}}}],["v1.0.0",{"_index":5166,"t":{"2048":{"position":[[469,6]]}}}],["v1.8.9",{"_index":3227,"t":{"477":{"position":[[4408,8]]},"479":{"position":[[3475,8]]}}}],["v2",{"_index":865,"t":{"26":{"position":[[1265,2]]},"60":{"position":[[39,2]]},"62":{"position":[[11,2],[144,2],[194,2],[733,2]]},"64":{"position":[[356,2],[694,2],[901,3]]},"68":{"position":[[51,2]]},"76":{"position":[[171,4]]},"86":{"position":[[653,2]]},"359":{"position":[[856,2]]},"560":{"position":[[124,2]]},"566":{"position":[[277,3]]},"572":{"position":[[39,2]]},"1379":{"position":[[856,2]]},"1505":{"position":[[11,2]]},"1515":{"position":[[374,2]]},"1877":{"position":[[120,2]]},"2238":{"position":[[536,2]]},"2636":{"position":[[11,2]]},"2646":{"position":[[374,2]]},"2660":{"position":[[59,2]]},"3068":{"position":[[151,3]]},"3549":{"position":[[536,2]]}}}],["v2.x",{"_index":3958,"t":{"768":{"position":[[609,4]]}}}],["v3",{"_index":1262,"t":{"60":{"position":[[257,2]]},"62":{"position":[[63,2],[1863,2]]},"64":{"position":[[92,2],[262,2],[700,2],[840,2],[881,2],[1015,2]]},"66":{"position":[[70,2]]},"68":{"position":[[187,2]]},"70":{"position":[[204,2]]},"72":{"position":[[14,2],[110,2]]},"74":{"position":[[160,2],[718,2]]},"78":{"position":[[11,2]]},"82":{"position":[[20,2],[464,4]]},"86":{"position":[[11,2],[1023,2]]},"88":{"position":[[1058,2]]},"90":{"position":[[43,2],[188,2],[363,3],[477,2],[701,2]]},"118":{"position":[[488,3]]},"122":{"position":[[506,2]]},"144":{"position":[[367,3]]},"146":{"position":[[191,2]]},"279":{"position":[[518,3]]},"375":{"position":[[48,2],[358,2],[525,2]]},"383":{"position":[[1282,3]]},"387":{"position":[[650,2]]},"419":{"position":[[201,2]]},"545":{"position":[[188,3]]},"548":{"position":[[14,2]]},"550":{"position":[[14,2]]},"552":{"position":[[98,2]]},"554":{"position":[[14,2]]},"556":{"position":[[14,2]]},"558":{"position":[[52,3]]},"560":{"position":[[741,2]]},"562":{"position":[[60,2]]},"564":{"position":[[80,2]]},"566":{"position":[[11,2]]},"568":{"position":[[75,2]]},"572":{"position":[[46,2],[826,2]]},"1499":{"position":[[410,2],[498,2]]},"1505":{"position":[[18,2]]},"1507":{"position":[[1081,2]]},"1515":{"position":[[380,3]]},"1787":{"position":[[609,2]]},"1877":{"position":[[127,3]]},"2630":{"position":[[410,2],[498,2]]},"2636":{"position":[[18,2]]},"2638":{"position":[[1081,2]]},"2646":{"position":[[380,3]]},"2660":{"position":[[222,3]]},"2976":{"position":[[753,2]]}}}],["v3.0.0",{"_index":4279,"t":{"1053":{"position":[[25,6]]},"1499":{"position":[[215,6]]},"2630":{"position":[[215,6]]}}}],["v3.1.0",{"_index":4350,"t":{"1089":{"position":[[18,7],[553,6],[831,6]]},"2172":{"position":[[527,6],[805,6]]},"3401":{"position":[[527,6],[805,6]]}}}],["v3.1.1",{"_index":3717,"t":{"604":{"position":[[1783,7],[1990,7]]},"1071":{"position":[[2722,6],[2871,6]]}}}],["v3.2.0",{"_index":3923,"t":{"748":{"position":[[25,6]]},"750":{"position":[[25,6]]},"975":{"position":[[219,6]]},"979":{"position":[[572,7]]},"1031":{"position":[[25,6]]},"1033":{"position":[[25,6]]},"1109":{"position":[[2285,7]]},"1111":{"position":[[523,7]]},"1113":{"position":[[652,6]]},"1115":{"position":[[508,6]]},"1117":{"position":[[411,6]]},"1119":{"position":[[384,6]]}}}],["v3.2.2",{"_index":3680,"t":{"592":{"position":[[215,6]]}}}],["v3/php_laravel_chat_tutori",{"_index":2115,"t":{"150":{"position":[[142,28]]}}}],["v3_use_offset",{"_index":3599,"t":{"545":{"position":[[129,13]]},"570":{"position":[[186,13]]}}}],["v3—onli",{"_index":5571,"t":{"3068":{"position":[[159,7]]}}}],["v4",{"_index":55,"t":{"2":{"position":[[687,2]]},"118":{"position":[[524,2]]},"144":{"position":[[403,2]]},"196":{"position":[[36,3],[270,2],[427,2]]},"202":{"position":[[396,3],[469,2],[600,2]]},"204":{"position":[[890,3]]},"210":{"position":[[52,2]]},"216":{"position":[[75,3],[708,2]]},"279":{"position":[[554,2]]},"373":{"position":[[80,3]]},"375":{"position":[[834,2]]},"377":{"position":[[263,2],[1222,2]]},"379":{"position":[[451,3]]},"383":{"position":[[742,2]]},"385":{"position":[[785,3],[1930,2]]},"387":{"position":[[124,3],[657,3]]},"397":{"position":[[11,2]]},"399":{"position":[[509,3]]},"401":{"position":[[127,2],[186,2]]},"403":{"position":[[51,3],[767,2]]},"405":{"position":[[251,3]]},"407":{"position":[[124,2],[606,3]]},"1319":{"position":[[11,2]]},"1499":{"position":[[194,3],[504,2],[783,3],[801,2],[869,2],[1016,3]]},"1501":{"position":[[100,2],[391,3]]},"1503":{"position":[[51,2],[231,2]]},"1507":{"position":[[142,2],[258,2],[711,2],[850,2],[1087,2]]},"1511":{"position":[[14,2],[974,2]]},"1741":{"position":[[994,2]]},"1877":{"position":[[11,2],[220,2],[643,2]]},"2032":{"position":[[266,2]]},"2038":{"position":[[251,2]]},"2324":{"position":[[53,3]]},"2341":{"position":[[53,3]]},"2396":{"position":[[53,3]]},"2466":{"position":[[11,2]]},"2630":{"position":[[194,3],[504,2],[783,3],[801,2],[869,2],[1016,3]]},"2632":{"position":[[100,2],[391,3]]},"2634":{"position":[[51,2],[231,2]]},"2638":{"position":[[142,2],[258,2],[711,2],[850,2],[1087,2]]},"2642":{"position":[[14,2],[974,2]]},"2652":{"position":[[681,2]]},"2654":{"position":[[64,2],[75,2]]},"2660":{"position":[[226,2]]},"2883":{"position":[[541,2]]},"2930":{"position":[[994,2]]},"3068":{"position":[[261,3],[682,2]]},"3273":{"position":[[266,2]]},"3279":{"position":[[251,2]]},"3597":{"position":[[53,3]]}}}],["v4.0.0",{"_index":4841,"t":{"1557":{"position":[[215,6]]},"2652":{"position":[[523,6]]}}}],["v4.0.1",{"_index":5125,"t":{"1975":{"position":[[20,6]]}}}],["v4.1.0",{"_index":3078,"t":{"467":{"position":[[876,6]]},"481":{"position":[[5360,6]]},"2032":{"position":[[397,6],[593,6],[788,6],[1021,6]]},"2038":{"position":[[400,6],[614,6],[827,6],[1078,6]]}}}],["v4.1.1",{"_index":4792,"t":{"1499":{"position":[[1788,6]]},"1977":{"position":[[20,6]]},"1987":{"position":[[20,6]]},"2156":{"position":[[20,6]]},"2630":{"position":[[1788,6]]}}}],["v4.1.3",{"_index":5020,"t":{"1797":{"position":[[27,6]]},"2030":{"position":[[636,6]]}}}],["v5",{"_index":2289,"t":{"194":{"position":[[14,2],[1329,3]]},"196":{"position":[[339,2]]},"198":{"position":[[14,2],[1723,2]]},"202":{"position":[[190,2],[476,2],[553,2]]},"204":{"position":[[990,3],[1788,2],[2115,2]]},"210":{"position":[[456,2]]},"212":{"position":[[485,3]]},"214":{"position":[[11,2]]},"220":{"position":[[60,3],[115,3],[306,3]]},"248":{"position":[[7,2]]},"2032":{"position":[[357,3]]},"2038":{"position":[[351,3]]},"2652":{"position":[[14,2],[687,2]]},"2654":{"position":[[25,2],[82,2]]},"2656":{"position":[[730,2]]},"2660":{"position":[[16,2],[233,2]]},"3273":{"position":[[357,3]]},"3279":{"position":[[351,3]]}}}],["v5.0.2",{"_index":5715,"t":{"3371":{"position":[[849,6]]},"3405":{"position":[[1753,6]]}}}],["v5.0.3",{"_index":5629,"t":{"3327":{"position":[[1129,8]]}}}],["v5.0.4",{"_index":5581,"t":{"3229":{"position":[[27,6]]},"3381":{"position":[[3434,6]]}}}],["v5.1.0",{"_index":5557,"t":{"2984":{"position":[[1033,7]]},"3393":{"position":[[597,8]]},"3405":{"position":[[2395,7]]},"3467":{"position":[[451,6]]}}}],["v5.1.1",{"_index":5458,"t":{"2722":{"position":[[453,6]]},"2760":{"position":[[215,6]]},"2774":{"position":[[533,8]]},"3198":{"position":[[36,6]]}}}],["v6",{"_index":3550,"t":{"523":{"position":[[466,2]]},"1431":{"position":[[476,2]]},"2614":{"position":[[491,2]]}}}],["v6.2.7",{"_index":3230,"t":{"477":{"position":[[4503,6]]}}}],["v8",{"_index":1273,"t":{"62":{"position":[[366,2]]}}}],["v9.0.0",{"_index":3228,"t":{"477":{"position":[[4457,7]]}}}],["v97",{"_index":3037,"t":{"399":{"position":[[395,4]]},"2434":{"position":[[678,4]]},"3612":{"position":[[705,4]]}}}],["vacat",{"_index":5428,"t":{"2506":{"position":[[1429,8]]},"2722":{"position":[[224,7],[942,7],[1244,10],[2083,7],[2169,7]]},"2778":{"position":[[882,7]]}}}],["valid",{"_index":1754,"t":{"110":{"position":[[4038,8]]},"136":{"position":[[1288,8],[1653,8]]},"178":{"position":[[2489,9]]},"198":{"position":[[334,5],[657,5]]},"210":{"position":[[65,8]]},"212":{"position":[[684,8]]},"297":{"position":[[373,5]]},"299":{"position":[[604,10]]},"307":{"position":[[546,5]]},"323":{"position":[[681,5]]},"339":{"position":[[245,10],[517,11]]},"341":{"position":[[503,8]]},"355":{"position":[[560,9],[878,9],[1320,9]]},"359":{"position":[[494,8]]},"387":{"position":[[395,5]]},"598":{"position":[[130,5]]},"700":{"position":[[404,8],[581,10]]},"742":{"position":[[539,5]]},"764":{"position":[[783,5],[1519,5]]},"770":{"position":[[25,5]]},"864":{"position":[[143,10]]},"868":{"position":[[323,5]]},"870":{"position":[[85,8],[201,10]]},"941":{"position":[[287,5]]},"943":{"position":[[245,5]]},"1023":{"position":[[68,6]]},"1049":{"position":[[1101,5]]},"1067":{"position":[[306,8]]},"1069":{"position":[[1484,5]]},"1071":{"position":[[806,8]]},"1073":{"position":[[321,8],[478,9]]},"1099":{"position":[[705,9]]},"1293":{"position":[[546,5]]},"1309":{"position":[[681,5]]},"1327":{"position":[[245,10],[517,11]]},"1329":{"position":[[503,8]]},"1347":{"position":[[560,9],[878,9],[1320,9]]},"1379":{"position":[[494,8]]},"1489":{"position":[[205,5],[529,5]]},"1495":{"position":[[5486,6],[5793,5]]},"1521":{"position":[[330,5]]},"1563":{"position":[[130,5]]},"1569":{"position":[[526,10],[1244,10]]},"1650":{"position":[[222,6],[355,6]]},"1741":{"position":[[590,5]]},"1743":{"position":[[147,8]]},"1761":{"position":[[539,5]]},"1783":{"position":[[783,5],[1519,5]]},"1789":{"position":[[25,5]]},"1797":{"position":[[235,11]]},"1807":{"position":[[68,6]]},"1827":{"position":[[44,5]]},"1869":{"position":[[355,5]]},"1877":{"position":[[333,5]]},"1903":{"position":[[370,8],[526,8],[622,10],[848,10]]},"1905":{"position":[[343,8],[499,8],[595,10]]},"1987":{"position":[[146,5],[174,6]]},"2016":{"position":[[287,5]]},"2018":{"position":[[245,5]]},"2064":{"position":[[143,10]]},"2066":{"position":[[323,5]]},"2068":{"position":[[384,5]]},"2070":{"position":[[85,8],[201,10]]},"2072":{"position":[[91,8],[209,10]]},"2148":{"position":[[306,8]]},"2150":{"position":[[1484,5]]},"2152":{"position":[[806,8]]},"2154":{"position":[[321,8],[478,9]]},"2156":{"position":[[353,8],[742,9]]},"2182":{"position":[[705,9]]},"2230":{"position":[[1101,5]]},"2436":{"position":[[560,9],[878,9],[1320,9]]},"2440":{"position":[[546,5]]},"2456":{"position":[[681,5]]},"2474":{"position":[[245,10],[517,11]]},"2476":{"position":[[503,8]]},"2530":{"position":[[958,8],[2780,8]]},"2652":{"position":[[207,5]]},"2662":{"position":[[5548,6],[5852,5]]},"2682":{"position":[[205,5],[529,5]]},"2690":{"position":[[330,5]]},"2728":{"position":[[526,10],[1244,10]]},"2766":{"position":[[130,5]]},"2867":{"position":[[222,6],[355,6]]},"2930":{"position":[[590,5]]},"2932":{"position":[[147,8]]},"2950":{"position":[[581,5]]},"2972":{"position":[[769,5],[1459,5]]},"2978":{"position":[[25,5]]},"2986":{"position":[[201,11]]},"2996":{"position":[[68,6]]},"3016":{"position":[[44,5]]},"3060":{"position":[[406,5]]},"3068":{"position":[[345,5]]},"3096":{"position":[[370,8],[526,8],[622,10],[848,10]]},"3098":{"position":[[343,8],[499,8],[595,10]]},"3142":{"position":[[143,10]]},"3144":{"position":[[323,5]]},"3146":{"position":[[384,5]]},"3148":{"position":[[85,8],[201,10]]},"3150":{"position":[[91,8],[209,10]]},"3202":{"position":[[119,5],[147,6]]},"3235":{"position":[[287,5]]},"3237":{"position":[[245,5]]},"3377":{"position":[[306,8]]},"3379":{"position":[[1531,5]]},"3383":{"position":[[321,8],[478,9]]},"3385":{"position":[[326,8],[715,9]]},"3411":{"position":[[705,9]]},"3417":{"position":[[1101,5]]}}}],["validateclientind",{"_index":1755,"t":{"110":{"position":[[4061,24]]}}}],["validateclientindication(ind",{"_index":1726,"t":{"110":{"position":[[1965,35],[4184,35]]}}}],["valu",{"_index":68,"t":{"2":{"position":[[771,7],[2137,6]]},"12":{"position":[[775,5]]},"42":{"position":[[453,5]]},"70":{"position":[[951,5]]},"110":{"position":[[559,5],[1096,6],[1143,6],[2672,5],[2827,5],[3584,6],[3739,5],[3852,5],[3912,5],[3989,6]]},"200":{"position":[[1512,5]]},"250":{"position":[[1591,5],[1795,6],[2079,5]]},"481":{"position":[[1595,5],[1762,5],[4187,5],[5142,5],[6566,5]]},"513":{"position":[[340,7]]},"523":{"position":[[930,6]]},"560":{"position":[[405,6]]},"614":{"position":[[623,7]]},"678":{"position":[[469,5]]},"712":{"position":[[264,6]]},"742":{"position":[[887,5],[922,6],[1031,5]]},"758":{"position":[[1828,8]]},"760":{"position":[[47,9]]},"764":{"position":[[872,5],[905,5]]},"768":{"position":[[210,5],[305,5]]},"878":{"position":[[437,5],[668,6]]},"884":{"position":[[486,6]]},"900":{"position":[[68,6]]},"912":{"position":[[532,5],[696,7]]},"941":{"position":[[69,5]]},"961":{"position":[[123,5]]},"963":{"position":[[847,8],[1012,7],[1513,5]]},"965":{"position":[[2103,6],[3025,6]]},"971":{"position":[[207,6],[610,5]]},"975":{"position":[[1082,6],[1492,5]]},"991":{"position":[[275,6]]},"1065":{"position":[[4533,9]]},"1071":{"position":[[3757,8]]},"1081":{"position":[[402,5]]},"1093":{"position":[[2030,5]]},"1109":{"position":[[2195,6]]},"1111":{"position":[[568,6]]},"1113":{"position":[[1292,8]]},"1129":{"position":[[358,5]]},"1143":{"position":[[170,5]]},"1166":{"position":[[780,5]]},"1172":{"position":[[687,5],[790,5]]},"1200":{"position":[[108,5]]},"1213":{"position":[[233,5]]},"1277":{"position":[[157,5]]},"1279":{"position":[[158,5]]},"1283":{"position":[[1101,5]]},"1375":{"position":[[340,7]]},"1431":{"position":[[1163,6]]},"1477":{"position":[[210,6]]},"1491":{"position":[[223,6]]},"1495":{"position":[[297,5],[1795,5],[1925,7],[3792,5],[6479,5],[8958,5],[9436,8],[9521,5],[10009,9],[10101,5]]},"1513":{"position":[[109,6]]},"1579":{"position":[[391,5]]},"1650":{"position":[[733,5]]},"1729":{"position":[[464,5]]},"1761":{"position":[[887,5],[922,6],[1031,5]]},"1777":{"position":[[1355,8]]},"1779":{"position":[[47,9]]},"1783":{"position":[[872,5],[905,5]]},"1787":{"position":[[210,5],[305,5]]},"1803":{"position":[[103,5]]},"1823":{"position":[[569,8],[735,8]]},"1891":{"position":[[264,6]]},"1945":{"position":[[437,5],[668,6]]},"1951":{"position":[[486,6]]},"1967":{"position":[[68,6]]},"1975":{"position":[[52,5]]},"1981":{"position":[[532,5],[696,7]]},"1983":{"position":[[208,5]]},"2016":{"position":[[69,5]]},"2026":{"position":[[252,6],[597,5],[720,5]]},"2030":{"position":[[1162,6],[1295,6],[1640,5],[1710,5]]},"2052":{"position":[[275,6]]},"2098":{"position":[[123,5]]},"2100":{"position":[[847,8],[1012,7],[1513,5]]},"2102":{"position":[[2314,7],[2364,6],[3295,6]]},"2146":{"position":[[4650,9]]},"2152":{"position":[[3788,8]]},"2164":{"position":[[402,5]]},"2176":{"position":[[2030,5]]},"2190":{"position":[[2249,6]]},"2192":{"position":[[543,6]]},"2194":{"position":[[1356,8]]},"2210":{"position":[[405,5]]},"2224":{"position":[[170,5]]},"2332":{"position":[[108,5]]},"2359":{"position":[[456,6]]},"2381":{"position":[[340,7]]},"2387":{"position":[[233,5]]},"2424":{"position":[[157,5]]},"2426":{"position":[[158,5]]},"2430":{"position":[[1101,5]]},"2434":{"position":[[1015,5]]},"2526":{"position":[[340,7]]},"2614":{"position":[[1192,6]]},"2644":{"position":[[109,6]]},"2662":{"position":[[298,5],[1810,5],[1940,7],[3835,5],[6552,5],[9078,5],[9592,8],[9677,5],[10122,9],[10221,5]]},"2670":{"position":[[210,6]]},"2684":{"position":[[223,6]]},"2772":{"position":[[391,5]]},"2780":{"position":[[726,5]]},"2873":{"position":[[1244,5]]},"2877":{"position":[[1230,5]]},"2883":{"position":[[1547,5]]},"2885":{"position":[[69,7]]},"2918":{"position":[[464,5]]},"2950":{"position":[[942,5],[973,5],[1059,5]]},"2966":{"position":[[1433,8]]},"2968":{"position":[[49,9]]},"2972":{"position":[[859,5]]},"2976":{"position":[[354,5],[449,5]]},"2992":{"position":[[103,5]]},"3012":{"position":[[569,8],[735,8]]},"3082":{"position":[[264,6]]},"3086":{"position":[[254,6],[599,5],[722,5]]},"3158":{"position":[[437,5],[668,6]]},"3164":{"position":[[486,6]]},"3180":{"position":[[68,6]]},"3188":{"position":[[25,5]]},"3194":{"position":[[532,5],[696,7]]},"3196":{"position":[[208,5]]},"3235":{"position":[[69,5]]},"3257":{"position":[[123,5]]},"3259":{"position":[[847,8],[1012,7],[1513,5]]},"3261":{"position":[[2314,7],[2364,6],[3295,6]]},"3267":{"position":[[252,6],[597,5],[720,5]]},"3271":{"position":[[1128,6],[1261,6],[1606,5],[1676,5]]},"3291":{"position":[[275,6]]},"3329":{"position":[[76,7]]},"3349":{"position":[[3782,6]]},"3371":{"position":[[1024,6],[1097,7],[1250,8]]},"3375":{"position":[[4679,9]]},"3381":{"position":[[3791,8]]},"3393":{"position":[[402,5]]},"3405":{"position":[[2242,5]]},"3425":{"position":[[386,9]]},"3431":{"position":[[1757,6]]},"3433":{"position":[[618,6]]},"3435":{"position":[[1424,8]]},"3451":{"position":[[465,5]]},"3465":{"position":[[170,5]]},"3588":{"position":[[108,5]]},"3601":{"position":[[233,5]]},"3612":{"position":[[1042,5]]},"3619":{"position":[[157,5]]},"3621":{"position":[[158,5]]},"3625":{"position":[[1101,5]]},"3647":{"position":[[456,6]]},"3669":{"position":[[340,7]]}}}],["valuabl",{"_index":268,"t":{"4":{"position":[[1057,8]]},"78":{"position":[[23,8]]},"170":{"position":[[147,8]]}}}],["value'};const",{"_index":3513,"t":{"509":{"position":[[269,14]]},"1371":{"position":[[269,14]]},"2377":{"position":[[331,14]]},"2522":{"position":[[278,14]]},"3665":{"position":[[331,14]]}}}],["value,if",{"_index":1585,"t":{"98":{"position":[[1660,8]]}}}],["value=\"log",{"_index":2701,"t":{"283":{"position":[[1387,10]]}}}],["valuelength",{"_index":1742,"t":{"110":{"position":[[3330,11],[3392,13],[3473,12],[3660,11]]}}}],["values.yaml",{"_index":3677,"t":{"592":{"position":[[130,11]]},"1557":{"position":[[130,11]]},"2760":{"position":[[130,11]]}}}],["valuesummari",{"_index":3117,"t":{"473":{"position":[[950,13],[1076,13]]}}}],["vanilla",{"_index":2097,"t":{"146":{"position":[[381,7]]},"168":{"position":[[168,7]]}}}],["var",{"_index":1735,"t":{"110":{"position":[[2982,3],[3172,3],[3326,3],[4108,3]]},"128":{"position":[[1166,3]]},"572":{"position":[[461,4]]},"604":{"position":[[783,3]]},"768":{"position":[[89,3],[614,3]]},"770":{"position":[[212,3]]},"772":{"position":[[185,3]]},"1184":{"position":[[1613,3]]},"1523":{"position":[[838,4]]},"1533":{"position":[[871,3]]},"1787":{"position":[[89,3],[612,3]]},"1789":{"position":[[212,3]]},"1791":{"position":[[185,3]]},"2692":{"position":[[838,4]]},"2702":{"position":[[871,3]]},"2976":{"position":[[756,3]]},"3327":{"position":[[352,4]]},"3351":{"position":[[2295,3]]}}}],["vari",{"_index":188,"t":{"2":{"position":[[3076,4]]},"16":{"position":[[6930,4]]},"52":{"position":[[939,4]]},"309":{"position":[[100,5],[559,5]]},"361":{"position":[[910,5]]},"363":{"position":[[1081,6]]},"1059":{"position":[[250,6]]},"1178":{"position":[[459,5]]},"1295":{"position":[[100,5],[559,5]]},"1381":{"position":[[915,5]]},"1383":{"position":[[1232,6]]},"2140":{"position":[[250,6]]},"2442":{"position":[[100,5],[559,5]]},"2532":{"position":[[971,5]]},"3369":{"position":[[255,6]]}}}],["variabl",{"_index":2388,"t":{"212":{"position":[[708,9]]},"299":{"position":[[492,9]]},"331":{"position":[[109,9]]},"451":{"position":[[71,10]]},"519":{"position":[[135,10]]},"572":{"position":[[388,9]]},"598":{"position":[[325,9]]},"600":{"position":[[1078,9]]},"604":{"position":[[696,9]]},"878":{"position":[[262,9],[462,8],[633,9],[944,9],[1036,8],[1069,9]]},"880":{"position":[[252,8]]},"943":{"position":[[224,8]]},"985":{"position":[[444,9],[1255,8]]},"1061":{"position":[[537,8]]},"1317":{"position":[[169,9]]},"1451":{"position":[[71,10]]},"1495":{"position":[[5932,9]]},"1523":{"position":[[46,9],[197,9],[400,8],[885,9],[974,10]]},"1525":{"position":[[79,9]]},"1527":{"position":[[73,9]]},"1529":{"position":[[74,9]]},"1533":{"position":[[784,9]]},"1563":{"position":[[325,9]]},"1567":{"position":[[463,8]]},"1569":{"position":[[135,8],[598,10],[644,10],[1007,8],[1133,8]]},"1571":{"position":[[48,8],[328,10],[512,10]]},"1573":{"position":[[124,9]]},"1619":{"position":[[1360,9]]},"1797":{"position":[[59,9],[136,9],[462,10],[473,9],[632,10],[643,9],[910,8]]},"1945":{"position":[[262,9],[462,8],[633,9],[944,9],[1036,8],[1069,9]]},"1947":{"position":[[252,8]]},"2018":{"position":[[224,8]]},"2044":{"position":[[444,9],[1255,8]]},"2142":{"position":[[537,8]]},"2464":{"position":[[169,9]]},"2592":{"position":[[72,10]]},"2662":{"position":[[5993,9]]},"2692":{"position":[[46,9],[197,9],[400,8],[885,9],[974,10]]},"2694":{"position":[[79,9]]},"2696":{"position":[[73,9]]},"2698":{"position":[[74,9]]},"2702":{"position":[[784,9]]},"2726":{"position":[[463,8]]},"2728":{"position":[[135,8],[598,10],[644,10],[1007,8],[1133,8]]},"2730":{"position":[[48,8],[328,10],[512,10]]},"2732":{"position":[[124,9]]},"2766":{"position":[[325,9]]},"2806":{"position":[[1360,9]]},"2986":{"position":[[25,9],[102,9],[428,10],[439,9],[598,10],[609,9],[876,8]]},"3154":{"position":[[103,9]]},"3156":{"position":[[265,9]]},"3158":{"position":[[262,9],[462,8],[633,9],[944,9],[1036,8],[1069,9]]},"3160":{"position":[[252,8]]},"3237":{"position":[[224,8]]},"3285":{"position":[[444,9],[1255,8]]},"3327":{"position":[[1035,8]]},"3371":{"position":[[537,8],[1142,8]]}}}],["variad",{"_index":3181,"t":{"475":{"position":[[2350,8]]}}}],["variant",{"_index":454,"t":{"10":{"position":[[1564,8]]},"951":{"position":[[534,8]]},"2078":{"position":[[534,8]]},"3245":{"position":[[534,8]]}}}],["variat",{"_index":2169,"t":{"166":{"position":[[43,10]]},"477":{"position":[[420,10]]},"1923":{"position":[[167,11]]},"3116":{"position":[[167,11]]}}}],["varieti",{"_index":1788,"t":{"120":{"position":[[575,7]]},"148":{"position":[[878,7]]},"279":{"position":[[1189,7]]}}}],["varint",{"_index":4551,"t":{"1164":{"position":[[1643,6],[1815,6]]},"2244":{"position":[[1339,6],[1511,6]]},"3555":{"position":[[1339,6],[1511,6]]}}}],["variou",{"_index":294,"t":{"6":{"position":[[457,7]]},"8":{"position":[[1558,7]]},"18":{"position":[[627,7]]},"68":{"position":[[595,7],[1740,7]]},"164":{"position":[[29,7]]},"166":{"position":[[1919,7]]},"172":{"position":[[319,7]]},"204":{"position":[[663,7]]},"226":{"position":[[60,7],[1032,7]]},"256":{"position":[[266,7]]},"266":{"position":[[498,7]]},"279":{"position":[[400,7]]},"381":{"position":[[582,7]]},"417":{"position":[[260,7]]},"439":{"position":[[20,7]]},"467":{"position":[[93,7],[697,7]]},"475":{"position":[[2164,7]]},"477":{"position":[[393,7],[3227,7]]},"485":{"position":[[105,7]]},"754":{"position":[[259,7]]},"808":{"position":[[311,7]]},"953":{"position":[[263,7]]},"1085":{"position":[[181,7]]},"1087":{"position":[[77,7]]},"1162":{"position":[[2198,7]]},"1164":{"position":[[3064,7]]},"1182":{"position":[[100,7]]},"1270":{"position":[[511,7]]},"1401":{"position":[[257,7]]},"1423":{"position":[[20,7]]},"1773":{"position":[[259,7]]},"1859":{"position":[[311,7]]},"2080":{"position":[[263,7]]},"2168":{"position":[[181,7]]},"2170":{"position":[[77,7]]},"2250":{"position":[[100,7]]},"2294":{"position":[[649,7]]},"2506":{"position":[[387,7],[663,7],[1615,7]]},"2558":{"position":[[258,7]]},"2580":{"position":[[20,7]]},"2962":{"position":[[277,7]]},"3050":{"position":[[311,7]]},"3060":{"position":[[959,7]]},"3072":{"position":[[586,7]]},"3247":{"position":[[263,7]]},"3324":{"position":[[25,7],[654,7]]},"3347":{"position":[[323,7]]},"3349":{"position":[[3695,7]]},"3397":{"position":[[181,7]]},"3399":{"position":[[77,7]]},"3524":{"position":[[649,7]]},"3561":{"position":[[100,7]]}}}],["vars.ten",{"_index":4811,"t":{"1523":{"position":[[1589,12]]},"2692":{"position":[[1589,12]]}}}],["vars.us",{"_index":4807,"t":{"1523":{"position":[[1177,10],[1620,10]]},"2692":{"position":[[1177,10],[1620,10]]}}}],["vector",{"_index":2283,"t":{"192":{"position":[[829,6]]}}}],["vendor",{"_index":3190,"t":{"475":{"position":[[3473,7]]}}}],["venv",{"_index":1798,"t":{"122":{"position":[[425,4]]}}}],["verb",{"_index":2458,"t":{"228":{"position":[[500,5]]}}}],["veri",{"_index":316,"t":{"8":{"position":[[372,4]]},"10":{"position":[[1678,4]]},"12":{"position":[[1626,4]]},"14":{"position":[[1056,4],[1218,4]]},"16":{"position":[[4167,4],[6249,4],[6341,4]]},"18":{"position":[[1198,4]]},"28":{"position":[[1196,4]]},"30":{"position":[[239,4]]},"34":{"position":[[1970,4],[1997,4]]},"42":{"position":[[1325,4],[1617,4]]},"50":{"position":[[1621,4]]},"54":{"position":[[1340,4]]},"62":{"position":[[930,4]]},"70":{"position":[[79,4]]},"74":{"position":[[316,4],[1305,4]]},"78":{"position":[[389,4]]},"112":{"position":[[264,4]]},"118":{"position":[[758,4]]},"180":{"position":[[191,4]]},"182":{"position":[[1735,4]]},"190":{"position":[[8,4]]},"204":{"position":[[1933,4]]},"222":{"position":[[100,4]]},"226":{"position":[[745,4]]},"242":{"position":[[230,4]]},"256":{"position":[[1484,4]]},"260":{"position":[[316,4]]},"262":{"position":[[1464,4]]},"279":{"position":[[795,4]]},"301":{"position":[[40,4]]},"319":{"position":[[13,4]]},"329":{"position":[[97,4]]},"351":{"position":[[169,4]]},"399":{"position":[[422,4]]},"411":{"position":[[228,4]]},"443":{"position":[[394,4]]},"457":{"position":[[836,4]]},"475":{"position":[[2565,4]]},"477":{"position":[[1107,4]]},"481":{"position":[[3370,4],[5181,4]]},"489":{"position":[[578,4]]},"552":{"position":[[43,4]]},"712":{"position":[[172,4]]},"714":{"position":[[193,4]]},"734":{"position":[[85,4],[1158,5]]},"912":{"position":[[482,4]]},"951":{"position":[[277,4]]},"983":{"position":[[178,4]]},"1107":{"position":[[851,4]]},"1129":{"position":[[886,4]]},"1213":{"position":[[826,4]]},"1283":{"position":[[542,4]]},"1285":{"position":[[169,4],[338,4],[428,4]]},"1305":{"position":[[13,4]]},"1315":{"position":[[97,4]]},"1339":{"position":[[169,4]]},"1457":{"position":[[832,4]]},"1495":{"position":[[56,4],[10287,4]]},"1549":{"position":[[579,4]]},"1678":{"position":[[128,4],[762,4]]},"1680":{"position":[[1202,4]]},"1881":{"position":[[85,4],[1189,5]]},"1891":{"position":[[172,4]]},"1893":{"position":[[193,4]]},"1981":{"position":[[482,4]]},"2042":{"position":[[178,4]]},"2048":{"position":[[555,4]]},"2078":{"position":[[277,4]]},"2188":{"position":[[854,4]]},"2210":{"position":[[928,4]]},"2387":{"position":[[826,4]]},"2430":{"position":[[542,4]]},"2432":{"position":[[169,4],[338,4],[428,4]]},"2452":{"position":[[13,4]]},"2462":{"position":[[97,4]]},"2486":{"position":[[169,4]]},"2506":{"position":[[224,4]]},"2598":{"position":[[851,4]]},"2662":{"position":[[57,4],[10406,4]]},"2718":{"position":[[579,4]]},"2832":{"position":[[128,4],[768,4]]},"2834":{"position":[[1208,4]]},"3072":{"position":[[88,4],[1477,5]]},"3082":{"position":[[172,4]]},"3084":{"position":[[193,4]]},"3194":{"position":[[482,4]]},"3245":{"position":[[277,4]]},"3283":{"position":[[178,4]]},"3345":{"position":[[698,4]]},"3347":{"position":[[4032,4]]},"3349":{"position":[[308,4]]},"3427":{"position":[[824,4]]},"3451":{"position":[[988,4]]},"3601":{"position":[[826,4]]},"3625":{"position":[[542,4]]},"3627":{"position":[[169,4],[338,4],[428,4]]}}}],["verif",{"_index":2376,"t":{"210":{"position":[[826,12]]},"975":{"position":[[472,13]]},"2032":{"position":[[241,13]]},"2038":{"position":[[226,13]]},"3018":{"position":[[307,12]]},"3229":{"position":[[166,12]]},"3273":{"position":[[241,13]]},"3279":{"position":[[226,13]]}}}],["verifi",{"_index":1698,"t":{"110":{"position":[[63,6]]},"128":{"position":[[2362,6]]},"210":{"position":[[644,9]]},"212":{"position":[[670,9]]},"776":{"position":[[368,8]]},"1795":{"position":[[368,8],[635,8]]},"2032":{"position":[[1057,6]]},"2038":{"position":[[1114,6]]},"2970":{"position":[[343,8]]},"2984":{"position":[[368,8],[635,8]]},"3018":{"position":[[125,9]]},"3273":{"position":[[949,6]]},"3279":{"position":[[1006,6]]}}}],["versa",{"_index":1030,"t":{"36":{"position":[[507,6]]},"2652":{"position":[[750,6]]}}}],["version",{"_index":1090,"t":{"42":{"position":[[1823,9]]},"46":{"position":[[1141,7]]},"50":{"position":[[1662,8]]},"60":{"position":[[219,7],[607,7]]},"62":{"position":[[1978,8]]},"82":{"position":[[441,7]]},"86":{"position":[[216,7]]},"88":{"position":[[82,7],[128,8],[1011,7]]},"128":{"position":[[2850,7]]},"146":{"position":[[122,8]]},"196":{"position":[[88,7]]},"200":{"position":[[119,7]]},"204":{"position":[[2310,8],[2402,7]]},"218":{"position":[[688,9],[860,7],[884,7]]},"236":{"position":[[897,11],[916,7],[1033,7],[1097,7],[1153,8]]},"248":{"position":[[198,8],[265,7]]},"250":{"position":[[1549,8]]},"383":{"position":[[512,8]]},"391":{"position":[[123,8]]},"401":{"position":[[67,7],[80,7],[326,7]]},"441":{"position":[[82,7]]},"481":{"position":[[5689,7]]},"523":{"position":[[598,7],[634,7]]},"531":{"position":[[149,8]]},"566":{"position":[[189,7]]},"576":{"position":[[975,7]]},"578":{"position":[[597,8]]},"590":{"position":[[47,7]]},"592":{"position":[[86,7]]},"598":{"position":[[80,8],[217,7]]},"606":{"position":[[235,9],[1014,9]]},"862":{"position":[[19,7],[52,7]]},"945":{"position":[[213,7]]},"973":{"position":[[332,7]]},"993":{"position":[[553,7]]},"999":{"position":[[445,7],[487,7]]},"1065":{"position":[[2832,7],[2860,7]]},"1131":{"position":[[458,10]]},"1166":{"position":[[1141,10],[1271,7],[1288,7]]},"1249":{"position":[[279,7],[309,7]]},"1323":{"position":[[349,8]]},"1425":{"position":[[82,7]]},"1431":{"position":[[831,7],[867,7]]},"1435":{"position":[[202,8]]},"1439":{"position":[[149,8]]},"1495":{"position":[[3330,7]]},"1499":{"position":[[60,8],[148,8],[969,7],[1750,8]]},"1535":{"position":[[226,9],[925,9]]},"1555":{"position":[[47,7]]},"1557":{"position":[[86,7]]},"1563":{"position":[[80,8],[217,7]]},"1579":{"position":[[568,7]]},"1583":{"position":[[1459,7]]},"1585":{"position":[[646,8]]},"1686":{"position":[[420,7]]},"1877":{"position":[[80,9]]},"2020":{"position":[[213,7]]},"2028":{"position":[[332,7]]},"2054":{"position":[[553,7]]},"2060":{"position":[[19,7],[52,7]]},"2106":{"position":[[445,7],[487,7]]},"2146":{"position":[[2949,7],[2977,7]]},"2212":{"position":[[458,10]]},"2294":{"position":[[598,8]]},"2347":{"position":[[480,7]]},"2400":{"position":[[279,7],[309,7]]},"2470":{"position":[[349,8]]},"2506":{"position":[[1449,8]]},"2582":{"position":[[86,7]]},"2614":{"position":[[855,7],[891,7]]},"2618":{"position":[[202,8]]},"2622":{"position":[[149,8]]},"2630":{"position":[[60,8],[148,8],[969,7],[1750,8]]},"2652":{"position":[[458,8],[727,8]]},"2656":{"position":[[295,7]]},"2662":{"position":[[3347,7]]},"2704":{"position":[[226,9],[925,9]]},"2758":{"position":[[47,7]]},"2760":{"position":[[86,7]]},"2766":{"position":[[80,8],[217,7]]},"2772":{"position":[[568,7]]},"2780":{"position":[[259,8]]},"2782":{"position":[[645,8]]},"2810":{"position":[[420,7]]},"3068":{"position":[[53,8],[112,8],[694,9]]},"3138":{"position":[[19,7],[52,7]]},"3239":{"position":[[213,7]]},"3269":{"position":[[332,7]]},"3293":{"position":[[553,7]]},"3307":{"position":[[445,7],[487,7]]},"3324":{"position":[[1607,7],[1697,7]]},"3333":{"position":[[520,7]]},"3375":{"position":[[2949,7],[2977,7]]},"3453":{"position":[[274,10]]},"3524":{"position":[[598,8]]},"3567":{"position":[[279,7],[309,7]]},"3635":{"position":[[480,7]]}}}],["via",{"_index":1035,"t":{"38":{"position":[[140,3]]},"46":{"position":[[1224,3]]},"150":{"position":[[833,3]]},"204":{"position":[[680,3]]},"246":{"position":[[1411,3]]},"301":{"position":[[4239,3]]},"335":{"position":[[209,3],[235,3]]},"345":{"position":[[72,4]]},"355":{"position":[[512,4]]},"439":{"position":[[88,3]]},"600":{"position":[[1034,3]]},"690":{"position":[[124,3]]},"878":{"position":[[241,3]]},"977":{"position":[[143,3]]},"1011":{"position":[[39,4]]},"1061":{"position":[[518,3]]},"1323":{"position":[[209,3],[235,3]]},"1333":{"position":[[72,4]]},"1347":{"position":[[512,4]]},"1423":{"position":[[88,3]]},"1619":{"position":[[1316,3]]},"1945":{"position":[[241,3]]},"2034":{"position":[[143,3]]},"2092":{"position":[[39,4]]},"2142":{"position":[[518,3]]},"2436":{"position":[[512,4]]},"2470":{"position":[[209,3],[235,3]]},"2480":{"position":[[72,4]]},"2580":{"position":[[95,3]]},"2806":{"position":[[1316,3]]},"3158":{"position":[[241,3]]},"3275":{"position":[[143,3]]},"3301":{"position":[[39,4]]},"3320":{"position":[[39,4]]},"3371":{"position":[[518,3]]}}}],["viabl",{"_index":2150,"t":{"162":{"position":[[1269,6]]},"2610":{"position":[[1895,6]]}}}],["vice",{"_index":1029,"t":{"36":{"position":[[502,4]]},"2652":{"position":[[745,4]]}}}],["video",{"_index":2094,"t":{"146":{"position":[[77,6]]},"232":{"position":[[192,6]]},"636":{"position":[[45,6],[60,5]]},"638":{"position":[[45,6],[60,5]]},"977":{"position":[[1758,6]]},"1107":{"position":[[1414,5]]},"1605":{"position":[[45,6],[60,5]]},"1607":{"position":[[45,6],[60,5]]},"2034":{"position":[[1758,6]]},"2188":{"position":[[1417,5]]},"2434":{"position":[[1318,5]]},"2800":{"position":[[45,6],[60,5]]},"2802":{"position":[[45,6],[60,5]]},"3275":{"position":[[1758,6]]},"3612":{"position":[[1345,5]]}}}],["view",{"_index":1783,"t":{"118":{"position":[[204,4],[279,4]]},"128":{"position":[[29,5],[44,4],[330,5],[1322,4],[1349,5],[1519,5],[2384,4],[3179,4],[3258,4]]},"130":{"position":[[30,5],[43,4],[312,4],[339,4],[1927,4],[1954,4],[2216,4],[2867,4]]},"136":{"position":[[1964,4]]},"156":{"position":[[8,4]]},"158":{"position":[[195,6]]},"166":{"position":[[104,4]]},"283":{"position":[[999,5],[1454,5]]},"319":{"position":[[682,4]]},"1305":{"position":[[682,4]]},"2452":{"position":[[682,4]]},"3349":{"position":[[32,4]]}}}],["views.connect",{"_index":2050,"t":{"136":{"position":[[547,14]]}}}],["views.index",{"_index":1887,"t":{"128":{"position":[[1937,12]]},"130":{"position":[[2333,12]]},"136":{"position":[[421,12]]}}}],["views.pi",{"_index":1826,"t":{"126":{"position":[[340,8],[604,8]]},"128":{"position":[[456,8],[1779,11]]},"130":{"position":[[292,8]]}}}],["views.publish",{"_index":2056,"t":{"136":{"position":[[673,14]]}}}],["views.room",{"_index":1984,"t":{"130":{"position":[[2405,11]]},"136":{"position":[[493,11]]}}}],["views.subscrib",{"_index":2053,"t":{"136":{"position":[[609,16]]}}}],["views/app.html",{"_index":2704,"t":{"283":{"position":[[1468,14]]}}}],["views/login.html",{"_index":2688,"t":{"283":{"position":[[1013,16]]}}}],["viewsurlpattern",{"_index":1886,"t":{"128":{"position":[[1907,16]]},"130":{"position":[[2303,16]]},"136":{"position":[[391,16]]}}}],["virtual",{"_index":4108,"t":{"951":{"position":[[893,7]]},"953":{"position":[[899,7]]},"2032":{"position":[[1164,7]]},"2038":{"position":[[1221,7]]},"2078":{"position":[[893,7]]},"2080":{"position":[[899,7]]},"3245":{"position":[[893,7]]},"3247":{"position":[[899,7]]},"3273":{"position":[[1056,7]]},"3279":{"position":[[1113,7]]}}}],["virtualenv",{"_index":1795,"t":{"122":{"position":[[369,11]]}}}],["visibl",{"_index":2467,"t":{"234":{"position":[[94,7]]},"319":{"position":[[903,7]]},"389":{"position":[[960,7]]},"1305":{"position":[[903,7]]},"2452":{"position":[[903,7]]}}}],["visual",{"_index":2298,"t":{"194":{"position":[[865,15]]},"373":{"position":[[858,15]]},"477":{"position":[[5873,10]]},"479":{"position":[[4653,10]]},"1013":{"position":[[159,10]]},"2094":{"position":[[159,10]]},"3303":{"position":[[159,10]]},"3322":{"position":[[159,10]]}}}],["vitali",{"_index":3047,"t":{"407":{"position":[[151,6]]}}}],["vite",{"_index":2789,"t":{"299":{"position":[[719,4]]},"301":{"position":[[53,4],[390,4]]}}}],["vite@latest",{"_index":2794,"t":{"301":{"position":[[75,11]]}}}],["vladimir",{"_index":3193,"t":{"477":{"position":[[184,8]]}}}],["void",{"_index":5242,"t":{"2345":{"position":[[2722,4],[2830,4],[2965,4]]},"2355":{"position":[[2458,4],[2595,4],[2731,4]]},"2359":{"position":[[2178,4]]},"3633":{"position":[[2722,4],[2830,4],[2965,4]]},"3643":{"position":[[2458,4],[2595,4],[2731,4]]},"3647":{"position":[[2178,4]]}}}],["volum",{"_index":2500,"t":{"246":{"position":[[330,6]]},"527":{"position":[[273,8]]},"1435":{"position":[[297,8]]},"2618":{"position":[[297,8]]},"2722":{"position":[[1766,7]]}}}],["vuej",{"_index":2195,"t":{"168":{"position":[[151,5]]}}}],["vulner",{"_index":4289,"t":{"1065":{"position":[[598,10]]},"2146":{"position":[[598,10]]},"3375":{"position":[[598,10]]}}}],["vvv",{"_index":1645,"t":{"104":{"position":[[594,3]]},"106":{"position":[[1136,3],[1202,3],[1730,3]]},"110":{"position":[[777,3]]}}}],["vvvpost",{"_index":4401,"t":{"1109":{"position":[[899,7]]},"2190":{"position":[[959,7]]}}}],["vyxywx1p1lv9uc9cau04vpa6lgg5gsw5lz1iw",{"_index":4638,"t":{"1262":{"position":[[659,37]]},"2413":{"position":[[668,37]]},"3580":{"position":[[668,37]]}}}],["w3c",{"_index":2366,"t":{"208":{"position":[[425,3]]}}}],["wait",{"_index":778,"t":{"18":{"position":[[2223,4]]},"38":{"position":[[71,7]]},"116":{"position":[[232,4]]},"166":{"position":[[1126,4]]},"256":{"position":[[1368,7]]},"471":{"position":[[614,5]]},"473":{"position":[[2678,5]]},"483":{"position":[[1435,7],[1831,5]]},"912":{"position":[[396,4]]},"953":{"position":[[636,4]]},"1178":{"position":[[404,4]]},"1479":{"position":[[86,4]]},"1493":{"position":[[86,4]]},"1981":{"position":[[396,4]]},"1983":{"position":[[82,4]]},"2080":{"position":[[636,4]]},"2240":{"position":[[2163,4],[2279,4]]},"2296":{"position":[[653,5]]},"2349":{"position":[[211,4]]},"2672":{"position":[[86,4]]},"2686":{"position":[[86,4]]},"3194":{"position":[[396,4]]},"3196":{"position":[[82,4]]},"3247":{"position":[[636,4]]},"3349":{"position":[[782,5],[4009,5]]},"3486":{"position":[[653,5]]},"3551":{"position":[[2163,4],[2279,4]]},"3637":{"position":[[211,4]]}}}],["wanicon",{"_index":1498,"t":{"90":{"position":[[768,7]]}}}],["wanna",{"_index":419,"t":{"10":{"position":[[625,5]]}}}],["want",{"_index":605,"t":{"16":{"position":[[1429,4],[2462,4]]},"18":{"position":[[2162,4],[2324,4]]},"28":{"position":[[263,4]]},"34":{"position":[[861,4]]},"40":{"position":[[141,4]]},"48":{"position":[[46,4]]},"50":{"position":[[7,4]]},"54":{"position":[[470,4]]},"90":{"position":[[307,4]]},"112":{"position":[[249,4]]},"120":{"position":[[28,4]]},"134":{"position":[[173,4]]},"136":{"position":[[2064,4]]},"148":{"position":[[29,4]]},"172":{"position":[[41,4]]},"174":{"position":[[1297,4]]},"180":{"position":[[520,4],[564,4]]},"210":{"position":[[296,4]]},"222":{"position":[[282,4],[672,4]]},"244":{"position":[[14,4]]},"246":{"position":[[3774,4]]},"250":{"position":[[1657,4],[1724,4]]},"262":{"position":[[159,4],[1257,5]]},"279":{"position":[[296,4]]},"323":{"position":[[141,4]]},"339":{"position":[[1589,4]]},"379":{"position":[[1501,4]]},"385":{"position":[[1676,4]]},"403":{"position":[[686,4]]},"443":{"position":[[623,4]]},"457":{"position":[[193,4],[509,4]]},"463":{"position":[[60,4]]},"475":{"position":[[3109,6]]},"560":{"position":[[73,4]]},"612":{"position":[[100,4]]},"648":{"position":[[406,4],[1043,4]]},"652":{"position":[[112,4]]},"666":{"position":[[304,4],[901,4]]},"680":{"position":[[94,4],[153,4]]},"694":{"position":[[210,4]]},"696":{"position":[[384,4]]},"734":{"position":[[625,4]]},"740":{"position":[[167,4]]},"742":{"position":[[678,4]]},"754":{"position":[[50,4]]},"764":{"position":[[1837,4]]},"832":{"position":[[127,4]]},"856":{"position":[[7,4],[254,4]]},"939":{"position":[[255,4]]},"949":{"position":[[82,4]]},"977":{"position":[[912,5]]},"999":{"position":[[246,4]]},"1029":{"position":[[195,4]]},"1049":{"position":[[355,4]]},"1061":{"position":[[297,4]]},"1063":{"position":[[75,4]]},"1065":{"position":[[947,4],[1230,4],[3219,4]]},"1071":{"position":[[3832,4]]},"1073":{"position":[[3171,4]]},"1101":{"position":[[715,4]]},"1107":{"position":[[1044,4]]},"1137":{"position":[[240,4]]},"1283":{"position":[[1426,4]]},"1285":{"position":[[245,4],[297,4],[355,4]]},"1309":{"position":[[141,4]]},"1327":{"position":[[1589,4]]},"1343":{"position":[[7,4]]},"1457":{"position":[[190,4],[508,4]]},"1463":{"position":[[60,4]]},"1475":{"position":[[733,4]]},"1495":{"position":[[9334,4]]},"1499":{"position":[[466,4]]},"1547":{"position":[[171,4]]},"1676":{"position":[[744,4]]},"1694":{"position":[[406,4],[1043,4]]},"1699":{"position":[[112,4]]},"1710":{"position":[[304,4],[901,4]]},"1723":{"position":[[7,4],[254,4]]},"1731":{"position":[[94,4],[153,4]]},"1741":{"position":[[2103,4]]},"1743":{"position":[[1322,4]]},"1745":{"position":[[424,4]]},"1747":{"position":[[570,4]]},"1759":{"position":[[167,4]]},"1761":{"position":[[678,4]]},"1773":{"position":[[50,4]]},"1783":{"position":[[1837,4]]},"1813":{"position":[[195,4]]},"1823":{"position":[[610,4]]},"1875":{"position":[[384,4]]},"1881":{"position":[[625,4]]},"1899":{"position":[[478,4]]},"1923":{"position":[[356,4]]},"2014":{"position":[[255,4]]},"2034":{"position":[[912,5]]},"2048":{"position":[[187,4]]},"2076":{"position":[[82,4]]},"2086":{"position":[[346,4]]},"2106":{"position":[[246,4]]},"2142":{"position":[[297,4]]},"2144":{"position":[[75,4]]},"2146":{"position":[[1064,4],[1347,4],[3336,4],[6120,4]]},"2152":{"position":[[3863,4]]},"2154":{"position":[[3171,4]]},"2156":{"position":[[1274,4]]},"2188":{"position":[[1047,4]]},"2218":{"position":[[240,4]]},"2230":{"position":[[355,4]]},"2256":{"position":[[280,4]]},"2258":{"position":[[765,4]]},"2345":{"position":[[5200,4]]},"2355":{"position":[[4756,4]]},"2357":{"position":[[762,4]]},"2361":{"position":[[710,4]]},"2369":{"position":[[150,4]]},"2430":{"position":[[1426,4]]},"2432":{"position":[[245,4],[297,4],[355,4]]},"2456":{"position":[[141,4]]},"2474":{"position":[[1589,4]]},"2490":{"position":[[7,4]]},"2530":{"position":[[348,4]]},"2598":{"position":[[193,4],[517,4]]},"2604":{"position":[[61,4]]},"2630":{"position":[[466,4]]},"2662":{"position":[[9481,4]]},"2668":{"position":[[733,4]]},"2716":{"position":[[171,4]]},"2818":{"position":[[406,4],[1043,4]]},"2823":{"position":[[112,4]]},"2830":{"position":[[747,4]]},"2899":{"position":[[304,4],[901,4]]},"2912":{"position":[[7,4],[254,4]]},"2920":{"position":[[94,4],[153,4]]},"2930":{"position":[[2103,4]]},"2932":{"position":[[1322,4]]},"2934":{"position":[[424,4]]},"2936":{"position":[[570,4]]},"2950":{"position":[[731,4]]},"2970":{"position":[[115,4]]},"3002":{"position":[[195,4]]},"3012":{"position":[[610,4]]},"3072":{"position":[[913,4]]},"3092":{"position":[[478,4]]},"3116":{"position":[[356,4]]},"3233":{"position":[[255,4]]},"3243":{"position":[[82,4]]},"3253":{"position":[[346,4]]},"3275":{"position":[[912,5]]},"3307":{"position":[[246,4]]},"3327":{"position":[[953,4]]},"3371":{"position":[[297,4]]},"3373":{"position":[[75,4]]},"3375":{"position":[[1064,4],[1347,4],[3336,4],[6149,4]]},"3381":{"position":[[3866,4]]},"3383":{"position":[[3171,4]]},"3385":{"position":[[1247,4]]},"3417":{"position":[[355,4]]},"3459":{"position":[[240,4]]},"3490":{"position":[[441,4]]},"3492":{"position":[[765,4]]},"3625":{"position":[[1426,4]]},"3627":{"position":[[245,4],[297,4],[355,4]]},"3633":{"position":[[5200,4]]},"3643":{"position":[[4756,4]]},"3645":{"position":[[762,4]]},"3649":{"position":[[710,4]]},"3657":{"position":[[150,4]]}}}],["warn",{"_index":2387,"t":{"212":{"position":[[547,4],[1239,8]]},"598":{"position":[[613,7]]},"1487":{"position":[[162,4]]},"1563":{"position":[[613,7]]},"1975":{"position":[[252,7]]},"2680":{"position":[[162,4]]},"2766":{"position":[[613,7]]},"3188":{"position":[[225,7]]},"3198":{"position":[[1039,7]]},"3329":{"position":[[116,4]]}}}],["wasm",{"_index":357,"t":{"8":{"position":[[1136,4]]}}}],["wasn't",{"_index":3010,"t":{"385":{"position":[[1519,6]]}}}],["wasrecov",{"_index":5373,"t":{"2361":{"position":[[563,13],[1032,13]]},"3649":{"position":[[563,13],[1032,13]]}}}],["watch",{"_index":1906,"t":{"128":{"position":[[2504,8]]},"1583":{"position":[[120,8]]},"2778":{"position":[[120,8]]}}}],["way",{"_index":228,"t":{"4":{"position":[[214,3]]},"12":{"position":[[1531,3]]},"16":{"position":[[562,3]]},"28":{"position":[[1371,3]]},"30":{"position":[[414,3]]},"32":{"position":[[142,4],[743,3],[957,3],[1207,3]]},"42":{"position":[[1981,3]]},"46":{"position":[[3413,5]]},"54":{"position":[[1265,3]]},"58":{"position":[[614,3]]},"68":{"position":[[603,4]]},"74":{"position":[[2131,3]]},"86":{"position":[[2190,3]]},"100":{"position":[[167,3]]},"108":{"position":[[595,4],[1077,4]]},"122":{"position":[[318,3]]},"162":{"position":[[915,3]]},"166":{"position":[[1845,3],[1927,5]]},"172":{"position":[[327,4],[501,4],[601,4]]},"174":{"position":[[356,5]]},"176":{"position":[[739,3]]},"178":{"position":[[99,3]]},"180":{"position":[[71,3]]},"182":{"position":[[106,3],[295,3],[1146,3]]},"184":{"position":[[273,3]]},"192":{"position":[[6,3]]},"198":{"position":[[579,3]]},"204":{"position":[[1075,3]]},"206":{"position":[[277,3]]},"210":{"position":[[58,3],[369,3]]},"236":{"position":[[1133,3]]},"250":{"position":[[1531,4]]},"262":{"position":[[771,3],[1111,3],[2069,3]]},"264":{"position":[[28,3],[494,3]]},"266":{"position":[[246,3]]},"268":{"position":[[123,3]]},"279":{"position":[[75,4]]},"309":{"position":[[449,3]]},"321":{"position":[[59,3],[1251,3]]},"325":{"position":[[514,3],[554,3]]},"339":{"position":[[433,3],[891,3],[985,4]]},"341":{"position":[[18,4],[819,3]]},"345":{"position":[[107,3]]},"359":{"position":[[1379,4]]},"361":{"position":[[1426,4],[1660,3]]},"363":{"position":[[551,3]]},"367":{"position":[[48,3],[393,3]]},"371":{"position":[[672,3]]},"379":{"position":[[1620,3],[4296,4]]},"383":{"position":[[960,3]]},"387":{"position":[[1726,3]]},"389":{"position":[[926,3]]},"393":{"position":[[471,3]]},"423":{"position":[[48,3]]},"439":{"position":[[35,5]]},"449":{"position":[[267,3]]},"453":{"position":[[866,3]]},"455":{"position":[[160,3],[258,3]]},"475":{"position":[[729,4]]},"477":{"position":[[1120,3],[1611,3],[2584,3]]},"479":{"position":[[1512,3]]},"483":{"position":[[242,3]]},"489":{"position":[[617,3]]},"509":{"position":[[101,3]]},"523":{"position":[[37,3]]},"554":{"position":[[201,3]]},"564":{"position":[[172,4]]},"576":{"position":[[115,3]]},"578":{"position":[[187,5]]},"612":{"position":[[16,3]]},"618":{"position":[[843,3]]},"718":{"position":[[687,3]]},"734":{"position":[[260,3]]},"764":{"position":[[2051,3]]},"780":{"position":[[325,3]]},"808":{"position":[[174,3]]},"856":{"position":[[305,3]]},"868":{"position":[[295,3]]},"874":{"position":[[40,5]]},"876":{"position":[[357,4]]},"880":{"position":[[317,3]]},"892":{"position":[[659,4],[1085,3]]},"961":{"position":[[306,3]]},"979":{"position":[[33,3]]},"989":{"position":[[412,4],[513,3]]},"999":{"position":[[545,5]]},"1041":{"position":[[602,3]]},"1047":{"position":[[9,3]]},"1069":{"position":[[311,4]]},"1077":{"position":[[117,4],[638,3]]},"1107":{"position":[[230,4],[556,4]]},"1129":{"position":[[701,3]]},"1162":{"position":[[819,3],[2322,3]]},"1166":{"position":[[1064,4]]},"1182":{"position":[[149,3]]},"1184":{"position":[[190,3]]},"1213":{"position":[[93,3]]},"1295":{"position":[[449,3]]},"1307":{"position":[[59,3],[1251,3]]},"1311":{"position":[[514,3],[554,3]]},"1327":{"position":[[433,3],[891,3],[985,4]]},"1329":{"position":[[18,4],[819,3]]},"1333":{"position":[[107,3]]},"1341":{"position":[[96,3]]},"1371":{"position":[[101,3]]},"1379":{"position":[[1379,4]]},"1381":{"position":[[1431,4],[1665,3]]},"1383":{"position":[[564,3]]},"1387":{"position":[[48,3],[393,3]]},"1391":{"position":[[672,3]]},"1407":{"position":[[48,3]]},"1423":{"position":[[35,5]]},"1449":{"position":[[267,3]]},"1453":{"position":[[867,3]]},"1455":{"position":[[169,3],[335,3]]},"1469":{"position":[[625,3]]},"1475":{"position":[[1520,3]]},"1479":{"position":[[76,3]]},"1493":{"position":[[76,3]]},"1495":{"position":[[5981,3],[7556,4]]},"1547":{"position":[[16,3]]},"1573":{"position":[[198,4]]},"1585":{"position":[[187,5]]},"1629":{"position":[[28,3],[668,3]]},"1633":{"position":[[254,3]]},"1676":{"position":[[37,3],[1384,3],[1882,3]]},"1680":{"position":[[637,4]]},"1682":{"position":[[421,3],[753,3]]},"1723":{"position":[[305,3]]},"1783":{"position":[[2051,3]]},"1831":{"position":[[325,3]]},"1851":{"position":[[127,3]]},"1859":{"position":[[174,3]]},"1869":{"position":[[952,3]]},"1881":{"position":[[260,3]]},"1897":{"position":[[1040,3]]},"1941":{"position":[[40,5]]},"1943":{"position":[[357,4]]},"1947":{"position":[[317,3]]},"1959":{"position":[[659,4],[1085,3]]},"2036":{"position":[[33,3]]},"2050":{"position":[[412,4],[513,3]]},"2066":{"position":[[295,3]]},"2068":{"position":[[356,3]]},"2086":{"position":[[448,4]]},"2098":{"position":[[365,3]]},"2102":{"position":[[2189,4],[4412,3]]},"2106":{"position":[[545,5]]},"2132":{"position":[[602,3]]},"2150":{"position":[[311,4]]},"2156":{"position":[[1060,3]]},"2160":{"position":[[117,4],[838,3]]},"2188":{"position":[[233,4],[559,4]]},"2210":{"position":[[748,3]]},"2228":{"position":[[9,3]]},"2250":{"position":[[149,3]]},"2377":{"position":[[18,3],[113,3]]},"2387":{"position":[[93,3]]},"2434":{"position":[[1105,4]]},"2442":{"position":[[449,3]]},"2454":{"position":[[59,3],[1251,3]]},"2458":{"position":[[514,3],[554,3]]},"2474":{"position":[[433,3],[891,3],[985,4]]},"2476":{"position":[[18,4],[819,3]]},"2480":{"position":[[107,3]]},"2488":{"position":[[96,3]]},"2522":{"position":[[106,3]]},"2530":{"position":[[2630,3]]},"2532":{"position":[[1839,3]]},"2534":{"position":[[588,3]]},"2538":{"position":[[48,3],[399,4]]},"2542":{"position":[[685,3]]},"2564":{"position":[[48,3]]},"2590":{"position":[[278,3]]},"2594":{"position":[[910,3]]},"2596":{"position":[[174,3],[344,3]]},"2610":{"position":[[613,3]]},"2662":{"position":[[6045,3],[7638,4]]},"2668":{"position":[[1520,3]]},"2672":{"position":[[76,3]]},"2686":{"position":[[76,3]]},"2716":{"position":[[16,3]]},"2722":{"position":[[2243,3]]},"2732":{"position":[[198,4]]},"2750":{"position":[[98,3]]},"2782":{"position":[[187,5]]},"2830":{"position":[[37,3],[1387,3],[1885,3]]},"2834":{"position":[[637,4]]},"2836":{"position":[[421,3],[753,3]]},"2846":{"position":[[28,3],[668,3]]},"2850":{"position":[[254,3]]},"2912":{"position":[[305,3]]},"3022":{"position":[[325,3]]},"3034":{"position":[[339,3]]},"3042":{"position":[[127,3]]},"3050":{"position":[[174,3]]},"3072":{"position":[[268,3],[542,3]]},"3090":{"position":[[1040,3]]},"3144":{"position":[[295,3]]},"3146":{"position":[[356,3]]},"3154":{"position":[[40,5]]},"3156":{"position":[[365,4]]},"3160":{"position":[[321,3]]},"3172":{"position":[[659,4],[1085,3]]},"3198":{"position":[[858,3],[1008,4]]},"3253":{"position":[[448,4]]},"3257":{"position":[[365,3]]},"3261":{"position":[[2189,4],[4412,3]]},"3277":{"position":[[33,3]]},"3289":{"position":[[412,4],[513,3]]},"3307":{"position":[[545,5]]},"3345":{"position":[[1195,3]]},"3347":{"position":[[524,3]]},"3349":{"position":[[4091,3]]},"3361":{"position":[[602,3]]},"3379":{"position":[[358,4]]},"3385":{"position":[[1033,3]]},"3389":{"position":[[117,4],[838,3]]},"3415":{"position":[[9,3]]},"3427":{"position":[[229,4],[464,3]]},"3451":{"position":[[808,3]]},"3561":{"position":[[149,3]]},"3601":{"position":[[93,3]]},"3612":{"position":[[1132,4]]},"3665":{"position":[[18,3],[113,3]]}}}],["wc",{"_index":4115,"t":{"953":{"position":[[545,2]]},"2080":{"position":[[545,2]]},"3247":{"position":[[545,2]]}}}],["we'll",{"_index":2763,"t":{"293":{"position":[[270,5]]},"467":{"position":[[1212,5]]},"1879":{"position":[[930,5]]}}}],["we'r",{"_index":2290,"t":{"194":{"position":[[17,5]]},"204":{"position":[[902,5]]},"246":{"position":[[2511,5],[3051,5]]},"270":{"position":[[888,5]]},"387":{"position":[[928,5]]}}}],["we'v",{"_index":2321,"t":{"200":{"position":[[130,5]]},"204":{"position":[[1951,5]]},"212":{"position":[[71,5]]},"244":{"position":[[257,5]]},"256":{"position":[[160,5]]}}}],["wealthi",{"_index":2602,"t":{"256":{"position":[[784,7]]}}}],["web",{"_index":248,"t":{"4":{"position":[[608,3]]},"18":{"position":[[970,3]]},"26":{"position":[[415,3]]},"28":{"position":[[1578,3]]},"78":{"position":[[985,3]]},"116":{"position":[[84,3]]},"214":{"position":[[1358,3]]},"218":{"position":[[527,3]]},"256":{"position":[[302,4]]},"260":{"position":[[560,5]]},"268":{"position":[[42,3]]},"270":{"position":[[287,3]]},"277":{"position":[[611,3]]},"289":{"position":[[876,3],[928,3],[1036,3]]},"295":{"position":[[118,3]]},"297":{"position":[[425,3],[714,3]]},"299":{"position":[[586,3],[1033,3]]},"301":{"position":[[4258,3]]},"335":{"position":[[261,3]]},"339":{"position":[[474,3]]},"421":{"position":[[56,3]]},"435":{"position":[[24,3]]},"465":{"position":[[140,3]]},"497":{"position":[[200,3]]},"517":{"position":[[175,3]]},"640":{"position":[[123,3]]},"776":{"position":[[25,3],[388,3]]},"854":{"position":[[57,3],[140,3],[436,3],[533,3]]},"856":{"position":[[26,3],[64,3],[278,3]]},"858":{"position":[[149,3],[292,3]]},"892":{"position":[[550,3]]},"920":{"position":[[6,3],[110,3],[137,3]]},"926":{"position":[[212,3],[275,3],[986,3]]},"939":{"position":[[130,3]]},"999":{"position":[[649,3]]},"1003":{"position":[[216,3]]},"1213":{"position":[[68,3]]},"1323":{"position":[[261,3]]},"1327":{"position":[[474,3]]},"1353":{"position":[[668,3]]},"1359":{"position":[[200,3]]},"1405":{"position":[[56,3]]},"1465":{"position":[[140,3]]},"1495":{"position":[[1118,3],[1206,3],[1763,3],[4256,3],[4520,3],[5146,3],[5809,3],[9158,3],[9197,3]]},"1617":{"position":[[123,3]]},"1635":{"position":[[188,3]]},"1650":{"position":[[385,5]]},"1721":{"position":[[57,3],[140,3],[436,3],[533,3]]},"1723":{"position":[[26,3],[64,3],[278,3]]},"1725":{"position":[[149,3],[292,3]]},"1795":{"position":[[25,3],[388,3]]},"1959":{"position":[[550,3]]},"1995":{"position":[[6,3],[110,3],[137,3]]},"2001":{"position":[[212,3],[275,3],[986,3]]},"2014":{"position":[[130,3]]},"2106":{"position":[[649,3]]},"2110":{"position":[[216,3]]},"2387":{"position":[[68,3]]},"2470":{"position":[[261,3]]},"2474":{"position":[[474,3]]},"2510":{"position":[[200,3]]},"2546":{"position":[[713,3]]},"2562":{"position":[[62,3]]},"2606":{"position":[[149,3]]},"2662":{"position":[[1133,3],[1221,3],[1778,3],[4317,3],[4582,3],[5208,3],[5868,3],[9289,3],[9332,3]]},"2804":{"position":[[123,3]]},"2852":{"position":[[188,3]]},"2867":{"position":[[385,5]]},"2910":{"position":[[57,3],[140,3],[436,3],[533,3]]},"2912":{"position":[[26,3],[64,3],[278,3]]},"2914":{"position":[[201,3],[330,3]]},"2972":{"position":[[1241,3]]},"2984":{"position":[[25,3],[388,3]]},"3172":{"position":[[550,3]]},"3210":{"position":[[6,3],[110,3],[137,3]]},"3218":{"position":[[212,3],[275,3],[1076,3]]},"3233":{"position":[[130,3]]},"3307":{"position":[[649,3]]},"3311":{"position":[[216,3]]},"3490":{"position":[[193,4]]},"3601":{"position":[[68,3]]},"3629":{"position":[[1359,3]]}}}],["web.dev",{"_index":1505,"t":{"94":{"position":[[227,7]]},"252":{"position":[[1259,8]]}}}],["webhook",{"_index":5455,"t":{"2722":{"position":[[80,8],[2268,8]]}}}],["webrtc",{"_index":1514,"t":{"94":{"position":[[1062,6]]}}}],["websit",{"_index":1803,"t":{"122":{"position":[[886,7]]},"226":{"position":[[172,8]]}}}],["websocat",{"_index":2407,"t":{"214":{"position":[[297,9]]},"3629":{"position":[[294,9]]}}}],["websocket",{"_index":23,"t":{"2":{"position":[[254,9],[1458,9],[2222,9],[2517,9],[3485,9]]},"4":{"position":[[23,9],[172,9],[242,9],[309,9],[727,9],[781,9],[870,9],[1081,9],[1131,9]]},"6":{"position":[[63,9],[226,9]]},"8":{"position":[[95,9],[542,9],[802,9],[849,9],[999,10],[1321,9],[1394,10],[1611,9]]},"10":{"position":[[241,9],[1313,9]]},"12":{"position":[[167,9],[411,9],[658,9],[904,9]]},"14":{"position":[[74,9],[445,9],[575,10],[618,9],[1039,9],[1085,9],[1307,9],[1401,9],[1512,9]]},"16":{"position":[[829,9],[930,9],[1092,9],[2198,9],[2902,9],[3127,9],[4650,9],[5582,9],[7490,9],[7555,9]]},"18":{"position":[[53,9],[139,9],[277,9],[668,9],[1815,9],[3108,9],[3311,9],[3463,9],[3518,9]]},"20":{"position":[[226,9],[1658,9]]},"26":{"position":[[477,9]]},"28":{"position":[[597,10],[692,9],[858,9],[1698,9]]},"30":{"position":[[343,9],[719,9]]},"36":{"position":[[196,9]]},"38":{"position":[[849,9]]},"40":{"position":[[1119,9]]},"42":{"position":[[1487,9],[1527,9],[1796,9]]},"46":{"position":[[319,9]]},"54":{"position":[[531,9],[914,10],[925,9],[995,11]]},"68":{"position":[[452,9],[2168,9]]},"82":{"position":[[365,9]]},"94":{"position":[[584,9],[676,9]]},"106":{"position":[[892,9]]},"116":{"position":[[1031,9]]},"118":{"position":[[295,9]]},"130":{"position":[[2892,9],[3028,9],[3148,9]]},"132":{"position":[[1080,9],[1721,9],[1844,9]]},"136":{"position":[[180,9]]},"138":{"position":[[1326,9]]},"142":{"position":[[326,9]]},"144":{"position":[[287,9]]},"146":{"position":[[206,9]]},"160":{"position":[[58,9],[599,9],[978,9]]},"162":{"position":[[567,9]]},"164":{"position":[[923,9]]},"166":{"position":[[1103,9]]},"168":{"position":[[974,9]]},"194":{"position":[[594,11]]},"214":{"position":[[65,9],[103,9],[1071,9],[1223,9],[1482,9]]},"216":{"position":[[358,9]]},"222":{"position":[[201,9]]},"232":{"position":[[128,9]]},"246":{"position":[[1016,9],[1879,9]]},"250":{"position":[[951,9],[1178,9]]},"252":{"position":[[386,9],[686,9],[877,10],[1015,9]]},"256":{"position":[[1457,9]]},"283":{"position":[[1843,9]]},"287":{"position":[[514,9],[1513,9]]},"291":{"position":[[228,9]]},"307":{"position":[[473,9]]},"309":{"position":[[144,9],[413,9]]},"327":{"position":[[264,10]]},"331":{"position":[[198,9]]},"333":{"position":[[101,10]]},"339":{"position":[[719,9],[911,9]]},"373":{"position":[[172,9],[601,11]]},"379":{"position":[[0,9],[138,9],[260,9],[664,9],[1116,9],[1164,9],[1314,9],[1442,9],[3055,9],[3574,12],[4347,10],[4493,9],[4626,9]]},"383":{"position":[[73,11],[629,9]]},"397":{"position":[[245,9],[363,10]]},"399":{"position":[[853,9],[1015,12],[1233,9],[1970,10]]},"403":{"position":[[579,9]]},"413":{"position":[[369,9]]},"419":{"position":[[36,10],[144,9]]},"429":{"position":[[160,9]]},"453":{"position":[[1065,10],[1148,9]]},"467":{"position":[[133,10]]},"469":{"position":[[1092,12]]},"485":{"position":[[1440,9]]},"509":{"position":[[161,9]]},"892":{"position":[[86,9],[136,9]]},"918":{"position":[[14,9],[410,9]]},"928":{"position":[[11,9]]},"930":{"position":[[248,9],[679,9]]},"999":{"position":[[510,9]]},"1065":{"position":[[642,9],[2536,10]]},"1067":{"position":[[832,10]]},"1069":{"position":[[259,9],[810,9]]},"1071":{"position":[[2134,9]]},"1073":{"position":[[2168,10]]},"1077":{"position":[[405,9]]},"1087":{"position":[[200,9]]},"1101":{"position":[[219,12]]},"1153":{"position":[[158,11]]},"1162":{"position":[[2072,9],[2334,9]]},"1164":{"position":[[2580,9]]},"1166":{"position":[[89,9],[274,9]]},"1180":{"position":[[56,9],[122,9]]},"1190":{"position":[[11,9],[51,9]]},"1196":{"position":[[178,10]]},"1213":{"position":[[582,10]]},"1249":{"position":[[47,9]]},"1258":{"position":[[48,9]]},"1260":{"position":[[84,9]]},"1262":{"position":[[34,9],[96,9]]},"1268":{"position":[[254,10]]},"1270":{"position":[[700,10]]},"1275":{"position":[[70,9]]},"1277":{"position":[[88,9],[191,9]]},"1279":{"position":[[89,9],[192,9]]},"1281":{"position":[[193,9]]},"1283":{"position":[[32,9],[88,9],[140,9],[163,9],[286,9],[335,9],[403,9],[569,9],[599,9],[1263,9]]},"1285":{"position":[[546,9],[687,9]]},"1293":{"position":[[473,9]]},"1295":{"position":[[144,9],[413,9]]},"1313":{"position":[[264,10]]},"1317":{"position":[[258,9]]},"1319":{"position":[[401,9],[554,10]]},"1321":{"position":[[101,10]]},"1327":{"position":[[719,9],[911,9]]},"1371":{"position":[[161,9]]},"1397":{"position":[[373,9]]},"1403":{"position":[[36,10],[80,9],[132,9],[260,9]]},"1413":{"position":[[149,9]]},"1495":{"position":[[143,10],[3550,9],[3903,9],[8580,9]]},"1513":{"position":[[155,9]]},"1682":{"position":[[879,9],[1039,9]]},"1959":{"position":[[86,9],[136,9]]},"1993":{"position":[[14,9],[625,9]]},"2003":{"position":[[11,9]]},"2005":{"position":[[248,9],[868,9]]},"2106":{"position":[[510,9]]},"2146":{"position":[[642,9],[2653,10]]},"2148":{"position":[[832,10]]},"2150":{"position":[[259,9],[810,9]]},"2152":{"position":[[2134,9]]},"2154":{"position":[[2168,10]]},"2156":{"position":[[2233,10]]},"2160":{"position":[[555,9]]},"2170":{"position":[[200,9]]},"2240":{"position":[[407,9]]},"2248":{"position":[[56,9],[106,9]]},"2262":{"position":[[425,9]]},"2281":{"position":[[158,11]]},"2290":{"position":[[600,9]]},"2292":{"position":[[254,10]]},"2328":{"position":[[178,10]]},"2347":{"position":[[405,9]]},"2387":{"position":[[582,10]]},"2400":{"position":[[47,9]]},"2409":{"position":[[48,9]]},"2411":{"position":[[84,9]]},"2413":{"position":[[34,9],[96,9]]},"2422":{"position":[[70,9]]},"2424":{"position":[[88,9],[191,9]]},"2426":{"position":[[89,9],[192,9]]},"2428":{"position":[[193,9]]},"2430":{"position":[[32,9],[88,9],[140,9],[163,9],[286,9],[335,9],[403,9],[569,9],[599,9],[1263,9]]},"2432":{"position":[[546,9],[687,9]]},"2434":{"position":[[1570,9],[1727,12],[1940,9],[2822,10]]},"2440":{"position":[[473,9]]},"2442":{"position":[[144,9],[413,9]]},"2460":{"position":[[264,10]]},"2464":{"position":[[258,9]]},"2466":{"position":[[401,9],[554,10]]},"2468":{"position":[[101,10]]},"2474":{"position":[[719,9],[911,9]]},"2500":{"position":[[440,9]]},"2506":{"position":[[194,9],[802,9]]},"2522":{"position":[[164,9]]},"2554":{"position":[[389,9]]},"2560":{"position":[[36,10],[80,10],[133,9],[308,9]]},"2570":{"position":[[149,9]]},"2610":{"position":[[1574,9]]},"2644":{"position":[[155,9]]},"2662":{"position":[[144,10],[3583,9],[3961,9],[8681,9]]},"2836":{"position":[[879,9],[1039,9]]},"3172":{"position":[[86,9],[136,9]]},"3198":{"position":[[718,9]]},"3208":{"position":[[14,9],[625,9]]},"3220":{"position":[[11,9]]},"3222":{"position":[[248,9],[955,9]]},"3307":{"position":[[510,9]]},"3375":{"position":[[642,9],[2653,10]]},"3377":{"position":[[832,10]]},"3379":{"position":[[259,9],[857,9]]},"3381":{"position":[[1937,9]]},"3383":{"position":[[2168,10]]},"3385":{"position":[[2206,10]]},"3389":{"position":[[555,9]]},"3399":{"position":[[200,9]]},"3482":{"position":[[724,9]]},"3484":{"position":[[254,10]]},"3496":{"position":[[425,9]]},"3508":{"position":[[158,11]]},"3551":{"position":[[407,9]]},"3559":{"position":[[56,9],[106,9]]},"3567":{"position":[[47,9]]},"3576":{"position":[[48,9]]},"3578":{"position":[[84,9]]},"3580":{"position":[[34,9],[96,9]]},"3584":{"position":[[178,10]]},"3601":{"position":[[582,10]]},"3612":{"position":[[1597,9],[1754,12],[1967,9],[3021,10]]},"3617":{"position":[[70,9]]},"3619":{"position":[[88,9],[191,9]]},"3621":{"position":[[89,9],[192,9]]},"3623":{"position":[[193,9]]},"3625":{"position":[[32,9],[88,9],[140,9],[163,9],[286,9],[335,9],[403,9],[569,9],[599,9],[1263,9]]},"3627":{"position":[[546,9],[687,9]]},"3629":{"position":[[62,9],[100,9],[1072,9],[1224,9],[1483,9]]},"3635":{"position":[[405,9]]}}}],["websocket'",{"_index":488,"t":{"12":{"position":[[301,11]]}}}],["websocket/sockj",{"_index":965,"t":{"32":{"position":[[1078,16]]}}}],["websocket_compress",{"_index":4654,"t":{"1283":{"position":[[622,21],[1155,21]]},"2430":{"position":[[622,21],[1155,21]]},"3625":{"position":[[622,21],[1155,21]]}}}],["websocket_compression_level",{"_index":4658,"t":{"1283":{"position":[[1467,27]]},"2430":{"position":[[1467,27]]},"3625":{"position":[[1467,27]]}}}],["websocket_compression_min_s",{"_index":4655,"t":{"1283":{"position":[[930,31]]},"2430":{"position":[[930,31]]},"3625":{"position":[[930,31]]}}}],["websocket_dis",{"_index":4069,"t":{"928":{"position":[[34,17]]},"2003":{"position":[[34,17]]},"3220":{"position":[[34,17]]}}}],["websocket_handler_prefix",{"_index":4073,"t":{"930":{"position":[[176,24]]},"2005":{"position":[[176,24]]},"3222":{"position":[[176,24]]}}}],["websocket_message_size_limit",{"_index":3633,"t":{"570":{"position":[[660,28]]}}}],["websocket_ping_interv",{"_index":3629,"t":{"570":{"position":[[537,23],[1039,23],[2095,23]]},"2658":{"position":[[393,23]]}}}],["websocket_read_buffer_s",{"_index":209,"t":{"2":{"position":[[3672,29]]},"1277":{"position":[[307,29]]},"2424":{"position":[[307,29]]},"3619":{"position":[[307,29]]}}}],["websocket_use_write_buffer_pool",{"_index":4652,"t":{"1281":{"position":[[30,31]]},"2428":{"position":[[30,31]]},"3623":{"position":[[30,31]]}}}],["websocket_write_buffer_s",{"_index":210,"t":{"2":{"position":[[3707,30]]},"1279":{"position":[[266,30]]},"2426":{"position":[[266,30]]},"3621":{"position":[[266,30]]}}}],["websocket_write_timeout",{"_index":3631,"t":{"570":{"position":[[601,23],[983,23],[2151,23]]}}}],["websockethandl",{"_index":962,"t":{"32":{"position":[[703,16]]}}}],["webtransport",{"_index":904,"t":{"28":{"position":[[1542,12]]},"54":{"position":[[834,12],[898,12]]},"92":{"position":[[8,12],[210,12]]},"94":{"position":[[0,12],[265,12],[391,12],[410,12],[877,12],[1203,12],[1223,12],[1278,12],[1336,12],[1406,12],[1419,12],[1493,12],[1623,12],[1665,12],[1933,12],[2271,12],[3133,12]]},"98":{"position":[[6,12]]},"102":{"position":[[71,12]]},"104":{"position":[[515,12],[598,12]]},"106":{"position":[[1140,12],[1565,12],[1686,12]]},"108":{"position":[[1022,12]]},"110":{"position":[[33,12],[781,12]]},"116":{"position":[[0,12],[361,12],[498,12],[720,12],[953,12]]},"194":{"position":[[653,14]]},"373":{"position":[[340,13]]},"399":{"position":[[120,12],[196,12],[235,12],[342,12],[474,12],[603,12],[670,12],[697,15],[832,12],[919,15],[1316,12],[1465,12],[1735,12],[1816,12]]},"407":{"position":[[520,12],[589,12]]},"467":{"position":[[165,13]]},"1319":{"position":[[308,12],[349,12]]},"1403":{"position":[[291,12]]},"2434":{"position":[[0,12],[13,12],[125,12],[187,12],[280,12],[370,12],[443,12],[561,15],[597,12],[692,12],[1402,12],[1544,12],[1636,15],[1999,12],[2296,12],[2558,12],[2639,12]]},"2466":{"position":[[308,12],[349,12]]},"2560":{"position":[[349,12]]},"3482":{"position":[[891,13]]},"3612":{"position":[[0,12],[13,12],[152,12],[214,12],[307,12],[397,12],[470,12],[588,15],[624,12],[719,12],[1429,12],[1571,12],[1663,15],[2026,12],[2154,12],[2495,12],[2757,12],[2838,12]]}}}],["webtransportserverqu",{"_index":1643,"t":{"104":{"position":[[481,22],[624,22],[780,23],[813,24],[878,24]]},"106":{"position":[[228,24],[657,24],[1217,24]]},"110":{"position":[[1249,24],[1940,24],[2076,24],[4159,24]]},"112":{"position":[[386,24]]}}}],["webtransportserverquic.func",{"_index":1648,"t":{"104":{"position":[[711,27]]}}}],["webtransportserverquic.typ",{"_index":1634,"t":{"104":{"position":[[140,27]]}}}],["wed",{"_index":4443,"t":{"1125":{"position":[[738,4]]},"2206":{"position":[[738,4]]}}}],["week",{"_index":4018,"t":{"868":{"position":[[172,6]]},"2066":{"position":[[172,6]]},"2068":{"position":[[222,6]]},"3144":{"position":[[172,6]]},"3146":{"position":[[222,6]]}}}],["weight",{"_index":3164,"t":{"475":{"position":[[809,6],[988,6]]},"981":{"position":[[428,6],[536,6]]},"2040":{"position":[[428,6],[536,6]]},"3281":{"position":[[428,6],[536,6]]}}}],["welcom",{"_index":3940,"t":{"758":{"position":[[977,11],[989,8],[1037,11],[1049,8]]},"1349":{"position":[[59,7]]},"1777":{"position":[[504,11],[516,8],[564,11],[576,8]]},"2494":{"position":[[59,7]]},"2966":{"position":[[582,11],[594,8],[642,11],[654,8]]}}}],["well",{"_index":216,"t":{"4":{"position":[[85,4]]},"8":{"position":[[360,5]]},"16":{"position":[[716,5],[4097,4],[4172,5],[4836,4],[5252,4]]},"26":{"position":[[1103,4],[1156,4]]},"28":{"position":[[12,5]]},"44":{"position":[[1403,4]]},"58":{"position":[[342,4]]},"62":{"position":[[1147,4]]},"120":{"position":[[310,5]]},"136":{"position":[[0,5]]},"138":{"position":[[1823,4]]},"148":{"position":[[610,5]]},"172":{"position":[[574,5]]},"192":{"position":[[21,5]]},"194":{"position":[[946,4]]},"246":{"position":[[1623,5],[4235,5]]},"252":{"position":[[45,5]]},"270":{"position":[[126,4]]},"279":{"position":[[778,4]]},"325":{"position":[[56,4]]},"341":{"position":[[404,4]]},"361":{"position":[[1230,5]]},"369":{"position":[[394,4]]},"371":{"position":[[597,4]]},"373":{"position":[[939,4]]},"377":{"position":[[1654,4]]},"387":{"position":[[1685,5]]},"411":{"position":[[182,4],[233,4]]},"467":{"position":[[1232,4]]},"473":{"position":[[758,5]]},"475":{"position":[[2505,4]]},"558":{"position":[[309,4]]},"696":{"position":[[1288,5]]},"1164":{"position":[[3842,4]]},"1180":{"position":[[518,4]]},"1268":{"position":[[31,4]]},"1311":{"position":[[56,4]]},"1329":{"position":[[404,4]]},"1381":{"position":[[1235,5]]},"1389":{"position":[[394,4]]},"1391":{"position":[[597,4]]},"1395":{"position":[[182,4]]},"1399":{"position":[[18,4]]},"1469":{"position":[[1452,4]]},"1875":{"position":[[981,5]]},"2240":{"position":[[1483,4]]},"2292":{"position":[[31,4]]},"2458":{"position":[[56,4]]},"2476":{"position":[[404,4]]},"2498":{"position":[[378,4]]},"2504":{"position":[[822,4],[995,4],[1104,4]]},"2532":{"position":[[1301,5]]},"2540":{"position":[[398,4]]},"2542":{"position":[[605,4]]},"2552":{"position":[[182,4]]},"2556":{"position":[[18,4]]},"2592":{"position":[[221,4]]},"2610":{"position":[[1749,4]]},"2946":{"position":[[81,4]]},"3070":{"position":[[352,5]]},"3345":{"position":[[835,4]]},"3484":{"position":[[31,4]]},"3551":{"position":[[1483,4]]}}}],["went",{"_index":871,"t":{"26":{"position":[[1542,4]]},"166":{"position":[[2458,4]]},"246":{"position":[[4338,4]]},"250":{"position":[[544,4]]},"479":{"position":[[4772,4]]},"481":{"position":[[3203,4]]},"782":{"position":[[124,4]]},"840":{"position":[[123,4]]},"1833":{"position":[[136,4]]},"2345":{"position":[[4723,4]]},"2355":{"position":[[4342,4]]},"3024":{"position":[[136,4]]},"3633":{"position":[[4723,4]]},"3643":{"position":[[4342,4]]}}}],["wget",{"_index":3682,"t":{"594":{"position":[[41,4]]},"596":{"position":[[41,4]]},"1559":{"position":[[41,4]]},"1561":{"position":[[41,4]]},"2762":{"position":[[41,4]]},"2764":{"position":[[41,4]]}}}],["whant",{"_index":5062,"t":{"1863":{"position":[[1114,6]]},"3054":{"position":[[1114,6]]}}}],["what'",{"_index":1212,"t":{"50":{"position":[[1832,6]]},"387":{"position":[[1691,6]]},"467":{"position":[[840,6]]},"560":{"position":[[1037,6]]},"1461":{"position":[[111,6]]},"2602":{"position":[[115,6]]}}}],["whatev",{"_index":730,"t":{"16":{"position":[[7058,8]]},"112":{"position":[[208,8]]},"246":{"position":[[1768,8]]},"977":{"position":[[899,8]]},"1495":{"position":[[5972,8]]},"2034":{"position":[[899,8]]},"2750":{"position":[[89,8]]},"3275":{"position":[[899,8]]}}}],["whatsapp",{"_index":2083,"t":{"138":{"position":[[1615,8]]}}}],["what’",{"_index":2648,"t":{"270":{"position":[[820,6]]}}}],["whenev",{"_index":2210,"t":{"174":{"position":[[984,8]]},"192":{"position":[[323,8]]},"678":{"position":[[136,8]]},"734":{"position":[[944,8]]},"1635":{"position":[[715,8]]},"1729":{"position":[[136,8]]},"2722":{"position":[[169,8],[247,8]]},"2852":{"position":[[715,8]]},"2918":{"position":[[136,8]]},"3345":{"position":[[426,8]]}}}],["wherea",{"_index":5543,"t":{"2950":{"position":[[1040,7]]}}}],["whether",{"_index":2190,"t":{"166":{"position":[[1967,7]]},"178":{"position":[[853,7]]},"180":{"position":[[871,7]]},"236":{"position":[[374,7]]},"361":{"position":[[1446,7]]},"475":{"position":[[572,7]]},"481":{"position":[[88,7]]},"560":{"position":[[620,8]]},"600":{"position":[[511,8]]},"965":{"position":[[1568,7]]},"1073":{"position":[[330,7]]},"1166":{"position":[[1320,7]]},"1168":{"position":[[484,7]]},"1172":{"position":[[514,7]]},"1251":{"position":[[62,7]]},"1381":{"position":[[1451,7]]},"1523":{"position":[[456,7]]},"1619":{"position":[[793,8]]},"1656":{"position":[[685,7],[777,7],[867,7],[1057,7]]},"1660":{"position":[[786,7],[1005,7]]},"1664":{"position":[[550,7]]},"1749":{"position":[[11,7]]},"1751":{"position":[[11,7]]},"1753":{"position":[[11,7]]},"2102":{"position":[[1740,7]]},"2146":{"position":[[6108,7]]},"2154":{"position":[[330,7]]},"2361":{"position":[[423,7],[603,7],[853,7]]},"2402":{"position":[[62,7]]},"2504":{"position":[[205,7]]},"2532":{"position":[[1598,7]]},"2692":{"position":[[456,7]]},"2752":{"position":[[54,7]]},"2806":{"position":[[793,8]]},"2873":{"position":[[381,7],[475,7],[567,7]]},"2877":{"position":[[345,7],[444,7]]},"2881":{"position":[[323,7]]},"2938":{"position":[[11,7]]},"2940":{"position":[[11,7]]},"2942":{"position":[[11,7]]},"3261":{"position":[[1740,7]]},"3375":{"position":[[6137,7]]},"3383":{"position":[[330,7]]},"3569":{"position":[[62,7]]},"3649":{"position":[[423,7],[603,7],[853,7]]}}}],["whichev",{"_index":5453,"t":{"2662":{"position":[[6035,9]]}}}],["whitelist",{"_index":4424,"t":{"1117":{"position":[[418,9]]},"2198":{"position":[[384,9]]},"3439":{"position":[[318,9]]}}}],["whose",{"_index":4575,"t":{"1172":{"position":[[129,5]]},"2610":{"position":[[64,5]]}}}],["wide",{"_index":314,"t":{"8":{"position":[[338,6]]},"116":{"position":[[399,4]]},"226":{"position":[[277,6]]},"385":{"position":[[116,6]]},"945":{"position":[[496,6]]},"1479":{"position":[[350,4],[574,4]]},"2020":{"position":[[496,6]]},"2672":{"position":[[350,4],[574,4]]},"3239":{"position":[[496,6]]}}}],["wider",{"_index":1294,"t":{"62":{"position":[[1594,5]]}}}],["widget",{"_index":4700,"t":{"1495":{"position":[[348,6]]},"2662":{"position":[[353,6]]}}}],["wiki",{"_index":4060,"t":{"922":{"position":[[348,4]]},"1997":{"position":[[348,4]]},"3212":{"position":[[348,4]]}}}],["wild",{"_index":4201,"t":{"999":{"position":[[222,5]]},"2106":{"position":[[222,5]]},"3307":{"position":[[222,5]]}}}],["wildcard",{"_index":1235,"t":{"54":{"position":[[273,8]]},"391":{"position":[[1108,8]]},"892":{"position":[[823,8]]},"993":{"position":[[387,8]]},"1475":{"position":[[812,8],[1869,8]]},"1481":{"position":[[21,9],[190,11]]},"1487":{"position":[[95,11]]},"1959":{"position":[[823,8]]},"2054":{"position":[[387,8]]},"2668":{"position":[[812,8],[1869,8]]},"2674":{"position":[[21,9],[190,11]]},"2680":{"position":[[95,11]]},"3172":{"position":[[823,8]]},"3293":{"position":[[387,8]]}}}],["win",{"_index":574,"t":{"14":{"position":[[1504,3]]},"347":{"position":[[2082,4]]},"1335":{"position":[[2086,4]]},"2482":{"position":[[2328,4]]}}}],["window",{"_index":564,"t":{"14":{"position":[[1158,6]]},"16":{"position":[[1740,6],[6704,6]]},"20":{"position":[[1127,6]]},"22":{"position":[[483,6]]},"42":{"position":[[245,8]]},"317":{"position":[[220,6]]},"437":{"position":[[38,8]]},"523":{"position":[[414,7]]},"1049":{"position":[[1380,7]]},"1172":{"position":[[953,6]]},"1207":{"position":[[391,6],[960,6],[1262,6]]},"1303":{"position":[[220,6]]},"1421":{"position":[[38,8]]},"1431":{"position":[[424,7]]},"1495":{"position":[[5312,6]]},"1577":{"position":[[553,6]]},"2230":{"position":[[1380,7]]},"2339":{"position":[[391,6],[960,6]]},"2450":{"position":[[220,6]]},"2578":{"position":[[38,8]]},"2614":{"position":[[439,7]]},"2662":{"position":[[5374,6]]},"2770":{"position":[[553,6]]},"3417":{"position":[[1380,7]]},"3595":{"position":[[391,6],[960,6]]}}}],["window.location.host",{"_index":1953,"t":{"130":{"position":[[1088,20]]},"166":{"position":[[1197,20]]}}}],["window.location.pathnam",{"_index":1874,"t":{"128":{"position":[[1225,24]]}}}],["windows_amd64",{"_index":3547,"t":{"523":{"position":[[422,15]]},"1431":{"position":[[432,15]]},"2614":{"position":[[447,15]]}}}],["wire",{"_index":899,"t":{"28":{"position":[[1161,4]]},"1283":{"position":[[237,5]]},"1285":{"position":[[318,4]]},"2258":{"position":[[1330,4]]},"2430":{"position":[[237,5]]},"2432":{"position":[[318,4]]},"3492":{"position":[[1330,4]]},"3625":{"position":[[237,5]]},"3627":{"position":[[318,4]]}}}],["wire/backend/centrifugo",{"_index":2873,"t":{"321":{"position":[[226,23]]},"1307":{"position":[[226,23]]},"2454":{"position":[[226,23]]}}}],["wise",{"_index":3893,"t":{"712":{"position":[[571,6]]},"714":{"position":[[412,6]]},"718":{"position":[[589,6]]},"742":{"position":[[893,7]]},"1487":{"position":[[237,6]]},"1761":{"position":[[893,7]]},"1891":{"position":[[571,6]]},"1893":{"position":[[412,6]]},"1897":{"position":[[942,6]]},"2680":{"position":[[237,6]]},"3082":{"position":[[571,6]]},"3084":{"position":[[412,6]]},"3090":{"position":[[942,6]]}}}],["wish",{"_index":3946,"t":{"762":{"position":[[146,4]]},"1781":{"position":[[146,4]]},"2948":{"position":[[149,4]]}}}],["within",{"_index":1843,"t":{"128":{"position":[[154,6],[251,6]]},"214":{"position":[[474,6]]},"262":{"position":[[1532,6]]},"289":{"position":[[1029,6]]},"363":{"position":[[175,6]]},"379":{"position":[[4400,6]]},"383":{"position":[[913,6]]},"389":{"position":[[508,6]]},"391":{"position":[[567,6]]},"1383":{"position":[[178,6]]},"1869":{"position":[[1235,6]]},"2500":{"position":[[337,6]]},"2534":{"position":[[183,6]]},"2966":{"position":[[510,6]]},"2968":{"position":[[150,6],[441,6]]},"3060":{"position":[[976,6],[1321,6]]},"3066":{"position":[[423,6]]},"3070":{"position":[[811,6]]},"3324":{"position":[[677,6]]},"3347":{"position":[[2829,6]]},"3629":{"position":[[475,6]]}}}],["without",{"_index":162,"t":{"2":{"position":[[2539,7]]},"6":{"position":[[372,7]]},"10":{"position":[[2189,7]]},"16":{"position":[[3012,7],[5838,7],[5890,7]]},"18":{"position":[[2329,7],[3473,7]]},"20":{"position":[[1459,7]]},"42":{"position":[[2656,7]]},"64":{"position":[[191,7]]},"94":{"position":[[1037,7]]},"142":{"position":[[373,7]]},"182":{"position":[[148,7],[1898,7]]},"204":{"position":[[1107,7]]},"210":{"position":[[375,7]]},"214":{"position":[[1738,7]]},"216":{"position":[[572,7]]},"250":{"position":[[280,7],[355,7]]},"252":{"position":[[336,7]]},"256":{"position":[[1360,7],[1626,7]]},"264":{"position":[[239,7]]},"291":{"position":[[274,7]]},"303":{"position":[[96,7]]},"323":{"position":[[77,8]]},"339":{"position":[[210,7],[1433,8],[1618,7]]},"355":{"position":[[451,7]]},"359":{"position":[[746,8]]},"363":{"position":[[352,7]]},"377":{"position":[[931,7]]},"381":{"position":[[811,7]]},"385":{"position":[[710,7],[1720,7]]},"387":{"position":[[87,7]]},"401":{"position":[[189,7]]},"403":{"position":[[628,7]]},"411":{"position":[[77,7]]},"453":{"position":[[898,7]]},"467":{"position":[[554,7]]},"475":{"position":[[365,7]]},"477":{"position":[[1710,7],[2625,7],[3450,7]]},"481":{"position":[[4250,7],[5697,7],[5778,7]]},"483":{"position":[[1427,7]]},"489":{"position":[[97,7],[694,7]]},"497":{"position":[[530,7]]},"562":{"position":[[166,7]]},"578":{"position":[[71,7]]},"580":{"position":[[7,7]]},"696":{"position":[[127,7]]},"700":{"position":[[560,7]]},"702":{"position":[[117,7]]},"704":{"position":[[557,7]]},"718":{"position":[[810,7]]},"732":{"position":[[394,7]]},"858":{"position":[[199,7]]},"884":{"position":[[364,7]]},"892":{"position":[[410,7],[456,7]]},"910":{"position":[[72,7],[115,7]]},"935":{"position":[[83,7]]},"937":{"position":[[436,7]]},"945":{"position":[[318,7]]},"951":{"position":[[143,7]]},"999":{"position":[[41,7]]},"1049":{"position":[[1702,7]]},"1065":{"position":[[183,7],[1734,7]]},"1071":{"position":[[1253,7]]},"1073":{"position":[[1103,7]]},"1079":{"position":[[1300,7]]},"1097":{"position":[[1012,7]]},"1099":{"position":[[881,7]]},"1125":{"position":[[1191,8]]},"1162":{"position":[[1484,7]]},"1180":{"position":[[510,7]]},"1186":{"position":[[192,7]]},"1309":{"position":[[77,8]]},"1327":{"position":[[210,7],[1433,8],[1618,7]]},"1341":{"position":[[408,7]]},"1347":{"position":[[451,7]]},"1355":{"position":[[183,7],[303,7]]},"1359":{"position":[[530,7]]},"1379":{"position":[[746,8]]},"1383":{"position":[[365,7]]},"1395":{"position":[[77,7]]},"1453":{"position":[[899,7]]},"1469":{"position":[[110,7],[702,7]]},"1507":{"position":[[145,7],[908,7]]},"1523":{"position":[[966,7]]},"1569":{"position":[[589,8]]},"1585":{"position":[[71,7]]},"1587":{"position":[[7,7]]},"1613":{"position":[[147,7]]},"1629":{"position":[[239,7]]},"1639":{"position":[[885,7]]},"1645":{"position":[[102,7]]},"1680":{"position":[[1027,7]]},"1725":{"position":[[199,7]]},"1741":{"position":[[920,7],[997,7]]},"1861":{"position":[[135,7]]},"1865":{"position":[[1482,7],[1738,7]]},"1875":{"position":[[127,7]]},"1877":{"position":[[554,7],[646,7]]},"1889":{"position":[[120,7]]},"1897":{"position":[[1163,7]]},"1903":{"position":[[827,7]]},"1937":{"position":[[851,7]]},"1951":{"position":[[364,7]]},"1959":{"position":[[410,7],[456,7]]},"1985":{"position":[[72,7],[115,7]]},"2010":{"position":[[83,7]]},"2012":{"position":[[436,7]]},"2020":{"position":[[318,7]]},"2078":{"position":[[143,7]]},"2098":{"position":[[246,7]]},"2106":{"position":[[41,7]]},"2146":{"position":[[183,7],[1851,7]]},"2152":{"position":[[1253,7]]},"2154":{"position":[[1103,7]]},"2156":{"position":[[1372,7]]},"2162":{"position":[[1300,7]]},"2180":{"position":[[1228,7]]},"2182":{"position":[[881,7]]},"2206":{"position":[[1191,8]]},"2230":{"position":[[1702,7]]},"2258":{"position":[[628,7]]},"2345":{"position":[[831,7]]},"2369":{"position":[[377,7]]},"2383":{"position":[[569,7]]},"2434":{"position":[[2119,7]]},"2436":{"position":[[451,7]]},"2456":{"position":[[77,8]]},"2474":{"position":[[210,7],[1433,8],[1618,7]]},"2488":{"position":[[408,7]]},"2510":{"position":[[530,7]]},"2530":{"position":[[1468,8],[1807,7],[1892,8]]},"2534":{"position":[[378,7]]},"2548":{"position":[[183,7],[303,7]]},"2552":{"position":[[77,7]]},"2594":{"position":[[942,7]]},"2610":{"position":[[687,7]]},"2638":{"position":[[145,7],[908,7]]},"2692":{"position":[[966,7]]},"2722":{"position":[[1894,7]]},"2728":{"position":[[589,8]]},"2738":{"position":[[147,7]]},"2782":{"position":[[71,7]]},"2834":{"position":[[1033,7]]},"2846":{"position":[[239,7]]},"2856":{"position":[[885,7]]},"2862":{"position":[[110,7]]},"2914":{"position":[[215,7],[365,7]]},"2930":{"position":[[920,7],[997,7]]},"3052":{"position":[[135,7]]},"3056":{"position":[[1482,7],[1738,7]]},"3080":{"position":[[120,7]]},"3090":{"position":[[1163,7]]},"3096":{"position":[[827,7]]},"3134":{"position":[[851,7]]},"3164":{"position":[[364,7]]},"3172":{"position":[[410,7],[456,7]]},"3200":{"position":[[72,7],[115,7]]},"3227":{"position":[[83,7]]},"3231":{"position":[[436,7]]},"3239":{"position":[[318,7]]},"3245":{"position":[[143,7]]},"3257":{"position":[[246,7]]},"3307":{"position":[[41,7]]},"3347":{"position":[[3685,7]]},"3375":{"position":[[183,7],[1851,7]]},"3381":{"position":[[1056,7]]},"3383":{"position":[[1103,7]]},"3385":{"position":[[1345,7]]},"3391":{"position":[[1300,7]]},"3409":{"position":[[1228,7]]},"3411":{"position":[[881,7]]},"3417":{"position":[[1702,7]]},"3447":{"position":[[1033,8]]},"3467":{"position":[[787,7]]},"3492":{"position":[[628,7]]},"3612":{"position":[[2318,7]]},"3629":{"position":[[1739,7]]},"3633":{"position":[[831,7]]},"3657":{"position":[[377,7]]},"3671":{"position":[[569,7]]}}}],["won't",{"_index":41,"t":{"2":{"position":[[536,5]]},"4":{"position":[[286,5],[519,5]]},"16":{"position":[[77,5],[1179,5]]},"34":{"position":[[1248,5]]},"176":{"position":[[958,5]]},"198":{"position":[[678,5]]},"204":{"position":[[2335,5]]},"206":{"position":[[603,5]]},"228":{"position":[[58,5]]},"270":{"position":[[192,5]]},"287":{"position":[[184,5]]},"341":{"position":[[735,5]]},"347":{"position":[[440,5]]},"371":{"position":[[685,5]]},"415":{"position":[[219,5]]},"572":{"position":[[313,5]]},"600":{"position":[[918,5]]},"682":{"position":[[689,5]]},"734":{"position":[[2251,5]]},"742":{"position":[[205,5],[511,5]]},"951":{"position":[[228,5]]},"969":{"position":[[470,5]]},"993":{"position":[[362,5]]},"1041":{"position":[[353,5]]},"1043":{"position":[[487,5]]},"1065":{"position":[[3753,5],[4591,5]]},"1073":{"position":[[595,5],[3071,5]]},"1149":{"position":[[1016,5]]},"1279":{"position":[[230,5]]},"1281":{"position":[[147,5]]},"1329":{"position":[[735,5]]},"1335":{"position":[[444,5]]},"1391":{"position":[[685,5]]},"1399":{"position":[[393,5]]},"1473":{"position":[[407,5]]},"1511":{"position":[[911,5]]},"1619":{"position":[[1200,5]]},"1639":{"position":[[1217,5]]},"1733":{"position":[[689,5]]},"1761":{"position":[[205,5],[511,5]]},"1865":{"position":[[7,5]]},"1879":{"position":[[231,5]]},"1881":{"position":[[2138,5]]},"1903":{"position":[[410,5]]},"1905":{"position":[[383,5]]},"1923":{"position":[[1013,5]]},"1987":{"position":[[76,5]]},"2024":{"position":[[470,5]]},"2054":{"position":[[362,5]]},"2078":{"position":[[228,5]]},"2132":{"position":[[353,5]]},"2134":{"position":[[487,5]]},"2146":{"position":[[3870,5],[4708,5]]},"2152":{"position":[[4802,5]]},"2154":{"position":[[595,5],[3071,5]]},"2160":{"position":[[387,5]]},"2277":{"position":[[1016,5]]},"2426":{"position":[[230,5]]},"2428":{"position":[[147,5]]},"2476":{"position":[[735,5]]},"2504":{"position":[[811,5]]},"2530":{"position":[[1729,5]]},"2542":{"position":[[698,5]]},"2556":{"position":[[400,5]]},"2642":{"position":[[911,5]]},"2652":{"position":[[228,5]]},"2660":{"position":[[136,5]]},"2666":{"position":[[407,5]]},"2722":{"position":[[2072,5]]},"2806":{"position":[[1200,5]]},"2856":{"position":[[1217,5]]},"2922":{"position":[[653,5]]},"3056":{"position":[[7,5]]},"3072":{"position":[[2426,5]]},"3096":{"position":[[410,5]]},"3098":{"position":[[383,5]]},"3116":{"position":[[1013,5]]},"3202":{"position":[[49,5]]},"3245":{"position":[[228,5]]},"3265":{"position":[[470,5]]},"3293":{"position":[[362,5]]},"3347":{"position":[[3938,5]]},"3361":{"position":[[353,5]]},"3363":{"position":[[487,5]]},"3375":{"position":[[3899,5],[4737,5]]},"3381":{"position":[[3386,5],[4805,5]]},"3383":{"position":[[595,5],[3071,5]]},"3389":{"position":[[387,5]]},"3504":{"position":[[1016,5]]},"3621":{"position":[[230,5]]},"3623":{"position":[[147,5]]}}}],["wonder",{"_index":260,"t":{"4":{"position":[[955,9]]},"481":{"position":[[4066,6]]},"3347":{"position":[[2267,6]]}}}],["word",{"_index":1071,"t":{"42":{"position":[[300,6]]},"246":{"position":[[1446,6]]},"262":{"position":[[687,6]]},"2530":{"position":[[745,5]]},"3425":{"position":[[240,6]]}}}],["work",{"_index":275,"t":{"6":{"position":[[37,4]]},"12":{"position":[[143,4]]},"14":{"position":[[1714,4]]},"16":{"position":[[172,4],[687,4],[3332,5]]},"30":{"position":[[9,7]]},"42":{"position":[[2374,4]]},"46":{"position":[[3577,7]]},"48":{"position":[[435,5]]},"50":{"position":[[374,5],[1978,4]]},"58":{"position":[[231,5]]},"62":{"position":[[658,4]]},"68":{"position":[[821,4]]},"78":{"position":[[156,5]]},"92":{"position":[[106,7]]},"94":{"position":[[2253,4],[2671,4],[3151,5]]},"98":{"position":[[756,7]]},"102":{"position":[[61,4]]},"106":{"position":[[909,6]]},"112":{"position":[[933,7]]},"114":{"position":[[96,4]]},"120":{"position":[[850,5]]},"126":{"position":[[384,7]]},"128":{"position":[[2389,6],[2678,4]]},"136":{"position":[[2255,4]]},"142":{"position":[[93,4]]},"148":{"position":[[1191,5]]},"150":{"position":[[23,7]]},"162":{"position":[[622,4]]},"182":{"position":[[302,4]]},"196":{"position":[[163,4]]},"204":{"position":[[109,4],[469,6]]},"206":{"position":[[222,4],[753,5]]},"210":{"position":[[304,4]]},"214":{"position":[[1202,7]]},"216":{"position":[[453,5],[567,4]]},"218":{"position":[[259,7],[611,7]]},"222":{"position":[[348,4]]},"236":{"position":[[25,4]]},"246":{"position":[[1602,5],[4377,7]]},"250":{"position":[[166,4]]},"252":{"position":[[182,4]]},"254":{"position":[[72,7]]},"258":{"position":[[9,4]]},"270":{"position":[[56,7]]},"277":{"position":[[793,4]]},"279":{"position":[[1321,5]]},"295":{"position":[[44,5]]},"301":{"position":[[404,8],[1275,7],[2124,8]]},"323":{"position":[[250,4]]},"331":{"position":[[16,5],[216,7]]},"333":{"position":[[77,4]]},"359":{"position":[[741,4]]},"367":{"position":[[106,5],[450,5]]},"377":{"position":[[1630,7]]},"379":{"position":[[1334,5],[2180,5],[2595,6],[3115,6]]},"385":{"position":[[516,6],[700,5]]},"393":{"position":[[294,5]]},"397":{"position":[[430,4],[484,4]]},"411":{"position":[[147,5]]},"437":{"position":[[11,5]]},"457":{"position":[[21,4]]},"473":{"position":[[4175,4]]},"475":{"position":[[46,4],[1705,7],[2499,5],[3151,4],[3381,7]]},"477":{"position":[[351,6]]},"479":{"position":[[518,5],[693,4]]},"483":{"position":[[50,5]]},"485":{"position":[[626,4]]},"489":{"position":[[473,7]]},"517":{"position":[[393,7]]},"531":{"position":[[465,5]]},"539":{"position":[[65,7]]},"566":{"position":[[124,5]]},"604":{"position":[[2245,7]]},"644":{"position":[[432,7]]},"648":{"position":[[602,7]]},"652":{"position":[[1002,7]]},"654":{"position":[[1061,7]]},"662":{"position":[[393,7]]},"666":{"position":[[468,7]]},"670":{"position":[[518,7]]},"704":{"position":[[157,5]]},"718":{"position":[[952,5]]},"720":{"position":[[264,4]]},"734":{"position":[[1834,4]]},"906":{"position":[[191,5]]},"918":{"position":[[683,4]]},"920":{"position":[[22,5]]},"926":{"position":[[875,5],[1005,4]]},"965":{"position":[[2025,5]]},"979":{"position":[[500,5]]},"983":{"position":[[948,5]]},"985":{"position":[[376,7],[903,5]]},"987":{"position":[[48,5],[312,4]]},"993":{"position":[[238,5]]},"1043":{"position":[[539,5],[1136,4]]},"1049":{"position":[[1127,5]]},"1065":{"position":[[836,4]]},"1073":{"position":[[1580,4]]},"1087":{"position":[[139,4],[565,5]]},"1097":{"position":[[28,4]]},"1105":{"position":[[16,5]]},"1162":{"position":[[2300,5],[2445,4]]},"1182":{"position":[[1308,7]]},"1213":{"position":[[661,4]]},"1270":{"position":[[758,4]]},"1285":{"position":[[532,5]]},"1309":{"position":[[250,4]]},"1317":{"position":[[16,5],[276,7]]},"1319":{"position":[[430,4],[621,4],[665,4]]},"1321":{"position":[[77,4]]},"1353":{"position":[[840,4]]},"1379":{"position":[[741,4]]},"1387":{"position":[[106,5],[450,5]]},"1395":{"position":[[147,5]]},"1421":{"position":[[11,5]]},"1431":{"position":[[753,7]]},"1439":{"position":[[488,5]]},"1457":{"position":[[18,4]]},"1469":{"position":[[485,7]]},"1477":{"position":[[44,4],[303,4]]},"1491":{"position":[[57,4],[318,4]]},"1495":{"position":[[735,7],[8461,8],[8999,5],[10364,7]]},"1499":{"position":[[910,7],[994,5]]},"1501":{"position":[[287,4],[357,7]]},"1521":{"position":[[1067,5]]},"1533":{"position":[[2749,7]]},"1625":{"position":[[520,7]]},"1690":{"position":[[432,7]]},"1694":{"position":[[602,7]]},"1699":{"position":[[1002,7]]},"1702":{"position":[[1107,7]]},"1706":{"position":[[393,7]]},"1710":{"position":[[468,7]]},"1715":{"position":[[518,7]]},"1787":{"position":[[750,7]]},"1881":{"position":[[1721,4]]},"1897":{"position":[[1305,5]]},"1973":{"position":[[191,5]]},"1993":{"position":[[898,4]]},"1995":{"position":[[22,5]]},"2001":{"position":[[875,5],[1005,4]]},"2036":{"position":[[500,5]]},"2042":{"position":[[948,5]]},"2044":{"position":[[376,7],[903,5]]},"2046":{"position":[[48,5],[312,4]]},"2048":{"position":[[56,5],[308,5],[380,4],[710,4]]},"2054":{"position":[[238,5]]},"2102":{"position":[[2178,5]]},"2134":{"position":[[539,5],[1148,4]]},"2146":{"position":[[836,4]]},"2154":{"position":[[1580,4]]},"2170":{"position":[[139,4],[565,5]]},"2180":{"position":[[43,4]]},"2186":{"position":[[16,5]]},"2230":{"position":[[1127,5]]},"2250":{"position":[[1308,7]]},"2258":{"position":[[26,4],[1206,4],[1553,4]]},"2290":{"position":[[590,4]]},"2296":{"position":[[89,5],[1164,5]]},"2345":{"position":[[5093,5]]},"2359":{"position":[[2981,4]]},"2371":{"position":[[276,5]]},"2383":{"position":[[257,4]]},"2387":{"position":[[661,4]]},"2432":{"position":[[532,5]]},"2434":{"position":[[1282,4]]},"2456":{"position":[[250,4]]},"2464":{"position":[[16,5],[276,7]]},"2466":{"position":[[430,4],[621,4],[665,4]]},"2468":{"position":[[77,4]]},"2500":{"position":[[397,4]]},"2504":{"position":[[817,4]]},"2530":{"position":[[72,7],[1463,4],[1793,8]]},"2538":{"position":[[106,5],[461,5]]},"2546":{"position":[[891,4]]},"2552":{"position":[[147,5]]},"2578":{"position":[[11,5]]},"2598":{"position":[[18,4]]},"2610":{"position":[[779,4]]},"2614":{"position":[[777,7]]},"2622":{"position":[[424,5]]},"2630":{"position":[[910,7],[994,5]]},"2632":{"position":[[287,4],[357,7]]},"2652":{"position":[[66,4],[473,4]]},"2656":{"position":[[89,5]]},"2662":{"position":[[750,7],[8563,8],[9119,5],[10485,7]]},"2670":{"position":[[44,4],[303,4]]},"2684":{"position":[[57,4],[318,4]]},"2690":{"position":[[1067,5]]},"2702":{"position":[[2749,7]]},"2756":{"position":[[364,7]]},"2780":{"position":[[213,5]]},"2814":{"position":[[432,7]]},"2818":{"position":[[602,7]]},"2823":{"position":[[966,7]]},"2826":{"position":[[1071,7]]},"2842":{"position":[[520,7]]},"2895":{"position":[[393,7]]},"2899":{"position":[[468,7]]},"2904":{"position":[[482,7]]},"2976":{"position":[[894,7]]},"3072":{"position":[[2009,4]]},"3090":{"position":[[1305,5]]},"3186":{"position":[[191,5]]},"3198":{"position":[[649,5]]},"3208":{"position":[[898,4]]},"3210":{"position":[[22,5]]},"3218":{"position":[[965,5],[1095,4]]},"3261":{"position":[[2178,5]]},"3277":{"position":[[500,5]]},"3283":{"position":[[948,5]]},"3285":{"position":[[376,7],[903,5]]},"3287":{"position":[[148,4],[330,5],[402,4],[434,4],[550,4],[782,4]]},"3293":{"position":[[238,5]]},"3347":{"position":[[2888,4],[3781,4]]},"3353":{"position":[[20,5]]},"3363":{"position":[[539,5],[1148,4]]},"3375":{"position":[[836,4]]},"3383":{"position":[[1580,4]]},"3399":{"position":[[139,4],[565,5]]},"3409":{"position":[[43,4]]},"3417":{"position":[[1127,5]]},"3425":{"position":[[16,5]]},"3482":{"position":[[714,4]]},"3486":{"position":[[89,5],[1164,5]]},"3490":{"position":[[593,5]]},"3492":{"position":[[26,4],[1206,4],[1553,4]]},"3561":{"position":[[1308,7]]},"3601":{"position":[[661,4]]},"3612":{"position":[[1309,4]]},"3627":{"position":[[532,5]]},"3629":{"position":[[1203,7]]},"3633":{"position":[[5093,5]]},"3647":{"position":[[2981,4]]},"3659":{"position":[[276,5]]},"3671":{"position":[[257,4]]}}}],["workaround",{"_index":1304,"t":{"64":{"position":[[501,10]]},"246":{"position":[[3003,10]]},"349":{"position":[[269,10]]},"1337":{"position":[[269,10]]},"2482":{"position":[[1177,10],[1844,10],[2472,11]]},"2484":{"position":[[269,10]]}}}],["worker",{"_index":2471,"t":{"236":{"position":[[174,6]]},"238":{"position":[[337,6]]},"240":{"position":[[47,7],[143,8],[158,7],[258,7],[300,6],[784,7],[856,6],[1001,6]]},"244":{"position":[[134,6]]},"262":{"position":[[1908,7],[2115,6]]},"1627":{"position":[[286,8]]},"1666":{"position":[[196,7]]},"1670":{"position":[[70,6],[487,7],[626,6]]},"2844":{"position":[[286,8]]},"2862":{"position":[[812,7]]},"2883":{"position":[[196,7]]},"2889":{"position":[[70,6]]}}}],["worker/thread",{"_index":2559,"t":{"250":{"position":[[185,13]]}}}],["worker_connect",{"_index":2020,"t":{"134":{"position":[[677,18]]},"1005":{"position":[[28,18],[73,18]]},"2112":{"position":[[28,18],[73,18]]},"3313":{"position":[[28,18],[73,18]]}}}],["workflow",{"_index":2311,"t":{"198":{"position":[[560,9]]},"258":{"position":[[73,8]]},"359":{"position":[[670,8]]},"423":{"position":[[109,9]]},"562":{"position":[[207,8]]},"1027":{"position":[[642,8]]},"1067":{"position":[[334,8]]},"1164":{"position":[[3909,9]]},"1188":{"position":[[554,8]]},"1379":{"position":[[670,8]]},"1407":{"position":[[111,9]]},"1811":{"position":[[642,8]]},"2148":{"position":[[334,8]]},"2156":{"position":[[394,8]]},"2240":{"position":[[1550,9]]},"2530":{"position":[[1392,8]]},"2564":{"position":[[111,8]]},"3000":{"position":[[642,8]]},"3377":{"position":[[334,8]]},"3385":{"position":[[367,8]]},"3551":{"position":[[1550,9]]}}}],["workload",{"_index":180,"t":{"2":{"position":[[2900,9]]},"2610":{"position":[[1100,8]]}}}],["world",{"_index":387,"t":{"8":{"position":[[1707,6]]},"26":{"position":[[586,5],[1122,6]]},"50":{"position":[[2222,6]]},"70":{"position":[[788,5]]},"246":{"position":[[724,6],[3465,5]]},"264":{"position":[[280,5]]},"385":{"position":[[66,6]]},"413":{"position":[[429,5]]},"963":{"position":[[1350,5]]},"1397":{"position":[[433,5]]},"1495":{"position":[[4188,5]]},"1629":{"position":[[280,5]]},"2100":{"position":[[1350,5]]},"2554":{"position":[[449,5]]},"2662":{"position":[[4248,5]]},"2846":{"position":[[280,5]]},"2862":{"position":[[835,5]]},"3259":{"position":[[1350,5]]}}}],["worri",{"_index":3012,"t":{"385":{"position":[[1728,8]]},"403":{"position":[[646,5]]},"467":{"position":[[562,8]]},"469":{"position":[[303,5]]},"3345":{"position":[[1400,5]]}}}],["wors",{"_index":569,"t":{"14":{"position":[[1296,5]]},"74":{"position":[[1068,5]]},"212":{"position":[[350,6]]},"473":{"position":[[3599,5]]},"481":{"position":[[391,5]]},"2506":{"position":[[1605,5]]}}}],["worst",{"_index":3404,"t":{"481":{"position":[[2922,5]]}}}],["worth",{"_index":703,"t":{"16":{"position":[[5511,5]]},"204":{"position":[[2161,5]]},"397":{"position":[[227,5]]},"473":{"position":[[3486,5]]},"485":{"position":[[1674,5]]},"1319":{"position":[[383,5]]},"1507":{"position":[[817,5]]},"2466":{"position":[[383,5]]},"2638":{"position":[[817,5]]}}}],["wq",{"_index":1676,"t":{"106":{"position":[[1198,3],[1727,2]]}}}],["wrap",{"_index":942,"t":{"32":{"position":[[268,4],[772,7]]},"150":{"position":[[52,7]]},"194":{"position":[[1124,4]]},"262":{"position":[[704,4]]},"301":{"position":[[1208,4]]},"373":{"position":[[1117,4]]},"379":{"position":[[292,7]]},"417":{"position":[[178,4]]},"1401":{"position":[[174,4]]},"1797":{"position":[[960,7]]},"2238":{"position":[[101,8]]},"2558":{"position":[[175,4]]},"2986":{"position":[[926,7]]},"3549":{"position":[[101,8]]}}}],["wrapper",{"_index":1856,"t":{"128":{"position":[[693,9]]},"158":{"position":[[151,9]]},"379":{"position":[[862,8]]},"477":{"position":[[499,7]]},"1355":{"position":[[362,8]]},"2548":{"position":[[362,8]]}}}],["write",{"_index":201,"t":{"2":{"position":[[3463,5]]},"4":{"position":[[227,5]]},"12":{"position":[[961,5]]},"16":{"position":[[6544,7]]},"18":{"position":[[2502,5]]},"22":{"position":[[66,7]]},"58":{"position":[[484,7],[621,5]]},"94":{"position":[[3269,7]]},"112":{"position":[[895,5]]},"244":{"position":[[128,5]]},"250":{"position":[[1170,7]]},"277":{"position":[[676,5]]},"281":{"position":[[652,8]]},"283":{"position":[[1869,5]]},"471":{"position":[[419,5]]},"479":{"position":[[741,5],[2362,8],[3235,5]]},"481":{"position":[[1405,5],[2532,5],[5165,5]]},"489":{"position":[[780,7]]},"560":{"position":[[969,5]]},"828":{"position":[[20,6],[63,5],[109,7]]},"888":{"position":[[230,7]]},"989":{"position":[[126,7]]},"995":{"position":[[315,5]]},"1101":{"position":[[723,5]]},"1139":{"position":[[299,5]]},"1164":{"position":[[488,7]]},"1279":{"position":[[55,5]]},"1281":{"position":[[18,6],[165,5]]},"1283":{"position":[[1058,7]]},"1353":{"position":[[733,5]]},"1469":{"position":[[792,7]]},"1549":{"position":[[603,7]]},"1583":{"position":[[1009,5],[1283,5]]},"1611":{"position":[[129,5]]},"1863":{"position":[[619,6],[690,7]]},"1955":{"position":[[230,7]]},"2050":{"position":[[126,7]]},"2056":{"position":[[315,5]]},"2220":{"position":[[299,5]]},"2244":{"position":[[203,7]]},"2256":{"position":[[288,5]]},"2426":{"position":[[55,5]]},"2428":{"position":[[18,6],[165,5]]},"2430":{"position":[[1058,7]]},"2434":{"position":[[651,7]]},"2498":{"position":[[36,5],[208,7]]},"2502":{"position":[[359,5],[536,5]]},"2546":{"position":[[780,5]]},"2718":{"position":[[603,7]]},"2736":{"position":[[129,5]]},"2778":{"position":[[1107,5],[1381,5]]},"3054":{"position":[[619,6],[690,7]]},"3168":{"position":[[230,7]]},"3289":{"position":[[126,7]]},"3295":{"position":[[315,5]]},"3349":{"position":[[2242,5]]},"3461":{"position":[[299,5]]},"3490":{"position":[[449,5]]},"3555":{"position":[[203,7]]},"3612":{"position":[[678,7]]},"3621":{"position":[[55,5]]},"3623":{"position":[[18,6],[165,5]]},"3625":{"position":[[1058,7]]}}}],["writer",{"_index":497,"t":{"12":{"position":[[723,8]]}}}],["written",{"_index":855,"t":{"26":{"position":[[839,7]]},"118":{"position":[[465,7]]},"128":{"position":[[3241,7]]},"144":{"position":[[344,7]]},"226":{"position":[[697,7]]},"246":{"position":[[2496,7]]},"250":{"position":[[126,7]]},"279":{"position":[[495,7],[1038,8]]},"373":{"position":[[471,7]]},"395":{"position":[[149,7]]},"413":{"position":[[32,7]]},"479":{"position":[[127,7]]},"489":{"position":[[60,7]]},"491":{"position":[[173,7]]},"1083":{"position":[[37,8]]},"1164":{"position":[[3847,7]]},"1397":{"position":[[25,7]]},"1469":{"position":[[73,7]]},"1613":{"position":[[122,7]]},"1676":{"position":[[1371,7]]},"2166":{"position":[[37,8]]},"2240":{"position":[[1488,7]]},"2498":{"position":[[398,7]]},"2506":{"position":[[89,7]]},"2554":{"position":[[25,7]]},"2610":{"position":[[91,7]]},"2656":{"position":[[407,7]]},"2738":{"position":[[122,7]]},"2830":{"position":[[1374,7]]},"3395":{"position":[[37,8]]},"3551":{"position":[[1488,7]]}}}],["wrn",{"_index":2394,"t":{"212":{"position":[[923,5],[1007,5],[1087,5],[1168,5]]}}}],["wrong",{"_index":3968,"t":{"782":{"position":[[129,5]]},"806":{"position":[[192,5]]},"830":{"position":[[114,5]]},"840":{"position":[[128,5]]},"850":{"position":[[190,5]]},"1475":{"position":[[349,6],[905,6]]},"1833":{"position":[[141,5]]},"1857":{"position":[[189,5]]},"1863":{"position":[[843,5]]},"2668":{"position":[[349,6],[905,6]]},"3024":{"position":[[141,5]]},"3048":{"position":[[189,5]]},"3054":{"position":[[843,5]]}}}],["wrote",{"_index":839,"t":{"26":{"position":[[2,5]]}}}],["ws",{"_index":2179,"t":{"166":{"position":[[1187,7]]},"1255":{"position":[[90,3]]},"2406":{"position":[[77,3]]},"3573":{"position":[[77,3]]}}}],["ws://centrifugo.example.com/connection/websocket",{"_index":4562,"t":{"1166":{"position":[[126,48]]}}}],["ws://localhost:8000/connection/http_stream",{"_index":5133,"t":{"1993":{"position":[[150,42]]},"3208":{"position":[[150,42]]}}}],["ws://localhost:8000/connection/ss",{"_index":5134,"t":{"1993":{"position":[[263,34]]},"3208":{"position":[[263,34]]}}}],["ws://localhost:8000/connection/uni_websocket",{"_index":4639,"t":{"1262":{"position":[[729,46]]},"2413":{"position":[[738,46]]},"3580":{"position":[[738,46]]}}}],["ws://localhost:8000/connection/uni_websocket\"connect",{"_index":4640,"t":{"1262":{"position":[[885,55]]},"2413":{"position":[[894,55]]},"3580":{"position":[[894,55]]}}}],["ws://localhost:8000/connection/websocket",{"_index":1989,"t":{"130":{"position":[[2943,40],[3172,42]]},"918":{"position":[[42,40]]},"1993":{"position":[[42,40]]},"2345":{"position":[[1815,43],[3277,43]]},"2351":{"position":[[1219,43]]},"2359":{"position":[[1042,43],[2517,43]]},"3208":{"position":[[42,40]]},"3633":{"position":[[1815,43],[3277,43]]},"3639":{"position":[[1270,43]]},"3647":{"position":[[1042,43],[2517,43]]}}}],["ws://localhost:8000/connection/websocket\"let",{"_index":5238,"t":{"2345":{"position":[[2506,45]]},"2359":{"position":[[1414,45]]},"3633":{"position":[[2506,45]]},"3647":{"position":[[1414,45]]}}}],["ws://localhost:8000/connection/websocket?cf_ws_frame_ping_pong=tru",{"_index":5776,"t":{"3629":{"position":[[1096,68]]}}}],["ws://localhost:8000/connection/websocket?format=protobuf",{"_index":1018,"t":{"34":{"position":[[2871,56]]}}}],["ws://localhost:8000/connection/websocketconnect",{"_index":2409,"t":{"214":{"position":[[721,49]]},"3629":{"position":[[722,49]]}}}],["ws://localhost:8000/websocket/connection?cf_ws_frame_ping_pong=tru",{"_index":2418,"t":{"214":{"position":[[1095,68]]}}}],["wscat",{"_index":2406,"t":{"214":{"position":[[290,6],[675,5],[705,5]]},"1262":{"position":[[59,5],[705,5],[720,5],[876,5]]},"2413":{"position":[[59,5],[714,5],[729,5],[885,5]]},"3580":{"position":[[59,5],[714,5],[729,5],[885,5]]},"3629":{"position":[[287,6],[676,5],[706,5]]}}}],["wsgi.pi",{"_index":1816,"t":{"124":{"position":[[377,7]]}}}],["wshandler",{"_index":930,"t":{"30":{"position":[[817,9],[933,10]]},"32":{"position":[[801,9]]}}}],["wss",{"_index":3041,"t":{"399":{"position":[[1216,7]]},"2434":{"position":[[1923,7]]},"3612":{"position":[[1950,7]]}}}],["wss://centrifugo.example.com/connection/websocket",{"_index":4563,"t":{"1166":{"position":[[197,49]]}}}],["wss://centrifugo.example.com/connection/websocket?format=protobuf",{"_index":4660,"t":{"1285":{"position":[[726,65]]},"2432":{"position":[[726,65]]},"3627":{"position":[[726,65]]}}}],["wss://localhost:8000/connection/websocket",{"_index":5415,"t":{"2434":{"position":[[1750,43]]},"3612":{"position":[[1777,43]]}}}],["wss://your_centrifugo.com/connection/websocket",{"_index":2985,"t":{"379":{"position":[[3597,48]]},"399":{"position":[[1038,48]]}}}],["www.example.com",{"_index":4259,"t":{"1049":{"position":[[146,18]]},"2230":{"position":[[146,18]]},"3417":{"position":[[146,18]]}}}],["www.flaticon.com",{"_index":1499,"t":{"90":{"position":[[781,16]]}}}],["www.freepik.com",{"_index":2285,"t":{"192":{"position":[[862,15]]}}}],["x",{"_index":2031,"t":{"134":{"position":[[933,1],[974,1],[1035,1],[1358,1],[1399,1],[1460,1]]},"204":{"position":[[422,5],[1441,2],[1517,5],[1679,1]]},"228":{"position":[[520,1]]},"262":{"position":[[284,1]]},"287":{"position":[[898,1],[939,1],[1000,1],[1235,1],[1276,1],[1337,1]]},"560":{"position":[[243,2],[256,2],[275,2]]},"604":{"position":[[453,2]]},"610":{"position":[[954,2]]},"658":{"position":[[163,1]]},"1001":{"position":[[866,1],[907,1],[1120,1],[1161,1]]},"1003":{"position":[[426,1],[467,1],[670,1],[711,1]]},"1061":{"position":[[423,2],[436,2],[455,2],[621,1],[634,1]]},"1207":{"position":[[735,1],[1021,1],[1333,1]]},"1533":{"position":[[542,2]]},"1545":{"position":[[948,2]]},"1577":{"position":[[575,1],[848,1]]},"1686":{"position":[[163,1]]},"2108":{"position":[[866,1],[907,1],[1120,1],[1161,1]]},"2110":{"position":[[426,1],[467,1],[670,1],[711,1]]},"2142":{"position":[[423,2],[436,2],[455,2],[621,1],[634,1]]},"2339":{"position":[[735,1],[1021,1]]},"2656":{"position":[[775,1]]},"2662":{"position":[[10014,2]]},"2702":{"position":[[542,2]]},"2714":{"position":[[948,2]]},"2744":{"position":[[303,1]]},"2770":{"position":[[575,1],[848,1],[1167,2]]},"2810":{"position":[[163,1]]},"2823":{"position":[[475,2]]},"2826":{"position":[[352,2]]},"2904":{"position":[[67,2]]},"2906":{"position":[[67,2]]},"2920":{"position":[[301,2]]},"2922":{"position":[[208,2]]},"2924":{"position":[[166,2]]},"3076":{"position":[[632,2]]},"3084":{"position":[[813,2]]},"3198":{"position":[[829,2]]},"3309":{"position":[[866,1],[907,1],[1120,1],[1161,1]]},"3311":{"position":[[426,1],[467,1],[670,1],[711,1]]},"3333":{"position":[[353,2],[706,2]]},"3371":{"position":[[423,2],[436,2],[455,2],[621,1],[634,1],[997,2]]},"3425":{"position":[[187,1],[308,2]]},"3427":{"position":[[204,1],[234,1],[430,1]]},"3431":{"position":[[234,2],[720,2],[1034,2]]},"3433":{"position":[[111,2]]},"3443":{"position":[[264,2]]},"3445":{"position":[[146,2]]},"3447":{"position":[[479,2]]},"3451":{"position":[[91,2]]},"3455":{"position":[[312,2]]},"3467":{"position":[[1324,2],[1762,1],[1868,1]]},"3595":{"position":[[735,1],[1021,1]]}}}],["x/net",{"_index":374,"t":{"8":{"position":[[1459,5]]}}}],["x509",{"_index":1559,"t":{"98":{"position":[[595,4],[2087,4]]},"100":{"position":[[291,4]]},"106":{"position":[[1521,4]]},"1051":{"position":[[150,4],[229,4],[317,4]]},"1053":{"position":[[198,4],[277,4],[372,4]]},"2232":{"position":[[150,4],[229,4],[317,4]]},"2234":{"position":[[166,4],[245,4],[340,4]]},"2434":{"position":[[1118,4]]},"3419":{"position":[[150,4],[229,4],[317,4]]},"3421":{"position":[[166,4],[245,4],[340,4]]},"3612":{"position":[[1145,4]]}}}],["x6yxmxlfxnhyrzehvtu_m2ncaxf6hnu7vndm",{"_index":4613,"t":{"1207":{"position":[[609,36],[897,39]]},"2339":{"position":[[609,36],[897,39]]},"3595":{"position":[[609,36],[897,39]]}}}],["x6yxmxlfxnhyrzehvtu_m2ncaxf6hnu7vndm\"}'{\"type\":6,\"data\":{\"client\":\"cf5dc239",{"_index":4620,"t":{"1207":{"position":[[1495,75]]}}}],["x86_64",{"_index":65,"t":{"2":{"position":[[742,6]]}}}],["x:test2",{"_index":5731,"t":{"3455":{"position":[[447,10]]}}}],["xenial",{"_index":3580,"t":{"531":{"position":[[284,6]]},"1439":{"position":[[307,6]]}}}],["xeon(r",{"_index":52,"t":{"2":{"position":[[667,7]]}}}],["xhr",{"_index":551,"t":{"14":{"position":[[766,3]]}}}],["xhttp",{"_index":2172,"t":{"166":{"position":[[725,5]]}}}],["xhttp.open(\"post",{"_index":2174,"t":{"166":{"position":[[755,18]]}}}],["xhttp.send(json.stringifi",{"_index":2178,"t":{"166":{"position":[[859,27]]}}}],["xhttp.setrequestheader(\"x",{"_index":2175,"t":{"166":{"position":[[808,25]]}}}],["xmlhttprequest",{"_index":2173,"t":{"166":{"position":[[737,17]]}}}],["xp",{"_index":4270,"t":{"1049":{"position":[[1388,2],[1402,3]]},"2230":{"position":[[1388,2],[1402,3]]},"3417":{"position":[[1388,2],[1402,3]]}}}],["xxx",{"_index":3839,"t":{"652":{"position":[[581,4],[586,3],[590,5]]},"734":{"position":[[1764,3]]},"1035":{"position":[[106,3],[110,3],[204,3],[208,3]]},"1699":{"position":[[581,4],[586,3],[590,5]]},"1881":{"position":[[1647,3]]},"2823":{"position":[[533,4],[538,3],[542,5]]},"3072":{"position":[[1935,3]]}}}],["xxx:hello",{"_index":3914,"t":{"734":{"position":[[1700,9]]},"1881":{"position":[[1583,9]]},"3072":{"position":[[1871,9]]}}}],["xxxx",{"_index":4249,"t":{"1035":{"position":[[101,4],[114,4],[198,5],[212,6]]}}}],["xyz",{"_index":1084,"t":{"42":{"position":[[1017,6]]}}}],["yaml",{"_index":3553,"t":{"523":{"position":[[1012,4]]},"572":{"position":[[235,4],[260,4],[269,5]]},"866":{"position":[[253,4]]},"880":{"position":[[130,5]]},"882":{"position":[[60,5]]},"888":{"position":[[0,4],[187,4]]},"1431":{"position":[[1245,4]]},"1947":{"position":[[130,5]]},"1949":{"position":[[60,5]]},"1955":{"position":[[0,4],[187,4]]},"2062":{"position":[[292,4]]},"2614":{"position":[[1278,4]]},"3140":{"position":[[292,4]]},"3160":{"position":[[130,5]]},"3162":{"position":[[60,5]]},"3168":{"position":[[0,4],[187,4]]}}}],["yandex/clickhous",{"_index":3783,"t":{"612":{"position":[[611,17],[710,17]]}}}],["ye",{"_index":1118,"t":{"46":{"position":[[99,4]]},"68":{"position":[[1304,4]]},"166":{"position":[[2036,4]]},"178":{"position":[[927,3]]},"311":{"position":[[0,4]]},"329":{"position":[[0,4]]},"331":{"position":[[0,4]]},"652":{"position":[[734,3]]},"654":{"position":[[655,3],[710,3]]},"670":{"position":[[317,3]]},"672":{"position":[[296,3]]},"680":{"position":[[554,3]]},"682":{"position":[[1143,3],[1417,3],[1468,3]]},"684":{"position":[[419,3]]},"758":{"position":[[1146,3],[1185,3],[1266,3],[1357,3],[1433,3],[1645,3],[1688,3],[1731,3],[1771,3]]},"1065":{"position":[[686,4],[2735,3],[2847,3],[2957,3],[3067,3],[3179,3],[3659,3],[3783,3],[3825,3],[3956,3],[4040,3],[4235,3],[4393,3],[4543,3]]},"1067":{"position":[[1101,3],[1268,3],[1365,3],[1448,3],[1490,3]]},"1069":{"position":[[1075,3],[1181,3],[1231,3],[1301,3],[1461,3],[1523,3]]},"1071":{"position":[[2459,3],[2568,3],[2744,3],[2945,3],[2984,3],[3113,3],[3196,3],[3362,3],[3574,3],[3617,3],[3660,3],[3700,3]]},"1073":{"position":[[2489,3],[2528,3],[2598,3],[2762,3],[2868,3],[3039,3]]},"1093":{"position":[[1128,3],[1247,3]]},"1109":{"position":[[1950,3],[1995,3],[2367,3],[2424,3]]},"1111":{"position":[[275,3],[329,3]]},"1113":{"position":[[129,3],[169,3],[1109,3],[1152,3],[1195,3],[1235,3]]},"1115":{"position":[[224,3],[266,3]]},"1117":{"position":[[187,3],[623,3],[657,3]]},"1119":{"position":[[178,3]]},"1121":{"position":[[833,3],[1154,3],[1198,3]]},"1123":{"position":[[569,3]]},"1125":{"position":[[1007,3],[1434,3],[1470,3],[1588,3],[1639,3],[1685,3]]},"1127":{"position":[[568,3]]},"1297":{"position":[[0,4]]},"1315":{"position":[[0,4]]},"1317":{"position":[[0,4]]},"1579":{"position":[[490,3],[553,3],[725,3],[766,3],[889,3],[960,3],[1054,3],[1126,3],[1249,3],[1370,3],[1410,3],[1540,3],[1580,3]]},"1650":{"position":[[189,3],[268,3],[328,3],[895,3]]},"1652":{"position":[[663,3],[754,3],[845,3],[938,3]]},"1656":{"position":[[999,3],[1035,3]]},"1658":{"position":[[114,3],[139,3]]},"1660":{"position":[[933,3],[983,3],[1103,3],[1144,3],[1171,3]]},"1662":{"position":[[238,3],[261,3]]},"1664":{"position":[[644,3],[676,3],[701,3]]},"1666":{"position":[[327,3],[392,3],[1550,3],[1662,3],[1828,3]]},"1668":{"position":[[868,3],[935,3],[1010,3]]},"1699":{"position":[[734,3]]},"1702":{"position":[[655,3]]},"1715":{"position":[[317,3]]},"1717":{"position":[[296,3]]},"1731":{"position":[[554,3]]},"1733":{"position":[[1143,3],[1417,3],[1468,3]]},"1735":{"position":[[419,3]]},"1777":{"position":[[673,3],[712,3],[793,3],[884,3],[960,3],[1172,3],[1215,3],[1258,3],[1298,3]]},"1823":{"position":[[212,3],[270,3],[341,3],[416,3],[487,3]]},"2146":{"position":[[686,4],[2852,3],[2964,3],[3074,3],[3184,3],[3296,3],[3776,3],[3900,3],[3942,3],[4073,3],[4157,3],[4352,3],[4510,3],[4660,3]]},"2148":{"position":[[1101,3],[1268,3],[1365,3],[1448,3],[1490,3]]},"2150":{"position":[[1075,3],[1181,3],[1231,3],[1301,3],[1461,3],[1523,3]]},"2152":{"position":[[2459,3],[2568,3],[2710,3],[2877,3],[2916,3],[3045,3],[3128,3],[3294,3],[3506,3],[3549,3],[3605,3],[3668,3],[3724,3]]},"2154":{"position":[[2489,3],[2528,3],[2598,3],[2762,3],[2868,3],[3039,3]]},"2156":{"position":[[2570,3],[2741,3],[2838,3],[2921,3],[2960,3]]},"2176":{"position":[[1128,3],[1247,3]]},"2190":{"position":[[2004,3],[2049,3],[2651,3],[2708,3]]},"2192":{"position":[[275,3],[329,3]]},"2194":{"position":[[129,3],[169,3],[1074,3],[1117,3],[1173,3],[1236,3],[1292,3]]},"2196":{"position":[[224,3],[266,3]]},"2198":{"position":[[187,3],[589,3],[623,3]]},"2200":{"position":[[178,3]]},"2202":{"position":[[876,3],[1197,3],[1241,3]]},"2204":{"position":[[569,3]]},"2206":{"position":[[1007,3],[1434,3],[1470,3],[1588,3],[1639,3],[1685,3]]},"2208":{"position":[[568,3]]},"2444":{"position":[[0,4]]},"2462":{"position":[[0,4]]},"2464":{"position":[[0,4]]},"2750":{"position":[[43,3],[128,3],[170,3]]},"2752":{"position":[[50,3],[130,3]]},"2772":{"position":[[490,3],[553,3],[725,3],[766,3],[889,3],[960,3],[1054,3],[1126,3],[1249,3],[1370,3],[1410,3],[1540,3],[1580,3]]},"2823":{"position":[[698,3]]},"2826":{"position":[[619,3]]},"2867":{"position":[[189,3],[268,3],[328,3],[802,3]]},"2869":{"position":[[522,3],[613,3],[691,3],[756,3]]},"2873":{"position":[[160,3],[1083,3],[1335,3],[1372,3],[1418,3],[1458,3]]},"2875":{"position":[[114,3],[139,3]]},"2877":{"position":[[1055,3],[1321,3],[1367,3],[1394,3]]},"2879":{"position":[[238,3],[261,3]]},"2881":{"position":[[799,3],[831,3],[856,3]]},"2883":{"position":[[327,3],[392,3],[1829,3],[1941,3],[2107,3]]},"2885":{"position":[[148,3]]},"2887":{"position":[[868,3],[935,3],[1010,3]]},"2904":{"position":[[281,3]]},"2906":{"position":[[260,3]]},"2920":{"position":[[518,3]]},"2922":{"position":[[1107,3],[1381,3],[1432,3]]},"2924":{"position":[[383,3]]},"2966":{"position":[[751,3],[790,3],[871,3],[962,3],[1038,3],[1250,3],[1293,3],[1336,3],[1376,3]]},"3012":{"position":[[212,3],[270,3],[341,3],[416,3],[487,3]]},"3375":{"position":[[686,4],[2852,3],[2964,3],[3074,3],[3184,3],[3296,3],[3776,3],[3929,3],[3971,3],[4102,3],[4186,3],[4381,3],[4539,3],[4689,3]]},"3377":{"position":[[1101,3],[1268,3],[1365,3],[1448,3],[1490,3]]},"3379":{"position":[[1122,3],[1228,3],[1278,3],[1348,3],[1508,3],[1570,3]]},"3381":{"position":[[2262,3],[2371,3],[2513,3],[2680,3],[2719,3],[2848,3],[2931,3],[3097,3],[3259,3],[3509,3],[3552,3],[3608,3],[3671,3],[3727,3]]},"3383":{"position":[[2489,3],[2528,3],[2598,3],[2762,3],[2868,3],[3039,3]]},"3385":{"position":[[2543,3],[2714,3],[2813,3],[2913,3],[2952,3]]},"3405":{"position":[[1128,3],[1247,3]]},"3431":{"position":[[1512,3],[1557,3],[2159,3],[2216,3]]},"3433":{"position":[[350,3],[404,3]]},"3435":{"position":[[197,3],[237,3],[1142,3],[1185,3],[1241,3],[1304,3],[1360,3]]},"3437":{"position":[[130,3],[172,3]]},"3439":{"position":[[121,3],[523,3],[557,3]]},"3441":{"position":[[171,3]]},"3443":{"position":[[714,3],[1035,3],[1079,3]]},"3445":{"position":[[410,3]]},"3447":{"position":[[849,3],[1276,3],[1312,3],[1430,3],[1481,3],[1527,3]]},"3449":{"position":[[254,3]]}}}],["year",{"_index":1233,"t":{"54":{"position":[[205,5]]},"60":{"position":[[19,5]]},"62":{"position":[[126,6]]},"88":{"position":[[397,4]]},"218":{"position":[[286,5]]},"375":{"position":[[69,5]]},"407":{"position":[[557,5]]},"2610":{"position":[[1133,6]]}}}],["yep",{"_index":841,"t":{"26":{"position":[[58,4]]}}}],["yet\")}func",{"_index":1725,"t":{"110":{"position":[[1926,10],[2062,10]]}}}],["yii",{"_index":3498,"t":{"489":{"position":[[393,4]]},"1469":{"position":[[399,4]]},"2610":{"position":[[399,4]]}}}],["yoola.io",{"_index":862,"t":{"26":{"position":[[1192,9]]}}}],["you'd",{"_index":5417,"t":{"2482":{"position":[[556,5]]}}}],["you'll",{"_index":4388,"t":{"1109":{"position":[[87,6]]},"2190":{"position":[[136,6]]},"3431":{"position":[[135,6]]}}}],["you'r",{"_index":2358,"t":{"206":{"position":[[541,6]]}}}],["you'v",{"_index":2449,"t":{"226":{"position":[[832,6]]},"373":{"position":[[374,6]]},"393":{"position":[[595,6]]}}}],["youngest",{"_index":355,"t":{"8":{"position":[[1083,8]]}}}],["your",{"_index":1618,"t":{"100":{"position":[[494,5]]},"134":{"position":[[1573,7]]}}}],["your_api_key",{"_index":4400,"t":{"1109":{"position":[[882,15]]},"2118":{"position":[[362,13]]},"2190":{"position":[[942,15]]},"2656":{"position":[[786,13],[847,14]]},"3333":{"position":[[365,13],[718,13]]},"3427":{"position":[[146,17],[245,14]]}}}],["your_api_key\"data",{"_index":4391,"t":{"1109":{"position":[[480,18]]},"2190":{"position":[[540,18]]},"3431":{"position":[[592,18]]}}}],["your_api_key>\"http/1.1",{"_index":4417,"t":{"1109":{"position":[[1588,23]]},"2190":{"position":[[1642,23]]}}}],["your_app_id",{"_index":4923,"t":{"1641":{"position":[[186,16]]},"2858":{"position":[[186,16]]}}}],["your_app_secret",{"_index":4925,"t":{"1641":{"position":[[221,20]]},"2858":{"position":[[221,20]]}}}],["your_key_id",{"_index":4933,"t":{"1643":{"position":[[347,16]]},"2860":{"position":[[347,16]]}}}],["your_license_key",{"_index":3694,"t":{"598":{"position":[[398,21]]},"1563":{"position":[[398,21]]},"2766":{"position":[[398,21]]}}}],["your_team_id",{"_index":4935,"t":{"1643":{"position":[[386,15]]},"2860":{"position":[[386,15]]}}}],["yourself",{"_index":561,"t":{"14":{"position":[[1104,9]]},"16":{"position":[[5704,9]]},"457":{"position":[[817,8]]},"1457":{"position":[[813,8]]},"2598":{"position":[[832,8]]}}}],["youself",{"_index":2112,"t":{"148":{"position":[[571,8]]}}}],["youtub",{"_index":4670,"t":{"1349":{"position":[[409,7]]},"2494":{"position":[[411,7]]}}}],["you’d",{"_index":1805,"t":{"124":{"position":[[87,5]]}}}],["you’ll",{"_index":1905,"t":{"128":{"position":[[2451,6],[3271,6]]}}}],["you’r",{"_index":1817,"t":{"126":{"position":[[68,6]]}}}],["yum",{"_index":3691,"t":{"596":{"position":[[155,3]]},"1561":{"position":[[169,3]]},"2764":{"position":[[153,3]]}}}],["z",{"_index":5024,"t":{"1797":{"position":[[801,6]]},"2986":{"position":[[767,6]]}}}],["z0",{"_index":1982,"t":{"130":{"position":[[2392,2]]},"136":{"position":[[480,2]]},"734":{"position":[[604,2]]},"1093":{"position":[[1218,2]]},"1099":{"position":[[667,2]]},"1881":{"position":[[604,2]]},"2176":{"position":[[1218,2]]},"2182":{"position":[[667,2]]},"3072":{"position":[[892,2]]},"3405":{"position":[[1218,2]]},"3411":{"position":[[667,2]]}}}],["za",{"_index":3907,"t":{"734":{"position":[[601,2]]},"1093":{"position":[[1215,2]]},"1099":{"position":[[664,2]]},"1881":{"position":[[601,2]]},"2176":{"position":[[1215,2]]},"2182":{"position":[[664,2]]},"3072":{"position":[[889,2]]},"3405":{"position":[[1215,2]]},"3411":{"position":[[664,2]]}}}],["zero",{"_index":1377,"t":{"74":{"position":[[1223,4]]},"541":{"position":[[142,4]]},"714":{"position":[[322,4]]},"961":{"position":[[142,5]]},"1164":{"position":[[3569,6]]},"1670":{"position":[[349,4]]},"1893":{"position":[[322,4]]},"2098":{"position":[[142,5]]},"2242":{"position":[[106,6]]},"2744":{"position":[[59,4],[132,4]]},"3084":{"position":[[322,4]]},"3257":{"position":[[142,5]]},"3553":{"position":[[106,6]]}}}],["zeromq",{"_index":624,"t":{"16":{"position":[[2104,6],[5655,6],[5819,6]]}}}],["zobnin",{"_index":1276,"t":{"62":{"position":[[464,6]]}}}],["zone",{"_index":2632,"t":{"262":{"position":[[2747,4]]},"2844":{"position":[[519,4]]}}}],["zoo",{"_index":2556,"t":{"246":{"position":[[3896,4]]}}}],["zset",{"_index":1158,"t":{"46":{"position":[[2693,4]]},"469":{"position":[[1556,5]]}}}]],"pipeline":["stemmer"]}}] \ No newline at end of file +[{"documents":[{"i":1,"t":"Million connections with Centrifugo","u":"/blog/2020/02/10/million-connections-with-centrifugo","b":["Blog"]},{"i":3,"t":"Scaling WebSocket in Go and beyond","u":"/blog/2020/11/12/scaling-websocket","b":["Blog"]},{"i":23,"t":"Centrifugo v3 released","u":"/blog/2021/08/31/hello-centrifugo-v3","b":["Blog"]},{"i":55,"t":"Centrifuge – real-time messaging with Go","u":"/blog/2021/01/15/centrifuge-intro","b":["Blog"]},{"i":91,"t":"Experimenting with QUIC and WebTransport","u":"/blog/2020/10/16/experimenting-with-quic-transport","b":["Blog"]},{"i":117,"t":"Centrifugo v4 released – a little revolution","u":"/blog/2022/07/19/centrifugo-v4-released","b":["Blog"]},{"i":153,"t":"Building a multi-room chat application with Laravel and Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","b":["Blog"]},{"i":181,"t":"Centrifugo integration with Django – building a basic chat application","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","b":["Blog"]},{"i":207,"t":"Setting up Keycloak SSO authentication flow and connecting to Centrifugo WebSocket","u":"/blog/2023/03/31/keycloak-sso-centrifugo","b":["Blog"]},{"i":217,"t":"Asynchronous message streaming to Centrifugo with Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","b":["Blog"]},{"i":241,"t":"Centrifugo integration with NodeJS tutorial","u":"/blog/2021/10/18/integrating-with-nodejs","b":["Blog"]},{"i":255,"t":"Discovering Centrifugo PRO: push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","b":["Blog"]},{"i":279,"t":"Using Centrifugo in RabbitX","u":"/blog/2023/08/29/using-centrifugo-in-rabbitx","b":["Blog"]},{"i":281,"t":"","u":"/blog/archive","b":["Blog"]},{"i":282,"t":"Centrifuge library","u":"/docs/3/ecosystem/centrifuge","b":[]},{"i":284,"t":"101 ways to subscribe user on a personal channel in Centrifugo","u":"/blog/2022/07/29/101-way-to-subscribe","b":["Blog"]},{"i":306,"t":"Attributions","u":"/docs/3/attributions","b":[]},{"i":310,"t":"Centrifugo v5 released","u":"/blog/2023/06/29/centrifugo-v5-released","b":["Blog"]},{"i":338,"t":"Framework integrations","u":"/docs/3/ecosystem/integrations","b":[]},{"i":340,"t":"flow_diagrams","u":"/docs/3/flow_diagrams","b":[]},{"i":342,"t":"Design overview","u":"/docs/3/getting-started/design","b":["Getting Started"]},{"i":358,"t":"Main highlights","u":"/docs/3/getting-started/highlights","b":["Getting Started"]},{"i":394,"t":"Frequently Asked Questions","u":"/docs/3/faq","b":["FAQ"]},{"i":444,"t":"Client API showcase","u":"/docs/3/getting-started/client_api","b":["Getting Started"]},{"i":464,"t":"Improving Centrifugo Redis Engine throughput and allocation efficiency with Rueidis Go library","u":"/blog/2022/12/20/improving-redis-engine-performance","b":["Blog"]},{"i":484,"t":"Integration guide","u":"/docs/3/getting-started/integration","b":["Getting Started"]},{"i":506,"t":"Install Centrifugo","u":"/docs/3/getting-started/installation","b":["Getting Started"]},{"i":522,"t":"Centrifugo introduction","u":"/docs/3/getting-started/introduction","b":["Getting Started"]},{"i":530,"t":"Database-driven namespace configuration","u":"/docs/3/pro/db_namespaces","b":[]},{"i":536,"t":"Install and run PRO version","u":"/docs/3/pro/install_and_run","b":["Centrifugo PRO"]},{"i":550,"t":"Centrifugo PRO overview","u":"/docs/3/pro/overview","b":["Centrifugo PRO"]},{"i":558,"t":"CPU and RSS stats","u":"/docs/3/pro/process_stats","b":["Centrifugo PRO","PRO features"]},{"i":560,"t":"Operation throttling","u":"/docs/3/pro/throttling","b":["Centrifugo PRO","PRO features"]},{"i":564,"t":"Faster performance","u":"/docs/3/pro/performance","b":["Centrifugo PRO","PRO features"]},{"i":584,"t":"Migrating to v3","u":"/docs/3/getting-started/migration_v3","b":["Getting Started"]},{"i":621,"t":"User and channel tracing","u":"/docs/3/pro/tracing","b":["Centrifugo PRO","PRO features"]},{"i":625,"t":"Token revocation API","u":"/docs/3/pro/token_revocation","b":["Centrifugo PRO","PRO features"]},{"i":639,"t":"Singleflight","u":"/docs/3/pro/singleflight","b":["Centrifugo PRO","PRO features"]},{"i":641,"t":"Quickstart tutorial ⏱️","u":"/docs/3/getting-started/quickstart","b":["Getting Started"]},{"i":645,"t":"User blocking API","u":"/docs/3/pro/user_block","b":["Centrifugo PRO","PRO features"]},{"i":659,"t":"User connections API","u":"/docs/3/pro/user_connections","b":["Centrifugo PRO","PRO features"]},{"i":661,"t":"Admin web UI","u":"/docs/3/server/admin_web","b":["Server guide"]},{"i":669,"t":"Analytics with ClickHouse","u":"/docs/3/pro/analytics","b":["Centrifugo PRO","PRO features"]},{"i":683,"t":"User status","u":"/docs/3/pro/user_status","b":["Centrifugo PRO","PRO features"]},{"i":695,"t":"Error and disconnect codes","u":"/docs/3/server/codes","b":["Server guide"]},{"i":769,"t":"Client authentication","u":"/docs/3/server/authentication","b":["Server guide"]},{"i":811,"t":"Channels","u":"/docs/3/server/channels","b":["Server guide"]},{"i":859,"t":"Console commands","u":"/docs/3/server/console_commands","b":["Server guide"]},{"i":871,"t":"Configure Centrifugo","u":"/docs/3/server/configuration","b":["Server guide"]},{"i":946,"t":"Infrastructure tuning","u":"/docs/3/server/infra_tuning","b":["Server guide"]},{"i":958,"t":"Monitoring","u":"/docs/3/server/monitoring","b":["Server guide"]},{"i":966,"t":"History and recovery","u":"/docs/3/server/history_and_recovery","b":["Server guide"]},{"i":974,"t":"Load balancing","u":"/docs/3/server/load_balancing","b":["Server guide"]},{"i":984,"t":"Private channels","u":"/docs/3/server/private_channels","b":["Server guide"]},{"i":1006,"t":"Proxy to backend","u":"/docs/3/server/proxy","b":["Server guide"]},{"i":1052,"t":"Configure TLS","u":"/docs/3/server/tls","b":["Server guide"]},{"i":1062,"t":"Client real-time SDKs","u":"/docs/3/transports/client_sdk","b":["Real-time transports / SDK","Bidirectional"]},{"i":1064,"t":"Server-side subscriptions","u":"/docs/3/server/server_subs","b":["Server guide"]},{"i":1072,"t":"SockJS","u":"/docs/3/transports/sockjs","b":["Real-time transports / SDK","Bidirectional"]},{"i":1087,"t":"Real-time transports","u":"/docs/3/transports/overview","b":["Real-time transports / SDK"]},{"i":1095,"t":"Unidirectional SSE (EventSource)","u":"/docs/3/transports/uni_sse","b":["Real-time transports / SDK","Unidirectional"]},{"i":1110,"t":"Unidirectional GRPC","u":"/docs/3/transports/uni_grpc","b":["Real-time transports / SDK","Unidirectional"]},{"i":1131,"t":"Unidirectional WebSocket","u":"/docs/3/transports/uni_websocket","b":["Real-time transports / SDK","Unidirectional"]},{"i":1148,"t":"WebSocket","u":"/docs/3/transports/websocket","b":["Real-time transports / SDK","Bidirectional"]},{"i":1163,"t":"Unidirectional HTTP streaming","u":"/docs/3/transports/uni_http_stream","b":["Real-time transports / SDK","Unidirectional"]},{"i":1180,"t":"Engines, scalability","u":"/docs/3/server/engines","b":["Server guide"]},{"i":1210,"t":"flow_diagrams","u":"/docs/4/flow_diagrams","b":[]},{"i":1212,"t":"Attributions","u":"/docs/4/attributions","b":[]},{"i":1216,"t":"Join community","u":"/docs/4/getting-started/community","b":["Getting Started"]},{"i":1218,"t":"Frequently Asked Questions","u":"/docs/4/faq","b":["FAQ"]},{"i":1274,"t":"Design overview","u":"/docs/4/getting-started/design","b":["Getting Started"]},{"i":1290,"t":"Ecosystem notes","u":"/docs/4/getting-started/ecosystem","b":["Getting Started"]},{"i":1296,"t":"Client API showcase","u":"/docs/4/getting-started/client_api","b":[]},{"i":1316,"t":"Main highlights","u":"/docs/4/getting-started/highlights","b":["Getting Started"]},{"i":1352,"t":"Centrifugo introduction","u":"/docs/4/getting-started/introduction","b":["Getting Started"]},{"i":1356,"t":"Migrating to v4","u":"/docs/4/getting-started/migration_v4","b":["Getting Started"]},{"i":1378,"t":"Integration guide","u":"/docs/4/getting-started/integration","b":["Getting Started"]},{"i":1400,"t":"Client protocol","u":"/docs/3/transports/client_protocol","b":["Real-time transports / SDK","Bidirectional"]},{"i":1434,"t":"Install Centrifugo","u":"/docs/4/getting-started/installation","b":["Getting Started"]},{"i":1450,"t":"Server API","u":"/docs/3/server/server_api","b":["Server guide"]},{"i":1492,"t":"CEL expressions","u":"/docs/4/pro/cel_expressions","b":["Centrifugo PRO","PRO features"]},{"i":1504,"t":"Quickstart tutorial ⏱️","u":"/docs/4/getting-started/quickstart","b":["Getting Started"]},{"i":1506,"t":"Channel patterns","u":"/docs/4/pro/channel_patterns","b":["Centrifugo PRO","PRO features"]},{"i":1516,"t":"Message batching control","u":"/docs/4/pro/client_message_batching","b":["Centrifugo PRO","PRO features"]},{"i":1524,"t":"Centrifugo PRO overview","u":"/docs/4/pro/overview","b":["Centrifugo PRO"]},{"i":1532,"t":"Faster performance","u":"/docs/4/pro/performance","b":["Centrifugo PRO","PRO features"]},{"i":1552,"t":"Install and run PRO version","u":"/docs/4/pro/install_and_run","b":["Centrifugo PRO"]},{"i":1566,"t":"Channel capabilities","u":"/docs/4/pro/capabilities","b":["Centrifugo PRO","PRO features"]},{"i":1590,"t":"Singleflight","u":"/docs/4/pro/singleflight","b":["Centrifugo PRO","PRO features"]},{"i":1592,"t":"CPU and RSS stats","u":"/docs/4/pro/process_stats","b":["Centrifugo PRO","PRO features"]},{"i":1594,"t":"Connections API","u":"/docs/4/pro/connections","b":["Centrifugo PRO","PRO features"]},{"i":1600,"t":"Analytics with ClickHouse","u":"/docs/4/pro/analytics","b":["Centrifugo PRO","PRO features"]},{"i":1620,"t":"User and channel tracing","u":"/docs/4/pro/tracing","b":["Centrifugo PRO","PRO features"]},{"i":1624,"t":"Token revocation API","u":"/docs/4/pro/token_revocation","b":["Centrifugo PRO","PRO features"]},{"i":1640,"t":"User blocking API","u":"/docs/4/pro/user_block","b":["Centrifugo PRO","PRO features"]},{"i":1655,"t":"Admin web UI","u":"/docs/4/server/admin_web","b":["Server guide"]},{"i":1663,"t":"Operation throttling","u":"/docs/4/pro/throttling","b":["Centrifugo PRO","PRO features"]},{"i":1673,"t":"Push notification API","u":"/docs/4/pro/push_notifications","b":["Centrifugo PRO","PRO features"]},{"i":1726,"t":"User status API","u":"/docs/4/pro/user_status","b":["Centrifugo PRO","PRO features"]},{"i":1738,"t":"Channel JWT authorization","u":"/docs/4/server/channel_token_auth","b":["Server guide"]},{"i":1768,"t":"Channel permission model","u":"/docs/4/server/channel_permissions","b":["Server guide"]},{"i":1784,"t":"Client JWT authentication","u":"/docs/4/server/authentication","b":["Server guide"]},{"i":1828,"t":"Channels and namespaces","u":"/docs/4/server/channels","b":["Server guide"]},{"i":1900,"t":"Helper CLI commands","u":"/docs/4/server/console_commands","b":["Server guide"]},{"i":1916,"t":"Infrastructure tuning","u":"/docs/4/server/infra_tuning","b":["Server guide"]},{"i":1930,"t":"Error and disconnect codes","u":"/docs/4/server/codes","b":["Server guide"]},{"i":1968,"t":"Metrics monitoring","u":"/docs/4/server/monitoring","b":["Server guide"]},{"i":1976,"t":"History and recovery","u":"/docs/4/server/history_and_recovery","b":["Server guide"]},{"i":1984,"t":"Configure Centrifugo","u":"/docs/4/server/configuration","b":["Server guide"]},{"i":2067,"t":"Load balancing","u":"/docs/4/server/load_balancing","b":["Server guide"]},{"i":2077,"t":"Engines and scalability","u":"/docs/4/server/engines","b":["Server guide"]},{"i":2113,"t":"Online presence","u":"/docs/4/server/presence","b":["Server guide"]},{"i":2127,"t":"Server-side subscriptions","u":"/docs/4/server/server_subs","b":["Server guide"]},{"i":2135,"t":"Configure TLS","u":"/docs/4/server/tls","b":["Server guide"]},{"i":2145,"t":"Client protocol","u":"/docs/4/transports/client_protocol","b":["Real-time transports / SDK","Bidirectional"]},{"i":2163,"t":"Client real-time SDKs","u":"/docs/4/transports/client_sdk","b":["Real-time transports / SDK","Bidirectional"]},{"i":2175,"t":"HTTP streaming, with bidirectional emulation","u":"/docs/4/transports/http_stream","b":["Real-time transports / SDK","Bidirectional"]},{"i":2182,"t":"Real-time transports","u":"/docs/4/transports/overview","b":["Real-time transports / SDK"]},{"i":2192,"t":"SockJS","u":"/docs/4/transports/sockjs","b":["Real-time transports / SDK","Bidirectional"]},{"i":2207,"t":"SSE (EventSource), with bidirectional emulation","u":"/docs/4/transports/sse","b":["Real-time transports / SDK","Bidirectional"]},{"i":2214,"t":"Unidirectional HTTP streaming","u":"/docs/4/transports/uni_http_stream","b":["Real-time transports / SDK","Unidirectional"]},{"i":2231,"t":"Unidirectional SSE (EventSource)","u":"/docs/4/transports/uni_sse","b":["Real-time transports / SDK","Unidirectional"]},{"i":2244,"t":"Unidirectional WebSocket","u":"/docs/4/transports/uni_websocket","b":["Real-time transports / SDK","Unidirectional"]},{"i":2261,"t":"Unidirectional GRPC","u":"/docs/4/transports/uni_grpc","b":["Real-time transports / SDK","Unidirectional"]},{"i":2282,"t":"Server API walkthrough","u":"/docs/4/server/server_api","b":["Server guide"]},{"i":2324,"t":"Attributions","u":"/docs/attributions","b":[]},{"i":2328,"t":"WebSocket","u":"/docs/4/transports/websocket","b":["Real-time transports / SDK","Bidirectional"]},{"i":2343,"t":"Proxy events to the backend","u":"/docs/4/server/proxy","b":["Server guide"]},{"i":2391,"t":"WebTransport","u":"/docs/4/transports/webtransport","b":["Real-time transports / SDK","Bidirectional"]},{"i":2393,"t":"flow_diagrams","u":"/docs/flow_diagrams","b":[]},{"i":2395,"t":"Join community","u":"/docs/getting-started/community","b":["Getting Started"]},{"i":2397,"t":"Design overview","u":"/docs/getting-started/design","b":["Getting Started"]},{"i":2413,"t":"Comparing with others","u":"/docs/getting-started/comparisons","b":["Getting Started"]},{"i":2425,"t":"Ecosystem notes","u":"/docs/getting-started/ecosystem","b":["Getting Started"]},{"i":2431,"t":"Main highlights","u":"/docs/getting-started/highlights","b":["Getting Started"]},{"i":2467,"t":"Frequently Asked Questions","u":"/docs/faq","b":["FAQ"]},{"i":2523,"t":"Centrifugo introduction","u":"/docs/getting-started/introduction","b":["Getting Started"]},{"i":2527,"t":"Migrating to v4","u":"/docs/getting-started/migration_v4","b":[]},{"i":2549,"t":"Integration guide","u":"/docs/getting-started/integration","b":["Getting Started"]},{"i":2571,"t":"Migrating to v5","u":"/docs/getting-started/migration_v5","b":["Getting Started"]},{"i":2583,"t":"Client API showcase","u":"/docs/getting-started/client_api","b":[]},{"i":2603,"t":"Install Centrifugo","u":"/docs/getting-started/installation","b":["Getting Started"]},{"i":2619,"t":"Client SDK API","u":"/docs/4/transports/client_api","b":["Real-time transports / SDK","Bidirectional"]},{"i":2661,"t":"Channel capabilities","u":"/docs/pro/capabilities","b":["Centrifugo PRO","PRO version features"]},{"i":2685,"t":"Quickstart tutorial ⏱️","u":"/docs/getting-started/quickstart","b":["Getting Started"]},{"i":2687,"t":"CEL expressions","u":"/docs/pro/cel_expressions","b":["Centrifugo PRO","PRO version features"]},{"i":2699,"t":"Message batching control","u":"/docs/pro/client_message_batching","b":["Centrifugo PRO","PRO version features"]},{"i":2707,"t":"Channel state events","u":"/docs/pro/channel_state_events","b":["Centrifugo PRO","PRO version features"]},{"i":2709,"t":"Distributed rate limit API","u":"/docs/pro/distributed_rate_limit","b":[]},{"i":2721,"t":"Install and run PRO version","u":"/docs/pro/install_and_run","b":["Centrifugo PRO"]},{"i":2735,"t":"Observability enhancements","u":"/docs/pro/observability_enhancements","b":["Centrifugo PRO","PRO version features"]},{"i":2737,"t":"Faster performance","u":"/docs/pro/performance","b":["Centrifugo PRO","PRO version features"]},{"i":2757,"t":"Centrifugo PRO","u":"/docs/pro/overview","b":["Centrifugo PRO"]},{"i":2765,"t":"Channel patterns","u":"/docs/pro/channel_patterns","b":["Centrifugo PRO","PRO version features"]},{"i":2775,"t":"CPU and RSS stats","u":"/docs/pro/process_stats","b":["Centrifugo PRO","PRO version features"]},{"i":2777,"t":"Connections API","u":"/docs/pro/connections","b":["Centrifugo PRO","PRO version features"]},{"i":2783,"t":"Singleflight","u":"/docs/pro/singleflight","b":["Centrifugo PRO","PRO version features"]},{"i":2785,"t":"Analytics with ClickHouse","u":"/docs/pro/analytics","b":["Centrifugo PRO","PRO version features"]},{"i":2807,"t":"User and channel tracing","u":"/docs/pro/tracing","b":["Centrifugo PRO","PRO version features"]},{"i":2811,"t":"Token revocation API","u":"/docs/pro/token_revocation","b":["Centrifugo PRO","PRO version features"]},{"i":2827,"t":"Operation rate limits","u":"/docs/pro/rate_limiting","b":["Centrifugo PRO","PRO version features"]},{"i":2837,"t":"Push notification API","u":"/docs/pro/push_notifications","b":["Centrifugo PRO","PRO version features"]},{"i":2892,"t":"User blocking API","u":"/docs/pro/user_block","b":["Centrifugo PRO","PRO version features"]},{"i":2907,"t":"Admin web UI","u":"/docs/server/admin_web","b":["Server guide"]},{"i":2915,"t":"User status API","u":"/docs/pro/user_status","b":["Centrifugo PRO","PRO version features"]},{"i":2927,"t":"Channel permission model","u":"/docs/server/channel_permissions","b":["Server guide"]},{"i":2943,"t":"Channel JWT authorization","u":"/docs/server/channel_token_auth","b":["Server guide"]},{"i":2975,"t":"Error and disconnect codes","u":"/docs/server/codes","b":["Server guide"]},{"i":3013,"t":"Client JWT authentication","u":"/docs/server/authentication","b":["Server guide"]},{"i":3057,"t":"Channels and namespaces","u":"/docs/server/channels","b":["Server guide"]},{"i":3135,"t":"History and recovery","u":"/docs/server/history_and_recovery","b":["Server guide"]},{"i":3143,"t":"Infrastructure tuning","u":"/docs/server/infra_tuning","b":["Server guide"]},{"i":3157,"t":"Helper CLI commands","u":"/docs/server/console_commands","b":["Server guide"]},{"i":3173,"t":"Configure Centrifugo","u":"/docs/server/configuration","b":["Server guide"]},{"i":3262,"t":"Engines and scalability","u":"/docs/server/engines","b":["Server guide"]},{"i":3296,"t":"Metrics monitoring","u":"/docs/server/monitoring","b":[]},{"i":3304,"t":"Load balancing","u":"/docs/server/load_balancing","b":["Server guide"]},{"i":3314,"t":"Server observability","u":"/docs/server/observability","b":["Server guide"]},{"i":3330,"t":"Server-side subscriptions","u":"/docs/server/server_subs","b":["Server guide"]},{"i":3338,"t":"Online presence","u":"/docs/server/presence","b":["Server guide"]},{"i":3350,"t":"Configure TLS","u":"/docs/server/tls","b":["Server guide"]},{"i":3360,"t":"Proxy subscription streams","u":"/docs/server/proxy_streams","b":["Server guide"]},{"i":3374,"t":"Client protocol","u":"/docs/transports/client_protocol","b":["Real-time transports / SDK","Bidirectional"]},{"i":3392,"t":"Client real-time SDKs","u":"/docs/transports/client_sdk","b":["Real-time transports / SDK","Bidirectional"]},{"i":3404,"t":"Proxy events to the backend","u":"/docs/server/proxy","b":["Server guide"]},{"i":3452,"t":"HTTP streaming, with bidirectional emulation","u":"/docs/transports/http_stream","b":["Real-time transports / SDK","Bidirectional"]},{"i":3459,"t":"Real-time transports","u":"/docs/transports/overview","b":["Real-time transports / SDK"]},{"i":3467,"t":"SockJS","u":"/docs/transports/sockjs","b":["Real-time transports / SDK","Bidirectional"]},{"i":3482,"t":"SSE (EventSource), with bidirectional emulation","u":"/docs/transports/sse","b":["Real-time transports / SDK","Bidirectional"]},{"i":3489,"t":"Unidirectional client protocol","u":"/docs/transports/uni_client_protocol","b":["Real-time transports / SDK","Unidirectional"]},{"i":3493,"t":"Server API walkthrough","u":"/docs/server/server_api","b":["Server guide"]},{"i":3543,"t":"Unidirectional GRPC","u":"/docs/transports/uni_grpc","b":["Real-time transports / SDK","Unidirectional"]},{"i":3564,"t":"Unidirectional HTTP streaming","u":"/docs/transports/uni_http_stream","b":["Real-time transports / SDK","Unidirectional"]},{"i":3581,"t":"Unidirectional SSE (EventSource)","u":"/docs/transports/uni_sse","b":["Real-time transports / SDK","Unidirectional"]},{"i":3594,"t":"Unidirectional WebSocket","u":"/docs/transports/uni_websocket","b":["Real-time transports / SDK","Unidirectional"]},{"i":3611,"t":"WebTransport","u":"/docs/transports/webtransport","b":["Real-time transports / SDK","Bidirectional"]},{"i":3613,"t":"WebSocket","u":"/docs/transports/websocket","b":["Real-time transports / SDK","Bidirectional"]},{"i":3630,"t":"Client SDK API","u":"/docs/transports/client_api","b":["Real-time transports / SDK","Bidirectional"]}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/1",[0,4.619,1,3.391,2,1.942]],["t/3",[3,4.026,4,2.597,5,3.328,6,4.026]],["t/23",[2,1.942,7,4.136,8,3.818]],["t/55",[5,2.649,9,2.869,10,2.243,11,2.149,12,2.149,13,2.484]],["t/91",[14,4.619,15,4.619,16,3.818]],["t/117",[2,1.347,8,2.649,10,2.243,17,2.649,18,3.204,19,3.204]],["t/153",[2,1.222,20,2.603,21,2.907,22,2.907,23,2.603,24,2.603,25,2.907]],["t/181",[2,1.118,10,1.862,20,2.383,23,2.383,24,2.383,26,1.862,27,2.661,28,2.661]],["t/207",[1,1.801,2,1.031,4,1.582,29,2.453,30,2.453,31,2.453,32,2.453,33,1.901,34,2.453]],["t/217",[2,1.5,13,2.766,35,3.568,36,2.393,37,3.568]],["t/241",[2,1.692,26,2.818,38,4.026,39,3.121]],["t/255",[2,1.347,40,3.204,41,2.149,42,2.649,43,2.649,44,1.4]],["t/279",[2,1.942,45,4.619,46,4.619]],["t/281",[]],["t/282",[9,4.85,47,4.85]],["t/284",[2,1.222,48,2.907,49,2.907,50,2.907,51,1.696,52,2.907,53,1.446]],["t/306",[54,5.411]],["t/310",[2,1.942,8,3.818,55,4.136]],["t/338",[26,3.791,56,5.416]],["t/340",[57,5.411]],["t/342",[58,4.477,59,3.976]],["t/358",[60,4.477,61,4.477]],["t/394",[62,3.818,63,3.818,64,3.818]],["t/444",[44,2.019,65,2.412,66,3.818]],["t/464",[2,0.956,5,1.881,47,2.037,67,2.275,68,2.275,69,1.764,70,2.275,71,2.275,72,2.275,73,2.275]],["t/484",[26,3.791,74,4.477]],["t/506",[2,2.277,75,3.791]],["t/522",[2,2.277,76,4.477]],["t/530",[77,4.026,78,4.026,79,3.328,80,2.701]],["t/536",[41,2.701,75,2.818,81,3.328,82,3.328]],["t/550",[2,1.942,41,3.098,59,3.391]],["t/558",[83,3.818,84,3.818,85,3.818]],["t/560",[86,4.477,87,4.85]],["t/564",[88,4.477,89,4.477]],["t/584",[7,4.85,90,4.199]],["t/621",[51,2.694,53,2.297,91,3.818]],["t/625",[44,2.019,92,3.818,93,3.818]],["t/639",[94,5.411]],["t/641",[10,3.233,39,3.581,95,3.818]],["t/645",[44,2.019,51,2.694,96,3.818]],["t/659",[1,3.391,44,2.019,51,2.694]],["t/661",[97,3.818,98,3.818,99,3.818]],["t/669",[100,4.477,101,4.477]],["t/683",[51,3.159,102,4.477]],["t/695",[103,3.818,104,3.818,105,3.818]],["t/769",[33,4.199,65,2.828]],["t/811",[53,3.256]],["t/859",[106,5.416,107,4.477]],["t/871",[2,2.277,80,3.633]],["t/946",[108,4.477,109,4.477]],["t/958",[110,5.411]],["t/966",[111,4.477,112,4.477]],["t/974",[113,4.477,114,4.477]],["t/984",[53,2.694,115,5.416]],["t/1006",[116,4.199,117,4.477]],["t/1052",[80,3.633,118,4.477]],["t/1062",[11,2.701,12,2.701,65,2.103,119,2.956]],["t/1064",[120,3.098,121,3.818,122,3.581]],["t/1072",[123,5.411]],["t/1087",[11,3.098,12,3.098,124,3.818]],["t/1095",[125,2.543,126,3.391,127,3.391]],["t/1110",[125,2.981,128,4.477]],["t/1131",[4,3.494,125,2.981]],["t/1148",[4,4.223]],["t/1163",[36,3.098,125,2.543,129,3.391]],["t/1180",[69,4.199,130,4.477]],["t/1210",[57,5.411]],["t/1212",[54,5.411]],["t/1216",[131,4.85,132,4.85]],["t/1218",[62,3.818,63,3.818,64,3.818]],["t/1274",[58,4.477,59,3.976]],["t/1290",[133,4.85,134,4.85]],["t/1296",[44,2.019,65,2.412,66,3.818]],["t/1316",[60,4.477,61,4.477]],["t/1352",[2,2.277,76,4.477]],["t/1356",[17,4.477,90,4.199]],["t/1378",[26,3.791,74,4.477]],["t/1400",[65,2.828,135,4.199]],["t/1434",[2,2.277,75,3.791]],["t/1450",[44,2.367,120,3.633]],["t/1492",[136,4.85,137,4.85]],["t/1504",[10,3.233,39,3.581,95,3.818]],["t/1506",[53,2.694,138,4.85]],["t/1516",[13,3.581,139,4.136,140,4.136]],["t/1524",[2,1.942,41,3.098,59,3.391]],["t/1532",[88,4.477,89,4.477]],["t/1552",[41,2.701,75,2.818,81,3.328,82,3.328]],["t/1566",[53,2.694,141,4.85]],["t/1590",[94,5.411]],["t/1592",[83,3.818,84,3.818,85,3.818]],["t/1594",[1,3.976,44,2.367]],["t/1600",[100,4.477,101,4.477]],["t/1620",[51,2.694,53,2.297,91,3.818]],["t/1624",[44,2.019,92,3.818,93,3.818]],["t/1640",[44,2.019,51,2.694,96,3.818]],["t/1655",[97,3.818,98,3.818,99,3.818]],["t/1663",[86,4.477,87,4.85]],["t/1673",[42,3.818,43,3.818,44,2.019]],["t/1726",[44,2.019,51,2.694,102,3.818]],["t/1738",[53,2.297,142,3.581,143,4.136]],["t/1768",[53,2.297,144,4.136,145,4.136]],["t/1784",[33,3.581,65,2.412,142,3.581]],["t/1828",[53,2.694,79,4.477]],["t/1900",[107,3.818,146,4.136,147,4.136]],["t/1916",[108,4.477,109,4.477]],["t/1930",[103,3.818,104,3.818,105,3.818]],["t/1968",[110,4.477,148,4.85]],["t/1976",[111,4.477,112,4.477]],["t/1984",[2,2.277,80,3.633]],["t/2067",[113,4.477,114,4.477]],["t/2077",[69,4.199,130,4.477]],["t/2113",[149,4.85,150,4.85]],["t/2127",[120,3.098,121,3.818,122,3.581]],["t/2135",[80,3.633,118,4.477]],["t/2145",[65,2.828,135,4.199]],["t/2163",[11,2.701,12,2.701,65,2.103,119,2.956]],["t/2175",[36,2.701,129,2.956,151,3.121,152,3.121]],["t/2182",[11,3.098,12,3.098,124,3.818]],["t/2192",[123,5.411]],["t/2207",[126,2.956,127,2.956,151,3.121,152,3.121]],["t/2214",[36,3.098,125,2.543,129,3.391]],["t/2231",[125,2.543,126,3.391,127,3.391]],["t/2244",[4,3.494,125,2.981]],["t/2261",[125,2.981,128,4.477]],["t/2282",[44,2.019,120,3.098,153,4.136]],["t/2324",[54,5.411]],["t/2328",[4,4.223]],["t/2343",[116,3.581,117,3.818,154,3.818]],["t/2391",[16,5.411]],["t/2393",[57,5.411]],["t/2395",[131,4.85,132,4.85]],["t/2397",[58,4.477,59,3.976]],["t/2413",[155,5.416,156,5.416]],["t/2425",[133,4.85,134,4.85]],["t/2431",[60,4.477,61,4.477]],["t/2467",[62,3.818,63,3.818,64,3.818]],["t/2523",[2,2.277,76,4.477]],["t/2527",[17,4.477,90,4.199]],["t/2549",[26,3.791,74,4.477]],["t/2571",[55,4.85,90,4.199]],["t/2583",[44,2.019,65,2.412,66,3.818]],["t/2603",[2,2.277,75,3.791]],["t/2619",[44,2.019,65,2.412,119,3.391]],["t/2661",[53,2.694,141,4.85]],["t/2685",[10,3.233,39,3.581,95,3.818]],["t/2687",[136,4.85,137,4.85]],["t/2699",[13,3.581,139,4.136,140,4.136]],["t/2707",[53,2.297,154,3.818,157,4.619]],["t/2709",[44,1.76,158,4.026,159,3.605,160,3.605]],["t/2721",[41,2.701,75,2.818,81,3.328,82,3.328]],["t/2735",[161,4.85,162,5.416]],["t/2737",[88,4.477,89,4.477]],["t/2757",[2,2.277,41,3.633]],["t/2765",[53,2.694,138,4.85]],["t/2775",[83,3.818,84,3.818,85,3.818]],["t/2777",[1,3.976,44,2.367]],["t/2783",[94,5.411]],["t/2785",[100,4.477,101,4.477]],["t/2807",[51,2.694,53,2.297,91,3.818]],["t/2811",[44,2.019,92,3.818,93,3.818]],["t/2827",[86,3.818,159,4.136,160,4.136]],["t/2837",[42,3.818,43,3.818,44,2.019]],["t/2892",[44,2.019,51,2.694,96,3.818]],["t/2907",[97,3.818,98,3.818,99,3.818]],["t/2915",[44,2.019,51,2.694,102,3.818]],["t/2927",[53,2.297,144,4.136,145,4.136]],["t/2943",[53,2.297,142,3.581,143,4.136]],["t/2975",[103,3.818,104,3.818,105,3.818]],["t/3013",[33,3.581,65,2.412,142,3.581]],["t/3057",[53,2.694,79,4.477]],["t/3135",[111,4.477,112,4.477]],["t/3143",[108,4.477,109,4.477]],["t/3157",[107,3.818,146,4.136,147,4.136]],["t/3173",[2,2.277,80,3.633]],["t/3262",[69,4.199,130,4.477]],["t/3296",[110,4.477,148,4.85]],["t/3304",[113,4.477,114,4.477]],["t/3314",[120,3.633,161,4.85]],["t/3330",[120,3.098,121,3.818,122,3.581]],["t/3338",[149,4.85,150,4.85]],["t/3350",[80,3.633,118,4.477]],["t/3360",[36,3.098,116,3.581,122,3.581]],["t/3374",[65,2.828,135,4.199]],["t/3392",[11,2.701,12,2.701,65,2.103,119,2.956]],["t/3404",[116,3.581,117,3.818,154,3.818]],["t/3452",[36,2.701,129,2.956,151,3.121,152,3.121]],["t/3459",[11,3.098,12,3.098,124,3.818]],["t/3467",[123,5.411]],["t/3482",[126,2.956,127,2.956,151,3.121,152,3.121]],["t/3489",[65,2.412,125,2.543,135,3.581]],["t/3493",[44,2.019,120,3.098,153,4.136]],["t/3543",[125,2.981,128,4.477]],["t/3564",[36,3.098,125,2.543,129,3.391]],["t/3581",[125,2.543,126,3.391,127,3.391]],["t/3594",[4,3.494,125,2.981]],["t/3611",[16,5.411]],["t/3613",[4,4.223]],["t/3630",[44,2.019,65,2.412,119,3.391]]],"invertedIndex":[["",{"_index":10,"t":{"55":{"position":[[11,1]]},"117":{"position":[[23,1]]},"181":{"position":[[35,1]]},"641":{"position":[[20,2]]},"1504":{"position":[[20,2]]},"2685":{"position":[[20,2]]}}}],["101",{"_index":48,"t":{"284":{"position":[[0,3]]}}}],["admin",{"_index":97,"t":{"661":{"position":[[0,5]]},"1655":{"position":[[0,5]]},"2907":{"position":[[0,5]]}}}],["alloc",{"_index":71,"t":{"464":{"position":[[49,10]]}}}],["analyt",{"_index":100,"t":{"669":{"position":[[0,9]]},"1600":{"position":[[0,9]]},"2785":{"position":[[0,9]]}}}],["api",{"_index":44,"t":{"255":{"position":[[47,3]]},"444":{"position":[[7,3]]},"625":{"position":[[17,3]]},"645":{"position":[[14,3]]},"659":{"position":[[17,3]]},"1296":{"position":[[7,3]]},"1450":{"position":[[7,3]]},"1594":{"position":[[12,3]]},"1624":{"position":[[17,3]]},"1640":{"position":[[14,3]]},"1673":{"position":[[18,3]]},"1726":{"position":[[12,3]]},"2282":{"position":[[7,3]]},"2583":{"position":[[7,3]]},"2619":{"position":[[11,3]]},"2709":{"position":[[23,3]]},"2777":{"position":[[12,3]]},"2811":{"position":[[17,3]]},"2837":{"position":[[18,3]]},"2892":{"position":[[14,3]]},"2915":{"position":[[12,3]]},"3493":{"position":[[7,3]]},"3630":{"position":[[11,3]]}}}],["applic",{"_index":24,"t":{"153":{"position":[[27,11]]},"181":{"position":[[59,11]]}}}],["ask",{"_index":63,"t":{"394":{"position":[[11,5]]},"1218":{"position":[[11,5]]},"2467":{"position":[[11,5]]}}}],["asynchron",{"_index":35,"t":{"217":{"position":[[0,12]]}}}],["attribut",{"_index":54,"t":{"306":{"position":[[0,12]]},"1212":{"position":[[0,12]]},"2324":{"position":[[0,12]]}}}],["authent",{"_index":33,"t":{"207":{"position":[[24,14]]},"769":{"position":[[7,14]]},"1784":{"position":[[11,14]]},"3013":{"position":[[11,14]]}}}],["author",{"_index":143,"t":{"1738":{"position":[[12,13]]},"2943":{"position":[[12,13]]}}}],["backend",{"_index":117,"t":{"1006":{"position":[[9,7]]},"2343":{"position":[[20,7]]},"3404":{"position":[[20,7]]}}}],["balanc",{"_index":114,"t":{"974":{"position":[[5,9]]},"2067":{"position":[[5,9]]},"3304":{"position":[[5,9]]}}}],["basic",{"_index":28,"t":{"181":{"position":[[48,5]]}}}],["batch",{"_index":139,"t":{"1516":{"position":[[8,8]]},"2699":{"position":[[8,8]]}}}],["bentho",{"_index":37,"t":{"217":{"position":[[50,7]]}}}],["beyond",{"_index":6,"t":{"3":{"position":[[28,6]]}}}],["bidirect",{"_index":151,"t":{"2175":{"position":[[21,13]]},"2207":{"position":[[24,13]]},"3452":{"position":[[21,13]]},"3482":{"position":[[24,13]]}}}],["block",{"_index":96,"t":{"645":{"position":[[5,8]]},"1640":{"position":[[5,8]]},"2892":{"position":[[5,8]]}}}],["build",{"_index":20,"t":{"153":{"position":[[0,8]]},"181":{"position":[[37,8]]}}}],["capabl",{"_index":141,"t":{"1566":{"position":[[8,12]]},"2661":{"position":[[8,12]]}}}],["cel",{"_index":136,"t":{"1492":{"position":[[0,3]]},"2687":{"position":[[0,3]]}}}],["centrifug",{"_index":9,"t":{"55":{"position":[[0,10]]},"282":{"position":[[0,10]]}}}],["centrifugo",{"_index":2,"t":{"1":{"position":[[25,10]]},"23":{"position":[[0,10]]},"117":{"position":[[0,10]]},"153":{"position":[[56,10]]},"181":{"position":[[0,10]]},"207":{"position":[[62,10]]},"217":{"position":[[34,10]]},"241":{"position":[[0,10]]},"255":{"position":[[12,10]]},"279":{"position":[[6,10]]},"284":{"position":[[52,10]]},"310":{"position":[[0,10]]},"464":{"position":[[10,10]]},"506":{"position":[[8,10]]},"522":{"position":[[0,10]]},"550":{"position":[[0,10]]},"871":{"position":[[10,10]]},"1352":{"position":[[0,10]]},"1434":{"position":[[8,10]]},"1524":{"position":[[0,10]]},"1984":{"position":[[10,10]]},"2523":{"position":[[0,10]]},"2603":{"position":[[8,10]]},"2757":{"position":[[0,10]]},"3173":{"position":[[10,10]]}}}],["channel",{"_index":53,"t":{"284":{"position":[[41,7]]},"621":{"position":[[9,7]]},"811":{"position":[[0,8]]},"984":{"position":[[8,8]]},"1506":{"position":[[0,7]]},"1566":{"position":[[0,7]]},"1620":{"position":[[9,7]]},"1738":{"position":[[0,7]]},"1768":{"position":[[0,7]]},"1828":{"position":[[0,8]]},"2661":{"position":[[0,7]]},"2707":{"position":[[0,7]]},"2765":{"position":[[0,7]]},"2807":{"position":[[9,7]]},"2927":{"position":[[0,7]]},"2943":{"position":[[0,7]]},"3057":{"position":[[0,8]]}}}],["chat",{"_index":23,"t":{"153":{"position":[[22,4]]},"181":{"position":[[54,4]]}}}],["cli",{"_index":147,"t":{"1900":{"position":[[7,3]]},"3157":{"position":[[7,3]]}}}],["clickhous",{"_index":101,"t":{"669":{"position":[[15,10]]},"1600":{"position":[[15,10]]},"2785":{"position":[[15,10]]}}}],["client",{"_index":65,"t":{"444":{"position":[[0,6]]},"769":{"position":[[0,6]]},"1062":{"position":[[0,6]]},"1296":{"position":[[0,6]]},"1400":{"position":[[0,6]]},"1784":{"position":[[0,6]]},"2145":{"position":[[0,6]]},"2163":{"position":[[0,6]]},"2583":{"position":[[0,6]]},"2619":{"position":[[0,6]]},"3013":{"position":[[0,6]]},"3374":{"position":[[0,6]]},"3392":{"position":[[0,6]]},"3489":{"position":[[15,6]]},"3630":{"position":[[0,6]]}}}],["code",{"_index":105,"t":{"695":{"position":[[21,5]]},"1930":{"position":[[21,5]]},"2975":{"position":[[21,5]]}}}],["command",{"_index":107,"t":{"859":{"position":[[8,8]]},"1900":{"position":[[11,8]]},"3157":{"position":[[11,8]]}}}],["commun",{"_index":132,"t":{"1216":{"position":[[5,9]]},"2395":{"position":[[5,9]]}}}],["compar",{"_index":155,"t":{"2413":{"position":[[0,9]]}}}],["configur",{"_index":80,"t":{"530":{"position":[[26,13]]},"871":{"position":[[0,9]]},"1052":{"position":[[0,9]]},"1984":{"position":[[0,9]]},"2135":{"position":[[0,9]]},"3173":{"position":[[0,9]]},"3350":{"position":[[0,9]]}}}],["connect",{"_index":1,"t":{"1":{"position":[[8,11]]},"207":{"position":[[48,10]]},"659":{"position":[[5,11]]},"1594":{"position":[[0,11]]},"2777":{"position":[[0,11]]}}}],["consol",{"_index":106,"t":{"859":{"position":[[0,7]]}}}],["control",{"_index":140,"t":{"1516":{"position":[[17,7]]},"2699":{"position":[[17,7]]}}}],["cpu",{"_index":83,"t":{"558":{"position":[[0,3]]},"1592":{"position":[[0,3]]},"2775":{"position":[[0,3]]}}}],["databas",{"_index":77,"t":{"530":{"position":[[0,8]]}}}],["design",{"_index":58,"t":{"342":{"position":[[0,6]]},"1274":{"position":[[0,6]]},"2397":{"position":[[0,6]]}}}],["disconnect",{"_index":104,"t":{"695":{"position":[[10,10]]},"1930":{"position":[[10,10]]},"2975":{"position":[[10,10]]}}}],["discov",{"_index":40,"t":{"255":{"position":[[0,11]]}}}],["distribut",{"_index":158,"t":{"2709":{"position":[[0,11]]}}}],["django",{"_index":27,"t":{"181":{"position":[[28,6]]}}}],["driven",{"_index":78,"t":{"530":{"position":[[9,6]]}}}],["ecosystem",{"_index":133,"t":{"1290":{"position":[[0,9]]},"2425":{"position":[[0,9]]}}}],["effici",{"_index":72,"t":{"464":{"position":[[60,10]]}}}],["emul",{"_index":152,"t":{"2175":{"position":[[35,9]]},"2207":{"position":[[38,9]]},"3452":{"position":[[35,9]]},"3482":{"position":[[38,9]]}}}],["engin",{"_index":69,"t":{"464":{"position":[[27,6]]},"1180":{"position":[[0,8]]},"2077":{"position":[[0,7]]},"3262":{"position":[[0,7]]}}}],["enhanc",{"_index":162,"t":{"2735":{"position":[[14,12]]}}}],["error",{"_index":103,"t":{"695":{"position":[[0,5]]},"1930":{"position":[[0,5]]},"2975":{"position":[[0,5]]}}}],["event",{"_index":154,"t":{"2343":{"position":[[6,6]]},"2707":{"position":[[14,6]]},"3404":{"position":[[6,6]]}}}],["eventsourc",{"_index":127,"t":{"1095":{"position":[[19,13]]},"2207":{"position":[[4,14]]},"2231":{"position":[[19,13]]},"3482":{"position":[[4,14]]},"3581":{"position":[[19,13]]}}}],["experi",{"_index":14,"t":{"91":{"position":[[0,13]]}}}],["express",{"_index":137,"t":{"1492":{"position":[[4,11]]},"2687":{"position":[[4,11]]}}}],["faster",{"_index":88,"t":{"564":{"position":[[0,6]]},"1532":{"position":[[0,6]]},"2737":{"position":[[0,6]]}}}],["flow",{"_index":34,"t":{"207":{"position":[[39,4]]}}}],["flow_diagram",{"_index":57,"t":{"340":{"position":[[0,13]]},"1210":{"position":[[0,13]]},"2393":{"position":[[0,13]]}}}],["framework",{"_index":56,"t":{"338":{"position":[[0,9]]}}}],["frequent",{"_index":62,"t":{"394":{"position":[[0,10]]},"1218":{"position":[[0,10]]},"2467":{"position":[[0,10]]}}}],["go",{"_index":5,"t":{"3":{"position":[[21,2]]},"55":{"position":[[38,2]]},"464":{"position":[[84,2]]}}}],["grpc",{"_index":128,"t":{"1110":{"position":[[15,4]]},"2261":{"position":[[15,4]]},"3543":{"position":[[15,4]]}}}],["guid",{"_index":74,"t":{"484":{"position":[[12,5]]},"1378":{"position":[[12,5]]},"2549":{"position":[[12,5]]}}}],["helper",{"_index":146,"t":{"1900":{"position":[[0,6]]},"3157":{"position":[[0,6]]}}}],["highlight",{"_index":61,"t":{"358":{"position":[[5,10]]},"1316":{"position":[[5,10]]},"2431":{"position":[[5,10]]}}}],["histori",{"_index":111,"t":{"966":{"position":[[0,7]]},"1976":{"position":[[0,7]]},"3135":{"position":[[0,7]]}}}],["http",{"_index":129,"t":{"1163":{"position":[[15,4]]},"2175":{"position":[[0,4]]},"2214":{"position":[[15,4]]},"3452":{"position":[[0,4]]},"3564":{"position":[[15,4]]}}}],["improv",{"_index":67,"t":{"464":{"position":[[0,9]]}}}],["infrastructur",{"_index":108,"t":{"946":{"position":[[0,14]]},"1916":{"position":[[0,14]]},"3143":{"position":[[0,14]]}}}],["instal",{"_index":75,"t":{"506":{"position":[[0,7]]},"536":{"position":[[0,7]]},"1434":{"position":[[0,7]]},"1552":{"position":[[0,7]]},"2603":{"position":[[0,7]]},"2721":{"position":[[0,7]]}}}],["integr",{"_index":26,"t":{"181":{"position":[[11,11]]},"241":{"position":[[11,11]]},"338":{"position":[[10,12]]},"484":{"position":[[0,11]]},"1378":{"position":[[0,11]]},"2549":{"position":[[0,11]]}}}],["introduct",{"_index":76,"t":{"522":{"position":[[11,12]]},"1352":{"position":[[11,12]]},"2523":{"position":[[11,12]]}}}],["join",{"_index":131,"t":{"1216":{"position":[[0,4]]},"2395":{"position":[[0,4]]}}}],["jwt",{"_index":142,"t":{"1738":{"position":[[8,3]]},"1784":{"position":[[7,3]]},"2943":{"position":[[8,3]]},"3013":{"position":[[7,3]]}}}],["keycloak",{"_index":31,"t":{"207":{"position":[[11,8]]}}}],["laravel",{"_index":25,"t":{"153":{"position":[[44,7]]}}}],["librari",{"_index":47,"t":{"282":{"position":[[11,7]]},"464":{"position":[[87,7]]}}}],["limit",{"_index":160,"t":{"2709":{"position":[[17,5]]},"2827":{"position":[[15,6]]}}}],["littl",{"_index":18,"t":{"117":{"position":[[27,6]]}}}],["load",{"_index":113,"t":{"974":{"position":[[0,4]]},"2067":{"position":[[0,4]]},"3304":{"position":[[0,4]]}}}],["main",{"_index":60,"t":{"358":{"position":[[0,4]]},"1316":{"position":[[0,4]]},"2431":{"position":[[0,4]]}}}],["messag",{"_index":13,"t":{"55":{"position":[[23,9]]},"217":{"position":[[13,7]]},"1516":{"position":[[0,7]]},"2699":{"position":[[0,7]]}}}],["metric",{"_index":148,"t":{"1968":{"position":[[0,7]]},"3296":{"position":[[0,7]]}}}],["migrat",{"_index":90,"t":{"584":{"position":[[0,9]]},"1356":{"position":[[0,9]]},"2527":{"position":[[0,9]]},"2571":{"position":[[0,9]]}}}],["million",{"_index":0,"t":{"1":{"position":[[0,7]]}}}],["model",{"_index":145,"t":{"1768":{"position":[[19,5]]},"2927":{"position":[[19,5]]}}}],["monitor",{"_index":110,"t":{"958":{"position":[[0,10]]},"1968":{"position":[[8,10]]},"3296":{"position":[[8,10]]}}}],["multi",{"_index":21,"t":{"153":{"position":[[11,5]]}}}],["namespac",{"_index":79,"t":{"530":{"position":[[16,9]]},"1828":{"position":[[13,10]]},"3057":{"position":[[13,10]]}}}],["nodej",{"_index":38,"t":{"241":{"position":[[28,6]]}}}],["note",{"_index":134,"t":{"1290":{"position":[[10,5]]},"2425":{"position":[[10,5]]}}}],["notif",{"_index":43,"t":{"255":{"position":[[33,13]]},"1673":{"position":[[5,12]]},"2837":{"position":[[5,12]]}}}],["observ",{"_index":161,"t":{"2735":{"position":[[0,13]]},"3314":{"position":[[7,13]]}}}],["onlin",{"_index":149,"t":{"2113":{"position":[[0,6]]},"3338":{"position":[[0,6]]}}}],["oper",{"_index":86,"t":{"560":{"position":[[0,9]]},"1663":{"position":[[0,9]]},"2827":{"position":[[0,9]]}}}],["other",{"_index":156,"t":{"2413":{"position":[[15,6]]}}}],["overview",{"_index":59,"t":{"342":{"position":[[7,8]]},"550":{"position":[[15,8]]},"1274":{"position":[[7,8]]},"1524":{"position":[[15,8]]},"2397":{"position":[[7,8]]}}}],["pattern",{"_index":138,"t":{"1506":{"position":[[8,8]]},"2765":{"position":[[8,8]]}}}],["perform",{"_index":89,"t":{"564":{"position":[[7,11]]},"1532":{"position":[[7,11]]},"2737":{"position":[[7,11]]}}}],["permiss",{"_index":144,"t":{"1768":{"position":[[8,10]]},"2927":{"position":[[8,10]]}}}],["person",{"_index":52,"t":{"284":{"position":[[32,8]]}}}],["presenc",{"_index":150,"t":{"2113":{"position":[[7,8]]},"3338":{"position":[[7,8]]}}}],["privat",{"_index":115,"t":{"984":{"position":[[0,7]]}}}],["pro",{"_index":41,"t":{"255":{"position":[[23,4]]},"536":{"position":[[16,3]]},"550":{"position":[[11,3]]},"1524":{"position":[[11,3]]},"1552":{"position":[[16,3]]},"2721":{"position":[[16,3]]},"2757":{"position":[[11,3]]}}}],["protocol",{"_index":135,"t":{"1400":{"position":[[7,8]]},"2145":{"position":[[7,8]]},"3374":{"position":[[7,8]]},"3489":{"position":[[22,8]]}}}],["proxi",{"_index":116,"t":{"1006":{"position":[[0,5]]},"2343":{"position":[[0,5]]},"3360":{"position":[[0,5]]},"3404":{"position":[[0,5]]}}}],["push",{"_index":42,"t":{"255":{"position":[[28,4]]},"1673":{"position":[[0,4]]},"2837":{"position":[[0,4]]}}}],["question",{"_index":64,"t":{"394":{"position":[[17,9]]},"1218":{"position":[[17,9]]},"2467":{"position":[[17,9]]}}}],["quic",{"_index":15,"t":{"91":{"position":[[19,4]]}}}],["quickstart",{"_index":95,"t":{"641":{"position":[[0,10]]},"1504":{"position":[[0,10]]},"2685":{"position":[[0,10]]}}}],["rabbitx",{"_index":46,"t":{"279":{"position":[[20,7]]}}}],["rate",{"_index":159,"t":{"2709":{"position":[[12,4]]},"2827":{"position":[[10,4]]}}}],["real",{"_index":11,"t":{"55":{"position":[[13,4]]},"1062":{"position":[[7,4]]},"1087":{"position":[[0,4]]},"2163":{"position":[[7,4]]},"2182":{"position":[[0,4]]},"3392":{"position":[[7,4]]},"3459":{"position":[[0,4]]}}}],["recoveri",{"_index":112,"t":{"966":{"position":[[12,8]]},"1976":{"position":[[12,8]]},"3135":{"position":[[12,8]]}}}],["redi",{"_index":68,"t":{"464":{"position":[[21,5]]}}}],["releas",{"_index":8,"t":{"23":{"position":[[14,8]]},"117":{"position":[[14,8]]},"310":{"position":[[14,8]]}}}],["revoc",{"_index":93,"t":{"625":{"position":[[6,10]]},"1624":{"position":[[6,10]]},"2811":{"position":[[6,10]]}}}],["revolut",{"_index":19,"t":{"117":{"position":[[34,10]]}}}],["room",{"_index":22,"t":{"153":{"position":[[17,4]]}}}],["rss",{"_index":84,"t":{"558":{"position":[[8,3]]},"1592":{"position":[[8,3]]},"2775":{"position":[[8,3]]}}}],["rueidi",{"_index":73,"t":{"464":{"position":[[76,7]]}}}],["run",{"_index":81,"t":{"536":{"position":[[12,3]]},"1552":{"position":[[12,3]]},"2721":{"position":[[12,3]]}}}],["scalabl",{"_index":130,"t":{"1180":{"position":[[9,11]]},"2077":{"position":[[12,11]]},"3262":{"position":[[12,11]]}}}],["scale",{"_index":3,"t":{"3":{"position":[[0,7]]}}}],["sdk",{"_index":119,"t":{"1062":{"position":[[17,4]]},"2163":{"position":[[17,4]]},"2619":{"position":[[7,3]]},"3392":{"position":[[17,4]]},"3630":{"position":[[7,3]]}}}],["server",{"_index":120,"t":{"1064":{"position":[[0,6]]},"1450":{"position":[[0,6]]},"2127":{"position":[[0,6]]},"2282":{"position":[[0,6]]},"3314":{"position":[[0,6]]},"3330":{"position":[[0,6]]},"3493":{"position":[[0,6]]}}}],["set",{"_index":29,"t":{"207":{"position":[[0,7]]}}}],["showcas",{"_index":66,"t":{"444":{"position":[[11,8]]},"1296":{"position":[[11,8]]},"2583":{"position":[[11,8]]}}}],["side",{"_index":121,"t":{"1064":{"position":[[7,4]]},"2127":{"position":[[7,4]]},"3330":{"position":[[7,4]]}}}],["singleflight",{"_index":94,"t":{"639":{"position":[[0,12]]},"1590":{"position":[[0,12]]},"2783":{"position":[[0,12]]}}}],["sockj",{"_index":123,"t":{"1072":{"position":[[0,6]]},"2192":{"position":[[0,6]]},"3467":{"position":[[0,6]]}}}],["sse",{"_index":126,"t":{"1095":{"position":[[15,3]]},"2207":{"position":[[0,3]]},"2231":{"position":[[15,3]]},"3482":{"position":[[0,3]]},"3581":{"position":[[15,3]]}}}],["sso",{"_index":32,"t":{"207":{"position":[[20,3]]}}}],["stat",{"_index":85,"t":{"558":{"position":[[12,5]]},"1592":{"position":[[12,5]]},"2775":{"position":[[12,5]]}}}],["state",{"_index":157,"t":{"2707":{"position":[[8,5]]}}}],["statu",{"_index":102,"t":{"683":{"position":[[5,6]]},"1726":{"position":[[5,6]]},"2915":{"position":[[5,6]]}}}],["stream",{"_index":36,"t":{"217":{"position":[[21,9]]},"1163":{"position":[[20,9]]},"2175":{"position":[[5,10]]},"2214":{"position":[[20,9]]},"3360":{"position":[[19,7]]},"3452":{"position":[[5,10]]},"3564":{"position":[[20,9]]}}}],["subscrib",{"_index":50,"t":{"284":{"position":[[12,9]]}}}],["subscript",{"_index":122,"t":{"1064":{"position":[[12,13]]},"2127":{"position":[[12,13]]},"3330":{"position":[[12,13]]},"3360":{"position":[[6,12]]}}}],["throttl",{"_index":87,"t":{"560":{"position":[[10,10]]},"1663":{"position":[[10,10]]}}}],["throughput",{"_index":70,"t":{"464":{"position":[[34,10]]}}}],["time",{"_index":12,"t":{"55":{"position":[[18,4]]},"1062":{"position":[[12,4]]},"1087":{"position":[[5,4]]},"2163":{"position":[[12,4]]},"2182":{"position":[[5,4]]},"3392":{"position":[[12,4]]},"3459":{"position":[[5,4]]}}}],["tl",{"_index":118,"t":{"1052":{"position":[[10,3]]},"2135":{"position":[[10,3]]},"3350":{"position":[[10,3]]}}}],["token",{"_index":92,"t":{"625":{"position":[[0,5]]},"1624":{"position":[[0,5]]},"2811":{"position":[[0,5]]}}}],["trace",{"_index":91,"t":{"621":{"position":[[17,7]]},"1620":{"position":[[17,7]]},"2807":{"position":[[17,7]]}}}],["transport",{"_index":124,"t":{"1087":{"position":[[10,10]]},"2182":{"position":[[10,10]]},"3459":{"position":[[10,10]]}}}],["tune",{"_index":109,"t":{"946":{"position":[[15,6]]},"1916":{"position":[[15,6]]},"3143":{"position":[[15,6]]}}}],["tutori",{"_index":39,"t":{"241":{"position":[[35,8]]},"641":{"position":[[11,8]]},"1504":{"position":[[11,8]]},"2685":{"position":[[11,8]]}}}],["ui",{"_index":99,"t":{"661":{"position":[[10,2]]},"1655":{"position":[[10,2]]},"2907":{"position":[[10,2]]}}}],["unidirect",{"_index":125,"t":{"1095":{"position":[[0,14]]},"1110":{"position":[[0,14]]},"1131":{"position":[[0,14]]},"1163":{"position":[[0,14]]},"2214":{"position":[[0,14]]},"2231":{"position":[[0,14]]},"2244":{"position":[[0,14]]},"2261":{"position":[[0,14]]},"3489":{"position":[[0,14]]},"3543":{"position":[[0,14]]},"3564":{"position":[[0,14]]},"3581":{"position":[[0,14]]},"3594":{"position":[[0,14]]}}}],["up",{"_index":30,"t":{"207":{"position":[[8,2]]}}}],["us",{"_index":45,"t":{"279":{"position":[[0,5]]}}}],["user",{"_index":51,"t":{"284":{"position":[[22,4]]},"621":{"position":[[0,4]]},"645":{"position":[[0,4]]},"659":{"position":[[0,4]]},"683":{"position":[[0,4]]},"1620":{"position":[[0,4]]},"1640":{"position":[[0,4]]},"1726":{"position":[[0,4]]},"2807":{"position":[[0,4]]},"2892":{"position":[[0,4]]},"2915":{"position":[[0,4]]}}}],["v3",{"_index":7,"t":{"23":{"position":[[11,2]]},"584":{"position":[[13,2]]}}}],["v4",{"_index":17,"t":{"117":{"position":[[11,2]]},"1356":{"position":[[13,2]]},"2527":{"position":[[13,2]]}}}],["v5",{"_index":55,"t":{"310":{"position":[[11,2]]},"2571":{"position":[[13,2]]}}}],["version",{"_index":82,"t":{"536":{"position":[[20,7]]},"1552":{"position":[[20,7]]},"2721":{"position":[[20,7]]}}}],["walkthrough",{"_index":153,"t":{"2282":{"position":[[11,11]]},"3493":{"position":[[11,11]]}}}],["way",{"_index":49,"t":{"284":{"position":[[4,4]]}}}],["web",{"_index":98,"t":{"661":{"position":[[6,3]]},"1655":{"position":[[6,3]]},"2907":{"position":[[6,3]]}}}],["websocket",{"_index":4,"t":{"3":{"position":[[8,9]]},"207":{"position":[[73,9]]},"1131":{"position":[[15,9]]},"1148":{"position":[[0,9]]},"2244":{"position":[[15,9]]},"2328":{"position":[[0,9]]},"3594":{"position":[[15,9]]},"3613":{"position":[[0,9]]}}}],["webtransport",{"_index":16,"t":{"91":{"position":[[28,12]]},"2391":{"position":[[0,12]]},"3611":{"position":[[0,12]]}}}]],"pipeline":["stemmer"]}},{"documents":[{"i":5,"t":"WebSocket server tasks","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-server-tasks","p":3},{"i":7,"t":"WebSocket libraries","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-libraries","p":3},{"i":9,"t":"OS tuning","u":"/blog/2020/11/12/scaling-websocket","h":"#os-tuning","p":3},{"i":11,"t":"Sending many messages","u":"/blog/2020/11/12/scaling-websocket","h":"#sending-many-messages","p":3},{"i":13,"t":"WebSocket fallback transport","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-fallback-transport","p":3},{"i":15,"t":"Performance is not scalability","u":"/blog/2020/11/12/scaling-websocket","h":"#performance-is-not-scalability","p":3},{"i":17,"t":"Massive reconnect","u":"/blog/2020/11/12/scaling-websocket","h":"#massive-reconnect","p":3},{"i":19,"t":"Message event stream benefits","u":"/blog/2020/11/12/scaling-websocket","h":"#message-event-stream-benefits","p":3},{"i":21,"t":"Conclusion","u":"/blog/2020/11/12/scaling-websocket","h":"#conclusion","p":3},{"i":25,"t":"Centrifugo v2 flashbacks","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-v2-flashbacks","p":23},{"i":27,"t":"Backwards compatibility","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#backwards-compatibility","p":23},{"i":29,"t":"License change","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#license-change","p":23},{"i":31,"t":"Unidirectional real-time transports","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#unidirectional-real-time-transports","p":23},{"i":33,"t":"History iteration API","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#history-iteration-api","p":23},{"i":35,"t":"Redis Streams by default","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#redis-streams-by-default","p":23},{"i":37,"t":"Tarantool engine","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#tarantool-engine","p":23},{"i":39,"t":"GRPC proxy","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#grpc-proxy","p":23},{"i":41,"t":"Server API improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#server-api-improvements","p":23},{"i":43,"t":"Better clustering","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#better-clustering","p":23},{"i":45,"t":"Client improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#client-improvements","p":23},{"i":47,"t":"New documentation site","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#new-documentation-site","p":23},{"i":49,"t":"Performance improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#performance-improvements","p":23},{"i":51,"t":"Centrifugo PRO","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-pro","p":23},{"i":53,"t":"Conclusion","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#conclusion","p":23},{"i":57,"t":"How it's all started","u":"/blog/2021/01/15/centrifuge-intro","h":"#how-its-all-started","p":55},{"i":59,"t":"So what is Centrifuge?","u":"/blog/2021/01/15/centrifuge-intro","h":"#so-what-is-centrifuge","p":55},{"i":61,"t":"Centrifuge Node","u":"/blog/2021/01/15/centrifuge-intro","h":"#centrifuge-node","p":55},{"i":63,"t":"Authentication","u":"/blog/2021/01/15/centrifuge-intro","h":"#authentication","p":55},{"i":65,"t":"Channel subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#channel-subscriptions","p":55},{"i":67,"t":"Async message passing","u":"/blog/2021/01/15/centrifuge-intro","h":"#async-message-passing","p":55},{"i":69,"t":"RPC","u":"/blog/2021/01/15/centrifuge-intro","h":"#rpc","p":55},{"i":71,"t":"Server-side subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#server-side-subscriptions","p":55},{"i":73,"t":"Windowed history in channel","u":"/blog/2021/01/15/centrifuge-intro","h":"#windowed-history-in-channel","p":55},{"i":75,"t":"Online presence and presence stats","u":"/blog/2021/01/15/centrifuge-intro","h":"#online-presence-and-presence-stats","p":55},{"i":77,"t":"Scalability aspects","u":"/blog/2021/01/15/centrifuge-intro","h":"#scalability-aspects","p":55},{"i":79,"t":"Order and delivery properties","u":"/blog/2021/01/15/centrifuge-intro","h":"#order-and-delivery-properties","p":55},{"i":81,"t":"Ecosystem","u":"/blog/2021/01/15/centrifuge-intro","h":"#ecosystem","p":55},{"i":83,"t":"Performance","u":"/blog/2021/01/15/centrifuge-intro","h":"#performance","p":55},{"i":85,"t":"Limitations","u":"/blog/2021/01/15/centrifuge-intro","h":"#limitations","p":55},{"i":87,"t":"Examples","u":"/blog/2021/01/15/centrifuge-intro","h":"#examples","p":55},{"i":89,"t":"Conclusion","u":"/blog/2021/01/15/centrifuge-intro","h":"#conclusion","p":55},{"i":93,"t":"Overview","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#overview","p":91},{"i":95,"t":"Install Chrome Canary","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#install-chrome-canary","p":91},{"i":97,"t":"Generate self-signed TLS certificates","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#generate-self-signed-tls-certificates","p":91},{"i":99,"t":"Run client example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#run-client-example","p":91},{"i":101,"t":"Writing a QUIC server","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#writing-a-quic-server","p":91},{"i":103,"t":"Server skeleton","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#server-skeleton","p":91},{"i":105,"t":"Accept QUIC connections","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#accept-quic-connections","p":91},{"i":107,"t":"Connection Session handling","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#connection-session-handling","p":91},{"i":109,"t":"Parsing client indication","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#parsing-client-indication","p":91},{"i":111,"t":"Communicating over bidirectional streams","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#communicating-over-bidirectional-streams","p":91},{"i":113,"t":"Full server example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#full-server-example","p":91},{"i":115,"t":"Conclusion","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#conclusion","p":91},{"i":119,"t":"Centrifugo v3 flashbacks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#centrifugo-v3-flashbacks","p":117},{"i":121,"t":"Unified client SDK API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#unified-client-sdk-api","p":117},{"i":123,"t":"Modern WebSocket emulation in Javascript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#modern-websocket-emulation-in-javascript","p":117},{"i":125,"t":"No layering in client protocol","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#no-layering-in-client-protocol","p":117},{"i":127,"t":"Redesigned PING-PONG","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#redesigned-ping-pong","p":117},{"i":129,"t":"Secure by default channel namespaces","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#secure-by-default-channel-namespaces","p":117},{"i":131,"t":"Private channel concept revised","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#private-channel-concept-revised","p":117},{"i":133,"t":"Optimistic subscriptions","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#optimistic-subscriptions","p":117},{"i":135,"t":"Channel capabilities","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#channel-capabilities","p":117},{"i":137,"t":"Better connections API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#better-connections-api","p":117},{"i":139,"t":"Javascript client moved to TypeScript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#javascript-client-moved-to-typescript","p":117},{"i":141,"t":"Experimenting with HTTP/3","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-http3","p":117},{"i":143,"t":"Experimenting with WebTransport","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-webtransport","p":117},{"i":145,"t":"Migration guide","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#migration-guide","p":117},{"i":147,"t":"Conclusion","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#conclusion","p":117},{"i":149,"t":"Join community","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#join-community","p":117},{"i":151,"t":"Special thanks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#special-thanks","p":117},{"i":155,"t":"Application overview","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-overview","p":153},{"i":157,"t":"Why integrate Laravel with Centrifugo?","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#why-integrate-laravel-with-centrifugo","p":153},{"i":159,"t":"Setup and start a project","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#setup-and-start-a-project","p":153},{"i":161,"t":"Application structure","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-structure","p":153},{"i":163,"t":"Environment settings","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#environment-settings","p":153},{"i":165,"t":"Database migrations and models","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#database-migrations-and-models","p":153},{"i":167,"t":"Broadcasting","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#broadcasting","p":153},{"i":169,"t":"Interaction with Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#interaction-with-centrifugo","p":153},{"i":171,"t":"Connect proxy controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#connect-proxy-controller","p":153},{"i":173,"t":"Room controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#room-controller","p":153},{"i":175,"t":"Client side","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#client-side","p":153},{"i":177,"t":"Possible improvements","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#possible-improvements","p":153},{"i":179,"t":"Conclusion","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#conclusion","p":153},{"i":183,"t":"Why integrate Django with Centrifugo","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#why-integrate-django-with-centrifugo","p":181},{"i":185,"t":"Prerequisites","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#prerequisites","p":181},{"i":187,"t":"Creating a project","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-a-project","p":181},{"i":189,"t":"Creating the chat app","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-the-chat-app","p":181},{"i":191,"t":"Add the index view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-index-view","p":181},{"i":193,"t":"Add the room view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-room-view","p":181},{"i":195,"t":"Starting Centrifugo server","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#starting-centrifugo-server","p":181},{"i":197,"t":"Adding Nginx","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#adding-nginx","p":181},{"i":199,"t":"Implementing proxy handlers","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#implementing-proxy-handlers","p":181},{"i":201,"t":"What could be improved","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#what-could-be-improved","p":181},{"i":203,"t":"Tutorial source code with docker-compose","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#tutorial-source-code-with-docker-compose","p":181},{"i":205,"t":"Conclusion","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#conclusion","p":181},{"i":209,"t":"TLDR","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#tldr","p":207},{"i":211,"t":"Keycloak","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#keycloak","p":207},{"i":213,"t":"Centrifugo","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#centrifugo","p":207},{"i":215,"t":"React app with Vite","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#react-app-with-vite","p":207},{"i":219,"t":"Start Centrifugo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#start-centrifugo","p":217},{"i":221,"t":"Install and run Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#install-and-run-benthos","p":217},{"i":223,"t":"Configure Benthos input and output","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#configure-benthos-input-and-output","p":217},{"i":225,"t":"Push messages to Redis queue","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#push-messages-to-redis-queue","p":217},{"i":227,"t":"Demo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#demo","p":217},{"i":229,"t":"Pitfalls of async publishing","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#pitfalls-of-async-publishing","p":217},{"i":231,"t":"Late delivery","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#late-delivery","p":217},{"i":233,"t":"Ordering concerns","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#ordering-concerns","p":217},{"i":235,"t":"Throughput when ordering preserved","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#throughput-when-ordering-preserved","p":217},{"i":237,"t":"Error handling","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#error-handling","p":217},{"i":239,"t":"Conclusion","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#conclusion","p":217},{"i":243,"t":"What we are building","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#what-we-are-building","p":241},{"i":245,"t":"Creating Express.js app","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#creating-expressjs-app","p":241},{"i":247,"t":"Starting Centrifugo","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#starting-centrifugo","p":241},{"i":249,"t":"Adding Nginx","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#adding-nginx","p":241},{"i":251,"t":"Send real-time messages","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#send-real-time-messages","p":241},{"i":253,"t":"Conclusion","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#conclusion","p":241},{"i":257,"t":"Centrifugo PRO goals","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#centrifugo-pro-goals","p":255},{"i":259,"t":"Why providing push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#why-providing-push-notifications-api","p":255},{"i":261,"t":"Push notifications API like no one provides","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-api-like-no-one-provides","p":255},{"i":263,"t":"Frontend decisions","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#frontend-decisions","p":255},{"i":265,"t":"Server implementation","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#server-implementation","p":255},{"i":267,"t":"How we keep tokens","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-keep-tokens","p":255},{"i":269,"t":"How we send notifications","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-send-notifications","p":255},{"i":271,"t":"Secure unified topics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#secure-unified-topics","p":255},{"i":273,"t":"Push analytics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-analytics","p":255},{"i":275,"t":"Push notifications UI","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-ui","p":255},{"i":277,"t":"Conclusion","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#conclusion","p":255},{"i":286,"t":"Setup","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#setup","p":284},{"i":288,"t":"#1 – user-limited channel","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#1--user-limited-channel","p":284},{"i":290,"t":"#2 - channel token authorization","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#2---channel-token-authorization","p":284},{"i":292,"t":"#3 - subscribe proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#3---subscribe-proxy","p":284},{"i":294,"t":"#4 - server-side channel in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#4---server-side-channel-in-connection-jwt","p":284},{"i":296,"t":"#5 - server-side channel in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#5---server-side-channel-in-connect-proxy","p":284},{"i":298,"t":"#6 - automatic personal channel subscription","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#6---automatic-personal-channel-subscription","p":284},{"i":300,"t":"#7 – capabilities in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#7--capabilities-in-connection-jwt","p":284},{"i":302,"t":"#8 – capabilities in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#8--capabilities-in-connect-proxy","p":284},{"i":304,"t":"Teardown","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#teardown","p":284},{"i":308,"t":"Landing Page Images","u":"/docs/3/attributions","h":"#landing-page-images","p":306},{"i":312,"t":"Dropping old client protocol","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#dropping-old-client-protocol","p":310},{"i":314,"t":"Token behaviour adjustments in SDKs","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#token-behaviour-adjustments-in-sdks","p":310},{"i":316,"t":"history_meta_ttl refactoring","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#history_meta_ttl-refactoring","p":310},{"i":318,"t":"Node communication protocol update","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#node-communication-protocol-update","p":310},{"i":320,"t":"New HTTP API format","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#new-http-api-format","p":310},{"i":322,"t":"OpenAPI spec and Swagger UI","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#openapi-spec-and-swagger-ui","p":310},{"i":324,"t":"OpenTelemetry for server API","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#opentelemetry-for-server-api","p":310},{"i":326,"t":"Separate config for subscription token","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#separate-config-for-subscription-token","p":310},{"i":328,"t":"Unknown config keys warnings","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#unknown-config-keys-warnings","p":310},{"i":330,"t":"Simplifying protocol debug with Postman","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#simplifying-protocol-debug-with-postman","p":310},{"i":332,"t":"The future of SockJS","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#the-future-of-sockjs","p":310},{"i":334,"t":"Introducing Centrifugal Labs LTD","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#introducing-centrifugal-labs-ltd","p":310},{"i":336,"t":"Conclusion","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#conclusion","p":310},{"i":344,"t":"Idiomatic usage","u":"/docs/3/getting-started/design","h":"#idiomatic-usage","p":342},{"i":346,"t":"Message history considerations","u":"/docs/3/getting-started/design","h":"#message-history-considerations","p":342},{"i":348,"t":"Message delivery model","u":"/docs/3/getting-started/design","h":"#message-delivery-model","p":342},{"i":350,"t":"Message order guarantees","u":"/docs/3/getting-started/design","h":"#message-order-guarantees","p":342},{"i":352,"t":"Graceful degradation","u":"/docs/3/getting-started/design","h":"#graceful-degradation","p":342},{"i":354,"t":"Online presence considerations","u":"/docs/3/getting-started/design","h":"#online-presence-considerations","p":342},{"i":356,"t":"Scalability considerations","u":"/docs/3/getting-started/design","h":"#scalability-considerations","p":342},{"i":360,"t":"Simple integration","u":"/docs/3/getting-started/highlights","h":"#simple-integration","p":358},{"i":362,"t":"Great performance","u":"/docs/3/getting-started/highlights","h":"#great-performance","p":358},{"i":364,"t":"Built-in scalability","u":"/docs/3/getting-started/highlights","h":"#built-in-scalability","p":358},{"i":366,"t":"Strict client protocol","u":"/docs/3/getting-started/highlights","h":"#strict-client-protocol","p":358},{"i":368,"t":"Variety of real-time transports","u":"/docs/3/getting-started/highlights","h":"#variety-of-real-time-transports","p":358},{"i":370,"t":"Flexible authentication","u":"/docs/3/getting-started/highlights","h":"#flexible-authentication","p":358},{"i":372,"t":"Connection management","u":"/docs/3/getting-started/highlights","h":"#connection-management","p":358},{"i":374,"t":"Channel (room) concept","u":"/docs/3/getting-started/highlights","h":"#channel-room-concept","p":358},{"i":376,"t":"Different types of subscriptions","u":"/docs/3/getting-started/highlights","h":"#different-types-of-subscriptions","p":358},{"i":378,"t":"RPC over bidirectional connection","u":"/docs/3/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":358},{"i":380,"t":"Online presence information","u":"/docs/3/getting-started/highlights","h":"#online-presence-information","p":358},{"i":382,"t":"Message history in channels","u":"/docs/3/getting-started/highlights","h":"#message-history-in-channels","p":358},{"i":384,"t":"Embedded admin web UI","u":"/docs/3/getting-started/highlights","h":"#embedded-admin-web-ui","p":358},{"i":386,"t":"Cross-platform","u":"/docs/3/getting-started/highlights","h":"#cross-platform","p":358},{"i":388,"t":"Ready to deploy","u":"/docs/3/getting-started/highlights","h":"#ready-to-deploy","p":358},{"i":390,"t":"Open-source","u":"/docs/3/getting-started/highlights","h":"#open-source","p":358},{"i":392,"t":"Pro features","u":"/docs/3/getting-started/highlights","h":"#pro-features","p":358},{"i":396,"t":"How many connections can one Centrifugo instance handle?","u":"/docs/3/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":394},{"i":398,"t":"Memory usage per connection?","u":"/docs/3/faq","h":"#memory-usage-per-connection","p":394},{"i":400,"t":"Can Centrifugo scale horizontally?","u":"/docs/3/faq","h":"#can-centrifugo-scale-horizontally","p":394},{"i":402,"t":"Message delivery model","u":"/docs/3/faq","h":"#message-delivery-model","p":394},{"i":404,"t":"Message order guarantees","u":"/docs/3/faq","h":"#message-order-guarantees","p":394},{"i":406,"t":"Should I create channels explicitly?","u":"/docs/3/faq","h":"#should-i-create-channels-explicitly","p":394},{"i":408,"t":"What about best practices with the number of channels?","u":"/docs/3/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":394},{"i":410,"t":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/3/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":394},{"i":412,"t":"Can I have both binary and JSON clients in one channel?","u":"/docs/3/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":394},{"i":414,"t":"Online presence for chat apps - online status of your contacts","u":"/docs/3/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":394},{"i":416,"t":"Centrifugo stops accepting new connections, why?","u":"/docs/3/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":394},{"i":418,"t":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/3/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":394},{"i":420,"t":"Does Centrifugo work with HTTP/2?","u":"/docs/3/faq","h":"#does-centrifugo-work-with-http2","p":394},{"i":422,"t":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/3/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":394},{"i":424,"t":"What if I need to send push notifications to mobile or web applications?","u":"/docs/3/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":394},{"i":426,"t":"How can I know a message is delivered to a client?","u":"/docs/3/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":394},{"i":428,"t":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/3/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":394},{"i":430,"t":"How to create a secure channel for two users only (private chat case)?","u":"/docs/3/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":394},{"i":432,"t":"What's the best way to organize channel configuration?","u":"/docs/3/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":394},{"i":434,"t":"Does Centrifugo support webhooks?","u":"/docs/3/faq","h":"#does-centrifugo-support-webhooks","p":394},{"i":436,"t":"Why Centrifugo does not have disconnect hooks?","u":"/docs/3/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":394},{"i":438,"t":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/3/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":394},{"i":440,"t":"How scalable is the online presence and join/leave features?","u":"/docs/3/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":394},{"i":442,"t":"I have not found an answer to my question here:","u":"/docs/3/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":394},{"i":446,"t":"Connecting to a server","u":"/docs/3/getting-started/client_api","h":"#connecting-to-a-server","p":444},{"i":448,"t":"Disconnecting from a server","u":"/docs/3/getting-started/client_api","h":"#disconnecting-from-a-server","p":444},{"i":450,"t":"Reconnecting to a server","u":"/docs/3/getting-started/client_api","h":"#reconnecting-to-a-server","p":444},{"i":452,"t":"Connection lifecycle events","u":"/docs/3/getting-started/client_api","h":"#connection-lifecycle-events","p":444},{"i":454,"t":"Subscribe to a channel","u":"/docs/3/getting-started/client_api","h":"#subscribe-to-a-channel","p":444},{"i":456,"t":"Server-side subscriptions","u":"/docs/3/getting-started/client_api","h":"#server-side-subscriptions","p":444},{"i":458,"t":"Send RPC","u":"/docs/3/getting-started/client_api","h":"#send-rpc","p":444},{"i":460,"t":"Call channel history","u":"/docs/3/getting-started/client_api","h":"#call-channel-history","p":444},{"i":462,"t":"Presence and presence stats","u":"/docs/3/getting-started/client_api","h":"#presence-and-presence-stats","p":444},{"i":466,"t":"Broker and PresenceManager","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#broker-and-presencemanager","p":464},{"i":468,"t":"Redigo","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo","p":464},{"i":470,"t":"Redigo with pipelining","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo-with-pipelining","p":464},{"i":472,"t":"Motivation to migrate","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#motivation-to-migrate","p":464},{"i":474,"t":"Go-redis/redis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#go-redisredis","p":464},{"i":476,"t":"Rueidis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#rueidis","p":464},{"i":478,"t":"Switching to Rueidis: reducing CPU usage","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#switching-to-rueidis-reducing-cpu-usage","p":464},{"i":480,"t":"Adding latency","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#adding-latency","p":464},{"i":482,"t":"Conclusion","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#conclusion","p":464},{"i":486,"t":"0. Install","u":"/docs/3/getting-started/integration","h":"#0-install","p":484},{"i":488,"t":"1. Configure Centrifugo","u":"/docs/3/getting-started/integration","h":"#1-configure-centrifugo","p":484},{"i":490,"t":"2. Configure your backend","u":"/docs/3/getting-started/integration","h":"#2-configure-your-backend","p":484},{"i":492,"t":"3. Connect to Centrifugo","u":"/docs/3/getting-started/integration","h":"#3-connect-to-centrifugo","p":484},{"i":494,"t":"4. Subscribe to channels","u":"/docs/3/getting-started/integration","h":"#4-subscribe-to-channels","p":484},{"i":496,"t":"5. Publish to channel","u":"/docs/3/getting-started/integration","h":"#5-publish-to-channel","p":484},{"i":498,"t":"6. Deploy to production","u":"/docs/3/getting-started/integration","h":"#6-deploy-to-production","p":484},{"i":500,"t":"7. Monitor Centrifugo","u":"/docs/3/getting-started/integration","h":"#7-monitor-centrifugo","p":484},{"i":502,"t":"8. Scale Centrifugo","u":"/docs/3/getting-started/integration","h":"#8-scale-centrifugo","p":484},{"i":504,"t":"9. Read FAQ","u":"/docs/3/getting-started/integration","h":"#9-read-faq","p":484},{"i":508,"t":"Install from the binary release","u":"/docs/3/getting-started/installation","h":"#install-from-the-binary-release","p":506},{"i":510,"t":"Docker image","u":"/docs/3/getting-started/installation","h":"#docker-image","p":506},{"i":512,"t":"Docker-compose example","u":"/docs/3/getting-started/installation","h":"#docker-compose-example","p":506},{"i":514,"t":"Kubernetes Helm chart","u":"/docs/3/getting-started/installation","h":"#kubernetes-helm-chart","p":506},{"i":516,"t":"RPM and DEB packages for Linux","u":"/docs/3/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":506},{"i":518,"t":"With brew on macOS","u":"/docs/3/getting-started/installation","h":"#with-brew-on-macos","p":506},{"i":520,"t":"Build from source","u":"/docs/3/getting-started/installation","h":"#build-from-source","p":506},{"i":524,"t":"Motivation","u":"/docs/3/getting-started/introduction","h":"#motivation","p":522},{"i":526,"t":"Concepts","u":"/docs/3/getting-started/introduction","h":"#concepts","p":522},{"i":528,"t":"Join community","u":"/docs/3/getting-started/introduction","h":"#join-community","p":522},{"i":532,"t":"How it works","u":"/docs/3/pro/db_namespaces","h":"#how-it-works","p":530},{"i":534,"t":"Configuration","u":"/docs/3/pro/db_namespaces","h":"#configuration","p":530},{"i":538,"t":"Binary release","u":"/docs/3/pro/install_and_run","h":"#binary-release","p":536},{"i":540,"t":"Docker image","u":"/docs/3/pro/install_and_run","h":"#docker-image","p":536},{"i":542,"t":"Kubernetes","u":"/docs/3/pro/install_and_run","h":"#kubernetes","p":536},{"i":544,"t":"Debian and Ubuntu","u":"/docs/3/pro/install_and_run","h":"#debian-and-ubuntu","p":536},{"i":546,"t":"Centos","u":"/docs/3/pro/install_and_run","h":"#centos","p":536},{"i":548,"t":"Setting PRO license key","u":"/docs/3/pro/install_and_run","h":"#setting-pro-license-key","p":536},{"i":552,"t":"Features","u":"/docs/3/pro/overview","h":"#features","p":550},{"i":554,"t":"Sandbox mode","u":"/docs/3/pro/overview","h":"#sandbox-mode","p":550},{"i":556,"t":"Pricing","u":"/docs/3/pro/overview","h":"#pricing","p":550},{"i":562,"t":"Redis throttling","u":"/docs/3/pro/throttling","h":"#redis-throttling","p":560},{"i":566,"t":"Faster HTTP API","u":"/docs/3/pro/performance","h":"#faster-http-api","p":564},{"i":568,"t":"Faster GRPC API","u":"/docs/3/pro/performance","h":"#faster-grpc-api","p":564},{"i":570,"t":"Faster HTTP proxy","u":"/docs/3/pro/performance","h":"#faster-http-proxy","p":564},{"i":572,"t":"Faster GRPC proxy","u":"/docs/3/pro/performance","h":"#faster-grpc-proxy","p":564},{"i":574,"t":"Faster JWT decoding","u":"/docs/3/pro/performance","h":"#faster-jwt-decoding","p":564},{"i":576,"t":"Faster GRPC unidirectional stream","u":"/docs/3/pro/performance","h":"#faster-grpc-unidirectional-stream","p":564},{"i":578,"t":"Examples","u":"/docs/3/pro/performance","h":"#examples","p":564},{"i":580,"t":"Publish HTTP API","u":"/docs/3/pro/performance","h":"#publish-http-api","p":564},{"i":582,"t":"History HTTP API","u":"/docs/3/pro/performance","h":"#history-http-api","p":564},{"i":586,"t":"Client-side changes","u":"/docs/3/getting-started/migration_v3","h":"#client-side-changes","p":584},{"i":588,"t":"No unlimited history by default","u":"/docs/3/getting-started/migration_v3","h":"#no-unlimited-history-by-default","p":584},{"i":590,"t":"Publication limit for recovery","u":"/docs/3/getting-started/migration_v3","h":"#publication-limit-for-recovery","p":584},{"i":592,"t":"Seq/Gen fields removed","u":"/docs/3/getting-started/migration_v3","h":"#seqgen-fields-removed","p":584},{"i":594,"t":"Server-side changes","u":"/docs/3/getting-started/migration_v3","h":"#server-side-changes","p":584},{"i":595,"t":"Time interval options are duration","u":"/docs/3/getting-started/migration_v3","h":"#time-interval-options-are-duration","p":584},{"i":597,"t":"Channel options changes","u":"/docs/3/getting-started/migration_v3","h":"#channel-options-changes","p":584},{"i":599,"t":"Some command-line flags removed","u":"/docs/3/getting-started/migration_v3","h":"#some-command-line-flags-removed","p":584},{"i":601,"t":"Enforced request Origin check","u":"/docs/3/getting-started/migration_v3","h":"#enforced-request-origin-check","p":584},{"i":603,"t":"Updated GRPC API Protobuf package","u":"/docs/3/getting-started/migration_v3","h":"#updated-grpc-api-protobuf-package","p":584},{"i":605,"t":"Channels API method changed","u":"/docs/3/getting-started/migration_v3","h":"#channels-api-method-changed","p":584},{"i":607,"t":"HTTP proxy changes","u":"/docs/3/getting-started/migration_v3","h":"#http-proxy-changes","p":584},{"i":609,"t":"JWT changes","u":"/docs/3/getting-started/migration_v3","h":"#jwt-changes","p":584},{"i":611,"t":"Redis configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#redis-configuration-changes","p":584},{"i":613,"t":"Redis streams used by default","u":"/docs/3/getting-started/migration_v3","h":"#redis-streams-used-by-default","p":584},{"i":615,"t":"SockJS disabled by default","u":"/docs/3/getting-started/migration_v3","h":"#sockjs-disabled-by-default","p":584},{"i":617,"t":"Other configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#other-configuration-changes","p":584},{"i":619,"t":"v2 to v3 config converter","u":"/docs/3/getting-started/migration_v3","h":"#v2-to-v3-config-converter","p":584},{"i":623,"t":"Save to a file","u":"/docs/3/pro/tracing","h":"#save-to-a-file","p":621},{"i":627,"t":"How it works","u":"/docs/3/pro/token_revocation","h":"#how-it-works","p":625},{"i":629,"t":"Configure","u":"/docs/3/pro/token_revocation","h":"#configure","p":625},{"i":631,"t":"Redis persistence engine","u":"/docs/3/pro/token_revocation","h":"#redis-persistence-engine","p":625},{"i":633,"t":"Database persistence engine","u":"/docs/3/pro/token_revocation","h":"#database-persistence-engine","p":625},{"i":635,"t":"Revoke token API","u":"/docs/3/pro/token_revocation","h":"#revoke-token-api","p":625},{"i":637,"t":"Invalidate user tokens API","u":"/docs/3/pro/token_revocation","h":"#invalidate-user-tokens-api","p":625},{"i":643,"t":"More examples","u":"/docs/3/getting-started/quickstart","h":"#more-examples","p":641},{"i":647,"t":"How it works","u":"/docs/3/pro/user_block","h":"#how-it-works","p":645},{"i":649,"t":"Configure","u":"/docs/3/pro/user_block","h":"#configure","p":645},{"i":651,"t":"Redis persistence engine","u":"/docs/3/pro/user_block","h":"#redis-persistence-engine","p":645},{"i":653,"t":"Database persistence engine","u":"/docs/3/pro/user_block","h":"#database-persistence-engine","p":645},{"i":655,"t":"Block user API","u":"/docs/3/pro/user_block","h":"#block-user-api","p":645},{"i":657,"t":"Unblock user API","u":"/docs/3/pro/user_block","h":"#unblock-user-api","p":645},{"i":663,"t":"Options","u":"/docs/3/server/admin_web","h":"#options","p":661},{"i":665,"t":"Using custom web interface","u":"/docs/3/server/admin_web","h":"#using-custom-web-interface","p":661},{"i":667,"t":"Admin insecure mode","u":"/docs/3/server/admin_web","h":"#admin-insecure-mode","p":661},{"i":671,"t":"Configuration","u":"/docs/3/pro/analytics","h":"#configuration","p":669},{"i":673,"t":"Connections table","u":"/docs/3/pro/analytics","h":"#connections-table","p":669},{"i":675,"t":"Operations table","u":"/docs/3/pro/analytics","h":"#operations-table","p":669},{"i":677,"t":"Query examples","u":"/docs/3/pro/analytics","h":"#query-examples","p":669},{"i":679,"t":"Development","u":"/docs/3/pro/analytics","h":"#development","p":669},{"i":681,"t":"How export works","u":"/docs/3/pro/analytics","h":"#how-export-works","p":669},{"i":685,"t":"Client-side status update RPC","u":"/docs/3/pro/user_status","h":"#client-side-status-update-rpc","p":683},{"i":687,"t":"update_user_status server API","u":"/docs/3/pro/user_status","h":"#update_user_status-server-api","p":683},{"i":689,"t":"get_user_status server API","u":"/docs/3/pro/user_status","h":"#get_user_status-server-api","p":683},{"i":691,"t":"delete_user_status server API","u":"/docs/3/pro/user_status","h":"#delete_user_status-server-api","p":683},{"i":693,"t":"Configuration","u":"/docs/3/pro/user_status","h":"#configuration","p":683},{"i":697,"t":"Client error codes","u":"/docs/3/server/codes","h":"#client-error-codes","p":695},{"i":699,"t":"Internal","u":"/docs/3/server/codes","h":"#internal","p":695},{"i":701,"t":"Unauthorized","u":"/docs/3/server/codes","h":"#unauthorized","p":695},{"i":703,"t":"Unknown Channel","u":"/docs/3/server/codes","h":"#unknown-channel","p":695},{"i":705,"t":"Permission Denied","u":"/docs/3/server/codes","h":"#permission-denied","p":695},{"i":707,"t":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found","p":695},{"i":709,"t":"Already Subscribed","u":"/docs/3/server/codes","h":"#already-subscribed","p":695},{"i":711,"t":"Limit Exceeded","u":"/docs/3/server/codes","h":"#limit-exceeded","p":695},{"i":713,"t":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request","p":695},{"i":715,"t":"Not Available","u":"/docs/3/server/codes","h":"#not-available","p":695},{"i":717,"t":"Token Expired","u":"/docs/3/server/codes","h":"#token-expired","p":695},{"i":719,"t":"Expired","u":"/docs/3/server/codes","h":"#expired","p":695},{"i":721,"t":"Too Many Requests","u":"/docs/3/server/codes","h":"#too-many-requests","p":695},{"i":723,"t":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position","p":695},{"i":725,"t":"Client disconnect codes","u":"/docs/3/server/codes","h":"#client-disconnect-codes","p":695},{"i":727,"t":"Normal","u":"/docs/3/server/codes","h":"#normal","p":695},{"i":729,"t":"Shutdown","u":"/docs/3/server/codes","h":"#shutdown","p":695},{"i":731,"t":"Invalid Token","u":"/docs/3/server/codes","h":"#invalid-token","p":695},{"i":733,"t":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-1","p":695},{"i":735,"t":"Server Error","u":"/docs/3/server/codes","h":"#server-error","p":695},{"i":737,"t":"Expired","u":"/docs/3/server/codes","h":"#expired-1","p":695},{"i":739,"t":"Subscription Expired","u":"/docs/3/server/codes","h":"#subscription-expired","p":695},{"i":741,"t":"Stale","u":"/docs/3/server/codes","h":"#stale","p":695},{"i":743,"t":"Slow","u":"/docs/3/server/codes","h":"#slow","p":695},{"i":745,"t":"Write Error","u":"/docs/3/server/codes","h":"#write-error","p":695},{"i":747,"t":"Insufficient State","u":"/docs/3/server/codes","h":"#insufficient-state","p":695},{"i":749,"t":"Force Reconnect","u":"/docs/3/server/codes","h":"#force-reconnect","p":695},{"i":751,"t":"Force No Reconnect","u":"/docs/3/server/codes","h":"#force-no-reconnect","p":695},{"i":753,"t":"Connection Limit","u":"/docs/3/server/codes","h":"#connection-limit","p":695},{"i":755,"t":"Server API error codes","u":"/docs/3/server/codes","h":"#server-api-error-codes","p":695},{"i":757,"t":"Internal","u":"/docs/3/server/codes","h":"#internal-1","p":695},{"i":759,"t":"Unknown channel","u":"/docs/3/server/codes","h":"#unknown-channel-1","p":695},{"i":761,"t":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found-1","p":695},{"i":763,"t":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-2","p":695},{"i":765,"t":"Not Available","u":"/docs/3/server/codes","h":"#not-available-1","p":695},{"i":767,"t":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position-1","p":695},{"i":771,"t":"Claims","u":"/docs/3/server/authentication","h":"#claims","p":769},{"i":773,"t":"sub","u":"/docs/3/server/authentication","h":"#sub","p":769},{"i":775,"t":"exp","u":"/docs/3/server/authentication","h":"#exp","p":769},{"i":777,"t":"iat","u":"/docs/3/server/authentication","h":"#iat","p":769},{"i":779,"t":"jti","u":"/docs/3/server/authentication","h":"#jti","p":769},{"i":781,"t":"aud","u":"/docs/3/server/authentication","h":"#aud","p":769},{"i":783,"t":"iss","u":"/docs/3/server/authentication","h":"#iss","p":769},{"i":785,"t":"info","u":"/docs/3/server/authentication","h":"#info","p":769},{"i":787,"t":"b64info","u":"/docs/3/server/authentication","h":"#b64info","p":769},{"i":789,"t":"channels","u":"/docs/3/server/authentication","h":"#channels","p":769},{"i":791,"t":"subs","u":"/docs/3/server/authentication","h":"#subs","p":769},{"i":793,"t":"meta","u":"/docs/3/server/authentication","h":"#meta","p":769},{"i":795,"t":"expire_at","u":"/docs/3/server/authentication","h":"#expire_at","p":769},{"i":797,"t":"Connection expiration","u":"/docs/3/server/authentication","h":"#connection-expiration","p":769},{"i":799,"t":"Examples","u":"/docs/3/server/authentication","h":"#examples","p":769},{"i":801,"t":"Simplest token","u":"/docs/3/server/authentication","h":"#simplest-token","p":769},{"i":803,"t":"Token with expiration","u":"/docs/3/server/authentication","h":"#token-with-expiration","p":769},{"i":805,"t":"Token with additional connection info","u":"/docs/3/server/authentication","h":"#token-with-additional-connection-info","p":769},{"i":807,"t":"Investigating problems with JWT","u":"/docs/3/server/authentication","h":"#investigating-problems-with-jwt","p":769},{"i":809,"t":"JSON Web Key support","u":"/docs/3/server/authentication","h":"#json-web-key-support","p":769},{"i":813,"t":"Channel name rules","u":"/docs/3/server/channels","h":"#channel-name-rules","p":811},{"i":815,"t":"namespace boundary (:)","u":"/docs/3/server/channels","h":"#namespace-boundary-","p":811},{"i":817,"t":"private channel prefix ($)","u":"/docs/3/server/channels","h":"#private-channel-prefix-","p":811},{"i":819,"t":"user channel boundary (#)","u":"/docs/3/server/channels","h":"#user-channel-boundary-","p":811},{"i":821,"t":"Channel options","u":"/docs/3/server/channels","h":"#channel-options","p":811},{"i":823,"t":"publish","u":"/docs/3/server/channels","h":"#publish","p":811},{"i":825,"t":"subscribe_to_publish","u":"/docs/3/server/channels","h":"#subscribe_to_publish","p":811},{"i":827,"t":"anonymous","u":"/docs/3/server/channels","h":"#anonymous","p":811},{"i":829,"t":"presence","u":"/docs/3/server/channels","h":"#presence","p":811},{"i":831,"t":"presence_disable_for_client","u":"/docs/3/server/channels","h":"#presence_disable_for_client","p":811},{"i":833,"t":"join_leave","u":"/docs/3/server/channels","h":"#join_leave","p":811},{"i":835,"t":"history_size","u":"/docs/3/server/channels","h":"#history_size","p":811},{"i":837,"t":"history_ttl","u":"/docs/3/server/channels","h":"#history_ttl","p":811},{"i":839,"t":"position","u":"/docs/3/server/channels","h":"#position","p":811},{"i":841,"t":"recover","u":"/docs/3/server/channels","h":"#recover","p":811},{"i":843,"t":"history_disable_for_client","u":"/docs/3/server/channels","h":"#history_disable_for_client","p":811},{"i":845,"t":"protected","u":"/docs/3/server/channels","h":"#protected","p":811},{"i":847,"t":"proxy_subscribe","u":"/docs/3/server/channels","h":"#proxy_subscribe","p":811},{"i":849,"t":"proxy_publish","u":"/docs/3/server/channels","h":"#proxy_publish","p":811},{"i":851,"t":"subscribe_proxy_name","u":"/docs/3/server/channels","h":"#subscribe_proxy_name","p":811},{"i":853,"t":"publish_proxy_name","u":"/docs/3/server/channels","h":"#publish_proxy_name","p":811},{"i":855,"t":"Channel options config example","u":"/docs/3/server/channels","h":"#channel-options-config-example","p":811},{"i":857,"t":"Channel namespaces","u":"/docs/3/server/channels","h":"#channel-namespaces","p":811},{"i":861,"t":"version command","u":"/docs/3/server/console_commands","h":"#version-command","p":859},{"i":863,"t":"checkconfig command","u":"/docs/3/server/console_commands","h":"#checkconfig-command","p":859},{"i":865,"t":"genconfig command","u":"/docs/3/server/console_commands","h":"#genconfig-command","p":859},{"i":867,"t":"gentoken command","u":"/docs/3/server/console_commands","h":"#gentoken-command","p":859},{"i":869,"t":"checktoken command","u":"/docs/3/server/console_commands","h":"#checktoken-command","p":859},{"i":873,"t":"Configuration sources","u":"/docs/3/server/configuration","h":"#configuration-sources","p":871},{"i":875,"t":"Command-line flags","u":"/docs/3/server/configuration","h":"#command-line-flags","p":871},{"i":877,"t":"OS environment variables","u":"/docs/3/server/configuration","h":"#os-environment-variables","p":871},{"i":879,"t":"Configuration file","u":"/docs/3/server/configuration","h":"#configuration-file","p":871},{"i":881,"t":"Config file formats","u":"/docs/3/server/configuration","h":"#config-file-formats","p":871},{"i":883,"t":"JSON config format","u":"/docs/3/server/configuration","h":"#json-config-format","p":871},{"i":885,"t":"TOML config format","u":"/docs/3/server/configuration","h":"#toml-config-format","p":871},{"i":887,"t":"YAML config format","u":"/docs/3/server/configuration","h":"#yaml-config-format","p":871},{"i":889,"t":"Important options","u":"/docs/3/server/configuration","h":"#important-options","p":871},{"i":891,"t":"allowed_origins","u":"/docs/3/server/configuration","h":"#allowed_origins","p":871},{"i":893,"t":"address","u":"/docs/3/server/configuration","h":"#address","p":871},{"i":895,"t":"port","u":"/docs/3/server/configuration","h":"#port","p":871},{"i":897,"t":"engine","u":"/docs/3/server/configuration","h":"#engine","p":871},{"i":899,"t":"Advanced options","u":"/docs/3/server/configuration","h":"#advanced-options","p":871},{"i":901,"t":"client_channel_limit","u":"/docs/3/server/configuration","h":"#client_channel_limit","p":871},{"i":903,"t":"channel_max_length","u":"/docs/3/server/configuration","h":"#channel_max_length","p":871},{"i":905,"t":"client_user_connection_limit","u":"/docs/3/server/configuration","h":"#client_user_connection_limit","p":871},{"i":907,"t":"client_queue_max_size","u":"/docs/3/server/configuration","h":"#client_queue_max_size","p":871},{"i":909,"t":"client_anonymous","u":"/docs/3/server/configuration","h":"#client_anonymous","p":871},{"i":911,"t":"client_concurrency","u":"/docs/3/server/configuration","h":"#client_concurrency","p":871},{"i":913,"t":"gomaxprocs","u":"/docs/3/server/configuration","h":"#gomaxprocs","p":871},{"i":915,"t":"Endpoint configuration.","u":"/docs/3/server/configuration","h":"#endpoint-configuration","p":871},{"i":917,"t":"Default endpoints.","u":"/docs/3/server/configuration","h":"#default-endpoints","p":871},{"i":919,"t":"Admin endpoints.","u":"/docs/3/server/configuration","h":"#admin-endpoints","p":871},{"i":921,"t":"Debug endpoints.","u":"/docs/3/server/configuration","h":"#debug-endpoints","p":871},{"i":923,"t":"Health check endpoint","u":"/docs/3/server/configuration","h":"#health-check-endpoint","p":871},{"i":925,"t":"Custom internal ports","u":"/docs/3/server/configuration","h":"#custom-internal-ports","p":871},{"i":927,"t":"Disable default endpoints","u":"/docs/3/server/configuration","h":"#disable-default-endpoints","p":871},{"i":929,"t":"Customize handler endpoints","u":"/docs/3/server/configuration","h":"#customize-handler-endpoints","p":871},{"i":931,"t":"Signal handling","u":"/docs/3/server/configuration","h":"#signal-handling","p":871},{"i":933,"t":"Insecure modes","u":"/docs/3/server/configuration","h":"#insecure-modes","p":871},{"i":934,"t":"Insecure client connection","u":"/docs/3/server/configuration","h":"#insecure-client-connection","p":871},{"i":936,"t":"Insecure API mode","u":"/docs/3/server/configuration","h":"#insecure-api-mode","p":871},{"i":938,"t":"Insecure admin mode","u":"/docs/3/server/configuration","h":"#insecure-admin-mode","p":871},{"i":940,"t":"Setting time duration options","u":"/docs/3/server/configuration","h":"#setting-time-duration-options","p":871},{"i":942,"t":"Setting namespaces over env","u":"/docs/3/server/configuration","h":"#setting-namespaces-over-env","p":871},{"i":944,"t":"Anonymous usage stats","u":"/docs/3/server/configuration","h":"#anonymous-usage-stats","p":871},{"i":948,"t":"Open files limit","u":"/docs/3/server/infra_tuning","h":"#open-files-limit","p":946},{"i":950,"t":"Ephemeral port exhaustion","u":"/docs/3/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":946},{"i":952,"t":"Sockets in TIME_WAIT state","u":"/docs/3/server/infra_tuning","h":"#sockets-in-time_wait-state","p":946},{"i":954,"t":"Proxy max connections","u":"/docs/3/server/infra_tuning","h":"#proxy-max-connections","p":946},{"i":956,"t":"Conntrack table","u":"/docs/3/server/infra_tuning","h":"#conntrack-table","p":946},{"i":960,"t":"Prometheus","u":"/docs/3/server/monitoring","h":"#prometheus","p":958},{"i":962,"t":"Graphite","u":"/docs/3/server/monitoring","h":"#graphite","p":958},{"i":964,"t":"Grafana dashboard","u":"/docs/3/server/monitoring","h":"#grafana-dashboard","p":958},{"i":968,"t":"History design","u":"/docs/3/server/history_and_recovery","h":"#history-design","p":966},{"i":970,"t":"History iteration API","u":"/docs/3/server/history_and_recovery","h":"#history-iteration-api","p":966},{"i":972,"t":"Automatic message recovery","u":"/docs/3/server/history_and_recovery","h":"#automatic-message-recovery","p":966},{"i":976,"t":"Nginx configuration","u":"/docs/3/server/load_balancing","h":"#nginx-configuration","p":974},{"i":978,"t":"Separate domain for Centrifugo","u":"/docs/3/server/load_balancing","h":"#separate-domain-for-centrifugo","p":974},{"i":980,"t":"Embed to a location of web site","u":"/docs/3/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":974},{"i":982,"t":"worker_connections","u":"/docs/3/server/load_balancing","h":"#worker_connections","p":974},{"i":986,"t":"Claims","u":"/docs/3/server/private_channels","h":"#claims","p":984},{"i":988,"t":"client","u":"/docs/3/server/private_channels","h":"#client","p":984},{"i":990,"t":"channel","u":"/docs/3/server/private_channels","h":"#channel","p":984},{"i":992,"t":"info","u":"/docs/3/server/private_channels","h":"#info","p":984},{"i":994,"t":"b64info","u":"/docs/3/server/private_channels","h":"#b64info","p":984},{"i":996,"t":"exp","u":"/docs/3/server/private_channels","h":"#exp","p":984},{"i":998,"t":"expire_at","u":"/docs/3/server/private_channels","h":"#expire_at","p":984},{"i":1000,"t":"aud","u":"/docs/3/server/private_channels","h":"#aud","p":984},{"i":1002,"t":"iss","u":"/docs/3/server/private_channels","h":"#iss","p":984},{"i":1004,"t":"Example","u":"/docs/3/server/private_channels","h":"#example","p":984},{"i":1008,"t":"HTTP proxy","u":"/docs/3/server/proxy","h":"#http-proxy","p":1006},{"i":1010,"t":"HTTP request structure","u":"/docs/3/server/proxy","h":"#http-request-structure","p":1006},{"i":1012,"t":"Proxy HTTP headers","u":"/docs/3/server/proxy","h":"#proxy-http-headers","p":1006},{"i":1014,"t":"Proxy GRPC metadata","u":"/docs/3/server/proxy","h":"#proxy-grpc-metadata","p":1006},{"i":1016,"t":"Connect proxy","u":"/docs/3/server/proxy","h":"#connect-proxy","p":1006},{"i":1018,"t":"Refresh proxy","u":"/docs/3/server/proxy","h":"#refresh-proxy","p":1006},{"i":1020,"t":"RPC proxy","u":"/docs/3/server/proxy","h":"#rpc-proxy","p":1006},{"i":1022,"t":"Subscribe proxy","u":"/docs/3/server/proxy","h":"#subscribe-proxy","p":1006},{"i":1024,"t":"Publish proxy","u":"/docs/3/server/proxy","h":"#publish-proxy","p":1006},{"i":1026,"t":"Return custom error","u":"/docs/3/server/proxy","h":"#return-custom-error","p":1006},{"i":1028,"t":"Return custom disconnect","u":"/docs/3/server/proxy","h":"#return-custom-disconnect","p":1006},{"i":1030,"t":"GRPC proxy","u":"/docs/3/server/proxy","h":"#grpc-proxy","p":1006},{"i":1032,"t":"GRPC proxy options","u":"/docs/3/server/proxy","h":"#grpc-proxy-options","p":1006},{"i":1034,"t":"GRPC proxy example","u":"/docs/3/server/proxy","h":"#grpc-proxy-example","p":1006},{"i":1036,"t":"Header proxy rules","u":"/docs/3/server/proxy","h":"#header-proxy-rules","p":1006},{"i":1038,"t":"Binary mode","u":"/docs/3/server/proxy","h":"#binary-mode","p":1006},{"i":1040,"t":"Granular proxy mode","u":"/docs/3/server/proxy","h":"#granular-proxy-mode","p":1006},{"i":1042,"t":"Enable granular proxy mode","u":"/docs/3/server/proxy","h":"#enable-granular-proxy-mode","p":1006},{"i":1044,"t":"Defining a list of proxies","u":"/docs/3/server/proxy","h":"#defining-a-list-of-proxies","p":1006},{"i":1046,"t":"Granular connect and refresh","u":"/docs/3/server/proxy","h":"#granular-connect-and-refresh","p":1006},{"i":1048,"t":"Granular subscribe and publish","u":"/docs/3/server/proxy","h":"#granular-subscribe-and-publish","p":1006},{"i":1050,"t":"Granular RPC","u":"/docs/3/server/proxy","h":"#granular-rpc","p":1006},{"i":1054,"t":"Using crt and key files","u":"/docs/3/server/tls","h":"#using-crt-and-key-files","p":1052},{"i":1056,"t":"Automatic certificates","u":"/docs/3/server/tls","h":"#automatic-certificates","p":1052},{"i":1058,"t":"TLS for GRPC API","u":"/docs/3/server/tls","h":"#tls-for-grpc-api","p":1052},{"i":1060,"t":"TLS for GRPC unidirectional stream","u":"/docs/3/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":1052},{"i":1066,"t":"Dynamic server-side subscriptions","u":"/docs/3/server/server_subs","h":"#dynamic-server-side-subscriptions","p":1064},{"i":1068,"t":"Automatic personal channel subscription","u":"/docs/3/server/server_subs","h":"#automatic-personal-channel-subscription","p":1064},{"i":1070,"t":"Maintain single user connection","u":"/docs/3/server/server_subs","h":"#maintain-single-user-connection","p":1064},{"i":1074,"t":"SockJS caveats","u":"/docs/3/transports/sockjs","h":"#sockjs-caveats","p":1072},{"i":1076,"t":"Sticky sessions","u":"/docs/3/transports/sockjs","h":"#sticky-sessions","p":1072},{"i":1078,"t":"Browser only","u":"/docs/3/transports/sockjs","h":"#browser-only","p":1072},{"i":1080,"t":"JSON only","u":"/docs/3/transports/sockjs","h":"#json-only","p":1072},{"i":1082,"t":"Options","u":"/docs/3/transports/sockjs","h":"#options","p":1072},{"i":1083,"t":"sockjs","u":"/docs/3/transports/sockjs","h":"#sockjs","p":1072},{"i":1085,"t":"sockjs_url","u":"/docs/3/transports/sockjs","h":"#sockjs_url","p":1072},{"i":1089,"t":"Bidirectional","u":"/docs/3/transports/overview","h":"#bidirectional","p":1087},{"i":1091,"t":"Unidirectional","u":"/docs/3/transports/overview","h":"#unidirectional","p":1087},{"i":1093,"t":"Unidirectional message types","u":"/docs/3/transports/overview","h":"#unidirectional-message-types","p":1087},{"i":1097,"t":"Connect command","u":"/docs/3/transports/uni_sse","h":"#connect-command","p":1095},{"i":1099,"t":"Supported data formats","u":"/docs/3/transports/uni_sse","h":"#supported-data-formats","p":1095},{"i":1101,"t":"Pings","u":"/docs/3/transports/uni_sse","h":"#pings","p":1095},{"i":1103,"t":"Options","u":"/docs/3/transports/uni_sse","h":"#options","p":1095},{"i":1104,"t":"uni_sse","u":"/docs/3/transports/uni_sse","h":"#uni_sse","p":1095},{"i":1106,"t":"uni_sse_max_request_body_size","u":"/docs/3/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":1095},{"i":1108,"t":"Browser example","u":"/docs/3/transports/uni_sse","h":"#browser-example","p":1095},{"i":1112,"t":"Supported data formats","u":"/docs/3/transports/uni_grpc","h":"#supported-data-formats","p":1110},{"i":1114,"t":"Options","u":"/docs/3/transports/uni_grpc","h":"#options","p":1110},{"i":1115,"t":"uni_grpc","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc","p":1110},{"i":1117,"t":"uni_grpc_port","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_port","p":1110},{"i":1119,"t":"uni_grpc_address","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_address","p":1110},{"i":1121,"t":"uni_grpc_max_receive_message_size","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":1110},{"i":1123,"t":"uni_grpc_tls","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls","p":1110},{"i":1125,"t":"uni_grpc_tls_cert","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":1110},{"i":1127,"t":"uni_grpc_tls_key","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_key","p":1110},{"i":1129,"t":"Example","u":"/docs/3/transports/uni_grpc","h":"#example","p":1110},{"i":1133,"t":"Connect command","u":"/docs/3/transports/uni_websocket","h":"#connect-command","p":1131},{"i":1135,"t":"SubscribeRequest","u":"/docs/3/transports/uni_websocket","h":"#subscriberequest","p":1131},{"i":1137,"t":"Supported data formats","u":"/docs/3/transports/uni_websocket","h":"#supported-data-formats","p":1131},{"i":1139,"t":"Pings","u":"/docs/3/transports/uni_websocket","h":"#pings","p":1131},{"i":1141,"t":"Options","u":"/docs/3/transports/uni_websocket","h":"#options","p":1131},{"i":1142,"t":"uni_websocket","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket","p":1131},{"i":1144,"t":"uni_websocket_message_size_limit","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":1131},{"i":1146,"t":"Example","u":"/docs/3/transports/uni_websocket","h":"#example","p":1131},{"i":1150,"t":"Options","u":"/docs/3/transports/websocket","h":"#options","p":1148},{"i":1151,"t":"websocket_message_size_limit","u":"/docs/3/transports/websocket","h":"#websocket_message_size_limit","p":1148},{"i":1153,"t":"websocket_read_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_read_buffer_size","p":1148},{"i":1155,"t":"websocket_write_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_write_buffer_size","p":1148},{"i":1157,"t":"websocket_use_write_buffer_pool","u":"/docs/3/transports/websocket","h":"#websocket_use_write_buffer_pool","p":1148},{"i":1159,"t":"websocket_compression","u":"/docs/3/transports/websocket","h":"#websocket_compression","p":1148},{"i":1161,"t":"Protobuf binary protocol","u":"/docs/3/transports/websocket","h":"#protobuf-binary-protocol","p":1148},{"i":1165,"t":"Connect command","u":"/docs/3/transports/uni_http_stream","h":"#connect-command","p":1163},{"i":1167,"t":"Supported data formats","u":"/docs/3/transports/uni_http_stream","h":"#supported-data-formats","p":1163},{"i":1169,"t":"Pings","u":"/docs/3/transports/uni_http_stream","h":"#pings","p":1163},{"i":1171,"t":"Options","u":"/docs/3/transports/uni_http_stream","h":"#options","p":1163},{"i":1172,"t":"uni_http_stream","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream","p":1163},{"i":1174,"t":"uni_http_stream_max_request_body_size","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":1163},{"i":1176,"t":"Connecting using CURL","u":"/docs/3/transports/uni_http_stream","h":"#connecting-using-curl","p":1163},{"i":1178,"t":"Browser example","u":"/docs/3/transports/uni_http_stream","h":"#browser-example","p":1163},{"i":1182,"t":"Memory engine","u":"/docs/3/server/engines","h":"#memory-engine","p":1180},{"i":1184,"t":"Memory engine options","u":"/docs/3/server/engines","h":"#memory-engine-options","p":1180},{"i":1186,"t":"Redis engine","u":"/docs/3/server/engines","h":"#redis-engine","p":1180},{"i":1188,"t":"Redis engine options","u":"/docs/3/server/engines","h":"#redis-engine-options","p":1180},{"i":1190,"t":"Scaling with Redis tutorial","u":"/docs/3/server/engines","h":"#scaling-with-redis-tutorial","p":1180},{"i":1192,"t":"Redis Sentinel for high availability","u":"/docs/3/server/engines","h":"#redis-sentinel-for-high-availability","p":1180},{"i":1194,"t":"Haproxy instead of Sentinel configuration","u":"/docs/3/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":1180},{"i":1196,"t":"Redis sharding","u":"/docs/3/server/engines","h":"#redis-sharding","p":1180},{"i":1198,"t":"Redis cluster","u":"/docs/3/server/engines","h":"#redis-cluster","p":1180},{"i":1200,"t":"KeyDB Engine","u":"/docs/3/server/engines","h":"#keydb-engine","p":1180},{"i":1202,"t":"Tarantool engine","u":"/docs/3/server/engines","h":"#tarantool-engine","p":1180},{"i":1204,"t":"Tarantool engine options","u":"/docs/3/server/engines","h":"#tarantool-engine-options","p":1180},{"i":1206,"t":"Nats broker","u":"/docs/3/server/engines","h":"#nats-broker","p":1180},{"i":1208,"t":"Options","u":"/docs/3/server/engines","h":"#options","p":1180},{"i":1214,"t":"Landing Page Images","u":"/docs/4/attributions","h":"#landing-page-images","p":1212},{"i":1220,"t":"How many connections can one Centrifugo instance handle?","u":"/docs/4/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":1218},{"i":1222,"t":"Memory usage per connection?","u":"/docs/4/faq","h":"#memory-usage-per-connection","p":1218},{"i":1224,"t":"Can Centrifugo scale horizontally?","u":"/docs/4/faq","h":"#can-centrifugo-scale-horizontally","p":1218},{"i":1226,"t":"Message delivery model","u":"/docs/4/faq","h":"#message-delivery-model","p":1218},{"i":1228,"t":"Message order guarantees","u":"/docs/4/faq","h":"#message-order-guarantees","p":1218},{"i":1230,"t":"Should I create channels explicitly?","u":"/docs/4/faq","h":"#should-i-create-channels-explicitly","p":1218},{"i":1232,"t":"What about best practices with the number of channels?","u":"/docs/4/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":1218},{"i":1234,"t":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/4/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":1218},{"i":1236,"t":"Can I have both binary and JSON clients in one channel?","u":"/docs/4/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":1218},{"i":1238,"t":"Online presence for chat apps - online status of your contacts","u":"/docs/4/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":1218},{"i":1240,"t":"Centrifugo stops accepting new connections, why?","u":"/docs/4/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":1218},{"i":1242,"t":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/4/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":1218},{"i":1244,"t":"Does Centrifugo work with HTTP/2?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http2","p":1218},{"i":1246,"t":"Does Centrifugo work with HTTP/3?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http3","p":1218},{"i":1248,"t":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/4/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":1218},{"i":1250,"t":"What if I need to send push notifications to mobile or web applications?","u":"/docs/4/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":1218},{"i":1252,"t":"How can I know a message is delivered to a client?","u":"/docs/4/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":1218},{"i":1254,"t":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/4/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":1218},{"i":1256,"t":"How to create a secure channel for two users only (private chat case)?","u":"/docs/4/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":1218},{"i":1258,"t":"What's the best way to organize channel configuration?","u":"/docs/4/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":1218},{"i":1260,"t":"Does Centrifugo support webhooks?","u":"/docs/4/faq","h":"#does-centrifugo-support-webhooks","p":1218},{"i":1262,"t":"Why Centrifugo does not have disconnect hooks?","u":"/docs/4/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":1218},{"i":1264,"t":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/4/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":1218},{"i":1266,"t":"How scalable is the online presence and join/leave features?","u":"/docs/4/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":1218},{"i":1268,"t":"How to send initial data to channel subscriber?","u":"/docs/4/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":1218},{"i":1270,"t":"Does Centrifugo support multitenancy?","u":"/docs/4/faq","h":"#does-centrifugo-support-multitenancy","p":1218},{"i":1272,"t":"I have not found an answer to my question here:","u":"/docs/4/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":1218},{"i":1276,"t":"Idiomatic usage","u":"/docs/4/getting-started/design","h":"#idiomatic-usage","p":1274},{"i":1278,"t":"Message history considerations","u":"/docs/4/getting-started/design","h":"#message-history-considerations","p":1274},{"i":1280,"t":"Message delivery model","u":"/docs/4/getting-started/design","h":"#message-delivery-model","p":1274},{"i":1282,"t":"Message order guarantees","u":"/docs/4/getting-started/design","h":"#message-order-guarantees","p":1274},{"i":1284,"t":"Graceful degradation","u":"/docs/4/getting-started/design","h":"#graceful-degradation","p":1274},{"i":1286,"t":"Online presence considerations","u":"/docs/4/getting-started/design","h":"#online-presence-considerations","p":1274},{"i":1288,"t":"Scalability considerations","u":"/docs/4/getting-started/design","h":"#scalability-considerations","p":1274},{"i":1292,"t":"Centrifuge library for Go","u":"/docs/4/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":1290},{"i":1294,"t":"Framework integrations","u":"/docs/4/getting-started/ecosystem","h":"#framework-integrations","p":1290},{"i":1298,"t":"Connecting to a server","u":"/docs/4/getting-started/client_api","h":"#connecting-to-a-server","p":1296},{"i":1300,"t":"Disconnecting from a server","u":"/docs/4/getting-started/client_api","h":"#disconnecting-from-a-server","p":1296},{"i":1302,"t":"Reconnecting to a server","u":"/docs/4/getting-started/client_api","h":"#reconnecting-to-a-server","p":1296},{"i":1304,"t":"Connection lifecycle events","u":"/docs/4/getting-started/client_api","h":"#connection-lifecycle-events","p":1296},{"i":1306,"t":"Subscribe to a channel","u":"/docs/4/getting-started/client_api","h":"#subscribe-to-a-channel","p":1296},{"i":1308,"t":"Server-side subscriptions","u":"/docs/4/getting-started/client_api","h":"#server-side-subscriptions","p":1296},{"i":1310,"t":"Send RPC","u":"/docs/4/getting-started/client_api","h":"#send-rpc","p":1296},{"i":1312,"t":"Call channel history","u":"/docs/4/getting-started/client_api","h":"#call-channel-history","p":1296},{"i":1314,"t":"Presence and presence stats","u":"/docs/4/getting-started/client_api","h":"#presence-and-presence-stats","p":1296},{"i":1318,"t":"Simple integration","u":"/docs/4/getting-started/highlights","h":"#simple-integration","p":1316},{"i":1320,"t":"Great performance","u":"/docs/4/getting-started/highlights","h":"#great-performance","p":1316},{"i":1322,"t":"Built-in scalability","u":"/docs/4/getting-started/highlights","h":"#built-in-scalability","p":1316},{"i":1324,"t":"Strict client protocol","u":"/docs/4/getting-started/highlights","h":"#strict-client-protocol","p":1316},{"i":1326,"t":"Variety of real-time transports","u":"/docs/4/getting-started/highlights","h":"#variety-of-real-time-transports","p":1316},{"i":1328,"t":"Flexible authentication","u":"/docs/4/getting-started/highlights","h":"#flexible-authentication","p":1316},{"i":1330,"t":"Connection management","u":"/docs/4/getting-started/highlights","h":"#connection-management","p":1316},{"i":1332,"t":"Channel (room) concept","u":"/docs/4/getting-started/highlights","h":"#channel-room-concept","p":1316},{"i":1334,"t":"Different types of subscriptions","u":"/docs/4/getting-started/highlights","h":"#different-types-of-subscriptions","p":1316},{"i":1336,"t":"RPC over bidirectional connection","u":"/docs/4/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":1316},{"i":1338,"t":"Online presence information","u":"/docs/4/getting-started/highlights","h":"#online-presence-information","p":1316},{"i":1340,"t":"Message history in channels","u":"/docs/4/getting-started/highlights","h":"#message-history-in-channels","p":1316},{"i":1342,"t":"Embedded admin web UI","u":"/docs/4/getting-started/highlights","h":"#embedded-admin-web-ui","p":1316},{"i":1344,"t":"Cross-platform","u":"/docs/4/getting-started/highlights","h":"#cross-platform","p":1316},{"i":1346,"t":"Ready to deploy","u":"/docs/4/getting-started/highlights","h":"#ready-to-deploy","p":1316},{"i":1348,"t":"Open-source","u":"/docs/4/getting-started/highlights","h":"#open-source","p":1316},{"i":1350,"t":"Pro features","u":"/docs/4/getting-started/highlights","h":"#pro-features","p":1316},{"i":1354,"t":"Background","u":"/docs/4/getting-started/introduction","h":"#background","p":1352},{"i":1358,"t":"Client SDK migration","u":"/docs/4/getting-started/migration_v4","h":"#client-sdk-migration","p":1356},{"i":1360,"t":"Unidirectional transport migration","u":"/docs/4/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":1356},{"i":1362,"t":"SockJS migration","u":"/docs/4/getting-started/migration_v4","h":"#sockjs-migration","p":1356},{"i":1364,"t":"Channel ASCII enforced","u":"/docs/4/getting-started/migration_v4","h":"#channel-ascii-enforced","p":1356},{"i":1366,"t":"Subscription token migration","u":"/docs/4/getting-started/migration_v4","h":"#subscription-token-migration","p":1356},{"i":1368,"t":"User-limited channel migration","u":"/docs/4/getting-started/migration_v4","h":"#user-limited-channel-migration","p":1356},{"i":1370,"t":"Namespace configuration migration","u":"/docs/4/getting-started/migration_v4","h":"#namespace-configuration-migration","p":1356},{"i":1372,"t":"Proxy disconnect code changes","u":"/docs/4/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":1356},{"i":1374,"t":"Other configuration option changes","u":"/docs/4/getting-started/migration_v4","h":"#other-configuration-option-changes","p":1356},{"i":1376,"t":"Server API changes","u":"/docs/4/getting-started/migration_v4","h":"#server-api-changes","p":1356},{"i":1380,"t":"0. Install","u":"/docs/4/getting-started/integration","h":"#0-install","p":1378},{"i":1382,"t":"1. Configure Centrifugo","u":"/docs/4/getting-started/integration","h":"#1-configure-centrifugo","p":1378},{"i":1384,"t":"2. Configure your backend","u":"/docs/4/getting-started/integration","h":"#2-configure-your-backend","p":1378},{"i":1386,"t":"3. Connect to Centrifugo","u":"/docs/4/getting-started/integration","h":"#3-connect-to-centrifugo","p":1378},{"i":1388,"t":"4. Subscribe to channels","u":"/docs/4/getting-started/integration","h":"#4-subscribe-to-channels","p":1378},{"i":1390,"t":"5. Publish to channel","u":"/docs/4/getting-started/integration","h":"#5-publish-to-channel","p":1378},{"i":1392,"t":"6. Deploy to production","u":"/docs/4/getting-started/integration","h":"#6-deploy-to-production","p":1378},{"i":1394,"t":"7. Monitor Centrifugo","u":"/docs/4/getting-started/integration","h":"#7-monitor-centrifugo","p":1378},{"i":1396,"t":"8. Scale Centrifugo","u":"/docs/4/getting-started/integration","h":"#8-scale-centrifugo","p":1378},{"i":1398,"t":"9. Read FAQ","u":"/docs/4/getting-started/integration","h":"#9-read-faq","p":1378},{"i":1402,"t":"Client implementation feature matrix","u":"/docs/3/transports/client_protocol","h":"#client-implementation-feature-matrix","p":1400},{"i":1404,"t":"Top level framing","u":"/docs/3/transports/client_protocol","h":"#top-level-framing","p":1400},{"i":1406,"t":"Connect","u":"/docs/3/transports/client_protocol","h":"#connect","p":1400},{"i":1408,"t":"Subscribe","u":"/docs/3/transports/client_protocol","h":"#subscribe","p":1400},{"i":1410,"t":"Unsubscribe","u":"/docs/3/transports/client_protocol","h":"#unsubscribe","p":1400},{"i":1412,"t":"Refresh","u":"/docs/3/transports/client_protocol","h":"#refresh","p":1400},{"i":1414,"t":"RPC-like calls: publish, history, presence","u":"/docs/3/transports/client_protocol","h":"#rpc-like-calls-publish-history-presence","p":1400},{"i":1416,"t":"Asynchronous server-to-client messages","u":"/docs/3/transports/client_protocol","h":"#asynchronous-server-to-client-messages","p":1400},{"i":1418,"t":"Ping Pong","u":"/docs/3/transports/client_protocol","h":"#ping-pong","p":1400},{"i":1420,"t":"Handle disconnects","u":"/docs/3/transports/client_protocol","h":"#handle-disconnects","p":1400},{"i":1422,"t":"Handle errors","u":"/docs/3/transports/client_protocol","h":"#handle-errors","p":1400},{"i":1424,"t":"Client implementation advices","u":"/docs/3/transports/client_protocol","h":"#client-implementation-advices","p":1400},{"i":1426,"t":"Server side subscriptions (SSS)","u":"/docs/3/transports/client_protocol","h":"#server-side-subscriptions-sss","p":1400},{"i":1428,"t":"Message recovery","u":"/docs/3/transports/client_protocol","h":"#message-recovery","p":1400},{"i":1430,"t":"Disconnect code and reason","u":"/docs/3/transports/client_protocol","h":"#disconnect-code-and-reason","p":1400},{"i":1432,"t":"Additional notes","u":"/docs/3/transports/client_protocol","h":"#additional-notes","p":1400},{"i":1436,"t":"Install from the binary release","u":"/docs/4/getting-started/installation","h":"#install-from-the-binary-release","p":1434},{"i":1438,"t":"Docker image","u":"/docs/4/getting-started/installation","h":"#docker-image","p":1434},{"i":1440,"t":"Docker-compose example","u":"/docs/4/getting-started/installation","h":"#docker-compose-example","p":1434},{"i":1442,"t":"Kubernetes Helm chart","u":"/docs/4/getting-started/installation","h":"#kubernetes-helm-chart","p":1434},{"i":1444,"t":"RPM and DEB packages for Linux","u":"/docs/4/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":1434},{"i":1446,"t":"With brew on macOS","u":"/docs/4/getting-started/installation","h":"#with-brew-on-macos","p":1434},{"i":1448,"t":"Build from source","u":"/docs/4/getting-started/installation","h":"#build-from-source","p":1434},{"i":1452,"t":"HTTP API","u":"/docs/3/server/server_api","h":"#http-api","p":1450},{"i":1454,"t":"HTTP API authorization","u":"/docs/3/server/server_api","h":"#http-api-authorization","p":1450},{"i":1456,"t":"publish","u":"/docs/3/server/server_api","h":"#publish","p":1450},{"i":1458,"t":"broadcast","u":"/docs/3/server/server_api","h":"#broadcast","p":1450},{"i":1460,"t":"subscribe","u":"/docs/3/server/server_api","h":"#subscribe","p":1450},{"i":1462,"t":"unsubscribe","u":"/docs/3/server/server_api","h":"#unsubscribe","p":1450},{"i":1464,"t":"disconnect","u":"/docs/3/server/server_api","h":"#disconnect","p":1450},{"i":1466,"t":"refresh","u":"/docs/3/server/server_api","h":"#refresh","p":1450},{"i":1468,"t":"presence","u":"/docs/3/server/server_api","h":"#presence","p":1450},{"i":1470,"t":"presence_stats","u":"/docs/3/server/server_api","h":"#presence_stats","p":1450},{"i":1472,"t":"history","u":"/docs/3/server/server_api","h":"#history","p":1450},{"i":1474,"t":"history_remove","u":"/docs/3/server/server_api","h":"#history_remove","p":1450},{"i":1476,"t":"channels","u":"/docs/3/server/server_api","h":"#channels","p":1450},{"i":1478,"t":"info","u":"/docs/3/server/server_api","h":"#info","p":1450},{"i":1480,"t":"Command pipelining","u":"/docs/3/server/server_api","h":"#command-pipelining","p":1450},{"i":1482,"t":"HTTP API libraries","u":"/docs/3/server/server_api","h":"#http-api-libraries","p":1450},{"i":1484,"t":"GRPC API","u":"/docs/3/server/server_api","h":"#grpc-api","p":1450},{"i":1486,"t":"GRPC example for Python","u":"/docs/3/server/server_api","h":"#grpc-example-for-python","p":1450},{"i":1488,"t":"GRPC example for Go","u":"/docs/3/server/server_api","h":"#grpc-example-for-go","p":1450},{"i":1490,"t":"GRPC API key authorization","u":"/docs/3/server/server_api","h":"#grpc-api-key-authorization","p":1450},{"i":1494,"t":"subscribe_cel","u":"/docs/4/pro/cel_expressions","h":"#subscribe_cel","p":1492},{"i":1496,"t":"Expression variables","u":"/docs/4/pro/cel_expressions","h":"#expression-variables","p":1492},{"i":1498,"t":"publish_cel","u":"/docs/4/pro/cel_expressions","h":"#publish_cel","p":1492},{"i":1500,"t":"history_cel","u":"/docs/4/pro/cel_expressions","h":"#history_cel","p":1492},{"i":1502,"t":"presence_cel","u":"/docs/4/pro/cel_expressions","h":"#presence_cel","p":1492},{"i":1508,"t":"Configuration","u":"/docs/4/pro/channel_patterns","h":"#configuration","p":1506},{"i":1510,"t":"Implementation details","u":"/docs/4/pro/channel_patterns","h":"#implementation-details","p":1506},{"i":1512,"t":"Variables","u":"/docs/4/pro/channel_patterns","h":"#variables","p":1506},{"i":1514,"t":"Using varibles","u":"/docs/4/pro/channel_patterns","h":"#using-varibles","p":1506},{"i":1518,"t":"client_write_delay","u":"/docs/4/pro/client_message_batching","h":"#client_write_delay","p":1516},{"i":1520,"t":"client_reply_without_queue","u":"/docs/4/pro/client_message_batching","h":"#client_reply_without_queue","p":1516},{"i":1522,"t":"client_max_messages_in_frame","u":"/docs/4/pro/client_message_batching","h":"#client_max_messages_in_frame","p":1516},{"i":1526,"t":"Features","u":"/docs/4/pro/overview","h":"#features","p":1524},{"i":1528,"t":"Try for free in sandbox mode","u":"/docs/4/pro/overview","h":"#try-for-free-in-sandbox-mode","p":1524},{"i":1530,"t":"Pricing","u":"/docs/4/pro/overview","h":"#pricing","p":1524},{"i":1534,"t":"Faster HTTP API","u":"/docs/4/pro/performance","h":"#faster-http-api","p":1532},{"i":1536,"t":"Faster GRPC API","u":"/docs/4/pro/performance","h":"#faster-grpc-api","p":1532},{"i":1538,"t":"Faster HTTP proxy","u":"/docs/4/pro/performance","h":"#faster-http-proxy","p":1532},{"i":1540,"t":"Faster GRPC proxy","u":"/docs/4/pro/performance","h":"#faster-grpc-proxy","p":1532},{"i":1542,"t":"Faster JWT decoding","u":"/docs/4/pro/performance","h":"#faster-jwt-decoding","p":1532},{"i":1544,"t":"Faster GRPC unidirectional stream","u":"/docs/4/pro/performance","h":"#faster-grpc-unidirectional-stream","p":1532},{"i":1546,"t":"Examples","u":"/docs/4/pro/performance","h":"#examples","p":1532},{"i":1548,"t":"Publish HTTP API","u":"/docs/4/pro/performance","h":"#publish-http-api","p":1532},{"i":1550,"t":"History HTTP API","u":"/docs/4/pro/performance","h":"#history-http-api","p":1532},{"i":1554,"t":"Binary release","u":"/docs/4/pro/install_and_run","h":"#binary-release","p":1552},{"i":1556,"t":"Docker image","u":"/docs/4/pro/install_and_run","h":"#docker-image","p":1552},{"i":1558,"t":"Kubernetes","u":"/docs/4/pro/install_and_run","h":"#kubernetes","p":1552},{"i":1560,"t":"Debian and Ubuntu","u":"/docs/4/pro/install_and_run","h":"#debian-and-ubuntu","p":1552},{"i":1562,"t":"Centos","u":"/docs/4/pro/install_and_run","h":"#centos","p":1552},{"i":1564,"t":"Setting PRO license key","u":"/docs/4/pro/install_and_run","h":"#setting-pro-license-key","p":1552},{"i":1568,"t":"Connection capabilities","u":"/docs/4/pro/capabilities","h":"#connection-capabilities","p":1566},{"i":1570,"t":"Caps processing behavior","u":"/docs/4/pro/capabilities","h":"#caps-processing-behavior","p":1566},{"i":1572,"t":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations","p":1566},{"i":1574,"t":"Revoking connection caps","u":"/docs/4/pro/capabilities","h":"#revoking-connection-caps","p":1566},{"i":1576,"t":"Example: wildcard match","u":"/docs/4/pro/capabilities","h":"#example-wildcard-match","p":1566},{"i":1578,"t":"Example: regex match","u":"/docs/4/pro/capabilities","h":"#example-regex-match","p":1566},{"i":1580,"t":"Example: different types of match","u":"/docs/4/pro/capabilities","h":"#example-different-types-of-match","p":1566},{"i":1582,"t":"Example: full access to all channels","u":"/docs/4/pro/capabilities","h":"#example-full-access-to-all-channels","p":1566},{"i":1584,"t":"Subscription capabilities","u":"/docs/4/pro/capabilities","h":"#subscription-capabilities","p":1566},{"i":1586,"t":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations-1","p":1566},{"i":1588,"t":"Revoking subscription permissions","u":"/docs/4/pro/capabilities","h":"#revoking-subscription-permissions","p":1566},{"i":1596,"t":"Example","u":"/docs/4/pro/connections","h":"#example","p":1594},{"i":1598,"t":"connections","u":"/docs/4/pro/connections","h":"#connections","p":1594},{"i":1602,"t":"Configuration","u":"/docs/4/pro/analytics","h":"#configuration","p":1600},{"i":1604,"t":"Connections table","u":"/docs/4/pro/analytics","h":"#connections-table","p":1600},{"i":1606,"t":"Subscriptions table","u":"/docs/4/pro/analytics","h":"#subscriptions-table","p":1600},{"i":1608,"t":"Operations table","u":"/docs/4/pro/analytics","h":"#operations-table","p":1600},{"i":1610,"t":"Publications table","u":"/docs/4/pro/analytics","h":"#publications-table","p":1600},{"i":1612,"t":"Notifications table","u":"/docs/4/pro/analytics","h":"#notifications-table","p":1600},{"i":1614,"t":"Query examples","u":"/docs/4/pro/analytics","h":"#query-examples","p":1600},{"i":1616,"t":"Development","u":"/docs/4/pro/analytics","h":"#development","p":1600},{"i":1618,"t":"How export works","u":"/docs/4/pro/analytics","h":"#how-export-works","p":1600},{"i":1622,"t":"Save to a file","u":"/docs/4/pro/tracing","h":"#save-to-a-file","p":1620},{"i":1626,"t":"How it works","u":"/docs/4/pro/token_revocation","h":"#how-it-works","p":1624},{"i":1628,"t":"Configure","u":"/docs/4/pro/token_revocation","h":"#configure","p":1624},{"i":1630,"t":"Redis persistence engine","u":"/docs/4/pro/token_revocation","h":"#redis-persistence-engine","p":1624},{"i":1632,"t":"Database persistence engine","u":"/docs/4/pro/token_revocation","h":"#database-persistence-engine","p":1624},{"i":1634,"t":"Revoke token API","u":"/docs/4/pro/token_revocation","h":"#revoke-token-api","p":1624},{"i":1635,"t":"revoke_token","u":"/docs/4/pro/token_revocation","h":"#revoke_token","p":1624},{"i":1637,"t":"Invalidate user tokens API","u":"/docs/4/pro/token_revocation","h":"#invalidate-user-tokens-api","p":1624},{"i":1638,"t":"invalidate_user_tokens","u":"/docs/4/pro/token_revocation","h":"#invalidate_user_tokens","p":1624},{"i":1642,"t":"How it works","u":"/docs/4/pro/user_block","h":"#how-it-works","p":1640},{"i":1644,"t":"Configure","u":"/docs/4/pro/user_block","h":"#configure","p":1640},{"i":1646,"t":"Redis persistence engine","u":"/docs/4/pro/user_block","h":"#redis-persistence-engine","p":1640},{"i":1648,"t":"Database persistence engine","u":"/docs/4/pro/user_block","h":"#database-persistence-engine","p":1640},{"i":1650,"t":"Block API","u":"/docs/4/pro/user_block","h":"#block--api","p":1640},{"i":1651,"t":"block_user","u":"/docs/4/pro/user_block","h":"#block_user","p":1640},{"i":1653,"t":"unblock_user","u":"/docs/4/pro/user_block","h":"#unblock_user","p":1640},{"i":1657,"t":"Options","u":"/docs/4/server/admin_web","h":"#options","p":1655},{"i":1659,"t":"Using custom web interface","u":"/docs/4/server/admin_web","h":"#using-custom-web-interface","p":1655},{"i":1661,"t":"Admin insecure mode","u":"/docs/4/server/admin_web","h":"#admin-insecure-mode","p":1655},{"i":1665,"t":"In-memory per connection throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-connection-throttling","p":1663},{"i":1667,"t":"In-memory per user throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-user-throttling","p":1663},{"i":1669,"t":"Redis per user throttling","u":"/docs/4/pro/throttling","h":"#redis-per-user-throttling","p":1663},{"i":1671,"t":"Disconnecting abusive or misbehaving connections","u":"/docs/4/pro/throttling","h":"#disconnecting-abusive-or-misbehaving-connections","p":1663},{"i":1675,"t":"Motivation and design choices","u":"/docs/4/pro/push_notifications","h":"#motivation-and-design-choices","p":1673},{"i":1677,"t":"Storage for tokens","u":"/docs/4/pro/push_notifications","h":"#storage-for-tokens","p":1673},{"i":1679,"t":"Efficient queuing","u":"/docs/4/pro/push_notifications","h":"#efficient-queuing","p":1673},{"i":1681,"t":"Unified secure topics","u":"/docs/4/pro/push_notifications","h":"#unified-secure-topics","p":1673},{"i":1683,"t":"Non-obtrusive proxying","u":"/docs/4/pro/push_notifications","h":"#non-obtrusive-proxying","p":1673},{"i":1685,"t":"Builtin analytics","u":"/docs/4/pro/push_notifications","h":"#builtin-analytics","p":1673},{"i":1687,"t":"Steps to integrate","u":"/docs/4/pro/push_notifications","h":"#steps-to-integrate","p":1673},{"i":1689,"t":"Configuration","u":"/docs/4/pro/push_notifications","h":"#configuration","p":1673},{"i":1691,"t":"FCM","u":"/docs/4/pro/push_notifications","h":"#fcm","p":1673},{"i":1693,"t":"HMS","u":"/docs/4/pro/push_notifications","h":"#hms","p":1673},{"i":1695,"t":"APNs","u":"/docs/4/pro/push_notifications","h":"#apns","p":1673},{"i":1697,"t":"Other options","u":"/docs/4/pro/push_notifications","h":"#other-options","p":1673},{"i":1699,"t":"Use PostgreSQL as queue","u":"/docs/4/pro/push_notifications","h":"#use-postgresql-as-queue","p":1673},{"i":1701,"t":"API description","u":"/docs/4/pro/push_notifications","h":"#api-description","p":1673},{"i":1702,"t":"device_register","u":"/docs/4/pro/push_notifications","h":"#device_register","p":1673},{"i":1704,"t":"device_update","u":"/docs/4/pro/push_notifications","h":"#device_update","p":1673},{"i":1706,"t":"device_remove","u":"/docs/4/pro/push_notifications","h":"#device_remove","p":1673},{"i":1708,"t":"device_list","u":"/docs/4/pro/push_notifications","h":"#device_list","p":1673},{"i":1710,"t":"device_topic_update","u":"/docs/4/pro/push_notifications","h":"#device_topic_update","p":1673},{"i":1712,"t":"device_topic_list","u":"/docs/4/pro/push_notifications","h":"#device_topic_list","p":1673},{"i":1714,"t":"user_topic_update","u":"/docs/4/pro/push_notifications","h":"#user_topic_update","p":1673},{"i":1716,"t":"user_topic_list","u":"/docs/4/pro/push_notifications","h":"#user_topic_list","p":1673},{"i":1718,"t":"send_push_notification","u":"/docs/4/pro/push_notifications","h":"#send_push_notification","p":1673},{"i":1720,"t":"update_push_status","u":"/docs/4/pro/push_notifications","h":"#update_push_status","p":1673},{"i":1722,"t":"Metrics","u":"/docs/4/pro/push_notifications","h":"#metrics","p":1673},{"i":1724,"t":"Further reading and tutorials","u":"/docs/4/pro/push_notifications","h":"#further-reading-and-tutorials","p":1673},{"i":1728,"t":"Client-side status update RPC","u":"/docs/4/pro/user_status","h":"#client-side-status-update-rpc","p":1726},{"i":1730,"t":"update_user_status server API","u":"/docs/4/pro/user_status","h":"#update_user_status-server-api","p":1726},{"i":1732,"t":"get_user_status server API","u":"/docs/4/pro/user_status","h":"#get_user_status-server-api","p":1726},{"i":1734,"t":"delete_user_status server API","u":"/docs/4/pro/user_status","h":"#delete_user_status-server-api","p":1726},{"i":1736,"t":"Configuration","u":"/docs/4/pro/user_status","h":"#configuration","p":1726},{"i":1740,"t":"Subscription JWT claims","u":"/docs/4/server/channel_token_auth","h":"#subscription-jwt-claims","p":1738},{"i":1742,"t":"sub","u":"/docs/4/server/channel_token_auth","h":"#sub","p":1738},{"i":1744,"t":"channel","u":"/docs/4/server/channel_token_auth","h":"#channel","p":1738},{"i":1746,"t":"info","u":"/docs/4/server/channel_token_auth","h":"#info","p":1738},{"i":1748,"t":"b64info","u":"/docs/4/server/channel_token_auth","h":"#b64info","p":1738},{"i":1750,"t":"exp","u":"/docs/4/server/channel_token_auth","h":"#exp","p":1738},{"i":1752,"t":"expire_at","u":"/docs/4/server/channel_token_auth","h":"#expire_at","p":1738},{"i":1754,"t":"aud","u":"/docs/4/server/channel_token_auth","h":"#aud","p":1738},{"i":1756,"t":"iss","u":"/docs/4/server/channel_token_auth","h":"#iss","p":1738},{"i":1758,"t":"iat","u":"/docs/4/server/channel_token_auth","h":"#iat","p":1738},{"i":1760,"t":"jti","u":"/docs/4/server/channel_token_auth","h":"#jti","p":1738},{"i":1762,"t":"override","u":"/docs/4/server/channel_token_auth","h":"#override","p":1738},{"i":1764,"t":"Example","u":"/docs/4/server/channel_token_auth","h":"#example","p":1738},{"i":1766,"t":"With gensubtoken cli command","u":"/docs/4/server/channel_token_auth","h":"#with-gensubtoken-cli-command","p":1738},{"i":1770,"t":"Subscribe permission model","u":"/docs/4/server/channel_permissions","h":"#subscribe-permission-model","p":1768},{"i":1772,"t":"Publish permission model","u":"/docs/4/server/channel_permissions","h":"#publish-permission-model","p":1768},{"i":1774,"t":"History permission model","u":"/docs/4/server/channel_permissions","h":"#history-permission-model","p":1768},{"i":1776,"t":"Presence permission model","u":"/docs/4/server/channel_permissions","h":"#presence-permission-model","p":1768},{"i":1778,"t":"Positioning permission model","u":"/docs/4/server/channel_permissions","h":"#positioning-permission-model","p":1768},{"i":1780,"t":"Recovery permission model","u":"/docs/4/server/channel_permissions","h":"#recovery-permission-model","p":1768},{"i":1782,"t":"Join/Leave permission model","u":"/docs/4/server/channel_permissions","h":"#joinleave-permission-model","p":1768},{"i":1786,"t":"Connection JWT claims","u":"/docs/4/server/authentication","h":"#connection-jwt-claims","p":1784},{"i":1788,"t":"sub","u":"/docs/4/server/authentication","h":"#sub","p":1784},{"i":1790,"t":"exp","u":"/docs/4/server/authentication","h":"#exp","p":1784},{"i":1792,"t":"iat","u":"/docs/4/server/authentication","h":"#iat","p":1784},{"i":1794,"t":"jti","u":"/docs/4/server/authentication","h":"#jti","p":1784},{"i":1796,"t":"aud","u":"/docs/4/server/authentication","h":"#aud","p":1784},{"i":1798,"t":"iss","u":"/docs/4/server/authentication","h":"#iss","p":1784},{"i":1800,"t":"info","u":"/docs/4/server/authentication","h":"#info","p":1784},{"i":1802,"t":"b64info","u":"/docs/4/server/authentication","h":"#b64info","p":1784},{"i":1804,"t":"channels","u":"/docs/4/server/authentication","h":"#channels","p":1784},{"i":1806,"t":"subs","u":"/docs/4/server/authentication","h":"#subs","p":1784},{"i":1808,"t":"meta","u":"/docs/4/server/authentication","h":"#meta","p":1784},{"i":1810,"t":"expire_at","u":"/docs/4/server/authentication","h":"#expire_at","p":1784},{"i":1812,"t":"Connection expiration","u":"/docs/4/server/authentication","h":"#connection-expiration","p":1784},{"i":1814,"t":"Examples","u":"/docs/4/server/authentication","h":"#examples","p":1784},{"i":1816,"t":"Simplest token","u":"/docs/4/server/authentication","h":"#simplest-token","p":1784},{"i":1818,"t":"Token with expiration","u":"/docs/4/server/authentication","h":"#token-with-expiration","p":1784},{"i":1820,"t":"Token with additional connection info","u":"/docs/4/server/authentication","h":"#token-with-additional-connection-info","p":1784},{"i":1822,"t":"Investigating problems with JWT","u":"/docs/4/server/authentication","h":"#investigating-problems-with-jwt","p":1784},{"i":1824,"t":"JSON Web Key support","u":"/docs/4/server/authentication","h":"#json-web-key-support","p":1784},{"i":1826,"t":"Dynamic JWKs endpoint","u":"/docs/4/server/authentication","h":"#dynamic-jwks-endpoint","p":1784},{"i":1830,"t":"What is channel","u":"/docs/4/server/channels","h":"#what-is-channel","p":1828},{"i":1832,"t":"Channel name rules","u":"/docs/4/server/channels","h":"#channel-name-rules","p":1828},{"i":1834,"t":"namespace boundary (:)","u":"/docs/4/server/channels","h":"#namespace-boundary-","p":1828},{"i":1836,"t":"user channel boundary (#)","u":"/docs/4/server/channels","h":"#user-channel-boundary-","p":1828},{"i":1838,"t":"private channel prefix ($)","u":"/docs/4/server/channels","h":"#private-channel-prefix-","p":1828},{"i":1840,"t":"Channel is just a string","u":"/docs/4/server/channels","h":"#channel-is-just-a-string","p":1828},{"i":1842,"t":"Channel namespaces","u":"/docs/4/server/channels","h":"#channel-namespaces","p":1828},{"i":1844,"t":"Channel options","u":"/docs/4/server/channels","h":"#channel-options","p":1828},{"i":1846,"t":"presence","u":"/docs/4/server/channels","h":"#presence","p":1828},{"i":1848,"t":"join_leave","u":"/docs/4/server/channels","h":"#join_leave","p":1828},{"i":1850,"t":"force_push_join_leave","u":"/docs/4/server/channels","h":"#force_push_join_leave","p":1828},{"i":1852,"t":"history_size","u":"/docs/4/server/channels","h":"#history_size","p":1828},{"i":1854,"t":"history_ttl","u":"/docs/4/server/channels","h":"#history_ttl","p":1828},{"i":1856,"t":"force_positioning","u":"/docs/4/server/channels","h":"#force_positioning","p":1828},{"i":1858,"t":"force_recovery","u":"/docs/4/server/channels","h":"#force_recovery","p":1828},{"i":1860,"t":"allow_subscribe_for_client","u":"/docs/4/server/channels","h":"#allow_subscribe_for_client","p":1828},{"i":1862,"t":"allow_subscribe_for_anonymous","u":"/docs/4/server/channels","h":"#allow_subscribe_for_anonymous","p":1828},{"i":1864,"t":"allow_publish_for_subscriber","u":"/docs/4/server/channels","h":"#allow_publish_for_subscriber","p":1828},{"i":1866,"t":"allow_publish_for_client","u":"/docs/4/server/channels","h":"#allow_publish_for_client","p":1828},{"i":1868,"t":"allow_publish_for_anonymous","u":"/docs/4/server/channels","h":"#allow_publish_for_anonymous","p":1828},{"i":1870,"t":"allow_history_for_subscriber","u":"/docs/4/server/channels","h":"#allow_history_for_subscriber","p":1828},{"i":1872,"t":"allow_history_for_client","u":"/docs/4/server/channels","h":"#allow_history_for_client","p":1828},{"i":1874,"t":"allow_history_for_anonymous","u":"/docs/4/server/channels","h":"#allow_history_for_anonymous","p":1828},{"i":1876,"t":"allow_presence_for_subscriber","u":"/docs/4/server/channels","h":"#allow_presence_for_subscriber","p":1828},{"i":1878,"t":"allow_presence_for_client","u":"/docs/4/server/channels","h":"#allow_presence_for_client","p":1828},{"i":1880,"t":"allow_presence_for_anonymous","u":"/docs/4/server/channels","h":"#allow_presence_for_anonymous","p":1828},{"i":1882,"t":"allow_user_limited_channels","u":"/docs/4/server/channels","h":"#allow_user_limited_channels","p":1828},{"i":1884,"t":"channel_regex","u":"/docs/4/server/channels","h":"#channel_regex","p":1828},{"i":1886,"t":"proxy_subscribe","u":"/docs/4/server/channels","h":"#proxy_subscribe","p":1828},{"i":1888,"t":"proxy_publish","u":"/docs/4/server/channels","h":"#proxy_publish","p":1828},{"i":1890,"t":"proxy_sub_refresh","u":"/docs/4/server/channels","h":"#proxy_sub_refresh","p":1828},{"i":1892,"t":"subscribe_proxy_name","u":"/docs/4/server/channels","h":"#subscribe_proxy_name","p":1828},{"i":1894,"t":"publish_proxy_name","u":"/docs/4/server/channels","h":"#publish_proxy_name","p":1828},{"i":1896,"t":"sub_refresh_proxy_name","u":"/docs/4/server/channels","h":"#sub_refresh_proxy_name","p":1828},{"i":1898,"t":"Channel config examples","u":"/docs/4/server/channels","h":"#channel-config-examples","p":1828},{"i":1902,"t":"version","u":"/docs/4/server/console_commands","h":"#version","p":1900},{"i":1904,"t":"genconfig","u":"/docs/4/server/console_commands","h":"#genconfig","p":1900},{"i":1906,"t":"checkconfig","u":"/docs/4/server/console_commands","h":"#checkconfig","p":1900},{"i":1908,"t":"gentoken","u":"/docs/4/server/console_commands","h":"#gentoken","p":1900},{"i":1910,"t":"gensubtoken","u":"/docs/4/server/console_commands","h":"#gensubtoken","p":1900},{"i":1912,"t":"checktoken","u":"/docs/4/server/console_commands","h":"#checktoken","p":1900},{"i":1914,"t":"checksubtoken","u":"/docs/4/server/console_commands","h":"#checksubtoken","p":1900},{"i":1918,"t":"Open files limit","u":"/docs/4/server/infra_tuning","h":"#open-files-limit","p":1916},{"i":1920,"t":"Ephemeral port exhaustion","u":"/docs/4/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":1916},{"i":1922,"t":"Sockets in TIME_WAIT state","u":"/docs/4/server/infra_tuning","h":"#sockets-in-time_wait-state","p":1916},{"i":1924,"t":"Proxy max connections","u":"/docs/4/server/infra_tuning","h":"#proxy-max-connections","p":1916},{"i":1926,"t":"Conntrack table","u":"/docs/4/server/infra_tuning","h":"#conntrack-table","p":1916},{"i":1928,"t":"Additional server protection","u":"/docs/4/server/infra_tuning","h":"#additional-server-protection","p":1916},{"i":1932,"t":"Client error codes","u":"/docs/4/server/codes","h":"#client-error-codes","p":1930},{"i":1934,"t":"Internal","u":"/docs/4/server/codes","h":"#internal","p":1930},{"i":1936,"t":"Unauthorized","u":"/docs/4/server/codes","h":"#unauthorized","p":1930},{"i":1938,"t":"Unknown Channel","u":"/docs/4/server/codes","h":"#unknown-channel","p":1930},{"i":1940,"t":"Permission Denied","u":"/docs/4/server/codes","h":"#permission-denied","p":1930},{"i":1942,"t":"Method Not Found","u":"/docs/4/server/codes","h":"#method-not-found","p":1930},{"i":1944,"t":"Already Subscribed","u":"/docs/4/server/codes","h":"#already-subscribed","p":1930},{"i":1946,"t":"Limit Exceeded","u":"/docs/4/server/codes","h":"#limit-exceeded","p":1930},{"i":1948,"t":"Bad Request","u":"/docs/4/server/codes","h":"#bad-request","p":1930},{"i":1950,"t":"Not Available","u":"/docs/4/server/codes","h":"#not-available","p":1930},{"i":1952,"t":"Token Expired","u":"/docs/4/server/codes","h":"#token-expired","p":1930},{"i":1954,"t":"Expired","u":"/docs/4/server/codes","h":"#expired","p":1930},{"i":1956,"t":"Too Many Requests","u":"/docs/4/server/codes","h":"#too-many-requests","p":1930},{"i":1958,"t":"Unrecoverable Position","u":"/docs/4/server/codes","h":"#unrecoverable-position","p":1930},{"i":1960,"t":"Client disconnect codes","u":"/docs/4/server/codes","h":"#client-disconnect-codes","p":1930},{"i":1962,"t":"DisconnectConnectionClosed","u":"/docs/4/server/codes","h":"#disconnectconnectionclosed","p":1930},{"i":1964,"t":"Non-terminal disconnect codes","u":"/docs/4/server/codes","h":"#non-terminal-disconnect-codes","p":1930},{"i":1966,"t":"Terminal disconnect codes","u":"/docs/4/server/codes","h":"#terminal-disconnect-codes","p":1930},{"i":1970,"t":"Prometheus","u":"/docs/4/server/monitoring","h":"#prometheus","p":1968},{"i":1972,"t":"Graphite","u":"/docs/4/server/monitoring","h":"#graphite","p":1968},{"i":1974,"t":"Grafana dashboard","u":"/docs/4/server/monitoring","h":"#grafana-dashboard","p":1968},{"i":1978,"t":"History design","u":"/docs/4/server/history_and_recovery","h":"#history-design","p":1976},{"i":1980,"t":"History iteration API","u":"/docs/4/server/history_and_recovery","h":"#history-iteration-api","p":1976},{"i":1982,"t":"Automatic message recovery","u":"/docs/4/server/history_and_recovery","h":"#automatic-message-recovery","p":1976},{"i":1986,"t":"Configuration sources","u":"/docs/4/server/configuration","h":"#configuration-sources","p":1984},{"i":1988,"t":"Command-line flags","u":"/docs/4/server/configuration","h":"#command-line-flags","p":1984},{"i":1990,"t":"OS environment variables","u":"/docs/4/server/configuration","h":"#os-environment-variables","p":1984},{"i":1992,"t":"Configuration file","u":"/docs/4/server/configuration","h":"#configuration-file","p":1984},{"i":1994,"t":"Config file formats","u":"/docs/4/server/configuration","h":"#config-file-formats","p":1984},{"i":1996,"t":"JSON config format","u":"/docs/4/server/configuration","h":"#json-config-format","p":1984},{"i":1998,"t":"TOML config format","u":"/docs/4/server/configuration","h":"#toml-config-format","p":1984},{"i":2000,"t":"YAML config format","u":"/docs/4/server/configuration","h":"#yaml-config-format","p":1984},{"i":2002,"t":"Important options","u":"/docs/4/server/configuration","h":"#important-options","p":1984},{"i":2004,"t":"allowed_origins","u":"/docs/4/server/configuration","h":"#allowed_origins","p":1984},{"i":2006,"t":"address","u":"/docs/4/server/configuration","h":"#address","p":1984},{"i":2008,"t":"port","u":"/docs/4/server/configuration","h":"#port","p":1984},{"i":2010,"t":"engine","u":"/docs/4/server/configuration","h":"#engine","p":1984},{"i":2012,"t":"Advanced options","u":"/docs/4/server/configuration","h":"#advanced-options","p":1984},{"i":2014,"t":"client_channel_limit","u":"/docs/4/server/configuration","h":"#client_channel_limit","p":1984},{"i":2016,"t":"channel_max_length","u":"/docs/4/server/configuration","h":"#channel_max_length","p":1984},{"i":2018,"t":"client_user_connection_limit","u":"/docs/4/server/configuration","h":"#client_user_connection_limit","p":1984},{"i":2020,"t":"client_connection_limit","u":"/docs/4/server/configuration","h":"#client_connection_limit","p":1984},{"i":2022,"t":"client_connection_rate_limit","u":"/docs/4/server/configuration","h":"#client_connection_rate_limit","p":1984},{"i":2024,"t":"client_queue_max_size","u":"/docs/4/server/configuration","h":"#client_queue_max_size","p":1984},{"i":2026,"t":"client_concurrency","u":"/docs/4/server/configuration","h":"#client_concurrency","p":1984},{"i":2028,"t":"client_stale_close_delay","u":"/docs/4/server/configuration","h":"#client_stale_close_delay","p":1984},{"i":2030,"t":"allow_anonymous_connect_without_token","u":"/docs/4/server/configuration","h":"#allow_anonymous_connect_without_token","p":1984},{"i":2032,"t":"disallow_anonymous_connection_tokens","u":"/docs/4/server/configuration","h":"#disallow_anonymous_connection_tokens","p":1984},{"i":2034,"t":"gomaxprocs","u":"/docs/4/server/configuration","h":"#gomaxprocs","p":1984},{"i":2036,"t":"Endpoint configuration.","u":"/docs/4/server/configuration","h":"#endpoint-configuration","p":1984},{"i":2038,"t":"Default endpoints.","u":"/docs/4/server/configuration","h":"#default-endpoints","p":1984},{"i":2040,"t":"Admin endpoints.","u":"/docs/4/server/configuration","h":"#admin-endpoints","p":1984},{"i":2042,"t":"Debug endpoints.","u":"/docs/4/server/configuration","h":"#debug-endpoints","p":1984},{"i":2044,"t":"Health check endpoint","u":"/docs/4/server/configuration","h":"#health-check-endpoint","p":1984},{"i":2046,"t":"Custom internal ports","u":"/docs/4/server/configuration","h":"#custom-internal-ports","p":1984},{"i":2048,"t":"Disable default endpoints","u":"/docs/4/server/configuration","h":"#disable-default-endpoints","p":1984},{"i":2050,"t":"Customize handler endpoints","u":"/docs/4/server/configuration","h":"#customize-handler-endpoints","p":1984},{"i":2052,"t":"Signal handling","u":"/docs/4/server/configuration","h":"#signal-handling","p":1984},{"i":2054,"t":"Insecure modes","u":"/docs/4/server/configuration","h":"#insecure-modes","p":1984},{"i":2055,"t":"Insecure client connection","u":"/docs/4/server/configuration","h":"#insecure-client-connection","p":1984},{"i":2057,"t":"Insecure API mode","u":"/docs/4/server/configuration","h":"#insecure-api-mode","p":1984},{"i":2059,"t":"Insecure admin mode","u":"/docs/4/server/configuration","h":"#insecure-admin-mode","p":1984},{"i":2061,"t":"Setting time duration options","u":"/docs/4/server/configuration","h":"#setting-time-duration-options","p":1984},{"i":2063,"t":"Setting namespaces over env","u":"/docs/4/server/configuration","h":"#setting-namespaces-over-env","p":1984},{"i":2065,"t":"Anonymous usage stats","u":"/docs/4/server/configuration","h":"#anonymous-usage-stats","p":1984},{"i":2069,"t":"Nginx configuration","u":"/docs/4/server/load_balancing","h":"#nginx-configuration","p":2067},{"i":2071,"t":"Separate domain for Centrifugo","u":"/docs/4/server/load_balancing","h":"#separate-domain-for-centrifugo","p":2067},{"i":2073,"t":"Embed to a location of web site","u":"/docs/4/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":2067},{"i":2075,"t":"worker_connections","u":"/docs/4/server/load_balancing","h":"#worker_connections","p":2067},{"i":2079,"t":"Memory engine","u":"/docs/4/server/engines","h":"#memory-engine","p":2077},{"i":2081,"t":"Memory engine options","u":"/docs/4/server/engines","h":"#memory-engine-options","p":2077},{"i":2083,"t":"Redis engine","u":"/docs/4/server/engines","h":"#redis-engine","p":2077},{"i":2085,"t":"Redis engine options","u":"/docs/4/server/engines","h":"#redis-engine-options","p":2077},{"i":2087,"t":"Configuring Redis TLS","u":"/docs/4/server/engines","h":"#configuring-redis-tls","p":2077},{"i":2089,"t":"Scaling with Redis tutorial","u":"/docs/4/server/engines","h":"#scaling-with-redis-tutorial","p":2077},{"i":2091,"t":"Redis Sentinel for high availability","u":"/docs/4/server/engines","h":"#redis-sentinel-for-high-availability","p":2077},{"i":2093,"t":"Redis Sentinel TLS","u":"/docs/4/server/engines","h":"#redis-sentinel-tls","p":2077},{"i":2095,"t":"Haproxy instead of Sentinel configuration","u":"/docs/4/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":2077},{"i":2097,"t":"Redis sharding","u":"/docs/4/server/engines","h":"#redis-sharding","p":2077},{"i":2099,"t":"Redis cluster","u":"/docs/4/server/engines","h":"#redis-cluster","p":2077},{"i":2101,"t":"KeyDB Engine","u":"/docs/4/server/engines","h":"#keydb-engine","p":2077},{"i":2103,"t":"Other Redis compatible","u":"/docs/4/server/engines","h":"#other-redis-compatible","p":2077},{"i":2105,"t":"Tarantool engine","u":"/docs/4/server/engines","h":"#tarantool-engine","p":2077},{"i":2107,"t":"Tarantool engine options","u":"/docs/4/server/engines","h":"#tarantool-engine-options","p":2077},{"i":2109,"t":"Nats broker","u":"/docs/4/server/engines","h":"#nats-broker","p":2077},{"i":2111,"t":"Options","u":"/docs/4/server/engines","h":"#options","p":2077},{"i":2115,"t":"Overview","u":"/docs/4/server/presence","h":"#overview","p":2113},{"i":2117,"t":"Enabling Online Presence","u":"/docs/4/server/presence","h":"#enabling-online-presence","p":2113},{"i":2119,"t":"Retrieving presence on the client side","u":"/docs/4/server/presence","h":"#retrieving-presence-on-the-client-side","p":2113},{"i":2121,"t":"Join and leave events","u":"/docs/4/server/presence","h":"#join-and-leave-events","p":2113},{"i":2123,"t":"Implementation notes","u":"/docs/4/server/presence","h":"#implementation-notes","p":2113},{"i":2125,"t":"Conclusion","u":"/docs/4/server/presence","h":"#conclusion","p":2113},{"i":2129,"t":"Dynamic server-side subscriptions","u":"/docs/4/server/server_subs","h":"#dynamic-server-side-subscriptions","p":2127},{"i":2131,"t":"Automatic personal channel subscription","u":"/docs/4/server/server_subs","h":"#automatic-personal-channel-subscription","p":2127},{"i":2133,"t":"Maintain single user connection","u":"/docs/4/server/server_subs","h":"#maintain-single-user-connection","p":2127},{"i":2137,"t":"Using crt and key files","u":"/docs/4/server/tls","h":"#using-crt-and-key-files","p":2135},{"i":2139,"t":"Automatic certificates","u":"/docs/4/server/tls","h":"#automatic-certificates","p":2135},{"i":2141,"t":"TLS for GRPC API","u":"/docs/4/server/tls","h":"#tls-for-grpc-api","p":2135},{"i":2143,"t":"TLS for GRPC unidirectional stream","u":"/docs/4/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":2135},{"i":2147,"t":"Protobuf schema","u":"/docs/4/transports/client_protocol","h":"#protobuf-schema","p":2145},{"i":2149,"t":"Command-Reply","u":"/docs/4/transports/client_protocol","h":"#command-reply","p":2145},{"i":2151,"t":"Asynchronous pushes","u":"/docs/4/transports/client_protocol","h":"#asynchronous-pushes","p":2145},{"i":2153,"t":"Top level batching","u":"/docs/4/transports/client_protocol","h":"#top-level-batching","p":2145},{"i":2155,"t":"Ping Pong","u":"/docs/4/transports/client_protocol","h":"#ping-pong","p":2145},{"i":2157,"t":"Handle disconnects","u":"/docs/4/transports/client_protocol","h":"#handle-disconnects","p":2145},{"i":2159,"t":"Handle errors","u":"/docs/4/transports/client_protocol","h":"#handle-errors","p":2145},{"i":2161,"t":"Additional notes","u":"/docs/4/transports/client_protocol","h":"#additional-notes","p":2145},{"i":2165,"t":"List of client SDKs","u":"/docs/4/transports/client_sdk","h":"#list-of-client-sdks","p":2163},{"i":2167,"t":"Protobuf and JSON formats in SDKs","u":"/docs/4/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":2163},{"i":2169,"t":"SDK feature matrix","u":"/docs/4/transports/client_sdk","h":"#sdk-feature-matrix","p":2163},{"i":2171,"t":"Connection related features","u":"/docs/4/transports/client_sdk","h":"#connection-related-features","p":2163},{"i":2173,"t":"Client-side subscription related features","u":"/docs/4/transports/client_sdk","h":"#client-side-subscription-related-features","p":2163},{"i":2177,"t":"Options","u":"/docs/4/transports/http_stream","h":"#options","p":2175},{"i":2178,"t":"http_stream","u":"/docs/4/transports/http_stream","h":"#http_stream","p":2175},{"i":2180,"t":"http_stream_max_request_body_size","u":"/docs/4/transports/http_stream","h":"#http_stream_max_request_body_size","p":2175},{"i":2184,"t":"Bidirectional","u":"/docs/4/transports/overview","h":"#bidirectional","p":2182},{"i":2186,"t":"Unidirectional","u":"/docs/4/transports/overview","h":"#unidirectional","p":2182},{"i":2188,"t":"Unidirectional message types","u":"/docs/4/transports/overview","h":"#unidirectional-message-types","p":2182},{"i":2190,"t":"PING/PONG behavior","u":"/docs/4/transports/overview","h":"#pingpong-behavior","p":2182},{"i":2194,"t":"SockJS caveats","u":"/docs/4/transports/sockjs","h":"#sockjs-caveats","p":2192},{"i":2196,"t":"Sticky sessions","u":"/docs/4/transports/sockjs","h":"#sticky-sessions","p":2192},{"i":2198,"t":"Browser only","u":"/docs/4/transports/sockjs","h":"#browser-only","p":2192},{"i":2200,"t":"JSON only","u":"/docs/4/transports/sockjs","h":"#json-only","p":2192},{"i":2202,"t":"Options","u":"/docs/4/transports/sockjs","h":"#options","p":2192},{"i":2203,"t":"sockjs","u":"/docs/4/transports/sockjs","h":"#sockjs","p":2192},{"i":2205,"t":"sockjs_url","u":"/docs/4/transports/sockjs","h":"#sockjs_url","p":2192},{"i":2209,"t":"Options","u":"/docs/4/transports/sse","h":"#options","p":2207},{"i":2210,"t":"sse","u":"/docs/4/transports/sse","h":"#sse","p":2207},{"i":2212,"t":"sse_max_request_body_size","u":"/docs/4/transports/sse","h":"#sse_max_request_body_size","p":2207},{"i":2216,"t":"Connect command","u":"/docs/4/transports/uni_http_stream","h":"#connect-command","p":2214},{"i":2218,"t":"Supported data formats","u":"/docs/4/transports/uni_http_stream","h":"#supported-data-formats","p":2214},{"i":2220,"t":"Pings","u":"/docs/4/transports/uni_http_stream","h":"#pings","p":2214},{"i":2222,"t":"Options","u":"/docs/4/transports/uni_http_stream","h":"#options","p":2214},{"i":2223,"t":"uni_http_stream","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream","p":2214},{"i":2225,"t":"uni_http_stream_max_request_body_size","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":2214},{"i":2227,"t":"Connecting using CURL","u":"/docs/4/transports/uni_http_stream","h":"#connecting-using-curl","p":2214},{"i":2229,"t":"Browser example","u":"/docs/4/transports/uni_http_stream","h":"#browser-example","p":2214},{"i":2233,"t":"Connect command","u":"/docs/4/transports/uni_sse","h":"#connect-command","p":2231},{"i":2235,"t":"Supported data formats","u":"/docs/4/transports/uni_sse","h":"#supported-data-formats","p":2231},{"i":2237,"t":"Options","u":"/docs/4/transports/uni_sse","h":"#options","p":2231},{"i":2238,"t":"uni_sse","u":"/docs/4/transports/uni_sse","h":"#uni_sse","p":2231},{"i":2240,"t":"uni_sse_max_request_body_size","u":"/docs/4/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":2231},{"i":2242,"t":"Browser example","u":"/docs/4/transports/uni_sse","h":"#browser-example","p":2231},{"i":2246,"t":"Connect command","u":"/docs/4/transports/uni_websocket","h":"#connect-command","p":2244},{"i":2248,"t":"SubscribeRequest","u":"/docs/4/transports/uni_websocket","h":"#subscriberequest","p":2244},{"i":2250,"t":"Supported data formats","u":"/docs/4/transports/uni_websocket","h":"#supported-data-formats","p":2244},{"i":2252,"t":"Pings","u":"/docs/4/transports/uni_websocket","h":"#pings","p":2244},{"i":2254,"t":"Options","u":"/docs/4/transports/uni_websocket","h":"#options","p":2244},{"i":2255,"t":"uni_websocket","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket","p":2244},{"i":2257,"t":"uni_websocket_message_size_limit","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":2244},{"i":2259,"t":"Example","u":"/docs/4/transports/uni_websocket","h":"#example","p":2244},{"i":2263,"t":"Supported data formats","u":"/docs/4/transports/uni_grpc","h":"#supported-data-formats","p":2261},{"i":2265,"t":"Options","u":"/docs/4/transports/uni_grpc","h":"#options","p":2261},{"i":2266,"t":"uni_grpc","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc","p":2261},{"i":2268,"t":"uni_grpc_port","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_port","p":2261},{"i":2270,"t":"uni_grpc_address","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_address","p":2261},{"i":2272,"t":"uni_grpc_max_receive_message_size","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":2261},{"i":2274,"t":"uni_grpc_tls","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls","p":2261},{"i":2276,"t":"uni_grpc_tls_cert","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":2261},{"i":2278,"t":"uni_grpc_tls_key","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_key","p":2261},{"i":2280,"t":"Example","u":"/docs/4/transports/uni_grpc","h":"#example","p":2261},{"i":2284,"t":"HTTP API","u":"/docs/4/server/server_api","h":"#http-api","p":2282},{"i":2286,"t":"HTTP API authorization","u":"/docs/4/server/server_api","h":"#http-api-authorization","p":2282},{"i":2288,"t":"publish","u":"/docs/4/server/server_api","h":"#publish","p":2282},{"i":2290,"t":"broadcast","u":"/docs/4/server/server_api","h":"#broadcast","p":2282},{"i":2292,"t":"subscribe","u":"/docs/4/server/server_api","h":"#subscribe","p":2282},{"i":2294,"t":"unsubscribe","u":"/docs/4/server/server_api","h":"#unsubscribe","p":2282},{"i":2296,"t":"disconnect","u":"/docs/4/server/server_api","h":"#disconnect","p":2282},{"i":2298,"t":"refresh","u":"/docs/4/server/server_api","h":"#refresh","p":2282},{"i":2300,"t":"presence","u":"/docs/4/server/server_api","h":"#presence","p":2282},{"i":2302,"t":"presence_stats","u":"/docs/4/server/server_api","h":"#presence_stats","p":2282},{"i":2304,"t":"history","u":"/docs/4/server/server_api","h":"#history","p":2282},{"i":2306,"t":"history_remove","u":"/docs/4/server/server_api","h":"#history_remove","p":2282},{"i":2308,"t":"channels","u":"/docs/4/server/server_api","h":"#channels","p":2282},{"i":2310,"t":"info","u":"/docs/4/server/server_api","h":"#info","p":2282},{"i":2312,"t":"Command pipelining","u":"/docs/4/server/server_api","h":"#command-pipelining","p":2282},{"i":2314,"t":"HTTP API libraries","u":"/docs/4/server/server_api","h":"#http-api-libraries","p":2282},{"i":2316,"t":"GRPC API","u":"/docs/4/server/server_api","h":"#grpc-api","p":2282},{"i":2318,"t":"GRPC example for Python","u":"/docs/4/server/server_api","h":"#grpc-example-for-python","p":2282},{"i":2320,"t":"GRPC example for Go","u":"/docs/4/server/server_api","h":"#grpc-example-for-go","p":2282},{"i":2322,"t":"GRPC API key authorization","u":"/docs/4/server/server_api","h":"#grpc-api-key-authorization","p":2282},{"i":2326,"t":"Landing Page Images","u":"/docs/attributions","h":"#landing-page-images","p":2324},{"i":2330,"t":"Options","u":"/docs/4/transports/websocket","h":"#options","p":2328},{"i":2331,"t":"websocket_message_size_limit","u":"/docs/4/transports/websocket","h":"#websocket_message_size_limit","p":2328},{"i":2333,"t":"websocket_read_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_read_buffer_size","p":2328},{"i":2335,"t":"websocket_write_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_write_buffer_size","p":2328},{"i":2337,"t":"websocket_use_write_buffer_pool","u":"/docs/4/transports/websocket","h":"#websocket_use_write_buffer_pool","p":2328},{"i":2339,"t":"websocket_compression","u":"/docs/4/transports/websocket","h":"#websocket_compression","p":2328},{"i":2341,"t":"Protobuf binary protocol","u":"/docs/4/transports/websocket","h":"#protobuf-binary-protocol","p":2328},{"i":2345,"t":"HTTP proxy","u":"/docs/4/server/proxy","h":"#http-proxy","p":2343},{"i":2347,"t":"HTTP request structure","u":"/docs/4/server/proxy","h":"#http-request-structure","p":2343},{"i":2349,"t":"Proxy HTTP headers","u":"/docs/4/server/proxy","h":"#proxy-http-headers","p":2343},{"i":2351,"t":"Proxy GRPC metadata","u":"/docs/4/server/proxy","h":"#proxy-grpc-metadata","p":2343},{"i":2353,"t":"Connect proxy","u":"/docs/4/server/proxy","h":"#connect-proxy","p":2343},{"i":2355,"t":"Refresh proxy","u":"/docs/4/server/proxy","h":"#refresh-proxy","p":2343},{"i":2357,"t":"RPC proxy","u":"/docs/4/server/proxy","h":"#rpc-proxy","p":2343},{"i":2359,"t":"Subscribe proxy","u":"/docs/4/server/proxy","h":"#subscribe-proxy","p":2343},{"i":2361,"t":"Publish proxy","u":"/docs/4/server/proxy","h":"#publish-proxy","p":2343},{"i":2363,"t":"Sub refresh proxy","u":"/docs/4/server/proxy","h":"#sub-refresh-proxy","p":2343},{"i":2365,"t":"Return custom error","u":"/docs/4/server/proxy","h":"#return-custom-error","p":2343},{"i":2367,"t":"Return custom disconnect","u":"/docs/4/server/proxy","h":"#return-custom-disconnect","p":2343},{"i":2369,"t":"GRPC proxy","u":"/docs/4/server/proxy","h":"#grpc-proxy","p":2343},{"i":2371,"t":"GRPC proxy options","u":"/docs/4/server/proxy","h":"#grpc-proxy-options","p":2343},{"i":2373,"t":"GRPC proxy example","u":"/docs/4/server/proxy","h":"#grpc-proxy-example","p":2343},{"i":2375,"t":"Header proxy rules","u":"/docs/4/server/proxy","h":"#header-proxy-rules","p":2343},{"i":2377,"t":"Binary mode","u":"/docs/4/server/proxy","h":"#binary-mode","p":2343},{"i":2379,"t":"Granular proxy mode","u":"/docs/4/server/proxy","h":"#granular-proxy-mode","p":2343},{"i":2381,"t":"Enable granular proxy mode","u":"/docs/4/server/proxy","h":"#enable-granular-proxy-mode","p":2343},{"i":2383,"t":"Defining a list of proxies","u":"/docs/4/server/proxy","h":"#defining-a-list-of-proxies","p":2343},{"i":2385,"t":"Granular connect and refresh","u":"/docs/4/server/proxy","h":"#granular-connect-and-refresh","p":2343},{"i":2387,"t":"Granular subscribe, publish, sub refresh","u":"/docs/4/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":2343},{"i":2389,"t":"Granular RPC","u":"/docs/4/server/proxy","h":"#granular-rpc","p":2343},{"i":2399,"t":"Idiomatic usage","u":"/docs/getting-started/design","h":"#idiomatic-usage","p":2397},{"i":2401,"t":"Message history considerations","u":"/docs/getting-started/design","h":"#message-history-considerations","p":2397},{"i":2403,"t":"Message delivery model","u":"/docs/getting-started/design","h":"#message-delivery-model","p":2397},{"i":2405,"t":"Message order guarantees","u":"/docs/getting-started/design","h":"#message-order-guarantees","p":2397},{"i":2407,"t":"Graceful degradation","u":"/docs/getting-started/design","h":"#graceful-degradation","p":2397},{"i":2409,"t":"Online presence considerations","u":"/docs/getting-started/design","h":"#online-presence-considerations","p":2397},{"i":2411,"t":"Scalability considerations","u":"/docs/getting-started/design","h":"#scalability-considerations","p":2397},{"i":2415,"t":"Centrifugo vs Socket.io","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-socketio","p":2413},{"i":2417,"t":"Centrifugo vs Pusher, Ably, Pubnub","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-pusher-ably-pubnub","p":2413},{"i":2419,"t":"Centrifugo vs Redis","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-redis","p":2413},{"i":2421,"t":"Centrifugo vs Kafka","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-kafka","p":2413},{"i":2423,"t":"Centrifugo vs Nats","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-nats","p":2413},{"i":2427,"t":"Centrifuge library for Go","u":"/docs/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":2425},{"i":2429,"t":"Framework integrations","u":"/docs/getting-started/ecosystem","h":"#framework-integrations","p":2425},{"i":2433,"t":"Simple integration","u":"/docs/getting-started/highlights","h":"#simple-integration","p":2431},{"i":2435,"t":"Great performance","u":"/docs/getting-started/highlights","h":"#great-performance","p":2431},{"i":2437,"t":"Built-in scalability","u":"/docs/getting-started/highlights","h":"#built-in-scalability","p":2431},{"i":2439,"t":"Strict client protocol","u":"/docs/getting-started/highlights","h":"#strict-client-protocol","p":2431},{"i":2441,"t":"Variety of real-time transports","u":"/docs/getting-started/highlights","h":"#variety-of-real-time-transports","p":2431},{"i":2443,"t":"Flexible authentication","u":"/docs/getting-started/highlights","h":"#flexible-authentication","p":2431},{"i":2445,"t":"Connection management","u":"/docs/getting-started/highlights","h":"#connection-management","p":2431},{"i":2447,"t":"Channel (room) concept","u":"/docs/getting-started/highlights","h":"#channel-room-concept","p":2431},{"i":2449,"t":"Different types of subscriptions","u":"/docs/getting-started/highlights","h":"#different-types-of-subscriptions","p":2431},{"i":2451,"t":"RPC over bidirectional connection","u":"/docs/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":2431},{"i":2453,"t":"Online presence information","u":"/docs/getting-started/highlights","h":"#online-presence-information","p":2431},{"i":2455,"t":"Message history in channels","u":"/docs/getting-started/highlights","h":"#message-history-in-channels","p":2431},{"i":2457,"t":"Embedded admin web UI","u":"/docs/getting-started/highlights","h":"#embedded-admin-web-ui","p":2431},{"i":2459,"t":"Cross-platform","u":"/docs/getting-started/highlights","h":"#cross-platform","p":2431},{"i":2461,"t":"Ready to deploy","u":"/docs/getting-started/highlights","h":"#ready-to-deploy","p":2431},{"i":2463,"t":"Open-source","u":"/docs/getting-started/highlights","h":"#open-source","p":2431},{"i":2465,"t":"PRO features","u":"/docs/getting-started/highlights","h":"#pro-features","p":2431},{"i":2469,"t":"How many connections can one Centrifugo instance handle?","u":"/docs/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":2467},{"i":2471,"t":"Memory usage per connection?","u":"/docs/faq","h":"#memory-usage-per-connection","p":2467},{"i":2473,"t":"Can Centrifugo scale horizontally?","u":"/docs/faq","h":"#can-centrifugo-scale-horizontally","p":2467},{"i":2475,"t":"Message delivery model","u":"/docs/faq","h":"#message-delivery-model","p":2467},{"i":2477,"t":"Message order guarantees","u":"/docs/faq","h":"#message-order-guarantees","p":2467},{"i":2479,"t":"Should I create channels explicitly?","u":"/docs/faq","h":"#should-i-create-channels-explicitly","p":2467},{"i":2481,"t":"What about best practices with the number of channels?","u":"/docs/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":2467},{"i":2483,"t":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":2467},{"i":2485,"t":"Can I have both binary and JSON clients in one channel?","u":"/docs/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":2467},{"i":2487,"t":"Online presence for chat apps - online status of your contacts","u":"/docs/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":2467},{"i":2489,"t":"Centrifugo stops accepting new connections, why?","u":"/docs/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":2467},{"i":2491,"t":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":2467},{"i":2493,"t":"Does Centrifugo work with HTTP/2?","u":"/docs/faq","h":"#does-centrifugo-work-with-http2","p":2467},{"i":2495,"t":"Does Centrifugo work with HTTP/3?","u":"/docs/faq","h":"#does-centrifugo-work-with-http3","p":2467},{"i":2497,"t":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":2467},{"i":2499,"t":"What if I need to send push notifications to mobile or web applications?","u":"/docs/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":2467},{"i":2501,"t":"How can I know a message is delivered to a client?","u":"/docs/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":2467},{"i":2503,"t":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":2467},{"i":2505,"t":"How to create a secure channel for two users only (private chat case)?","u":"/docs/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":2467},{"i":2507,"t":"What's the best way to organize channel configuration?","u":"/docs/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":2467},{"i":2509,"t":"Does Centrifugo support webhooks?","u":"/docs/faq","h":"#does-centrifugo-support-webhooks","p":2467},{"i":2511,"t":"Why Centrifugo does not have disconnect hooks?","u":"/docs/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":2467},{"i":2513,"t":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":2467},{"i":2515,"t":"How scalable is the online presence and join/leave features?","u":"/docs/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":2467},{"i":2517,"t":"How to send initial data to channel subscriber?","u":"/docs/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":2467},{"i":2519,"t":"Does Centrifugo support multitenancy?","u":"/docs/faq","h":"#does-centrifugo-support-multitenancy","p":2467},{"i":2521,"t":"I have not found an answer to my question here","u":"/docs/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":2467},{"i":2525,"t":"Background","u":"/docs/getting-started/introduction","h":"#background","p":2523},{"i":2529,"t":"Client SDK migration","u":"/docs/getting-started/migration_v4","h":"#client-sdk-migration","p":2527},{"i":2531,"t":"Unidirectional transport migration","u":"/docs/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":2527},{"i":2533,"t":"SockJS migration","u":"/docs/getting-started/migration_v4","h":"#sockjs-migration","p":2527},{"i":2535,"t":"Channel ASCII enforced","u":"/docs/getting-started/migration_v4","h":"#channel-ascii-enforced","p":2527},{"i":2537,"t":"Subscription token migration","u":"/docs/getting-started/migration_v4","h":"#subscription-token-migration","p":2527},{"i":2539,"t":"User-limited channel migration","u":"/docs/getting-started/migration_v4","h":"#user-limited-channel-migration","p":2527},{"i":2541,"t":"Namespace configuration migration","u":"/docs/getting-started/migration_v4","h":"#namespace-configuration-migration","p":2527},{"i":2543,"t":"Proxy disconnect code changes","u":"/docs/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":2527},{"i":2545,"t":"Other configuration option changes","u":"/docs/getting-started/migration_v4","h":"#other-configuration-option-changes","p":2527},{"i":2547,"t":"Server API changes","u":"/docs/getting-started/migration_v4","h":"#server-api-changes","p":2527},{"i":2551,"t":"0. Install","u":"/docs/getting-started/integration","h":"#0-install","p":2549},{"i":2553,"t":"1. Configure Centrifugo","u":"/docs/getting-started/integration","h":"#1-configure-centrifugo","p":2549},{"i":2555,"t":"2. Configure your backend","u":"/docs/getting-started/integration","h":"#2-configure-your-backend","p":2549},{"i":2557,"t":"3. Connect to Centrifugo","u":"/docs/getting-started/integration","h":"#3-connect-to-centrifugo","p":2549},{"i":2559,"t":"4. Subscribe to channels","u":"/docs/getting-started/integration","h":"#4-subscribe-to-channels","p":2549},{"i":2561,"t":"5. Publish to a channel","u":"/docs/getting-started/integration","h":"#5-publish-to-a-channel","p":2549},{"i":2563,"t":"6. Deploy to production","u":"/docs/getting-started/integration","h":"#6-deploy-to-production","p":2549},{"i":2565,"t":"7. Monitor Centrifugo","u":"/docs/getting-started/integration","h":"#7-monitor-centrifugo","p":2549},{"i":2567,"t":"8. Scale Centrifugo","u":"/docs/getting-started/integration","h":"#8-scale-centrifugo","p":2549},{"i":2569,"t":"9. Read FAQ","u":"/docs/getting-started/integration","h":"#9-read-faq","p":2549},{"i":2573,"t":"Client SDK token behaviour adjustments","u":"/docs/getting-started/migration_v5","h":"#client-sdk-token-behaviour-adjustments","p":2571},{"i":2575,"t":"Node communication format changed","u":"/docs/getting-started/migration_v5","h":"#node-communication-format-changed","p":2571},{"i":2577,"t":"Old HTTP API format is DEPRECATED","u":"/docs/getting-started/migration_v5","h":"#old-http-api-format-is-deprecated","p":2571},{"i":2579,"t":"Other changes","u":"/docs/getting-started/migration_v5","h":"#other-changes","p":2571},{"i":2581,"t":"Shutting down Centrifugo v2 doc site","u":"/docs/getting-started/migration_v5","h":"#shutting-down-centrifugo-v2-doc-site","p":2571},{"i":2585,"t":"Connecting to a server","u":"/docs/getting-started/client_api","h":"#connecting-to-a-server","p":2583},{"i":2587,"t":"Disconnecting from a server","u":"/docs/getting-started/client_api","h":"#disconnecting-from-a-server","p":2583},{"i":2589,"t":"Reconnecting to a server","u":"/docs/getting-started/client_api","h":"#reconnecting-to-a-server","p":2583},{"i":2591,"t":"Connection lifecycle events","u":"/docs/getting-started/client_api","h":"#connection-lifecycle-events","p":2583},{"i":2593,"t":"Subscribe to a channel","u":"/docs/getting-started/client_api","h":"#subscribe-to-a-channel","p":2583},{"i":2595,"t":"Server-side subscriptions","u":"/docs/getting-started/client_api","h":"#server-side-subscriptions","p":2583},{"i":2597,"t":"Send RPC","u":"/docs/getting-started/client_api","h":"#send-rpc","p":2583},{"i":2599,"t":"Call channel history","u":"/docs/getting-started/client_api","h":"#call-channel-history","p":2583},{"i":2601,"t":"Presence and presence stats","u":"/docs/getting-started/client_api","h":"#presence-and-presence-stats","p":2583},{"i":2605,"t":"Install from the binary release","u":"/docs/getting-started/installation","h":"#install-from-the-binary-release","p":2603},{"i":2607,"t":"Docker image","u":"/docs/getting-started/installation","h":"#docker-image","p":2603},{"i":2609,"t":"Docker-compose example","u":"/docs/getting-started/installation","h":"#docker-compose-example","p":2603},{"i":2611,"t":"Kubernetes Helm chart","u":"/docs/getting-started/installation","h":"#kubernetes-helm-chart","p":2603},{"i":2613,"t":"RPM and DEB packages for Linux","u":"/docs/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":2603},{"i":2615,"t":"With brew on macOS","u":"/docs/getting-started/installation","h":"#with-brew-on-macos","p":2603},{"i":2617,"t":"Build from source","u":"/docs/getting-started/installation","h":"#build-from-source","p":2603},{"i":2621,"t":"Client connection states","u":"/docs/4/transports/client_api","h":"#client-connection-states","p":2619},{"i":2623,"t":"Client common options","u":"/docs/4/transports/client_api","h":"#client-common-options","p":2619},{"i":2625,"t":"Client methods","u":"/docs/4/transports/client_api","h":"#client-methods","p":2619},{"i":2627,"t":"Client connection token","u":"/docs/4/transports/client_api","h":"#client-connection-token","p":2619},{"i":2629,"t":"Connection PING/PONG","u":"/docs/4/transports/client_api","h":"#connection-pingpong","p":2619},{"i":2631,"t":"Subscription states","u":"/docs/4/transports/client_api","h":"#subscription-states","p":2619},{"i":2633,"t":"Subscription management","u":"/docs/4/transports/client_api","h":"#subscription-management","p":2619},{"i":2635,"t":"Listen to channel publications","u":"/docs/4/transports/client_api","h":"#listen-to-channel-publications","p":2619},{"i":2637,"t":"Subscription recovery state","u":"/docs/4/transports/client_api","h":"#subscription-recovery-state","p":2619},{"i":2639,"t":"Subscription common options","u":"/docs/4/transports/client_api","h":"#subscription-common-options","p":2619},{"i":2641,"t":"Subscription methods","u":"/docs/4/transports/client_api","h":"#subscription-methods","p":2619},{"i":2643,"t":"Subscription token","u":"/docs/4/transports/client_api","h":"#subscription-token","p":2619},{"i":2645,"t":"Server-side subscriptions","u":"/docs/4/transports/client_api","h":"#server-side-subscriptions","p":2619},{"i":2647,"t":"Error codes","u":"/docs/4/transports/client_api","h":"#error-codes","p":2619},{"i":2649,"t":"Unsubscribe codes","u":"/docs/4/transports/client_api","h":"#unsubscribe-codes","p":2619},{"i":2651,"t":"Disconnect codes","u":"/docs/4/transports/client_api","h":"#disconnect-codes","p":2619},{"i":2653,"t":"RPC","u":"/docs/4/transports/client_api","h":"#rpc","p":2619},{"i":2655,"t":"Channel history API","u":"/docs/4/transports/client_api","h":"#channel-history-api","p":2619},{"i":2657,"t":"Presence and presence stats API","u":"/docs/4/transports/client_api","h":"#presence-and-presence-stats-api","p":2619},{"i":2659,"t":"SDK common best practices","u":"/docs/4/transports/client_api","h":"#sdk-common-best-practices","p":2619},{"i":2663,"t":"Connection capabilities","u":"/docs/pro/capabilities","h":"#connection-capabilities","p":2661},{"i":2665,"t":"Caps processing behavior","u":"/docs/pro/capabilities","h":"#caps-processing-behavior","p":2661},{"i":2667,"t":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations","p":2661},{"i":2669,"t":"Revoking connection caps","u":"/docs/pro/capabilities","h":"#revoking-connection-caps","p":2661},{"i":2671,"t":"Example: wildcard match","u":"/docs/pro/capabilities","h":"#example-wildcard-match","p":2661},{"i":2673,"t":"Example: regex match","u":"/docs/pro/capabilities","h":"#example-regex-match","p":2661},{"i":2675,"t":"Example: different types of match","u":"/docs/pro/capabilities","h":"#example-different-types-of-match","p":2661},{"i":2677,"t":"Example: full access to all channels","u":"/docs/pro/capabilities","h":"#example-full-access-to-all-channels","p":2661},{"i":2679,"t":"Subscription capabilities","u":"/docs/pro/capabilities","h":"#subscription-capabilities","p":2661},{"i":2681,"t":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations-1","p":2661},{"i":2683,"t":"Revoking subscription permissions","u":"/docs/pro/capabilities","h":"#revoking-subscription-permissions","p":2661},{"i":2689,"t":"subscribe_cel","u":"/docs/pro/cel_expressions","h":"#subscribe_cel","p":2687},{"i":2691,"t":"Expression variables","u":"/docs/pro/cel_expressions","h":"#expression-variables","p":2687},{"i":2693,"t":"publish_cel","u":"/docs/pro/cel_expressions","h":"#publish_cel","p":2687},{"i":2695,"t":"history_cel","u":"/docs/pro/cel_expressions","h":"#history_cel","p":2687},{"i":2697,"t":"presence_cel","u":"/docs/pro/cel_expressions","h":"#presence_cel","p":2687},{"i":2701,"t":"client_write_delay","u":"/docs/pro/client_message_batching","h":"#client_write_delay","p":2699},{"i":2703,"t":"client_reply_without_queue","u":"/docs/pro/client_message_batching","h":"#client_reply_without_queue","p":2699},{"i":2705,"t":"client_max_messages_in_frame","u":"/docs/pro/client_message_batching","h":"#client_max_messages_in_frame","p":2699},{"i":2711,"t":"Overview","u":"/docs/pro/distributed_rate_limit","h":"#overview","p":2709},{"i":2713,"t":"Configuration","u":"/docs/pro/distributed_rate_limit","h":"#configuration","p":2709},{"i":2715,"t":"API description","u":"/docs/pro/distributed_rate_limit","h":"#api-description","p":2709},{"i":2717,"t":"rate_limit request","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-request","p":2709},{"i":2719,"t":"rate_limit result","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-result","p":2709},{"i":2723,"t":"Binary release","u":"/docs/pro/install_and_run","h":"#binary-release","p":2721},{"i":2725,"t":"Docker image","u":"/docs/pro/install_and_run","h":"#docker-image","p":2721},{"i":2727,"t":"Kubernetes","u":"/docs/pro/install_and_run","h":"#kubernetes","p":2721},{"i":2729,"t":"Debian and Ubuntu","u":"/docs/pro/install_and_run","h":"#debian-and-ubuntu","p":2721},{"i":2731,"t":"Centos","u":"/docs/pro/install_and_run","h":"#centos","p":2721},{"i":2733,"t":"Setting PRO license key","u":"/docs/pro/install_and_run","h":"#setting-pro-license-key","p":2721},{"i":2739,"t":"Faster HTTP API","u":"/docs/pro/performance","h":"#faster-http-api","p":2737},{"i":2741,"t":"Faster GRPC API","u":"/docs/pro/performance","h":"#faster-grpc-api","p":2737},{"i":2743,"t":"Faster HTTP proxy","u":"/docs/pro/performance","h":"#faster-http-proxy","p":2737},{"i":2745,"t":"Faster GRPC proxy","u":"/docs/pro/performance","h":"#faster-grpc-proxy","p":2737},{"i":2747,"t":"Faster JWT decoding","u":"/docs/pro/performance","h":"#faster-jwt-decoding","p":2737},{"i":2749,"t":"Faster GRPC unidirectional stream","u":"/docs/pro/performance","h":"#faster-grpc-unidirectional-stream","p":2737},{"i":2751,"t":"Examples","u":"/docs/pro/performance","h":"#examples","p":2737},{"i":2753,"t":"Publish HTTP API","u":"/docs/pro/performance","h":"#publish-http-api","p":2737},{"i":2755,"t":"History HTTP API","u":"/docs/pro/performance","h":"#history-http-api","p":2737},{"i":2759,"t":"Features","u":"/docs/pro/overview","h":"#features","p":2757},{"i":2761,"t":"Pricing","u":"/docs/pro/overview","h":"#pricing","p":2757},{"i":2763,"t":"Try for free in sandbox mode","u":"/docs/pro/overview","h":"#try-for-free-in-sandbox-mode","p":2757},{"i":2767,"t":"Configuration","u":"/docs/pro/channel_patterns","h":"#configuration","p":2765},{"i":2769,"t":"Implementation details","u":"/docs/pro/channel_patterns","h":"#implementation-details","p":2765},{"i":2771,"t":"Variables","u":"/docs/pro/channel_patterns","h":"#variables","p":2765},{"i":2773,"t":"Using varibles","u":"/docs/pro/channel_patterns","h":"#using-varibles","p":2765},{"i":2779,"t":"Example","u":"/docs/pro/connections","h":"#example","p":2777},{"i":2781,"t":"connections","u":"/docs/pro/connections","h":"#connections","p":2777},{"i":2787,"t":"Configuration","u":"/docs/pro/analytics","h":"#configuration","p":2785},{"i":2789,"t":"Connections table","u":"/docs/pro/analytics","h":"#connections-table","p":2785},{"i":2791,"t":"Subscriptions table","u":"/docs/pro/analytics","h":"#subscriptions-table","p":2785},{"i":2793,"t":"Operations table","u":"/docs/pro/analytics","h":"#operations-table","p":2785},{"i":2795,"t":"Publications table","u":"/docs/pro/analytics","h":"#publications-table","p":2785},{"i":2797,"t":"Notifications table","u":"/docs/pro/analytics","h":"#notifications-table","p":2785},{"i":2799,"t":"Query examples","u":"/docs/pro/analytics","h":"#query-examples","p":2785},{"i":2801,"t":"Development","u":"/docs/pro/analytics","h":"#development","p":2785},{"i":2803,"t":"How export works","u":"/docs/pro/analytics","h":"#how-export-works","p":2785},{"i":2805,"t":"Exposed metrics","u":"/docs/pro/analytics","h":"#exposed-metrics","p":2785},{"i":2809,"t":"Save to a file","u":"/docs/pro/tracing","h":"#save-to-a-file","p":2807},{"i":2813,"t":"How it works","u":"/docs/pro/token_revocation","h":"#how-it-works","p":2811},{"i":2815,"t":"Configure","u":"/docs/pro/token_revocation","h":"#configure","p":2811},{"i":2817,"t":"Redis persistence engine","u":"/docs/pro/token_revocation","h":"#redis-persistence-engine","p":2811},{"i":2819,"t":"Database persistence engine","u":"/docs/pro/token_revocation","h":"#database-persistence-engine","p":2811},{"i":2821,"t":"Revoke token API","u":"/docs/pro/token_revocation","h":"#revoke-token-api","p":2811},{"i":2822,"t":"revoke_token","u":"/docs/pro/token_revocation","h":"#revoke_token","p":2811},{"i":2824,"t":"Invalidate user tokens API","u":"/docs/pro/token_revocation","h":"#invalidate-user-tokens-api","p":2811},{"i":2825,"t":"invalidate_user_tokens","u":"/docs/pro/token_revocation","h":"#invalidate_user_tokens","p":2811},{"i":2829,"t":"In-memory per connection rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-connection-rate-limit","p":2827},{"i":2831,"t":"In-memory per user rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-user-rate-limit","p":2827},{"i":2833,"t":"Redis per user rate limit","u":"/docs/pro/rate_limiting","h":"#redis-per-user-rate-limit","p":2827},{"i":2835,"t":"Disconnecting abusive or misbehaving connections","u":"/docs/pro/rate_limiting","h":"#disconnecting-abusive-or-misbehaving-connections","p":2827},{"i":2839,"t":"Motivation and design choices","u":"/docs/pro/push_notifications","h":"#motivation-and-design-choices","p":2837},{"i":2841,"t":"Storage for tokens","u":"/docs/pro/push_notifications","h":"#storage-for-tokens","p":2837},{"i":2843,"t":"Efficient queuing","u":"/docs/pro/push_notifications","h":"#efficient-queuing","p":2837},{"i":2845,"t":"Unified secure topics","u":"/docs/pro/push_notifications","h":"#unified-secure-topics","p":2837},{"i":2847,"t":"Non-obtrusive proxying","u":"/docs/pro/push_notifications","h":"#non-obtrusive-proxying","p":2837},{"i":2849,"t":"Builtin analytics","u":"/docs/pro/push_notifications","h":"#builtin-analytics","p":2837},{"i":2851,"t":"Steps to integrate","u":"/docs/pro/push_notifications","h":"#steps-to-integrate","p":2837},{"i":2853,"t":"Configuration","u":"/docs/pro/push_notifications","h":"#configuration","p":2837},{"i":2855,"t":"FCM","u":"/docs/pro/push_notifications","h":"#fcm","p":2837},{"i":2857,"t":"HMS","u":"/docs/pro/push_notifications","h":"#hms","p":2837},{"i":2859,"t":"APNs","u":"/docs/pro/push_notifications","h":"#apns","p":2837},{"i":2861,"t":"Other options","u":"/docs/pro/push_notifications","h":"#other-options","p":2837},{"i":2863,"t":"Use PostgreSQL as queue","u":"/docs/pro/push_notifications","h":"#use-postgresql-as-queue","p":2837},{"i":2865,"t":"API description","u":"/docs/pro/push_notifications","h":"#api-description","p":2837},{"i":2866,"t":"device_register","u":"/docs/pro/push_notifications","h":"#device_register","p":2837},{"i":2868,"t":"device_update","u":"/docs/pro/push_notifications","h":"#device_update","p":2837},{"i":2870,"t":"device_remove","u":"/docs/pro/push_notifications","h":"#device_remove","p":2837},{"i":2872,"t":"device_list","u":"/docs/pro/push_notifications","h":"#device_list","p":2837},{"i":2874,"t":"device_topic_update","u":"/docs/pro/push_notifications","h":"#device_topic_update","p":2837},{"i":2876,"t":"device_topic_list","u":"/docs/pro/push_notifications","h":"#device_topic_list","p":2837},{"i":2878,"t":"user_topic_update","u":"/docs/pro/push_notifications","h":"#user_topic_update","p":2837},{"i":2880,"t":"user_topic_list","u":"/docs/pro/push_notifications","h":"#user_topic_list","p":2837},{"i":2882,"t":"send_push_notification","u":"/docs/pro/push_notifications","h":"#send_push_notification","p":2837},{"i":2884,"t":"cancel_push","u":"/docs/pro/push_notifications","h":"#cancel_push","p":2837},{"i":2886,"t":"update_push_status","u":"/docs/pro/push_notifications","h":"#update_push_status","p":2837},{"i":2888,"t":"Exposed metrics","u":"/docs/pro/push_notifications","h":"#exposed-metrics","p":2837},{"i":2890,"t":"Further reading and tutorials","u":"/docs/pro/push_notifications","h":"#further-reading-and-tutorials","p":2837},{"i":2894,"t":"How it works","u":"/docs/pro/user_block","h":"#how-it-works","p":2892},{"i":2896,"t":"Configure","u":"/docs/pro/user_block","h":"#configure","p":2892},{"i":2898,"t":"Redis persistence engine","u":"/docs/pro/user_block","h":"#redis-persistence-engine","p":2892},{"i":2900,"t":"Database persistence engine","u":"/docs/pro/user_block","h":"#database-persistence-engine","p":2892},{"i":2902,"t":"Block API","u":"/docs/pro/user_block","h":"#block--api","p":2892},{"i":2903,"t":"block_user","u":"/docs/pro/user_block","h":"#block_user","p":2892},{"i":2905,"t":"unblock_user","u":"/docs/pro/user_block","h":"#unblock_user","p":2892},{"i":2909,"t":"Options","u":"/docs/server/admin_web","h":"#options","p":2907},{"i":2911,"t":"Using custom web interface","u":"/docs/server/admin_web","h":"#using-custom-web-interface","p":2907},{"i":2913,"t":"Admin insecure mode","u":"/docs/server/admin_web","h":"#admin-insecure-mode","p":2907},{"i":2917,"t":"Client-side status update RPC","u":"/docs/pro/user_status","h":"#client-side-status-update-rpc","p":2915},{"i":2919,"t":"update_user_status server API","u":"/docs/pro/user_status","h":"#update_user_status-server-api","p":2915},{"i":2921,"t":"get_user_status server API","u":"/docs/pro/user_status","h":"#get_user_status-server-api","p":2915},{"i":2923,"t":"delete_user_status server API","u":"/docs/pro/user_status","h":"#delete_user_status-server-api","p":2915},{"i":2925,"t":"Configuration","u":"/docs/pro/user_status","h":"#configuration","p":2915},{"i":2929,"t":"Subscribe permission model","u":"/docs/server/channel_permissions","h":"#subscribe-permission-model","p":2927},{"i":2931,"t":"Publish permission model","u":"/docs/server/channel_permissions","h":"#publish-permission-model","p":2927},{"i":2933,"t":"History permission model","u":"/docs/server/channel_permissions","h":"#history-permission-model","p":2927},{"i":2935,"t":"Presence permission model","u":"/docs/server/channel_permissions","h":"#presence-permission-model","p":2927},{"i":2937,"t":"Positioning permission model","u":"/docs/server/channel_permissions","h":"#positioning-permission-model","p":2927},{"i":2939,"t":"Recovery permission model","u":"/docs/server/channel_permissions","h":"#recovery-permission-model","p":2927},{"i":2941,"t":"Join/Leave permission model","u":"/docs/server/channel_permissions","h":"#joinleave-permission-model","p":2927},{"i":2945,"t":"Subscription JWT claims","u":"/docs/server/channel_token_auth","h":"#subscription-jwt-claims","p":2943},{"i":2947,"t":"sub","u":"/docs/server/channel_token_auth","h":"#sub","p":2943},{"i":2949,"t":"channel","u":"/docs/server/channel_token_auth","h":"#channel","p":2943},{"i":2951,"t":"info","u":"/docs/server/channel_token_auth","h":"#info","p":2943},{"i":2953,"t":"b64info","u":"/docs/server/channel_token_auth","h":"#b64info","p":2943},{"i":2955,"t":"exp","u":"/docs/server/channel_token_auth","h":"#exp","p":2943},{"i":2957,"t":"expire_at","u":"/docs/server/channel_token_auth","h":"#expire_at","p":2943},{"i":2959,"t":"aud","u":"/docs/server/channel_token_auth","h":"#aud","p":2943},{"i":2961,"t":"iss","u":"/docs/server/channel_token_auth","h":"#iss","p":2943},{"i":2963,"t":"iat","u":"/docs/server/channel_token_auth","h":"#iat","p":2943},{"i":2965,"t":"jti","u":"/docs/server/channel_token_auth","h":"#jti","p":2943},{"i":2967,"t":"override","u":"/docs/server/channel_token_auth","h":"#override","p":2943},{"i":2969,"t":"Example","u":"/docs/server/channel_token_auth","h":"#example","p":2943},{"i":2971,"t":"gensubtoken cli command","u":"/docs/server/channel_token_auth","h":"#gensubtoken-cli-command","p":2943},{"i":2973,"t":"Separate subscription token config","u":"/docs/server/channel_token_auth","h":"#separate-subscription-token-config","p":2943},{"i":2977,"t":"Client error codes","u":"/docs/server/codes","h":"#client-error-codes","p":2975},{"i":2979,"t":"Internal","u":"/docs/server/codes","h":"#internal","p":2975},{"i":2981,"t":"Unauthorized","u":"/docs/server/codes","h":"#unauthorized","p":2975},{"i":2983,"t":"Unknown Channel","u":"/docs/server/codes","h":"#unknown-channel","p":2975},{"i":2985,"t":"Permission Denied","u":"/docs/server/codes","h":"#permission-denied","p":2975},{"i":2987,"t":"Method Not Found","u":"/docs/server/codes","h":"#method-not-found","p":2975},{"i":2989,"t":"Already Subscribed","u":"/docs/server/codes","h":"#already-subscribed","p":2975},{"i":2991,"t":"Limit Exceeded","u":"/docs/server/codes","h":"#limit-exceeded","p":2975},{"i":2993,"t":"Bad Request","u":"/docs/server/codes","h":"#bad-request","p":2975},{"i":2995,"t":"Not Available","u":"/docs/server/codes","h":"#not-available","p":2975},{"i":2997,"t":"Token Expired","u":"/docs/server/codes","h":"#token-expired","p":2975},{"i":2999,"t":"Expired","u":"/docs/server/codes","h":"#expired","p":2975},{"i":3001,"t":"Too Many Requests","u":"/docs/server/codes","h":"#too-many-requests","p":2975},{"i":3003,"t":"Unrecoverable Position","u":"/docs/server/codes","h":"#unrecoverable-position","p":2975},{"i":3005,"t":"Client disconnect codes","u":"/docs/server/codes","h":"#client-disconnect-codes","p":2975},{"i":3007,"t":"DisconnectConnectionClosed","u":"/docs/server/codes","h":"#disconnectconnectionclosed","p":2975},{"i":3009,"t":"Non-terminal disconnect codes","u":"/docs/server/codes","h":"#non-terminal-disconnect-codes","p":2975},{"i":3011,"t":"Terminal disconnect codes","u":"/docs/server/codes","h":"#terminal-disconnect-codes","p":2975},{"i":3015,"t":"Connection JWT Claims","u":"/docs/server/authentication","h":"#connection-jwt-claims","p":3013},{"i":3017,"t":"sub","u":"/docs/server/authentication","h":"#sub","p":3013},{"i":3019,"t":"exp","u":"/docs/server/authentication","h":"#exp","p":3013},{"i":3021,"t":"iat","u":"/docs/server/authentication","h":"#iat","p":3013},{"i":3023,"t":"jti","u":"/docs/server/authentication","h":"#jti","p":3013},{"i":3025,"t":"aud","u":"/docs/server/authentication","h":"#aud","p":3013},{"i":3027,"t":"iss","u":"/docs/server/authentication","h":"#iss","p":3013},{"i":3029,"t":"info","u":"/docs/server/authentication","h":"#info","p":3013},{"i":3031,"t":"b64info","u":"/docs/server/authentication","h":"#b64info","p":3013},{"i":3033,"t":"channels","u":"/docs/server/authentication","h":"#channels","p":3013},{"i":3035,"t":"subs","u":"/docs/server/authentication","h":"#subs","p":3013},{"i":3037,"t":"meta","u":"/docs/server/authentication","h":"#meta","p":3013},{"i":3039,"t":"expire_at","u":"/docs/server/authentication","h":"#expire_at","p":3013},{"i":3041,"t":"Connection expiration","u":"/docs/server/authentication","h":"#connection-expiration","p":3013},{"i":3043,"t":"Examples","u":"/docs/server/authentication","h":"#examples","p":3013},{"i":3045,"t":"Simplest token","u":"/docs/server/authentication","h":"#simplest-token","p":3013},{"i":3047,"t":"Token with expiration","u":"/docs/server/authentication","h":"#token-with-expiration","p":3013},{"i":3049,"t":"Token with additional connection info","u":"/docs/server/authentication","h":"#token-with-additional-connection-info","p":3013},{"i":3051,"t":"Investigating problems with JWT","u":"/docs/server/authentication","h":"#investigating-problems-with-jwt","p":3013},{"i":3053,"t":"JSON Web Key support","u":"/docs/server/authentication","h":"#json-web-key-support","p":3013},{"i":3055,"t":"Dynamic JWKs endpoint","u":"/docs/server/authentication","h":"#dynamic-jwks-endpoint","p":3013},{"i":3059,"t":"What is a channel","u":"/docs/server/channels","h":"#what-is-a-channel","p":3057},{"i":3061,"t":"Channel name rules","u":"/docs/server/channels","h":"#channel-name-rules","p":3057},{"i":3063,"t":"namespace boundary (:)","u":"/docs/server/channels","h":"#namespace-boundary-","p":3057},{"i":3065,"t":"user channel boundary (#)","u":"/docs/server/channels","h":"#user-channel-boundary-","p":3057},{"i":3067,"t":"private channel prefix ($)","u":"/docs/server/channels","h":"#private-channel-prefix-","p":3057},{"i":3069,"t":"Channel is just a string","u":"/docs/server/channels","h":"#channel-is-just-a-string","p":3057},{"i":3071,"t":"Channel namespaces","u":"/docs/server/channels","h":"#channel-namespaces","p":3057},{"i":3073,"t":"Channel options","u":"/docs/server/channels","h":"#channel-options","p":3057},{"i":3075,"t":"presence","u":"/docs/server/channels","h":"#presence","p":3057},{"i":3077,"t":"join_leave","u":"/docs/server/channels","h":"#join_leave","p":3057},{"i":3079,"t":"force_push_join_leave","u":"/docs/server/channels","h":"#force_push_join_leave","p":3057},{"i":3081,"t":"history_size","u":"/docs/server/channels","h":"#history_size","p":3057},{"i":3083,"t":"history_ttl","u":"/docs/server/channels","h":"#history_ttl","p":3057},{"i":3085,"t":"history_meta_ttl","u":"/docs/server/channels","h":"#history_meta_ttl","p":3057},{"i":3087,"t":"force_positioning","u":"/docs/server/channels","h":"#force_positioning","p":3057},{"i":3089,"t":"force_recovery","u":"/docs/server/channels","h":"#force_recovery","p":3057},{"i":3091,"t":"allow_subscribe_for_client","u":"/docs/server/channels","h":"#allow_subscribe_for_client","p":3057},{"i":3093,"t":"allow_subscribe_for_anonymous","u":"/docs/server/channels","h":"#allow_subscribe_for_anonymous","p":3057},{"i":3095,"t":"allow_publish_for_subscriber","u":"/docs/server/channels","h":"#allow_publish_for_subscriber","p":3057},{"i":3097,"t":"allow_publish_for_client","u":"/docs/server/channels","h":"#allow_publish_for_client","p":3057},{"i":3099,"t":"allow_publish_for_anonymous","u":"/docs/server/channels","h":"#allow_publish_for_anonymous","p":3057},{"i":3101,"t":"allow_history_for_subscriber","u":"/docs/server/channels","h":"#allow_history_for_subscriber","p":3057},{"i":3103,"t":"allow_history_for_client","u":"/docs/server/channels","h":"#allow_history_for_client","p":3057},{"i":3105,"t":"allow_history_for_anonymous","u":"/docs/server/channels","h":"#allow_history_for_anonymous","p":3057},{"i":3107,"t":"allow_presence_for_subscriber","u":"/docs/server/channels","h":"#allow_presence_for_subscriber","p":3057},{"i":3109,"t":"allow_presence_for_client","u":"/docs/server/channels","h":"#allow_presence_for_client","p":3057},{"i":3111,"t":"allow_presence_for_anonymous","u":"/docs/server/channels","h":"#allow_presence_for_anonymous","p":3057},{"i":3113,"t":"allow_user_limited_channels","u":"/docs/server/channels","h":"#allow_user_limited_channels","p":3057},{"i":3115,"t":"channel_regex","u":"/docs/server/channels","h":"#channel_regex","p":3057},{"i":3117,"t":"proxy_subscribe","u":"/docs/server/channels","h":"#proxy_subscribe","p":3057},{"i":3119,"t":"proxy_publish","u":"/docs/server/channels","h":"#proxy_publish","p":3057},{"i":3121,"t":"proxy_sub_refresh","u":"/docs/server/channels","h":"#proxy_sub_refresh","p":3057},{"i":3123,"t":"proxy_subscribe_stream","u":"/docs/server/channels","h":"#proxy_subscribe_stream","p":3057},{"i":3125,"t":"subscribe_proxy_name","u":"/docs/server/channels","h":"#subscribe_proxy_name","p":3057},{"i":3127,"t":"publish_proxy_name","u":"/docs/server/channels","h":"#publish_proxy_name","p":3057},{"i":3129,"t":"sub_refresh_proxy_name","u":"/docs/server/channels","h":"#sub_refresh_proxy_name","p":3057},{"i":3131,"t":"subscribe_stream_proxy_name","u":"/docs/server/channels","h":"#subscribe_stream_proxy_name","p":3057},{"i":3133,"t":"Channel config examples","u":"/docs/server/channels","h":"#channel-config-examples","p":3057},{"i":3137,"t":"History design","u":"/docs/server/history_and_recovery","h":"#history-design","p":3135},{"i":3139,"t":"History iteration API","u":"/docs/server/history_and_recovery","h":"#history-iteration-api","p":3135},{"i":3141,"t":"Automatic message recovery","u":"/docs/server/history_and_recovery","h":"#automatic-message-recovery","p":3135},{"i":3145,"t":"Open files limit","u":"/docs/server/infra_tuning","h":"#open-files-limit","p":3143},{"i":3147,"t":"Ephemeral port exhaustion","u":"/docs/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":3143},{"i":3149,"t":"Sockets in TIME_WAIT state","u":"/docs/server/infra_tuning","h":"#sockets-in-time_wait-state","p":3143},{"i":3151,"t":"Proxy max connections","u":"/docs/server/infra_tuning","h":"#proxy-max-connections","p":3143},{"i":3153,"t":"Conntrack table","u":"/docs/server/infra_tuning","h":"#conntrack-table","p":3143},{"i":3155,"t":"Additional server protection","u":"/docs/server/infra_tuning","h":"#additional-server-protection","p":3143},{"i":3159,"t":"version","u":"/docs/server/console_commands","h":"#version","p":3157},{"i":3161,"t":"genconfig","u":"/docs/server/console_commands","h":"#genconfig","p":3157},{"i":3163,"t":"checkconfig","u":"/docs/server/console_commands","h":"#checkconfig","p":3157},{"i":3165,"t":"gentoken","u":"/docs/server/console_commands","h":"#gentoken","p":3157},{"i":3167,"t":"gensubtoken","u":"/docs/server/console_commands","h":"#gensubtoken","p":3157},{"i":3169,"t":"checktoken","u":"/docs/server/console_commands","h":"#checktoken","p":3157},{"i":3171,"t":"checksubtoken","u":"/docs/server/console_commands","h":"#checksubtoken","p":3157},{"i":3175,"t":"Configuration sources","u":"/docs/server/configuration","h":"#configuration-sources","p":3173},{"i":3177,"t":"Command-line flags","u":"/docs/server/configuration","h":"#command-line-flags","p":3173},{"i":3179,"t":"OS environment variables","u":"/docs/server/configuration","h":"#os-environment-variables","p":3173},{"i":3181,"t":"Configuration file","u":"/docs/server/configuration","h":"#configuration-file","p":3173},{"i":3183,"t":"Config file formats","u":"/docs/server/configuration","h":"#config-file-formats","p":3173},{"i":3185,"t":"JSON config format","u":"/docs/server/configuration","h":"#json-config-format","p":3173},{"i":3187,"t":"TOML config format","u":"/docs/server/configuration","h":"#toml-config-format","p":3173},{"i":3189,"t":"YAML config format","u":"/docs/server/configuration","h":"#yaml-config-format","p":3173},{"i":3191,"t":"Important options","u":"/docs/server/configuration","h":"#important-options","p":3173},{"i":3193,"t":"allowed_origins","u":"/docs/server/configuration","h":"#allowed_origins","p":3173},{"i":3195,"t":"address","u":"/docs/server/configuration","h":"#address","p":3173},{"i":3197,"t":"port","u":"/docs/server/configuration","h":"#port","p":3173},{"i":3199,"t":"engine","u":"/docs/server/configuration","h":"#engine","p":3173},{"i":3201,"t":"Advanced options","u":"/docs/server/configuration","h":"#advanced-options","p":3173},{"i":3203,"t":"client_channel_limit","u":"/docs/server/configuration","h":"#client_channel_limit","p":3173},{"i":3205,"t":"channel_max_length","u":"/docs/server/configuration","h":"#channel_max_length","p":3173},{"i":3207,"t":"client_user_connection_limit","u":"/docs/server/configuration","h":"#client_user_connection_limit","p":3173},{"i":3209,"t":"client_connection_limit","u":"/docs/server/configuration","h":"#client_connection_limit","p":3173},{"i":3211,"t":"client_connection_rate_limit","u":"/docs/server/configuration","h":"#client_connection_rate_limit","p":3173},{"i":3213,"t":"client_queue_max_size","u":"/docs/server/configuration","h":"#client_queue_max_size","p":3173},{"i":3215,"t":"client_concurrency","u":"/docs/server/configuration","h":"#client_concurrency","p":3173},{"i":3217,"t":"client_stale_close_delay","u":"/docs/server/configuration","h":"#client_stale_close_delay","p":3173},{"i":3219,"t":"client_user_id_http_header","u":"/docs/server/configuration","h":"#client_user_id_http_header","p":3173},{"i":3221,"t":"allow_anonymous_connect_without_token","u":"/docs/server/configuration","h":"#allow_anonymous_connect_without_token","p":3173},{"i":3223,"t":"disallow_anonymous_connection_tokens","u":"/docs/server/configuration","h":"#disallow_anonymous_connection_tokens","p":3173},{"i":3225,"t":"gomaxprocs","u":"/docs/server/configuration","h":"#gomaxprocs","p":3173},{"i":3227,"t":"Endpoint configuration","u":"/docs/server/configuration","h":"#endpoint-configuration","p":3173},{"i":3229,"t":"Default endpoints","u":"/docs/server/configuration","h":"#default-endpoints","p":3173},{"i":3231,"t":"Admin endpoints","u":"/docs/server/configuration","h":"#admin-endpoints","p":3173},{"i":3233,"t":"Debug endpoints","u":"/docs/server/configuration","h":"#debug-endpoints","p":3173},{"i":3235,"t":"Health check endpoint","u":"/docs/server/configuration","h":"#health-check-endpoint","p":3173},{"i":3237,"t":"Swagger UI for server API","u":"/docs/server/configuration","h":"#swagger-ui-for-server-api","p":3173},{"i":3239,"t":"Custom internal ports","u":"/docs/server/configuration","h":"#custom-internal-ports","p":3173},{"i":3241,"t":"Disable default endpoints","u":"/docs/server/configuration","h":"#disable-default-endpoints","p":3173},{"i":3243,"t":"Customize handler endpoints","u":"/docs/server/configuration","h":"#customize-handler-endpoints","p":3173},{"i":3245,"t":"Signal handling","u":"/docs/server/configuration","h":"#signal-handling","p":3173},{"i":3247,"t":"Insecure modes","u":"/docs/server/configuration","h":"#insecure-modes","p":3173},{"i":3248,"t":"Insecure client connection","u":"/docs/server/configuration","h":"#insecure-client-connection","p":3173},{"i":3250,"t":"Disable client token signature check","u":"/docs/server/configuration","h":"#disable-client-token-signature-check","p":3173},{"i":3252,"t":"Insecure API mode","u":"/docs/server/configuration","h":"#insecure-api-mode","p":3173},{"i":3254,"t":"Insecure admin mode","u":"/docs/server/configuration","h":"#insecure-admin-mode","p":3173},{"i":3256,"t":"Setting time duration options","u":"/docs/server/configuration","h":"#setting-time-duration-options","p":3173},{"i":3258,"t":"Setting namespaces over env","u":"/docs/server/configuration","h":"#setting-namespaces-over-env","p":3173},{"i":3260,"t":"Anonymous usage stats","u":"/docs/server/configuration","h":"#anonymous-usage-stats","p":3173},{"i":3264,"t":"Memory engine","u":"/docs/server/engines","h":"#memory-engine","p":3262},{"i":3266,"t":"Memory engine options","u":"/docs/server/engines","h":"#memory-engine-options","p":3262},{"i":3268,"t":"Redis engine","u":"/docs/server/engines","h":"#redis-engine","p":3262},{"i":3270,"t":"Redis engine options","u":"/docs/server/engines","h":"#redis-engine-options","p":3262},{"i":3272,"t":"Configuring Redis TLS","u":"/docs/server/engines","h":"#configuring-redis-tls","p":3262},{"i":3274,"t":"Scaling with Redis tutorial","u":"/docs/server/engines","h":"#scaling-with-redis-tutorial","p":3262},{"i":3276,"t":"Redis Sentinel for high availability","u":"/docs/server/engines","h":"#redis-sentinel-for-high-availability","p":3262},{"i":3278,"t":"Redis Sentinel TLS","u":"/docs/server/engines","h":"#redis-sentinel-tls","p":3262},{"i":3280,"t":"Haproxy instead of Sentinel configuration","u":"/docs/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":3262},{"i":3282,"t":"Redis sharding","u":"/docs/server/engines","h":"#redis-sharding","p":3262},{"i":3284,"t":"Redis cluster","u":"/docs/server/engines","h":"#redis-cluster","p":3262},{"i":3286,"t":"Other Redis compatible","u":"/docs/server/engines","h":"#other-redis-compatible","p":3262},{"i":3288,"t":"Tarantool engine","u":"/docs/server/engines","h":"#tarantool-engine","p":3262},{"i":3290,"t":"Tarantool engine options","u":"/docs/server/engines","h":"#tarantool-engine-options","p":3262},{"i":3292,"t":"Nats broker","u":"/docs/server/engines","h":"#nats-broker","p":3262},{"i":3294,"t":"Options","u":"/docs/server/engines","h":"#options","p":3262},{"i":3298,"t":"Prometheus","u":"/docs/server/monitoring","h":"#prometheus","p":3296},{"i":3300,"t":"Graphite","u":"/docs/server/monitoring","h":"#graphite","p":3296},{"i":3302,"t":"Grafana dashboard","u":"/docs/server/monitoring","h":"#grafana-dashboard","p":3296},{"i":3306,"t":"Nginx configuration","u":"/docs/server/load_balancing","h":"#nginx-configuration","p":3304},{"i":3308,"t":"Separate domain for Centrifugo","u":"/docs/server/load_balancing","h":"#separate-domain-for-centrifugo","p":3304},{"i":3310,"t":"Embed to a location of web site","u":"/docs/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":3304},{"i":3312,"t":"worker_connections","u":"/docs/server/load_balancing","h":"#worker_connections","p":3304},{"i":3316,"t":"Metrics","u":"/docs/server/observability","h":"#metrics","p":3314},{"i":3317,"t":"Prometheus metrics","u":"/docs/server/observability","h":"#prometheus-metrics","p":3314},{"i":3319,"t":"Graphite metrics","u":"/docs/server/observability","h":"#graphite-metrics","p":3314},{"i":3321,"t":"Grafana dashboard","u":"/docs/server/observability","h":"#grafana-dashboard","p":3314},{"i":3323,"t":"Exposed metrics","u":"/docs/server/observability","h":"#exposed-metrics","p":3314},{"i":3325,"t":"Traces","u":"/docs/server/observability","h":"#traces","p":3314},{"i":3326,"t":"OpenTelemetry","u":"/docs/server/observability","h":"#opentelemetry","p":3314},{"i":3328,"t":"Logs","u":"/docs/server/observability","h":"#logs","p":3314},{"i":3332,"t":"Dynamic server-side subscriptions","u":"/docs/server/server_subs","h":"#dynamic-server-side-subscriptions","p":3330},{"i":3334,"t":"Automatic personal channel subscription","u":"/docs/server/server_subs","h":"#automatic-personal-channel-subscription","p":3330},{"i":3336,"t":"Maintain single user connection","u":"/docs/server/server_subs","h":"#maintain-single-user-connection","p":3330},{"i":3340,"t":"Enabling online presence","u":"/docs/server/presence","h":"#enabling-online-presence","p":3338},{"i":3342,"t":"Retrieving presence on the client side","u":"/docs/server/presence","h":"#retrieving-presence-on-the-client-side","p":3338},{"i":3344,"t":"Join and leave events","u":"/docs/server/presence","h":"#join-and-leave-events","p":3338},{"i":3346,"t":"Implementation notes","u":"/docs/server/presence","h":"#implementation-notes","p":3338},{"i":3348,"t":"Conclusion","u":"/docs/server/presence","h":"#conclusion","p":3338},{"i":3352,"t":"Using crt and key files","u":"/docs/server/tls","h":"#using-crt-and-key-files","p":3350},{"i":3354,"t":"Automatic certificates","u":"/docs/server/tls","h":"#automatic-certificates","p":3350},{"i":3356,"t":"TLS for GRPC API","u":"/docs/server/tls","h":"#tls-for-grpc-api","p":3350},{"i":3358,"t":"TLS for GRPC unidirectional stream","u":"/docs/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":3350},{"i":3362,"t":"Scalability concerns","u":"/docs/server/proxy_streams","h":"#scalability-concerns","p":3360},{"i":3364,"t":"Motivation and design","u":"/docs/server/proxy_streams","h":"#motivation-and-design","p":3360},{"i":3366,"t":"Unidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#unidirectional-subscription-streams","p":3360},{"i":3368,"t":"Bidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#bidirectional-subscription-streams","p":3360},{"i":3370,"t":"Granular proxy mode","u":"/docs/server/proxy_streams","h":"#granular-proxy-mode","p":3360},{"i":3372,"t":"Full example","u":"/docs/server/proxy_streams","h":"#full-example","p":3360},{"i":3376,"t":"Protobuf schema","u":"/docs/transports/client_protocol","h":"#protobuf-schema","p":3374},{"i":3378,"t":"Command-Reply","u":"/docs/transports/client_protocol","h":"#command-reply","p":3374},{"i":3380,"t":"Asynchronous pushes","u":"/docs/transports/client_protocol","h":"#asynchronous-pushes","p":3374},{"i":3382,"t":"Top level batching","u":"/docs/transports/client_protocol","h":"#top-level-batching","p":3374},{"i":3384,"t":"Ping Pong","u":"/docs/transports/client_protocol","h":"#ping-pong","p":3374},{"i":3386,"t":"Handle disconnects","u":"/docs/transports/client_protocol","h":"#handle-disconnects","p":3374},{"i":3388,"t":"Handle errors","u":"/docs/transports/client_protocol","h":"#handle-errors","p":3374},{"i":3390,"t":"Additional notes","u":"/docs/transports/client_protocol","h":"#additional-notes","p":3374},{"i":3394,"t":"List of client SDKs","u":"/docs/transports/client_sdk","h":"#list-of-client-sdks","p":3392},{"i":3396,"t":"Protobuf and JSON formats in SDKs","u":"/docs/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":3392},{"i":3398,"t":"SDK feature matrix","u":"/docs/transports/client_sdk","h":"#sdk-feature-matrix","p":3392},{"i":3400,"t":"Connection related features","u":"/docs/transports/client_sdk","h":"#connection-related-features","p":3392},{"i":3402,"t":"Client-side subscription related features","u":"/docs/transports/client_sdk","h":"#client-side-subscription-related-features","p":3392},{"i":3406,"t":"HTTP proxy","u":"/docs/server/proxy","h":"#http-proxy","p":3404},{"i":3408,"t":"HTTP request structure","u":"/docs/server/proxy","h":"#http-request-structure","p":3404},{"i":3410,"t":"Proxy HTTP headers","u":"/docs/server/proxy","h":"#proxy-http-headers","p":3404},{"i":3412,"t":"Proxy GRPC metadata","u":"/docs/server/proxy","h":"#proxy-grpc-metadata","p":3404},{"i":3414,"t":"Connect proxy","u":"/docs/server/proxy","h":"#connect-proxy","p":3404},{"i":3416,"t":"Refresh proxy","u":"/docs/server/proxy","h":"#refresh-proxy","p":3404},{"i":3418,"t":"RPC proxy","u":"/docs/server/proxy","h":"#rpc-proxy","p":3404},{"i":3420,"t":"Subscribe proxy","u":"/docs/server/proxy","h":"#subscribe-proxy","p":3404},{"i":3422,"t":"Publish proxy","u":"/docs/server/proxy","h":"#publish-proxy","p":3404},{"i":3424,"t":"Sub refresh proxy","u":"/docs/server/proxy","h":"#sub-refresh-proxy","p":3404},{"i":3426,"t":"Return custom error","u":"/docs/server/proxy","h":"#return-custom-error","p":3404},{"i":3428,"t":"Return custom disconnect","u":"/docs/server/proxy","h":"#return-custom-disconnect","p":3404},{"i":3430,"t":"GRPC proxy","u":"/docs/server/proxy","h":"#grpc-proxy","p":3404},{"i":3432,"t":"GRPC proxy options","u":"/docs/server/proxy","h":"#grpc-proxy-options","p":3404},{"i":3434,"t":"GRPC proxy example","u":"/docs/server/proxy","h":"#grpc-proxy-example","p":3404},{"i":3436,"t":"Header proxy rules","u":"/docs/server/proxy","h":"#header-proxy-rules","p":3404},{"i":3438,"t":"Binary mode","u":"/docs/server/proxy","h":"#binary-mode","p":3404},{"i":3440,"t":"Granular proxy mode","u":"/docs/server/proxy","h":"#granular-proxy-mode","p":3404},{"i":3442,"t":"Enable granular proxy mode","u":"/docs/server/proxy","h":"#enable-granular-proxy-mode","p":3404},{"i":3444,"t":"Defining a list of proxies","u":"/docs/server/proxy","h":"#defining-a-list-of-proxies","p":3404},{"i":3446,"t":"Granular connect and refresh","u":"/docs/server/proxy","h":"#granular-connect-and-refresh","p":3404},{"i":3448,"t":"Granular subscribe, publish, sub refresh","u":"/docs/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":3404},{"i":3450,"t":"Granular RPC","u":"/docs/server/proxy","h":"#granular-rpc","p":3404},{"i":3454,"t":"Options","u":"/docs/transports/http_stream","h":"#options","p":3452},{"i":3455,"t":"http_stream","u":"/docs/transports/http_stream","h":"#http_stream","p":3452},{"i":3457,"t":"http_stream_max_request_body_size","u":"/docs/transports/http_stream","h":"#http_stream_max_request_body_size","p":3452},{"i":3461,"t":"Bidirectional","u":"/docs/transports/overview","h":"#bidirectional","p":3459},{"i":3463,"t":"Unidirectional","u":"/docs/transports/overview","h":"#unidirectional","p":3459},{"i":3465,"t":"PING/PONG behavior","u":"/docs/transports/overview","h":"#pingpong-behavior","p":3459},{"i":3469,"t":"SockJS caveats","u":"/docs/transports/sockjs","h":"#sockjs-caveats","p":3467},{"i":3471,"t":"Sticky sessions","u":"/docs/transports/sockjs","h":"#sticky-sessions","p":3467},{"i":3473,"t":"Browser only","u":"/docs/transports/sockjs","h":"#browser-only","p":3467},{"i":3475,"t":"JSON only","u":"/docs/transports/sockjs","h":"#json-only","p":3467},{"i":3477,"t":"Options","u":"/docs/transports/sockjs","h":"#options","p":3467},{"i":3478,"t":"sockjs","u":"/docs/transports/sockjs","h":"#sockjs","p":3467},{"i":3480,"t":"sockjs_url","u":"/docs/transports/sockjs","h":"#sockjs_url","p":3467},{"i":3484,"t":"Options","u":"/docs/transports/sse","h":"#options","p":3482},{"i":3485,"t":"sse","u":"/docs/transports/sse","h":"#sse","p":3482},{"i":3487,"t":"sse_max_request_body_size","u":"/docs/transports/sse","h":"#sse_max_request_body_size","p":3482},{"i":3491,"t":"Unidirectional message types","u":"/docs/transports/uni_client_protocol","h":"#unidirectional-message-types","p":3489},{"i":3495,"t":"HTTP API","u":"/docs/server/server_api","h":"#http-api","p":3493},{"i":3497,"t":"HTTP API authorization","u":"/docs/server/server_api","h":"#http-api-authorization","p":3493},{"i":3499,"t":"API methods","u":"/docs/server/server_api","h":"#api-methods","p":3493},{"i":3501,"t":"publish","u":"/docs/server/server_api","h":"#publish","p":3493},{"i":3503,"t":"broadcast","u":"/docs/server/server_api","h":"#broadcast","p":3493},{"i":3505,"t":"subscribe","u":"/docs/server/server_api","h":"#subscribe","p":3493},{"i":3507,"t":"unsubscribe","u":"/docs/server/server_api","h":"#unsubscribe","p":3493},{"i":3509,"t":"disconnect","u":"/docs/server/server_api","h":"#disconnect","p":3493},{"i":3511,"t":"refresh","u":"/docs/server/server_api","h":"#refresh","p":3493},{"i":3513,"t":"presence","u":"/docs/server/server_api","h":"#presence","p":3493},{"i":3515,"t":"presence_stats","u":"/docs/server/server_api","h":"#presence_stats","p":3493},{"i":3517,"t":"history","u":"/docs/server/server_api","h":"#history","p":3493},{"i":3519,"t":"history_remove","u":"/docs/server/server_api","h":"#history_remove","p":3493},{"i":3521,"t":"channels","u":"/docs/server/server_api","h":"#channels","p":3493},{"i":3523,"t":"info","u":"/docs/server/server_api","h":"#info","p":3493},{"i":3525,"t":"batch","u":"/docs/server/server_api","h":"#batch","p":3493},{"i":3527,"t":"HTTP API libraries","u":"/docs/server/server_api","h":"#http-api-libraries","p":3493},{"i":3529,"t":"GRPC API","u":"/docs/server/server_api","h":"#grpc-api","p":3493},{"i":3531,"t":"GRPC example for Python","u":"/docs/server/server_api","h":"#grpc-example-for-python","p":3493},{"i":3533,"t":"GRPC example for Go","u":"/docs/server/server_api","h":"#grpc-example-for-go","p":3493},{"i":3535,"t":"GRPC API key authorization","u":"/docs/server/server_api","h":"#grpc-api-key-authorization","p":3493},{"i":3537,"t":"Transport error mode","u":"/docs/server/server_api","h":"#transport-error-mode","p":3493},{"i":3539,"t":"Centrifugo error code to HTTP code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-http-code","p":3493},{"i":3541,"t":"Centrifugo error code to GRPC code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-grpc-code","p":3493},{"i":3545,"t":"Supported data formats","u":"/docs/transports/uni_grpc","h":"#supported-data-formats","p":3543},{"i":3547,"t":"Options","u":"/docs/transports/uni_grpc","h":"#options","p":3543},{"i":3548,"t":"uni_grpc","u":"/docs/transports/uni_grpc","h":"#uni_grpc","p":3543},{"i":3550,"t":"uni_grpc_port","u":"/docs/transports/uni_grpc","h":"#uni_grpc_port","p":3543},{"i":3552,"t":"uni_grpc_address","u":"/docs/transports/uni_grpc","h":"#uni_grpc_address","p":3543},{"i":3554,"t":"uni_grpc_max_receive_message_size","u":"/docs/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":3543},{"i":3556,"t":"uni_grpc_tls","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls","p":3543},{"i":3558,"t":"uni_grpc_tls_cert","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":3543},{"i":3560,"t":"uni_grpc_tls_key","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_key","p":3543},{"i":3562,"t":"Example","u":"/docs/transports/uni_grpc","h":"#example","p":3543},{"i":3566,"t":"Connect command","u":"/docs/transports/uni_http_stream","h":"#connect-command","p":3564},{"i":3568,"t":"Supported data formats","u":"/docs/transports/uni_http_stream","h":"#supported-data-formats","p":3564},{"i":3570,"t":"Pings","u":"/docs/transports/uni_http_stream","h":"#pings","p":3564},{"i":3572,"t":"Options","u":"/docs/transports/uni_http_stream","h":"#options","p":3564},{"i":3573,"t":"uni_http_stream","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream","p":3564},{"i":3575,"t":"uni_http_stream_max_request_body_size","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":3564},{"i":3577,"t":"Connecting using CURL","u":"/docs/transports/uni_http_stream","h":"#connecting-using-curl","p":3564},{"i":3579,"t":"Browser example","u":"/docs/transports/uni_http_stream","h":"#browser-example","p":3564},{"i":3583,"t":"Connect command","u":"/docs/transports/uni_sse","h":"#connect-command","p":3581},{"i":3585,"t":"Supported data formats","u":"/docs/transports/uni_sse","h":"#supported-data-formats","p":3581},{"i":3587,"t":"Options","u":"/docs/transports/uni_sse","h":"#options","p":3581},{"i":3588,"t":"uni_sse","u":"/docs/transports/uni_sse","h":"#uni_sse","p":3581},{"i":3590,"t":"uni_sse_max_request_body_size","u":"/docs/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":3581},{"i":3592,"t":"Browser example","u":"/docs/transports/uni_sse","h":"#browser-example","p":3581},{"i":3596,"t":"Connect command","u":"/docs/transports/uni_websocket","h":"#connect-command","p":3594},{"i":3598,"t":"SubscribeRequest","u":"/docs/transports/uni_websocket","h":"#subscriberequest","p":3594},{"i":3600,"t":"Supported data formats","u":"/docs/transports/uni_websocket","h":"#supported-data-formats","p":3594},{"i":3602,"t":"Pings","u":"/docs/transports/uni_websocket","h":"#pings","p":3594},{"i":3604,"t":"Options","u":"/docs/transports/uni_websocket","h":"#options","p":3594},{"i":3605,"t":"uni_websocket","u":"/docs/transports/uni_websocket","h":"#uni_websocket","p":3594},{"i":3607,"t":"uni_websocket_message_size_limit","u":"/docs/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":3594},{"i":3609,"t":"Example","u":"/docs/transports/uni_websocket","h":"#example","p":3594},{"i":3615,"t":"Options","u":"/docs/transports/websocket","h":"#options","p":3613},{"i":3616,"t":"websocket_message_size_limit","u":"/docs/transports/websocket","h":"#websocket_message_size_limit","p":3613},{"i":3618,"t":"websocket_read_buffer_size","u":"/docs/transports/websocket","h":"#websocket_read_buffer_size","p":3613},{"i":3620,"t":"websocket_write_buffer_size","u":"/docs/transports/websocket","h":"#websocket_write_buffer_size","p":3613},{"i":3622,"t":"websocket_use_write_buffer_pool","u":"/docs/transports/websocket","h":"#websocket_use_write_buffer_pool","p":3613},{"i":3624,"t":"websocket_compression","u":"/docs/transports/websocket","h":"#websocket_compression","p":3613},{"i":3626,"t":"Protobuf binary protocol","u":"/docs/transports/websocket","h":"#protobuf-binary-protocol","p":3613},{"i":3628,"t":"Debugging with Postman, wscat, etc","u":"/docs/transports/websocket","h":"#debugging-with-postman-wscat-etc","p":3613},{"i":3632,"t":"Client connection states","u":"/docs/transports/client_api","h":"#client-connection-states","p":3630},{"i":3634,"t":"Client common options","u":"/docs/transports/client_api","h":"#client-common-options","p":3630},{"i":3636,"t":"Client methods","u":"/docs/transports/client_api","h":"#client-methods","p":3630},{"i":3638,"t":"Client connection token","u":"/docs/transports/client_api","h":"#client-connection-token","p":3630},{"i":3640,"t":"Connection PING/PONG","u":"/docs/transports/client_api","h":"#connection-pingpong","p":3630},{"i":3642,"t":"Subscription states","u":"/docs/transports/client_api","h":"#subscription-states","p":3630},{"i":3644,"t":"Subscription management","u":"/docs/transports/client_api","h":"#subscription-management","p":3630},{"i":3646,"t":"Listen to channel publications","u":"/docs/transports/client_api","h":"#listen-to-channel-publications","p":3630},{"i":3648,"t":"Subscription recovery state","u":"/docs/transports/client_api","h":"#subscription-recovery-state","p":3630},{"i":3650,"t":"Subscription common options","u":"/docs/transports/client_api","h":"#subscription-common-options","p":3630},{"i":3652,"t":"Subscription methods","u":"/docs/transports/client_api","h":"#subscription-methods","p":3630},{"i":3654,"t":"Subscription token","u":"/docs/transports/client_api","h":"#subscription-token","p":3630},{"i":3656,"t":"Server-side subscriptions","u":"/docs/transports/client_api","h":"#server-side-subscriptions","p":3630},{"i":3658,"t":"Error codes","u":"/docs/transports/client_api","h":"#error-codes","p":3630},{"i":3660,"t":"Unsubscribe codes","u":"/docs/transports/client_api","h":"#unsubscribe-codes","p":3630},{"i":3662,"t":"Disconnect codes","u":"/docs/transports/client_api","h":"#disconnect-codes","p":3630},{"i":3664,"t":"RPC","u":"/docs/transports/client_api","h":"#rpc","p":3630},{"i":3666,"t":"Channel history API","u":"/docs/transports/client_api","h":"#channel-history-api","p":3630},{"i":3668,"t":"Presence and presence stats API","u":"/docs/transports/client_api","h":"#presence-and-presence-stats-api","p":3630},{"i":3670,"t":"SDK common best practices","u":"/docs/transports/client_api","h":"#sdk-common-best-practices","p":3630}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/5",[0,4.801,1,3.159,2,6.232]],["t/7",[0,5.703,3,5.854]],["t/9",[4,6.242,5,7.403]],["t/11",[6,4.421,7,4.801,8,3.301]],["t/13",[0,4.801,9,6.232,10,4.69]],["t/15",[11,5.854,12,5.163]],["t/17",[13,7.403,14,5.854]],["t/19",[8,2.85,15,3.964,16,3.753,17,5.381]],["t/21",[18,6.164]],["t/25",[19,2.915,20,5.479,21,5.778]],["t/27",[22,7.403,23,6.508]],["t/29",[24,6.242,25,4.808]],["t/31",[10,4.049,26,3.54,27,4.384,28,3.964]],["t/33",[29,3.583,30,5.255,31,2.712]],["t/35",[16,4.347,32,3.346,33,4.421]],["t/37",[34,5.703,35,4.031]],["t/39",[36,3.87,37,3.321]],["t/41",[1,3.159,31,2.712,38,5.077]],["t/43",[39,6.863,40,6.242]],["t/45",[38,6.03,41,3.588]],["t/47",[42,4.69,43,6.232,44,5.077]],["t/49",[11,5.854,38,6.03]],["t/51",[19,3.463,45,5.571]],["t/53",[18,6.164]],["t/57",[46,7.403,47,6.03]],["t/59",[48,7.425]],["t/61",[48,6.03,49,6.508]],["t/63",[50,7.686]],["t/65",[51,2.982,52,3.731]],["t/67",[8,3.301,53,5.778,54,6.232]],["t/69",[55,5.779]],["t/71",[1,3.159,52,3.141,56,3.712]],["t/73",[29,3.583,51,2.511,57,6.232]],["t/75",[58,3.588,59,4.512,60,3.964]],["t/77",[12,5.163,61,7.403]],["t/79",[62,4.591,63,4.69,64,6.232]],["t/81",[65,9.116]],["t/83",[11,7.208]],["t/85",[66,6.078]],["t/87",[67,4.442]],["t/89",[18,6.164]],["t/93",[68,7.686]],["t/95",[69,4.69,70,6.232,71,6.232]],["t/97",[72,4.735,73,4.735,74,4.735,75,3.358,76,3.992]],["t/99",[41,3.021,67,3.037,77,5.778]],["t/101",[1,3.159,78,5.778,79,5.778]],["t/103",[1,3.753,80,7.403]],["t/105",[79,5.778,81,5.255,82,2.529]],["t/107",[82,2.529,83,5.255,84,4.215]],["t/109",[41,3.021,85,6.232,86,6.232]],["t/111",[16,3.753,87,4.384,88,3.887,89,4.049]],["t/113",[1,3.159,67,3.037,90,5.255]],["t/115",[18,6.164]],["t/119",[19,2.915,21,5.778,91,5.778]],["t/121",[31,2.342,41,2.608,92,4.538,93,3.694]],["t/123",[0,4.145,94,5.381,95,5.381,96,4.989]],["t/125",[41,3.021,97,6.232,98,4.502]],["t/127",[99,6.232,100,4.421,101,5.255]],["t/129",[33,3.817,51,2.168,102,4.145,103,3.753]],["t/131",[51,2.168,104,4.145,105,4.384,106,5.381]],["t/133",[52,3.731,107,7.403]],["t/135",[51,2.982,108,5.703]],["t/137",[31,2.712,39,5.778,82,2.529]],["t/139",[41,2.608,96,4.989,109,5.381,110,5.381]],["t/141",[111,6.863,112,6.508]],["t/143",[111,6.863,113,7.403]],["t/145",[114,4.936,115,7.403]],["t/147",[18,6.164]],["t/149",[87,6.03,116,6.242]],["t/151",[117,7.403,118,7.403]],["t/155",[68,6.242,119,6.03]],["t/157",[19,2.915,120,4.591,121,6.232]],["t/159",[47,5.077,122,5.778,123,5.778]],["t/161",[119,6.03,124,6.242]],["t/163",[125,6.242,126,5.347]],["t/165",[114,4.155,127,4.801,128,3.864]],["t/167",[129,7.686]],["t/169",[19,3.463,130,7.403]],["t/171",[37,2.795,82,2.529,131,5.778]],["t/173",[131,6.863,132,6.03]],["t/175",[41,3.588,56,4.41]],["t/177",[38,6.03,133,6.242]],["t/179",[18,6.164]],["t/183",[19,2.915,120,4.591,134,6.232]],["t/185",[135,9.116]],["t/187",[123,6.863,136,5.453]],["t/189",[136,4.591,137,4.801,138,4.591]],["t/191",[139,5.778,140,6.232,141,5.778]],["t/193",[132,5.077,139,5.778,141,5.778]],["t/195",[1,3.159,19,2.915,47,5.077]],["t/197",[142,6.508,143,5.571]],["t/199",[37,2.795,144,4.69,145,5.255]],["t/201",[38,7.425]],["t/203",[146,3.744,147,3.42,148,2.876,149,3.42,150,3.992]],["t/205",[18,6.164]],["t/209",[151,9.116]],["t/211",[152,9.116]],["t/213",[19,4.264]],["t/215",[138,4.591,153,6.232,154,6.232]],["t/219",[19,3.463,47,6.03]],["t/221",[69,4.69,77,5.778,155,5.778]],["t/223",[155,4.989,156,2.666,157,5.381,158,5.381]],["t/225",[8,2.85,32,2.889,159,3.887,160,4.731]],["t/227",[161,9.116]],["t/229",[53,5.778,162,6.232,163,3.678]],["t/231",[63,5.571,164,7.403]],["t/233",[62,5.453,165,6.863]],["t/235",[62,4.591,166,6.232,167,6.232]],["t/237",[84,5.006,168,4.749]],["t/239",[18,6.164]],["t/243",[169,7.686]],["t/245",[136,4.591,138,4.591,170,6.232]],["t/247",[19,3.463,47,6.03]],["t/249",[142,6.508,143,5.571]],["t/251",[6,3.817,8,2.85,27,4.384,28,3.964]],["t/253",[18,6.164]],["t/257",[19,2.915,45,4.69,171,6.232]],["t/259",[31,2.342,159,3.887,172,4.989,173,3.964]],["t/261",[31,2.061,159,3.42,172,4.39,173,3.488,174,3.647]],["t/263",[175,7.403,176,7.403]],["t/265",[1,3.753,144,5.571]],["t/267",[177,7.403,178,4.091]],["t/269",[6,5.251,173,5.453]],["t/271",[92,5.255,102,4.801,179,5.479]],["t/273",[159,5.347,180,6.508]],["t/275",[159,4.502,173,4.591,181,4.928]],["t/277",[18,6.164]],["t/286",[122,8.451]],["t/288",[51,1.907,66,3.157,182,3.992,183,3.302,184,2.936]],["t/290",[51,2.168,178,2.973,185,4.538,186,4.145]],["t/292",[37,2.795,187,5.255,188,3.748]],["t/294",[1,2.143,51,1.703,56,2.518,82,1.715,189,3.564,190,2.902]],["t/296",[1,2.143,37,1.896,51,1.703,56,2.518,82,1.715,191,3.564]],["t/298",[51,1.907,52,2.386,192,3.992,193,3.42,194,3.992]],["t/300",[82,1.921,108,3.647,183,3.302,190,3.25,195,3.992]],["t/302",[37,2.124,82,1.921,108,3.647,183,3.302,196,3.992]],["t/304",[197,9.116]],["t/308",[198,5.479,199,5.479,200,4.591]],["t/312",[41,2.608,98,3.887,201,5.381,202,4.989]],["t/314",[93,3.694,178,2.973,203,4.989,204,4.989]],["t/316",[205,6.863,206,7.403]],["t/318",[49,4.731,87,4.384,98,3.887,207,4.384]],["t/320",[31,2.342,42,4.049,208,2.973,209,3.094]],["t/322",[181,4.255,210,5.381,211,5.381,212,4.989]],["t/324",[1,3.159,31,2.712,213,5.778]],["t/326",[52,2.712,178,2.973,214,4.384,215,3.412]],["t/328",[215,3.412,216,4.384,217,3.694,218,5.381]],["t/330",[98,3.887,219,5.381,220,4.384,221,4.989]],["t/332",[222,7.403,223,5.347]],["t/334",[48,4.384,224,5.381,225,5.381,226,5.381]],["t/336",[18,6.164]],["t/344",[227,6.508,228,5.347]],["t/346",[8,3.301,29,3.583,229,4.591]],["t/348",[8,3.301,63,4.69,128,3.864]],["t/350",[8,3.301,62,4.591,230,4.928]],["t/352",[231,6.508,232,6.508]],["t/354",[58,4.155,59,3.525,229,4.591]],["t/356",[12,5.163,229,5.453]],["t/360",[120,5.453,233,6.508]],["t/362",[11,5.854,234,6.508]],["t/364",[12,5.163,235,6.508]],["t/366",[41,3.021,98,4.502,236,5.479]],["t/368",[10,4.049,27,4.384,28,3.964,237,4.731]],["t/370",[50,6.242,238,6.508]],["t/372",[82,3.004,239,6.03]],["t/374",[51,2.511,105,5.077,132,5.077]],["t/376",[52,3.141,240,4.69,241,4.69]],["t/378",[55,3.412,82,2.183,88,3.887,89,4.049]],["t/380",[58,4.155,59,3.525,242,5.479]],["t/382",[8,3.301,29,3.583,51,2.511]],["t/384",[181,4.255,243,4.731,244,3.753,245,3.588]],["t/386",[246,6.508,247,6.508]],["t/388",[248,6.508,249,5.854]],["t/390",[147,5.347,250,5.854]],["t/392",[45,5.571,251,4.87]],["t/396",[7,3.256,19,1.977,82,1.715,84,2.858,174,3.256,252,3.716]],["t/398",[82,2.183,228,3.887,253,3.694,254,3.964]],["t/400",[19,2.915,255,4.591,256,5.479]],["t/402",[8,3.301,63,4.69,128,3.864]],["t/404",[8,3.301,62,4.591,230,4.928]],["t/406",[51,2.511,136,4.591,257,5.479]],["t/408",[51,2.168,258,4.049,259,4.384,260,4.731]],["t/410",[8,3.241,51,1.538,163,2.253,261,2.812,262,3.356,263,3.356]],["t/412",[41,2.049,51,1.703,174,3.256,264,3.716,265,2.818,266,2.858]],["t/414",[58,4.08,59,2.159,137,2.941,138,2.812,267,3.019,268,3.356]],["t/416",[19,2.215,42,3.563,81,3.992,82,1.921,269,4.162]],["t/418",[19,1.786,37,1.712,143,2.873,270,2.393,271,3.356,272,3.356,273,3.356]],["t/420",[19,2.915,274,4.155,275,5.479]],["t/422",[19,1.628,82,1.412,240,2.619,261,2.564,270,2.182,276,2.752,277,2.427,278,3.06]],["t/424",[6,2.708,119,3.11,159,2.757,173,2.812,245,2.545,279,3.356,280,3.356]],["t/426",[8,2.85,41,2.608,281,4.731,282,4.731]],["t/428",[0,2.941,8,2.022,41,1.85,42,2.873,82,1.549,88,2.757,163,2.253]],["t/430",[51,1.402,102,2.681,104,2.681,136,2.564,137,2.681,184,2.158,283,3.06,284,3.06]],["t/432",[51,1.703,156,2.094,258,3.181,261,3.114,285,3.716,286,3.716]],["t/434",[19,2.915,287,3.907,288,5.479]],["t/436",[19,2.915,289,3.583,290,5.479]],["t/438",[15,2.812,56,2.274,133,3.219,138,2.812,291,3.11,292,2.873,293,3.019]],["t/440",[12,3.302,58,3.157,59,2.678,251,3.115,292,3.563]],["t/442",[294,4.145,295,4.731,296,4.731,297,4.731]],["t/446",[1,3.753,82,3.004]],["t/448",[1,3.753,289,4.256]],["t/450",[1,3.753,14,5.854]],["t/452",[15,4.591,82,2.529,298,5.479]],["t/454",[51,2.982,188,4.452]],["t/456",[1,3.159,52,3.141,56,3.712]],["t/458",[6,5.251,55,4.693]],["t/460",[29,3.583,51,2.511,299,5.255]],["t/462",[59,5.02,60,4.591]],["t/466",[300,6.242,301,7.403]],["t/468",[302,8.451]],["t/470",[302,6.863,303,6.508]],["t/472",[114,4.936,304,6.03]],["t/474",[305,5.854,306,7.403]],["t/476",[307,8.451]],["t/478",[228,3.42,307,4.39,308,4.735,309,4.735,310,4.735]],["t/480",[142,6.508,311,7.403]],["t/482",[18,6.164]],["t/486",[69,5.571,312,6.508]],["t/488",[19,2.915,156,3.087,182,5.255]],["t/490",[156,3.087,185,5.255,293,4.928]],["t/492",[19,2.915,82,2.529,187,5.255]],["t/494",[51,2.511,188,3.748,189,5.255]],["t/496",[51,2.511,163,3.678,191,5.255]],["t/498",[192,5.255,249,4.928,313,5.479]],["t/500",[19,2.915,195,5.255,314,5.479]],["t/502",[19,2.915,196,5.255,255,4.591]],["t/504",[315,5.479,316,5.077,317,5.479]],["t/508",[69,4.69,265,4.155,318,4.928]],["t/510",[149,5.347,200,5.453]],["t/512",[67,3.037,149,4.502,150,5.255]],["t/514",[319,4.928,320,5.479,321,5.479]],["t/516",[322,4.731,323,4.731,324,4.538,325,4.731]],["t/518",[326,6.508,327,6.508]],["t/520",[147,5.347,169,6.242]],["t/524",[304,7.425]],["t/526",[105,7.425]],["t/528",[87,6.03,116,6.242]],["t/532",[274,6.078]],["t/534",[156,4.516]],["t/538",[265,4.936,318,5.854]],["t/540",[149,5.347,200,5.453]],["t/542",[319,7.208]],["t/544",[328,6.508,329,6.508]],["t/546",[330,8.013]],["t/548",[24,4.538,45,4.049,126,3.887,217,3.694]],["t/552",[251,5.996]],["t/554",[331,6.508,332,4.369]],["t/556",[333,8.013]],["t/562",[32,3.975,334,6.242]],["t/566",[31,2.712,208,3.444,335,3.998]],["t/568",[31,2.712,36,3.258,335,3.998]],["t/570",[37,2.795,208,3.444,335,3.998]],["t/572",[36,3.258,37,2.795,335,3.998]],["t/574",[190,4.278,335,3.998,336,5.479]],["t/576",[16,3.753,26,3.54,36,2.813,335,3.452]],["t/578",[67,4.442]],["t/580",[31,2.712,163,3.678,208,3.444]],["t/582",[29,3.583,31,2.712,208,3.444]],["t/586",[25,4.047,41,3.021,56,3.712]],["t/588",[29,3.583,33,4.421,337,6.232]],["t/590",[66,4.155,338,5.077,339,4.591]],["t/592",[340,6.232,341,6.232,342,5.778]],["t/594",[1,3.159,25,4.047,56,3.712]],["t/595",[28,3.964,343,5.381,344,2.505,345,4.538]],["t/597",[25,4.047,51,2.511,344,2.901]],["t/599",[342,4.989,346,3.236,347,4.538,348,4.538]],["t/601",[349,4.731,350,3.694,351,5.381,352,4.384]],["t/603",[31,2.061,36,2.475,207,3.857,324,3.992,353,3.563]],["t/605",[25,3.495,31,2.342,51,2.168,354,3.887]],["t/607",[25,4.047,37,2.795,208,3.444]],["t/609",[25,4.808,190,5.082]],["t/611",[25,4.047,32,3.346,156,3.087]],["t/613",[16,3.753,32,2.889,33,3.817,270,3.373]],["t/615",[33,4.421,223,4.502,355,5.077]],["t/617",[25,4.808,156,3.667]],["t/619",[20,4.731,91,4.989,215,3.412,356,5.381]],["t/623",[357,6.508,358,4.936]],["t/627",[274,6.078]],["t/629",[156,4.516]],["t/631",[32,3.346,35,3.394,359,4.347]],["t/633",[35,3.394,127,4.801,359,4.347]],["t/635",[31,2.712,178,3.444,360,4.801]],["t/637",[31,2.342,178,2.973,184,3.337,361,4.538]],["t/643",[67,3.607,362,7.403]],["t/647",[274,6.078]],["t/649",[156,4.516]],["t/651",[32,3.346,35,3.394,359,4.347]],["t/653",[35,3.394,127,4.801,359,4.347]],["t/655",[31,2.712,184,3.864,363,5.479]],["t/657",[31,2.712,184,3.864,364,6.232]],["t/663",[344,4.243]],["t/665",[245,3.588,270,3.373,365,3.588,366,4.731]],["t/667",[244,4.347,332,3.678,367,4.155]],["t/671",[156,4.516]],["t/673",[82,3.004,368,4.936]],["t/675",[368,4.936,369,6.508]],["t/677",[67,3.607,370,6.508]],["t/679",[371,8.013]],["t/681",[274,4.936,372,6.508]],["t/685",[41,2.295,55,3.002,56,2.82,207,3.857,267,3.744]],["t/687",[1,3.159,31,2.712,373,5.479]],["t/689",[1,3.159,31,2.712,374,5.479]],["t/691",[1,3.159,31,2.712,375,5.479]],["t/693",[156,4.516]],["t/697",[41,3.021,148,3.785,168,3.998]],["t/699",[376,7.022]],["t/701",[377,8.013]],["t/703",[51,2.982,216,6.03]],["t/705",[378,4.693,379,6.508]],["t/707",[294,5.703,354,5.347]],["t/709",[188,4.452,380,6.508]],["t/711",[66,4.936,381,6.508]],["t/713",[350,5.082,382,6.03]],["t/715",[383,7.022]],["t/717",[178,4.091,384,4.749]],["t/719",[384,5.847]],["t/721",[7,5.703,350,5.082]],["t/723",[385,6.242,386,5.703]],["t/725",[41,3.021,148,3.785,289,3.583]],["t/727",[387,9.116]],["t/729",[388,9.116]],["t/731",[178,4.091,361,6.242]],["t/733",[350,5.082,382,6.03]],["t/735",[1,3.753,168,4.749]],["t/737",[384,5.847]],["t/739",[52,3.731,384,4.749]],["t/741",[389,9.116]],["t/743",[390,9.116]],["t/745",[78,6.863,168,4.749]],["t/747",[391,7.403,392,5.347]],["t/749",[14,5.854,393,6.863]],["t/751",[14,5.854,393,6.863]],["t/753",[66,4.936,82,3.004]],["t/755",[1,2.728,31,2.342,148,3.268,168,3.452]],["t/757",[376,7.022]],["t/759",[51,2.982,216,6.03]],["t/761",[294,5.703,354,5.347]],["t/763",[350,5.082,382,6.03]],["t/765",[383,7.022]],["t/767",[385,6.242,386,5.703]],["t/771",[394,7.208]],["t/773",[395,6.357]],["t/775",[396,7.208]],["t/777",[397,7.425]],["t/779",[398,7.425]],["t/781",[399,7.208]],["t/783",[400,7.208]],["t/785",[401,6.357]],["t/787",[402,7.208]],["t/789",[51,3.672]],["t/791",[395,6.357]],["t/793",[403,8.013]],["t/795",[404,7.208]],["t/797",[82,3.004,384,4.749]],["t/799",[67,4.442]],["t/801",[178,4.091,405,6.508]],["t/803",[178,4.091,384,4.749]],["t/805",[82,2.183,178,2.973,401,3.753,406,4.049]],["t/807",[190,4.278,407,5.479,408,5.479]],["t/809",[217,3.694,245,3.588,266,3.639,287,3.373]],["t/813",[51,2.511,409,5.479,410,4.928]],["t/815",[103,4.347,183,4.347,411,4.928]],["t/817",[51,2.168,104,4.145,183,3.753,412,4.731]],["t/819",[51,2.168,183,3.753,184,3.337,411,4.255]],["t/821",[51,2.982,344,3.446]],["t/823",[163,5.38]],["t/825",[413,9.116]],["t/827",[414,7.686]],["t/829",[59,5.155]],["t/831",[415,9.116]],["t/833",[416,8.013]],["t/835",[417,8.013]],["t/837",[418,8.013]],["t/839",[386,7.022]],["t/841",[419,9.116]],["t/843",[420,9.116]],["t/845",[421,8.013]],["t/847",[422,8.013]],["t/849",[423,8.013]],["t/851",[424,8.013]],["t/853",[425,8.013]],["t/855",[51,2.168,67,2.622,215,3.412,344,2.505]],["t/857",[51,2.982,103,5.163]],["t/861",[346,4.452,426,6.508]],["t/863",[346,4.452,427,6.508]],["t/865",[346,4.452,428,6.508]],["t/867",[346,4.452,429,6.508]],["t/869",[346,4.452,430,6.508]],["t/873",[147,5.347,156,3.667]],["t/875",[346,3.748,347,5.255,348,5.255]],["t/877",[4,5.255,125,5.255,431,4.801]],["t/879",[156,3.667,358,4.936]],["t/881",[209,3.583,215,3.951,358,4.155]],["t/883",[209,3.583,215,3.951,266,4.215]],["t/885",[209,3.583,215,3.951,432,5.479]],["t/887",[209,3.583,215,3.951,433,5.479]],["t/889",[344,3.446,434,6.508]],["t/891",[435,8.013]],["t/893",[436,8.013]],["t/895",[437,6.714]],["t/897",[35,4.963]],["t/899",[344,3.446,438,6.508]],["t/901",[439,8.013]],["t/903",[440,8.013]],["t/905",[441,8.013]],["t/907",[442,8.013]],["t/909",[443,9.116]],["t/911",[444,8.013]],["t/913",[445,8.013]],["t/915",[156,3.667,446,4.496]],["t/917",[33,5.251,446,4.496]],["t/919",[244,5.163,446,4.496]],["t/921",[220,6.03,446,4.496]],["t/923",[352,5.077,446,3.785,447,5.479]],["t/925",[365,4.155,376,4.801,437,4.591]],["t/927",[33,4.421,355,5.077,446,3.785]],["t/929",[145,5.255,365,4.155,446,3.785]],["t/931",[84,5.006,448,6.508]],["t/933",[332,4.369,367,4.936]],["t/934",[41,3.021,82,2.529,367,4.155]],["t/936",[31,2.712,332,3.678,367,4.155]],["t/938",[244,4.347,332,3.678,367,4.155]],["t/940",[28,3.964,126,3.887,344,2.505,345,4.538]],["t/942",[88,3.887,103,3.753,126,3.887,449,4.731]],["t/944",[60,4.591,228,4.502,414,5.255]],["t/948",[66,4.155,250,4.928,358,4.155]],["t/950",[437,4.591,450,5.479,451,5.479]],["t/952",[392,4.502,452,5.479,453,5.479]],["t/954",[37,2.795,82,2.529,454,5.479]],["t/956",[368,4.936,455,6.508]],["t/960",[456,7.686]],["t/962",[457,7.686]],["t/964",[458,6.242,459,6.242]],["t/968",[29,4.256,460,5.854]],["t/970",[29,3.583,30,5.255,31,2.712]],["t/972",[8,3.301,193,4.502,339,4.591]],["t/976",[143,5.571,156,3.667]],["t/978",[19,2.915,214,5.077,461,5.479]],["t/980",[44,4.384,245,3.588,462,4.731,463,4.731]],["t/982",[464,8.013]],["t/986",[394,7.208]],["t/988",[41,4.418]],["t/990",[51,3.672]],["t/992",[401,6.357]],["t/994",[402,7.208]],["t/996",[396,7.208]],["t/998",[404,7.208]],["t/1000",[399,7.208]],["t/1002",[400,7.208]],["t/1004",[67,4.442]],["t/1008",[37,3.321,208,4.091]],["t/1010",[124,5.255,208,3.444,350,4.278]],["t/1012",[37,2.795,208,3.444,465,4.928]],["t/1014",[36,3.258,37,2.795,466,5.479]],["t/1016",[37,3.321,82,3.004]],["t/1018",[37,3.321,467,5.006]],["t/1020",[37,3.321,55,4.693]],["t/1022",[37,3.321,188,4.452]],["t/1024",[37,3.321,163,4.369]],["t/1026",[168,3.998,365,4.155,468,4.928]],["t/1028",[289,3.583,365,4.155,468,4.928]],["t/1030",[36,3.87,37,3.321]],["t/1032",[36,3.258,37,2.795,344,2.901]],["t/1034",[36,3.258,37,2.795,67,3.037]],["t/1036",[37,2.795,410,4.928,465,4.928]],["t/1038",[265,4.936,332,4.369]],["t/1040",[37,2.795,332,3.678,469,4.1]],["t/1042",[37,2.414,332,3.176,469,3.54,470,4.384]],["t/1044",[37,2.795,471,5.479,472,5.077]],["t/1046",[82,2.529,467,4.215,469,4.1]],["t/1048",[163,3.678,188,3.748,469,4.1]],["t/1050",[55,4.693,469,4.87]],["t/1054",[217,3.694,270,3.373,358,3.588,473,4.731]],["t/1056",[76,6.242,193,5.347]],["t/1058",[31,2.712,36,3.258,75,4.421]],["t/1060",[16,3.753,26,3.54,36,2.813,75,3.817]],["t/1066",[1,2.728,52,2.712,56,3.206,474,4.384]],["t/1068",[51,2.168,52,2.712,193,3.887,194,4.538]],["t/1070",[82,2.183,184,3.337,276,4.255,475,4.731]],["t/1074",[223,5.347,476,6.508]],["t/1076",[83,6.242,477,6.508]],["t/1078",[277,6.357]],["t/1080",[266,6.164]],["t/1082",[344,4.243]],["t/1083",[223,6.584]],["t/1085",[478,8.013]],["t/1089",[89,6.859]],["t/1091",[26,5.996]],["t/1093",[8,3.301,26,4.1,241,4.69]],["t/1097",[82,3.004,346,4.452]],["t/1099",[209,3.583,287,3.907,479,4.215]],["t/1101",[100,6.466]],["t/1103",[344,4.243]],["t/1104",[480,8.013]],["t/1106",[481,8.013]],["t/1108",[67,3.607,277,5.163]],["t/1112",[209,3.583,287,3.907,479,4.215]],["t/1114",[344,4.243]],["t/1115",[482,8.013]],["t/1117",[483,8.013]],["t/1119",[484,8.013]],["t/1121",[485,8.013]],["t/1123",[486,8.013]],["t/1125",[487,8.013]],["t/1127",[488,8.013]],["t/1129",[67,4.442]],["t/1133",[82,3.004,346,4.452]],["t/1135",[489,8.013]],["t/1137",[209,3.583,287,3.907,479,4.215]],["t/1139",[100,6.466]],["t/1141",[344,4.243]],["t/1142",[490,8.013]],["t/1144",[491,8.013]],["t/1146",[67,4.442]],["t/1150",[344,4.243]],["t/1151",[492,8.013]],["t/1153",[493,8.013]],["t/1155",[494,8.013]],["t/1157",[495,8.013]],["t/1159",[496,8.013]],["t/1161",[98,4.502,265,4.155,353,4.69]],["t/1165",[82,3.004,346,4.452]],["t/1167",[209,3.583,287,3.907,479,4.215]],["t/1169",[100,6.466]],["t/1171",[344,4.243]],["t/1172",[497,8.013]],["t/1174",[498,8.013]],["t/1176",[82,2.529,270,3.907,499,5.479]],["t/1178",[67,3.607,277,5.163]],["t/1182",[35,4.031,253,5.082]],["t/1184",[35,3.394,253,4.278,344,2.901]],["t/1186",[32,3.975,35,4.031]],["t/1188",[32,3.346,35,3.394,344,2.901]],["t/1190",[32,3.346,146,4.928,255,4.591]],["t/1192",[32,2.889,383,4.145,500,4.049,501,4.731]],["t/1194",[156,2.666,500,4.049,502,4.731,503,4.731]],["t/1196",[32,3.975,504,6.508]],["t/1198",[32,3.975,40,6.242]],["t/1200",[35,4.031,505,6.863]],["t/1202",[34,5.703,35,4.031]],["t/1204",[34,4.801,35,3.394,344,2.901]],["t/1206",[300,6.242,506,6.242]],["t/1208",[344,4.243]],["t/1214",[198,5.479,199,5.479,200,4.591]],["t/1220",[7,3.256,19,1.977,82,1.715,84,2.858,174,3.256,252,3.716]],["t/1222",[82,2.183,228,3.887,253,3.694,254,3.964]],["t/1224",[19,2.915,255,4.591,256,5.479]],["t/1226",[8,3.301,63,4.69,128,3.864]],["t/1228",[8,3.301,62,4.591,230,4.928]],["t/1230",[51,2.511,136,4.591,257,5.479]],["t/1232",[51,2.168,258,4.049,259,4.384,260,4.731]],["t/1234",[8,3.241,51,1.538,163,2.253,261,2.812,262,3.356,263,3.356]],["t/1236",[41,2.049,51,1.703,174,3.256,264,3.716,265,2.818,266,2.858]],["t/1238",[58,4.08,59,2.159,137,2.941,138,2.812,267,3.019,268,3.356]],["t/1240",[19,2.215,42,3.563,81,3.992,82,1.921,269,4.162]],["t/1242",[19,1.786,37,1.712,143,2.873,270,2.393,271,3.356,272,3.356,273,3.356]],["t/1244",[19,2.915,274,4.155,275,5.479]],["t/1246",[19,2.915,112,5.479,274,4.155]],["t/1248",[19,1.628,82,1.412,240,2.619,261,2.564,270,2.182,276,2.752,277,2.427,278,3.06]],["t/1250",[6,2.708,119,3.11,159,2.757,173,2.812,245,2.545,279,3.356,280,3.356]],["t/1252",[8,2.85,41,2.608,281,4.731,282,4.731]],["t/1254",[0,2.941,8,2.022,41,1.85,42,2.873,82,1.549,88,2.757,163,2.253]],["t/1256",[51,1.402,102,2.681,104,2.681,136,2.564,137,2.681,184,2.158,283,3.06,284,3.06]],["t/1258",[51,1.703,156,2.094,258,3.181,261,3.114,285,3.716,286,3.716]],["t/1260",[19,2.915,287,3.907,288,5.479]],["t/1262",[19,2.915,289,3.583,290,5.479]],["t/1264",[15,2.812,56,2.274,133,3.219,138,2.812,291,3.11,292,2.873,293,3.019]],["t/1266",[12,3.302,58,3.157,59,2.678,251,3.115,292,3.563]],["t/1268",[6,3.358,51,1.907,188,2.847,479,3.202,507,4.39]],["t/1270",[19,2.915,287,3.907,508,5.778]],["t/1272",[294,4.145,295,4.731,296,4.731,297,4.731]],["t/1276",[227,6.508,228,5.347]],["t/1278",[8,3.301,29,3.583,229,4.591]],["t/1280",[8,3.301,63,4.69,128,3.864]],["t/1282",[8,3.301,62,4.591,230,4.928]],["t/1284",[231,6.508,232,6.508]],["t/1286",[58,4.155,59,3.525,229,4.591]],["t/1288",[12,5.163,229,5.453]],["t/1292",[3,4.928,48,5.077,305,4.928]],["t/1294",[120,5.453,509,6.863]],["t/1298",[1,3.753,82,3.004]],["t/1300",[1,3.753,289,4.256]],["t/1302",[1,3.753,14,5.854]],["t/1304",[15,4.591,82,2.529,298,5.479]],["t/1306",[51,2.982,188,4.452]],["t/1308",[1,3.159,52,3.141,56,3.712]],["t/1310",[6,5.251,55,4.693]],["t/1312",[29,3.583,51,2.511,299,5.255]],["t/1314",[59,5.02,60,4.591]],["t/1318",[120,5.453,233,6.508]],["t/1320",[11,5.854,234,6.508]],["t/1322",[12,5.163,235,6.508]],["t/1324",[41,3.021,98,4.502,236,5.479]],["t/1326",[10,4.049,27,4.384,28,3.964,237,4.731]],["t/1328",[50,6.242,238,6.508]],["t/1330",[82,3.004,239,6.03]],["t/1332",[51,2.511,105,5.077,132,5.077]],["t/1334",[52,3.141,240,4.69,241,4.69]],["t/1336",[55,3.412,82,2.183,88,3.887,89,4.049]],["t/1338",[58,4.155,59,3.525,242,5.479]],["t/1340",[8,3.301,29,3.583,51,2.511]],["t/1342",[181,4.255,243,4.731,244,3.753,245,3.588]],["t/1344",[246,6.508,247,6.508]],["t/1346",[248,6.508,249,5.854]],["t/1348",[147,5.347,250,5.854]],["t/1350",[45,5.571,251,4.87]],["t/1354",[510,8.451]],["t/1358",[41,3.021,93,4.278,114,4.155]],["t/1360",[10,4.69,26,4.1,114,4.155]],["t/1362",[114,4.936,223,5.347]],["t/1364",[51,2.511,349,5.479,511,5.778]],["t/1366",[52,3.141,114,4.155,178,3.444]],["t/1368",[51,2.168,66,3.588,114,3.588,184,3.337]],["t/1370",[103,4.347,114,4.155,156,3.087]],["t/1372",[25,3.495,37,2.414,148,3.268,289,3.094]],["t/1374",[25,4.047,156,3.087,344,2.901]],["t/1376",[1,3.159,25,4.047,31,2.712]],["t/1380",[69,5.571,312,6.508]],["t/1382",[19,2.915,156,3.087,182,5.255]],["t/1384",[156,3.087,185,5.255,293,4.928]],["t/1386",[19,2.915,82,2.529,187,5.255]],["t/1388",[51,2.511,188,3.748,189,5.255]],["t/1390",[51,2.511,163,3.678,191,5.255]],["t/1392",[192,5.255,249,4.928,313,5.479]],["t/1394",[19,2.915,195,5.255,314,5.479]],["t/1396",[19,2.915,196,5.255,255,4.591]],["t/1398",[315,5.479,316,5.077,317,5.479]],["t/1402",[41,2.608,144,4.049,251,3.54,512,4.731]],["t/1404",[513,5.479,514,5.479,515,6.232]],["t/1406",[82,3.699]],["t/1408",[188,5.482]],["t/1410",[516,7.208]],["t/1412",[467,6.164]],["t/1414",[29,2.722,55,3.002,59,2.678,163,2.794,299,3.992]],["t/1416",[1,2.728,8,2.85,41,2.608,517,4.731]],["t/1418",[100,5.251,101,6.242]],["t/1420",[84,5.006,289,4.256]],["t/1422",[84,5.006,168,4.749]],["t/1424",[41,3.021,144,4.69,518,6.232]],["t/1426",[1,2.728,52,2.712,56,3.206,519,5.381]],["t/1428",[8,3.921,339,5.453]],["t/1430",[148,3.785,289,3.583,520,6.232]],["t/1432",[406,5.571,521,6.03]],["t/1436",[69,4.69,265,4.155,318,4.928]],["t/1438",[149,5.347,200,5.453]],["t/1440",[67,3.037,149,4.502,150,5.255]],["t/1442",[319,4.928,320,5.479,321,5.479]],["t/1444",[322,4.731,323,4.731,324,4.538,325,4.731]],["t/1446",[326,6.508,327,6.508]],["t/1448",[147,5.347,169,6.242]],["t/1452",[31,3.222,208,4.091]],["t/1454",[31,2.712,186,4.801,208,3.444]],["t/1456",[163,5.38]],["t/1458",[129,7.686]],["t/1460",[188,5.482]],["t/1462",[516,7.208]],["t/1464",[289,5.24]],["t/1466",[467,6.164]],["t/1468",[59,5.155]],["t/1470",[522,8.013]],["t/1472",[29,5.24]],["t/1474",[523,8.013]],["t/1476",[51,3.672]],["t/1478",[401,6.357]],["t/1480",[303,6.508,346,4.452]],["t/1482",[3,4.928,31,2.712,208,3.444]],["t/1484",[31,3.222,36,3.87]],["t/1486",[36,3.258,67,3.037,524,5.479]],["t/1488",[36,3.258,67,3.037,305,4.928]],["t/1490",[31,2.342,36,2.813,186,4.145,217,3.694]],["t/1494",[525,8.451]],["t/1496",[431,5.703,526,6.863]],["t/1498",[527,8.451]],["t/1500",[528,8.451]],["t/1502",[529,8.451]],["t/1508",[156,4.516]],["t/1510",[144,5.571,530,6.863]],["t/1512",[431,7.022]],["t/1514",[270,4.64,531,6.863]],["t/1518",[532,8.451]],["t/1520",[533,8.451]],["t/1522",[534,8.451]],["t/1526",[251,5.996]],["t/1528",[331,4.731,332,3.176,535,4.989,536,4.989]],["t/1530",[333,8.013]],["t/1534",[31,2.712,208,3.444,335,3.998]],["t/1536",[31,2.712,36,3.258,335,3.998]],["t/1538",[37,2.795,208,3.444,335,3.998]],["t/1540",[36,3.258,37,2.795,335,3.998]],["t/1542",[190,4.278,335,3.998,336,5.479]],["t/1544",[16,3.753,26,3.54,36,2.813,335,3.452]],["t/1546",[67,4.442]],["t/1548",[31,2.712,163,3.678,208,3.444]],["t/1550",[29,3.583,31,2.712,208,3.444]],["t/1554",[265,4.936,318,5.854]],["t/1556",[149,5.347,200,5.453]],["t/1558",[319,7.208]],["t/1560",[328,6.508,329,6.508]],["t/1562",[330,8.013]],["t/1564",[24,4.538,45,4.049,126,3.887,217,3.694]],["t/1568",[82,3.004,108,5.703]],["t/1570",[537,5.255,538,5.778,539,5.255]],["t/1572",[384,4.749,540,6.242]],["t/1574",[82,2.529,360,4.801,537,5.255]],["t/1576",[67,3.037,541,5.778,542,4.928]],["t/1578",[67,3.037,542,4.928,543,5.778]],["t/1580",[67,2.622,240,4.049,241,4.049,542,4.255]],["t/1582",[51,2.168,67,2.622,90,4.538,544,4.989]],["t/1584",[52,3.731,108,5.703]],["t/1586",[384,4.749,540,6.242]],["t/1588",[52,3.141,360,4.801,378,3.951]],["t/1596",[67,4.442]],["t/1598",[82,3.699]],["t/1602",[156,4.516]],["t/1604",[82,3.004,368,4.936]],["t/1606",[52,3.731,368,4.936]],["t/1608",[368,4.936,369,6.508]],["t/1610",[338,6.03,368,4.936]],["t/1612",[173,5.453,368,4.936]],["t/1614",[67,3.607,370,6.508]],["t/1616",[371,8.013]],["t/1618",[274,4.936,372,6.508]],["t/1622",[357,6.508,358,4.936]],["t/1626",[274,6.078]],["t/1628",[156,4.516]],["t/1630",[32,3.346,35,3.394,359,4.347]],["t/1632",[35,3.394,127,4.801,359,4.347]],["t/1634",[31,2.712,178,3.444,360,4.801]],["t/1635",[545,8.451]],["t/1637",[31,2.342,178,2.973,184,3.337,361,4.538]],["t/1638",[546,8.451]],["t/1642",[274,6.078]],["t/1644",[156,4.516]],["t/1646",[32,3.346,35,3.394,359,4.347]],["t/1648",[35,3.394,127,4.801,359,4.347]],["t/1650",[31,3.222,363,6.508]],["t/1651",[547,8.451]],["t/1653",[548,8.451]],["t/1657",[344,4.243]],["t/1659",[245,3.588,270,3.373,365,3.588,366,4.731]],["t/1661",[244,4.347,332,3.678,367,4.155]],["t/1665",[82,2.183,253,3.694,254,3.964,334,4.538]],["t/1667",[184,3.337,253,3.694,254,3.964,334,4.538]],["t/1669",[32,2.889,184,3.337,254,3.964,334,4.538]],["t/1671",[82,2.183,289,3.094,549,4.989,550,4.989]],["t/1675",[304,5.077,460,4.928,551,5.778]],["t/1677",[178,4.091,552,6.863]],["t/1679",[553,6.863,554,6.863]],["t/1681",[92,5.255,102,4.801,179,5.479]],["t/1683",[37,2.795,555,5.255,556,5.778]],["t/1685",[180,6.508,557,6.863]],["t/1687",[120,5.453,558,6.863]],["t/1689",[156,4.516]],["t/1691",[559,8.451]],["t/1693",[560,8.451]],["t/1695",[561,8.451]],["t/1697",[344,4.243]],["t/1699",[160,5.479,270,3.907,562,5.778]],["t/1701",[31,3.222,563,6.508]],["t/1702",[564,8.451]],["t/1704",[565,8.451]],["t/1706",[566,8.451]],["t/1708",[567,8.451]],["t/1710",[568,8.451]],["t/1712",[569,8.451]],["t/1714",[570,8.451]],["t/1716",[571,8.451]],["t/1718",[572,8.451]],["t/1720",[573,8.451]],["t/1722",[574,7.022]],["t/1724",[146,4.928,316,5.077,575,5.778]],["t/1728",[41,2.295,55,3.002,56,2.82,207,3.857,267,3.744]],["t/1730",[1,3.159,31,2.712,373,5.479]],["t/1732",[1,3.159,31,2.712,374,5.479]],["t/1734",[1,3.159,31,2.712,375,5.479]],["t/1736",[156,4.516]],["t/1740",[52,3.141,190,4.278,394,4.928]],["t/1742",[395,6.357]],["t/1744",[51,3.672]],["t/1746",[401,6.357]],["t/1748",[402,7.208]],["t/1750",[396,7.208]],["t/1752",[404,7.208]],["t/1754",[399,7.208]],["t/1756",[400,7.208]],["t/1758",[397,7.425]],["t/1760",[398,7.425]],["t/1762",[576,8.451]],["t/1764",[67,4.442]],["t/1766",[346,3.748,577,5.255,578,5.778]],["t/1770",[128,3.864,188,3.748,378,3.951]],["t/1772",[128,3.864,163,3.678,378,3.951]],["t/1774",[29,3.583,128,3.864,378,3.951]],["t/1776",[59,3.525,128,3.864,378,3.951]],["t/1778",[128,3.864,378,3.951,386,4.801]],["t/1780",[128,3.864,339,4.591,378,3.951]],["t/1782",[128,3.864,292,4.69,378,3.951]],["t/1786",[82,2.529,190,4.278,394,4.928]],["t/1788",[395,6.357]],["t/1790",[396,7.208]],["t/1792",[397,7.425]],["t/1794",[398,7.425]],["t/1796",[399,7.208]],["t/1798",[400,7.208]],["t/1800",[401,6.357]],["t/1802",[402,7.208]],["t/1804",[51,3.672]],["t/1806",[395,6.357]],["t/1808",[403,8.013]],["t/1810",[404,7.208]],["t/1812",[82,3.004,384,4.749]],["t/1814",[67,4.442]],["t/1816",[178,4.091,405,6.508]],["t/1818",[178,4.091,384,4.749]],["t/1820",[82,2.183,178,2.973,401,3.753,406,4.049]],["t/1822",[190,4.278,407,5.479,408,5.479]],["t/1824",[217,3.694,245,3.588,266,3.639,287,3.373]],["t/1826",[446,3.785,474,5.077,579,5.778]],["t/1830",[51,3.672]],["t/1832",[51,2.511,409,5.479,410,4.928]],["t/1834",[103,4.347,183,4.347,411,4.928]],["t/1836",[51,2.168,183,3.753,184,3.337,411,4.255]],["t/1838",[51,2.168,104,4.145,183,3.753,412,4.731]],["t/1840",[51,2.982,580,6.863]],["t/1842",[51,2.982,103,5.163]],["t/1844",[51,2.982,344,3.446]],["t/1846",[59,5.155]],["t/1848",[416,8.013]],["t/1850",[581,8.451]],["t/1852",[417,8.013]],["t/1854",[418,8.013]],["t/1856",[582,8.451]],["t/1858",[583,8.451]],["t/1860",[584,8.451]],["t/1862",[585,8.451]],["t/1864",[586,8.451]],["t/1866",[587,8.451]],["t/1868",[588,8.451]],["t/1870",[589,8.451]],["t/1872",[590,8.451]],["t/1874",[591,8.451]],["t/1876",[592,8.451]],["t/1878",[593,8.451]],["t/1880",[594,8.451]],["t/1882",[595,8.451]],["t/1884",[596,8.451]],["t/1886",[422,8.013]],["t/1888",[423,8.013]],["t/1890",[597,8.451]],["t/1892",[424,8.013]],["t/1894",[425,8.013]],["t/1896",[598,8.451]],["t/1898",[51,2.511,67,3.037,215,3.951]],["t/1902",[426,8.013]],["t/1904",[428,8.013]],["t/1906",[427,8.013]],["t/1908",[429,8.013]],["t/1910",[577,7.686]],["t/1912",[430,8.013]],["t/1914",[599,8.451]],["t/1918",[66,4.155,250,4.928,358,4.155]],["t/1920",[437,4.591,450,5.479,451,5.479]],["t/1922",[392,4.502,452,5.479,453,5.479]],["t/1924",[37,2.795,82,2.529,454,5.479]],["t/1926",[368,4.936,455,6.508]],["t/1928",[1,3.159,406,4.69,421,5.479]],["t/1932",[41,3.021,148,3.785,168,3.998]],["t/1934",[376,7.022]],["t/1936",[377,8.013]],["t/1938",[51,2.982,216,6.03]],["t/1940",[378,4.693,379,6.508]],["t/1942",[294,5.703,354,5.347]],["t/1944",[188,4.452,380,6.508]],["t/1946",[66,4.936,381,6.508]],["t/1948",[350,5.082,382,6.03]],["t/1950",[383,7.022]],["t/1952",[178,4.091,384,4.749]],["t/1954",[384,5.847]],["t/1956",[7,5.703,350,5.082]],["t/1958",[385,6.242,386,5.703]],["t/1960",[41,3.021,148,3.785,289,3.583]],["t/1962",[600,8.451]],["t/1964",[148,3.268,289,3.094,555,4.538,601,4.538]],["t/1966",[148,3.785,289,3.583,601,5.255]],["t/1970",[456,7.686]],["t/1972",[457,7.686]],["t/1974",[458,6.242,459,6.242]],["t/1978",[29,4.256,460,5.854]],["t/1980",[29,3.583,30,5.255,31,2.712]],["t/1982",[8,3.301,193,4.502,339,4.591]],["t/1986",[147,5.347,156,3.667]],["t/1988",[346,3.748,347,5.255,348,5.255]],["t/1990",[4,5.255,125,5.255,431,4.801]],["t/1992",[156,3.667,358,4.936]],["t/1994",[209,3.583,215,3.951,358,4.155]],["t/1996",[209,3.583,215,3.951,266,4.215]],["t/1998",[209,3.583,215,3.951,432,5.479]],["t/2000",[209,3.583,215,3.951,433,5.479]],["t/2002",[344,3.446,434,6.508]],["t/2004",[435,8.013]],["t/2006",[436,8.013]],["t/2008",[437,6.714]],["t/2010",[35,4.963]],["t/2012",[344,3.446,438,6.508]],["t/2014",[439,8.013]],["t/2016",[440,8.013]],["t/2018",[441,8.013]],["t/2020",[602,8.451]],["t/2022",[603,8.451]],["t/2024",[442,8.013]],["t/2026",[444,8.013]],["t/2028",[604,8.451]],["t/2030",[605,8.451]],["t/2032",[606,8.451]],["t/2034",[445,8.013]],["t/2036",[156,3.667,446,4.496]],["t/2038",[33,5.251,446,4.496]],["t/2040",[244,5.163,446,4.496]],["t/2042",[220,6.03,446,4.496]],["t/2044",[352,5.077,446,3.785,447,5.479]],["t/2046",[365,4.155,376,4.801,437,4.591]],["t/2048",[33,4.421,355,5.077,446,3.785]],["t/2050",[145,5.255,365,4.155,446,3.785]],["t/2052",[84,5.006,448,6.508]],["t/2054",[332,4.369,367,4.936]],["t/2055",[41,3.021,82,2.529,367,4.155]],["t/2057",[31,2.712,332,3.678,367,4.155]],["t/2059",[244,4.347,332,3.678,367,4.155]],["t/2061",[28,3.964,126,3.887,344,2.505,345,4.538]],["t/2063",[88,3.887,103,3.753,126,3.887,449,4.731]],["t/2065",[60,4.591,228,4.502,414,5.255]],["t/2069",[143,5.571,156,3.667]],["t/2071",[19,2.915,214,5.077,461,5.479]],["t/2073",[44,4.384,245,3.588,462,4.731,463,4.731]],["t/2075",[464,8.013]],["t/2079",[35,4.031,253,5.082]],["t/2081",[35,3.394,253,4.278,344,2.901]],["t/2083",[32,3.975,35,4.031]],["t/2085",[32,3.346,35,3.394,344,2.901]],["t/2087",[32,3.346,75,4.421,156,3.087]],["t/2089",[32,3.346,146,4.928,255,4.591]],["t/2091",[32,2.889,383,4.145,500,4.049,501,4.731]],["t/2093",[32,3.346,75,4.421,500,4.69]],["t/2095",[156,2.666,500,4.049,502,4.731,503,4.731]],["t/2097",[32,3.975,504,6.508]],["t/2099",[32,3.975,40,6.242]],["t/2101",[35,4.031,505,6.863]],["t/2103",[23,6.508,32,3.975]],["t/2105",[34,5.703,35,4.031]],["t/2107",[34,4.801,35,3.394,344,2.901]],["t/2109",[300,6.242,506,6.242]],["t/2111",[344,4.243]],["t/2115",[68,7.686]],["t/2117",[58,4.155,59,3.525,470,5.077]],["t/2119",[41,2.608,56,3.206,59,3.043,607,4.989]],["t/2121",[15,4.591,116,5.255,608,5.778]],["t/2123",[144,5.571,521,6.03]],["t/2125",[18,6.164]],["t/2129",[1,2.728,52,2.712,56,3.206,474,4.384]],["t/2131",[51,2.168,52,2.712,193,3.887,194,4.538]],["t/2133",[82,2.183,184,3.337,276,4.255,475,4.731]],["t/2137",[217,3.694,270,3.373,358,3.588,473,4.731]],["t/2139",[76,6.242,193,5.347]],["t/2141",[31,2.712,36,3.258,75,4.421]],["t/2143",[16,3.753,26,3.54,36,2.813,75,3.817]],["t/2147",[353,5.571,609,6.863]],["t/2149",[346,4.452,610,6.863]],["t/2151",[159,5.347,517,6.508]],["t/2153",[513,5.479,514,5.479,611,5.479]],["t/2155",[100,5.251,101,6.242]],["t/2157",[84,5.006,289,4.256]],["t/2159",[84,5.006,168,4.749]],["t/2161",[406,5.571,521,6.03]],["t/2165",[41,3.021,93,4.278,472,5.077]],["t/2167",[93,3.694,209,3.094,266,3.639,353,4.049]],["t/2169",[93,4.278,251,4.1,512,5.479]],["t/2171",[82,2.529,251,4.1,612,5.255]],["t/2173",[41,2.295,52,2.386,56,2.82,251,3.115,612,3.992]],["t/2177",[344,4.243]],["t/2178",[613,8.451]],["t/2180",[614,8.451]],["t/2184",[89,6.859]],["t/2186",[26,5.996]],["t/2188",[8,3.301,26,4.1,241,4.69]],["t/2190",[539,6.242,615,6.242]],["t/2194",[223,5.347,476,6.508]],["t/2196",[83,6.242,477,6.508]],["t/2198",[277,6.357]],["t/2200",[266,6.164]],["t/2202",[344,4.243]],["t/2203",[223,6.584]],["t/2205",[478,8.013]],["t/2209",[344,4.243]],["t/2210",[616,8.451]],["t/2212",[617,8.451]],["t/2216",[82,3.004,346,4.452]],["t/2218",[209,3.583,287,3.907,479,4.215]],["t/2220",[100,6.466]],["t/2222",[344,4.243]],["t/2223",[497,8.013]],["t/2225",[498,8.013]],["t/2227",[82,2.529,270,3.907,499,5.479]],["t/2229",[67,3.607,277,5.163]],["t/2233",[82,3.004,346,4.452]],["t/2235",[209,3.583,287,3.907,479,4.215]],["t/2237",[344,4.243]],["t/2238",[480,8.013]],["t/2240",[481,8.013]],["t/2242",[67,3.607,277,5.163]],["t/2246",[82,3.004,346,4.452]],["t/2248",[489,8.013]],["t/2250",[209,3.583,287,3.907,479,4.215]],["t/2252",[100,6.466]],["t/2254",[344,4.243]],["t/2255",[490,8.013]],["t/2257",[491,8.013]],["t/2259",[67,4.442]],["t/2263",[209,3.583,287,3.907,479,4.215]],["t/2265",[344,4.243]],["t/2266",[482,8.013]],["t/2268",[483,8.013]],["t/2270",[484,8.013]],["t/2272",[485,8.013]],["t/2274",[486,8.013]],["t/2276",[487,8.013]],["t/2278",[488,8.013]],["t/2280",[67,4.442]],["t/2284",[31,3.222,208,4.091]],["t/2286",[31,2.712,186,4.801,208,3.444]],["t/2288",[163,5.38]],["t/2290",[129,7.686]],["t/2292",[188,5.482]],["t/2294",[516,7.208]],["t/2296",[289,5.24]],["t/2298",[467,6.164]],["t/2300",[59,5.155]],["t/2302",[522,8.013]],["t/2304",[29,5.24]],["t/2306",[523,8.013]],["t/2308",[51,3.672]],["t/2310",[401,6.357]],["t/2312",[303,6.508,346,4.452]],["t/2314",[3,4.928,31,2.712,208,3.444]],["t/2316",[31,3.222,36,3.87]],["t/2318",[36,3.258,67,3.037,524,5.479]],["t/2320",[36,3.258,67,3.037,305,4.928]],["t/2322",[31,2.342,36,2.813,186,4.145,217,3.694]],["t/2326",[198,5.479,199,5.479,200,4.591]],["t/2330",[344,4.243]],["t/2331",[492,8.013]],["t/2333",[493,8.013]],["t/2335",[494,8.013]],["t/2337",[495,8.013]],["t/2339",[496,8.013]],["t/2341",[98,4.502,265,4.155,353,4.69]],["t/2345",[37,3.321,208,4.091]],["t/2347",[124,5.255,208,3.444,350,4.278]],["t/2349",[37,2.795,208,3.444,465,4.928]],["t/2351",[36,3.258,37,2.795,466,5.479]],["t/2353",[37,3.321,82,3.004]],["t/2355",[37,3.321,467,5.006]],["t/2357",[37,3.321,55,4.693]],["t/2359",[37,3.321,188,4.452]],["t/2361",[37,3.321,163,4.369]],["t/2363",[37,2.795,395,4.347,467,4.215]],["t/2365",[168,3.998,365,4.155,468,4.928]],["t/2367",[289,3.583,365,4.155,468,4.928]],["t/2369",[36,3.87,37,3.321]],["t/2371",[36,3.258,37,2.795,344,2.901]],["t/2373",[36,3.258,37,2.795,67,3.037]],["t/2375",[37,2.795,410,4.928,465,4.928]],["t/2377",[265,4.936,332,4.369]],["t/2379",[37,2.795,332,3.678,469,4.1]],["t/2381",[37,2.414,332,3.176,469,3.54,470,4.384]],["t/2383",[37,2.795,471,5.479,472,5.077]],["t/2385",[82,2.529,467,4.215,469,4.1]],["t/2387",[163,2.794,188,2.847,395,3.302,467,3.202,469,3.115]],["t/2389",[55,4.693,469,4.87]],["t/2399",[227,6.508,228,5.347]],["t/2401",[8,3.301,29,3.583,229,4.591]],["t/2403",[8,3.301,63,4.69,128,3.864]],["t/2405",[8,3.301,62,4.591,230,4.928]],["t/2407",[231,6.508,232,6.508]],["t/2409",[58,4.155,59,3.525,229,4.591]],["t/2411",[12,5.163,229,5.453]],["t/2415",[19,2.915,618,5.077,619,6.232]],["t/2417",[19,2.215,618,3.857,620,4.735,621,4.735,622,4.735]],["t/2419",[19,2.915,32,3.346,618,5.077]],["t/2421",[19,2.915,618,5.077,623,6.232]],["t/2423",[19,2.915,506,5.255,618,5.077]],["t/2427",[3,4.928,48,5.077,305,4.928]],["t/2429",[120,5.453,509,6.863]],["t/2433",[120,5.453,233,6.508]],["t/2435",[11,5.854,234,6.508]],["t/2437",[12,5.163,235,6.508]],["t/2439",[41,3.021,98,4.502,236,5.479]],["t/2441",[10,4.049,27,4.384,28,3.964,237,4.731]],["t/2443",[50,6.242,238,6.508]],["t/2445",[82,3.004,239,6.03]],["t/2447",[51,2.511,105,5.077,132,5.077]],["t/2449",[52,3.141,240,4.69,241,4.69]],["t/2451",[55,3.412,82,2.183,88,3.887,89,4.049]],["t/2453",[58,4.155,59,3.525,242,5.479]],["t/2455",[8,3.301,29,3.583,51,2.511]],["t/2457",[181,4.255,243,4.731,244,3.753,245,3.588]],["t/2459",[246,6.508,247,6.508]],["t/2461",[248,6.508,249,5.854]],["t/2463",[147,5.347,250,5.854]],["t/2465",[45,5.571,251,4.87]],["t/2469",[7,3.256,19,1.977,82,1.715,84,2.858,174,3.256,252,3.716]],["t/2471",[82,2.183,228,3.887,253,3.694,254,3.964]],["t/2473",[19,2.915,255,4.591,256,5.479]],["t/2475",[8,3.301,63,4.69,128,3.864]],["t/2477",[8,3.301,62,4.591,230,4.928]],["t/2479",[51,2.511,136,4.591,257,5.479]],["t/2481",[51,2.168,258,4.049,259,4.384,260,4.731]],["t/2483",[8,3.241,51,1.538,163,2.253,261,2.812,262,3.356,263,3.356]],["t/2485",[41,2.049,51,1.703,174,3.256,264,3.716,265,2.818,266,2.858]],["t/2487",[58,4.08,59,2.159,137,2.941,138,2.812,267,3.019,268,3.356]],["t/2489",[19,2.215,42,3.563,81,3.992,82,1.921,269,4.162]],["t/2491",[19,1.786,37,1.712,143,2.873,270,2.393,271,3.356,272,3.356,273,3.356]],["t/2493",[19,2.915,274,4.155,275,5.479]],["t/2495",[19,2.915,112,5.479,274,4.155]],["t/2497",[19,1.628,82,1.412,240,2.619,261,2.564,270,2.182,276,2.752,277,2.427,278,3.06]],["t/2499",[6,2.708,119,3.11,159,2.757,173,2.812,245,2.545,279,3.356,280,3.356]],["t/2501",[8,2.85,41,2.608,281,4.731,282,4.731]],["t/2503",[0,2.941,8,2.022,41,1.85,42,2.873,82,1.549,88,2.757,163,2.253]],["t/2505",[51,1.402,102,2.681,104,2.681,136,2.564,137,2.681,184,2.158,283,3.06,284,3.06]],["t/2507",[51,1.703,156,2.094,258,3.181,261,3.114,285,3.716,286,3.716]],["t/2509",[19,2.915,287,3.907,288,5.479]],["t/2511",[19,2.915,289,3.583,290,5.479]],["t/2513",[15,2.812,56,2.274,133,3.219,138,2.812,291,3.11,292,2.873,293,3.019]],["t/2515",[12,3.302,58,3.157,59,2.678,251,3.115,292,3.563]],["t/2517",[6,3.358,51,1.907,188,2.847,479,3.202,507,4.39]],["t/2519",[19,2.915,287,3.907,508,5.778]],["t/2521",[294,4.145,295,4.731,296,4.731,297,4.731]],["t/2525",[510,8.451]],["t/2529",[41,3.021,93,4.278,114,4.155]],["t/2531",[10,4.69,26,4.1,114,4.155]],["t/2533",[114,4.936,223,5.347]],["t/2535",[51,2.511,349,5.479,511,5.778]],["t/2537",[52,3.141,114,4.155,178,3.444]],["t/2539",[51,2.168,66,3.588,114,3.588,184,3.337]],["t/2541",[103,4.347,114,4.155,156,3.087]],["t/2543",[25,3.495,37,2.414,148,3.268,289,3.094]],["t/2545",[25,4.047,156,3.087,344,2.901]],["t/2547",[1,3.159,25,4.047,31,2.712]],["t/2551",[69,5.571,312,6.508]],["t/2553",[19,2.915,156,3.087,182,5.255]],["t/2555",[156,3.087,185,5.255,293,4.928]],["t/2557",[19,2.915,82,2.529,187,5.255]],["t/2559",[51,2.511,188,3.748,189,5.255]],["t/2561",[51,2.511,163,3.678,191,5.255]],["t/2563",[192,5.255,249,4.928,313,5.479]],["t/2565",[19,2.915,195,5.255,314,5.479]],["t/2567",[19,2.915,196,5.255,255,4.591]],["t/2569",[315,5.479,316,5.077,317,5.479]],["t/2573",[41,2.295,93,3.25,178,2.616,203,4.39,204,4.39]],["t/2575",[25,3.495,49,4.731,87,4.384,209,3.094]],["t/2577",[31,2.061,202,4.39,208,2.616,209,2.722,624,4.735]],["t/2579",[25,5.92]],["t/2581",[19,1.977,20,3.716,44,3.443,625,4.227,626,4.227,627,4.227]],["t/2585",[1,3.753,82,3.004]],["t/2587",[1,3.753,289,4.256]],["t/2589",[1,3.753,14,5.854]],["t/2591",[15,4.591,82,2.529,298,5.479]],["t/2593",[51,2.982,188,4.452]],["t/2595",[1,3.159,52,3.141,56,3.712]],["t/2597",[6,5.251,55,4.693]],["t/2599",[29,3.583,51,2.511,299,5.255]],["t/2601",[59,5.02,60,4.591]],["t/2605",[69,4.69,265,4.155,318,4.928]],["t/2607",[149,5.347,200,5.453]],["t/2609",[67,3.037,149,4.502,150,5.255]],["t/2611",[319,4.928,320,5.479,321,5.479]],["t/2613",[322,4.731,323,4.731,324,4.538,325,4.731]],["t/2615",[326,6.508,327,6.508]],["t/2617",[147,5.347,169,6.242]],["t/2621",[41,3.021,82,2.529,392,4.502]],["t/2623",[41,3.021,344,2.901,628,4.928]],["t/2625",[41,3.588,354,5.347]],["t/2627",[41,3.021,82,2.529,178,3.444]],["t/2629",[82,3.004,615,6.242]],["t/2631",[52,3.731,392,5.347]],["t/2633",[52,3.731,239,6.03]],["t/2635",[51,2.511,291,5.077,338,5.077]],["t/2637",[52,3.141,339,4.591,392,4.502]],["t/2639",[52,3.141,344,2.901,628,4.928]],["t/2641",[52,3.731,354,5.347]],["t/2643",[52,3.731,178,4.091]],["t/2645",[1,3.159,52,3.141,56,3.712]],["t/2647",[148,4.496,168,4.749]],["t/2649",[148,4.496,516,5.854]],["t/2651",[148,4.496,289,4.256]],["t/2653",[55,5.779]],["t/2655",[29,3.583,31,2.712,51,2.511]],["t/2657",[31,2.342,59,4.512,60,3.964]],["t/2659",[93,3.694,258,4.049,259,4.384,628,4.255]],["t/2663",[82,3.004,108,5.703]],["t/2665",[537,5.255,538,5.778,539,5.255]],["t/2667",[384,4.749,540,6.242]],["t/2669",[82,2.529,360,4.801,537,5.255]],["t/2671",[67,3.037,541,5.778,542,4.928]],["t/2673",[67,3.037,542,4.928,543,5.778]],["t/2675",[67,2.622,240,4.049,241,4.049,542,4.255]],["t/2677",[51,2.168,67,2.622,90,4.538,544,4.989]],["t/2679",[52,3.731,108,5.703]],["t/2681",[384,4.749,540,6.242]],["t/2683",[52,3.141,360,4.801,378,3.951]],["t/2689",[525,8.451]],["t/2691",[431,5.703,526,6.863]],["t/2693",[527,8.451]],["t/2695",[528,8.451]],["t/2697",[529,8.451]],["t/2701",[532,8.451]],["t/2703",[533,8.451]],["t/2705",[534,8.451]],["t/2711",[68,7.686]],["t/2713",[156,4.516]],["t/2715",[31,3.222,563,6.508]],["t/2717",[350,5.082,629,6.863]],["t/2719",[629,6.863,630,7.403]],["t/2723",[265,4.936,318,5.854]],["t/2725",[149,5.347,200,5.453]],["t/2727",[319,7.208]],["t/2729",[328,6.508,329,6.508]],["t/2731",[330,8.013]],["t/2733",[24,4.538,45,4.049,126,3.887,217,3.694]],["t/2739",[31,2.712,208,3.444,335,3.998]],["t/2741",[31,2.712,36,3.258,335,3.998]],["t/2743",[37,2.795,208,3.444,335,3.998]],["t/2745",[36,3.258,37,2.795,335,3.998]],["t/2747",[190,4.278,335,3.998,336,5.479]],["t/2749",[16,3.753,26,3.54,36,2.813,335,3.452]],["t/2751",[67,4.442]],["t/2753",[31,2.712,163,3.678,208,3.444]],["t/2755",[29,3.583,31,2.712,208,3.444]],["t/2759",[251,5.996]],["t/2761",[333,8.013]],["t/2763",[331,4.731,332,3.176,535,4.989,536,4.989]],["t/2767",[156,4.516]],["t/2769",[144,5.571,530,6.863]],["t/2771",[431,7.022]],["t/2773",[270,4.64,531,6.863]],["t/2779",[67,4.442]],["t/2781",[82,3.699]],["t/2787",[156,4.516]],["t/2789",[82,3.004,368,4.936]],["t/2791",[52,3.731,368,4.936]],["t/2793",[368,4.936,369,6.508]],["t/2795",[338,6.03,368,4.936]],["t/2797",[173,5.453,368,4.936]],["t/2799",[67,3.607,370,6.508]],["t/2801",[371,8.013]],["t/2803",[274,4.936,372,6.508]],["t/2805",[574,5.703,631,6.508]],["t/2809",[357,6.508,358,4.936]],["t/2813",[274,6.078]],["t/2815",[156,4.516]],["t/2817",[32,3.346,35,3.394,359,4.347]],["t/2819",[35,3.394,127,4.801,359,4.347]],["t/2821",[31,2.712,178,3.444,360,4.801]],["t/2822",[545,8.451]],["t/2824",[31,2.342,178,2.973,184,3.337,361,4.538]],["t/2825",[546,8.451]],["t/2829",[66,3.157,82,1.921,253,3.25,254,3.488,632,4.162]],["t/2831",[66,3.157,184,2.936,253,3.25,254,3.488,632,4.162]],["t/2833",[32,2.542,66,3.157,184,2.936,254,3.488,632,4.162]],["t/2835",[82,2.183,289,3.094,549,4.989,550,4.989]],["t/2839",[304,5.077,460,4.928,551,5.778]],["t/2841",[178,4.091,552,6.863]],["t/2843",[553,6.863,554,6.863]],["t/2845",[92,5.255,102,4.801,179,5.479]],["t/2847",[37,2.795,555,5.255,556,5.778]],["t/2849",[180,6.508,557,6.863]],["t/2851",[120,5.453,558,6.863]],["t/2853",[156,4.516]],["t/2855",[559,8.451]],["t/2857",[560,8.451]],["t/2859",[561,8.451]],["t/2861",[344,4.243]],["t/2863",[160,5.479,270,3.907,562,5.778]],["t/2865",[31,3.222,563,6.508]],["t/2866",[564,8.451]],["t/2868",[565,8.451]],["t/2870",[566,8.451]],["t/2872",[567,8.451]],["t/2874",[568,8.451]],["t/2876",[569,8.451]],["t/2878",[570,8.451]],["t/2880",[571,8.451]],["t/2882",[572,8.451]],["t/2884",[633,9.116]],["t/2886",[573,8.451]],["t/2888",[574,5.703,631,6.508]],["t/2890",[146,4.928,316,5.077,575,5.778]],["t/2894",[274,6.078]],["t/2896",[156,4.516]],["t/2898",[32,3.346,35,3.394,359,4.347]],["t/2900",[35,3.394,127,4.801,359,4.347]],["t/2902",[31,3.222,363,6.508]],["t/2903",[547,8.451]],["t/2905",[548,8.451]],["t/2909",[344,4.243]],["t/2911",[245,3.588,270,3.373,365,3.588,366,4.731]],["t/2913",[244,4.347,332,3.678,367,4.155]],["t/2917",[41,2.295,55,3.002,56,2.82,207,3.857,267,3.744]],["t/2919",[1,3.159,31,2.712,373,5.479]],["t/2921",[1,3.159,31,2.712,374,5.479]],["t/2923",[1,3.159,31,2.712,375,5.479]],["t/2925",[156,4.516]],["t/2929",[128,3.864,188,3.748,378,3.951]],["t/2931",[128,3.864,163,3.678,378,3.951]],["t/2933",[29,3.583,128,3.864,378,3.951]],["t/2935",[59,3.525,128,3.864,378,3.951]],["t/2937",[128,3.864,378,3.951,386,4.801]],["t/2939",[128,3.864,339,4.591,378,3.951]],["t/2941",[128,3.864,292,4.69,378,3.951]],["t/2945",[52,3.141,190,4.278,394,4.928]],["t/2947",[395,6.357]],["t/2949",[51,3.672]],["t/2951",[401,6.357]],["t/2953",[402,7.208]],["t/2955",[396,7.208]],["t/2957",[404,7.208]],["t/2959",[399,7.208]],["t/2961",[400,7.208]],["t/2963",[397,7.425]],["t/2965",[398,7.425]],["t/2967",[576,8.451]],["t/2969",[67,4.442]],["t/2971",[346,3.748,577,5.255,578,5.778]],["t/2973",[52,2.712,178,2.973,214,4.384,215,3.412]],["t/2977",[41,3.021,148,3.785,168,3.998]],["t/2979",[376,7.022]],["t/2981",[377,8.013]],["t/2983",[51,2.982,216,6.03]],["t/2985",[378,4.693,379,6.508]],["t/2987",[294,5.703,354,5.347]],["t/2989",[188,4.452,380,6.508]],["t/2991",[66,4.936,381,6.508]],["t/2993",[350,5.082,382,6.03]],["t/2995",[383,7.022]],["t/2997",[178,4.091,384,4.749]],["t/2999",[384,5.847]],["t/3001",[7,5.703,350,5.082]],["t/3003",[385,6.242,386,5.703]],["t/3005",[41,3.021,148,3.785,289,3.583]],["t/3007",[600,8.451]],["t/3009",[148,3.268,289,3.094,555,4.538,601,4.538]],["t/3011",[148,3.785,289,3.583,601,5.255]],["t/3015",[82,2.529,190,4.278,394,4.928]],["t/3017",[395,6.357]],["t/3019",[396,7.208]],["t/3021",[397,7.425]],["t/3023",[398,7.425]],["t/3025",[399,7.208]],["t/3027",[400,7.208]],["t/3029",[401,6.357]],["t/3031",[402,7.208]],["t/3033",[51,3.672]],["t/3035",[395,6.357]],["t/3037",[403,8.013]],["t/3039",[404,7.208]],["t/3041",[82,3.004,384,4.749]],["t/3043",[67,4.442]],["t/3045",[178,4.091,405,6.508]],["t/3047",[178,4.091,384,4.749]],["t/3049",[82,2.183,178,2.973,401,3.753,406,4.049]],["t/3051",[190,4.278,407,5.479,408,5.479]],["t/3053",[217,3.694,245,3.588,266,3.639,287,3.373]],["t/3055",[446,3.785,474,5.077,579,5.778]],["t/3059",[51,3.672]],["t/3061",[51,2.511,409,5.479,410,4.928]],["t/3063",[103,4.347,183,4.347,411,4.928]],["t/3065",[51,2.168,183,3.753,184,3.337,411,4.255]],["t/3067",[51,2.168,104,4.145,183,3.753,412,4.731]],["t/3069",[51,2.982,580,6.863]],["t/3071",[51,2.982,103,5.163]],["t/3073",[51,2.982,344,3.446]],["t/3075",[59,5.155]],["t/3077",[416,8.013]],["t/3079",[581,8.451]],["t/3081",[417,8.013]],["t/3083",[418,8.013]],["t/3085",[205,8.451]],["t/3087",[582,8.451]],["t/3089",[583,8.451]],["t/3091",[584,8.451]],["t/3093",[585,8.451]],["t/3095",[586,8.451]],["t/3097",[587,8.451]],["t/3099",[588,8.451]],["t/3101",[589,8.451]],["t/3103",[590,8.451]],["t/3105",[591,8.451]],["t/3107",[592,8.451]],["t/3109",[593,8.451]],["t/3111",[594,8.451]],["t/3113",[595,8.451]],["t/3115",[596,8.451]],["t/3117",[422,8.013]],["t/3119",[423,8.013]],["t/3121",[597,8.451]],["t/3123",[634,9.116]],["t/3125",[424,8.013]],["t/3127",[425,8.013]],["t/3129",[598,8.451]],["t/3131",[635,9.116]],["t/3133",[51,2.511,67,3.037,215,3.951]],["t/3137",[29,4.256,460,5.854]],["t/3139",[29,3.583,30,5.255,31,2.712]],["t/3141",[8,3.301,193,4.502,339,4.591]],["t/3145",[66,4.155,250,4.928,358,4.155]],["t/3147",[437,4.591,450,5.479,451,5.479]],["t/3149",[392,4.502,452,5.479,453,5.479]],["t/3151",[37,2.795,82,2.529,454,5.479]],["t/3153",[368,4.936,455,6.508]],["t/3155",[1,3.159,406,4.69,421,5.479]],["t/3159",[426,8.013]],["t/3161",[428,8.013]],["t/3163",[427,8.013]],["t/3165",[429,8.013]],["t/3167",[577,7.686]],["t/3169",[430,8.013]],["t/3171",[599,8.451]],["t/3175",[147,5.347,156,3.667]],["t/3177",[346,3.748,347,5.255,348,5.255]],["t/3179",[4,5.255,125,5.255,431,4.801]],["t/3181",[156,3.667,358,4.936]],["t/3183",[209,3.583,215,3.951,358,4.155]],["t/3185",[209,3.583,215,3.951,266,4.215]],["t/3187",[209,3.583,215,3.951,432,5.479]],["t/3189",[209,3.583,215,3.951,433,5.479]],["t/3191",[344,3.446,434,6.508]],["t/3193",[435,8.013]],["t/3195",[436,8.013]],["t/3197",[437,6.714]],["t/3199",[35,4.963]],["t/3201",[344,3.446,438,6.508]],["t/3203",[439,8.013]],["t/3205",[440,8.013]],["t/3207",[441,8.013]],["t/3209",[602,8.451]],["t/3211",[603,8.451]],["t/3213",[442,8.013]],["t/3215",[444,8.013]],["t/3217",[604,8.451]],["t/3219",[636,9.116]],["t/3221",[605,8.451]],["t/3223",[606,8.451]],["t/3225",[445,8.013]],["t/3227",[156,3.667,446,4.496]],["t/3229",[33,5.251,446,4.496]],["t/3231",[244,5.163,446,4.496]],["t/3233",[220,6.03,446,4.496]],["t/3235",[352,5.077,446,3.785,447,5.479]],["t/3237",[1,2.728,31,2.342,181,4.255,212,4.989]],["t/3239",[365,4.155,376,4.801,437,4.591]],["t/3241",[33,4.421,355,5.077,446,3.785]],["t/3243",[145,5.255,365,4.155,446,3.785]],["t/3245",[84,5.006,448,6.508]],["t/3247",[332,4.369,367,4.936]],["t/3248",[41,3.021,82,2.529,367,4.155]],["t/3250",[41,2.295,178,2.616,352,3.857,355,3.857,637,4.735]],["t/3252",[31,2.712,332,3.678,367,4.155]],["t/3254",[244,4.347,332,3.678,367,4.155]],["t/3256",[28,3.964,126,3.887,344,2.505,345,4.538]],["t/3258",[88,3.887,103,3.753,126,3.887,449,4.731]],["t/3260",[60,4.591,228,4.502,414,5.255]],["t/3264",[35,4.031,253,5.082]],["t/3266",[35,3.394,253,4.278,344,2.901]],["t/3268",[32,3.975,35,4.031]],["t/3270",[32,3.346,35,3.394,344,2.901]],["t/3272",[32,3.346,75,4.421,156,3.087]],["t/3274",[32,3.346,146,4.928,255,4.591]],["t/3276",[32,2.889,383,4.145,500,4.049,501,4.731]],["t/3278",[32,3.346,75,4.421,500,4.69]],["t/3280",[156,2.666,500,4.049,502,4.731,503,4.731]],["t/3282",[32,3.975,504,6.508]],["t/3284",[32,3.975,40,6.242]],["t/3286",[23,6.508,32,3.975]],["t/3288",[34,5.703,35,4.031]],["t/3290",[34,4.801,35,3.394,344,2.901]],["t/3292",[300,6.242,506,6.242]],["t/3294",[344,4.243]],["t/3298",[456,7.686]],["t/3300",[457,7.686]],["t/3302",[458,6.242,459,6.242]],["t/3306",[143,5.571,156,3.667]],["t/3308",[19,2.915,214,5.077,461,5.479]],["t/3310",[44,4.384,245,3.588,462,4.731,463,4.731]],["t/3312",[464,8.013]],["t/3316",[574,7.022]],["t/3317",[456,6.242,574,5.703]],["t/3319",[457,6.242,574,5.703]],["t/3321",[458,6.242,459,6.242]],["t/3323",[574,5.703,631,6.508]],["t/3325",[638,9.116]],["t/3326",[213,8.451]],["t/3328",[639,9.116]],["t/3332",[1,2.728,52,2.712,56,3.206,474,4.384]],["t/3334",[51,2.168,52,2.712,193,3.887,194,4.538]],["t/3336",[82,2.183,184,3.337,276,4.255,475,4.731]],["t/3340",[58,4.155,59,3.525,470,5.077]],["t/3342",[41,2.608,56,3.206,59,3.043,607,4.989]],["t/3344",[15,4.591,116,5.255,608,5.778]],["t/3346",[144,5.571,521,6.03]],["t/3348",[18,6.164]],["t/3352",[217,3.694,270,3.373,358,3.588,473,4.731]],["t/3354",[76,6.242,193,5.347]],["t/3356",[31,2.712,36,3.258,75,4.421]],["t/3358",[16,3.753,26,3.54,36,2.813,75,3.817]],["t/3362",[12,5.163,165,6.863]],["t/3364",[304,6.03,460,5.854]],["t/3366",[16,4.347,26,4.1,52,3.141]],["t/3368",[16,4.347,52,3.141,89,4.69]],["t/3370",[37,2.795,332,3.678,469,4.1]],["t/3372",[67,3.607,90,6.242]],["t/3376",[353,5.571,609,6.863]],["t/3378",[346,4.452,610,6.863]],["t/3380",[159,5.347,517,6.508]],["t/3382",[513,5.479,514,5.479,611,5.479]],["t/3384",[100,5.251,101,6.242]],["t/3386",[84,5.006,289,4.256]],["t/3388",[84,5.006,168,4.749]],["t/3390",[406,5.571,521,6.03]],["t/3394",[41,3.021,93,4.278,472,5.077]],["t/3396",[93,3.694,209,3.094,266,3.639,353,4.049]],["t/3398",[93,4.278,251,4.1,512,5.479]],["t/3400",[82,2.529,251,4.1,612,5.255]],["t/3402",[41,2.295,52,2.386,56,2.82,251,3.115,612,3.992]],["t/3406",[37,3.321,208,4.091]],["t/3408",[124,5.255,208,3.444,350,4.278]],["t/3410",[37,2.795,208,3.444,465,4.928]],["t/3412",[36,3.258,37,2.795,466,5.479]],["t/3414",[37,3.321,82,3.004]],["t/3416",[37,3.321,467,5.006]],["t/3418",[37,3.321,55,4.693]],["t/3420",[37,3.321,188,4.452]],["t/3422",[37,3.321,163,4.369]],["t/3424",[37,2.795,395,4.347,467,4.215]],["t/3426",[168,3.998,365,4.155,468,4.928]],["t/3428",[289,3.583,365,4.155,468,4.928]],["t/3430",[36,3.87,37,3.321]],["t/3432",[36,3.258,37,2.795,344,2.901]],["t/3434",[36,3.258,37,2.795,67,3.037]],["t/3436",[37,2.795,410,4.928,465,4.928]],["t/3438",[265,4.936,332,4.369]],["t/3440",[37,2.795,332,3.678,469,4.1]],["t/3442",[37,2.414,332,3.176,469,3.54,470,4.384]],["t/3444",[37,2.795,471,5.479,472,5.077]],["t/3446",[82,2.529,467,4.215,469,4.1]],["t/3448",[163,2.794,188,2.847,395,3.302,467,3.202,469,3.115]],["t/3450",[55,4.693,469,4.87]],["t/3454",[344,4.243]],["t/3455",[613,8.451]],["t/3457",[614,8.451]],["t/3461",[89,6.859]],["t/3463",[26,5.996]],["t/3465",[539,6.242,615,6.242]],["t/3469",[223,5.347,476,6.508]],["t/3471",[83,6.242,477,6.508]],["t/3473",[277,6.357]],["t/3475",[266,6.164]],["t/3477",[344,4.243]],["t/3478",[223,6.584]],["t/3480",[478,8.013]],["t/3484",[344,4.243]],["t/3485",[616,8.451]],["t/3487",[617,8.451]],["t/3491",[8,3.301,26,4.1,241,4.69]],["t/3495",[31,3.222,208,4.091]],["t/3497",[31,2.712,186,4.801,208,3.444]],["t/3499",[31,3.222,354,5.347]],["t/3501",[163,5.38]],["t/3503",[129,7.686]],["t/3505",[188,5.482]],["t/3507",[516,7.208]],["t/3509",[289,5.24]],["t/3511",[467,6.164]],["t/3513",[59,5.155]],["t/3515",[522,8.013]],["t/3517",[29,5.24]],["t/3519",[523,8.013]],["t/3521",[51,3.672]],["t/3523",[401,6.357]],["t/3525",[611,8.013]],["t/3527",[3,4.928,31,2.712,208,3.444]],["t/3529",[31,3.222,36,3.87]],["t/3531",[36,3.258,67,3.037,524,5.479]],["t/3533",[36,3.258,67,3.037,305,4.928]],["t/3535",[31,2.342,36,2.813,186,4.145,217,3.694]],["t/3537",[10,4.69,168,3.998,332,3.678]],["t/3539",[19,2.215,148,4.4,168,3.037,208,2.616]],["t/3541",[19,2.215,36,2.475,148,4.4,168,3.037]],["t/3545",[209,3.583,287,3.907,479,4.215]],["t/3547",[344,4.243]],["t/3548",[482,8.013]],["t/3550",[483,8.013]],["t/3552",[484,8.013]],["t/3554",[485,8.013]],["t/3556",[486,8.013]],["t/3558",[487,8.013]],["t/3560",[488,8.013]],["t/3562",[67,4.442]],["t/3566",[82,3.004,346,4.452]],["t/3568",[209,3.583,287,3.907,479,4.215]],["t/3570",[100,6.466]],["t/3572",[344,4.243]],["t/3573",[497,8.013]],["t/3575",[498,8.013]],["t/3577",[82,2.529,270,3.907,499,5.479]],["t/3579",[67,3.607,277,5.163]],["t/3583",[82,3.004,346,4.452]],["t/3585",[209,3.583,287,3.907,479,4.215]],["t/3587",[344,4.243]],["t/3588",[480,8.013]],["t/3590",[481,8.013]],["t/3592",[67,3.607,277,5.163]],["t/3596",[82,3.004,346,4.452]],["t/3598",[489,8.013]],["t/3600",[209,3.583,287,3.907,479,4.215]],["t/3602",[100,6.466]],["t/3604",[344,4.243]],["t/3605",[490,8.013]],["t/3607",[491,8.013]],["t/3609",[67,4.442]],["t/3615",[344,4.243]],["t/3616",[492,8.013]],["t/3618",[493,8.013]],["t/3620",[494,8.013]],["t/3622",[495,8.013]],["t/3624",[496,8.013]],["t/3626",[98,4.502,265,4.155,353,4.69]],["t/3628",[220,4.384,221,4.989,640,5.381,641,5.381]],["t/3632",[41,3.021,82,2.529,392,4.502]],["t/3634",[41,3.021,344,2.901,628,4.928]],["t/3636",[41,3.588,354,5.347]],["t/3638",[41,3.021,82,2.529,178,3.444]],["t/3640",[82,3.004,615,6.242]],["t/3642",[52,3.731,392,5.347]],["t/3644",[52,3.731,239,6.03]],["t/3646",[51,2.511,291,5.077,338,5.077]],["t/3648",[52,3.141,339,4.591,392,4.502]],["t/3650",[52,3.141,344,2.901,628,4.928]],["t/3652",[52,3.731,354,5.347]],["t/3654",[52,3.731,178,4.091]],["t/3656",[1,3.159,52,3.141,56,3.712]],["t/3658",[148,4.496,168,4.749]],["t/3660",[148,4.496,516,5.854]],["t/3662",[148,4.496,289,4.256]],["t/3664",[55,5.779]],["t/3666",[29,3.583,31,2.712,51,2.511]],["t/3668",[31,2.342,59,4.512,60,3.964]],["t/3670",[93,3.694,258,4.049,259,4.384,628,4.255]]],"invertedIndex":[["",{"_index":183,"t":{"288":{"position":[[3,1]]},"300":{"position":[[3,1]]},"302":{"position":[[3,1]]},"815":{"position":[[19,3]]},"817":{"position":[[23,3]]},"819":{"position":[[22,3]]},"1834":{"position":[[19,3]]},"1836":{"position":[[22,3]]},"1838":{"position":[[23,3]]},"3063":{"position":[[19,3]]},"3065":{"position":[[22,3]]},"3067":{"position":[[23,3]]}}}],["0",{"_index":312,"t":{"486":{"position":[[0,2]]},"1380":{"position":[[0,2]]},"2551":{"position":[[0,2]]}}}],["1",{"_index":182,"t":{"288":{"position":[[0,2]]},"488":{"position":[[0,2]]},"1382":{"position":[[0,2]]},"2553":{"position":[[0,2]]}}}],["2",{"_index":185,"t":{"290":{"position":[[0,2]]},"490":{"position":[[0,2]]},"1384":{"position":[[0,2]]},"2555":{"position":[[0,2]]}}}],["3",{"_index":187,"t":{"292":{"position":[[0,2]]},"492":{"position":[[0,2]]},"1386":{"position":[[0,2]]},"2557":{"position":[[0,2]]}}}],["4",{"_index":189,"t":{"294":{"position":[[0,2]]},"494":{"position":[[0,2]]},"1388":{"position":[[0,2]]},"2559":{"position":[[0,2]]}}}],["5",{"_index":191,"t":{"296":{"position":[[0,2]]},"496":{"position":[[0,2]]},"1390":{"position":[[0,2]]},"2561":{"position":[[0,2]]}}}],["6",{"_index":192,"t":{"298":{"position":[[0,2]]},"498":{"position":[[0,2]]},"1392":{"position":[[0,2]]},"2563":{"position":[[0,2]]}}}],["7",{"_index":195,"t":{"300":{"position":[[0,2]]},"500":{"position":[[0,2]]},"1394":{"position":[[0,2]]},"2565":{"position":[[0,2]]}}}],["8",{"_index":196,"t":{"302":{"position":[[0,2]]},"502":{"position":[[0,2]]},"1396":{"position":[[0,2]]},"2567":{"position":[[0,2]]}}}],["9",{"_index":315,"t":{"504":{"position":[[0,2]]},"1398":{"position":[[0,2]]},"2569":{"position":[[0,2]]}}}],["abli",{"_index":621,"t":{"2417":{"position":[[22,5]]}}}],["abus",{"_index":549,"t":{"1671":{"position":[[14,7]]},"2835":{"position":[[14,7]]}}}],["accept",{"_index":81,"t":{"105":{"position":[[0,6]]},"416":{"position":[[17,9]]},"1240":{"position":[[17,9]]},"2489":{"position":[[17,9]]}}}],["access",{"_index":544,"t":{"1582":{"position":[[14,6]]},"2677":{"position":[[14,6]]}}}],["ad",{"_index":142,"t":{"197":{"position":[[0,6]]},"249":{"position":[[0,6]]},"480":{"position":[[0,6]]}}}],["add",{"_index":139,"t":{"191":{"position":[[0,3]]},"193":{"position":[[0,3]]}}}],["addit",{"_index":406,"t":{"805":{"position":[[11,10]]},"1432":{"position":[[0,10]]},"1820":{"position":[[11,10]]},"1928":{"position":[[0,10]]},"2161":{"position":[[0,10]]},"3049":{"position":[[11,10]]},"3155":{"position":[[0,10]]},"3390":{"position":[[0,10]]}}}],["address",{"_index":436,"t":{"893":{"position":[[0,7]]},"2006":{"position":[[0,7]]},"3195":{"position":[[0,7]]}}}],["adjust",{"_index":204,"t":{"314":{"position":[[16,11]]},"2573":{"position":[[27,11]]}}}],["admin",{"_index":244,"t":{"384":{"position":[[9,5]]},"667":{"position":[[0,5]]},"919":{"position":[[0,5]]},"938":{"position":[[9,5]]},"1342":{"position":[[9,5]]},"1661":{"position":[[0,5]]},"2040":{"position":[[0,5]]},"2059":{"position":[[9,5]]},"2457":{"position":[[9,5]]},"2913":{"position":[[0,5]]},"3231":{"position":[[0,5]]},"3254":{"position":[[9,5]]}}}],["advanc",{"_index":438,"t":{"899":{"position":[[0,8]]},"2012":{"position":[[0,8]]},"3201":{"position":[[0,8]]}}}],["advic",{"_index":518,"t":{"1424":{"position":[[22,7]]}}}],["allow_anonymous_connect_without_token",{"_index":605,"t":{"2030":{"position":[[0,37]]},"3221":{"position":[[0,37]]}}}],["allow_history_for_anonym",{"_index":591,"t":{"1874":{"position":[[0,27]]},"3105":{"position":[[0,27]]}}}],["allow_history_for_cli",{"_index":590,"t":{"1872":{"position":[[0,24]]},"3103":{"position":[[0,24]]}}}],["allow_history_for_subscrib",{"_index":589,"t":{"1870":{"position":[[0,28]]},"3101":{"position":[[0,28]]}}}],["allow_presence_for_anonym",{"_index":594,"t":{"1880":{"position":[[0,28]]},"3111":{"position":[[0,28]]}}}],["allow_presence_for_cli",{"_index":593,"t":{"1878":{"position":[[0,25]]},"3109":{"position":[[0,25]]}}}],["allow_presence_for_subscrib",{"_index":592,"t":{"1876":{"position":[[0,29]]},"3107":{"position":[[0,29]]}}}],["allow_publish_for_anonym",{"_index":588,"t":{"1868":{"position":[[0,27]]},"3099":{"position":[[0,27]]}}}],["allow_publish_for_cli",{"_index":587,"t":{"1866":{"position":[[0,24]]},"3097":{"position":[[0,24]]}}}],["allow_publish_for_subscrib",{"_index":586,"t":{"1864":{"position":[[0,28]]},"3095":{"position":[[0,28]]}}}],["allow_subscribe_for_anonym",{"_index":585,"t":{"1862":{"position":[[0,29]]},"3093":{"position":[[0,29]]}}}],["allow_subscribe_for_cli",{"_index":584,"t":{"1860":{"position":[[0,26]]},"3091":{"position":[[0,26]]}}}],["allow_user_limited_channel",{"_index":595,"t":{"1882":{"position":[[0,27]]},"3113":{"position":[[0,27]]}}}],["allowed_origin",{"_index":435,"t":{"891":{"position":[[0,15]]},"2004":{"position":[[0,15]]},"3193":{"position":[[0,15]]}}}],["alreadi",{"_index":380,"t":{"709":{"position":[[0,7]]},"1944":{"position":[[0,7]]},"2989":{"position":[[0,7]]}}}],["analyt",{"_index":180,"t":{"273":{"position":[[5,9]]},"1685":{"position":[[8,9]]},"2849":{"position":[[8,9]]}}}],["anonym",{"_index":414,"t":{"827":{"position":[[0,9]]},"944":{"position":[[0,9]]},"2065":{"position":[[0,9]]},"3260":{"position":[[0,9]]}}}],["answer",{"_index":295,"t":{"442":{"position":[[20,6]]},"1272":{"position":[[20,6]]},"2521":{"position":[[20,6]]}}}],["api",{"_index":31,"t":{"33":{"position":[[18,3]]},"41":{"position":[[7,3]]},"121":{"position":[[19,3]]},"137":{"position":[[19,3]]},"259":{"position":[[33,3]]},"261":{"position":[[19,3]]},"320":{"position":[[9,3]]},"324":{"position":[[25,3]]},"566":{"position":[[12,3]]},"568":{"position":[[12,3]]},"580":{"position":[[13,3]]},"582":{"position":[[13,3]]},"603":{"position":[[13,3]]},"605":{"position":[[9,3]]},"635":{"position":[[13,3]]},"637":{"position":[[23,3]]},"655":{"position":[[11,3]]},"657":{"position":[[13,3]]},"687":{"position":[[26,3]]},"689":{"position":[[23,3]]},"691":{"position":[[26,3]]},"755":{"position":[[7,3]]},"936":{"position":[[9,3]]},"970":{"position":[[18,3]]},"1058":{"position":[[13,3]]},"1376":{"position":[[7,3]]},"1452":{"position":[[5,3]]},"1454":{"position":[[5,3]]},"1482":{"position":[[5,3]]},"1484":{"position":[[5,3]]},"1490":{"position":[[5,3]]},"1534":{"position":[[12,3]]},"1536":{"position":[[12,3]]},"1548":{"position":[[13,3]]},"1550":{"position":[[13,3]]},"1634":{"position":[[13,3]]},"1637":{"position":[[23,3]]},"1650":{"position":[[7,3]]},"1701":{"position":[[0,3]]},"1730":{"position":[[26,3]]},"1732":{"position":[[23,3]]},"1734":{"position":[[26,3]]},"1980":{"position":[[18,3]]},"2057":{"position":[[9,3]]},"2141":{"position":[[13,3]]},"2284":{"position":[[5,3]]},"2286":{"position":[[5,3]]},"2314":{"position":[[5,3]]},"2316":{"position":[[5,3]]},"2322":{"position":[[5,3]]},"2547":{"position":[[7,3]]},"2577":{"position":[[9,3]]},"2655":{"position":[[16,3]]},"2657":{"position":[[28,3]]},"2715":{"position":[[0,3]]},"2739":{"position":[[12,3]]},"2741":{"position":[[12,3]]},"2753":{"position":[[13,3]]},"2755":{"position":[[13,3]]},"2821":{"position":[[13,3]]},"2824":{"position":[[23,3]]},"2865":{"position":[[0,3]]},"2902":{"position":[[7,3]]},"2919":{"position":[[26,3]]},"2921":{"position":[[23,3]]},"2923":{"position":[[26,3]]},"3139":{"position":[[18,3]]},"3237":{"position":[[22,3]]},"3252":{"position":[[9,3]]},"3356":{"position":[[13,3]]},"3495":{"position":[[5,3]]},"3497":{"position":[[5,3]]},"3499":{"position":[[0,3]]},"3527":{"position":[[5,3]]},"3529":{"position":[[5,3]]},"3535":{"position":[[5,3]]},"3666":{"position":[[16,3]]},"3668":{"position":[[28,3]]}}}],["apn",{"_index":561,"t":{"1695":{"position":[[0,4]]},"2859":{"position":[[0,4]]}}}],["app",{"_index":138,"t":{"189":{"position":[[18,3]]},"215":{"position":[[6,3]]},"245":{"position":[[20,3]]},"414":{"position":[[25,4]]},"438":{"position":[[53,3]]},"1238":{"position":[[25,4]]},"1264":{"position":[[53,3]]},"2487":{"position":[[25,4]]},"2513":{"position":[[53,3]]}}}],["applic",{"_index":119,"t":{"155":{"position":[[0,11]]},"161":{"position":[[0,11]]},"424":{"position":[[59,13]]},"1250":{"position":[[59,13]]},"2499":{"position":[[59,13]]}}}],["ascii",{"_index":511,"t":{"1364":{"position":[[8,5]]},"2535":{"position":[[8,5]]}}}],["aspect",{"_index":61,"t":{"77":{"position":[[12,7]]}}}],["async",{"_index":53,"t":{"67":{"position":[[0,5]]},"229":{"position":[[12,5]]}}}],["asynchron",{"_index":517,"t":{"1416":{"position":[[0,12]]},"2151":{"position":[[0,12]]},"3380":{"position":[[0,12]]}}}],["aud",{"_index":399,"t":{"781":{"position":[[0,3]]},"1000":{"position":[[0,3]]},"1754":{"position":[[0,3]]},"1796":{"position":[[0,3]]},"2959":{"position":[[0,3]]},"3025":{"position":[[0,3]]}}}],["authent",{"_index":50,"t":{"63":{"position":[[0,14]]},"370":{"position":[[9,14]]},"1328":{"position":[[9,14]]},"2443":{"position":[[9,14]]}}}],["author",{"_index":186,"t":{"290":{"position":[[19,13]]},"1454":{"position":[[9,13]]},"1490":{"position":[[13,13]]},"2286":{"position":[[9,13]]},"2322":{"position":[[13,13]]},"3497":{"position":[[9,13]]},"3535":{"position":[[13,13]]}}}],["automat",{"_index":193,"t":{"298":{"position":[[5,9]]},"972":{"position":[[0,9]]},"1056":{"position":[[0,9]]},"1068":{"position":[[0,9]]},"1982":{"position":[[0,9]]},"2131":{"position":[[0,9]]},"2139":{"position":[[0,9]]},"3141":{"position":[[0,9]]},"3334":{"position":[[0,9]]},"3354":{"position":[[0,9]]}}}],["avail",{"_index":383,"t":{"715":{"position":[[4,9]]},"765":{"position":[[4,9]]},"1192":{"position":[[24,12]]},"1950":{"position":[[4,9]]},"2091":{"position":[[24,12]]},"2995":{"position":[[4,9]]},"3276":{"position":[[24,12]]}}}],["b64info",{"_index":402,"t":{"787":{"position":[[0,7]]},"994":{"position":[[0,7]]},"1748":{"position":[[0,7]]},"1802":{"position":[[0,7]]},"2953":{"position":[[0,7]]},"3031":{"position":[[0,7]]}}}],["backend",{"_index":293,"t":{"438":{"position":[[57,7]]},"490":{"position":[[18,7]]},"1264":{"position":[[57,7]]},"1384":{"position":[[18,7]]},"2513":{"position":[[57,7]]},"2555":{"position":[[18,7]]}}}],["background",{"_index":510,"t":{"1354":{"position":[[0,10]]},"2525":{"position":[[0,10]]}}}],["backward",{"_index":22,"t":{"27":{"position":[[0,9]]}}}],["bad",{"_index":382,"t":{"713":{"position":[[0,3]]},"733":{"position":[[0,3]]},"763":{"position":[[0,3]]},"1948":{"position":[[0,3]]},"2993":{"position":[[0,3]]}}}],["batch",{"_index":611,"t":{"2153":{"position":[[10,8]]},"3382":{"position":[[10,8]]},"3525":{"position":[[0,5]]}}}],["befor",{"_index":273,"t":{"418":{"position":[[54,6]]},"1242":{"position":[[54,6]]},"2491":{"position":[[54,6]]}}}],["behavior",{"_index":539,"t":{"1570":{"position":[[16,8]]},"2190":{"position":[[10,8]]},"2665":{"position":[[16,8]]},"3465":{"position":[[10,8]]}}}],["behaviour",{"_index":203,"t":{"314":{"position":[[6,9]]},"2573":{"position":[[17,9]]}}}],["benefit",{"_index":17,"t":{"19":{"position":[[21,8]]}}}],["bentho",{"_index":155,"t":{"221":{"position":[[16,7]]},"223":{"position":[[10,7]]}}}],["best",{"_index":258,"t":{"408":{"position":[[11,4]]},"432":{"position":[[11,4]]},"1232":{"position":[[11,4]]},"1258":{"position":[[11,4]]},"2481":{"position":[[11,4]]},"2507":{"position":[[11,4]]},"2659":{"position":[[11,4]]},"3670":{"position":[[11,4]]}}}],["better",{"_index":39,"t":{"43":{"position":[[0,6]]},"137":{"position":[[0,6]]}}}],["bidirect",{"_index":89,"t":{"111":{"position":[[19,13]]},"378":{"position":[[9,13]]},"1089":{"position":[[0,13]]},"1336":{"position":[[9,13]]},"2184":{"position":[[0,13]]},"2451":{"position":[[9,13]]},"3368":{"position":[[0,13]]},"3461":{"position":[[0,13]]}}}],["binari",{"_index":265,"t":{"412":{"position":[[16,6]]},"508":{"position":[[17,6]]},"538":{"position":[[0,6]]},"1038":{"position":[[0,6]]},"1161":{"position":[[9,6]]},"1236":{"position":[[16,6]]},"1436":{"position":[[17,6]]},"1554":{"position":[[0,6]]},"2341":{"position":[[9,6]]},"2377":{"position":[[0,6]]},"2485":{"position":[[16,6]]},"2605":{"position":[[17,6]]},"2723":{"position":[[0,6]]},"3438":{"position":[[0,6]]},"3626":{"position":[[9,6]]}}}],["block",{"_index":363,"t":{"655":{"position":[[0,5]]},"1650":{"position":[[0,5]]},"2902":{"position":[[0,5]]}}}],["block_us",{"_index":547,"t":{"1651":{"position":[[0,10]]},"2903":{"position":[[0,10]]}}}],["both",{"_index":264,"t":{"412":{"position":[[11,4]]},"1236":{"position":[[11,4]]},"2485":{"position":[[11,4]]}}}],["boundari",{"_index":411,"t":{"815":{"position":[[10,8]]},"819":{"position":[[13,8]]},"1834":{"position":[[10,8]]},"1836":{"position":[[13,8]]},"3063":{"position":[[10,8]]},"3065":{"position":[[13,8]]}}}],["brew",{"_index":326,"t":{"518":{"position":[[5,4]]},"1446":{"position":[[5,4]]},"2615":{"position":[[5,4]]}}}],["broadcast",{"_index":129,"t":{"167":{"position":[[0,12]]},"1458":{"position":[[0,9]]},"2290":{"position":[[0,9]]},"3503":{"position":[[0,9]]}}}],["broker",{"_index":300,"t":{"466":{"position":[[0,6]]},"1206":{"position":[[5,6]]},"2109":{"position":[[5,6]]},"3292":{"position":[[5,6]]}}}],["browser",{"_index":277,"t":{"422":{"position":[[71,7]]},"1078":{"position":[[0,7]]},"1108":{"position":[[0,7]]},"1178":{"position":[[0,7]]},"1248":{"position":[[71,7]]},"2198":{"position":[[0,7]]},"2229":{"position":[[0,7]]},"2242":{"position":[[0,7]]},"2497":{"position":[[71,7]]},"3473":{"position":[[0,7]]},"3579":{"position":[[0,7]]},"3592":{"position":[[0,7]]}}}],["build",{"_index":169,"t":{"243":{"position":[[12,8]]},"520":{"position":[[0,5]]},"1448":{"position":[[0,5]]},"2617":{"position":[[0,5]]}}}],["built",{"_index":235,"t":{"364":{"position":[[0,5]]},"1322":{"position":[[0,5]]},"2437":{"position":[[0,5]]}}}],["builtin",{"_index":557,"t":{"1685":{"position":[[0,7]]},"2849":{"position":[[0,7]]}}}],["call",{"_index":299,"t":{"460":{"position":[[0,4]]},"1312":{"position":[[0,4]]},"1414":{"position":[[9,6]]},"2599":{"position":[[0,4]]}}}],["canari",{"_index":71,"t":{"95":{"position":[[15,6]]}}}],["cancel_push",{"_index":633,"t":{"2884":{"position":[[0,11]]}}}],["cap",{"_index":537,"t":{"1570":{"position":[[0,4]]},"1574":{"position":[[20,4]]},"2665":{"position":[[0,4]]},"2669":{"position":[[20,4]]}}}],["capabl",{"_index":108,"t":{"135":{"position":[[8,12]]},"300":{"position":[[5,12]]},"302":{"position":[[5,12]]},"1568":{"position":[[11,12]]},"1584":{"position":[[13,12]]},"2663":{"position":[[11,12]]},"2679":{"position":[[13,12]]}}}],["case",{"_index":284,"t":{"430":{"position":[[64,6]]},"1256":{"position":[[64,6]]},"2505":{"position":[[64,6]]}}}],["caveat",{"_index":476,"t":{"1074":{"position":[[7,7]]},"2194":{"position":[[7,7]]},"3469":{"position":[[7,7]]}}}],["cento",{"_index":330,"t":{"546":{"position":[[0,6]]},"1562":{"position":[[0,6]]},"2731":{"position":[[0,6]]}}}],["centrifug",{"_index":48,"t":{"59":{"position":[[11,11]]},"61":{"position":[[0,10]]},"334":{"position":[[12,11]]},"1292":{"position":[[0,10]]},"2427":{"position":[[0,10]]}}}],["centrifugo",{"_index":19,"t":{"25":{"position":[[0,10]]},"51":{"position":[[0,10]]},"119":{"position":[[0,10]]},"157":{"position":[[27,11]]},"169":{"position":[[17,10]]},"183":{"position":[[26,10]]},"195":{"position":[[9,10]]},"213":{"position":[[0,10]]},"219":{"position":[[6,10]]},"247":{"position":[[9,10]]},"257":{"position":[[0,10]]},"396":{"position":[[29,10]]},"400":{"position":[[4,10]]},"416":{"position":[[0,10]]},"418":{"position":[[10,10]]},"420":{"position":[[5,10]]},"422":{"position":[[45,10]]},"434":{"position":[[5,10]]},"436":{"position":[[4,10]]},"488":{"position":[[13,10]]},"492":{"position":[[14,10]]},"500":{"position":[[11,10]]},"502":{"position":[[9,10]]},"978":{"position":[[20,10]]},"1220":{"position":[[29,10]]},"1224":{"position":[[4,10]]},"1240":{"position":[[0,10]]},"1242":{"position":[[10,10]]},"1244":{"position":[[5,10]]},"1246":{"position":[[5,10]]},"1248":{"position":[[45,10]]},"1260":{"position":[[5,10]]},"1262":{"position":[[4,10]]},"1270":{"position":[[5,10]]},"1382":{"position":[[13,10]]},"1386":{"position":[[14,10]]},"1394":{"position":[[11,10]]},"1396":{"position":[[9,10]]},"2071":{"position":[[20,10]]},"2415":{"position":[[0,10]]},"2417":{"position":[[0,10]]},"2419":{"position":[[0,10]]},"2421":{"position":[[0,10]]},"2423":{"position":[[0,10]]},"2469":{"position":[[29,10]]},"2473":{"position":[[4,10]]},"2489":{"position":[[0,10]]},"2491":{"position":[[10,10]]},"2493":{"position":[[5,10]]},"2495":{"position":[[5,10]]},"2497":{"position":[[45,10]]},"2509":{"position":[[5,10]]},"2511":{"position":[[4,10]]},"2519":{"position":[[5,10]]},"2553":{"position":[[13,10]]},"2557":{"position":[[14,10]]},"2565":{"position":[[11,10]]},"2567":{"position":[[9,10]]},"2581":{"position":[[14,10]]},"3308":{"position":[[20,10]]},"3539":{"position":[[0,10]]},"3541":{"position":[[0,10]]}}}],["certif",{"_index":76,"t":{"97":{"position":[[25,12]]},"1056":{"position":[[10,12]]},"2139":{"position":[[10,12]]},"3354":{"position":[[10,12]]}}}],["chang",{"_index":25,"t":{"29":{"position":[[8,6]]},"586":{"position":[[12,7]]},"594":{"position":[[12,7]]},"597":{"position":[[16,7]]},"605":{"position":[[20,7]]},"607":{"position":[[11,7]]},"609":{"position":[[4,7]]},"611":{"position":[[20,7]]},"617":{"position":[[20,7]]},"1372":{"position":[[22,7]]},"1374":{"position":[[27,7]]},"1376":{"position":[[11,7]]},"2543":{"position":[[22,7]]},"2545":{"position":[[27,7]]},"2547":{"position":[[11,7]]},"2575":{"position":[[26,7]]},"2579":{"position":[[6,7]]}}}],["channel",{"_index":51,"t":{"65":{"position":[[0,7]]},"73":{"position":[[20,7]]},"129":{"position":[[18,7]]},"131":{"position":[[8,7]]},"135":{"position":[[0,7]]},"288":{"position":[[18,7]]},"290":{"position":[[5,7]]},"294":{"position":[[17,7]]},"296":{"position":[[17,7]]},"298":{"position":[[24,7]]},"374":{"position":[[0,7]]},"382":{"position":[[19,8]]},"406":{"position":[[16,8]]},"408":{"position":[[45,9]]},"410":{"position":[[69,8]]},"412":{"position":[[47,8]]},"430":{"position":[[23,7]]},"432":{"position":[[32,7]]},"454":{"position":[[15,7]]},"460":{"position":[[5,7]]},"494":{"position":[[16,8]]},"496":{"position":[[14,7]]},"597":{"position":[[0,7]]},"605":{"position":[[0,8]]},"703":{"position":[[8,7]]},"759":{"position":[[8,7]]},"789":{"position":[[0,8]]},"813":{"position":[[0,7]]},"817":{"position":[[8,7]]},"819":{"position":[[5,7]]},"821":{"position":[[0,7]]},"855":{"position":[[0,7]]},"857":{"position":[[0,7]]},"990":{"position":[[0,7]]},"1068":{"position":[[19,7]]},"1230":{"position":[[16,8]]},"1232":{"position":[[45,9]]},"1234":{"position":[[69,8]]},"1236":{"position":[[47,8]]},"1256":{"position":[[23,7]]},"1258":{"position":[[32,7]]},"1268":{"position":[[28,7]]},"1306":{"position":[[15,7]]},"1312":{"position":[[5,7]]},"1332":{"position":[[0,7]]},"1340":{"position":[[19,8]]},"1364":{"position":[[0,7]]},"1368":{"position":[[13,7]]},"1388":{"position":[[16,8]]},"1390":{"position":[[14,7]]},"1476":{"position":[[0,8]]},"1582":{"position":[[28,8]]},"1744":{"position":[[0,7]]},"1804":{"position":[[0,8]]},"1830":{"position":[[8,7]]},"1832":{"position":[[0,7]]},"1836":{"position":[[5,7]]},"1838":{"position":[[8,7]]},"1840":{"position":[[0,7]]},"1842":{"position":[[0,7]]},"1844":{"position":[[0,7]]},"1898":{"position":[[0,7]]},"1938":{"position":[[8,7]]},"2131":{"position":[[19,7]]},"2308":{"position":[[0,8]]},"2447":{"position":[[0,7]]},"2455":{"position":[[19,8]]},"2479":{"position":[[16,8]]},"2481":{"position":[[45,9]]},"2483":{"position":[[69,8]]},"2485":{"position":[[47,8]]},"2505":{"position":[[23,7]]},"2507":{"position":[[32,7]]},"2517":{"position":[[28,7]]},"2535":{"position":[[0,7]]},"2539":{"position":[[13,7]]},"2559":{"position":[[16,8]]},"2561":{"position":[[16,7]]},"2593":{"position":[[15,7]]},"2599":{"position":[[5,7]]},"2635":{"position":[[10,7]]},"2655":{"position":[[0,7]]},"2677":{"position":[[28,8]]},"2949":{"position":[[0,7]]},"2983":{"position":[[8,7]]},"3033":{"position":[[0,8]]},"3059":{"position":[[10,7]]},"3061":{"position":[[0,7]]},"3065":{"position":[[5,7]]},"3067":{"position":[[8,7]]},"3069":{"position":[[0,7]]},"3071":{"position":[[0,7]]},"3073":{"position":[[0,7]]},"3133":{"position":[[0,7]]},"3334":{"position":[[19,7]]},"3521":{"position":[[0,8]]},"3646":{"position":[[10,7]]},"3666":{"position":[[0,7]]}}}],["channel_max_length",{"_index":440,"t":{"903":{"position":[[0,18]]},"2016":{"position":[[0,18]]},"3205":{"position":[[0,18]]}}}],["channel_regex",{"_index":596,"t":{"1884":{"position":[[0,13]]},"3115":{"position":[[0,13]]}}}],["chart",{"_index":321,"t":{"514":{"position":[[16,5]]},"1442":{"position":[[16,5]]},"2611":{"position":[[16,5]]}}}],["chat",{"_index":137,"t":{"189":{"position":[[13,4]]},"414":{"position":[[20,4]]},"430":{"position":[[59,4]]},"1238":{"position":[[20,4]]},"1256":{"position":[[59,4]]},"2487":{"position":[[20,4]]},"2505":{"position":[[59,4]]}}}],["check",{"_index":352,"t":{"601":{"position":[[24,5]]},"923":{"position":[[7,5]]},"2044":{"position":[[7,5]]},"3235":{"position":[[7,5]]},"3250":{"position":[[31,5]]}}}],["checkconfig",{"_index":427,"t":{"863":{"position":[[0,11]]},"1906":{"position":[[0,11]]},"3163":{"position":[[0,11]]}}}],["checksubtoken",{"_index":599,"t":{"1914":{"position":[[0,13]]},"3171":{"position":[[0,13]]}}}],["checktoken",{"_index":430,"t":{"869":{"position":[[0,10]]},"1912":{"position":[[0,10]]},"3169":{"position":[[0,10]]}}}],["choic",{"_index":551,"t":{"1675":{"position":[[22,7]]},"2839":{"position":[[22,7]]}}}],["chrome",{"_index":70,"t":{"95":{"position":[[8,6]]}}}],["claim",{"_index":394,"t":{"771":{"position":[[0,6]]},"986":{"position":[[0,6]]},"1740":{"position":[[17,6]]},"1786":{"position":[[15,6]]},"2945":{"position":[[17,6]]},"3015":{"position":[[15,6]]}}}],["cli",{"_index":578,"t":{"1766":{"position":[[17,3]]},"2971":{"position":[[12,3]]}}}],["client",{"_index":41,"t":{"45":{"position":[[0,6]]},"99":{"position":[[4,6]]},"109":{"position":[[8,6]]},"121":{"position":[[8,6]]},"125":{"position":[[15,6]]},"139":{"position":[[11,6]]},"175":{"position":[[0,6]]},"312":{"position":[[13,6]]},"366":{"position":[[7,6]]},"412":{"position":[[32,7]]},"426":{"position":[[43,7]]},"428":{"position":[[62,7]]},"586":{"position":[[0,6]]},"685":{"position":[[0,6]]},"697":{"position":[[0,6]]},"725":{"position":[[0,6]]},"934":{"position":[[9,6]]},"988":{"position":[[0,6]]},"1236":{"position":[[32,7]]},"1252":{"position":[[43,7]]},"1254":{"position":[[62,7]]},"1324":{"position":[[7,6]]},"1358":{"position":[[0,6]]},"1402":{"position":[[0,6]]},"1416":{"position":[[23,6]]},"1424":{"position":[[0,6]]},"1728":{"position":[[0,6]]},"1932":{"position":[[0,6]]},"1960":{"position":[[0,6]]},"2055":{"position":[[9,6]]},"2119":{"position":[[27,6]]},"2165":{"position":[[8,6]]},"2173":{"position":[[0,6]]},"2439":{"position":[[7,6]]},"2485":{"position":[[32,7]]},"2501":{"position":[[43,7]]},"2503":{"position":[[62,7]]},"2529":{"position":[[0,6]]},"2573":{"position":[[0,6]]},"2621":{"position":[[0,6]]},"2623":{"position":[[0,6]]},"2625":{"position":[[0,6]]},"2627":{"position":[[0,6]]},"2917":{"position":[[0,6]]},"2977":{"position":[[0,6]]},"3005":{"position":[[0,6]]},"3248":{"position":[[9,6]]},"3250":{"position":[[8,6]]},"3342":{"position":[[27,6]]},"3394":{"position":[[8,6]]},"3402":{"position":[[0,6]]},"3632":{"position":[[0,6]]},"3634":{"position":[[0,6]]},"3636":{"position":[[0,6]]},"3638":{"position":[[0,6]]}}}],["client_anonym",{"_index":443,"t":{"909":{"position":[[0,16]]}}}],["client_channel_limit",{"_index":439,"t":{"901":{"position":[[0,20]]},"2014":{"position":[[0,20]]},"3203":{"position":[[0,20]]}}}],["client_concurr",{"_index":444,"t":{"911":{"position":[[0,18]]},"2026":{"position":[[0,18]]},"3215":{"position":[[0,18]]}}}],["client_connection_limit",{"_index":602,"t":{"2020":{"position":[[0,23]]},"3209":{"position":[[0,23]]}}}],["client_connection_rate_limit",{"_index":603,"t":{"2022":{"position":[[0,28]]},"3211":{"position":[[0,28]]}}}],["client_max_messages_in_fram",{"_index":534,"t":{"1522":{"position":[[0,28]]},"2705":{"position":[[0,28]]}}}],["client_queue_max_s",{"_index":442,"t":{"907":{"position":[[0,21]]},"2024":{"position":[[0,21]]},"3213":{"position":[[0,21]]}}}],["client_reply_without_queu",{"_index":533,"t":{"1520":{"position":[[0,26]]},"2703":{"position":[[0,26]]}}}],["client_stale_close_delay",{"_index":604,"t":{"2028":{"position":[[0,24]]},"3217":{"position":[[0,24]]}}}],["client_user_connection_limit",{"_index":441,"t":{"905":{"position":[[0,28]]},"2018":{"position":[[0,28]]},"3207":{"position":[[0,28]]}}}],["client_user_id_http_head",{"_index":636,"t":{"3219":{"position":[[0,26]]}}}],["client_write_delay",{"_index":532,"t":{"1518":{"position":[[0,18]]},"2701":{"position":[[0,18]]}}}],["cluster",{"_index":40,"t":{"43":{"position":[[7,10]]},"1198":{"position":[[6,7]]},"2099":{"position":[[6,7]]},"3284":{"position":[[6,7]]}}}],["code",{"_index":148,"t":{"203":{"position":[[16,4]]},"697":{"position":[[13,5]]},"725":{"position":[[18,5]]},"755":{"position":[[17,5]]},"1372":{"position":[[17,4]]},"1430":{"position":[[11,4]]},"1932":{"position":[[13,5]]},"1960":{"position":[[18,5]]},"1964":{"position":[[24,5]]},"1966":{"position":[[20,5]]},"2543":{"position":[[17,4]]},"2647":{"position":[[6,5]]},"2649":{"position":[[12,5]]},"2651":{"position":[[11,5]]},"2977":{"position":[[13,5]]},"3005":{"position":[[18,5]]},"3009":{"position":[[24,5]]},"3011":{"position":[[20,5]]},"3539":{"position":[[17,4],[30,4]]},"3541":{"position":[[17,4],[30,4]]},"3658":{"position":[[6,5]]},"3660":{"position":[[12,5]]},"3662":{"position":[[11,5]]}}}],["command",{"_index":346,"t":{"599":{"position":[[5,7]]},"861":{"position":[[8,7]]},"863":{"position":[[12,7]]},"865":{"position":[[10,7]]},"867":{"position":[[9,7]]},"869":{"position":[[11,7]]},"875":{"position":[[0,7]]},"1097":{"position":[[8,7]]},"1133":{"position":[[8,7]]},"1165":{"position":[[8,7]]},"1480":{"position":[[0,7]]},"1766":{"position":[[21,7]]},"1988":{"position":[[0,7]]},"2149":{"position":[[0,7]]},"2216":{"position":[[8,7]]},"2233":{"position":[[8,7]]},"2246":{"position":[[8,7]]},"2312":{"position":[[0,7]]},"2971":{"position":[[16,7]]},"3177":{"position":[[0,7]]},"3378":{"position":[[0,7]]},"3566":{"position":[[8,7]]},"3583":{"position":[[8,7]]},"3596":{"position":[[8,7]]}}}],["common",{"_index":628,"t":{"2623":{"position":[[7,6]]},"2639":{"position":[[13,6]]},"2659":{"position":[[4,6]]},"3634":{"position":[[7,6]]},"3650":{"position":[[13,6]]},"3670":{"position":[[4,6]]}}}],["commun",{"_index":87,"t":{"111":{"position":[[0,13]]},"149":{"position":[[5,9]]},"318":{"position":[[5,13]]},"528":{"position":[[5,9]]},"2575":{"position":[[5,13]]}}}],["compat",{"_index":23,"t":{"27":{"position":[[10,13]]},"2103":{"position":[[12,10]]},"3286":{"position":[[12,10]]}}}],["compos",{"_index":150,"t":{"203":{"position":[[33,7]]},"512":{"position":[[7,7]]},"1440":{"position":[[7,7]]},"2609":{"position":[[7,7]]}}}],["concept",{"_index":105,"t":{"131":{"position":[[16,7]]},"374":{"position":[[15,7]]},"526":{"position":[[0,8]]},"1332":{"position":[[15,7]]},"2447":{"position":[[15,7]]}}}],["concern",{"_index":165,"t":{"233":{"position":[[9,8]]},"3362":{"position":[[12,8]]}}}],["conclus",{"_index":18,"t":{"21":{"position":[[0,10]]},"53":{"position":[[0,10]]},"89":{"position":[[0,10]]},"115":{"position":[[0,10]]},"147":{"position":[[0,10]]},"179":{"position":[[0,10]]},"205":{"position":[[0,10]]},"239":{"position":[[0,10]]},"253":{"position":[[0,10]]},"277":{"position":[[0,10]]},"336":{"position":[[0,10]]},"482":{"position":[[0,10]]},"2125":{"position":[[0,10]]},"3348":{"position":[[0,10]]}}}],["config",{"_index":215,"t":{"326":{"position":[[9,6]]},"328":{"position":[[8,6]]},"619":{"position":[[9,6]]},"855":{"position":[[16,6]]},"881":{"position":[[0,6]]},"883":{"position":[[5,6]]},"885":{"position":[[5,6]]},"887":{"position":[[5,6]]},"1898":{"position":[[8,6]]},"1994":{"position":[[0,6]]},"1996":{"position":[[5,6]]},"1998":{"position":[[5,6]]},"2000":{"position":[[5,6]]},"2973":{"position":[[28,6]]},"3133":{"position":[[8,6]]},"3183":{"position":[[0,6]]},"3185":{"position":[[5,6]]},"3187":{"position":[[5,6]]},"3189":{"position":[[5,6]]}}}],["configur",{"_index":156,"t":{"223":{"position":[[0,9]]},"432":{"position":[[40,14]]},"488":{"position":[[3,9]]},"490":{"position":[[3,9]]},"534":{"position":[[0,13]]},"611":{"position":[[6,13]]},"617":{"position":[[6,13]]},"629":{"position":[[0,9]]},"649":{"position":[[0,9]]},"671":{"position":[[0,13]]},"693":{"position":[[0,13]]},"873":{"position":[[0,13]]},"879":{"position":[[0,13]]},"915":{"position":[[9,14]]},"976":{"position":[[6,13]]},"1194":{"position":[[28,13]]},"1258":{"position":[[40,14]]},"1370":{"position":[[10,13]]},"1374":{"position":[[6,13]]},"1382":{"position":[[3,9]]},"1384":{"position":[[3,9]]},"1508":{"position":[[0,13]]},"1602":{"position":[[0,13]]},"1628":{"position":[[0,9]]},"1644":{"position":[[0,9]]},"1689":{"position":[[0,13]]},"1736":{"position":[[0,13]]},"1986":{"position":[[0,13]]},"1992":{"position":[[0,13]]},"2036":{"position":[[9,14]]},"2069":{"position":[[6,13]]},"2087":{"position":[[0,11]]},"2095":{"position":[[28,13]]},"2507":{"position":[[40,14]]},"2541":{"position":[[10,13]]},"2545":{"position":[[6,13]]},"2553":{"position":[[3,9]]},"2555":{"position":[[3,9]]},"2713":{"position":[[0,13]]},"2767":{"position":[[0,13]]},"2787":{"position":[[0,13]]},"2815":{"position":[[0,9]]},"2853":{"position":[[0,13]]},"2896":{"position":[[0,9]]},"2925":{"position":[[0,13]]},"3175":{"position":[[0,13]]},"3181":{"position":[[0,13]]},"3227":{"position":[[9,13]]},"3272":{"position":[[0,11]]},"3280":{"position":[[28,13]]},"3306":{"position":[[6,13]]}}}],["connect",{"_index":82,"t":{"105":{"position":[[12,11]]},"107":{"position":[[0,10]]},"137":{"position":[[7,11]]},"171":{"position":[[0,7]]},"294":{"position":[[28,10]]},"296":{"position":[[28,7]]},"300":{"position":[[21,10]]},"302":{"position":[[21,7]]},"372":{"position":[[0,10]]},"378":{"position":[[23,10]]},"396":{"position":[[9,11]]},"398":{"position":[[17,11]]},"416":{"position":[[31,12]]},"422":{"position":[[31,10]]},"428":{"position":[[44,10]]},"446":{"position":[[0,10]]},"452":{"position":[[0,10]]},"492":{"position":[[3,7]]},"673":{"position":[[0,11]]},"753":{"position":[[0,10]]},"797":{"position":[[0,10]]},"805":{"position":[[22,10]]},"934":{"position":[[16,10]]},"954":{"position":[[10,11]]},"1016":{"position":[[0,7]]},"1046":{"position":[[9,7]]},"1070":{"position":[[21,10]]},"1097":{"position":[[0,7]]},"1133":{"position":[[0,7]]},"1165":{"position":[[0,7]]},"1176":{"position":[[0,10]]},"1220":{"position":[[9,11]]},"1222":{"position":[[17,11]]},"1240":{"position":[[31,12]]},"1248":{"position":[[31,10]]},"1254":{"position":[[44,10]]},"1298":{"position":[[0,10]]},"1304":{"position":[[0,10]]},"1330":{"position":[[0,10]]},"1336":{"position":[[23,10]]},"1386":{"position":[[3,7]]},"1406":{"position":[[0,7]]},"1568":{"position":[[0,10]]},"1574":{"position":[[9,10]]},"1598":{"position":[[0,11]]},"1604":{"position":[[0,11]]},"1665":{"position":[[14,10]]},"1671":{"position":[[37,11]]},"1786":{"position":[[0,10]]},"1812":{"position":[[0,10]]},"1820":{"position":[[22,10]]},"1924":{"position":[[10,11]]},"2055":{"position":[[16,10]]},"2133":{"position":[[21,10]]},"2171":{"position":[[0,10]]},"2216":{"position":[[0,7]]},"2227":{"position":[[0,10]]},"2233":{"position":[[0,7]]},"2246":{"position":[[0,7]]},"2353":{"position":[[0,7]]},"2385":{"position":[[9,7]]},"2445":{"position":[[0,10]]},"2451":{"position":[[23,10]]},"2469":{"position":[[9,11]]},"2471":{"position":[[17,11]]},"2489":{"position":[[31,12]]},"2497":{"position":[[31,10]]},"2503":{"position":[[44,10]]},"2557":{"position":[[3,7]]},"2585":{"position":[[0,10]]},"2591":{"position":[[0,10]]},"2621":{"position":[[7,10]]},"2627":{"position":[[7,10]]},"2629":{"position":[[0,10]]},"2663":{"position":[[0,10]]},"2669":{"position":[[9,10]]},"2781":{"position":[[0,11]]},"2789":{"position":[[0,11]]},"2829":{"position":[[14,10]]},"2835":{"position":[[37,11]]},"3015":{"position":[[0,10]]},"3041":{"position":[[0,10]]},"3049":{"position":[[22,10]]},"3151":{"position":[[10,11]]},"3248":{"position":[[16,10]]},"3336":{"position":[[21,10]]},"3400":{"position":[[0,10]]},"3414":{"position":[[0,7]]},"3446":{"position":[[9,7]]},"3566":{"position":[[0,7]]},"3577":{"position":[[0,10]]},"3583":{"position":[[0,7]]},"3596":{"position":[[0,7]]},"3632":{"position":[[7,10]]},"3638":{"position":[[7,10]]},"3640":{"position":[[0,10]]}}}],["conntrack",{"_index":455,"t":{"956":{"position":[[0,9]]},"1926":{"position":[[0,9]]},"3153":{"position":[[0,9]]}}}],["consider",{"_index":229,"t":{"346":{"position":[[16,14]]},"354":{"position":[[16,14]]},"356":{"position":[[12,14]]},"1278":{"position":[[16,14]]},"1286":{"position":[[16,14]]},"1288":{"position":[[12,14]]},"2401":{"position":[[16,14]]},"2409":{"position":[[16,14]]},"2411":{"position":[[12,14]]}}}],["considir",{"_index":540,"t":{"1572":{"position":[[11,14]]},"1586":{"position":[[11,14]]},"2667":{"position":[[11,14]]},"2681":{"position":[[11,14]]}}}],["contact",{"_index":268,"t":{"414":{"position":[[54,8]]},"1238":{"position":[[54,8]]},"2487":{"position":[[54,8]]}}}],["control",{"_index":131,"t":{"171":{"position":[[14,10]]},"173":{"position":[[5,10]]}}}],["convert",{"_index":356,"t":{"619":{"position":[[16,9]]}}}],["cpu",{"_index":310,"t":{"478":{"position":[[31,3]]}}}],["creat",{"_index":136,"t":{"187":{"position":[[0,8]]},"189":{"position":[[0,8]]},"245":{"position":[[0,8]]},"406":{"position":[[9,6]]},"430":{"position":[[7,6]]},"1230":{"position":[[9,6]]},"1256":{"position":[[7,6]]},"2479":{"position":[[9,6]]},"2505":{"position":[[7,6]]}}}],["cross",{"_index":246,"t":{"386":{"position":[[0,5]]},"1344":{"position":[[0,5]]},"2459":{"position":[[0,5]]}}}],["crt",{"_index":473,"t":{"1054":{"position":[[6,3]]},"2137":{"position":[[6,3]]},"3352":{"position":[[6,3]]}}}],["curl",{"_index":499,"t":{"1176":{"position":[[17,4]]},"2227":{"position":[[17,4]]},"3577":{"position":[[17,4]]}}}],["custom",{"_index":365,"t":{"665":{"position":[[6,6]]},"925":{"position":[[0,6]]},"929":{"position":[[0,9]]},"1026":{"position":[[7,6]]},"1028":{"position":[[7,6]]},"1659":{"position":[[6,6]]},"2046":{"position":[[0,6]]},"2050":{"position":[[0,9]]},"2365":{"position":[[7,6]]},"2367":{"position":[[7,6]]},"2911":{"position":[[6,6]]},"3239":{"position":[[0,6]]},"3243":{"position":[[0,9]]},"3426":{"position":[[7,6]]},"3428":{"position":[[7,6]]}}}],["dashboard",{"_index":459,"t":{"964":{"position":[[8,9]]},"1974":{"position":[[8,9]]},"3302":{"position":[[8,9]]},"3321":{"position":[[8,9]]}}}],["data",{"_index":479,"t":{"1099":{"position":[[10,4]]},"1112":{"position":[[10,4]]},"1137":{"position":[[10,4]]},"1167":{"position":[[10,4]]},"1268":{"position":[[20,4]]},"2218":{"position":[[10,4]]},"2235":{"position":[[10,4]]},"2250":{"position":[[10,4]]},"2263":{"position":[[10,4]]},"2517":{"position":[[20,4]]},"3545":{"position":[[10,4]]},"3568":{"position":[[10,4]]},"3585":{"position":[[10,4]]},"3600":{"position":[[10,4]]}}}],["databas",{"_index":127,"t":{"165":{"position":[[0,8]]},"633":{"position":[[0,8]]},"653":{"position":[[0,8]]},"1632":{"position":[[0,8]]},"1648":{"position":[[0,8]]},"2819":{"position":[[0,8]]},"2900":{"position":[[0,8]]}}}],["deb",{"_index":323,"t":{"516":{"position":[[8,3]]},"1444":{"position":[[8,3]]},"2613":{"position":[[8,3]]}}}],["debian",{"_index":328,"t":{"544":{"position":[[0,6]]},"1560":{"position":[[0,6]]},"2729":{"position":[[0,6]]}}}],["debug",{"_index":220,"t":{"330":{"position":[[21,5]]},"921":{"position":[[0,5]]},"2042":{"position":[[0,5]]},"3233":{"position":[[0,5]]},"3628":{"position":[[0,9]]}}}],["decis",{"_index":176,"t":{"263":{"position":[[9,9]]}}}],["decod",{"_index":336,"t":{"574":{"position":[[11,8]]},"1542":{"position":[[11,8]]},"2747":{"position":[[11,8]]}}}],["default",{"_index":33,"t":{"35":{"position":[[17,7]]},"129":{"position":[[10,7]]},"588":{"position":[[24,7]]},"613":{"position":[[22,7]]},"615":{"position":[[19,7]]},"917":{"position":[[0,7]]},"927":{"position":[[8,7]]},"2038":{"position":[[0,7]]},"2048":{"position":[[8,7]]},"3229":{"position":[[0,7]]},"3241":{"position":[[8,7]]}}}],["defin",{"_index":471,"t":{"1044":{"position":[[0,8]]},"2383":{"position":[[0,8]]},"3444":{"position":[[0,8]]}}}],["degrad",{"_index":232,"t":{"352":{"position":[[9,11]]},"1284":{"position":[[9,11]]},"2407":{"position":[[9,11]]}}}],["delete_user_statu",{"_index":375,"t":{"691":{"position":[[0,18]]},"1734":{"position":[[0,18]]},"2923":{"position":[[0,18]]}}}],["deliv",{"_index":282,"t":{"426":{"position":[[28,9]]},"1252":{"position":[[28,9]]},"2501":{"position":[[28,9]]}}}],["deliveri",{"_index":63,"t":{"79":{"position":[[10,8]]},"231":{"position":[[5,8]]},"348":{"position":[[8,8]]},"402":{"position":[[8,8]]},"1226":{"position":[[8,8]]},"1280":{"position":[[8,8]]},"2403":{"position":[[8,8]]},"2475":{"position":[[8,8]]}}}],["demo",{"_index":161,"t":{"227":{"position":[[0,4]]}}}],["deni",{"_index":379,"t":{"705":{"position":[[11,6]]},"1940":{"position":[[11,6]]},"2985":{"position":[[11,6]]}}}],["deploy",{"_index":249,"t":{"388":{"position":[[9,6]]},"498":{"position":[[3,6]]},"1346":{"position":[[9,6]]},"1392":{"position":[[3,6]]},"2461":{"position":[[9,6]]},"2563":{"position":[[3,6]]}}}],["deprec",{"_index":624,"t":{"2577":{"position":[[23,10]]}}}],["descript",{"_index":563,"t":{"1701":{"position":[[4,11]]},"2715":{"position":[[4,11]]},"2865":{"position":[[4,11]]}}}],["design",{"_index":460,"t":{"968":{"position":[[8,6]]},"1675":{"position":[[15,6]]},"1978":{"position":[[8,6]]},"2839":{"position":[[15,6]]},"3137":{"position":[[8,6]]},"3364":{"position":[[15,6]]}}}],["detail",{"_index":530,"t":{"1510":{"position":[[15,7]]},"2769":{"position":[[15,7]]}}}],["develop",{"_index":371,"t":{"679":{"position":[[0,11]]},"1616":{"position":[[0,11]]},"2801":{"position":[[0,11]]}}}],["device_list",{"_index":567,"t":{"1708":{"position":[[0,11]]},"2872":{"position":[[0,11]]}}}],["device_regist",{"_index":564,"t":{"1702":{"position":[[0,15]]},"2866":{"position":[[0,15]]}}}],["device_remov",{"_index":566,"t":{"1706":{"position":[[0,13]]},"2870":{"position":[[0,13]]}}}],["device_topic_list",{"_index":569,"t":{"1712":{"position":[[0,17]]},"2876":{"position":[[0,17]]}}}],["device_topic_upd",{"_index":568,"t":{"1710":{"position":[[0,19]]},"2874":{"position":[[0,19]]}}}],["device_upd",{"_index":565,"t":{"1704":{"position":[[0,13]]},"2868":{"position":[[0,13]]}}}],["differ",{"_index":240,"t":{"376":{"position":[[0,9]]},"422":{"position":[[61,9]]},"1248":{"position":[[61,9]]},"1334":{"position":[[0,9]]},"1580":{"position":[[9,9]]},"2449":{"position":[[0,9]]},"2497":{"position":[[61,9]]},"2675":{"position":[[9,9]]}}}],["disabl",{"_index":355,"t":{"615":{"position":[[7,8]]},"927":{"position":[[0,7]]},"2048":{"position":[[0,7]]},"3241":{"position":[[0,7]]},"3250":{"position":[[0,7]]}}}],["disallow_anonymous_connection_token",{"_index":606,"t":{"2032":{"position":[[0,36]]},"3223":{"position":[[0,36]]}}}],["disconnect",{"_index":289,"t":{"436":{"position":[[29,10]]},"448":{"position":[[0,13]]},"725":{"position":[[7,10]]},"1028":{"position":[[14,10]]},"1262":{"position":[[29,10]]},"1300":{"position":[[0,13]]},"1372":{"position":[[6,10]]},"1420":{"position":[[7,11]]},"1430":{"position":[[0,10]]},"1464":{"position":[[0,10]]},"1671":{"position":[[0,13]]},"1960":{"position":[[7,10]]},"1964":{"position":[[13,10]]},"1966":{"position":[[9,10]]},"2157":{"position":[[7,11]]},"2296":{"position":[[0,10]]},"2367":{"position":[[14,10]]},"2511":{"position":[[29,10]]},"2543":{"position":[[6,10]]},"2587":{"position":[[0,13]]},"2651":{"position":[[0,10]]},"2835":{"position":[[0,13]]},"3005":{"position":[[7,10]]},"3009":{"position":[[13,10]]},"3011":{"position":[[9,10]]},"3386":{"position":[[7,11]]},"3428":{"position":[[14,10]]},"3509":{"position":[[0,10]]},"3662":{"position":[[0,10]]}}}],["disconnectconnectionclos",{"_index":600,"t":{"1962":{"position":[[0,26]]},"3007":{"position":[[0,26]]}}}],["django",{"_index":134,"t":{"183":{"position":[[14,6]]}}}],["doc",{"_index":627,"t":{"2581":{"position":[[28,3]]}}}],["docker",{"_index":149,"t":{"203":{"position":[[26,6]]},"510":{"position":[[0,6]]},"512":{"position":[[0,6]]},"540":{"position":[[0,6]]},"1438":{"position":[[0,6]]},"1440":{"position":[[0,6]]},"1556":{"position":[[0,6]]},"2607":{"position":[[0,6]]},"2609":{"position":[[0,6]]},"2725":{"position":[[0,6]]}}}],["document",{"_index":43,"t":{"47":{"position":[[4,13]]}}}],["domain",{"_index":461,"t":{"978":{"position":[[9,6]]},"2071":{"position":[[9,6]]},"3308":{"position":[[9,6]]}}}],["down",{"_index":626,"t":{"2581":{"position":[[9,4]]}}}],["drop",{"_index":201,"t":{"312":{"position":[[0,8]]}}}],["durat",{"_index":345,"t":{"595":{"position":[[26,8]]},"940":{"position":[[13,8]]},"2061":{"position":[[13,8]]},"3256":{"position":[[13,8]]}}}],["dynam",{"_index":474,"t":{"1066":{"position":[[0,7]]},"1826":{"position":[[0,7]]},"2129":{"position":[[0,7]]},"3055":{"position":[[0,7]]},"3332":{"position":[[0,7]]}}}],["ecosystem",{"_index":65,"t":{"81":{"position":[[0,9]]}}}],["effici",{"_index":553,"t":{"1679":{"position":[[0,9]]},"2843":{"position":[[0,9]]}}}],["emb",{"_index":462,"t":{"980":{"position":[[0,5]]},"2073":{"position":[[0,5]]},"3310":{"position":[[0,5]]}}}],["embed",{"_index":243,"t":{"384":{"position":[[0,8]]},"1342":{"position":[[0,8]]},"2457":{"position":[[0,8]]}}}],["emul",{"_index":95,"t":{"123":{"position":[[17,9]]}}}],["enabl",{"_index":470,"t":{"1042":{"position":[[0,6]]},"2117":{"position":[[0,8]]},"2381":{"position":[[0,6]]},"3340":{"position":[[0,8]]},"3442":{"position":[[0,6]]}}}],["endpoint",{"_index":446,"t":{"915":{"position":[[0,8]]},"917":{"position":[[8,10]]},"919":{"position":[[6,10]]},"921":{"position":[[6,10]]},"923":{"position":[[13,8]]},"927":{"position":[[16,9]]},"929":{"position":[[18,9]]},"1826":{"position":[[13,8]]},"2036":{"position":[[0,8]]},"2038":{"position":[[8,10]]},"2040":{"position":[[6,10]]},"2042":{"position":[[6,10]]},"2044":{"position":[[13,8]]},"2048":{"position":[[16,9]]},"2050":{"position":[[18,9]]},"3055":{"position":[[13,8]]},"3227":{"position":[[0,8]]},"3229":{"position":[[8,9]]},"3231":{"position":[[6,9]]},"3233":{"position":[[6,9]]},"3235":{"position":[[13,8]]},"3241":{"position":[[16,9]]},"3243":{"position":[[18,9]]}}}],["enforc",{"_index":349,"t":{"601":{"position":[[0,8]]},"1364":{"position":[[14,8]]},"2535":{"position":[[14,8]]}}}],["engin",{"_index":35,"t":{"37":{"position":[[10,6]]},"631":{"position":[[18,6]]},"633":{"position":[[21,6]]},"651":{"position":[[18,6]]},"653":{"position":[[21,6]]},"897":{"position":[[0,6]]},"1182":{"position":[[7,6]]},"1184":{"position":[[7,6]]},"1186":{"position":[[6,6]]},"1188":{"position":[[6,6]]},"1200":{"position":[[6,6]]},"1202":{"position":[[10,6]]},"1204":{"position":[[10,6]]},"1630":{"position":[[18,6]]},"1632":{"position":[[21,6]]},"1646":{"position":[[18,6]]},"1648":{"position":[[21,6]]},"2010":{"position":[[0,6]]},"2079":{"position":[[7,6]]},"2081":{"position":[[7,6]]},"2083":{"position":[[6,6]]},"2085":{"position":[[6,6]]},"2101":{"position":[[6,6]]},"2105":{"position":[[10,6]]},"2107":{"position":[[10,6]]},"2817":{"position":[[18,6]]},"2819":{"position":[[21,6]]},"2898":{"position":[[18,6]]},"2900":{"position":[[21,6]]},"3199":{"position":[[0,6]]},"3264":{"position":[[7,6]]},"3266":{"position":[[7,6]]},"3268":{"position":[[6,6]]},"3270":{"position":[[6,6]]},"3288":{"position":[[10,6]]},"3290":{"position":[[10,6]]}}}],["env",{"_index":449,"t":{"942":{"position":[[24,3]]},"2063":{"position":[[24,3]]},"3258":{"position":[[24,3]]}}}],["environ",{"_index":125,"t":{"163":{"position":[[0,11]]},"877":{"position":[[3,11]]},"1990":{"position":[[3,11]]},"3179":{"position":[[3,11]]}}}],["ephemer",{"_index":450,"t":{"950":{"position":[[0,9]]},"1920":{"position":[[0,9]]},"3147":{"position":[[0,9]]}}}],["error",{"_index":168,"t":{"237":{"position":[[0,5]]},"697":{"position":[[7,5]]},"735":{"position":[[7,5]]},"745":{"position":[[6,5]]},"755":{"position":[[11,5]]},"1026":{"position":[[14,5]]},"1422":{"position":[[7,6]]},"1932":{"position":[[7,5]]},"2159":{"position":[[7,6]]},"2365":{"position":[[14,5]]},"2647":{"position":[[0,5]]},"2977":{"position":[[7,5]]},"3388":{"position":[[7,6]]},"3426":{"position":[[14,5]]},"3537":{"position":[[10,5]]},"3539":{"position":[[11,5]]},"3541":{"position":[[11,5]]},"3658":{"position":[[0,5]]}}}],["etc",{"_index":641,"t":{"3628":{"position":[[31,3]]}}}],["event",{"_index":15,"t":{"19":{"position":[[8,5]]},"438":{"position":[[39,6]]},"452":{"position":[[21,6]]},"1264":{"position":[[39,6]]},"1304":{"position":[[21,6]]},"2121":{"position":[[15,6]]},"2513":{"position":[[39,6]]},"2591":{"position":[[21,6]]},"3344":{"position":[[15,6]]}}}],["exampl",{"_index":67,"t":{"87":{"position":[[0,8]]},"99":{"position":[[11,7]]},"113":{"position":[[12,7]]},"512":{"position":[[15,7]]},"578":{"position":[[0,8]]},"643":{"position":[[5,8]]},"677":{"position":[[6,8]]},"799":{"position":[[0,8]]},"855":{"position":[[23,7]]},"1004":{"position":[[0,7]]},"1034":{"position":[[11,7]]},"1108":{"position":[[8,7]]},"1129":{"position":[[0,7]]},"1146":{"position":[[0,7]]},"1178":{"position":[[8,7]]},"1440":{"position":[[15,7]]},"1486":{"position":[[5,7]]},"1488":{"position":[[5,7]]},"1546":{"position":[[0,8]]},"1576":{"position":[[0,8]]},"1578":{"position":[[0,8]]},"1580":{"position":[[0,8]]},"1582":{"position":[[0,8]]},"1596":{"position":[[0,7]]},"1614":{"position":[[6,8]]},"1764":{"position":[[0,7]]},"1814":{"position":[[0,8]]},"1898":{"position":[[15,8]]},"2229":{"position":[[8,7]]},"2242":{"position":[[8,7]]},"2259":{"position":[[0,7]]},"2280":{"position":[[0,7]]},"2318":{"position":[[5,7]]},"2320":{"position":[[5,7]]},"2373":{"position":[[11,7]]},"2609":{"position":[[15,7]]},"2671":{"position":[[0,8]]},"2673":{"position":[[0,8]]},"2675":{"position":[[0,8]]},"2677":{"position":[[0,8]]},"2751":{"position":[[0,8]]},"2779":{"position":[[0,7]]},"2799":{"position":[[6,8]]},"2969":{"position":[[0,7]]},"3043":{"position":[[0,8]]},"3133":{"position":[[15,8]]},"3372":{"position":[[5,7]]},"3434":{"position":[[11,7]]},"3531":{"position":[[5,7]]},"3533":{"position":[[5,7]]},"3562":{"position":[[0,7]]},"3579":{"position":[[8,7]]},"3592":{"position":[[8,7]]},"3609":{"position":[[0,7]]}}}],["exceed",{"_index":381,"t":{"711":{"position":[[6,8]]},"1946":{"position":[[6,8]]},"2991":{"position":[[6,8]]}}}],["exclud",{"_index":262,"t":{"410":{"position":[[11,7]]},"1234":{"position":[[11,7]]},"2483":{"position":[[11,7]]}}}],["exhaust",{"_index":451,"t":{"950":{"position":[[15,10]]},"1920":{"position":[[15,10]]},"3147":{"position":[[15,10]]}}}],["exp",{"_index":396,"t":{"775":{"position":[[0,3]]},"996":{"position":[[0,3]]},"1750":{"position":[[0,3]]},"1790":{"position":[[0,3]]},"2955":{"position":[[0,3]]},"3019":{"position":[[0,3]]}}}],["experi",{"_index":111,"t":{"141":{"position":[[0,13]]},"143":{"position":[[0,13]]}}}],["expir",{"_index":384,"t":{"717":{"position":[[6,7]]},"719":{"position":[[0,7]]},"737":{"position":[[0,7]]},"739":{"position":[[13,7]]},"797":{"position":[[11,10]]},"803":{"position":[[11,10]]},"1572":{"position":[[0,10]]},"1586":{"position":[[0,10]]},"1812":{"position":[[11,10]]},"1818":{"position":[[11,10]]},"1952":{"position":[[6,7]]},"1954":{"position":[[0,7]]},"2667":{"position":[[0,10]]},"2681":{"position":[[0,10]]},"2997":{"position":[[6,7]]},"2999":{"position":[[0,7]]},"3041":{"position":[[11,10]]},"3047":{"position":[[11,10]]}}}],["expire_at",{"_index":404,"t":{"795":{"position":[[0,9]]},"998":{"position":[[0,9]]},"1752":{"position":[[0,9]]},"1810":{"position":[[0,9]]},"2957":{"position":[[0,9]]},"3039":{"position":[[0,9]]}}}],["explicitli",{"_index":257,"t":{"406":{"position":[[25,11]]},"1230":{"position":[[25,11]]},"2479":{"position":[[25,11]]}}}],["export",{"_index":372,"t":{"681":{"position":[[4,6]]},"1618":{"position":[[4,6]]},"2803":{"position":[[4,6]]}}}],["expos",{"_index":631,"t":{"2805":{"position":[[0,7]]},"2888":{"position":[[0,7]]},"3323":{"position":[[0,7]]}}}],["express",{"_index":526,"t":{"1496":{"position":[[0,10]]},"2691":{"position":[[0,10]]}}}],["express.j",{"_index":170,"t":{"245":{"position":[[9,10]]}}}],["fallback",{"_index":9,"t":{"13":{"position":[[10,8]]}}}],["faq",{"_index":317,"t":{"504":{"position":[[8,3]]},"1398":{"position":[[8,3]]},"2569":{"position":[[8,3]]}}}],["faster",{"_index":335,"t":{"566":{"position":[[0,6]]},"568":{"position":[[0,6]]},"570":{"position":[[0,6]]},"572":{"position":[[0,6]]},"574":{"position":[[0,6]]},"576":{"position":[[0,6]]},"1534":{"position":[[0,6]]},"1536":{"position":[[0,6]]},"1538":{"position":[[0,6]]},"1540":{"position":[[0,6]]},"1542":{"position":[[0,6]]},"1544":{"position":[[0,6]]},"2739":{"position":[[0,6]]},"2741":{"position":[[0,6]]},"2743":{"position":[[0,6]]},"2745":{"position":[[0,6]]},"2747":{"position":[[0,6]]},"2749":{"position":[[0,6]]}}}],["fcm",{"_index":559,"t":{"1691":{"position":[[0,3]]},"2855":{"position":[[0,3]]}}}],["featur",{"_index":251,"t":{"392":{"position":[[4,8]]},"440":{"position":[[51,9]]},"552":{"position":[[0,8]]},"1266":{"position":[[51,9]]},"1350":{"position":[[4,8]]},"1402":{"position":[[22,7]]},"1526":{"position":[[0,8]]},"2169":{"position":[[4,7]]},"2171":{"position":[[19,8]]},"2173":{"position":[[33,8]]},"2465":{"position":[[4,8]]},"2515":{"position":[[51,9]]},"2759":{"position":[[0,8]]},"3398":{"position":[[4,7]]},"3400":{"position":[[19,8]]},"3402":{"position":[[33,8]]}}}],["field",{"_index":341,"t":{"592":{"position":[[8,6]]}}}],["file",{"_index":358,"t":{"623":{"position":[[10,4]]},"879":{"position":[[14,4]]},"881":{"position":[[7,4]]},"948":{"position":[[5,5]]},"1054":{"position":[[18,5]]},"1622":{"position":[[10,4]]},"1918":{"position":[[5,5]]},"1992":{"position":[[14,4]]},"1994":{"position":[[7,4]]},"2137":{"position":[[18,5]]},"2809":{"position":[[10,4]]},"3145":{"position":[[5,5]]},"3181":{"position":[[14,4]]},"3183":{"position":[[7,4]]},"3352":{"position":[[18,5]]}}}],["flag",{"_index":348,"t":{"599":{"position":[[18,5]]},"875":{"position":[[13,5]]},"1988":{"position":[[13,5]]},"3177":{"position":[[13,5]]}}}],["flashback",{"_index":21,"t":{"25":{"position":[[14,10]]},"119":{"position":[[14,10]]}}}],["flexibl",{"_index":238,"t":{"370":{"position":[[0,8]]},"1328":{"position":[[0,8]]},"2443":{"position":[[0,8]]}}}],["forc",{"_index":393,"t":{"749":{"position":[[0,5]]},"751":{"position":[[0,5]]}}}],["force_posit",{"_index":582,"t":{"1856":{"position":[[0,17]]},"3087":{"position":[[0,17]]}}}],["force_push_join_leav",{"_index":581,"t":{"1850":{"position":[[0,21]]},"3079":{"position":[[0,21]]}}}],["force_recoveri",{"_index":583,"t":{"1858":{"position":[[0,14]]},"3089":{"position":[[0,14]]}}}],["format",{"_index":209,"t":{"320":{"position":[[13,6]]},"881":{"position":[[12,7]]},"883":{"position":[[12,6]]},"885":{"position":[[12,6]]},"887":{"position":[[12,6]]},"1099":{"position":[[15,7]]},"1112":{"position":[[15,7]]},"1137":{"position":[[15,7]]},"1167":{"position":[[15,7]]},"1994":{"position":[[12,7]]},"1996":{"position":[[12,6]]},"1998":{"position":[[12,6]]},"2000":{"position":[[12,6]]},"2167":{"position":[[18,7]]},"2218":{"position":[[15,7]]},"2235":{"position":[[15,7]]},"2250":{"position":[[15,7]]},"2263":{"position":[[15,7]]},"2575":{"position":[[19,6]]},"2577":{"position":[[13,6]]},"3183":{"position":[[12,7]]},"3185":{"position":[[12,6]]},"3187":{"position":[[12,6]]},"3189":{"position":[[12,6]]},"3396":{"position":[[18,7]]},"3545":{"position":[[15,7]]},"3568":{"position":[[15,7]]},"3585":{"position":[[15,7]]},"3600":{"position":[[15,7]]}}}],["found",{"_index":294,"t":{"442":{"position":[[11,5]]},"707":{"position":[[11,5]]},"761":{"position":[[11,5]]},"1272":{"position":[[11,5]]},"1942":{"position":[[11,5]]},"2521":{"position":[[11,5]]},"2987":{"position":[[11,5]]}}}],["frame",{"_index":515,"t":{"1404":{"position":[[10,7]]}}}],["framework",{"_index":509,"t":{"1294":{"position":[[0,9]]},"2429":{"position":[[0,9]]}}}],["free",{"_index":536,"t":{"1528":{"position":[[8,4]]},"2763":{"position":[[8,4]]}}}],["frontend",{"_index":175,"t":{"263":{"position":[[0,8]]}}}],["full",{"_index":90,"t":{"113":{"position":[[0,4]]},"1582":{"position":[[9,4]]},"2677":{"position":[[9,4]]},"3372":{"position":[[0,4]]}}}],["further",{"_index":575,"t":{"1724":{"position":[[0,7]]},"2890":{"position":[[0,7]]}}}],["futur",{"_index":222,"t":{"332":{"position":[[4,6]]}}}],["genconfig",{"_index":428,"t":{"865":{"position":[[0,9]]},"1904":{"position":[[0,9]]},"3161":{"position":[[0,9]]}}}],["gener",{"_index":72,"t":{"97":{"position":[[0,8]]}}}],["gensubtoken",{"_index":577,"t":{"1766":{"position":[[5,11]]},"1910":{"position":[[0,11]]},"2971":{"position":[[0,11]]},"3167":{"position":[[0,11]]}}}],["gentoken",{"_index":429,"t":{"867":{"position":[[0,8]]},"1908":{"position":[[0,8]]},"3165":{"position":[[0,8]]}}}],["get_user_statu",{"_index":374,"t":{"689":{"position":[[0,15]]},"1732":{"position":[[0,15]]},"2921":{"position":[[0,15]]}}}],["go",{"_index":305,"t":{"474":{"position":[[0,2]]},"1292":{"position":[[23,2]]},"1488":{"position":[[17,2]]},"2320":{"position":[[17,2]]},"2427":{"position":[[23,2]]},"3533":{"position":[[17,2]]}}}],["goal",{"_index":171,"t":{"257":{"position":[[15,5]]}}}],["gomaxproc",{"_index":445,"t":{"913":{"position":[[0,10]]},"2034":{"position":[[0,10]]},"3225":{"position":[[0,10]]}}}],["grace",{"_index":231,"t":{"352":{"position":[[0,8]]},"1284":{"position":[[0,8]]},"2407":{"position":[[0,8]]}}}],["grafana",{"_index":458,"t":{"964":{"position":[[0,7]]},"1974":{"position":[[0,7]]},"3302":{"position":[[0,7]]},"3321":{"position":[[0,7]]}}}],["granular",{"_index":469,"t":{"1040":{"position":[[0,8]]},"1042":{"position":[[7,8]]},"1046":{"position":[[0,8]]},"1048":{"position":[[0,8]]},"1050":{"position":[[0,8]]},"2379":{"position":[[0,8]]},"2381":{"position":[[7,8]]},"2385":{"position":[[0,8]]},"2387":{"position":[[0,8]]},"2389":{"position":[[0,8]]},"3370":{"position":[[0,8]]},"3440":{"position":[[0,8]]},"3442":{"position":[[7,8]]},"3446":{"position":[[0,8]]},"3448":{"position":[[0,8]]},"3450":{"position":[[0,8]]}}}],["graphit",{"_index":457,"t":{"962":{"position":[[0,8]]},"1972":{"position":[[0,8]]},"3300":{"position":[[0,8]]},"3319":{"position":[[0,8]]}}}],["great",{"_index":234,"t":{"362":{"position":[[0,5]]},"1320":{"position":[[0,5]]},"2435":{"position":[[0,5]]}}}],["grpc",{"_index":36,"t":{"39":{"position":[[0,4]]},"568":{"position":[[7,4]]},"572":{"position":[[7,4]]},"576":{"position":[[7,4]]},"603":{"position":[[8,4]]},"1014":{"position":[[6,4]]},"1030":{"position":[[0,4]]},"1032":{"position":[[0,4]]},"1034":{"position":[[0,4]]},"1058":{"position":[[8,4]]},"1060":{"position":[[8,4]]},"1484":{"position":[[0,4]]},"1486":{"position":[[0,4]]},"1488":{"position":[[0,4]]},"1490":{"position":[[0,4]]},"1536":{"position":[[7,4]]},"1540":{"position":[[7,4]]},"1544":{"position":[[7,4]]},"2141":{"position":[[8,4]]},"2143":{"position":[[8,4]]},"2316":{"position":[[0,4]]},"2318":{"position":[[0,4]]},"2320":{"position":[[0,4]]},"2322":{"position":[[0,4]]},"2351":{"position":[[6,4]]},"2369":{"position":[[0,4]]},"2371":{"position":[[0,4]]},"2373":{"position":[[0,4]]},"2741":{"position":[[7,4]]},"2745":{"position":[[7,4]]},"2749":{"position":[[7,4]]},"3356":{"position":[[8,4]]},"3358":{"position":[[8,4]]},"3412":{"position":[[6,4]]},"3430":{"position":[[0,4]]},"3432":{"position":[[0,4]]},"3434":{"position":[[0,4]]},"3529":{"position":[[0,4]]},"3531":{"position":[[0,4]]},"3533":{"position":[[0,4]]},"3535":{"position":[[0,4]]},"3541":{"position":[[25,4]]}}}],["guarante",{"_index":230,"t":{"350":{"position":[[14,10]]},"404":{"position":[[14,10]]},"1228":{"position":[[14,10]]},"1282":{"position":[[14,10]]},"2405":{"position":[[14,10]]},"2477":{"position":[[14,10]]}}}],["guid",{"_index":115,"t":{"145":{"position":[[10,5]]}}}],["handl",{"_index":84,"t":{"107":{"position":[[19,8]]},"237":{"position":[[6,8]]},"396":{"position":[[49,7]]},"931":{"position":[[7,8]]},"1220":{"position":[[49,7]]},"1420":{"position":[[0,6]]},"1422":{"position":[[0,6]]},"2052":{"position":[[7,8]]},"2157":{"position":[[0,6]]},"2159":{"position":[[0,6]]},"2469":{"position":[[49,7]]},"3245":{"position":[[7,8]]},"3386":{"position":[[0,6]]},"3388":{"position":[[0,6]]}}}],["handler",{"_index":145,"t":{"199":{"position":[[19,8]]},"929":{"position":[[10,7]]},"2050":{"position":[[10,7]]},"3243":{"position":[[10,7]]}}}],["haproxi",{"_index":502,"t":{"1194":{"position":[[0,7]]},"2095":{"position":[[0,7]]},"3280":{"position":[[0,7]]}}}],["header",{"_index":465,"t":{"1012":{"position":[[11,7]]},"1036":{"position":[[0,6]]},"2349":{"position":[[11,7]]},"2375":{"position":[[0,6]]},"3410":{"position":[[11,7]]},"3436":{"position":[[0,6]]}}}],["health",{"_index":447,"t":{"923":{"position":[[0,6]]},"2044":{"position":[[0,6]]},"3235":{"position":[[0,6]]}}}],["helm",{"_index":320,"t":{"514":{"position":[[11,4]]},"1442":{"position":[[11,4]]},"2611":{"position":[[11,4]]}}}],["here",{"_index":297,"t":{"442":{"position":[[42,5]]},"1272":{"position":[[42,5]]},"2521":{"position":[[42,4]]}}}],["high",{"_index":501,"t":{"1192":{"position":[[19,4]]},"2091":{"position":[[19,4]]},"3276":{"position":[[19,4]]}}}],["histori",{"_index":29,"t":{"33":{"position":[[0,7]]},"73":{"position":[[9,7]]},"346":{"position":[[8,7]]},"382":{"position":[[8,7]]},"460":{"position":[[13,7]]},"582":{"position":[[0,7]]},"588":{"position":[[13,7]]},"968":{"position":[[0,7]]},"970":{"position":[[0,7]]},"1278":{"position":[[8,7]]},"1312":{"position":[[13,7]]},"1340":{"position":[[8,7]]},"1414":{"position":[[25,8]]},"1472":{"position":[[0,7]]},"1550":{"position":[[0,7]]},"1774":{"position":[[0,7]]},"1978":{"position":[[0,7]]},"1980":{"position":[[0,7]]},"2304":{"position":[[0,7]]},"2401":{"position":[[8,7]]},"2455":{"position":[[8,7]]},"2599":{"position":[[13,7]]},"2655":{"position":[[8,7]]},"2755":{"position":[[0,7]]},"2933":{"position":[[0,7]]},"3137":{"position":[[0,7]]},"3139":{"position":[[0,7]]},"3517":{"position":[[0,7]]},"3666":{"position":[[8,7]]}}}],["history_cel",{"_index":528,"t":{"1500":{"position":[[0,11]]},"2695":{"position":[[0,11]]}}}],["history_disable_for_cli",{"_index":420,"t":{"843":{"position":[[0,26]]}}}],["history_meta_ttl",{"_index":205,"t":{"316":{"position":[[0,16]]},"3085":{"position":[[0,16]]}}}],["history_remov",{"_index":523,"t":{"1474":{"position":[[0,14]]},"2306":{"position":[[0,14]]},"3519":{"position":[[0,14]]}}}],["history_s",{"_index":417,"t":{"835":{"position":[[0,12]]},"1852":{"position":[[0,12]]},"3081":{"position":[[0,12]]}}}],["history_ttl",{"_index":418,"t":{"837":{"position":[[0,11]]},"1854":{"position":[[0,11]]},"3083":{"position":[[0,11]]}}}],["hm",{"_index":560,"t":{"1693":{"position":[[0,3]]},"2857":{"position":[[0,3]]}}}],["hook",{"_index":290,"t":{"436":{"position":[[40,6]]},"1262":{"position":[[40,6]]},"2511":{"position":[[40,6]]}}}],["horizont",{"_index":256,"t":{"400":{"position":[[21,13]]},"1224":{"position":[[21,13]]},"2473":{"position":[[21,13]]}}}],["http",{"_index":208,"t":{"320":{"position":[[4,4]]},"566":{"position":[[7,4]]},"570":{"position":[[7,4]]},"580":{"position":[[8,4]]},"582":{"position":[[8,4]]},"607":{"position":[[0,4]]},"1008":{"position":[[0,4]]},"1010":{"position":[[0,4]]},"1012":{"position":[[6,4]]},"1452":{"position":[[0,4]]},"1454":{"position":[[0,4]]},"1482":{"position":[[0,4]]},"1534":{"position":[[7,4]]},"1538":{"position":[[7,4]]},"1548":{"position":[[8,4]]},"1550":{"position":[[8,4]]},"2284":{"position":[[0,4]]},"2286":{"position":[[0,4]]},"2314":{"position":[[0,4]]},"2345":{"position":[[0,4]]},"2347":{"position":[[0,4]]},"2349":{"position":[[6,4]]},"2577":{"position":[[4,4]]},"2739":{"position":[[7,4]]},"2743":{"position":[[7,4]]},"2753":{"position":[[8,4]]},"2755":{"position":[[8,4]]},"3406":{"position":[[0,4]]},"3408":{"position":[[0,4]]},"3410":{"position":[[6,4]]},"3495":{"position":[[0,4]]},"3497":{"position":[[0,4]]},"3527":{"position":[[0,4]]},"3539":{"position":[[25,4]]}}}],["http/2",{"_index":275,"t":{"420":{"position":[[26,7]]},"1244":{"position":[[26,7]]},"2493":{"position":[[26,7]]}}}],["http/3",{"_index":112,"t":{"141":{"position":[[19,6]]},"1246":{"position":[[26,7]]},"2495":{"position":[[26,7]]}}}],["http_stream",{"_index":613,"t":{"2178":{"position":[[0,11]]},"3455":{"position":[[0,11]]}}}],["http_stream_max_request_body_s",{"_index":614,"t":{"2180":{"position":[[0,33]]},"3457":{"position":[[0,33]]}}}],["iat",{"_index":397,"t":{"777":{"position":[[0,3]]},"1758":{"position":[[0,3]]},"1792":{"position":[[0,3]]},"2963":{"position":[[0,3]]},"3021":{"position":[[0,3]]}}}],["idiomat",{"_index":227,"t":{"344":{"position":[[0,9]]},"1276":{"position":[[0,9]]},"2399":{"position":[[0,9]]}}}],["imag",{"_index":200,"t":{"308":{"position":[[13,6]]},"510":{"position":[[7,5]]},"540":{"position":[[7,5]]},"1214":{"position":[[13,6]]},"1438":{"position":[[7,5]]},"1556":{"position":[[7,5]]},"2326":{"position":[[13,6]]},"2607":{"position":[[7,5]]},"2725":{"position":[[7,5]]}}}],["implement",{"_index":144,"t":{"199":{"position":[[0,12]]},"265":{"position":[[7,14]]},"1402":{"position":[[7,14]]},"1424":{"position":[[7,14]]},"1510":{"position":[[0,14]]},"2123":{"position":[[0,14]]},"2769":{"position":[[0,14]]},"3346":{"position":[[0,14]]}}}],["import",{"_index":434,"t":{"889":{"position":[[0,9]]},"2002":{"position":[[0,9]]},"3191":{"position":[[0,9]]}}}],["improv",{"_index":38,"t":{"41":{"position":[[11,12]]},"45":{"position":[[7,12]]},"49":{"position":[[12,12]]},"177":{"position":[[9,12]]},"201":{"position":[[14,8]]}}}],["index",{"_index":140,"t":{"191":{"position":[[8,5]]}}}],["indic",{"_index":86,"t":{"109":{"position":[[15,10]]}}}],["info",{"_index":401,"t":{"785":{"position":[[0,4]]},"805":{"position":[[33,4]]},"992":{"position":[[0,4]]},"1478":{"position":[[0,4]]},"1746":{"position":[[0,4]]},"1800":{"position":[[0,4]]},"1820":{"position":[[33,4]]},"2310":{"position":[[0,4]]},"2951":{"position":[[0,4]]},"3029":{"position":[[0,4]]},"3049":{"position":[[33,4]]},"3523":{"position":[[0,4]]}}}],["inform",{"_index":242,"t":{"380":{"position":[[16,11]]},"1338":{"position":[[16,11]]},"2453":{"position":[[16,11]]}}}],["initi",{"_index":507,"t":{"1268":{"position":[[12,7]]},"2517":{"position":[[12,7]]}}}],["input",{"_index":157,"t":{"223":{"position":[[18,5]]}}}],["insecur",{"_index":367,"t":{"667":{"position":[[6,8]]},"933":{"position":[[0,8]]},"934":{"position":[[0,8]]},"936":{"position":[[0,8]]},"938":{"position":[[0,8]]},"1661":{"position":[[6,8]]},"2054":{"position":[[0,8]]},"2055":{"position":[[0,8]]},"2057":{"position":[[0,8]]},"2059":{"position":[[0,8]]},"2913":{"position":[[6,8]]},"3247":{"position":[[0,8]]},"3248":{"position":[[0,8]]},"3252":{"position":[[0,8]]},"3254":{"position":[[0,8]]}}}],["instal",{"_index":69,"t":{"95":{"position":[[0,7]]},"221":{"position":[[0,7]]},"486":{"position":[[3,7]]},"508":{"position":[[0,7]]},"1380":{"position":[[3,7]]},"1436":{"position":[[0,7]]},"2551":{"position":[[3,7]]},"2605":{"position":[[0,7]]}}}],["instanc",{"_index":252,"t":{"396":{"position":[[40,8]]},"1220":{"position":[[40,8]]},"2469":{"position":[[40,8]]}}}],["instead",{"_index":503,"t":{"1194":{"position":[[8,7]]},"2095":{"position":[[8,7]]},"3280":{"position":[[8,7]]}}}],["insuffici",{"_index":391,"t":{"747":{"position":[[0,12]]}}}],["integr",{"_index":120,"t":{"157":{"position":[[4,9]]},"183":{"position":[[4,9]]},"360":{"position":[[7,11]]},"1294":{"position":[[10,12]]},"1318":{"position":[[7,11]]},"1687":{"position":[[9,9]]},"2429":{"position":[[10,12]]},"2433":{"position":[[7,11]]},"2851":{"position":[[9,9]]}}}],["interact",{"_index":130,"t":{"169":{"position":[[0,11]]}}}],["interfac",{"_index":366,"t":{"665":{"position":[[17,9]]},"1659":{"position":[[17,9]]},"2911":{"position":[[17,9]]}}}],["intern",{"_index":376,"t":{"699":{"position":[[0,8]]},"757":{"position":[[0,8]]},"925":{"position":[[7,8]]},"1934":{"position":[[0,8]]},"2046":{"position":[[7,8]]},"2979":{"position":[[0,8]]},"3239":{"position":[[7,8]]}}}],["interv",{"_index":343,"t":{"595":{"position":[[5,8]]}}}],["introduc",{"_index":224,"t":{"334":{"position":[[0,11]]}}}],["invalid",{"_index":361,"t":{"637":{"position":[[0,10]]},"731":{"position":[[0,7]]},"1637":{"position":[[0,10]]},"2824":{"position":[[0,10]]}}}],["invalidate_user_token",{"_index":546,"t":{"1638":{"position":[[0,22]]},"2825":{"position":[[0,22]]}}}],["investig",{"_index":407,"t":{"807":{"position":[[0,13]]},"1822":{"position":[[0,13]]},"3051":{"position":[[0,13]]}}}],["iss",{"_index":400,"t":{"783":{"position":[[0,3]]},"1002":{"position":[[0,3]]},"1756":{"position":[[0,3]]},"1798":{"position":[[0,3]]},"2961":{"position":[[0,3]]},"3027":{"position":[[0,3]]}}}],["it'",{"_index":46,"t":{"57":{"position":[[4,4]]}}}],["iter",{"_index":30,"t":{"33":{"position":[[8,9]]},"970":{"position":[[8,9]]},"1980":{"position":[[8,9]]},"3139":{"position":[[8,9]]}}}],["javascript",{"_index":96,"t":{"123":{"position":[[30,10]]},"139":{"position":[[0,10]]}}}],["join",{"_index":116,"t":{"149":{"position":[[0,4]]},"528":{"position":[[0,4]]},"2121":{"position":[[0,4]]},"3344":{"position":[[0,4]]}}}],["join/leav",{"_index":292,"t":{"438":{"position":[[28,10]]},"440":{"position":[[40,10]]},"1264":{"position":[[28,10]]},"1266":{"position":[[40,10]]},"1782":{"position":[[0,10]]},"2513":{"position":[[28,10]]},"2515":{"position":[[40,10]]},"2941":{"position":[[0,10]]}}}],["join_leav",{"_index":416,"t":{"833":{"position":[[0,10]]},"1848":{"position":[[0,10]]},"3077":{"position":[[0,10]]}}}],["json",{"_index":266,"t":{"412":{"position":[[27,4]]},"809":{"position":[[0,4]]},"883":{"position":[[0,4]]},"1080":{"position":[[0,4]]},"1236":{"position":[[27,4]]},"1824":{"position":[[0,4]]},"1996":{"position":[[0,4]]},"2167":{"position":[[13,4]]},"2200":{"position":[[0,4]]},"2485":{"position":[[27,4]]},"3053":{"position":[[0,4]]},"3185":{"position":[[0,4]]},"3396":{"position":[[13,4]]},"3475":{"position":[[0,4]]}}}],["jti",{"_index":398,"t":{"779":{"position":[[0,3]]},"1760":{"position":[[0,3]]},"1794":{"position":[[0,3]]},"2965":{"position":[[0,3]]},"3023":{"position":[[0,3]]}}}],["jwk",{"_index":579,"t":{"1826":{"position":[[8,4]]},"3055":{"position":[[8,4]]}}}],["jwt",{"_index":190,"t":{"294":{"position":[[39,3]]},"300":{"position":[[32,3]]},"574":{"position":[[7,3]]},"609":{"position":[[0,3]]},"807":{"position":[[28,3]]},"1542":{"position":[[7,3]]},"1740":{"position":[[13,3]]},"1786":{"position":[[11,3]]},"1822":{"position":[[28,3]]},"2747":{"position":[[7,3]]},"2945":{"position":[[13,3]]},"3015":{"position":[[11,3]]},"3051":{"position":[[28,3]]}}}],["kafka",{"_index":623,"t":{"2421":{"position":[[14,5]]}}}],["keep",{"_index":177,"t":{"267":{"position":[[7,4]]}}}],["key",{"_index":217,"t":{"328":{"position":[[15,4]]},"548":{"position":[[20,3]]},"809":{"position":[[9,3]]},"1054":{"position":[[14,3]]},"1490":{"position":[[9,3]]},"1564":{"position":[[20,3]]},"1824":{"position":[[9,3]]},"2137":{"position":[[14,3]]},"2322":{"position":[[9,3]]},"2733":{"position":[[20,3]]},"3053":{"position":[[9,3]]},"3352":{"position":[[14,3]]},"3535":{"position":[[9,3]]}}}],["keycloak",{"_index":152,"t":{"211":{"position":[[0,8]]}}}],["keydb",{"_index":505,"t":{"1200":{"position":[[0,5]]},"2101":{"position":[[0,5]]}}}],["know",{"_index":281,"t":{"426":{"position":[[10,4]]},"1252":{"position":[[10,4]]},"2501":{"position":[[10,4]]}}}],["kubernet",{"_index":319,"t":{"514":{"position":[[0,10]]},"542":{"position":[[0,10]]},"1442":{"position":[[0,10]]},"1558":{"position":[[0,10]]},"2611":{"position":[[0,10]]},"2727":{"position":[[0,10]]}}}],["lab",{"_index":225,"t":{"334":{"position":[[24,4]]}}}],["land",{"_index":198,"t":{"308":{"position":[[0,7]]},"1214":{"position":[[0,7]]},"2326":{"position":[[0,7]]}}}],["laravel",{"_index":121,"t":{"157":{"position":[[14,7]]}}}],["late",{"_index":164,"t":{"231":{"position":[[0,4]]}}}],["latenc",{"_index":311,"t":{"480":{"position":[[7,7]]}}}],["layer",{"_index":97,"t":{"125":{"position":[[3,8]]}}}],["leav",{"_index":608,"t":{"2121":{"position":[[9,5]]},"3344":{"position":[[9,5]]}}}],["level",{"_index":514,"t":{"1404":{"position":[[4,5]]},"2153":{"position":[[4,5]]},"3382":{"position":[[4,5]]}}}],["librari",{"_index":3,"t":{"7":{"position":[[10,9]]},"1292":{"position":[[11,7]]},"1482":{"position":[[9,9]]},"2314":{"position":[[9,9]]},"2427":{"position":[[11,7]]},"3527":{"position":[[9,9]]}}}],["licens",{"_index":24,"t":{"29":{"position":[[0,7]]},"548":{"position":[[12,7]]},"1564":{"position":[[12,7]]},"2733":{"position":[[12,7]]}}}],["lifecycl",{"_index":298,"t":{"452":{"position":[[11,9]]},"1304":{"position":[[11,9]]},"2591":{"position":[[11,9]]}}}],["limit",{"_index":66,"t":{"85":{"position":[[0,11]]},"288":{"position":[[10,7]]},"590":{"position":[[12,5]]},"711":{"position":[[0,5]]},"753":{"position":[[11,5]]},"948":{"position":[[11,5]]},"1368":{"position":[[5,7]]},"1918":{"position":[[11,5]]},"1946":{"position":[[0,5]]},"2539":{"position":[[5,7]]},"2829":{"position":[[30,5]]},"2831":{"position":[[24,5]]},"2833":{"position":[[20,5]]},"2991":{"position":[[0,5]]},"3145":{"position":[[11,5]]}}}],["line",{"_index":347,"t":{"599":{"position":[[13,4]]},"875":{"position":[[8,4]]},"1988":{"position":[[8,4]]},"3177":{"position":[[8,4]]}}}],["linux",{"_index":325,"t":{"516":{"position":[[25,5]]},"1444":{"position":[[25,5]]},"2613":{"position":[[25,5]]}}}],["list",{"_index":472,"t":{"1044":{"position":[[11,4]]},"2165":{"position":[[0,4]]},"2383":{"position":[[11,4]]},"3394":{"position":[[0,4]]},"3444":{"position":[[11,4]]}}}],["listen",{"_index":291,"t":{"438":{"position":[[18,6]]},"1264":{"position":[[18,6]]},"2513":{"position":[[18,6]]},"2635":{"position":[[0,6]]},"3646":{"position":[[0,6]]}}}],["locat",{"_index":463,"t":{"980":{"position":[[11,8]]},"2073":{"position":[[11,8]]},"3310":{"position":[[11,8]]}}}],["log",{"_index":639,"t":{"3328":{"position":[[0,4]]}}}],["ltd",{"_index":226,"t":{"334":{"position":[[29,3]]}}}],["maco",{"_index":327,"t":{"518":{"position":[[13,5]]},"1446":{"position":[[13,5]]},"2615":{"position":[[13,5]]}}}],["maintain",{"_index":475,"t":{"1070":{"position":[[0,8]]},"2133":{"position":[[0,8]]},"3336":{"position":[[0,8]]}}}],["manag",{"_index":239,"t":{"372":{"position":[[11,10]]},"1330":{"position":[[11,10]]},"2445":{"position":[[11,10]]},"2633":{"position":[[13,10]]},"3644":{"position":[[13,10]]}}}],["mani",{"_index":7,"t":{"11":{"position":[[8,4]]},"396":{"position":[[4,4]]},"721":{"position":[[4,4]]},"1220":{"position":[[4,4]]},"1956":{"position":[[4,4]]},"2469":{"position":[[4,4]]},"3001":{"position":[[4,4]]}}}],["massiv",{"_index":13,"t":{"17":{"position":[[0,7]]}}}],["match",{"_index":542,"t":{"1576":{"position":[[18,5]]},"1578":{"position":[[15,5]]},"1580":{"position":[[28,5]]},"2671":{"position":[[18,5]]},"2673":{"position":[[15,5]]},"2675":{"position":[[28,5]]}}}],["matrix",{"_index":512,"t":{"1402":{"position":[[30,6]]},"2169":{"position":[[12,6]]},"3398":{"position":[[12,6]]}}}],["max",{"_index":454,"t":{"954":{"position":[[6,3]]},"1924":{"position":[[6,3]]},"3151":{"position":[[6,3]]}}}],["memori",{"_index":253,"t":{"398":{"position":[[0,6]]},"1182":{"position":[[0,6]]},"1184":{"position":[[0,6]]},"1222":{"position":[[0,6]]},"1665":{"position":[[3,6]]},"1667":{"position":[[3,6]]},"2079":{"position":[[0,6]]},"2081":{"position":[[0,6]]},"2471":{"position":[[0,6]]},"2829":{"position":[[3,6]]},"2831":{"position":[[3,6]]},"3264":{"position":[[0,6]]},"3266":{"position":[[0,6]]}}}],["messag",{"_index":8,"t":{"11":{"position":[[13,8]]},"19":{"position":[[0,7]]},"67":{"position":[[6,7]]},"225":{"position":[[5,8]]},"251":{"position":[[15,8]]},"346":{"position":[[0,7]]},"348":{"position":[[0,7]]},"350":{"position":[[0,7]]},"382":{"position":[[0,7]]},"402":{"position":[[0,7]]},"404":{"position":[[0,7]]},"410":{"position":[[19,7],[54,7]]},"426":{"position":[[17,7]]},"428":{"position":[[18,8]]},"972":{"position":[[10,7]]},"1093":{"position":[[15,7]]},"1226":{"position":[[0,7]]},"1228":{"position":[[0,7]]},"1234":{"position":[[19,7],[54,7]]},"1252":{"position":[[17,7]]},"1254":{"position":[[18,8]]},"1278":{"position":[[0,7]]},"1280":{"position":[[0,7]]},"1282":{"position":[[0,7]]},"1340":{"position":[[0,7]]},"1416":{"position":[[30,8]]},"1428":{"position":[[0,7]]},"1982":{"position":[[10,7]]},"2188":{"position":[[15,7]]},"2401":{"position":[[0,7]]},"2403":{"position":[[0,7]]},"2405":{"position":[[0,7]]},"2455":{"position":[[0,7]]},"2475":{"position":[[0,7]]},"2477":{"position":[[0,7]]},"2483":{"position":[[19,7],[54,7]]},"2501":{"position":[[17,7]]},"2503":{"position":[[18,8]]},"3141":{"position":[[10,7]]},"3491":{"position":[[15,7]]}}}],["meta",{"_index":403,"t":{"793":{"position":[[0,4]]},"1808":{"position":[[0,4]]},"3037":{"position":[[0,4]]}}}],["metadata",{"_index":466,"t":{"1014":{"position":[[11,8]]},"2351":{"position":[[11,8]]},"3412":{"position":[[11,8]]}}}],["method",{"_index":354,"t":{"605":{"position":[[13,6]]},"707":{"position":[[0,6]]},"761":{"position":[[0,6]]},"1942":{"position":[[0,6]]},"2625":{"position":[[7,7]]},"2641":{"position":[[13,7]]},"2987":{"position":[[0,6]]},"3499":{"position":[[4,7]]},"3636":{"position":[[7,7]]},"3652":{"position":[[13,7]]}}}],["metric",{"_index":574,"t":{"1722":{"position":[[0,7]]},"2805":{"position":[[8,7]]},"2888":{"position":[[8,7]]},"3316":{"position":[[0,7]]},"3317":{"position":[[11,7]]},"3319":{"position":[[9,7]]},"3323":{"position":[[8,7]]}}}],["migrat",{"_index":114,"t":{"145":{"position":[[0,9]]},"165":{"position":[[9,10]]},"472":{"position":[[14,7]]},"1358":{"position":[[11,9]]},"1360":{"position":[[25,9]]},"1362":{"position":[[7,9]]},"1366":{"position":[[19,9]]},"1368":{"position":[[21,9]]},"1370":{"position":[[24,9]]},"2529":{"position":[[11,9]]},"2531":{"position":[[25,9]]},"2533":{"position":[[7,9]]},"2537":{"position":[[19,9]]},"2539":{"position":[[21,9]]},"2541":{"position":[[24,9]]}}}],["misbehav",{"_index":550,"t":{"1671":{"position":[[25,11]]},"2835":{"position":[[25,11]]}}}],["mobil",{"_index":280,"t":{"424":{"position":[[45,6]]},"1250":{"position":[[45,6]]},"2499":{"position":[[45,6]]}}}],["mode",{"_index":332,"t":{"554":{"position":[[8,4]]},"667":{"position":[[15,4]]},"933":{"position":[[9,5]]},"936":{"position":[[13,4]]},"938":{"position":[[15,4]]},"1038":{"position":[[7,4]]},"1040":{"position":[[15,4]]},"1042":{"position":[[22,4]]},"1528":{"position":[[24,4]]},"1661":{"position":[[15,4]]},"2054":{"position":[[9,5]]},"2057":{"position":[[13,4]]},"2059":{"position":[[15,4]]},"2377":{"position":[[7,4]]},"2379":{"position":[[15,4]]},"2381":{"position":[[22,4]]},"2763":{"position":[[24,4]]},"2913":{"position":[[15,4]]},"3247":{"position":[[9,5]]},"3252":{"position":[[13,4]]},"3254":{"position":[[15,4]]},"3370":{"position":[[15,4]]},"3438":{"position":[[7,4]]},"3440":{"position":[[15,4]]},"3442":{"position":[[22,4]]},"3537":{"position":[[16,4]]}}}],["model",{"_index":128,"t":{"165":{"position":[[24,6]]},"348":{"position":[[17,5]]},"402":{"position":[[17,5]]},"1226":{"position":[[17,5]]},"1280":{"position":[[17,5]]},"1770":{"position":[[21,5]]},"1772":{"position":[[19,5]]},"1774":{"position":[[19,5]]},"1776":{"position":[[20,5]]},"1778":{"position":[[23,5]]},"1780":{"position":[[20,5]]},"1782":{"position":[[22,5]]},"2403":{"position":[[17,5]]},"2475":{"position":[[17,5]]},"2929":{"position":[[21,5]]},"2931":{"position":[[19,5]]},"2933":{"position":[[19,5]]},"2935":{"position":[[20,5]]},"2937":{"position":[[23,5]]},"2939":{"position":[[20,5]]},"2941":{"position":[[22,5]]}}}],["modern",{"_index":94,"t":{"123":{"position":[[0,6]]}}}],["monitor",{"_index":314,"t":{"500":{"position":[[3,7]]},"1394":{"position":[[3,7]]},"2565":{"position":[[3,7]]}}}],["more",{"_index":362,"t":{"643":{"position":[[0,4]]}}}],["motiv",{"_index":304,"t":{"472":{"position":[[0,10]]},"524":{"position":[[0,10]]},"1675":{"position":[[0,10]]},"2839":{"position":[[0,10]]},"3364":{"position":[[0,10]]}}}],["move",{"_index":109,"t":{"139":{"position":[[18,5]]}}}],["multiten",{"_index":508,"t":{"1270":{"position":[[24,13]]},"2519":{"position":[[24,13]]}}}],["name",{"_index":409,"t":{"813":{"position":[[8,4]]},"1832":{"position":[[8,4]]},"3061":{"position":[[8,4]]}}}],["namespac",{"_index":103,"t":{"129":{"position":[[26,10]]},"815":{"position":[[0,9]]},"857":{"position":[[8,10]]},"942":{"position":[[8,10]]},"1370":{"position":[[0,9]]},"1834":{"position":[[0,9]]},"1842":{"position":[[8,10]]},"2063":{"position":[[8,10]]},"2541":{"position":[[0,9]]},"3063":{"position":[[0,9]]},"3071":{"position":[[8,10]]},"3258":{"position":[[8,10]]}}}],["nat",{"_index":506,"t":{"1206":{"position":[[0,4]]},"2109":{"position":[[0,4]]},"2423":{"position":[[14,4]]},"3292":{"position":[[0,4]]}}}],["need",{"_index":279,"t":{"424":{"position":[[10,4]]},"1250":{"position":[[10,4]]},"2499":{"position":[[10,4]]}}}],["new",{"_index":42,"t":{"47":{"position":[[0,3]]},"320":{"position":[[0,3]]},"416":{"position":[[27,3]]},"428":{"position":[[14,3]]},"1240":{"position":[[27,3]]},"1254":{"position":[[14,3]]},"2489":{"position":[[27,3]]},"2503":{"position":[[14,3]]}}}],["nginx",{"_index":143,"t":{"197":{"position":[[7,5]]},"249":{"position":[[7,5]]},"418":{"position":[[48,5]]},"976":{"position":[[0,5]]},"1242":{"position":[[48,5]]},"2069":{"position":[[0,5]]},"2491":{"position":[[48,5]]},"3306":{"position":[[0,5]]}}}],["node",{"_index":49,"t":{"61":{"position":[[11,4]]},"318":{"position":[[0,4]]},"2575":{"position":[[0,4]]}}}],["non",{"_index":555,"t":{"1683":{"position":[[0,3]]},"1964":{"position":[[0,3]]},"2847":{"position":[[0,3]]},"3009":{"position":[[0,3]]}}}],["normal",{"_index":387,"t":{"727":{"position":[[0,6]]}}}],["note",{"_index":521,"t":{"1432":{"position":[[11,5]]},"2123":{"position":[[15,5]]},"2161":{"position":[[11,5]]},"3346":{"position":[[15,5]]},"3390":{"position":[[11,5]]}}}],["notif",{"_index":173,"t":{"259":{"position":[[19,13]]},"261":{"position":[[5,13]]},"269":{"position":[[12,13]]},"275":{"position":[[5,13]]},"424":{"position":[[28,13]]},"1250":{"position":[[28,13]]},"1612":{"position":[[0,13]]},"2499":{"position":[[28,13]]},"2797":{"position":[[0,13]]}}}],["number",{"_index":260,"t":{"408":{"position":[[35,6]]},"1232":{"position":[[35,6]]},"2481":{"position":[[35,6]]}}}],["obtrus",{"_index":556,"t":{"1683":{"position":[[4,9]]},"2847":{"position":[[4,9]]}}}],["old",{"_index":202,"t":{"312":{"position":[[9,3]]},"2577":{"position":[[0,3]]}}}],["on",{"_index":174,"t":{"261":{"position":[[31,3]]},"396":{"position":[[25,3]]},"412":{"position":[[43,3]]},"1220":{"position":[[25,3]]},"1236":{"position":[[43,3]]},"2469":{"position":[[25,3]]},"2485":{"position":[[43,3]]}}}],["onlin",{"_index":58,"t":{"75":{"position":[[0,6]]},"354":{"position":[[0,6]]},"380":{"position":[[0,6]]},"414":{"position":[[0,6],[32,6]]},"440":{"position":[[20,6]]},"1238":{"position":[[0,6],[32,6]]},"1266":{"position":[[20,6]]},"1286":{"position":[[0,6]]},"1338":{"position":[[0,6]]},"2117":{"position":[[9,6]]},"2409":{"position":[[0,6]]},"2453":{"position":[[0,6]]},"2487":{"position":[[0,6],[32,6]]},"2515":{"position":[[20,6]]},"3340":{"position":[[9,6]]}}}],["open",{"_index":250,"t":{"390":{"position":[[0,4]]},"948":{"position":[[0,4]]},"1348":{"position":[[0,4]]},"1918":{"position":[[0,4]]},"2463":{"position":[[0,4]]},"3145":{"position":[[0,4]]}}}],["openapi",{"_index":210,"t":{"322":{"position":[[0,7]]}}}],["opentelemetri",{"_index":213,"t":{"324":{"position":[[0,13]]},"3326":{"position":[[0,13]]}}}],["oper",{"_index":369,"t":{"675":{"position":[[0,10]]},"1608":{"position":[[0,10]]},"2793":{"position":[[0,10]]}}}],["optimist",{"_index":107,"t":{"133":{"position":[[0,10]]}}}],["option",{"_index":344,"t":{"595":{"position":[[14,7]]},"597":{"position":[[8,7]]},"663":{"position":[[0,7]]},"821":{"position":[[8,7]]},"855":{"position":[[8,7]]},"889":{"position":[[10,7]]},"899":{"position":[[9,7]]},"940":{"position":[[22,7]]},"1032":{"position":[[11,7]]},"1082":{"position":[[0,7]]},"1103":{"position":[[0,7]]},"1114":{"position":[[0,7]]},"1141":{"position":[[0,7]]},"1150":{"position":[[0,7]]},"1171":{"position":[[0,7]]},"1184":{"position":[[14,7]]},"1188":{"position":[[13,7]]},"1204":{"position":[[17,7]]},"1208":{"position":[[0,7]]},"1374":{"position":[[20,6]]},"1657":{"position":[[0,7]]},"1697":{"position":[[6,7]]},"1844":{"position":[[8,7]]},"2002":{"position":[[10,7]]},"2012":{"position":[[9,7]]},"2061":{"position":[[22,7]]},"2081":{"position":[[14,7]]},"2085":{"position":[[13,7]]},"2107":{"position":[[17,7]]},"2111":{"position":[[0,7]]},"2177":{"position":[[0,7]]},"2202":{"position":[[0,7]]},"2209":{"position":[[0,7]]},"2222":{"position":[[0,7]]},"2237":{"position":[[0,7]]},"2254":{"position":[[0,7]]},"2265":{"position":[[0,7]]},"2330":{"position":[[0,7]]},"2371":{"position":[[11,7]]},"2545":{"position":[[20,6]]},"2623":{"position":[[14,7]]},"2639":{"position":[[20,7]]},"2861":{"position":[[6,7]]},"2909":{"position":[[0,7]]},"3073":{"position":[[8,7]]},"3191":{"position":[[10,7]]},"3201":{"position":[[9,7]]},"3256":{"position":[[22,7]]},"3266":{"position":[[14,7]]},"3270":{"position":[[13,7]]},"3290":{"position":[[17,7]]},"3294":{"position":[[0,7]]},"3432":{"position":[[11,7]]},"3454":{"position":[[0,7]]},"3477":{"position":[[0,7]]},"3484":{"position":[[0,7]]},"3547":{"position":[[0,7]]},"3572":{"position":[[0,7]]},"3587":{"position":[[0,7]]},"3604":{"position":[[0,7]]},"3615":{"position":[[0,7]]},"3634":{"position":[[14,7]]},"3650":{"position":[[20,7]]}}}],["order",{"_index":62,"t":{"79":{"position":[[0,5]]},"233":{"position":[[0,8]]},"235":{"position":[[16,8]]},"350":{"position":[[8,5]]},"404":{"position":[[8,5]]},"1228":{"position":[[8,5]]},"1282":{"position":[[8,5]]},"2405":{"position":[[8,5]]},"2477":{"position":[[8,5]]}}}],["organ",{"_index":286,"t":{"432":{"position":[[23,8]]},"1258":{"position":[[23,8]]},"2507":{"position":[[23,8]]}}}],["origin",{"_index":351,"t":{"601":{"position":[[17,6]]}}}],["os",{"_index":4,"t":{"9":{"position":[[0,2]]},"877":{"position":[[0,2]]},"1990":{"position":[[0,2]]},"3179":{"position":[[0,2]]}}}],["output",{"_index":158,"t":{"223":{"position":[[28,6]]}}}],["over",{"_index":88,"t":{"111":{"position":[[14,4]]},"378":{"position":[[4,4]]},"428":{"position":[[27,4]]},"942":{"position":[[19,4]]},"1254":{"position":[[27,4]]},"1336":{"position":[[4,4]]},"2063":{"position":[[19,4]]},"2451":{"position":[[4,4]]},"2503":{"position":[[27,4]]},"3258":{"position":[[19,4]]}}}],["overrid",{"_index":576,"t":{"1762":{"position":[[0,8]]},"2967":{"position":[[0,8]]}}}],["overview",{"_index":68,"t":{"93":{"position":[[0,8]]},"155":{"position":[[12,8]]},"2115":{"position":[[0,8]]},"2711":{"position":[[0,8]]}}}],["packag",{"_index":324,"t":{"516":{"position":[[12,8]]},"603":{"position":[[26,7]]},"1444":{"position":[[12,8]]},"2613":{"position":[[12,8]]}}}],["page",{"_index":199,"t":{"308":{"position":[[8,4]]},"1214":{"position":[[8,4]]},"2326":{"position":[[8,4]]}}}],["pars",{"_index":85,"t":{"109":{"position":[[0,7]]}}}],["pass",{"_index":54,"t":{"67":{"position":[[14,7]]}}}],["per",{"_index":254,"t":{"398":{"position":[[13,3]]},"1222":{"position":[[13,3]]},"1665":{"position":[[10,3]]},"1667":{"position":[[10,3]]},"1669":{"position":[[6,3]]},"2471":{"position":[[13,3]]},"2829":{"position":[[10,3]]},"2831":{"position":[[10,3]]},"2833":{"position":[[6,3]]}}}],["perform",{"_index":11,"t":{"15":{"position":[[0,11]]},"49":{"position":[[0,11]]},"83":{"position":[[0,11]]},"362":{"position":[[6,11]]},"1320":{"position":[[6,11]]},"2435":{"position":[[6,11]]}}}],["permiss",{"_index":378,"t":{"705":{"position":[[0,10]]},"1588":{"position":[[22,11]]},"1770":{"position":[[10,10]]},"1772":{"position":[[8,10]]},"1774":{"position":[[8,10]]},"1776":{"position":[[9,10]]},"1778":{"position":[[12,10]]},"1780":{"position":[[9,10]]},"1782":{"position":[[11,10]]},"1940":{"position":[[0,10]]},"2683":{"position":[[22,11]]},"2929":{"position":[[10,10]]},"2931":{"position":[[8,10]]},"2933":{"position":[[8,10]]},"2935":{"position":[[9,10]]},"2937":{"position":[[12,10]]},"2939":{"position":[[9,10]]},"2941":{"position":[[11,10]]},"2985":{"position":[[0,10]]}}}],["persist",{"_index":359,"t":{"631":{"position":[[6,11]]},"633":{"position":[[9,11]]},"651":{"position":[[6,11]]},"653":{"position":[[9,11]]},"1630":{"position":[[6,11]]},"1632":{"position":[[9,11]]},"1646":{"position":[[6,11]]},"1648":{"position":[[9,11]]},"2817":{"position":[[6,11]]},"2819":{"position":[[9,11]]},"2898":{"position":[[6,11]]},"2900":{"position":[[9,11]]}}}],["person",{"_index":194,"t":{"298":{"position":[[15,8]]},"1068":{"position":[[10,8]]},"2131":{"position":[[10,8]]},"3334":{"position":[[10,8]]}}}],["ping",{"_index":100,"t":{"127":{"position":[[11,4]]},"1101":{"position":[[0,5]]},"1139":{"position":[[0,5]]},"1169":{"position":[[0,5]]},"1418":{"position":[[0,4]]},"2155":{"position":[[0,4]]},"2220":{"position":[[0,5]]},"2252":{"position":[[0,5]]},"3384":{"position":[[0,4]]},"3570":{"position":[[0,5]]},"3602":{"position":[[0,5]]}}}],["ping/pong",{"_index":615,"t":{"2190":{"position":[[0,9]]},"2629":{"position":[[11,9]]},"3465":{"position":[[0,9]]},"3640":{"position":[[11,9]]}}}],["pipelin",{"_index":303,"t":{"470":{"position":[[12,10]]},"1480":{"position":[[8,10]]},"2312":{"position":[[8,10]]}}}],["pitfal",{"_index":162,"t":{"229":{"position":[[0,8]]}}}],["platform",{"_index":247,"t":{"386":{"position":[[6,8]]},"1344":{"position":[[6,8]]},"2459":{"position":[[6,8]]}}}],["pong",{"_index":101,"t":{"127":{"position":[[16,4]]},"1418":{"position":[[5,4]]},"2155":{"position":[[5,4]]},"3384":{"position":[[5,4]]}}}],["port",{"_index":437,"t":{"895":{"position":[[0,4]]},"925":{"position":[[16,5]]},"950":{"position":[[10,4]]},"1920":{"position":[[10,4]]},"2008":{"position":[[0,4]]},"2046":{"position":[[16,5]]},"3147":{"position":[[10,4]]},"3197":{"position":[[0,4]]},"3239":{"position":[[16,5]]}}}],["posit",{"_index":386,"t":{"723":{"position":[[14,8]]},"767":{"position":[[14,8]]},"839":{"position":[[0,8]]},"1778":{"position":[[0,11]]},"1958":{"position":[[14,8]]},"2937":{"position":[[0,11]]},"3003":{"position":[[14,8]]}}}],["possibl",{"_index":133,"t":{"177":{"position":[[0,8]]},"438":{"position":[[6,8]]},"1264":{"position":[[6,8]]},"2513":{"position":[[6,8]]}}}],["postgresql",{"_index":562,"t":{"1699":{"position":[[4,10]]},"2863":{"position":[[4,10]]}}}],["postman",{"_index":221,"t":{"330":{"position":[[32,7]]},"3628":{"position":[[15,8]]}}}],["practic",{"_index":259,"t":{"408":{"position":[[16,9]]},"1232":{"position":[[16,9]]},"2481":{"position":[[16,9]]},"2659":{"position":[[16,9]]},"3670":{"position":[[16,9]]}}}],["prefix",{"_index":412,"t":{"817":{"position":[[16,6]]},"1838":{"position":[[16,6]]},"3067":{"position":[[16,6]]}}}],["prerequisit",{"_index":135,"t":{"185":{"position":[[0,13]]}}}],["presenc",{"_index":59,"t":{"75":{"position":[[7,8],[20,8]]},"354":{"position":[[7,8]]},"380":{"position":[[7,8]]},"414":{"position":[[7,8]]},"440":{"position":[[27,8]]},"462":{"position":[[0,8],[13,8]]},"829":{"position":[[0,8]]},"1238":{"position":[[7,8]]},"1266":{"position":[[27,8]]},"1286":{"position":[[7,8]]},"1314":{"position":[[0,8],[13,8]]},"1338":{"position":[[7,8]]},"1414":{"position":[[34,8]]},"1468":{"position":[[0,8]]},"1776":{"position":[[0,8]]},"1846":{"position":[[0,8]]},"2117":{"position":[[16,8]]},"2119":{"position":[[11,8]]},"2300":{"position":[[0,8]]},"2409":{"position":[[7,8]]},"2453":{"position":[[7,8]]},"2487":{"position":[[7,8]]},"2515":{"position":[[27,8]]},"2601":{"position":[[0,8],[13,8]]},"2657":{"position":[[0,8],[13,8]]},"2935":{"position":[[0,8]]},"3075":{"position":[[0,8]]},"3340":{"position":[[16,8]]},"3342":{"position":[[11,8]]},"3513":{"position":[[0,8]]},"3668":{"position":[[0,8],[13,8]]}}}],["presence_cel",{"_index":529,"t":{"1502":{"position":[[0,12]]},"2697":{"position":[[0,12]]}}}],["presence_disable_for_cli",{"_index":415,"t":{"831":{"position":[[0,27]]}}}],["presence_stat",{"_index":522,"t":{"1470":{"position":[[0,14]]},"2302":{"position":[[0,14]]},"3515":{"position":[[0,14]]}}}],["presencemanag",{"_index":301,"t":{"466":{"position":[[11,15]]}}}],["preserv",{"_index":167,"t":{"235":{"position":[[25,9]]}}}],["price",{"_index":333,"t":{"556":{"position":[[0,7]]},"1530":{"position":[[0,7]]},"2761":{"position":[[0,7]]}}}],["privat",{"_index":104,"t":{"131":{"position":[[0,7]]},"430":{"position":[[50,8]]},"817":{"position":[[0,7]]},"1256":{"position":[[50,8]]},"1838":{"position":[[0,7]]},"2505":{"position":[[50,8]]},"3067":{"position":[[0,7]]}}}],["pro",{"_index":45,"t":{"51":{"position":[[11,3]]},"257":{"position":[[11,3]]},"392":{"position":[[0,3]]},"548":{"position":[[8,3]]},"1350":{"position":[[0,3]]},"1564":{"position":[[8,3]]},"2465":{"position":[[0,3]]},"2733":{"position":[[8,3]]}}}],["problem",{"_index":408,"t":{"807":{"position":[[14,8]]},"1822":{"position":[[14,8]]},"3051":{"position":[[14,8]]}}}],["process",{"_index":538,"t":{"1570":{"position":[[5,10]]},"2665":{"position":[[5,10]]}}}],["product",{"_index":313,"t":{"498":{"position":[[13,10]]},"1392":{"position":[[13,10]]},"2563":{"position":[[13,10]]}}}],["project",{"_index":123,"t":{"159":{"position":[[18,7]]},"187":{"position":[[11,7]]}}}],["prometheu",{"_index":456,"t":{"960":{"position":[[0,10]]},"1970":{"position":[[0,10]]},"3298":{"position":[[0,10]]},"3317":{"position":[[0,10]]}}}],["properti",{"_index":64,"t":{"79":{"position":[[19,10]]}}}],["protect",{"_index":421,"t":{"845":{"position":[[0,9]]},"1928":{"position":[[18,10]]},"3155":{"position":[[18,10]]}}}],["protobuf",{"_index":353,"t":{"603":{"position":[[17,8]]},"1161":{"position":[[0,8]]},"2147":{"position":[[0,8]]},"2167":{"position":[[0,8]]},"2341":{"position":[[0,8]]},"3376":{"position":[[0,8]]},"3396":{"position":[[0,8]]},"3626":{"position":[[0,8]]}}}],["protocol",{"_index":98,"t":{"125":{"position":[[22,8]]},"312":{"position":[[20,8]]},"318":{"position":[[19,8]]},"330":{"position":[[12,8]]},"366":{"position":[[14,8]]},"1161":{"position":[[16,8]]},"1324":{"position":[[14,8]]},"2341":{"position":[[16,8]]},"2439":{"position":[[14,8]]},"3626":{"position":[[16,8]]}}}],["provid",{"_index":172,"t":{"259":{"position":[[4,9]]},"261":{"position":[[35,8]]}}}],["proxi",{"_index":37,"t":{"39":{"position":[[5,5]]},"171":{"position":[[8,5]]},"199":{"position":[[13,5]]},"292":{"position":[[15,5]]},"296":{"position":[[36,5]]},"302":{"position":[[29,5]]},"418":{"position":[[37,5]]},"570":{"position":[[12,5]]},"572":{"position":[[12,5]]},"607":{"position":[[5,5]]},"954":{"position":[[0,5]]},"1008":{"position":[[5,5]]},"1012":{"position":[[0,5]]},"1014":{"position":[[0,5]]},"1016":{"position":[[8,5]]},"1018":{"position":[[8,5]]},"1020":{"position":[[4,5]]},"1022":{"position":[[10,5]]},"1024":{"position":[[8,5]]},"1030":{"position":[[5,5]]},"1032":{"position":[[5,5]]},"1034":{"position":[[5,5]]},"1036":{"position":[[7,5]]},"1040":{"position":[[9,5]]},"1042":{"position":[[16,5]]},"1044":{"position":[[19,7]]},"1242":{"position":[[37,5]]},"1372":{"position":[[0,5]]},"1538":{"position":[[12,5]]},"1540":{"position":[[12,5]]},"1683":{"position":[[14,8]]},"1924":{"position":[[0,5]]},"2345":{"position":[[5,5]]},"2349":{"position":[[0,5]]},"2351":{"position":[[0,5]]},"2353":{"position":[[8,5]]},"2355":{"position":[[8,5]]},"2357":{"position":[[4,5]]},"2359":{"position":[[10,5]]},"2361":{"position":[[8,5]]},"2363":{"position":[[12,5]]},"2369":{"position":[[5,5]]},"2371":{"position":[[5,5]]},"2373":{"position":[[5,5]]},"2375":{"position":[[7,5]]},"2379":{"position":[[9,5]]},"2381":{"position":[[16,5]]},"2383":{"position":[[19,7]]},"2491":{"position":[[37,5]]},"2543":{"position":[[0,5]]},"2743":{"position":[[12,5]]},"2745":{"position":[[12,5]]},"2847":{"position":[[14,8]]},"3151":{"position":[[0,5]]},"3370":{"position":[[9,5]]},"3406":{"position":[[5,5]]},"3410":{"position":[[0,5]]},"3412":{"position":[[0,5]]},"3414":{"position":[[8,5]]},"3416":{"position":[[8,5]]},"3418":{"position":[[4,5]]},"3420":{"position":[[10,5]]},"3422":{"position":[[8,5]]},"3424":{"position":[[12,5]]},"3430":{"position":[[5,5]]},"3432":{"position":[[5,5]]},"3434":{"position":[[5,5]]},"3436":{"position":[[7,5]]},"3440":{"position":[[9,5]]},"3442":{"position":[[16,5]]},"3444":{"position":[[19,7]]}}}],["proxy_publish",{"_index":423,"t":{"849":{"position":[[0,13]]},"1888":{"position":[[0,13]]},"3119":{"position":[[0,13]]}}}],["proxy_sub_refresh",{"_index":597,"t":{"1890":{"position":[[0,17]]},"3121":{"position":[[0,17]]}}}],["proxy_subscrib",{"_index":422,"t":{"847":{"position":[[0,15]]},"1886":{"position":[[0,15]]},"3117":{"position":[[0,15]]}}}],["proxy_subscribe_stream",{"_index":634,"t":{"3123":{"position":[[0,22]]}}}],["public",{"_index":338,"t":{"590":{"position":[[0,11]]},"1610":{"position":[[0,12]]},"2635":{"position":[[18,12]]},"2795":{"position":[[0,12]]},"3646":{"position":[[18,12]]}}}],["publish",{"_index":163,"t":{"229":{"position":[[18,10]]},"410":{"position":[[27,9]]},"428":{"position":[[6,7]]},"496":{"position":[[3,7]]},"580":{"position":[[0,7]]},"823":{"position":[[0,7]]},"1024":{"position":[[0,7]]},"1048":{"position":[[23,7]]},"1234":{"position":[[27,9]]},"1254":{"position":[[6,7]]},"1390":{"position":[[3,7]]},"1414":{"position":[[16,8]]},"1456":{"position":[[0,7]]},"1548":{"position":[[0,7]]},"1772":{"position":[[0,7]]},"2288":{"position":[[0,7]]},"2361":{"position":[[0,7]]},"2387":{"position":[[20,8]]},"2483":{"position":[[27,9]]},"2503":{"position":[[6,7]]},"2561":{"position":[[3,7]]},"2753":{"position":[[0,7]]},"2931":{"position":[[0,7]]},"3422":{"position":[[0,7]]},"3448":{"position":[[20,8]]},"3501":{"position":[[0,7]]}}}],["publish_cel",{"_index":527,"t":{"1498":{"position":[[0,11]]},"2693":{"position":[[0,11]]}}}],["publish_proxy_nam",{"_index":425,"t":{"853":{"position":[[0,18]]},"1894":{"position":[[0,18]]},"3127":{"position":[[0,18]]}}}],["pubnub",{"_index":622,"t":{"2417":{"position":[[28,6]]}}}],["push",{"_index":159,"t":{"225":{"position":[[0,4]]},"259":{"position":[[14,4]]},"261":{"position":[[0,4]]},"273":{"position":[[0,4]]},"275":{"position":[[0,4]]},"424":{"position":[[23,4]]},"1250":{"position":[[23,4]]},"2151":{"position":[[13,6]]},"2499":{"position":[[23,4]]},"3380":{"position":[[13,6]]}}}],["pusher",{"_index":620,"t":{"2417":{"position":[[14,7]]}}}],["python",{"_index":524,"t":{"1486":{"position":[[17,6]]},"2318":{"position":[[17,6]]},"3531":{"position":[[17,6]]}}}],["queri",{"_index":370,"t":{"677":{"position":[[0,5]]},"1614":{"position":[[0,5]]},"2799":{"position":[[0,5]]}}}],["question",{"_index":296,"t":{"442":{"position":[[33,8]]},"1272":{"position":[[33,8]]},"2521":{"position":[[33,8]]}}}],["queu",{"_index":554,"t":{"1679":{"position":[[10,7]]},"2843":{"position":[[10,7]]}}}],["queue",{"_index":160,"t":{"225":{"position":[[23,5]]},"1699":{"position":[[18,5]]},"2863":{"position":[[18,5]]}}}],["quic",{"_index":79,"t":{"101":{"position":[[10,4]]},"105":{"position":[[7,4]]}}}],["rate",{"_index":632,"t":{"2829":{"position":[[25,4]]},"2831":{"position":[[19,4]]},"2833":{"position":[[15,4]]}}}],["rate_limit",{"_index":629,"t":{"2717":{"position":[[0,10]]},"2719":{"position":[[0,10]]}}}],["react",{"_index":153,"t":{"215":{"position":[[0,5]]}}}],["read",{"_index":316,"t":{"504":{"position":[[3,4]]},"1398":{"position":[[3,4]]},"1724":{"position":[[8,7]]},"2569":{"position":[[3,4]]},"2890":{"position":[[8,7]]}}}],["readi",{"_index":248,"t":{"388":{"position":[[0,5]]},"1346":{"position":[[0,5]]},"2461":{"position":[[0,5]]}}}],["real",{"_index":27,"t":{"31":{"position":[[15,4]]},"251":{"position":[[5,4]]},"368":{"position":[[11,4]]},"1326":{"position":[[11,4]]},"2441":{"position":[[11,4]]}}}],["reason",{"_index":520,"t":{"1430":{"position":[[20,6]]}}}],["receiv",{"_index":263,"t":{"410":{"position":[[42,9]]},"1234":{"position":[[42,9]]},"2483":{"position":[[42,9]]}}}],["reconnect",{"_index":14,"t":{"17":{"position":[[8,9]]},"450":{"position":[[0,12]]},"749":{"position":[[6,9]]},"751":{"position":[[9,9]]},"1302":{"position":[[0,12]]},"2589":{"position":[[0,12]]}}}],["recov",{"_index":419,"t":{"841":{"position":[[0,7]]}}}],["recoveri",{"_index":339,"t":{"590":{"position":[[22,8]]},"972":{"position":[[18,8]]},"1428":{"position":[[8,8]]},"1780":{"position":[[0,8]]},"1982":{"position":[[18,8]]},"2637":{"position":[[13,8]]},"2939":{"position":[[0,8]]},"3141":{"position":[[18,8]]},"3648":{"position":[[13,8]]}}}],["redesign",{"_index":99,"t":{"127":{"position":[[0,10]]}}}],["redi",{"_index":32,"t":{"35":{"position":[[0,5]]},"225":{"position":[[17,5]]},"562":{"position":[[0,5]]},"611":{"position":[[0,5]]},"613":{"position":[[0,5]]},"631":{"position":[[0,5]]},"651":{"position":[[0,5]]},"1186":{"position":[[0,5]]},"1188":{"position":[[0,5]]},"1190":{"position":[[13,5]]},"1192":{"position":[[0,5]]},"1196":{"position":[[0,5]]},"1198":{"position":[[0,5]]},"1630":{"position":[[0,5]]},"1646":{"position":[[0,5]]},"1669":{"position":[[0,5]]},"2083":{"position":[[0,5]]},"2085":{"position":[[0,5]]},"2087":{"position":[[12,5]]},"2089":{"position":[[13,5]]},"2091":{"position":[[0,5]]},"2093":{"position":[[0,5]]},"2097":{"position":[[0,5]]},"2099":{"position":[[0,5]]},"2103":{"position":[[6,5]]},"2419":{"position":[[14,5]]},"2817":{"position":[[0,5]]},"2833":{"position":[[0,5]]},"2898":{"position":[[0,5]]},"3268":{"position":[[0,5]]},"3270":{"position":[[0,5]]},"3272":{"position":[[12,5]]},"3274":{"position":[[13,5]]},"3276":{"position":[[0,5]]},"3278":{"position":[[0,5]]},"3282":{"position":[[0,5]]},"3284":{"position":[[0,5]]},"3286":{"position":[[6,5]]}}}],["redigo",{"_index":302,"t":{"468":{"position":[[0,6]]},"470":{"position":[[0,6]]}}}],["redis/redi",{"_index":306,"t":{"474":{"position":[[3,11]]}}}],["reduc",{"_index":309,"t":{"478":{"position":[[22,8]]}}}],["refactor",{"_index":206,"t":{"316":{"position":[[17,11]]}}}],["refresh",{"_index":467,"t":{"1018":{"position":[[0,7]]},"1046":{"position":[[21,7]]},"1412":{"position":[[0,7]]},"1466":{"position":[[0,7]]},"2298":{"position":[[0,7]]},"2355":{"position":[[0,7]]},"2363":{"position":[[4,7]]},"2385":{"position":[[21,7]]},"2387":{"position":[[33,7]]},"3416":{"position":[[0,7]]},"3424":{"position":[[4,7]]},"3446":{"position":[[21,7]]},"3448":{"position":[[33,7]]},"3511":{"position":[[0,7]]}}}],["regex",{"_index":543,"t":{"1578":{"position":[[9,5]]},"2673":{"position":[[9,5]]}}}],["relat",{"_index":612,"t":{"2171":{"position":[[11,7]]},"2173":{"position":[[25,7]]},"3400":{"position":[[11,7]]},"3402":{"position":[[25,7]]}}}],["releas",{"_index":318,"t":{"508":{"position":[[24,7]]},"538":{"position":[[7,7]]},"1436":{"position":[[24,7]]},"1554":{"position":[[7,7]]},"2605":{"position":[[24,7]]},"2723":{"position":[[7,7]]}}}],["remov",{"_index":342,"t":{"592":{"position":[[15,7]]},"599":{"position":[[24,7]]}}}],["repli",{"_index":610,"t":{"2149":{"position":[[8,5]]},"3378":{"position":[[8,5]]}}}],["request",{"_index":350,"t":{"601":{"position":[[9,7]]},"713":{"position":[[4,7]]},"721":{"position":[[9,8]]},"733":{"position":[[4,7]]},"763":{"position":[[4,7]]},"1010":{"position":[[5,7]]},"1948":{"position":[[4,7]]},"1956":{"position":[[9,8]]},"2347":{"position":[[5,7]]},"2717":{"position":[[11,7]]},"2993":{"position":[[4,7]]},"3001":{"position":[[9,8]]},"3408":{"position":[[5,7]]}}}],["result",{"_index":630,"t":{"2719":{"position":[[11,6]]}}}],["retriev",{"_index":607,"t":{"2119":{"position":[[0,10]]},"3342":{"position":[[0,10]]}}}],["return",{"_index":468,"t":{"1026":{"position":[[0,6]]},"1028":{"position":[[0,6]]},"2365":{"position":[[0,6]]},"2367":{"position":[[0,6]]},"3426":{"position":[[0,6]]},"3428":{"position":[[0,6]]}}}],["revers",{"_index":272,"t":{"418":{"position":[[29,7]]},"1242":{"position":[[29,7]]},"2491":{"position":[[29,7]]}}}],["revis",{"_index":106,"t":{"131":{"position":[[24,7]]}}}],["revok",{"_index":360,"t":{"635":{"position":[[0,6]]},"1574":{"position":[[0,8]]},"1588":{"position":[[0,8]]},"1634":{"position":[[0,6]]},"2669":{"position":[[0,8]]},"2683":{"position":[[0,8]]},"2821":{"position":[[0,6]]}}}],["revoke_token",{"_index":545,"t":{"1635":{"position":[[0,12]]},"2822":{"position":[[0,12]]}}}],["room",{"_index":132,"t":{"173":{"position":[[0,4]]},"193":{"position":[[8,4]]},"374":{"position":[[8,6]]},"1332":{"position":[[8,6]]},"2447":{"position":[[8,6]]}}}],["rpc",{"_index":55,"t":{"69":{"position":[[0,3]]},"378":{"position":[[0,3]]},"458":{"position":[[5,3]]},"685":{"position":[[26,3]]},"1020":{"position":[[0,3]]},"1050":{"position":[[9,3]]},"1310":{"position":[[5,3]]},"1336":{"position":[[0,3]]},"1414":{"position":[[0,3]]},"1728":{"position":[[26,3]]},"2357":{"position":[[0,3]]},"2389":{"position":[[9,3]]},"2451":{"position":[[0,3]]},"2597":{"position":[[5,3]]},"2653":{"position":[[0,3]]},"2917":{"position":[[26,3]]},"3418":{"position":[[0,3]]},"3450":{"position":[[9,3]]},"3664":{"position":[[0,3]]}}}],["rpm",{"_index":322,"t":{"516":{"position":[[0,3]]},"1444":{"position":[[0,3]]},"2613":{"position":[[0,3]]}}}],["rueidi",{"_index":307,"t":{"476":{"position":[[0,7]]},"478":{"position":[[13,8]]}}}],["rule",{"_index":410,"t":{"813":{"position":[[13,5]]},"1036":{"position":[[13,5]]},"1832":{"position":[[13,5]]},"2375":{"position":[[13,5]]},"3061":{"position":[[13,5]]},"3436":{"position":[[13,5]]}}}],["run",{"_index":77,"t":{"99":{"position":[[0,3]]},"221":{"position":[[12,3]]}}}],["sandbox",{"_index":331,"t":{"554":{"position":[[0,7]]},"1528":{"position":[[16,7]]},"2763":{"position":[[16,7]]}}}],["save",{"_index":357,"t":{"623":{"position":[[0,4]]},"1622":{"position":[[0,4]]},"2809":{"position":[[0,4]]}}}],["scalabl",{"_index":12,"t":{"15":{"position":[[19,11]]},"77":{"position":[[0,11]]},"356":{"position":[[0,11]]},"364":{"position":[[9,11]]},"440":{"position":[[4,8]]},"1266":{"position":[[4,8]]},"1288":{"position":[[0,11]]},"1322":{"position":[[9,11]]},"2411":{"position":[[0,11]]},"2437":{"position":[[9,11]]},"2515":{"position":[[4,8]]},"3362":{"position":[[0,11]]}}}],["scale",{"_index":255,"t":{"400":{"position":[[15,5]]},"502":{"position":[[3,5]]},"1190":{"position":[[0,7]]},"1224":{"position":[[15,5]]},"1396":{"position":[[3,5]]},"2089":{"position":[[0,7]]},"2473":{"position":[[15,5]]},"2567":{"position":[[3,5]]},"3274":{"position":[[0,7]]}}}],["schema",{"_index":609,"t":{"2147":{"position":[[9,6]]},"3376":{"position":[[9,6]]}}}],["sdk",{"_index":93,"t":{"121":{"position":[[15,3]]},"314":{"position":[[31,4]]},"1358":{"position":[[7,3]]},"2165":{"position":[[15,4]]},"2167":{"position":[[29,4]]},"2169":{"position":[[0,3]]},"2529":{"position":[[7,3]]},"2573":{"position":[[7,3]]},"2659":{"position":[[0,3]]},"3394":{"position":[[15,4]]},"3396":{"position":[[29,4]]},"3398":{"position":[[0,3]]},"3670":{"position":[[0,3]]}}}],["secur",{"_index":102,"t":{"129":{"position":[[0,6]]},"271":{"position":[[0,6]]},"430":{"position":[[16,6]]},"1256":{"position":[[16,6]]},"1681":{"position":[[8,6]]},"2505":{"position":[[16,6]]},"2845":{"position":[[8,6]]}}}],["self",{"_index":73,"t":{"97":{"position":[[9,4]]}}}],["send",{"_index":6,"t":{"11":{"position":[[0,7]]},"251":{"position":[[0,4]]},"269":{"position":[[7,4]]},"424":{"position":[[18,4]]},"458":{"position":[[0,4]]},"1250":{"position":[[18,4]]},"1268":{"position":[[7,4]]},"1310":{"position":[[0,4]]},"2499":{"position":[[18,4]]},"2517":{"position":[[7,4]]},"2597":{"position":[[0,4]]}}}],["send_push_notif",{"_index":572,"t":{"1718":{"position":[[0,22]]},"2882":{"position":[[0,22]]}}}],["sentinel",{"_index":500,"t":{"1192":{"position":[[6,8]]},"1194":{"position":[[19,8]]},"2091":{"position":[[6,8]]},"2093":{"position":[[6,8]]},"2095":{"position":[[19,8]]},"3276":{"position":[[6,8]]},"3278":{"position":[[6,8]]},"3280":{"position":[[19,8]]}}}],["separ",{"_index":214,"t":{"326":{"position":[[0,8]]},"978":{"position":[[0,8]]},"2071":{"position":[[0,8]]},"2973":{"position":[[0,8]]},"3308":{"position":[[0,8]]}}}],["seq/gen",{"_index":340,"t":{"592":{"position":[[0,7]]}}}],["server",{"_index":1,"t":{"5":{"position":[[10,6]]},"41":{"position":[[0,6]]},"71":{"position":[[0,6]]},"101":{"position":[[15,6]]},"103":{"position":[[0,6]]},"113":{"position":[[5,6]]},"195":{"position":[[20,6]]},"265":{"position":[[0,6]]},"294":{"position":[[5,6]]},"296":{"position":[[5,6]]},"324":{"position":[[18,6]]},"446":{"position":[[16,6]]},"448":{"position":[[21,6]]},"450":{"position":[[18,6]]},"456":{"position":[[0,6]]},"594":{"position":[[0,6]]},"687":{"position":[[19,6]]},"689":{"position":[[16,6]]},"691":{"position":[[19,6]]},"735":{"position":[[0,6]]},"755":{"position":[[0,6]]},"1066":{"position":[[8,6]]},"1298":{"position":[[16,6]]},"1300":{"position":[[21,6]]},"1302":{"position":[[18,6]]},"1308":{"position":[[0,6]]},"1376":{"position":[[0,6]]},"1416":{"position":[[13,6]]},"1426":{"position":[[0,6]]},"1730":{"position":[[19,6]]},"1732":{"position":[[16,6]]},"1734":{"position":[[19,6]]},"1928":{"position":[[11,6]]},"2129":{"position":[[8,6]]},"2547":{"position":[[0,6]]},"2585":{"position":[[16,6]]},"2587":{"position":[[21,6]]},"2589":{"position":[[18,6]]},"2595":{"position":[[0,6]]},"2645":{"position":[[0,6]]},"2919":{"position":[[19,6]]},"2921":{"position":[[16,6]]},"2923":{"position":[[19,6]]},"3155":{"position":[[11,6]]},"3237":{"position":[[15,6]]},"3332":{"position":[[8,6]]},"3656":{"position":[[0,6]]}}}],["session",{"_index":83,"t":{"107":{"position":[[11,7]]},"1076":{"position":[[7,8]]},"2196":{"position":[[7,8]]},"3471":{"position":[[7,8]]}}}],["set",{"_index":126,"t":{"163":{"position":[[12,8]]},"548":{"position":[[0,7]]},"940":{"position":[[0,7]]},"942":{"position":[[0,7]]},"1564":{"position":[[0,7]]},"2061":{"position":[[0,7]]},"2063":{"position":[[0,7]]},"2733":{"position":[[0,7]]},"3256":{"position":[[0,7]]},"3258":{"position":[[0,7]]}}}],["setup",{"_index":122,"t":{"159":{"position":[[0,5]]},"286":{"position":[[0,5]]}}}],["shard",{"_index":504,"t":{"1196":{"position":[[6,8]]},"2097":{"position":[[6,8]]},"3282":{"position":[[6,8]]}}}],["shut",{"_index":625,"t":{"2581":{"position":[[0,8]]}}}],["shutdown",{"_index":388,"t":{"729":{"position":[[0,8]]}}}],["side",{"_index":56,"t":{"71":{"position":[[7,4]]},"175":{"position":[[7,4]]},"294":{"position":[[12,4]]},"296":{"position":[[12,4]]},"438":{"position":[[65,5]]},"456":{"position":[[7,4]]},"586":{"position":[[7,4]]},"594":{"position":[[7,4]]},"685":{"position":[[7,4]]},"1066":{"position":[[15,4]]},"1264":{"position":[[65,5]]},"1308":{"position":[[7,4]]},"1426":{"position":[[7,4]]},"1728":{"position":[[7,4]]},"2119":{"position":[[34,4]]},"2129":{"position":[[15,4]]},"2173":{"position":[[7,4]]},"2513":{"position":[[65,5]]},"2595":{"position":[[7,4]]},"2645":{"position":[[7,4]]},"2917":{"position":[[7,4]]},"3332":{"position":[[15,4]]},"3342":{"position":[[34,4]]},"3402":{"position":[[7,4]]},"3656":{"position":[[7,4]]}}}],["sign",{"_index":74,"t":{"97":{"position":[[14,6]]}}}],["signal",{"_index":448,"t":{"931":{"position":[[0,6]]},"2052":{"position":[[0,6]]},"3245":{"position":[[0,6]]}}}],["signatur",{"_index":637,"t":{"3250":{"position":[[21,9]]}}}],["simpl",{"_index":233,"t":{"360":{"position":[[0,6]]},"1318":{"position":[[0,6]]},"2433":{"position":[[0,6]]}}}],["simplest",{"_index":405,"t":{"801":{"position":[[0,8]]},"1816":{"position":[[0,8]]},"3045":{"position":[[0,8]]}}}],["simplifi",{"_index":219,"t":{"330":{"position":[[0,11]]}}}],["singl",{"_index":276,"t":{"422":{"position":[[24,6]]},"1070":{"position":[[9,6]]},"1248":{"position":[[24,6]]},"2133":{"position":[[9,6]]},"2497":{"position":[[24,6]]},"3336":{"position":[[9,6]]}}}],["site",{"_index":44,"t":{"47":{"position":[[18,4]]},"980":{"position":[[27,4]]},"2073":{"position":[[27,4]]},"2581":{"position":[[32,4]]},"3310":{"position":[[27,4]]}}}],["skeleton",{"_index":80,"t":{"103":{"position":[[7,8]]}}}],["slow",{"_index":390,"t":{"743":{"position":[[0,4]]}}}],["socket",{"_index":452,"t":{"952":{"position":[[0,7]]},"1922":{"position":[[0,7]]},"3149":{"position":[[0,7]]}}}],["socket.io",{"_index":619,"t":{"2415":{"position":[[14,9]]}}}],["sockj",{"_index":223,"t":{"332":{"position":[[14,6]]},"615":{"position":[[0,6]]},"1074":{"position":[[0,6]]},"1083":{"position":[[0,6]]},"1362":{"position":[[0,6]]},"2194":{"position":[[0,6]]},"2203":{"position":[[0,6]]},"2533":{"position":[[0,6]]},"3469":{"position":[[0,6]]},"3478":{"position":[[0,6]]}}}],["sockjs_url",{"_index":478,"t":{"1085":{"position":[[0,10]]},"2205":{"position":[[0,10]]},"3480":{"position":[[0,10]]}}}],["sourc",{"_index":147,"t":{"203":{"position":[[9,6]]},"390":{"position":[[5,6]]},"520":{"position":[[11,6]]},"873":{"position":[[14,7]]},"1348":{"position":[[5,6]]},"1448":{"position":[[11,6]]},"1986":{"position":[[14,7]]},"2463":{"position":[[5,6]]},"2617":{"position":[[11,6]]},"3175":{"position":[[14,7]]}}}],["spec",{"_index":211,"t":{"322":{"position":[[8,4]]}}}],["special",{"_index":117,"t":{"151":{"position":[[0,7]]}}}],["sse",{"_index":616,"t":{"2210":{"position":[[0,3]]},"3485":{"position":[[0,3]]}}}],["sse_max_request_body_s",{"_index":617,"t":{"2212":{"position":[[0,25]]},"3487":{"position":[[0,25]]}}}],["sss",{"_index":519,"t":{"1426":{"position":[[26,5]]}}}],["stale",{"_index":389,"t":{"741":{"position":[[0,5]]}}}],["start",{"_index":47,"t":{"57":{"position":[[13,7]]},"159":{"position":[[10,5]]},"195":{"position":[[0,8]]},"219":{"position":[[0,5]]},"247":{"position":[[0,8]]}}}],["stat",{"_index":60,"t":{"75":{"position":[[29,5]]},"462":{"position":[[22,5]]},"944":{"position":[[16,5]]},"1314":{"position":[[22,5]]},"2065":{"position":[[16,5]]},"2601":{"position":[[22,5]]},"2657":{"position":[[22,5]]},"3260":{"position":[[16,5]]},"3668":{"position":[[22,5]]}}}],["state",{"_index":392,"t":{"747":{"position":[[13,5]]},"952":{"position":[[21,5]]},"1922":{"position":[[21,5]]},"2621":{"position":[[18,6]]},"2631":{"position":[[13,6]]},"2637":{"position":[[22,5]]},"3149":{"position":[[21,5]]},"3632":{"position":[[18,6]]},"3642":{"position":[[13,6]]},"3648":{"position":[[22,5]]}}}],["statu",{"_index":267,"t":{"414":{"position":[[39,6]]},"685":{"position":[[12,6]]},"1238":{"position":[[39,6]]},"1728":{"position":[[12,6]]},"2487":{"position":[[39,6]]},"2917":{"position":[[12,6]]}}}],["step",{"_index":558,"t":{"1687":{"position":[[0,5]]},"2851":{"position":[[0,5]]}}}],["sticki",{"_index":477,"t":{"1076":{"position":[[0,6]]},"2196":{"position":[[0,6]]},"3471":{"position":[[0,6]]}}}],["stop",{"_index":269,"t":{"416":{"position":[[11,5]]},"1240":{"position":[[11,5]]},"2489":{"position":[[11,5]]}}}],["storag",{"_index":552,"t":{"1677":{"position":[[0,7]]},"2841":{"position":[[0,7]]}}}],["stream",{"_index":16,"t":{"19":{"position":[[14,6]]},"35":{"position":[[6,7]]},"111":{"position":[[33,7]]},"576":{"position":[[27,6]]},"613":{"position":[[6,7]]},"1060":{"position":[[28,6]]},"1544":{"position":[[27,6]]},"2143":{"position":[[28,6]]},"2749":{"position":[[27,6]]},"3358":{"position":[[28,6]]},"3366":{"position":[[28,7]]},"3368":{"position":[[27,7]]}}}],["strict",{"_index":236,"t":{"366":{"position":[[0,6]]},"1324":{"position":[[0,6]]},"2439":{"position":[[0,6]]}}}],["string",{"_index":580,"t":{"1840":{"position":[[18,6]]},"3069":{"position":[[18,6]]}}}],["structur",{"_index":124,"t":{"161":{"position":[[12,9]]},"1010":{"position":[[13,9]]},"2347":{"position":[[13,9]]},"3408":{"position":[[13,9]]}}}],["sub",{"_index":395,"t":{"773":{"position":[[0,3]]},"791":{"position":[[0,4]]},"1742":{"position":[[0,3]]},"1788":{"position":[[0,3]]},"1806":{"position":[[0,4]]},"2363":{"position":[[0,3]]},"2387":{"position":[[29,3]]},"2947":{"position":[[0,3]]},"3017":{"position":[[0,3]]},"3035":{"position":[[0,4]]},"3424":{"position":[[0,3]]},"3448":{"position":[[29,3]]}}}],["sub_refresh_proxy_nam",{"_index":598,"t":{"1896":{"position":[[0,22]]},"3129":{"position":[[0,22]]}}}],["subscrib",{"_index":188,"t":{"292":{"position":[[5,9]]},"454":{"position":[[0,9]]},"494":{"position":[[3,9]]},"709":{"position":[[8,10]]},"1022":{"position":[[0,9]]},"1048":{"position":[[9,9]]},"1268":{"position":[[36,11]]},"1306":{"position":[[0,9]]},"1388":{"position":[[3,9]]},"1408":{"position":[[0,9]]},"1460":{"position":[[0,9]]},"1770":{"position":[[0,9]]},"1944":{"position":[[8,10]]},"2292":{"position":[[0,9]]},"2359":{"position":[[0,9]]},"2387":{"position":[[9,10]]},"2517":{"position":[[36,11]]},"2559":{"position":[[3,9]]},"2593":{"position":[[0,9]]},"2929":{"position":[[0,9]]},"2989":{"position":[[8,10]]},"3420":{"position":[[0,9]]},"3448":{"position":[[9,10]]},"3505":{"position":[[0,9]]}}}],["subscribe_cel",{"_index":525,"t":{"1494":{"position":[[0,13]]},"2689":{"position":[[0,13]]}}}],["subscribe_proxy_nam",{"_index":424,"t":{"851":{"position":[[0,20]]},"1892":{"position":[[0,20]]},"3125":{"position":[[0,20]]}}}],["subscribe_stream_proxy_nam",{"_index":635,"t":{"3131":{"position":[[0,27]]}}}],["subscribe_to_publish",{"_index":413,"t":{"825":{"position":[[0,20]]}}}],["subscriberequest",{"_index":489,"t":{"1135":{"position":[[0,16]]},"2248":{"position":[[0,16]]},"3598":{"position":[[0,16]]}}}],["subscript",{"_index":52,"t":{"65":{"position":[[8,13]]},"71":{"position":[[12,13]]},"133":{"position":[[11,13]]},"298":{"position":[[32,12]]},"326":{"position":[[20,12]]},"376":{"position":[[19,13]]},"456":{"position":[[12,13]]},"739":{"position":[[0,12]]},"1066":{"position":[[20,13]]},"1068":{"position":[[27,12]]},"1308":{"position":[[12,13]]},"1334":{"position":[[19,13]]},"1366":{"position":[[0,12]]},"1426":{"position":[[12,13]]},"1584":{"position":[[0,12]]},"1588":{"position":[[9,12]]},"1606":{"position":[[0,13]]},"1740":{"position":[[0,12]]},"2129":{"position":[[20,13]]},"2131":{"position":[[27,12]]},"2173":{"position":[[12,12]]},"2449":{"position":[[19,13]]},"2537":{"position":[[0,12]]},"2595":{"position":[[12,13]]},"2631":{"position":[[0,12]]},"2633":{"position":[[0,12]]},"2637":{"position":[[0,12]]},"2639":{"position":[[0,12]]},"2641":{"position":[[0,12]]},"2643":{"position":[[0,12]]},"2645":{"position":[[12,13]]},"2679":{"position":[[0,12]]},"2683":{"position":[[9,12]]},"2791":{"position":[[0,13]]},"2945":{"position":[[0,12]]},"2973":{"position":[[9,12]]},"3332":{"position":[[20,13]]},"3334":{"position":[[27,12]]},"3366":{"position":[[15,12]]},"3368":{"position":[[14,12]]},"3402":{"position":[[12,12]]},"3642":{"position":[[0,12]]},"3644":{"position":[[0,12]]},"3648":{"position":[[0,12]]},"3650":{"position":[[0,12]]},"3652":{"position":[[0,12]]},"3654":{"position":[[0,12]]},"3656":{"position":[[12,13]]}}}],["support",{"_index":287,"t":{"434":{"position":[[16,7]]},"809":{"position":[[13,7]]},"1099":{"position":[[0,9]]},"1112":{"position":[[0,9]]},"1137":{"position":[[0,9]]},"1167":{"position":[[0,9]]},"1260":{"position":[[16,7]]},"1270":{"position":[[16,7]]},"1824":{"position":[[13,7]]},"2218":{"position":[[0,9]]},"2235":{"position":[[0,9]]},"2250":{"position":[[0,9]]},"2263":{"position":[[0,9]]},"2509":{"position":[[16,7]]},"2519":{"position":[[16,7]]},"3053":{"position":[[13,7]]},"3545":{"position":[[0,9]]},"3568":{"position":[[0,9]]},"3585":{"position":[[0,9]]},"3600":{"position":[[0,9]]}}}],["swagger",{"_index":212,"t":{"322":{"position":[[17,7]]},"3237":{"position":[[0,7]]}}}],["switch",{"_index":308,"t":{"478":{"position":[[0,9]]}}}],["tab",{"_index":278,"t":{"422":{"position":[[79,5]]},"1248":{"position":[[79,5]]},"2497":{"position":[[79,5]]}}}],["tabl",{"_index":368,"t":{"673":{"position":[[12,5]]},"675":{"position":[[11,5]]},"956":{"position":[[10,5]]},"1604":{"position":[[12,5]]},"1606":{"position":[[14,5]]},"1608":{"position":[[11,5]]},"1610":{"position":[[13,5]]},"1612":{"position":[[14,5]]},"1926":{"position":[[10,5]]},"2789":{"position":[[12,5]]},"2791":{"position":[[14,5]]},"2793":{"position":[[11,5]]},"2795":{"position":[[13,5]]},"2797":{"position":[[14,5]]},"3153":{"position":[[10,5]]}}}],["tarantool",{"_index":34,"t":{"37":{"position":[[0,9]]},"1202":{"position":[[0,9]]},"1204":{"position":[[0,9]]},"2105":{"position":[[0,9]]},"2107":{"position":[[0,9]]},"3288":{"position":[[0,9]]},"3290":{"position":[[0,9]]}}}],["task",{"_index":2,"t":{"5":{"position":[[17,5]]}}}],["teardown",{"_index":197,"t":{"304":{"position":[[0,8]]}}}],["termin",{"_index":601,"t":{"1964":{"position":[[4,8]]},"1966":{"position":[[0,8]]},"3009":{"position":[[4,8]]},"3011":{"position":[[0,8]]}}}],["thank",{"_index":118,"t":{"151":{"position":[[8,6]]}}}],["throttl",{"_index":334,"t":{"562":{"position":[[6,10]]},"1665":{"position":[[25,10]]},"1667":{"position":[[19,10]]},"1669":{"position":[[15,10]]}}}],["throughput",{"_index":166,"t":{"235":{"position":[[0,10]]}}}],["time",{"_index":28,"t":{"31":{"position":[[20,4]]},"251":{"position":[[10,4]]},"368":{"position":[[16,4]]},"595":{"position":[[0,4]]},"940":{"position":[[8,4]]},"1326":{"position":[[16,4]]},"2061":{"position":[[8,4]]},"2441":{"position":[[16,4]]},"3256":{"position":[[8,4]]}}}],["time_wait",{"_index":453,"t":{"952":{"position":[[11,9]]},"1922":{"position":[[11,9]]},"3149":{"position":[[11,9]]}}}],["tl",{"_index":75,"t":{"97":{"position":[[21,3]]},"1058":{"position":[[0,3]]},"1060":{"position":[[0,3]]},"2087":{"position":[[18,3]]},"2093":{"position":[[15,3]]},"2141":{"position":[[0,3]]},"2143":{"position":[[0,3]]},"3272":{"position":[[18,3]]},"3278":{"position":[[15,3]]},"3356":{"position":[[0,3]]},"3358":{"position":[[0,3]]}}}],["tldr",{"_index":151,"t":{"209":{"position":[[0,4]]}}}],["token",{"_index":178,"t":{"267":{"position":[[12,6]]},"290":{"position":[[13,5]]},"314":{"position":[[0,5]]},"326":{"position":[[33,5]]},"635":{"position":[[7,5]]},"637":{"position":[[16,6]]},"717":{"position":[[0,5]]},"731":{"position":[[8,5]]},"801":{"position":[[9,5]]},"803":{"position":[[0,5]]},"805":{"position":[[0,5]]},"1366":{"position":[[13,5]]},"1634":{"position":[[7,5]]},"1637":{"position":[[16,6]]},"1677":{"position":[[12,6]]},"1816":{"position":[[9,5]]},"1818":{"position":[[0,5]]},"1820":{"position":[[0,5]]},"1952":{"position":[[0,5]]},"2537":{"position":[[13,5]]},"2573":{"position":[[11,5]]},"2627":{"position":[[18,5]]},"2643":{"position":[[13,5]]},"2821":{"position":[[7,5]]},"2824":{"position":[[16,6]]},"2841":{"position":[[12,6]]},"2973":{"position":[[22,5]]},"2997":{"position":[[0,5]]},"3045":{"position":[[9,5]]},"3047":{"position":[[0,5]]},"3049":{"position":[[0,5]]},"3250":{"position":[[15,5]]},"3638":{"position":[[18,5]]},"3654":{"position":[[13,5]]}}}],["toml",{"_index":432,"t":{"885":{"position":[[0,4]]},"1998":{"position":[[0,4]]},"3187":{"position":[[0,4]]}}}],["top",{"_index":513,"t":{"1404":{"position":[[0,3]]},"2153":{"position":[[0,3]]},"3382":{"position":[[0,3]]}}}],["topic",{"_index":179,"t":{"271":{"position":[[15,6]]},"1681":{"position":[[15,6]]},"2845":{"position":[[15,6]]}}}],["trace",{"_index":638,"t":{"3325":{"position":[[0,6]]}}}],["transport",{"_index":10,"t":{"13":{"position":[[19,9]]},"31":{"position":[[25,10]]},"368":{"position":[[21,10]]},"1326":{"position":[[21,10]]},"1360":{"position":[[15,9]]},"2441":{"position":[[21,10]]},"2531":{"position":[[15,9]]},"3537":{"position":[[0,9]]}}}],["tri",{"_index":535,"t":{"1528":{"position":[[0,3]]},"2763":{"position":[[0,3]]}}}],["tune",{"_index":5,"t":{"9":{"position":[[3,6]]}}}],["tutori",{"_index":146,"t":{"203":{"position":[[0,8]]},"1190":{"position":[[19,8]]},"1724":{"position":[[20,9]]},"2089":{"position":[[19,8]]},"2890":{"position":[[20,9]]},"3274":{"position":[[19,8]]}}}],["two",{"_index":283,"t":{"430":{"position":[[35,3]]},"1256":{"position":[[35,3]]},"2505":{"position":[[35,3]]}}}],["type",{"_index":241,"t":{"376":{"position":[[10,5]]},"1093":{"position":[[23,5]]},"1334":{"position":[[10,5]]},"1580":{"position":[[19,5]]},"2188":{"position":[[23,5]]},"2449":{"position":[[10,5]]},"2675":{"position":[[19,5]]},"3491":{"position":[[23,5]]}}}],["typescript",{"_index":110,"t":{"139":{"position":[[27,10]]}}}],["ubuntu",{"_index":329,"t":{"544":{"position":[[11,6]]},"1560":{"position":[[11,6]]},"2729":{"position":[[11,6]]}}}],["ui",{"_index":181,"t":{"275":{"position":[[19,2]]},"322":{"position":[[25,2]]},"384":{"position":[[19,2]]},"1342":{"position":[[19,2]]},"2457":{"position":[[19,2]]},"3237":{"position":[[8,2]]}}}],["unauthor",{"_index":377,"t":{"701":{"position":[[0,12]]},"1936":{"position":[[0,12]]},"2981":{"position":[[0,12]]}}}],["unblock",{"_index":364,"t":{"657":{"position":[[0,7]]}}}],["unblock_us",{"_index":548,"t":{"1653":{"position":[[0,12]]},"2905":{"position":[[0,12]]}}}],["uni_grpc",{"_index":482,"t":{"1115":{"position":[[0,8]]},"2266":{"position":[[0,8]]},"3548":{"position":[[0,8]]}}}],["uni_grpc_address",{"_index":484,"t":{"1119":{"position":[[0,16]]},"2270":{"position":[[0,16]]},"3552":{"position":[[0,16]]}}}],["uni_grpc_max_receive_message_s",{"_index":485,"t":{"1121":{"position":[[0,33]]},"2272":{"position":[[0,33]]},"3554":{"position":[[0,33]]}}}],["uni_grpc_port",{"_index":483,"t":{"1117":{"position":[[0,13]]},"2268":{"position":[[0,13]]},"3550":{"position":[[0,13]]}}}],["uni_grpc_tl",{"_index":486,"t":{"1123":{"position":[[0,12]]},"2274":{"position":[[0,12]]},"3556":{"position":[[0,12]]}}}],["uni_grpc_tls_cert",{"_index":487,"t":{"1125":{"position":[[0,17]]},"2276":{"position":[[0,17]]},"3558":{"position":[[0,17]]}}}],["uni_grpc_tls_key",{"_index":488,"t":{"1127":{"position":[[0,16]]},"2278":{"position":[[0,16]]},"3560":{"position":[[0,16]]}}}],["uni_http_stream",{"_index":497,"t":{"1172":{"position":[[0,15]]},"2223":{"position":[[0,15]]},"3573":{"position":[[0,15]]}}}],["uni_http_stream_max_request_body_s",{"_index":498,"t":{"1174":{"position":[[0,37]]},"2225":{"position":[[0,37]]},"3575":{"position":[[0,37]]}}}],["uni_ss",{"_index":480,"t":{"1104":{"position":[[0,7]]},"2238":{"position":[[0,7]]},"3588":{"position":[[0,7]]}}}],["uni_sse_max_request_body_s",{"_index":481,"t":{"1106":{"position":[[0,29]]},"2240":{"position":[[0,29]]},"3590":{"position":[[0,29]]}}}],["uni_websocket",{"_index":490,"t":{"1142":{"position":[[0,13]]},"2255":{"position":[[0,13]]},"3605":{"position":[[0,13]]}}}],["uni_websocket_message_size_limit",{"_index":491,"t":{"1144":{"position":[[0,32]]},"2257":{"position":[[0,32]]},"3607":{"position":[[0,32]]}}}],["unidirect",{"_index":26,"t":{"31":{"position":[[0,14]]},"576":{"position":[[12,14]]},"1060":{"position":[[13,14]]},"1091":{"position":[[0,14]]},"1093":{"position":[[0,14]]},"1360":{"position":[[0,14]]},"1544":{"position":[[12,14]]},"2143":{"position":[[13,14]]},"2186":{"position":[[0,14]]},"2188":{"position":[[0,14]]},"2531":{"position":[[0,14]]},"2749":{"position":[[12,14]]},"3358":{"position":[[13,14]]},"3366":{"position":[[0,14]]},"3463":{"position":[[0,14]]},"3491":{"position":[[0,14]]}}}],["unifi",{"_index":92,"t":{"121":{"position":[[0,7]]},"271":{"position":[[7,7]]},"1681":{"position":[[0,7]]},"2845":{"position":[[0,7]]}}}],["unknown",{"_index":216,"t":{"328":{"position":[[0,7]]},"703":{"position":[[0,7]]},"759":{"position":[[0,7]]},"1938":{"position":[[0,7]]},"2983":{"position":[[0,7]]}}}],["unlimit",{"_index":337,"t":{"588":{"position":[[3,9]]}}}],["unrecover",{"_index":385,"t":{"723":{"position":[[0,13]]},"767":{"position":[[0,13]]},"1958":{"position":[[0,13]]},"3003":{"position":[[0,13]]}}}],["unsubscrib",{"_index":516,"t":{"1410":{"position":[[0,11]]},"1462":{"position":[[0,11]]},"2294":{"position":[[0,11]]},"2649":{"position":[[0,11]]},"3507":{"position":[[0,11]]},"3660":{"position":[[0,11]]}}}],["updat",{"_index":207,"t":{"318":{"position":[[28,6]]},"603":{"position":[[0,7]]},"685":{"position":[[19,6]]},"1728":{"position":[[19,6]]},"2917":{"position":[[19,6]]}}}],["update_push_statu",{"_index":573,"t":{"1720":{"position":[[0,18]]},"2886":{"position":[[0,18]]}}}],["update_user_statu",{"_index":373,"t":{"687":{"position":[[0,18]]},"1730":{"position":[[0,18]]},"2919":{"position":[[0,18]]}}}],["us",{"_index":270,"t":{"418":{"position":[[6,3]]},"422":{"position":[[18,3]]},"613":{"position":[[14,4]]},"665":{"position":[[0,5]]},"1054":{"position":[[0,5]]},"1176":{"position":[[11,5]]},"1242":{"position":[[6,3]]},"1248":{"position":[[18,3]]},"1514":{"position":[[0,5]]},"1659":{"position":[[0,5]]},"1699":{"position":[[0,3]]},"2137":{"position":[[0,5]]},"2227":{"position":[[11,5]]},"2491":{"position":[[6,3]]},"2497":{"position":[[18,3]]},"2773":{"position":[[0,5]]},"2863":{"position":[[0,3]]},"2911":{"position":[[0,5]]},"3352":{"position":[[0,5]]},"3577":{"position":[[11,5]]}}}],["usag",{"_index":228,"t":{"344":{"position":[[10,5]]},"398":{"position":[[7,5]]},"478":{"position":[[35,5]]},"944":{"position":[[10,5]]},"1222":{"position":[[7,5]]},"1276":{"position":[[10,5]]},"2065":{"position":[[10,5]]},"2399":{"position":[[10,5]]},"2471":{"position":[[7,5]]},"3260":{"position":[[10,5]]}}}],["user",{"_index":184,"t":{"288":{"position":[[5,4]]},"430":{"position":[[39,5]]},"637":{"position":[[11,4]]},"655":{"position":[[6,4]]},"657":{"position":[[8,4]]},"819":{"position":[[0,4]]},"1070":{"position":[[16,4]]},"1256":{"position":[[39,5]]},"1368":{"position":[[0,4]]},"1637":{"position":[[11,4]]},"1667":{"position":[[14,4]]},"1669":{"position":[[10,4]]},"1836":{"position":[[0,4]]},"2133":{"position":[[16,4]]},"2505":{"position":[[39,5]]},"2539":{"position":[[0,4]]},"2824":{"position":[[11,4]]},"2831":{"position":[[14,4]]},"2833":{"position":[[10,4]]},"3065":{"position":[[0,4]]},"3336":{"position":[[16,4]]}}}],["user_topic_list",{"_index":571,"t":{"1716":{"position":[[0,15]]},"2880":{"position":[[0,15]]}}}],["user_topic_upd",{"_index":570,"t":{"1714":{"position":[[0,17]]},"2878":{"position":[[0,17]]}}}],["v2",{"_index":20,"t":{"25":{"position":[[11,2]]},"619":{"position":[[0,2]]},"2581":{"position":[[25,2]]}}}],["v3",{"_index":91,"t":{"119":{"position":[[11,2]]},"619":{"position":[[6,2]]}}}],["variabl",{"_index":431,"t":{"877":{"position":[[15,9]]},"1496":{"position":[[11,9]]},"1512":{"position":[[0,9]]},"1990":{"position":[[15,9]]},"2691":{"position":[[11,9]]},"2771":{"position":[[0,9]]},"3179":{"position":[[15,9]]}}}],["varibl",{"_index":531,"t":{"1514":{"position":[[6,8]]},"2773":{"position":[[6,8]]}}}],["varieti",{"_index":237,"t":{"368":{"position":[[0,7]]},"1326":{"position":[[0,7]]},"2441":{"position":[[0,7]]}}}],["version",{"_index":426,"t":{"861":{"position":[[0,7]]},"1902":{"position":[[0,7]]},"3159":{"position":[[0,7]]}}}],["view",{"_index":141,"t":{"191":{"position":[[14,4]]},"193":{"position":[[13,4]]}}}],["vite",{"_index":154,"t":{"215":{"position":[[15,4]]}}}],["vs",{"_index":618,"t":{"2415":{"position":[[11,2]]},"2417":{"position":[[11,2]]},"2419":{"position":[[11,2]]},"2421":{"position":[[11,2]]},"2423":{"position":[[11,2]]}}}],["warn",{"_index":218,"t":{"328":{"position":[[20,8]]}}}],["way",{"_index":261,"t":{"410":{"position":[[4,3]]},"422":{"position":[[11,3]]},"432":{"position":[[16,3]]},"1234":{"position":[[4,3]]},"1248":{"position":[[11,3]]},"1258":{"position":[[16,3]]},"2483":{"position":[[4,3]]},"2497":{"position":[[11,3]]},"2507":{"position":[[16,3]]}}}],["web",{"_index":245,"t":{"384":{"position":[[15,3]]},"424":{"position":[[55,3]]},"665":{"position":[[13,3]]},"809":{"position":[[5,3]]},"980":{"position":[[23,3]]},"1250":{"position":[[55,3]]},"1342":{"position":[[15,3]]},"1659":{"position":[[13,3]]},"1824":{"position":[[5,3]]},"2073":{"position":[[23,3]]},"2457":{"position":[[15,3]]},"2499":{"position":[[55,3]]},"2911":{"position":[[13,3]]},"3053":{"position":[[5,3]]},"3310":{"position":[[23,3]]}}}],["webhook",{"_index":288,"t":{"434":{"position":[[24,9]]},"1260":{"position":[[24,9]]},"2509":{"position":[[24,9]]}}}],["websocket",{"_index":0,"t":{"5":{"position":[[0,9]]},"7":{"position":[[0,9]]},"13":{"position":[[0,9]]},"123":{"position":[[7,9]]},"428":{"position":[[34,9]]},"1254":{"position":[[34,9]]},"2503":{"position":[[34,9]]}}}],["websocket_compress",{"_index":496,"t":{"1159":{"position":[[0,21]]},"2339":{"position":[[0,21]]},"3624":{"position":[[0,21]]}}}],["websocket_message_size_limit",{"_index":492,"t":{"1151":{"position":[[0,28]]},"2331":{"position":[[0,28]]},"3616":{"position":[[0,28]]}}}],["websocket_read_buffer_s",{"_index":493,"t":{"1153":{"position":[[0,26]]},"2333":{"position":[[0,26]]},"3618":{"position":[[0,26]]}}}],["websocket_use_write_buffer_pool",{"_index":495,"t":{"1157":{"position":[[0,31]]},"2337":{"position":[[0,31]]},"3622":{"position":[[0,31]]}}}],["websocket_write_buffer_s",{"_index":494,"t":{"1155":{"position":[[0,27]]},"2335":{"position":[[0,27]]},"3620":{"position":[[0,27]]}}}],["webtransport",{"_index":113,"t":{"143":{"position":[[19,12]]}}}],["what'",{"_index":285,"t":{"432":{"position":[[0,6]]},"1258":{"position":[[0,6]]},"2507":{"position":[[0,6]]}}}],["wildcard",{"_index":541,"t":{"1576":{"position":[[9,8]]},"2671":{"position":[[9,8]]}}}],["window",{"_index":57,"t":{"73":{"position":[[0,8]]}}}],["without",{"_index":271,"t":{"418":{"position":[[21,7]]},"1242":{"position":[[21,7]]},"2491":{"position":[[21,7]]}}}],["work",{"_index":274,"t":{"420":{"position":[[16,4]]},"532":{"position":[[7,5]]},"627":{"position":[[7,5]]},"647":{"position":[[7,5]]},"681":{"position":[[11,5]]},"1244":{"position":[[16,4]]},"1246":{"position":[[16,4]]},"1618":{"position":[[11,5]]},"1626":{"position":[[7,5]]},"1642":{"position":[[7,5]]},"2493":{"position":[[16,4]]},"2495":{"position":[[16,4]]},"2803":{"position":[[11,5]]},"2813":{"position":[[7,5]]},"2894":{"position":[[7,5]]}}}],["worker_connect",{"_index":464,"t":{"982":{"position":[[0,18]]},"2075":{"position":[[0,18]]},"3312":{"position":[[0,18]]}}}],["write",{"_index":78,"t":{"101":{"position":[[0,7]]},"745":{"position":[[0,5]]}}}],["wscat",{"_index":640,"t":{"3628":{"position":[[24,6]]}}}],["yaml",{"_index":433,"t":{"887":{"position":[[0,4]]},"2000":{"position":[[0,4]]},"3189":{"position":[[0,4]]}}}]],"pipeline":["stemmer"]}},{"documents":[{"i":2,"t":"In order to get an understanding about possible hardware requirements for reasonably massive Centrifugo setup we made a test stand inside Kubernetes. Our goal was to run server based on Centrifuge library (the core of Centrifugo server) with one million WebSocket connections and send many messages to connected clients. While sending many messages we have been looking at delivery time latency. In fact we will see that about 30 million messages per minute (500k messages per second) will be delivered to connected clients and latency won't be larger than 200ms in 99 percentile. Server nodes have been run on machines with the following configuration: CPU Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz Linux Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64 GNU/Linux Some sysctl values: fs.file-max = 3276750fs.nr_open = 1048576net.ipv4.tcp_mem = 3086496 4115330 6172992net.ipv4.tcp_rmem = 8192 8388608 16777216net.ipv4.tcp_wmem = 4096 4194394 16777216net.core.rmem_max = 33554432net.core.wmem_max = 33554432 Kubernetes used these machines as its nodes. We started 20 Centrifuge-based server pods. Our clients connected to server pods using Centrifuge Protobuf protocol. To scale horizontally we used Redis Engine and sharded it to 5 different Redis instances (each Redis instance consumes 1 CPU max). To achieve many client connections we used 100 Kubernetes pods each generating about 10k client connections to server. Here are some numbers we achieved: 1 million WebSocket connections Each connection subscribed to 2 channels: one personal channel and one group channel (with 10 subscribers in it), i.e. we had about 1.1 million active channels at each moment. 28 million messages per minute (about 500k per second) delivered to clients 200k per minute constant connect/disconnect rate to simulate real-life situation where clients connect/disconnect from server 200ms delivery latency in 99 percentile The size of each published message was about 100 bytes And here are some numbers about final resource usage on server side (we don't actually interested in client side resource usage here): 40 CPU total for server nodes when load achieved values claimed above (20 pods, ~2 CPU each) 27 GB of RAM used mostly to handle 1 mln WebSocket connections, i.e. about 30kb RAM per connection 0.32 CPU usage on every Redis instance 100 mbit/sec rx и 150 mbit/sec tx of network used on each server pod The picture that demonstrates experiment (better to open image in new tab): This also demonstrates that to handle one million of WebSocket connections without many messages sent to clients you need about 10 CPU total for server nodes and about 5% of CPU on each of Redis instances. In this case CPU mostly spent on connect/disconnect flow, ping/pong frames, subscriptions to channels. If we enable history and history message recovery features we see an increased Redis CPU usage: 64% instead of 32% on the same workload. Other resources usage is pretty the same. The results mean that one can theoretically achieve the comparable numbers on single modern server machine. But numbers can vary a lot in case of different load scenarios. In this benchmark we looked at basic use case where we only connect many clients and send Publications to them. There are many features in Centrifuge library and in Centrifugo not covered by this artificial experiment. Also note that though benchmark was made for Centrifuge library for Centrifugo you can expect similar results. Read and write buffer sizes of websocket connections were set to 512 kb on server side (sizes of buffers affect memory usage), with Centrifugo this means that to reproduce the same configuration you need to set: { ... \"websocket_read_buffer_size\": 512, \"websocket_write_buffer_size\": 512}","s":"Million connections with Centrifugo","u":"/blog/2020/02/10/million-connections-with-centrifugo","h":"","p":1},{"i":4,"t":"I believe that in 2020 WebSocket is still an entertaining technology which is not so well-known and understood like HTTP. In this blog post I'd like to tell about state of WebSocket in Go language ecosystem, and a way we could write scalable WebSocket servers with Go and beyond Go. We won't talk a lot about WebSocket transport pros and cons – I'll provide links to other resources on this topic. Most advices here are generic enough and can be easily approximated to other programming languages. Also in this post we won't talk about ready to use solutions (if you are looking for it – check out Real-time Web Technologies guide by Phil Leggetter), just general considerations. There is not so much information about scaling WebSocket on the internet so if you are interested in WebSocket and real-time messaging technologies - keep on reading. If you don't know what WebSocket is – check out the following curious links: https://hpbn.co/websocket/ – a wonderful chapter of great book by Ilya Grigorik https://lucumr.pocoo.org/2012/9/24/websockets-101/ – valuable thoughts about WebSocket from Armin Ronacher As soon as you know WebSocket basics – we can proceed.","s":"Scaling WebSocket in Go and beyond","u":"/blog/2020/11/12/scaling-websocket","h":"","p":3},{"i":6,"t":"Speaking about scalable servers that work with many persistent WebSocket connections – I found several important tasks such a server should be able to do: Maintain many active connections Send many messages to clients Support WebSocket fallback to scale to every client Authenticate incoming connections and invalidate connections Survive massive reconnect of all clients without loosing messages note Of course not all of these points equally important in various situations. Below we will look at some tips which relate to these points.","s":"WebSocket server tasks","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-server-tasks","p":3},{"i":8,"t":"In Go language ecosystem we have several libraries which can be used as a building block for a WebSocket server. Package golang.org/x/net/websocket is considered deprecated. The default choice in the community is gorilla/websocket library. Made by Gary Burd (who also gifted us an awesome Redigo package to communicate with Redis) – it's widely used, performs well, has a very good API – so in most cases you should go with it. Some people think that library not actively maintained at moment – but this is not quite true, it implements full WebSocket RFC, so actually it can be considered done. In 2018 my ex-colleague Sergey Kamardin open-sourced gobwas/ws library. It provides a bit lower-level API than gorilla/websocket thus allows reducing RAM usage per connection and has nice optimizations for WebSocket upgrade process. It does not support WebSocket permessage-deflate compression but otherwise a good alternative you can consider using. If you have not read Sergey's famous post A Million WebSockets and Go – make a bookmark! One more library is nhooyr/websocket. It's the youngest one and actively maintained. It compiles to WASM which can be a cool thing for someone. The API is a bit different from what gorilla/websocket offers, and one of the big advantages I see is that it solves a problem with a proper WebSocket closing handshake which is a bit hard to do right with Gorilla WebSocket. You can consider all listed libraries except one from x/net for your project. Take a library, follow its examples (make attention to goroutine-safety of various API operations). Personally I prefer Gorilla WebSocket at moment since it's feature-complete and battle tested by tons of projects around Go world.","s":"WebSocket libraries","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-libraries","p":3},{"i":10,"t":"OK, so you have chosen a library and built a server on top of it. As soon as you put it in production the interesting things start happening. Let's start with several OS specific key things you should do to prepare for many connections from WebSocket clients. Every connection will cost you an open file descriptor, so you should tune a maximum number of open file descriptors your process can use. An errors like too many open files raise due to OS limit on file descriptors which is usually 256-1024 by default (see with ulimit -n on Unix). A nice overview on how to do this on different systems can be found in Riak docs. Wanna more connections? Make this limit higher. Nice tip here is to limit a maximum number of connections your process can serve – making it less than known file descriptor limit: // ulimit -n == 65535if conns.Len() >= 65500 { return errors.New(\"connection limit reached\")}conns.Add(conn) – otherwise you have a risk to not even able to look at pprof when things go bad. And you always need monitoring of open file descriptors. You can also consider using netutil.LimitListener for this task, but don't forget to put pprof on another port with another HTTP server instance in this case. Keep attention on Ephemeral ports problem which is often happens between your load balancer and your WebSocket server. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On balancer/server boundary you are limited in 65536 possible variants by default. But actually due to some OS limits and sockets in TIME_WAIT state the number is even less. A very good explanation and how to deal with it can be found in Pusher blog. Your possible number of connections also limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article. One more thing you can do is tune your network stack for performance. Do this only if you understand that you need it. Maybe start with this gist, but don't optimize without full understanding why you are doing this.","s":"OS tuning","u":"/blog/2020/11/12/scaling-websocket","h":"#os-tuning","p":3},{"i":12,"t":"Now let's speak about sending many messages. The general tips follows. Make payload smaller. This is obvious – fewer data means more effective work on all layers. BTW WebSocket framing overhead is minimal and adds only 2-8 bytes to your payload. You can read detailed dedicated research in Dissecting WebSocket's Overhead article. You can reduce an amount of data traveling over network with permessage-deflate WebSocket extension, so your data will be compressed. Though using permessage-deflate is not always a good thing for server due to poor performance of flate, so you should be prepared for a CPU and RAM resource usage on server side. While Gorilla WebSocket has a lot of optimizations internally by reusing flate writers, overhead is still noticeable. The increase value heavily depends on your load profile. Make less system calls. Every syscall will have a constant overhead, and actually in WebSocket server under load you will mostly see read and write system calls in your CPU profiles. An advice here – try to use client-server protocol that supports message batching, so you can join individual messages together. Use effective message serialization protocol. Maybe use code generation for JSON to avoid extensive usage of reflect package done by Go std lib. Maybe use sth like gogo/protobuf package which allows to speedup Protobuf marshalling and unmarshalling. Unfortunately Gogo Protobuf is going through hard times at this moment. Try to serialize a message only once when sending to many subscribers. Have a way to scale to several machines - more power, more possible messages. We will talk about this very soon.","s":"Sending many messages","u":"/blog/2020/11/12/scaling-websocket","h":"#sending-many-messages","p":3},{"i":14,"t":"Even in 2020 there are still users which cannot establish connection with WebSocket server. Actually the problem mostly appears with browsers. Some users still use old browsers. But they have a choice – install a newer browser. Still, there could also be users behind corporate proxies. Employees can have a trusted certificate installed on their machine so company proxy can re-encrypt even TLS traffic. Also, some browser extensions can block WebSocket traffic. One ready solution to this is Sockjs-Go library. This is a mature library that provides fallback transport for WebSocket. If client does not succeed with WebSocket connection establishment then client can use some of HTTP transports for client-server communication: EventSource aka Server-Sent Events, XHR-streaming, Long-Polling etc. The downside with those transports is that to achieve bidirectional communication you should use sticky sessions on your load balancer since SockJS keeps connection session state in process memory. We will talk about many instances of your WebSocket server very soon. You can implement WebSocket fallback yourself, this should be simple if you have a sliding window message stream on your backend which we will discuss very soon. Maybe look at GRPC, depending on application it could be better or worse than WebSocket – in general you can expect a better performance and less resource consumption from WebSocket for bidirectional communication case. My measurements for a bidirectional scenario showed 3x win for WebSocket (binary + GOGO protobuf) in terms of server CPU consumption and 4 times less RAM per connection. Though if you only need RPC then GRPC can be a better choice. But you need additional proxy to work with GRPC from a browser.","s":"WebSocket fallback transport","u":"/blog/2020/11/12/scaling-websocket","h":"#websocket-fallback-transport","p":3},{"i":16,"t":"You can optimize client-server protocol, tune your OS, but at some point you won't be able to use only one process on one server machine. You need to scale connections and work your server does over different server machines. Horizontal scaling is also good for a server high availability. Actually there are some sort of real-time applications where a single isolated process makes sense - for example multiplayer games where limited number of players play independent game rounds. As soon as you distribute connections over several machines you have to find a way to deliver a message to a certain user. The basic approach here is to publish messages to all server instances. This can work but this does not scale well. You need a sort of instance discovery to make this less painful. Here comes PUB/SUB, where you can connect WebSocket server instances over central PUB/SUB broker. Clients that establish connections with your WebSocket server subscribe to topics (channels) in a broker, and as soon as you publish a message to that topic it will be delivered to all active subscribers on WebSocket server instances. If server node does not have interested subscriber then it won't get a message from a broker thus you are getting effective network communication. Actually the main picture of this post illustrates exactly this architecture: Let's think about requirements for a broker for real-time messaging application. We want a broker: with reasonable performance and possibility to scale which maintains message order in topics can support millions of topics, where each topic should be ephemeral and lightweight – topics can be created when user comes to application and removed after user goes away possibility to keep a sliding window of messages inside channel to help us survive massive reconnect scenario (will talk about this later below, can be a separate part from broker actually) Personally when we talk about such brokers here are some options that come into my mind: RabbitMQ Kafka or Pulsar Nats or Nats-Streaming Tarantool Redis Sure there are more exist including libraries like ZeroMQ or nanomsg. Below I'll try to consider these solutions for the task of making scalable WebSocket server facing many user connections from Internet. If you are looking for unreliable at most once PUB/SUB then any of solutions mentioned above should be sufficient. Many real-time messaging apps are ok with at most once guarantee delivery. If you don't want to miss messages then things are a bit harder. Let's try to evaluate these options for a task where application has lots of different topics from which it wants to receive messages with at least once guarantee (having a personal topic per client is common thing in applications). A short analysis below can be a bit biased, but I believe thoughts are reasonable enough. I did not found enough information on the internet about scaling WebSocket beyond a single server process, so I'll try to fill the gap a little based on my personal knowledge without pretending to be absolutely objective in these considerations. In some posts on the internet about scaling WebSocket I saw advices to use RabbitMQ for PUB/SUB stuff in real-time messaging server. While this is a great messaging server, it does not like a high rate of queue bind and unbind type of load. It will work, but you will need to use a lot of server resources for not so big number of clients (imagine having millions of queues inside RabbitMQ). I have an example from my practice where RabbitMQ consumed about 70 CPU cores to serve real-time messages for 100k online connections. After replacing it with Redis keeping the same message delivery semantics we got only 0.3 CPU consumption on broker side. Kafka and Pulsar are great solutions, but not for this task I believe. The problem is again in dynamic ephemeral nature of our topics. Kafka also likes a more stable configuration of its topics. Keeping messages on disk can be an overkill for real-time messaging task. Also your consumers on Kafka server should pull from millions of different topics, not sure how well it performs, but my thoughts at moment - this should not perform very well. Kafka itself scales perfectly, you will definitely be able to achieve a goal but resource usage will be significant. Here is a post from Trello where they moved from RabbitMQ to Kafka for similar real-time messaging task and got about 5x resource usage improvements. Note also that the more partitions you have the more heavy failover process you get. Nats and Nats-Streaming. Raw Nats can only provide at most once guarantee. BTW recently Nats developers released native WebSocket support, so you can consider it for your application. Nats-Streaming server as broker will allow you to not lose messages. To be fair I don't have enough information about how well Nats-Streaming scales to millions of topics. An upcoming Jetstream which will be a part of Nats server can also be an interesting option – like Kafka it provides a persistent stream of messages for at least once delivery semantics. But again, it involves disk storage, a nice thing for backend microservices communication but can be an overkill for real-time messaging task. Sure Tarantool can fit to this task well too. It's fast, im-memory and flexible. Some possible problems with Tarantool are not so healthy state of its client libraries, complexity and the fact that it's heavily enterprise-oriented. You should invest enough time to benefit from it, but this can worth it actually. See an article on how to do a performant broker for WebSocket applications with Tarantool. Building PUB/SUB system on top of ZeroMQ will require you to build separate broker yourself. This could be an unnecessary complexity for your system. It's possible to implement PUB/SUB pattern with ZeroMQ and nanomsg without a central broker, but in this case messages without active subscribers on a server will be dropped on a consumer side thus all publications will travel to all server nodes. My personal choice at moment is Redis. While Redis PUB/SUB itself provides at most once guarantee, you can build at least once delivery on top of PUB/SUB and Redis data structures (though this can be challenging enough). Redis is very fast (especially when using pipelining protocol feature), and what is more important – very predictable. It gives you a good understanding of operation time complexity. You can shard topics over different Redis instances running in HA setup - with Sentinel or with Redis Cluster. It allows writing LUA procedures with some advanced logic which can be uploaded over client protocol thus feels like ordinary commands. You can use Redis to keep sliding window event stream which gives you access to missed messages from a certain position. We will talk about this later. OK, the end of opinionated thoughts here :) Depending on your choice the implementation of your system will vary and will have different properties – so try to evaluate possible solutions based on your application requirements. Anyway, whatever broker will be your choice, try to follow this rules to build effective PUB/SUB system: take into account message delivery guarantees of your system: at most once or at least once, ideally you should have an option to have both for different real-time features in your app make sure to use one or pool of connections between your server and a broker, don't create new connection per each client or topic that comes to your WebSocket server use effective serialization format between your WebSocket server and broker","s":"Performance is not scalability","u":"/blog/2020/11/12/scaling-websocket","h":"#performance-is-not-scalability","p":3},{"i":18,"t":"Let's talk about one more problem that is unique for Websocket servers compared to HTTP. Your app can have thousands or millions of active WebSocket connections. In contract to stateless HTTP APIs your application is stateful. It uses push model. As soon as you deploying your WebSocket server or reload your load balancer (Nginx maybe) – connections got dropped and all that army of users start reconnecting. And this can be like an avalanche actually. How to survive? First of all - use exponential backoff strategies on client side. I.e. reconnect with intervals like 1, 2, 4, 8, 16 seconds with some random jitter. Turn on various rate limiting strategies on your WebSocket server, some of them should be turned on your backend load balancer level (like controlling TCP connection establishment rate), some are application specific (maybe limit an amount of requests from certain user). One more interesting technique to survive massive reconnect is using JWT (JSON Web Token) for authentication. I'll try to explain why this can be useful. As soon as your client start reconnecting you will have to authenticate each connection. In massive setups with many persistent connection this can be a very significant load on your Session backend. Since you need an extra request to your session storage for every client coming back. This can be a no problem for some infrastructures but can be really disastrous for others. JWT allows to reduce this spike in load on session storage since it can have all required authentication information inside its payload. When using JWT make sure you have chosen a reasonable JWT expiration time – expiration interval depends on your application nature and just one of trade-offs you should deal with as developer. Don't forget about making an effective connection between your WebSocket server and broker – as soon as all clients start reconnecting you should resubscribe your server nodes to all topics as fast as possible. Use techniques like smart batching at this moment. Let's look at a small piece of code that demonstrates this technique. Imagine we have a source channel from which we get items to process. We don’t want to process items individually but in batch. For this we wait for first item coming from channel, then try to collect as many items from channel buffer as we want without blocking and timeouts involved. And then process slice of items we collected at once. For example build Redis pipeline from them and send to Redis in one connection write call. maxBatchSize := 50for { select { case item := <-sourceCh: batch := []string{item} loop: for len(batch) < maxBatchSize { select { case item := <-sourceCh: batch = append(batch, item) default: break loop } } // Do sth with collected batch of items. println(len(batch)) }} Look at a complete example in a Go playground: https://play.golang.org/p/u7SAGOLmDke. I also made a repo where I demonstrate how this technique together with Redis pipelining feature allows to fully utilize connection for a good performance https://github.com/FZambia/redigo-smart-batching. Another advice for those who run WebSocket services in Kubernetes. Learn how your ingress behaves – for example Nginx ingress can reload its configuration on every change inside Kubernetes services map resulting into closing all active WebSocket connections. Proxies like Envoy don't have this behaviour, so you can reduce number of mass disconnections in your system. You can also proxy WebSocket without using ingress at all over configured WebSocket service NodePort.","s":"Massive reconnect","u":"/blog/2020/11/12/scaling-websocket","h":"#massive-reconnect","p":3},{"i":20,"t":"Here comes a final part of this post. Maybe the most important one. Not only mass client re-connections could create a significant load on a session backend but also a huge load on your main application database. Why? Because WebSocket applications are stateful. Clients rely on a stream of messages coming from a backend to maintain its state actual. As soon as connection dropped client tries to reconnect. In some scenarios it also wants to restore its actual state. What if client reconnected after 3 seconds? How many state updates it could miss? Nobody knows. So to make sure state is actual client tries to get it from application database. This is again a significant spike in load on your main database in massive reconnect scenario. In can be really painful with many active connections. So what I think is nice to have for scenarios where we can't afford to miss messages (like in chat-like apps for example) is having effective and performant stream of messages inside each channel. Keep this stream in fast in-memory storage. This stream can have time retention and be limited in size (think about it as a sliding window of messages). I already mentioned that Redis can do this – it's possible to keep messages in Redis List or Redis Stream data structures. Other broker solutions could give you access to such a stream inside each channel out of the box. So as soon as client reconnects it can restore its state from fast in-memory event stream without even querying your database. Actually to survive mass reconnect scenario you don't need to keep such a stream for a long time – several minutes should be enough. You can even create your own Websocket fallback implementation (like Long-Polling) utilizing event stream with limited retention.","s":"Message event stream benefits","u":"/blog/2020/11/12/scaling-websocket","h":"#message-event-stream-benefits","p":3},{"i":22,"t":"Hope advices given here will be useful for a reader and will help writing a more robust and more scalable real-time application backends. Centrifugo server and Centrifuge library for Go language have most of the mechanics described here including the last one – message stream for topics limited by size and retention period. Both also have techniques to prevent message loss due to at most once nature of Redis PUB/SUB giving at least once delivery guarantee inside message history window size and retention period.","s":"Conclusion","u":"/blog/2020/11/12/scaling-websocket","h":"#conclusion","p":3},{"i":24,"t":"After almost three years of Centrifugo v2 life cycle we are happy to announce the next major release of Centrifugo. During the last several months deep in our Centrifugal laboratory we had been synthesizing an improved version of the server. New Centrifugo v3 is targeting to improve Centrifugo adoption for basic real-time application cases, improves server performance and extends existing features with new functionality. It comes with unidirectional real-time transports, protocol speedups, super-fast engine implementation based on Tarantool, new documentation site, GRPC proxy, API extensions and PRO version which provides unique possibilities for business adopters.","s":"Centrifugo v3 released","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"","p":23},{"i":26,"t":"Centrifugo v2 life cycle has come to an end. Before discussing v3 let's look back at what has been done during the last three years. Centrifugo v2 was a pretty huge refactoring of v1. Since the v2 release, Centrifugo is built on top of new Centrifuge library for Go language. Centrifuge library evolved significantly since its initial release and now powers Grafana v8 real-time streaming among other things. Here is an awesome demo made by my colleague Alexander Zobnin that demonstrates real-time telemetry of Assetto Corsa sports car streamed in real-time to Grafana dashboard: Centrifugo integrated with Redis Streams, got Redis Cluster support, can now work with Nats server as a PUB/SUB broker. Notable additions of Centrifugo v2 were server-side subscriptions with some interesting features on top – like maintaining a single global connection from one user and automatic personal channel subscription upon user connect. A very good addition which increased Centrifugo adoption a lot was introduction of proxy to backend. This made Centrifugo fit many setups where JWT authentication and existing subscription permission model did not suit well before. Client ecosystem improved significantly. The fact that client protocol migrated to a strict Protobuf schema allowed to introduce binary protocol format (in addition to JSON) and simplify building client connectors. We now have much better and complete client libraries (compared to v1 situation). We also have an official Helm chart, Grafana dashboard for Prometheus datasource, and so on. Centrifugo is becoming more noticeable in a wider real-time technology community. For example, it was included in a periodic table of real-time created by Ably.com (one of the most powerful real-time messaging cloud services at the moment): Of course, there are many aspects where Centrifugo can be improved. And v3 addresses some of them. Below we will look at the most notable features and changes of the new major Centrifugo version.","s":"Centrifugo v2 flashbacks","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-v2-flashbacks","p":23},{"i":28,"t":"Let's start with the most important thing – backwards compatibility concerns. In Centrifugo v3 client protocol mostly stayed the same. We expect that most applications will be able to update without any change on a client-side. This was an important concern for v3 given how painful the update cycle can be on mobile devices and lessons learned from v1 to v2 migration. There is one breaking change though which can affect users who use history API manually from a client-side (we provide a temporary workaround to give apps a chance to migrate smoothly). On a server-side, much more changes happened, especially in the configuration: some options were renamed, some were removed. We provide a v2 to v3 configuration converter which can help dealing with changes. In most cases, all you should do is adapt Centrifugo configuration to match v3 changes and redeploy Centrifugo using v3 build instead of v2. All features are still there (or a replacement exists, like for channels API). For more details, refer to the v3 migration guide.","s":"Backwards compatibility","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#backwards-compatibility","p":23},{"i":30,"t":"As some of you know we considered changing Centrifugo license to AGPL v3 for a new release. After thinking a lot about this we decided to not step into this area. But the license has been changed: the license of OSS Centrifugo is now Apache 2.0 instead of MIT. Apache 2.0 is also a permissive OSS license, it's just a bit more concrete in some aspects.","s":"License change","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#license-change","p":23},{"i":32,"t":"Server-side subscriptions introduced in Centrifugo v2 and recent improvements in the underlying Centrifuge library opened a road for a unidirectional approach. This means that Centrifugo v3 provides a set of unidirectional real-time transports where messages flow only in one direction – from a server to a client. Why is this change important? Centrifugo originally concentrated on using bidirectional transports for client-server communication. Like WebSocket and SockJS. Bidirectional transports allow implementing some great protocol features since a client can communicate with a server in various ways after establishing a persistent connection. While this is a great opportunity this also leads to an increased complexity. Centrifugo users had to use special client connector libraries which abstracted underlying work into a simple public API. But internally connectors do many things: matching requests to responses, handling timeouts, handling an ordering, queuing operations, error handling. So the client connector is a pretty complex piece of software. But what if a user just needs to receive real-time updates from a stable set of channels known in connection time? Can we simplify everything and avoid using custom software on a client-side? With unidirectional transports, the answer is yes. Clients can now connect to Centrifugo using a bunch of unidirectional transports. And the greatest thing is that in this case, developers should not depend on Centrifugo client connectors at all – just use native browser APIs or GRPC-generated code. It's finally possible to consume events from Centrifugo using CURL (see an example). Using unidirectional transports you can still benefit from Centrifugo built-in scalability with various engines, utilize built-in authentication over JWT or the connect proxy feature. With subscribe server API (see below) it's even possible to subscribe unidirectional client to server-side channels dynamically. With refresh server API or the refresh proxy feature it's possible to manage a connection expiration. Centrifugo supports the following unidirectional transports: EventSource (SSE) HTTP streaming Unidirectional WebSocket Unidirectional GRPC stream We expect that introducing unidirectional transports will significantly increase Centrifugo adoption.","s":"Unidirectional real-time transports","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#unidirectional-real-time-transports","p":23},{"i":34,"t":"There was a rather important limitation of Centrifugo history API – it was not very suitable for keeping large streams because a call to a history could only return the entire channel history. Centrifugo v3 introduces an API to iterate over a stream. It's possible to do from the current stream beginning or end, in both directions – forward and backward, with configured limit. Also with certain starting stream position if it's known. This, among other things, can help to implement manual missed message recovery on a client-side to reduce the load on the application backend. Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }} caution This new API does not remove the need in having the main application database – that's still mandatory for idiomatic Centrifugo usage.","s":"History iteration API","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#history-iteration-api","p":23},{"i":36,"t":"In Centrifugo v3 Redis engine uses Redis Stream data structure by default for keeping channel history. Before v3 Redis Streams were supported by not enabled by default so almost nobody used them. This change is important in terms of introducing history iteration API described above – since Redis Streams allow doing iteration effectively.","s":"Redis Streams by default","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#redis-streams-by-default","p":23},{"i":38,"t":"As you may know, Centrifugo has several built-in engines that allow scaling Centrifugo nodes (using PUB/SUB) and keep shared history and presence state. Before v3 Centrifugo had in-memory and Redis (or KeyDB) engines available. Introducing a new engine to Centrifugo is pretty hard since the engine should provide a very robust PUB/SUB performance, fast history and presence operations, possibility to publish a message to PUB/SUB and save to history atomically. It also should allow dealing with ephemeral frequently changing subscriptions. It's typical for Centrifugo use case to have millions of users each subscribed to a unique channel and constantly connecting/disconnecting (thus subscribing/unsubscribing). In v3 we added experimental support for the Tarantool engine. It fits nicely all the requirements above and provides a huge performance speedup for history and presence operations compared to Redis. According to our benchmarks, the speedup can be up to 4-10x depending on operation. The PUB/SUB performance of Tarantool is comparable with Redis (10-20% worse according to our internal benchmarks to be exact, but that's pretty much the same). For example, let's look at Centrifugo benchmark where we recover zero messages (i.e. emulate a situations when many connections disconnected for a very short time interval due to load balancer reload). For Redis engine: Redis engine, single Redis instance BenchmarkRedisRecover 26883 ns/op 1204 B/op 28 allocs/op Compare it with the same operation measured with Tarantool engine: Tarantool engine, single Tarantool instance BenchmarkTarantoolRecover 6292 ns/op 563 B/op 10 allocs/op Tarantool can provide new storage properties (like synchronous replication), new adoption. We are pretty excited about adding it as an option. The reason why Tarantool support is experimental is because Tarantool integration involves one more moving piece – the Centrifuge Lua module which should be run by a Tarantool server. This increases deployment complexity and given the fact that many users have their own best practices in Tarantool deployment we are still evaluating a sufficient way to distribute Lua part. For now, we are targeting standalone (see examples in centrifugal/tarantool-centrifuge) and Cartridge Tarantool setups (with centrifugal/rotor). Refer to the Tarantool Engine documentation for more details.","s":"Tarantool engine","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#tarantool-engine","p":23},{"i":40,"t":"Centrifugo can now transform events received over persistent connections from users into GRPC calls to the application backend (in addition to the HTTP proxy available in v2). GRPC support should make Centrifugo ready for today's microservice architecture where GRPC is a huge player for inter-service communication. So we mostly just provide more choices for Centrifugo users here. GRPC has some good advantages – for example an application backend RPC layer which is responsible for communication with Centrifugo can now be generated from Protobuf definitions for all popular programming languages.","s":"GRPC proxy","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#grpc-proxy","p":23},{"i":42,"t":"Centrifugo v3 has some valuable server API improvements. The new subscribe API method allows subscribing connection to a channel at any point in time. This works by utilizing server-side subscriptions. So it's not only possible to subscribe connection to a list of server-side channels during the connection establishment phase – but also later during the connection lifetime. This may be very useful for the unidirectional approach - by emulating client-side subscribe call over request to application backend which in turn calls subscribe Centrifugo server API. Publish API now returns the current top stream position (offset and epoch) for channels with history enabled. Server history API inherited iteration possibilities described above. Channels command now returns a number of clients in a channel, also supports channel filtering by a pattern. Since we changed how channels call implemented internally there is no limitation anymore to call it when using Redis cluster. Admin web UI has been updated too to support new API methods, so you can play with new API from its actions tab.","s":"Server API improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#server-api-improvements","p":23},{"i":44,"t":"Centrifugo behaves a bit better in cluster mode: as soon as a node leaves a cluster gracefully (upon graceful termination) it sends a shutdown signal to the control channel thus giving other nodes a chance to immediately delete that node from the local registry.","s":"Better clustering","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#better-clustering","p":23},{"i":46,"t":"While preparing the v3 release we improved client connectors too. All existing client connectors now actualized to the latest protocol, support server-side subscriptions, history API. One important detail is that it's not required to set ?format=protobuf URL param now when connecting to Centrifugo from mobile devices - this is now managed internally by using the WebSocket subprotocol mechanism (requires using the latest client connector version and Centrifugo v3).","s":"Client improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#client-improvements","p":23},{"i":48,"t":"You are reading this post on a new project site. It's built with amazing Docusaurus. A lot of documents were actualized, extended, and rewritten. We also now have new chapters like: Main highlights Design overview History and recovery Error and disconnect codes. Server API and proxy documentation have been improved significantly.","s":"New documentation site","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#new-documentation-site","p":23},{"i":50,"t":"Centrifugo v3 has some notable performance improvements. JSON client protocol now utilizes a couple of libraries (easyjson for encoding and segmentio/encoding for unmarshaling). Actually we use a slightly customized version of easyjson library to achieve even faster performance than it provides out-of-the-box. Changes allowed to speed up JSON encoding and decoding up to 4-5x for small messages. For large payloads speed up can be even more noticeable – we observed up to 30x performance boost when serializing 5kb messages. For example, let's look at a JSON serialization benchmark result for 256 byte payload. Here is what we had before: Centrifugo v2 JSON encoding/decoding cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHzBenchmarkMarshal-12 5883 ns/op 1121 B/op 6 allocs/opBenchmarkMarshalParallel-12 1009 ns/op 1121 B/op 6 allocs/opBenchmarkUnmarshal-12 1717 ns/op 1328 B/op 16 allocs/opBenchmarkUnmarshalParallel-12 492.2 ns/op 1328 B/op 16 allocs/op And what we have now with mentioned JSON optimizations: Centrifugo v3 JSON encoding/decoding cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHzBenchmarkMarshal-12 461.3 ns/op 928 B/op 3 allocs/opBenchmarkMarshalParallel-12 250.6 ns/op 928 B/op 3 allocs/opBenchmarkUnmarshal-12 476.5 ns/op 136 B/op 3 allocs/opBenchmarkUnmarshalParallel-12 107.2 ns/op 136 B/op 3 allocs/op tip Centrifugo Protobuf protocol is still faster than JSON for encoding/decoding on a server-side. Of course, JSON encoding is only one part of Centrifugo – so you should not expect overall 4x performance improvement. But loaded setups should notice the difference and this should also be a good thing for reducing garbage collection pauses. Centrifugo inherited a couple of other improvements from the Centrifuge library. In-memory connection hub is now sharded – this should reduce lock contention between operations in different channels. In our artificial benchmarks we noticed a 3x better hub throughput, but in reality the benefit is heavily depends on the usage pattern. Centrifugo now allocates less during message broadcasting to a large number of subscribers. Also, an upgrade to Go 1.17 for builds results in ~5% performance boost overall, thanks to a new way of passing function arguments and results using registers instead of the stack introduced in Go 1.17.","s":"Performance improvements","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#performance-improvements","p":23},{"i":52,"t":"The final notable thing is an introduction of Centrifugo PRO. This is an extended version of Centrifugo built on top of the OSS version. It provides some unique features targeting business adopters. Those who followed Centrifugo for a long time know that there were some attempts to make project development sustainable. Buy me a coffee and Opencollective approaches were not successful, during a year we got ~300$ of total contributions. While we appreciate these contributions a lot - this does not fairly justify a time spent on Centrifugo maintenance these days and does not allow bringing it to the next level. So here is an another attempt to monetize Centrifugo. Centrifugo PRO details and features described here in docs. Let's see how it goes. We believe that a set of additional functionality can provide great advantages for both small and large-scale Centrifugo setups. PRO features can give useful insights on a system, protect from client API misusing, reduce server resource usage, and more. PRO version will be released soon after Centrifugo v3 OSS.","s":"Centrifugo PRO","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#centrifugo-pro","p":23},{"i":54,"t":"There are some other changes introduced in v3 but not mentioned here. The full list can be found in the release notes and the migration guide. Hope we stepped into an exciting time of the v3 life cycle and many improvements will follow. Join our communities in Telegram and Discord if you have questions or want to follow Centrifugo development: Enjoy Centrifugo v3, and let the Centrifugal force be with you. Special thanks Special thanks to Anton Silischev for the help with v3 tests, examples and CI. To Leon Sorokin for the spinning CSS Centrifugo logo. To Michael Filonenko for the help with Tarantool. To German Saprykin for Dart magic. Thanks to the community members who tested out Centrifugo v3 beta, found bugs and sent improvements. Icons used here made by wanicon from www.flaticon.com","s":"Conclusion","u":"/blog/2021/08/31/hello-centrifugo-v3","h":"#conclusion","p":23},{"i":56,"t":"In this post I'll try to introduce Centrifuge - the heart of Centrifugo. Centrifuge is a real-time messaging library for the Go language. This post is going to be pretty long (looks like I am a huge fan of long reads) – so make sure you also have a drink (probably two) and let's go!","s":"Centrifuge – real-time messaging with Go","u":"/blog/2021/01/15/centrifuge-intro","h":"","p":55},{"i":58,"t":"I wrote several blog posts before (for example this one – yep, it's on Medium...) about an original motivation of Centrifugo server. danger Centrifugo server is not the same as Centrifuge library for Go. It's a full-featured project built on top of Centrifuge library. Naming can be confusing, but it's not too hard once you spend some time with ecosystem. In short – Centrifugo was implemented to help traditional web frameworks dealing with many persistent connections (like WebSocket or SockJS HTTP transports). So frameworks like Django or Ruby on Rails, or frameworks from the PHP world could be used on a backend but still provide real-time messaging features like chats, multiplayer browser games, etc for users. With a little help from Centrifugo. Now there are cases when Centrifugo server used in conjunction even with a backend written in Go. While Go mostly has no problems dealing with many concurrent connections – Centrifugo provides some features beyond simple message passing between a client and a server. That makes it useful, especially since design is pretty non-obtrusive and fits well microservices world. Centrifugo is used in some well-known projects (like ManyChat, Yoola.io, Spot.im, Badoo etc). At the end of 2018, I released Centrifugo v2 based on a real-time messaging library for Go language – Centrifuge – the subject of this post. It was a pretty hard experience to decouple Centrifuge out of the monolithic Centrifugo server – I was unable to make all the things right immediately, so Centrifuge library API went through several iterations where I introduced backward-incompatible changes. All those changes targeted to make Centrifuge a more generic tool and remove opinionated or limiting parts.","s":"How it's all started","u":"/blog/2021/01/15/centrifuge-intro","h":"#how-its-all-started","p":55},{"i":60,"t":"This is ... well, a framework to build real-time messaging applications with Go language. If you ever heard about socket.io – then you can think about Centrifuge as an analogue. I think the most popular applications these days are chats of different forms, but I want to emphasize that Centrifuge is not a framework to build chats – it's a generic instrument that can be used to create different sorts of real-time applications – real-time charts, multiplayer games. The obvious choice for real-time messaging transport to achieve fast and cross-platform bidirectional communication these days is WebSocket. Especially if you are targeting a browser environment. You mostly don't need to use WebSocket HTTP polyfills in 2021 (though there are still corner cases so Centrifuge supports SockJS polyfill). Centrifuge has its own custom protocol on top of plain WebSocket or SockJS frames. The reason why Centrifuge has its own protocol on top of underlying transport is that it provides several useful primitives to build real-time applications. The protocol described as strict Protobuf schema. It's possible to pass JSON or binary Protobuf-encoded data over the wire with Centrifuge. note GRPC is very handy these days too (and can be used in a browser with a help of additional proxies), some developers prefer using it for real-time messaging apps – especially when one-way communication needed. It can be a bit better from integration perspective but more resource-consuming on server side and a bit trickier to deploy. note Take a look at WebTransport – a brand-new spec for web browsers to allow fast communication between a client and a server on top of QUIC – it may be a good alternative to WebSocket in the future. This in a draft status at the moment, but it's already possible to play with in Chrome. Own protocol is one of the things that prove the framework status of Centrifuge. This dictates certain limits (for example, you can't just use an alternative message encoding) and makes developers use custom client connectors on a front-end side to communicate with a Centrifuge-based server (see more about connectors in ecosystem part). But protocol solves many practical tasks – and here we are going to look at real-time features it provides for a developer.","s":"So what is Centrifuge?","u":"/blog/2021/01/15/centrifuge-intro","h":"#so-what-is-centrifuge","p":55},{"i":62,"t":"To start working with Centrifuge you need to start Centrifuge server Node. Node is a core of Centrifuge – it has many useful methods – set event handlers, publish messages to channels, etc. We will look at some events and channels concept very soon. Also, Node abstracts away scalability aspects, so you don't need to think about how to scale WebSocket connections over different server instances and still have a way to deliver published messages to interested clients. For now, let's start a single instance of Node that will serve connections for us: node, err := centrifuge.New(centrifuge.DefaultConfig)if err != nil { log.Fatal(err)}if err := node.Run(); err != nil { log.Fatal(err)} It's also required to serve a WebSocket handler – this is possible just by registering centrifuge.WebsocketHandler in HTTP mux: wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})http.Handle(\"/connection/websocket\", wsHandler) Now it's possible to connect to a server (using Centrifuge connector for a browser called centrifuge-js): const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.connect(); Though connection will be rejected by the server since we also need to provide authentication details – Centrifuge expects explicitly provided connection Credentials to accept connection.","s":"Centrifuge Node","u":"/blog/2021/01/15/centrifuge-intro","h":"#centrifuge-node","p":55},{"i":64,"t":"Let's look at how we can tell Centrifuge details about connected user identity, so it could accept an incoming connection. There are two main ways to authenticate client connection in Centrifuge. The first one is over the native middleware mechanism. It's possible to wrap centrifuge.WebsocketHandler or centrifuge.SockjsHandler with middleware that checks user authentication and tells Centrifuge current user ID over context.Context: func auth(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cred := ¢rifuge.Credentials{ UserID: \"42\", } newCtx := centrifuge.SetCredentials(r.Context(), cred) r = r.WithContext(newCtx) h.ServeHTTP(w, r) })} So WebsocketHandler can be registered this way (note that a handler now wrapped by auth middleware): wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})http.Handle(\"/connection/websocket\", auth(wsHandler)) Another authentication way is a bit more generic – developers can authenticate connection based on custom token sent from a client inside first WebSocket/SockJS frame. This is called connect frame in terms of Centrifuge protocol. Any string token can be set – this opens a way to use JWT, Paceto, and any other kind of authentication tokens. For example see an authenticaton with JWT. note BTW it's also possible to pass any information from client side with a first connect message from client to server and return custom information about server state to a client. This is out of post scope though. Nothing prevents you to integrate Centrifuge with OAuth2 or another framework session mechanism – like Gin for example.","s":"Authentication","u":"/blog/2021/01/15/centrifuge-intro","h":"#authentication","p":55},{"i":66,"t":"As soon as a client connected and successfully authenticated it can subscribe to channels. Channel (room or topic in other systems) is a lightweight and ephemeral entity in Centrifuge. Channel can have different features (we will look at some channel features below). Channels created automatically as soon as the first subscriber joins and destroyed as soon as the last subscriber left. The application can have many real-time features – even on one app screen. So sometimes client subscribes to several channels – each related to a specific real-time feature (for example one channel for chat updates, one channel likes notification stream, etc). Channel is just an ASCII string. A developer is responsible to find the best channel naming convention suitable for an application. Channel naming convention is an important aspect since in many cases developers want to authorize subscription to a channel on the server side – so only authorized users could listen to specific channel updates. Let's look at a basic subscription example on the client-side: centrifuge.subscribe('example', function(msgCtx) { console.log(msgCtx)}) On the server-side, you need to define subscribe event handler. If subscribe event handler not set then the connection won't be able to subscribe to channels at all. Subscribe event handler is where a developer may check permissions of the current connection to read channel updates. Here is a basic example of subscribe event handler that simply allows subscriptions to channel example for all authenticated connections and reject subscriptions to all other channels: node.OnConnect(func(client *centrifuge.Client) { client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) { if e.Channel != \"example\" { cb(centrifuge.SubscribeReply{}, centrifuge.ErrorPermissionDenied) return } cb(centrifuge.SubscribeReply{}, nil) })}) You may notice a callback style of reacting to connection related things. While not being very idiomatic for Go it's very practical actually. The reason why we use callback style inside client event handlers is that it gives a developer possibility to control operation concurrency (i.e. process sth in separate goroutines or goroutine pool) and still control the order of events. See an example that demonstrates concurrency control in action. Now if some event published to a channel: // Here is how we can publish data to a channel.node.Publish(\"example\", []byte(`{\"input\": \"hello\"}`)) – data will be delivered to a subscribed client, and message will be printed to Javascript console. PUB/SUB in its usual form. note Though Centrifuge protocol based on Protobuf schema in example above we published a JSON message into a channel. By default, we can only send JSON to connections since default protocol format is JSON. But we can switch to Protobuf-based binary protocol by connecting to ws://localhost:8000/connection/websocket?format=protobuf endpoint – then it's possible to send binary data to clients.","s":"Channel subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#channel-subscriptions","p":55},{"i":68,"t":"While Centrifuge mostly shines when you need channel semantics it's also possible to send any data to connection directly – to achieve bidirectional asynchronous communication, just what a native WebSocket provides. To send a message to a server one can use the send method on the client-side: centrifuge.send({\"input\": \"hello\"}); On the server-side data will be available inside a message handler: client.OnMessage(func(e centrifuge.MessageEvent) { log.Printf(\"message from client: %s\", e.Data)}) And vice-versa, to send data to a client use Send method of centrifuge.Client: client.Send([]byte(`{\"input\": \"hello\"}`)) To listen to it on the client-side: centrifuge.on('message', function(data) { console.log(data);});","s":"Async message passing","u":"/blog/2021/01/15/centrifuge-intro","h":"#async-message-passing","p":55},{"i":70,"t":"RPC is a primitive for sending a request from a client to a server and waiting for a response (in this case all communication still happens via asynchronous message passing internally, but Centrifuge takes care of matching response data to request previously sent). On client side it's as simple as: const resp = await centrifuge.namedRPC('my_method', {}); On server side RPC event handler should be set to make calls available: client.OnRPC(func(e centrifuge.RPCEvent, cb centrifuge.RPCCallback) { if e.Method == \"my_method\" { cb(centrifuge.RPCReply{Data: []byte(`{\"result\": \"42\"}`)}, nil) return } cb(centrifuge.RPCReply{}, centrifuge.ErrorMethodNotFound)}) Note, that it's possible to pass the name of RPC and depending on it and custom request params return different results to a client – just like a regular HTTP request but over asynchronous WebSocket (or SockJS) connection.","s":"RPC","u":"/blog/2021/01/15/centrifuge-intro","h":"#rpc","p":55},{"i":72,"t":"In many cases, a client is a source of knowledge which channels it wants to subscribe to on a specific application screen. But sometimes you want to control subscriptions to channels on a server-side. This is also possible in Centrifuge. It's possible to provide a slice of channels to subscribe connection to at the moment of connection establishment phase: node.OnConnecting(func(ctx context.Context, e centrifuge.ConnectEvent) (centrifuge.ConnectReply, error) { return centrifuge.ConnectReply{ Subscriptions: map[string]centrifuge.SubscribeOptions{ \"example\": {}, }, }, nil}) Note, that OnConnecting does not follow callback-style – this is because it can only happen once at the start of each connection – so there is no need to control operation concurrency. In this case on the client-side you will have access to messages published to channels by listening to on('publish') event: centrifuge.on('publish', function(msgCtx) { console.log(msgCtx);}); Also, centrifuge.Client has Subscribe and Unsubscribe methods so it's possible to subscribe/unsubscribe client to/from channel somewhere in the middle of its long WebSocket session.","s":"Server-side subscriptions","u":"/blog/2021/01/15/centrifuge-intro","h":"#server-side-subscriptions","p":55},{"i":74,"t":"Every time a message published to a channel it's possible to provide custom history options. For example: node.Publish( \"example\", []byte(`{\"input\": \"hello\"}`), centrifuge.WithHistory(300, time.Minute),) In this case, Centrifuge will maintain a windowed Publication cache for a channel - or in other words, maintain a publication stream. This stream will have time retention (one minute in the example above) and the maximum size will be limited to the value provided during Publish (300 in the example above). Every message inside a history stream has an incremental offset field. Also, a stream has a field called epoch – this is a unique identifier of stream generation - thus client will have a possibility to distinguish situations where a stream is completely removed and there is no guarantee that no messages have been lost in between even if offset looks fine. Client protocol provides a possibility to paginate over a stream from a certain position with a limit: const streamPosition = {'offset': 0, epoch: 'xyz'} resp = await sub.history({since: streamPosition, limit: 10}); Iteration over history stream is a new feature which is just merged into Centrifuge master branch and can only be used from Javascript client at the moment. Also, Centrifuge has an automatic message recovery feature. Automatic recovery is very useful in scenarios when tons of persistent connections start reconnecting at once. I already described why this is useful in one of my previous posts about Websocket scalability. In short – since WebSocket connections are stateful then at the moment of mass reconnect they can create a very big spike in load on your main application database. Such mass reconnects are a usual thing in practice - for example when you reload your load balancers or re-deploying the Websocket server (new code version). Of course, recovery can also be useful for regular short network disconnects - when a user travels in the subway for example. But you always need a way to load an actual state from the main application database in case of an unsuccessful recovery. To enable automatic recovery you can provide the Recover flag in subscribe options: client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) { cb(centrifuge.SubscribeReply{ Options: centrifuge.SubscribeOptions{ Recover: true, }, }, nil)}) Obviously, recovery will work only for channels where history stream maintained. The limitation in recovery is that all missed publications sent to client in one protocol frame – pagination is not supported during recovery process. This means that recovery is mostly effective for not too long offline time without tons of missed messages.","s":"Windowed history in channel","u":"/blog/2021/01/15/centrifuge-intro","h":"#windowed-history-in-channel","p":55},{"i":76,"t":"Another cool thing Centrifuge exposes to developers is online presence information for channels. Presence information contains a list of active channel subscribers. This is useful to show the online status of players in a game for example. Also, it's possible to turn on Join/Leave message feature inside channels: so each time connection subscribes to a channel all channel subscribers receive a Join message with client information (client ID, user ID). As soon as the client unsubscribes Leave message is sent to remaining channel subscribers with information who left a channel. Here is how to enable both online presence and join/leave features for a subscription to channel: client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) { cb(centrifuge.SubscribeReply{ Options: centrifuge.SubscribeOptions{ Presence: true, JoinLeave: true, }, }, nil)}) On a client-side then it's possible to call for the presence and setting event handler for join/leave messages. The important thing to be aware of when using Join/Leave messages is that this feature can dramatically increase CPU utilization and overall traffic in channels with a big number of active subscribers – since on every client connect/disconnect event such Join or Leave message must be sent to all subscribers. The advice here – avoid using Join/Leave messages or be ready to scale (Join/Leave messages scale well when adding more Centrifuge Nodes – more about scalability below). One more thing to remember is that online presence information can also be pretty expensive to request in channels with many active subscribers – since it returns information about all connections – thus payload in response can be large. To help a bit with this situation Centrifuge has a presence stats client API method. Presence stats only contain two counters: the number of active connections in the channel and amount of unique users in the channel. If you still need to somehow process online presence in rooms with a massive number of active subscribers – then I think you better do it in near real-time - for example with fast OLAP like ClickHouse.","s":"Online presence and presence stats","u":"/blog/2021/01/15/centrifuge-intro","h":"#online-presence-and-presence-stats","p":55},{"i":78,"t":"To be fair it's not too hard to implement most of the features above inside one in-memory process. Yes, it takes time, but the code is mostly straightforward. When it comes to scalability things tend to be a bit harder. Centrifuge designed with the idea in mind that one machine is not enough to handle all application WebSocket connections. Connections should scale over application backend instances, and it should be simple to add more application nodes when the amount of users (connections) grows. Centrifuge abstracts scalability over the Node instance and two interfaces: Broker interface and PresenceManager interface. A broker is responsible for PUB/SUB and streaming semantics: type Broker interface { Run(BrokerEventHandler) error Subscribe(ch string) error Unsubscribe(ch string) error Publish(ch string, data []byte, opts PublishOptions) (StreamPosition, error) PublishJoin(ch string, info *ClientInfo) error PublishLeave(ch string, info *ClientInfo) error PublishControl(data []byte, nodeID string) error History(ch string, filter HistoryFilter) ([]*Publication, StreamPosition, error) RemoveHistory(ch string) error} See full version with comments in source code. Every Centrifuge Node subscribes to channels via a broker. This provides a possibility to scale connections over many node instances – published messages will flow only to nodes with active channel subscribers. It's and important thing to combine PUB/SUB with history inside a Broker implementation to achieve an atomicity of saving message into history stream and publishing it to PUB/SUB with generated offset. PresenceManager is responsible for online presence information management: type PresenceManager interface { Presence(ch string) (map[string]*ClientInfo, error) PresenceStats(ch string) (PresenceStats, error) AddPresence(ch string, clientID string, info *ClientInfo, expire time.Duration) error RemovePresence(ch string, clientID string) error} Full code with comments. Broker and PresenceManager together form an Engine interface: type Engine interface { Broker PresenceManager} By default, Centrifuge uses MemoryEngine that does not use any external services but limits developers to using only one Centrifuge Node (i.e. one server instance). Memory Engine is fast and can be suitable for some scenarios - even in production (with configured backup instance) – but as soon as the number of connections grows – you may need to load balance connections to different server instances. Here comes the Redis Engine. Redis Engine utilizes Redis for Broker and PresenceManager parts. History cache saved to Redis STREAM or Redis LIST data structures. For presence, Centrifuge uses a combination of HASH and ZSET structures. Centrifuge tries to fully utilize the connection between Node and Redis by using pipelining where possible and smart batching technique. All operations done in a single RTT with the help of Lua scripts loaded automatically to Redis on engine start. Redis is pretty fast and will allow your app to scale to some limits. When Redis starts being a bottleneck it's possible to shard data over different Redis instances. Client-side consistent sharding is built-in in Centrifuge and allows scaling further. It's also possible to achieve Redis's high availability with built-in Sentinel support. Redis Cluster supported too. So Redis Engine covers many options to communicate with Redis deployed in different ways. At Avito we served about 800k active connections in the messenger app with ease using a slightly adapted Centrifuge Redis Engine, so an approach proved to be working for rather big applications. We will look at some more concrete numbers below in the performance section. Both Broker and PresenceManager are pluggable, so it's possible to replace them with alternative implementations. Examples show how to use Nats server for at most once only PUB/SUB together with Centrifuge. Also, we have an example of full-featured Engine for Tarantool database – Tarantool Engine shows even better throughput for history and presence operations than Redis-based Engine (up to 10x for some ops).","s":"Scalability aspects","u":"/blog/2021/01/15/centrifuge-intro","h":"#scalability-aspects","p":55},{"i":80,"t":"Since Centrifuge is a messaging system I also want to describe its order and message delivery guarantees. Message ordering in channels supported. As soon as you publish messages into channels one after another of course. Message delivery model is at most once by default. This is mostly comes from PUB/SUB model – message can be dropped on Centrifuge level if subscriber is offline or simply on broker level – since Redis PUB/SUB also works with at most once guarantee. Though if you maintain history stream inside a channel then things become a bit different. In this case you can tell Centrifuge to check client position inside stream. Since every publication has a unique incremental offset Centrifuge can track that client has correct offset inside a channel stream. If Centrifuge detects any missed messages it disconnects a client with special code – thus make it reconnect and recover messages from history stream. Since a message first saved to history stream and then published to PUB/SUB inside broker these mechanisms allow achieving at least once message delivery guarantee. Even if stream completely expired or dropped from broker memory Centrifuge will give a client a tip that messages could be lost – so client has a chance to restore state from a main application database.","s":"Order and delivery properties","u":"/blog/2021/01/15/centrifuge-intro","h":"#order-and-delivery-properties","p":55},{"i":82,"t":"Here I want to be fair with my readers – Centrifuge is not ideal. This is a project maintained mostly by one person at the moment with all consequences. This hits an ecosystem a lot, can make some design choices opinionated or non-optimal. I mentioned in the first post that Centrifuge built on top of the custom protocol. The protocol is based on a strict Protobuf schema, works with JSON and binary data transfer, supports many features. But – this means that to connect to the Centrifuge-based server developers have to use custom connectors that can speak with Centrifuge over its custom protocol. The difficulty here is that protocol is asynchronous. Asynchronous protocols are harder to implement than synchronous ones. Multiplexing frames allows achieving good performance and fully utilize a single connection – but it hurts simplicity. At this moment Centrifuge has client connectors for: centrifuge-js - Javascript client for a browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-mobile - for mobile development based on centrifuge-go and gomobile project centrifuge-swift - for iOS native development centrifuge-java - for Android native development and general Java centrifuge-dart - for Dart and Flutter Not all clients support all protocol features. Another drawback is that all clients do not have a persistent maintainer – I mostly maintain everything myself. Connectors can have non-idiomatic and pretty dumb code since I had no previous experience with mobile development, they lack proper tests and documentation. This is unfortunate. The good thing is that all connectors feel very similar, I am quickly releasing new versions when someone sends a pull request with improvements or bug fixes. So all connectors are alive. I maintain a feature matrix in connectors to let users understand what's supported. Actually feature support is pretty nice throughout all these connectors - there are only several things missing and not so much work required to make all connectors full-featured. But I really need help here. It will be a big mistake to not mention Centrifugo as a big plus for Centrifuge library ecosystem. Centrifugo is a server deployed in many projects throughout the world. Many features of Centrifuge library and its connectors have already been tested by Centrifugo users. One more thing to mention is that Centrifuge does not have v1 release. It still evolves – I believe that the most dramatic changes have already been made and backward compatibility issues will be minimal in the next releases – but can't say for sure.","s":"Ecosystem","u":"/blog/2021/01/15/centrifuge-intro","h":"#ecosystem","p":55},{"i":84,"t":"I made a test stand in Kubernetes with one million connections. I can't call this a proper benchmark – since in a benchmark your main goal is to destroy a system, in my test I just achieved some reasonable numbers on limited hardware. These numbers should give a good insight into a possible throughput, latency, and estimate hardware requirements (at least approximately). Connections landed on different server pods, 5 Redis instances have been used to scale connections between pods. The detailed test stand description can be found in Centrifugo documentation. Some quick conclusions are: One connection costs about 30kb of RAM Redis broker CPU utilization increases linearly with more messages traveling around 1 million connections with 500k delivered messages per second with 200ms delivery latency in 99 percentile can be served with hardware amount equal to one modern physical server machine. The possible amount of messages can vary a lot depending on the number of channel subscribers though.","s":"Performance","u":"/blog/2021/01/15/centrifuge-intro","h":"#performance","p":55},{"i":86,"t":"Centrifuge does not allow subscribing on the same channel twice inside a single connection. It's not simple to add due to design decisions made – though there was no single user report about this in seven years of Centrifugo/Centrifuge history. Centrifuge does not support wildcard subscriptions. Not only because I never needed this myself but also due to some design choices made – so be aware of this. SockJS fallback does not support binary data - only JSON. If you want to use binary in your application then you can only use WebSocket with Centrifuge - there is no built-in fallback transport in this case. SockJS also requires sticky session support from your load balancer to emulate a stateful bidirectional connection with its HTTP fallback transports. Ideally, Centrifuge will go away from SockJS at some point, maybe when WebTransport becomes mature so users will have a choice between WebTransport or WebSocket. Websocket permessage-deflate compression supported (thanks to Gorilla WebSocket), but it can be pretty expensive in terms of CPU utilization and memory usage – the overhead depends on usage pattern, it's pretty hard to estimate in numbers. As said above you cannot only rely on Centrifuge for state recovery – it's still required to have a way to fully load application state from the main database. Also, I am not very happy with current error and disconnect handling throughout the connector ecosystem – this can be improved though, and I have some ideas for the future.","s":"Limitations","u":"/blog/2021/01/15/centrifuge-intro","h":"#limitations","p":55},{"i":88,"t":"I am adding examples to _examples folder of Centrifuge repo. These examples completely cover Centrifuge API - including things not mentioned here. Check out the tips & tricks section of README – it contains some additional insights about an implementation.","s":"Examples","u":"/blog/2021/01/15/centrifuge-intro","h":"#examples","p":55},{"i":90,"t":"I think Centrifuge could be a nice alternative to socket.io - with a better performance, main server implementation in Go language, and even more builtin features to build real-time apps. Centrifuge ecosystem definitely needs more work, especially in client connectors area, tutorials, community, stabilizing API, etc. Centrifuge fits pretty well proprietary application development where time matters and deadlines are close, so developers tend to choose a ready solution instead of writing their own. I believe Centrifuge can be a great time saver here. For Centrifugo server users Centrifuge package provides a way to write a more flexible server code adapted for business requirements but still use the same real-time core and have the same protocol features.","s":"Conclusion","u":"/blog/2021/01/15/centrifuge-intro","h":"#conclusion","p":55},{"i":92,"t":"UPDATE: WebTransport spec is still evolving. Most information here is not actual anymore. For example the working group has no plan to implement both QuicTransport and HTTP3-based transports – only HTTP3 based WebTransport is going to be implemented. Maybe we will publish a follow-up of this post at some point.","s":"Experimenting with QUIC and WebTransport","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"","p":91},{"i":94,"t":"WebTransport is a new browser API offering low-latency, bidirectional, client-server messaging. If you have not heard about it before I suggest to first read a post called Experimenting with QuicTransport published recently on web.dev – it gives a nice overview to WebTransport and shows client-side code examples. Here we will concentrate on implementing server side. Some key points about WebTransport spec: WebTransport standard will provide a possibility to use streaming client-server communication using modern transports such as QUIC and HTTP/3 It can be a good alternative to WebSocket messaging, standard provides some capabilities that are not possible with current WebSocket spec: possibility to get rid of head-of-line blocking problems using individual streams for different data, the possibility to reuse a single connection to a server in different browser tabs WebTransport also defines an unreliable stream API using UDP datagrams (which is possible since QUIC is UDP-based) – which is what browsers did not have before without a rather complex WebRTC setup involving ICE, STUN, etc. This is sweet for in-browser real-time games. To help you figure out things here are links to current WebTransport specs: WebTransport overview – this spec gives an overview of WebTransport and provides requirements to transport layer WebTransport over QUIC – this spec describes QUIC-based transport for WebTransport WebTransport over HTTP/3 – this spec describes HTTP/3-based transport for WebTransport (actually HTTP/3 is a protocol defined on top of QUIC) At moment Chrome only implements trial possibility to try out WebTransport standard and only implements WebTransport over QUIC. Developers can initialize transport with code like this: const transport = new QuicTransport('quic-transport://localhost:4433/path'); In case of HTTP/3 transport one will use URL like 'https://localhost:4433/path' in transport constructor. All WebTransport underlying transports should support instantiation over URL – that's one of the spec requirements. I decided that this is a cool possibility to finally play with QUIC protocol and its Go implementation github.com/lucas-clemente/quic-go. danger Please keep in mind that all things described in this post are work in progress. WebTransport drafts, Quic-Go library, even QUIC protocol itself are subjects to change. You should not use it in production yet. Experimenting with QuicTransport post contains links to a client example and companion Python server implementation. We will use a linked client example to connect to a server that runs on localhost and uses github.com/lucas-clemente/quic-go library. To make our example work we need to open client example in Chrome, and actually, at this moment we need to install Chrome Canary. The reason behind this is that the quic-go library supports QUIC draft-29 while Chrome < 85 implements QuicTransport over draft-27. If you read this post at a time when Chrome stable 85 already released then most probably you don't need to install Canary release and just use your stable Chrome. We also need to generate self-signed certificates since WebTransport only works with a TLS layer, and we should make Chrome trust our certificates. Let's prepare our client environment before writing a server and first install Chrome Canary.","s":"Overview","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#overview","p":91},{"i":96,"t":"Go to https://www.google.com/intl/en/chrome/canary/, download and install Chrome Canary. We will use it to open client example. note If you have Chrome >= 85 then most probably you can skip this step.","s":"Install Chrome Canary","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#install-chrome-canary","p":91},{"i":98,"t":"Since WebTransport based on modern network transports like QUIC and HTTP/3 security is a keystone. For our experiment we will create a self-signed TLS certificate using openssl. Make sure you have openssl installed: $ which openssl/usr/bin/openssl Then run: openssl genrsa -des3 -passout pass:x -out server.pass.key 2048openssl rsa -passin pass:x -in server.pass.key -out server.keyrm server.pass.keyopenssl req -new -key server.key -out server.csr Set localhost for Common Name when asked. The self-signed TLS certificate generated from the server.key private key and server.csr files: openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt After these manipulations you should have server.crt and server.key files in your working directory. To help you with process here is my console output during these steps (click to open): ??? example \"My console output generating self-signed certificates\" ```bash$ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048Generating RSA private key, 2048 bit long modulus...........................................................................................+++.....................+++e is 65537 (0x10001)$ lsserver.pass.key$ openssl rsa -passin pass:x -in server.pass.key -out server.keywriting RSA key$ lsserver.key server.pass.key$ rm server.pass.key$ openssl req -new -key server.key -out server.csrYou are about to be asked to enter information that will be incorporatedinto your certificate request.What you are about to enter is what is called a Distinguished Name or a DN.There are quite a few fields but you can leave some blankFor some fields there will be a default value,If you enter '.', the field will be left blank.-----Country Name (2 letter code) []:RUState or Province Name (full name) []:Locality Name (eg, city) []:Organization Name (eg, company) []:Organizational Unit Name (eg, section) []:Common Name (eg, fully qualified host name) []:localhostEmail Address []:Please enter the following 'extra' attributesto be sent with your certificate requestA challenge password []:$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crtSignature oksubject=/C=RU/CN=localhostGetting Private key$ lsserver.crt server.csr server.key```","s":"Generate self-signed TLS certificates","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#generate-self-signed-tls-certificates","p":91},{"i":100,"t":"Now the last step. What we need to do is run Chrome Canary with some flags that will allow it to trust our self-signed certificates. I suppose there is an alternative way making Chrome trust your certificates, but I have not tried it. First let's find out a fingerprint of our cert: openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 In my case base64 fingerprint was pe2P0fQwecKFMc6kz3+Y5MuVwVwEtGXyST5vJeaOO/M=, yours will be different. Then run Chrome Canary with some additional flags that will make it trust out certs (close other Chrome Canary instances before running it): $ /Applications/Google\\ Chrome\\ Canary.app/Contents/MacOS/Google\\ Chrome\\ Canary \\ --origin-to-force-quic-on=localhost:4433 \\ --ignore-certificate-errors-spki-list=pe2P0fQwecKFMc6kz3+Y5MuVwVwEtGXyST5vJeaOO/M= This example is for MacOS, for your system see docs on how to run Chrome/Chromium with custom flags. Now you can open https://googlechrome.github.io/samples/quictransport/client.html URL in started browser and click Connect button. What? Connection not established? OK, this is fine since we need to run our server :)","s":"Run client example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#run-client-example","p":91},{"i":102,"t":"Maybe in future we will have libraries that are specified to work with WebTransport over QUIC or HTTP/3, but for now we should implement server manually. As said above we will use github.com/lucas-clemente/quic-go library to do this.","s":"Writing a QUIC server","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#writing-a-quic-server","p":91},{"i":104,"t":"First, let's define a simple skeleton for our server: package mainimport ( \"errors\" \"log\" \"github.com/lucas-clemente/quic-go\")// Config for WebTransportServerQuic.type Config struct { // ListenAddr sets an address to bind server to. ListenAddr string // TLSCertPath defines a path to .crt cert file. TLSCertPath string // TLSKeyPath defines a path to .key cert file TLSKeyPath string // AllowedOrigins represents list of allowed origins to connect from. AllowedOrigins []string}// WebTransportServerQuic can handle WebTransport QUIC connections according// to https://tools.ietf.org/html/draft-vvv-webtransport-quic-02.type WebTransportServerQuic struct { config Config}// NewWebTransportServerQuic creates new WebTransportServerQuic.func NewWebTransportServerQuic(config Config) *WebTransportServerQuic { return &WebTransportServerQuic{ config: config, }}// Run server.func (s *WebTransportServerQuic) Run() error { return errors.New(\"not implemented\")}func main() { server := NewWebTransportServerQuic(Config{ ListenAddr: \"0.0.0.0:4433\", TLSCertPath: \"server.crt\", TLSKeyPath: \"server.key\", AllowedOrigins: []string{\"localhost\", \"googlechrome.github.io\"}, }) if err := server.Run(); err != nil { log.Fatal(err) }}","s":"Server skeleton","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#server-skeleton","p":91},{"i":106,"t":"Let's concentrate on implementing Run method. We need to accept QUIC client connections. This can be done by creating quic.Listener instance and using its .Accept method to accept incoming client sessions. // Run server.func (s *WebTransportServerQuic) Run() error { listener, err := quic.ListenAddr(s.config.ListenAddr, s.generateTLSConfig(), nil) if err != nil { return err } for { sess, err := listener.Accept(context.Background()) if err != nil { return err } log.Printf(\"session accepted: %s\", sess.RemoteAddr().String()) go func() { defer func() { _ = sess.CloseWithError(0, \"bye\") log.Println(\"close session\") }() s.handleSession(sess) }() }}func (s *WebTransportServerQuic) handleSession(sess quic.Session) { // Not implemented yet. } An interesting thing to note is that QUIC allows closing connection with specific application-level integer code and custom string reason. Just like WebSocket if you worked with it. Also note, that we are starting our Listener with TLS configuration returned by s.generateTLSConfig() method. Let's take a closer look at how this method can be implemented. // https://tools.ietf.org/html/draft-vvv-webtransport-quic-02#section-3.1const alpnQuicTransport = \"wq-vvv-01\"func (s *WebTransportServerQuic) generateTLSConfig() *tls.Config { cert, err := tls.LoadX509KeyPair(s.config.TLSCertPath, s.config.TLSKeyPath) if err != nil { log.Fatal(err) } return &tls.Config{ Certificates: []tls.Certificate{cert}, NextProtos: []string{alpnQuicTransport}, }} Inside generateTLSConfig we load x509 certs from cert files generated above. WebTransport uses ALPN (Application-Layer Protocol Negotiation to prevent handshakes with a server that does not support WebTransport spec. This is just a string wq-vvv-01 inside NextProtos slice of our *tls.Config.","s":"Accept QUIC connections","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#accept-quic-connections","p":91},{"i":108,"t":"At this moment if you run a server and open a client example in Chrome then click Connect button – you should see that connection successfully established in event log area: Now if you try to send data to a server nothing will happen. That's because we have not implemented reading data from session streams. Streams in QUIC provide a lightweight, ordered byte-stream abstraction to an application. Streams can be unidirectional or bidirectional. Streams can be short-lived, streams can also be long-lived and can last the entire duration of a connection. Client example provides three possible ways to communicate with a server: Send a datagram Open a unidirectional stream Open a bidirectional stream Unfortunately, quic-go library does not support sending UDP datagrams at this moment. To do this quic-go should implement one more draft called An Unreliable Datagram Extension to QUIC. There is already an ongoing pull request that implements it. This means that it's too early for us to experiment with unreliable UDP WebTransport client-server communication in Go. By the way, the interesting facts about UDP over QUIC are that QUIC congestion control mechanism will still apply and QUIC datagrams can support acknowledgements. Implementing a unidirectional stream is possible with quic-go since the library supports creating and accepting unidirectional streams, but I'll leave this for a reader (though we will need accepting one unidirectional stream for parsing client indication anyway – see below). Here we will only concentrate on implementing a server for a bidirectional case. We are in the Centrifugo blog, and this is the most interesting type of stream for me personally.","s":"Connection Session handling","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#connection-session-handling","p":91},{"i":110,"t":"According to section-3.2 of Quic WebTransport spec in order to verify that the client's origin allowed connecting to the server, the user agent has to communicate the origin to the server. This is accomplished by sending a special message, called client indication, on stream 2, which is the first client-initiated unidirectional stream. Here we will implement this. In the beginning of our session handler we will accept a unidirectional stream initiated by a client. At moment spec defines two client indication keys: Origin and Path. In our case an origin value will be https://googlechrome.github.io and path will be /counter. Let's define some constants and structures: // client indication stream can not exceed 65535 bytes in length.// https://tools.ietf.org/html/draft-vvv-webtransport-quic-02#section-3.2const maxClientIndicationLength = 65535// define known client indication keys.type clientIndicationKey int16const ( clientIndicationKeyOrigin clientIndicationKey = 0 clientIndicationKeyPath = 1)// ClientIndication container.type ClientIndication struct { // Origin client indication value. Origin string // Path client indication value. Path string} Now what we should do is accept unidirectional stream inside session handler: func (s *WebTransportServerQuic) handleSession(sess quic.Session) { stream, err := sess.AcceptUniStream(context.Background()) if err != nil { log.Println(err) return } log.Printf(\"uni stream accepted, id: %d\", stream.StreamID()) indication, err := receiveClientIndication(stream) if err != nil { log.Println(err) return } log.Printf(\"client indication: %+v\", indication) if err := s.validateClientIndication(indication); err != nil { log.Println(err) return } // this method blocks. if err := s.communicate(sess); err != nil { log.Println(err) }}func receiveClientIndication(stream quic.ReceiveStream) (ClientIndication, error) { return ClientIndication{}, errors.New(\"not implemented yet\")}func (s *WebTransportServerQuic) validateClientIndication(indication ClientIndication) error { return errors.New(\"not implemented yet\")}func (s *WebTransportServerQuic) communicate(sess quic.Session) error { return errors.New(\"not implemented yet\")} As you can see to accept a unidirectional stream with data we can use .AcceptUniStream method of quic.Session. After accepting a stream we should read client indication data from it. According to spec it will contain a client indication in the following format: 0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Key (16) | Length (16) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Value (*) ...+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The code below parses client indication out of a stream data, we decode key-value pairs from uni stream until an end of stream (indicated by EOF): func receiveClientIndication(stream quic.ReceiveStream) (ClientIndication, error) { var clientIndication ClientIndication // read no more than maxClientIndicationLength bytes. reader := io.LimitReader(stream, maxClientIndicationLength) done := false for { if done { break } var key int16 err := binary.Read(reader, binary.BigEndian, &key) if err != nil { if err == io.EOF { done = true } else { return clientIndication, err } } var valueLength int16 err = binary.Read(reader, binary.BigEndian, &valueLength) if err != nil { return clientIndication, err } buf := make([]byte, valueLength) n, err := reader.Read(buf) if err != nil { if err == io.EOF { // still need to process indication value. done = true } else { return clientIndication, err } } if int16(n) != valueLength { return clientIndication, errors.New(\"read less than expected\") } value := string(buf) switch clientIndicationKey(key) { case clientIndicationKeyOrigin: clientIndication.Origin = value case clientIndicationKeyPath: clientIndication.Path = value default: log.Printf(\"skip unknown client indication key: %d: %s\", key, value) } } return clientIndication, nil} We also validate Origin inside validateClientIndication method of our server: var errBadOrigin = errors.New(\"bad origin\")func (s *WebTransportServerQuic) validateClientIndication(indication ClientIndication) error { u, err := url.Parse(indication.Origin) if err != nil { return errBadOrigin } if !stringInSlice(u.Host, s.config.AllowedOrigins) { return errBadOrigin } return nil}func stringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false} Do you have stringInSlice function in every Go project? I do :)","s":"Parsing client indication","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#parsing-client-indication","p":91},{"i":112,"t":"The final part here is accepting a bidirectional stream from a client, reading it, and sending responses back. Here we will just echo everything a client sends to a server back to a client. You can implement whatever bidirectional communication you want actually. Very similar to unidirectional case we can call .AcceptStream method of session to accept a bidirectional stream. func (s *WebTransportServerQuic) communicate(sess quic.Session) error { for { stream, err := sess.AcceptStream(context.Background()) if err != nil { return err } log.Printf(\"stream accepted: %d\", stream.StreamID()) if _, err := io.Copy(stream, stream); err != nil { return err } }} When you press Send button in client example it creates a bidirectional stream, sends data to it, then closes stream. Thus our code is sufficient. For a more complex communication that involves many concurrent streams you will have to write a more complex code that allows working with streams concurrently on server side.","s":"Communicating over bidirectional streams","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#communicating-over-bidirectional-streams","p":91},{"i":114,"t":"Full server code can be found in a Gist. Again – this is a toy example based on things that all work in progress.","s":"Full server example","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#full-server-example","p":91},{"i":116,"t":"WebTransport is an interesting technology that can open new possibilities in modern Web development. At this moment it's possible to play with it using QUIC transport – here we looked at how one can do that. Though we still have to wait a bit until all these things will be suitable for production usage. Also, even when ready we will still have to think about WebTransport fallback options – since wide adoption of browsers that support some new technology and infrastructure takes time. Actually WebTransport spec authors consider fallback options in design. This was mentioned in IETF slides (PDF, 2.6MB), but I have not found any additional information beyond that. Personally, I think the most exciting thing about WebTransport is the possibility to exchange UDP datagrams, which can help a lot to in-browser gaming. Unfortunately, we can't test it at this moment with Go (but it's already possible using Python as server as shown in the example). WebTransport could be a nice candidate for a new Centrifugo transport next to WebSocket and SockJS – time will show.","s":"Conclusion","u":"/blog/2020/10/16/experimenting-with-quic-transport","h":"#conclusion","p":91},{"i":118,"t":"Today we are excited to announce the next generation of Centrifugo – Centrifugo v4. The release takes Centrifugo to the next level in terms of client protocol performance, WebSocket fallback simplicity, SDK ecosystem and channel security model. It also comes with a couple of cutting-edge technologies to experiment with such as HTTP/3 and WebTransport. About Centrifugo If you've never heard of Centrifugo before, it's an open-source scalable real-time messaging server written in Go language. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, GRPC, SockJS). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server. Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer. Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.","s":"Centrifugo v4 released – a little revolution","u":"/blog/2022/07/19/centrifugo-v4-released","h":"","p":117},{"i":120,"t":"Let's start from looking back a bit. Centrifugo v3 was released last year. It had a great list of improvements – like unidirectional transports support (EventSource, HTTP-streaming and GRPC), GRPC transport for proxy, history iteration API, faster JSON protocol, super-fast but experimental Tarantool engine implementation, and others. During the Centrifugo v3 lifecycle we added even more JSON protocol optimizations and introduced a granular proxy mode. Experimental Tarantool engine has also evolved a bit. But Centrifugo v3 did not contain anything... let's say revolutional. Revolutional for Centrifugo itself, community, or even the entire field of open-source real-time messaging. With this release, we feel that we bring innovation to the ecosystem. Now let's talk about it and introduce all the major things of the brand new v4 release.","s":"Centrifugo v3 flashbacks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#centrifugo-v3-flashbacks","p":117},{"i":122,"t":"The most challenging part of Centrifugo project is not a server itself. Client SDKs are the hardest part of the ecosystem. We try to time additional improvements to the SDKs with each major release of the server. But this time the SDKs are the centerpiece of the v4 release. Centrifugo uses bidirectional asynchronous protocol between client and server. On top of this protocol SDK provides a request-response over an asynchronous connection, reconnection logic, subscription management and multiplexing, timeout and error handling, ping-pong, token refresh, etc. Some of these things are not that trivial to implement. And all this should be implemented in different programming languages. As you may know, we have official real-time SDKs in Javascript, Dart, Swift, Java and Go. While implementing the same protocol and same functions, all SDKs behaved slightly differently. That was the result of the missing SDK specification. Without a strict SDK spec, it was hard to document things, hard to explain the exact details of the real-time SDK behavior. What we did earlier in the Centrifugo documentation – was pointing users to specific SDK Github repo to look for behaviour details. The coolest thing about Centrifugo v4 is the next generation SDK API. We now have a client SDK API specification. It's a source of truth for SDKs behavior which try to follow the spec closely. The new SDK API is the result of several iterations and reflections on possible states, transitions, token refresh mechanism, etc. Users in our Telegram group may remember how it all started: And after several iterations these prototypes turned into working mechanisms with well-defined behaviour: A few things that have been revised from the ground up: Client states, transitions, events Subscription states, transitions, events Connection and subscription token refresh behavior Ping-pong behavior (see details below) Resubscribe logic (SDKs can now resubscribe with backoff) Error handling Unified backoff behavior (based on full jitter technique) We now also have a separation between temporary and non-temporary protocol errors – this allows us to handle subscription internal server errors on the SDK level, making subscriptions more resilient, with automatic resubscriptions, and to ensure individual subscription failures do not affect the entire connection. The mechanics described in the client SDK API specification are now implemented in all of our official SDKs. The SDKs now support all major client protocol features that currently exist. We believe this is a big step forward for the Centrifugo ecosystem and community.","s":"Unified client SDK API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#unified-client-sdk-api","p":117},{"i":124,"t":"WebSocket is supported almost everywhere these days. But there is a case that we believe is the last one preventing users to connect over WebSocket - corporate proxies. With the root certificate installed on employee computer machines, these proxies can block WebSocket traffic, even if it's wrapped in a TLS layer. That's really annoying, and often developers choose to not support clients connecting from such \"broken\" environments at all. Prior to v4, Centrifugo users could use the SockJS polyfill library to fill this gap. SockJS is great software – stable and field proven. It is still used by some huge real-time messaging players out there to polyfill the WebSocket transport. But SockJS is an extra frontend dependency with a bunch of legacy transports, and the future of it is unknown. SockJS comes with a notable overhead – it's an aditional protocol wrapper, consumes more memory per connection on a server (at least when using SockJS-Go library – the only choice for implementing SockJS server in Go language these days). When using SockJS, Centrifugo users were losing the ability to use our main pure WebSocket transport because SockJS uses its own WebSocket implementation on a server side. SockJS does not support binary data transfer – only JSON format can be used with it. As you know, our main WebSocket transport works fine with binary in case of using Protobuf protocol format. So with SockJS we don't have fallback for WebSocket with a binary data transfer. And finally, if you want to use SockJS with a distributed backend, you must enable sticky session support on the load-balancer level. This way you can point requests from the client to the server to the correct server node – the one which maintains a persistent unidirectional HTTP connection. We danced around the idea of replacing SockJS for a long time. But only now we are ready to provide our alternative to it – meet Centrifugo own bidirectional emulation layer. It's based on two additional transports: HTTP-streaming (using modern browser ReadableStream API in JavaScript, supports both binary Protobuf and JSON transfer) Eventsource (Server-Sent Events, SSE) – while a bit older choice and works with JSON only EventSource transport is loved by many developers and can provide fallback in slightly older browsers which don't have ReadableStream, so we implemented bidirectional emulation with it too. So when the fallback is used, you always have a real-time, persistent connection in server -> to -> client direction. Requests in client -> to -> server direction are regular HTTP – similar to how SockJS works. But our bidirectional emulation layer does not require sticky sessions – Centrifugo can proxy client-to-server requests to the correct node in the cluster. Having sticky sessions is an optimization for Centrifugo bidirectional emulation layer, not a requirement. We believe that this is a game changer for our users – no need to bother about proper load balancing, especially since in most cases 95% or even more users will be able to connect using the WebSocket transport. Here is a simplified diagram of how it works: The bidirectional emulation layer is only supported by the Javascript SDK (centrifuge-js) – as we think fallbacks mostly make sense for browsers. If we find use cases where other SDKs can benefit from HTTP based transport – we can expand on them later. Let's look at example of using this feature from the Javascript side. To use fallbacks, all you need to do is to set up a list of desired transports with endpoints: const transports = [ { transport: 'websocket', endpoint: 'wss://your_centrifugo.com/connection/websocket' }, { transport: 'http_stream', endpoint: 'https://your_centrifugo.com/connection/http_stream' }, { transport: 'sse', endpoint: 'https://your_centrifugo.com/connection/sse' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() note We are using explicit transport endpoints in the above example due to the fact that transport endpoints can be configured separately in Centrifugo – there is no single entry point for all transports. Like the one in Socket.IO or SockJS when developer can only point client to the base address. In Centrifugo case, we are requesting an explicit transport/endpoint configuration from the SDK user. By the way, a few advantages of HTTP-based transport over WebSocket: Sessions can be automatically multiplexed within a single connection by the browser when the server is running over HTTP/2, while with WebSocket browsers open a separate connection in each browser tab Better compression support (may be enabled on load balancer level) WebSocket requires special configuration in some load balancers to get started (ex. Nginx) SockJS is still supported by Centrifugo and centrifuge-js, but it's now DEPRECATED.","s":"Modern WebSocket emulation in Javascript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#modern-websocket-emulation-in-javascript","p":117},{"i":126,"t":"Not only the API of client SDK has changed, but also the format of Centrifugo protocol messages. New format is more human-readable (in JSON case, of course), has a more compact ping message size (more on that below). The client protocol is now one-shot encode/decode compatible. Previously, Centrifugo protocol had a layered structure and we had to encode some messages before appending them to the top-level message. Or decode two or three times to unwrap the message envelope. To achieve good performance when encoding and decoding client protocol messages, Centrifugo had to use various optimization techniques – like buffer memory pools, byte slice memory pools. By restructuring the message format, we were able to avoid layering, which allowed us to slightly increase the performance of encoding/decoding without additional optimization tricks. We also simplified the client protocol documentation overview a bit.","s":"No layering in client protocol","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#no-layering-in-client-protocol","p":117},{"i":128,"t":"In many cases in practice (when dealing with persistent connections like WebSocket), pings and pongs are the most dominant types of messages passed between client and server. Your application may have many concurrent connections, but only a few of them receive the useful payload. But at the same time, we still need to send pings and respond with pongs. Thus, optimizing the ping-pong process can significantly reduce server resource usage. One optimization comes from the revised PING-PONG behaviour. Previous versions of Centrifugo and SDKs sent ping/pong in both \"client->to->server\" and \"server->to->client\" directions (for WebSocket transport). This allowed finding non-active connections on both client and server sides. In Centrifugo v4 we only send pings from a server to a client and expect pong from a client. On the client-side, we have a timer which fires if there hasn't been a ping from the server within the configured time, so we still have a way to detect closed connections. Sending pings only in one direction results in 2 times less ping-pong messages - and this should be really noticable for Centrifugo installations with thousands of concurrent connections. In our experiments with 10k connections, server CPU usage was reduced by 30% compared to Centrifugo v3. Pings and pongs are application-level messages. Ping is just an empty asynchronous reply – for example in JSON case it's a 2-byte message: {}. Pong is an empty command – also, {} in JSON case. Having application-level pings from the server also allows unifying the PING format for all unidirectional transports. Another improvement is that Centrifugo now randomizes the time it sends first ping to the client (but no longer than the configured ping interval). This allows to spread ping-pongs in time, providing a smoother CPU profile, especially after a massive reconnect scenario.","s":"Redesigned PING-PONG","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#redesigned-ping-pong","p":117},{"i":130,"t":"Data security and privacy are more important than ever in today's world. And as Centrifugo becomes more popular and widely used, the need to be secure by default only increases. Previously, by default, clients could subcribe to all channels in a namespace (except private channels, which are now revised – see details below). It was possible to use \"protected\": true option to make namespace protected, but we are not sure if everyone did that. This is extra configuration and additional knowledge on how Centrifugo works. Also, a common confusion we ran into: if server-side subscriptions were dictated by a connection JWT, many users would expect client-side subscriptions to those channels to not work. But without the protected option enabled, this was not the case. In Centrifugo v4, by default, it is not possible to subscribe to a channel in a namespace. The namespace must be configured to allow subscriptions from clients, or token authorization must be used. There are a bunch of new namespace options to tune the namespace behavior. Also the ability to provide a regular expression for channels in the namespace. The new permission-related channel option names better reflect the purpose of the option. For example, compare \"publish\": true and \"allow_publish_for_client\": true. The second one is more readable and provides a better understanding of the effect once turned on. Centrifugo is now more strict when checking channel name. Only ASCII symbols allowed – it was already mentioned in docs before, but wasn't actually enforced. Now we are fixing this. We understand that these changes will make running Centrifugo more of a challenge, especially when all you want is a public access to all the channels without worrying too much about permissions. It's still possible to achieve, but now the intent must be expicitly expressed in the config. Check out the updated documentation about channels and namespaces. Our v4 migration guide contains an automatic converter for channel namespace options.","s":"Secure by default channel namespaces","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#secure-by-default-channel-namespaces","p":117},{"i":132,"t":"A private channel is a special channel starting with $ that could not be subscribed to without a subscription JWT. Prior to v4, having a known prefix allowed us to distinguish between public channels and private channels. But since namespaces are now non-public by default, this distinction is not really important. This means 2 things: it's now possible to subscribe to any channel by having a valid subscription JWT (not just those that start with $) channels beginning with $ can only be subscribed with a subscription JWT, even if they belong to a namespace where subscriptions allowed for all clients. This is for security compatibility between v3 and v4. Another notable change in a subscription JWT – client claim is now DEPRECATED. There is no need to put it in the subscription token anymore. Centrifugo supports it only for backwards compatibility, but it will be completely removed in the future releases. The reason we're removing client claim is actually interesting. Due to the fact that client claim was a required part of the subscription JWT applications could run into a situation where during the massive reconnect scenario (say, million connections reconnect) many requests for new subscription tokens can be generated because the subscription token must contain the client ID generated by Centrifugo for the new connection. That could make it unusually hard for the application backend to handle the load. With a connection JWT we had no such problem – as connections could simply reuse the previous token to reconnect to Centrifugo. Now the subscription token behaves just like the connection token, so we get a scalable solution for token-based subscriptions as well. What's more, this change paved the way for another big improvement...","s":"Private channel concept revised","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#private-channel-concept-revised","p":117},{"i":134,"t":"The improvement we just mentioned is called optimistic subscriptions. If any of you are familiar with the QUIC protocol, then optimistic subscriptions are somewhat similar to the 0-RTT feature in QUIC. The idea is simple – we can include subscription commands to the first frame sent to the server. Previously, we sent subscriptions only after receiving a successful Connect Reply to a Connect Command from a server. But with the new changes in token behaviour, it seems so logical to put subscribe commands within the initial connect frame. Especially since Centrifugo protocol always supported batching of commands. Even token-based subscriptions can now be included into the initial frame during reconnect process, since the previous token can be reused now. The benefit is awesome – in most scenarios, we save one RTT of latency when connecting to Centrifugo and subscribing to channels (which is actually the most common way to use Centrifugo). While not visible on localhost, this is pretty important in real-life. And this is less syscalls for the server after all, resulting in less CPU usage. Optimistic subscriptions are also great for bidirectional emulation with HTTP, as they avoid the long path of proxying a request to the correct Centrifugo node when connecting. Optimistic subscriptions are now only part of centrifuge-js. At some point, we plan to roll out this important optimization to all other client SDKs.","s":"Optimistic subscriptions","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#optimistic-subscriptions","p":117},{"i":136,"t":"The channel capabilities feature is introduced as part of Centrifugo PRO. Initially, we aimed to make it a part of the OSS version. But the lack of feedback on this feature made us nervous it's really needed. So adding it to PRO, where we still have room to evaluate the idea, seemed like the safer decision at the moment. Centrifugo allows configuring channel permissions on a per-namespace level. When creating a new real-time feature, it is recommended to create a new namespace for it and configure permissions. But to achieve a better channel permission control within a namespace the Channel capabilities can be used now. The channel capability feature provides a possibility to set capabilities on an individual connection basis, or an individual channel subscription basis. For example, in a connection JWT developers can set sth like: { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] } ]} And this tells Centrifugo that the connection is able to subscribe on channels news or user_42 using client-side subscriptionsat any time while the connection is active. Centrifugo also supports wildcard and regex channel matches. Subscription JWT can provide capabilities for the channel too, so permissions may be controlled on an individual subscription basis, ex. the ability to publish and call history API may be expressed with allow claim in subscription JWT: { \"allow\": [\"pub\", \"hst\"]} Read more about this mechanism in Channel capabilities chapter.","s":"Channel capabilities","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#channel-capabilities","p":117},{"i":138,"t":"Another addition to Centrifugo PRO is the improved connection API. Previously, we could only return all connections from a specific user. The API now supports filtering all connections: by user ID, by subscribed channel, by additional meta information attached to the connection. The filtering works by user ID or with a help of CEL expressions (Common Expression Language). CEL expressions provide a developer-friendly, fast and secure (as they are not Turing-complete) way to evaluate some conditions. They are used in some Google services (ex. Firebase), in Envoy RBAC configuration, etc. If you've never seen it before – take a look, cool project. We are also evaluating how to use CEL expressions for a dynamic and efficient channel permission checks, but that's an early story. The connections API call result contains more useful information: a list of client's active channels, information about the tokens used to connect and subscribe, meta information attached to the connection.","s":"Better connections API","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#better-connections-api","p":117},{"i":140,"t":"It's no secret that centrifuge-js is the most popular SDK in the Centrifugo ecosystem. We put additional love to it – and centrifuge-js is now fully written in Typescript ❤️ This was a long awaited improvement, and it finally happened! The entire public API is strictly typed. The cool thing is that even EventEmitter events and event handlers are the subject to type checks - this should drastically simplify and speedup development and also help to reduce error possibility.","s":"Javascript client moved to TypeScript","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#javascript-client-moved-to-typescript","p":117},{"i":142,"t":"Centrifugo v4 has an experimental HTTP/3 support. Once TLS is enabled and \"http3\": true option is set all the endpoints on an external port will be served by a HTTP/3 server based on lucas-clemente/quic-go implementation. It's worth noting that WebSocket will still use HTTP/1.1 for its Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and EventSource should work just fine with HTTP/3. HTTP/3 does not currently work with our ACME autocert TLS - i.e. you need to explicitly provide paths to cert and key files as described here.","s":"Experimenting with HTTP/3","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-http3","p":117},{"i":144,"t":"Having HTTP/3 on board allowed us to make one more thing. Some of you may remember the post Experimenting with QUIC and WebTransport published in our blog before. We danced around the idea to add WebTransport to Centrifugo since then. WebTransport IETF specification is still a draft, it changed a lot since our first blog post about it. But WebTransport object is already part of Chrome (since v97) and things seem to be very close to the release. So we added experimental WebTransport support to Centrifugo v4. This is made possible with the help of the marten-seemann/webtransport-go library. To use WebTransport you need to run HTTP/3 experimental server and enable WebTransport endpoint with \"webtransport\": true option in the configuration. Then you can connect to that endpoint using centrifuge-js. For example, let's enable WebTransport and use WebSocket as a fallback option: const transports = [ { transport: 'webtransport', endpoint: 'https://your_centrifugo.com/connection/webtransport' }, { transport: 'websocket', endpoint: 'wss://your_centrifugo.com/connection/websocket' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in HTTP/3 and specifically WebTransport non-TLS communication is simply not supported by the specification. In Centrifugo case, we utilize the bidirectional reliable stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication formats are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional. Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification we can not recommended it for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket, now we are more confident that Centrifugo will be able to provide a proper support of it.","s":"Experimenting with WebTransport","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#experimenting-with-webtransport","p":117},{"i":146,"t":"The migration guide contains steps to upgrade your Centrifugo from version 3 to version 4. While there are many changes in the v4 release, it should be possible to migrate to Centrifugo v4 without changing the code on the client side at all. And then, after updating the server, gradually update the client-side to the latest version of the stack.","s":"Migration guide","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#migration-guide","p":117},{"i":148,"t":"To sum it up, here are some benefits of Centrifugo v4: unified experience thoughout application frontend environments an optimized protocol which is generally faster, more compact and human-readable in JSON case, provides more resilient behavior for subscriptions revised channel namespace security model, more granular permission control more efficient and flexible use of subscription tokens better initial latency – thanks to optimistic subscriptions and the ability to pre-create subscription tokens (as the client claim not needed anymore) the ability to use more efficient WebSocket bidirectional emulation in the browser without having to worry about sticky sessions, unless you want to optimize the real-time infrastructure That's it. We now begin the era of v4 and it is going to be awesome, no doubt.","s":"Conclusion","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#conclusion","p":117},{"i":150,"t":"The release contains many changes that strongly affect developing with Centrifugo. And of course you may have some questions or issues regarding new or changed concepts. Join our communities in Telegram (the most active) and Discord: Enjoy Centrifugo v4, and let the Centrifugal force be with you.","s":"Join community","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#join-community","p":117},{"i":152,"t":"The refactoring of client SDKs and introducing unified behavior based on the common spec was the hardest part of Centrifugo v4 release. Many thanks to Vitaly Puzrin (who is the author of several popular open-source libraries such as markdown-it, fontello, and others). We had a series of super productive sessions with him on client SDK API design. Some great ideas emerged from these sessions and the result seems like a huge step forward for Centrifugal projects. Also, thanks to Anton Silischev who helped a lot with WebTransport prototypes earlier this year, so we could quickly adopt WebTransport for v4. tip As some of you know, Centrifugo server is built on top of the Centrifuge library for Go. Most of the optimizations and improvements described here are now also part of Centrifuge library. With its new unified SDK behavior and bidirectional emulation layer, it seems a solid alternative to Socket.IO in the Go language ecosystem. In some cases, Centrifuge library can be a more flexible solution than Centrifugo, since Centrifugo (as a standalone server) dictates some mechanics and rules that must be followed. In the case of Centrifugo, the business logic must live on the application backend side, with Centrifuge library it can be kept closer to the real-time transport layer. Attributions This post used images from freepik.com: background by liuzishan. Also image by kenshinstock.","s":"Special thanks","u":"/blog/2022/07/19/centrifugo-v4-released","h":"#special-thanks","p":117},{"i":154,"t":"In this tutorial, we will create a multi-room chat server using Laravel framework and Centrifugo real-time messaging server. Authenticated users of our chat app will be able to create new chat rooms, join existing rooms and instantly communicate inside rooms with the help of Centrifugo WebSocket real-time transport. caution This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step.","s":"Building a multi-room chat application with Laravel and Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"","p":153},{"i":156,"t":"The result will look like this: Sorry, your browser doesn't support embedded video. For the backend, we are using Laravel (version 8.65) as one of the most popular PHP frameworks. Centrifugo v3 will accept WebSocket client connections. And we will implement an integration layer between Laravel and Centrifugo. For CSS styles we are using recently released Bootstrap 5. Also, some vanilla JS instead of frameworks like React/Vue/whatever to make frontend Javascript code simple – so most developers out there could understand the mechanics. We are also using a bit old-fashioned server rendering here where server renders templates for different room routes (URLs) – i.e. our app is not a SPA app – mostly for the same reasons: to keep example short and let reader focus on Centrifugo and Laravel integration parts. To generate fake user avatars we are requesting images from https://robohash.org/ which can generate unique robot puctures based on some input string (username in our case). Robots like to chat with each other! tip We also have some ideas on further possible app improvements at the end of this post.","s":"Application overview","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-overview","p":153},{"i":158,"t":"Why would Laravel developers want to integrate a project with Centrifugo for real-time messaging functionality? That's a good question. There are several points which could be a good motivation: Centrifugo is open-source and self-hosted. So you can run it on your own infrastructure. Popular Laravel real-time broadcasting intergrations (Pusher and Ably) are paid cloud solutions. At scale Centrifugo will cost you less than cloud solutions. Of course cloud solutions do not require additional server setup – but everything is a trade-off right? So you should decide for youself. Centrifugo is fast and scales well. It has an optimized Redis Engine with client-side sharding and Redis Cluster support. Centrifugo can also scale with KeyDB, Nats, or Tarantool. So it's possible to handle millions of connections distributed over different Centrifugo nodes. Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for self-hosted real-time servers that scale to many nodes (like fast message history cache, or maintaining single user connection, both client-side and server-side subscriptions, etc). Centrifugo is lightweight, single binary server which works as a separate service – it can be a universal tool in the developer's pocket, can migrate with you from one project to another, no matter what programming language or framework is used for business logic. Hope this makes sense as a good motivation to give Centrifugo a try in your Laravel project. Let's get started!","s":"Why integrate Laravel with Centrifugo?","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#why-integrate-laravel-with-centrifugo","p":153},{"i":160,"t":"For the convenience of working with the example, we wrapped the end result into docker compose. To start the app clone examples repo, cd into v3/php_laravel_chat_tutorial directory and run: docker compose up At the first launch, the necessary images will be downloaded (will take some time and network bytes). When the main service is started, you should see something like this in container logs: ...app | Database seeding completed successfully.app | [10-Dec-2021 12:25:05] NOTICE: fpm is running, pid 112app | [10-Dec-2021 12:25:05] NOTICE: ready to handle connections Then go to http://localhost/ – you should see: Register (using some fake credentials) or sign up – and proceed to the chat rooms. Pay attention to the configuration of Centrifugo and Nginx. Also, on entrypoint which does some things: dependencies are installed via composer copying settings from .env.example db migrations are performed and the necessary npm packages are installed php-fpm starts","s":"Setup and start a project","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#setup-and-start-a-project","p":153},{"i":162,"t":"We assume you already familar with Laravel concepts, so we will just point you to some core aspects of the Laravel application structure and will pay more attention to Centrifugo integration parts.","s":"Application structure","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#application-structure","p":153},{"i":164,"t":"After the first launch of the application, all settings will be copied from the file .env.example to .env. Next, we will take a closer look at some settings.","s":"Environment settings","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#environment-settings","p":153},{"i":166,"t":"You can view the database structure here. We will use the following tables which will be then translated to the application models: Laravel standard user authentication tables. See https://laravel.com/docs/8.x/authentication. In the service we are using Laravel Breeze. For more information see official docs. rooms table. Basically - describes different rooms in the app every user can create. rooms many-to-many relation to users. Allows to add users into rooms when join button clicked or automatically upon room creation. messages. Keeps message history in rooms.","s":"Database migrations and models","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#database-migrations-and-models","p":153},{"i":168,"t":"For broadcasting we are using laravel-centrifugo library. It helps to simplify interaction between Laravel and Centrifugo by providing some convenient wrappers. Step-by-step configuration can be viewed in the readme file of this library. Pay attention to the CENTRIFUGO_API_KEY setting. It is used to send API requests from Laravel to Centrifugo and must match in .env and centrifugo.json files. And we also telling laravel-centrifugo the URL of Centrifugo. That's all we need to configure for this example app. See more information about Laravel broadcasting here. tip As an alternative to laravel-centrifugo, you can use phpcent – it's an official generic API client which allows publishing to Centrifugo HTTP API. But it does know nothing about Laravel broadcasting specifics.","s":"Broadcasting","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#broadcasting","p":153},{"i":170,"t":"When user opens a chat app it connects to Centrifugo over WebSocket transport. Let's take a closer look at Centrifugo server configuration file we use for this example app: { \"port\": 8000, \"engine\": \"memory\", \"api_key\": \"some-long-api-key-which-you-should-keep-secret\", \"allowed_origins\": [ \"http://localhost\", ], \"proxy_connect_endpoint\": \"http://nginx/centrifugo/connect/\", \"proxy_http_headers\": [ \"Cookie\" ], \"namespaces\": [ { \"name\": \"personal\" } ]} This configuration defines a connect proxy endpoint which is targeting Nginx and then proxied to Laravel. Centrifugo will proxy Cookie header of WebSocket HTTP Upgrade requests to Laravel – this allows using native Laravel authentication. We also defined a \"personal\" namespace – we will subscribe each user to a personal channel in this namespace inside connect proxy handler. Using namespaces for different real-time features is one of Centrifugo best-practices. Allowed origins must be properly set to prevent cross-site WebSocket connection hijacking.","s":"Interaction with Centrifugo","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#interaction-with-centrifugo","p":153},{"i":172,"t":"To use native Laravel user authentication middlewares, we will use Centrifugo proxy feature. When user connects to Centrifugo it's connection attempt will be transformed into HTTP request from Centrifugo to Laravel and will hit the connect proxy controller: class CentrifugoProxyController extends Controller{ public function connect() { return new JsonResponse([ 'result' => [ 'user' => (string) Auth::user()->id, 'channels' => [\"personal:#\".Auth::user()->id], ] ]); }} This controller protected by auth middleware. Since Centrifugo proxies Cookie header of initial WebSocket HTTP Upgrade request Laravel auth layer will work just fine. So in a controller you already has access to the current authenticated user. In the response from controller we tell Centrifugo the ID of connecting user and subscribe user to its personal channel (using user-limited channel feature of Centrifugo). Returning a channel in such way will subscribe user to it using server-side subscriptions mechanism. tip Note, that in our chat app we are using a single personal channel for each user to receive real-time updates from all rooms. We are not creating separate subscriptions for each room user joined too. This will allow us to scale more easily in the future, and basically the only viable solution in case of room list pagination in chat application like this. It does not mean you can not combine personal user channels and separate room channels for different tasks though. Some additional tips can be found in Centrifugo FAQ.","s":"Connect proxy controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#connect-proxy-controller","p":153},{"i":174,"t":"In RoomController we perform various actions with rooms: displaying rooms create rooms join users to rooms publish messages When we publish a message in a room, we send a message to the personal channel of all users joined to the room using the broadcast method of Centrifugo API. It allows publishing the same message into many channels. $message = Message::create([ 'sender_id' => Auth::user()->id, 'message' => $requestData[\"message\"], 'room_id' => $id,]);$room = Room::with('users')->find($id);$channels = [];foreach ($room->users as $user) { $channels[] = \"personal:#\" . $user->id;}$this->centrifugo->broadcast($channels, [ \"text\" => $message->message, \"createdAt\" => $message->created_at->toDateTimeString(), \"roomId\" => $id, \"senderId\" => Auth::user()->id, \"senderName\" => Auth::user()->name,]); We also add some fields to the published message which will be used when dynamically displaying a message coming from a WebSocket connection (see Client side below).","s":"Room controller","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#room-controller","p":153},{"i":176,"t":"Our chat is basically a one page with some variations dependng on the current route. So we use a single view for the entire chat app. On the page we have a form for creating rooms. The user who created the room automatically joins it upon creation. Other users need to join manually (using join button in the room). When sending a message (using the chat room message input), we make an AJAX request that hits RoomController shown above. A message saved into the database and then broadcasted to all users who joined this room. Here is a code that processes sending on ENTER: messageInput.onkeyup = function(e) { if (e.keyCode === 13) { e.preventDefault(); const message = messageInput.value; if (!message) { return; } const xhttp = new XMLHttpRequest(); xhttp.open(\"POST\", \"/rooms/\" + roomId + \"/publish\"); xhttp.setRequestHeader(\"X-CSRF-TOKEN\", csrfToken); xhttp.send(JSON.stringify({ message: message })); messageInput.value = ''; }}; After the message is processed on the server and broadcasted to Centrifugo it instantly comes to client-side. To receive the message we are connecting to Centrifugo WebSocket endpoint and wait for a message in the publish event handler: const url = \"ws://\" + window.location.host + \"/connection/websocket\";const centrifuge = new Centrifuge(url);centrifuge.on('connect', function(ctx) { console.log(\"connected to Centrifugo\", ctx);});centrifuge.on('disconnect', function(ctx) { console.log(\"disconnected from Centrifugo\", ctx);});centrifuge.on('publish', function(ctx) { if (ctx.data.roomId.toString() === currentRoomId) { addMessage(ctx.data); scrollToLastMessage(); } addRoomLastMessage(ctx.data);});centrifuge.connect(); We are using centrifuge-js client connector library to communicate with Centrifugo. This client abstracts away bidirectional asynchronous protocol complexity for us providing a simple way to listen connect, disconnect events and communicate with a server in various ways. In publish event handler we check whether the message belongs to the room the user is currently in. If yes, then we add it to the message history of the room. We also add this message to the room in the list on the left as the last chat message in room. If necessary, we crop the text for normal display. tip In our example we only subscribe each user to a single channel, but user can be subscribed to several server-side channels. To distinguish between them use ctx.channel inside publish event handler. And that's it! We went through all the main parts of the integration.","s":"Client side","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#client-side","p":153},{"i":178,"t":"As promised, here is a list with several possible app improvements: Transform to a single page app, use productive Javascript frameworks like React or VueJS instead of vanilla JS. Add message read statuses - as soon as one of the chat participants read the message mark it read in the database. Introduce user-to-user chats. Support pagination for the message history, maybe for chat room list also. Don't show all rooms in the system – add functionality to search room by name. Horizontal scaling (using multiple nodes of Centrifugo, for example with Redis Engine) – mostly one line in Centrifugo config if you have Redis running. Gracefully handle temporary disconnects by loading missed messages from the database or Centrifugo channel history cache. Optionally replace connect proxy with JWT authentication to reduce HTTP calls from Centrifugo to Laravel. This may drastically reduce resources for Laravel backend at scale. Try using Centrifugo RPC proxy feature to use WebSocket connection for message publish instead of issuing AJAX request.","s":"Possible improvements","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#possible-improvements","p":153},{"i":180,"t":"We built a chat app with Laravel and Centrifugo. While there is still an area for improvements, this example is not really the basic. It's already valuable in the current form and may be transformed into part of your production system with minimal tweaks. Hope you enjoyed this tutorial. If you have any questions after reading – join our community channels. We touched only part of Centrifugo concepts here – take a look at detailed Centrifugo docs nearby. And let the Centrifugal force be with you!","s":"Conclusion","u":"/blog/2021/12/14/laravel-multi-room-chat-tutorial","h":"#conclusion","p":153},{"i":182,"t":"In this tutorial, we will create a basic chat server using the Django framework and Centrifugo. Our chat application will have two pages: A page that lets you type the name of a chat room to join. A room view that lets you see messages posted in a chat room you joined. The room view will use a WebSocket to communicate with the Django server (with help from Centrifugo) and listen for any messages that are published to the room channel. caution This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step. The result will look like this: tip Some of you will notice that this tutorial looks very similar to Chat app tutorial of Django Channels. This is intentional to let Pythonistas already familiar with Django Channels feel how Centrifugo compares to Channels in terms of the integration process.","s":"Centrifugo integration with Django – building a basic chat application","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"","p":181},{"i":184,"t":"Why would Django developers want to integrate a project with Centrifugo for real-time messaging functionality? This is a good question especially since there is a popular Django Channels project which solves the same task. I found several points which could be a good motivation: Centrifugo is fast and scales well. We have an optimized Redis Engine with client-side sharding and Redis Cluster support. Centrifugo can also scale with KeyDB, Nats, or Tarantool. So it's possible to handle millions of connections distributed over different server nodes. Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for real-time servers that scale to many nodes. Check out our doc! With Centrifugo you don't need to rewrite the existing application to introduce real-time messaging features to your users. Centrifugo works as a separate service – so can be a universal tool in the developer's pocket, can migrate from one project to another, no matter what programming language or framework is used for business logic.","s":"Why integrate Django with Centrifugo","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#why-integrate-django-with-centrifugo","p":181},{"i":186,"t":"We assume that you are already familiar with basic Django concepts. If not take a look at the official Django tutorial first and then come back to this tutorial. Also, make sure you read a bit about Centrifugo – introduction and quickstart tutorial. We also assume that you have Django installed already. One possible way to quickly install Django locally is to create virtualenv, activate it, and install Django: python3 -m venv env. env/bin/activatepip install django Alos, make sure you have Centrifugo v3 installed already. This tutorial also uses Docker to run Redis. We use Redis as a Centrifugo engine – this allows us to have a scalable solution in the end. Using Redis is optional actually, Centrifugo uses a Memory engine by default (but it does not allow scaling Centrifugo nodes). We will also run Nginx with Docker to serve the entire app. Install Docker from its official website but I am sure you already have one.","s":"Prerequisites","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#prerequisites","p":181},{"i":188,"t":"First, let's create a Django project. From the command line, cd into a directory where you’d like to store your code, then run the following command: django-admin startproject mysite This will create a mysite directory in your current directory with the following contents: ❯ tree mysitemysite├── manage.py└── mysite ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py","s":"Creating a project","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-a-project","p":181},{"i":190,"t":"We will put the code for the chat server inside chat app. Make sure you’re in the same directory as manage.py and type this command: python3 manage.py startapp chat That’ll create a directory chat, which is laid out like this: ❯ tree chatchat├── __init__.py├── admin.py├── apps.py├── migrations│ └── __init__.py├── models.py├── tests.py└── views.py For this tutorial, we will only be working with chat/views.py and chat/__init__.py. Feel free to remove all other files from the chat directory. After removing unnecessary files, the chat directory should look like this: ❯ tree chatchat├── __init__.py└── views.py We need to tell our project that the chat app is installed. Edit the mysite/settings.py file and add 'chat' to the INSTALLED_APPS setting. It’ll look like this: # mysite/settings.pyINSTALLED_APPS = [ 'chat', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles',]","s":"Creating the chat app","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#creating-the-chat-app","p":181},{"i":192,"t":"We will now create the first view, an index view that lets you type the name of a chat room to join. Create a templates directory in your chat directory. Within the templates directory, you have just created, create another directory called chat, and within that create a file called index.html to hold the template for the index view. Your chat directory should now look like this: ❯ tree chatchat├── __init__.py├── templates│ └── chat│ └── index.html└── views.py Put the following code in chat/templates/chat/index.html: chat/templates/chat/index.html Select a chat room
    Type a room name to JOIN
    Create the view function for the room view. Put the following code in chat/views.py: chat/views.py from django.shortcuts import renderdef index(request): return render(request, 'chat/index.html') To call the view, we need to map it to a URL - and for this, we need a URLconf. To create a URLconf in the chat directory, create a file called urls.py. Your app directory should now look like this: ❯ tree chatchat├── __init__.py├── templates│ └── chat│ └── index.html└── views.py└── urls.py In the chat/urls.py file include the following code: chat/urls.py from django.urls import pathfrom . import viewsurlpatterns = [ path('', views.index, name='index'),] The next step is to point the root URLconf at the chat.urls module. In mysite/urls.py, add an import for django.conf.urls.include and insert an include() in the urlpatterns list, so you have: mysite/urls.py from django.conf.urls import includefrom django.urls import pathfrom django.contrib import adminurlpatterns = [ path('chat/', include('chat.urls')), path('admin/', admin.site.urls),] Let’s verify that the index view works. Run the following command: python3 manage.py runserver You’ll see the following output on the command line: Watching for file changes with StatReloaderPerforming system checks...System check identified no issues (0 silenced).You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.Run 'python manage.py migrate' to apply them.October 21, 2020 - 18:49:39Django version 3.1.2, using settings 'mysite.settings'Starting development server at http://localhost:8000/Quit the server with CONTROL-C. Go to http://localhost:8000/chat/ in your browser and you should see the a text input to provide a room name. Type in \"lobby\" as the room name and press Enter. You should be redirected to the room view at http://localhost:8000/chat/room/lobby/ but we haven’t written the room view yet, so you’ll get a \"Page not found\" error page. Go to the terminal where you ran the runserver command and press Control-C to stop the server.","s":"Add the index view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-index-view","p":181},{"i":194,"t":"We will now create the second view, a room view that lets you see messages posted in a particular chat room. Create a new file chat/templates/chat/room.html. Your app directory should now look like this: chat├── __init__.py├── templates│ └── chat│ ├── index.html│ └── room.html├── urls.py└── views.py Create the view template for the room view in chat/templates/chat/room.html: chat/templates/chat/room.html Chat Room
      {{ room_name|json_script:\"room-name\" }} Create the view function for the room view in chat/views.py: chat/views.py from django.shortcuts import renderdef index(request): return render(request, 'chat/index.html')def room(request, room_name): return render(request, 'chat/room.html', { 'room_name': room_name }) Create the route for the room view in chat/urls.py: # chat/urls.pyfrom django.urls import path, re_pathfrom . import viewsurlpatterns = [ path('', views.index, name='index'), re_path('room/(?P[A-z0-9_-]+)/', views.room, name='room'),] Start the development server: python3 manage.py runserver Go to http://localhost:8000/chat/ in your browser and to see the index page. Type in \"lobby\" as the room name and press enter. You should be redirected to the room page at http://localhost:8000/chat/lobby/ which now displays an empty chat log. Type the message \"hello\" and press Enter. Nothing happens! In particular, the message does not appear in the chat log. Why? The room view is trying to open a WebSocket connection with Centrifugo using the URL ws://localhost:8000/connection/websocket but we haven’t started Centrifugo to accept WebSocket connections yet. If you open your browser’s JavaScript console, you should see an error that looks like this: WebSocket connection to 'ws://localhost:8000/connection/websocket' failed And since port 8000 has already been allocated we will start Centrifugo at a different port actually.","s":"Add the room view","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#add-the-room-view","p":181},{"i":196,"t":"As promised we will use Centrifugo with Redis engine. So first thing to do before running Centrifugo is to start Redis: docker run -it --rm -p 6379:6379 redis:6 Then create a configuration file for Centrifugo: { \"port\": 8001, \"engine\": \"redis\", \"redis_address\": \"redis://localhost:6379\", \"allowed_origins\": \"http://localhost:9000\", \"proxy_connect_endpoint\": \"http://localhost:8000/chat/centrifugo/connect/\", \"proxy_publish_endpoint\": \"http://localhost:8000/chat/centrifugo/publish/\", \"proxy_subscribe_endpoint\": \"http://localhost:8000/chat/centrifugo/subscribe/\", \"proxy_http_headers\": [\"Cookie\"], \"namespaces\": [ { \"name\": \"rooms\", \"publish\": true, \"proxy_publish\": true, \"proxy_subscribe\": true } ]} And run Centrifugo with it like this: centrifugo -c config.json Let's describe some options we used here: port - sets the port Centrifugo runs on since we are running everything on localhost we make it different (8001) from the port allocated for the Django server (8000). engine - as promised we are using Redis engine so we can easily scale Centrifigo nodes to handle lots of WebSocket connections redis_address allows setting Redis address allowed_origins - we will connect from http://localhost:9000 so we need to allow it namespaces – we are using rooms: prefix when subscribing to a channel, i.e. using Centrifugo rooms namespace. Here we define this namespace and tell Centrifigo to proxy subscribe and publish events for channels in the namespace. tip It's a good practice to use different namespaces in Centrifugo for different real-time features as this allows enabling only required options for a specific task. Also, config has some options related to Centrifugo proxy feature. This feature allows proxying WebSocket events to the configured endpoints. We will proxy three types of events: Connect (called when a user establishes WebSocket connection with Centrifugo) Subscribe (called when a user wants to subscribe on a channel) Publish (called when a user tries to publish data to a channel)","s":"Starting Centrifugo server","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#starting-centrifugo-server","p":181},{"i":198,"t":"In Centrifugo config we set endpoints which we will soon implement inside our Django app. You may notice that the allowed origin has a URL with port 9000. That's because we want to proxy Cookie headers from a persistent connection established with Centrifugo to the Django app and need Centrifugo and Django to share the same origin (so browsers can send Django session cookies to Centrifugo). While not used in this tutorial (we will use fake tutorial-user as user ID here) – this can be useful if you decide to authenticate connections using Django native sessions framework later. To achieve this we should also add Nginx with a configuration like this: nginx.conf events { worker_connections 1024;}error_log /dev/stdout info;http { access_log /dev/stdout; server { listen 9000; server_name localhost; location / { proxy_pass http://host.docker.internal:8000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /connection/websocket { proxy_pass http://host.docker.internal:8001; proxy_http_version 1.1; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } }} Start Nginx (replace the path to nginx.conf to yours): docker run -it --rm -v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro -p 9000:9000 --add-host=host.docker.internal:host-gateway nginx Note that we are exposing port 9000 to localhost and use a possibility to use host.docker.internal host to communicate from inside Docker network with services which are running on localhost (on the host machine). See this answer on SO. Open http://localhost:9000. Nginx should now properly proxy requests to Django server and to Centrifugo, but we still need to do some things.","s":"Adding Nginx","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#adding-nginx","p":181},{"i":200,"t":"Well, now if you try to open a chat page with Nginx, Centrifugo, Django, and Redis running you will notice some errors in Centrifugo logs. That's because Centrifugo tries to proxy WebSocket connect events to Django to authenticate them but we have not created event handlers in Django yet. Let's fix this. Extend chat/urls.py: chat/urls.py from django.urls import path, re_pathfrom . import viewsurlpatterns = [ path('', views.index, name='index'), re_path('room/(?P[A-z0-9_-]+)/', views.room, name='room'), path('centrifugo/connect/', views.connect, name='connect'), path('centrifugo/subscribe/', views.subscribe, name='subscribe'), path('centrifugo/publish/', views.publish, name='publish'),] Extend chat/views.py: chat/views.py from django.http import JsonResponsefrom django.views.decorators.csrf import csrf_exempt@csrf_exemptdef connect(request): # In connect handler we must authenticate connection. # Here we return a fake user ID to Centrifugo to keep tutorial short. # More details about connect result format can be found in proxy docs: # https://centrifugal.dev/docs/server/proxy#connect-proxy logger.debug(request.body) response = { 'result': { 'user': 'tutorial-user' } } return JsonResponse(response)@csrf_exemptdef publish(request): # In publish handler we can validate publication request initialted by a user. # Here we return an empty object – thus allowing publication. # More details about publish result format can be found in proxy docs: # https://centrifugal.dev/docs/server/proxy#publish-proxy response = { 'result': {} } return JsonResponse(response)@csrf_exemptdef subscribe(request): # In subscribe handler we can validate user subscription request to a channel. # Here we return an empty object – thus allowing subscription. # More details about subscribe result format can be found in proxy docs: # https://centrifugal.dev/docs/server/proxy#subscribe-proxy response = { 'result': {} } return JsonResponse(response) connect view will accept all connections and return user ID as tutorial-user. In real app you most probably want to use Django sessions and return real authenticated user ID instead of tutorial-user. Since we told Centrifugo to proxy connection Cookie headers native Django user authentication will work just fine. Restart Django and try the chat app again. You should now successfully connect. Open a browser tab to the room page at http://localhost:9000/chat/room/lobby/. Open a second browser tab to the same room page. In the second browser tab, type the message \"hello\" and press Enter. You should now see \"hello\" echoed in the chat log in both the second browser tab and in the first browser tab. You now have a basic fully-functional chat server!","s":"Implementing proxy handlers","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#implementing-proxy-handlers","p":181},{"i":202,"t":"The list is large, but it's fun to do. To name some possible improvements: Replace tutorial-user used here with native Django session framework. We already proxying the Cookie header to Django from Centrifugo, so you can reuse native Django authentication. Only allow authenticated users to join rooms. Create Room model and add users to it – thus you will be able to check permissions inside subscribe and publish handlers. Create Message model to display chat history in Room. Replace Django devserver with something more suitable for production like Gunicorn. Check out Centrifugo possibilities like presence to display online users. Use cent Centrifugo HTTP API library to publish something to a user on behalf of a server. In this case you can avoid using publish proxy, publish messages to Django over convinient AJAX call - and then call Centrifugo HTTP API to publish message into a channel. You can replace connect proxy (which is an HTTP call from Centrifugo to Django on each connect) with JWT authentication. JWT authentication may result in a better application performance (since no additional proxy requests will be issued on connect). It can allow your Django app to handle millions of users on a reasonably small hardware and survive mass reconnects from all those users. More details can be found in Scaling WebSocket in Go and beyond blog post. Instead of using subscribe proxy you can put channel into connect proxy result or into JWT – thus using server-side subscriptions and avoid subscribe proxy HTTP call. One more thing I'd like to note is that if you aim to build a chat application like WhatsApp or Telegram where you have a screen with list of chats (which can be pretty long!) you should not create a separate channel for each room. In this case using separate channel per room does not scale well and you better use personal channel for each user to receive all user-related messages. And as soon as message published to a chat you can send message to each participant's channel. In this case, take a look at Centrifugo broadcast API.","s":"What could be improved","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#what-could-be-improved","p":181},{"i":204,"t":"The full example which can run by issuing a single docker compose up can be found on Github. It also has some CSS styles so that the chat looks like shown in the beginning.","s":"Tutorial source code with docker-compose","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#tutorial-source-code-with-docker-compose","p":181},{"i":206,"t":"Here we implemented a basic chat app with Django and Centrifugo. While a chat still requires work to be suitable for production this example can help understand core concepts of Centrifugo - specifically channel namespaces and proxy features. It's possible to use unidirectional Centrifugo transports instead of bidirectional WebSocket used here – in this case, you can go without using centrifuge-js at all. Centrifugo scales perfectly if you need to handle more connections – thanks to Centrifugo built-in PUB/SUB engines. It's also possible to use server-side subscriptions, keep channel history cache, use JWT authentication instead of connect proxy, enable channel presence, and more. All the power of Centrifugo is in your hands. Hope you enjoyed this tutorial. And let the Centrifugal force be with you! Join our community channels in case of any questions left after reading this.","s":"Conclusion","u":"/blog/2021/11/04/integrating-with-django-building-chat-application","h":"#conclusion","p":181},{"i":208,"t":"Securing user authentication and management can often be a challenging task when developing a modern application. As a result, many developers choose to delegate this responsibility to third-party identity providers, such as Okta, Auth0, or Keycloak. In this blog post, we'll go through the process of setting up Single Sign-On (SSO) authentication using Keycloak - popular and powerful identity provider. After setting up SSO we will create React application and connect to Centrifugo using access token generated by Keycloak for our test user:","s":"Setting up Keycloak SSO authentication flow and connecting to Centrifugo WebSocket","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"","p":207},{"i":210,"t":"The integraion is possible since Centrifugo works with standard JWT for authentication and additionally supports JSON Web Key specification. Here is a final source code.","s":"TLDR","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#tldr","p":207},{"i":212,"t":"First, run Keycloak using the following Docker command: docker run --rm -it -p 8080:8080 \\ -e KEYCLOAK_ADMIN=admin \\ -e KEYCLOAK_ADMIN_PASSWORD=admin \\ quay.io/keycloak/keycloak:21.0.1 start-dev After starting Keycloak, go to http://localhost:8080/admin and login. Then perform the following tasks: Create a new realm named myrealm. Create a new client named myclient. Set valid redirect URIs to http://localhost:5173/*, and web origins as http://localhost:5173. Create a user named myuser and set a password for it (in Credentials tab). See this guide for additional details and illustrations of the process. Make sure your created client is public (this is default) since we will request token directly from the web application.","s":"Keycloak","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#keycloak","p":207},{"i":214,"t":"Next, run Centrifugo using the following Docker command: docker run --rm -it -p 8000:8000 \\ -e CENTRIFUGO_ALLOWED_ORIGINS=\"http://localhost:5173\" \\ -e CENTRIFUGO_TOKEN_JWKS_PUBLIC_ENDPOINT=\"http://host.docker.internal:8080/realms/myrealm/protocol/openid-connect/certs\" \\ -e CENTRIFUGO_ALLOW_USER_LIMITED_CHANNELS=true \\ -e CENTRIFUGO_ADMIN=true \\ -e CENTRIFUGO_ADMIN_SECRET=secret \\ -e CENTRIFUGO_ADMIN_PASSWORD=admin \\ centrifugo/centrifugo:v4.1.2 centrifugo Some comments about environment variables used here: CENTRIFUGO_TOKEN_JWKS_PUBLIC_ENDPOINT allows tell Centrifugo to use JSON Web Key spec when validating tokens, we point to Keycloak's JWKS endpoint CENTRIFUGO_ALLOWED_ORIGINS is required since we will build Vite + React based app running on http://localhost:5173 CENTRIFUGO_ALLOW_USER_LIMITED_CHANNELS - not required to connect, but you will see in the source code that we additionally subscribe to a user personal channel CENTRIFUGO_ADMIN, CENTRIFUGO_ADMIN_SECRET, CENTRIFUGO_ADMIN_PASSWORD - to enable Centrifugo admin web UI Also note we are using host.docker.internal to access host port from inside the Docker network.","s":"Centrifugo","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#centrifugo","p":207},{"i":216,"t":"Now, let's create a new React app using very popular Vite tool: npm create vite@latest keycloak_sso_auth -- --template reactcd keycloak_sso_authnpm install Also, install the necessary additional packages for the React app: npm install --save @react-keycloak/web centrifuge keycloak-js And start the development server: npm run dev Navigate to http://localhost:5173/. You should see default Vite template working, we are going to modify it a bit. caution Use localhost, not 127.0.0.1 - since we used localhost for Keyloak and Centrifugo configurations above. Add the following into main.jsx: import React from 'react'import ReactDOM from 'react-dom/client'import { ReactKeycloakProvider } from '@react-keycloak/web'import App from './App'import './index.css'import Keycloak from \"keycloak-js\";const keycloakClient = new Keycloak({ url: \"http://localhost:8080\", realm: \"myrealm\", clientId: \"myclient\"})ReactDOM.createRoot(document.getElementById('root')).render( ,) Note that we configured Keycloak instance pointing it to our Keycloak server. We also use @react-keycloak/web package to wrap React app into ReactKeycloakProvider component. It simplifies working with Keycloak by providing some useful hooks - we are using this hook below. Our App component inside App.jsx may look like this: import React, { useState, useEffect } from 'react';import logo from './assets/centrifugo.svg'import { Centrifuge } from \"centrifuge\";import { useKeycloak } from '@react-keycloak/web'import './App.css'function App() { const { keycloak, initialized } = useKeycloak() if (!initialized) { return null; } return (

      SSO with Keycloak and Centrifugo

      {keycloak.authenticated ? (

      Logged in as {keycloak.tokenParsed?.preferred_username}

      ) : ( )}
      );}export default App This is actually enough for SSO flow to start working! You can click on login button and make sure that it's possible to use myuser credentials to log into the application. And log out after that. The only missing part is Centrifugo. We can initialize connection inside useEffect hook of App component: useEffect(() => { if (!initialized || !keycloak.authenticated) { return; } const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: keycloak.token, getToken: function () { return new Promise((resolve, reject) => { keycloak.updateToken(5).then(function () { resolve(keycloak.token); }).catch(function (err) { reject(err); keycloak.logout(); }); }) } }); centrifuge.connect(); return () => { centrifuge.disconnect(); };}, [keycloak, initialized]); The important thing here is how we configure tokens: we are using Keycloak client methods to set initial token and refresh the token when required. I also added some extra elements to the code to make it look a bit nicer. For example, we can listen to Centriffuge client state changes and show connection indicator on the page: function App() { const [connectionState, setConnectionState] = useState(\"disconnected\"); const stateToEmoji = { \"disconnected\": \"🔴\", \"connecting\": \"🟠\", \"connected\": \"🟢\" } ... useEffect(() => { ... centrifuge.on('state', function (ctx) { setConnectionState(ctx.newState); }) ... return ( ... {stateToEmoji[connectionState]} You can find more details about Centrifugo client SDK API and states in client SDK spec. If you look at source code on Github - you will also find an example of channel subscription to a user personal channel: function App() { ... const [publishedData, setPublishedData] = useState(\"\"); ... useEffect(() => { ... const userChannel = \"#\" + keycloak.tokenParsed?.sub; const sub = centrifuge.newSubscription(userChannel); sub.on(\"publication\", function (ctx) { setPublishedData(JSON.stringify(ctx.data)); }).subscribe(); ... You can now: test the SSO setup by logging into application making sure connection is successful try publishing a message into a user channel via the Centrifugo Web UI. The published message will appear on application screen in real-time. That's it! We have successfully set up Keycloak SSO authentication with Centrifugo and a React application. Again, source code is on Github.","s":"React app with Vite","u":"/blog/2023/03/31/keycloak-sso-centrifugo","h":"#react-app-with-vite","p":207},{"i":218,"t":"Centrifugo provides HTTP and GRPC APIs for publishing messages into channels. Publish server API is very straighforward to use - it's a simple request with a channel and data to be delivered to active WebSocket connections subscribed to a channel. Sometimes though Centrifugo users want to avoid synchronous calls to Centrifugo API delegating this work to asynchronous tasks. Many companies have convenient infrastructure for messaging processing tasks - like Kafka, Nats Jetstream, Redis, RabbitMQ, etc. Some using transactional outbox pattern to reliably deliver events upon database changes and have a ready infrastructure to push such events to some queue. From which want to re-publish events to Centrifugo. In this post we get familiar with a tool called Benthos and show how it may simplify integrating your asynchronous message flow with Centrifugo. And we discuss some non-obvious pitfalls of asynchronous publishing approach in regards to real-time applications.","s":"Asynchronous message streaming to Centrifugo with Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"","p":217},{"i":220,"t":"First start Centrifugo (with debug logging to see incoming API requests in logs): centrifugo genconfigcentrifugo -c config.json --log_level debug Hope this step is already simple for you, if not - check out Quickstart tutorial.","s":"Start Centrifugo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#start-centrifugo","p":217},{"i":222,"t":"Benthos is an awesome tool which allows consuming data from various inputs, process data, then output it into configured outputs. See more detailed description on Benthos' website. The number of inputs supported by Benthos is huge: check it out here. Most of the major systems widely used these days are supported. Benthos also supports many outputs – but here we only interested in message transfer to Centrifugo. There is no built-in Centrifugo output in Benthos but it provides a generic http_client output which may be used to send requests to any HTTP server. Benthos may also help with retries, provides tools for additional data processing and transformations. Just like Centrifugo Benthos written in Go language – so its installation is very straighforward and similar to Centrifugo. See official instructions. Let's assume you've installed Benthos and have benthos executable available in your system. Let's create Benthos configuration file: benthos create > config.yaml Take a look at generated config.yaml - it contains various options for Benthos instance, the most important (for the context of this post) are input and output sections. And after that you can start Benthos instance with: benthos -c config.yaml Now we need to tell Benthos from where to get data and how to send it to Centrifugo.","s":"Install and run Benthos","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#install-and-run-benthos","p":217},{"i":224,"t":"For our example here we will user Redis List as an input, won't add any additional data processing and will output messages consumed from Redis List into Centrifugo publish server HTTP API. To achieve this add the following as input in Benthos config.yaml: input: label: \"centrifugo_redis_consumer\" redis_list: url: \"redis://127.0.0.1:6379\" key: \"centrifugo.publish\" And configure the output like this: output: label: \"centrifugo_http_publisher\" http_client: url: \"http://localhost:8000/api/publish\" verb: POST headers: X-API-Key: \"\" timeout: 5s max_in_flight: 20 The output points to Centrifugo publish HTTP API. Replace with your Centrifugo api_key (found in Centrifugo configuration file).","s":"Configure Benthos input and output","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#configure-benthos-input-and-output","p":217},{"i":226,"t":"Start Benthos instance: benthos -c config.yaml You will see errors while Benthos tries to connect to input Redis source. So start Redis server: docker run --rm -it --name redis redis:7 Now connect to Redis (using redis-cli): docker exec -it redis redis-cli And push command to Redis list: 127.0.0.1:6379> rpush centrifugo.publish '{\"channel\": \"chat\", \"data\": {\"input\": \"test\"}}'(integer) 1 This message will be consumed from Redis list by Benthos and published to Centrifugo HTTP API. If you have active subscribers to channel chat – you will see messages delivered to them. That's it! tip When using Redis List input you can scale out Benthos instances to run several of them if needed.","s":"Push messages to Redis queue","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#push-messages-to-redis-queue","p":217},{"i":228,"t":"Here is a quick demonstration of the described integration. See how we push messages into Redis List and those are delivered to WebSocket clients: Sorry, your browser doesn't support embedded video.","s":"Demo","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#demo","p":217},{"i":230,"t":"This all seems simple. But publishing messages asynchronously may highlight some pitfalls not visible or not applicable for synchronous publishing to Centrifugo API.","s":"Pitfalls of async publishing","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#pitfalls-of-async-publishing","p":217},{"i":232,"t":"Most of the time it will work just fine. But one day you can come across intermediate queue growth and increased delivery lag. This may happen due to temporary Centrifugo or worker process unavailability. As soon as system comes back to normal queued messages will be delivered. Depending on the real-time feature implemented this may be a concern to think about and decide whether this is desired or not. Your application should be designed accordingly to deal with late delivery. BTW late delivery may be a case even with synchronous publishing – it just almost never strikes. But theoretically client can reload browser page and load initial app state while message flying from the backend to client over Centrifugo. It's not Centrifugo specific actually - it's just a nature of networks and involved latencies. In general solution to prevent late delivery UX issues completely is using object versioning. Object version should be updated in the database on every change from which the real-time event is generated. Attach object version information to every real-time message. Also get object version on initial state load. This way you can compare versions and drop non-actual real-time messages on client side. Possible strategy may be using synchronous API for real-time features where at most once delivery is acceptable and use asynchronous delivery where you need to deliver messages with at least once guarantees. In a latter case you most probably designed proper idempotency behaviour on client side anyway.","s":"Late delivery","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#late-delivery","p":217},{"i":234,"t":"Another thing to consider is message ordering. Centrifugo itself may provide message ordering in channels. If you published one message to Centrifugo API, then another one – you can expect that messages will be delivered to a client in the same order. But as soon as you have an intermediary layer like Benthos or any other asynchronous worker process – then you must be careful about ordering. In case of Benthos and example here you can set max_in_flight parameter to 1 instead of 20 and keep only one instance of Benthos running to preserve ordering. In case of streaming from Kafka you can rely on Kafka message partitioning feature to preserve message ordering.","s":"Ordering concerns","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#ordering-concerns","p":217},{"i":236,"t":"If you preserved ordering in your asynchronous workers then the next thing to consider is throughput limitations. You have a limited number of workers, these workers send requests to Centrifugo one by one. In this case throughput is limited by the number of workers and RTT (round-trip time) between worker process and Centrifugo. If we talk about using Redis List structure as a queue - you can possibly shard data amongst different Redis List queues by some key to improve throughput. In this case you need to push messages where order should be preserved into a specific queue. In this case your get a setup similar to Kafka partitioning. In case of using manually partitioned queues or using Kafka you can have parallelism equal to the number of partitions. Let's say you have 20 workers which can publish messages in parallel and 5ms RTT time between worker and Centrifugo. In this case you can publish 20*(1000/5) = 4000 messages per second max. To improve throughput futher consider increasing worker number or batching publish requests to Centrifugo (using Centrifugo's batch API).","s":"Throughput when ordering preserved","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#throughput-when-ordering-preserved","p":217},{"i":238,"t":"When publishing asynchronously you should also don't forget about error handling. Benthos will handle network errors automatically for you. But there could be internal errors from Centrifugo returned as part of response. It's not very convenient to handle with Benthos out of the box – so we think about adding transport-level error mode to Centrifugo.","s":"Error handling","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#error-handling","p":217},{"i":240,"t":"Sometimes you want to publish to Centrifugo asynchronously using messaging systems convenient for your company. Usually you can write worker process to re-publish messages to Centrifugo. Sometimes it may be simplified using helpful tools like Benthos. Here we've shown how Benthos may be used to transfer messages from Redis List queue to Centrifugo API. With some modifications you can achieve the same for other input sources - such as Kafka, RabbitMQ, Nats Jetstream, etc. But publishing messages asynchronously highlights several pifalls - like late delivery, ordering issues, throughput considerations and error handling – which should be carefully addressed. Different real-time features may require different strategies.","s":"Conclusion","u":"/blog/2023/08/19/asynchronous-message-streaming-to-centrifugo-with-benthos","h":"#conclusion","p":217},{"i":242,"t":"Centrifugo is a scalable real-time messaging server in a language-agnostic way. In this tutorial we will integrate Centrifugo with NodeJS backend using a connect proxy feature of Centrifugo for user authentication and native session middleware of ExpressJS framework. Why would NodeJS developers want to integrate a project with Centrifugo? This is a good question especially since there are lots of various tools for real-time messaging available in NodeJS ecosystem. caution This tutorial was written for Centrifugo v3. We recently released Centrifugo v4 which makes some parts of this tutorial obsolete. The core concepts are similar though – so this can still be used as a Centrifugo learning step. I found several points which could be a good motivation: Centrifugo scales well – we have a very optimized Redis Engine with client-side sharding and Redis Cluster support. We can also scale with KeyDB, Nats, or Tarantool. Centrifugo can scale to millions connections distributed over different server nodes. Centrifugo is pretty fast (written in Go) and can handle thousands of clients per node. Client protocol is optimized for thousands of messages per second. Centrifugo provides a variety of features out-of-the-box – some of them are unique, especially for real-time servers that scale to many nodes. Centrifugo works as a separate service – so can be a universal tool in developer's pocket, can migrate from one project to another, no matter what programming language or framework is used for a business logic. Having said this all – let's move to a tutorial itself.","s":"Centrifugo integration with NodeJS tutorial","u":"/blog/2021/10/18/integrating-with-nodejs","h":"","p":241},{"i":244,"t":"Not a super-cool app to be honest. Our goal here is to give a reader an idea how integration with Centrifugo could look like. There are many possible apps which could be built on top of this knowledge. The end result here will allow application user to authenticate and once authenticated – connect to Centrifugo. Centrifugo will proxy connection requests to NodeJS backend and native ExpressJS session middleware will be used for connection authentication. We will also send some periodical real-time messages to a user personal channel. The full source code of this tutorial located on Github. You can clone examples repo and run this demo by simply writing: docker compose up","s":"What we are building","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#what-we-are-building","p":241},{"i":246,"t":"Start new NodeJS app: npm init Install dependencies: npm install express express-session cookie-parser axios morgan Create index.js file. index.js const express = require('express');const cookieParser = require(\"cookie-parser\");const sessions = require('express-session');const morgan = require('morgan');const axios = require('axios');const app = express();const port = 3000;app.use(express.json());const oneDay = 1000 * 60 * 60 * 24;app.use(sessions({ secret: \"this_is_my_secret_key\", saveUninitialized: true, cookie: { maxAge: oneDay }, resave: false}));app.use(cookieParser());app.use(express.urlencoded({ extended: true }))app.use(express.json())app.use(express.static('static'));app.use(morgan('dev'));app.get('/', (req, res) => { if (req.session.userid) { res.sendFile('views/app.html', { root: __dirname }); } else res.sendFile('views/login.html', { root: __dirname })});app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`);}); Create login.html file in views folder: views/login.html

      Login (username: demo-user, password: demo-pass)

      Also create app.html file in views folder: views/app.html
      Make attention that we import centrifuge-js client here which abstracts away Centrifugo bidirectional WebSocket protocol. Let's write an HTTP handler for login form: index.js const myusername = 'demo-user'const mypassword = 'demo-pass'app.post('/login', (req, res) => { if (req.body.username == myusername && req.body.password == mypassword) { req.session.userid = req.body.username; res.redirect('/'); } else { res.send('Invalid username or password'); }}); In this example we use hardcoded username and password for out single user. Of course in real app you will have a database with user credentials. But since our goal is only show integration with Centrifugo – we are skipping these hard parts here. Also create a handler for a logout request: index.js app.get('/logout', (req, res) => { req.session.destroy(); res.redirect('/');}); Now if you run an app with node index.js you will see a login form using which you can authenticate. At this point this is a mostly convenient NodeJS application, let's add Centrifugo integration.","s":"Creating Express.js app","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#creating-expressjs-app","p":241},{"i":248,"t":"Run Centrifugo with config.json like this: config.json { \"token_hmac_secret_key\": \"secret\", \"admin\": true, \"admin_password\": \"password\", \"admin_secret\": \"my_admin_secret\", \"api_key\": \"my_api_key\", \"allowed_origins\": [ \"http://localhost:9000\" ], \"user_subscribe_to_personal\": true, \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_http_headers\": [ \"Cookie\" ]} I.e.: ./centrifugo -c config.json Create app.js file in static folder: static/app.js function drawText(text) { const div = document.createElement('div'); div.innerHTML = text; document.getElementById('log').appendChild(div);}const centrifuge = new Centrifuge('ws://localhost:9000/connection/websocket');centrifuge.on('connect', function () { drawText('Connected to Centrifugo');});centrifuge.on('disconnect', function () { drawText('Disconnected from Centrifugo');});centrifuge.on('publish', function (ctx) { drawText('Publication, time = ' + ctx.data.time);});centrifuge.connect();","s":"Starting Centrifugo","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#starting-centrifugo","p":241},{"i":250,"t":"Since we are going to use native session auth of ExpressJS we can't just connect from localhost:3000 (where our NodeJS app is served) to Centrifugo running on localhost:8000 – browser won't send a Cookie header to Centrifugo in this case. Due to this reason we need a reverse proxy which will terminate a traffic from frontend and proxy requests to NodeJS process or to Centrifugo depending on URL path. In this case both browser and NodeJS app will share the same origin – so Cookie will be sent to Centrifugo in WebSocket Upgrade request. tip Alternatively, we could also use JWT authentication of Centrifugo but that's a topic for another tutorial. Here we are using connect proxy feature for auth. Nginx config will look like this: server { listen 9000; server_name localhost; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /connection { proxy_pass http://localhost:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; }} Run Nginx and open http://localhost:9000. After authenticating in app you should see an attempt to connect to a WebSocket endpoint. But connection will fail since we need to implement connect proxy handler in NodeJS app. index.js app.post('/centrifugo/connect', (req, res) => { if (req.session.userid) { res.json({ result: { user: req.session.userid } }); } else res.json({ disconnect: { code: 1000, reason: \"unauthorized\", reconnect: false } });}); Restart NodeJS process and try opening an app again. Application should now successfully connect to Centrifugo.","s":"Adding Nginx","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#adding-nginx","p":241},{"i":252,"t":"Let's also periodically publish current server time to a client's personal channel. In Centrifugo configuration we set a user_subscribe_to_personal option which turns on automatic subscription to a personal channel for each connected user. We can use axios library and send publish API requests to Centrifugo periodically (according to API docs): index.js const centrifugoApiClient = axios.create({ baseURL: `http://centrifugo:8000/api`, headers: { Authorization: `apikey my_api_key`, 'Content-Type': 'application/json', },});setInterval(async () => { try { await centrifugoApiClient.post('', { method: 'publish', params: { channel: '#' + myusername, // construct personal channel name. data: { time: Math.floor(new Date().getTime() / 1000), }, }, }); } catch (e) { console.error(e.message); }}, 5000); After restarting NodeJS you should see periodical updates on application web page. You can also log in into Centrifugo admin web UI http://localhost:8000 using password password - and play with other available server API from within web interface.","s":"Send real-time messages","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#send-real-time-messages","p":241},{"i":254,"t":"While not being super useful this example can help understanding core concepts of Centrifugo - specifically connect proxy feature and server API. It's possible to use unidirectional Centrifugo transports instead of bidrectional WebSocket used here – in this case you can go without using centrifuge-js at all. This application scales perfectly if you need to handle more connections – thanks to Centrifugo builtin PUB/SUB engines. It's also possible to use client-side subscriptions, keep channel history cache, enable channel presence and more. All the power of Centrifugo is in your hands.","s":"Conclusion","u":"/blog/2021/10/18/integrating-with-nodejs","h":"#conclusion","p":241},{"i":256,"t":"In our v5 release post, we announced the upcoming launch of Centrifugo PRO. We are happy to say that it was released soon after that, and at this point, we already have several customers of the PRO version. I think it's time to look at the current state of the PRO version and finally start talking more about its benefits. In this post, we will talk more about one of the coolest PRO features we have at this point: the push notifications API.","s":"Discovering Centrifugo PRO: push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"","p":255},{"i":258,"t":"When Centrifugo was originally created, its main goal was to help introduce real-time messaging features to existing systems, written in traditional frameworks which work on top of the worker/thread model. Serving many concurrent connections is a non-trivial task in general, and without native efficient concurrency support, it becomes mostly impossible without a shift in the technology stack. Integrating with Centrifugo makes it simple to introduce an efficient real-time layer, while keeping the existing application architecture. As time went on, Centrifugo got some unique features which now justify its usage even in conjunction with languages/frameworks with good concurrency support. Simply using Centrifugo for at-most-once PUB/SUB may already save a lot of development time. The task, which seems trivial at first glance, has a lot of challenges in practice: client SDKs with reconnect and channel multiplexing, scalability to many nodes, WebSocket fallbacks, etc. The combination of useful possibilities has made Centrifugo an attractive component for building enterprise-level applications. Let's be honest here - for pet projects, developers often prefer writing WebSocket communications themselves, and Centrifugo may be too heavy and an extra dependency. But in a corporate environment, the decision on which technology to use should take into account a lot of factors, like those we just mentioned above. Using a mature technology is often preferred to building from scratch and making all the mistakes along the way. With the PRO version, our goal is to provide even more value for established businesses when switching to Centrifugo. We want to solve tricky cases and simplify them for our customers; we want to step into related areas where we see we can provide sufficient value. One rule we try to follow for PRO features that extend Centrifugo’s scope is this: we are not trying to replicate something that already exists in other systems, but rather, we strive to improve upon it. We focus on solving practical issues that we observe, providing a unique value proposition for our customers. This post describes one such example — we will demonstrate our approach to push notifications, which is one the features of Centrifugo PRO.","s":"Centrifugo PRO goals","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#centrifugo-pro-goals","p":255},{"i":260,"t":"Why provide a push notifications API at all? Well, actually, real-time messages and push notifications are so close that many developers hardly see the difference before starting to work with both more closely. I’ve heard several stories where chat functionality on mobile devices was implemented using only native push notifications — without using a separate real-time transport like WebSocket while the app is in the foreground. While this is not a recommended approach due to the delivery properties of push notifications, it proves that real-time messages and push notifications are closely related concepts and sometimes may interchange with each other. When developers introduce WebSocket communication in an application, they often ask the question—what should I do next to deliver some important messages to a user who is currently not actively using the application? WebSockets are great when the app is in the foreground, but when the app goes to the background, the recommended approach is to close the WebSocket connection. This is important to save battery, and operating systems force the closing of connections after some time anyway. The delivery of important app data is then possible over push notifications. See a good overview of them on web.dev. Previously, Centrifugo positioned itself solely as a transport layer for real-time messages. In our FAQ, we emphasized this fact and suggested using separate software products to send push notifications. Now, with Centrifugo PRO, we provide this functionality to our customers. We have extended our server API with methods to manage and send push notifications. I promised to tell you why we believe our implementation is super cool. Let’s dive into the details.","s":"Why providing push notifications API","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#why-providing-push-notifications-api","p":255},{"i":262,"t":"Push notifications are super handy, but there’s a bit to do to get them working right. Let's break it down! On the user's side (frontend)​ Request permission from the user to receive push notifications. Integrate with the platform-specific notification service (e.g., Apple Push Notification Service for iOS, Firebase Cloud Messaging for Android) to obtain the device token. Send the device token to the server for storage and future use. Integrate with the platform-specific notification handler to listen for incoming push notifications Handle incoming push notifications: display the notification content to the user, either as a banner, alert, or in-app message, depending on the user's preferences and the type of notification. Handle user actions on the notification, such as opening the app, dismissing the notification, or taking a specific action related to the notification content. On the server (backend)​ Store device tokens in a database when received from the client side Regularly clean up the database to remove stale or invalid device tokens. and handle scenarios where a device token becomes invalid or is revoked by the user, ensuring that no further notifications are sent to that device. Integrate with platform-specific notification services (e.g., APNS, FCM) to send notifications to devices. Handle errors or failures in sending notifications and implement retry mechanisms if necessary. Track the delivery status of each push notification sent out. Monitor the open rates, click-through rates, and other relevant metrics for the notifications. Use analytics to understand user behavior in response to notifications and refine the notification strategy based on insights gained. We believe that we were able to achieve a unique combination of design decisions which allows us to provide push notification support like no one else provides. Let’s dive into what makes our approach special!","s":"Push notifications API like no one provides","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-api-like-no-one-provides","p":255},{"i":264,"t":"When providing the push notification feature, other solutions like Pusher or Ably also offer their own SDKs for managing notifications on the client side. What we've learned, though, during the Centrifugo life cycle, is that creating and maintaining client SDKs for various environments (iOS, Android, Web, Flutter) is one of the hardest parts of the Centrifugo project. So the decision here was simple and natural: Centrifugo PRO does not introduce any client SDKs for push notifications on the client side. When integrating with Centrifugo, you can simply use the native SDKs provided by each platform. We bypass the complexities of SDK development and concentrate on server-side improvements. With this decision, we are not introducing any limitations to the client side. You get: Wealthy documentation and community support. Platforms like APNs provide comprehensive documentation, tutorials, and best practices, making the integration process smoother. Stability and reliability: native SDKs are rigorously tested and frequently updated by the platform providers. This ensures that they are stable, reliable, and free from critical bugs. Access to the latest features. As platform providers roll out new features or enhancements, native SDKs are usually the first to get updated. This ensures that your application can leverage the latest functionalities without waiting for SDKs to catch up. This approach was not possible with our real-time SDKs, as WebSocket communication is very low-level, and Centrifugo’s main goal was to provide some high-level features on top of it. However, with push notifications, proceeding without a custom SDK seems like a choice beneficial for everyone.","s":"Frontend decisions","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#frontend-decisions","p":255},{"i":266,"t":"The main work we did was on the server side. Let's go through the entire workflow of push notification delivery and describe what Centrifugo PRO provides for each step.","s":"Server implementation","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#server-implementation","p":255},{"i":268,"t":"Let's suppose you got the permission from the user and received the device push token. At this point you must save it to database for sending notifications later using this token. Centrifugo PRO provides API called device_register to do exactly this. At this point, we use PostgreSQL for storing tokens – which is a very popular SQL database. Probably we will add more storage backend options in the future. When calling Centrifugo device_register API you can provide user ID, list of topics to subscribe, platform from which the user came from (ios, android, web), also push notifications provider. To deliver push notifications to devices Centrifugo PRO integrates with the following push notification providers: fcm - Firebase Cloud Messaging (FCM) hms - Huawei Messaging Service (HMS) Push Kit apns - Apple Push Notification service (APNs) So we basically cover all the most popular platforms out of the box. After registering the device token, Centrifugo PRO returns a device_id to you. This device ID must be stored on the client device. As long as the frontend has this device_id, it can update the device's push token information from time to time to keep it current (by just calling device_register again, but with device_id attached). After saving the token, your backend can start sending push notifications to devices.","s":"How we keep tokens","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-keep-tokens","p":255},{"i":270,"t":"To send push notifications we provide another API called send_push_notification. You need to provide some filter in the API request to tell Centrifugo who you want to send notification. You also need to provide push notification payload. For example, using Centrifugo HTTP API: curl -X POST http://localhost:8000/api/send_push_notification \\-H \"Authorization: apikey \" \\-d @- <<'EOF'{ \"recipient\": { \"filter\": { \"topics\": [\"test\"] } }, \"notification\": { \"fcm\": { \"message\": { \"notification\": {\"title\": \"Hello\", \"body\": \"How are you?\"} } } }}EOF Here is another important decision we made: Centrifugo PRO allows you to specify raw JSON objects for each provider we support. In other words, we do not wrap the push notifications API for FCM, APNS, HMS - we give you a way to construct the entire push notification message. This means the Centrifugo push API supports all the fields of push notification payloads out-of-the-box, for all push providers. You can simply use the documentation of FCM, APNs, and send the constructed requests to Centrifugo. There is no need for us to update Centrifugo PRO in any way to support new fields added by providers to push APIs. When you send a push notification with a filter and push payload for each provider you want, it's queued by Centrifugo. We use Redis Streams for queuing and optionally a queue based on PostgreSQL (less efficient, but still robust enough). The fact that the notification is being queued means a very fast response time – so you can integrate with Centrifugo from within the hot paths of your application backend. You may additionally provide a push expiration time and a unique push identifier. If you have not provided a unique identifier, Centrifugo generates one for you and returns it in the response. The unique identifier may later be used to track push status in Centrifugo PRO's push notification analytics. We then have efficient workers which process the queue with minimal latency and send push notifications using batch requests for each provider - i.e., we do this in the most effective way possible. We conducted a benchmark of our worker system with FCM – and we can easily send several million pushes per minute. Another decision we made - Centrifugo PRO supports sending push notifications to a raw list of tokens. This makes it possible for our customers to use their own token storage. For example, such storage could already exist before you started using Centrifugo, or you might need a different storage/schema. In such cases, you can use Centrifugo just as an effective push sender server. Finally, Centrifugo PRO supports sending delayed push notification - to queue push for a later delivery, so for example you can send notification based on user time zone and let Centrifugo PRO send it when needed. Or you may send slightly delayed push notification together with real-time message and if client provided an ack to real-time message - cancel push notification.","s":"How we send notifications","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#how-we-send-notifications","p":255},{"i":272,"t":"FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that device can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM and HMS topics by introducing an additional API to manage device subscriptions to topics. Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs. Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. See user_topic_list and user_topic_update. As soon as user registers a device – it will be automatically subscribed to its own topics pre-created over the API. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with push notifications (with FCM in particular) – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call - Centrifugo PRO API gives you freedom here.","s":"Secure unified topics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#secure-unified-topics","p":255},{"i":274,"t":"Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Push providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API. This API allows updating ClickHouse table and add status for each push sent: delivered or interacted It's then possible to make queries to ClickHouse and build various analytical reports. Or use ClickHouse for real-time graphs - for example, from Grafana.","s":"Push analytics","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-analytics","p":255},{"i":276,"t":"Finally, Centrifugo PRO provides a simple web UI for inspecting registered devices. It can simplify development, provide a way to look at live data, and send simple push notification alerts to users or topics.","s":"Push notifications UI","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#push-notifications-ui","p":255},{"i":278,"t":"We really believe in our push notifications and will be working hard to make them even better. The API we already have serves well to cover common push notification delivery use cases, but we won't stop here. Some areas for improvements are: functionality of built-in push notifications web UI, extending push analytics by providing user friendly UI for the insights about push delivery and engagement. The good thing is that we already have a ground for making this. Take a look at the documentation of Centrifugo PRO push notification API for more formal details and some things not mentioned here. Probably at the time you are reading this we already added something great to the API. Even though Centrifugo PRO is pretty new, it already has a lot of helpful features, and we have plans to add even more. You can see what’s coming up next on our Centrifugo PRO planned features board. We're excited to share more blog posts like this one in the future.","s":"Conclusion","u":"/blog/2023/10/29/discovering-centrifugo-pro-push-notifications","h":"#conclusion","p":255},{"i":280,"t":"This post introduces a new format in Centrifugal blog – interview with a Centrifugo user! Let's dive into an exciting chat with the engineering team of RabbitX platform, a global permissionless perpetuals exchange powered on Starknet. We will discover how Centrifugo helped RabbitX to build a broker platform with current trading volume of 25 million USD daily! 🚀🎉 [Q] Hey team - thanks for your desire to share your Centrifugo use case. First of all, could you provide some information about RabbitX - what is it?​ RabbitX is a global permissionless perpetuals exchange built on Starknet. RabbitX is building the most secure and liquid global derivatives network, giving you 24/7 access to global markets anywhere in the world, with 20x leverage. In its core there is an orderbook - where traders match against market makers, which require to support high throughput and low latency tech stack. The technologies that we are using: Tarantool as in-memory database and business logic server Centrifugo as our major websocket server Different stark tech to support decentralized settlement [Q] Great! What is the goal of Centrifugo in your project? Which real-time features you have?​ Almost all the information users see in our terminal is streamed over Centrifugo. We use it for financial order books, candlestick chart updates, and stat number updates. We can also send real-time personal user notifications via Centrifugo. Instead of all the words, here is a short recording of our terminal trading BTC: [Q] We know that you are using Centrifugo Tarantool engine - could you explain why and how it works in your case?​ Well, that's an interesting thing. We heavily use Tarantool in our system. It grants us immense flexibility, performance, and the power to craft whatever we envision. It ensures the atomicity essential for trading match-making. When we were in search of a WebSocket real-time bus for messages, we were pleasantly surprised to discover that Centrifugo integrates with Tarantool. In our scenario, this allowed us to bypass additional network round-trips, as we can stream data directly from Tarantool to Centrifugo channels. Reducing latency is paramount for financial instruments. Furthermore, I can mention that over our nine months in production, we didn't encounter any issues with Centrifugo – it performed flawlessly! Regarding authentication, we employ Centrifugo's JWT authentication and subscribe proxy. Thus, subscriptions are authorized on our specialized service written in Go. We're also actively using Centrifugo possibility to send initial channel data in the subscribe proxy response. One challenge we overcame was bridging the gap between the subscription's initial request and the continuous message stream in the order book component. To address this, we employed our own sequence numbers in events, coupled with Centrifugo's channel history – this allowed us to deal with missed events when needed. Actually the gaps in event stream are rare in practice and our workaround not needed most of the time, but now we're confident our users never experience this issue. [Q] Looking at RabbitX terminal app we see quite modern UI - could you share more details about it too?​ Our frontend is built on top of React in combination with TradingView Supercharts. And of course we are using centrifuge-js SDK for establishing connections with Centrifugo. [Q] So you are nine months in production at this point. Can you share some real world numbers related to your Centrifugo setup?​ At this point we can have up to a thousand active concurrent traders and send more than 60 messages per second towards one client in peak hours. All the load is served with a single Centrifugo instance (and we have one standby instance). [Q] Anything else you want to share with readers of Centrifugal blog?​ When we designed the system the main goal was to have a homogeneous tech zoo, with a small amount of different technologies, to keep the number of failure points as small as possible. Tarantool is a sort of technology that really allows us to achieve this, we were able to add different decentralized mechanics to our system because of that. It’s not only an in-memory database, but in reality the app server as well. In our case, the fact Centrifugo supports Tarantool broker was a big discovery – the integration went smoothly, and everything has been working great since then.","s":"Using Centrifugo in RabbitX","u":"/blog/2023/08/29/using-centrifugo-in-rabbitx","h":"","p":279},{"i":283,"t":"Centrifuge library Centrifugo is a server built on top of Centrifuge library for Go language. Due to its standalone language-agnostic nature Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a more tight integration of real-time server with application business logic you may consider using Centrifuge library to build something similar to Centrifugo but with customized behavior. Library README has detailed description, link to examples and introduction post. Many Centrifugo features should be re-implemented when using Centrifuge - like API layer, admin web UI, proxy etc (if you need those of course). And you need to write backend in Go language. But core functionality like a client-server protocol (all Centrifugo client connectors work with Centrifuge library based server) and Redis engine to scale come out of the box. tip Many things said in Centrifugo doc can be considered as extra documentation for Centrifuge library (for example part about infrastructure tuning or transport description). But not all of them.","s":"Centrifuge library","u":"/docs/3/ecosystem/centrifuge","h":"","p":282},{"i":285,"t":"Let's say you develop an application and want a real-time connection which is subscribed to one channel. Let's also assume that this channel is used for user personal notifications. So only one user in the application can subcribe to that channel to receive its notifications in real-time. In this post we will look at various ways to achieve this with Centrifugo, and consider trade-offs of the available approaches. The main goal of this tutorial is to help Centrifugo newcomers be aware of all the ways to control channel permissions by reading just one document. And... well, there are actually 8 ways I found, not 101 😇","s":"101 ways to subscribe user on a personal channel in Centrifugo","u":"/blog/2022/07/29/101-way-to-subscribe","h":"","p":284},{"i":287,"t":"To make the post a bit easier to consume let's setup some things. Let's assume that the user for which we provide all the examples in this post has ID \"17\". Of course in real-life the examples given here can be extrapolated to any user ID. When you create a real-time connection to Centrifugo the connection is authenticated using the one of the following ways: using connection JWT using connection request proxy from Centrifugo to the configured endpoint of the application backend (connect proxy) As soon as the connection is successfully established and authenticated Centrifugo knows the ID of connected user. This is important to understand. And let's define a namespace in Centrifugo configuration which will be used for personal user channels: { ... \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} Defining namespaces for each new real-time feature is a good practice in Centrifugo. As an awesome improvement we also enabled presence in the personal namespace, so whenever users subscribe to a channel in this namespace Centrifugo will maintain online presence information for each channel. So you can find out all connections of the specific user existing at any moment. Defining presence is fully optional though - turn it of if you don't need presence information and don't want to spend additional server resources on maintaining presence.","s":"Setup","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#setup","p":284},{"i":289,"t":"tip Probably the most performant approach. All you need to do is to extend namespace configuration with allow_user_limited_channels option: { \"namespaces\": [ { \"name\": \"personal\", \"presence\": true, \"allow_user_limited_channels\": true } ]} On the client side you need to have sth like this (of course the ID of current user will be dynamic in real-life): const sub = centrifuge.newSubscription('personal:#17');sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe(); Here you are subscribing to a channel in personal namespace and listening to publications coming from a channel. Having # in channel name tells Centrifugo that this is a user-limited channel (because # is a special symbol that is treated in a special way by Centrifugo as soon as allow_user_limited_channels enabled). In this case the user ID part of user-limited channel is \"17\". So Centrifugo allows user with ID \"17\" to subscribe on personal:#17 channel. Other users won't be able to subscribe on it. To publish updates to subscription all you need to do is to publish to personal:#17 using server publish API (HTTP or GRPC).","s":"#1 – user-limited channel","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#1--user-limited-channel","p":284},{"i":291,"t":"tip Probably the most flexible approach, with reasonably good performance characteristics. Another way we will look at is using subscription JWT for subscribing. When you create Subscription object on the client side you can pass it a subscription token, and also provide a function to retrieve subscription token (useful to automatically handle token refresh, it also handles initial token loading). const token = await getSubscriptionToken('personal:17');const sub = centrifuge.newSubscription('personal:17', { token: token});sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe(); Inside getSubscriptionToken you can issue a request to the backend, for example in browser it's possible to do with fetch API. On the backend side you know the ID of current user due to the native session mechanism of your app, so you can decide whether current user has permission to subsribe on personal:17 or not. If yes – return subscription JWT according to our rules. If not - return empty string so subscription will go to unsubscribed state with unauthorized reason. Here are examples for generating subscription HMAC SHA-256 JWTs for channel personal:17 and HMAC secret key secret: Python NodeJS import jwtimport timeclaims = { \"sub\": \"17\", \"channel\": \"personal:17\" \"exp\": int(time.time()) + 30*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ 'sub': '17', 'channel': 'personal:17' }) .setProtectedHeader({ alg }) .setExpirationTime('30m') .sign(secret) console.log(token);})(); Since we set expiration time for subscription JWT tokens we also need to provide a getToken function to a client on the frontend side: const sub = centrifuge.newSubscription('personal:17', { getToken: async function (ctx) { const token = await getSubscriptionToken('personal:17'); return token; }});sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe(); This function will be called by SDK automatically to refresh subscription token when it's going to expire. And note that we omitted setting token option here – since SDK is smart enough to call provided getToken function to extract initial subscription token from the backend. The good thing in using subscription JWT approach is that you can provide token expiration time, so permissions to subscribe on a channel will be validated from time to time while connection is active. You can also provide additional channel context info which will be attached to presence information (using info claim of subscription JWT). And you can granularly control channel permissions using allow claim of token – and give client capabilities to publish, call history or presence information (this is Centrifugo PRO feature at this point). Token also allows to override some namespace options on per-subscription basis (with override claim). Using subscription tokens is a general approach for any channels where you need to check access first, not only for personal user channels.","s":"#2 - channel token authorization","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#2---channel-token-authorization","p":284},{"i":293,"t":"tip Probably the most secure approach. Subscription JWT gives client a way to subscribe on a channel, and avoid requesting your backend for permission on every resubscribe. Token approach is very good in massive reconnect scenario, when you have many connections and they all resubscribe at once (due to your load balancer reload, for example). But this means that if you unsubscribed client from a channel using server API, client can still resubscribe with token again - until token will expire. In some cases you may want to avoid this. Also, in some cases you want to be notified when someone subscribes to a channel. In this case you may use subscribe proxy feature. When using subscribe proxy every attempt of a client to subscribe on a channel will be translated to request (HTTP or GRPC) from Centrifugo to the application backend. Application backend can decide whether client is allowed to subscribe or not. One advantage of using subscribe proxy is that backend can additionally provide initial channel data for the subscribing client. This is possible using data field of subscribe result generated by backend subscribe handler. { \"proxy_subscribe_endpoint\": \"http://localhost:9000/centrifugo/subscribe\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true, \"proxy_subscribe\": true } ]} And on the backend side define a route /centrifugo/subscribe, check permissions of user upon subscription and return result to Centrifugo according to our subscribe proxy docs. Or simply run GRPC server using our proxy definitions and react on subscription attempt sent from Centrifugo to backend over GRPC. On the client-side code is as simple as: const sub = centrifuge.newSubscription('personal:17');sub.on('publication', function(ctx) { console.log(ctx.data);})sub.subscribe();","s":"#3 - subscribe proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#3---subscribe-proxy","p":284},{"i":295,"t":"tip The approach where you don't need to manage client-side subscriptions. Server-side subscriptions is a way to consume publications from channels without even create Subscription objects on the client side. In general, client side Subscription objects provide a more flexible and controllable way to work with subscriptions. Clients can subscribe/unsubscribe on channels at any point. Client-side subscriptions provide more details about state transitions. With server-side subscriptions though you are consuming publications directly from Client instance: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { token: 'CONNECTION-JWT'});client.on('publication', function(ctx) { console.log('publication received from server-side channel', ctx.channel, ctx.data);});client.connect(); In this case you don't have separate Subscription objects and need to look at ctx.channel upon receiving publication or to publication content to decide how to handle it. Server-side subscriptions could be a good choice if you are using Centrifugo unidirectional transports and don't need dynamic subscribe/unsubscribe behavior. The first way to subscribe client on a server-side channel is to include channels claim into connection JWT: { \"sub\": \"17\", \"channels\": [\"personal:17\"]} Upon successful connection user will be subscribed to a server-side channel by Centrifugo. One downside of using server-side channels is that errors in one server-side channel (like impossible to recover missed messages) may affect the entire connection and result into reconnects, while with client-side subscriptions individual subsription failures do not affect the entire connection. But having one server-side channel per-connection seems a very reasonable idea to me in many cases. And if you have stable set of subscriptions which do not require lifetime state management – this can be a nice approach without additional protocol/network overhead involved.","s":"#4 - server-side channel in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#4---server-side-channel-in-connection-jwt","p":284},{"i":297,"t":"Similar to the previous one for cases when you are authenticating connections over connect proxy instead of using JWT. This is possible using channels field of connect proxy handler result. The code on the client-side is the same as in Option #4 – since we only change the way how list of server-side channels is provided.","s":"#5 - server-side channel in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#5---server-side-channel-in-connect-proxy","p":284},{"i":299,"t":"tip Almost no code approach. As we pointed above Centrifugo knows an ID of the user due to authentication process. So why not combining this knowledge with automatic server-side personal channel subscription? Centrifugo provides exactly this with user personal channel feature. { \"user_subscribe_to_personal\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} This feature only subscribes non-anonymous users to personal channels (those with non-empty user ID). The configuration above will subscribe our user \"17\" to channel personal:#17 automatically after successful authentication.","s":"#6 - automatic personal channel subscription","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#6---automatic-personal-channel-subscription","p":284},{"i":301,"t":"Allows using client-side subscriptions, but skip receiving subscription token. This is only available in Centrifugo PRO at this point. So when generating JWT you can provide additional caps claim which contains channel resource capabilities: Python NodeJS import jwtimport timeclaims = { \"sub\": \"17\", \"exp\": int(time.time()) + 30*60, \"caps\": [ { \"channels\": [\"personal:17\"], \"allow\": [\"sub\"] } ]}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose');(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '17', caps: [ { \"channels\": [\"personal:17\"], \"allow\": [\"sub\"] } ] }) .setProtectedHeader({ alg }) .setExpirationTime('30m') .sign(secret) console.log(token);})(); While in case of single channel the benefit of using this approach is not really obvious, it can help when you are using several channels with stric access permissions per connection, where providing capabilities can help to save some traffic and CPU resources since we avoid generating subscription token for each individual channel.","s":"#7 – capabilities in connection JWT","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#7--capabilities-in-connection-jwt","p":284},{"i":303,"t":"This is very similar to the previous approach, but capabilities are passed to Centrifugo in connect proxy result. So if you are using connect proxy for auth then you can still provide capabilities in the same form as in JWT. This is also a Centrifugo PRO feature.","s":"#8 – capabilities in connect proxy","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#8--capabilities-in-connect-proxy","p":284},{"i":305,"t":"Which way to choose? Well, it depends. Since your application will have more than only a personal user channel in many cases you should decide which approach suits you better in each particular case – it's hard to give the universal advice. Client-side subscriptions are more flexible in general, so I'd suggest using them whenever possible. Though you may use unidirectional transports of Centrifugo where subscribing to channels from the client side is not simple to achieve (though still possible using our server subscribe API). Server-side subscriptions make more sense there. The good news is that all our official bidirectional client SDKs support all the approaches mentioned in this post. Hope designing the channel configuration on top of Centrifugo will be a pleasant experience for you. Attributions Internet network vector created by rawpixel.com - www.freepik.com Cyber security icons created by Smashicons - Flaticon","s":"Teardown","u":"/blog/2022/07/29/101-way-to-subscribe","h":"#teardown","p":284},{"i":307,"t":"Attributions","s":"Attributions","u":"/docs/3/attributions","h":"","p":306},{"i":309,"t":"The following images have been used in the landing page. Icons made by Freepik https://www.flaticon.com/packs/web-development-19","s":"Landing Page Images","u":"/docs/3/attributions","h":"#landing-page-images","p":306},{"i":311,"t":"In Centrifugo v5 we're phasing out old client protocol support, introducing a more intuitive HTTP API, adjusting token management behaviour in SDKs, improving configuration process, and refactoring the history meta ttl option. As the result you get a cleaner, more user-friendly, and optimized Centrifugo experience. And we have important news about the project - check it out in the end of this post. What is Centrifugo? Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, SSE/EventSource, GRPC, SockJS, WebTransport). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server. Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer. Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency. Let's proceed and take a look at most notable changes of Centrifugo v5.","s":"Centrifugo v5 released","u":"/blog/2023/06/29/centrifugo-v5-released","h":"","p":310},{"i":313,"t":"With the introduction of Centrifugo v4, our previous major release, we rolled out a new version of the client protocol along with a set of client SDKs designed to work in conjunction with it. Nevertheless, we maintained support for the old client protocol in Centrifugo v4 to facilitate a seamless migration of applications. In Centrifugo v5 we are discontinuing support for the old protocol. If you have been using Centrifugo v4 with the latest SDKs, this change should have no impact on you. From our perspective, removing support for the old protocol allows us to eliminate a considerable amount of non-obvious branching in the source code and cleanup Protobuf schema definitions.","s":"Dropping old client protocol","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#dropping-old-client-protocol","p":310},{"i":315,"t":"In Centrifugo v5 we are adjusting client SDK specification in the aspect of connection token management. Previously, returning an empty token string from getToken callback resulted in client disconnection with unauthorized reason. There was some problem with it though. We did not take into account the fact that empty token may be a valid scenario actually. Centrifugo supports options to avoid using token at all for anonymous access. So the lack of possibility to switch between token/no token scenarios did not allow users to easily implement login/logout workflow. The only way was re-initializing SDK. Now returning an empty string from getToken is a valid scenario which won't result into disconnect on the client side. It's still possible to disconnect client by returning a special error from getToken function. And we are putting back setToken method to our SDKs – so it's now possible to reset the token to be empty upon user logout. An abstract example in Javascript which demonstrates how login/logout flow may be now implemented with our SDK: const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', { // Provide function which returns empty string for anonymous users, // and proper JWT for authenticated users. getToken: getTokenImplementation});centrifuge.connect();loginButton.addEventListener('click', function() { centrifuge.disconnect(); // Do actual login here. centrifuge.connect();});logoutButton.addEventListener('click', function() { centrifuge.disconnect(); // Reset token - so that getToken will be called on next connect attempt. centrifuge.setToken(\"\"); // Do actual logout here. centrifuge.connect();}); We updated all our SDKs to inherit described behaviour - check out v5 migration guide for more details.","s":"Token behaviour adjustments in SDKs","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#token-behaviour-adjustments-in-sdks","p":310},{"i":317,"t":"One of Centrifugo's key advantages for real-time messaging tasks is its ephemeral channels and per-channel history. In version 5, we've improved one aspect of handling history by offering users the ability to tune the history meta TTL option at the channel namespace level. The history meta TTL is the duration Centrifugo retains meta information about each channel stream, such as the latest offset and current stream epoch. This data allows users to successfully restore connections upon reconnection, particularly useful when subscribed to mostly inactive channels where publications are infrequent. Although the history meta TTL can usually be reasonably large (significantly larger than history TTL), there are certain scenarios where it's beneficial to minimize it as much as possible. One such use case is illustrated in this example. Using Centrifugo SDK and channels with history, it's possible to reliably stream results of asynchronous tasks to clients. As another example, consider a ChatGPT use case where clients ask questions, subscribe to a channel with the answer, and then the response is streamed towards the client token by token. This all may be done over a secure, separate channel protected with a token. With the ability to use a relatively small history meta TTL in the channel namespace, implementing such things is now simpler. Hence, history_meta_ttl is now an option at the channel namespace level (instead of per-engine). However, setting it is optional as we have a global default value for it - see details in the doc.","s":"history_meta_ttl refactoring","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#history_meta_ttl-refactoring","p":310},{"i":319,"t":"When running in cluster Centrifugo nodes can communicate between each other using broker's PUB/SUB. Nodes exchange some information - like statistics, emulation requests, etc. In Centrifugo v5 we are simplifying and making inter-node communication protocol slightly faster by removing extra encoding layers from it's format. Something similar to what we did for our client protocol in Centrifugo v4. This change, however, leads to an incompatibility between Centrifugo v4 and v5 nodes in terms of their communication protocols. Consequently, Centrifugo v5 cannot be part of a cluster with Centrifugo v4 nodes.","s":"Node communication protocol update","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#node-communication-protocol-update","p":310},{"i":321,"t":"From the beginning Centrifugo HTTP API exposed one /api endpoint to make requests with all command types. To work properly HTTP API had to add one additional layer to request JSON payload to be able to distinguish between different API methods: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey API_KEY\" \\ --request POST \\ --data '{\"method\": \"publish\", \"params\": {\"channel\": \"test\", \"data\": {\"x\": 1}}}' \\ http://localhost:8000/api And it worked fine. It additionally supported request batching where users could send many commands to Centrifugo in one request using line-delimited JSON. However, the fact that we were accommodating various commands via a single API endpoint resulted in nested serialized payloads for each command. The top-level method would determine the structure of the params. We addressed this issue in the client protocol in Centrifugo v4, and now we're addressing a similar issue in the inter-node communication protocol in Centrifugo v5. At some point we introduced GRPC API in Centrifugo. In GRPC case we don't have a way to send batches of commands without defining a separate method to do so. These developments highlighted the need for us to align the HTTP API format more closely with the GRPC API. Specifically, we need to separate the command method from the actual method payload, moving towards a structure like this: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: API_KEY\" \\ --request POST \\ --data '{\"channel\": \"test\", \"data\": {\"x\": 1}}' \\ http://localhost:8000/api/publish Note: /api/publish instead of /api in path payload does not include method and params keys anymore we also support X-API-Key header for setting API key to be closer to OpenAPI specification (see more about OpenAPI below) In v5 we implemented the approach above. Many thanks to @StringNick for the help with the implementation and discussions. Our HTTP and GRPC API are very similar now. We've also introduced a new batch method to send multiple commands in both HTTP and GRPC APIs, a feature that was previously unavailable in GRPC. Documentation for v5 was updated to reflect this change. But it worth noting - old API format id still supported. It will be supported for some time while we are migrating our HTTP API libraries to use modern API version. Hopefully users won't be affected by this migration a lot, just switching to a new version of library at some point.","s":"New HTTP API format","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#new-http-api-format","p":310},{"i":323,"t":"One additional benefit of moving to the new HTTP format is the possibility to define a clear OpenAPI schema for each API method Centrifugo has. It was previously quite tricky due to the fact we had one endpoint capable to work with all kinds of commands. This change paves the way for generating HTTP clients based on our OpenAPI specification. We now have Swagger UI built-in. To access it, launch Centrifugo with the \"swagger\": true option and navigate to http://localhost:8000/swagger. The Swagger UI operates on the internal port, so if you're running Centrifugo using our Kubernetes Helm chart, it won't be exposed to the same ingress as client connection endpoints. This is similar to how our Prometheus, admin, API, and debug endpoints currently work.","s":"OpenAPI spec and Swagger UI","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#openapi-spec-and-swagger-ui","p":310},{"i":325,"t":"Another good addition is an OpenTelemetry tracing support for HTTP and GRPC server API requests. If you are using OpenTelemetry in your services you can now now enable tracing export in Centrifugo and find Centrifugo API request exported traces in your tracing collector UI. Description and simple example with Jaeger may be found in observability chapter. We only support OTLP HTTP export format and trace format defined in W3C spec: https://www.w3.org/TR/trace-context/.","s":"OpenTelemetry for server API","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#opentelemetry-for-server-api","p":310},{"i":327,"t":"With the introduction of JWKS support in Centrifugo v4 (a way to validate JWT tokens using a remote endpoint which manages keys and key rotation - see JWK spec) Centrifugo users can rely on JWKS provider (like Keycloak, AWS Cognito) for making authentication. But at the same time developers may want to work with channels using subscription tokens managed in a custom way – without using the same JWKS configuration used for connection tokens. Centrifugo v5 allows doing by introducing the separate_subscription_token_config option. When separate_subscription_token_config is true Centrifugo does not look at general token options at all when verifying subscription tokens and uses config options starting from subscription_token_ prefix instead. Here is an example how to use JWKS for connection tokens, but have HMAC-based verification for subscription tokens: config.json { \"token_jwks_public_endpoint\": \"https://example.com/openid-connect/certs\", \"separate_subscription_token_config\": true, \"subscription_token_hmac_secret_key\": \"separate_secret_which_must_be_strong\"}","s":"Separate config for subscription token","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#separate-config-for-subscription-token","p":310},{"i":329,"t":"With every release, Centrifugo offers more and more options. One thing we've noticed is that some options from previous Centrifugo options, which were already removed, still persist in the user's configuration file. Another issue is that a single typo in the configuration key can cost hours of debugging especially for Centrifugo new users. What is worse, the typo might result in unexpected behavior if the feature isn't properly tested before being run in production. In Centrifugo v5, we are addressing these problems. Now, Centrifugo logs on WARN level about unknown keys found in the configuration upon server start-up. Not only in the configuration file but also verifying the validity of environment variables (looking at those starting with CENTRIFUGO_ prefix). This should help clean up the configuration to align with the latest Centrifugo release and catch typos at an early stage. It looks like this: 08:25:33 [WRN] unknown key found in the namespace object key=watch namespace_name=xx08:25:33 [WRN] unknown key found in the proxy object key=type proxy_name=connect08:25:33 [WRN] unknown key found in the configuration file key=granulr_proxy_mode08:25:33 [WRN] unknown key found in the environment key=CENTRIFUGO_ADDRES These warnings do not prevent server to start so you can gradually clean up the configuration.","s":"Unknown config keys warnings","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#unknown-config-keys-warnings","p":310},{"i":331,"t":"Centrifugo v5 supports a special url parameter for bidirectional websocket which turns on using native WebSocket frame ping-pong mechanism instead of server-to-client application level pings Centrifugo uses by default. This simplifies debugging Centrifugo protocol with tools like Postman, wscat, websocat, etc. Previously it was inconvenient due to the fact Centrifugo sends periodic ping message to the client ({} in JSON protocol scenario) and expects pong response back within some time period. Otherwise Centrifugo closes connection. This results in problems with mentioned tools because you had to manually send {} pong message upon ping message. So typical session in wscat could look like this: ❯ wscat --connect ws://localhost:8000/connection/websocketConnected (press CTRL+C to quit)> {\"id\": 1, \"connect\": {}}< {\"id\":1,\"connect\":{\"client\":\"9ac9de4e-5289-4ad6-9aa7-8447f007083e\",\"version\":\"0.0.0\",\"ping\":25,\"pong\":true}}< {}Disconnected (code: 3012, reason: \"no pong\") The parameter is called cf_ws_frame_ping_pong, to use it connect to Centrifugo bidirectional WebSocket endpoint like ws://localhost:8000/websocket/connection?cf_ws_frame_ping_pong=true. Here is an example which demonstrates working with Postman WebSocket where we connect to local Centrifugo and subscribe to two channels test1 and test2: You can then proceed to Centrifugo admin web UI, publish something to these channels and see publications in Postman. Note, how we sent several JSON commands in one WebSocket frame to Centrifugo from Postman in the example above - this is possible since Centrifugo protocol supports batches of commands in line-delimited format. We consider this feature to be used only for debugging, in production prefer using our SDKs without using cf_ws_frame_ping_pong parameter – because app-level ping-pong is more efficient and our SDKs detect broken connections due to it.","s":"Simplifying protocol debug with Postman","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#simplifying-protocol-debug-with-postman","p":310},{"i":333,"t":"As you know SockJS is deprecated in Centrifugal ecosystem since Centrifugo v4. In this release we are not removing support for it – but we may do this in the next release. Unfortunately, SockJS client repo is poorly maintained these days. And some of its iframe-based transports are becoming archaic. If you depend on SockJS and you really need fallback for WebSocket – consider switching to Centrifugo own bidirectional emulation for the browser which works over HTTP-streaming (using modern fetch API with Readable streams) or SSE. It should be more performant and work without sticky sessions requirement (sticky sessions is an optimization in our implementation). More details may be found in Centrifugo v4 release post. If you think SockJS is still required for your use case - reach us out so we could think about the future steps together.","s":"The future of SockJS","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#the-future-of-sockjs","p":310},{"i":335,"t":"Finally, some important news we mentioned in the beginning! Centrifugo is now backed by the company called Centrifugal Labs LTD - a Cyprus-registered technology company. This should help us to finally launch Centrifugo PRO offering – the product we have been working on for a couple of years now and which has some unique and powerful features like real-time analytics or push notification API. As a Centrifugo user you will start noticing mentions of Centrifugal Labs LTD in our licenses, Github organization, throughout this web site. And that's mostly it - no radical changes at this point. We will still be working on improving Centrifugo trying to find a balance between OSS and PRO versions. Which is difficult TBH – but we will try. An ideal plan for us – make Centrifugo development sustainable enough to have the possibility for features from the PRO version flow to the OSS version eventually. The reality may be harder than this of course.","s":"Introducing Centrifugal Labs LTD","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#introducing-centrifugal-labs-ltd","p":310},{"i":337,"t":"That's all about most notable things happened in Centrifugo v5. We updated documentation to reflect the changes in v5, also some documentation chapters were rewritten. For example, take a look at the refreshed Design overview. Several more changes and details outlined in the migration guide for Centifugo v5. Please feel free to contact in the community rooms if you have questions about the release. And as usual, let the Centrifugal force be with you!","s":"Conclusion","u":"/blog/2023/06/29/centrifugo-v5-released","h":"#conclusion","p":310},{"i":339,"t":"Framework integrations tip In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. But there are some community-driven projects that provide integration for more native experience or even some additional functionality on top of Centrifugo. laravel-centrifugo integration with Laravel framework laravel-centrifugo-broadcaster one more integration with Laravel framework to consider CentrifugoBundle integration with Symfony framework Django-instant integration with Django framework","s":"Framework integrations","u":"/docs/3/ecosystem/integrations","h":"","p":338},{"i":341,"t":"flow_diagrams For swimlines.io: Client <- App Backend: JWTnote:The backend generates JWT for a user and passes it to the client side.Client -> Centrifugo: Client connects to Centrifugo with JWT...: {fas-spinner} Persistent connection establishedClient -> Centrifugo: Client issues channel subscribe requestsCentrifugo -->> Client: Client receives real-time updates from channels Client -> Centrifugo: Connect requestnote:Client connects to Centrifugo without JWT.Centrifugo -> App backend: Sends request further (via HTTP or GRPC)note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.App backend -> Centrifugo: Connect replyCentrifugo -> Client: Connect Reply...: {fas-spinner} Persistent connection established Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers. Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers.","s":"flow_diagrams","u":"/docs/3/flow_diagrams","h":"","p":340},{"i":343,"t":"On this page","s":"Design overview","u":"/docs/3/getting-started/design","h":"","p":342},{"i":345,"t":"Originally Centrifugo was built with the unidirectional flow as the main approach. Though Centrifugo itself used a bidirectional protocol between a client and a server to allow client dynamically create subscriptions, Centrifugo did not allow using it for sending data from client to server. With this approach publications travel only from server to a client. All requests that generate new data first go to the application backend (for example over AJAX call of backend API). The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event from a backend side to Centrifugo API. This is a pretty natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is decoupled from the application in this case. During Centrifugo v2 life cycle this paradigm evolved a bit. It's now possible to send RPC requests from client to Centrifugo and the request will be then proxied to the application backend. Also, connection attempts and publications to channels can now be proxied. So bidirectional connection between client and Centrifugo is now available for utilizing by developers in both directions. For example, here is how publish diagram could look like when using publish request proxy feature: So at the moment, the number of possible integration ways increased.","s":"Idiomatic usage","u":"/docs/3/getting-started/design","h":"#idiomatic-usage","p":342},{"i":347,"t":"Idiomatic Centrifugo usage requires having the main application database from which initial and actual state can be loaded at any point in time. While Centrifugo has channel history, it has been mostly designed to reduce the load on the main application database when all users reconnect at once (in case of load balancer configuration reload, Centrifugo restart, temporary network problems, etc). This allows to radically reduce the load on the application main database during reconnect storm. Since such disconnects are usually pretty short in time having a reasonably small number of messages cached in history is sufficient. The addition of history iteration API shifts possible use cases a bit. Calling history chunk by chunk allows keeping larger number of publications per channel. But depending on Engine used and configuration of the underlying storage history stream persistence characteristics can vary. For example, with Memory Engine history will be lost upon Centrifugo restart. With Redis or Tarantool engines history will survive Centrifugo restarts but depending on a storage configuration it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis RDB and AOF, configure replication for storage high-availability, use Redis Cluster or maybe synchronous replication with Tarantool. Centrifugo provides ways to distinguish whether the missed messages can't be restored from Centrifugo history upon recovery so a client should restore state from the main application database. So Centrifugo message history can be used as a complementary way to restore messages and thus reduce a load on the main application database most of the time.","s":"Message history considerations","u":"/docs/3/getting-started/design","h":"#message-history-considerations","p":342},{"i":349,"t":"By default, the message delivery model of Centrifugo is at most once. With history and the position/recovery features enabled it's possible to achieve at least once guarantee within history retention time and size. After abnormal disconnect clients have an option to recover missed messages from the publication stream cache that Centrifugo maintains. Without the positioning or recovery features enabled a message sent to Centrifugo can be theoretically lost while moving towards clients. Centrifugo tries to do its best to prevent message loss on a way to online clients, but the application should tolerate a loss. As noted Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. Also, it compensates at most once delivery of broker (Redis, Tarantool) PUB/SUB by using additional publication offset checks and periodic offset synchronization. At this moment Centrifugo message recovery is designed for a short-term disconnect period (think no more than one hour for a typical chat application, but this can vary). After this period (which can be configured per channel basis) Centrifugo removes messages from the channel history cache. In this case, Centrifugo may tell the client that some messages can not be recovered, so your application state should be loaded from the main database.","s":"Message delivery model","u":"/docs/3/getting-started/design","h":"#message-delivery-model","p":342},{"i":351,"t":"Message order in channels is guaranteed to be the same while you publish messages into channel one after another or publish them in one request. If you do parallel publications into the same channel then Centrifugo can't guarantee message order since those may be processed concurrently by Centrifugo.","s":"Message order guarantees","u":"/docs/3/getting-started/design","h":"#message-order-guarantees","p":342},{"i":353,"t":"It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely only on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in AJAX call response and render it. This way user that posts a comment will think that everything works just fine. Be careful to not draw comments twice in this case - think about idempotent identifiers for your entities.","s":"Graceful degradation","u":"/docs/3/getting-started/design","h":"#graceful-degradation","p":342},{"i":355,"t":"Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to the network failures and unexpected shut down of Centrifugo node, there are chances that clients can be presented in a presence up to one minute more (until presence entry expiration). Also, channel presence does not scale well for channels with lots of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows the response size becomes larger. In some cases, presence_stats API call can be sufficient to avoid receiving the entire presence state.","s":"Online presence considerations","u":"/docs/3/getting-started/design","h":"#online-presence-considerations","p":342},{"i":357,"t":"Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with Nats broker. See engines. All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications. But, if you approach broker resource limits (CPU or memory) then it's possible: Use Centrifugo consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool) Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters) Nats broker should scale well itself in cluster setup All brokers can be set up in highly available way so there won't be a single point of failure. All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact and the fact that Centrifugo provides hooks for the application to understand history loss makes the process of resharding mostly automatic. As soon as you need to add additional broker shard (when using client-side sharding) you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently part of the data will stay on the same broker nodes. Applications should handle cases that channel data moved to another shard and restore a state from the main application database when needed.","s":"Scalability considerations","u":"/docs/3/getting-started/design","h":"#scalability-considerations","p":342},{"i":359,"t":"On this page","s":"Main highlights","u":"/docs/3/getting-started/highlights","h":"","p":358},{"i":361,"t":"Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.). It works as a standalone service with well-defined communication contracts. It fits very well in both monolithic and microservice architecture. Application developers should not change backend philosophy at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.","s":"Simple integration","u":"/docs/3/getting-started/highlights","h":"#simple-integration","p":358},{"i":363,"t":"Centrifugo is pretty fast. It's written in Go language, uses fast and battle-tested open-source libraries internally, has some internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTT with broker, connection hub sharding to avoid contention, JSON and Protobuf encoding speedups over code generation and other. See a Million WebSocket with Centrifugo post in our blog to see some real-world numbers.","s":"Great performance","u":"/docs/3/getting-started/highlights","h":"#great-performance","p":358},{"i":365,"t":"Centrifugo scales to many machines with a help of PUB/SUB brokers. The main PUB/SUB engine Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck also. There are other options to scale: KeyDB, Nats, Tarantool.","s":"Built-in scalability","u":"/docs/3/getting-started/highlights","h":"#built-in-scalability","p":358},{"i":367,"t":"Centrifugo supports JSON and binary Protobuf protocol for client-server communication. The bidirectional protocol is defined by strict schema and several ready-to-use connectors wrap this protocol, handle asynchronous message passing, timeouts, reconnect, and various Centrifugo client API features.","s":"Strict client protocol","u":"/docs/3/getting-started/highlights","h":"#strict-client-protocol","p":358},{"i":369,"t":"The main transport in Centrifugo is WebSocket. It's a bidirectional transport on top of TCP with low overhead. For browsers that do not support WebSocket Centrifugo provides SockJS support. Centrifugo v3 also introduced support for unidirectional transports for real-time updates: like SSE (EventSource), HTTP streaming, GRPC unidirectional stream. Using unidirectional transport is sufficient for many real-time applications and does not require using custom client connectors – just native APIs or GRPC-generated code.","s":"Variety of real-time transports","u":"/docs/3/getting-started/highlights","h":"#variety-of-real-time-transports","p":358},{"i":371,"t":"Centrifugo can authenticate connections using JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon connection attempt. It's possible to proxy original request headers or request metadata (in the case of GRPC connection). It supports the JWK specification.","s":"Flexible authentication","u":"/docs/3/getting-started/highlights","h":"#flexible-authentication","p":358},{"i":373,"t":"Connections can expire, developers can choose a way to handle connection refresh – using client-side refresh workflow, or server-side call from Centrifugo to the application backend.","s":"Connection management","u":"/docs/3/getting-started/highlights","h":"#connection-management","p":358},{"i":375,"t":"Centrifugo is a PUB/SUB server – users subscribe to channels to receive real-time updates. Message sent to a channel will be delivered to all active subscribers. There are several different types of channels to deal with permissions.","s":"Channel (room) concept","u":"/docs/3/getting-started/highlights","h":"#channel-room-concept","p":358},{"i":377,"t":"Centrifugo is unique in terms of the fact that it supports both client-side and server-side channel subscriptions.","s":"Different types of subscriptions","u":"/docs/3/getting-started/highlights","h":"#different-types-of-subscriptions","p":358},{"i":379,"t":"You can fully utilize bidirectional persistent connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing external traffic and, in most cases, provides better latency characteristics.","s":"RPC over bidirectional connection","u":"/docs/3/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":358},{"i":381,"t":"It's possible to turn on an online presence feature for channels so you will have information about active channel subscribers. Channel join and leave events (when a user subscribes/unsubscribes) can also be sent.","s":"Online presence information","u":"/docs/3/getting-started/highlights","h":"#online-presence-information","p":358},{"i":383,"t":"Optionally Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With a channel history, Centrifugo can help to survive the mass reconnect scenario. Clients can automatically recover missed messages from a cache – thus reducing the load on your primary database. It's also possible to manually iterate over a stream from a client or a server-side.","s":"Message history in channels","u":"/docs/3/getting-started/highlights","h":"#message-history-in-channels","p":358},{"i":385,"t":"Built-in administrative web UI allows publishing messages to channels, looking at Centrifugo cluster state, monitoring stats, etc.","s":"Embedded admin web UI","u":"/docs/3/getting-started/highlights","h":"#embedded-admin-web-ui","p":358},{"i":387,"t":"Centrifugo works on Linux, macOS, and Windows.","s":"Cross-platform","u":"/docs/3/getting-started/highlights","h":"#cross-platform","p":358},{"i":389,"t":"Centrifugo supports various deploy ways: in Docker, using prepared RPM or DEB packages, via Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, has an official Grafana dashboard for Prometheus data source.","s":"Ready to deploy","u":"/docs/3/getting-started/highlights","h":"#ready-to-deploy","p":358},{"i":391,"t":"Centrifugo stands on top of open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All client connectors are also MIT-licensed.","s":"Open-source","u":"/docs/3/getting-started/highlights","h":"#open-source","p":358},{"i":393,"t":"Centrifugo PRO extends Centrifugo with several unique features which can give interesting advantages for business adopters. With Centrifugo PRO it's possible to trace specific user or specific channel events in real-time. Centrifugo PRO integrates with ClickHouse for real-time connection analytics. This all may help with understanding client behavior, inspect and analyze an application on a very granular level. Centrifugo PRO offers even more extensions that tend to be useful in practice. This includes user active status and throttling features. Active status is useful to build messenger-like applications where you want to show online indicators of users based on last activity time, throttling can help you limit the number of operations each user may execute on a Centrifugo cluster. For additional details, refer to the Centrifugo PRO documentation.","s":"Pro features","u":"/docs/3/getting-started/highlights","h":"#pro-features","p":358},{"i":395,"t":"On this page","s":"Frequently Asked Questions","u":"/docs/3/faq","h":"","p":394},{"i":397,"t":"This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here. Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case. You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.","s":"How many connections can one Centrifugo instance handle?","u":"/docs/3/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":394},{"i":399,"t":"Depending on transport used and features enabled the amount of RAM required per each connection can vary. For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections. For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.","s":"Memory usage per connection?","u":"/docs/3/faq","h":"#memory-usage-per-connection","p":394},{"i":401,"t":"Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker. See engines and scalability considerations.","s":"Can Centrifugo scale horizontally?","u":"/docs/3/faq","h":"#can-centrifugo-scale-horizontally","p":394},{"i":403,"t":"See design overview","s":"Message delivery model","u":"/docs/3/faq","h":"#message-delivery-model","p":394},{"i":405,"t":"See design overview.","s":"Message order guarantees","u":"/docs/3/faq","h":"#message-order-guarantees","p":394},{"i":407,"t":"No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel. When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).","s":"Should I create channels explicitly?","u":"/docs/3/faq","h":"#should-i-create-channels-explicitly","p":394},{"i":409,"t":"Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application. But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc. One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.","s":"What about best practices with the number of channels?","u":"/docs/3/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":394},{"i":411,"t":"Currently, no. We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this: Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID) Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates. Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point. Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel. In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.","s":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/3/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":394},{"i":413,"t":"No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere. Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end). To summarize: if you are using binary Protobuf clients and binary payloads everywhere – you are fine. if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine. if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.","s":"Can I have both binary and JSON clients in one channel?","u":"/docs/3/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":394},{"i":415,"t":"While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel. You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.","s":"Online presence for chat apps - online status of your contacts","u":"/docs/3/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":394},{"i":417,"t":"The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.","s":"Centrifugo stops accepting new connections, why?","u":"/docs/3/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":394},{"i":419,"t":"Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.","s":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/3/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":394},{"i":421,"t":"Yes, Centrifugo works with HTTP/2. You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable: GODEBUG=\"http2server=0\" centrifugo -c config.json Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.","s":"Does Centrifugo work with HTTP/2?","u":"/docs/3/faq","h":"#does-centrifugo-work-with-http2","p":394},{"i":423,"t":"If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.","s":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/3/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":394},{"i":425,"t":"Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via GCM, or browsers over Web Push API. This is a goal for another software. But the reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.","s":"What if I need to send push notifications to mobile or web applications?","u":"/docs/3/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":394},{"i":427,"t":"You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.","s":"How can I know a message is delivered to a client?","u":"/docs/3/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":394},{"i":429,"t":"It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend. We suggest using one of the available approaches: When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API. Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network. Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself). Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.","s":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/3/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":394},{"i":431,"t":"There are several ways to achieve it: use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers you can use subscribe proxy feature to validate subscriptions, see chapter about proxy finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations","s":"How to create a secure channel for two users only (private chat case)?","u":"/docs/3/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":394},{"i":433,"t":"In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled. For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients. The same relates to other channel options.","s":"What's the best way to organize channel configuration?","u":"/docs/3/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":394},{"i":435,"t":"Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there. Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.","s":"Does Centrifugo support webhooks?","u":"/docs/3/faq","h":"#does-centrifugo-support-webhooks","p":394},{"i":437,"t":"Centrifugo does not support disconnect hooks at this point. First of all, there is no guarantee that the disconnect process will have a time to execute on the client-side (as the client can just switch off its device or simply lose internet connection). This means that a server may notice a connection loss with some delay (thanks to PING/PONG). Also, Centrifugo node can be unexpectedly killed. So there is a chance that disconnect event won't have a chance to be emitted to the backend. One more reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. To reduce the load during connect process Centrifugo has JWT authentication. Even if disconnect events were queued/rate-limited there could be situations when your app processes disconnect hook while user already reconnected and connect event processed. This is a racy situation which you will need to handle somehow (possibly based on unique client ID attached to each connection). If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend from the client-side and update user status somewhere on the backend (use Redis maybe). This is a pretty robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo refresh proxy for the task of periodic backend pinging. In this case you will notice that user (or particular client) left app with some delay – this may be a acceptable trade-off in many cases. Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together.","s":"Why Centrifugo does not have disconnect hooks?","u":"/docs/3/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":394},{"i":439,"t":"No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.","s":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/3/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":394},{"i":441,"t":"Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel). There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers. You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs. The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.","s":"How scalable is the online presence and join/leave features?","u":"/docs/3/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":394},{"i":443,"t":"Ask in our community rooms:","s":"I have not found an answer to my question here:","u":"/docs/3/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":394},{"i":445,"t":"On this page","s":"Client API showcase","u":"/docs/3/getting-started/client_api","h":"","p":444},{"i":447,"t":"Each Centrifugo client allows connecting to a server. const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.connect(); In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to: const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.setToken('')centrifuge.connect(); See authentication chapter for more information on how to generate connection JWT. If you are using connect proxy then you may go without setting JWT.","s":"Connecting to a server","u":"/docs/3/getting-started/client_api","h":"#connecting-to-a-server","p":444},{"i":449,"t":"After connecting you can disconnect from a server at any moment. centrifuge.disconnect();","s":"Disconnecting from a server","u":"/docs/3/getting-started/client_api","h":"#disconnecting-from-a-server","p":444},{"i":451,"t":"Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.","s":"Reconnecting to a server","u":"/docs/3/getting-started/client_api","h":"#reconnecting-to-a-server","p":444},{"i":453,"t":"All client implementations allow setting handlers on connect and disconnect events. For example: centrifuge.on('connect', function(connectCtx){ console.log('connected', connectCtx)});centrifuge.on('disconnect', function(disconnectCtx){ console.log('disconnected', disconnectCtx)});","s":"Connection lifecycle events","u":"/docs/3/getting-started/client_api","h":"#connection-lifecycle-events","p":444},{"i":455,"t":"Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel. centrifuge.subscribe('channel', function(messageCtx) { console.log(messageCtx);}) Client can subscribe to different channels. Subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events. In idiomatic case messages published to channels from application backend over Centrifugo server API. Though it's not always true. Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel properly configured. Client keeps last seen Publication offset and restores missed publications since known offset upon reconnecting. If recovery failed then client implementation provides a flag inside subscribe event to let the application know that some publications were missed – so you may need to load state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery in a dedicated chapter.","s":"Subscribe to a channel","u":"/docs/3/getting-started/client_api","h":"#subscribe-to-a-channel","p":444},{"i":457,"t":"To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance: centrifuge.on('publish', function(messageCtx) { console.log(messageCtx);}); It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.","s":"Server-side subscriptions","u":"/docs/3/getting-started/client_api","h":"#server-side-subscriptions","p":444},{"i":459,"t":"A client can send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the WebSocket or SockJS connection. RPC is only available when RPC proxy configured. const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"Send RPC","u":"/docs/3/getting-started/client_api","h":"#send-rpc","p":444},{"i":461,"t":"Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel: Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Call channel history","u":"/docs/3/getting-started/client_api","h":"#call-channel-history","p":444},{"i":463,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats","u":"/docs/3/getting-started/client_api","h":"#presence-and-presence-stats","p":444},{"i":465,"t":"The main objective of Centrifugo is to manage persistent client connections established over various real-time transports (including WebSocket, HTTP-Streaming, SSE, WebTransport, etc – see here) and offer an API for publishing data towards established connections. Clients subscribe to channels, hence Centrifugo implements PUB/SUB mechanics to transmit published data to all online channel subscribers. Centrifugo employs Redis as its primary scalability option – so that it's possible to distribute client connections amongst numerous Centrifugo nodes without worrying about channel subscribers connected to separate nodes. Redis is incredibly mature, simple, and fast in-memory storage. Due to various built-in data structures and PUB/SUB support Redis is a perfect fit to be both Centrifugo Broker and PresenceManager (we will describe what's this shortly). In Centrifugo v4.1.0 we introduced an updated implementation of our Redis Engine (Engine in Centrifugo == Broker + PresenceManager) which provides sufficient performance improvements to our users. This post discusses the factors that prompted us to update Redis Engine implementation and provides some insight into the results we managed to achieve. We'll examine a few well-known Go libraries for Redis communication and contrast them against Centrifugo tasks.","s":"Improving Centrifugo Redis Engine throughput and allocation efficiency with Rueidis Go library","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"","p":464},{"i":467,"t":"Before we get started, let's define what Centrifugo's Broker and PresenceManager terms mean. Broker is an interface responsible for maintaining subscriptions from different Centrifugo nodes (initiated by client connections). That helps to scale client connections over many Centrifugo instances and not worry about the same channel subscribers being connected to different nodes – since all Centrifugo nodes connected with PUB/SUB. Messages published to one node are delivered to a channel subscriber connected to another node. Another major part of Broker is keeping an expiring publication history for channels (streams). So that Centrifugo may provide a fast cache for messages missed by clients upon going offline for a short period and compensate at most once delivery of Redis PUB/SUB using Publication incremental offsets. Centrifugo uses STREAM and HASH data structures in Redis to store channel history and stream meta information. In general Centrifugo architecture may be perfectly illustrated by this picture (Gophers are Centrifugo nodes all connected to Broker, and sockets are WebSockets): PresenceManager is an interface responsible for managing online presence information - list of currently active channel subscribers. While the connection is alive we periodically update presence entries for channels connection subscribed to (for channels where presence is enabled). Presence data should expire if not updated by a client connection for some time. Centrifugo uses two Redis data structures for managing presence in channels - HASH and ZSET.","s":"Broker and PresenceManager","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#broker-and-presencemanager","p":464},{"i":469,"t":"For a long time, the gomodule/redigo package served as the foundation for the Redis Engine implementation in Centrifugo. Huge props go to Mr Gary Burd for creating it. Redigo offers a connection Pool to Redis. A simple usage of it involves getting the connection from the pool, issuing request to Redis over that connection, and then putting the connection back to the pool after receiving the result from Redis. Let's write a simple benchmark which demonstrates simple usage of Redigo and measures SET operation performance: func BenchmarkRedigo(b *testing.B) { pool := redigo.Pool{ MaxIdle: 128, MaxActive: 128, Wait: true, Dial: func() (redigo.Conn, error) { return redigo.Dial(\"tcp\", \":6379\") }, } defer pool.Close() b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { c := pool.Get() _, err := c.Do(\"SET\", \"redigo\", \"test\") if err != nil { b.Fatal(err) } c.Close() } })} Let's run it: BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/op Seems pretty fast, but we can improve it further.","s":"Redigo","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo","p":464},{"i":471,"t":"To increase a throughput in Centrifugo, instead of using Redigo's Pool for each operation, we acquired a dedicated connection from the Pool and utilized Redis pipelining to send multiple commands where possible. Redis pipelining improves performance by executing multiple commands using a single client-server-client round trip. Instead of executing many commands one by one, you can queue the commands in a pipeline and then execute the queued commands as if it is a single command. Redis processes commands in order and sends individual response for each command. Given a single CPU nature of Redis, reducing the number of active connections when using pipelining has a positive impact on throughput – therefore pipelining is beneficial from this angle as well. You can quickly estimate the benefits of pipelining by running Redis locally and running redis-benchmark which comes with Redis distribution over it: > redis-benchmark -n 100000 set key valueSummary: throughput summary: 84674.01 requests per second And with pipelining: > redis-benchmark -n 100000 -P 64 set key valueSummary: throughput summary: 666880.00 requests per second In Centrifugo we are using smart batching technique for collecting pipeline (also described in one of the previous posts in this blog). To demonstrate benefits from using pipelining let's look at the following benchmark: const ( maxCommandsInPipeline = 512 numPipelineWorkers = 1)type command struct { errCh chan error}type sender struct { cmdCh chan command pool redigo.Pool}func newSender(pool redigo.Pool) *sender { p := &sender{ cmdCh: make(chan command), pool: pool, } go func() { for { for i := 0; i < numPipelineWorkers; i++ { p.runPipelineRoutine() } } }() return p}func (s *sender) send() error { errCh := make(chan error, 1) cmd := command{ errCh: errCh, } // Submit command to be executed by runPipelineRoutine. s.cmdCh <- cmd return <-errCh}func (s *sender) runPipelineRoutine() { conn := p.pool.Get() defer conn.Close() for { select { case cmd := <-s.cmdCh: commands := []command{cmd} conn.Send(\"set\", \"redigo\", \"test\") loop: // Collect batch of commands to send to Redis in one RTT. for i := 0; i < maxCommandsInPipeline; i++ { select { case cmd := <-s.cmdCh: commands = append(commands, cmd) conn.Send(\"set\", \"redigo\", \"test\") default: break loop } } // Flush all collected commands to the network. err := conn.Flush() if err != nil { for i := 0; i < len(commands); i++ { commands[i].errCh <- err } continue } // Read responses to commands, they come in order. for i := 0; i < len(commands); i++ { _, err := conn.Receive() commands[i].errCh <- err } } }}func BenchmarkRedigoPipelininig(b *testing.B) { pool := redigo.Pool{ Wait: true, Dial: func() (redigo.Conn, error) { return redigo.Dial(\"tcp\", \":6379\") }, } defer pool.Close() sender := newSender(pool) b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { err := sender.send() if err != nil { b.Fatal(err) } } })} This is a strategy that we employed in Centrifugo for a long time. As you can see code with automatic pipelining gets more complex, and in real life it's even more complicated to support different types of commands, channel send timeouts, and server shutdowns. What about the performance of this approach? BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/opBenchmarkRedigoPipelininig-8 1840758 604.7 ns/op 176 B/op 4 allocs/op Operation latency reduced from 4648 ns/op to 604.7 ns/op – not bad right? It's worth mentioning that upon increased RTT between application and Redis the approach with pipelining will provide worse throughput. But it still can be better than in pool-based approach. Let's say we have latency 5ms between app and Redis. This means that with pool size of 128 you will be able to issue up to 128 * (1000 / 5) = 25600 requests per second over 128 connections. With the pipelining approach above the theoretical limit is 512 * (1000 / 5) = 102400 requests per second over a single connection (though in case of using code for pipelining shown above we need to have larger parallelism, say 512 instead of 128). And it can scale further if you increase numPipelineWorkers to work over several connections in paralell. Though increasing numPipelineWorkers has negative effect on CPU – we will discuss this later in this post. Redigo is an awesome battle-tested library that served us great for a long time.","s":"Redigo with pipelining","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#redigo-with-pipelining","p":464},{"i":473,"t":"There are three modes in which Centrifugo can work with Redis these days: Connecting to a standalone single Redis instance Connecting to Redis in master-replica configuration, where Redis Sentinel controls the failover process Connecting to Redis Cluster All modes additionally can be used with client-side consistent sharding. So it's possible to scale Redis even without a Redis Cluster setup. Unfortunately, with pure Redigo library, it's only possible to implement [ 1 ] – i.e. connecting to a single standalone Redis instance. To support the scheme with Sentinel you whether need to have a proxy between the application and Redis which proxies the connection to Redis master. For example, with Haproxy it's possible in this way: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 on-marked-down shutdown-sessions on-marked-up shutdown-backup-sessions server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:6379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin Or, you need to additionally import FZambia/sentinel library - which provides a communication layer with Redis Sentinel on top of Redigo's connection Pool. For communicating with Redis Cluster one more library may be used – mna/redisc which is also a layer on top of redigo basic functionality. Combining redigo + FZambia/sentinel + mna/redisc we managed to implement all three connection modes. This worked, though resulted in rather tricky Redis setup. Also, it was difficult to re-use existing pipelining code we had for a standalone Redis with Redis Cluster. As a result, Centrifugo only used pipelining in a standalone or Sentinel Redis cases. When using Redis Cluster, however, Centrifugo merely used the connection pool to issue requests thus not benefiting from request pipelining. Due to this we had some code duplication to send the same requests in various Redis configurations. Another thing is that Redigo uses interface{} for command construction. To send command to Redis Redigo has Do method which accepts name of the command and variadic interface{} arguments to construct command arguments: Do(commandName string, args ...interface{}) (reply interface{}, err error) While this works well and you can issue any command to Redis, you need to be very accurate when constructing a command. This also adds some allocation overhead. As we know more memory allocations lead to the increased CPU utilization because the allocation process itself requires more processing power and the GC is under more strain. At some point we felt that eliminating additional dependencies (even though I am the author of one of them) and reducing allocations in Redis communication layer is a nice step forward for Centrifugo. So we started looking around for redigo alternatives. To summarize, here is what we wanted from Redis library: Possibility to work with all three Redis setup options we support: standalone, master-replica(s) with Sentinel, Redis Cluster, so we can depend on one library instead of three Less memory allocations (and more type-safety API is a plus) Support working with RESP2-only Redis servers as we need that for backwards compatibility. And some vendors like Redis Enterprise still support RESP2 protocol only The library should be actively maintained","s":"Motivation to migrate","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#motivation-to-migrate","p":464},{"i":475,"t":"The most obvious alternative to Redigo is go-redis/redis package. It's popular, regularly gets updates, used by a huge amount of Go projects (Grafana, Thanos, etc.). And maintained by Vladimir Mihailenco who created several more awesome Go libraries, like msgpack for example. I personally successfully used go-redis/redis in several other projects I worked on. To avoid setup boilerplate for various Redis installation variations go-redis/redis has UniversalClient. From docs: UniversalClient is a wrapper client which, based on the provided options, represents either a ClusterClient, a FailoverClient, or a single-node Client. This can be useful for testing cluster-specific applications locally or having different clients in different environments. In terms of implementation go-redis/redis also has internal pool of connections to Redis, similar to redigo. It's also possible to use Client.Pipeline method to allocate a Pipeliner interface and use it for pipelining. So UniversalClient reduces setup boilerplate for different Redis installation types and number of dependencies we had, and it provide very similar way to pipeline requests so we could easily re-implement things we had with Redigo. Go-redis also provides more type-safety when constructing commands compared to Redigo, almost every command in Redis is implemented as a separate method of Client, for example Publish defined as: func (c Client) Publish(ctx context.Context, channel string, message interface{}) *IntCmd You can see though that we still have interface{} here for message argument type. I suppose this was implemented in such way for convenience – to pass both string or []byte. But it still produces some extra allocations. Without pipelining the simplest program with go-redis/redis may look like this: func BenchmarkGoredis(b *testing.B) { client := redis.NewUniversalClient(&redis.UniversalOptions{ Addrs: []string{\":6379\"}, PoolSize: 128, }) defer client.Close() b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { resp := client.Set(context.Background(), \"goredis\", \"test\", 0) if resp.Err() != nil { b.Fatal(resp.Err()) } } })} Let's run it: BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/opBenchmarkGoredis-8 268444 4561 ns/op 244 B/op 8 allocs/op Result is pretty comparable to Redigo, though Go-redis allocates more (btw most of allocations come from the connection liveness check upon getting from the pool which can not be turned off). It's interesting – if we dive deeper into what is it we can discover that this is the only way in Go to check connection was closed without reading data from it. The approach was originally introduced by go-sql-driver/mysql, it's not cross-platform, and related issue may be found in Go issue tracker. But as I said in Centrifugo we already used pipelining over the dedicated connection for all operations so we avoid frequently getting connections from the pool. And early experiments proved that go-redis may provide some performance benefits for our use case. At some point @j178 sent a pull request to Centrifuge library with Broker and PresenceManager implementations based on go-redis/redis. The amount of code to cover all the various Redis setups was reduced, we got only one dependency instead of three 🔥 But what about performance? Here we will show results for several operations which are typical for Centrifugo: Publish a message to a channel without saving it to the history - this is just a Redis PUBLISH command going through Redis PUB/SUB system (RedisPublish) Publish message to a channel with saving it to history - this involves executing the LUA script on Redis side where we add a publication to STREAM data structure, update meta information HASH, and finally PUBLISH to PUB/SUB (RedisPublish_History) Subscribe to a channel - that's a SUBSCRIBE Redis command, this is important to have it fast as Centrifugo should be able to re-subscribe to all the channels in the system upon mass client reconnect scenario (RedisSubscribe) Recovering missed publication state from channel STREAM, this is again may be called lots of times when all clients reconnect at once (RedisRecover). Updating connection presence information - many connections may periodically update their channel online presence information in Redis (RedisAddPresence) Here are the benchmark results we got when comparing redigo (v1.8.9) implementation (old) and go-redis/redis (v9.0.0-rc.2) implementation (new) with Redis v6.2.7 on Mac with M1 processor and benchmark paralellism 128: ❯ benchstat redigo_p128.txt goredis_p128.txtname old time/op new time/op deltaRedisPublish-8 1.45µs ±10% 1.88µs ± 4% +29.32% (p=0.000 n=10+10)RedisPublish_History-8 12.5µs ± 6% 9.7µs ± 3% -22.77% (p=0.000 n=10+10)RedisSubscribe-8 1.47µs ±24% 1.47µs ±10% ~ (p=0.469 n=10+10)RedisRecover-8 18.4µs ± 2% 6.3µs ± 0% -65.78% (p=0.000 n=10+8)RedisAddPresence-8 3.72µs ± 1% 3.40µs ± 1% -8.74% (p=0.000 n=10+10)name old alloc/op new alloc/op deltaRedisPublish-8 483B ± 0% 499B ± 0% +3.37% (p=0.000 n=9+10)RedisPublish_History-8 1.30kB ± 0% 1.08kB ± 0% -16.67% (p=0.000 n=10+10)RedisSubscribe-8 892B ± 2% 662B ± 6% -25.83% (p=0.000 n=10+10)RedisRecover-8 1.25kB ± 1% 1.00kB ± 0% -19.91% (p=0.000 n=10+10)RedisAddPresence-8 907B ± 0% 827B ± 0% -8.82% (p=0.002 n=7+8)name old allocs/op new allocs/op deltaRedisPublish-8 10.0 ± 0% 9.0 ± 0% -10.00% (p=0.000 n=10+10)RedisPublish_History-8 29.0 ± 0% 25.0 ± 0% -13.79% (p=0.000 n=10+10)RedisSubscribe-8 22.0 ± 0% 14.0 ± 0% -36.36% (p=0.000 n=8+7)RedisRecover-8 29.0 ± 0% 23.0 ± 0% -20.69% (p=0.000 n=10+10)RedisAddPresence-8 18.0 ± 0% 17.0 ± 0% -5.56% (p=0.000 n=10+10) danger Please note that this benchmark is not a pure performance comparison of two Go libraries for Redis – it's a performance comparison of Centrifugo Engine methods upon switching to a new library. Or visualized in Grafana: note Centrifugo benchmarks results shown in the post use parallelism 128. If someone interested to check numbers for paralellism 1 or 16 – check out this comment on Github. We observe a noticeable reduction in allocations in these benchmarks and in most benchmarks (presented here and other not listed in this post) we observed a reduced latency. Overall, results convinced us that the migration from redigo to go-redis/redis may provide Centrifugo with everything we aimed for – all the goals for a redigo alternative outlined above were successfully fullfilled. One good thing go-redis/redis allowed us to do is to use Redis pipelining also in a Redis Cluster case. It's possible due to the fact that go-redis/redis re-maps pipeline objects internally based on keys to execute pipeline on the correct node of Redis Cluster. Actually, we could do the same based on redigo + mna/redisc, but here we got it for free. BTW, there is a page with comparison between redigo and go-redis/redis in go-redis/redis docs which outlines some things I mentioned here and some others. But we have not migrated to go-redis/redis in the end. And the reason is another library – rueidis.","s":"Go-redis/redis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#go-redisredis","p":464},{"i":477,"t":"While results were good with go-redis/redis we also made an attempt to implement Redis Engine on top of rueian/rueidis library written by @rueian. According to docs, rueidis is: A fast Golang Redis client that supports Client Side Caching, Auto Pipelining, Generics OM, RedisJSON, RedisBloom, RediSearch, RedisAI, RedisGears, etc. The readme of rueidis contains benchmark results where it hugely outperforms go-redis/redis in terms of operation latency/throughput in both single Redis and Redis Custer setups: rueidis works with standalone Redis, Sentinel Redis and Redis Cluster out of the box. Just like UniversalClient of go-redis/redis. So it also allowed us to reduce code boilerplate to work with all these setups. Again, let's try to write a simple program like we had for Redigo and Go-redis above: func BenchmarkRueidis(b *testing.B) { client, err := rueidis.NewClient(rueidis.ClientOption{ InitAddress: []string{\":6379\"}, }) if err != nil { b.Fatal(err) } b.ResetTimer() b.SetParallelism(128) b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { cmd := client.B().Set().Key(\"rueidis\").Value(\"test\").Build() res := client.Do(context.Background(), cmd) if res.Error() != nil { b.Fatal(res.Error()) } } })} And run it: BenchmarkRedigo-8 228804 4648 ns/op 62 B/op 2 allocs/opBenchmarkGoredis-8 268444 4561 ns/op 244 B/op 8 allocs/opBenchmarkRueidis-8 2908591 418.5 ns/op 4 B/op 1 allocs/op rueidis library comes with automatic implicit pipelining, so you can send each request in isolated way while rueidis makes sure the request becomes part of the pipeline sent to Redis – thus utilizing the connection between an application and Redis most efficiently with maximized throughput. The idea of implicit pipelining with Redis is not new and Go ecosystem already had joomcode/redispipe library which implemented it (though it comes with some limitations which made it unsuitable for Centrifugo use case). So applications that use a pool-based approach for communication with Redis may observe dramatic improvements in latency and throughput when switching to the Rueidis library. For Centrifugo we didn't expect such a huge speed-up as shown in the above graphs since we already used pipelining in Redis Engine. But rueidis implements some ideas which allow it to be efficient. Insights about these ideas are provided by Rueidis author in a \"Writing a High-Performance Golang Client Library\" series of posts on Medium: Part 1: Batching on Pipeline Part 2: Reading Again From Channels? Part 3: Remove the Bad Busy Loops With the Sync.Cond I did some prototypes with rueidis which were super-promising in terms of performance. There were some issues found during that early prototyping (mostly with PUB/SUB) – but all of them were quickly resolved by Rueian. Until v0.0.80 release rueidis did not support RESP2 though, so we could not replace our Redis Engine implementation with it. But as soon as it got RESP2 support we opened a pull request with alternative implementation. Since auto-pipelining is used in rueidis by default we were able to remove some of our own pipelining management code – so the Engine implementation is more concise now. One more thing to mention is a simpler PUB/SUB code we were able to write with rueidis. One example is that in redigo case we had to periodically PING PUB/SUB connection to maintain it alive, rueidis does this automatically. Regarding performance, here are the benchmark results we got when comparing redigo (v1.8.9) implementation (old) and rueidis (v0.0.90) implementation (new): ❯ benchstat redigo_p128.txt rueidis_p128.txtname old time/op new time/op deltaRedisPublish-8 1.45µs ±10% 0.56µs ± 1% -61.53% (p=0.000 n=10+9)RedisPublish_History-8 12.5µs ± 6% 9.7µs ± 1% -22.43% (p=0.000 n=10+9)RedisSubscribe-8 1.47µs ±24% 1.45µs ± 1% ~ (p=0.484 n=10+9)RedisRecover-8 18.4µs ± 2% 6.2µs ± 1% -66.08% (p=0.000 n=10+10)RedisAddPresence-8 3.72µs ± 1% 3.60µs ± 1% -3.34% (p=0.000 n=10+10)name old alloc/op new alloc/op deltaRedisPublish-8 483B ± 0% 91B ± 0% -81.16% (p=0.000 n=9+10)RedisPublish_History-8 1.30kB ± 0% 0.39kB ± 0% -70.08% (p=0.000 n=10+8)RedisSubscribe-8 892B ± 2% 360B ± 0% -59.66% (p=0.000 n=10+10)RedisRecover-8 1.25kB ± 1% 0.36kB ± 1% -71.52% (p=0.000 n=10+10)RedisAddPresence-8 907B ± 0% 151B ± 1% -83.34% (p=0.000 n=7+9)name old allocs/op new allocs/op deltaRedisPublish-8 10.0 ± 0% 2.0 ± 0% -80.00% (p=0.000 n=10+10)RedisPublish_History-8 29.0 ± 0% 10.0 ± 0% -65.52% (p=0.000 n=10+10)RedisSubscribe-8 22.0 ± 0% 6.0 ± 0% -72.73% (p=0.002 n=8+10)RedisRecover-8 29.0 ± 0% 7.0 ± 0% -75.86% (p=0.000 n=10+10)RedisAddPresence-8 18.0 ± 0% 3.0 ± 0% -83.33% (p=0.000 n=10+10) Or visualized in Grafana: 2.5x times more publication throughput than we had before! Instead of 700k publications/sec, we went towards 1.7 million publications/sec due to drastically decreased publish operation latency (1.45µs -> 0.59µs). This means that our previous Engine implementation under-utilized Redis, and Rueidis just pushes us towards Redis limits. The latency of most other operations is also reduced. The allocation effectiveness of the implementation based on \"rueidis\" is best. As you can see rueidis helped us to generate sufficiently fewer memory allocations for all our Redis operations. Allocation improvements directly affect Centrifugo node CPU usage. Though we will talk about CPU more later below. For Redis Cluster case we also got benchmark results similar to the standalone Redis results above. I might add that I enjoyed building commands with rueidis. All Redis commands may be constructed using a builder approach. Rueidis comes with builders generated for all Redis commands. As an illustration, this is a process of building a PUBLISH Redis command: This drastically reduces a chance to make a stupid mistake while constructing a command. Instead of always opening Redis docs to see a command syntax it's now possible to just start typing - and quickly come to the complete command to send.","s":"Rueidis","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#rueidis","p":464},{"i":479,"t":"After making all these benchmarks and implementing Engine in Rueidis I decided to check whether Centrifugo consumes less CPU with it. I expected a notable CPU reduction as Rueidis Engine implementation allocates much less than Redigo-based. Turned out it's not that simple. I ran Centrifugo with some artificial load and noticed that CPU consumption of the new implementation is actually... worse than we had with Redigo-based engine under equal conditions!😩 But why? As I mentioned above Redis pipelining is a technique when several commands may be combined into one batch to send over the network. In case of automatic pipelining the size of generated batches start playing a crucial role in application and Redis CPU usage – since smaller command batches result into more read/write system calls to the kernel on both application and Redis server sides. That's why projects like Twemproxy which sit between app and Redis have sich a good effect on Redis CPU usage among other things. As we have seen above, Rueidis provides a better throughput and latency, but it's more agressive in terms of flushing data to the network. So in its default configuration we get smaller batches under th equal conditions than we had before in our own pipelining implementation based on Redigo (shown in the beginning of this post). Luckily, there is an option in Rueidis called MaxFlushDelay which allows to slow down write loop a bit to give Rueidis a chance to collect more commands to send in one batch. When this option is used Rueidis will make a pause after each network flush not bigger than selected value of MaxFlushDelay (please note, that this is a delay after flushing collected pipeline commands, not an additional delay for each request). Using some reasonable value it's possible to drastically reduce both application and Redis CPU utilization. To demonstrate this I created a repo: https://github.com/FZambia/pipelines. This repo contains three benchmarks where we use automatic pipelining: based on redigo, based on go-redis/redis and rueidis. In these benchmarks we produce concurrent requests, but instead of pushing the system towards the limits we are limiting number of requests sent to Redis, so we put all libraries in equal conditions. To rate limit requests we are using uber-go/ratelimit library. For example, to allow rate no more than 100k commands per second we can do sth like this: rl := ratelimit.New(100, ratelimit.Per(time.Millisecond))for { rl.Take() ...} We limit requests per second we could actually just write ratelimit.New(100000) – but we aim to get a more smooth distribution of requests over time - so using millisecond resolution. Let's run all the benchmarks in the default configuration: Average CPU usage during the test (a bit rough but enough for demonstration): Redigo Go-redis/redis Rueidis Application CPU, % 95 99 116 Redis CPU, % 36 35 42 OK, Rueidis-based implementation is the worst here despite of allocating less than others. So let's try to change this by setting MaxFlushDelay to sth like 100 microseconds: Now CPU usage is: Redigo Go-redis/redis Rueidis Application CPU, % 95 99 59 Redis CPU, % 36 35 12 So we can achieve great CPU usage reduction. CPU went from 116% to 59% for the application side, and from 42% to only 12% for Redis! We are sacrificing latency though. Given the fact the CPU utilization reduction is very notable the trade-off is pretty fair. caution It's definitely possible to improve CPU usage in Redigo and Go-redis/redis cases too – using similar technique. But the goal here was to improve Rueidis-based engine implementation to make it comparable or better than our Redigo-based implementation in terms of CPU utilization. As you can see we were able to achieve better CPU results just by using 100 microseconds delay after each network flush. In real life, where we are not running Redis on localhost and have some network latency in between application and Redis, this delay should be insignificant at all. Indeed, adding MaxFlushDelay can even improve (!) the latency you have. You may wonder what happened with benchmarks we showed above after we added MaxFlushDelay option. In Centrifugo we chose default value 100 microseconds, and here are results on localhost (old without delay, new with delay): > benchstat rueidis_p128.txt rueidis_delay_p128.txtname old time/op new time/op deltaRedisPublish-8 559ns ± 1% 468ns ± 0% -16.35% (p=0.000 n=9+8)RedisPublish_History-8 9.72µs ± 1% 9.67µs ± 1% -0.52% (p=0.007 n=9+8)RedisSubscribe-8 1.45µs ± 1% 1.27µs ± 1% -12.49% (p=0.000 n=9+10)RedisRecover-8 6.25µs ± 1% 5.85µs ± 0% -6.32% (p=0.000 n=10+10)RedisAddPresence-8 3.60µs ± 1% 3.33µs ± 1% -7.52% (p=0.000 n=10+10)(rest is not important here...) It's even better for this set of benchmarks. Though while it's better for these benchmarks the numbers may differ for other under different conditions. For example, in the benchmarks we run we use concurrency 128, if we reduce concurrency we will notice reduced throughput – as batches Rueidis collects become smaller. Smaller batches + some delay to collect = less requests per second to send. The problem is that the value to pause Rueidis write loop is a very use case specific, it's pretty hard to provide a reasonable default for it. Depending on request rate/size, network latency etc. you may choose a larger or smaller delay. In v4.1.0 we start with hardcoded 100 microsecond MaxFlushDelay which seems sufficient for most use cases and showed good results in the benchmarks - though possibly we will have to make it tunable later. To check that Centrifugo benchmarks also utilize less CPU I added rate limiter (50k rps per second) to benchmarks and compared version without MaxFlushDelay and with 100 microsecond MaxFlushDelay: 50k req per second Without delay With 100mks delay BenchmarkPublish Centrifugo - 75%, Redis - 24% Centrifugo - 44%, Redis - 9% BenchmarkPublish_History Centrifugo - 80% , Redis - 67% Centrifugo - 55%, Redis - 50% BenchmarkSubscribe Centrifugo - 80%, Redis - 30% Centrifugo - 45% , Redis - 14% BenchmarkRecover Centrifugo - 84%, Redis - 51% Centrifugo - 51%, Redis - 36% BenchmarkPresence Centrifugo - 114%, Redis - 69% Centrifugo - 90%, Redis - 60% note In this test I replaced BenchmarkAddPresence with BenchmarkPresence (get information about all online subscribers in channel) to also make sure we have CPU reduction when using read-intensive method, i.e. when Redis response is reasonably large. We observe a notable CPU usage improvement here. Hope you understand now why increasing numPipelineWorkers value in the pipelining code showed before results into increased CPU usage on app and Redis sides – due to smaller batch sizes and more read/write system calls as the consequence. note BTW, would it be a nice thing if Go benchmarking suite could show a CPU usage of the process in addition to time and alloc stats? 🤔","s":"Switching to Rueidis: reducing CPU usage","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#switching-to-rueidis-reducing-cpu-usage","p":464},{"i":481,"t":"The last thing to check is how new implementation works upon increased RTT between application and Redis. To add artificial latency on localhost on Linux one can use tc tool as shown here by Daniel Stenberg. But I am on MacOS so the simplest way I found was using Shopify/toxiproxy. Sth like running a server: toxyproxy-server And then in another terminal I used toxiproxy-cli to create toxic Redis proxy with additional latency on port 26379: toxiproxy-cli create -l localhost:26379 -u localhost:6379 toxic_redistoxiproxy-cli toxic add -t latency -a latency=5 toxic_redis The benchmark results are (old is Redigo-based, new is Rueidis-based): > benchstat redigo_latency_p128.txt rueidis_delay_latency_p128.txtname old time/op new time/op deltaRedisPublish-8 31.5µs ± 1% 5.6µs ± 3% -82.26% (p=0.000 n=9+10)RedisPublish_History-8 62.8µs ± 3% 10.6µs ± 4% -83.05% (p=0.000 n=10+10)RedisSubscribe-8 1.52µs ± 5% 6.05µs ± 8% +298.70% (p=0.000 n=8+10)RedisRecover-8 48.3µs ± 3% 7.3µs ± 4% -84.80% (p=0.000 n=10+10)RedisAddPresence-8 52.3µs ± 4% 5.8µs ± 2% -88.94% (p=0.000 n=10+10)(rest is not important here...) We see that new Engine implementation behaves much better for most cases. But what happened to Subscribe operation? It did not change at all in Redigo case – the same performance as if there is no additional latency involved! Turned out that when we call Subscribe in Redigo case, Redigo only flushes data to the network without waiting synchronously for subscribe result. It makes sense in general and we can listen to subscribe notifications asynchronously, but in Centrifugo we relied on the returned error thinking that it includes succesful subscription result from Redis - meaning that we already subscribed to a channel at that point. And this could theoretically lead to some rare bugs in Centrifugo. Rueidis library waits for subscribe response. So here the behavior of rueidis while differs from redigo in terms of throughput under increased latency just fits Centrifugo better in terms of behavior. So we go with it.","s":"Adding latency","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#adding-latency","p":464},{"i":483,"t":"Migrating from Redigo to Rueidis library was not just a task of rewriting code, we had to carefully test various aspects of Redis Engine behaviour – latency, throughput, CPU utilization of application, and even CPU utilization of Redis itself under the equal application load conditions. I think that we will find more projects in Go ecosystem using rueidis library shortly. Not just because of its allocation efficiency and out-of-the-box throughput, but also due to a convenient type-safe command API. For most Centrifugo users this migration means more efficient CPU usage as new implementation allocates less memory (less work to allocate and less strain on GC) and we tried to find a reasonable batch size to reduce the number of system calls for common operations. While latency and throughput of single Centrifugo node should be better as we make concurrent Redis calls from many goroutines. Hopefully readers will learn some tips from this post which can help to achieve effective communication with Redis from Go or another programming language. A few key takeaways: Redis pipelining may increase throughput and reduce latency, it can also reduce CPU utilization of Redis Don't blindly trust Go benchmark numbers but also think about CPU effect of changes you made (sometimes of the external system also) Reduce the number of system calls to decrease CPU utilization Everything is a trade-off – latency or resource usage? Your own WebSocket server or Centrifugo? Don't rely on someone's else benchmarks, including those published here. Measure for your own use case. Take into account your load profile, paralellism, network latency, data size, etc. P.S. One thing worth mentioning and which may be helpful for someone is that during our comparison experiments we discovered that Redis 7 has a major latency increase compared to Redis 6 when executing Lua scripts. So if you have performance sensitive code with Lua scripts take a look at this Redis issue. With the help of Redis developers some things already improved in unstable Redis branch, hopefully that issue will be closed at the time you read this post.","s":"Conclusion","u":"/blog/2022/12/20/improving-redis-engine-performance","h":"#conclusion","p":464},{"i":485,"t":"On this page","s":"Integration guide","u":"/docs/3/getting-started/integration","h":"","p":484},{"i":487,"t":"First, you need to do is download/install Centrifugo server. See install chapter for details.","s":"0. Install","u":"/docs/3/getting-started/integration","h":"#0-install","p":484},{"i":489,"t":"Create basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and chapter about server API for API description. The simplest way to do this automatically is by using genconfig command: ./centrifugo genconfig – which will generate config.json file for you with all required fields. Properly configure allowed_origins option.","s":"1. Configure Centrifugo","u":"/docs/3/getting-started/integration","h":"#1-configure-centrifugo","p":484},{"i":491,"t":"In the configuration file of your application backend register several variables: Centrifugo secret and Centrifugo API key you set on a previous step and Centrifugo API address. By default, the API address is http://localhost:8000/api. You must never reveal token secret and API key to your users.","s":"2. Configure your backend","u":"/docs/3/getting-started/integration","h":"#2-configure-your-backend","p":484},{"i":493,"t":"Now your users can start connecting to Centrifugo. You should get a client library (see list of available client SDK) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here. Every client should provide a connection token (JWT) on connect. You must generate this token on your backend side using Centrifugo secret key you set to backend configuration (note that in the case of RSA tokens you are generating JWT with a private key). See how to generate this JWT in special chapter. You pass this token from the backend to your frontend app (pass it in template context or use separate request from client-side to get user-specific JWT from backend side). And use this token when connecting to Centrifugo (for example browser client has a special method setToken). There is also a way to authenticate connections without using JWT - see chapter about proxying to backend. You are connecting to Centrifugo using one of the available transports. At this moment you can choose from: WebSocket, with JSON or binary protobuf protocol. See more info in a chapter about WebSocket transport SockJS (only supports JSON protocol). See more info about SockJS transport","s":"3. Connect to Centrifugo","u":"/docs/3/getting-started/integration","h":"#3-connect-to-centrifugo","p":484},{"i":495,"t":"After connecting to Centrifugo subscribe clients to channels they are interested in. See more about channels in special chapter. All client libraries provide a way to handle messages coming to a client from a channel after subscribing to it. There is also a way to subscribe connection to a list of channels on the server side at the moment of connection establishment. See chapter about server-side subscriptions.","s":"4. Subscribe to channels","u":"/docs/3/getting-started/integration","h":"#4-subscribe-to-channels","p":484},{"i":497,"t":"So everything should work now – as soon as a user opens some page of your application it must successfully connect to Centrifugo and subscribe to a channel (or channels). Now let's imagine you want to send a real-time message to users subscribed on a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, the administrator just created a new post, the user pressed the like button, etc. Anyway, this is an event your backend just got, and you want to immediately share it with interested users. You can do this using Centrifugo HTTP API. To simplify your life we have several API libraries for different languages. You can publish messages into a channel using one of those libraries or you can simply follow API description to construct API requests yourself - this is very simple. Also Centrifugo supports GRPC API. As soon as you published a message to the channel it must be delivered to your client.","s":"5. Publish to channel","u":"/docs/3/getting-started/integration","h":"#5-publish-to-channel","p":484},{"i":499,"t":"To put this all into production you need to deploy Centrifugo on your production server. To help you with this we have many things like Docker image, rpm and deb packages, Nginx configuration. See Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.","s":"6. Deploy to production","u":"/docs/3/getting-started/integration","h":"#6-deploy-to-production","p":484},{"i":501,"t":"Don't forget to monitor your production Centrifugo setup.","s":"7. Monitor Centrifugo","u":"/docs/3/getting-started/integration","h":"#7-monitor-centrifugo","p":484},{"i":503,"t":"As soon as you are close to machine resource limits you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using Redis engine.","s":"8. Scale Centrifugo","u":"/docs/3/getting-started/integration","h":"#8-scale-centrifugo","p":484},{"i":505,"t":"That's all for basics. The documentation actually covers lots of other concepts Centrifugo server has: scalability, private channels, admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ.","s":"9. Read FAQ","u":"/docs/3/getting-started/integration","h":"#9-read-faq","p":484},{"i":507,"t":"On this page","s":"Install Centrifugo","u":"/docs/3/getting-started/installation","h":"","p":506},{"i":509,"t":"For a local development the simplest way to get Centrifugo is from binary release (i.e. single all-contained executable file). Binary releases available on Github. Download latest release for your operating system, unpack it and you are done. Centrifugo is pre-built for: Linux 64-bit (linux_amd64) Linux 32-bit (linux_386) Linux ARM 64-bit (linux_arm64) MacOS (darwin_amd64) MacOS on Apple Silicon (darwin_arm64) Windows (windows_amd64) FreeBSD (freebsd_amd64) ARM v6 (linux_armv6) Archives contain a single statically compiled binary centrifugo file that is ready to run: ./centrifugo -h See the version of Centrifugo: ./centrifugo version Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo then there is genconfig command which generates a minimal configuration file to get started: ./centrifugo genconfig It creates a configuration file config.json with some auto-generated option values in a current directory (by default). tip It's possible to generate file in YAML or TOML format, i.e. ./centrifugo genconfig -c config.toml Having a configuration file you can finally run Centrifugo instance: ./centrifugo --config=config.json We will talk about a configuration in detail in the next sections. You can also put or symlink centrifugo into your bin OS directory and run it from anywhere: centrifugo --config=config.json","s":"Install from the binary release","u":"/docs/3/getting-started/installation","h":"#install-from-the-binary-release","p":506},{"i":511,"t":"Centrifugo server has a docker image available on Docker Hub. docker pull centrifugo/centrifugo Run: docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json Note that docker allows setting nofile limits in command-line arguments which is pretty important to handle lots of simultaneous persistent connections and not run out of open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter. caution Pin to the exact Docker Image tag in production, for example: centrifugo/centrifugo:v3.0.0, this will help to avoid unexpected problems during re-deploy process.","s":"Docker image","u":"/docs/3/getting-started/installation","h":"#docker-image","p":506},{"i":513,"t":"Create configuration file config.json: { \"token_hmac_secret_key\": \"my_secret\", \"api_key\": \"my_api_key\", \"admin_password\": \"password\", \"admin_secret\": \"secret\", \"admin\": true} Create docker-compose.yml: centrifugo: container_name: centrifugo image: centrifugo/centrifugo:v3 volumes: - ./config.json:/centrifugo/config.json command: centrifugo -c config.json ports: - 8000:8000 ulimits: nofile: soft: 65535 hard: 65535 Run with: docker-compose up","s":"Docker-compose example","u":"/docs/3/getting-started/installation","h":"#docker-compose-example","p":506},{"i":515,"t":"See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.","s":"Kubernetes Helm chart","u":"/docs/3/getting-started/installation","h":"#kubernetes-helm-chart","p":506},{"i":517,"t":"Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io. At moment, we support versions of the following distributions: 64-bit Debian 9 Stretch 64-bit Debian 10 Buster 64-bit Debian 11 Bullseye 64-bit Ubuntu 16.04 Xenial 64-bit Ubuntu 18.04 Bionic 64-bit Ubuntu 20.04 Focal Fossa 64-bit Centos 7 64-bit Centos 8 See full list of available packages and installation instructions. Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.","s":"RPM and DEB packages for Linux","u":"/docs/3/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":506},{"i":519,"t":"If you are developing on macOS then you can install Centrifugo over brew: brew tap centrifugal/centrifugobrew install centrifugo","s":"With brew on macOS","u":"/docs/3/getting-started/installation","h":"#with-brew-on-macos","p":506},{"i":521,"t":"You need Go language installed: git clone https://github.com/centrifugal/centrifugo.gitcd centrifugogo build./centrifugo","s":"Build from source","u":"/docs/3/getting-started/installation","h":"#build-from-source","p":506},{"i":523,"t":"On this page","s":"Centrifugo introduction","u":"/docs/3/getting-started/introduction","h":"","p":522},{"i":525,"t":"Centrifugo was born to help applications with a server-side written in a language or a framework without built-in concurrency support. In this case, dealing with persistent connections is a real headache that usually can only be resolved by introducing a shift in the technology stack and spending enough time to create a production-ready solution. For example, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others have poor and not performant support of working with many persistent connections for the real-time messaging task. In this case, Centrifugo is a very straightforward and non-obtrusive way to introduce real-time updates and handle lots of persistent connections without radical changes in application backend architecture. Developers could proceed writing a backend with a favorite language or favorite framework, keep existing architecture – and just let Centrifugo deal with persistent connections. At the moment, Centrifugo provides some advanced and unique features that can simplify a developer's life and save months of development, even if the application backend is built with the asynchronous concurrent language. One example is that Centrifugo can scale out-of-the-box to many machines with several supported brokers. And there are more things to mention – see detailed highlights further in the docs.","s":"Motivation","u":"/docs/3/getting-started/introduction","h":"#motivation","p":522},{"i":527,"t":"As mentioned above, Centrifugo runs as a standalone service that cares about handling persistent connections from application users. Application backend and frontend can be written in any programming language. Clients connect to Centrifugo and subscribe to channels. As soon as some event happens application backend can publish a message with event payload into a channel using Centrifugo API. The message will be delivered to all clients currently connected and subscribed to a channel. So Centrifugo is a user-facing PUB/SUB server in a nutshell. Here is a simplified scheme:","s":"Concepts","u":"/docs/3/getting-started/introduction","h":"#concepts","p":522},{"i":529,"t":"We have rooms in Telegram and Discord: See you there!","s":"Join community","u":"/docs/3/getting-started/introduction","h":"#join-community","p":522},{"i":531,"t":"On this page","s":"Database-driven namespace configuration","u":"/docs/3/pro/db_namespaces","h":"","p":530},{"i":533,"t":"As soon as you point Centrifugo PRO to an admin storage and enable storage namespace management, Centrifugo will load namespaces from database table on start. Changes made in web UI will then propagate to all running Centrifugo nodes in up to 30 seconds. info Centrifugo nodes cache namespace configuration in memory so if Centrifugo temporarily lost connection to a database it will continue working with previous namespace configuration until connection problems will be resolved.","s":"How it works","u":"/docs/3/pro/db_namespaces","h":"#how-it-works","p":530},{"i":535,"t":"By default namespace database management is off – i.e. namespaces loaded on Centrifugo start from a configuration file (or environment variable). To enable namespace management through database add the following into configuration file: config.json { ... \"admin_storage\": { \"enabled\": true, \"storage_type\": \"sqlite\", \"storage_dsn\": \"/path/to/centrifugo.db\", \"manage_namespaces\": true }} Centrifugo PRO supports several SQL database backends to keep namespace information: SQLite (storage_type: sqlite) PostgreSQL (storage_type: postgresql) MySQL (storage_type: mysql) Each storage type has its own storage_dsn format. For SQLite it's just a path to a db file. PostgreSQL dsn format described here. Example: config.json { ... \"admin_storage\": { \"enabled\": true, \"storage_type\": \"postgresql\", \"storage_dsn\": \"host=localhost user=postgres password=mysecretpassword dbname=centrifugo port=5432 sslmode=disable\", \"manage_namespaces\": true }} MySQL dsn format described here. Example: config.json { ... \"admin_storage\": { \"enabled\": true, \"storage_type\": \"mysql\", \"storage_dsn\": \"user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local\", \"manage_namespaces\": true }}","s":"Configuration","u":"/docs/3/pro/db_namespaces","h":"#configuration","p":530},{"i":537,"t":"On this page","s":"Install and run PRO version","u":"/docs/3/pro/install_and_run","h":"","p":536},{"i":539,"t":"Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).","s":"Binary release","u":"/docs/3/pro/install_and_run","h":"#binary-release","p":536},{"i":541,"t":"Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro: docker run --ulimit nofile=65536:65536 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v3.2.2 centrifugo -c config.json","s":"Docker image","u":"/docs/3/pro/install_and_run","h":"#docker-image","p":536},{"i":543,"t":"You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag: values.yaml ...image: registry: docker.io repository: centrifugo/centrifugo-pro tag: v3.2.2","s":"Kubernetes","u":"/docs/3/pro/install_and_run","h":"#kubernetes","p":536},{"i":545,"t":"DEB package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v3.2.2/centrifugo-pro_3.2.2-0_amd64.debsudo dpkg -i centrifugo-pro_3.2.2-0_amd64.deb","s":"Debian and Ubuntu","u":"/docs/3/pro/install_and_run","h":"#debian-and-ubuntu","p":536},{"i":547,"t":"RPM package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v3.2.2/centrifugo-pro-3.2.2-0.x86_64.rpmsudo yum install centrifugo-pro-3.2.2-0.x86_64.rpm","s":"Centos","u":"/docs/3/pro/install_and_run","h":"#centos","p":536},{"i":549,"t":"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits. Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this: config.json { ... \"license\": \"\"} tip If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.","s":"Setting PRO license key","u":"/docs/3/pro/install_and_run","h":"#setting-pro-license-key","p":536},{"i":551,"t":"On this page","s":"Centrifugo PRO overview","u":"/docs/3/pro/overview","h":"","p":550},{"i":553,"t":"Centrifugo PRO includes the following features: Everything from Centrifugo OSS Channel and user tracing provides a way to look at all client protocol frames in the specified channel or per user ID. Real-time analytics with ClickHouse for a great system observability, reporting and trending. User status feature to understand activity state for a list of users. Operation throttling to protect client API from misusing and frontend bugs. User connections API to query for all active user sessions with additional information. User blocking API to block/unblock abusive users by ID. JWT revoking and invalidation to revoking tokens by token ID (JTI) and invalidating user's tokens on issue time basis. Faster performance to reduce resource usage on server side. Singleflight for online presence and history to reduce load on the broker. Near real-time CPU and RSS memory usage stats. info PRO features can change with time. We reserve a right to move features from PRO to OSS version if there is a clear signal that this is required to do for the Centrifugo ecosystem.","s":"Features","u":"/docs/3/pro/overview","h":"#features","p":550},{"i":555,"t":"You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example: Centrifugo handles up to 50 concurrent connections up to 2 server nodes supported up to 20 API requests per second allowed This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future. caution Centrifugo PRO is distributed under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept license terms.","s":"Sandbox mode","u":"/docs/3/pro/overview","h":"#sandbox-mode","p":550},{"i":557,"t":"To run without limits Centrifugo PRO requires a license key. At this point we are not issuing license keys for Centrifugo PRO as we are in the process of defining the pricing strategy for it. Please contact us over centrifugal.dev@gmail.com, we can add you to the list of interested customers and will appreciate if you share which features you are mostly interested in.","s":"Pricing","u":"/docs/3/pro/overview","h":"#pricing","p":550},{"i":559,"t":"CPU and RSS stats A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI. Here is how this looks like: The information updated in near real-time (with several seconds delay). It's also available as part of info API.","s":"CPU and RSS stats","u":"/docs/3/pro/process_stats","h":"","p":558},{"i":561,"t":"On this page","s":"Operation throttling","u":"/docs/3/pro/throttling","h":"","p":560},{"i":563,"t":"At this moment Centrifugo PRO provides throttling over Redis. It's only possible to throttle by the user ID. Requests from anonymous users can't be throttled. Throttling with Redis uses token bucket algorithm internally. Here is a list of operations that can be throttled: connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with method resolution) An example configuration: config.json { ... \"redis_throttling\": { \"enabled\": false, \"redis_address\": \"localhost:6379\", \"buckets\": { \"publish\": { \"enabled\": true, \"interval\": \"1s\", \"rate\": 1, \"capacity\": 1 }, \"rpc\": { \"enabled\": true, \"interval\": \"1s\", \"rate\": 10, \"capacity\": 1, \"method_override\": [ { \"method\": \"updateActiveStatus\", \"interval\": \"20s\", \"rate\": 1, \"capacity\": 1 } ] } } }} This configuration enables throttling and throttles publish attempts in a way that only 1 publication is possible in 1 second from the same user. Redis configuration for throttling feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for throttling feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_throttling\": { \"enabled\": false, \"use_redis_from_engine\": true, \"buckets\": { \"publish\": { \"enabled\": true, \"interval\": \"1s\", \"rate\": 1, \"capacity\": 1 } } }} In this case throttling will simply connect to Redis instances configured for an Engine.","s":"Redis throttling","u":"/docs/3/pro/throttling","h":"#redis-throttling","p":560},{"i":565,"t":"On this page","s":"Faster performance","u":"/docs/3/pro/performance","h":"","p":564},{"i":567,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.","s":"Faster HTTP API","u":"/docs/3/pro/performance","h":"#faster-http-api","p":564},{"i":569,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC API","u":"/docs/3/pro/performance","h":"#faster-grpc-api","p":564},{"i":571,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster HTTP proxy","u":"/docs/3/pro/performance","h":"#faster-http-proxy","p":564},{"i":573,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC proxy","u":"/docs/3/pro/performance","h":"#faster-grpc-proxy","p":564},{"i":575,"t":"Centrifugo PRO has an optimized decoding of JWT claims.","s":"Faster JWT decoding","u":"/docs/3/pro/performance","h":"#faster-jwt-decoding","p":564},{"i":577,"t":"Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.","s":"Faster GRPC unidirectional stream","u":"/docs/3/pro/performance","h":"#faster-grpc-unidirectional-stream","p":564},{"i":579,"t":"Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.","s":"Examples","u":"/docs/3/pro/performance","h":"#examples","p":564},{"i":581,"t":"Sorry, your browser doesn't support embedded video. In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.","s":"Publish HTTP API","u":"/docs/3/pro/performance","h":"#publish-http-api","p":564},{"i":583,"t":"Sorry, your browser doesn't support embedded video. In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.","s":"History HTTP API","u":"/docs/3/pro/performance","h":"#history-http-api","p":564},{"i":585,"t":"On this page","s":"Migrating to v3","u":"/docs/3/getting-started/migration_v3","h":"","p":584},{"i":587,"t":"Client protocol has some backward incompatible changes regarding working with history API and removing deprecated fields.","s":"Client-side changes","u":"/docs/3/getting-started/migration_v3","h":"#client-side-changes","p":584},{"i":589,"t":"Call to history API from client-side now does not return all publications from history cache. It returns only information about a stream with zero publications. Clients should explicitly provide a limit when calling history API. Also, the maximum allowed limit can be set by client_history_max_publication_limit option (by default 300). We provide a boolean flag use_unlimited_history_by_default on configuration file top level to enable previous behavior while you migrate client applications to use explicit limit.","s":"No unlimited history by default","u":"/docs/3/getting-started/migration_v3","h":"#no-unlimited-history-by-default","p":584},{"i":591,"t":"The maximum number of messages that can be recovered is now limited by client_recovery_max_publication_limit option which is by default 300.","s":"Publication limit for recovery","u":"/docs/3/getting-started/migration_v3","h":"#publication-limit-for-recovery","p":584},{"i":593,"t":"Deprecated seq/gen now removed and Centrifugo uses offset field for a position in a stream. This means that there is no need for v3_use_offset option anymore – it's not used in Centrifugo v3.","s":"Seq/Gen fields removed","u":"/docs/3/getting-started/migration_v3","h":"#seqgen-fields-removed","p":584},{"i":596,"t":"In Centrifugo v3 all time intervals should be configured using duration. For example \"proxy_connect_timeout\": 1 should be changed to \"proxy_connect_timeout\": \"1s\". We provide a configuration converter which takes this change into account.","s":"Time interval options are duration","u":"/docs/3/getting-started/migration_v3","h":"#time-interval-options-are-duration","p":584},{"i":598,"t":"In Centrifugo v3 history_recover option becomes recover. Option history_lifetime renamed to history_ttl and it's now a duration. Option server_side removed, see protected option as a replacement. We provide a configuration converter which takes these changes into account.","s":"Channel options changes","u":"/docs/3/getting-started/migration_v3","h":"#channel-options-changes","p":584},{"i":600,"t":"Configuring over command-line flags is not very convenient for production deployments, Centrifugo v3 reduced the number of command-line flags available – it mostly has flags frequently useful for development now.","s":"Some command-line flags removed","u":"/docs/3/getting-started/migration_v3","h":"#some-command-line-flags-removed","p":584},{"i":602,"t":"In Centrifugo v3 you should explicitly set a list of allowed origins which are allowed to connect to client transport endpoints. config.json { ... \"allowed_origins\": [\"https://mysite.com\"]} There is a way to disable origin check, but it's discouraged and insecure in case you are using connect proxy feature. config.json { ... \"allowed_origins\": [\"*\"]}","s":"Enforced request Origin check","u":"/docs/3/getting-started/migration_v3","h":"#enforced-request-origin-check","p":584},{"i":604,"t":"In Centrifugo v3 we addressed an issue where package name in Protobuf definitions resulted in some inconvenience and attempts to rename it. But it's not possible to rename it since GRPC uses it as part of RPC methods internally. Now GRPC API package looks like this: package centrifugal.centrifugo.api; This means you need to regenerate your GRPC code which communicates with Centrifugo using the latest Protobuf definitions. Refer to the GRPC API doc.","s":"Updated GRPC API Protobuf package","u":"/docs/3/getting-started/migration_v3","h":"#updated-grpc-api-protobuf-package","p":584},{"i":606,"t":"The response format of channels API call changed in v3. See description in API doc. The channels method has new additional possibilities like showing the number of connections in a channel and filter channels by pattern. info Channels API call still has the same concern as before: this method does not scale well for many active channels in a system and is mostly recommended for administrative/debug purposes.","s":"Channels API method changed","u":"/docs/3/getting-started/migration_v3","h":"#channels-api-method-changed","p":584},{"i":608,"t":"When using HTTP proxy you should now set an explicit list of headers you want to proxy. To mimic the behavior of Centrifugo v2 add to your configuration: config.json { \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} If you had a list of extra HTTP headers using proxy_extra_http_headers then additionally extend list above with values from proxy_extra_http_headers. Then you can remove proxy_extra_http_headers - it's not used anymore. Another important change is how Centrifugo proxies binary data over HTTP JSON proxy. Previously proxy mode (whether to use base64 fields or not) could be configured using encoding=binary URL param of connection. With Centrifugo v3 it's only possible to use binary mode by enabling \"proxy_binary_encoding\": true option. BTW according to our community poll only 2% of Centrifugo users used binary mode in HTTP proxy. If you have problems with new behavior – write about your situation to our community chats – and we will see what's possible.","s":"HTTP proxy changes","u":"/docs/3/getting-started/migration_v3","h":"#http-proxy-changes","p":584},{"i":610,"t":"eto claim of subscription JWT removed. But since Centrifugo v3 introduced an additional expire_at claim it's still possible to implement one-time subscription tokens without enabling subscription expiration workflow by setting \"expire_at: 0\" in subscription JWT claims.","s":"JWT changes","u":"/docs/3/getting-started/migration_v3","h":"#jwt-changes","p":584},{"i":612,"t":"Redis configuration was a bit messy - especially in the Redis sharding case, in v3 we decided to clean up it a bit. Make it more explicit and reduce the number of possible ways to configure. Refer to the Redis Engine docs for the new configuration details. The important thing is that there is no separate redis_host and redis_port option anymore – those are replaced with single redis_address option.","s":"Redis configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#redis-configuration-changes","p":584},{"i":614,"t":"Centrifugo v3 will use Redis Stream data structure to keep history instead of lists. danger This requires Redis >= 5.0.1 to work. If you still need List data structure or have an old Redis version you can use \"redis_use_lists\": true to mimic the default behavior of Centrifugo v2.","s":"Redis streams used by default","u":"/docs/3/getting-started/migration_v3","h":"#redis-streams-used-by-default","p":584},{"i":616,"t":"Our poll showed that most Centrifugo users do not use SockJS transport. In v3 it's disabled by default. You can enable it by setting \"sockjs\": true in configuration.","s":"SockJS disabled by default","u":"/docs/3/getting-started/migration_v3","h":"#sockjs-disabled-by-default","p":584},{"i":618,"t":"Here is a full list of configuration option changes. We provide a best-effort configuration converter. allowed_origins is now required to be set to authorize requests with Origin header v3_use_offset removed redis_streams removed tls_autocert_force_rsa removed redis_pubsub_num_workers removed sockjs_disable removed secret renamed to token_hmac_secret_key history_lifetime renamed to history_ttl history_recover renamed to recover client_presence_ping_interval renamed to client_presence_update_interval client_ping_interval renamed to websocket_ping_interval client_message_write_timeout renamed to websocket_write_timeout client_request_max_size renamed to websocket_message_size_limit client_presence_expire_interval renamed to presence_ttl memory_history_meta_ttl renamed to history_meta_ttl redis_history_meta_ttl renamed to history_meta_ttl redis_sequence_ttl renamed to history_meta_ttl redis_presence_ttl renamed to presence_ttl presence_ttl should be converted to duration websocket_write_timeout should be converted to duration websocket_ping_interval should be converted to duration client_presence_update_interval should be converted to duration history_ttl should be converted to duration history_meta_ttl should be converted to duration nats_dial_timeout should be converted to duration nats_write_timeout should be converted to duration graphite_interval should be converted to duration shutdown_timeout should be converted to duration shutdown_termination_delay should be converted to duration proxy_connect_timeout should be converted to duration proxy_refresh_timeout should be converted to duration proxy_rpc_timeout should be converted to duration proxy_subscribe_timeout should be converted to duration proxy_publish_timeout should be converted to duration client_expired_close_delay should be converted to duration client_expired_sub_close_delay should be converted to duration client_stale_close_delay should be converted to duration client_channel_position_check_delay should be converted to duration node_info_metrics_aggregate_interval should be converted to duration websocket_ping_interval should be converted to duration websocket_write_timeout should be converted to duration sockjs_heartbeat_delay should be converted to duration redis_idle_timeout should be converted to duration redis_connect_timeout should be converted to duration redis_read_timeout should be converted to duration redis_write_timeout should be converted to duration redis_cluster_addrs renamed to redis_cluster_address redis_sentinels renamed to redis_sentinel_address redis_master_name renamed to redis_sentinel_master_name","s":"Other configuration changes","u":"/docs/3/getting-started/migration_v3","h":"#other-configuration-changes","p":584},{"i":620,"t":"Here is a converter between Centrifugo v2 and v3 JSON configuration. It can help to translate most of the things automatically for you. If you are using Centrifugo with TOML format then you can use online converter as initial step. Or yaml-to-json and json-to-yaml for YAML. tip It's fully client-side: your data won't be sent anywhere. danger Unfortunately, we can't migrate environment variables and command-line flags automatically - so if you are using env vars or command-line flags to configure Centrifugo you still need to migrate manually. Also, be aware: this converter tool is the best effort only – we can not guarantee it solves all corner cases, especially in Redis configuration. You may still need to fix some things manually, for example - properly fill allowed_origins. Convert Here will be configuration for v3 Here will be log of changes made in your config","s":"v2 to v3 config converter","u":"/docs/3/getting-started/migration_v3","h":"#v2-to-v3-config-converter","p":584},{"i":622,"t":"On this page","s":"User and channel tracing","u":"/docs/3/pro/tracing","h":"","p":621},{"i":624,"t":"It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing. curl -X POST http://localhost:8000/admin/trace -H \"Authorization: token \" -d '{\"type\": \"user\", \"entity\": \"56\"}' -o trace.txt Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future.","s":"Save to a file","u":"/docs/3/pro/tracing","h":"#save-to-a-file","p":621},{"i":626,"t":"On this page","s":"Token revocation API","u":"/docs/3/pro/token_revocation","h":"","p":625},{"i":628,"t":"By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart. But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/3/pro/token_revocation","h":"#how-it-works","p":625},{"i":630,"t":"Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/3/pro/token_revocation","h":"#configure","p":625},{"i":632,"t":"Revocation data can be kept in Redis. To enable this configuration should be: { ... \"token_revoke\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/3/pro/token_revocation","h":"#redis-persistence-engine","p":625},{"i":634,"t":"Revocation data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"token_revoke\": { \"persistence_engine\": \"database\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/3/pro/token_revocation","h":"#database-persistence-engine","p":625},{"i":636,"t":"Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT). caution This API assumes that JWTs you are using contain \"jti\" claim which is a unique token ID (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"revoke_token\", \"params\": {\"uid\": \"xxx-xxx-xxx\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api Revoke token params​ Parameter name Parameter type Required Description uid string yes Token unique ID (JTI claim in case of JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). Revoke token result​ Empty object at the moment.","s":"Revoke token API","u":"/docs/3/pro/token_revocation","h":"#revoke-token-api","p":625},{"i":638,"t":"Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application. caution This API assumes that JWTs you are using contain \"iat\" claim which is a time token was issued at (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"invalidate_user_tokens\", \"params\": {\"user\": \"test\", \"issued_before\": 1635845022, \"expire_at\": 1635845122}}' \\ http://localhost:8000/api Invalidate user tokens params​ Parameter name Parameter type Required Description user string yes User ID to invalidate tokens for issued_before int yes All tokens issued at before this time will be considered revoked (in case of JWT this requires iat to be properly set in JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). Invalidate user tokens result​ Empty object at the moment.","s":"Invalidate user tokens API","u":"/docs/3/pro/token_revocation","h":"#invalidate-user-tokens-api","p":625},{"i":640,"t":"Singleflight Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. This option can radically reduce a load on a broker in the following situations: Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature) Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine. To enable: config.json { ... \"use_singleflight\": true} Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.","s":"Singleflight","u":"/docs/3/pro/singleflight","h":"","p":639},{"i":642,"t":"On this page","s":"Quickstart tutorial ⏱️","u":"/docs/3/getting-started/quickstart","h":"","p":641},{"i":644,"t":"Several more examples are located on Github – check out this repo. Also, check out our blog with several tutorials.","s":"More examples","u":"/docs/3/getting-started/quickstart","h":"#more-examples","p":641},{"i":646,"t":"On this page","s":"User blocking API","u":"/docs/3/pro/user_block","h":"","p":645},{"i":648,"t":"By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart. But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/3/pro/user_block","h":"#how-it-works","p":645},{"i":650,"t":"User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/3/pro/user_block","h":"#configure","p":645},{"i":652,"t":"Blocking data can be kept in Redis. To enable this configuration should be: { ... \"user_block\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/3/pro/user_block","h":"#redis-persistence-engine","p":645},{"i":654,"t":"Blocking data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"user_block\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/3/pro/user_block","h":"#database-persistence-engine","p":645},{"i":656,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"block_user\", \"params\": {\"user\": \"2695\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api Block user params​ Parameter name Parameter type Required Description user string yes User ID to block expire_at int no Unix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache). Block user result​ Empty object at the moment.","s":"Block user API","u":"/docs/3/pro/user_block","h":"#block-user-api","p":645},{"i":658,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"unblock_user\", \"params\": {\"user\": \"2695\"}}' \\ http://localhost:8000/api Unblock user params​ Parameter name Parameter type Required Description user string yes User ID to unblock Unblock user result​ Empty object at the moment.","s":"Unblock user API","u":"/docs/3/pro/user_block","h":"#unblock-user-api","p":645},{"i":660,"t":"On this page","s":"User connections API","u":"/docs/3/pro/user_connections","h":"","p":659},{"i":662,"t":"On this page","s":"Admin web UI","u":"/docs/3/server/admin_web","h":"","p":661},{"i":664,"t":"admin (boolean, default: false) – enables/disables admin web UI admin_password (string, default: \"\") – this is a password to log into admin web interface admin_secret (string, default: \"\") - this is a secret key for authentication token set on successful login. Make both admin_password and admin_secret strong and keep them in secret. After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface. tip Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.","s":"Options","u":"/docs/3/server/admin_web","h":"#options","p":661},{"i":666,"t":"If you want to use custom web interface you can specify path to web interface directory dist: config.json { ..., \"admin\": true, \"admin_password\": \"\", \"admin_secret\": \"\", \"admin_web_path\": \"\"} This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.","s":"Using custom web interface","u":"/docs/3/server/admin_web","h":"#using-custom-web-interface","p":661},{"i":668,"t":"There is also an option to run Centrifugo in insecure admin mode - in this case you don't need to set admin_password and admin_secret in config – in web interface you will be logged in automatically without any password. Note that this is only an option for production if you protected admin web interface with firewall rules. Otherwise anyone in internet will have full access to admin functionality described above. To enable insecure admin mode: config.json { ..., \"admin\": true, \"admin_insecure\": true, \"admin_password\": \"\", \"admin_secret\": \"\"}","s":"Admin insecure mode","u":"/docs/3/server/admin_web","h":"#admin-insecure-mode","p":661},{"i":670,"t":"On this page","s":"Analytics with ClickHouse","u":"/docs/3/pro/analytics","h":"","p":669},{"i":672,"t":"To enable integration with ClickHouse add the following section to a configuration file: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\", \"tcp://127.0.0.1:9001\", \"tcp://127.0.0.1:9002\", \"tcp://127.0.0.1:9003\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"centrifugo_cluster\", \"export_connections\": true, \"export_operations\": true, \"export_http_headers\": [ \"User-Agent\", \"Origin\", \"X-Real-Ip\", ] }} All ClickHouse analytics options scoped to clickhouse_analytics section of configuration. Toggle this feature using enabled boolean option. tip While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo. Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option. You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database. export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available. export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available. export_http_headers is a list of HTTP headers to export for connection information. export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport. skip_schema_initialization (new in Centrifugo PRO v3.1.1) - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process. skip_ping_on_start (new in Centrifugo PRO v3.1.1) - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.","s":"Configuration","u":"/docs/3/pro/analytics","h":"#configuration","p":669},{"i":674,"t":"SHOW CREATE TABLE centrifugo.connections;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections( `client` FixedString(36), `user` String, `name` String, `version` String, `transport` String, `channels` Array(String), `headers.key` Array(String), `headers.value` Array(String), `metadata.key` Array(String), `metadata.value` Array(String), `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└─────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.connections_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections_distributed( `client` FixedString(36), `user` String, `name` String, `version` String, `transport` String, `channels` Array(String), `headers.key` Array(String), `headers.value` Array(String), `metadata.key` Array(String), `metadata.value` Array(String), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Connections table","u":"/docs/3/pro/analytics","h":"#connections-table","p":669},{"i":676,"t":"SHOW CREATE TABLE centrifugo.operations;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations( `client` FixedString(36), `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.operations_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `client` FixedString(36), `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Operations table","u":"/docs/3/pro/analytics","h":"#operations-table","p":669},{"i":678,"t":"Show unique users which were connected: SELECT DISTINCT userFROM centrifugo.connections_distributed;┌─user─────┐│ user_1 ││ user_2 ││ user_3 ││ user_4 ││ user_5 │└──────────┘ Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111): SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish');┌─count()─┐│ 4502 │└─────────┘ The same for a specific user: SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');┌─count()─┐│ 1214 │└─────────┘ Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since connections table contain periodic snapshot entries, clients could subscribe/unsubscribe in between snapshots – this is reflected in operations table): SELECT COUNT(Distinct(user))FROM centrifugo.connections_distributedWHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));┌─uniqExact(user)─┐│ 101 │└─────────────────┘ Show top 10 users which called publish operation during last one minute: SELECT COUNT(op) AS num_ops, userFROM centrifugo.operations_distributedWHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))GROUP BY userORDER BY num_ops DESCLIMIT 10;┌─num_ops─┬─user─────┐│ 56 │ user_200 ││ 11 │ user_75 ││ 6 │ user_87 ││ 6 │ user_65 ││ 6 │ user_39 ││ 5 │ user_28 ││ 5 │ user_63 ││ 5 │ user_89 ││ 3 │ user_32 ││ 3 │ user_52 │└─────────┴──────────┘","s":"Query examples","u":"/docs/3/pro/analytics","h":"#query-examples","p":669},{"i":680,"t":"The recommended way to run ClickHouse in production is with cluster. But during development you may want to run Centrifugo with single instance ClickHouse. To do this set only one ClickHouse dsn and do not set cluster name: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"\", \"export_connections\": true, \"export_operations\": true, \"export_http_headers\": [ \"Origin\", \"User-Agent\" ] }} Run ClickHouse locally: docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click yandex/clickhouse-server Run ClickHouse client: docker run -it --rm --link click:clickhouse-server yandex/clickhouse-client --host clickhouse-server Issue queries: :) SELECT * FROM centrifugo.operations┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘","s":"Development","u":"/docs/3/pro/analytics","h":"#development","p":669},{"i":682,"t":"When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 5 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are not configurable at the moment but just reach us out if you need to tune these values. Several metrics are exposed to monitor export process health: centrifugo_clickhouse_analytics_flush_duration_seconds summary centrifugo_clickhouse_analytics_batch_size summary centrifugo_clickhouse_analytics_drop_count counter","s":"How export works","u":"/docs/3/pro/analytics","h":"#how-export-works","p":669},{"i":684,"t":"On this page","s":"User status","u":"/docs/3/pro/user_status","h":"","p":683},{"i":686,"t":"Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript: await centrifuge.namedRPC('update_user_status', {}); note Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example. This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.","s":"Client-side status update RPC","u":"/docs/3/pro/user_status","h":"#client-side-status-update-rpc","p":683},{"i":688,"t":"It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"update_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Update user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to update status for Update user status result​ Empty object at the moment.","s":"update_user_status server API","u":"/docs/3/pro/user_status","h":"#update_user_status-server-api","p":683},{"i":690,"t":"Now on a backend side you have access to a bulk API to effectively get status of particular users. Call RPC method of server API (over HTTP or GRPC): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"get_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api You should get a response like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\", \"active\":1627107289, \"online\":1627107289 } ] }} In case information about last status update time not available the response will be like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\" } ] }} I.e. status object will present in a response but active field won't be set for status object. Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time). Get user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to get status for Get user status result​ Field name Field type Optional Description statuses array of UserStatus no Statuses for each user in params (same order) UserStatus​ Field name Field type Optional Description user string no User ID active integer yes Last active time (Unix seconds) online integer yes Last online time (Unix seconds)","s":"get_user_status server API","u":"/docs/3/pro/user_status","h":"#get_user_status-server-api","p":683},{"i":692,"t":"If you need to clear user status information for some reason there is a delete_user_status server API call: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"delete_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Delete user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to delete status for Delete user status result​ Empty object at the moment.","s":"delete_user_status server API","u":"/docs/3/pro/user_status","h":"#delete_user_status-server-api","p":683},{"i":694,"t":"To enable Redis active status feature: config.json { ... \"redis_active_status\": { \"enabled\": true, \"redis_address\": \"127.0.0.1:6379\" }} Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_active_status\": { \"enabled\": true, \"use_redis_from_engine\": true, }} In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine. expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day: config.json { ... \"redis_active_status\": { ... \"expire_interval\": \"24h\" }}","s":"Configuration","u":"/docs/3/pro/user_status","h":"#configuration","p":683},{"i":696,"t":"On this page","s":"Error and disconnect codes","u":"/docs/3/server/codes","h":"","p":695},{"i":698,"t":"Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client. Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).","s":"Client error codes","u":"/docs/3/server/codes","h":"#client-error-codes","p":695},{"i":700,"t":"Code: 100, Message: \"internal server error\". Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.","s":"Internal","u":"/docs/3/server/codes","h":"#internal","p":695},{"i":702,"t":"Code: 101, Message: \"unauthorized\". Error Unauthorized says that request is unauthorized.","s":"Unauthorized","u":"/docs/3/server/codes","h":"#unauthorized","p":695},{"i":704,"t":"Code: 102, Message: \"unknown channel\". Error Unknown Channel means that channel name does not exist. Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.","s":"Unknown Channel","u":"/docs/3/server/codes","h":"#unknown-channel","p":695},{"i":706,"t":"Code: 103, Message: \"permission denied\". Error Permission Denied means that access to resource not allowed.","s":"Permission Denied","u":"/docs/3/server/codes","h":"#permission-denied","p":695},{"i":708,"t":"Code: 104, Message: \"method not found\". Error Method Not Found means that method sent in command does not exist.","s":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found","p":695},{"i":710,"t":"Code: 105, Message: \"already subscribed\". Error Already Subscribed returned when client wants to subscribe on channel it already subscribed to.","s":"Already Subscribed","u":"/docs/3/server/codes","h":"#already-subscribed","p":695},{"i":712,"t":"Code: 106, Message: \"limit exceeded\". Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.","s":"Limit Exceeded","u":"/docs/3/server/codes","h":"#limit-exceeded","p":695},{"i":714,"t":"Code: 107, Message: \"bad request\". Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.","s":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request","p":695},{"i":716,"t":"Code: 108, Message: \"not available\". Error Not Available means that resource is not enabled. For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.","s":"Not Available","u":"/docs/3/server/codes","h":"#not-available","p":695},{"i":718,"t":"Code: 109, Message: \"token expired\". Error Token Expired indicates that connection token expired.","s":"Token Expired","u":"/docs/3/server/codes","h":"#token-expired","p":695},{"i":720,"t":"Code: 110, Message: \"expired\". Error Expired indicates that connection expired (no token involved).","s":"Expired","u":"/docs/3/server/codes","h":"#expired","p":695},{"i":722,"t":"Code: 111, Message: \"too many requests\". Error Too Many Requests means that server rejected request due to rate limiting strategies.","s":"Too Many Requests","u":"/docs/3/server/codes","h":"#too-many-requests","p":695},{"i":724,"t":"Code: 112, Message: \"unrecoverable position\". Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch passed.","s":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position","p":695},{"i":726,"t":"Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes). note We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. Actually most client connectors don't provide access to reading a disconnect code these days (only a reason). This is what we are planning to improve.","s":"Client disconnect codes","u":"/docs/3/server/codes","h":"#client-disconnect-codes","p":695},{"i":728,"t":"Code: 3000. DisconnectNormal is clean disconnect when client cleanly closed connection. This is mostly useful for server metrics, since client never receives this disconnect code (since already gone).","s":"Normal","u":"/docs/3/server/codes","h":"#normal","p":695},{"i":730,"t":"Code: 3001, Reason: \"shutdown\", Reconnect: true. Disconnect Shutdown sent when node is going to shut down.","s":"Shutdown","u":"/docs/3/server/codes","h":"#shutdown","p":695},{"i":732,"t":"Code: 3002, Reason: \"invalid token\", Reconnect: false. Disconnect Invalid Token sent when client came with invalid token.","s":"Invalid Token","u":"/docs/3/server/codes","h":"#invalid-token","p":695},{"i":734,"t":"Code: 3003, Reason: \"bad request\", Reconnect: false. Disconnect Bad Request sent when client uses malformed protocol","s":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-1","p":695},{"i":736,"t":"Code: 3004, Reason: \"internal server error\", Reconnect: true. Disconnect Server Error sent when internal error occurred on server.","s":"Server Error","u":"/docs/3/server/codes","h":"#server-error","p":695},{"i":738,"t":"Code: 3005, Reason: \"expired\", Reconnect: true. Disconnect Expired sent when client connection expired.","s":"Expired","u":"/docs/3/server/codes","h":"#expired-1","p":695},{"i":740,"t":"Code: 3006, Reason: \"subscription expired\", Reconnect: true. Disconnect Subscription Expired sent when client subscription expired.","s":"Subscription Expired","u":"/docs/3/server/codes","h":"#subscription-expired","p":695},{"i":742,"t":"Code: 3007, Reason: \"stale\", Reconnect: false. Disconnect Stale sent to close connection that did not become authenticated in configured interval after dialing. Usually this means a broken client implementation.","s":"Stale","u":"/docs/3/server/codes","h":"#stale","p":695},{"i":744,"t":"Code: 3008, Reason: \"slow\", Reconnect: true. Disconnect Slow sent when a client can't read messages fast enough.","s":"Slow","u":"/docs/3/server/codes","h":"#slow","p":695},{"i":746,"t":"Code: 3009, Reason: \"write error\", Reconnect: true. Disconnect Write Error sent when an error occurred while writing to client connection.","s":"Write Error","u":"/docs/3/server/codes","h":"#write-error","p":695},{"i":748,"t":"Code: 3010, Reason: \"insufficient state\", Reconnect: true. Disconnect Insufficient State sent when server detects wrong client position in channel Publication stream. Disconnect allows client to restore missed publications on reconnect.","s":"Insufficient State","u":"/docs/3/server/codes","h":"#insufficient-state","p":695},{"i":750,"t":"Code: 3011, Reason: \"force reconnect\", Reconnect: true. Disconnect Force Reconnect sent when server disconnects connection but want it to return back shortly.","s":"Force Reconnect","u":"/docs/3/server/codes","h":"#force-reconnect","p":695},{"i":752,"t":"Code: 3012, Reason: \"force disconnect\", Reconnect: false. Disconnect Force No Reconnect sent when server disconnects connection and asks it to not reconnect again.","s":"Force No Reconnect","u":"/docs/3/server/codes","h":"#force-no-reconnect","p":695},{"i":754,"t":"Code: 3013, Reason: \"connection limit\", Reconnect: false. Disconnect Connection Limit can be sent when client connection exceeds a configured connection limit (per user ID or due to other rule).","s":"Connection Limit","u":"/docs/3/server/codes","h":"#connection-limit","p":695},{"i":756,"t":"Server API errors are errors that can be returned to a API caller in replies to commands (in both HTTP and GRPC server APIs).","s":"Server API error codes","u":"/docs/3/server/codes","h":"#server-api-error-codes","p":695},{"i":758,"t":"Code: 100, Message: \"internal server error\". ErrorInternal means server error, if returned this is a signal that something went wrong with Centrifugo itself.","s":"Internal","u":"/docs/3/server/codes","h":"#internal-1","p":695},{"i":760,"t":"Code: 102, Message: \"unknown channel\". Error Unknown Channel means that namespace in channel name does not exist.","s":"Unknown channel","u":"/docs/3/server/codes","h":"#unknown-channel-1","p":695},{"i":762,"t":"Code: 104, Message: \"method not found\". Error Method Not Found means that method sent in command does not exist in Centrifugo.","s":"Method Not Found","u":"/docs/3/server/codes","h":"#method-not-found-1","p":695},{"i":764,"t":"Code: 107, Message: \"bad request\". Error Bad Request says that Centrifugo can not parse received data because it is malformed or there are required fields missing.","s":"Bad Request","u":"/docs/3/server/codes","h":"#bad-request-2","p":695},{"i":766,"t":"Code: 108, Message: \"not available\". Error Not Available means that resource is not enabled.","s":"Not Available","u":"/docs/3/server/codes","h":"#not-available-1","p":695},{"i":768,"t":"Code: 112, Message: \"unrecoverable position\". ErrorUnrecoverablePosition means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch.","s":"Unrecoverable Position","u":"/docs/3/server/codes","h":"#unrecoverable-position-1","p":695},{"i":770,"t":"On this page","s":"Client authentication","u":"/docs/3/server/authentication","h":"","p":769},{"i":772,"t":"Centrifugo uses the following claims in a JWT: sub, exp, iat, jti, info, b64info, channels, subs.","s":"Claims","u":"/docs/3/server/authentication","h":"#claims","p":769},{"i":774,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). If a user is not currently authenticated in an application, but you want to let him connect to Centrifugo anyway – you can use an empty string as a user ID in sub claim. This is called anonymous access. In this case, the anonymous option must be enabled in Centrifugo configuration for channels that the client will subscribe to.","s":"sub","u":"/docs/3/server/authentication","h":"#sub","p":769},{"i":776,"t":"This is a UNIX timestamp seconds when the token will expire. This is a standard JWT claim - all JWT libraries for different languages provide an API to set it. If exp claim is not provided then Centrifugo won't expire connection. When provided special algorithm will find connections with exp in the past and activate the connection refresh mechanism. Refresh mechanism allows connection to survive and be prolonged. In case of refresh failure, the client connection will be eventually closed by Centrifugo and won't be accepted until new valid and actual credentials are provided in the connection token. You can use the connection expiration mechanism in cases when you don't want users of your app to be subscribed on channels after being banned/deactivated in the application. Or to protect your users from token leakage (providing a reasonably short time of expiration). Choose exp value wisely, you don't need small values because the refresh mechanism will hit your application often with refresh requests. But setting this value too large can lead to slow user connection deactivation. This is a trade-off. Read more about connection expiration below.","s":"exp","u":"/docs/3/server/authentication","h":"#exp","p":769},{"i":778,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/3/server/authentication","h":"#iat","p":769},{"i":780,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/3/server/authentication","h":"#jti","p":769},{"i":782,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But you can force this check by setting token_audience string option: config.json { \"token_audience\": \"centrifugo\"} caution Setting token_audience will also affect subscription tokens (used for private channels).","s":"aud","u":"/docs/3/server/authentication","h":"#aud","p":769},{"i":784,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But you can force this check by setting token_issuer string option: config.json { \"token_issuer\": \"my_app\"} caution Setting token_issuer will also affect subscription tokens (used for private channels).","s":"iss","u":"/docs/3/server/authentication","h":"#iss","p":769},{"i":786,"t":"This claim is optional - this is additional information about client connection that can be provided for Centrifugo. This information will be included in presence information, join/leave events, and channel publication if it was published from a client-side.","s":"info","u":"/docs/3/server/authentication","h":"#info","p":769},{"i":788,"t":"If you are using binary Protobuf protocol you may want info to be custom bytes. Use this field in this case. This field contains a base64 representation of your bytes. After receiving Centrifugo will decode base64 back to bytes and will embed the result into various places described above.","s":"b64info","u":"/docs/3/server/authentication","h":"#b64info","p":769},{"i":790,"t":"An optional array of strings with server-side channels to subscribe a client to. See more details about server-side subscriptions. caution By providing a list of channels in JWT with channels claim you are not making them automatically unaccessible by other users. Other users can still call a client-side .subscribe() method and subscribe to these channels if channel permissions allow doing this. If you need to protect channels from being subscribed by other connections then you can use private channels inside this channels array (i.e. starting with $) or turn on protected option for channels namespaces.","s":"channels","u":"/docs/3/server/authentication","h":"#channels","p":769},{"i":792,"t":"An optional map of channels with options. This is like a channels claim but allows more control over server-side subscription since every channel can be annotated with info, data, and so on using options. tip This claim is called subs as a shortcut from subscriptions. The claim sub described above is a standart JWT claim to provide a user ID (it's a shortcut from subject). While claims have similar names they have different purpose in a connection JWT. caution By providing a map of channels in JWT with subs claim you are not making channels automatically unaccessible by other users. Other users can still call a client-side .subscribe() method and subscribe to these channels if channel permissions allow doing this. If you need to protect channels from being subscribed by other connections then you can use private channels inside this subs map (i.e. starting with $) or turn on protected option for channels namespaces. Example: { ... \"subs\": { \"channel1\": { \"data\": {\"welcome\": \"welcome to channel1\"} }, \"channel2\": { \"data\": {\"welcome\": \"welcome to channel2\"} } }} Subscribe options:​ Field Type Optional Description info JSON object yes Custom channel info b64info string yes Custom channel info in Base64 - to pass binary channel info data JSON object yes Custom JSON data to return in subscription context inside Connect reply b64data string yes Same as data but in Base64 to send binary data override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false}","s":"subs","u":"/docs/3/server/authentication","h":"#subs","p":769},{"i":794,"t":"Meta is an additional JSON object (ex. {\"key\": \"value\"}) that will be attached to a connection. Unlike info it's never exposed to clients and only accessible on a backend side. It will be included in proxy calls from Centrifugo to the application backend. Also, there is a get_user_connections API method in Centrifugo PRO that returns this data in the user connection object.","s":"meta","u":"/docs/3/server/authentication","h":"#meta","p":769},{"i":796,"t":"By default, Centrifugo looks on exp claim to configure connection expiration. In most cases this is fine, but there could be situations where you wish to decouple token expiration check with connection expiration time. As soon as the expire_at claim is provided (set) in JWT Centrifugo relies on it for setting connection expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the connection should expire. Set it to the future time for expiring connection at some point Set it to 0 to disable connection expiration (but still check token exp claim).","s":"expire_at","u":"/docs/3/server/authentication","h":"#expire_at","p":769},{"i":798,"t":"As said above exp claim in a connection token allows expiring client connection at some point in time. Let's look in detail at what happens when Centrifugo detects that the connection is going to expire. First, you should do is enable client expiration mechanism in Centrifugo providing a connection JWT with expiration: import jwtimport timetoken = jwt.encode({\"sub\": \"42\", \"exp\": int(time.time()) + 10*60}, \"secret\").decode()print(token) Let's suppose that you set exp field to timestamp that will expire in 10 minutes and the client connected to Centrifugo with this token. During 10 minutes the connection will be kept by Centrifugo. When this time passed Centrifugo gives the connection some time (configured, 25 seconds by default) to refresh its credentials and provide a new valid token with new exp. When a client first connects to Centrifugo it receives the ttl value in connect reply. That ttl value contains the number of seconds after which the client must send the refresh command with new credentials to Centrifugo. Centrifugo clients must handle this ttl field and automatically start the refresh process. For example, a Javascript browser client will send an AJAX POST request to your application when it's time to refresh credentials. By default, this request goes to /centrifuge/refresh URL endpoint. In response your server must return JSON with a new connection JWT: { \"token\": token} So you must just return the same connection JWT for your user when rendering the page initially. But with actual valid exp. Javascript client will then send them to Centrifugo server and connection will be refreshed for a time you set in exp. In this case, you know which user wants to refresh its connection because this is just a general request to your app - so your session mechanism will tell you about the user. If you don't want to refresh the connection for this user - just return 403 Forbidden on refresh request to your application backend. Javascript client also has options to hook into a refresh mechanism to implement your custom way of refreshing. Other Centrifugo clients also should have hooks to refresh credentials but depending on client API for this can be different - see specific client docs.","s":"Connection expiration","u":"/docs/3/server/authentication","h":"#connection-expiration","p":769},{"i":800,"t":"Let's look at how to generate connection HS256 JWT in Python:","s":"Examples","u":"/docs/3/server/authentication","h":"#examples","p":769},{"i":802,"t":"Python NodeJS import jwttoken = jwt.encode({\"sub\": \"42\"}, \"secret\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret');console.log(token); Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users. Then you can pass this token to your client side and use it when connecting to Centrifugo: Using centrifuge-js v2.x var centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\");centrifuge.setToken(token);centrifuge.connect();","s":"Simplest token","u":"/docs/3/server/authentication","h":"#simplest-token","p":769},{"i":804,"t":"HS256 token that will be valid for 5 minutes: Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"exp\": int(time.time()) + 5*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret', { expiresIn: 5 * 60 });console.log(token);","s":"Token with expiration","u":"/docs/3/server/authentication","h":"#token-with-expiration","p":769},{"i":806,"t":"Let's attach user name: Python NodeJS import jwtclaims = {\"sub\": \"42\", \"info\": {\"name\": \"Alexander Emelin\"}}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42', info: {\"name\": \"Alexander Emelin\"} }, 'secret');console.log(token);","s":"Token with additional connection info","u":"/docs/3/server/authentication","h":"#token-with-additional-connection-info","p":769},{"i":808,"t":"You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.","s":"Investigating problems with JWT","u":"/docs/3/server/authentication","h":"#investigating-problems-with-jwt","p":769},{"i":810,"t":"Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT). A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address). As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels. At the moment Centrifugo caches keys loaded from an endpoint for one hour. Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment). Only RSA algorithm is supported. JWKS support enabled both connection and private channel subscription tokens.","s":"JSON Web Key support","u":"/docs/3/server/authentication","h":"#json-web-key-support","p":769},{"i":812,"t":"On this page","s":"Channels","u":"/docs/3/server/channels","h":"","p":811},{"i":814,"t":"Only ASCII symbols must be used in channel string. Channel name length limited by 255 characters by default (can be changed via configuration file option channel_max_length). Several symbols in channel names reserved for Centrifugo internal needs: : – for namespace channel boundary (see below) $ – for private channel prefix (see below) # – for user channel boundary (see below) * – for the future Centrifugo needs & – for the future Centrifugo needs / – for the future Centrifugo needs","s":"Channel name rules","u":"/docs/3/server/channels","h":"#channel-name-rules","p":811},{"i":816,"t":": – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about available channel options and more about namespaces below. If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public. info A namespace is part of the channel name. If a user subscribed to a channel with namespace, like public:chat – then you need to publish messages into public:chat channel to be delivered to the user. We often see some confusion from developers trying to publish messages into chat and thinking that namespace is somehow stripped upon subscription. It's not true.","s":"namespace boundary (:)","u":"/docs/3/server/channels","h":"#namespace-boundary-","p":811},{"i":818,"t":"If the channel starts with $ then it is considered private. The subscription on a private channel must be properly signed by your backend. Use private channels if you pass sensitive data inside the channel and want to control access permissions on your backend. For example $secrets is a private channel, $public:chat - is a private channel that belongs to namespace public. Subscription request to private channels requires additional JWT from your application backend. Read detailed chapter about private channels. If you need a personal channel for a single user (or maybe a channel for a short and stable set of users) then consider using a user-limited channel (see below) as a simpler alternative that does not require an additional subscription token from your backend. Also, consider using server-side subscriptions or subscribe proxy feature of Centrifugo to model channels with restrictive access.","s":"private channel prefix ($)","u":"/docs/3/server/channels","h":"#private-channel-prefix-","p":811},{"i":820,"t":"# – is a user channel boundary. This is a separator to create personal channels for users (we call this user-limited channels) without the need to provide a subscription token. For example, if the channel is news#42 then the only user with ID 42 can subscribe to this channel (Centrifugo knows user ID because clients provide it in connection credentials with connection JWT). If you want to create a user-limited channel in namespace personal then you can use a name like personal:user#42 for example. tip Channel like $personal:user#42 - i.e. channel with both private prefix $ and user channel boundary # does not have a lot of sense, most probably you can just use personal:user#42 as the ID of the user protected by authentication JWT or set by application backend when the connect proxy feature is used. Moreover, you can provide several user IDs in channel name separated by a comma: dialog#42,43 – in this case only the user with ID 42 and user with ID 43 will be able to subscribe on this channel. This is useful for channels with a static list of allowed users, for example for single user personal messages channel, for dialog channel between certainly defined users. As soon as you need to manage access to a channel dynamically for many users this channel type does not suit well.","s":"user channel boundary (#)","u":"/docs/3/server/channels","h":"#user-channel-boundary-","p":811},{"i":822,"t":"Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.","s":"Channel options","u":"/docs/3/server/channels","h":"#channel-options","p":811},{"i":824,"t":"publish (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). Keep in mind that your application will never receive such messages. In an idiomatic use case, all messages must be published to Centrifugo by an application backend using Centrifugo API (HTTP or GRPC). Or using publish proxy. Since in those cases your application has a chance to validate a message, save it into a database, and only after that broadcast to all subscribers. But the publish option still can be useful to send something without backend-side validation and saving it into a database. This option can also be handy for demos and quick prototyping real-time app ideas.","s":"publish","u":"/docs/3/server/channels","h":"#publish","p":811},{"i":826,"t":"subscribe_to_publish (boolean, default false) - when the publish option is enabled client can publish into a channel without being subscribed to it. This option enables an automatic check that the client subscribed to a channel before allowing a client to publish.","s":"subscribe_to_publish","u":"/docs/3/server/channels","h":"#subscribe_to_publish","p":811},{"i":828,"t":"anonymous (boolean, default false) – this option enables anonymous user access (i.e. for a user with an empty user ID). In most situations, your application works with authenticated users so every user has its unique user ID (set inside JWT sub claim or provided by backend when using connect proxy). But if you provide real-time features for public access you may need unauthenticated access to some channels. Turn on this option and use an empty string as a user ID. See also related global option client_anonymous which allows anonymous users to connect without JWT.","s":"anonymous","u":"/docs/3/server/channels","h":"#anonymous","p":811},{"i":830,"t":"presence (boolean, default false) – enable/disable online presence information for channels. Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels. Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk).","s":"presence","u":"/docs/3/server/channels","h":"#presence","p":811},{"i":832,"t":"presence_disable_for_client (boolean, default false) – allows making presence calls available only for a server-side API. By default, presence information is available for both client and server-side APIs.","s":"presence_disable_for_client","u":"/docs/3/server/channels","h":"#presence_disable_for_client","p":811},{"i":834,"t":"join_leave (boolean, default false) – enable/disable sending join(leave) messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info. caution Keep in mind that join/leave messages can generate a big number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature or to scale Centrifugo.","s":"join_leave","u":"/docs/3/server/channels","h":"#join_leave","p":811},{"i":836,"t":"history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted. Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below). caution Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk).","s":"history_size","u":"/docs/3/server/channels","h":"#history_size","p":811},{"i":838,"t":"history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision). As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels. By default history TTL duration is zero – this means that channel history is disabled. Again – to turn on history you should wisely configure both history_size and history_ttl options. For example for top-level channels (which do not belong to a namespace): config.json { ... \"history_size\": 10, \"history_ttl\": \"60s\"}","s":"history_ttl","u":"/docs/3/server/channels","h":"#history_ttl","p":811},{"i":840,"t":"position (boolean, default false) – when the position feature is on Centrifugo tries to compensate at most once delivery of PUB/SUB messages checking client position inside a stream. If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API. position option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream).","s":"position","u":"/docs/3/server/channels","h":"#position","p":811},{"i":842,"t":"recover (boolean, default false) – when enabled Centrifugo will try to recover missed publications after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the position option described above. recover option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages). Also, note that not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.","s":"recover","u":"/docs/3/server/channels","h":"#recover","p":811},{"i":844,"t":"history_disable_for_client (boolean, default false) – allows making history available only for a server-side API. By default false – i.e. history calls are available for both client and server-side APIs. note History recovery mechanism if enabled will continue to work for clients anyway even if history_disable_for_client is on.","s":"history_disable_for_client","u":"/docs/3/server/channels","h":"#history_disable_for_client","p":811},{"i":846,"t":"protected (boolean, default false) – when on will prevent a client to subscribe to arbitrary channels in a namespace. In this case, Centrifugo will only allow a client to subscribe on user-limited channels, on channels returned by the proxy response, or channels listed inside JWT. Client-side subscriptions to arbitrary channels will be rejected with PermissionDenied error. Server-side channels belonging to the protected namespace passed by the client itself during connect will be ignored.","s":"protected","u":"/docs/3/server/channels","h":"#protected","p":811},{"i":848,"t":"proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter","s":"proxy_subscribe","u":"/docs/3/server/channels","h":"#proxy_subscribe","p":811},{"i":850,"t":"proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter","s":"proxy_publish","u":"/docs/3/server/channels","h":"#proxy_publish","p":811},{"i":852,"t":"subscribe_proxy_name (string, default \"\") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.","s":"subscribe_proxy_name","u":"/docs/3/server/channels","h":"#subscribe_proxy_name","p":811},{"i":854,"t":"publish_proxy_name (string, default \"\") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.","s":"publish_proxy_name","u":"/docs/3/server/channels","h":"#publish_proxy_name","p":811},{"i":856,"t":"Let's look at how to set some of these options in a config: config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"anonymous\": true, \"publish\": true, \"subscribe_to_publish\": true, \"presence\": true, \"join_leave\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"recover\": true} Here we set channel options on config top-level – these options will affect channels without namespace. Below we describe namespaces and how to define channel options for a namespace.","s":"Channel options config example","u":"/docs/3/server/channels","h":"#channel-options-config-example","p":811},{"i":858,"t":"It's possible to configure a list of channel namespaces. Namespaces are optional but very useful. A namespace allows setting custom options for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application. Namespace has a name, and the same channel options (with the same defaults) as described above. name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$). If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator: public:messages gossips:messages Where public and gossips are namespace names. Centrifugo will look for : symbol in the channel name, will extract the namespace name, and will apply namespace options whenever required. All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured: config.json { \"token_hmac_secret_key\": \"very-long-secret-key\", \"api_key\": \"secret-api-key\", \"anonymous\": true, \"publish\": true, \"presence\": true, \"join_leave\": true, \"history_size\": 10, \"history_ttl\": \"30s\", \"namespaces\": [ { \"name\": \"public\", \"publish\": true, \"anonymous\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"recover\": true }, { \"name\": \"gossips\", \"presence\": true, \"join_leave\": true } ]} Channel news will use globally defined channel options. Channel public:news will use public namespace options. Channel gossips:news will use gossips namespace options. Channel xxx:hello will result into subscription error since there is no xxx namespace defined in configuration above. Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2. note There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly.","s":"Channel namespaces","u":"/docs/3/server/channels","h":"#channel-namespaces","p":811},{"i":860,"t":"On this page","s":"Console commands","u":"/docs/3/server/console_commands","h":"","p":859},{"i":862,"t":"To show Centrifugo version and exit run: centrifugo version","s":"version command","u":"/docs/3/server/console_commands","h":"#version-command","p":859},{"i":864,"t":"Centrifugo has special command to check configuration file checkconfig: centrifugo checkconfig --config=config.json If any errors found during validation – program will exit with error message and exit code 1.","s":"checkconfig command","u":"/docs/3/server/console_commands","h":"#checkconfig-command","p":859},{"i":866,"t":"Another command is genconfig: centrifugo genconfig -c config.json It will automatically generate the minimal required configuration file. If any errors happen – program will exit with error message and exit code 1. genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file: centrifugo genconfig -c config.toml","s":"genconfig command","u":"/docs/3/server/console_commands","h":"#genconfig-command","p":859},{"i":868,"t":"Another command is gentoken: centrifugo gentoken -c config.json -u 28282 It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1.","s":"gentoken command","u":"/docs/3/server/console_commands","h":"#gentoken-command","p":859},{"i":870,"t":"One more command is checktoken: centrifugo checktoken -c config.json It will validate your connection JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1.","s":"checktoken command","u":"/docs/3/server/console_commands","h":"#checktoken-command","p":859},{"i":872,"t":"On this page","s":"Configure Centrifugo","u":"/docs/3/server/configuration","h":"","p":871},{"i":874,"t":"Centrifugo can be configured in several ways.","s":"Configuration sources","u":"/docs/3/server/configuration","h":"#configuration-sources","p":871},{"i":876,"t":"Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer environment or configuration file sources. Command-line options have the highest priority when set than other ways to configure Centrifugo.","s":"Command-line flags","u":"/docs/3/server/configuration","h":"#command-line-flags","p":871},{"i":878,"t":"All Centrifugo options can be set over env in the format CENTRIFUGO_ (i.e. option name with CENTRIFUGO_ prefix, all in uppercase). Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags. Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use \"true\" value for an environment variable (or simply \"1\"). To set false use \"false\" or \"0\". Example: export CENTRIFUGO_PROMETHEUS=\"1\" Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example: export CENTRIFUGO_ALLOWED_ORIGINS=\"https://mysite1.example.com https://mysite2.example.com\" For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name. Empty environment variables are considered unset (!) and will fall back to the next configuration source.","s":"OS environment variables","u":"/docs/3/server/configuration","h":"#os-environment-variables","p":871},{"i":880,"t":"Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable prevails over the same option in config file). A simple way to start with Centrifugo is to run: centrifugo genconfig This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo: centrifugo -c config.json","s":"Configuration file","u":"/docs/3/server/configuration","h":"#configuration-file","p":871},{"i":882,"t":"Centrifugo supports three configuration file formats: JSON, YAML, or TOML.","s":"Config file formats","u":"/docs/3/server/configuration","h":"#config-file-formats","p":871},{"i":884,"t":"Here is an example of Centrifugo JSON configuration file: config.json { \"allowed_origins\": [\"http://localhost:3000\"], \"token_hmac_secret_key\": \"\", \"api_key\": \"\"} token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT. api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients. allowed_origins option described below.","s":"JSON config format","u":"/docs/3/server/configuration","h":"#json-config-format","p":871},{"i":886,"t":"Centrifugo also supports TOML format for configuration file: centrifugo --config=config.toml Where config.toml contains: config.toml allowed_origins: [ \"http://localhost:3000\" ]token_hmac_secret_key = \"\"api_key = \"\"log_level = \"debug\" In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.","s":"TOML config format","u":"/docs/3/server/configuration","h":"#toml-config-format","p":871},{"i":888,"t":"YAML format is also supported: config.yaml allowed_origins: - \"http://localhost:3000\"token_hmac_secret_key: \"\"api_key: \"\"log_level: debug With YAML remember to use spaces, not tabs when writing a configuration file.","s":"YAML config format","u":"/docs/3/server/configuration","h":"#yaml-config-format","p":871},{"i":890,"t":"Let's describe some important options you can configure when running Centrifugo.","s":"Important options","u":"/docs/3/server/configuration","h":"#important-options","p":871},{"i":892,"t":"This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins. As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array. Connection requests without Origin header set are passing through without any checks (i.e. always allowed). For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way: \"allowed_origins\": [ \"http://localhost:3000\"] When connecting from https://example.com: \"allowed_origins\": [ \"https://example.com\"] Origin pattern can contain wildcard symbol * to match subdomains: \"allowed_origins\": [ \"https://*.example.com\"] – in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check. It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature): \"allowed_origins\": [ \"*\"]","s":"allowed_origins","u":"/docs/3/server/configuration","h":"#allowed_origins","p":871},{"i":894,"t":"Bind your Centrifugo to a specific interface address (string, by default \"\" - listen on all available interfaces).","s":"address","u":"/docs/3/server/configuration","h":"#address","p":871},{"i":896,"t":"Port to bind Centrifugo to (string, by default \"8000\").","s":"port","u":"/docs/3/server/configuration","h":"#port","p":871},{"i":898,"t":"Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.","s":"engine","u":"/docs/3/server/configuration","h":"#engine","p":871},{"i":900,"t":"These options allow tweaking server behavior, in most cases default values are good to start with.","s":"Advanced options","u":"/docs/3/server/configuration","h":"#advanced-options","p":871},{"i":902,"t":"Default: 128 Sets the maximum number of different channel subscriptions a single client can have. tip When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.","s":"client_channel_limit","u":"/docs/3/server/configuration","h":"#client_channel_limit","p":871},{"i":904,"t":"Default: 255 Sets the maximum length of the channel name.","s":"channel_max_length","u":"/docs/3/server/configuration","h":"#channel_max_length","p":871},{"i":906,"t":"Default: 0 The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited. The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).","s":"client_user_connection_limit","u":"/docs/3/server/configuration","h":"#client_user_connection_limit","p":871},{"i":908,"t":"Default: 1048576 Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.","s":"client_queue_max_size","u":"/docs/3/server/configuration","h":"#client_queue_max_size","p":871},{"i":910,"t":"Default: false Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID) and only can subscribe to channels with anonymous option enabled.","s":"client_anonymous","u":"/docs/3/server/configuration","h":"#client_anonymous","p":871},{"i":912,"t":"Default: 0 client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently. By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).","s":"client_concurrency","u":"/docs/3/server/configuration","h":"#client_concurrency","p":871},{"i":914,"t":"Default: 0 By default, Centrifugo runs on all available CPU cores. To limit the number of cores Centrifugo can utilize in one moment use this option.","s":"gomaxprocs","u":"/docs/3/server/configuration","h":"#gomaxprocs","p":871},{"i":916,"t":"After Centrifugo started there are several endpoints available.","s":"Endpoint configuration.","u":"/docs/3/server/configuration","h":"#endpoint-configuration","p":871},{"i":918,"t":"Bidirectional WebSocket default endpoint: ws://localhost:8000/connection/websocket Bidirectional SockJS default endpoint (disabled by default): http://localhost:8000/connection/sockjs Unidirectional EventSource endpoint (disabled by default): http://localhost:8000/connection/uni_sse Unidirectional HTTP streaming endpoint (disabled by default): http://localhost:8000/connection/uni_http_stream Unidirectional WebSocket endpoint (disabled by default): http://localhost:8000/connection/uni_websocket Unidirectional EventSource (SSE) endpoint (disabled by default): http://localhost:8000/connection/uni_sse Server HTTP API endpoint: http://localhost:8000/api By default, all endpoints work on port 8000. This can be changed with port option: { \"port\": 9000} In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.","s":"Default endpoints.","u":"/docs/3/server/configuration","h":"#default-endpoints","p":871},{"i":920,"t":"Admin web UI endpoint works on root path by default, i.e. http://localhost:8000. For more details about admin web UI, refer to the Admin web UI documentation.","s":"Admin endpoints.","u":"/docs/3/server/configuration","h":"#admin-endpoints","p":871},{"i":922,"t":"Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config: { ... \"debug\": true} And endpoint: http://localhost:8000/debug/pprof/ – will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.","s":"Debug endpoints.","u":"/docs/3/server/configuration","h":"#debug-endpoints","p":871},{"i":924,"t":"Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag: centrifugo -c config.json --health","s":"Health check endpoint","u":"/docs/3/server/configuration","h":"#health-check-endpoint","p":871},{"i":926,"t":"We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal: API endpoint (/api) - for HTTP API requests Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format Health check endpoint (/health) - used to do health checks Debug endpoints (/debug/pprof) - used to inspect internal server state It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration. Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work. To run internal endpoints on custom port use internal_port option: { ... \"internal_port\": 9000} So admin web interface will work on address: http://localhost:9000 Also, debug page will be available on a new custom port too: http://localhost:9000/debug/pprof/ The same for API and Prometheus endpoints.","s":"Custom internal ports","u":"/docs/3/server/configuration","h":"#custom-internal-ports","p":871},{"i":928,"t":"To disable websocket endpoint set websocket_disable boolean option to true. To disable API endpoint set api_disable boolean option to true.","s":"Disable default endpoints","u":"/docs/3/server/configuration","h":"#disable-default-endpoints","p":871},{"i":930,"t":"It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options: admin_handler_prefix (default \"\") - to control Admin panel URL prefix websocket_handler_prefix (default \"/connection/websocket\") - to control WebSocket URL prefix sockjs_handler_prefix (default \"/connection/sockjs\") - to control SockJS URL prefix uni_sse_handler_prefix (default \"/connection/uni_sse\") - to control unidirectional Eventsource URL prefix uni_http_stream_handler_prefix (default \"/connection/uni_http_stream\") - to control unidirectional HTTP streaming URL prefix uni_websocket_handler_prefix (default \"/connection/uni_websocket\") - to control unidirectional WebSocket URL prefix api_handler_prefix (default \"/api\") - to control HTTP API URL prefix prometheus_handler_prefix (default \"/metrics\") - to control Prometheus URL prefix health_handler_prefix (default \"/health\") - to control health check URL prefix","s":"Customize handler endpoints","u":"/docs/3/server/configuration","h":"#customize-handler-endpoints","p":871},{"i":932,"t":"It's possible to send HUP signal to Centrifugo to reload a configuration: kill -HUP Though at moment this will only reload token secrets and channel options (top-level and namespaces). Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.","s":"Signal handling","u":"/docs/3/server/configuration","h":"#signal-handling","p":871},{"i":935,"t":"The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. This mode can be useful for demo projects based on Centrifugo, local projects, or real-time application prototyping. Don't use it in production.","s":"Insecure client connection","u":"/docs/3/server/configuration","h":"#insecure-client-connection","p":871},{"i":937,"t":"This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules. The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.","s":"Insecure API mode","u":"/docs/3/server/configuration","h":"#insecure-api-mode","p":871},{"i":939,"t":"This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).","s":"Insecure admin mode","u":"/docs/3/server/configuration","h":"#insecure-admin-mode","p":871},{"i":941,"t":"Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use \"5s\". The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option). Valid time units are \"ms\" (milliseconds), \"s\" (seconds), \"m\" (minutes), \"h\" (hours). Some examples: \"1000ms\" // 1000 milliseconds\"1s\" // 1 second\"12h\" // 12 hours\"720h\" // 30 days","s":"Setting time duration options","u":"/docs/3/server/configuration","h":"#setting-time-duration-options","p":871},{"i":943,"t":"While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special: CENTRIFUGO_NAMESPACES='[{\"name\": \"ns1\"}, {\"name\": \"ns2\"}]' ./centrifugo I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.","s":"Setting namespaces over env","u":"/docs/3/server/configuration","h":"#setting-namespaces-over-env","p":871},{"i":945,"t":"Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage. Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal. To disable sending usage stats set usage_stats_disable option: config.json { \"usage_stats_disable\": true}","s":"Anonymous usage stats","u":"/docs/3/server/configuration","h":"#anonymous-usage-stats","p":871},{"i":947,"t":"On this page","s":"Infrastructure tuning","u":"/docs/3/server/infra_tuning","h":"","p":946},{"i":949,"t":"You should increase a max number of open files Centrifugo process can open if you want to handle more connections. To get the current open files limit run: ulimit -n On Linux you can check limits for a running process using: cat /proc//limits The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default. See this document to get more info on how to increase this number. If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536. You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).","s":"Open files limit","u":"/docs/3/server/infra_tuning","h":"#open-files-limit","p":946},{"i":951,"t":"Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default). In order to eliminate a problem you can: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces See a post in Pusher blog about this problem and more detailed solution steps.","s":"Ephemeral port exhaustion","u":"/docs/3/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":946},{"i":953,"t":"On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state. Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side. Look how many socket descriptors in TIME_WAIT state. netstat -an |grep TIME_WAIT | grep | wc -l Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html The advices here are similar to ephemeral port exhaustion problem: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces","s":"Sockets in TIME_WAIT state","u":"/docs/3/server/infra_tuning","h":"#sockets-in-time_wait-state","p":946},{"i":955,"t":"Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established. Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.","s":"Proxy max connections","u":"/docs/3/server/infra_tuning","h":"#proxy-max-connections","p":946},{"i":957,"t":"More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.","s":"Conntrack table","u":"/docs/3/server/infra_tuning","h":"#conntrack-table","p":946},{"i":959,"t":"On this page","s":"Monitoring","u":"/docs/3/server/monitoring","h":"","p":958},{"i":961,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus","u":"/docs/3/server/monitoring","h":"#prometheus","p":958},{"i":963,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite","u":"/docs/3/server/monitoring","h":"#graphite","p":958},{"i":965,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/3/server/monitoring","h":"#grafana-dashboard","p":958},{"i":967,"t":"On this page","s":"History and recovery","u":"/docs/3/server/history_and_recovery","h":"","p":966},{"i":969,"t":"History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero. Centrifugo is not designed to keep publications streams forever. Streams are ephemeral and can expire or can be lost at any moment. But Centrifugo provides a way for an application or a client to understand that stream history lost. In this case, the main application database should be the source of truth and state recovery. When history is on every publication is published into a channel saved into history. Depending on the engine used history stream implementation can differ. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside spaces. Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of truth anymore. tip History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.","s":"History design","u":"/docs/3/server/history_and_recovery","h":"#history-design","p":966},{"i":971,"t":"History iteration based on three fields: limit since reverse Combining these fields you can iterate over a stream in both directions. Get current stream top offset and epoch: history(limit: 0, since: null, reverse: false) Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: false) Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: true) Get history from the current beginning (up to 10): history(limit: 10, since: null, reverse: false) Get history from the current end in reversed direction (up to 10): history(limit: 10, since: null, reverse: true) Get up to 10 publications since known stream position (described by offset and epoch values): history(limit: 10, since: {offset: 0, epoch: \"epoch\"}, reverse: false) Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest): history(limit: 10, since: {offset: 11, epoch: \"epoch\"}, reverse: true) Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }}","s":"History iteration API","u":"/docs/3/server/history_and_recovery","h":"#history-iteration-api","p":966},{"i":973,"t":"One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while. In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnecting at the same time). danger Message recovery protocol feature designed to be used together with reasonably small Publication stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time. To enable recovery mechanism for channels set the recover boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not. The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default. Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client libraries (for bidirectional transport), but it's good to know how recovery works under the hood. The recovery feature heavily relies on offset and epoch values described above. epoch handles cases when history storage has been restarted while the client was in a disconnected state so publication numeration in a channel started from scratch. For example at the moment Memory engine does not persist publication sequences on disk so every restart will start numeration from scratch. After each restart a new epoch field is generated, and we can understand in the recovery process that the client could miss messages thus returning the correct recovered flag in a subscribe Reply. This also applies to the Redis engine – if you do not use AOF with fsync then sequences can be lost after Redis restart. When using the Redis engine you need to use a fully in-memory model strategy or AOF with fsync to guarantee the reliability of the recovered flag sent by Centrifugo. When a server receives subscribe command with the boolean flag recover set to true and offset, epoch set to values last seen by a client (see SubscribeRequest type in protocol definitions) it will try to find all missed publications from history cache. Recovered publications will be passed to the client in a subscribe Reply in the correct order, so your publication handler will be automatically called to process each missed message. You can also manually implement your recovery algorithm on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client reconnects completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.","s":"Automatic message recovery","u":"/docs/3/server/history_and_recovery","h":"#automatic-message-recovery","p":966},{"i":975,"t":"On this page","s":"Load balancing","u":"/docs/3/server/load_balancing","h":"","p":974},{"i":977,"t":"Although it's possible to use Centrifugo without any reverse proxy before it, it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with edge cases when handling HTTP/Websocket connections from the wild. Also you probably want some sort of load balancing eventually between Centrifugo nodes so that proxy can be such a balancer too. In this section we will look at Nginx configuration to deploy Centrifugo. Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections. There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).","s":"Nginx configuration","u":"/docs/3/server/load_balancing","h":"#nginx-configuration","p":974},{"i":979,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}#server {# listen 80;# server_name centrifugo.example.com;# rewrite ^(.*) https://$server_name$1 permanent;#}server { server_name centrifugo.example.com; listen 80; #listen 443 ssl; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_certificate /etc/nginx/ssl/server.crt; #ssl_certificate_key /etc/nginx/ssl/server.key; include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1000; gzip_proxied any; # Only retry if there was a communication error, not a timeout # on the Centrifugo server (to avoid propagating \"queries of death\" # to all frontends) proxy_next_upstream error; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; location /connection { proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location / { proxy_pass http://centrifugo; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }}","s":"Separate domain for Centrifugo","u":"/docs/3/server/load_balancing","h":"#separate-domain-for-centrifugo","p":974},{"i":981,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}server { # ... your web site Nginx config location /centrifugo/ { rewrite ^/centrifugo/(.*) /$1 break; proxy_pass http://centrifugo; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } location /centrifugo/connection { rewrite ^/centrifugo(.*) $1 break; proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}","s":"Embed to a location of web site","u":"/docs/3/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":974},{"i":983,"t":"You may also need to update worker_connections option of Nginx: events { worker_connections 65535;}","s":"worker_connections","u":"/docs/3/server/load_balancing","h":"#worker_connections","p":974},{"i":985,"t":"On this page","s":"Private channels","u":"/docs/3/server/private_channels","h":"","p":984},{"i":987,"t":"Private channel subscription token claims are: client, channel, info, b64info, exp and expire_at. What do they mean? Let's describe it in detail.","s":"Claims","u":"/docs/3/server/private_channels","h":"#claims","p":984},{"i":989,"t":"Required. Client ID which wants to subscribe on a channel (string). note Centrifugo server generates a unique client ID for each incoming connection. This client ID regenerated for a client on every reconnect. You must use this client ID for a private channel subscription token. If you are using centrifuge-js library then Client ID and channels that the user wants to subscribe will be automatically added to AJAX POST request to application backend. In other cases refer to specific client documentation (in most cases you will have client ID and channel in private subscription event context).","s":"client","u":"/docs/3/server/private_channels","h":"#client","p":984},{"i":991,"t":"Required. Channel that client tries to subscribe to (string).","s":"channel","u":"/docs/3/server/private_channels","h":"#channel","p":984},{"i":993,"t":"Optional. Additional information for connection inside this channel (valid JSON).","s":"info","u":"/docs/3/server/private_channels","h":"#info","p":984},{"i":995,"t":"Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.","s":"b64info","u":"/docs/3/server/private_channels","h":"#b64info","p":984},{"i":997,"t":"Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time. At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs. Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.","s":"exp","u":"/docs/3/server/private_channels","h":"#exp","p":984},{"i":999,"t":"Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the subscription should expire. Set it to the future time for expiring subscription at some point Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.","s":"expire_at","u":"/docs/3/server/private_channels","h":"#expire_at","p":984},{"i":1001,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.","s":"aud","u":"/docs/3/server/private_channels","h":"#aud","p":984},{"i":1003,"t":"Handled since Centrifugo v3.2.0 By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.","s":"iss","u":"/docs/3/server/private_channels","h":"#iss","p":984},{"i":1005,"t":"So to generate a subscription token you can use something like this in Python (assuming client ID is xxxx-xxx-xxx-xxxx and the private channel is $gossips): import jwttoken = jwt.encode({ \"client\": \"xxxx-xxx-xxx-xxxx\", \"channel\": \"$gossips\"}, \"secret\", algorithm=\"HS256\").decode()print(token) Where \"secret\" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).","s":"Example","u":"/docs/3/server/private_channels","h":"#example","p":984},{"i":1007,"t":"On this page","s":"Proxy to backend","u":"/docs/3/server/proxy","h":"","p":1006},{"i":1009,"t":"HTTP proxy in Centrifugo converts client connection events into HTTP call to the application backend.","s":"HTTP proxy","u":"/docs/3/server/proxy","h":"#http-proxy","p":1006},{"i":1011,"t":"All proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).","s":"HTTP request structure","u":"/docs/3/server/proxy","h":"#http-request-structure","p":1006},{"i":1013,"t":"The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example: config.json { ... \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} Alternatively, you can set a list of headers via an environment variable (space separated): export CENTRIFUGO_PROXY_HTTP_HEADERS=\"Cookie User-Agent X-B3-TraceId X-B3-SpanId\" ./centrifugo note Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend.","s":"Proxy HTTP headers","u":"/docs/3/server/proxy","h":"#proxy-http-headers","p":1006},{"i":1015,"t":"When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied. See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.","s":"Proxy GRPC metadata","u":"/docs/3/server/proxy","h":"#proxy-grpc-metadata","p":1006},{"i":1017,"t":"With the following options in the configuration file: { ... \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_connect_timeout\": \"1s\"} – connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request. danger Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections. Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. tip If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server. This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired). Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\"} Expected response example: {\"result\": {\"user\": \"56\"}} This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result. Several app examples which use connect proxy can be found in our blog: With NodeJS With Django With Laravel Connect request fields​ This is what sent from Centrifugo to application backend in case of connect proxy request. Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) name string yes optional name of the client (this field will only be set if provided by a client on connect) version string yes optional version of the client (this field will only be set if provided by a client on connect) data JSON yes optional data from client (this field will only be set if provided by a client on connect) b64data string yes optional data from the client in base64 format (if the binary proxy mode is used) channels Array of strings yes list of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result Connect result fields​ This is what application returns to Centrifugo inside result field in case of connect proxy request. Field Type Optional Description user string no user ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests expire_at integer yes a timestamp when connection must be considered expired. If not set or set to 0 connection won't expire at all info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages data JSON yes a custom data to send to the client in connect command response. b64data string yes a custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client channels array of strings yes allows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions subs map of SubscribeOptions yes map of channels with options to subscribe connection to. See more details about server-side subscriptions meta JSON object (ex. {\"key\": \"value\"}) yes a custom data to attach to connection (this won't be exposed to client-side) Options​ proxy_connect_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend. Example​ Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return \"56\" as user ID): class CentrifugoConnectHandler(tornado.web.RequestHandler): def check_xsrf_cookie(self): pass def post(self): self.set_header('Content-Type', 'application/json; charset=\"utf-8\"') data = json.dumps({ 'result': { 'user': '56' } }) self.write(data)def main(): options.parse_command_line() app = tornado.web.Application([ (r'/centrifugo/connect', CentrifugoConnectHandler), ]) app.listen(3000) tornado.ioloop.IOLoop.instance().start()if __name__ == '__main__': main() This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side. We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea.","s":"Connect proxy","u":"/docs/3/server/proxy","h":"#connect-proxy","p":1006},{"i":1019,"t":"With the following options in the configuration file: { ... \"proxy_refresh_endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"proxy_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side. The payload sent to app backend in refresh request (when the connection is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_refresh_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.","s":"Refresh proxy","u":"/docs/3/server/proxy","h":"#refresh-proxy","p":1006},{"i":1021,"t":"With the following option in the configuration file: { ... \"proxy_rpc_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_rpc_timeout\": \"1s\"} RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket (or SockJS) connection in a bidirectional way. Payload example sent to app backend in RPC request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"method\": \"getCurrentPrice\", \"data\":{\"params\": {\"object_id\": 12}}} Expected response example: {\"result\": {\"data\": {\"answer\": \"2019\"}}} RPC request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process method string yes an RPC method string, if the client does not use named RPC call then method will be omitted data JSON yes RPC custom data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) RPC result fields​ Field Type Optional Description data JSON yes RPC response - any valid JSON is supported b64data string yes can be set instead of data for binary response encoded in base64 format Options​ proxy_rpc_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend. See below on how to return a custom error.","s":"RPC proxy","u":"/docs/3/server/proxy","h":"#rpc-proxy","p":1006},{"i":1023,"t":"With the following option in the configuration file: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\"} – subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel. tip Subscribe proxy does not proxy private and user-limited channels at the moment. That's because those are already providing a level of security (user-limited channels check current user ID, private channels require subscription token). In some cases you may use subscribe proxy as a replacement for private channels actually: if you prefer to check permissions using the proxy to backend mechanism – just stop using $ prefixes in channels, properly configure subscribe proxy and validate subscriptions upon proxy from Centrifugo to your backend (issued each time user tries to subscribe on a channel for which subscribe proxy enabled). Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace. So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} Or for channels in namespace sun: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_subscribe\": true }]} Payload example sent to app backend in subscribe request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\"} Expected response example if subscription is allowed: {\"result\": {}} Subscribe request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to subscribe to meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) data JSON yes custom data from client sent with subscription request (this field will only be set if provided by a client on subscribe). Available since Centrifugo v3.1.1 b64data string yes optional subscription data from the client in base64 format (if the binary proxy mode is used). Available since Centrifugo v3.1.1 Subscribe result fields​ Field Type Optional Description info JSON yes a channel info JSON b64info string yes a binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using data JSON yes a custom data to send to the client in subscribe command reply. b64data string yes a custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false} See below on how to return an error in case you don't want to allow subscribing. Options​ proxy_subscribe_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.","s":"Subscribe proxy","u":"/docs/3/server/proxy","h":"#subscribe-proxy","p":1006},{"i":1025,"t":"With the following option in the configuration file: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\"} – publish calls sent by a client will be proxied to proxy_publish_endpoint. This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side. Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too. So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"publish\": true, \"proxy_publish\": true} Or for channels in namespace sun: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"publish\": true, \"proxy_publish\": true }]} Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace). Payload example sent to app backend in a publish request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\", \"data\":{\"input\":\"hello\"}} Expected response example if publish is allowed: {\"result\": {}} Publish request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to publish to data JSON yes data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Publish result fields​ Field Type Optional Description data JSON yes an optional JSON data to send into a channel instead of original data sent by a client b64data string yes a binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing skip_history bool yes when set to true Centrifugo won't save publication to the channel history See below on how to return an error in case you don't want to allow publishing. Options​ proxy_publish_timeout (float, in seconds) config option controls timeout of HTTP POST request sent to app backend.","s":"Publish proxy","u":"/docs/3/server/proxy","h":"#publish-proxy","p":1006},{"i":1027,"t":"Application backend can return JSON object that contains an error to return it to the client: { \"error\": { \"code\": 1000, \"message\": \"custom error\" }} Application should use error codes >= 1000, error codes in the range 0-999 are reserved by Centrifugo internal protocol. Error code field is uint32 internally. note Returning custom error does not apply to response on refresh request as there is no sense in returning an error (will not reach client anyway).","s":"Return custom error","u":"/docs/3/server/proxy","h":"#return-custom-error","p":1006},{"i":1029,"t":"Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way: { \"disconnect\": { \"code\": 4000, \"reconnect\": false, \"reason\": \"custom disconnect\" }} Application must use numbers in the range 4000-4999 for custom disconnect codes. Code is uint32 internally. Numbers below 4000 are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 symbols. note Returning custom disconnect does not apply to response on refresh request as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason.","s":"Return custom disconnect","u":"/docs/3/server/proxy","h":"#return-custom-disconnect","p":1006},{"i":1031,"t":"Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server. GRPC service definitions can be found in the Centrifugo repository: proxy.proto. tip GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions. Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept). All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy: config.json { ... \"proxy_connect_endpoint\": \"grpc://localhost:12000\", \"proxy_connect_timeout\": \"1s\"} Refresh proxy: config.json { ... \"proxy_refresh_endpoint\": \"grpc://localhost:12000\", \"proxy_refresh_timeout\": \"1s\"} Or for RPC proxy: config.json { ... \"proxy_rpc_endpoint\": \"grpc://localhost:12000\", \"proxy_rpc_timeout\": \"1s\"} For publish proxy in namespace chat: config.json { ... \"proxy_publish_endpoint\": \"grpc://localhost:12000\", \"proxy_publish_timeout\": \"1s\" \"namespaces\": [ { \"name\": \"chat\", \"publish\": true, \"proxy_publish\": true } ]} Use subscribe proxy for all channels without namespaces: config.json { ... \"proxy_subscribe_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} So the same as for HTTP, just the different endpoint scheme.","s":"GRPC proxy","u":"/docs/3/server/proxy","h":"#grpc-proxy","p":1006},{"i":1033,"t":"Some additional options exist to control GRPC proxy behavior. proxy_grpc_cert_file​ String, default: \"\". Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. proxy_grpc_credentials_key​ String, default \"\" (i.e. not used). Add custom key to per-RPC credentials. proxy_grpc_credentials_value​ String, default \"\" (i.e. not used). A custom value for proxy_grpc_credentials_key.","s":"GRPC proxy options","u":"/docs/3/server/proxy","h":"#grpc-proxy-options","p":1006},{"i":1035,"t":"We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.: Copy proxy Protobuf definitions Generate GRPC code Run backend service with you custom business logic Point Centrifugo to it.","s":"GRPC proxy example","u":"/docs/3/server/proxy","h":"#grpc-proxy-example","p":1006},{"i":1037,"t":"Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios: Client protocol type Proxy type Client headers Client metadata HTTP HTTP In proxy request headers N/A GRPC GRPC N/A In proxy request metadata HTTP GRPC In proxy request metadata N/A GRPC HTTP N/A In proxy request headers","s":"Header proxy rules","u":"/docs/3/server/proxy","h":"#header-proxy-rules","p":1006},{"i":1039,"t":"As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding. Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this. Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side). But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON. In this case it's possible to turn Centrifugo proxy into binary mode by using: config.json { ... \"proxy_binary_encoding\": true} Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues. While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine. Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.","s":"Binary mode","u":"/docs/3/server/proxy","h":"#binary-mode","p":1006},{"i":1041,"t":"New in Centrifugo v3.1.0. By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture). Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.","s":"Granular proxy mode","u":"/docs/3/server/proxy","h":"#granular-proxy-mode","p":1006},{"i":1043,"t":"Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below. config.json { ... \"granular_proxy_mode\": true}","s":"Enable granular proxy mode","u":"/docs/3/server/proxy","h":"#enable-granular-proxy-mode","p":1006},{"i":1045,"t":"When using granular proxy mode on configuration top level you can define \"proxies\" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint. Here is an example: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"connect\", \"endpoint\": \"http://localhost:3000/centrifugo/connect\", \"timeout\": \"500ms\", \"http_headers\": [\"Cookie\"] }, { \"name\": \"refresh\", \"endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"timeout\": \"500ms\" }, { \"name\": \"subscribe1\", \"endpoint\": \"http://localhost:3001/centrifugo/subscribe\" }, { \"name\": \"publish1\", \"endpoint\": \"http://localhost:3001/centrifugo/publish\" }, { \"name\": \"rpc1\", \"endpoint\": \"http://localhost:3001/centrifugo/rpc\" }, { \"name\": \"subscribe2\", \"endpoint\": \"http://localhost:3002/centrifugo/subscribe\" }, { \"name\": \"publish2\", \"endpoint\": \"grpc://localhost:3002\" } { \"name\": \"rpc2\", \"endpoint\": \"grpc://localhost:3002\" } ]} Let's look at all fields for a proxy object which is possible to set for each proxy inside \"proxies\" array. Field name Field type Required Description name string yes Unique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$ endpoint string yes HTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC. timeout duration (string) no Proxy request timeout, default \"1s\" http_headers array of strings no List of headers to proxy, by default no headers grpc_metadata array of strings no List of GRPC metadata keys to proxy, by default no metadata keys binary_encoding bool no Use base64 for payloads include_connection_meta bool no Include meta information (attached on connect) grpc_cert_file string no Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. grpc_credentials_key string no Add custom key to per-RPC credentials. grpc_credentials_value string no A custom value for grpc_credentials_key.","s":"Defining a list of proxies","u":"/docs/3/server/proxy","h":"#defining-a-list-of-proxies","p":1006},{"i":1047,"t":"As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event. To enable connect proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\"} We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea. Let's also add refresh proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\", \"refresh_proxy_name\": \"refresh\"}","s":"Granular connect and refresh","u":"/docs/3/server/proxy","h":"#granular-connect-and-refresh","p":1006},{"i":1049,"t":"Subscribe and publish proxy work per-namespace. This means that subscribe_proxy_name and publish_proxy_name are just a channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object. config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_proxy_name\": \"subscribe1\", \"publish\": true, \"publish_proxy_name\": \"publish1\" }, { \"name\": \"ns2\", \"subscribe_proxy_name\": \"subscribe2\", \"publish\": true, \"publish_proxy_name\": \"publish2\" } ]} If namespace does not have \"subscribe_proxy_name\" or \"subscribe_proxy_name\" is empty then no subscribe proxy will be used for a namespace. If namespace does not have \"publish_proxy_name\" or \"publish_proxy_name\" is empty then no publish proxy will be used for a namespace. tip You can define subscribe_proxy_name and publish_proxy_name on configuration top level – and in this case publish and subscribe requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.","s":"Granular subscribe and publish","u":"/docs/3/server/proxy","h":"#granular-subscribe-and-publish","p":1006},{"i":1051,"t":"Analogous to channel namespaces it's possible to configure rpc namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [...], \"rpc_namespaces\": [ { \"name\": \"rpc_ns1\", \"rpc_proxy_name\": \"rpc1\", }, { \"name\": \"rpc_ns2\", \"rpc_proxy_name\": \"rpc2\" } ]} The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces). Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start. tip The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.","s":"Granular RPC","u":"/docs/3/server/proxy","h":"#granular-rpc","p":1006},{"i":1053,"t":"On this page","s":"Configure TLS","u":"/docs/3/server/tls","h":"","p":1052},{"i":1055,"t":"In first way you already have cert and key files. For development you can create self-signed certificate - see this instruction as example. config.json { ... \"tls\": true, \"tls_key\": \"server.key\", \"tls_cert\": \"server.crt\"} And run: ./centrifugo --config=config.json","s":"Using crt and key files","u":"/docs/3/server/tls","h":"#using-crt-and-key-files","p":1052},{"i":1057,"t":"For automatic certificates from Let's Encrypt add into configuration file: config.json { ... \"tls_autocert\": true, \"tls_autocert_host_whitelist\": \"www.example.com\", \"tls_autocert_cache_dir\": \"/tmp/certs\", \"tls_autocert_email\": \"user@example.com\", \"tls_autocert_http\": true, \"tls_autocert_http_addr\": \":80\"} tls_autocert (boolean) says Centrifugo that you want automatic certificate handling using ACME provider. tls_autocert_host_whitelist (string) is a string with your app domain address. This can be comma-separated list. It's optional but recommended for extra security. tls_autocert_cache_dir (string) is a path to a folder to cache issued certificate files. This is optional but will increase performance. tls_autocert_email (string) is optional - it's an email address ACME provider will send notifications about problems with your certificates. tls_autocert_http (boolean) is an option to handle http_01 ACME challenge on non-TLS port. tls_autocert_http_addr (string) can be used to set address for handling http_01 ACME challenge (default is :80) When configured correctly and your domain is valid (localhost will not work) - certificates will be retrieved on first request to Centrifugo. Also Let's Encrypt certificates will be automatically renewed. There are two options that allow Centrifugo to support TLS client connections from older browsers such as Chrome 49 on Windows XP and IE8 on XP: tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys. tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints. uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.","s":"Automatic certificates","u":"/docs/3/server/tls","h":"#automatic-certificates","p":1052},{"i":1059,"t":"You can provide custom certificate files to configure TLS for GRPC API server. grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server","s":"TLS for GRPC API","u":"/docs/3/server/tls","h":"#tls-for-grpc-api","p":1052},{"i":1061,"t":"Starting from Centrifugo v3.0.0 you can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint. uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server","s":"TLS for GRPC unidirectional stream","u":"/docs/3/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":1052},{"i":1063,"t":"Client real-time SDKs The following SDKs allow connecting to Centrifugo from the application frontend: No need in clients for unidirectional approach Client libraries listed here speak Centrifugo bidirectional protocol (WebSocket). If you aim to use unidirectional approach you don't need client connectors – just use standard APIs. See the difference here. centrifuge-js – for browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-mobile - for iOS/Android with centrifuge-go as basis and gomobile centrifuge-dart - for Dart and Flutter centrifuge-swift – for native iOS development centrifuge-java – for native Android development and general Java See a description of client protocol if you want to write a custom client bidirectional connector.","s":"Client real-time SDKs","u":"/docs/3/transports/client_sdk","h":"","p":1062},{"i":1065,"t":"On this page","s":"Server-side subscriptions","u":"/docs/3/server/server_subs","h":"","p":1064},{"i":1067,"t":"See subscribe and unsubscribe server API","s":"Dynamic server-side subscriptions","u":"/docs/3/server/server_subs","h":"#dynamic-server-side-subscriptions","p":1064},{"i":1069,"t":"It's possible to automatically subscribe a user to a personal server-side channel. To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel. For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above. As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default \"\") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.","s":"Automatic personal channel subscription","u":"/docs/3/server/server_subs","h":"#automatic-personal-channel-subscription","p":1064},{"i":1071,"t":"Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes. user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect). This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel. Example config: { \"user_subscribe_to_personal\": true, \"user_personal_single_connection\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} note Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.","s":"Maintain single user connection","u":"/docs/3/server/server_subs","h":"#maintain-single-user-connection","p":1064},{"i":1073,"t":"On this page","s":"SockJS","u":"/docs/3/transports/sockjs","h":"","p":1072},{"i":1075,"t":"caution There are several important caveats to know when using SockJS – see below.","s":"SockJS caveats","u":"/docs/3/transports/sockjs","h":"#sockjs-caveats","p":1072},{"i":1077,"t":"First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend. For example, with Nginx sticky support can be enabled with ip_hash directive for upstream: upstream centrifugo { ip_hash; server 127.0.0.1:8000; server 127.0.0.2:8000;} With this configuration Nginx will proxy connections with the same ip address to the same upstream backend. But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair. So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.","s":"Sticky sessions","u":"/docs/3/transports/sockjs","h":"#sticky-sessions","p":1072},{"i":1079,"t":"SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.","s":"Browser only","u":"/docs/3/transports/sockjs","h":"#browser-only","p":1072},{"i":1081,"t":"One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.","s":"JSON only","u":"/docs/3/transports/sockjs","h":"#json-only","p":1072},{"i":1084,"t":"Boolean, default: false. Enables SockJS transport.","s":"sockjs","u":"/docs/3/transports/sockjs","h":"#sockjs","p":1072},{"i":1086,"t":"Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.","s":"sockjs_url","u":"/docs/3/transports/sockjs","h":"#sockjs_url","p":1072},{"i":1088,"t":"On this page","s":"Real-time transports","u":"/docs/3/transports/overview","h":"","p":1087},{"i":1090,"t":"Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus. Bidirectional transports come with a cost that developers need to use a special client connector library which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state and request queueing/timeouts/errors. Centrifugo has client SDKs for bidirectional communication for popular environments.","s":"Bidirectional","u":"/docs/3/transports/overview","h":"#bidirectional","p":1087},{"i":1092,"t":"Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time. The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource, HTTP streaming), or GRPC generated code to receive real-time updates from Centrifugo – thus avoiding dependency to a client connector that abstracts bidirectional communication. The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo).","s":"Unidirectional","u":"/docs/3/transports/overview","h":"#unidirectional","p":1087},{"i":1094,"t":"In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as: message Push { enum PushType { PUBLICATION = 0; JOIN = 1; LEAVE = 2; UNSUBSCRIBE = 3; MESSAGE = 4; SUBSCRIBE = 5; CONNECT = 6; DISCONNECT = 7; REFRESH = 8; } PushType type = 1; string channel = 2; bytes data = 3;} So unidirectional connection will receive various pushes. All you need to do is look at Push type and process it or skip it. In most cases you will be most interested in CONNECT and PUBLICATION types. tip In case of unidirectional WebSocket, EventSource and HTTP-streaming which currently work only with JSON data field of Push will come as an embedded JSON instead of bytes (again – the same mechanism as for Centrifugo bidirectional JSON protocol). Just try using any unidirectional transport and you will quickly get the idea.","s":"Unidirectional message types","u":"/docs/3/transports/overview","h":"#unidirectional-message-types","p":1087},{"i":1096,"t":"On this page","s":"Unidirectional SSE (EventSource)","u":"/docs/3/transports/uni_sse","h":"","p":1095},{"i":1098,"t":"Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example: const url = new URL('http://localhost:8000/connection/uni_sse');url.searchParams.append(\"cf_connect\", JSON.stringify({ 'token': ''}));const eventSource = new EventSource(url); Refer to the full Connect command description – it's the same as for unidirectional WebSocket. The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases. tip Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.","s":"Connect command","u":"/docs/3/transports/uni_sse","h":"#connect-command","p":1095},{"i":1100,"t":"JSON","s":"Supported data formats","u":"/docs/3/transports/uni_sse","h":"#supported-data-formats","p":1095},{"i":1102,"t":"Centrifugo sends SSE data like this as pings: event: pingdata: I.e. with event name ping and empty data. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/3/transports/uni_sse","h":"#pings","p":1095},{"i":1105,"t":"Boolean, default: false. Enables unidirectional SSE (EventSource) endpoint. config.json { ... \"uni_sse\": true}","s":"uni_sse","u":"/docs/3/transports/uni_sse","h":"#uni_sse","p":1095},{"i":1107,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes (when using HTTP POST requests to connect).","s":"uni_sse_max_request_body_size","u":"/docs/3/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":1095},{"i":1109,"t":"A basic browser example can be found here.","s":"Browser example","u":"/docs/3/transports/uni_sse","h":"#browser-example","p":1095},{"i":1111,"t":"On this page","s":"Unidirectional GRPC","u":"/docs/3/transports/uni_grpc","h":"","p":1110},{"i":1113,"t":"JSON and binary.","s":"Supported data formats","u":"/docs/3/transports/uni_grpc","h":"#supported-data-formats","p":1110},{"i":1116,"t":"Boolean, default: false. Enables unidirectional GRPC endpoint. config.json { ... \"uni_grpc\": true}","s":"uni_grpc","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc","p":1110},{"i":1118,"t":"String, default \"11000\". Port to listen on.","s":"uni_grpc_port","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_port","p":1110},{"i":1120,"t":"String, default \"\" (listen on all interfaces) Address to bind uni GRPC to.","s":"uni_grpc_address","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_address","p":1110},{"i":1122,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from GRPC connection in bytes.","s":"uni_grpc_max_receive_message_size","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":1110},{"i":1124,"t":"Boolean, default: false Enable custom TLS for unidirectional GRPC server.","s":"uni_grpc_tls","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls","p":1110},{"i":1126,"t":"String, default: \"\". Path to cert file.","s":"uni_grpc_tls_cert","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":1110},{"i":1128,"t":"String, default: \"\". Path to key file.","s":"uni_grpc_tls_key","u":"/docs/3/transports/uni_grpc","h":"#uni_grpc_tls_key","p":1110},{"i":1130,"t":"A basic example can be found here. It uses Go language, but for other languages approach is mostly the same: Copy Protobuf definitions Generate GRPC client code Use generated code to connect to Centrifugo Process Push messages, drop unknown Push types, handle those necessary for the application.","s":"Example","u":"/docs/3/transports/uni_grpc","h":"#example","p":1110},{"i":1132,"t":"On this page","s":"Unidirectional WebSocket","u":"/docs/3/transports/uni_websocket","h":"","p":1131},{"i":1134,"t":"It's possible to send connect command as first WebSocket message (as JSON). Field name Field type Required Description token string no Connection JWT, not required when using the connect proxy feature. data any JSON no Custom JSON connection data name string no Application name version string no Application version subs map of channel to SubscribeRequest no Pass an information about desired subscriptions to a server","s":"Connect command","u":"/docs/3/transports/uni_websocket","h":"#connect-command","p":1131},{"i":1136,"t":"Field name Field type Required Description recover boolean no Whether a client wants to recover from a certain position offset integer no Known stream position offset when recover is used epoch string no Known stream position epoch when recover is used","s":"SubscribeRequest","u":"/docs/3/transports/uni_websocket","h":"#subscriberequest","p":1131},{"i":1138,"t":"JSON","s":"Supported data formats","u":"/docs/3/transports/uni_websocket","h":"#supported-data-formats","p":1131},{"i":1140,"t":"Centrifugo uses empty messages (frame with no payload at all) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/3/transports/uni_websocket","h":"#pings","p":1131},{"i":1143,"t":"Boolean, default: false. Enables unidirectional WebSocket endpoint. config.json { ... \"uni_websocket\": true}","s":"uni_websocket","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket","p":1131},{"i":1145,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from WebSocket connection in bytes.","s":"uni_websocket_message_size_limit","u":"/docs/3/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":1131},{"i":1147,"t":"Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal. First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example: config.json { \"token_hmac_secret_key\": \"secret\", \"uni_websocket\":true, \"user_subscribe_to_personal\": true} Run Centrifugo: ./centrifugo -c config.json In another terminal: ❯ ./centrifugo -c config.json -u test_userHMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw Install wscat and run: wscat -c \"ws://localhost:8000/connection/uni_websocket\" This will establish a connection with a server and you then can send connect command to a server: ❯ wscat -c \"ws://localhost:8000/connection/uni_websocket\"Connected (press CTRL+C to quit)> {\"token\": \"eyJh..5lz1Iw\", \"subs\": {\"abc\": {}}}< {\"type\":6,\"data\":{\"client\":\"8ceaa299-4c7b-4254-9d65-c61b6883833a\",\"version\":\"3.0.0\",\"subs\":{\"#test_user\":{\"recoverable\":true,\"epoch\":\"StoH\",\"positioned\":true},\"abc\":{\"recoverable\":true,\"epoch\":\"nNgd\",\"positioned\":true},\"expires\":true,\"ttl\":604653}} The connection will receive pings (empty messages) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.","s":"Example","u":"/docs/3/transports/uni_websocket","h":"#example","p":1131},{"i":1149,"t":"On this page","s":"WebSocket","u":"/docs/3/transports/websocket","h":"","p":1148},{"i":1152,"t":"Default: 65536 (64KB) Maximum allowed size of a message received from WebSocket connection in bytes.","s":"websocket_message_size_limit","u":"/docs/3/transports/websocket","h":"#websocket_message_size_limit","p":1148},{"i":1154,"t":"In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size). config.json { ... \"websocket_read_buffer_size\": 512}","s":"websocket_read_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_read_buffer_size","p":1148},{"i":1156,"t":"In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused): config.json { ... \"websocket_write_buffer_size\": 512}","s":"websocket_write_buffer_size","u":"/docs/3/transports/websocket","h":"#websocket_write_buffer_size","p":1148},{"i":1158,"t":"If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.","s":"websocket_use_write_buffer_pool","u":"/docs/3/transports/websocket","h":"#websocket_use_write_buffer_pool","p":1148},{"i":1160,"t":"An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire. We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally. caution Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable. To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature. Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client. It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.","s":"websocket_compression","u":"/docs/3/transports/websocket","h":"#websocket_compression","p":1148},{"i":1162,"t":"In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if: you want less traffic on wire as Protobuf is very compact you want maximum performance on server-side as Protobuf encoding/decoding is very efficient you can sacrifice human-readable JSON for your application Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like: wss://centrifugo.example.com/connection/websocket?format=protobuf After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes. tip You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.","s":"Protobuf binary protocol","u":"/docs/3/transports/websocket","h":"#protobuf-binary-protocol","p":1148},{"i":1164,"t":"On this page","s":"Unidirectional HTTP streaming","u":"/docs/3/transports/uni_http_stream","h":"","p":1163},{"i":1166,"t":"It's possible to pass initial connect command by posting a JSON body to a streaming endpoint. Refer to the full Connect command description – it's the same as for unidirectional WebSocket.","s":"Connect command","u":"/docs/3/transports/uni_http_stream","h":"#connect-command","p":1163},{"i":1168,"t":"JSON","s":"Supported data formats","u":"/docs/3/transports/uni_http_stream","h":"#supported-data-formats","p":1163},{"i":1170,"t":"Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/3/transports/uni_http_stream","h":"#pings","p":1163},{"i":1173,"t":"Boolean, default: false. Enables unidirectional HTTP streaming endpoint. config.json { ... \"uni_http_stream\": true}","s":"uni_http_stream","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream","p":1163},{"i":1175,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"uni_http_stream_max_request_body_size","u":"/docs/3/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":1163},{"i":1177,"t":"Let's look how simple it is to connect to Centrifugo using HTTP streaming. We will start from scratch, generate new configuration file: centrifugo genconfig Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect: config.json { ... \"uni_http_stream\": true, \"user_subscribe_to_personal\": true} Run Centrifugo: centrifugo -c config.json In separate terminal window create token for a user: ❯ go run main.go gentoken -u user12HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request: curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}' Open one more terminal window and publish message to a personal user channel: curl -X POST http://localhost:8000/api \\ -d '{\"method\": \"publish\", \"params\": {\"channel\": \"#user12\", \"data\": {\"input\": \"hello\"}}}' \\ -H \"Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc\" You should see this message received in a terminal window with established connection to HTTP streaming endpoint: ❯ curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}'{\"type\":6,\"data\":{\"client\":\"cf5dc239-83ac-4d0f-b9ed-9733d7f7b61b\",\"version\":\"dev\",\"subs\":{\"#user12\":{}}}}nullnullnullnullnull{\"channel\":\"#user12\",\"data\":{\"data\":{\"input\": \"hello\"}}} null messages are pings from a server. That's all, happy streaming!","s":"Connecting using CURL","u":"/docs/3/transports/uni_http_stream","h":"#connecting-using-curl","p":1163},{"i":1179,"t":"A basic browser example can be found here.","s":"Browser example","u":"/docs/3/transports/uni_http_stream","h":"#browser-example","p":1163},{"i":1181,"t":"On this page","s":"Engines, scalability","u":"/docs/3/server/engines","h":"","p":1180},{"i":1183,"t":"Used by default. Supports only one node. Nice choice to start with. Supports all features keeping everything in Centrifugo node process memory. You don't need to install Redis when using this engine. Advantages: Super fast since it does not involve network at all Does not require separate broker setup Disadvantages: Does not allow scaling nodes (actually you still can scale Centrifugo with Memory engine but you have to publish data into each Centrifugo node and you won't have consistent history and presence state throughout Centrifugo nodes) Does not persist message history in channels between Centrifugo restarts","s":"Memory engine","u":"/docs/3/server/engines","h":"#memory-engine","p":1180},{"i":1185,"t":"history_meta_ttl​ Duration, default 0s. history_meta_ttl sets a time in seconds of history stream metadata expiration. Stream metadata is information about the current offset number in the channel and epoch value. By default, metadata for channels does not expire. Though in some cases – when channels are created for а short time and then not used anymore – created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory leak. Setting a reasonable value to this option (usually much bigger than the history retention period) can help. In this case, unused channel metadata will eventually expire.","s":"Memory engine options","u":"/docs/3/server/engines","h":"#memory-engine-options","p":1180},{"i":1187,"t":"Redis is an open-source, in-memory data structure store, used as a database, cache, and message broker. Centrifugo Redis engine allows scaling Centrifugo nodes to different machines. Nodes will use Redis as a message broker (utilizing Redis PUB/SUB for node communication) and keep presence and history data in Redis. Minimal Redis version is 5.0.1","s":"Redis engine","u":"/docs/3/server/engines","h":"#redis-engine","p":1180},{"i":1189,"t":"Several configuration options related to Redis engine. redis_address​ String, default \"127.0.0.1:6379\" - Redis server address. redis_password​ String, default \"\" - Redis password. redis_user​ Available since Centrifugo v3.2.0 String, default \"\" - Redis user for ACL-based auth. redis_db​ Integer, default 0 - number of Redis db to use. redis_tls​ Boolean, default false - enable Redis TLS connection. redis_tls_skip_verify​ Boolean, default false - disable Redis TLS host verification. redis_prefix​ String, default \"centrifugo\" – custom prefix to use for channels and keys in Redis. redis_use_lists​ Boolean, default false – turns on using Redis Lists instead of Stream data structure for keeping history (not recommended, keeping this for backwards compatibility mostly). history_meta_ttl​ Similar to a Memory engine Redis engine also looks at history_meta_ttl option (duration, default 0) - which sets a time of history stream metadata expiration in Redis Engine (with seconds resolution). Meta key in Redis is a HASH that contains the current offset number in channel and epoch value. By default, metadata for channels does not expire. Though in some cases – when channels are created for а short time and then not used anymore – created stream metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory leak. Setting a reasonable value to this option (usually much bigger than the history retention period) can help. In this case, unused channel metadata will eventually expire.","s":"Redis engine options","u":"/docs/3/server/engines","h":"#redis-engine-options","p":1180},{"i":1191,"t":"Let's see how to start several Centrifugo nodes using the Redis Engine. We will start 3 Centrifugo nodes and all those nodes will be connected via Redis. First, you should have Redis running. As soon as it's running - we can launch 3 Centrifugo instances. Open your terminal and start the first one: centrifugo --config=config.json --port=8000 --engine=redis --redis_address=127.0.0.1:6379 If your Redis is on the same machine and runs on its default port you can omit redis_address option in the command above. Then open another terminal and start another Centrifugo instance: centrifugo --config=config.json --port=8001 --engine=redis --redis_address=127.0.0.1:6379 Note that we use another port number (8001) as port 8000 is already busy by our first Centrifugo instance. If you are starting Centrifugo instances on different machines then you most probably can use the same port number (8000 or whatever you want) for all instances. And finally, let's start the third instance: centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379 Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes. To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation. tip In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers. Here is a live example where we locally start two Centrifugo nodes both connected to local Redis: Sorry, your browser doesn't support embedded video.","s":"Scaling with Redis tutorial","u":"/docs/3/server/engines","h":"#scaling-with-redis-tutorial","p":1180},{"i":1193,"t":"Centrifugo supports the official way to add high availability to Redis - Redis Sentinel. For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name: redis_sentinel_address (string, default \"\") - comma separated list of Sentinel addresses for HA. At least one known server required. redis_sentinel_master_name (string, default \"\") - name of Redis master Sentinel monitors Also: redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1 redis_sentinel_user (available since v3.2.0) - optional string user (used only in Redis ACL-based auth). So you can start Centrifugo which will use Sentinels to discover Redis master instances like this: centrifugo --config=config.json Where config.json: config.json { ... \"engine\": \"redis\", \"redis_sentinel_address\": \"127.0.0.1:26379\", \"redis_sentinel_master_name\": \"mymaster\"} Sentinel configuration files can look like this: port 26379sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 10000sentinel failover-timeout mymaster 60000 You can find how to properly set up Sentinels in official documentation. Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.","s":"Redis Sentinel for high availability","u":"/docs/3/server/engines","h":"#redis-sentinel-for-high-availability","p":1180},{"i":1195,"t":"Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case. For example, you can use something like this in Haproxy config: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:16379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin And then just point Centrifugo to this Haproxy: centrifugo --config=config.json --engine=redis --redis_address=\"localhost:16379\"","s":"Haproxy instead of Sentinel configuration","u":"/docs/3/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":1180},{"i":1197,"t":"Centrifugo has built-in Redis sharding support. This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale. At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples. To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"127.0.0.1:6379\", \"127.0.0.1:6380\", ]} To start Centrifugo with Redis instances on different hosts: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"192.168.1.34:6379\", \"192.168.1.35:6379\", ]} If you also need to customize AUTH password, Redis DB number then you can use an extended address notation. note Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers. When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).","s":"Redis sharding","u":"/docs/3/server/engines","h":"#redis-sharding","p":1180},{"i":1199,"t":"Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\" ]} You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start. To set the same over environment variable: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001\" CENTRIFUGO_ENGINE=redis ./centrifugo If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\", \"localhost:30101,localhost:30102,localhost:30103\" ]} Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump). To reproduce the same over environment variable use space to separate different clusters: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001,localhost:30002 localhost:30101,localhost:30102\" CENTRIFUGO_ENGINE=redis ./centrifugo","s":"Redis cluster","u":"/docs/3/server/engines","h":"#redis-cluster","p":1180},{"i":1201,"t":"EXPERIMENTAL Centrifugo Redis engine seamlessly works with KeyDB. KeyDB server is compatible with Redis and provides several additional features beyond. caution We can't give any promises about compatibility with KeyDB in the future Centrifugo releases - while KeyDB is fully compatible with Redis things should work just fine. That's why we consider this as EXPERIMENTAL feature. Use KeyDB instead of Redis only if you are sure you need it. Nothing stops you from running several Redis instances per each core you have, configure sharding, and obtain even better performance than KeyDB can provide (due to lack of synchronization between threads in Redis). To run Centrifugo with KeyDB all you need to do is use redis engine but run the KeyDB server instead of Redis.","s":"KeyDB Engine","u":"/docs/3/server/engines","h":"#keydb-engine","p":1180},{"i":1203,"t":"EXPERIMENTAL Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics. caution EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves. There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge. info To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together. There are several supported Tarantool topologies to which Centrifugo can connect: One standalone Tarantool instance Many standalone Tarantool instances and consistently shard data between them Tarantool running in Cartridge Tarantool with replica and automatic failover in Cartridge Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7) After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes): config.json { ... \"engine\": \"tarantool\", \"tarantool_address\": \"127.0.0.1:3301\"} See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework. See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.","s":"Tarantool engine","u":"/docs/3/server/engines","h":"#tarantool-engine","p":1180},{"i":1205,"t":"tarantool_address​ String or array of strings. Default tcp://127.0.0.1:3301. Connection address to Tarantool. tarantool_mode​ String, default standalone A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication). All modes support client-side consistent sharding (similar to what Redis engine provides). tarantool_user​ String, default \"\". Allows setting a user. tarantool_password​ String, default \"\". Allows setting a password. history_meta_ttl​ Duration, default 0s. Same option as for Memory engine and Redis engine also applies to Tarantool case.","s":"Tarantool engine options","u":"/docs/3/server/engines","h":"#tarantool-engine-options","p":1180},{"i":1207,"t":"It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below. Limitations: Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available. Nats wildcard channel subscriptions with symbols * and > not supported. First start Nats server: $ nats-server[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set][3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready Then start Centrifugo with broker option: centrifugo --broker=nats --config=config.json And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine): centrifugo --broker=nats --config=config.json --port=8001 Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.","s":"Nats broker","u":"/docs/3/server/engines","h":"#nats-broker","p":1180},{"i":1209,"t":"nats_url​ String, default nats://127.0.0.1:4222. Connection url in format nats://derek:pass@localhost:4222. nats_prefix​ String, default centrifugo. Prefix for channels used by Centrifugo inside Nats. nats_dial_timeout​ Duration, default 1s. Timeout for dialing with Nats. nats_write_timeout​ Duration, default 1s. Write (and flush) timeout for a connection to Nats.","s":"Options","u":"/docs/3/server/engines","h":"#options","p":1180},{"i":1211,"t":"flow_diagrams For swimlanes.io: Client <- App Backend: JWTnote:The backend generates JWT for a user and passes it to the client side.Client -> Centrifugo: Client connects to Centrifugo with JWT...: {fas-spinner} Persistent connection establishedClient -> Centrifugo: Client issues channel subscribe requestsCentrifugo -->> Client: Client receives real-time updates from channels Client -> Centrifugo: Connect requestnote:Client connects to Centrifugo without JWT.Centrifugo -> App backend: Sends request further (via HTTP or GRPC)note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.App backend -> Centrifugo: Connect replyCentrifugo -> Client: Connect Reply...: {fas-spinner} Persistent connection established Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers. Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers.","s":"flow_diagrams","u":"/docs/4/flow_diagrams","h":"","p":1210},{"i":1213,"t":"Attributions","s":"Attributions","u":"/docs/4/attributions","h":"","p":1212},{"i":1215,"t":"The following images have been used in the landing page. Icons made by kerismaker: https://www.flaticon.com/packs/web-maintenance-35","s":"Landing Page Images","u":"/docs/4/attributions","h":"#landing-page-images","p":1212},{"i":1217,"t":"Join community If you find Centrifugo interesting – please welcome to our community rooms in Telegram (the most active) and Discord: We are trying to create a respectful and curious community. In those rooms you may find answers to questions not fully covered by the documentation, share your thoughts and ideas on the real-time messaging topics and Centrifugo in particular. We also have Twitter account and Youtube channel. See you there!","s":"Join community","u":"/docs/4/getting-started/community","h":"","p":1216},{"i":1219,"t":"On this page","s":"Frequently Asked Questions","u":"/docs/4/faq","h":"","p":1218},{"i":1221,"t":"This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here. Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case. You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.","s":"How many connections can one Centrifugo instance handle?","u":"/docs/4/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":1218},{"i":1223,"t":"Depending on transport used and features enabled the amount of RAM required per each connection can vary. For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections. For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.","s":"Memory usage per connection?","u":"/docs/4/faq","h":"#memory-usage-per-connection","p":1218},{"i":1225,"t":"Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker. See engines and scalability considerations.","s":"Can Centrifugo scale horizontally?","u":"/docs/4/faq","h":"#can-centrifugo-scale-horizontally","p":1218},{"i":1227,"t":"See design overview","s":"Message delivery model","u":"/docs/4/faq","h":"#message-delivery-model","p":1218},{"i":1229,"t":"See design overview.","s":"Message order guarantees","u":"/docs/4/faq","h":"#message-order-guarantees","p":1218},{"i":1231,"t":"No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel. When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).","s":"Should I create channels explicitly?","u":"/docs/4/faq","h":"#should-i-create-channels-explicitly","p":1218},{"i":1233,"t":"Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application. But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc. One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.","s":"What about best practices with the number of channels?","u":"/docs/4/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":1218},{"i":1235,"t":"Currently, no. We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this: Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID) Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates. Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point. Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel. In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.","s":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/4/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":1218},{"i":1237,"t":"No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere. Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end). To summarize: if you are using binary Protobuf clients and binary payloads everywhere – you are fine. if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine. if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.","s":"Can I have both binary and JSON clients in one channel?","u":"/docs/4/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":1218},{"i":1239,"t":"While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel. You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.","s":"Online presence for chat apps - online status of your contacts","u":"/docs/4/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":1218},{"i":1241,"t":"The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.","s":"Centrifugo stops accepting new connections, why?","u":"/docs/4/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":1218},{"i":1243,"t":"Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.","s":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/4/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":1218},{"i":1245,"t":"Yes, Centrifugo works with HTTP/2. This is provided by built-in Go http server implementation. You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable: GODEBUG=\"http2server=0\" centrifugo -c config.json Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.","s":"Does Centrifugo work with HTTP/2?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http2","p":1218},{"i":1247,"t":"Centrifugo v4 added an experimental HTTP/3 support. As soon as you enabled TLS and provided \"http3\": true option all endpoints on external port will be served by HTTP/3 server based on github.com/quic-go/quic-go implementation. This (among other benefits which HTTP/3 can provide) is a step towards a proper WebTransport support. For now we support WebTransport experimentally. It's worth noting that WebSocket transport does not work over HTTP/3, it still starts with HTTP/1.1 Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and Eventsource should work just fine with HTTP/3. HTTP/3 does not work with ACME autocert TLS at the moment - i.e. you need to explicitly provide paths to cert and key files as described here.","s":"Does Centrifugo work with HTTP/3?","u":"/docs/4/faq","h":"#does-centrifugo-work-with-http3","p":1218},{"i":1249,"t":"If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.","s":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/4/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":1218},{"i":1251,"t":"Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via FCM, or browsers over Web Push API. We are preparing our own push notifications API as part of Centrifugo PRO version. This is under construction though. The reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.","s":"What if I need to send push notifications to mobile or web applications?","u":"/docs/4/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":1218},{"i":1253,"t":"You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.","s":"How can I know a message is delivered to a client?","u":"/docs/4/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":1218},{"i":1255,"t":"It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend. We suggest using one of the available approaches: When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API. Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network. Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself). Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.","s":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/4/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":1218},{"i":1257,"t":"There are several ways to achieve it: use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers you can use subscribe proxy feature to validate subscriptions, see chapter about proxy finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations","s":"How to create a secure channel for two users only (private chat case)?","u":"/docs/4/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":1218},{"i":1259,"t":"In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled. For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients. The same relates to other channel options.","s":"What's the best way to organize channel configuration?","u":"/docs/4/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":1218},{"i":1261,"t":"Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there. Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.","s":"Does Centrifugo support webhooks?","u":"/docs/4/faq","h":"#does-centrifugo-support-webhooks","p":1218},{"i":1263,"t":"Centrifugo does not support disconnect hooks at this point. First of all, there is no guarantee that the disconnect process will have a time to execute on the client-side (as the client can just switch off its device or simply lose internet connection). This means that a server may notice a connection loss with some delay (thanks to PING/PONG mechanism). Centrifugo node can be unexpectedly killed. So there is a chance that disconnect event won't have a chance to be emitted to the backend. One more reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. To reduce the load during connect process Centrifugo has JWT authentication. Even if disconnect events were queued/rate-limited there could be situations when your app processes disconnect hook while user already reconnected and connect event processed. This is a racy situation which you will need to handle somehow (possibly based on unique client ID attached to each connection). If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend from the client-side and update user status somewhere on the backend (use Redis maybe). This is a pretty robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo refresh proxy for the task of periodic backend pinging. In this case you will notice that user (or particular client) left app with some delay – this may be a acceptable trade-off in many cases. Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together.","s":"Why Centrifugo does not have disconnect hooks?","u":"/docs/4/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":1218},{"i":1265,"t":"No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.","s":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/4/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":1218},{"i":1267,"t":"Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel). There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers. You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs. The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.","s":"How scalable is the online presence and join/leave features?","u":"/docs/4/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":1218},{"i":1269,"t":"Sometimes you need to send some initial state towards channel subscriber. Centrifugo provides a way to attach any data to a successful subscribe reply when using subscribe proxy feature. See data and b64data fields. This data will be part of subscribed event context. And of course, you can always simply send request to get initial data from the application backend before or after subscribing to a channel without Centrifugo connection involved (i.e. using sth like general AJAX/HTTP call or passing data to the template when rendering an application page).","s":"How to send initial data to channel subscriber?","u":"/docs/4/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":1218},{"i":1271,"t":"If you want to use Centrifugo with different projects the recommended approach is to have different Centrifugo installations for each project. Multitenancy is better to solve on infrastructure level in case of Centrifugo. It's possible to share one Redis setup though by setting unique redis_prefix. But we recommend having completely isolated setups.","s":"Does Centrifugo support multitenancy?","u":"/docs/4/faq","h":"#does-centrifugo-support-multitenancy","p":1218},{"i":1273,"t":"Ask in our community rooms:","s":"I have not found an answer to my question here:","u":"/docs/4/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":1218},{"i":1275,"t":"On this page","s":"Design overview","u":"/docs/4/getting-started/design","h":"","p":1274},{"i":1277,"t":"Originally Centrifugo was built with the unidirectional flow as the main approach. Though Centrifugo itself used a bidirectional protocol between a client and a server to allow client dynamically create subscriptions, Centrifugo did not allow using it for sending data from client to server. With this approach publications travel only from server to a client. All requests that generate new data first go to the application backend (for example over AJAX call of backend API). The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event from a backend side to Centrifugo API. This is a pretty natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is decoupled from the application in this case. During Centrifugo v2 life cycle this paradigm evolved a bit. It's now possible to send RPC requests from client to Centrifugo and the request will be then proxied to the application backend. Also, connection attempts and publications to channels can now be proxied. So bidirectional connection between client and Centrifugo is now available for utilizing by developers in both directions. For example, here is how publish diagram could look like when using publish request proxy feature: So at the moment, the number of possible integration ways increased.","s":"Idiomatic usage","u":"/docs/4/getting-started/design","h":"#idiomatic-usage","p":1274},{"i":1279,"t":"Idiomatic Centrifugo usage requires having the main application database from which initial and actual state can be loaded at any point in time. While Centrifugo has channel history, it has been mostly designed to reduce the load on the main application database when all users reconnect at once (in case of load balancer configuration reload, Centrifugo restart, temporary network problems, etc). This allows to radically reduce the load on the application main database during reconnect storm. Since such disconnects are usually pretty short in time having a reasonably small number of messages cached in history is sufficient. The addition of history iteration API shifts possible use cases a bit. Manually calling history chunk by chunk allows keeping larger number of publications per channel. Depending on Engine used and configuration of the underlying storage history stream persistence characteristics can vary. For example, with Memory Engine history will be lost upon Centrifugo restart. With Redis or Tarantool engines history will survive Centrifugo restarts but depending on a storage configuration it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis RDB and AOF, configure replication for storage high-availability, use Redis Cluster or maybe synchronous replication with Tarantool. Centrifugo provides ways to distinguish whether the missed messages can't be restored from Centrifugo history upon recovery so a client should restore state from the main application database. So Centrifugo message history can be used as a complementary way to restore messages and thus reduce a load on the main application database most of the time.","s":"Message history considerations","u":"/docs/4/getting-started/design","h":"#message-history-considerations","p":1274},{"i":1281,"t":"By default, the message delivery model of Centrifugo is at most once. With history and the positioning/recovery features enabled it's possible to achieve at least once guarantee within history retention time and size. After abnormal disconnect clients have an option to recover missed messages from the publication channel stream history that Centrifugo maintains. Without the positioning or recovery features enabled a message sent to Centrifugo can be theoretically lost while moving towards clients. Centrifugo tries to do its best to prevent message loss on a way to online clients, but the application should tolerate a loss. As noted Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. Also, it compensates at most once delivery of broker PUB/SUB system (Redis, Tarantool) by using additional publication offset checks and periodic offset synchronization. So publication loss missed in PUB/SUB layer will be detected eventually and client may catch up the state loading it from history. At this moment Centrifugo message recovery is designed for a short-term disconnect period (think no more than one hour for a typical chat application, but this can vary). After this period (which can be configured per channel basis) Centrifugo removes messages from the channel history cache. In this case, Centrifugo may tell the client that some messages can not be recovered, so your application state should be loaded from the main database.","s":"Message delivery model","u":"/docs/4/getting-started/design","h":"#message-delivery-model","p":1274},{"i":1283,"t":"Message order in channels is guaranteed to be the same while you publish messages into channel one after another or publish them in one request. If you do parallel publications into the same channel then Centrifugo can't guarantee message order since those may be processed concurrently by Centrifugo.","s":"Message order guarantees","u":"/docs/4/getting-started/design","h":"#message-order-guarantees","p":1274},{"i":1285,"t":"It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely only on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in AJAX call response and render it. This way user that posts a comment will think that everything works just fine. Be careful to not draw comments twice in this case - think about idempotent identifiers for your entities.","s":"Graceful degradation","u":"/docs/4/getting-started/design","h":"#graceful-degradation","p":1274},{"i":1287,"t":"Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to the network failures and unexpected shut down of Centrifugo node, there are chances that clients can be presented in a presence up to one minute more (until presence entry expiration). Also, channel presence does not scale well for channels with lots of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows the response size becomes larger. In some cases, presence_stats API call can be sufficient to avoid receiving the entire presence state.","s":"Online presence considerations","u":"/docs/4/getting-started/design","h":"#online-presence-considerations","p":1274},{"i":1289,"t":"Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with Nats broker. See engines. All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications. But, if you approach broker resource limits (CPU or memory) then it's possible: Use Centrifugo consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool) Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters) Nats broker should scale well itself in cluster setup All brokers can be set up in highly available way so there won't be a single point of failure. All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact and the fact that Centrifugo provides hooks for the application to understand history loss makes the process of resharding mostly automatic. As soon as you need to add additional broker shard (when using client-side sharding) you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently part of the data will stay on the same broker nodes. Applications should handle cases that channel data moved to another shard and restore a state from the main application database when needed.","s":"Scalability considerations","u":"/docs/4/getting-started/design","h":"#scalability-considerations","p":1274},{"i":1291,"t":"On this page","s":"Ecosystem notes","u":"/docs/4/getting-started/ecosystem","h":"","p":1290},{"i":1293,"t":"Centrifugo is built on top of Centrifuge library for Go language. Due to its standalone language-agnostic nature Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a more tight integration of real-time server with application business logic you may consider using Centrifuge library to build something similar to Centrifugo but with customized behavior. Centrifuge library can be considered as Socket.IO analogue in Go language ecosystem. Library README has detailed description, link to examples and introduction post. Many Centrifugo features should be re-implemented when using Centrifuge - like API layer, admin web UI, proxy etc (if you need those of course). And you need to write in Go language. But the core functionality like a client-server protocol (all Centrifugo client SDKs work with Centrifuge library based server) and Redis engine to scale come out of the box – in most cases this is enough to start building an app. tip Many things said in Centrifugo doc can be considered as an extra documentation for Centrifuge library (for example part about infrastructure tuning or transport description). But not all of them.","s":"Centrifuge library for Go","u":"/docs/4/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":1290},{"i":1295,"t":"There are some community-driven projects that provide integration with frameworks for more native experience. tip In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. Integrating directly may allow using all Centrifugo features without limitations which can be introduced by third-party wrapper. laravel-centrifugo integration with Laravel framework laravel-centrifugo-broadcaster one more integration with Laravel framework to consider CentrifugoBundle integration with Symfony framework Django-instant integration with Django framework roadrunner-php/centrifugo integration with RoadRunner spiral/roadrunner-bridge integration with Spiral Framework","s":"Framework integrations","u":"/docs/4/getting-started/ecosystem","h":"#framework-integrations","p":1290},{"i":1297,"t":"On this page","s":"Client API showcase","u":"/docs/4/getting-started/client_api","h":"","p":1296},{"i":1299,"t":"Each Centrifugo client allows connecting to a server. const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.connect(); In most cases you will need to pass JWT (JSON Web Token) for authentication, so the example above transforms to: const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');centrifuge.setToken('')centrifuge.connect(); See authentication chapter for more information on how to generate connection JWT. If you are using connect proxy then you may go without setting JWT.","s":"Connecting to a server","u":"/docs/4/getting-started/client_api","h":"#connecting-to-a-server","p":1296},{"i":1301,"t":"After connecting you can disconnect from a server at any moment. centrifuge.disconnect();","s":"Disconnecting from a server","u":"/docs/4/getting-started/client_api","h":"#disconnecting-from-a-server","p":1296},{"i":1303,"t":"Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.","s":"Reconnecting to a server","u":"/docs/4/getting-started/client_api","h":"#reconnecting-to-a-server","p":1296},{"i":1305,"t":"All client implementations allow setting handlers on connect and disconnect events. For example: centrifuge.on('connect', function(connectCtx){ console.log('connected', connectCtx)});centrifuge.on('disconnect', function(disconnectCtx){ console.log('disconnected', disconnectCtx)});","s":"Connection lifecycle events","u":"/docs/4/getting-started/client_api","h":"#connection-lifecycle-events","p":1296},{"i":1307,"t":"Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel. centrifuge.subscribe('channel', function(messageCtx) { console.log(messageCtx);}) Client can subscribe to different channels. Subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events. In idiomatic case messages published to channels from application backend over Centrifugo server API. Though it's not always true. Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel properly configured. Client keeps last seen Publication offset and restores missed publications since known offset upon reconnecting. If recovery failed then client implementation provides a flag inside subscribe event to let the application know that some publications were missed – so you may need to load state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery in a dedicated chapter.","s":"Subscribe to a channel","u":"/docs/4/getting-started/client_api","h":"#subscribe-to-a-channel","p":1296},{"i":1309,"t":"To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance: centrifuge.on('publish', function(messageCtx) { console.log(messageCtx);}); It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.","s":"Server-side subscriptions","u":"/docs/4/getting-started/client_api","h":"#server-side-subscriptions","p":1296},{"i":1311,"t":"A client can send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the WebSocket or SockJS connection. RPC is only available when RPC proxy configured. const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"Send RPC","u":"/docs/4/getting-started/client_api","h":"#send-rpc","p":1296},{"i":1313,"t":"Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel: Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Call channel history","u":"/docs/4/getting-started/client_api","h":"#call-channel-history","p":1296},{"i":1315,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats","u":"/docs/4/getting-started/client_api","h":"#presence-and-presence-stats","p":1296},{"i":1317,"t":"On this page","s":"Main highlights","u":"/docs/4/getting-started/highlights","h":"","p":1316},{"i":1319,"t":"Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.). It works as a standalone service with well-defined communication contracts. It nicely fits both monolithic and microservice architectures. Application developers should not change backend philosophy and technology stack at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.","s":"Simple integration","u":"/docs/4/getting-started/highlights","h":"#simple-integration","p":1316},{"i":1321,"t":"Centrifugo is fast. It's written in Go language, built on top of fast and battle-tested open-source libraries, has some smart internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTT with broker, connection hub sharding to avoid lock contention, JSON and Protobuf encoding speedups over code generation and other. See a Million WebSocket with Centrifugo post in our blog to see some real-world numbers.","s":"Great performance","u":"/docs/4/getting-started/highlights","h":"#great-performance","p":1316},{"i":1323,"t":"Centrifugo scales well to many machines with a help of PUB/SUB brokers. So as soon as you have more client connections in the application – you can spread them over different Centrifugo nodes which will be connected together into a cluster. The main PUB/SUB engine Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck also. There are other options to scale: KeyDB, Nats, Tarantool. See docs.","s":"Built-in scalability","u":"/docs/4/getting-started/highlights","h":"#built-in-scalability","p":1316},{"i":1325,"t":"Centrifugo supports JSON and binary Protobuf protocol for client-server communication. The bidirectional protocol is defined by a strict schema and several ready-to-use SDKs wrap this protocol, handle asynchronous message passing, timeouts, reconnects, and various Centrifugo client API features. See the detailed information about client real-time transports in a dedicated section.","s":"Strict client protocol","u":"/docs/4/getting-started/highlights","h":"#strict-client-protocol","p":1316},{"i":1327,"t":"The main transport in Centrifugo is WebSocket. For browsers that do not support WebSocket Centrifugo provides its own bidirectional WebSocket emulation layer based on HTTP-streaming and SSE (EventSource), and also supports SockJS as an older but battle-tested WebSocket polyfill option, and WebTransport in experimental form. Centrifugo also supports unidirectional transports for real-time updates: like SSE (EventSource), HTTP streaming, GRPC unidirectional stream. Using unidirectional transport is sufficient for many real-time applications and does not require using our client SDKs – just native standards or GRPC-generated code. See the detailed information about client real-time transports in a dedicated section.","s":"Variety of real-time transports","u":"/docs/4/getting-started/highlights","h":"#variety-of-real-time-transports","p":1316},{"i":1329,"t":"Centrifugo can authenticate connections using JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon client connection to Centrifugo. It's possible to proxy original request headers or request metadata (in the case of GRPC connection). It supports the JWK specification.","s":"Flexible authentication","u":"/docs/4/getting-started/highlights","h":"#flexible-authentication","p":1316},{"i":1331,"t":"Connections can expire, developers can choose a way to handle connection refresh – using a client-side refresh workflow, or a server-side call from Centrifugo to the application backend.","s":"Connection management","u":"/docs/4/getting-started/highlights","h":"#connection-management","p":1316},{"i":1333,"t":"Centrifugo is a PUB/SUB server – users subscribe on channels to receive real-time updates. Message sent to a channel is delivered to all online channel subscribers.","s":"Channel (room) concept","u":"/docs/4/getting-started/highlights","h":"#channel-room-concept","p":1316},{"i":1335,"t":"Centrifugo supports client-side (initiated by a client) and server-side (forced by a server) channel subscriptions.","s":"Different types of subscriptions","u":"/docs/4/getting-started/highlights","h":"#different-types-of-subscriptions","p":1316},{"i":1337,"t":"You can fully utilize bidirectional connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing external traffic and, in most cases, provides better latency characteristics.","s":"RPC over bidirectional connection","u":"/docs/4/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":1316},{"i":1339,"t":"Online presence feature for channels provides information about active channel subscribers. Also, channel join and leave events (when someone subscribes/unsubscribes) can be received on the client side.","s":"Online presence information","u":"/docs/4/getting-started/highlights","h":"#online-presence-information","p":1316},{"i":1341,"t":"Optionally Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With a channel history, Centrifugo can help to survive the mass reconnect scenario – clients can automatically catch up missed state from a fast cache thus reducing the load on your primary database. It's also possible to manually iterate over a stream from a client or a server-side.","s":"Message history in channels","u":"/docs/4/getting-started/highlights","h":"#message-history-in-channels","p":1316},{"i":1343,"t":"Built-in admin UI allows publishing messages to channels, look at Centrifugo cluster information, and more.","s":"Embedded admin web UI","u":"/docs/4/getting-started/highlights","h":"#embedded-admin-web-ui","p":1316},{"i":1345,"t":"Centrifugo works on Linux, macOS, and Windows.","s":"Cross-platform","u":"/docs/4/getting-started/highlights","h":"#cross-platform","p":1316},{"i":1347,"t":"Centrifugo supports various deploy ways: in Docker, using prepared RPM or DEB packages, via Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, has an official Grafana dashboard for Prometheus data source.","s":"Ready to deploy","u":"/docs/4/getting-started/highlights","h":"#ready-to-deploy","p":1316},{"i":1349,"t":"Centrifugo stands on top of open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All our official client SDKs and API libraries are MIT-licensed.","s":"Open-source","u":"/docs/4/getting-started/highlights","h":"#open-source","p":1316},{"i":1351,"t":"Centrifugo PRO extends Centrifugo with several unique features which can give interesting advantages for business adopters. For additional details, refer to the Centrifugo PRO documentation.","s":"Pro features","u":"/docs/4/getting-started/highlights","h":"#pro-features","p":1316},{"i":1353,"t":"On this page","s":"Centrifugo introduction","u":"/docs/4/getting-started/introduction","h":"","p":1352},{"i":1355,"t":"Centrifugo was born a decade ago to help applications with a server-side written in a language or a framework without built-in concurrency support. In this case, dealing with persistent connections is a real headache that usually can only be resolved by introducing a shift in the technology stack and spending time to create a production-ready solution. For example, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others have poor or not really performant support of working with many persistent connections for the real-time messaging tasks. In this case, Centrifugo is a straightforward and non-obtrusive way to introduce real-time updates and handle lots of persistent connections without radical changes in the application backend architecture. Developers could proceed writing the application backend with a favorite language or favorite framework, keep existing architecture – and just let Centrifugo deal with persistent connections and be a real-time messaging transport layer. These days Centrifugo provides some advanced and unique features that can simplify a developer's life and save months of development. Even if the application backend is built with the asynchronous concurrent language. One example is that Centrifugo has built-in support for scalability to many machines to handle more connections and still making sure channel subscribers on different Centrifugo nodes receive all the publications. Centrifugo fits well modern architectures and may be a universal real-time component regardless of the application technology stack. There are more things to mention, the documentation uncovers them step by step.","s":"Background","u":"/docs/4/getting-started/introduction","h":"#background","p":1352},{"i":1357,"t":"On this page","s":"Migrating to v4","u":"/docs/4/getting-started/migration_v4","h":"","p":1356},{"i":1359,"t":"New generation of client protocol requires using the latest versions of client SDKs. During the next several days we will release the following SDK versions which are compatible with Centrifugo v4: centrifuge-js >= v3.0.0 centrifuge-go >= v0.9.0 centrifuge-dart >= v0.9.0 centrifuge-swift >= v0.5.0 centrifuge-java >= v0.2.0 New client SDKs support only new client protocol – you can not connect to Centrifugo v3 with them. If you have a production system where you want to upgrade Centrifugo from v3 to v4 then the plan is: danger If you are using private channels (starting with $) or user-limited channels (containing #) then carefully read about subscription token migration and user-limited channels migration below. Upgrade Centrifugo and its configuration to adopt changes in v4. In Centrifugo v4 config turn on use_client_protocol_v1_by_default. Run Centrifugo v4 – all current clients should continue working with it. Then on the client-side uprade client SDK version to the one which works with Centrifugo v4, adopt changes in SDK API dictated by our new client SDK API spec. Important thing – add ?cf_protocol_version=v2 URL param to the connection endpoint to tell Centrifugo that modern generation of protocol is being used by the connection (otherwise, it assumes old protocol since we have use_client_protocol_v1_by_default option enabled). As soon as all your clients migrated to use new protocol generation you can remove use_client_protocol_v1_by_default option from the server configuration. After that you can remove ?cf_protocol_version=v2 from connection endpoint on the client-side. tip If you are using mobile client SDKs then most probably some time must pass while clients update their apps to use an updated Centrifugo SDK version. tip Starting from Centrifugo v4.1.1 it's possible to completely turn off client protocol v1 by setting disable_client_protocol_v1 boolean option to true.","s":"Client SDK migration","u":"/docs/4/getting-started/migration_v4","h":"#client-sdk-migration","p":1356},{"i":1361,"t":"Client protocol framing also changed in unidirectional transports. The good news is that Centrifugo v4 still supports previous format for unidirectional transports. When you are enabling use_client_protocol_v1_by_default option described above you also make unidirectional transports to work over old protocol format. So your existing clients will continue working just fine with Centrifugo v4. Then the same steps to migrate described above can be applied to unidirectional transport case. The only difference that in unidirectional approach you are not using Centrifugo SDKs.","s":"Unidirectional transport migration","u":"/docs/4/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":1356},{"i":1363,"t":"SockJS is now DEPRECATED in Centrifugo. Centrifugo v4 may be the last release which supports it. We now offer our own bidirectional emulation layer on top of HTTP-streaming and EventSource. See additional information in Centrifugo v4 introduction post.","s":"SockJS migration","u":"/docs/4/getting-started/migration_v4","h":"#sockjs-migration","p":1356},{"i":1365,"t":"Centrifugo v2 and v3 docs mentioned the fact that channels must contain only ASCII characters. But it was not actually enforced by a server. Now Centrifugo is more strict. If a channel has non-ASCII characters then the 107: bad request error will be returned to the client. Please reach us out if this behavior is not suitable for your use case – we can discuss the use case and think on a proper solution together.","s":"Channel ASCII enforced","u":"/docs/4/getting-started/migration_v4","h":"#channel-ascii-enforced","p":1356},{"i":1367,"t":"Subscription token now requires sub claim (current user ID) to be set. In most cases the only change which is required to smoothly migrate to v4 without breaking things is to add a boolean option \"skip_user_check_in_subscription_token\": true to a Centrifugo v4 configuration. This skips the check of sub claim to contain the current user ID set to a connection during authentication. After that start adding sub claim (with current user ID) to subscription tokens. As soon as all subscription tokens in your system contain user ID in sub claim you can remove the skip_user_check_in_subscription_token from a server configuration. One more important note is that client claim in subscription token in Centrifugo v4 only supported for backwards compatibility. It must not be included into new subscription tokens. It's worth mentioning that Centrifugo v4 does not allow subscribing on channels starting with $ without token even if namespace marked as available for subscribing using sth like allow_subscribe_for_client option. This is done to prevent potential security risk during v3 -> v4 migration when client previously not available to subscribe to channels starting with $ in any case may get permissions to do so.","s":"Subscription token migration","u":"/docs/4/getting-started/migration_v4","h":"#subscription-token-migration","p":1356},{"i":1369,"t":"User-limited channel support should now be allowed over a separate channel namespace option allow_user_limited_channels. See below the namespace option converter which takes this change into account.","s":"User-limited channel migration","u":"/docs/4/getting-started/migration_v4","h":"#user-limited-channel-migration","p":1356},{"i":1371,"t":"In Centrifugo v4 namespace configuration options have been changed. Centrifugo now has secure by default namespaces. First thing to do is to read the new docs about channels and namespaces. Then you can use the following converter which will transform your old namespace configuration to a new one. This converter tries to keep backwards compatibility – i.e. it should be possible to deploy Centrifugo with namespace configuration from converter output and have the same behaviour as before regarding channel permissions. We believe that new option names should provide a more readable configuration and may help to reveal some potential security improvements in your namespace configuration – i.e. making it more strict and protective. caution Do not blindly deploy things to production – test your system first, go through the possible usage scenarios and/or test cases. tip It's fully client-side: your data won't be sent anywhere. Convert Here will be configuration for v4 Here will be log of changes made in your config","s":"Namespace configuration migration","u":"/docs/4/getting-started/migration_v4","h":"#namespace-configuration-migration","p":1356},{"i":1373,"t":"reconnect flag from custom disconnect code is removed. Reconnect advice is now determined by disconnect code value. This allowed us avoiding using JSON in WebSocket CLOSE frame reason. See proxy docs docs for more details.","s":"Proxy disconnect code changes","u":"/docs/4/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":1356},{"i":1375,"t":"Several other non-namespace related options have been renamed or removed: client_anonymous option renamed to allow_anonymous_connect_without_token – new name better describes the purpose of this option which was previously not clear. Converter above takes this into account. use_unlimited_history_by_default option was removed. It was used to help migrating from Centrifugo v2 to v3.","s":"Other configuration option changes","u":"/docs/4/getting-started/migration_v4","h":"#other-configuration-option-changes","p":1356},{"i":1377,"t":"The only breaking change is that user_connections API method (which is available in Centrifugo PRO only) was renamed to connections. The method is more generic now with a broader possibilities – so previous name does not match the current behavior.","s":"Server API changes","u":"/docs/4/getting-started/migration_v4","h":"#server-api-changes","p":1356},{"i":1379,"t":"On this page","s":"Integration guide","u":"/docs/4/getting-started/integration","h":"","p":1378},{"i":1381,"t":"First, you need to do is download/install Centrifugo server. See install chapter for details.","s":"0. Install","u":"/docs/4/getting-started/integration","h":"#0-install","p":1378},{"i":1383,"t":"Create basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and chapter about server API for API description. The simplest way to do this automatically is by using genconfig command: ./centrifugo genconfig – which will generate config.json file for you with a minimal set of fields to start from. Properly configure allowed_origins option.","s":"1. Configure Centrifugo","u":"/docs/4/getting-started/integration","h":"#1-configure-centrifugo","p":1378},{"i":1385,"t":"In the configuration file of your application backend register several variables: Centrifugo token secret (if you decided to stick with JWT authentication) and Centrifugo API key you set on a previous step, also Centrifugo API endpoint address. By default, the API address is http://localhost:8000/api. You must never reveal token secret and API key to your users.","s":"2. Configure your backend","u":"/docs/4/getting-started/integration","h":"#2-configure-your-backend","p":1378},{"i":1387,"t":"Now your users can start connecting to Centrifugo. You should get a client library (see list of available client SDKs) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here. Every client should provide a connection token (JWT) on connect. You must generate this token on your backend side using Centrifugo secret key you set to backend configuration (note that in the case of RSA tokens you are generating JWT with a private key). See how to generate this JWT in special chapter. You pass this token from the backend to your frontend app (pass it in template context or use separate request from client-side to get user-specific JWT from backend side). And use this token when connecting to Centrifugo (for example browser client has a special method setToken). There is also a way to authenticate connections without using JWT - see chapter about proxying to backend. You are connecting to Centrifugo using one of the available transports.","s":"3. Connect to Centrifugo","u":"/docs/4/getting-started/integration","h":"#3-connect-to-centrifugo","p":1378},{"i":1389,"t":"After connecting to Centrifugo subscribe clients to channels they are interested in. See more about channels in special chapter. All bidirectional client SDKs provide a way to handle messages coming to a client from a channel after subscribing to it. Learn more about client SDK possibilities from client SDK API spec. There is also a way to subscribe connection to a list of channels on the server side at the moment of connection establishment. See chapter about server-side subscriptions.","s":"4. Subscribe to channels","u":"/docs/4/getting-started/integration","h":"#4-subscribe-to-channels","p":1378},{"i":1391,"t":"Everything should work now – as soon as a user opens some page of your application it must successfully connect to Centrifugo and subscribe to a channel (or channels). Now let's imagine you want to send a real-time message to users subscribed on a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, the administrator just created a new post, the user pressed the \"like\" button, etc. Anyway, this is an event your backend just got, and you want to immediately send it to interested users. You can do this using Centrifugo HTTP API. To simplify your life we have several API libraries for different languages. You can publish messages into a channel using one of those libraries or you can simply follow API description to construct API requests yourself - this is very simple. Also Centrifugo supports GRPC API. As soon as you published a message to the channel it must be delivered to your online client subscribed to that channel.","s":"5. Publish to channel","u":"/docs/4/getting-started/integration","h":"#5-publish-to-channel","p":1378},{"i":1393,"t":"To put this all into production you need to deploy Centrifugo on your production server. To help you with this we have many things like Docker image, rpm and deb packages, Nginx configuration. See Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.","s":"6. Deploy to production","u":"/docs/4/getting-started/integration","h":"#6-deploy-to-production","p":1378},{"i":1395,"t":"Don't forget to configure metrics monitoring your production Centrifugo setup. This may help you to understand what's going on with Centrifugo setup, understand number of connections, operation count and latency distributions, etc.","s":"7. Monitor Centrifugo","u":"/docs/4/getting-started/integration","h":"#7-monitor-centrifugo","p":1378},{"i":1397,"t":"As soon as you are close to machine resource limits you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using Redis engine, or with KeyDB, or with Tarantool, or with Nats broker. Engines and scalability chapter describes available options in detail.","s":"8. Scale Centrifugo","u":"/docs/4/getting-started/integration","h":"#8-scale-centrifugo","p":1378},{"i":1399,"t":"That's all for basics. The documentation actually covers lots of other concepts Centrifugo server has: scalability, private channels, admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ – it contains lot of useful information.","s":"9. Read FAQ","u":"/docs/4/getting-started/integration","h":"#9-read-faq","p":1378},{"i":1401,"t":"On this page","s":"Client protocol","u":"/docs/3/transports/client_protocol","h":"","p":1400},{"i":1403,"t":"First we will look at list of features bidirectional client library should support. If you are an author of client library you can use this list as a checklist. Our current client feature matrix looks like this: connect to server (both Centrifugo and Centrifuge-based) using JSON protocol format connect to server (both Centrifugo and Centrifuge-based) using Protobuf protocol format connect with token (JWT in Centrifugo case, any string token in Centrifuge library case) connect to server with custom headers (not available in a browser) automatic reconnect in case of connection problems (server restart, unavailable network) an exponential backoff for reconnect process possibility to set handlers for connect and disconnect events extract and expose disconnect code and reason subscribe to a channel and provide a way to handle asynchronous Publications coming from it handle Join and Leave messages from a channel handle Unsubscribe notifications provide publish method of Subscription object provide unsubscribe method of Subscription provide presence method of Subscription provide presence stats method of Subscription provide history method of Subscription provide publish method on top level provide unsubscribe method on top level provide presence method on top level provide presence stats method on top level provide history method on top level send asynchronous messages to server handle asynchronous messages from server send RPC requests to server publish to channel without being subscribed subscribe to private (token-protected) channels with a token implement client-side connection token refresh mechanism implement private channel subscription token refresh mechanism client protocol level ping/pong to find a broken connection automatic reconnect in case of connect or subscribe command timeouts handle connection expired error handle subscription expired error server-side subscriptions message recovery mechanism for client-side subscriptions message recovery mechanism for server-side subscriptions This document describes protocol specifics for Websocket transport which supports binary and text formats to transfer data. As Centrifugo and Centrifuge library for Go have various types of messages it serializes protocol messages using JSON or Protobuf formats. info SockJS works almost the same way as JSON websocket described here but has its own extra framing on top of Centrifuge protocol messages. SockJS can only work with JSON - it's not possible to transfer binary data over it.","s":"Client implementation feature matrix","u":"/docs/3/transports/client_protocol","h":"#client-implementation-feature-matrix","p":1400},{"i":1405,"t":"Centrifuge protocol defined in Protobuf schema. That schema is a source of the truth. Below we describe messages from that schema. In bidirectional case client sends Command to server and server sends Reply to client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages. One request from client to server and one response from server to client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data. When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \\n: {\"id\": 1, \"method\": 1, \"params\": {\"channel\": \"ch1\"}}{\"id\": 2, \"method\": 1, \"params\": {\"channel\": \"ch2\"}} For example here is how we do this in Javascript client when JSON format used: function encodeCommands(commands) { const encodedCommands = []; for (const i in commands) { if (commands.hasOwnProperty(i)) { encodedCommands.push(JSON.stringify(commands[i])); } } return encodedCommands.join('\\n');} info This doc will use JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo. When Protobuf format used then many Command can be sent from client to server in length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example. The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there. For example here is how we read server response and extracting individual replies in Javascript client when JSON format used: function decodeReplies(data) { const replies = []; const encodedReplies = data.split('\\n'); for (const i in encodedReplies) { if (encodedReplies.hasOwnProperty(i)) { if (!encodedReplies[i]) { continue; } const reply = JSON.parse(encodedReplies[i]); replies.push(reply); } } return replies;} For Protobuf case see existing client implementations for decoding example. As you can see each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous protocol where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before. So you can expect something like this in response after sending commands to server: {\"id\": 1, \"result\": {}}{\"id\": 2, \"result\": {}} Besides id Reply from server to client have two important fields: result and error. result contains useful payload object which is different for various Reply messages. error contains error description if Command processing resulted in some error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can parse result object appropriately. error looks like this in JSON case: { \"code\": 100, \"message\": \"internal server error\"} We will talk more about error handling below. The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to client in any moment - not as reaction to issued Command but as message from server to client in arbitrary time. For example this can be a message published into channel. Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow. Communication with Centrifuge/Centrifugo server starts with issuing connect command.","s":"Top level framing","u":"/docs/3/transports/client_protocol","h":"#top-level-framing","p":1400},{"i":1407,"t":"First of all client must dial with a server and then send connect Command to it. Default Websocket endpoint in Centrifugo is: ws://centrifugo.example.com/connection/websocket In case of using TLS: wss://centrifugo.example.com/connection/websocket After a successful dial to WebSocket endpoint client must send connect command to server to authorize itself. connect command looks like: { \"id\": 1, \"method\": 0, \"params\": { \"token\": \"JWT\", \"data\": {} }} All methods defined in Protobuf schema: message Command { uint32 id = 1; enum MethodType { CONNECT = 0; SUBSCRIBE = 1; UNSUBSCRIBE = 2; PUBLISH = 3; PRESENCE = 4; PRESENCE_STATS = 5; HISTORY = 6; PING = 7; SEND = 8; RPC = 9; REFRESH = 10; SUB_REFRESH = 11; } MethodType method = 2; bytes params = 3;} So here we are using a enum value for CONNECT (0). Params fields: optional string token - connection token. Can be omitted if token-based auth not used. data - can contain custom connect data, for example it can contain client settings. In response to connect command server sends a connect reply. It looks this way: { \"id\": 1, \"result\":{ \"client\": \"421bf374-dd01-4f82-9def-8c31697e956f\", \"version\": \"2.0.0\" }} result has some fields: string client - unique client connection ID server issued to this connection string version - server version optional bool expires - whether a server will expire connection at some point optional int32 ttl - time in seconds until connection expires","s":"Connect","u":"/docs/3/transports/client_protocol","h":"#connect","p":1400},{"i":1409,"t":"As soon as client successfully connected and got unique connection ID it is ready to subscribe on channels. To do this it must send subscribe command to server: { \"id\": 2, \"method\": 1, \"params\": { \"channel\": \"ch1\" }} Fields that can be set in params are: string channel - channel to subscribe In response to subscribe a client receives reply like: { \"id\": 2, \"result\": {}} result can have the following fields that relate to subscription expiration: optional bool expires - indicates whether subscription expires or not. optional uint32 ttl - number of seconds until subscription expire. Also several fields that relate to message recovery: optional bool recoverable - means that messages can be recovered in this subscription. optional uint64 offset - current publication offset inside channel optional string epoch - current epoch inside channel optional array publications - this is an array of missed publications in channel. When received client must call general publication event handler for each message in this array. optional bool recovered - this flag set to true when server thinks that all missed publications successfully recovered and send in subscribe reply (in publications array) and false otherwise. See more about meaning of recovery related fields in special doc chapter. After a client received a successful reply on subscribe command it will receive asynchronous reply messages published to this channel. Messages can be of several types: Publication message Join message Leave message Unsubscribe message See more about asynchronous messages below.","s":"Subscribe","u":"/docs/3/transports/client_protocol","h":"#subscribe","p":1400},{"i":1411,"t":"When client wants to unsubscribe from a channel and therefore stop receiving asynchronous subscription messages from connection related to channel it must call unsubscribe command: { \"id\": 3, \"method\": 2, \"params\": { \"channel\": \"ch1\" }} Actually server response does not mean a lot for a client - it must immediately remove channel subscription from internal implementation data structures and ignore all messages related to channel.","s":"Unsubscribe","u":"/docs/3/transports/client_protocol","h":"#unsubscribe","p":1400},{"i":1413,"t":"It's possible to turn on client connection expiration mechanism on a server. While enabled server will keep track of connections whose time of life is close to the end (connection lifetime set on connection authentication phase). In this case connection will be closed. Client can prevent closing connection refreshing its connection credentials. To do this it must send refresh command to server. refresh command is similar to connect: { \"id\": 4, \"method\": 10, \"params\": { \"token\": \"\" }} The tip whether a connection must be refreshed by a client comes in reply to connect command shown above - fields expires and ttl. When client connection expire mechanism is on the value of field expires in connect reply is true. In this case client implementation should look at ttl value which is seconds left until connection will be considered expired. Client must send refresh command after this ttl seconds. Server gives client a configured window to refresh token after ttl passed and then closes connection if client have not updated its token. When connecting with already expired token an error will be returned (with code 109). In this case client should refresh its token and reconnect with exponential backoff.","s":"Refresh","u":"/docs/3/transports/client_protocol","h":"#refresh","p":1400},{"i":1415,"t":"The mechanics of these calls is simple - client sends command and expects response from server. publish command allows to publish a message into a channel from a client. tip To publish from client publish option in Centrifugo configuration must be set to true history allows asking a server for channel history if enabled. presence allows asking a server for channel presence information if enabled. presence_stats allows asking for short presence info (num clients and unique users in a channel).","s":"RPC-like calls: publish, history, presence","u":"/docs/3/transports/client_protocol","h":"#rpc-like-calls-publish-history-presence","p":1400},{"i":1417,"t":"There are several types of asynchronous messages that can come from a server to a client. All of them relate to the current client subscriptions. The most important message is Publication: { \"result\":{ \"channel\":\"ch1\", \"data\":{ \"data\":{\"input\":\"1\"}, \"info\":{ \"user\":\"2694\", \"client\":\"5c48510e-cf49-4fa8-a9b2-490b22231e74\", \"conn_info\":{\"name\":\"Alexander\"}, \"chan_info\":{} } } }} Publication is a message published into channel. Note that there is no id field in this message - this symptom allows to distinguish it from Reply to Command. Next message is Join message: { \"result\":{ \"type\":1, \"channel\":\"ch1\", \"data\":{ \"info\":{ \"user\":\"2694\", \"client\":\"5c48510e-cf49-4fa8-a9b2-490b22231e74\", \"conn_info\":{\"name\":\"Alexander\"}, \"chan_info\":{} } } }} Join messages sent when someone joined (subscribed on) channel. tip To enable Join and Leave messages join_leave option must be enabled in Centrifugo for a channel namespace. Leave messages sent when someone left (unsubscribed from) channel. { \"result\":{ \"type\":2, \"channel\":\"ch1\", \"data\":{ \"info\":{ \"user\":\"2694\", \"client\":\"5c48510e-cf49-4fa8-a9b2-490b22231e74\", \"conn_info\":{\"name\":\"Alexander\"}, \"chan_info\":{} } } }} Finally Unsubscribe message that means that server unsubscribed current client from a channel: { \"result\":{ \"type\":3, \"channel\":\"ch1\", \"data\":{} }} It's possible to distinguish between different types of asynchronous messages looking at type field (for Publication this field not set or 0).","s":"Asynchronous server-to-client messages","u":"/docs/3/transports/client_protocol","h":"#asynchronous-server-to-client-messages","p":1400},{"i":1419,"t":"To maintain connection alive and detect broken connections client must periodically send ping commands to server and expect replies to it. Ping command looks like: { \"id\":32, \"method\":\"ping\"} Server just echoes this command back. When client does not receive ping reply for some time it must consider connection broken and try to reconnect. Recommended ping interval is 25 seconds, recommended period to wait for pong is 1-5 seconds. Though those numbers can vary.","s":"Ping Pong","u":"/docs/3/transports/client_protocol","h":"#ping-pong","p":1400},{"i":1421,"t":"Client should handle disconnect advices from server. In websocket case disconnect advice is sent in reason field of CLOSE Websocket frame. Reason contains string which is disconnect object encoded into JSON (even in case of Protobuf scenario). That objects looks like: { \"reason\": \"shutdown\", \"reconnect\": true } It contains string reason of connection closing and advice to reconnect or not. Client should take this reconnect advice into account. In case of network problems and random disconnect from server without well known reason client should always try to reconnect with exponential intervals.","s":"Handle disconnects","u":"/docs/3/transports/client_protocol","h":"#handle-disconnects","p":1400},{"i":1423,"t":"This section contains advices to error handling in client implementations. Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems. Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again. Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side. The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance. Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).","s":"Handle errors","u":"/docs/3/transports/client_protocol","h":"#handle-errors","p":1400},{"i":1425,"t":"Here are some advices about client public API. Examples here are in Javascript language. This is just an attempt to help in developing a client - but rules here is not obligatorily the best way to implement client. Create client instance: const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", {}); Set connection token (in case of using Centrifugo): centrifuge.setToken(\"XXX\") Connect to server: centrifuge.connect(); 2 event handlers can be set to centrifuge object: connect and disconnect centrifuge.on('connect', function(context) { console.log(context);});centrifuge.on('disconnect', function(context) { console.log(context);}); Client created in disconnected state with reconnect attribute set to true and reconnecting flag set to false . After connect() called state goes to connecting. It's only possible to connect from disconnected state. Every time connect() called reconnect flag of client must be set to true. After each failed connect attempt state must be set to disconnected, disconnect event must be emitted (only if reconnecting flag is false), and then reconnecting flag must be set to true (if client should continue reconnecting) to not emit disconnect event again after next in a row connect attempt failure. In case of failure next connection attempt must be scheduled automatically with backoff strategy. On successful connect reconnecting flag must be set to false, backoff retry must be reset and connect event must be emitted. When connection lost then the same set of actions as when connect failed must be performed. Client must allow to subscribe on channels: var subscription = centrifuge.subscribe(\"channel\", eventHandlers); Subscription object created and control immediately returned to caller - subscribing must be performed asynchronously. This is required because client can automatically reconnect later so event-based model better suites for subscriptions. Subscription should support several event handlers: handler for publication received from channel join message handler leave message handler error handler subscribe success event handler unsubscribe event handler Every time client connects to server it must restore all subscriptions. Every time client disconnects from server it must call unsubscribe handlers for all active subscriptions and then emit disconnect event. Client must periodically (once in 25 secs, configurable) send ping messages to server. If pong has not beed received in 5 secs (configurable) then client must disconnect from server and try to reconnect with backoff strategy. Client can automatically batch several requests into one frame to server and also must be able to handle several replies received from server in one frame.","s":"Client implementation advices","u":"/docs/3/transports/client_protocol","h":"#client-implementation-advices","p":1400},{"i":1427,"t":"It's also possible to subscribe connection to channels on server side. In this case we call this server-side subscription. Client should only handle asynchronous messages coming from a server without need to create subscriptions on client side. SSS should be kept separate from client-side subs SSS requires new event handlers on top-level of Client - Subscribe, Publish, Join, Leave, Unsubscribe, event handlers will be called with event context similar to client-side subs case but with channel field Connect Reply contains SSS set by a server on connect, on reconnect client has a chance to recover missed Publications Server side subscription can happen at any moment - Sub Push will be sent to client","s":"Server side subscriptions (SSS)","u":"/docs/3/transports/client_protocol","h":"#server-side-subscriptions-sss","p":1400},{"i":1429,"t":"Client should automatically recover messages after being disconnected due to network problems and set appropriate fields in subscribe event context. Two important fields in onSubscribeSuccess event context are isRecovered and isResubscribe. First field let user know what server thinks about subscription state - were all messages recovered or not. The second field must only be true if resubscribe was caused by temporary network connection lost. If user initiated resubscribe himself (calling unsubscribe method and then subscribe method) then recover workflow should not be used and isResubscribe must be false.","s":"Message recovery","u":"/docs/3/transports/client_protocol","h":"#message-recovery","p":1400},{"i":1431,"t":"In case of Websocket it is sent by server in CLOSE Websocket frame. This is a string containing JSON object with fields: reason (string) and reconnect (bool). Client should give users access to these fields in disconnect event and automatically follow reconnect advice.","s":"Disconnect code and reason","u":"/docs/3/transports/client_protocol","h":"#disconnect-code-and-reason","p":1400},{"i":1433,"t":"Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in reply to a subscribe command.","s":"Additional notes","u":"/docs/3/transports/client_protocol","h":"#additional-notes","p":1400},{"i":1435,"t":"On this page","s":"Install Centrifugo","u":"/docs/4/getting-started/installation","h":"","p":1434},{"i":1437,"t":"For a local development you can download prebuilt Centrifugo binary release (i.e. single all-contained executable file) for your system. Binary releases available on Github. Download latest release for your operating system, unpack it and you are done. Centrifugo is pre-built for: Linux 64-bit (linux_amd64) Linux 32-bit (linux_386) Linux ARM 64-bit (linux_arm64) MacOS (darwin_amd64) MacOS on Apple Silicon (darwin_arm64) Windows (windows_amd64) FreeBSD (freebsd_amd64) ARM v6 (linux_armv6) Archives contain a single statically compiled binary centrifugo file that is ready to run: ./centrifugo If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack centrifugo binary to your current working directory: curl -sSLf https://centrifugal.dev/install.sh | sh See the version of Centrifugo: ./centrifugo version Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo then there is genconfig command which generates a minimal configuration file to get started: ./centrifugo genconfig It creates a configuration file config.json with some auto-generated option values in a current directory (by default). tip It's possible to generate file in YAML or TOML format, i.e. ./centrifugo genconfig -c config.toml Having a configuration file you can finally run Centrifugo instance: ./centrifugo --config=config.json We will talk about a configuration in detail in the next sections. You can also put or symlink centrifugo into your bin OS directory and run it from anywhere: centrifugo --config=config.json","s":"Install from the binary release","u":"/docs/4/getting-started/installation","h":"#install-from-the-binary-release","p":1434},{"i":1439,"t":"Centrifugo server has a docker image available on Docker Hub. docker pull centrifugo/centrifugo Run: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo centrifugo -c config.json Note that docker allows setting nofile limits in command-line arguments which is pretty important to handle lots of simultaneous persistent connections and not run out of open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter. caution Pin to the exact Docker Image tag in production, for example: centrifugo/centrifugo:v4.0.0, this will help to avoid unexpected problems during re-deploy process.","s":"Docker image","u":"/docs/4/getting-started/installation","h":"#docker-image","p":1434},{"i":1441,"t":"Create configuration file config.json: { \"token_hmac_secret_key\": \"my_secret\", \"api_key\": \"my_api_key\", \"admin_password\": \"password\", \"admin_secret\": \"secret\", \"admin\": true} Create docker-compose.yml: version: \"3.9\"services: centrifugo: container_name: centrifugo image: centrifugo/centrifugo:v4 volumes: - ./config.json:/centrifugo/config.json command: centrifugo -c config.json ports: - 8000:8000 ulimits: nofile: soft: 65535 hard: 65535 Run with: docker-compose up","s":"Docker-compose example","u":"/docs/4/getting-started/installation","h":"#docker-compose-example","p":1434},{"i":1443,"t":"See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.","s":"Kubernetes Helm chart","u":"/docs/4/getting-started/installation","h":"#kubernetes-helm-chart","p":1434},{"i":1445,"t":"Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io. At moment, we support versions of the following distributions: 64-bit Debian 8 Jessie 64-bit Debian 9 Stretch 64-bit Debian 10 Buster 64-bit Debian 11 Bullseye 64-bit Ubuntu 16.04 Xenial 64-bit Ubuntu 18.04 Bionic 64-bit Ubuntu 20.04 Focal Fossa 64-bit Centos 7 64-bit Centos 8 See full list of available packages and installation instructions. Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.","s":"RPM and DEB packages for Linux","u":"/docs/4/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":1434},{"i":1447,"t":"If you are developing on macOS then you can install Centrifugo over brew: brew tap centrifugal/centrifugobrew install centrifugo","s":"With brew on macOS","u":"/docs/4/getting-started/installation","h":"#with-brew-on-macos","p":1434},{"i":1449,"t":"You need Go language installed: git clone https://github.com/centrifugal/centrifugo.gitcd centrifugogo build./centrifugo","s":"Build from source","u":"/docs/4/getting-started/installation","h":"#build-from-source","p":1434},{"i":1451,"t":"On this page","s":"Server API","u":"/docs/3/server/server_api","h":"","p":1450},{"i":1453,"t":"Server HTTP API works on /api endpoint (by default). It has a simple request format: this is an HTTP POST request with application/json Content-Type and with JSON command body. Here we will look at available methods and parameters tip In some cases, you can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests.","s":"HTTP API","u":"/docs/3/server/server_api","h":"#http-api","p":1450},{"i":1455,"t":"HTTP API protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like: config.json { ... \"api_key\": \"\"} This API key must be set in the request Authorization header in this way: Authorization: apikey It's also possible to pass API key over URL query param. This solves some edge cases where it's not possible to use the Authorization header. Simply add ?api_key= query param to the API endpoint. Keep in mind that passing the API key in the Authorization header is a recommended way. It's possible to disable API key check on Centrifugo side using the api_insecure configuration option. Be sure to protect the API endpoint by firewall rules, in this case, to prevent anyone on the internet to send commands over your unprotected Centrifugo API endpoint. API key auth is not very safe for man-in-the-middle so we also recommended running Centrifugo with TLS. A command is a JSON object with two properties: method and params. method is the name of the API command you want to call. params is an object with command arguments. Each method can have its own params Before looking at all available commands here is a CURL that calls info command: curl --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"info\", \"params\": {}}' \\ http://localhost:8000/api Here is a live example: Your browser does not support the video tag. Now let's investigate each API method in detail.","s":"HTTP API authorization","u":"/docs/3/server/server_api","h":"#http-api-authorization","p":1450},{"i":1457,"t":"Publish command allows publishing data into a channel. Most probably this is a command you'll use most. It looks like this: { \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } } } Let's apply all information said above and send publish command to Centrifugo. We will send a request using the requests library for Python. import jsonimport requestscommand = { \"method\": \"publish\", \"params\": { \"channel\": \"docs\", \"data\": { \"content\": \"1\" } }}api_key = \"YOUR_API_KEY\"data = json.dumps(command)headers = {'Content-type': 'application/json', 'Authorization': 'apikey ' + api_key}resp = requests.post(\"https://centrifuge.example.com/api\", data=data, headers=headers)print(resp.json()) The same using httpie console tool: echo '{\"method\": \"publish\", \"params\": {\"channel\": \"chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \" -vvvPOST /api HTTP/1.1Accept: application/json, */*Accept-Encoding: gzip, deflateAuthorization: apikey KEYConnection: keep-aliveContent-Length: 80Content-Type: application/jsonHost: localhost:8000User-Agent: HTTPie/0.9.8{ \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } }}HTTP/1.1 200 OKContent-Length: 3Content-Type: application/jsonDate: Thu, 17 May 2018 22:01:42 GMT{ \"result\": {}} In case of error response object can contain error field (here we artificially publishing to a channel with unknown namespace): echo '{\"method\": \"publish\", \"params\": {\"channel\": \"unknown:chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \"HTTP/1.1 200 OKContent-Length: 55Content-Type: application/jsonDate: Thu, 17 May 2018 22:03:09 GMT{ \"error\": { \"code\": 102, \"message\": \"namespace not found\" }} error object contains error code and message - this is also the same for other commands described below. Publish params​ Parameter name Parameter type Required Description channel string yes Name of channel to publish data any JSON yes Custom JSON data to publish into a channel skip_history bool no Skip adding publication to history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients (available since v3.2.0) Publish result​ Field name Field type Optional Description offset integer yes Offset of publication in history stream epoch string yes Epoch of current stream","s":"publish","u":"/docs/3/server/server_api","h":"#publish","p":1450},{"i":1459,"t":"Similar to publish but allows to send the same data into many channels. { \"method\": \"broadcast\", \"params\": { \"channels\": [\"CHANNEL_1\", \"CHANNEL_2\"], \"data\": { \"text\": \"hello\" } }} Broadcast params​ Parameter name Parameter type Required Description channels Array of strings yes List of channels to publish data to data any JSON yes Custom JSON data to publish into each channel skip_history bool no Skip adding publications to channels' history for this request tags map[string]string no Publication tags (available since v3.2.0) - map with arbitrary string keys and values which is attached to publication and will be delivered to clients Broadcast result​ Field name Field type Optional Description responses Array of publish responses no Responses for each individual publish (with possible error and publish result)","s":"broadcast","u":"/docs/3/server/server_api","h":"#broadcast","p":1450},{"i":1461,"t":"subscribe allows subscribing user to a channel. Subscribe params​ Parameter name Parameter type Required Description user string yes User ID to subscribe channel string yes Name of channel to subscribe user to info any JSON no Attach custom data to subscription (will be used in presence and join/leave messages) b64info string no info in base64 for binary mode (will be decoded by Centrifugo) client string no Specific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs) session string no Specific client session to subscribe (user still required to be set). Available since Centrifugo v3.2.0 data any JSON no Custom subscription data (will be sent to client in Subscribe push) b64data string no Same as data but in base64 format (will be decoded by Centrifugo) recover_since StreamPosition object no Stream position to recover from override Override object no Allows dynamically override some channel options defined in Centrifugo configuration (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false} Subscribe result​ Empty object at the moment.","s":"subscribe","u":"/docs/3/server/server_api","h":"#subscribe","p":1450},{"i":1463,"t":"unsubscribe allows unsubscribing user from a channel. { \"method\": \"unsubscribe\", \"params\": { \"channel\": \"CHANNEL NAME\", \"user\": \"USER ID\" }} Unsubscribe params​ Parameter name Parameter type Required Description user string yes User ID to unsubscribe channel string yes Name of channel to unsubscribe user to client string no Specific client ID to unsubscribe (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Available since Centrifugo v3.2.0 Unsubscribe result​ Empty object at the moment.","s":"unsubscribe","u":"/docs/3/server/server_api","h":"#unsubscribe","p":1450},{"i":1465,"t":"disconnect allows disconnecting a user by ID. { \"method\": \"disconnect\", \"params\": { \"user\": \"USER ID\" }} Disconnect params​ Parameter name Parameter type Required Description user string yes User ID to disconnect client string no Specific client ID to disconnect (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Available since Centrifugo v3.2.0 whitelist Array of strings no Array of client IDs to keep disconnect Disconnect object no Provide custom disconnect object, see below Disconnect object​ Field name Field type Required Description code int yes Disconnect code reason string yes Disconnect reason reconnect bool no Reconnect advice Disconnect result​ Empty object at the moment.","s":"disconnect","u":"/docs/3/server/server_api","h":"#disconnect","p":1450},{"i":1467,"t":"refresh allows refreshing user connection (mostly useful when unidirectional transports are used). Refresh params​ Parameter name Parameter type Required Description user string yes User ID to refresh client string no Client ID to refresh (user still required to be set) session string no Specific client session to refresh (user still required to be set). Available since Centrifugo v3.2.0 expired bool no Mark connection as expired and close with Disconnect Expired reason expire_at int no Unix time (in seconds) in the future when the connection will expire Refresh result​ Empty object at the moment.","s":"refresh","u":"/docs/3/server/server_api","h":"#refresh","p":1450},{"i":1469,"t":"presence allows getting channel online presence information (all clients currently subscribed on this channel). tip Presence in channels is not enabled by default. See how to enable it over channel options. { \"method\": \"presence\", \"params\": { \"channel\": \"chat\" }} Example: fz@centrifugo: echo '{\"method\": \"presence\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 127Content-Type: application/jsonDate: Thu, 17 May 2018 22:13:17 GMT{ \"result\": { \"presence\": { \"c54313b2-0442-499a-a70c-051f8588020f\": { \"client\": \"c54313b2-0442-499a-a70c-051f8588020f\", \"user\": \"42\" }, \"adad13b1-0442-499a-a70c-051f858802da\": { \"client\": \"adad13b1-0442-499a-a70c-051f858802da\", \"user\": \"42\" } } }} Presence params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence result​ Field name Field type Optional Description presence Map of client ID (string) to ClientInfo object no Offset of publication in history stream ClientInfo​ Field name Field type Optional Description client string no Client ID user string no User ID conn_info JSON yes Optional connection info chan_info JSON yes Optional channel info","s":"presence","u":"/docs/3/server/server_api","h":"#presence","p":1450},{"i":1471,"t":"presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID). { \"method\": \"presence_stats\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"presence_stats\", \"params\": {\"channel\": \"public:chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": { \"num_clients\": 0, \"num_users\": 0 }} Presence stats params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence stats result​ Field name Field type Optional Description num_clients integer no Total number of clients in channel num_users integer no Total number of unique users in channel","s":"presence_stats","u":"/docs/3/server/server_api","h":"#presence_stats","p":1450},{"i":1473,"t":"history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter. tip History in channels is not enabled by default. See how to enable it over channel options. { \"method\": \"history\", \"params\": { \"channel\": \"chat\", \"limit\": 2 }} Example: echo '{\"method\": \"history\", \"params\": {\"channel\": \"chat\", \"limit\": 2}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 129Content-Type: application/jsonDate: Wed, 21 Jul 2021 05:30:48 GMT{ \"result\": { \"epoch\": \"qFhv\", \"offset\": 4, \"publications\": [ { \"data\": { \"text\": \"hello\" }, \"offset\": 2 }, { \"data\": { \"text\": \"hello\" }, \"offset\": 3 } ] }} History params​ Parameter name Parameter type Required Description channel string yes Name of channel to call history from limit int no Limit number of returned publications, if not set in request then only current stream position information will present in result (without any publications) since StreamPosition object no To return publications after this position reverse bool no Iterate in reversed order (from latest to earliest) StreamPosition​ Field name Field type Required Description offset integer yes Offset in a stream epoch string yes Stream epoch History result​ Field name Field type Optional Description publications Array of publication objects yes List of publications in channel offset integer yes Top offset in history stream epoch string yes Epoch of current stream","s":"history","u":"/docs/3/server/server_api","h":"#history","p":1450},{"i":1475,"t":"history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state. { \"method\": \"history_remove\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"history_remove\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": {}} History remove params​ Parameter name Parameter type Required Description channel string yes Name of channel to remove history History remove result​ Empty object at the moment.","s":"history_remove","u":"/docs/3/server/server_api","h":"#history_remove","p":1450},{"i":1477,"t":"channels return active channels (with one or more active subscribers in it). { \"method\": \"channels\", \"params\": {}} Channels params​ Parameter name Parameter type Required Description pattern string no Pattern to filter channels Channels result​ Field name Field type Optional Description channels Map of string to ChannelInfo no Map where key is channel and value is ChannelInfo (see below) ChannelInfo​ Field name Field type Optional Description num_clients integer no Total number of connections currently subscribed to a channel caution Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels RPC extension will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.","s":"channels","u":"/docs/3/server/server_api","h":"#channels","p":1450},{"i":1479,"t":"info method allows getting information about running Centrifugo nodes. Example: echo '{\"method\": \"info\", \"params\": {}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 184Content-Type: application/jsonDate: Thu, 17 May 2018 22:07:58 GMT{ \"result\": { \"nodes\": [ { \"name\": \"Alexanders-MacBook-Pro.local_8000\", \"num_channels\": 0, \"num_clients\": 0, \"num_users\": 0, \"uid\": \"f844a2ed-5edf-4815-b83c-271974003db9\", \"uptime\": 0, \"version\": \"\" } ] }} Info params​ Empty object at the moment. Info result​ Field name Field type Optional Description nodes Array of Node objects no Information about all nodes in a cluster","s":"info","u":"/docs/3/server/server_api","h":"#info","p":1450},{"i":1481,"t":"It's possible to combine several commands into one request to Centrifugo. To do this use JSON streaming format. This can improve server throughput and reduce traffic traveling around.","s":"Command pipelining","u":"/docs/3/server/server_api","h":"#command-pipelining","p":1450},{"i":1483,"t":"Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header. But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually: cent for Python phpcent for PHP gocent for Go rubycent for Ruby Also, there are API libraries created by community: crystalcent API client for Crystal language cent.js API client for NodeJS Centrifugo.AspNetCore API client for ASP.NET Core tip Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.","s":"HTTP API libraries","u":"/docs/3/server/server_api","h":"#http-api-libraries","p":1450},{"i":1485,"t":"Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC. GRPC API is also useful if you want to publish binary data to Centrifugo channels. tip GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description. You can enable GRPC API in Centrifugo using grpc_api option: config.json { ... \"grpc_api\": true} By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option. Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file. Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.","s":"GRPC API","u":"/docs/3/server/server_api","h":"#grpc-api","p":1450},{"i":1487,"t":"For example for Python you need to run sth like this according to GRPC docs: pip install grpcio-toolspython -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo: import grpcimport api_pb2_grpc as api_grpcimport api_pb2 as api_pbchannel = grpc.insecure_channel('localhost:10000')stub = api_grpc.CentrifugoApiStub(channel)try: resp = stub.Info(api_pb.InfoRequest())except grpc.RpcError as err: # GRPC level error. print(err.code(), err.details())else: if resp.error.code: # Centrifugo server level error. print(resp.error.code, resp.error.message) else: print(resp.result) Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.","s":"GRPC example for Python","u":"/docs/3/server/server_api","h":"#grpc-example-for-python","p":1450},{"i":1489,"t":"Here is a simple example of how to run Centrifugo with the GRPC Go client. You need protoc, protoc-gen-go and protoc-gen-go-grpc installed. First start Centrifugo itself with GRPC API enabled: CENTRIFUGO_GRPC_API=1 centrifugo --config config.json In another terminal tab: mkdir centrifugo_grpc_examplecd centrifugo_grpc_example/touch main.gogo mod init centrifugo_examplemkdir apiprotocd apiprotowget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto Run protoc to generate code: protoc -I ./ api.proto --go_out=. --go-grpc_out=. Put the following code to main.go file (created on the last step above): package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure()) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoApiClient(conn) for { resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} Then run: go run main.go The program starts and periodically publishes the same payload into chat:index channel.","s":"GRPC example for Go","u":"/docs/3/server/server_api","h":"#grpc-example-for-go","p":1450},{"i":1491,"t":"You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey . For example in Go language: package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")type keyAuth struct { key string}func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{ \"authorization\": \"apikey \" + t.key, }, nil}func (t keyAuth) RequireTransportSecurity() bool { return false}func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{\"xxx\"})) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoClient(conn) for { resp, err := client.Publish(context.Background(), &PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} For other languages refer to GRPC docs.","s":"GRPC API key authorization","u":"/docs/3/server/server_api","h":"#grpc-api-key-authorization","p":1450},{"i":1493,"t":"On this page","s":"CEL expressions","u":"/docs/4/pro/cel_expressions","h":"","p":1492},{"i":1495,"t":"We suppose that the main operation for which developers may use CEL expressions in Centrifugo is a subscribe operation. Let's look at it in detail. It's possible to configure subscribe_cel for a channel namespace (subscribe_cel is just an additional namespace channel option, with same rules applied). This expression should be a valid CEL expression. config.json { \"namespaces\": [ { \"name\": \"admin\", \"subscribe_cel\": \"'admin' in meta.roles\" } ]} In the example we are using custom meta information (must be an object) attached to the connection. As mentioned before in the doc this meta may be attached to the connection: when set in the connect proxy result or provided in JWT as meta claim An expression is evaluated for every subscription attempt to a channel in a namespace. So if meta attached to the connection is sth like this: { \"roles\": [\"admin\"]} – then for every channel in the admin namespace defined above expression will be evaluated to True and subscription will be accepted by Centrifugo. tip meta must be JSON object (any {}) for CEL expressions to work.","s":"subscribe_cel","u":"/docs/4/pro/cel_expressions","h":"#subscribe_cel","p":1492},{"i":1497,"t":"Inside the expression developers can use some variables which are injected by Centrifugo to the CEL runtime. Information about current user ID, meta information attached to the connection, all the variables defined in matched channel pattern will be available for CEL expression evaluation. Say client with user ID 123 subscribes to a channel /users/4 which matched the channel pattern /users/:user: Variable Type Example Description subscribed bool false Whether client is subscribed to channel, always false for subscribe operation user string \"123\" Current authenticated user ID (known from from JWT or connect proxy result) meta map[string]any {\"roles\": [\"admin\"]} Meta information attached to the connection by the apllication backend (in JWT or over connect proxy result) channel string \"/users/4\" Channel client tries to subscribe vars map[string]string {\"user\": \"4\"} Extracted variables from the matched channel pattern. It's empty in case of using channels without variables. In this case, to allow admin to subscribe on any user's channel or allow non-admin user to subscribe only on its own channel, you may construct an expression like this: { ... \"subscribe_cel\": \"vars.user == user or 'admin' in meta.roles\"} Let's look at one more example. Say client with user ID 123 subscribes to a channel /example.com/users/4 which matched the channel pattern /:tenant/users/:user. The permission check may be transformed into sth like this (assuming meta information has information about current connection tenant): { \"namespaces\": [ { \"name\": \"/:tenant/users/:user\", \"subscribe_cel\": \"vars.tenant == meta.tenant && (vars.user == user or 'admin' in meta.roles)\" } ]}","s":"Expression variables","u":"/docs/4/pro/cel_expressions","h":"#expression-variables","p":1492},{"i":1499,"t":"CEL expression to check permissions to publish into a channel. Same expression variables are available.","s":"publish_cel","u":"/docs/4/pro/cel_expressions","h":"#publish_cel","p":1492},{"i":1501,"t":"CEL expression to check permissions for channel history. Same expression variables are available.","s":"history_cel","u":"/docs/4/pro/cel_expressions","h":"#history_cel","p":1492},{"i":1503,"t":"CEL expression to check permissions for channel presence. Same expression variables are available.","s":"presence_cel","u":"/docs/4/pro/cel_expressions","h":"#presence_cel","p":1492},{"i":1505,"t":"Quickstart tutorial ⏱️ In this tutorial we will build a very simple browser application with Centrifugo. Users will connect to Centrifugo over WebSocket, subscribe to a channel, and start receiving all channel publications (messages published to that channel). In our case, we will send a counter value to all channel subscribers to update counter widget in all open browser tabs in real-time. First you need to install Centrifugo. In this example, we are using a binary file release which is fine for development. Once you have Centrifugo binary available on your machine you can generate minimal required configuration file with the following command: ./centrifugo genconfig This helper command will generate config.json file in the working directory with a content like this: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": []} Now we can start a server. Let's start Centrifugo with a built-in admin web interface: ./centrifugo --config=config.json --admin We could also enable the admin web interface by not using --admin flag but by adding \"admin\": true option to the JSON configuration file: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin\": true, \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": []} And then running Centrifugo only with a path to a configuration file: ./centrifugo --config=config.json Now open http://localhost:8000. You should see Centrifugo admin web panel. Enter admin_password value from the configuration file to log in (in our case it's d0683813-0916-4c49-979f-0e08a686b727, but you will have a different value). Inside the admin panel, you should see that one Centrifugo node is running, and it does not have connected clients: Now let's create index.html file with our simple app: index.html Centrifugo quick start
      -
      Note that we are using centrifuge-js 3.1.0 in this example, getting it from CDN, you better use its latest version at the moment of reading this tutorial. In real Javascript app you most probably will load centrifuge from NPM. In index.html above we created an instance of a Centrifuge client passing Centrifugo server default WebSocket endpoint address to it, then we subscribed to a channel called channel and provided a callback function to process incoming real-time messages (publications). Upon receiving a new publication we update page HTML and setting counter value to page title. We call .subscribe() to initialte subscription and .connect() method of Client to start a WebSocket connection. We also handle Client state transitions (disconnected, connecting, connected) and Subscription state transitions (unsubscribed, subscribing, subscribed) – see detailed description in client SDK spec. Now you need to serve this file with an HTTP server. In a real-world Javascript application, you will serve your HTML files with a web server of your choice – but for this simple example we can use a simple built-in Centrifugo static file server: ./centrifugo serve --port 3000 Alternatively, if you have Python 3 installed: python3 -m http.server 3000 These commands start a simple static file web server that serves the current directory on port 3000. Make sure you still have Centrifugo server running. Open http://localhost:3000/. Now if you look at browser developer tools or in Centrifugo logs you will notice that a connection can not be successfully established: 2021-09-01 10:17:33 [INF] request Origin is not authorized due to empty allowed_origins origin=http://localhost:3000 That's because we have not set allowed_origins in the configuration. Modify allowed_origins like this: config.json { ... \"allowed_origins\": [\"http://localhost:3000\"]} Allowed origins is a security option for request originating from web browsers – see more details in server configuration docs. Restart Centrifugo after modifying allowed_origins in a configuration file. Now if you reload a browser window with an application you should see new information logs in server output: 2022-06-10 09:44:21 [INF] invalid connection token error=\"invalid token: token format is not valid\" client=a65a8463-6a36-421d-814a-0083c88365292022-06-10 09:44:21 [INF] disconnect after handling command client=a65a8463-6a36-421d-814a-0083c8836529 command=\"id:1 connect:{token:\\\"\\\" name:\\\"js\\\"}\" reason=\"invalid token\" user= We still can not connect. That's because the client should provide a valid JWT (JSON Web Token) to authenticate itself. This token must be generated on your backend and passed to a client-side (over template variables or using separate AJAX call – whatever way you prefer). Since in our simple example we don't have an application backend we can quickly generate an example token for a user using centrifugo sub-command gentoken. Like this: ./centrifugo gentoken -u 123722 – where -u flag sets user ID. The output should be like this: HMAC SHA-256 JWT for user \"123722\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw – you will have another token value since this one is based on randomly generated token_hmac_secret_key from the configuration file we created at the beginning of this tutorial. See token authentication docs for information about proper token generation in a real application. Now we can copy generated HMAC SHA-256 JWT and paste it into Centrifugo constructor instead of placeholder in index.html file. I.e.: const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw\"}); If you reload your browser tab – the connection will be successfully established, but the client still can not subscribe to a channel: 2022-06-10 09:45:49 [INF] client command error error=\"permission denied\" client=88116489-350f-447f-9ff3-ab61c9341efe code=103 command=\"id:2 subscribe:{channel:\\\"channel\\\"}\" reply=\"id:2 error:{code:103 message:\\\"permission denied\\\"}\" user=123722 We need to give client a permission to subscribe on the channel channel. There are several ways to do this. For example, client can provide subscription JWT for a channel. But here we will use an option to allow all authenticated clients subscribe to any channel. To do this let's extend a server configuration with allow_subscribe_for_client option: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin\": true, \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": [\"http://localhost:3000\"], \"allow_subscribe_for_client\": true} tip A good practice with Centrifugo is configuring channel namespaces for different types of real-time features you have in the application. By defining namespaces you can achieve a granular control over channel behavior and permissions. Restart Centrifugo – and after doing this everything should start working. Client can successfully connect and successfully subscribe to a channel now. Open developer tools and look at WebSocket frames panel, you should see sth like this: Note, that in this example we generated connection JWT – but it has expiration time, so after some time Centrifugo stops accepting those tokens. In real-life you need to add a token refresh function to a client to rotate tokens. See out client API SDK spec. OK, the last thing we need to do here is to publish a new counter value to a channel and make sure our app works properly. We can do this over Centrifugo API sending an HTTP request to default API endpoint http://localhost:8000/api, but let's do this over the admin web panel first. Open Centrifugo admin web panel in another browser tab (http://localhost:8000/) and go to Actions section. Select publish action, insert channel name that you want to publish to – in our case this is a string channel and insert into data area JSON like this: { \"value\": 1} Click PUBLISH button and check out the application browser tab – counter value must be immediately received and displayed. Open several browser tabs with our app and make sure all tabs receive a message as soon as you publish it. BTW, let's also look at how you can publish data to a channel over Centrifugo server API from a terminal using curl tool: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey d7627bb6-2292-4911-82e1-615c0ed3eebb\" \\ --request POST \\ --data '{\"method\": \"publish\", \"params\": {\"channel\": \"channel\", \"data\": {\"value\": 2}}}' \\ http://localhost:8000/api – where for Authorization header we set api_key value from Centrifugo config file generated above. We did it! We built the simplest browser real-time app with Centrifugo and its Javascript client. It does not have a backend, it's not very useful, to be honest, but it should give you an insight on how to start working with Centrifugo server. Read more about Centrifugo server in the next documentations chapters – it can do much much more than we just showed here. Integration guide describes a process of idiomatic Centrifugo integration with your application backend.","s":"Quickstart tutorial ⏱️","u":"/docs/4/getting-started/quickstart","h":"","p":1504},{"i":1507,"t":"On this page","s":"Channel patterns","u":"/docs/4/pro/channel_patterns","h":"","p":1506},{"i":1509,"t":"Let's look at the example: { // rest of the config ... \"channel_patterns\": true, // required to turn on the feature. \"namespaces\": [ { \"name\": \"/users/:name\" // namespace options may go here ... }, { \"name\": \"/events/:project/:type\" // namespace options may go here ... } ]} As soon as namespace name starts with / - it's considered a channel pattern. Just like an HTTP path it consists of segments delimited by /. The : symbol in the segment beginning defines a variable part – more information below. In this case a channel to be used must be sth like /users/mario - i.e. start with / and match one of the patterns defined in the configuration. So this channel pattern matching mechanics behaves mostly like HTTP route matching in many frameworks. Given the configuration example above: if channel is /users/mario, then the namespace with the name /users/:name will match and we apply all the options defined for it to the channel. if channel is /events/42/news, then the namespace with the name /events/:project/:type will match. if channel is /events/42, then no namespace will match and the unknown channel error will be returned. Basic example demonstrating use of pattern channels in JS const client := new Centrifuge(\"ws://...\", {});const sub = client.newSubscription('/users/mario');sub.subscribe();client.connect();","s":"Configuration","u":"/docs/4/pro/channel_patterns","h":"#configuration","p":1506},{"i":1511,"t":"Some implementation restrictions and details to know about: When using channel patterns feature : symbol in a namespace name defines a variable part. It's not related to a namespace separator anymore – the entire channel is matched over the channel pattern. Similar to the HTTP routes semantics. So namespace separator is not needed at all when using channel patterns. Centrifugo only allows explicit channel pattern matching which do not result into channel pattern conflicts in runtime, this is checked during configuration validation on server start. Explicitly defined static patterns (without variables) have precedence over patterns with variables. There is no analogue of top-level namespace (like we have for standard namespace configuration) for channels starting with /. If a channel does not match any explicitly defined pattern then Centrifugo returns the 102: unknown channel error. If you define channel_regex inside channel pattern options – then regex matches over the entire channel (since variable parts are located in the namespace name in this case). Channel pattern must only contain ASCII characters. Duplicate variable names are not allowed inside an individual pattern, i.e. defining /users/:user/:user will result into validation error on start.","s":"Implementation details","u":"/docs/4/pro/channel_patterns","h":"#implementation-details","p":1506},{"i":1513,"t":": in the channel pattern name helps to define a variable to match against. Named parameters only match a single segment of the channel: Channel pattern \"/users/:name\":/users/mary ✅ match/users/john ✅ match/users/mary/info ❌ no match /users ❌ no match Another example for channel pattern /news/:type/:subtype, i.e. with multiple variables: Channel pattern \"/news/:type/:subtype\":/news/sport/football ✅ match/news/sport/volleyball ✅ match/news/sport ❌ no match/news ❌ no match Channel patterns support mid-segment variables, so the following is possible: Channel pattern \"/personal/user_:user\":/personal/user_mary ✅ match/personal/user_john ✅ match/personal/user_ ❌ no match","s":"Variables","u":"/docs/4/pro/channel_patterns","h":"#variables","p":1506},{"i":1515,"t":"Additional benefits of using channel patterns may be achieved together with Centrifugo PRO CEL expressions. Channel pattern variables are available inside CEL expressions for evaluation in a custom way.","s":"Using varibles","u":"/docs/4/pro/channel_patterns","h":"#using-varibles","p":1506},{"i":1517,"t":"On this page","s":"Message batching control","u":"/docs/4/pro/client_message_batching","h":"","p":1516},{"i":1519,"t":"The client_write_delay is a duration option, it is a time Centrifugo will try to collect messages inside each connection message write loop before sending them towards the connection. Enabling client_write_delay may reduce CPU usage of both server and client in case of high message rate inside individual connections. The reduction happens due to the lesser number of system calls to execute. Enabling client_write_delay limits the maximum throughput of messages towards the connection which may be achieved. For example, if client_write_delay is 100ms then the max throughput per second will be (1000 / 100) * client_max_messages_in_frame (16 by default), i.e. 160 messages per second. Though this should be more than enough for target Centrifugo use cases (frontend apps). Example: config.json { // Rest of config here ... \"client_write_delay\": \"100ms\"}","s":"client_write_delay","u":"/docs/4/pro/client_message_batching","h":"#client_write_delay","p":1516},{"i":1521,"t":"The client_reply_without_queue is a boolean option to not use client queue for replies to commands. When true replies are written to the transport without going through the connection message queue.","s":"client_reply_without_queue","u":"/docs/4/pro/client_message_batching","h":"#client_reply_without_queue","p":1516},{"i":1523,"t":"The client_max_messages_in_frame is an integer option which controls the maximum number of messages which may be joined by Centrifugo into one transport frame. By default, 16. Use -1 for unlimited number.","s":"client_max_messages_in_frame","u":"/docs/4/pro/client_message_batching","h":"#client_max_messages_in_frame","p":1516},{"i":1525,"t":"On this page","s":"Centrifugo PRO overview","u":"/docs/4/pro/overview","h":"","p":1524},{"i":1527,"t":"Centrifugo PRO is packed with the following features: Everything from Centrifugo OSS 🔍 Channel and user tracing allows watching client protocol frames in channel or per user ID in real time. 💹 Real-time analytics with ClickHouse for a great system observability, reporting and trending. 🛡️ Operation throttling to protect server from the real-time API misusing and frontend bugs. 🔥 Push notification API to manage device tokens and send mobile and browser push notifications. 🟢 User status API feature allows understanding activity state for a list of users. 🔌 Connections API to query, filter and inspect active connections. ✋ User blocking API to block/unblock abusive users by ID. 🛑 JWT revoking and invalidation API to revoke tokens by ID and invalidate user's tokens based on issue time. 💪 Channel capabilities for controlling channel permissions per connection or per subscription. 📜 Channel patterns allow defining channel configuration like HTTP routes with parameters. ✍️ CEL expressions to write custom efficient permission rules for channel operations. 🚀 Faster performance to reduce resource usage on server side. 🔮 Singleflight for online presence and history to reduce load on the broker. 🍔 Message batching control for advanced tuning of client connection write behaviour. 🪵 CPU and RSS memory usage stats of Centrifugo nodes in admin UI. info PRO features can change with time. We reserve a right to move features from PRO to OSS version if there is a clear signal that this is required to do for the ecosystem.","s":"Features","u":"/docs/4/pro/overview","h":"#features","p":1524},{"i":1529,"t":"You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example: Centrifugo handles up to 20 concurrent connections up to 2 server nodes supported up to 10 API requests per second allowed This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future. Centrifugo PRO license agreement Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.","s":"Try for free in sandbox mode","u":"/docs/4/pro/overview","h":"#try-for-free-in-sandbox-mode","p":1524},{"i":1531,"t":"To run without limits Centrifugo PRO requires a license key. At this point we are not issuing license keys for Centrifugo PRO as we are in the process of defining pricing strategy and distribution model for it. Please contact us over centrifugal.dev@gmail.com – so we can add you to the list of interested customers. Will appreciate if you share which PRO features you are mostly interested in.","s":"Pricing","u":"/docs/4/pro/overview","h":"#pricing","p":1524},{"i":1533,"t":"On this page","s":"Faster performance","u":"/docs/4/pro/performance","h":"","p":1532},{"i":1535,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.","s":"Faster HTTP API","u":"/docs/4/pro/performance","h":"#faster-http-api","p":1532},{"i":1537,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC API","u":"/docs/4/pro/performance","h":"#faster-grpc-api","p":1532},{"i":1539,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster HTTP proxy","u":"/docs/4/pro/performance","h":"#faster-http-proxy","p":1532},{"i":1541,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC proxy","u":"/docs/4/pro/performance","h":"#faster-grpc-proxy","p":1532},{"i":1543,"t":"Centrifugo PRO has an optimized decoding of JWT claims.","s":"Faster JWT decoding","u":"/docs/4/pro/performance","h":"#faster-jwt-decoding","p":1532},{"i":1545,"t":"Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.","s":"Faster GRPC unidirectional stream","u":"/docs/4/pro/performance","h":"#faster-grpc-unidirectional-stream","p":1532},{"i":1547,"t":"Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.","s":"Examples","u":"/docs/4/pro/performance","h":"#examples","p":1532},{"i":1549,"t":"Sorry, your browser doesn't support embedded video. In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.","s":"Publish HTTP API","u":"/docs/4/pro/performance","h":"#publish-http-api","p":1532},{"i":1551,"t":"Sorry, your browser doesn't support embedded video. In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.","s":"History HTTP API","u":"/docs/4/pro/performance","h":"#history-http-api","p":1532},{"i":1553,"t":"On this page","s":"Install and run PRO version","u":"/docs/4/pro/install_and_run","h":"","p":1552},{"i":1555,"t":"Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below).","s":"Binary release","u":"/docs/4/pro/install_and_run","h":"#binary-release","p":1552},{"i":1557,"t":"Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v4.0.0-beta.10 centrifugo -c config.json","s":"Docker image","u":"/docs/4/pro/install_and_run","h":"#docker-image","p":1552},{"i":1559,"t":"You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag: values.yaml ...image: registry: docker.io repository: centrifugo/centrifugo-pro tag: v4.0.0-beta.10","s":"Kubernetes","u":"/docs/4/pro/install_and_run","h":"#kubernetes","p":1552},{"i":1561,"t":"DEB package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v4.0.0-beta.10/centrifugo-pro_4.0.0-beta.10_amd64.debsudo dpkg -i centrifugo-pro_4.0.0-beta.10_amd64.deb","s":"Debian and Ubuntu","u":"/docs/4/pro/install_and_run","h":"#debian-and-ubuntu","p":1552},{"i":1563,"t":"RPM package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v4.0.0-beta.10/centrifugo-pro-4.0.0-beta.10.x86_64.rpmsudo yum install centrifugo-pro-4.0.0-beta.10.x86_64.rpm","s":"Centos","u":"/docs/4/pro/install_and_run","h":"#centos","p":1552},{"i":1565,"t":"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits. Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this: config.json { ... \"license\": \"\"} tip If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.","s":"Setting PRO license key","u":"/docs/4/pro/install_and_run","h":"#setting-pro-license-key","p":1552},{"i":1567,"t":"On this page","s":"Channel capabilities","u":"/docs/4/pro/capabilities","h":"","p":1566},{"i":1569,"t":"Connection capabilities can be set: in connection JWT (in caps claim) in connect proxy result (caps field) For example, here we are issuing permissions to subscribe on channel news and channel user_42 to a client: { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] } ]} Known capabilities: sub - subscribe to a channel to receive publications from it pub - publish into a channel (your backend won't be able to process the publication in this case) prs - call presence and presence stats API, also consume join/leave events upon subscribing hst - call history API, also make Subscription positioned or recoverable upon subscribing","s":"Connection capabilities","u":"/docs/4/pro/capabilities","h":"#connection-capabilities","p":1566},{"i":1571,"t":"Centrifugo processes caps objects till it finds a match to a channel. At this point it applies permissions in the matched object and stops processing remaining caps. If no match found – then 103 permission denied returned to a client (of course if namespace does not have other permission-related options enabled). Let's consider example like this: WRONG! { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"pub\"] }, { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, ]} Here we have two entries for channel news, but when client subscribes on news only the first entry will be taken into considiration by Centrifugo – so Subscription attempt will be rejected (since first cap object does not have sub capability). In real life you don't really want to have cap objects with identical channels – but below we will introduce wildcard matching where understanding how caps processed becomes important. Another example: WRONG! { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"pub\", \"hst\", \"prs\"] }, ]} One could expect that client will have [\"sub\", \"pub\", \"hst\", \"prs\"] capabilities for a channel user_42. But it's not true since Centrifugo processes caps objects and channels inside caps object in order – it finds a match to user_42 in first caps object, it contains only \"sub\" capability, processing stops. So user can subscribe to a channel, but can not publish, can not call history and presence APIs even though those capabilities are mentioned in caps object. The correct way to give all caps to the channel user_42 would be to split channels into different caps objects: CORRECT { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] }, ]} The processing behaves like this to avoid potential problems with possibly conflicting matches (mostly when using wildcard and regex matching – see below) and still allow overriding capabilities for specific channels.","s":"Caps processing behavior","u":"/docs/4/pro/capabilities","h":"#caps-processing-behavior","p":1566},{"i":1573,"t":"In JWT auth case – capabilities in JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In connect proxy case – capabilities will work until client connection close (disconnect) or connection refresh triggered (with refresh proxy you can provide an updated set of capabilities).","s":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations","p":1566},{"i":1575,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a connection expiration, then upon refresh: if using proxy – provide new caps in refresh proxy result, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). if JWT auth - provide new caps in connection token, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). In case of using connect proxy – you can disconnect a user (or client) with a reconnect code. New capabilities will be asked upon reconnection. In case of using token auth – revoke token (Centrifugo PRO feature) and disconnect user (or client) with reconnect code. Upon reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking connection caps","u":"/docs/4/pro/capabilities","h":"#revoking-connection-caps","p":1566},{"i":1577,"t":"It's possible to use wildcards in channel resource names. For example, let's give a permission to subscribe on all channels in news namespace. { \"caps\": [ { \"channels\": [\"news:*\"], \"match\": \"wildcard\", \"allow\": [\"sub\"] } ]} note Match type is used for all channels in caps object. If you need different matching behavior for different channels then split them on different caps objects.","s":"Example: wildcard match","u":"/docs/4/pro/capabilities","h":"#example-wildcard-match","p":1566},{"i":1579,"t":"Or regex: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } ]}","s":"Example: regex match","u":"/docs/4/pro/capabilities","h":"#example-regex-match","p":1566},{"i":1581,"t":"Of course it's possible to combine different types of match inside one caps array: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } { \"channels\": [\"user_42\"], \"allow\": [\"sub\"] } ]}","s":"Example: different types of match","u":"/docs/4/pro/capabilities","h":"#example-different-types-of-match","p":1566},{"i":1583,"t":"Let's look how to allow all permissions to a client: { \"caps\": [ { \"channels\": [\"*\"], \"match\": \"wildcard\", \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] } ]} Full access warn Should we mention that giving full access to a client is something to wisely consider? 🤔","s":"Example: full access to all channels","u":"/docs/4/pro/capabilities","h":"#example-full-access-to-all-channels","p":1566},{"i":1585,"t":"Subscription capabilities can be set: in subscription JWT (in allow claim) in subscribe proxy result (allow field) Subscription token already belongs to a channel (it has a channel claim). So users with a valid subscription token can subscribe to a channel. But it's possible to additionally grant channel permissions to a user for publishing and calling presence and history using allow claim: { \"allow\": [\"pub\", \"hst\", \"prs\"]} Putting sub permission to the Subscription token does not make much sense – Centrifugo only expects valid token for a subscription permission check.","s":"Subscription capabilities","u":"/docs/4/pro/capabilities","h":"#subscription-capabilities","p":1566},{"i":1587,"t":"In JWT auth case – capabilities in subscription JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In subscribe proxy case – capabilities will work until client unsubscribe (or connection close).","s":"Expiration considirations","u":"/docs/4/pro/capabilities","h":"#expiration-considirations-1","p":1566},{"i":1589,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a subscription expiration, then upon refresh: provide new caps in subscription token, Centrifugo will update channel caps. In case of using subscribe proxy – you can unsubscribe a user (or client) with a resubscribe code. Or disconnect with reconnect code. New capabilities will be set up upon resubscription/reconnection. In case of using JWT auth – revoke token (Centrifugo PRO feature) and unsubscribe/disconnect user (or client) with resubscribe/reconnect code. Upon resubscription/reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking subscription permissions","u":"/docs/4/pro/capabilities","h":"#revoking-subscription-permissions","p":1566},{"i":1591,"t":"Singleflight Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. It will do this by using in-memory component called singleflight. tip While it can seem similar, singleflight is not a cache. It only combines identical parallel requests into one. If requests come one after another – they will be sent separately to the broker or presence storage. This option can radically reduce a load on a broker in the following situations: Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature) Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine. To enable: config.json { ... \"use_singleflight\": true} Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.","s":"Singleflight","u":"/docs/4/pro/singleflight","h":"","p":1590},{"i":1593,"t":"CPU and RSS stats A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI. Here is how this looks like: The information updated in near real-time (with several seconds delay). It's also available as part of info API.","s":"CPU and RSS stats","u":"/docs/4/pro/process_stats","h":"","p":1592},{"i":1595,"t":"On this page","s":"Connections API","u":"/docs/4/pro/connections","h":"","p":1594},{"i":1597,"t":"Let's look at the quick example. First, generate a JWT for user 42: $ centrifugo genconfig Generate token for some user to be used in the example connections: $ centrifugo gentoken -u 42HMAC SHA-256 JWT for user 42 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y Run Centrifugo with uni_http_stream transport enabled (it will allow us connecting from the terminal with curl): CENTRIFUGO_UNI_HTTP_STREAM=1 centrifugo -c config.json Create new terminal window and run: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' In another terminal create one more connection: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' Now let's call connections over HTTP API: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"connections\", \"params\": {\"user\": \"42\"}}' \\ http://localhost:8000/api The result: { \"result\": { \"connections\": { \"db8bc772-2654-4283-851a-f29b888ace74\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" }, \"4bc3ca70-ecc5-439d-af14-a78ae18e31c7\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" } } }} Here we can see that user has 2 connections from terminal app. Each connection can be annotated with meta JSON information which is set during connection establishment (over meta claim of JWT or by returning meta in the connect proxy result).","s":"Example","u":"/docs/4/pro/connections","h":"#example","p":1594},{"i":1599,"t":"Returns information about active connections according to the request. connections params​ Parameter name Parameter type Required Description user string no fast filter by User ID expression string no CEL expression to filter users connections result​ Field name Field type Optional Description connections map[string]ConnectionInfo no active user connections map where key is client ID and value is ConnectionInfo ConnectionInfo​ Field name Field type Optional Description app_name string yes client app name (if provided by client) app_version string yes client app version (if provided by client) transport string no client connection transport protocol string no client connection protocol (json or protobuf) user string yes client user ID state ConnectionState yes connection state ConnectionState object​ Field name Field type Optional Description channels map[string]ChannelContext yes Channels client subscribed to connection_token ConnectionTokenInfo yes information about connection token subscription_tokens map yes information about channel tokens used to subscribe meta JSON object yes meta information attached to a connection ChannelContext object​ Field name Field type Optional Description source int yes The source of channel subscription ConnectionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued SubscriptionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued","s":"connections","u":"/docs/4/pro/connections","h":"#connections","p":1594},{"i":1601,"t":"On this page","s":"Analytics with ClickHouse","u":"/docs/4/pro/analytics","h":"","p":1600},{"i":1603,"t":"To enable integration with ClickHouse add the following section to a configuration file: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\", \"tcp://127.0.0.1:9001\", \"tcp://127.0.0.1:9002\", \"tcp://127.0.0.1:9003\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"centrifugo_cluster\", \"export_connections\": true, \"export_subscriptions\": true, \"export_operations\": true, \"export_publications\": true, \"export_notifications\": true, \"export_http_headers\": [ \"User-Agent\", \"Origin\", \"X-Real-Ip\" ] }} All ClickHouse analytics options scoped to clickhouse_analytics section of configuration. Toggle this feature using enabled boolean option. tip While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo. Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option. You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database. export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available. export_subscriptions tells Centrifugo to export subscription information snapshots. Information about subscription will be exported once a subscription established and then periodically while connection alive. See below on table structure to see which fields are available. export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available. export_publications tells Centrifugo to export publications for channels to a separate ClickHouse table. export_notifications tells Centrifugo to export push notifications to a separate ClickHouse table. export_http_headers is a list of HTTP headers to export for connection information. export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport. skip_schema_initialization - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process. skip_ping_on_start - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.","s":"Configuration","u":"/docs/4/pro/analytics","h":"#configuration","p":1600},{"i":1605,"t":"SHOW CREATE TABLE centrifugo.connections;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└─────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.connections_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections_distributed( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Connections table","u":"/docs/4/pro/analytics","h":"#connections-table","p":1600},{"i":1607,"t":"SHOW CREATE TABLE centrifugo.subscriptions┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.subscriptions_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions_distributed( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'subscriptions', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Subscriptions table","u":"/docs/4/pro/analytics","h":"#subscriptions-table","p":1600},{"i":1609,"t":"SHOW CREATE TABLE centrifugo.operations;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.operations_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Operations table","u":"/docs/4/pro/analytics","h":"#operations-table","p":1600},{"i":1611,"t":"SHOW CREATE TABLE centrifugo.publications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.publications( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.publications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'publications', murmurHash3_64(channel)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Publications table","u":"/docs/4/pro/analytics","h":"#publications-table","p":1600},{"i":1613,"t":"🚧 This PRO feature is under construction together with push notification API. SHOW CREATE TABLE centrifugo.notifications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.notifications( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.notifications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'notifications', murmurHash3_64(uid)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Notifications table","u":"/docs/4/pro/analytics","h":"#notifications-table","p":1600},{"i":1615,"t":"Show unique users which were connected: SELECT DISTINCT userFROM centrifugo.connections_distributed;┌─user─────┐│ user_1 ││ user_2 ││ user_3 ││ user_4 ││ user_5 │└──────────┘ Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111): SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish');┌─count()─┐│ 4502 │└─────────┘ The same for a specific user: SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');┌─count()─┐│ 1214 │└─────────┘ Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since subscriptions table contain periodic snapshot entries, clients could unsubscribe in between snapshots – this is reflected in operations table): SELECT COUNT(Distinct(user))FROM centrifugo.subscriptions_distributedWHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));┌─uniqExact(user)─┐│ 101 │└─────────────────┘ Show top 10 users which called publish operation during last one minute: SELECT COUNT(op) AS num_ops, userFROM centrifugo.operations_distributedWHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))GROUP BY userORDER BY num_ops DESCLIMIT 10;┌─num_ops─┬─user─────┐│ 56 │ user_200 ││ 11 │ user_75 ││ 6 │ user_87 ││ 6 │ user_65 ││ 6 │ user_39 ││ 5 │ user_28 ││ 5 │ user_63 ││ 5 │ user_89 ││ 3 │ user_32 ││ 3 │ user_52 │└─────────┴──────────┘ Show total number of push notifications to iOS devices sent during last 24 hours: SELECT COUNT(*)FROM centrifugo.notificationsWHERE (time > (now() - toIntervalHour(24))) AND (platform = 'ios')┌─count()─┐│ 31200 │└─────────┘","s":"Query examples","u":"/docs/4/pro/analytics","h":"#query-examples","p":1600},{"i":1617,"t":"The recommended way to run ClickHouse in production is with cluster. See an example of such cluster configuration made with Docker Compose. But during development you may want to run Centrifugo with single instance ClickHouse. To do this set only one ClickHouse dsn and do not set cluster name: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"\", \"export_connections\": true, \"export_subscriptions\": true, \"export_publications\": true, \"export_operations\": true, \"export_http_headers\": [ \"Origin\", \"User-Agent\" ] }} Run ClickHouse locally: docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click clickhouse/clickhouse-server Run ClickHouse client: docker run -it --rm --link click:clickhouse-server --entrypoint clickhouse-client clickhouse/clickhouse-server --host clickhouse-server Issue queries: :) SELECT * FROM centrifugo.operations┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘","s":"Development","u":"/docs/4/pro/analytics","h":"#development","p":1600},{"i":1619,"t":"When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 10 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are configurable. Centrifugo PRO uses very efficient code for writing data to ClickHouse, so analytics feature should only add a little overhead for Centrifugo node. Several metrics are exposed to monitor export process health: centrifugo_clickhouse_analytics_flush_duration_seconds summary centrifugo_clickhouse_analytics_batch_size summary centrifugo_clickhouse_analytics_drop_count counter","s":"How export works","u":"/docs/4/pro/analytics","h":"#how-export-works","p":1600},{"i":1621,"t":"On this page","s":"User and channel tracing","u":"/docs/4/pro/tracing","h":"","p":1620},{"i":1623,"t":"It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing. curl -X POST http://localhost:8000/admin/trace -H \"Authorization: token \" -d '{\"type\": \"user\", \"entity\": \"56\"}' -o trace.txt Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future as PRO version evolves.","s":"Save to a file","u":"/docs/4/pro/tracing","h":"#save-to-a-file","p":1620},{"i":1625,"t":"On this page","s":"Token revocation API","u":"/docs/4/pro/token_revocation","h":"","p":1624},{"i":1627,"t":"By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart. But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/4/pro/token_revocation","h":"#how-it-works","p":1624},{"i":1629,"t":"Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/4/pro/token_revocation","h":"#configure","p":1624},{"i":1631,"t":"Revocation data can be kept in Redis. To enable this configuration should be: { ... \"token_revoke\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/4/pro/token_revocation","h":"#redis-persistence-engine","p":1624},{"i":1633,"t":"Revocation data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"token_revoke\": { \"persistence_engine\": \"database\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/4/pro/token_revocation","h":"#database-persistence-engine","p":1624},{"i":1636,"t":"Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT). caution This API assumes that JWTs you are using contain \"jti\" claim which is a unique token ID (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"revoke_token\", \"params\": {\"uid\": \"xxx-xxx-xxx\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api revoke_token params​ Parameter name Parameter type Required Description uid string yes Token unique ID (JTI claim in case of JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). revoke_token result​ Empty object at the moment.","s":"revoke_token","u":"/docs/4/pro/token_revocation","h":"#revoke_token","p":1624},{"i":1639,"t":"Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application. caution This API assumes that JWTs you are using contain \"iat\" claim which is a time token was issued at (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"invalidate_user_tokens\", \"params\": {\"user\": \"test\", \"issued_before\": 1635845022, \"expire_at\": 1635845122}}' \\ http://localhost:8000/api invalidate_user_tokens params​ Parameter name Parameter type Required Description user string yes User ID to invalidate tokens for issued_before int no All tokens issued at before this Unix time will be considered revoked (in case of JWT this requires iat to be properly set in JWT), if not provided server uses current time expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). invalidate_user_tokens result​ Empty object.","s":"invalidate_user_tokens","u":"/docs/4/pro/token_revocation","h":"#invalidate_user_tokens","p":1624},{"i":1641,"t":"On this page","s":"User blocking API","u":"/docs/4/pro/user_block","h":"","p":1640},{"i":1643,"t":"By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart. But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/4/pro/user_block","h":"#how-it-works","p":1640},{"i":1645,"t":"User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/4/pro/user_block","h":"#configure","p":1640},{"i":1647,"t":"Blocking data can be kept in Redis. To enable this configuration should be: { ... \"user_block\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/4/pro/user_block","h":"#redis-persistence-engine","p":1640},{"i":1649,"t":"Blocking data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"user_block\": { \"persistence_engine\": \"database\" }} tip To quickly start local PostgreSQL database: docker run -it --rm -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:15","s":"Database persistence engine","u":"/docs/4/pro/user_block","h":"#database-persistence-engine","p":1640},{"i":1652,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"block_user\", \"params\": {\"user\": \"2695\", \"expire_at\": 1635845122}}' \\ http://localhost:8000/api block_user params​ Parameter name Parameter type Required Description user string yes User ID to block expire_at int no Unix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache). block_user result​ Empty object at the moment.","s":"block_user","u":"/docs/4/pro/user_block","h":"#block_user","p":1640},{"i":1654,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"unblock_user\", \"params\": {\"user\": \"2695\"}}' \\ http://localhost:8000/api unblock_user params​ Parameter name Parameter type Required Description user string yes User ID to unblock unblock_user result​ Empty object at the moment.","s":"unblock_user","u":"/docs/4/pro/user_block","h":"#unblock_user","p":1640},{"i":1656,"t":"On this page","s":"Admin web UI","u":"/docs/4/server/admin_web","h":"","p":1655},{"i":1658,"t":"admin (boolean, default: false) – enables/disables admin web UI admin_password (string, default: \"\") – this is a password to log into admin web interface admin_secret (string, default: \"\") - this is a secret key for authentication token set on successful login. Make both admin_password and admin_secret strong and keep them in secret. After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface. tip Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.","s":"Options","u":"/docs/4/server/admin_web","h":"#options","p":1655},{"i":1660,"t":"If you want to use custom web interface you can specify path to web interface directory dist: config.json { ..., \"admin\": true, \"admin_password\": \"\", \"admin_secret\": \"\", \"admin_web_path\": \"\"} This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.","s":"Using custom web interface","u":"/docs/4/server/admin_web","h":"#using-custom-web-interface","p":1655},{"i":1662,"t":"There is also an option to run Centrifugo in insecure admin mode - in this case you don't need to set admin_password and admin_secret in config – in web interface you will be logged in automatically without any password. Note that this is only an option for production if you protected admin web interface with firewall rules. Otherwise anyone in internet will have full access to admin functionality described above. To enable insecure admin mode: config.json { ..., \"admin\": true, \"admin_insecure\": true, \"admin_password\": \"\", \"admin_secret\": \"\"}","s":"Admin insecure mode","u":"/docs/4/server/admin_web","h":"#admin-insecure-mode","p":1655},{"i":1664,"t":"On this page","s":"Operation throttling","u":"/docs/4/pro/throttling","h":"","p":1663},{"i":1666,"t":"In-memory throttling is an efficient way to limit number of operations allowed on a per-connection basis – i.e. inside each individual real-time connection. Our throttling implementation uses token bucket algorithm internally. The list of operations which can be throttled on a per-connection level is: subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) In addition, Centrifugo allows defining two special buckets containers: total – define it to limit the total number of commands per interval (all commands sent from client count), these buckets will always be checked if defined, every command from the client always consumes token from total buckets default - define it if you don't want to configure some command buckets explicitly, default buckets will be used in case command buckets is not configured explicitly. config.json { ... \"client_command_throttling\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"total\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 20 }, { \"interval\": \"60s\", \"rate\": 50 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 }, ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"updateActiveStatus\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} tip Centrifugo real-time SDKs written in a way that if client receives an error during connect – it will try to reconnect to a server with backoff algorithm. The same for subscribing to channels (i.e. error from subscribe command) – subscription request will be retried with a backoff. Refresh and subscription refresh will be also retried automatically by SDK upon errors after in several seconds. Retries of other commands should be handled manually from the client side if needed – though usually you should choose throttling limits in a way that normal users of your app never hit the limits.","s":"In-memory per connection throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-connection-throttling","p":1663},{"i":1668,"t":"Another type of throttling in Centrifugo PRO is a per user ID in-memory throttling. Like per client throttling this one is also very efficient since also uses in-memory token buckets. The difference is that instead of throttling per individual client this type of throttling takes user ID into account. This type of throttling only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be throttled with it. The list of operations which can be throttled is similar to the in-memory throttling described above. But with additional connect method: total default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"user_command_throttling\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"updateActiveStatus\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }}","s":"In-memory per user throttling","u":"/docs/4/pro/throttling","h":"#in-memory-per-user-throttling","p":1663},{"i":1670,"t":"The next type of throttling in Centrifugo PRO is a distributed per user ID throttling with Redis as a bucket state storage. In this case limits are global for the entire Centrifugo cluster. If one user executed two commands on different Centrifugo nodes, Centrifugo consumes two tokens from the same bucket kept in Redis. Since this throttling goes to Redis to check limits, it adds some latency to a command processing. Our implementation tries to provide good throughput characteristics though – in our tests single Redis instance can handle more than 100k limit check requests per second. And it's possible to scale Redis in the same ways as for Centrifugo Redis Engine. This type of throttling only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be throttled with it. The implementation also uses token bucket algorithm internally. The list of operations which can be throttled is similar to the in-memory user command throttling described above. But without special bucket total: default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"redis_user_command_throttling\": { \"enabled\": true, \"redis_address\": \"localhost:6379\", \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"updateActiveStatus\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} Redis configuration for throttling feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for throttling feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis configuration declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_user_command_throttling\": { \"enabled\": true, \"use_redis_from_engine\": true, ... }} In this case throttling will simply connect to Redis instances configured for an Engine.","s":"Redis per user throttling","u":"/docs/4/pro/throttling","h":"#redis-per-user-throttling","p":1663},{"i":1672,"t":"Above we showed how you can define throttling strategies to protect server resources and prevent execution of many commands inside the connection and from certain user. But there are scenarios when abusive or broken connections may generate a significant load on the server just by calling commands and getting error responses due to throttling or due to other reasons (like malformed command). Centrifugo PRO provides a way to configure error limits per connection to deal with this case. Error limits are configured as in-memory buckets operating on a per-connection level. When these buckets are full due to lots of errors for an individual connection Centrifugo disconnects the client (with advice to not reconnect, so our SDKs may follow it). This way it's possible to get rid of the connection and rely on HTTP infrastracture tools to deal with client reconnections. Since WebSocket or other our transports (except unidirectional GRPC, but it's usually not available to the public port) are HTTP-based (or start with HTTP request in WebSocket Upgrade case) – developers can use Nginx limit_req_zone directive, Cloudflare rules, iptables, and so on, to protect Centrifugo from unwanted connections. tip Centrifugo PRO does not count internal errors for the error limit buckets – as internal errors is usually not a client's fault. The configuration on error limits per connection may look like this: config.json { ... \"client_error_limits\": { \"enabled\": true, \"total\": { \"buckets\" : [ { \"interval\": \"5s\", \"rate\": 20 } ] } }}","s":"Disconnecting abusive or misbehaving connections","u":"/docs/4/pro/throttling","h":"#disconnecting-abusive-or-misbehaving-connections","p":1663},{"i":1674,"t":"On this page","s":"Push notification API","u":"/docs/4/pro/push_notifications","h":"","p":1673},{"i":1676,"t":"We tried to be practical with our Push Notification API, let's look at its design choices and implementation properties we were able to achieve.","s":"Motivation and design choices","u":"/docs/4/pro/push_notifications","h":"#motivation-and-design-choices","p":1673},{"i":1678,"t":"To start delivering push notifications in the application, developers usually need to integrate with providers such as FCM, HMS, and APNs. This integration typically requires the storage of device tokens in the application database and the implementation of sending push messages to provider push services. Centrifugo PRO simplifies the process by providing a backend for device token storage, following best practices in token management. It reacts to errors and periodically removes stale devices/tokens to maintain a working set of device tokens based on provider recommendations.","s":"Storage for tokens","u":"/docs/4/pro/push_notifications","h":"#storage-for-tokens","p":1673},{"i":1680,"t":"Additionally, Centrifugo PRO provides an efficient, scalable queuing mechanism for sending push notifications. Developers can send notifications from the app backend to Centrifugo API with minimal latency and let Centrifugo process sending to FCM, HMS, APNs concurrently using built-in workers. In our tests, we achieved hundreds of thousands of pushes in tens of seconds.","s":"Efficient queuing","u":"/docs/4/pro/push_notifications","h":"#efficient-queuing","p":1673},{"i":1682,"t":"FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that client can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM, HMS topics by introducing an additional API to manage device subscriptions to topics. tip In some cases you may have real-time channels and device subscription topics with matching names – to send messages to both online and offline users. Though it's up to you. Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs. Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. Then – as soon as user registers a device – it will be automatically subscribed to its own topics. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with FCM – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call.","s":"Unified secure topics","u":"/docs/4/pro/push_notifications","h":"#unified-secure-topics","p":1673},{"i":1684,"t":"Unlike other solutions that combine different provider push sending APIs into a unified API, Centrifugo PRO provides a non-obtrusive proxy for all the mentioned providers. Developers can send notification payloads in a format defined by each provider. It's also possible to send notifications into native FCM, HMS topics or send to raw FCM, HMS, APNs tokens using Centrifugo PRO's push API, allowing them to combine native provider primitives with those added by Centrifugo (i.e., sending to a list of device IDs or to a list of topics).","s":"Non-obtrusive proxying","u":"/docs/4/pro/push_notifications","h":"#non-obtrusive-proxying","p":1673},{"i":1686,"t":"Furthermore, Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API.","s":"Builtin analytics","u":"/docs/4/pro/push_notifications","h":"#builtin-analytics","p":1673},{"i":1688,"t":"Add provider SDK on the frontend side, follow provider instructions for your platform to obtain a push token for a device. For example, for FCM see instructions for iOS, Android, Flutter, Web Browser). The same for HMS or APNs – frontend part should be handled by their native SDKs. Call Centrifugo PRO backend API with the obtained token. From the application backend call Centrifugo device_register API to register the device in Centrifugo PRO storage. Optionally provide list of topics to subscribe device to. Centrifugo returns a registered device object. Pass a generated device ID to the frontend and save it on the frontend together with a token received from FCM. Call Centrifugo send_push_notification API whenever it's time to deliver a push notification. At any moment you can inspect device storage by calling device_list API. Once user logs out from the app, you can detach user ID from device by using device_update or remove device with device_remove API.","s":"Steps to integrate","u":"/docs/4/pro/push_notifications","h":"#steps-to-integrate","p":1673},{"i":1690,"t":"In Centrifugo PRO you can configure one push provider or use all of them – this choice is up to you.","s":"Configuration","u":"/docs/4/pro/push_notifications","h":"#configuration","p":1673},{"i":1692,"t":"As mentioned above Centrifigo uses PostgreSQL for token storage. To enable push notifications make sure database section defined in the configration and fcm is in the push_notifications.enabled_providers list. Centrifugo PRO uses Redis for queuing push notification requests, so Redis address should be configured also. Finally, to integrate with FCM a path to the credentials file must be provided (see how to create one in this instruction). So the full configuration to start sending push notifications over FCM may look like this: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"fcm\"], \"fcm_credentials_file_path\": \"/path/to/service/account/credentials.json\" }} tip Actually, PostgreSQL database configuration is optional here – you can use push notifications API without it. In this case you will be able to send notifications to FCM, HMS, APNs raw tokens, FCM and HMS native topics and conditions. I.e. using Centrifugo as an efficient proxy for push notifications (for example if you already keep tokens in your database). But sending to device ids and topics, and token/topic management APIs won't be available for usage.","s":"FCM","u":"/docs/4/pro/push_notifications","h":"#fcm","p":1673},{"i":1694,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"hms\"], \"hms_app_id\": \"\", \"hms_app_secret\": \"\", }} tip See example how to get app id and app secret here.","s":"HMS","u":"/docs/4/pro/push_notifications","h":"#hms","p":1673},{"i":1696,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"apns\"], \"apns_endpoint\": \"development\", \"apns_bundle_id\": \"com.example.your_app\", \"apns_auth\": \"token\", \"apns_token_auth_key_path\": \"/path/to/auth/key/file.p8\", \"apns_token_key_id\": \"\", \"apns_token_team_id\": \"your_team_id\", }} We also support auth over p12 certificates with the following options: push_notifications.apns_cert_p12_path push_notifications.apns_cert_p12_b64 push_notifications.apns_cert_p12_password","s":"APNs","u":"/docs/4/pro/push_notifications","h":"#apns","p":1673},{"i":1698,"t":"push_notifications.max_inactive_device_days​ This option configures the number of days to keep device without updates. By default Centrifugo does not remove inactive devices.","s":"Other options","u":"/docs/4/pro/push_notifications","h":"#other-options","p":1673},{"i":1700,"t":"Coming soon 🚧 Centrifugo PRO utilizes Redis Streams as the default queue engine for push notifications. However, it also offers the option to employ PostgreSQL for queuing. It's as simple as: config.json { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"queue_engine\": \"database\", // rest of the options... }} tip Queue based on Redis streams is faster, so if you start with PostgreSQL based queue – you have an option to switch to a more performant implementation later. Though active push notifications will be lost during a switch.","s":"Use PostgreSQL as queue","u":"/docs/4/pro/push_notifications","h":"#use-postgresql-as-queue","p":1673},{"i":1703,"t":"Registers or updates device information. device_register request​ Field Type Required Description id string No ID of the device being registered (provide it when updating). provider string Yes Provider of the device token (valid choices: fcm, hms, apns). token string Yes Push notification token for the device. platform string Yes Platform of the device (valid choices: ios, android, web). user string No User associated with the device. topics array of strings No Device topic subscriptions. This should be a full list which replaces all the topics previously accociated with the device. User topics managed by UserTopic model will be automatically attached. tags map No Additional tags for the device (indexed key-value data). meta map No Additional metadata for the device (not indexed). device_register result​ Field Name Type Required Description id string Yes The device ID that was registered/updated.","s":"device_register","u":"/docs/4/pro/push_notifications","h":"#device_register","p":1673},{"i":1705,"t":"Call this method to update device. For example, when user logs out the app and you need to detach user ID from the device. device_update request​ Field Type Required Description ids repeated string No Device ids to filter users repeated string No Device users filter provider_tokens repeated DeviceProviderTokens No Provider tokens filter user_update DeviceUserUpdate No Optional user update object meta_update DeviceMetaUpdate No Optional device meta update object tags_update DeviceTagsUpdate No Optional device tags update object topics_update DeviceChannelsUpdate No Optional topics update object DeviceUserUpdate: Field Type Required Description user string Yes User to set DeviceMetaUpdate: Field Type Required Description meta map Yes Meta to set DeviceTagsUpdate: Field Type Required Description tags map Yes Tags to set DeviceChannelsUpdate: Field Type Required Description topics repeated string Yes Channels to set device_update result​ Empty object.","s":"device_update","u":"/docs/4/pro/push_notifications","h":"#device_update","p":1673},{"i":1707,"t":"Removes device from storage. This may be also called when user logs out the app and you don't need its device token after that. device_remove request​ Field Name Type Required Description ids repeated string No A list of device IDs to be removed users repeated string No A list of device user IDs to filter devices to remove provider_tokens ProviderTokens No Provider tokens to remove device_remove result​ Empty object.","s":"device_remove","u":"/docs/4/pro/push_notifications","h":"#device_remove","p":1673},{"i":1709,"t":"Returns a paginated list of registered devices according to request filter conditions. device_list request​ Field Type Required Description ids repeated string No List of device IDs to filter results. providers repeated string No List of device token providers to filter results. provider_tokens repeated ProviderTokens No Provider tokens to filter results. platforms repeated string No List of device platforms to filter results. users repeated string No List of device users to filter results. since string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_topics bool No Flag indicating whether to include topics information for each device. include_tags bool No Flag indicating whether to include tags information for each device. include_meta bool No Flag indicating whether to include meta information for each device. device_list result​ Field Name Type Required Description items repeated Device Yes A list of devices has_more bool Yes A flag indicating whether there are more devices available Device: Field Name Type Description id string The device's ID. provider string The device's token provider. token string The device's token. platform string The device's platform. user string The user associated with the device. topics array of strings Only included if include_topics was true tags map Only included if include_tags was true meta map Only included if include_meta was true","s":"device_list","u":"/docs/4/pro/push_notifications","h":"#device_list","p":1673},{"i":1711,"t":"Manage mapping of device to topics. device_topic_update request​ Field Type Required Description device_id string Yes Device ID. op string Yes add or remove or set topics repeated string No List of topics. device_topic_update result​ Empty object.","s":"device_topic_update","u":"/docs/4/pro/push_notifications","h":"#device_topic_update","p":1673},{"i":1713,"t":"List device to topic mapping. device_topic_list request​ Field Type Required Description device_ids repeated string No List of device IDs to filter results. device_providers repeated string No List of device token providers to filter results. device_provider_tokens repeated ProviderTokens No Provider tokens to filter results. device_platforms repeated string No List of device platforms to filter results. device_users repeated string No List of device users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Channel prefix to filter results. since string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_device bool No Flag indicating whether to include Device information for each object. device_topic_list result​ Field Name Type Required Description items repeated DeviceChannel Yes A list of DeviceChannel objects has_more bool Yes A flag indicating whether there are more devices available DeviceChannel: Field Type Required Description id string Yes ID of DeviceChannel device_id string Yes Device ID topic string Yes Channel","s":"device_topic_list","u":"/docs/4/pro/push_notifications","h":"#device_topic_list","p":1673},{"i":1715,"t":"Manage mapping of topics with users. These user topics will be automatically attached to user devices upon registering. And removed from device upon deattaching user. user_topic_update request​ Field Type Required Description user string Yes User ID. op string Yes add or remove or set topics repeated string No List of topics. user_topic_update result​ Empty object.","s":"user_topic_update","u":"/docs/4/pro/push_notifications","h":"#user_topic_update","p":1673},{"i":1717,"t":"List user to topic mapping. user_topic_list request​ Field Type Required Description users repeated string No List of users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Channel prefix to filter results. since string No Cursor for pagination (last id in previous batch, empty for first page). limit int32 No Maximum number of UserTopic objects to retrieve. user_topic_list result​ Field Name Type Description items repeated UserTopic A list of UserTopic objects has_more bool A flag indicating whether there are more devices available UserTopic: Field Type Required Description id string Yes ID of UserTopic user string Yes User ID topic string Yes Channel","s":"user_topic_list","u":"/docs/4/pro/push_notifications","h":"#user_topic_list","p":1673},{"i":1719,"t":"Send push notification to specific device_ids, or to topics, or native provider identifiers like fcm_tokens, or to fcm_topic. Request will be queued by Centrifugo, consumed by Centrifugo built-in workers and sent to the provider API. send_push_notification request​ Field name Type Required Description recipient PushRecipient Yes Recipient of push notification notification PushNotification Yes Push notification to send PushRecipient (you must set only one of the following fields): Field Type Required Description device_ids repeated string No Send to a list of device IDs (managed by Centrifugo) topics repeated string No Send to topics (managed by Centrifugo) fcm_tokens repeated string No Send to a list of FCM native tokens fcm_topic string No Send to a FCM native topic fcm_condition string No Send to a FCM native condition hms_tokens repeated string No Send to a list of HMS native tokens hms_topic string No Send to a HMS native topic hms_condition string No Send to a HMS native condition apns_tokens repeated string No Send to a list of APNs native tokens PushNotification: Field Type Required Description uid string No Unique send id, used for Centrifugo builtin analytics expire_at int64 No Unix timestamp when Centrifugo stops attempting to send this notification (this does not relate to notification TTL fields) fcm FcmPushNotification No Notification for FCM hms HmsPushNotification No Notification for HMS apns ApnsPushNotification No Notification for APNs FcmPushNotification: Field Type Required Description message JSON object Yes FCM Message described in FCM docs. HmsPushNotification: Field Type Required Description message JSON object Yes HMS Message described in HMS Push Kit docs. ApnsPushNotification: Field Type Required Description headers map No APNs headers payload JSON object Yes APNs payload send_push_notification result​ Field Name Type Description uid string Unique send id, matches uid in request if it was provided","s":"send_push_notification","u":"/docs/4/pro/push_notifications","h":"#send_push_notification","p":1673},{"i":1721,"t":"This API call is experimental, some changes may happen here. Centrifugo PRO also allows tracking status of push notification delivery and interaction. It's possible to use update_push_status API to save the updated status of push notification to the notifications analytics table. Then it's possible to build insights into push notification effectiveness by querying the table. The update_push_status API supposes that you are using uid field with each notification sent and you are using Centrifugo PRO generated device IDs (as described in steps to integrate). This is a part of server API at the moment, so you need to proxy requests to this endpoint over your backend. We can consider making this API suitable for requests from the client side – please reach out if your use case requires it. update_push_status request​ Field Type Required Description uid string Yes uid (unique send id) from send_push_notification status string Yes Status of push notification - delivered or interacted device_id string Yes Device ID msg_id string No Message ID update_push_status result​ Empty object.","s":"update_push_status","u":"/docs/4/pro/push_notifications","h":"#update_push_status","p":1673},{"i":1723,"t":"Several metrics are available to monitor the state of Centrifugo push worker system: centrifugo_push_notification_count - counter, shows total count of push notifications sent to providers (splitted by provider, recipient type, platform, success, error code). centrifugo_push_queue_consuming_lag - gauge, shows the lag of queues, should be close to zero most of the time. Splitted by provider and name of queue. centrifugo_push_consuming_inflight_jobs - gauge, shows immediate number of workers proceccing pushes. Splitted by provider and name of queue. centrifugo_push_job_duration_seconds - summary, provides insights about worker job duration timings. Splitted by provider and recipient type.","s":"Metrics","u":"/docs/4/pro/push_notifications","h":"#metrics","p":1673},{"i":1725,"t":"Coming soon.","s":"Further reading and tutorials","u":"/docs/4/pro/push_notifications","h":"#further-reading-and-tutorials","p":1673},{"i":1727,"t":"On this page","s":"User status API","u":"/docs/4/pro/user_status","h":"","p":1726},{"i":1729,"t":"Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript: await centrifuge.rpc('update_user_status', {}); note Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example. This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.","s":"Client-side status update RPC","u":"/docs/4/pro/user_status","h":"#client-side-status-update-rpc","p":1726},{"i":1731,"t":"It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"update_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Update user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to update status for Update user status result​ Empty object at the moment.","s":"update_user_status server API","u":"/docs/4/pro/user_status","h":"#update_user_status-server-api","p":1726},{"i":1733,"t":"Now on a backend side you have access to a bulk API to effectively get status of particular users. Call RPC method of server API (over HTTP or GRPC): curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"get_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api You should get a response like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\", \"active\":1627107289, \"online\":1627107289 } ] }} In case information about last status update time not available the response will be like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\" } ] }} I.e. status object will present in a response but active field won't be set for status object. Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time). Get user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to get status for Get user status result​ Field name Field type Optional Description statuses array of UserStatus no Statuses for each user in params (same order) UserStatus​ Field name Field type Optional Description user string no User ID active integer yes Last active time (Unix seconds) online integer yes Last online time (Unix seconds)","s":"get_user_status server API","u":"/docs/4/pro/user_status","h":"#get_user_status-server-api","p":1726},{"i":1735,"t":"If you need to clear user status information for some reason there is a delete_user_status server API call: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"delete_user_status\", \"params\": {\"users\": [\"42\"]}}' \\ http://localhost:8000/api Delete user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to delete status for Delete user status result​ Empty object at the moment.","s":"delete_user_status server API","u":"/docs/4/pro/user_status","h":"#delete_user_status-server-api","p":1726},{"i":1737,"t":"To enable Redis user status feature: config.json { ... \"user_status\": { \"enabled\": true, \"redis_address\": \"127.0.0.1:6379\" }} Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"user_status\": { \"enabled\": true, \"use_redis_from_engine\": true, }} In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine. expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day: config.json { ... \"user_status\": { ... \"expire_interval\": \"24h\" }}","s":"Configuration","u":"/docs/4/pro/user_status","h":"#configuration","p":1726},{"i":1739,"t":"On this page","s":"Channel JWT authorization","u":"/docs/4/server/channel_token_auth","h":"","p":1738},{"i":1741,"t":"For subscription JWT Centrifugo uses some standard claims defined in rfc7519, also some custom Centrifugo-specific.","s":"Subscription JWT claims","u":"/docs/4/server/channel_token_auth","h":"#subscription-jwt-claims","p":1738},{"i":1743,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). The value must match a user in connection JWT – since it's the same real-time connection. The missing claim will mean that token issued for anonymous user (i.e. with empty user ID).","s":"sub","u":"/docs/4/server/channel_token_auth","h":"#sub","p":1738},{"i":1745,"t":"Required. Channel that client tries to subscribe to with this token (string).","s":"channel","u":"/docs/4/server/channel_token_auth","h":"#channel","p":1738},{"i":1747,"t":"Optional. Additional information for connection inside this channel (valid JSON).","s":"info","u":"/docs/4/server/channel_token_auth","h":"#info","p":1738},{"i":1749,"t":"Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.","s":"b64info","u":"/docs/4/server/channel_token_auth","h":"#b64info","p":1738},{"i":1751,"t":"Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time. At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs. Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.","s":"exp","u":"/docs/4/server/channel_token_auth","h":"#exp","p":1738},{"i":1753,"t":"Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the subscription should expire. Set it to the future time for expiring subscription at some point Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.","s":"expire_at","u":"/docs/4/server/channel_token_auth","h":"#expire_at","p":1738},{"i":1755,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.","s":"aud","u":"/docs/4/server/channel_token_auth","h":"#aud","p":1738},{"i":1757,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.","s":"iss","u":"/docs/4/server/channel_token_auth","h":"#iss","p":1738},{"i":1759,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/4/server/channel_token_auth","h":"#iat","p":1738},{"i":1761,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/4/server/channel_token_auth","h":"#jti","p":1738},{"i":1763,"t":"One more claim is override. This is an object which allows overriding channel options for the particular channel subscriber which comes with subscription token. Field Type Optional Description presence BoolValue yes override presence channel option join_leave BoolValue yes override join_leave channel option force_push_join_leave BoolValue yes override force_push_join_leave channel option force_recovery BoolValue yes override force_recovery channel option force_positioning BoolValue yes override force_positioning channel option BoolValue is an object like this: { \"value\": true/false} So for example, you want to turn off emitting a presence information for a particular subscriber in a channel: { ... \"override\": { \"presence\": { \"value\": false } }}","s":"override","u":"/docs/4/server/channel_token_auth","h":"#override","p":1738},{"i":1765,"t":"So to generate a subscription token you can use something like this in Python (assuming user ID is 42 and the channel is gossips): import jwttoken = jwt.encode({ \"sub\": \"42\", \"channel\": \"$gossips\"}, \"secret\", algorithm=\"HS256\").decode()print(token) Where \"secret\" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).","s":"Example","u":"/docs/4/server/channel_token_auth","h":"#example","p":1738},{"i":1767,"t":"During development you can quickly generate valid subscription token using Centrifugo gensubtoken cli command. ./centrifugo gensubtoken -u 123722 -s channel You should see an output like this: HMAC SHA-256 JWT for user \"123722\" and channel \"channel\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDg0MzgsImNoYW5uZWwiOiJjaGFubmVsIn0.JyRI3ovNV-abV8VxCmZCD556o2F2mNL1UoU58gNR-uI But in real app subscription JWT must be generated by your application backend.","s":"With gensubtoken cli command","u":"/docs/4/server/channel_token_auth","h":"#with-gensubtoken-cli-command","p":1738},{"i":1769,"t":"On this page","s":"Channel permission model","u":"/docs/4/server/channel_permissions","h":"","p":1768},{"i":1771,"t":"By default, client's attempt to subscribe on a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel subscribe permissions: Provide subscription token Configure subscribe proxy Use user-limited channels Use subscribe_allowed_for_client namespace option Subscribe capabilities in connection token Subscribe capabilities in connect proxy Below, we are describing those in detail. Provide subscription token​ A client can provide a subscription token in subscribe request. See the format of the token. If client provides a valid token then subscription will be accepted. In Centrifugo PRO subscription token can additionally grant publish, history and presence permissions to a client. caution For namespaces with allow_subscribe_for_client option ON Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without token. This limitation exists to help users migrate to Centrifugo v4 without security risks. Configure subscribe proxy​ If client subscribes on a namespace with configured subscribe proxy then depending on proxy response subscription will be accepted or not. If a namespace has configured subscribe proxy, but user came with a token – then subscribe proxy is not used, we are relying on token in this case. If a namespace has subscribe proxy, but user subscribes on a user-limited channel – then subscribe proxy is not used also. Use user-limited channels​ If client subscribes on a user-limited channel and there is a user ID match then subscription will be accepted. caution User-limited channels must be enabled in a namespace using allow_user_limited_channels option. Use allow_subscribe_for_client namespace option​ allow_subscribe_for_client allows all authenticated non-anonymous connections to subscribe on all channels in a namespace. caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security. To additionally allow subscribing to anonymous connections take a look at allow_subscribe_for_anonymous option. Subscribe capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user subscribe to channels. Subscribe capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow user subscribe to channels.","s":"Subscribe permission model","u":"/docs/4/server/channel_permissions","h":"#subscribe-permission-model","p":1768},{"i":1773,"t":"tip In idiomatic Centrifugo use case data should be published to channels from the application backend (over server API). In this case backend can validate data, save it into persistent storage before publishing in real-time towards connections. When publishing from the client-side backend does not receive publication data at all – it just goes through Centrifugo (except using publish proxy). There are cases when direct publications from the client-side are desired (like typing indicators in chat applications) though. By default, client's attempt to publish data into a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel publish permissions: Configure publish proxy Use allow_publish_for_subscriber namespace option Use allow_publish_for_client namespace option Publish capabilities in connection token Publish capability in subscription token Publish capabilities in connect proxy Publish capability in subscribe proxy Use allow_publish_for_client namespace option​ allow_publish_for_client allows publications to channels of a namespace for all client connections. Use allow_publish_for_subscriber namespace option​ allow_publish_for_subscriber allows publications to channels of a namespace for all connections subscribed on a channel they want to publish data into. Configure publish proxy​ If client publishes to a namespace with configured publish proxy then depending on proxy response publication will be accepted or not. Configured publish proxy always used??? (what if user has permission in token or allow_publish_for_client?) Publish capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to channels. Publish capability in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to a channel. Publish capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client publish to certain channels. Publish capability in subscribe proxy​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber publish to channel.","s":"Publish permission model","u":"/docs/4/server/channel_permissions","h":"#publish-permission-model","p":1768},{"i":1775,"t":"By default, client's attempt to call history from a channel (with history retention configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel history permissions. Use allow_history_for_subscriber namespace option​ allow_history_for_subscriber allows history requests to all channels in a namespace for all client connections subscribed on a channel they want to call history for. Use allow_history_for_client namespace option​ allow_history_for_client allows history requests to all channels in a namespace for all client connections. History capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history for channels. History capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history from a channel. History capabilities in connect proxy​ This is a Centrifugo PRO feature. Connect proxy can return capability object to allow client call history from certain channels. History capability in subscribe proxy response​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber call history from channel.","s":"History permission model","u":"/docs/4/server/channel_permissions","h":"#history-permission-model","p":1768},{"i":1777,"t":"By default, client's attempt to call presence from a channel (with channel presence configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel presence permissions. Presence capability in subscribe proxy response​ Subscribe proxy can return capability object to allow subscriber call presence from channel. Use allow_presence_for_subscriber namespace option​ allow_presence_for_subscriber allows presence requests to all channels in a namespace for all client connections subscribed on a channel they want to call presence for. Use allow_presence_for_client namespace option​ allow_presence_for_client allows presence requests to all channels in a namespace for all client connections. Presence capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence for channels. Presence capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence of a channel. Presence capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client call presence from certain channels.","s":"Presence permission model","u":"/docs/4/server/channel_permissions","h":"#presence-permission-model","p":1768},{"i":1779,"t":"Server can whether turn on positioning for all channels in a namespace using \"force_positioning\": true option or client can create positioned subscriptions (but in this case client must have access to history capability).","s":"Positioning permission model","u":"/docs/4/server/channel_permissions","h":"#positioning-permission-model","p":1768},{"i":1781,"t":"Server can whether turn on automatic recovery for all channels in a namespace using \"force_recovery\": true option or client can create recoverable subscriptions (but in this case client must have access to history capability).","s":"Recovery permission model","u":"/docs/4/server/channel_permissions","h":"#recovery-permission-model","p":1768},{"i":1783,"t":"Server can whether force sending join/leave messages to all subscribers for all channels in a namespace using \"force_push_join_leave\": true option or client can ask server to include join/leave messages upon subscribing (but in this case client must have access to presence capability).","s":"Join/Leave permission model","u":"/docs/4/server/channel_permissions","h":"#joinleave-permission-model","p":1768},{"i":1785,"t":"On this page","s":"Client JWT authentication","u":"/docs/4/server/authentication","h":"","p":1784},{"i":1787,"t":"For connection JWT Centrifugo uses the some standart claims defined in rfc7519, also some custom Centrifugo-specific.","s":"Connection JWT claims","u":"/docs/4/server/authentication","h":"#connection-jwt-claims","p":1784},{"i":1789,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). If a user is not currently authenticated in an application, but you want to let him connect to Centrifugo anyway – you can use an empty string as a user ID in sub claim. This is called anonymous access. In this case, you may need to enable corresponding channel namespace options which enable access to protocol features for anonymous users.","s":"sub","u":"/docs/4/server/authentication","h":"#sub","p":1784},{"i":1791,"t":"This is a UNIX timestamp seconds when the token will expire. This is a standard JWT claim - all JWT libraries for different languages provide an API to set it. If exp claim is not provided then Centrifugo won't expire connection. When provided special algorithm will find connections with exp in the past and activate the connection refresh mechanism. Refresh mechanism allows connection to survive and be prolonged. In case of refresh failure, the client connection will be eventually closed by Centrifugo and won't be accepted until new valid and actual credentials are provided in the connection token. You can use the connection expiration mechanism in cases when you don't want users of your app to be subscribed on channels after being banned/deactivated in the application. Or to protect your users from token leakage (providing a reasonably short time of expiration). Choose exp value wisely, you don't need small values because the refresh mechanism will hit your application often with refresh requests. But setting this value too large can lead to slow user connection deactivation. This is a trade-off. Read more about connection expiration below.","s":"exp","u":"/docs/4/server/authentication","h":"#exp","p":1784},{"i":1793,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/4/server/authentication","h":"#iat","p":1784},{"i":1795,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/4/server/authentication","h":"#jti","p":1784},{"i":1797,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But you can force this check by setting token_audience string option: config.json { \"token_audience\": \"centrifugo\"} caution Setting token_audience will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.","s":"aud","u":"/docs/4/server/authentication","h":"#aud","p":1784},{"i":1799,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But you can force this check by setting token_issuer string option: config.json { \"token_issuer\": \"my_app\"} caution Setting token_issuer will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.","s":"iss","u":"/docs/4/server/authentication","h":"#iss","p":1784},{"i":1801,"t":"This claim is optional - this is additional information about client connection that can be provided for Centrifugo. This information will be included in presence information, join/leave events, and channel publication if it was published from a client-side.","s":"info","u":"/docs/4/server/authentication","h":"#info","p":1784},{"i":1803,"t":"If you are using binary Protobuf protocol you may want info to be custom bytes. Use this field in this case. This field contains a base64 representation of your bytes. After receiving Centrifugo will decode base64 back to bytes and will embed the result into various places described above.","s":"b64info","u":"/docs/4/server/authentication","h":"#b64info","p":1784},{"i":1805,"t":"An optional array of strings with server-side channels to subscribe a client to. See more details about server-side subscriptions.","s":"channels","u":"/docs/4/server/authentication","h":"#channels","p":1784},{"i":1807,"t":"An optional map of channels with options. This is like a channels claim but allows more control over server-side subscription since every channel can be annotated with info, data, and so on using options. tip This claim is called subs as a shortcut from subscriptions. The claim sub described above is a standart JWT claim to provide a user ID (it's a shortcut from subject). While claims have similar names they have different purpose in a connection JWT. Example: { ... \"subs\": { \"channel1\": { \"data\": {\"welcome\": \"welcome to channel1\"} }, \"channel2\": { \"data\": {\"welcome\": \"welcome to channel2\"} } }} Subscribe options:​ Field Type Optional Description info JSON object yes Custom channel info b64info string yes Custom channel info in Base64 - to pass binary channel info data JSON object yes Custom JSON data to return in subscription context inside Connect reply b64data string yes Same as data but in Base64 to send binary data override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false}","s":"subs","u":"/docs/4/server/authentication","h":"#subs","p":1784},{"i":1809,"t":"Meta is an additional JSON object (ex. {\"key\": \"value\"}) that will be attached to a connection. Unlike info it's never exposed to clients inside presence and join/leave payloads and only accessible on a backend side. It may be included in proxy calls from Centrifugo to the application backend (see proxy_include_connection_meta option). Also, there is a connections API method in Centrifugo PRO that returns this data in the connection description object.","s":"meta","u":"/docs/4/server/authentication","h":"#meta","p":1784},{"i":1811,"t":"By default, Centrifugo looks on exp claim to configure connection expiration. In most cases this is fine, but there could be situations where you wish to decouple token expiration check with connection expiration time. As soon as the expire_at claim is provided (set) in JWT Centrifugo relies on it for setting connection expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the connection should expire. Set it to the future time for expiring connection at some point Set it to 0 to disable connection expiration (but still check token exp claim).","s":"expire_at","u":"/docs/4/server/authentication","h":"#expire_at","p":1784},{"i":1813,"t":"As said above exp claim in a connection token allows expiring client connection at some point in time. Let's look in detail at what happens when Centrifugo detects that the connection is going to expire. First, you should do is enable client expiration mechanism in Centrifugo providing a connection JWT with expiration: import jwtimport timetoken = jwt.encode({\"sub\": \"42\", \"exp\": int(time.time()) + 10*60}, \"secret\").decode()print(token) Let's suppose that you set exp field to timestamp that will expire in 10 minutes and the client connected to Centrifugo with this token. During 10 minutes the connection will be kept by Centrifugo. When this time passed Centrifugo gives the connection some time (configured, 25 seconds by default) to refresh its credentials and provide a new valid token with new exp. When a client first connects to Centrifugo it receives the ttl value in connect reply. That ttl value contains the number of seconds after which the client must send the refresh command with new credentials to Centrifugo. Centrifugo clients must handle this ttl field and automatically start the refresh process. For example, a Javascript browser client will send an AJAX POST request to your application when it's time to refresh credentials. By default, this request goes to /centrifuge/refresh URL endpoint. In response your server must return JSON with a new connection JWT: { \"token\": token} So you must just return the same connection JWT for your user when rendering the page initially. But with actual valid exp. Javascript client will then send them to Centrifugo server and connection will be refreshed for a time you set in exp. In this case, you know which user wants to refresh its connection because this is just a general request to your app - so your session mechanism will tell you about the user. If you don't want to refresh the connection for this user - just return 403 Forbidden on refresh request to your application backend. Javascript client also has options to hook into a refresh mechanism to implement your custom way of refreshing. Other Centrifugo clients also should have hooks to refresh credentials but depending on client API for this can be different - see specific client docs.","s":"Connection expiration","u":"/docs/4/server/authentication","h":"#connection-expiration","p":1784},{"i":1815,"t":"Let's look at how to generate connection HS256 JWT in Python:","s":"Examples","u":"/docs/4/server/authentication","h":"#examples","p":1784},{"i":1817,"t":"Python NodeJS import jwttoken = jwt.encode({\"sub\": \"42\"}, \"secret\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret');console.log(token); Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users. Then you can pass this token to your client side and use it when connecting to Centrifugo: Using centrifuge-js v3 var centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: token});centrifuge.connect(); See more details about working with connection tokens and handling token expiration on the client-side in the real-time SDK API spec.","s":"Simplest token","u":"/docs/4/server/authentication","h":"#simplest-token","p":1784},{"i":1819,"t":"HS256 token that will be valid for 5 minutes: Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"exp\": int(time.time()) + 5*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42' }, 'secret', { expiresIn: 5 * 60 });console.log(token);","s":"Token with expiration","u":"/docs/4/server/authentication","h":"#token-with-expiration","p":1784},{"i":1821,"t":"Let's attach user name: Python NodeJS import jwtclaims = {\"sub\": \"42\", \"info\": {\"name\": \"Alexander Emelin\"}}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) var jwt = require('jsonwebtoken');var token = jwt.sign({ sub: '42', info: {\"name\": \"Alexander Emelin\"} }, 'secret');console.log(token);","s":"Token with additional connection info","u":"/docs/4/server/authentication","h":"#token-with-additional-connection-info","p":1784},{"i":1823,"t":"You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.","s":"Investigating problems with JWT","u":"/docs/4/server/authentication","h":"#investigating-problems-with-jwt","p":1784},{"i":1825,"t":"Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT). A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address). As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels. tip Read a tutorial in our blog about using Centrifugo with Keycloak SSO. In that case connection tokens are verified using public key loaded from the JWKS endpoint of Keycloak. At the moment Centrifugo caches keys loaded from an endpoint for one hour. Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment). Only RSA algorithm is supported. Once enabled JWKS used for both connection and channel subscription tokens.","s":"JSON Web Key support","u":"/docs/4/server/authentication","h":"#json-web-key-support","p":1784},{"i":1827,"t":"Available since Centrifugo v4.1.3 It's possible to extract variables from iss and aud JWT claims using Go regexp named groups, then use variables extracted during iss or aud matching to construct a JWKS endpoint dynamically upon token validation. In this case JWKS endpoint may be set in config as template. To achieve this Centrifugo provides two additional options: token_issuer_regex - match JWT issuer (iss claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. token_audience_regex - match JWT audience (aud claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. Let's look at the example: { \"token_issuer_regex\": \"https://example.com/auth/realms/(?P[A-z]+)\", \"token_jwks_public_endpoint\": \"https://keycloak:443/{{realm}}/protocol/openid-connect/certs\",} To use variable in token_jwks_public_endpoint it must be wrapped in {{ }}. When using token_issuer_regex and token_audience_regex make sure token_issuer and token_audience not used in the config - otherwise and error will be returned on Centrifugo start. caution Setting token_issuer_regex and token_audience_regex will also affect subscription tokens (used for channel token authorization). Please read this issue and reach out if your use case requires separate configuration for subscription tokens.","s":"Dynamic JWKs endpoint","u":"/docs/4/server/authentication","h":"#dynamic-jwks-endpoint","p":1784},{"i":1829,"t":"On this page","s":"Channels and namespaces","u":"/docs/4/server/channels","h":"","p":1828},{"i":1831,"t":"Centrifugo is a PUB/SUB system - it has publishers and subscribers. Channel is a route for publications. Clients can subscribe to a channel to receive all real-time messages published to a channel. A channel subscriber can also ask for a channel online presence or channel history information. Channel is just a string - news, comments, personal_feed are valid channel names. Though this string has some predefined rules as we will see below. You can define different channel behavior using a set of available channel options. Channels are ephemeral – you don't need to create them explicitly. Channels created automatically by Centrifugo as soon as the first client subscribes to a channel. As soon as the last subscriber leaves a channel - it's automatically cleaned up. Channel can belong to a channel namespace. Channel namespacing is a mechanism to define different behavior for different channels in Centrifugo. Using namespaces is a recommended way to manage channels – to turn on only those channel options which are required for a specific real-time feature you are implementing on top of Centrifugo. caution When using channel namespaces make sure you defined a namespace in configuration. Subscription attempts to a channel within a non-defined namespace will result into 102: unknown channel errors.","s":"What is channel","u":"/docs/4/server/channels","h":"#what-is-channel","p":1828},{"i":1833,"t":"Only ASCII symbols must be used in a channel string. Channel name length limited by 255 characters by default (controlled by configuration option channel_max_length). Several symbols in channel names reserved for Centrifugo internal needs: : – for namespace channel boundary (see below) # – for user channel boundary (see below) $ – for private channel prefix (see below) * – for the future Centrifugo needs & – for the future Centrifugo needs / – for the future Centrifugo needs","s":"Channel name rules","u":"/docs/4/server/channels","h":"#channel-name-rules","p":1828},{"i":1835,"t":": – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about about namespaces and channel options below. If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public. info A namespace is part of the channel name. If a user subscribed to a channel with namespace, like public:chat – then you need to publish messages into public:chat channel to be delivered to the user. We often see some confusion from developers trying to publish messages into chat and thinking that namespace is somehow stripped upon subscription. It's not true.","s":"namespace boundary (:)","u":"/docs/4/server/channels","h":"#namespace-boundary-","p":1828},{"i":1837,"t":"# – is a user channel boundary. This is a separator to create personal channels for users (we call this user-limited channels) without the need to provide a subscription token. For example, if the channel is news#42 then the only user with ID 42 can subscribe to this channel (Centrifugo knows user ID because clients provide it in connection credentials with connection JWT). If you want to create a user-limited channel in namespace personal then you can use a name like personal:user#42 for example. Moreover, you can provide several user IDs in channel name separated by a comma: dialog#42,43 – in this case only the user with ID 42 and user with ID 43 will be able to subscribe on this channel. This is useful for channels with a static list of allowed users, for example for single user personal messages channel, for dialog channel between certainly defined users. As soon as you need to manage access to a channel dynamically for many users this channel type does not suit well. tip User-limited channels must be enabled for a channel namespace using allow_user_limited_channels option. See below more information about channel options and channel namespaces.","s":"user channel boundary (#)","u":"/docs/4/server/channels","h":"#user-channel-boundary-","p":1828},{"i":1839,"t":"Centrifugo v4 has this option to achieve compatibility with previous Centrifugo versions. Previously (in Centrifugo v1, v2 and v3) only channels starting with $ could be subscribed with a subscription JWT. In Centrifugo v4 that's not the case anymore – clients can subscribe to any channel with a subscription token (if the token is valid – then subscription to a channel is accepted). But for namespaces with allow_subscribe_for_client option enabled Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without a subscription token. This limitation exists to help users migrate to Centrifugo v4 without security risks.","s":"private channel prefix ($)","u":"/docs/4/server/channels","h":"#private-channel-prefix-","p":1828},{"i":1841,"t":"Keep in mind that a channel is uniquely identified by its string representation. Do not expect that channels $news and news are the same. They are different because strings are not equal. So if a user subscribed to $news then user won't receive messages published to news. Channels dialog#42,43 and dialog#43,42 are two different channels too. Centrifugo only applies permission checks when a user subscribes to a channel. So if user-limited channels are enabled then the user with ID 42 will be able to subscribe on both dialog#42,43 and dialog#43,42. But Centrifugo does no magic regarding channel strings when keeping channel->to->subscribers map. So if the user subscribed on dialog#42,43 you must publish messages to exactly that channel: dialog#42,43. The same applies to channels with namespaces. Do not expect that channels chat:index and index are the same – they are different, moreover, belong to different namespaces. We'll look at the concept of channel namespaces in Centrifugo shortly.","s":"Channel is just a string","u":"/docs/4/server/channels","h":"#channel-is-just-a-string","p":1828},{"i":1843,"t":"It's possible to configure a list of channel namespaces. Namespaces are optional but very useful. A namespace allows setting custom options for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application. Namespace has a name, and the same channel options (with the same defaults) as described above. name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$). If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator: public:messages gossips:messages Where public and gossips are namespace names. Centrifugo looks for : symbol in the channel name, if found – extracts the namespace name, and applies namespace options while processing protocol commands from a client. All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured: config.json { \"token_hmac_secret_key\": \"very-long-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"30s\", \"namespaces\": [ { \"name\": \"facts\", \"history_size\": 10, \"history_ttl\": \"300s\" }, { \"name\": \"gossips\" } ]} Channel news will use globally defined channel options. Channel facts:sport will use facts namespace options. Channel gossips:sport will use gossips namespace options. Channel xxx:hello will result into subscription error since there is no xxx namespace defined in the configuration above. Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2. note There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly. There are many options which can be set for channel namespace (on top-level and to named one) to modify behavior of channels belonging to a namespace. Below we describe all these options.","s":"Channel namespaces","u":"/docs/4/server/channels","h":"#channel-namespaces","p":1828},{"i":1845,"t":"Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.","s":"Channel options","u":"/docs/4/server/channels","h":"#channel-options","p":1828},{"i":1847,"t":"presence (boolean, default false) – enable/disable online presence information for channels in a namespace. Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels. Let's say you have a channel chat:index and 2 users (with ID 2694 and 56) subscribed to it. And user 2694 has 2 connections to Centrifugo in different browser tabs. In presence data you may see sth like this: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"presence\", \"params\": {\"channel\": \"chat:index\"}}' \\ http://localhost:8000/api{ \"result\": { \"presence\": { \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\": { \"user\": \"2694\", \"client\": \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\", \"conn_info\": {\"name\": \"Alex\"} }, \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\": { \"user\": \"2694\", \"client\": \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\", \"conn_info\": {\"name\": \"Alex\"} } \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\": { \"user\": \"56\", \"client\": \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\", \"conn_info\": {\"name\": \"Alice\"} } } }} To call presence API from the client connection side client must have permission to do so. See presence permission model. caution Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or in a broker memory/disk). So only use it for channels where presence is required. See more details about online presence design.","s":"presence","u":"/docs/4/server/channels","h":"#presence","p":1828},{"i":1849,"t":"join_leave (boolean, default false) – enable/disable sending join and leave messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info (similar to entry inside presence information). Enabling join_leave means that Join/Leave messages will start being emitted, but by default they are not delivered to clients subscribed to a channel. You need to force this using namespace option force_push_join_leave or explicitly provide intent from a client-side (in this case client must have permission to call presence API). caution Keep in mind that join/leave messages can generate a huge number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature. It's hard to say what is \"large\" for you though – just estimate the load based on the fact that each subscribe/unsubscribe event in a channel with N subscribers will result into N messages broadcasted to all. If all clients reconnect at the same time the amount of generated messages is N^2. Join/leave messages distributed only with at most once delivery guarantee.","s":"join_leave","u":"/docs/4/server/channels","h":"#join_leave","p":1828},{"i":1851,"t":"Boolean, default false. When on all clients will receive join/leave events for a channel in a namespace automatically – without explicit intent to consume join/leave messages from the client side. If pushing join/leave is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel presence (by having an explicit capability or if allowed on a namespace level).","s":"force_push_join_leave","u":"/docs/4/server/channels","h":"#force_push_join_leave","p":1828},{"i":1853,"t":"history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted. Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below). caution Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk). So only use history for channels where it's required.","s":"history_size","u":"/docs/4/server/channels","h":"#history_size","p":1828},{"i":1855,"t":"history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision). As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels. By default history TTL duration is zero – this means that channel history is disabled. Again – to turn on history you should wisely configure both history_size and history_ttl options. For example for top-level channels (which do not belong to a namespace): config.json { ... \"history_size\": 10, \"history_ttl\": \"60s\"} Let's look at example. You enabled history for a namespace chat and sent two messages in channel chat:index. Then history will contain sth like this: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"history\", \"params\": {\"channel\": \"chat:index\", \"limit\": 100}}' \\ http://localhost:8000/api{ \"result\": { \"publications\": [ { \"data\": { \"input\": \"1\" }, \"offset\": 1 }, { \"data\": { \"input\": \"2\" }, \"offset\": 2 } ], \"epoch\": \"gWuY\", \"offset\": 2 }} To call history API from the client connection side client must have permission to do so. See history permission model. See additional information about offsets and epoch in History and recovery chapter. tip History persistence properties are dictated by Centrifugo engine used. For example, when using memory engine history is only kept till Centrifugo node restart. In Redis engine case persistence is determined by a Redis server persistence configuration (same for KeyDB and Tarantool).","s":"history_ttl","u":"/docs/4/server/channels","h":"#history_ttl","p":1828},{"i":1857,"t":"force_positioning (boolean, default false) – when the force_positioning option is on Centrifugo forces all subscriptions in a namespace to be positioned. I.e. Centrifugo will try to compensate at most once delivery of PUB/SUB broker checking client position inside a stream. If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API. force_positioning option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream). If positioning is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).","s":"force_positioning","u":"/docs/4/server/channels","h":"#force_positioning","p":1828},{"i":1859,"t":"force_recovery (boolean, default false) – when the position option is on Centrifugo forces all subscriptions in a namespace to be recoverable. When enabled Centrifugo will try to recover missed publications in channels after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the force_positioning option described above. force_recovery option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages). If recovery is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level). tip Not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.","s":"force_recovery","u":"/docs/4/server/channels","h":"#force_recovery","p":1828},{"i":1861,"t":"allow_subscribe_for_client (boolean, default false) – when on all non-anonymous clients will be able to subscribe to any channel in a namespace. To additionally allow anonymous users to subscribe turn on allow_subscribe_for_anonymous (see below). caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.","s":"allow_subscribe_for_client","u":"/docs/4/server/channels","h":"#allow_subscribe_for_client","p":1828},{"i":1863,"t":"allow_subscribe_for_anonymous (boolean, default false) – turn on if anonymous clients (with empty user ID) should be able to subscribe on channels in a namespace.","s":"allow_subscribe_for_anonymous","u":"/docs/4/server/channels","h":"#allow_subscribe_for_anonymous","p":1828},{"i":1865,"t":"allow_publish_for_subscriber (boolean, default false) - when the allow_publish_for_subscriber option is enabled client can publish into a channel in namespace directly from the client side over real-time connection but only if client subscribed to that channel. danger Keep in mind that in this case subscriber can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage). allow_publish_for_subscriber (or allow_publish_for_client mentioned below) option still can be useful to send something without backend-side validation and saving it into a database – for example, this option may be handy for demos and quick prototyping real-time app ideas.","s":"allow_publish_for_subscriber","u":"/docs/4/server/channels","h":"#allow_publish_for_subscriber","p":1828},{"i":1867,"t":"allow_publish_for_client (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). It's like allow_publish_for_subscriber – but client should not be a channel subscriber to publish. danger Keep in mind that in this case client can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).","s":"allow_publish_for_client","u":"/docs/4/server/channels","h":"#allow_publish_for_client","p":1828},{"i":1869,"t":"allow_publish_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to publish into channels in a namespace.","s":"allow_publish_for_anonymous","u":"/docs/4/server/channels","h":"#allow_publish_for_anonymous","p":1828},{"i":1871,"t":"allow_history_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call history API from that channel.","s":"allow_history_for_subscriber","u":"/docs/4/server/channels","h":"#allow_history_for_subscriber","p":1828},{"i":1873,"t":"allow_history_for_client (boolean, default false) – allows all clients to call history information in a namespace.","s":"allow_history_for_client","u":"/docs/4/server/channels","h":"#allow_history_for_client","p":1828},{"i":1875,"t":"allow_history_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call history from channels in a namespace.","s":"allow_history_for_anonymous","u":"/docs/4/server/channels","h":"#allow_history_for_anonymous","p":1828},{"i":1877,"t":"allow_presence_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call presence information from that channel.","s":"allow_presence_for_subscriber","u":"/docs/4/server/channels","h":"#allow_presence_for_subscriber","p":1828},{"i":1879,"t":"allow_presence_for_client (boolean, default false) – allows all clients to call presence information in a namespace.","s":"allow_presence_for_client","u":"/docs/4/server/channels","h":"#allow_presence_for_client","p":1828},{"i":1881,"t":"allow_presence_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call presence from channels in a namespace.","s":"allow_presence_for_anonymous","u":"/docs/4/server/channels","h":"#allow_presence_for_anonymous","p":1828},{"i":1883,"t":"allow_user_limited_channels (boolean, default false) - allows using user-limited channels in a namespace for checking subscribe permission. note If client subscribes to a user-limited channel while this option is off then server rejects subscription with 103: permission denied error.","s":"allow_user_limited_channels","u":"/docs/4/server/channels","h":"#allow_user_limited_channels","p":1828},{"i":1885,"t":"channel_regex (string, default \"\") – is an option to set a regular expression for channels allowed in the namespace. By default Centrifugo does not limit channel name variations. For example, if you have a namespace chat, then channel names inside this namespace are not really limited, it can be chat:index, chat:1, chat:2, chat:zzz and so on. But if you want to be strict and know possible channel patterns you can use channel_regex option. This is especially useful in namespaces where all clients can subscribe to channels. For example, let's only allow digits after chat: for channel names in a chat namespace: { \"namespaces\": [ { \"name\": \"chat\", \"allow_subscribe_for_client\": true, \"channel_regex\": \"^[\\d+]$\" } ]} danger Note, that we are skipping chat: part in regex. Since namespace prefix is the same for all channels in a namespace we only match the rest (after the prefix) of channel name. Channel regex only checked for client-side subscriptions, if you are using server-side subscriptions Centrifugo won't check the regex. Centrifugo uses Go language regexp package for regular expressions.","s":"channel_regex","u":"/docs/4/server/channels","h":"#channel_regex","p":1828},{"i":1887,"t":"proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter","s":"proxy_subscribe","u":"/docs/4/server/channels","h":"#proxy_subscribe","p":1828},{"i":1889,"t":"proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter","s":"proxy_publish","u":"/docs/4/server/channels","h":"#proxy_publish","p":1828},{"i":1891,"t":"proxy_sub_refresh (boolean, default false) – turns on sub refresh proxy, more info in proxy chapter","s":"proxy_sub_refresh","u":"/docs/4/server/channels","h":"#proxy_sub_refresh","p":1828},{"i":1893,"t":"subscribe_proxy_name (string, default \"\") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.","s":"subscribe_proxy_name","u":"/docs/4/server/channels","h":"#subscribe_proxy_name","p":1828},{"i":1895,"t":"publish_proxy_name (string, default \"\") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.","s":"publish_proxy_name","u":"/docs/4/server/channels","h":"#publish_proxy_name","p":1828},{"i":1897,"t":"sub_refresh_proxy_name (string, default \"\") – turns on sub refresh proxy when granular proxy mode is used. Note that proxy_sub_refresh option defined above is ignored in granular proxy mode.","s":"sub_refresh_proxy_name","u":"/docs/4/server/channels","h":"#sub_refresh_proxy_name","p":1828},{"i":1899,"t":"Let's look at how to set some of these options in a config. In this example we turning on presence, history features, forcing publication recovery. Also allowing all client connections (including anonymous users) to subscribe to channels and call publish, history, presence APIs if subscribed. config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true} Here we set channel options on config top-level – these options will affect channels without namespace. In many cases defining namespaces is a recommended approach so you can manage options for every real-time feature separately. With namespaces the above config may transform to: config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"namespaces\": [ { \"name\": \"feed\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true } ]} In this case channels should be prefixed with feed: to follow the behavior configured for a feed namespace.","s":"Channel config examples","u":"/docs/4/server/channels","h":"#channel-config-examples","p":1828},{"i":1901,"t":"On this page","s":"Helper CLI commands","u":"/docs/4/server/console_commands","h":"","p":1900},{"i":1903,"t":"To show Centrifugo version and exit run: centrifugo version","s":"version","u":"/docs/4/server/console_commands","h":"#version","p":1900},{"i":1905,"t":"Another command is genconfig: centrifugo genconfig -c config.json It will automatically generate the minimal required configuration file. This is mostly useful for development. If any errors happen – program will exit with error message and exit code 1. genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file: centrifugo genconfig -c config.toml","s":"genconfig","u":"/docs/4/server/console_commands","h":"#genconfig","p":1900},{"i":1907,"t":"Centrifugo has special command to check configuration file checkconfig: centrifugo checkconfig --config=config.json If any errors found during validation – program will exit with error message and exit code 1.","s":"checkconfig","u":"/docs/4/server/console_commands","h":"#checkconfig","p":1900},{"i":1909,"t":"Another command is gentoken: centrifugo gentoken -c config.json -u 28282 It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gentoken","u":"/docs/4/server/console_commands","h":"#gentoken","p":1900},{"i":1911,"t":"Another command is gensubtoken: centrifugo gensubtoken -c config.json -u 28282 -s channel It will automatically generate HMAC SHA-256 based subscription token for channel channel and user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -s channel -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gensubtoken","u":"/docs/4/server/console_commands","h":"#gensubtoken","p":1900},{"i":1913,"t":"One more command is checktoken: centrifugo checktoken -c config.json It will validate your connection JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checktoken","u":"/docs/4/server/console_commands","h":"#checktoken","p":1900},{"i":1915,"t":"One more command is checksubtoken: centrifugo checksubtoken -c config.json It will validate your subscription JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checksubtoken","u":"/docs/4/server/console_commands","h":"#checksubtoken","p":1900},{"i":1917,"t":"On this page","s":"Infrastructure tuning","u":"/docs/4/server/infra_tuning","h":"","p":1916},{"i":1919,"t":"You should increase a max number of open files Centrifugo process can open if you want to handle more connections. To get the current open files limit run: ulimit -n On Linux you can check limits for a running process using: cat /proc//limits The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default. See this document to get more info on how to increase this number. If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536. You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).","s":"Open files limit","u":"/docs/4/server/infra_tuning","h":"#open-files-limit","p":1916},{"i":1921,"t":"Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default). In order to eliminate a problem you can: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces See a post in Pusher blog about this problem and more detailed solution steps.","s":"Ephemeral port exhaustion","u":"/docs/4/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":1916},{"i":1923,"t":"On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state. Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side. Look how many socket descriptors in TIME_WAIT state. netstat -an |grep TIME_WAIT | grep | wc -l Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html The advices here are similar to ephemeral port exhaustion problem: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces","s":"Sockets in TIME_WAIT state","u":"/docs/4/server/infra_tuning","h":"#sockets-in-time_wait-state","p":1916},{"i":1925,"t":"Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established. Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.","s":"Proxy max connections","u":"/docs/4/server/infra_tuning","h":"#proxy-max-connections","p":1916},{"i":1927,"t":"More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.","s":"Conntrack table","u":"/docs/4/server/infra_tuning","h":"#conntrack-table","p":1916},{"i":1929,"t":"You should also consider adding additional protection to your Centrifugo endpoints. Centrifugo itself provides several options (described in configuration section) regarding server protection from the malicious behavior. Though an additional layer of DDOS protection on network or infrastructure level is highly recommended. For example, you may want to limit the number of connections coming from particular IP address. Here we list some possible ways you can use to protect your Centrifugo installation: Adding Nginx limit_conn_zone configuration Using stick tables of Haproxy Configuring rate limiting rules with Cloudflare The list is not exhaustive of course.","s":"Additional server protection","u":"/docs/4/server/infra_tuning","h":"#additional-server-protection","p":1916},{"i":1931,"t":"On this page","s":"Error and disconnect codes","u":"/docs/4/server/codes","h":"","p":1930},{"i":1933,"t":"Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client. Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).","s":"Client error codes","u":"/docs/4/server/codes","h":"#client-error-codes","p":1930},{"i":1935,"t":"Code: 100Message: \"internal server error\"Temporary: true Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.","s":"Internal","u":"/docs/4/server/codes","h":"#internal","p":1930},{"i":1937,"t":"Code: 101Message: \"unauthorized\" Error Unauthorized says that request is unauthorized.","s":"Unauthorized","u":"/docs/4/server/codes","h":"#unauthorized","p":1930},{"i":1939,"t":"Code: 102Message: \"unknown channel\" Error Unknown Channel means that channel name does not exist. Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.","s":"Unknown Channel","u":"/docs/4/server/codes","h":"#unknown-channel","p":1930},{"i":1941,"t":"Code: 103Message: \"permission denied\" Error Permission Denied means that access to resource not allowed.","s":"Permission Denied","u":"/docs/4/server/codes","h":"#permission-denied","p":1930},{"i":1943,"t":"Code: 104Message: \"method not found\" Error Method Not Found means that method sent in command does not exist.","s":"Method Not Found","u":"/docs/4/server/codes","h":"#method-not-found","p":1930},{"i":1945,"t":"Code: 105Message: \"already subscribed\" Error Already Subscribed returned when client wants to subscribe on channel it already subscribed to.","s":"Already Subscribed","u":"/docs/4/server/codes","h":"#already-subscribed","p":1930},{"i":1947,"t":"Code: 106Message: \"limit exceeded\" Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.","s":"Limit Exceeded","u":"/docs/4/server/codes","h":"#limit-exceeded","p":1930},{"i":1949,"t":"Code: 107Message: \"bad request\" Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.","s":"Bad Request","u":"/docs/4/server/codes","h":"#bad-request","p":1930},{"i":1951,"t":"Code: 108Message: \"not available\" Error Not Available means that resource is not enabled. For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.","s":"Not Available","u":"/docs/4/server/codes","h":"#not-available","p":1930},{"i":1953,"t":"Code: 109Message: \"token expired\" Error Token Expired indicates that connection token expired. Our SDKs handle it in a special way by updating token.","s":"Token Expired","u":"/docs/4/server/codes","h":"#token-expired","p":1930},{"i":1955,"t":"Code: 110Message: \"expired\" Error Expired indicates that connection expired (no token involved).","s":"Expired","u":"/docs/4/server/codes","h":"#expired","p":1930},{"i":1957,"t":"Code: 111Message: \"too many requests\"Temporary: true Error Too Many Requests means that server rejected request due to rate limiting strategies.","s":"Too Many Requests","u":"/docs/4/server/codes","h":"#too-many-requests","p":1930},{"i":1959,"t":"Code: 112Message: \"unrecoverable position\" Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch passed.","s":"Unrecoverable Position","u":"/docs/4/server/codes","h":"#unrecoverable-position","p":1930},{"i":1961,"t":"Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes). note We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. We expect these codes are mostly useful for logs and metrics.","s":"Client disconnect codes","u":"/docs/4/server/codes","h":"#client-disconnect-codes","p":1930},{"i":1963,"t":"Code: 3000Reason: \"connection closed\" DisconnectConnectionClosed is a special Disconnect object used when client connection was closed without any advice from a server side. This can be a clean disconnect, or temporary disconnect of the client due to internet connection loss. Server can not distinguish the actual reason of disconnect.","s":"DisconnectConnectionClosed","u":"/docs/4/server/codes","h":"#disconnectconnectionclosed","p":1930},{"i":1965,"t":"Client will reconnect after receiving such codes. Shutdown​ Code: 3001Reason: \"shutdown\" Disconnect Shutdown may be sent when node is going to shut down. DisconnectServerError​ Code: 3004Reason: \"internal server error\" DisconnectServerError issued when internal error occurred on server. DisconnectExpired​ Code: 3005Reason: \"connection expired\" DisconnectSubExpired​ Code: 3006Reason: \"subscription expired\" DisconnectSubExpired issued when client subscription expired. DisconnectSlow​ Code: 3008Reason: \"slow\" DisconnectSlow issued when client can't read messages fast enough. DisconnectWriteError​ Code: 3009Reason: \"write error\" DisconnectWriteError issued when an error occurred while writing to client connection. DisconnectInsufficientState​ Code: 3010Reason: \"insufficient state\" DisconnectInsufficientState issued when server detects wrong client position in channel Publication stream. Disconnect allows clien to restore missed publications on reconnect. DisconnectForceReconnect​ Code: 3011Reason: \"force reconnect\" DisconnectForceReconnect issued when server disconnects connection for some reason and whants it to reconnect. DisconnectNoPong​ Code: 3012Reason: \"no pong\" DisconnectNoPong may be issued when server disconnects bidirectional connection due to no pong received to application-level server-to-client pings in a configured time. DisconnectTooManyRequests​ Code: 3013Reason: \"too many requests\" DisconnectTooManyRequests may be issued when client sends too many commands to a server.","s":"Non-terminal disconnect codes","u":"/docs/4/server/codes","h":"#non-terminal-disconnect-codes","p":1930},{"i":1967,"t":"Client won't reconnect upon receiving such code. DisconnectInvalidToken​ Code: 3500Reason: \"invalid token\" DisconnectInvalidToken issued when client came with invalid token. DisconnectBadRequest​ Code: 3501Reason: \"bad request\" DisconnectBadRequest issued when client uses malformed protocol frames. DisconnectStale​ Code: 3502Reason: \"stale\" DisconnectStale issued to close connection that did not become authenticated in configured interval after dialing. DisconnectForceNoReconnect​ Code: 3503Reason: \"force disconnect\" DisconnectForceNoReconnect issued when server disconnects connection and asks it to not reconnect again. DisconnectConnectionLimit​ Code: 3504Reason: \"connection limit\" DisconnectConnectionLimit can be issued when client connection exceeds a configured connection limit (per user ID or due to other rule). DisconnectChannelLimit​ Code: 3505Reason: \"channel limit\" DisconnectChannelLimit can be issued when client connection exceeds a configured channel limit. DisconnectInappropriateProtocol​ Code: 3506Reason: \"inappropriate protocol\" DisconnectInappropriateProtocol can be issued when client connection format can not handle incoming data. For example, this happens when JSON-based clients receive binary data in a channel. This is usually an indicator of programmer error, JSON clients can not handle binary. DisconnectPermissionDenied​ Code: 3507Reason: \"permission denied\" DisconnectPermissionDenied may be issued when client attempts accessing a server without enough permissions. DisconnectNotAvailable​ Code: 3508Reason: \"not available\" DisconnectNotAvailable may be issued when ErrorNotAvailable does not fit message type, for example we issue DisconnectNotAvailable when client sends asynchronous message without MessageHandler set on server side. DisconnectTooManyErrors​ Code: 3509Reason: \"too many errors\" DisconnectTooManyErrors may be issued when client generates too many errors.","s":"Terminal disconnect codes","u":"/docs/4/server/codes","h":"#terminal-disconnect-codes","p":1930},{"i":1969,"t":"On this page","s":"Metrics monitoring","u":"/docs/4/server/monitoring","h":"","p":1968},{"i":1971,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus","u":"/docs/4/server/monitoring","h":"#prometheus","p":1968},{"i":1973,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite","u":"/docs/4/server/monitoring","h":"#graphite","p":1968},{"i":1975,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/4/server/monitoring","h":"#grafana-dashboard","p":1968},{"i":1977,"t":"On this page","s":"History and recovery","u":"/docs/4/server/history_and_recovery","h":"","p":1976},{"i":1979,"t":"History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero. Centrifugo is designed with an idea that history streams are ephemeral (can be created on the fly without explicit create call from Centrifugo users) and can expire or can be lost at any moment. Centrifugo provides a way for a client to understand that channel history lost. In this case, the main application database should be the source of truth and state recovery. When history is on every message published into a channel is saved into a history stream. The persistence properties of history are dictated by a Centrifugo engine used. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside Tarantool spaces. Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of state recovery anymore. tip History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.","s":"History design","u":"/docs/4/server/history_and_recovery","h":"#history-design","p":1976},{"i":1981,"t":"History iteration based on three fields: limit since reverse Combining these fields you can iterate over a stream in both directions. Get current stream top offset and epoch: history(limit: 0, since: null, reverse: false) Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: false) Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: true) Get history from the current beginning (up to 10): history(limit: 10, since: null, reverse: false) Get history from the current end in reversed direction (up to 10): history(limit: 10, since: null, reverse: true) Get up to 10 publications since known stream position (described by offset and epoch values): history(limit: 10, since: {offset: 0, epoch: \"epoch\"}, reverse: false) Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest): history(limit: 10, since: {offset: 11, epoch: \"epoch\"}, reverse: true) Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }}","s":"History iteration API","u":"/docs/4/server/history_and_recovery","h":"#history-iteration-api","p":1976},{"i":1983,"t":"One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while. In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnect at the same time). danger Message recovery protocol feature designed to be used together with reasonably small history stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time. To enable recovery mechanism for channels set the force_recovery boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. It's also possible to ask for enabling recovery from the client-side when configuring Subscription object – in this case client must have a permission to call history API. When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not. The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default. Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client SDKs (for bidirectional transport). The recovery process works this way: Let's suppose client subscribes on a channel with recovery on. Client receives subscribe reply from Centrifugo with two values: stream epoch and stream top offset, those values are saved by an SDK implementation. Every received publication has incremental offset, client SDK increments locally saved offset on each publication from a channel. Let's say at this point client disconnected for a while. Upon resubscribing to a channel SDK provides last seen epoch anf offset to Centrifugo. Centrifugo tries to load all the missed publications starting from the stream position provided by a client. If Centrifugo decides it can successfully recover client's state – then all missed publications returned in subscribe reply and client receives recovered: true in subscribed event context, and publication event handler of Subscription is called for every missed publication. Otherwise no publications returned and recovered flag of subscribed event context is set to false. epoch is useful for cases when history storage is temporary and can lose the history stream entirely. In this case comparing epoch values gives Centrifugo a tip that while publications exist and theoretically have same offsets as before - the stream is actually different, so it's impossible to use it for the recovery process. To summarize, here is a list of possible scenarios when Centrifugo can't recover client's state for a channel and provides recovered: false flag in subscribed event context: number of missed publications exceeds client_recovery_max_publication_limit option number of missed publications exceeds history_size namespace option client was away for a long time and history stream expired according to history_ttl namespace option storage used by Centrifugo engine lost the stream (restart, number of shards changed, cleared by the administrator, etc.) Having said this all, Centrifugo recovery is nice to keep the continuity of the connection and subscription. This speed-ups resubscribe in many cases and helps the backend to survive mass reconnect scenario since you avoid lots of requests for state loading. For setups with millions of connections this can be a life-saver. But we recommend applications to always have a way to load the state from the main application database. For example, on app reload. You can also manually implement your own recovery logic on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client resubscribe completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.","s":"Automatic message recovery","u":"/docs/4/server/history_and_recovery","h":"#automatic-message-recovery","p":1976},{"i":1985,"t":"On this page","s":"Configure Centrifugo","u":"/docs/4/server/configuration","h":"","p":1984},{"i":1987,"t":"Centrifugo can be configured in several ways.","s":"Configuration sources","u":"/docs/4/server/configuration","h":"#configuration-sources","p":1984},{"i":1989,"t":"Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer environment or configuration file sources. Command-line options have the highest priority when set than other ways to configure Centrifugo.","s":"Command-line flags","u":"/docs/4/server/configuration","h":"#command-line-flags","p":1984},{"i":1991,"t":"All Centrifugo options can be set over env in the format CENTRIFUGO_ (i.e. option name with CENTRIFUGO_ prefix, all in uppercase). Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags. Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use \"true\" value for an environment variable (or simply \"1\"). To set false use \"false\" or \"0\". Example: export CENTRIFUGO_PROMETHEUS=\"1\" Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example: export CENTRIFUGO_ALLOWED_ORIGINS=\"https://mysite1.example.com https://mysite2.example.com\" For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name. Empty environment variables are considered unset (!) and will fall back to the next configuration source.","s":"OS environment variables","u":"/docs/4/server/configuration","h":"#os-environment-variables","p":1984},{"i":1993,"t":"Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable prevails over the same option in config file). A simple way to start with Centrifugo is to run: centrifugo genconfig This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo: centrifugo -c config.json","s":"Configuration file","u":"/docs/4/server/configuration","h":"#configuration-file","p":1984},{"i":1995,"t":"Centrifugo supports three configuration file formats: JSON, YAML, or TOML.","s":"Config file formats","u":"/docs/4/server/configuration","h":"#config-file-formats","p":1984},{"i":1997,"t":"Here is an example of Centrifugo JSON configuration file: config.json { \"allowed_origins\": [\"http://localhost:3000\"], \"token_hmac_secret_key\": \"\", \"api_key\": \"\"} token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT. api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients. allowed_origins option described below.","s":"JSON config format","u":"/docs/4/server/configuration","h":"#json-config-format","p":1984},{"i":1999,"t":"Centrifugo also supports TOML format for configuration file: centrifugo --config=config.toml Where config.toml contains: config.toml allowed_origins: [ \"http://localhost:3000\" ]token_hmac_secret_key = \"\"api_key = \"\"log_level = \"debug\" In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.","s":"TOML config format","u":"/docs/4/server/configuration","h":"#toml-config-format","p":1984},{"i":2001,"t":"YAML format is also supported: config.yaml allowed_origins: - \"http://localhost:3000\"token_hmac_secret_key: \"\"api_key: \"\"log_level: debug With YAML remember to use spaces, not tabs when writing a configuration file.","s":"YAML config format","u":"/docs/4/server/configuration","h":"#yaml-config-format","p":1984},{"i":2003,"t":"Let's describe some important options you can configure when running Centrifugo.","s":"Important options","u":"/docs/4/server/configuration","h":"#important-options","p":1984},{"i":2005,"t":"This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins. As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array. Connection requests without Origin header set are passing through without any checks (i.e. always allowed). For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way: \"allowed_origins\": [ \"http://localhost:3000\"] When connecting from https://example.com: \"allowed_origins\": [ \"https://example.com\"] Origin pattern can contain wildcard symbol * to match subdomains: \"allowed_origins\": [ \"https://*.example.com\"] – in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check. It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature): \"allowed_origins\": [ \"*\"]","s":"allowed_origins","u":"/docs/4/server/configuration","h":"#allowed_origins","p":1984},{"i":2007,"t":"Bind your Centrifugo to a specific interface address (string, by default \"\" - listen on all available interfaces).","s":"address","u":"/docs/4/server/configuration","h":"#address","p":1984},{"i":2009,"t":"Port to bind Centrifugo to (string, by default \"8000\").","s":"port","u":"/docs/4/server/configuration","h":"#port","p":1984},{"i":2011,"t":"Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.","s":"engine","u":"/docs/4/server/configuration","h":"#engine","p":1984},{"i":2013,"t":"These options allow tweaking server behavior, in most cases default values are good to start with.","s":"Advanced options","u":"/docs/4/server/configuration","h":"#advanced-options","p":1984},{"i":2015,"t":"Default: 128 Sets the maximum number of different channel subscriptions a single client can have. tip When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.","s":"client_channel_limit","u":"/docs/4/server/configuration","h":"#client_channel_limit","p":1984},{"i":2017,"t":"Default: 255 Sets the maximum length of the channel name.","s":"channel_max_length","u":"/docs/4/server/configuration","h":"#channel_max_length","p":1984},{"i":2019,"t":"Default: 0 The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited. The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).","s":"client_user_connection_limit","u":"/docs/4/server/configuration","h":"#client_user_connection_limit","p":1984},{"i":2021,"t":"Added in Centrifugo v4.0.1 Default: 0 When set to a value > 0 client_connection_limit limits the max number of connections single Centrifugo node can handle. It acts on HTTP middleware level and stops processing request if the condition met. It logs a warning into logs in this case and increments centrifugo_node_client_connection_limit Prometheus counter. Client SDKs will attempt reconnecting. Some motivation behind this option may be found in this issue. Note, that at this point client_connection_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_limit","u":"/docs/4/server/configuration","h":"#client_connection_limit","p":1984},{"i":2023,"t":"Added in Centrifugo v4.1.1 Default: 0 client_connection_rate_limit sets the maximum number of HTTP requests to establish a new real-time connection a single Centrifugo node will accept per second (on real-time transport endpoints). All requests outside the limit will get 503 Service Unavailable code in response. Our SDKs handle this with backoff reconnection. By default, no limit is used. Note, that at this point client_connection_rate_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_rate_limit","u":"/docs/4/server/configuration","h":"#client_connection_rate_limit","p":1984},{"i":2025,"t":"Default: 1048576 Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.","s":"client_queue_max_size","u":"/docs/4/server/configuration","h":"#client_queue_max_size","p":1984},{"i":2027,"t":"Default: 0 client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently. By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).","s":"client_concurrency","u":"/docs/4/server/configuration","h":"#client_concurrency","p":1984},{"i":2029,"t":"Duration, default: 10s This option allows tuning the maximum time Centrifugo will wait for the connect frame (which contains authentication information) from the client after establishing connection. Default value should be reasonable for most use cases.","s":"client_stale_close_delay","u":"/docs/4/server/configuration","h":"#client_stale_close_delay","p":1984},{"i":2031,"t":"Default: false Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID). Access to channel operations should be explicitly enabled for anonymous connections.","s":"allow_anonymous_connect_without_token","u":"/docs/4/server/configuration","h":"#allow_anonymous_connect_without_token","p":1984},{"i":2033,"t":"Added in Centrifugo v4.1.1 Default: false When the option is set Centrifugo won't accept connections from anonymous users even if they provided a valid JWT. I.e. if token is valid, but sub claim is empty – then Centrifugo closes connection with advice to not reconnect again.","s":"disallow_anonymous_connection_tokens","u":"/docs/4/server/configuration","h":"#disallow_anonymous_connection_tokens","p":1984},{"i":2035,"t":"Default: 0 By default, Centrifugo runs on all available CPU cores (also Centrifugo can look at cgroup limits when rnning in Docker/Kubernetes). To limit the number of cores Centrifugo can utilize in one moment use this option.","s":"gomaxprocs","u":"/docs/4/server/configuration","h":"#gomaxprocs","p":1984},{"i":2037,"t":"After Centrifugo started there are several endpoints available.","s":"Endpoint configuration.","u":"/docs/4/server/configuration","h":"#endpoint-configuration","p":1984},{"i":2039,"t":"Bidirectional WebSocket default endpoint: ws://localhost:8000/connection/websocket Bidirectional emulation with HTTP-streaming (disabled by default): ws://localhost:8000/connection/http_stream Bidirectional emulation with SSE (EventSource) (disabled by default): ws://localhost:8000/connection/sse Bidirectional SockJS default endpoint (disabled by default): http://localhost:8000/connection/sockjs Unidirectional EventSource endpoint (disabled by default): http://localhost:8000/connection/uni_sse Unidirectional HTTP streaming endpoint (disabled by default): http://localhost:8000/connection/uni_http_stream Unidirectional WebSocket endpoint (disabled by default): http://localhost:8000/connection/uni_websocket Unidirectional SSE (EventSource) endpoint (disabled by default): http://localhost:8000/connection/uni_sse Server HTTP API endpoint: http://localhost:8000/api By default, all endpoints work on port 8000. This can be changed with port option: { \"port\": 9000} In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.","s":"Default endpoints.","u":"/docs/4/server/configuration","h":"#default-endpoints","p":1984},{"i":2041,"t":"Admin web UI endpoint works on root path by default, i.e. http://localhost:8000. For more details about admin web UI, refer to the Admin web UI documentation.","s":"Admin endpoints.","u":"/docs/4/server/configuration","h":"#admin-endpoints","p":1984},{"i":2043,"t":"Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config: { ... \"debug\": true} And endpoint: http://localhost:8000/debug/pprof/ – will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.","s":"Debug endpoints.","u":"/docs/4/server/configuration","h":"#debug-endpoints","p":1984},{"i":2045,"t":"Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag: centrifugo -c config.json --health","s":"Health check endpoint","u":"/docs/4/server/configuration","h":"#health-check-endpoint","p":1984},{"i":2047,"t":"We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal: API endpoint (/api) - for HTTP API requests Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format Health check endpoint (/health) - used to do health checks Debug endpoints (/debug/pprof) - used to inspect internal server state It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration. Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work. To run internal endpoints on custom port use internal_port option: { ... \"internal_port\": 9000} So admin web interface will work on address: http://localhost:9000 Also, debug page will be available on a new custom port too: http://localhost:9000/debug/pprof/ The same for API and Prometheus endpoints.","s":"Custom internal ports","u":"/docs/4/server/configuration","h":"#custom-internal-ports","p":1984},{"i":2049,"t":"To disable websocket endpoint set websocket_disable boolean option to true. To disable API endpoint set api_disable boolean option to true.","s":"Disable default endpoints","u":"/docs/4/server/configuration","h":"#disable-default-endpoints","p":1984},{"i":2051,"t":"It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options: admin_handler_prefix (default \"\") - to control Admin panel URL prefix websocket_handler_prefix (default \"/connection/websocket\") - to control WebSocket URL prefix http_stream_handler_prefix (default \"/connection/http_stream\") - to control HTTP-streaming URL prefix sse_handler_prefix (default \"/connection/sse\") - to control SSE/EventSource URL prefix sockjs_handler_prefix (default \"/connection/sockjs\") - to control SockJS URL prefix uni_sse_handler_prefix (default \"/connection/uni_sse\") - to control unidirectional Eventsource URL prefix uni_http_stream_handler_prefix (default \"/connection/uni_http_stream\") - to control unidirectional HTTP streaming URL prefix uni_websocket_handler_prefix (default \"/connection/uni_websocket\") - to control unidirectional WebSocket URL prefix api_handler_prefix (default \"/api\") - to control HTTP API URL prefix prometheus_handler_prefix (default \"/metrics\") - to control Prometheus URL prefix health_handler_prefix (default \"/health\") - to control health check URL prefix","s":"Customize handler endpoints","u":"/docs/4/server/configuration","h":"#customize-handler-endpoints","p":1984},{"i":2053,"t":"It's possible to send HUP signal to Centrifugo to reload a configuration: kill -HUP Though at moment this will only reload token secrets and channel options (top-level and namespaces). Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.","s":"Signal handling","u":"/docs/4/server/configuration","h":"#signal-handling","p":1984},{"i":2056,"t":"The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. It also disables permission checks on client API level - for presence and history calls. This mode can be useful for demo projects based on Centrifugo, integration tests, local projects, or real-time application prototyping. Don't use it in production until you 100% know what you are doing.","s":"Insecure client connection","u":"/docs/4/server/configuration","h":"#insecure-client-connection","p":1984},{"i":2058,"t":"This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules. The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.","s":"Insecure API mode","u":"/docs/4/server/configuration","h":"#insecure-api-mode","p":1984},{"i":2060,"t":"This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).","s":"Insecure admin mode","u":"/docs/4/server/configuration","h":"#insecure-admin-mode","p":1984},{"i":2062,"t":"Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use \"5s\". The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option). Valid time units are \"ms\" (milliseconds), \"s\" (seconds), \"m\" (minutes), \"h\" (hours). Some examples: \"1000ms\" // 1000 milliseconds\"1s\" // 1 second\"12h\" // 12 hours\"720h\" // 30 days","s":"Setting time duration options","u":"/docs/4/server/configuration","h":"#setting-time-duration-options","p":1984},{"i":2064,"t":"While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special: CENTRIFUGO_NAMESPACES='[{\"name\": \"ns1\"}, {\"name\": \"ns2\"}]' ./centrifugo I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.","s":"Setting namespaces over env","u":"/docs/4/server/configuration","h":"#setting-namespaces-over-env","p":1984},{"i":2066,"t":"Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage. Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal. To disable sending usage stats set usage_stats_disable option: config.json { \"usage_stats_disable\": true}","s":"Anonymous usage stats","u":"/docs/4/server/configuration","h":"#anonymous-usage-stats","p":1984},{"i":2068,"t":"On this page","s":"Load balancing","u":"/docs/4/server/load_balancing","h":"","p":2067},{"i":2070,"t":"Although it's possible to use Centrifugo without any reverse proxy before it, it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with edge cases when handling HTTP/Websocket connections from the wild. Also you probably want some sort of load balancing eventually between Centrifugo nodes so that proxy can be such a balancer too. In this section we will look at Nginx configuration to deploy Centrifugo. Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections. There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).","s":"Nginx configuration","u":"/docs/4/server/load_balancing","h":"#nginx-configuration","p":2067},{"i":2072,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}#server {# listen 80;# server_name centrifugo.example.com;# rewrite ^(.*) https://$server_name$1 permanent;#}server { server_name centrifugo.example.com; listen 80; #listen 443 ssl; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_certificate /etc/nginx/ssl/server.crt; #ssl_certificate_key /etc/nginx/ssl/server.key; include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1000; gzip_proxied any; # Only retry if there was a communication error, not a timeout # on the Centrifugo server (to avoid propagating \"queries of death\" # to all frontends) proxy_next_upstream error; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; location /connection { proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location / { proxy_pass http://centrifugo; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }}","s":"Separate domain for Centrifugo","u":"/docs/4/server/load_balancing","h":"#separate-domain-for-centrifugo","p":2067},{"i":2074,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}server { # ... your web site Nginx config location /centrifugo/ { rewrite ^/centrifugo/(.*) /$1 break; proxy_pass http://centrifugo; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } location /centrifugo/connection { rewrite ^/centrifugo(.*) $1 break; proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}","s":"Embed to a location of web site","u":"/docs/4/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":2067},{"i":2076,"t":"You may also need to update worker_connections option of Nginx: events { worker_connections 65535;}","s":"worker_connections","u":"/docs/4/server/load_balancing","h":"#worker_connections","p":2067},{"i":2078,"t":"On this page","s":"Engines and scalability","u":"/docs/4/server/engines","h":"","p":2077},{"i":2080,"t":"Used by default. Supports only one node. Nice choice to start with. Supports all features keeping everything in Centrifugo node process memory. You don't need to install Redis when using this engine. Advantages: Super fast since it does not involve network at all Does not require separate broker setup Disadvantages: Does not allow scaling nodes (actually you still can scale Centrifugo with Memory engine but you have to publish data into each Centrifugo node and you won't have consistent history and presence state throughout Centrifugo nodes) Does not persist message history in channels between Centrifugo restarts.","s":"Memory engine","u":"/docs/4/server/engines","h":"#memory-engine","p":2077},{"i":2082,"t":"history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. When using a history in a channel, Centrifugo keeps some metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help to expire metadata faster (or slower if you need it). The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Memory engine options","u":"/docs/4/server/engines","h":"#memory-engine-options","p":2077},{"i":2084,"t":"Redis is an open-source, in-memory data structure store, used as a database, cache, and message broker. Centrifugo Redis engine allows scaling Centrifugo nodes to different machines. Nodes will use Redis as a message broker (utilizing Redis PUB/SUB for node communication) and keep presence and history data in Redis. Minimal Redis version is 5.0.1 With Redis it's possible to come to the architecture like this:","s":"Redis engine","u":"/docs/4/server/engines","h":"#redis-engine","p":2077},{"i":2086,"t":"Several configuration options related to Redis engine. redis_address​ String, default \"127.0.0.1:6379\" - Redis server address. redis_password​ String, default \"\" - Redis password. redis_user​ String, default \"\" - Redis user for ACL-based auth. redis_db​ Integer, default 0 - number of Redis db to use. redis_prefix​ String, default \"centrifugo\" – custom prefix to use for channels and keys in Redis. redis_use_lists​ Boolean, default false – turns on using Redis Lists instead of Stream data structure for keeping history (not recommended, keeping this for backwards compatibility mostly). redis_force_resp2​ Available since Centrifugo v4.1.3 Boolean, default false. If set to true it forces using RESP2 protocol for communicating with Redis. By default, Redis client used by Centrifugo tries to detect supported Redis protocol automatically trying RESP3 first. history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. Similar to a Memory engine Redis engine also looks at history_meta_ttl option. Meta key in Redis is a HASH that contains the current offset number in channel and the stream epoch value. When using a history in a channel, Centrifugo saves metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help. The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Redis engine options","u":"/docs/4/server/engines","h":"#redis-engine-options","p":2077},{"i":2088,"t":"Some options may help you configuring TLS-protected communication between Centrifugo and Redis. redis_tls​ Boolean, default false - enable Redis TLS connection. redis_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_tls_skip_verify – but it will be removed in v5. redis_tls_cert​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_tls_cert_pem option. redis_tls_key​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_tls_key_pem option. redis_tls_root_ca​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_tls_root_ca_pem option. redis_tls_server_name​ Added in Centrifugo v4.1.0 String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Configuring Redis TLS","u":"/docs/4/server/engines","h":"#configuring-redis-tls","p":2077},{"i":2090,"t":"Let's see how to start several Centrifugo nodes using the Redis Engine. We will start 3 Centrifugo nodes and all those nodes will be connected via Redis. First, you should have Redis running. As soon as it's running - we can launch 3 Centrifugo instances. Open your terminal and start the first one: centrifugo --config=config.json --port=8000 --engine=redis --redis_address=127.0.0.1:6379 If your Redis is on the same machine and runs on its default port you can omit redis_address option in the command above. Then open another terminal and start another Centrifugo instance: centrifugo --config=config.json --port=8001 --engine=redis --redis_address=127.0.0.1:6379 Note that we use another port number (8001) as port 8000 is already busy by our first Centrifugo instance. If you are starting Centrifugo instances on different machines then you most probably can use the same port number (8000 or whatever you want) for all instances. And finally, let's start the third instance: centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379 Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes. To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation. tip In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers. Here is a live example where we locally start two Centrifugo nodes both connected to local Redis: Sorry, your browser doesn't support embedded video.","s":"Scaling with Redis tutorial","u":"/docs/4/server/engines","h":"#scaling-with-redis-tutorial","p":2077},{"i":2092,"t":"Centrifugo supports the official way to add high availability to Redis - Redis Sentinel. For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name: redis_sentinel_address (string, default \"\") - comma separated list of Sentinel addresses for HA. At least one known server required. redis_sentinel_master_name (string, default \"\") - name of Redis master Sentinel monitors Also: redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1 redis_sentinel_user - optional string user (used only in Redis ACL-based auth). So you can start Centrifugo which will use Sentinels to discover Redis master instances like this: centrifugo --config=config.json Where config.json: config.json { ... \"engine\": \"redis\", \"redis_sentinel_address\": \"127.0.0.1:26379\", \"redis_sentinel_master_name\": \"mymaster\"} Sentinel configuration file may look like this (for 3-node Sentinel setup with quorum 2): port 26379sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 10000sentinel failover-timeout mymaster 60000 You can find how to properly set up Sentinels in official documentation. Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.","s":"Redis Sentinel for high availability","u":"/docs/4/server/engines","h":"#redis-sentinel-for-high-availability","p":2077},{"i":2094,"t":"To configure TLS for Redis Sentinel use the following options. redis_sentinel_tls​ Boolean, default false - enable Redis TLS connection. redis_sentinel_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_sentinel_tls_skip_verify – but it will be removed in v5. redis_sentinel_tls_cert​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_sentinel_tls_cert_pem option. redis_sentinel_tls_key​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_sentinel_tls_key_pem option. redis_sentinel_tls_root_ca​ Added in Centrifugo v4.1.0 String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_sentinel_tls_root_ca_pem option. redis_sentinel_tls_server_name​ Added in Centrifugo v4.1.0 String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Redis Sentinel TLS","u":"/docs/4/server/engines","h":"#redis-sentinel-tls","p":2077},{"i":2096,"t":"Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case. For example, you can use something like this in Haproxy config: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:16379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin And then just point Centrifugo to this Haproxy: centrifugo --config=config.json --engine=redis --redis_address=\"localhost:16379\"","s":"Haproxy instead of Sentinel configuration","u":"/docs/4/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":2077},{"i":2098,"t":"Centrifugo has built-in Redis sharding support. This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale. At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples. To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"127.0.0.1:6379\", \"127.0.0.1:6380\", ]} To start Centrifugo with Redis instances on different hosts: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"192.168.1.34:6379\", \"192.168.1.35:6379\", ]} If you also need to customize AUTH password, Redis DB number then you can use an extended address notation. note Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers. When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).","s":"Redis sharding","u":"/docs/4/server/engines","h":"#redis-sharding","p":2077},{"i":2100,"t":"Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\" ]} You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start. To set the same over environment variable: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001\" CENTRIFUGO_ENGINE=redis ./centrifugo If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\", \"localhost:30101,localhost:30102,localhost:30103\" ]} Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump). To reproduce the same over environment variable use space to separate different clusters: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001,localhost:30002 localhost:30101,localhost:30102\" CENTRIFUGO_ENGINE=redis ./centrifugo","s":"Redis cluster","u":"/docs/4/server/engines","h":"#redis-cluster","p":2077},{"i":2102,"t":"EXPERIMENTAL Centrifugo Redis engine seamlessly works with KeyDB. KeyDB server is compatible with Redis and provides several additional features beyond. caution We can't give any promises about compatibility with KeyDB in the future Centrifugo releases - while KeyDB is fully compatible with Redis things should work just fine. That's why we consider this as EXPERIMENTAL feature. Use KeyDB instead of Redis only if you are sure you need it. Nothing stops you from running several Redis instances per each core you have, configure sharding, and obtain even better performance than KeyDB can provide (due to lack of synchronization between threads in Redis). To run Centrifugo with KeyDB all you need to do is use redis engine but run the KeyDB server instead of Redis.","s":"KeyDB Engine","u":"/docs/4/server/engines","h":"#keydb-engine","p":2077},{"i":2104,"t":"Other storages which are compatible with Centrifugo may work, but we did not make enough testing with them. Some of them still evolving and do not fully support Redis protocol. So if you want to use these storages with Centrifugo – please read carefully the notes below: AWS Elasticache – it was reported to work, but we suggest you testing the setup including failover tests and work under load. DragonflyDB - it's mostly compatible, the only problem with DragonflyDB v1.0.0 we observed is failing test regarding history iteration in reversed order (not very common). We have not tested a Redis Cluster emulation mode provided by DragonflyDB yet. We suggest you testing the setup including failover tests and work under load.","s":"Other Redis compatible","u":"/docs/4/server/engines","h":"#other-redis-compatible","p":2077},{"i":2106,"t":"EXPERIMENTAL Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics. caution EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves. There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge. info To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together. There are several supported Tarantool topologies to which Centrifugo can connect: One standalone Tarantool instance Many standalone Tarantool instances and consistently shard data between them Tarantool running in Cartridge Tarantool with replica and automatic failover in Cartridge Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7) After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes): config.json { ... \"engine\": \"tarantool\", \"tarantool_address\": \"127.0.0.1:3301\"} See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework. See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.","s":"Tarantool engine","u":"/docs/4/server/engines","h":"#tarantool-engine","p":2077},{"i":2108,"t":"tarantool_address​ String or array of strings. Default tcp://127.0.0.1:3301. Connection address to Tarantool. tarantool_mode​ String, default standalone A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication). All modes support client-side consistent sharding (similar to what Redis engine provides). tarantool_user​ String, default \"\". Allows setting a user. tarantool_password​ String, default \"\". Allows setting a password. history_meta_ttl​ Duration, default 2160h. Same option as for Memory engine and Redis engine also applies to Tarantool case.","s":"Tarantool engine options","u":"/docs/4/server/engines","h":"#tarantool-engine-options","p":2077},{"i":2110,"t":"It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below. Limitations: Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available. Nats wildcard channel subscriptions with symbols * and > not supported. First start Nats server: $ nats-server[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set][3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready Then start Centrifugo with broker option: centrifugo --broker=nats --config=config.json And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine): centrifugo --broker=nats --config=config.json --port=8001 Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.","s":"Nats broker","u":"/docs/4/server/engines","h":"#nats-broker","p":2077},{"i":2112,"t":"nats_url​ String, default nats://127.0.0.1:4222. Connection url in format nats://derek:pass@localhost:4222. nats_prefix​ String, default centrifugo. Prefix for channels used by Centrifugo inside Nats. nats_dial_timeout​ Duration, default 1s. Timeout for dialing with Nats. nats_write_timeout​ Duration, default 1s. Write (and flush) timeout for a connection to Nats.","s":"Options","u":"/docs/4/server/engines","h":"#options","p":2077},{"i":2114,"t":"On this page","s":"Online presence","u":"/docs/4/server/presence","h":"","p":2113},{"i":2116,"t":"Online presence provides an instantaneous snapshot of users currently connected to a specific channel. This information includes the user's unique ID and the connection timestamp.","s":"Overview","u":"/docs/4/server/presence","h":"#overview","p":2113},{"i":2118,"t":"To enable Online Presence, you need to set the presence option to true for the specific channel in your server configuration. { \"namespaces\": [{ \"namespace\": \"public\", \"presence\": true }]} After enabling this you can query presence information over server HTTP/GRPC presence call: curl --header \"Content-Type: application/json\" \\ --header \"Authorization: apikey YOUR_API_KEY\" \\ --request POST \\ --data '{\"method\": \"presence\", \"params\": {\"channel\": \"public:test\"}}' \\ http://localhost:8000/api See description of presence API.","s":"Enabling Online Presence","u":"/docs/4/server/presence","h":"#enabling-online-presence","p":2113},{"i":2120,"t":"Once presence enabled, you can retrieve the presence information on the client side too by calling the presence method on the channel. To do this you need to give the client permission to call presence. Once done, presence may be retrieved from the subscription: const presenceData = await subscription.presence(channel); It's also available on the top-level of the client (for example, if you need to call presence for server-side subscription): const presenceData = await client.presence(channel); If the permission check has passed successfully – both methods will return an object containing information about currently subscribed clients.","s":"Retrieving presence on the client side","u":"/docs/4/server/presence","h":"#retrieving-presence-on-the-client-side","p":2113},{"i":2122,"t":"Online Presence feature also allows real-time tracking of users joining or leaving a channel by subscribing to join and leave events: subscription.on('join', function(joinCtx) { console.log('client joined:', joinCtx);});subscription.on('leave', function(leaveCtx) { console.log('client left:', leaveCtx);}); And the same on client top-level for the needs of server-side subscriptions (analogous to the presence call described above). These events provide real-time updates and can be used to keep track of user activity and manage live interactions.","s":"Join and leave events","u":"/docs/4/server/presence","h":"#join-and-leave-events","p":2113},{"i":2124,"t":"The Online Presence feature might increase the load on your Centrifugo server, since Centrifugo need to maintain an addition data structure. Therefore, it is recommended to use this feature judiciously based on your server's capability and the necessity of real-time presence data in your application. Always make sure to secure the presence data, as it could expose sensitive information about user activity in your application. Ensure appropriate security measures are in place. Join and leave events delivered with at most once guarantee. See more about presence design in design overview chapter. Also check out FAQ.","s":"Implementation notes","u":"/docs/4/server/presence","h":"#implementation-notes","p":2113},{"i":2126,"t":"The Online Presence feature of Centrifugo is a highly useful tool for real-time applications. It provides instant and live data about user activity, which can be critical for interactive features like chats, collaborative tools, multiplayer games, or live tracking systems. Make sure to configure and use this feature appropriately to get the most out of your real-time application.","s":"Conclusion","u":"/docs/4/server/presence","h":"#conclusion","p":2113},{"i":2128,"t":"On this page","s":"Server-side subscriptions","u":"/docs/4/server/server_subs","h":"","p":2127},{"i":2130,"t":"See subscribe and unsubscribe server API","s":"Dynamic server-side subscriptions","u":"/docs/4/server/server_subs","h":"#dynamic-server-side-subscriptions","p":2127},{"i":2132,"t":"It's possible to automatically subscribe a user to a personal server-side channel. To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel. For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above. As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default \"\") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.","s":"Automatic personal channel subscription","u":"/docs/4/server/server_subs","h":"#automatic-personal-channel-subscription","p":2127},{"i":2134,"t":"Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes. user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect). This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel. Example config: config.json { \"user_subscribe_to_personal\": true, \"user_personal_single_connection\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} note Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.","s":"Maintain single user connection","u":"/docs/4/server/server_subs","h":"#maintain-single-user-connection","p":2127},{"i":2136,"t":"On this page","s":"Configure TLS","u":"/docs/4/server/tls","h":"","p":2135},{"i":2138,"t":"In first way you already have cert and key files. For development you can create self-signed certificate - see this instruction as example. config.json { ... \"tls\": true, \"tls_key\": \"server.key\", \"tls_cert\": \"server.crt\"} And run: ./centrifugo --config=config.json","s":"Using crt and key files","u":"/docs/4/server/tls","h":"#using-crt-and-key-files","p":2135},{"i":2140,"t":"For automatic certificates from Let's Encrypt add into configuration file: config.json { ... \"tls_autocert\": true, \"tls_autocert_host_whitelist\": \"www.example.com\", \"tls_autocert_cache_dir\": \"/tmp/certs\", \"tls_autocert_email\": \"user@example.com\", \"tls_autocert_http\": true, \"tls_autocert_http_addr\": \":80\"} tls_autocert (boolean) says Centrifugo that you want automatic certificate handling using ACME provider. tls_autocert_host_whitelist (string) is a string with your app domain address. This can be comma-separated list. It's optional but recommended for extra security. tls_autocert_cache_dir (string) is a path to a folder to cache issued certificate files. This is optional but will increase performance. tls_autocert_email (string) is optional - it's an email address ACME provider will send notifications about problems with your certificates. tls_autocert_http (boolean) is an option to handle http_01 ACME challenge on non-TLS port. tls_autocert_http_addr (string) can be used to set address for handling http_01 ACME challenge (default is :80) When configured correctly and your domain is valid (localhost will not work) - certificates will be retrieved on first request to Centrifugo. Also Let's Encrypt certificates will be automatically renewed. There are two options that allow Centrifugo to support TLS client connections from older browsers such as Chrome 49 on Windows XP and IE8 on XP: tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys. tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints. uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.","s":"Automatic certificates","u":"/docs/4/server/tls","h":"#automatic-certificates","p":2135},{"i":2142,"t":"You can provide custom certificate files to configure TLS for GRPC API server. grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server","s":"TLS for GRPC API","u":"/docs/4/server/tls","h":"#tls-for-grpc-api","p":2135},{"i":2144,"t":"You can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint. uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server","s":"TLS for GRPC unidirectional stream","u":"/docs/4/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":2135},{"i":2146,"t":"On this page","s":"Client protocol","u":"/docs/4/transports/client_protocol","h":"","p":2145},{"i":2148,"t":"Centrifugo is built on top of Centrifuge library for Go. Centrifuge library uses its own framing for wrapping Centrifuge-specific messages – synchronous commands from a client to a server (which expect replies from a server) and asynchronous pushes. Centrifuge client protocol is defined by a Protobuf schema. This is the source of truth. tip At the moment Protobuf schema contains some fields which are only used in client protocol v1. This is for backwards compatibility – server supports clients connecting over both client protocol v2 and client protocol v1. Client protocol v1 is considered deprecated and will be removed at some point in the future (giving enough time to our users to migrate).","s":"Protobuf schema","u":"/docs/4/transports/client_protocol","h":"#protobuf-schema","p":2145},{"i":2150,"t":"In bidirectional case client sends Command to a server and server sends Reply to a client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages. Each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous transport where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before on SDK level. In JSON case client can send command like this: {\"id\": 1, \"subscribe\": {\"channel\": \"example\"}} And client can expect something like this in response: {\"id\": 1, \"subscribe\": {}} Reply for different commands has corresponding field with command result (\"subscribe\" in example above). Reply can also contain error if Command processing resulted into an error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can handle result object appropriately. error looks like this in JSON case: { \"code\": 100, \"message\": \"internal server error\", \"temporary\": true} I.e. reply with error may look like this: {\"id\": 1, \"error\": {\"code\": 100, \"message\": \"internal server error\"}} We will talk more about error handling below. Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow. Current commands: connect – sent to authenticate connection, sth like hello from a client which can carry authentication token and arbitrary data. subscribe – sent to subscribe to a channel unsubscribe - sent to unsubscribe from a channel publish - sent to publish data into a channel presence - sent to request presence information from a channel presence_stats - sent to request presence stats information from a channel history - sent to request history information for a channel send - sent to send async message to a server (this command is a bit special since it must not contain id - as we don't wait for any response from a server in this case). rpc - sent to send RPC to a channel (execute arbitrary logic and wait for response) refresh - sent to refresh connection token sub_refresh - sent to refresh channel subscription token","s":"Command-Reply","u":"/docs/4/transports/client_protocol","h":"#command-reply","p":2145},{"i":2152,"t":"The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to a client at any moment - not as reaction to issued Command but as a message from a server to a client at arbitrary time. For example, this can be a message published into channel. There are several types of asynchronous messages that can come from a server to a client. pub is a message published into channel join messages sent when someone joined (subscribed on) channel. leave messages sent when someone left (unsubscribed from) channel. unsubscribe message sent when a server unsubscribed current client from a channel: subscribe may be sent when a server subscribes client to a channel. disconnect may be sent be a server before closing connection and contains disconnect code/reason message may be sent when server sends asynchronous message to a client connect push can be sent in unidirectional transport case refresh may be sent when a server refreshes client credentials (useful in unidirectional transports)","s":"Asynchronous pushes","u":"/docs/4/transports/client_protocol","h":"#asynchronous-pushes","p":2145},{"i":2154,"t":"To reduce number of system calls one request from a client to a server and one response from a server to a client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data. When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \\n: {\"id\": 1, \"subscribe\": {\"channel\": \"ch1\"}}{\"id\": 2, \"subscribe\": {\"channel\": \"ch2\"}} Here is an example how we do this in Javascript client when JSON format used: function encodeCommands(commands) { const encodedCommands = []; for (const i in commands) { if (commands.hasOwnProperty(i)) { encodedCommands.push(JSON.stringify(commands[i])); } } return encodedCommands.join('\\n');} info This doc uses JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo. When Protobuf format used then many Command can be sent from a client to a server in a length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example. The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there. tip Server can even send reply to a command and asynchronous message batched together in a one frame. For example here is how we read server response and extracting individual replies in Javascript client when JSON format used: function decodeReplies(data) { const replies = []; const encodedReplies = data.split('\\n'); for (const i in encodedReplies) { if (encodedReplies.hasOwnProperty(i)) { if (!encodedReplies[i]) { continue; } const reply = JSON.parse(encodedReplies[i]); replies.push(reply); } } return replies;} For Protobuf case see existing client implementations for decoding example.","s":"Top level batching","u":"/docs/4/transports/client_protocol","h":"#top-level-batching","p":2145},{"i":2156,"t":"To maintain connection alive and detect broken connections server periodically sends empty commands to clients and expects empty replies from them. When client does not receive ping from a server for some time it can consider connection broken and try to reconnect. Usually a server sends pings every 25 seconds.","s":"Ping Pong","u":"/docs/4/transports/client_protocol","h":"#ping-pong","p":2145},{"i":2158,"t":"Client should handle disconnect advices from server. In websocket case disconnect advice is sent in CLOSE Websocket frame. Disconnect advice contains uint32 code and human-readable string reason.","s":"Handle disconnects","u":"/docs/4/transports/client_protocol","h":"#handle-disconnects","p":2145},{"i":2160,"t":"This section contains advices to error handling in client implementations. Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems. Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again. Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side. The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance. Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).","s":"Handle errors","u":"/docs/4/transports/client_protocol","h":"#handle-errors","p":2145},{"i":2162,"t":"Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in a reply to a subscribe command.","s":"Additional notes","u":"/docs/4/transports/client_protocol","h":"#additional-notes","p":2145},{"i":2164,"t":"On this page","s":"Client real-time SDKs","u":"/docs/4/transports/client_sdk","h":"","p":2163},{"i":2166,"t":"centrifuge-js – for browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-dart - for Dart and Flutter centrifuge-swift – for native iOS development centrifuge-java – for native Android development and general Java See a description of client protocol if you want to write a custom bidirectional connector or eager to learn how Centrifugo protocol internals are structured.","s":"List of client SDKs","u":"/docs/4/transports/client_sdk","h":"#list-of-client-sdks","p":2163},{"i":2168,"t":"Centrifugo real-time SDKs work using two possible serialization formats: JSON and Protobuf. The entire bidirectional client protocol is described by the Protobuf schema. But those Protobuf messages may be also encoded as JSON objects (in JSON representation bytes fields in the Protobuf schema is replaced by the embedded JSON object in Centrifugo case). Our Javascript SDK - centrifuge-js - uses JSON serialization for protocol frames by default. This makes communication with Centrifugo server convenient as we are exchanging human-readable JSON frames between client and server. And it makes it possible to use centrifuge-js without extra dependency to protobuf.js library. It's possible to switch to Protobuf protocol with centrifuge-js SDK though, in case you want more compact Centrifuge protocol representation, faster decode/encode speeds on Centrifugo server side, or payloads you need to pass are custom binary. See more details on how to use centrifuge-js with Protobuf serialization in README. centrifuge-go real-time SDK for Go language also supports both JSON and Protobuf formats when communicating with Centrifugo server. Other SDKs, like centrifuge-dart, centrifuge-swift, centrifuge-java work using only Protobuf serialization for Centrifuge protocol internally. So they utilize the fastest and the most compact wire representation by default. Note, that while internally in those SDKs the serialization format is Protobuf, you can still send JSON towards these clients as JSON objects may be encoded as UTF-8 bytes. So these SDKs may work with both custom binary and JSON payloads. There are some important notes about JSON and Protobuf interoperability mentioned in our FAQ.","s":"Protobuf and JSON formats in SDKs","u":"/docs/4/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":2163},{"i":2170,"t":"Below you can find an information regarding support of different features in our official client SDKs","s":"SDK feature matrix","u":"/docs/4/transports/client_sdk","h":"#sdk-feature-matrix","p":2163},{"i":2172,"t":"Client feature js dart swift go java connect to a server ✅ ✅ ✅ ✅ ✅ setting client options ✅ ✅ ✅ ✅ ✅ automatic reconnect with backoff algorithm ✅ ✅ ✅ ✅ ✅ client state changes ✅ ✅ ✅ ✅ ✅ command-reply ✅ ✅ ✅ ✅ ✅ command timeouts ✅ ✅ ✅ ✅ ✅ async pushes ✅ ✅ ✅ ✅ ✅ ping-pong ✅ ✅ ✅ ✅ ✅ connection token refresh ✅ ✅ ✅ ✅ ✅ handle disconnect advice from server ✅ ✅ ✅ ✅ ✅ server-side subscriptions ✅ ✅ ✅ ✅ ✅ batching API ✅ bidirectional WebSocket emulation ✅","s":"Connection related features","u":"/docs/4/transports/client_sdk","h":"#connection-related-features","p":2163},{"i":2174,"t":"Client feature js dart swift go java subscrbe to a channel ✅ ✅ ✅ ✅ ✅ setting subscription options ✅ ✅ ✅ ✅ ✅ automatic resubscribe with backoff algorithm ✅ ✅ ✅ ✅ ✅ subscription state changes ✅ ✅ ✅ ✅ ✅ subscription command-reply ✅ ✅ ✅ ✅ ✅ subscription async pushes ✅ ✅ ✅ ✅ ✅ subscription token refresh ✅ ✅ ✅ ✅ ✅ handle unsubscribe advice from server ✅ ✅ ✅ ✅ ✅ manage subscription registry ✅ ✅ ✅ ✅ ✅ optimistic subscriptions ✅","s":"Client-side subscription related features","u":"/docs/4/transports/client_sdk","h":"#client-side-subscription-related-features","p":2163},{"i":2176,"t":"On this page","s":"HTTP streaming, with bidirectional emulation","u":"/docs/4/transports/http_stream","h":"","p":2175},{"i":2179,"t":"Boolean, default: false. Enables HTTP streaming endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"http_stream\": true}","s":"http_stream","u":"/docs/4/transports/http_stream","h":"#http_stream","p":2175},{"i":2181,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"http_stream_max_request_body_size","u":"/docs/4/transports/http_stream","h":"#http_stream_max_request_body_size","p":2175},{"i":2183,"t":"On this page","s":"Real-time transports","u":"/docs/4/transports/overview","h":"","p":2182},{"i":2185,"t":"Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus. Bidirectional transports come with a cost that developers need to use a special client connector library (SDK) which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state, handle request queueing/timeouts/errors, etc. Centrifugo has several official client SDKs for popular environments. All of them work over WebSocket transport. Our Javascript SDK also offers bidirectional fallbacks over HTTP-Streaming, Server-Sent Events (SSE) or SockJS.","s":"Bidirectional","u":"/docs/4/transports/overview","h":"#bidirectional","p":2182},{"i":2187,"t":"Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time. The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource/SSE, HTTP-streaming), or GRPC generated code to receive real-time updates from Centrifugo. Thus avoiding dependency to a client connector that abstracts bidirectional communication. The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo).","s":"Unidirectional","u":"/docs/4/transports/overview","h":"#unidirectional","p":2182},{"i":2189,"t":"In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as: message Push { string channel = 2; Publication pub = 4; Join join = 5; Leave leave = 6; Unsubscribe unsubscribe = 7; Message message = 8; Subscribe subscribe = 9; Connect connect = 10; Disconnect disconnect = 11; Refresh refresh = 12;} tip Some numbers in Protobuf definitions skipped for backwards compatibility with previous client protocol version. So unidirectional connection will receive various pushes. Every push contains one of the following objects: Publication Join Leave Unsubscribe Message Subscribe Connect Disconnect Refresh Some pushes belong to a channel which may be set on Push top level. All you need to do is look at Push, process messages you are interested in and ignore others. In most cases you will be most interested in pushes which contain Connect or Publication messages. For example, according to protocol schema Publication message type looks like this: message Publication { bytes data = 4; ClientInfo info = 5; uint64 offset = 6; map tags = 7;} tip In JSON protocol case Centrifugo replaces bytes type with embedded JSON. Just try using any unidirectional transport and you will quickly get the idea.","s":"Unidirectional message types","u":"/docs/4/transports/overview","h":"#unidirectional-message-types","p":2182},{"i":2191,"t":"Centrifugo server periodically sends pings to clients and expects pong from clients that works over bidirectional transports. Sending ping and receiving pong allows to find broken connections faster. Centrifugo sends pings on the Centrifugo client protocol level, thus it's possible for clients to handle ping messages on the client side to make sure connection is not broken (our bidirectional SDKs do this automatically). By default Centrifugo sends pings every 25 seconds. This may be changed using ping_interval option (duration, default \"25s\"). Centrifugo expects pong message from bidirectional client SDK after sending ping to it. By default, it waits no more than 8 seconds before closing a connection. This may be changed using pong_timeout option (duration, default \"8s\"). In most cases default ping/pong intervals are fine so you don't really need to tweak them. Reducing timeouts may help you to find non-gracefully closed connections faster, but will increase network traffic and CPU resource usage since ping/pongs are sent faster. caution ping_interval must be greater than pong_timeout in the current implementation. Here is a scheme how ping/pong works in bidirectional and unidirectional client scenarios:","s":"PING/PONG behavior","u":"/docs/4/transports/overview","h":"#pingpong-behavior","p":2182},{"i":2193,"t":"On this page","s":"SockJS","u":"/docs/4/transports/sockjs","h":"","p":2192},{"i":2195,"t":"caution There are several important caveats to know when using SockJS – see below.","s":"SockJS caveats","u":"/docs/4/transports/sockjs","h":"#sockjs-caveats","p":2192},{"i":2197,"t":"First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend. For example, with Nginx sticky support can be enabled with ip_hash directive for upstream: upstream centrifugo { ip_hash; server 127.0.0.1:8000; server 127.0.0.2:8000;} With this configuration Nginx will proxy connections with the same ip address to the same upstream backend. But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair. So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.","s":"Sticky sessions","u":"/docs/4/transports/sockjs","h":"#sticky-sessions","p":2192},{"i":2199,"t":"SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.","s":"Browser only","u":"/docs/4/transports/sockjs","h":"#browser-only","p":2192},{"i":2201,"t":"One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.","s":"JSON only","u":"/docs/4/transports/sockjs","h":"#json-only","p":2192},{"i":2204,"t":"Boolean, default: false. Enables SockJS transport.","s":"sockjs","u":"/docs/4/transports/sockjs","h":"#sockjs","p":2192},{"i":2206,"t":"Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.","s":"sockjs_url","u":"/docs/4/transports/sockjs","h":"#sockjs_url","p":2192},{"i":2208,"t":"On this page","s":"SSE (EventSource), with bidirectional emulation","u":"/docs/4/transports/sse","h":"","p":2207},{"i":2211,"t":"Boolean, default: false. Enables SSE (EventSource) endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"sse\": true}","s":"sse","u":"/docs/4/transports/sse","h":"#sse","p":2207},{"i":2213,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"sse_max_request_body_size","u":"/docs/4/transports/sse","h":"#sse_max_request_body_size","p":2207},{"i":2215,"t":"On this page","s":"Unidirectional HTTP streaming","u":"/docs/4/transports/uni_http_stream","h":"","p":2214},{"i":2217,"t":"It's possible to pass initial connect command by posting a JSON body to a streaming endpoint. Refer to the full Connect command description – it's the same as for unidirectional WebSocket.","s":"Connect command","u":"/docs/4/transports/uni_http_stream","h":"#connect-command","p":2214},{"i":2219,"t":"JSON","s":"Supported data formats","u":"/docs/4/transports/uni_http_stream","h":"#supported-data-formats","p":2214},{"i":2221,"t":"Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/4/transports/uni_http_stream","h":"#pings","p":2214},{"i":2224,"t":"Boolean, default: false. Enables unidirectional HTTP streaming endpoint. config.json { ... \"uni_http_stream\": true}","s":"uni_http_stream","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream","p":2214},{"i":2226,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"uni_http_stream_max_request_body_size","u":"/docs/4/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":2214},{"i":2228,"t":"Let's look how simple it is to connect to Centrifugo using HTTP streaming. We will start from scratch, generate new configuration file: centrifugo genconfig Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect: config.json { ... \"uni_http_stream\": true, \"user_subscribe_to_personal\": true} Run Centrifugo: centrifugo -c config.json In separate terminal window create token for a user: ❯ go run main.go gentoken -u user12HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request: curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}' Open one more terminal window and publish message to a personal user channel: curl -X POST http://localhost:8000/api \\ -d '{\"method\": \"publish\", \"params\": {\"channel\": \"#user12\", \"data\": {\"input\": \"hello\"}}}' \\ -H \"Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc\" You should see this messages coming from server. {} messages are pings from a server. That's all, happy streaming!","s":"Connecting using CURL","u":"/docs/4/transports/uni_http_stream","h":"#connecting-using-curl","p":2214},{"i":2230,"t":"A basic browser will come soon as we update docs for v4.","s":"Browser example","u":"/docs/4/transports/uni_http_stream","h":"#browser-example","p":2214},{"i":2232,"t":"On this page","s":"Unidirectional SSE (EventSource)","u":"/docs/4/transports/uni_sse","h":"","p":2231},{"i":2234,"t":"Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example: const url = new URL('http://localhost:8000/connection/uni_sse');url.searchParams.append(\"cf_connect\", JSON.stringify({ 'token': ''}));const eventSource = new EventSource(url); Refer to the full Connect command description – it's the same as for unidirectional WebSocket. The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases. tip Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.","s":"Connect command","u":"/docs/4/transports/uni_sse","h":"#connect-command","p":2231},{"i":2236,"t":"JSON","s":"Supported data formats","u":"/docs/4/transports/uni_sse","h":"#supported-data-formats","p":2231},{"i":2239,"t":"Boolean, default: false. Enables unidirectional SSE (EventSource) endpoint. config.json { ... \"uni_sse\": true}","s":"uni_sse","u":"/docs/4/transports/uni_sse","h":"#uni_sse","p":2231},{"i":2241,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"uni_sse_max_request_body_size","u":"/docs/4/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":2231},{"i":2243,"t":"A basic browser will come soon as we update docs for v4.","s":"Browser example","u":"/docs/4/transports/uni_sse","h":"#browser-example","p":2231},{"i":2245,"t":"On this page","s":"Unidirectional WebSocket","u":"/docs/4/transports/uni_websocket","h":"","p":2244},{"i":2247,"t":"It's possible to send connect command as first WebSocket message (as JSON). Field name Field type Required Description token string no Connection JWT, not required when using the connect proxy feature. data any JSON no Custom JSON connection data name string no Application name version string no Application version subs map of channel to SubscribeRequest no Pass an information about desired subscriptions to a server","s":"Connect command","u":"/docs/4/transports/uni_websocket","h":"#connect-command","p":2244},{"i":2249,"t":"Field name Field type Required Description recover boolean no Whether a client wants to recover from a certain position offset integer no Known stream position offset when recover is used epoch string no Known stream position epoch when recover is used","s":"SubscribeRequest","u":"/docs/4/transports/uni_websocket","h":"#subscriberequest","p":2244},{"i":2251,"t":"JSON","s":"Supported data formats","u":"/docs/4/transports/uni_websocket","h":"#supported-data-formats","p":2244},{"i":2253,"t":"Centrifugo uses empty commands ({} in JSON case) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/4/transports/uni_websocket","h":"#pings","p":2244},{"i":2256,"t":"Boolean, default: false. Enables unidirectional WebSocket endpoint. config.json { ... \"uni_websocket\": true}","s":"uni_websocket","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket","p":2244},{"i":2258,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from WebSocket connection in bytes.","s":"uni_websocket_message_size_limit","u":"/docs/4/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":2244},{"i":2260,"t":"Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal. First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example: config.json { \"token_hmac_secret_key\": \"secret\", \"uni_websocket\":true, \"user_subscribe_to_personal\": true} Run Centrifugo: ./centrifugo -c config.json In another terminal: ❯ ./centrifugo gentoken -c config.json -u test_userHMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw Install wscat and run: wscat -c \"ws://localhost:8000/connection/uni_websocket\" This will establish a connection with a server and you then can send connect command to a server: ❯ wscat -c \"ws://localhost:8000/connection/uni_websocket\"Connected (press CTRL+C to quit)> {\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2NTY1MDMwNDV9.3UYL-UCUBp27TybeBK7Z0OenwdsKwCMRe46fuEjJnzI\", \"subs\": {\"abc\": {}}}< {\"connect\":{\"client\":\"bfd28799-b958-4791-b9e9-b011eaef68c1\",\"version\":\"0.0.0\",\"subs\":{\"#test_user\":{}},\"expires\":true,\"ttl\":604407,\"ping\":25,\"session\":\"57b1287b-44ec-45c8-93fc-696c5294af25\"}} The connection will receive server pings (empty commands {}) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.","s":"Example","u":"/docs/4/transports/uni_websocket","h":"#example","p":2244},{"i":2262,"t":"On this page","s":"Unidirectional GRPC","u":"/docs/4/transports/uni_grpc","h":"","p":2261},{"i":2264,"t":"JSON and binary.","s":"Supported data formats","u":"/docs/4/transports/uni_grpc","h":"#supported-data-formats","p":2261},{"i":2267,"t":"Boolean, default: false. Enables unidirectional GRPC endpoint. config.json { ... \"uni_grpc\": true}","s":"uni_grpc","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc","p":2261},{"i":2269,"t":"String, default \"11000\". Port to listen on.","s":"uni_grpc_port","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_port","p":2261},{"i":2271,"t":"String, default \"\" (listen on all interfaces) Address to bind uni GRPC to.","s":"uni_grpc_address","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_address","p":2261},{"i":2273,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from GRPC connection in bytes.","s":"uni_grpc_max_receive_message_size","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":2261},{"i":2275,"t":"Boolean, default: false Enable custom TLS for unidirectional GRPC server.","s":"uni_grpc_tls","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls","p":2261},{"i":2277,"t":"String, default: \"\". Path to cert file.","s":"uni_grpc_tls_cert","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":2261},{"i":2279,"t":"String, default: \"\". Path to key file.","s":"uni_grpc_tls_key","u":"/docs/4/transports/uni_grpc","h":"#uni_grpc_tls_key","p":2261},{"i":2281,"t":"A basic example will come soon as we update docs for v4. In general, algorithm is like this: Copy Protobuf definitions Generate GRPC client code Use generated code to connect to Centrifugo Process Push messages, drop unknown Push types, handle those necessary for the application.","s":"Example","u":"/docs/4/transports/uni_grpc","h":"#example","p":2261},{"i":2283,"t":"On this page","s":"Server API walkthrough","u":"/docs/4/server/server_api","h":"","p":2282},{"i":2285,"t":"Server HTTP API works on /api endpoint (by default). It has a simple request format: this is an HTTP POST request with application/json Content-Type and with JSON command body. Here we will look at available command methods and parameters. tip In some cases, you can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests.","s":"HTTP API","u":"/docs/4/server/server_api","h":"#http-api","p":2282},{"i":2287,"t":"HTTP API is protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like: config.json { ... \"api_key\": \"\"} This API key must be set in the request Authorization header in this way: Authorization: apikey It's also possible to pass API key over URL query param. This solves some edge cases where it's not possible to use the Authorization header. Simply add ?api_key= query param to the API endpoint. Keep in mind that passing the API key in the Authorization header is a recommended way. It's possible to disable API key check on Centrifugo side using the api_insecure configuration option. Be sure to protect the API endpoint by firewall rules in this case – to prevent anyone on the internet to send commands over your unprotected Centrifugo API endpoint. API key auth is not very safe for man-in-the-middle so we also recommended running Centrifugo with TLS. A command is a JSON object with two properties: method and params. method is the name of the API command you want to call. params is an object with command arguments. Each method can have its own params Before looking at all available commands here is a CURL that calls info command: curl --header \"Authorization: apikey \" \\ --request POST \\ --data '{\"method\": \"info\", \"params\": {}}' \\ http://localhost:8000/api Here is a live example: Your browser does not support the video tag. Now let's investigate each API method in detail.","s":"HTTP API authorization","u":"/docs/4/server/server_api","h":"#http-api-authorization","p":2282},{"i":2289,"t":"Publish command allows publishing data into a channel (we call this message publication in Centrifugo). Most probably this is a command you'll use most of the time. It looks like this: { \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } }} Let's apply all information said above and send publish command to Centrifugo. We will send a request using the requests library for Python. import jsonimport requestscommand = { \"method\": \"publish\", \"params\": { \"channel\": \"docs\", \"data\": { \"content\": \"1\" } }}api_key = \"YOUR_API_KEY\"data = json.dumps(command)headers = {'Content-type': 'application/json', 'Authorization': 'apikey ' + api_key}resp = requests.post(\"https://centrifuge.example.com/api\", data=data, headers=headers)print(resp.json()) The same using httpie console tool: echo '{\"method\": \"publish\", \"params\": {\"channel\": \"chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \" -vvvPOST /api HTTP/1.1Accept: application/json, */*Accept-Encoding: gzip, deflateAuthorization: apikey KEYConnection: keep-aliveContent-Length: 80Content-Type: application/jsonHost: localhost:8000User-Agent: HTTPie/0.9.8{ \"method\": \"publish\", \"params\": { \"channel\": \"chat\", \"data\": { \"text\": \"hello\" } }}HTTP/1.1 200 OKContent-Length: 3Content-Type: application/jsonDate: Thu, 17 May 2018 22:01:42 GMT{ \"result\": {}} In case of error response object can contain error field. For example, let's publish to a channel with unknown namespace: echo '{\"method\": \"publish\", \"params\": {\"channel\": \"unknown:chat\", \"data\": {\"text\": \"hello\"}}}' | http \"localhost:8000/api\" Authorization:\"apikey \"HTTP/1.1 200 OKContent-Length: 55Content-Type: application/jsonDate: Thu, 17 May 2018 22:03:09 GMT{ \"error\": { \"code\": 102, \"message\": \"namespace not found\" }} error object contains error code and message - this is also the same for other commands described below. Publish params​ Parameter name Parameter type Required Description channel string yes Name of channel to publish data any JSON yes Custom JSON data to publish into a channel skip_history bool no Skip adding publication to history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Publish result​ Field name Field type Optional Description offset integer yes Offset of publication in history stream epoch string yes Epoch of current stream","s":"publish","u":"/docs/4/server/server_api","h":"#publish","p":2282},{"i":2291,"t":"Similar to publish but allows to send the same data into many channels. { \"method\": \"broadcast\", \"params\": { \"channels\": [\"CHANNEL_1\", \"CHANNEL_2\"], \"data\": { \"text\": \"hello\" } }} Broadcast params​ Parameter name Parameter type Required Description channels Array of strings yes List of channels to publish data to data any JSON yes Custom JSON data to publish into each channel skip_history bool no Skip adding publications to channels' history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Broadcast result​ Field name Field type Optional Description responses Array of publish responses no Responses for each individual publish (with possible error and publish result)","s":"broadcast","u":"/docs/4/server/server_api","h":"#broadcast","p":2282},{"i":2293,"t":"subscribe allows subscribing user to a channel. Subscribe params​ Parameter name Parameter type Required Description user string yes User ID to subscribe channel string yes Name of channel to subscribe user to info any JSON no Attach custom data to subscription (will be used in presence and join/leave messages) b64info string no info in base64 for binary mode (will be decoded by Centrifugo) client string no Specific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs) session string no Specific client session to subscribe (user still required to be set) data any JSON no Custom subscription data (will be sent to client in Subscribe push) b64data string no Same as data but in base64 format (will be decoded by Centrifugo) recover_since StreamPosition object no Stream position to recover from override Override object no Allows dynamically override some channel options defined in Centrifugo configuration (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} Subscribe result​ Empty object at the moment.","s":"subscribe","u":"/docs/4/server/server_api","h":"#subscribe","p":2282},{"i":2295,"t":"unsubscribe allows unsubscribing user from a channel. { \"method\": \"unsubscribe\", \"params\": { \"channel\": \"CHANNEL NAME\", \"user\": \"USER ID\" }} Unsubscribe params​ Parameter name Parameter type Required Description user string yes User ID to unsubscribe channel string yes Name of channel to unsubscribe user to client string no Specific client ID to unsubscribe (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Unsubscribe result​ Empty object at the moment.","s":"unsubscribe","u":"/docs/4/server/server_api","h":"#unsubscribe","p":2282},{"i":2297,"t":"disconnect allows disconnecting a user by ID. { \"method\": \"disconnect\", \"params\": { \"user\": \"USER ID\" }} Disconnect params​ Parameter name Parameter type Required Description user string yes User ID to disconnect client string no Specific client ID to disconnect (user still required to be set) session string no Specific client session to disconnect (user still required to be set). whitelist Array of strings no Array of client IDs to keep disconnect Disconnect object no Provide custom disconnect object, see below Disconnect object​ Field name Field type Required Description code int yes Disconnect code reason string yes Disconnect reason Disconnect result​ Empty object at the moment.","s":"disconnect","u":"/docs/4/server/server_api","h":"#disconnect","p":2282},{"i":2299,"t":"refresh allows refreshing user connection (mostly useful when unidirectional transports are used). Refresh params​ Parameter name Parameter type Required Description user string yes User ID to refresh client string no Client ID to refresh (user still required to be set) session string no Specific client session to refresh (user still required to be set). expired bool no Mark connection as expired and close with Disconnect Expired reason expire_at int no Unix time (in seconds) in the future when the connection will expire Refresh result​ Empty object at the moment.","s":"refresh","u":"/docs/4/server/server_api","h":"#refresh","p":2282},{"i":2301,"t":"presence allows getting channel online presence information (all clients currently subscribed on this channel). tip Presence in channels is not enabled by default. See how to enable it over channel options. Also check out dedicated chapter about it. { \"method\": \"presence\", \"params\": { \"channel\": \"chat\" }} Example: fz@centrifugo: echo '{\"method\": \"presence\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 127Content-Type: application/jsonDate: Thu, 17 May 2018 22:13:17 GMT{ \"result\": { \"presence\": { \"c54313b2-0442-499a-a70c-051f8588020f\": { \"client\": \"c54313b2-0442-499a-a70c-051f8588020f\", \"user\": \"42\" }, \"adad13b1-0442-499a-a70c-051f858802da\": { \"client\": \"adad13b1-0442-499a-a70c-051f858802da\", \"user\": \"42\" } } }} Presence params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence result​ Field name Field type Optional Description presence Map of client ID (string) to ClientInfo object no Offset of publication in history stream ClientInfo​ Field name Field type Optional Description client string no Client ID user string no User ID conn_info JSON yes Optional connection info chan_info JSON yes Optional channel info","s":"presence","u":"/docs/4/server/server_api","h":"#presence","p":2282},{"i":2303,"t":"presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID). { \"method\": \"presence_stats\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"presence_stats\", \"params\": {\"channel\": \"public:chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": { \"num_clients\": 0, \"num_users\": 0 }} Presence stats params​ Parameter name Parameter type Required Description channel string yes Name of channel to call presence from Presence stats result​ Field name Field type Optional Description num_clients integer no Total number of clients in channel num_users integer no Total number of unique users in channel","s":"presence_stats","u":"/docs/4/server/server_api","h":"#presence_stats","p":2282},{"i":2305,"t":"history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter. tip History in channels is not enabled by default. See how to enable it over channel options. { \"method\": \"history\", \"params\": { \"channel\": \"chat\", \"limit\": 2 }} Example: echo '{\"method\": \"history\", \"params\": {\"channel\": \"chat\", \"limit\": 2}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 129Content-Type: application/jsonDate: Wed, 21 Jul 2021 05:30:48 GMT{ \"result\": { \"epoch\": \"qFhv\", \"offset\": 4, \"publications\": [ { \"data\": { \"text\": \"hello\" }, \"offset\": 2 }, { \"data\": { \"text\": \"hello\" }, \"offset\": 3 } ] }} History params​ Parameter name Parameter type Required Description channel string yes Name of channel to call history from limit int no Limit number of returned publications, if not set in request then only current stream position information will present in result (without any publications) since StreamPosition object no To return publications after this position reverse bool no Iterate in reversed order (from latest to earliest) StreamPosition​ Field name Field type Required Description offset integer yes Offset in a stream epoch string yes Stream epoch History result​ Field name Field type Optional Description publications Array of publication objects yes List of publications in channel offset integer yes Top offset in history stream epoch string yes Epoch of current stream","s":"history","u":"/docs/4/server/server_api","h":"#history","p":2282},{"i":2307,"t":"history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state. { \"method\": \"history_remove\", \"params\": { \"channel\": \"chat\" }} Example: echo '{\"method\": \"history_remove\", \"params\": {\"channel\": \"chat\"}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 43Content-Type: application/jsonDate: Thu, 17 May 2018 22:09:44 GMT{ \"result\": {}} History remove params​ Parameter name Parameter type Required Description channel string yes Name of channel to remove history History remove result​ Empty object at the moment.","s":"history_remove","u":"/docs/4/server/server_api","h":"#history_remove","p":2282},{"i":2309,"t":"channels return active channels (with one or more active subscribers in it). { \"method\": \"channels\", \"params\": {}} Channels params​ Parameter name Parameter type Required Description pattern string no Pattern to filter channels, we are using gobwas/glob library for matching Channels result​ Field name Field type Optional Description channels Map of string to ChannelInfo no Map where key is channel and value is ChannelInfo (see below) ChannelInfo​ Field name Field type Optional Description num_clients integer no Total number of connections currently subscribed to a channel caution Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels API call will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.","s":"channels","u":"/docs/4/server/server_api","h":"#channels","p":2282},{"i":2311,"t":"info method allows getting information about running Centrifugo nodes. Example: echo '{\"method\": \"info\", \"params\": {}}' | http \"localhost:8000/api\" Authorization:\"apikey KEY\"HTTP/1.1 200 OKContent-Length: 184Content-Type: application/jsonDate: Thu, 17 May 2018 22:07:58 GMT{ \"result\": { \"nodes\": [ { \"name\": \"Alexanders-MacBook-Pro.local_8000\", \"num_channels\": 0, \"num_clients\": 0, \"num_users\": 0, \"uid\": \"f844a2ed-5edf-4815-b83c-271974003db9\", \"uptime\": 0, \"version\": \"\" } ] }} Info params​ Empty object at the moment. Info result​ Field name Field type Optional Description nodes Array of Node objects no Information about all nodes in a cluster","s":"info","u":"/docs/4/server/server_api","h":"#info","p":2282},{"i":2313,"t":"It's possible to combine several commands into one request to Centrifugo. To do this use JSON streaming format. This can improve server throughput and reduce traffic traveling around. Example: curl --header \"Authorization: apikey \" \\ --request POST \\ --data $'{\"method\": \"publish\", \"params\": {\"channel\": \"test1\", \"data\": {\"test\": 1}}}\\n{\"method\": \"publish\", \"params\": {\"channel\": \"test2\", \"data\": {\"test\": 2}}}' \\ http://localhost:8000/api{\"result\":{}}{\"result\":{}} Note that with CURL we had to use $ to properly send new line \\n character in data.","s":"Command pipelining","u":"/docs/4/server/server_api","h":"#command-pipelining","p":2282},{"i":2315,"t":"Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header. But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually: cent for Python phpcent for PHP gocent for Go rubycent for Ruby Also, there are API libraries created by community: crystalcent API client for Crystal language cent.js API client for NodeJS Centrifugo.AspNetCore API client for ASP.NET Core tip Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.","s":"HTTP API libraries","u":"/docs/4/server/server_api","h":"#http-api-libraries","p":2282},{"i":2317,"t":"Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC. GRPC API is also useful if you want to publish binary data to Centrifugo channels. tip GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description. You can enable GRPC API in Centrifugo using grpc_api option: config.json { ... \"grpc_api\": true} By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option. Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file. Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.","s":"GRPC API","u":"/docs/4/server/server_api","h":"#grpc-api","p":2282},{"i":2319,"t":"For example for Python you need to run sth like this according to GRPC docs: pip install grpcio-toolspython -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo: import grpcimport api_pb2_grpc as api_grpcimport api_pb2 as api_pbchannel = grpc.insecure_channel('localhost:10000')stub = api_grpc.CentrifugoApiStub(channel)try: resp = stub.Info(api_pb.InfoRequest())except grpc.RpcError as err: # GRPC level error. print(err.code(), err.details())else: if resp.error.code: # Centrifugo server level error. print(resp.error.code, resp.error.message) else: print(resp.result) Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.","s":"GRPC example for Python","u":"/docs/4/server/server_api","h":"#grpc-example-for-python","p":2282},{"i":2321,"t":"Here is a simple example of how to run Centrifugo with the GRPC Go client. You need protoc, protoc-gen-go and protoc-gen-go-grpc installed. First start Centrifugo itself with GRPC API enabled: CENTRIFUGO_GRPC_API=1 centrifugo --config config.json In another terminal tab: mkdir centrifugo_grpc_examplecd centrifugo_grpc_example/touch main.gogo mod init centrifugo_examplemkdir apiprotocd apiprotowget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto Run protoc to generate code: protoc -I ./ api.proto --go_out=. --go-grpc_out=. Put the following code to main.go file (created on the last step above): package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure()) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoApiClient(conn) for { resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} Then run: go run main.go The program starts and periodically publishes the same payload into chat:index channel.","s":"GRPC example for Go","u":"/docs/4/server/server_api","h":"#grpc-example-for-go","p":2282},{"i":2323,"t":"You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey . For example in Go language: package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")type keyAuth struct { key string}func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{ \"authorization\": \"apikey \" + t.key, }, nil}func (t keyAuth) RequireTransportSecurity() bool { return false}func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{\"xxx\"})) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoClient(conn) for { resp, err := client.Publish(context.Background(), &PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} For other languages refer to GRPC docs.","s":"GRPC API key authorization","u":"/docs/4/server/server_api","h":"#grpc-api-key-authorization","p":2282},{"i":2325,"t":"Attributions","s":"Attributions","u":"/docs/attributions","h":"","p":2324},{"i":2327,"t":"The following images have been used in the landing page. Icons made by kerismaker: https://www.flaticon.com/packs/web-maintenance-35","s":"Landing Page Images","u":"/docs/attributions","h":"#landing-page-images","p":2324},{"i":2329,"t":"On this page","s":"WebSocket","u":"/docs/4/transports/websocket","h":"","p":2328},{"i":2332,"t":"Default: 65536 (64KB) Maximum allowed size of a message received from WebSocket connection in bytes.","s":"websocket_message_size_limit","u":"/docs/4/transports/websocket","h":"#websocket_message_size_limit","p":2328},{"i":2334,"t":"In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size). config.json { ... \"websocket_read_buffer_size\": 512}","s":"websocket_read_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_read_buffer_size","p":2328},{"i":2336,"t":"In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused): config.json { ... \"websocket_write_buffer_size\": 512}","s":"websocket_write_buffer_size","u":"/docs/4/transports/websocket","h":"#websocket_write_buffer_size","p":2328},{"i":2338,"t":"If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.","s":"websocket_use_write_buffer_pool","u":"/docs/4/transports/websocket","h":"#websocket_use_write_buffer_pool","p":2328},{"i":2340,"t":"An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire. We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally. caution Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable. To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature. Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client. It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.","s":"websocket_compression","u":"/docs/4/transports/websocket","h":"#websocket_compression","p":2328},{"i":2342,"t":"In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if: you want less traffic on wire as Protobuf is very compact you want maximum performance on server-side as Protobuf encoding/decoding is very efficient you can sacrifice human-readable JSON for your application Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like: wss://centrifugo.example.com/connection/websocket?format=protobuf After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes. tip You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.","s":"Protobuf binary protocol","u":"/docs/4/transports/websocket","h":"#protobuf-binary-protocol","p":2328},{"i":2344,"t":"On this page","s":"Proxy events to the backend","u":"/docs/4/server/proxy","h":"","p":2343},{"i":2346,"t":"HTTP proxy in Centrifugo converts client connection events into HTTP calls to the application backend.","s":"HTTP proxy","u":"/docs/4/server/proxy","h":"#http-proxy","p":2343},{"i":2348,"t":"All proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).","s":"HTTP request structure","u":"/docs/4/server/proxy","h":"#http-request-structure","p":2343},{"i":2350,"t":"The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example: config.json { ... \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} Alternatively, you can set a list of headers via an environment variable (space separated): export CENTRIFUGO_PROXY_HTTP_HEADERS=\"Cookie User-Agent X-B3-TraceId X-B3-SpanId\" ./centrifugo note Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend.","s":"Proxy HTTP headers","u":"/docs/4/server/proxy","h":"#proxy-http-headers","p":2343},{"i":2352,"t":"When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied. See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.","s":"Proxy GRPC metadata","u":"/docs/4/server/proxy","h":"#proxy-grpc-metadata","p":2343},{"i":2354,"t":"With the following options in the configuration file: { ... \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_connect_timeout\": \"1s\"} – connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request. danger Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections. Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. In many cases your existing session mechanism will provide user authentication details to the connect proxy handler. tip If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server. This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired). Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\"} Expected response example: {\"result\": {\"user\": \"56\"}} This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result. Several app examples which use connect proxy can be found in our blog: With NodeJS With Django With Laravel Connect request fields​ This is what sent from Centrifugo to application backend in case of connect proxy request. Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) name string yes optional name of the client (this field will only be set if provided by a client on connect) version string yes optional version of the client (this field will only be set if provided by a client on connect) data JSON yes optional data from client (this field will only be set if provided by a client on connect) b64data string yes optional data from the client in base64 format (if the binary proxy mode is used) channels Array of strings yes list of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result Connect result fields​ This is what application returns to Centrifugo inside result field in case of connect proxy request. Field Type Optional Description user string no user ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests expire_at integer yes a timestamp when connection must be considered expired. If not set or set to 0 connection won't expire at all info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages data JSON yes a custom data to send to the client in connect command response. b64data string yes a custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client channels array of strings yes allows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions subs map of SubscribeOptions yes map of channels with options to subscribe connection to. See more details about server-side subscriptions meta JSON object (ex. {\"key\": \"value\"}) yes a custom data to attach to connection (this won't be exposed to client-side) Options​ proxy_connect_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. Example​ Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return \"56\" as user ID): class CentrifugoConnectHandler(tornado.web.RequestHandler): def check_xsrf_cookie(self): pass def post(self): self.set_header('Content-Type', 'application/json; charset=\"utf-8\"') data = json.dumps({ 'result': { 'user': '56' } }) self.write(data)def main(): options.parse_command_line() app = tornado.web.Application([ (r'/centrifugo/connect', CentrifugoConnectHandler), ]) app.listen(3000) tornado.ioloop.IOLoop.instance().start()if __name__ == '__main__': main() This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side. We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea. What if connection is unauthenticated/unauthorized to connect?​ In this case return a disconnect object as a response. See Return custom disconnect section. Depending on whether you want connection to reconnect or not (usually not) you can select the appropriate disconnect code. Sth like this in response: { \"disconnect\": { \"code\": 4501, \"reason\": \"unauthorized\" }} – may be sufficient enough. Choosing codes and reason is up to the developer, but follow the rules described in Return custom disconnect section.","s":"Connect proxy","u":"/docs/4/server/proxy","h":"#connect-proxy","p":2343},{"i":2356,"t":"With the following options in the configuration file: { ... \"proxy_refresh_endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"proxy_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side. The payload sent to app backend in refresh request (when the connection is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Refresh proxy","u":"/docs/4/server/proxy","h":"#refresh-proxy","p":2343},{"i":2358,"t":"With the following option in the configuration file: { ... \"proxy_rpc_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_rpc_timeout\": \"1s\"} RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket (or SockJS) connection in a bidirectional way. Payload example sent to app backend in RPC request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"method\": \"getCurrentPrice\", \"data\":{\"params\": {\"object_id\": 12}}} Expected response example: {\"result\": {\"data\": {\"answer\": \"2019\"}}} RPC request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process method string yes an RPC method string, if the client does not use named RPC call then method will be omitted data JSON yes RPC custom data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) RPC result fields​ Field Type Optional Description data JSON yes RPC response - any valid JSON is supported b64data string yes can be set instead of data for binary response encoded in base64 format Options​ proxy_rpc_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. See below on how to return a custom error.","s":"RPC proxy","u":"/docs/4/server/proxy","h":"#rpc-proxy","p":2343},{"i":2360,"t":"With the following option in the configuration file: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\"} – subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel. tip Subscribe proxy does not proxy private and user-limited channels at the moment. That's because those are already providing a level of security (user-limited channels check current user ID, private channels require subscription token). In some cases you may use subscribe proxy as a replacement for private channels actually: if you prefer to check permissions using the proxy to backend mechanism – just stop using $ prefixes in channels, properly configure subscribe proxy and validate subscriptions upon proxy from Centrifugo to your backend (issued each time user tries to subscribe on a channel for which subscribe proxy enabled). Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace. So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} Or for channels in namespace sun: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_subscribe\": true }]} Payload example sent to app backend in subscribe request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\"} Expected response example if subscription is allowed: {\"result\": {}} Subscribe request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to subscribe to meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) data JSON yes custom data from client sent with subscription request (this field will only be set if provided by a client on subscribe). b64data string yes optional subscription data from the client in base64 format (if the binary proxy mode is used). Subscribe result fields​ Field Type Optional Description info JSON yes a channel info JSON b64info string yes a binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using data JSON yes a custom data to send to the client in subscribe command reply. b64data string yes a custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} See below on how to return an error in case you don't want to allow subscribing. Options​ proxy_subscribe_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. What if connection is not allowed to subscribe?​ In this case you can return error object as a subscribe handler response. See return custom error section. In general, frontend applications should not try to subscribe to channels for which access is not allowed. But these situations can happen or malicious user can try to subscribe to a channel. In most scenarios returning: { \"error\": { \"code\": 403, \"message\": \"permission denied\" }} – is sufficient enough. Error code may be not 403 actually, no real reason to force HTTP semantics here - so it's up to Centrifugo user to decide. Just keep it in range [400, 1999] as described here. If case of returning response above, on client side unsubscribed event of Subscription object will be called with error code 403. Subscription won't resubscribe automatically after that.","s":"Subscribe proxy","u":"/docs/4/server/proxy","h":"#subscribe-proxy","p":2343},{"i":2362,"t":"With the following option in the configuration file: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\"} – publish calls sent by a client will be proxied to proxy_publish_endpoint. This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side. Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too. So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"publish\": true, \"proxy_publish\": true} Or for channels in namespace sun: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"publish\": true, \"proxy_publish\": true }]} Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace). Payload example sent to app backend in a publish request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\", \"data\":{\"input\":\"hello\"}} Expected response example if publish is allowed: {\"result\": {}} Publish request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to publish to data JSON yes data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Publish result fields​ Field Type Optional Description data JSON yes an optional JSON data to send into a channel instead of original data sent by a client b64data string yes a binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing skip_history bool yes when set to true Centrifugo won't save publication to the channel history See below on how to return an error in case you don't want to allow publishing. Options​ proxy_publish_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Publish proxy","u":"/docs/4/server/proxy","h":"#publish-proxy","p":2343},{"i":2364,"t":"Added in Centrifugo v4.1.1 With the following options in the configuration file: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_sub_refresh_endpoint when it's time to refresh the subscription. Centrifugo itself will ask your backend about subscription validity instead of subscription refresh workflow on the client-side. Like subscribe and publish proxy types, sub refresh proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_sub_refresh that enables sub refresh proxy for channels in the namespace. Only subscriptions which have expiration time will be validated over sub refresh proxy endpoint. Sub refresh proxy may be used as a periodical Subscription liveness callback from Centrifugo to app backend. caution In the current implementation the delay of Subscription refresh requests from Centrifugo to application backend may be up to one minute (was implemented this way from a simplicity and efficiency perspective). We assume this should be enough for many scenarios. But this may be improved if needed. Please reach us out with a detailed description of your use case where you want more accurate requests to refresh subscriptions. So to enable sub refresh proxy for channels without namespace define proxy_sub_refresh on a top configuration level: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh\": true} Or for channels in namespace sun: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_sub_refresh\": true }]} The payload sent to app backend in sub refresh request (when the subscription is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"channel\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Sub refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no channel for which Subscription is going to expire meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Sub refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a channel info JSON b64info string yes binary channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_sub_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Sub refresh proxy","u":"/docs/4/server/proxy","h":"#sub-refresh-proxy","p":2343},{"i":2366,"t":"Application backend can return JSON object that contains an error to return it to the client: { \"error\": { \"code\": 1000, \"message\": \"custom error\" }} Applications must use error codes in range [400, 1999]. Error code field is uint32 internally. note Returning custom error does not apply to response for refresh and sub refresh proxy requests as there is no sense in returning an error (will not reach client anyway).","s":"Return custom error","u":"/docs/4/server/proxy","h":"#return-custom-error","p":2343},{"i":2368,"t":"Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way: { \"disconnect\": { \"code\": 4500, \"reason\": \"disconnect reason\" }} Application must use numbers in the range 4000-4999 for custom disconnect codes: codes in range [4000, 4499] give client an advice to reconnect codes in range [4500, 4999] are terminal codes – client won't reconnect upon receiving it. Code is uint32 internally. Numbers outside of 4000-4999 range are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 ASCII symbols (i.e. 32 bytes max). note Returning custom disconnect does not apply to response for refresh and sub refresh proxy requests as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason.","s":"Return custom disconnect","u":"/docs/4/server/proxy","h":"#return-custom-disconnect","p":2343},{"i":2370,"t":"Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server. GRPC service definitions can be found in the Centrifugo repository: proxy.proto. tip GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions. Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept). All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy: config.json { ... \"proxy_connect_endpoint\": \"grpc://localhost:12000\", \"proxy_connect_timeout\": \"1s\"} Refresh proxy: config.json { ... \"proxy_refresh_endpoint\": \"grpc://localhost:12000\", \"proxy_refresh_timeout\": \"1s\"} Or for RPC proxy: config.json { ... \"proxy_rpc_endpoint\": \"grpc://localhost:12000\", \"proxy_rpc_timeout\": \"1s\"} For publish proxy in namespace chat: config.json { ... \"proxy_publish_endpoint\": \"grpc://localhost:12000\", \"proxy_publish_timeout\": \"1s\" \"namespaces\": [ { \"name\": \"chat\", \"publish\": true, \"proxy_publish\": true } ]} Use subscribe proxy for all channels without namespaces: config.json { ... \"proxy_subscribe_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} So the same as for HTTP, just the different endpoint scheme.","s":"GRPC proxy","u":"/docs/4/server/proxy","h":"#grpc-proxy","p":2343},{"i":2372,"t":"Some additional options exist to control GRPC proxy behavior. proxy_grpc_cert_file​ String, default: \"\". Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. proxy_grpc_credentials_key​ String, default \"\" (i.e. not used). Add custom key to per-RPC credentials. proxy_grpc_credentials_value​ String, default \"\" (i.e. not used). A custom value for proxy_grpc_credentials_key.","s":"GRPC proxy options","u":"/docs/4/server/proxy","h":"#grpc-proxy-options","p":2343},{"i":2374,"t":"We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.: Copy proxy Protobuf definitions Generate GRPC code Run backend service with you custom business logic Point Centrifugo to it.","s":"GRPC proxy example","u":"/docs/4/server/proxy","h":"#grpc-proxy-example","p":2343},{"i":2376,"t":"Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios: Client protocol type Proxy type Client headers Client metadata HTTP HTTP In proxy request headers N/A GRPC GRPC N/A In proxy request metadata HTTP GRPC In proxy request metadata N/A GRPC HTTP N/A In proxy request headers","s":"Header proxy rules","u":"/docs/4/server/proxy","h":"#header-proxy-rules","p":2343},{"i":2378,"t":"As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding. Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this. Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side). But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON. In this case it's possible to turn Centrifugo proxy into binary mode by using: config.json { ... \"proxy_binary_encoding\": true} Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues. While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine. Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.","s":"Binary mode","u":"/docs/4/server/proxy","h":"#binary-mode","p":2343},{"i":2380,"t":"By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture). Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.","s":"Granular proxy mode","u":"/docs/4/server/proxy","h":"#granular-proxy-mode","p":2343},{"i":2382,"t":"Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below. config.json { ... \"granular_proxy_mode\": true}","s":"Enable granular proxy mode","u":"/docs/4/server/proxy","h":"#enable-granular-proxy-mode","p":2343},{"i":2384,"t":"When using granular proxy mode on configuration top level you can define \"proxies\" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint. Here is an example: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"connect\", \"endpoint\": \"http://localhost:3000/centrifugo/connect\", \"timeout\": \"500ms\", \"http_headers\": [\"Cookie\"] }, { \"name\": \"refresh\", \"endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"timeout\": \"500ms\" }, { \"name\": \"subscribe1\", \"endpoint\": \"http://localhost:3001/centrifugo/subscribe\" }, { \"name\": \"publish1\", \"endpoint\": \"http://localhost:3001/centrifugo/publish\" }, { \"name\": \"rpc1\", \"endpoint\": \"http://localhost:3001/centrifugo/rpc\" }, { \"name\": \"subscribe2\", \"endpoint\": \"http://localhost:3002/centrifugo/subscribe\" }, { \"name\": \"publish2\", \"endpoint\": \"grpc://localhost:3002\" } { \"name\": \"rpc2\", \"endpoint\": \"grpc://localhost:3002\" } ]} Let's look at all fields for a proxy object which is possible to set for each proxy inside \"proxies\" array. Field name Field type Required Description name string yes Unique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$ endpoint string yes HTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC. timeout duration (string) no Proxy request timeout, default \"1s\" http_headers array of strings no List of headers to proxy, by default no headers grpc_metadata array of strings no List of GRPC metadata keys to proxy, by default no metadata keys binary_encoding bool no Use base64 for payloads include_connection_meta bool no Include meta information (attached on connect) grpc_cert_file string no Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. grpc_credentials_key string no Add custom key to per-RPC credentials. grpc_credentials_value string no A custom value for grpc_credentials_key.","s":"Defining a list of proxies","u":"/docs/4/server/proxy","h":"#defining-a-list-of-proxies","p":2343},{"i":2386,"t":"As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event. To enable connect proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\"} We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea. Let's also add refresh proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\", \"refresh_proxy_name\": \"refresh\"}","s":"Granular connect and refresh","u":"/docs/4/server/proxy","h":"#granular-connect-and-refresh","p":2343},{"i":2388,"t":"Subscribe, publish and sub refresh proxies work per-namespace. This means that subscribe_proxy_name, publish_proxy_name and sub_refresh_proxy_name are just channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object. config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_proxy_name\": \"subscribe1\", \"publish\": true, \"publish_proxy_name\": \"publish1\" }, { \"name\": \"ns2\", \"subscribe_proxy_name\": \"subscribe2\", \"publish\": true, \"publish_proxy_name\": \"publish2\" } ]} If namespace does not have \"subscribe_proxy_name\" or \"subscribe_proxy_name\" is empty then no subscribe proxy will be used for a namespace. If namespace does not have \"publish_proxy_name\" or \"publish_proxy_name\" is empty then no publish proxy will be used for a namespace. If namespace does not have \"sub_refresh_proxy_name\" or \"sub_refresh_proxy_name\" is empty then no sub refresh proxy will be used for a namespace. tip You can define subscribe_proxy_name, publish_proxy_name, sub_refresh_proxy_name on configuration top level – and in this case publish, subscribe and sub refresh requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.","s":"Granular subscribe, publish, sub refresh","u":"/docs/4/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":2343},{"i":2390,"t":"Analogous to channel namespaces it's possible to configure rpc namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [...], \"rpc_namespaces\": [ { \"name\": \"rpc_ns1\", \"rpc_proxy_name\": \"rpc1\", }, { \"name\": \"rpc_ns2\", \"rpc_proxy_name\": \"rpc2\" } ]} The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces). Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start. tip The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.","s":"Granular RPC","u":"/docs/4/server/proxy","h":"#granular-rpc","p":2343},{"i":2392,"t":"WebTransport WebTransport is an API offering low-latency, bidirectional, client-server messaging on top of HTTP/3. See Using WebTransport article that gives a good overview of it. danger WebTransport support in Centrifugo is EXPERIMENTAL and not recommended for production usage. WebTransport IETF specification is not finished yet and may have breaking changes. To use WebTransport you first need to run HTTP/3 experimental server and enable webtransport endpoint: config.json { \"http3\": true, \"tls\": true, \"tls_cert\": \"path/to/crt\", \"tls_key\": \"path/to/key\", \"webtransport\": true} In HTTP/3 and WebTransport case TLS is required. tip At the time of writing only Chrome (since v97) supports WebTransport API. If you are experimenting with self-signed certificates you may need to run Chrome with flags to force HTTP/3 on origin and ignore certificate errors: /path/to/your/Chrome --origin-to-force-quic-on=localhost:8000 --ignore-certificate-errors-spki-list=TSZTiMjLG+DNjESXdJh3f+S8C+RhsFCav7T24VNuCPQ= Where the value of --ignore-certificate-errors-spki-list is a certificate fingerprint obtained this way: openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 With not self-signed certs things should work just fine in Chrome. Here is a video tutorial that shows this in action: After starting Centrifugo with HTTP/3 and WebTransport endpoint you can connect to that endpoint (by default – /connection/webtransport) using centrifuge-js. For example, let's enable WebTransport and will use WebSocket as a fallback option: const transports = [ { transport: 'webtransport', endpoint: 'https://localhost:8000/connection/webtransport' }, { transport: 'websocket', endpoint: 'wss://localhost:8000/connection/websocket' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in WebTransport case non-TLS http:// scheme is simply not supported by the specification. tip Make sure you run Centrifugo without load balancer or reverse proxy in front, or make sure your proxy can proxy HTTP/3 traffic to Centrifugo. In Centrifugo case, we utilize a single bidirectional stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional. Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification it's an experimental feature. And it's not recommended for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket.","s":"WebTransport","u":"/docs/4/transports/webtransport","h":"","p":2391},{"i":2394,"t":"flow_diagrams For swimlanes.io: Client <- App Backend: JWTnote:The backend generates JWT for a user and passes it to the client side.Client -> Centrifugo: Client connects to Centrifugo with JWT...: {fas-spinner} Persistent connection establishedClient -> Centrifugo: Client issues channel subscribe requestsCentrifugo -->> Client: Client receives real-time updates from channels Client -> Centrifugo: Connect requestnote:Client connects to Centrifugo without JWT.Centrifugo -> App backend: Sends request further (via HTTP or GRPC)note: The application backend validates client connection and tells Centrifugo user credentials in Connect reply.App backend -> Centrifugo: Connect replyCentrifugo -> Client: Connect Reply...: {fas-spinner} Persistent connection established Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers. Client -> App Backend: Publish requestnote:Client sends data to publish to the application backend.Backend validates it, maybe modifies, optionally saves to the main database, constructs real-time update and publishes it to the Centrifugo server API.App Backend -> Centrifugo: Publish over Centrifugo APICentrifugo -->> Client: {far-bolt fa-lg} Real-time notificationnote: Centrifugo delivers real-time message to active channel subscribers.","s":"flow_diagrams","u":"/docs/flow_diagrams","h":"","p":2393},{"i":2396,"t":"Join community If you find Centrifugo interesting, you are welcome to join our community rooms on Telegram (the most active) and Discord: We strive to create a respectful and curious community. In these rooms, you may find answers to questions not fully covered by the documentation, share your thoughts and ideas on real-time messaging topics and Centrifugo in particular. We also have a Twitter account and a YouTube channel. See you there!","s":"Join community","u":"/docs/getting-started/community","h":"","p":2395},{"i":2398,"t":"On this page","s":"Design overview","u":"/docs/getting-started/design","h":"","p":2397},{"i":2400,"t":"Centrifugo is a standalone server that abstracts away the complexity of working with many persistent connections and efficiently broadcasting messages from the application backend. The fact that Centrifugo acts as a separate service dictates some idiomatic patterns for how to integrate with Centrifugo for real-time message delivery. Usually, you want to deliver content created by a user in your app to other users in real time. Each user may have several real-time connections with Centrifugo. For example, a user opened several browser tabs, with each tab creating a separate connection. Or a user has two mobile devices and created a separate connection to your app from each of them. We call a connection a client in Centrifugo. Thus, the words connection and client are synonyms for us. All requests from users that generate new data should first go to the application backend – i.e., calling the app backend API from the client side. The backend can validate the message, process it, save it into a database for long-term persistence – and then publish an event to a channel using Centrifugo server API. This event is then efficiently broadcast by Centrifugo to all active channel subscribers. The following diagram shows the process (assuming the client that generates new content is also a channel subscriber and thus also receives the real-time message): This is usually a natural workflow for applications since this is how applications traditionally work (without real-time features) and Centrifugo is fully decoupled from the application in this case. Centrifugo serves the role of a real-time transport layer in this case, and you may design the app with graceful degradation in mind – so that removing Centrifugo won't be a fatal problem for the application – it will continue working, just without the real-time features. If the original source of events is your app backend (without any user involvement) – then the above diagram simplifies to: So the backend publishes data to channels and if there are active subscribers – events are delivered. If there are no active subscribers then events are dropped by Centrifugo (or, in case of using history features in channels, events may be temporarily kept in the Centrifugo history stream). It's also possible to utilize Centrifugo's bidirectional connection for sending requests to the backend. To achieve this, Centrifugo provides event proxy features. It's possible to send RPC (with a custom request-response) requests from the client to Centrifugo and the request will then be proxied to the application backend (see RPC proxy). Moreover, the proxy provides a way to utilize the bidirectional connection for publishing into channels (using publish proxy). But again – in most real scenarios, your backend must validate the publication attempt, so the scheme will look like this:","s":"Idiomatic usage","u":"/docs/getting-started/design","h":"#idiomatic-usage","p":2397},{"i":2402,"t":"Idiomatic Centrifugo usage requires having the main application database from which the initial and actual state can be loaded at any point in time. While Centrifugo has channel history, it has been mostly designed to be a hot cache to reduce the load on the main application database when all users reconnect at once (in case of a load balancer configuration reload, Centrifugo restart, temporary network problems, etc.). This allows for a radical reduction in the load on the application's main database during a reconnect storm. Since such disconnects are usually pretty short in time, having a reasonably small number of messages cached in history is sufficient. The addition of the history iteration API shifts possible use cases a bit. Manually calling history chunk by chunk allows for keeping a larger number of publications per channel. Depending on the Engine used and the configuration of the underlying storage, history stream persistence characteristics can vary. For example, with the Memory Engine, history will be lost upon Centrifugo restart. With Redis or Tarantool engines, history will survive Centrifugo restarts, but depending on the storage configuration, it can be lost upon storage restart – so you should take into account storage configuration and persistence properties as well. For example, consider enabling Redis AOF with fsync for maximum durability, or configure replication for high availability, use Redis Cluster, or maybe synchronous replication with Tarantool. When using history with automatic recovery, Centrifugo provides clients with a flag to distinguish whether the missed messages were all successfully restored from Centrifugo history upon recovery or not. If not – the client may restore the state from the main application database. Centrifugo message history can be used as a complementary way to restore messages and thus reduce the load on the main application database most of the time.","s":"Message history considerations","u":"/docs/getting-started/design","h":"#message-history-considerations","p":2397},{"i":2404,"t":"By default, the message delivery model of Centrifugo is 'at most once'. With history and the positioning/recovery features enabled, it's possible to achieve 'at least once' guarantee within the history retention time and size. After an abnormal disconnect, clients have an option to recover missed messages from the publication channel stream history that Centrifugo maintains. Without the positioning or recovery features enabled, a message sent to Centrifugo could theoretically be lost while moving towards clients. Centrifugo makes its best effort only to prevent message loss on the way to online clients, but the application should tolerate the loss. As noted, Centrifugo has a feature called message recovery to automatically recover messages missed due to short network disconnections. It also compensates for the 'at most once' delivery of a broker PUB/SUB system (Redis, Tarantool) by using additional publication offset checks and periodic offset synchronization. So publication loss missed in the PUB/SUB layer will be detected eventually, and the client may catch up on the state by loading it from history.","s":"Message delivery model","u":"/docs/getting-started/design","h":"#message-delivery-model","p":2397},{"i":2406,"t":"Message order in channels is guaranteed to be the same when you publish messages into a channel one after another or publish them in one request. If you do parallel publications into the same channel, then Centrifugo can't guarantee message order since those are processed in parallel.","s":"Message order guarantees","u":"/docs/getting-started/design","h":"#message-order-guarantees","p":2397},{"i":2408,"t":"It is recommended to design an application in a way that users don't even notice when Centrifugo does not work. Use graceful degradation. For example, if a user posts a new comment over AJAX to your application backend - you should not rely solely on Centrifugo to receive a new comment from a channel and display it. You should return new comment data in the AJAX call response and render it. This way, the user that posts a comment will think that everything works just fine. Be careful not to draw comments twice in this case because you may also receive the same data from a channel - think about idempotent identifiers for your entities.","s":"Graceful degradation","u":"/docs/getting-started/design","h":"#graceful-degradation","p":2397},{"i":2410,"t":"Online presence in a channel is designed to be eventually consistent. It will return the correct state most of the time. But when using Redis or Tarantool engines, due to network failures and the unexpected shutdown of a Centrifugo node, there are chances that clients can be present in a presence for up to one minute more (until the presence entry expires). Also, channel presence does not scale well for channels with a lot of active subscribers. This is due to the fact that presence returns the entire snapshot of all clients in a channel – as soon as the number of active subscribers grows, the response size becomes larger. In some cases, the presence_stats API call can be sufficient to avoid receiving the entire presence state.","s":"Online presence considerations","u":"/docs/getting-started/design","h":"#online-presence-considerations","p":2397},{"i":2412,"t":"Centrifugo can scale horizontally with built-in engines (Redis, Tarantool, KeyDB) or with the Nats broker. See engines. All supported brokers are fast – they can handle hundreds of thousands of requests per second. This should be enough for most applications. But if you approach broker resource limits (CPU or memory), then it's possible: Use Centrifugo's consistent sharding support to balance queries between different broker instances (supported for Redis, KeyDB, Tarantool). Use Redis Cluster (it's also possible to consistently shard data between different Redis Clusters). Nats broker should scale well itself in a cluster setup. All brokers can be set up in a highly available way so there won't be a single point of failure. All Centrifugo data (history, online presence) is designed to be ephemeral and have an expiration time. Due to this fact, and the fact that Centrifugo provides hooks for the application to understand history loss, makes the process of resharding mostly automatic. As soon as you need to add an additional broker shard (when using client-side sharding), you can just add it to the configuration and restart Centrifugo. Since data is sharded consistently, part of the data will stay on the same broker nodes. Applications should handle cases where channel data moved to another shard and restore the state from the main application database when needed (i.e., when the recovered flag provided by the SDK is false).","s":"Scalability considerations","u":"/docs/getting-started/design","h":"#scalability-considerations","p":2397},{"i":2414,"t":"On this page","s":"Comparing with others","u":"/docs/getting-started/comparisons","h":"","p":2413},{"i":2416,"t":"Socket.io is a library; you need to write your own server on top of it. Centrifugo is a ready-to-use standalone server. Because of this, Socket.io may give you more flexibility, but you are mostly limited to writing code in JavaScript on the backend to get the most out of it. Since Centrifugo is a standalone server, it's a universal, language-agnostic element that integrates well with a backend written in any language. But because it's universal, it comes with integration rules, its own mechanics, and limitations of such a design.","s":"Centrifugo vs Socket.io","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-socketio","p":2413},{"i":2418,"t":"The main difference is that Centrifugo is a self-hosted solution, while the mentioned technologies are all cloud SaaS platforms. So, when using Centrifugo, you need to configure and run it on your own; you need to be skilled in engineering. The benefit is obvious – it's much cheaper once the integration is done. And all the data stays within your organization. With cloud services, all the hard work of setting up an infrastructure for a WebSocket server and its maintenance is done for you. But it's more expensive, and the data flows through an external network.","s":"Centrifugo vs Pusher, Ably, Pubnub","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-pusher-ably-pubnub","p":2413},{"i":2420,"t":"A popular question from newcomers – does Centrifugo provide the same as Redis PUB/SUB? The answer is that Centrifugo and Redis can't be directly compared at all. Centrifugo uses Redis internally for PUB/SUB scalability, keeping channel message history, and online presence. You can build a system similar to Centrifugo on top of Redis – but you would need to write a lot of code, i.e., replicate everything that Centrifugo provides out of the box – like real-time client SDKs, client protocol, re-implement all the transport endpoints, write efficient Redis integration, etc.","s":"Centrifugo vs Redis","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-redis","p":2413},{"i":2422,"t":"At first glance, Centrifugo provides concepts similar to Apache Kafka - it has channels which seem similar to Kafka's topics, channel history with time and size retention policies. So sometimes people ask whether Centrifugo may be used as a lightweight Kafka replacement. But Centrifugo and Kafka were designed for different purposes. Centrifugo is a real-time messaging system with push semantics. It provides a lightweight PUB/SUB with ephemeral in-memory channels, designed to be exposed to frontend real-time apps. It has limited guarantees about message persistence in channel history, though it provides client hooks to be notified about message loss and recover the state from the main application database. Kafka is a persistent disk-based message bus that you can't easily expose to frontend users. It won't work well with millions of topics where users connect and disconnect constantly – this would cause constant repartitioning and eventually require much more resources. Kafka fits well for service-to-service communication where topics may be pre-created and under control; Centrifugo fits well for frontend-to-backend real-time communication.","s":"Centrifugo vs Kafka","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-kafka","p":2413},{"i":2424,"t":"This is also a popular comparison request. Especially since both Centrifugo and Nats are written in the Go language, and Nats also supports connections from the application client side over the WebSocket protocol. Nats is a very powerful messaging system, and it also has a built-in Jetstream system to provide at least once delivery guarantees. It has a larger community, more SDKs for various languages, incredible performance. How Centrifugo is special: Centrifugo actually uses Nats as one of the options for a PUB/SUB broker, i.e., as a PUB/SUB scalability backend. Centrifugo was originally designed to be exposed to application frontend clients, providing various convenient authentication and channel authorization mechanisms for client-side integration. It supports more transports, including WebSocket fallbacks. It provides many unique features that are out of scope for Nats – like online presence, individual GRPC subscription streams, or some of Centrifugo PRO features like push notifications support, real-time analytics with ClickHouse, etc. Topics in Jetstream still should be pre-created, while Centrifugo has ephemeral channels – created on the fly, even when using a channel history cache. Centrifugo provides client connection event proxy features - it's possible to delegate authentication, channel authorization to the application backend, and provides channel state events (when a channel is occupied or vacated) in the PRO version. Nats is great, and we are constantly looking for tighter integration with Nats. But both systems have unique sets of features and may be better or worse for various tasks.","s":"Centrifugo vs Nats","u":"/docs/getting-started/comparisons","h":"#centrifugo-vs-nats","p":2413},{"i":2426,"t":"On this page","s":"Ecosystem notes","u":"/docs/getting-started/ecosystem","h":"","p":2425},{"i":2428,"t":"Centrifugo is built on top of the Centrifuge library for the Go language. Due to its standalone language-agnostic nature, Centrifugo dictates some rules developers should follow when integrating. If you need more freedom and a tighter integration of a real-time server with application business logic, you may consider using the Centrifuge library to build something similar to Centrifugo but with customized behavior. The Centrifuge library can be considered as an analogue of Socket.IO in the Go language ecosystem. The library's README has a detailed description, a link to examples, and an introduction post. Many Centrifugo features should be re-implemented when using Centrifuge - like the API layer, admin web UI, proxy, etc. (if you need those of course). And you need to write in the Go language. But the core functionality like a client-server protocol (all Centrifugo client SDKs work with a Centrifuge library-based server) and Redis engine to scale come out of the box – in most cases, this is enough to start building an app. tip Many things said in the Centrifugo doc can be considered as extra documentation for the Centrifuge library (for example, parts about infrastructure tuning or transport description). But not all of them.","s":"Centrifuge library for Go","u":"/docs/getting-started/ecosystem","h":"#centrifuge-library-for-go","p":2425},{"i":2430,"t":"There are some community-driven projects that provide integration with frameworks for more native experience. tip In general, integrating Centrifugo can be done in several steps even without third-party libraries – see our integration guide. Integrating directly may allow using all Centrifugo features without limitations which can be introduced by third-party wrapper. laravel-centrifugo integration with Laravel framework laravel-centrifugo-broadcaster one more integration with Laravel framework to consider CentrifugoBundle integration with Symfony framework Django-instant integration with Django framework roadrunner-php/centrifugo integration with RoadRunner spiral/roadrunner-bridge integration with Spiral Framework","s":"Framework integrations","u":"/docs/getting-started/ecosystem","h":"#framework-integrations","p":2425},{"i":2432,"t":"On this page","s":"Main highlights","u":"/docs/getting-started/highlights","h":"","p":2431},{"i":2434,"t":"Centrifugo was originally designed to be used in conjunction with frameworks without built-in concurrency support (like Django, Laravel, etc.). It works as a standalone service with well-defined communication contracts. It nicely fits both monolithic and microservice architectures. Application developers should not change the backend philosophy and technology stack at all – just integrate with Centrifugo HTTP or GRPC API and let users enjoy real-time updates.","s":"Simple integration","u":"/docs/getting-started/highlights","h":"#simple-integration","p":2431},{"i":2436,"t":"Centrifugo is fast. It's written in the Go language, built on top of fast and battle-tested open-source libraries, has some smart internal optimizations like message queuing on broadcasts, smart batching to reduce the number of RTTs with the broker, connection hub sharding to avoid lock contention, JSON and Protobuf encoding speedups through code generation, and others. See the Million WebSocket with Centrifugo post on our blog to see some real-world numbers.","s":"Great performance","u":"/docs/getting-started/highlights","h":"#great-performance","p":2431},{"i":2438,"t":"Centrifugo scales well to many machines with the help of PUB/SUB brokers. So as soon as you have more client connections in the application – you can spread them over different Centrifugo nodes which will be connected together into a cluster. The main PUB/SUB engine that Centrifugo integrates with is Redis. It supports client-side consistent sharding and Redis Cluster – so a single Redis instance won't be a bottleneck either. There are other options to scale: KeyDB, Nats, Tarantool. See docs about available engines.","s":"Built-in scalability","u":"/docs/getting-started/highlights","h":"#built-in-scalability","p":2431},{"i":2440,"t":"Centrifugo supports JSON and binary Protobuf protocols for client-server communication. The bidirectional protocol is defined by a strict schema and several ready-to-use SDKs wrap this protocol, handle asynchronous message passing, timeouts, reconnects, and various Centrifugo client API features. See detailed information about client real-time transports in a dedicated section.","s":"Strict client protocol","u":"/docs/getting-started/highlights","h":"#strict-client-protocol","p":2431},{"i":2442,"t":"The main transport in Centrifugo is WebSocket. For browsers that do not support WebSocket, Centrifugo provides its own bidirectional WebSocket emulation layer based on HTTP-streaming (using Fetch and Readable streams browser APIs) and SSE (EventSource). It also supports SockJS as an older but battle-tested WebSocket polyfill option. Additionally, WebTransport is supported in an experimental form. In addition to bidirectional transports, Centrifugo also supports a unidirectional approach for real-time updates: using SSE (EventSource), HTTP-streaming, and GRPC unidirectional stream. Utilizing a unidirectional transport is sufficient for many real-time applications and does not require using our client SDKs – just native standards or GRPC-generated code. See detailed information about client real-time transports in a dedicated section.","s":"Variety of real-time transports","u":"/docs/getting-started/highlights","h":"#variety-of-real-time-transports","p":2431},{"i":2444,"t":"Centrifugo can authenticate connections by checking JWT (JSON Web Token) or by issuing an HTTP/GRPC request to your application backend upon a client connection to Centrifugo. It's possible to proxy original request headers or request metadata (in the case of a GRPC connection). It supports the JWK specification.","s":"Flexible authentication","u":"/docs/getting-started/highlights","h":"#flexible-authentication","p":2431},{"i":2446,"t":"Connections can expire; developers can choose a way to handle connection refresh – using a client-side refresh workflow or a server-side call from Centrifugo to the application backend. Centrifugo provides APIs to disconnect users, unsubscribe users from channels, and inspect active channels.","s":"Connection management","u":"/docs/getting-started/highlights","h":"#connection-management","p":2431},{"i":2448,"t":"Centrifugo is a PUB/SUB server – users subscribe to channels to receive real-time updates. A message sent to a channel is delivered to all online channel subscribers.","s":"Channel (room) concept","u":"/docs/getting-started/highlights","h":"#channel-room-concept","p":2431},{"i":2450,"t":"Centrifugo supports client-side (initiated by the client) and server-side (forced by the server) channel subscriptions.","s":"Different types of subscriptions","u":"/docs/getting-started/highlights","h":"#different-types-of-subscriptions","p":2431},{"i":2452,"t":"You can fully utilize bidirectional connections by sending RPC calls from the client-side to a configured endpoint on your backend. Calling RPC over WebSocket avoids sending headers on each request – thus reducing incoming traffic.","s":"RPC over bidirectional connection","u":"/docs/getting-started/highlights","h":"#rpc-over-bidirectional-connection","p":2431},{"i":2454,"t":"The online presence feature for channels provides information about active channel subscribers. Also, channel join and leave events (when someone subscribes/unsubscribes) can be received on the client side.","s":"Online presence information","u":"/docs/getting-started/highlights","h":"#online-presence-information","p":2431},{"i":2456,"t":"Optionally, Centrifugo allows turning on history for publications in channels. This publication history has a limited size and retention period (TTL). With channel history, Centrifugo can help to survive the mass reconnect scenario – clients can automatically catch up on missed state from a fast cache thus reducing the load on your primary database. It's also possible to manually iterate over a history stream from the client or from the application backend side.","s":"Message history in channels","u":"/docs/getting-started/highlights","h":"#message-history-in-channels","p":2431},{"i":2458,"t":"The built-in admin UI allows publishing messages to channels, looking at Centrifugo cluster information, and more.","s":"Embedded admin web UI","u":"/docs/getting-started/highlights","h":"#embedded-admin-web-ui","p":2431},{"i":2460,"t":"Centrifugo works on Linux, macOS, and Windows.","s":"Cross-platform","u":"/docs/getting-started/highlights","h":"#cross-platform","p":2431},{"i":2462,"t":"Centrifugo supports various deployment methods: in Docker, using prepared RPM or DEB packages, via a Kubernetes Helm chart. It supports automatic TLS with Let's Encrypt TLS, outputs Prometheus/Graphite metrics, and has an official Grafana dashboard for the Prometheus data source.","s":"Ready to deploy","u":"/docs/getting-started/highlights","h":"#ready-to-deploy","p":2431},{"i":2464,"t":"Centrifugo stands on top of the open-source library Centrifuge (MIT license). The OSS version of Centrifugo is based on the permissive open-source license (Apache 2.0). All our official client SDKs and API libraries are MIT-licensed.","s":"Open-source","u":"/docs/getting-started/highlights","h":"#open-source","p":2431},{"i":2466,"t":"Centrifugo PRO extends Centrifugo with several unique features which can provide interesting advantages for business adopters. For additional details, refer to the Centrifugo PRO documentation.","s":"PRO features","u":"/docs/getting-started/highlights","h":"#pro-features","p":2431},{"i":2468,"t":"On this page","s":"Frequently Asked Questions","u":"/docs/faq","h":"","p":2467},{"i":2470,"t":"This depends on many factors. Real-time transport choice, hardware, message rate, size of messages, Centrifugo features enabled, client distribution over channels, compression on/off, etc. So no certain answer to this question exists. Common sense, performance measurements, and monitoring can help here. Generally, we suggest not put more than 50-100k clients on one node - but you should measure for your use case. You can find a description of a test stand with million WebSocket connections in this blog post. Though the point above is still valid – measure and monitor your setup.","s":"How many connections can one Centrifugo instance handle?","u":"/docs/faq","h":"#how-many-connections-can-one-centrifugo-instance-handle","p":2467},{"i":2472,"t":"Depending on transport used and features enabled the amount of RAM required per each connection can vary. For example, you can expect that each WebSocket connection will cost about 30-50 KB of RAM, thus a server with 1 GB of RAM can handle about 20-30k connections. For other real-time transports, the memory usage per connection can differ (for example, SockJS connections will cost ~ 2 times more RAM than pure WebSocket connections). So the best way is again – measure for your custom case since depending on Centrifugo transport/features memory usage can vary.","s":"Memory usage per connection?","u":"/docs/faq","h":"#memory-usage-per-connection","p":2467},{"i":2474,"t":"Yes, it can do this using built-in engines: Redis, KeyDB, Tarantool, or Nats broker. See engines and scalability considerations.","s":"Can Centrifugo scale horizontally?","u":"/docs/faq","h":"#can-centrifugo-scale-horizontally","p":2467},{"i":2476,"t":"See design overview","s":"Message delivery model","u":"/docs/faq","h":"#message-delivery-model","p":2467},{"i":2478,"t":"See design overview.","s":"Message order guarantees","u":"/docs/faq","h":"#message-order-guarantees","p":2467},{"i":2480,"t":"No. By default, channels are created automatically as soon as the first client subscribed to it. And destroyed automatically when the last client unsubscribes from a channel. When history inside the channel is on then a window of last messages is kept automatically during the retention period. So a client that comes later and subscribes to a channel can retrieve those messages using the call to the history API (or maybe by using the automatic recovery feature which also uses a history internally).","s":"Should I create channels explicitly?","u":"/docs/faq","h":"#should-i-create-channels-explicitly","p":2467},{"i":2482,"t":"Channel is a very lightweight ephemeral entity - Centrifugo can deal with lots of channels, don't be afraid to have many channels in an application. But keep in mind that one client should be subscribed to a reasonable number of channels at one moment. Client-side subscription to a channel requires a separate frame from client to server – more frames mean more heavy initial connection, more heavy reconnect, etc. One example which may lead to channel misusing is a messenger app where user can be part of many groups. In this case, using a separate channel for each group/chat in a messenger may be a bad approach. The problem is that messenger app may have chat list screen – a view that displays all user groups (probably with pagination). If you are using separate channel for each group then this may lead to lots of subscriptions. Also, with pagination, to receive updates from older chats (not visible on a screen due to pagination) – user may need to subscribe on their channels too. In this case, using a single personal channel for each user is a preferred approach. As soon as you need to deliver a message to a group you can use Centrifugo broadcast API to send it to many users. If your chat groups are huge in size then you may also need additional queuing system between your application backend and Centrifugo to broadcast a message to many personal channels.","s":"What about best practices with the number of channels?","u":"/docs/faq","h":"#what-about-best-practices-with-the-number-of-channels","p":2467},{"i":2484,"t":"Currently, no. We know that services like Pusher provide a way to exclude current client by providing a client ID (socket ID) in publish request. A couple of problems with this: Client can reconnect while message travels over wire/Backend/Centrifugo – in this case client has a chance to receive a message unexpectedly since it will have another client ID (socket ID) Client can call a history manually or message recovery process can run upon reconnect – in this case a message will present in a history Both cases may result in duplicate messages. These reasons prevent us adding such functionality into Centrifugo, the correct application architecture requires having some sort of idempotent identifier which allow dealing with message duplicates. Once added nobody will think about idempotency and this can lead to hard to catch/fix problems in an application. This can also make enabling channel history harder at some point. Centrifugo behaves similar to Kafka here – i.e. channel should be considered as immutable stream of events where each channel subscriber simply receives all messages published to a channel. In the future releases Centrifugo may have some sort of server-side message filtering, but we are searching for a proper and safe way of adding it.","s":"Any way to exclude message publisher from receiving a message from a channel?","u":"/docs/faq","h":"#any-way-to-exclude-message-publisher-from-receiving-a-message-from-a-channel","p":2467},{"i":2486,"t":"No. It's not possible to transparently encode binary data into JSON protocol (without converting binary to base64 for example which we don't want to do due to increased complexity and performance penalties). So if you have clients in a channel which work with JSON – you need to use JSON payloads everywhere. Most Centrifugo bidirectional connectors are using binary Protobuf protocol between a client and Centrifugo. But you can send JSON over Protobuf protocol just fine (since JSON is a UTF-8 encoded sequence of bytes in the end). To summarize: if you are using binary Protobuf clients and binary payloads everywhere – you are fine. if you are using binary or JSON clients and valid JSON payloads everywhere – you are fine. if you try to send binary data to JSON protocol based clients – you will get errors from Centrifugo.","s":"Can I have both binary and JSON clients in one channel?","u":"/docs/faq","h":"#can-i-have-both-binary-and-json-clients-in-one-channel","p":2467},{"i":2488,"t":"While online presence is a good feature it does not fit well for some apps. For example, if you make a chat app - you may probably use a single personal channel for each user. In this case, you cannot find who is online at moment using the built-in Centrifugo presence feature as users do not share a common channel. You can solve this using a separate service that tracks the online status of your users (for example in Redis) and has a bulk API that returns online status approximation for a list of users. This way you will have an efficient scalable way to deal with online statuses. This is also available as Centrifugo PRO feature.","s":"Online presence for chat apps - online status of your contacts","u":"/docs/faq","h":"#online-presence-for-chat-apps---online-status-of-your-contacts","p":2467},{"i":2490,"t":"The most popular reason behind this is reaching the open file limit. You can make it higher, we described how to do this nearby in this doc. Also, check out an article in our blog which mentions possible problems when dealing with many persistent connections like WebSocket.","s":"Centrifugo stops accepting new connections, why?","u":"/docs/faq","h":"#centrifugo-stops-accepting-new-connections-why","p":2467},{"i":2492,"t":"Yes, you can - Go standard library designed to allow this. Though proxy before Centrifugo can be very useful for load balancing clients.","s":"Can I use Centrifugo without reverse-proxy like Nginx before it?","u":"/docs/faq","h":"#can-i-use-centrifugo-without-reverse-proxy-like-nginx-before-it","p":2467},{"i":2494,"t":"Yes, Centrifugo works with HTTP/2. This is provided by built-in Go http server implementation. You can disable HTTP/2 running Centrifugo server with GODEBUG environment variable: GODEBUG=\"http2server=0\" centrifugo -c config.json Keep in mind that when using WebSocket you are working only over HTTP/1.1, so HTTP/2 support mostly makes sense for SockJS HTTP transports and unidirectional transports: like EventSource (SSE) and HTTP-streaming.","s":"Does Centrifugo work with HTTP/2?","u":"/docs/faq","h":"#does-centrifugo-work-with-http2","p":2467},{"i":2496,"t":"Centrifugo v4 added an experimental HTTP/3 support. As soon as you enabled TLS and provided \"http3\": true option all endpoints on external port will be served by HTTP/3 server based on github.com/quic-go/quic-go implementation. This (among other benefits which HTTP/3 can provide) is a step towards a proper WebTransport support. For now we support WebTransport experimentally. It's worth noting that WebSocket transport does not work over HTTP/3, it still starts with HTTP/1.1 Upgrade request (there is an interesting IETF draft BTW about Bootstrapping WebSockets with HTTP/3). But HTTP-streaming and Eventsource should work just fine with HTTP/3. HTTP/3 does not work with ACME autocert TLS at the moment - i.e. you need to explicitly provide paths to cert and key files as described here.","s":"Does Centrifugo work with HTTP/3?","u":"/docs/faq","h":"#does-centrifugo-work-with-http3","p":2467},{"i":2498,"t":"If the underlying transport is HTTP-based, and you use HTTP/2 then this will work automatically. For WebSocket, each browser tab creates a new connection.","s":"Is there a way to use a single connection to Centrifugo from different browser tabs?","u":"/docs/faq","h":"#is-there-a-way-to-use-a-single-connection-to-centrifugo-from-different-browser-tabs","p":2467},{"i":2500,"t":"Sometimes it's confusing to see a difference between real-time messages and push notifications. Centrifugo is a real-time messaging server. It can not send push notifications to devices - to Apple iOS devices via APNS, Android devices via FCM, or browsers over Web Push API. We are preparing our own push notifications API as part of Centrifugo PRO version. This is under construction though. The reasonable question here is how can you know when you need to send a real-time message to an online client or push notification to its device for an offline client. The solution is pretty simple. You can keep critical notifications for a client in the database. And when a client reads a message you should send an ack to your backend marking that notification as read by the client. Periodically you can check which notifications were sent to clients but they have not read it (no read ack received). For such notifications, you can send push notifications to its device using your own or another open-source solution. Look at Firebase for example.","s":"What if I need to send push notifications to mobile or web applications?","u":"/docs/faq","h":"#what-if-i-need-to-send-push-notifications-to-mobile-or-web-applications","p":2467},{"i":2502,"t":"You can, but Centrifugo does not have such an API. What you have to do to ensure your client has received a message is sending confirmation ack from your client to your application backend as soon as the client processed the message coming from a Centrifugo channel.","s":"How can I know a message is delivered to a client?","u":"/docs/faq","h":"#how-can-i-know-a-message-is-delivered-to-a-client","p":2467},{"i":2504,"t":"It's possible to publish messages into channels directly from a client (when publish channel option is enabled). But we strongly discourage this in production usage as those messages just go through Centrifugo without any additional control and validation from the application backend. We suggest using one of the available approaches: When a user generates an event it must be first delivered to your app backend using a convenient way (for example AJAX POST request for a web application), processed on the backend (validated, saved into the main application database), and then published to Centrifugo using Centrifugo HTTP or GRPC API. Utilize the RPC proxy feature – in this case, you can call RPC over Centrifugo WebSocket which will be translated to an HTTP request to your backend. After receiving this request on the backend you can publish a message to Centrifugo server API. This way you can utilize WebSocket transport between the client and your server in a bidirectional way. HTTP traffic will be concentrated inside your private network. Utilize the publish proxy feature – in this case client can call publish on the frontend, this publication request will be transformed into HTTP or GRPC call to the application backend. If your backend allows publishing - Centrifugo will pass the payload to the channel (i.e. will publish message to the channel itself). Sometimes publishing from a client directly into a channel (without any backend involved) can be useful though - for personal projects, for demonstrations (like we do in our examples) or if you trust your users and want to build an application without backend. In all cases when you don't need any message control on your backend.","s":"Can I publish new messages over a WebSocket connection from a client?","u":"/docs/faq","h":"#can-i-publish-new-messages-over-a-websocket-connection-from-a-client","p":2467},{"i":2506,"t":"There are several ways to achieve it: use a private channel (starting with $) - every time a user subscribes to it your backend should provide a sign to confirm that subscription request. Read more in channels chapter next is user limited channels (with #) - you can create a channel with a name like dialog#42,567 to limit subscribers only to the user with id 42 and user with ID 567, this does not fit well for channels with many or dynamic possible subscribers you can use subscribe proxy feature to validate subscriptions, see chapter about proxy finally, you can create a hard-to-guess channel name (based on some secret key and user IDs or just generate and save this long unique name into your main app database) so other users won't know this channel to subscribe on it. This is the simplest but not the safest way - but can be reasonable to consider in many situations","s":"How to create a secure channel for two users only (private chat case)?","u":"/docs/faq","h":"#how-to-create-a-secure-channel-for-two-users-only-private-chat-case","p":2467},{"i":2508,"t":"In most situations, your application needs several different real-time features. We suggest using namespaces for every real-time feature if it requires some option enabled. For example, if you need join/leave messages for a chat app - create a special channel namespace with this join_leave option enabled. Otherwise, your other channels will receive join/leave messages too - increasing load and traffic in the system but not used by clients. The same relates to other channel options.","s":"What's the best way to organize channel configuration?","u":"/docs/faq","h":"#whats-the-best-way-to-organize-channel-configuration","p":2467},{"i":2510,"t":"Proxy feature allows integrating Centrifugo with your session mechanism (via connect proxy) and provides a way to react to connection events (rpc, subscribe, publish). Also, it opens a road for bidirectional communication with RPC calls. And periodic connection refresh hooks are also there. Centrifugo does not support unsubscribe/disconnect hooks – see the reasoning below.","s":"Does Centrifugo support webhooks?","u":"/docs/faq","h":"#does-centrifugo-support-webhooks","p":2467},{"i":2512,"t":"UPDATE Centrifugo PRO now solves the pitfalls mentioned here with its Channel State Events feature. Centrifugo does not support disconnect hooks at this point. We understand that this may be useful for some use cases but there are some pitfalls which prevent us adding such hooks to Centrifugo. Let's consider a case when Centrifugo node is unexpectedly killed. In this case there is no chance for Centrifugo to emit disconnect events for connections on that node. While this may be rare thing in practice – it may lead to inconsistent state in you app if you'd rely on disconnect hooks. Another reason is that Centrifugo designed to scale to many concurrent connections. Think millions of them. As we mentioned in our blog there are cases when all connections start reconnecting at the same time. In this case Centrifugo could potentially generate lots of disconnect events. Even if disconnect events were queued, rate-limited, or suppressed for quickly reconnected clients there could be situations when your app processes disconnect hook after user already reconnected. This is a racy situation which also can lead to the inconsistency if not properly addressed. Is there a workaround though? If you need to know that client disconnected and program your business logic around this fact then the reasonable approach could be periodically call your backend while client connection is active and update status somewhere on the backend (possibly using Redis for this). Then periodically do clealup logic for connections/users not updated for a configured interval. This is a robust solution where you can't occasionally miss disconnect events. You can also utilize Centrifugo connect proxy + refresh proxy for getting notified about initial connection and get periodic refresh requests while connection is alive. The trade-off of the described workaround scenario is that you will notice disconnection only with some delay – this may be a acceptable in many cases though. Having said that, processing disconnect events may be reasonable – as a best-effort solution while taking into account everything said above. Centrifuge library for Go language (which is the core of Centrifugo) supports client disconnect callbacks on a server-side – so technically the possibility exists. If someone comes with a use case which definitely wins from having disconnect hooks in Centrifugo we are ready to discuss this and try to design a proper solution together. All the pitfalls and workarounds here may be also applied to unsubscribe event hooks.","s":"Why Centrifugo does not have disconnect hooks?","u":"/docs/faq","h":"#why-centrifugo-does-not-have-disconnect-hooks","p":2467},{"i":2514,"t":"No, join/leave events are only available in the client protocol. In most cases join event can be handled by using subscribe proxy. Leave events are harder – there is no unsubscribe hook available (mostly the same reasons as for disconnect hook described above). So the workaround here can be similar to one for disconnect – ping an app backend periodically while client is subscribed and thus know that client is currently in a channel with some approximation in time.","s":"Is it possible to listen to join/leave events on the app backend side?","u":"/docs/faq","h":"#is-it-possible-to-listen-to-joinleave-events-on-the-app-backend-side","p":2467},{"i":2516,"t":"Online presence is good for channels with a reasonably small number of active subscribers. As soon as there are tons of active subscribers, presence information becomes very expensive in terms of bandwidth (as it contains full information about all clients in a channel). There is presence_stats API method that can be helpful if you only need to know the number of clients (or unique users) in a channel. But in the case of the Redis engine even presence_stats call is not optimized for channels with more than several thousand active subscribers. You may consider using a separate service to deal with presence status information that provides information in near real-time maybe with some reasonable approximation. Centrifugo PRO provides a user status feature which may fit your needs. The same is true for join/leave messages - as soon as you turn on join/leave events for a channel with many active subscribers each subscriber starts generating indiviaual join/leave events. This may result in many messages sent to each subscriber in a channel, drastically multiplying amount of messages traveling through the system. Especially when all clients reconnect simulteniously. So be careful and estimate the possible load. There is no magic, unfortunately.","s":"How scalable is the online presence and join/leave features?","u":"/docs/faq","h":"#how-scalable-is-the-online-presence-and-joinleave-features","p":2467},{"i":2518,"t":"Sometimes you need to send some initial state towards channel subscriber. Centrifugo provides a way to attach any data to a successful subscribe reply when using subscribe proxy feature. See data and b64data fields. This data will be part of subscribed event context. And of course, you can always simply send request to get initial data from the application backend before or after subscribing to a channel without Centrifugo connection involved (i.e. using sth like general AJAX/HTTP call or passing data to the template when rendering an application page).","s":"How to send initial data to channel subscriber?","u":"/docs/faq","h":"#how-to-send-initial-data-to-channel-subscriber","p":2467},{"i":2520,"t":"If you want to use Centrifugo with different projects the recommended approach is to have different Centrifugo installations for each project. Multitenancy is better to solve on infrastructure level in case of Centrifugo. It's possible to share one Redis setup though by setting unique redis_prefix. But we recommend having completely isolated setups.","s":"Does Centrifugo support multitenancy?","u":"/docs/faq","h":"#does-centrifugo-support-multitenancy","p":2467},{"i":2522,"t":"Ask in our community rooms:","s":"I have not found an answer to my question here","u":"/docs/faq","h":"#i-have-not-found-an-answer-to-my-question-here","p":2467},{"i":2524,"t":"On this page","s":"Centrifugo introduction","u":"/docs/getting-started/introduction","h":"","p":2523},{"i":2526,"t":"Centrifugo was born more than a decade ago to help applications whose server-side code was written in a language or framework lacking built-in concurrency support. In such cases, managing persistent connections can be a real headache, usually resolvable only by altering the technology stack and investing time in developing a production-ready solution. For instance, frameworks like Django, Flask, Yii, Laravel, Ruby on Rails, and others offer limited or suboptimal support for handling numerous persistent connections for real-time messaging tasks. Here, Centrifugo provides a straightforward and non-obtrusive way to introduce real-time updates and manage many persistent connections without radical changes in the application backend architecture. Developers can continue to work on the application's backend using their preferred language or framework, and keep the existing architecture. Just let Centrifugo deal with persistent connections and be the real-time messaging transport layer. These days, Centrifugo offers advanced and unique features that can significantly simplify a developer's workload and save months (if not years) of development time, even if the application's backend is built with an asynchronous concurrent language or framework. One example is Centrifugo's built-in support for scaling across numerous machines to accommodate more connections while ensuring that channel subscribers on different Centrifugo nodes receive all publications. Or the fact that Centrifugo has a bunch of real-time SDKs which provide subscription multiplexing over a WebSocket connection, robust reconnect logic, built-in ping-pong, etc. And there are more things to mention: the documentation uncovers features step by step. Centrifugo fits well with modern architectures and can serve as a universal real-time component, regardless of the application's technology stack. It stands as a viable self-hosted alternative to cloud solutions like Pusher, Ably, or PubNub.","s":"Background","u":"/docs/getting-started/introduction","h":"#background","p":2523},{"i":2528,"t":"On this page","s":"Migrating to v4","u":"/docs/getting-started/migration_v4","h":"","p":2527},{"i":2530,"t":"New generation of client protocol requires using the latest versions of client SDKs. During the next several days we will release the following SDK versions which are compatible with Centrifugo v4: centrifuge-js >= v3.0.0 centrifuge-go >= v0.9.0 centrifuge-dart >= v0.9.0 centrifuge-swift >= v0.5.0 centrifuge-java >= v0.2.0 New client SDKs support only new client protocol – you can not connect to Centrifugo v3 with them. If you have a production system where you want to upgrade Centrifugo from v3 to v4 then the plan is: danger If you are using private channels (starting with $) or user-limited channels (containing #) then carefully read about subscription token migration and user-limited channels migration below. Upgrade Centrifugo and its configuration to adopt changes in v4. In Centrifugo v4 config turn on use_client_protocol_v1_by_default. Run Centrifugo v4 – all current clients should continue working with it. Then on the client-side uprade client SDK version to the one which works with Centrifugo v4, adopt changes in SDK API dictated by our new client SDK API spec. Important thing – add ?cf_protocol_version=v2 URL param to the connection endpoint to tell Centrifugo that modern generation of protocol is being used by the connection (otherwise, it assumes old protocol since we have use_client_protocol_v1_by_default option enabled). As soon as all your clients migrated to use new protocol generation you can remove use_client_protocol_v1_by_default option from the server configuration. After that you can remove ?cf_protocol_version=v2 from connection endpoint on the client-side. tip If you are using mobile client SDKs then most probably some time must pass while clients update their apps to use an updated Centrifugo SDK version. tip Starting from Centrifugo v4.1.1 it's possible to completely turn off client protocol v1 by setting disable_client_protocol_v1 boolean option to true.","s":"Client SDK migration","u":"/docs/getting-started/migration_v4","h":"#client-sdk-migration","p":2527},{"i":2532,"t":"Client protocol framing also changed in unidirectional transports. The good news is that Centrifugo v4 still supports previous format for unidirectional transports. When you are enabling use_client_protocol_v1_by_default option described above you also make unidirectional transports to work over old protocol format. So your existing clients will continue working just fine with Centrifugo v4. Then the same steps to migrate described above can be applied to unidirectional transport case. The only difference that in unidirectional approach you are not using Centrifugo SDKs.","s":"Unidirectional transport migration","u":"/docs/getting-started/migration_v4","h":"#unidirectional-transport-migration","p":2527},{"i":2534,"t":"SockJS is now DEPRECATED in Centrifugo. Centrifugo v4 may be the last release which supports it. We now offer our own bidirectional emulation layer on top of HTTP-streaming and EventSource. See additional information in Centrifugo v4 introduction post.","s":"SockJS migration","u":"/docs/getting-started/migration_v4","h":"#sockjs-migration","p":2527},{"i":2536,"t":"Centrifugo v2 and v3 docs mentioned the fact that channels must contain only ASCII characters. But it was not actually enforced by a server. Now Centrifugo is more strict. If a channel has non-ASCII characters then the 107: bad request error will be returned to the client. Please reach us out if this behavior is not suitable for your use case – we can discuss the use case and think on a proper solution together.","s":"Channel ASCII enforced","u":"/docs/getting-started/migration_v4","h":"#channel-ascii-enforced","p":2527},{"i":2538,"t":"Subscription token now requires sub claim (current user ID) to be set. In most cases the only change which is required to smoothly migrate to v4 without breaking things is to add a boolean option \"skip_user_check_in_subscription_token\": true to a Centrifugo v4 configuration. This skips the check of sub claim to contain the current user ID set to a connection during authentication. After that start adding sub claim (with current user ID) to subscription tokens. As soon as all subscription tokens in your system contain user ID in sub claim you can remove the skip_user_check_in_subscription_token from a server configuration. One more important note is that client claim in subscription token in Centrifugo v4 only supported for backwards compatibility. It must not be included into new subscription tokens. It's worth mentioning that Centrifugo v4 does not allow subscribing on channels starting with $ without token even if namespace marked as available for subscribing using sth like allow_subscribe_for_client option. This is done to prevent potential security risk during v3 -> v4 migration when client previously not available to subscribe to channels starting with $ in any case may get permissions to do so.","s":"Subscription token migration","u":"/docs/getting-started/migration_v4","h":"#subscription-token-migration","p":2527},{"i":2540,"t":"User-limited channel support should now be allowed over a separate channel namespace option allow_user_limited_channels. See below the namespace option converter which takes this change into account.","s":"User-limited channel migration","u":"/docs/getting-started/migration_v4","h":"#user-limited-channel-migration","p":2527},{"i":2542,"t":"In Centrifugo v4 namespace configuration options have been changed. Centrifugo now has secure by default namespaces. First thing to do is to read the new docs about channels and namespaces. Then you can use the following converter which will transform your old namespace configuration to a new one. This converter tries to keep backwards compatibility – i.e. it should be possible to deploy Centrifugo with namespace configuration from converter output and have the same behaviour as before regarding channel permissions. We believe that new option names should provide a more readable configuration and may help to reveal some potential security improvements in your namespace configuration – i.e. making it more strict and protective. caution Do not blindly deploy things to production – test your system first, go through the possible usage scenarios and/or test cases. tip It's fully client-side: your data won't be sent anywhere. Convert Here will be configuration for v4 Here will be log of changes made in your config","s":"Namespace configuration migration","u":"/docs/getting-started/migration_v4","h":"#namespace-configuration-migration","p":2527},{"i":2544,"t":"reconnect flag from custom disconnect code is removed. Reconnect advice is now determined by disconnect code value. This allowed us avoiding using JSON in WebSocket CLOSE frame reason. See proxy docs docs for more details.","s":"Proxy disconnect code changes","u":"/docs/getting-started/migration_v4","h":"#proxy-disconnect-code-changes","p":2527},{"i":2546,"t":"Several other non-namespace related options have been renamed or removed: client_anonymous option renamed to allow_anonymous_connect_without_token – new name better describes the purpose of this option which was previously not clear. Converter above takes this into account. use_unlimited_history_by_default option was removed. It was used to help migrating from Centrifugo v2 to v3.","s":"Other configuration option changes","u":"/docs/getting-started/migration_v4","h":"#other-configuration-option-changes","p":2527},{"i":2548,"t":"The only breaking change is that user_connections API method (which is available in Centrifugo PRO only) was renamed to connections. The method is more generic now with a broader possibilities – so previous name does not match the current behavior.","s":"Server API changes","u":"/docs/getting-started/migration_v4","h":"#server-api-changes","p":2527},{"i":2550,"t":"On this page","s":"Integration guide","u":"/docs/getting-started/integration","h":"","p":2549},{"i":2552,"t":"The first thing you need to do is download/install the Centrifugo server. See the install chapter for details.","s":"0. Install","u":"/docs/getting-started/integration","h":"#0-install","p":2549},{"i":2554,"t":"Create a basic configuration file with token_hmac_secret_key (or token_rsa_public_key) and api_key set, and then run Centrifugo. See this chapter for details about token_hmac_secret_key/token_rsa_public_key and the chapter about server API for the API description. The simplest way to do this automatically is by using the genconfig command: ./centrifugo genconfig – which will generate config.json file for you with a minimal set of fields to start from. Properly configure allowed_origins option.","s":"1. Configure Centrifugo","u":"/docs/getting-started/integration","h":"#1-configure-centrifugo","p":2549},{"i":2556,"t":"In the configuration file of your application backend, register several variables: the Centrifugo token secret (if you decided to stick with JWT authentication) and the Centrifugo API key you set in the previous step, as well as the Centrifugo API endpoint address. By default, the API address is http://localhost:8000/api. You must never reveal the token secret and API key to your users.","s":"2. Configure your backend","u":"/docs/getting-started/integration","h":"#2-configure-your-backend","p":2549},{"i":2558,"t":"Now your users can start connecting to Centrifugo. You should get a client library (see the list of available client SDKs) for your application frontend. Every library has a method to connect to Centrifugo. See information about Centrifugo connection endpoints here. Every client should provide a connection token (JWT) upon connecting. You must generate this token on your backend side using the Centrifugo secret key you set in the backend configuration (note that in the case of RSA tokens, you are generating JWT with a private key). See how to generate this JWT in the special chapter. You pass this token from the backend to your frontend app (pass it in the template context or use a separate request from the client-side to get a user-specific JWT from the backend side). And use this token when connecting to Centrifugo (for example, the browser client has a special method setToken). There is also a way to authenticate connections without using JWT - see the chapter about proxying to the backend. You connect to Centrifugo using one of the available transports.","s":"3. Connect to Centrifugo","u":"/docs/getting-started/integration","h":"#3-connect-to-centrifugo","p":2549},{"i":2560,"t":"After connecting to Centrifugo, subscribe clients to channels they are interested in. See more about channels in the special chapter. All bidirectional client SDKs provide a way to handle messages coming to a client from a channel after subscribing to it. Learn more about client SDK possibilities from the client SDK API spec. There is also a way to subscribe a connection to a list of channels on the server side at the moment of connection establishment. See the chapter about server-side subscriptions.","s":"4. Subscribe to channels","u":"/docs/getting-started/integration","h":"#4-subscribe-to-channels","p":2549},{"i":2562,"t":"Everything should work now – as soon as a user opens some page of your application, they must successfully connect to Centrifugo and subscribe to a channel (or channels). Now let's imagine you want to send a real-time message to users subscribed to a specific channel. This message can be a reaction to some event that happened in your app: someone posted a new comment, an administrator just created a new post, a user pressed the \"like\" button, etc. Anyway, this is an event your backend has just received, and you want to immediately send it to interested users. You can do this using the Centrifugo HTTP API. To simplify your life, we have several API libraries for different languages. You can publish messages into a channel using one of those libraries, or you can simply follow the API description to construct API requests yourself - this is very simple. Centrifugo also supports a GRPC API. As soon as you have published a message to the channel, it must be delivered to your online clients subscribed to that channel.","s":"5. Publish to a channel","u":"/docs/getting-started/integration","h":"#5-publish-to-a-channel","p":2549},{"i":2564,"t":"To put all this into production, you need to deploy Centrifugo on your production server. To help you with this, we have many things like a Docker image, rpm and deb packages, Nginx configuration. See the Infrastructure tuning chapter for some actions you have to do to prepare your server infrastructure for handling many persistent connections.","s":"6. Deploy to production","u":"/docs/getting-started/integration","h":"#6-deploy-to-production","p":2549},{"i":2566,"t":"Don't forget to configure metrics monitoring for your production Centrifugo setup. This may help you to understand what's going on with your Centrifugo setup, understand the number of connections, operation count, and latency distributions, etc.","s":"7. Monitor Centrifugo","u":"/docs/getting-started/integration","h":"#7-monitor-centrifugo","p":2549},{"i":2568,"t":"As soon as you are close to machine resource limits, you may want to scale Centrifugo – you can run many Centrifugo instances and load-balance clients between them using the Redis engine, or with KeyDB, or with Tarantool, or with a Nats broker. The Engines and scalability chapter describes available options in detail.","s":"8. Scale Centrifugo","u":"/docs/getting-started/integration","h":"#8-scale-centrifugo","p":2549},{"i":2570,"t":"That's all for the basics. The documentation actually covers a lot of other concepts Centrifugo server has: scalability, private channels, the admin web interface, SockJS fallback, Protobuf support, and more. And don't forget to read our FAQ – it contains a lot of useful information.","s":"9. Read FAQ","u":"/docs/getting-started/integration","h":"#9-read-faq","p":2549},{"i":2572,"t":"On this page","s":"Migrating to v5","u":"/docs/getting-started/migration_v5","h":"","p":2571},{"i":2574,"t":"In Centrifugo v5 client SDK spec was adjusted in regards how SDKs work with tokens. Returning an empty string from getToken function (for Javascript, and the same for analogous functions in other SDKs) is a valid scenario which won't result into disconnect on the client side. It's still possible to disconnect client by returning a special error from getToken function. We updated all our SDKs to inherit this behaviour. Specifically, here is a list of SDK versions which work according to adjusted spec: centrifuge-js >= v4.0.0 centrifuge-go >= v0.10.0 centrifuge-dart >= v0.10.0 centrifuge-swift >= v0.6.0 centrifuge-java >= v0.3.0 Nothing prevents you from updating Centrifugo v4 to v5 first and then migrate to new client versions or doing vice versa. This change is client-side only. But we bind it to major server release to make it more notable – as it changes the core SDK behavior.","s":"Client SDK token behaviour adjustments","u":"/docs/getting-started/migration_v5","h":"#client-sdk-token-behaviour-adjustments","p":2571},{"i":2576,"t":"Avoid running Centrifugo v5 in the same cluster with Centrifugo v4 nodes – v4 and v5 have backwards incompatible node communication protocols.","s":"Node communication format changed","u":"/docs/getting-started/migration_v5","h":"#node-communication-format-changed","p":2571},{"i":2578,"t":"Prefer using new HTTP API format instead of old one where possible. The old format still works and enabled by default. But we are planning to migrate our API libraries to the new format eventually – and then remove the old format. If you are using one of our HTTP API libs - at some point a new version will be released which will seamlessly migrate you to the modern HTTP API format. If you are using hand-written requests – then some refactoring is required. It should be rather straighforward: replace request path from /api to /api/ replace payload from having method and params on top level. Payload does not include method and params keys anymore. Please refer to: https://centrifugal.dev/blog/2023/06/29/centrifugo-v5-released#new-http-api-format prefer using X-API-Key: ')centrifuge.connect(); See authentication chapter for more information on how to generate connection JWT. If you are using connect proxy then you may go without setting JWT.","s":"Connecting to a server","u":"/docs/getting-started/client_api","h":"#connecting-to-a-server","p":2583},{"i":2588,"t":"After connecting you can disconnect from a server at any moment. centrifuge.disconnect();","s":"Disconnecting from a server","u":"/docs/getting-started/client_api","h":"#disconnecting-from-a-server","p":2583},{"i":2590,"t":"Centrifugo clients automatically reconnect to a server in case of temporary connection loss, also clients periodically ping the server to detect broken connections.","s":"Reconnecting to a server","u":"/docs/getting-started/client_api","h":"#reconnecting-to-a-server","p":2583},{"i":2592,"t":"All client implementations allow setting handlers on connect and disconnect events. For example: centrifuge.on('connect', function(connectCtx){ console.log('connected', connectCtx)});centrifuge.on('disconnect', function(disconnectCtx){ console.log('disconnected', disconnectCtx)});","s":"Connection lifecycle events","u":"/docs/getting-started/client_api","h":"#connection-lifecycle-events","p":2583},{"i":2594,"t":"Another core functionality of client API is the possibility to subscribe to a channel to receive all messages published to that channel. centrifuge.subscribe('channel', function(messageCtx) { console.log(messageCtx);}) Clients can subscribe to different channels. The subscribe method returns the Subscription object. It's also possible to react to different Subscription events: join and leave events, subscribe success and subscribe error events, unsubscribe events. In the idiomatic case, messages are published to channels from the application backend over the Centrifugo server API. However, this is not always the case. Centrifugo also provides a message recovery feature to restore missed publications in channels. Publications can be missed due to temporary disconnects (bad network) or server reloads. Recovery happens automatically on reconnect (due to bad network or server reloads) as soon as recovery in the channel is properly configured. The client keeps the last seen Publication offset and restores missed publications since the known offset upon reconnecting. If recovery fails, then the client implementation provides a flag inside the subscribe event to let the application know that some publications were missed – so you may need to load the state from scratch from the application backend. Not all Centrifugo clients implement a recovery feature – refer to specific client implementation docs. More details about recovery can be found in a dedicated chapter.","s":"Subscribe to a channel","u":"/docs/getting-started/client_api","h":"#subscribe-to-a-channel","p":2583},{"i":2596,"t":"To handle publications coming from server-side subscriptions client API allows listening publications simply on Centrifuge client instance: centrifuge.on('publish', function(messageCtx) { console.log(messageCtx);}); It's also possible to react on different server-side Subscription events: join and leave events, subscribe success, unsubscribe event. There is no subscribe error event here since the subscription was initiated on the server-side.","s":"Server-side subscriptions","u":"/docs/getting-started/client_api","h":"#server-side-subscriptions","p":2583},{"i":2598,"t":"A client can send an RPC to the server. RPC is a call that is not related to channels at all. It's just a way to call a server method from the client side over the WebSocket or SockJS connection. RPC is only available when an RPC proxy is configured. const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"Send RPC","u":"/docs/getting-started/client_api","h":"#send-rpc","p":2583},{"i":2600,"t":"Once subscribed client can call publication history inside a channel (only for channels where history configured) to get last publications in channel: Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Call channel history","u":"/docs/getting-started/client_api","h":"#call-channel-history","p":2583},{"i":2602,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats","u":"/docs/getting-started/client_api","h":"#presence-and-presence-stats","p":2583},{"i":2604,"t":"On this page","s":"Install Centrifugo","u":"/docs/getting-started/installation","h":"","p":2603},{"i":2606,"t":"For local development, you can download the prebuilt Centrifugo binary release (i.e., a single all-contained executable file) for your system. Binary releases are available on Github. Download the latest release for your operating system, unpack it, and you are done. Centrifugo is pre-built for: Linux 64-bit (linux_amd64) Linux 32-bit (linux_386) Linux ARM 64-bit (linux_arm64) MacOS (darwin_amd64) MacOS on Apple Silicon (darwin_arm64) Windows (windows_amd64) FreeBSD (freebsd_amd64) ARM v6 (linux_armv6) Archives contain a single statically compiled binary centrifugo file that is ready to run. ./centrifugo If you are unsure which distribution you need, then on Linux or MacOS you can use the following command to download and unpack the centrifugo binary to your current working directory: curl -sSLf https://centrifugal.dev/install.sh | sh See the version of Centrifugo: ./centrifugo version Centrifugo requires a configuration file with several secret keys. If you are new to Centrifugo, then there is a genconfig command which generates a minimal configuration file to get started: ./centrifugo genconfig This creates a configuration file config.json with some auto-generated option values in the current directory (by default). tip It's possible to generate a file in YAML or TOML format, for example: ./centrifugo genconfig -c config.toml With a configuration file, you can finally run a Centrifugo instance: ./centrifugo --config=config.json We will talk about configuration in detail in the next sections. You can also put or symlink centrifugo into your bin OS directory and run it from anywhere: centrifugo --config=config.json","s":"Install from the binary release","u":"/docs/getting-started/installation","h":"#install-from-the-binary-release","p":2603},{"i":2608,"t":"Centrifugo server has a docker image available on Docker Hub. docker pull centrifugo/centrifugo Run: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo:v5 centrifugo -c config.json Note that docker allows setting nofile limits in command-line arguments, which is quite important to handle many simultaneous persistent connections and not run out of the open file limit (each connection requires one file descriptor). See also infrastructure tuning chapter. caution Pin to the exact Docker Image tag in production, for instance: centrifugo/centrifugo:v5.0.0. This will help to avoid unexpected problems during redeployment process.","s":"Docker image","u":"/docs/getting-started/installation","h":"#docker-image","p":2603},{"i":2610,"t":"Create configuration file config.json: { \"token_hmac_secret_key\": \"my_secret\", \"api_key\": \"my_api_key\", \"admin_password\": \"password\", \"admin_secret\": \"secret\", \"admin\": true} Create docker-compose.yml: version: \"3.9\"services: centrifugo: container_name: centrifugo image: centrifugo/centrifugo:v5 volumes: - ./config.json:/centrifugo/config.json command: centrifugo -c config.json ports: - 8000:8000 ulimits: nofile: soft: 65535 hard: 65535 Run with: docker-compose up","s":"Docker-compose example","u":"/docs/getting-started/installation","h":"#docker-compose-example","p":2603},{"i":2612,"t":"See our official Kubernetes Helm chart. Follow instructions in a Centrifugo chart README to bootstrap Centrifugo inside your Kubernetes cluster.","s":"Kubernetes Helm chart","u":"/docs/getting-started/installation","h":"#kubernetes-helm-chart","p":2603},{"i":2614,"t":"Every time we make a new Centrifugo release we upload rpm and deb packages for popular Linux distributions on packagecloud.io. At moment, we support versions of the following distributions: 64-bit Debian 10 Buster 64-bit Debian 11 Bullseye 64-bit Ubuntu 18.04 Bionic 64-bit Ubuntu 20.04 Focal Fossa 64-bit Ubuntu 20.04 Jammy 64-bit Centos 7 See full list of available packages and installation instructions. Centrifugo also works on 32-bit architecture, but we don't support packaging for it since 64-bit is more convenient for servers today.","s":"RPM and DEB packages for Linux","u":"/docs/getting-started/installation","h":"#rpm-and-deb-packages-for-linux","p":2603},{"i":2616,"t":"If you are developing on macOS then you can install Centrifugo over brew: brew tap centrifugal/centrifugobrew install centrifugo","s":"With brew on macOS","u":"/docs/getting-started/installation","h":"#with-brew-on-macos","p":2603},{"i":2618,"t":"You need Go language installed. Then: git clone https://github.com/centrifugal/centrifugo.gitcd centrifugogo build./centrifugo","s":"Build from source","u":"/docs/getting-started/installation","h":"#build-from-source","p":2603},{"i":2620,"t":"On this page","s":"Client SDK API","u":"/docs/4/transports/client_api","h":"","p":2619},{"i":2622,"t":"Client connection has 4 states: disconnected connecting connected closed note closed state is only implemented by SDKs where it makes sense (need to clean up allocated resources when app gracefully shuts down – for example in Java SDK we close thread executors used internally). When a new Client is created it has a disconnected state. To connect to a server connect() method must be called. After calling connect Client moves to the connecting state. If a Client can't connect to a server it attempts to create a connection with an exponential backoff algorithm (with full jitter). If a connection to a server is successful then the state becomes connected. If a connection is lost (due to a missing network for example, or due to reconnect advice received from a server, or due to some client-side error that can't be recovered without reconnecting) Client goes to the connecting state again. In this state Client tries to reconnect (again, with an exponential backoff algorithm). The Client's state can become disconnected. This happens when Client's disconnect() method was called by a developer. Also, this can happen due to server advice from a server, or due to a terminal problem that happened on the client-side. Here is a program where we create a Client instance, set callbacks to listen to state updates and establish a connection with a server: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('connecting', function(ctx) { console.log('connecting', ctx);});client.on('connected', function(ctx) { console.log('connected', ctx);});client.on('disconnected', function(ctx) { console.log('disconnected', ctx);});client.connect(); final onEvent = (dynamic event) { print('client event> $event');};final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);client.connecting.listen(onEvent);client.connected.listen(onEvent);client.disconnected.listen(onEvent);await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate { func onConnecting(_ c: CentrifugeClient, _ e: CentrifugeConnectingEvent) { print(\"connecting\", e.code, e.reason) } func onConnected(_ client: CentrifugeClient, _ e: CentrifugeConnectedEvent) { print(\"connected with id\", e.client) } func onDisconnected(_ client: CentrifugeClient, _ e: CentrifugeDisconnectedEvent) { print(\"disconnected\", e.code, e.reason) }}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())client.connect() EventListener listener = new EventListener() { @Override public void onConnected(Client client, ConnectedEvent event) { System.out.println(\"connected\"); } @Override public void onConnecting(Client client, ConnectingEvent event) { System.out.printf(\"connecting: %s%n\", event.getReason()); } @Override public void onDisconnected(Client client, DisconnectedEvent event) { System.out.printf(\"disconnected %d %s\", event.getCode(), event.getReason()); }};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)defer client.Close()client.OnConnecting(func(e centrifuge.ConnectingEvent) { log.Printf(\"Connecting - %d (%s)\", e.Code, e.Reason)})client.OnConnected(func(e centrifuge.ConnectedEvent) { log.Printf(\"Connected with ID %s\", e.ClientID)})client.OnDisconnected(func(e centrifuge.DisconnectedEvent) { log.Printf(\"Disconnected: %d (%s)\", e.Code, e.Reason)})_ = client.connect() In case of successful connection Client states will transition like this: disconnected (initial) -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server and then successfully reconnected: connected -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server, but got a terminal error upon reconnection: connected -> connecting (on('connecting') called) -> disconnected (on('disconnected') called). In case of already connected Client came across terminal condition (for example, if during a connection token refresh application found that user has no permission to connect anymore): connected -> disconnected (on('disconnected') called). Both connecting and disconnected events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why the Client went to the connecting state or to the disconnected state. This diagram demonstrates possible Client state transitions: You can also listen for all errors happening internally (which are not reflected by state changes, for example, transport errors happening on initial connect, transport during reconnect, connection token refresh related errors, etc) while the client works by using error event: client.on('error', function(ctx) { console.log('client error', ctx);}); If you want to disconnect from a server call .disconnect() method: client.disconnect(); In this case on('disconnected') will be called. You can call connect() again when you need to establish a connection. closed state implemented in SDKs where resources like internal queues, thread executors, etc must be cleaned up when the Client is not needed anymore. All subscriptions should automatically go to the unsubscribed state upon closing. The client is not usable after going to a closed state.","s":"Client connection states","u":"/docs/4/transports/client_api","h":"#client-connection-states","p":2619},{"i":2624,"t":"There are several common options available when creating Client instance. option to set connection token and callback to get connection token upon expiration (see below mode details) option to set connect data option to configure operation timeout tweaks for reconnect backoff algorithm (min delay, max delay) configure max delay of server pings (to detect broken connection) configure headers to send in WebSocket upgrade request (except centrifuge-js) configure client name and version for analytics purpose","s":"Client common options","u":"/docs/4/transports/client_api","h":"#client-common-options","p":2619},{"i":2626,"t":"connect() – connect to a server disconnect() - disconnect from a server close() - close Client if not needed anymore send(data) - send asynchronous message to a server rpc(method, data) - send arbitrary RPC and wait for response","s":"Client methods","u":"/docs/4/transports/client_api","h":"#client-methods","p":2619},{"i":2628,"t":"All SDKs support connecting to Centrifugo with JWT. Initial connection token can be set in Client option upon initialization. Example: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE'}); If the token sets connection expiration then the client SDK will keep the token refreshed. It does this by calling a special callback function. This callback must return a new token. If a new token with updated connection expiration is returned from callback then it's sent to Centrifugo. If your callback returns an empty string – this means the user has no permission to connect to Centrifugo and the Client will move to a disconnected state. In case of error returned by your callback SDK will retry the operation after some jittered time. An example: function getToken(url, ctx) { return new Promise((resolve, reject) => { fetch(url, { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify(ctx) }) .then(res => { if (!res.ok) { throw new Error(`Unexpected status code ${res.status}`); } return res.json(); }) .then(data => { resolve(data.token); }) .catch(err => { reject(err); }); });}const client = new Centrifuge( 'ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE', getToken: function (ctx) { return getToken('/centrifuge/connection_token', ctx); } }); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authentication. In this case SDK should attempt to get a connection token before establishing an initial connection.","s":"Client connection token","u":"/docs/4/transports/client_api","h":"#client-connection-token","p":2619},{"i":2630,"t":"PINGs sent by a server, a client should answer with PONGs upon receiving PING. If a client does not receive PING from a server for a long time (ping interval + configured delay) – the connection is considered broken and will be re-established.","s":"Connection PING/PONG","u":"/docs/4/transports/client_api","h":"#connection-pingpong","p":2619},{"i":2632,"t":"Client allows subscribing on channels. This can be done by creating Subscription object. const sub = centrifuge.newSubscription(channel);sub.subscribe(); When anewSubscription method is called Client allocates a new Subscription instance and saves it in the internal subscription registry. Having a registry of allocated subscriptions allows SDK to manage resubscribes upon reconnecting to a server. Centrifugo connectors do not allow creating two subscriptions to the same channel – in this case, newSubscription can throw an exception. Subscription has 3 states: unsubscribed subscribing subscribed When a new Subscription is created it has an unsubscribed state. To initiate the actual process of subscribing to a channel subscribe() method of Subscription instance should be called. After calling subscribe() Subscription moves to subscribing state. If subscription to a channel is not successful then depending on error type subscription can automatically resubscribe (with exponential backoff) or go to an unsubscribed state (upon non-temporary error). If subscription to a channel is successful then the state becomes subscribed. Javascript Dart Swift Java Go const sub = client.newSubscription(channel);sub.on('subscribing', function(ctx) { console.log('subscribing');});sub.on('subscribed', function(ctx) { console.log('subscribed');});sub.on('unsubscribed', function(ctx) { console.log('unsubscribed');});sub.subscribe(); final onSubscriptionEvent = (dynamic event) async { print('subscription $channel> $event');};final subscription = client.newSubscription(channel);subscription.subscribing.listen(onSubscriptionEvent);subscription.subscribed.listen(onSubscriptionEvent);subscription.unsubscribed.listen(onSubscriptionEvent);await subscription.subscribe(); class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onSubscribing(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribingEvent) { print(\"subscribing\", e.code, e.reason) } func onSubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribedEvent) { print(\"subscribed\") } func onUnsubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeUnsubscribedEvent) { print(\"unsubscribed\", e.code, e.reason) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")} SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onSubscribed(Subscription sub, SubscribedEvent event) { System.out.println(\"subscribed to \" + sub.getChannel()); } @Override public void onSubscribing(Subscription sub, SubscribingEvent event) { System.out.printf(\"subscribing \" + sub.getChannel()); } @Override public void onUnsubscribed(Subscription sub, UnsubscribedEvent event) { System.out.println(\"unsubscribed \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();} sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnSubscribing(func(e centrifuge.SubscribingEvent) { log.Printf(\"Subscribing on channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})sub.OnSubscribed(func(e centrifuge.SubscribedEvent) { log.Printf(\"Subscribed on channel %s\", sub.Channel)})sub.OnUnsubscribed(func(e centrifuge.UnsubscribedEvent) { log.Printf(\"Unsubscribed from channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})err = sub.Subscribe()if err != nil { log.Fatalln(err)} Subscriptions also go to subscribing state when Client connection (i.e. transport) becomes unavailable. Upon connection re-establishement all subscriptions which are not in unsubscribed state will resubscribe automatically. In case of successful subscription states will transition like this: unsubscribed (initial) -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). In case of connected and subscribed Client temporary lost a connection with a server and then succesfully reconnected and resubscribed: subscribed -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). Both subscribing and unsubscribed events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why Subscription went to subscribing state or to unsubscribed state. This diagram demonstrates possible Subscription state transitions: You can listen for all errors happening internally in Subscription (which are not reflected by state changes, for example, temporary subscribe errors, subscription token related errors, etc) by using error event: sub.on('error', function(ctx) { console.log(\"subscription error\", ctx);}); If you want to unsubscribe from a channel call .unsubscribe() method: sub.unsubscribe(); In this case on('unsubscribed') will be called. Subscription still kept in Client's registry, but no resubscription attempts will be made. You can call subscribe() again when you need Subscription again. Or you can remove Subscription from Client's registry (see below).","s":"Subscription states","u":"/docs/4/transports/client_api","h":"#subscription-states","p":2619},{"i":2634,"t":"The client SDK provides several methods to manage the internal registry of client-side subscriptions. newSubscription(channel, options) allocates a new Subscription in the registry or throws an exception if the Subscription is already there. We will discuss common Subscription options below. getSubscription(channel) returns the existing Subscription by a channel from the registry (or null if it does not exist). removeSubscription(sub) removes Subscription from Client's registry. Subscription is automatically unsubscribed before being removed. Use this to free resources if you don't need a Subscription to a channel anymore. subscriptions() returns all registered subscriptions, so you can iterate over all and do some action if required (for example, you want to unsubscribe/remove all subscriptions).","s":"Subscription management","u":"/docs/4/transports/client_api","h":"#subscription-management","p":2619},{"i":2636,"t":"Of course the main point of having Subscriptions is the ability to listen for publications (i.e. messages published to a channel). sub.on('publication', function(ctx) { console.log(\"received publication\", ctx);}); Publication context has several fields: data - publication payload, this can be JSON or binary data offset - optional offset inside history stream, this is an incremental number tags - optional tags, this is a map with string keys and string values info - optional information about client connection who published this (only exists if publication comes from client-side publish() API). So minimal code where we connect to a server and listen for messages published into example channel may look like: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = client.newSubscription('example').on('publication', function(ctx) { console.log(\"received publication from a channel\", ctx.data);});sub.subscribe();client.connect(); final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);final subscription = client.newSubscription(channel);subscription.publication.listen((event) { print(event);});await subscription.subscribe();await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate {}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onPublication(_ s: CentrifugeSubscription, _ e: CentrifugePublicationEvent) { print(\"publication\", e.data) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")}client.connect() EventListener listener = new EventListener() {};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onPublication(Subscription sub, PublicationEvent event) { System.out.println(\"publication from \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();}client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)// defer client.Close()sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnPublication(func(e centrifuge.PublicationEvent) { log.Printf(\"Publication from channel\")})err = sub.Subscribe()if err != nil { log.Fatalln(err)}if err = client.connect(); err != nil { log.Fatalln(err)} Note, that we can call subscribe() before making a connection to a server – and this will work just fine, subscription goes to subscribing state and will be subscribed upon succesfull connection. And of course, it's possible to call .subscribe() after .connect().","s":"Listen to channel publications","u":"/docs/4/transports/client_api","h":"#listen-to-channel-publications","p":2619},{"i":2638,"t":"Subscriptions to channels with recovery option enabled maintain stream position information internally. On every publication received this information updated and used to recover missed publications upon resubscribe (caused by reconnect for example). When you call unsubscribe() Subscription position state is not cleared. So it's possible to call subscribe() later and catch up a state. The recovery process result – i.e. whether all missed publications recovered or not – can be found in on('subscribed') event context. Centrifuge protocol provides two fields: wasRecovering - boolean flag that tells whether recovery was used during subscription process resulted into subscribed state. Can be useful if you want to distinguish first subscribe attempt (when subscription does not have any position information yet) recovered - boolean flag that tells whether Centrifugo thinks that all missed publications can be successfully recovered and there is no need to load state from the main application database. It's always false when wasRecovering is false.","s":"Subscription recovery state","u":"/docs/4/transports/client_api","h":"#subscription-recovery-state","p":2619},{"i":2640,"t":"There are several common options available when creating Subscription instance. option to set subscription token and callback to get subscription token upon expiration (see below more details) option to set subscription data (attached to every subscribe/resubscribe request) options to tweak resubscribe backoff algorithm option to start Subscription since known Stream Position (i.e. attempt recovery on first subscribe) option to ask server to make subscription positioned (if not forced by a server) option to ask server to make subscription recoverable (if not forced by a server) option to ask server to push Join/Leave messages (if not forced by a server)","s":"Subscription common options","u":"/docs/4/transports/client_api","h":"#subscription-common-options","p":2619},{"i":2642,"t":"subscribe() – start subscribing to a channel unsubscribe() - unsubscribe from a channel publish(data) - publish data to Subscription channel history(options) - request Subscription channel history presence() - request Subscription channel online presence information presenceStats() - request Subscription channel online presence stats information (number of client connections and unique users in a channel).","s":"Subscription methods","u":"/docs/4/transports/client_api","h":"#subscription-methods","p":2619},{"i":2644,"t":"All SDKs support subscribing to Centrifugo channels with JWT. Channel subscription token can be set as a Subscription option upon initialization. Example: const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE'});sub.subscribe(); If token sets subscription expiration client SDK will keep token refreshed. It does this by calling special callback function. This callback must return a new token. If new token with updated subscription expiration returned from a calbback then it's sent to Centrifugo. If your callback returns an empty string – this means user has no permission to subscribe to a channel anymore and subscription will be unsubscribed. In case of error returned by your callback SDK will retry operation after some jittered time. An example: function getToken(url, ctx) { return new Promise((resolve, reject) => { fetch(url, { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify(ctx) }) .then(res => { if (!res.ok) { throw new Error(`Unexpected status code ${res.status}`); } return res.json(); }) .then(data => { resolve(data.token); }) .catch(err => { reject(err); }); });}const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE', getToken: function (ctx) { // ctx has channel in the Subscription token case. return getToken('/centrifuge/subscription_token', ctx); },});sub.subscribe(); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authorization for a channel subscription. In this case SDK should attempt to get a subscription token before initial subscribe.","s":"Subscription token","u":"/docs/4/transports/client_api","h":"#subscription-token","p":2619},{"i":2646,"t":"We encourage using client-side subscriptions where possible as they provide a better control and isolation from connection. But in some cases you may want to use server-side subscriptions (i.e. subscriptions created by server upon connection establishment). Technically, client SDK keeps server-side subscriptions in internal registry (similar to client-side subscriptions but without possibility to control them). To listen for server-side subscription events use callbacks as shown in example below: const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('subscribed', function(ctx) { // Called when subscribed to a server-side channel upon Client moving to // connected state or during connection lifetime if server sends Subscribe // push message. console.log('subscribed to server-side channel', ctx.channel);});client.on('subscribing', function(ctx) { // Called when existing connection lost (Client reconnects) or Client // explicitly disconnected. Client continue keeping server-side subscription // registry with stream position information where applicable. console.log('subscribing to server-side channel', ctx.channel);});client.on('unsubscribed', function(ctx) { // Called when server sent unsubscribe push or server-side subscription // previously existed in SDK registry disappeared upon Client reconnect. console.log('unsubscribed from server-side channel', ctx.channel);});client.on('publication', function(ctx) { // Called when server sends Publication over server-side subscription. console.log('publication receive from server-side channel', ctx.channel, ctx.data);});client.connect(); Server-side subscription events mostly mimic events of client-side subscriptions. But again – they do not provide control to the client and managed entirely by a server side. Additionally, Client has several top-level methods to call with server-side subscription related operations: publish(channel, data) history(channel, options) presence(channel) presenceStats(channel)","s":"Server-side subscriptions","u":"/docs/4/transports/client_api","h":"#server-side-subscriptions","p":2619},{"i":2648,"t":"Server can return error codes in range 100-1999. Error codes in interval 0-399 reserved by Centrifuge/Centrifugo server. Codes in range [400, 1999] may be returned by application code built on top of Centrifuge/Centrifugo. Server errors contain a temporary boolean flag which works as a signal that error may be fixed by a later retry. Errors with codes 0-100 can be used by client-side implementation. Client-side errors may not have code attached at all since in many languages error can be distinguished by its type.","s":"Error codes","u":"/docs/4/transports/client_api","h":"#error-codes","p":2619},{"i":2650,"t":"Server may return unsubscribe codes. Server unsubscribe codes must be in range [2000, 2999]. Unsubscribe codes >= 2500 coming from server to client result into automatic resubscribe attempt (i.e. client goes to subscribing state). Codes < 2500 result into going to unsubscribed state. Client implementation can use codes <2000 for client-side specific unsubscribe reasons.","s":"Unsubscribe codes","u":"/docs/4/transports/client_api","h":"#unsubscribe-codes","p":2619},{"i":2652,"t":"Server may send custom disconnect codes to a client. Custom disconnect codes must be in range [3000, 4999]. Client automatically reconnects upon receiving code in range 3000-3499, 4000-4499 (i.e. Client goes to connecting state). Other codes result into going to disconnected state. Client implementation can use codes <3000 for client-side specific disconnect reasons.","s":"Disconnect codes","u":"/docs/4/transports/client_api","h":"#disconnect-codes","p":2619},{"i":2654,"t":"An SDK provides a way to send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the real-time connection. RPC is only available when RPC proxy configured (so Centrifugo proxies the RPC to your application backend). const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"RPC","u":"/docs/4/transports/client_api","h":"#rpc","p":2619},{"i":2656,"t":"SDK provides a method to call publication history inside a channel (only for channels where history is enabled) to get last publications in a channel. Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Channel history API","u":"/docs/4/transports/client_api","h":"#channel-history-api","p":2619},{"i":2658,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats API","u":"/docs/4/transports/client_api","h":"#presence-and-presence-stats-api","p":2619},{"i":2660,"t":"Callbacks must be fast. Avoid blocking operations inside event handlers. Callbacks caused by protocol messages received from a server are called synchronously and connection read loop is blocked while such callbacks are being executed. Consider doing heavy work asynchronously. Do not blindly rely on the current Client or Subscription state when making client API calls – state can change at any moment, so don't forget to handle errors. Disconnect from a server when a mobile application goes to the background since a mobile OS can kill the connection at some point without any callbacks called.","s":"SDK common best practices","u":"/docs/4/transports/client_api","h":"#sdk-common-best-practices","p":2619},{"i":2662,"t":"On this page","s":"Channel capabilities","u":"/docs/pro/capabilities","h":"","p":2661},{"i":2664,"t":"Connection capabilities can be set: in connection JWT (in caps claim) in connect proxy result (caps field) For example, here we are issuing permissions to subscribe on channel news and channel user_42 to a client: { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] } ]} Known capabilities: sub - subscribe to a channel to receive publications from it pub - publish into a channel (your backend won't be able to process the publication in this case) prs - call presence and presence stats API, also consume join/leave events upon subscribing hst - call history API, also make Subscription positioned or recoverable upon subscribing","s":"Connection capabilities","u":"/docs/pro/capabilities","h":"#connection-capabilities","p":2661},{"i":2666,"t":"Centrifugo processes caps objects till it finds a match to a channel. At this point it applies permissions in the matched object and stops processing remaining caps. If no match found – then 103 permission denied returned to a client (of course if namespace does not have other permission-related options enabled). Let's consider example like this: WRONG! { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"pub\"] }, { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, ]} Here we have two entries for channel news, but when client subscribes on news only the first entry will be taken into considiration by Centrifugo – so Subscription attempt will be rejected (since first cap object does not have sub capability). In real life you don't really want to have cap objects with identical channels – but below we will introduce wildcard matching where understanding how caps processed becomes important. Another example: WRONG! { \"caps\": [ { \"channels\": [\"news\", \"user_42\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"pub\", \"hst\", \"prs\"] }, ]} One could expect that client will have [\"sub\", \"pub\", \"hst\", \"prs\"] capabilities for a channel user_42. But it's not true since Centrifugo processes caps objects and channels inside caps object in order – it finds a match to user_42 in first caps object, it contains only \"sub\" capability, processing stops. So user can subscribe to a channel, but can not publish, can not call history and presence APIs even though those capabilities are mentioned in caps object. The correct way to give all caps to the channel user_42 would be to split channels into different caps objects: CORRECT { \"caps\": [ { \"channels\": [\"news\"], \"allow\": [\"sub\"] }, { \"channels\": [\"user_42\"], \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] }, ]} The processing behaves like this to avoid potential problems with possibly conflicting matches (mostly when using wildcard and regex matching – see below) and still allow overriding capabilities for specific channels.","s":"Caps processing behavior","u":"/docs/pro/capabilities","h":"#caps-processing-behavior","p":2661},{"i":2668,"t":"In JWT auth case – capabilities in JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In connect proxy case – capabilities will work until client connection close (disconnect) or connection refresh triggered (with refresh proxy you can provide an updated set of capabilities).","s":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations","p":2661},{"i":2670,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a connection expiration, then upon refresh: if using proxy – provide new caps in refresh proxy result, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). if JWT auth - provide new caps in connection token, Centrifugo will update caps and unsubscribe a client from channels it does not have permissions anymore (only those obtained due to previous connection-wide capabilities). In case of using connect proxy – you can disconnect a user (or client) with a reconnect code. New capabilities will be asked upon reconnection. In case of using token auth – revoke token (Centrifugo PRO feature) and disconnect user (or client) with reconnect code. Upon reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking connection caps","u":"/docs/pro/capabilities","h":"#revoking-connection-caps","p":2661},{"i":2672,"t":"It's possible to use wildcards in channel resource names. For example, let's give a permission to subscribe on all channels in news namespace. { \"caps\": [ { \"channels\": [\"news:*\"], \"match\": \"wildcard\", \"allow\": [\"sub\"] } ]} note Match type is used for all channels in caps object. If you need different matching behavior for different channels then split them on different caps objects.","s":"Example: wildcard match","u":"/docs/pro/capabilities","h":"#example-wildcard-match","p":2661},{"i":2674,"t":"Or regex: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } ]}","s":"Example: regex match","u":"/docs/pro/capabilities","h":"#example-regex-match","p":2661},{"i":2676,"t":"Of course it's possible to combine different types of match inside one caps array: { \"caps\": [ { \"channels\": [\"^posts_[\\d]+$\"], \"match\": \"regex\", \"allow\": [\"sub\"] } { \"channels\": [\"user_42\"], \"allow\": [\"sub\"] } ]}","s":"Example: different types of match","u":"/docs/pro/capabilities","h":"#example-different-types-of-match","p":2661},{"i":2678,"t":"Let's look how to allow all permissions to a client: { \"caps\": [ { \"channels\": [\"*\"], \"match\": \"wildcard\", \"allow\": [\"sub\", \"pub\", \"hst\", \"prs\"] } ]} Full access warn Should we mention that giving full access to a client is something to wisely consider? 🤔","s":"Example: full access to all channels","u":"/docs/pro/capabilities","h":"#example-full-access-to-all-channels","p":2661},{"i":2680,"t":"Subscription capabilities can be set: in subscription JWT (in allow claim) in subscribe proxy result (allow field) Subscription token already belongs to a channel (it has a channel claim). So users with a valid subscription token can subscribe to a channel. But it's possible to additionally grant channel permissions to a user for publishing and calling presence and history using allow claim: { \"allow\": [\"pub\", \"hst\", \"prs\"]} Putting sub permission to the Subscription token does not make much sense – Centrifugo only expects valid token for a subscription permission check.","s":"Subscription capabilities","u":"/docs/pro/capabilities","h":"#subscription-capabilities","p":2661},{"i":2682,"t":"In JWT auth case – capabilities in subscription JWT will work till token expiration, that's why it's important to keep reasonably small token expiration times. We can recommend using sth like 5-10 mins as a good expiration value, but of course this is application specific. In subscribe proxy case – capabilities will work until client unsubscribe (or connection close).","s":"Expiration considirations","u":"/docs/pro/capabilities","h":"#expiration-considirations-1","p":2661},{"i":2684,"t":"If at some point you need to revoke some capability from a client: Simplest way is to wait for a subscription expiration, then upon refresh: provide new caps in subscription token, Centrifugo will update channel caps. In case of using subscribe proxy – you can unsubscribe a user (or client) with a resubscribe code. Or disconnect with reconnect code. New capabilities will be set up upon resubscription/reconnection. In case of using JWT auth – revoke token (Centrifugo PRO feature) and unsubscribe/disconnect user (or client) with resubscribe/reconnect code. Upon resubscription/reconnection user will receive an error that token revoked and will try to load a new one.","s":"Revoking subscription permissions","u":"/docs/pro/capabilities","h":"#revoking-subscription-permissions","p":2661},{"i":2686,"t":"Quickstart tutorial ⏱️ In this tutorial, we will build a very simple browser application with Centrifugo. Users will connect to Centrifugo over WebSocket, subscribe to a channel, and start receiving all channel publications (messages published to that channel). In our case, we will send a counter value to all channel subscribers to update the counter widget in all open browser tabs in real-time. First, you need to install Centrifugo. In this example, we are using a binary file release which is fine for development. Once you have the Centrifugo binary available on your machine, you can generate the minimal required configuration file with the following command: ./centrifugo genconfig This helper command will generate config.json file in the working directory with a content like this: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": []} Now we can start a server. Let's start Centrifugo with a built-in admin web interface: ./centrifugo --config=config.json --admin We could also enable the admin web interface by not using --admin flag but by adding \"admin\": true option to the JSON configuration file: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin\": true, \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": []} And then running Centrifugo only with a path to a configuration file: ./centrifugo --config=config.json Now open http://localhost:8000. You should see Centrifugo admin web panel. Enter admin_password value from the configuration file to log in (in our case it's d0683813-0916-4c49-979f-0e08a686b727, but you will have a different value). Inside the admin panel, you should see that one Centrifugo node is running, and it does not have connected clients: Now let's create index.html file with our simple app: index.html Centrifugo quick start
      -
      Note that we are using centrifuge-js 5.0.1 in this example, getting it from a CDN. You should use its latest version at the moment of reading this tutorial. In a real Javascript app, you would most likely load centrifuge from NPM. In the index.html above, we created an instance of a Centrifuge client by passing the Centrifugo server's default WebSocket endpoint address to it. Then we subscribed to a channel called channel and provided a callback function to process incoming real-time messages (publications). Upon receiving a new publication, we update the page's HTML by setting the counter value to the page title. We call .subscribe() to initiate the subscription and the .connect() method of the Client to start a WebSocket connection. We also handle Client state transitions (disconnected, connecting, connected) and Subscription state transitions (unsubscribed, subscribing, subscribed) – see a detailed description in client SDK spec. Now you need to serve this file with an HTTP server. In a real-world Javascript application, you would serve your HTML files with a web server of your choice – but for this simple example, we can use a simple built-in Centrifugo static file server: ./centrifugo serve --port 3000 Alternatively, if you have Python 3 installed: python3 -m http.server 3000 These commands start a simple static file web server that serves the current directory on port 3000. Make sure you still have Centrifugo server running. Open http://localhost:3000/. Now if you look at browser developer tools or in Centrifugo logs you will notice that a connection can not be successfully established: 2021-09-01 10:17:33 [INF] request Origin is not authorized due to empty allowed_origins origin=http://localhost:3000 That's because we have not set allowed_origins in the configuration. Modify allowed_origins like this: config.json { ... \"allowed_origins\": [\"http://localhost:3000\"]} Allowed origins is a security option for request originating from web browsers – see more details in server configuration docs. Restart Centrifugo after modifying allowed_origins in a configuration file. Now if you reload a browser window with an application you should see new information logs in server output: 2022-06-10 09:44:21 [INF] invalid connection token error=\"invalid token: token format is not valid\" client=a65a8463-6a36-421d-814a-0083c88365292022-06-10 09:44:21 [INF] disconnect after handling command client=a65a8463-6a36-421d-814a-0083c8836529 command=\"id:1 connect:{token:\\\"\\\" name:\\\"js\\\"}\" reason=\"invalid token\" user= We still cannot connect. That's because the client must provide a valid JWT (JSON Web Token) to authenticate itself. This token must be generated on your backend and passed to the client-side (over template variables or using a separate AJAX call – whichever way you prefer). Since in our simple example we don't have an application backend, we can quickly generate an example token for a user using the centrifugo sub-command gentoken, like this: ./centrifugo gentoken -u 123722 – where -u flag sets user ID. The output should be like this: HMAC SHA-256 JWT for user \"123722\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw – you will have a different token value since this one is based on the randomly generated token_hmac_secret_key from the configuration file we created at the beginning of this tutorial. See the token authentication docs for information about proper token generation in a real application. Now we can copy generated HMAC SHA-256 JWT and paste it into Centrifugo constructor instead of placeholder in index.html file. I.e.: const centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDgyOTl9.mUU9s5kj3yqp-SAEqloGy8QBgsLg0llA7lKUNwtHRnw\"}); If you reload your browser tab – the connection will be successfully established, but the client still cannot subscribe to a channel: 2022-06-10 09:45:49 [INF] client command error error=\"permission denied\" client=88116489-350f-447f-9ff3-ab61c9341efe code=103 command=\"id:2 subscribe:{channel:\\\"channel\\\"}\" reply=\"id:2 error:{code:103 message:\\\"permission denied\\\"}\" user=123722 We need to give the client permission to subscribe to the channel channel. There are several ways to do this. For example, the client can provide a subscription JWT for a channel. But here we will use an option to allow all authenticated clients to subscribe to any channel. To do this let's extend the server configuration with the allow_subscribe_for_client option: config.json { \"token_hmac_secret_key\": \"bbe7d157-a253-4094-9759-06a8236543f9\", \"admin\": true, \"admin_password\": \"d0683813-0916-4c49-979f-0e08a686b727\", \"admin_secret\": \"4e9eafcf-0120-4ddd-b668-8dc40072c78e\", \"api_key\": \"d7627bb6-2292-4911-82e1-615c0ed3eebb\", \"allowed_origins\": [\"http://localhost:3000\"], \"allow_subscribe_for_client\": true} tip A good practice with Centrifugo is to configure channel namespaces for the different types of real-time features you have in the application. By defining namespaces, you can achieve granular control over channel behavior and permissions. Restart Centrifugo – and after doing this, everything should start working. The client can now successfully connect and subscribe to a channel. Open the developer tools and look at the WebSocket frames panel; you should see something like this: Note that in this example, we generated a connection JWT – but it has an expiration time, so after some time, Centrifugo will stop accepting those tokens. In real life, you need to add a token refresh function to the client to rotate tokens. See our client API SDK spec. OK, the last thing we need to do here is to publish a new counter value to a channel and make sure our app works properly. We can do this over the Centrifugo API by sending an HTTP request to the default API endpoint http://localhost:8000/api, but let's do this over the admin web panel first. Open the Centrifugo admin web panel in another browser tab (http://localhost:8000/) and go to the Actions section. Select the publish action, insert the channel name that you want to publish to – in our case, this is the string channel – and insert into the data area JSON like this: { \"value\": 1} Click PUBLISH button and check out the application browser tab – counter value must be immediately received and displayed. Open several browser tabs with our app and make sure all tabs receive a message as soon as you publish it. BTW, let's also look at how you can publish data to a channel over Centrifugo server API from a terminal using curl tool: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: d7627bb6-2292-4911-82e1-615c0ed3eebb\" \\ --request POST \\ --data '{\"channel\": \"channel\", \"data\": {\"value\": 2}}' \\ http://localhost:8000/api/publish – where for Authorization header we set api_key value from Centrifugo config file generated above. We did it! We built the simplest browser real-time app with Centrifugo and its JavaScript client. It doesn't have a backend; it's not very useful, to be honest, but it should give you an insight into how to start working with the Centrifugo server. Read more about the Centrifugo server in the next documentation chapters – it can do much more than we just showed here. The Integration guide describes the process of idiomatic Centrifugo integration with your application backend.","s":"Quickstart tutorial ⏱️","u":"/docs/getting-started/quickstart","h":"","p":2685},{"i":2688,"t":"On this page","s":"CEL expressions","u":"/docs/pro/cel_expressions","h":"","p":2687},{"i":2690,"t":"We suppose that the main operation for which developers may use CEL expressions in Centrifugo is a subscribe operation. Let's look at it in detail. It's possible to configure subscribe_cel for a channel namespace (subscribe_cel is just an additional namespace channel option, with same rules applied). This expression should be a valid CEL expression. config.json { \"namespaces\": [ { \"name\": \"admin\", \"subscribe_cel\": \"'admin' in meta.roles\" } ]} In the example we are using custom meta information (must be an object) attached to the connection. As mentioned before in the doc this meta may be attached to the connection: when set in the connect proxy result or provided in JWT as meta claim An expression is evaluated for every subscription attempt to a channel in a namespace. So if meta attached to the connection is sth like this: { \"roles\": [\"admin\"]} – then for every channel in the admin namespace defined above expression will be evaluated to True and subscription will be accepted by Centrifugo. tip meta must be JSON object (any {}) for CEL expressions to work.","s":"subscribe_cel","u":"/docs/pro/cel_expressions","h":"#subscribe_cel","p":2687},{"i":2692,"t":"Inside the expression developers can use some variables which are injected by Centrifugo to the CEL runtime. Information about current user ID, meta information attached to the connection, all the variables defined in matched channel pattern will be available for CEL expression evaluation. Say client with user ID 123 subscribes to a channel /users/4 which matched the channel pattern /users/:user: Variable Type Example Description subscribed bool false Whether client is subscribed to channel, always false for subscribe operation user string \"123\" Current authenticated user ID (known from from JWT or connect proxy result) meta map[string]any {\"roles\": [\"admin\"]} Meta information attached to the connection by the apllication backend (in JWT or over connect proxy result) channel string \"/users/4\" Channel client tries to subscribe vars map[string]string {\"user\": \"4\"} Extracted variables from the matched channel pattern. It's empty in case of using channels without variables. In this case, to allow admin to subscribe on any user's channel or allow non-admin user to subscribe only on its own channel, you may construct an expression like this: { ... \"subscribe_cel\": \"vars.user == user or 'admin' in meta.roles\"} Let's look at one more example. Say client with user ID 123 subscribes to a channel /example.com/users/4 which matched the channel pattern /:tenant/users/:user. The permission check may be transformed into sth like this (assuming meta information has information about current connection tenant): { \"namespaces\": [ { \"name\": \"/:tenant/users/:user\", \"subscribe_cel\": \"vars.tenant == meta.tenant && (vars.user == user or 'admin' in meta.roles)\" } ]}","s":"Expression variables","u":"/docs/pro/cel_expressions","h":"#expression-variables","p":2687},{"i":2694,"t":"CEL expression to check permissions to publish into a channel. Same expression variables are available.","s":"publish_cel","u":"/docs/pro/cel_expressions","h":"#publish_cel","p":2687},{"i":2696,"t":"CEL expression to check permissions for channel history. Same expression variables are available.","s":"history_cel","u":"/docs/pro/cel_expressions","h":"#history_cel","p":2687},{"i":2698,"t":"CEL expression to check permissions for channel presence. Same expression variables are available.","s":"presence_cel","u":"/docs/pro/cel_expressions","h":"#presence_cel","p":2687},{"i":2700,"t":"On this page","s":"Message batching control","u":"/docs/pro/client_message_batching","h":"","p":2699},{"i":2702,"t":"The client_write_delay is a duration option, it is a time Centrifugo will try to collect messages inside each connection message write loop before sending them towards the connection. Enabling client_write_delay may reduce CPU usage of both server and client in case of high message rate inside individual connections. The reduction happens due to the lesser number of system calls to execute. Enabling client_write_delay limits the maximum throughput of messages towards the connection which may be achieved. For example, if client_write_delay is 100ms then the max throughput per second will be (1000 / 100) * client_max_messages_in_frame (16 by default), i.e. 160 messages per second. Though this should be more than enough for target Centrifugo use cases (frontend apps). Example: config.json { // Rest of config here ... \"client_write_delay\": \"100ms\"}","s":"client_write_delay","u":"/docs/pro/client_message_batching","h":"#client_write_delay","p":2699},{"i":2704,"t":"The client_reply_without_queue is a boolean option to not use client queue for replies to commands. When true replies are written to the transport without going through the connection message queue.","s":"client_reply_without_queue","u":"/docs/pro/client_message_batching","h":"#client_reply_without_queue","p":2699},{"i":2706,"t":"The client_max_messages_in_frame is an integer option which controls the maximum number of messages which may be joined by Centrifugo into one transport frame. By default, 16. Use -1 for unlimited number.","s":"client_max_messages_in_frame","u":"/docs/pro/client_message_batching","h":"#client_max_messages_in_frame","p":2699},{"i":2708,"t":"Channel state events Centrifugo PRO has a feature to enable channel state event webhooks to be sent to your configured backend endpoint: channel occupied event - called whenever first subscriber occipies the channel channel vacated event - called whenever last subscriber leaves the channel Preview state This feature is in the preview state now. We still need some time before it will be ready for usage in production. But starting from Centrifugo PRO v5.1.1 the feature is avalable for evaluation. To enable the feature you must use redis engine. Also, only channels with presence enabled and channel_state_events explicitly configured may deliver channel state notifications. When enabling channel state proxy Centrifugo PRO starts using another approach to create Redis keys for presence for namespaces where channel state events enabled, this is an important implementation detail. So the minimal config can look like this (occupied and vacated events for channels in chat namespace will be sent to proxy_channel_state_endpoint): config.json { ... \"engine\": \"redis\", \"proxy_channel_state_endpoint\": \"http://localhost:3000/centrifugo/channel_events\", \"namespaces\": [ { \"name\": \"chat\", \"presence\": true, \"channel_state_events\": [\"occupied\", \"vacated\"] } ]} The proxy endpoint is an extension of Centrifugo OSS proxy and proto definitions may be found in the same proxy.proto file - see NotifyChannelState rpc. Example of the payload your backend request handler will receive: { \"events\": [ {\"channel\": \"chat:index\", \"type\": \"occupied\", \"time_ms\": 1697206286533}, ]} Payload may contain a batch of events, that's why events is an array – this is important for achieving a high event throughput. Your backend must be fast enough to keep up with the events rate and volume, otherwise event queues will grow and eventually new events will be dropped by Centrifugo PRO. Respond with empty object without error set to let Centrifugo PRO know that events were processed successfully. If last channel client resubscribes to a channel fast (during 5 secs) – then Centrifugo PRO won't send vacated events. If client does not resubscribe during 5 secs - event will be sent. So vacated events always delivered with a delay. This is implemented in such way to avoid unnecessary webhooks for quick reconnect scenarios. Centrifugo PRO does the best effort delivering channel state events, making retries when the backend endpoint is unavailable (with exponential backoff), also survives cases when Centrifugo node dies unexpectedly. But there are rare scenarios, when notifications may be lost – like when data lost in Redis. For such cases we recommend syncing the state periodically looking at channel presence information using server API – this depends on the use case though.","s":"Channel state events","u":"/docs/pro/channel_state_events","h":"","p":2707},{"i":2710,"t":"On this page","s":"Distributed rate limit API","u":"/docs/pro/distributed_rate_limit","h":"","p":2709},{"i":2712,"t":"Centrifugo distributed rate limiting is a high performance zero-configuration Redis-based token bucket with milliseconds precision. Zero configuration in this case means that you don't have to preconfigure buckets in Centrifugo – bucket configuration is a part of request to check allowed limits. curl -X POST http://localhost:8000/api/rate_limit \\-H \"Authorization: apikey \" \\-d @- <<'EOF'{ \"key\": \"rate_limit_test\", \"interval\": 60000, \"rate\": 10}EOF Example result: { \"result\": { \"allowed\": true, \"tokens_left\": 9 }} Or, when no tokens left in a bucket: { \"result\": { \"allowed\": false, \"tokens_left\": 0, \"allowed_in\": 5208, \"server_time\": 1694627573210, }} In your app code call rate_limit API of Centrifugo PRO every time some action is executed and check allowed flag to allow or discard the action. Centrifugo PRO also returns allowed_in and server_time fields to help understanding when action will be allowed. These two fields are only appended when tokens_left are less than requested score. allowed_in + server_time will provide you a timestamp in the future (in milliseconds) when action is possible to be executed. So you can delay next action execution till that time if possible.","s":"Overview","u":"/docs/pro/distributed_rate_limit","h":"#overview","p":2709},{"i":2714,"t":"To enable distributed rate limiter: config.json { ... \"distributed_rate_limit\": { \"enabled\": true, \"redis_address\": \"localhost:6379\" } } Note, that just like most of other features in Centrifugo it's possible to configure Redis shards here or use Redis Cluster.","s":"Configuration","u":"/docs/pro/distributed_rate_limit","h":"#configuration","p":2709},{"i":2716,"t":"Now let's look at API description.","s":"API description","u":"/docs/pro/distributed_rate_limit","h":"#api-description","p":2709},{"i":2718,"t":"Field Type Required Description key string Yes Key for a bucket - you can construct keys whatever way you like interval integer Yes Interval in milliseconds rate integer Yes Allowed rate per provided interval score integer No Score for the current action, if not provided the default score 1 is used","s":"rate_limit request","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-request","p":2709},{"i":2720,"t":"Field Name Type Required Description allowed bool Yes Whether desired action is allowed at this point in time tokens_left integer Yes How many tokens left in a bucket allowed_in integer No Milliseconds till desired score will be allowed again server_time integer No Server time as Unix epoch in milliseconds used to calculate result","s":"rate_limit result","u":"/docs/pro/distributed_rate_limit","h":"#rate_limit-result","p":2709},{"i":2722,"t":"On this page","s":"Install and run PRO version","u":"/docs/pro/install_and_run","h":"","p":2721},{"i":2724,"t":"Centrifugo PRO binary releases available on Github. Note that we use a separate repo for PRO releases. Download latest release for your operating system, unpack it and run (see how to set license key below). If you doubt which distribution you need, then on Linux or MacOS you can use the following command to download and unpack centrifugo binary to your current working directory: curl -sSLf https://centrifugal.dev/install_pro.sh | sh","s":"Binary release","u":"/docs/pro/install_and_run","h":"#binary-release","p":2721},{"i":2726,"t":"Centrifugo PRO uses a different image from OSS version – centrifugo/centrifugo-pro: docker run --ulimit nofile=262144:262144 -v /host/dir/with/config/file:/centrifugo -p 8000:8000 centrifugo/centrifugo-pro:v5.1.2 centrifugo -c config.json","s":"Docker image","u":"/docs/pro/install_and_run","h":"#docker-image","p":2721},{"i":2728,"t":"You can use our official Helm chart but make sure you changed Docker image to use PRO version and point to the correct image tag: values.yaml ...image: registry: docker.io repository: centrifugo/centrifugo-pro tag: v5.1.2","s":"Kubernetes","u":"/docs/pro/install_and_run","h":"#kubernetes","p":2721},{"i":2730,"t":"DEB package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.2/centrifugo-pro_5.1.2_amd64.debsudo dpkg -i centrifugo-pro_5.1.2_amd64.deb","s":"Debian and Ubuntu","u":"/docs/pro/install_and_run","h":"#debian-and-ubuntu","p":2721},{"i":2732,"t":"RPM package available in release assets. wget https://github.com/centrifugal/centrifugo-pro/releases/download/v5.1.2/centrifugo-pro-5.1.2.x86_64.rpmsudo yum install centrifugo-pro-5.1.2.x86_64.rpm","s":"Centos","u":"/docs/pro/install_and_run","h":"#centos","p":2721},{"i":2734,"t":"Centrifugo PRO inherits all features and configuration options from open-source version. The only difference is that it expects a valid license key on start to avoid sandbox mode limits. Once you have installed a PRO version and have a license key you can set it in configuration over license field, or pass over environment variables as CENTRIFUGO_LICENSE. Like this: config.json { ... \"license\": \"\"} tip If license properly set then on Centrifugo PRO start you should see license information in logs: owner, license type and expiration date. All PRO features should be unlocked at this point. Warning about sandbox mode in logs on server start must disappear.","s":"Setting PRO license key","u":"/docs/pro/install_and_run","h":"#setting-pro-license-key","p":2721},{"i":2736,"t":"Observability enhancements Centrifugo PRO has some enhancements to exposed metrics. At this moment it provides channel namespace resolution to the following metrics: centrifugo_transport_messages_sent centrifugo_transport_messages_sent_size centrifugo_transport_messages_received centrifugo_transport_messages_received_size Since channel namespace resolution may add some overhead (though negligible in most cases), Centrifugo PRO requires it to be explicitly enabled using two boolean config options (available since Centrifugo PRO v5.1.1): config.json { ... \"channel_namespace_for_transport_messages_sent\": true, \"channel_namespace_for_transport_messages_received\": true} First option channel_namespace_for_transport_messages_sent enables channel namespace label for: centrifugo_transport_messages_sent centrifugo_transport_messages_sent_size Second option channel_namespace_for_transport_messages_received enables for: centrifugo_transport_messages_received centrifugo_transport_messages_received_size.","s":"Observability enhancements","u":"/docs/pro/observability_enhancements","h":"","p":2735},{"i":2738,"t":"On this page","s":"Faster performance","u":"/docs/pro/performance","h":"","p":2737},{"i":2740,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario. According to our benchmarks you can expect 10-15% more requests/sec for small message publications over HTTP API, and up to several times throughput boost when you are frequently get lots of messages from a history, see a couple of examples below.","s":"Faster HTTP API","u":"/docs/pro/performance","h":"#faster-http-api","p":2737},{"i":2742,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC API","u":"/docs/pro/performance","h":"#faster-grpc-api","p":2737},{"i":2744,"t":"Centrifugo PRO has an optimized JSON serialization/deserialization for HTTP proxy. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster HTTP proxy","u":"/docs/pro/performance","h":"#faster-http-proxy","p":2737},{"i":2746,"t":"Centrifugo PRO has an optimized Protobuf serialization/deserialization for GRPC API. The effect can be noticeable under load. The exact numbers heavily depend on usage scenario.","s":"Faster GRPC proxy","u":"/docs/pro/performance","h":"#faster-grpc-proxy","p":2737},{"i":2748,"t":"Centrifugo PRO has an optimized decoding of JWT claims.","s":"Faster JWT decoding","u":"/docs/pro/performance","h":"#faster-jwt-decoding","p":2737},{"i":2750,"t":"Centrifugo PRO has an optimized Protobuf deserialization for GRPC unidirectional stream. This only affects deserialization of initial connect command.","s":"Faster GRPC unidirectional stream","u":"/docs/pro/performance","h":"#faster-grpc-unidirectional-stream","p":2737},{"i":2752,"t":"Let's look at quick live comparisons of Centrifugo OSS and Centrifugo PRO regarding HTTP API performance.","s":"Examples","u":"/docs/pro/performance","h":"#examples","p":2737},{"i":2754,"t":"Sorry, your browser doesn't support embedded video. In this video you can see a 13% speed up for publish operation. But for more complex API calls with larger payloads the difference can be much bigger. See next example that demonstrates this.","s":"Publish HTTP API","u":"/docs/pro/performance","h":"#publish-http-api","p":2737},{"i":2756,"t":"Sorry, your browser doesn't support embedded video. In this video you can see an almost 2x overall speed up while asking 100 messages from Centrifugo history API.","s":"History HTTP API","u":"/docs/pro/performance","h":"#history-http-api","p":2737},{"i":2758,"t":"On this page","s":"Centrifugo PRO","u":"/docs/pro/overview","h":"","p":2757},{"i":2760,"t":"Centrifugo PRO is packed with the following features: Everything from Centrifugo OSS 🔍 Channel and user tracing allows watching client protocol frames in channel or per user ID in real time. 💹 Real-time analytics with ClickHouse for a great system observability, reporting and trending. 🛡️ Operation rate limits to protect server from the real-time API misusing and frontend bugs. 🔥 Push notification API to manage device tokens and send mobile and browser push notifications. 🟢 User status API feature allows understanding activity state for a list of users. 🔌 Connections API to query, filter and inspect active connections. ✋ User blocking API to block/unblock abusive users by ID. 🛑 JWT revoking and invalidation API to revoke tokens by ID and invalidate user's tokens based on issue time. 🔔 Channel state events to be notified on the backend about channel occupied and vacated events. 💪 Channel capabilities for controlling channel permissions per connection or per subscription. 📜 Channel patterns allow defining channel configuration like HTTP routes with parameters. ✍️ CEL expressions to write custom efficient permission rules for channel operations. 🚀 Faster performance to reduce resource usage on server side. 🔮 Singleflight for online presence and history to reduce load on the broker. 🍔 Message batching control for advanced tuning of client connection write behaviour. 🧐 Observability enhancements for additional more granular system state insights. 🪵 CPU and RSS memory usage stats of Centrifugo nodes in admin UI. Also, explore our Centrifugo PRO planned features board for a concise overview of upcoming features which are currently in progress and enhancements planned for a future.","s":"Features","u":"/docs/pro/overview","h":"#features","p":2757},{"i":2762,"t":"Centrifugo PRO requires a license key to run. The price for a license key is 3,499 EUR. A license key has an expiration date — six months since the purchase. Once your key expires, Centrifugo PRO will continue to work, but you will lose access to updates for versions released after the expiration date of your key. This model is like subscription, yet provides the flexibility to delay subsequent payments until an upgrade is required. Our services are exclusively available to corporate and business clients at this time. To purchase the license key, please reach out to us at sales@centrifugal.dev. We kindly ask that your email includes specific information about your company, such as its name and address. We would also value a brief description of how you plan to utilize Centrifugo PRO. The purchase is just an online payment by card (or other available method) in Centrifugal Labs store on Lemon Squeezy platform. We will share a payment link in response to your request – and everything else is automated.","s":"Pricing","u":"/docs/pro/overview","h":"#pricing","p":2757},{"i":2764,"t":"You can try out Centrifugo PRO for free. When you start Centrifugo PRO without license key then it's running in a sandbox mode. Sandbox mode limits the usage of Centrifigo PRO in several ways. For example: Centrifugo handles up to 20 concurrent connections up to 2 server nodes supported up to 5 API requests per second allowed This mode should be enough for development and trying out PRO features, but must not be used in production environment as we can introduce additional limitations in the future. Centrifugo PRO license agreement Centrifugo PRO is distributed by Centrifugal Labs LTD under commercial license which is different from OSS version. By downloading Centrifugo PRO you automatically accept commercial license terms.","s":"Try for free in sandbox mode","u":"/docs/pro/overview","h":"#try-for-free-in-sandbox-mode","p":2757},{"i":2766,"t":"On this page","s":"Channel patterns","u":"/docs/pro/channel_patterns","h":"","p":2765},{"i":2768,"t":"Let's look at the example: { // rest of the config ... \"channel_patterns\": true, // required to turn on the feature. \"namespaces\": [ { \"name\": \"/users/:name\" // namespace options may go here ... }, { \"name\": \"/events/:project/:type\" // namespace options may go here ... } ]} As soon as namespace name starts with / - it's considered a channel pattern. Just like an HTTP path it consists of segments delimited by /. The : symbol in the segment beginning defines a variable part – more information below. In this case a channel to be used must be sth like /users/mario - i.e. start with / and match one of the patterns defined in the configuration. So this channel pattern matching mechanics behaves mostly like HTTP route matching in many frameworks. Given the configuration example above: if channel is /users/mario, then the namespace with the name /users/:name will match and we apply all the options defined for it to the channel. if channel is /events/42/news, then the namespace with the name /events/:project/:type will match. if channel is /events/42, then no namespace will match and the unknown channel error will be returned. Basic example demonstrating use of pattern channels in JS const client := new Centrifuge(\"ws://...\", {});const sub = client.newSubscription('/users/mario');sub.subscribe();client.connect();","s":"Configuration","u":"/docs/pro/channel_patterns","h":"#configuration","p":2765},{"i":2770,"t":"Some implementation restrictions and details to know about: When using channel patterns feature : symbol in a namespace name defines a variable part. It's not related to a namespace separator anymore – the entire channel is matched over the channel pattern. Similar to the HTTP routes semantics. So namespace separator is not needed at all when using channel patterns. Centrifugo only allows explicit channel pattern matching which do not result into channel pattern conflicts in runtime, this is checked during configuration validation on server start. Explicitly defined static patterns (without variables) have precedence over patterns with variables. There is no analogue of top-level namespace (like we have for standard namespace configuration) for channels starting with /. If a channel does not match any explicitly defined pattern then Centrifugo returns the 102: unknown channel error. If you define channel_regex inside channel pattern options – then regex matches over the entire channel (since variable parts are located in the namespace name in this case). Channel pattern must only contain ASCII characters. Duplicate variable names are not allowed inside an individual pattern, i.e. defining /users/:user/:user will result into validation error on start.","s":"Implementation details","u":"/docs/pro/channel_patterns","h":"#implementation-details","p":2765},{"i":2772,"t":": in the channel pattern name helps to define a variable to match against. Named parameters only match a single segment of the channel: Channel pattern \"/users/:name\":/users/mary ✅ match/users/john ✅ match/users/mary/info ❌ no match /users ❌ no match Another example for channel pattern /news/:type/:subtype, i.e. with multiple variables: Channel pattern \"/news/:type/:subtype\":/news/sport/football ✅ match/news/sport/volleyball ✅ match/news/sport ❌ no match/news ❌ no match Channel patterns support mid-segment variables, so the following is possible: Channel pattern \"/personal/user_:user\":/personal/user_mary ✅ match/personal/user_john ✅ match/personal/user_ ❌ no match","s":"Variables","u":"/docs/pro/channel_patterns","h":"#variables","p":2765},{"i":2774,"t":"Additional benefits of using channel patterns may be achieved together with Centrifugo PRO CEL expressions. Channel pattern variables are available inside CEL expressions for evaluation in a custom way.","s":"Using varibles","u":"/docs/pro/channel_patterns","h":"#using-varibles","p":2765},{"i":2776,"t":"CPU and RSS stats A useful addition of Centrifugo PRO is an ability to show CPU and RSS memory usage of each node in admin web UI. Here is how this looks like: The information updated in near real-time (with several seconds delay). It's also available as part of info API.","s":"CPU and RSS stats","u":"/docs/pro/process_stats","h":"","p":2775},{"i":2778,"t":"On this page","s":"Connections API","u":"/docs/pro/connections","h":"","p":2777},{"i":2780,"t":"Let's look at the quick example. First, generate a JWT for user 42: $ centrifugo genconfig Generate token for some user to be used in the example connections: $ centrifugo gentoken -u 42HMAC SHA-256 JWT for user 42 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y Run Centrifugo with uni_http_stream transport enabled (it will allow us connecting from the terminal with curl): CENTRIFUGO_UNI_HTTP_STREAM=1 centrifugo -c config.json Create new terminal window and run: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' In another terminal create one more connection: curl -X POST http://localhost:8000/connection/uni_http_stream --data '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTYyNzcxMzMzNX0.s3eOhujiyBjc4u21nuHkbcWJll4Um0QqGU3PF-6Mf7Y\", \"name\": \"terminal\"}' Now let's call connections over HTTP API: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"42\"}' \\ http://localhost:8000/api/connections The result: { \"result\": { \"connections\": { \"db8bc772-2654-4283-851a-f29b888ace74\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" }, \"4bc3ca70-ecc5-439d-af14-a78ae18e31c7\": { \"app_name\": \"terminal\", \"transport\": \"uni_http_stream\", \"protocol\": \"json\" } } }} Here we can see that user has 2 connections from terminal app. Each connection can be annotated with meta JSON information which is set during connection establishment (over meta claim of JWT or by returning meta in the connect proxy result).","s":"Example","u":"/docs/pro/connections","h":"#example","p":2777},{"i":2782,"t":"Returns information about active connections according to the request. connections params​ Parameter name Parameter type Required Description user string no fast filter by User ID expression string no CEL expression to filter users connections result​ Field name Field type Optional Description connections map[string]ConnectionInfo no active user connections map where key is client ID and value is ConnectionInfo ConnectionInfo​ Field name Field type Optional Description app_name string yes client app name (if provided by client) app_version string yes client app version (if provided by client) transport string no client connection transport protocol string no client connection protocol (json or protobuf) user string yes client user ID state ConnectionState yes connection state ConnectionState object​ Field name Field type Optional Description channels map[string]ChannelContext yes Channels client subscribed to connection_token ConnectionTokenInfo yes information about connection token subscription_tokens map yes information about channel tokens used to subscribe meta JSON object yes meta information attached to a connection ChannelContext object​ Field name Field type Optional Description source int yes The source of channel subscription ConnectionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued SubscriptionTokenInfo object​ Field name Field type Optional Description uid string yes unique token ID (jti) issued_at int yes time (Unix seconds) when token was issued","s":"connections","u":"/docs/pro/connections","h":"#connections","p":2777},{"i":2784,"t":"Singleflight Centrifugo PRO provides an additional boolean option use_singleflight (default false). When this option enabled Centrifugo will automatically try to merge identical requests to history, online presence or presence stats issued at the same time into one real network request. It will do this by using in-memory component called singleflight. tip While it can seem similar, singleflight is not a cache. It only combines identical parallel requests into one. If requests come one after another – they will be sent separately to the broker or presence storage. This option can radically reduce a load on a broker in the following situations: Many clients subscribed to the same channel and in case of massive reconnect scenario try to access history simultaneously to restore a state (whether manually using history API or over automatic recovery feature) Many clients subscribed to the same channel and positioning feature is on so Centrifugo tracks client position Many clients subscribed to the same channel and in case of massive reconnect scenario try to call presence or presence stats simultaneously Using this option only makes sense with remote engine (Redis, KeyDB, Tarantool), it won't provide a benefit in case of using a Memory engine. To enable: config.json { ... \"use_singleflight\": true} Or via CENTRIFUGO_USE_SINGLEFLIGHT environment variable.","s":"Singleflight","u":"/docs/pro/singleflight","h":"","p":2783},{"i":2786,"t":"On this page","s":"Analytics with ClickHouse","u":"/docs/pro/analytics","h":"","p":2785},{"i":2788,"t":"To enable integration with ClickHouse add the following section to a configuration file: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\", \"tcp://127.0.0.1:9001\", \"tcp://127.0.0.1:9002\", \"tcp://127.0.0.1:9003\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"centrifugo_cluster\", \"export_connections\": true, \"export_subscriptions\": true, \"export_operations\": true, \"export_publications\": true, \"export_notifications\": true, \"export_http_headers\": [ \"User-Agent\", \"Origin\", \"X-Real-Ip\" ] }} All ClickHouse analytics options scoped to clickhouse_analytics section of configuration. Toggle this feature using enabled boolean option. tip While we have a nested configuration here it's still possible to use environment variables to set options. For example, use CENTRIFUGO_CLICKHOUSE_ANALYTICS_ENABLED env var name for configure enabled option mentioned above. I.e. nesting expressed as _ in Centrifugo. Centrifugo can export data to different ClickHouse instances, addresses of ClickHouse can be set over clickhouse_dsn option. You also need to set a ClickHouse cluster name (clickhouse_cluster) and database name clickhouse_database. export_connections tells Centrifugo to export connection information snapshots. Information about connection will be exported once a connection established and then periodically while connection alive. See below on table structure to see which fields are available. export_subscriptions tells Centrifugo to export subscription information snapshots. Information about subscription will be exported once a subscription established and then periodically while connection alive. See below on table structure to see which fields are available. export_operations tells Centrifugo to export individual client operation information. See below on table structure to see which fields are available. export_publications tells Centrifugo to export publications for channels to a separate ClickHouse table. export_notifications tells Centrifugo to export push notifications to a separate ClickHouse table. export_http_headers is a list of HTTP headers to export for connection information. export_grpc_metadata is a list of metadata keys to export for connection information for GRPC unidirectional transport. skip_schema_initialization - boolean, default false. By default Centrifugo tries to initialize table schema on start (if not exists). This flag allows skipping initialization process. skip_ping_on_start - boolean, default false. Centrifugo pings Clickhouse servers by default on start, if any of servers is unavailable – Centrifugo fails to start. This option allow skipping this check thus Centrifugo is able to start even if Clickhouse cluster not working correctly.","s":"Configuration","u":"/docs/pro/analytics","h":"#configuration","p":2785},{"i":2790,"t":"SHOW CREATE TABLE centrifugo.connections;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/connections', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└─────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.connections_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.connections_distributed( `client` String, `user` String, `name` String, `version` String, `transport` String, `headers` Map(String, Array(String)), `metadata` Map(String, Array(String)), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'connections', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Connections table","u":"/docs/pro/analytics","h":"#connections-table","p":2785},{"i":2792,"t":"SHOW CREATE TABLE centrifugo.subscriptions┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.subscriptions_distributed;┌─statement───────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.subscriptions_distributed( `client` String, `user` String, `channels` Array(String), `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'subscriptions', murmurHash3_64(client)) │└─────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Subscriptions table","u":"/docs/pro/analytics","h":"#subscriptions-table","p":2785},{"i":2794,"t":"SHOW CREATE TABLE centrifugo.operations;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = ReplicatedMergeTree('/clickhouse/tables/{cluster}/{shard}/operations', '{replica}')PARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.operations_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `client` String, `user` String, `op` String, `channel` String, `method` String, `error` UInt32, `disconnect` UInt32, `duration` UInt64, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'operations', murmurHash3_64(client)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Operations table","u":"/docs/pro/analytics","h":"#operations-table","p":2785},{"i":2796,"t":"SHOW CREATE TABLE centrifugo.publications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.publications( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.publications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `channel` String, `source` String, `size` UInt64, `client` String, `user` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'publications', murmurHash3_64(channel)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Publications table","u":"/docs/pro/analytics","h":"#publications-table","p":2785},{"i":2798,"t":"🚧 This PRO feature is under construction together with push notification API. SHOW CREATE TABLE centrifugo.notifications┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.notifications( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = MergeTreePARTITION BY toYYYYMMDD(time)ORDER BY timeTTL time + toIntervalDay(1)SETTINGS index_granularity = 8192 │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ And distributed one: SHOW CREATE TABLE centrifugo.notifications_distributed;┌─statement──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│ CREATE TABLE centrifugo.operations_distributed( `uid` String, `provider` String, `type` String, `recipient` String, `device_id` String, `platform` String, `user` String, `msg_id` String, `status` String, `error_message` String, `error_code` String, `time` DateTime)ENGINE = Distributed('centrifugo_cluster', 'centrifugo', 'notifications', murmurHash3_64(uid)) │└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘","s":"Notifications table","u":"/docs/pro/analytics","h":"#notifications-table","p":2785},{"i":2800,"t":"Show unique users which were connected: SELECT DISTINCT userFROM centrifugo.connections_distributed;┌─user─────┐│ user_1 ││ user_2 ││ user_3 ││ user_4 ││ user_5 │└──────────┘ Show total number of publication attempts which were throttled by Centrifugo (received Too many requests error with code 111): SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish');┌─count()─┐│ 4502 │└─────────┘ The same for a specific user: SELECT COUNT(*)FROM centrifugo.operations_distributedWHERE (error = 111) AND (op = 'publish') AND (user = 'user_200');┌─count()─┐│ 1214 │└─────────┘ Show number of unique users subscribed to a specific channel in last 5 minutes (this is approximate since subscriptions table contain periodic snapshot entries, clients could unsubscribe in between snapshots – this is reflected in operations table): SELECT COUNT(Distinct(user))FROM centrifugo.subscriptions_distributedWHERE arrayExists(x -> (x = 'chat:index'), channels) AND (time >= (now() - toIntervalMinute(5)));┌─uniqExact(user)─┐│ 101 │└─────────────────┘ Show top 10 users which called publish operation during last one minute: SELECT COUNT(op) AS num_ops, userFROM centrifugo.operations_distributedWHERE (op = 'publish') AND (time >= (now() - toIntervalMinute(1)))GROUP BY userORDER BY num_ops DESCLIMIT 10;┌─num_ops─┬─user─────┐│ 56 │ user_200 ││ 11 │ user_75 ││ 6 │ user_87 ││ 6 │ user_65 ││ 6 │ user_39 ││ 5 │ user_28 ││ 5 │ user_63 ││ 5 │ user_89 ││ 3 │ user_32 ││ 3 │ user_52 │└─────────┴──────────┘ Show total number of push notifications to iOS devices sent during last 24 hours: SELECT COUNT(*)FROM centrifugo.notificationsWHERE (time > (now() - toIntervalHour(24))) AND (platform = 'ios')┌─count()─┐│ 31200 │└─────────┘","s":"Query examples","u":"/docs/pro/analytics","h":"#query-examples","p":2785},{"i":2802,"t":"The recommended way to run ClickHouse in production is with cluster. See an example of such cluster configuration made with Docker Compose. But during development you may want to run Centrifugo with single instance ClickHouse. To do this set only one ClickHouse dsn and do not set cluster name: config.json { ... \"clickhouse_analytics\": { \"enabled\": true, \"clickhouse_dsn\": [ \"tcp://127.0.0.1:9000\" ], \"clickhouse_database\": \"centrifugo\", \"clickhouse_cluster\": \"\", \"export_connections\": true, \"export_subscriptions\": true, \"export_publications\": true, \"export_operations\": true, \"export_http_headers\": [ \"Origin\", \"User-Agent\" ] }} Run ClickHouse locally: docker run -it --rm -v /tmp/clickhouse:/var/lib/clickhouse -p 9000:9000 --name click clickhouse/clickhouse-server Run ClickHouse client: docker run -it --rm --link click:clickhouse-server --entrypoint clickhouse-client clickhouse/clickhouse-server --host clickhouse-server Issue queries: :) SELECT * FROM centrifugo.operations┌─client───────────────────────────────┬─user─┬─op──────────┬─channel─────┬─method─┬─error─┬─disconnect─┬─duration─┬────────────────time─┐│ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connecting │ │ │ 0 │ 0 │ 217894 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ connect │ │ │ 0 │ 0 │ 0 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ $chat:index │ │ 0 │ 0 │ 92714 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ presence │ $chat:index │ │ 0 │ 0 │ 3539 │ 2021-07-31 08:15:09 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test1 │ │ 0 │ 0 │ 2402 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test2 │ │ 0 │ 0 │ 634 │ 2021-07-31 08:15:12 ││ bd55ae3a-dd44-47cb-a4cc-c41f8e33803b │ 2694 │ subscribe │ test3 │ │ 0 │ 0 │ 412 │ 2021-07-31 08:15:12 │└──────────────────────────────────────┴──────┴─────────────┴─────────────┴────────┴───────┴────────────┴──────────┴─────────────────────┘","s":"Development","u":"/docs/pro/analytics","h":"#development","p":2785},{"i":2804,"t":"When ClickHouse analytics enabled Centrifugo nodes start exporting events to ClickHouse. Each node issues insert with events once in 10 seconds (flushing collected events in batches thus making insertion in ClickHouse efficient). Maximum batch size is 100k for each table at the momemt. If insert to ClickHouse failed Centrifugo retries it once and then buffers events in memory (up to 1 million entries). If ClickHouse still unavailable after collecting 1 million events then new events will be dropped until buffer has space. These limits are configurable. Centrifugo PRO uses very efficient code for writing data to ClickHouse, so analytics feature should only add a little overhead for Centrifugo node.","s":"How export works","u":"/docs/pro/analytics","h":"#how-export-works","p":2785},{"i":2806,"t":"Several metrics are exposed to monitor export process health: centrifugo_clickhouse_analytics_drop_count​ Type: Counter Labels: type Description: Total count of drops. Usage: Useful for tracking the number of data drops in ClickHouse analytics, helping identify potential issues with data processing. centrifugo_clickhouse_analytics_flush_duration_seconds​ Type: Summary Labels: type, retries, result Description: Duration of ClickHouse data flush in seconds. Usage: Helps in monitoring the performance of data flush operations in ClickHouse, aiding in performance tuning and issue resolution. centrifugo_clickhouse_analytics_batch_size​ Type: Summary Labels: type Description: Distribution of batch sizes for ClickHouse flush. Usage: Useful for understanding the size of data batches being flushed to ClickHouse, helping optimize performance.","s":"Exposed metrics","u":"/docs/pro/analytics","h":"#exposed-metrics","p":2785},{"i":2808,"t":"On this page","s":"User and channel tracing","u":"/docs/pro/tracing","h":"","p":2807},{"i":2810,"t":"It's possible to connect to the admin tracing endpoint with CURL using the admin session token. And then save tracing output to a file for later processing. curl -X POST http://localhost:8000/admin/trace -H \"Authorization: token \" -d '{\"type\": \"user\", \"entity\": \"56\"}' -o trace.txt Currently, you should copy the admin auth token from browser developer tools, this may be improved in the future as PRO version evolves.","s":"Save to a file","u":"/docs/pro/tracing","h":"#save-to-a-file","p":2807},{"i":2812,"t":"On this page","s":"Token revocation API","u":"/docs/pro/token_revocation","h":"","p":2811},{"i":2814,"t":"By default, information about token revocations shared throughout Centrifugo cluster and kept in a process memory. So token revocation information will be lost upon Centrifugo restart. But it's possible to enable revocation information persistence by configuring a persistence storage – in this case token revocation information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set reasonably small. Keeping pool of revoked tokens small allows avoiding expensive database lookups on every check – information is loaded periodically from the database and all checks performed over in-memory data structure – thus token revocation checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/pro/token_revocation","h":"#how-it-works","p":2811},{"i":2816,"t":"Token revocation features (both revocation by token ID and user token invalidation by issue time) are enabled by default in Centrifugo PRO (as soon as your JWTs has jti and iat claims you will be able to use revocation APIs). By default revocation information kept in a process memory. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/pro/token_revocation","h":"#configure","p":2811},{"i":2818,"t":"Revocation data can be kept in Redis. To enable this configuration should be: { ... \"token_revoke\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for revocation data. The reason is that we don't want to loose revocation information when additional Redis node added. So only one Redis shard can be provided for token_revoke and user_tokens_invalidate features. This should be fine given that working set of revoked entities should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of main Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/pro/token_revocation","h":"#redis-persistence-engine","p":2811},{"i":2820,"t":"Revocation data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"token_revoke\": { \"persistence_engine\": \"database\" }, \"user_tokens_invalidate\": { \"persistence_engine\": \"database\" }}","s":"Database persistence engine","u":"/docs/pro/token_revocation","h":"#database-persistence-engine","p":2811},{"i":2823,"t":"Allows revoking individual tokens. For example, this may be useful when token leakage has been detected and you want to revoke access for a particular tokens. BTW Centrifugo PRO provides user_connections API which has an information about tokens for active users connections (if set in JWT). caution This API assumes that JWTs you are using contain \"jti\" claim which is a unique token ID (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"uid\": \"xxx-xxx-xxx\", \"expire_at\": 1635845122}' \\ http://localhost:8000/api/revoke_token revoke_token params​ Parameter name Parameter type Required Description uid string yes Token unique ID (JTI claim in case of JWT) expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). revoke_token result​ Empty object at the moment.","s":"revoke_token","u":"/docs/pro/token_revocation","h":"#revoke_token","p":2811},{"i":2826,"t":"Allows revoking all tokens for a user which were issued before a certain time. For example, this may be useful after user changed a password in an application. caution This API assumes that JWTs you are using contain \"iat\" claim which is a time token was issued at (according to RFC). Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"test\", \"issued_before\": 1635845022, \"expire_at\": 1635845122}' \\ http://localhost:8000/api/invalidate_user_tokens invalidate_user_tokens params​ Parameter name Parameter type Required Description user string yes User ID to invalidate tokens for issued_before int no All tokens issued at before this Unix time will be considered revoked (in case of JWT this requires iat to be properly set in JWT), if not provided server uses current time expire_at int no Unix time in the future when revocation information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time (matching the expiration time of your JWTs) to keep working set of revocations small (since Centrifugo nodes periodically load all entries from the database table to construct in-memory cache). invalidate_user_tokens result​ Empty object.","s":"invalidate_user_tokens","u":"/docs/pro/token_revocation","h":"#invalidate_user_tokens","p":2811},{"i":2828,"t":"On this page","s":"Operation rate limits","u":"/docs/pro/rate_limiting","h":"","p":2827},{"i":2830,"t":"In-memory rate limit is an efficient way to limit number of operations allowed on a per-connection basis – i.e. inside each individual real-time connection. Our rate limit implementation uses token bucket algorithm internally. The list of operations which can be rate limited on a per-connection level is: subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) In addition, Centrifugo allows defining two special buckets containers: total – define it to limit the total number of commands per interval (all commands sent from client count), these buckets will always be checked if defined, every command from the client always consumes token from total buckets default - define it if you don't want to configure some command buckets explicitly, default buckets will be used in case command buckets is not configured explicitly. config.json { ... \"client_command_rate_limit\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"total\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 20 }, { \"interval\": \"60s\", \"rate\": 50 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 }, ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"update_user_status\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} tip Centrifugo real-time SDKs written in a way that if client receives an error during connect – it will try to reconnect to a server with backoff algorithm. The same for subscribing to channels (i.e. error from subscribe command) – subscription request will be retried with a backoff. Refresh and subscription refresh will be also retried automatically by SDK upon errors after in several seconds. Retries of other commands should be handled manually from the client side if needed – though usually you should choose rate limit limits in a way that normal users of your app never hit the limits.","s":"In-memory per connection rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-connection-rate-limit","p":2827},{"i":2832,"t":"Another type of rate limit in Centrifugo PRO is a per user ID in-memory rate limit. Like per client rate limit this one is also very efficient since also uses in-memory token buckets. The difference is that instead of rate limit per individual client this type of rate limit takes user ID into account. This type of rate limit only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be rate limited with it. The list of operations which can be rate limited is similar to the in-memory rate limit described above. But with additional connect method: total default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"user_command_rate_limit\": { \"enabled\": true, \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"update_user_status\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }}","s":"In-memory per user rate limit","u":"/docs/pro/rate_limiting","h":"#in-memory-per-user-rate-limit","p":2827},{"i":2834,"t":"The next type of rate limit in Centrifugo PRO is a distributed per user ID rate limit with Redis as a bucket state storage. In this case limits are global for the entire Centrifugo cluster. If one user executed two commands on different Centrifugo nodes, Centrifugo consumes two tokens from the same bucket kept in Redis. Since this rate limit goes to Redis to check limits, it adds some latency to a command processing. Our implementation tries to provide good throughput characteristics though – in our tests single Redis instance can handle more than 100k limit check requests per second. And it's possible to scale Redis in the same ways as for Centrifugo Redis Engine. This type of rate limit only checks commands coming from authenticated users – i.e. with non-empty user ID set. Requests from anonymous users can't be rate limited with it. The implementation also uses token bucket algorithm internally. The list of operations which can be rate limited is similar to the in-memory user command rate limit described above. But without special bucket total: default connect subscribe publish history presence presence_stats refresh sub_refresh rpc (with optional method resolution) The configuration is very similar: config.json { ... \"redis_user_command_rate_limit\": { \"enabled\": true, \"redis_address\": \"localhost:6379\", \"default\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 60 }, ] }, \"publish\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 1 } ] }, \"rpc\": { \"buckets\": [ { \"interval\": \"1s\", \"rate\": 10 } ], \"method_override\": [ { \"method\": \"update_user_status\", \"buckets\": [ { \"interval\": \"20s\", \"rate\": 1 } ] } ] } }} Redis configuration for rate limit feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for rate limit feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom rate limit Redis configuration declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"redis_user_command_rate_limit\": { \"enabled\": true, \"use_redis_from_engine\": true, ... }} In this case rate limit will simply connect to Redis instances configured for an Engine.","s":"Redis per user rate limit","u":"/docs/pro/rate_limiting","h":"#redis-per-user-rate-limit","p":2827},{"i":2836,"t":"Above we showed how you can define rate limit strategies to protect server resources and prevent execution of many commands inside the connection and from certain user. But there are scenarios when abusive or broken connections may generate a significant load on the server just by calling commands and getting error responses due to rate limit or due to other reasons (like malformed command). Centrifugo PRO provides a way to configure error limits per connection to deal with this case. Error limits are configured as in-memory buckets operating on a per-connection level. When these buckets are full due to lots of errors for an individual connection Centrifugo disconnects the client (with advice to not reconnect, so our SDKs may follow it). This way it's possible to get rid of the connection and rely on HTTP infrastracture tools to deal with client reconnections. Since WebSocket or other our transports (except unidirectional GRPC, but it's usually not available to the public port) are HTTP-based (or start with HTTP request in WebSocket Upgrade case) – developers can use Nginx limit_req_zone directive, Cloudflare rules, iptables, and so on, to protect Centrifugo from unwanted connections. tip Centrifugo PRO does not count internal errors for the error limit buckets – as internal errors is usually not a client's fault. The configuration on error limits per connection may look like this: config.json { ... \"client_error_limits\": { \"enabled\": true, \"total\": { \"buckets\" : [ { \"interval\": \"5s\", \"rate\": 20 } ] } }}","s":"Disconnecting abusive or misbehaving connections","u":"/docs/pro/rate_limiting","h":"#disconnecting-abusive-or-misbehaving-connections","p":2827},{"i":2838,"t":"On this page","s":"Push notification API","u":"/docs/pro/push_notifications","h":"","p":2837},{"i":2840,"t":"We tried to be practical with our Push Notification API, let's look at its design choices and implementation properties we were able to achieve.","s":"Motivation and design choices","u":"/docs/pro/push_notifications","h":"#motivation-and-design-choices","p":2837},{"i":2842,"t":"To start delivering push notifications in the application, developers usually need to integrate with providers such as FCM, HMS, and APNs. This integration typically requires the storage of device tokens in the application database and the implementation of sending push messages to provider push services. Centrifugo PRO simplifies the process by providing a backend for device token storage, following best practices in token management. It reacts to errors and periodically removes stale devices/tokens to maintain a working set of device tokens based on provider recommendations.","s":"Storage for tokens","u":"/docs/pro/push_notifications","h":"#storage-for-tokens","p":2837},{"i":2844,"t":"Additionally, Centrifugo PRO provides an efficient, scalable queuing mechanism for sending push notifications. Developers can send notifications from the app backend to Centrifugo API with minimal latency and let Centrifugo process sending to FCM, HMS, APNs concurrently using built-in workers. In our tests, we achieved several millions pushes per minute. Centrifugo PRO also supports delayed push notifications feature – to queue push for a later delivery, so for example you can send notification based on user time zone and let Centrifugo PRO send it when needed.","s":"Efficient queuing","u":"/docs/pro/push_notifications","h":"#efficient-queuing","p":2837},{"i":2846,"t":"FCM and HMS have a built-in way of sending notification to large groups of devices over topics mechanism (the same for HMS). One problem with native FCM or HMS topics though is that client can subscribe to any topic from the frontend side without any permission check. In today's world this is usually not desired. So Centrifugo PRO re-implements FCM, HMS topics by introducing an additional API to manage device subscriptions to topics. tip In some cases you may have real-time channels and device subscription topics with matching names – to send messages to both online and offline users. Though it's up to you. Centrifugo PRO device topic subscriptions also add a way to introduce the missing topic semantics for APNs. Centrifugo PRO additionally provides an API to create persistent bindings of user to notification topics. Then – as soon as user registers a device – it will be automatically subscribed to its own topics. As soon as user logs out from the app and you update user ID of the device - user topics binded to the device automatically removed/switched. This design solves one of the issues with FCM – if two different users use the same device it's becoming problematic to unsubscribe the device from large number of topics upon logout. Also, as soon as user to topic binding added (using user_topic_update API) – it will be synchronized across all user active devices. You can still manage such persistent subscriptions on the application backend side if you prefer and provide the full list inside device_register call.","s":"Unified secure topics","u":"/docs/pro/push_notifications","h":"#unified-secure-topics","p":2837},{"i":2848,"t":"Unlike other solutions that combine different provider push sending APIs into a unified API, Centrifugo PRO provides a non-obtrusive proxy for all the mentioned providers. Developers can send notification payloads in a format defined by each provider. It's also possible to send notifications into native FCM, HMS topics or send to raw FCM, HMS, APNs tokens using Centrifugo PRO's push API, allowing them to combine native provider primitives with those added by Centrifugo (i.e., sending to a list of device IDs or to a list of topics).","s":"Non-obtrusive proxying","u":"/docs/pro/push_notifications","h":"#non-obtrusive-proxying","p":2837},{"i":2850,"t":"Furthermore, Centrifugo PRO offers the ability to inspect sent push notifications using ClickHouse analytics. Providers may also offer their own analytics, such as FCM, which provides insight into push notification delivery. Centrifugo PRO also offers a way to analyze push notification delivery and interaction using the update_push_status API.","s":"Builtin analytics","u":"/docs/pro/push_notifications","h":"#builtin-analytics","p":2837},{"i":2852,"t":"Add provider SDK on the frontend side, follow provider instructions for your platform to obtain a push token for a device. For example, for FCM see instructions for iOS, Android, Flutter, Web Browser). The same for HMS or APNs – frontend part should be handled by their native SDKs. Call Centrifugo PRO backend API with the obtained token. From the application backend call Centrifugo device_register API to register the device in Centrifugo PRO storage. Optionally provide list of topics to subscribe device to. Centrifugo returns a registered device object. Pass a generated device ID to the frontend and save it on the frontend together with a token received from FCM. Call Centrifugo send_push_notification API whenever it's time to deliver a push notification. At any moment you can inspect device storage by calling device_list API. Once user logs out from the app, you can detach user ID from device by using device_update or remove device with device_remove API.","s":"Steps to integrate","u":"/docs/pro/push_notifications","h":"#steps-to-integrate","p":2837},{"i":2854,"t":"In Centrifugo PRO you can configure one push provider or use all of them – this choice is up to you.","s":"Configuration","u":"/docs/pro/push_notifications","h":"#configuration","p":2837},{"i":2856,"t":"As mentioned above Centrifigo uses PostgreSQL for token storage. To enable push notifications make sure database section defined in the configration and fcm is in the push_notifications.enabled_providers list. Centrifugo PRO uses Redis for queuing push notification requests, so Redis address should be configured also. Finally, to integrate with FCM a path to the credentials file must be provided (see how to create one in this instruction). So the full configuration to start sending push notifications over FCM may look like this: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"fcm\"], \"fcm_credentials_file_path\": \"/path/to/service/account/credentials.json\" }} tip Actually, PostgreSQL database configuration is optional here – you can use push notifications API without it. In this case you will be able to send notifications to FCM, HMS, APNs raw tokens, FCM and HMS native topics and conditions. I.e. using Centrifugo as an efficient proxy for push notifications (for example if you already keep tokens in your database). But sending to device ids and topics, and token/topic management APIs won't be available for usage.","s":"FCM","u":"/docs/pro/push_notifications","h":"#fcm","p":2837},{"i":2858,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"hms\"], \"hms_app_id\": \"\", \"hms_app_secret\": \"\", }} tip See example how to get app id and app secret here.","s":"HMS","u":"/docs/pro/push_notifications","h":"#hms","p":2837},{"i":2860,"t":"{ ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"redis_address\": \"localhost:6379\", \"enabled_providers\": [\"apns\"], \"apns_endpoint\": \"development\", \"apns_bundle_id\": \"com.example.your_app\", \"apns_auth\": \"token\", \"apns_token_auth_key_path\": \"/path/to/auth/key/file.p8\", \"apns_token_key_id\": \"\", \"apns_token_team_id\": \"your_team_id\", }} We also support auth over p12 certificates with the following options: push_notifications.apns_cert_p12_path push_notifications.apns_cert_p12_b64 push_notifications.apns_cert_p12_password","s":"APNs","u":"/docs/pro/push_notifications","h":"#apns","p":2837},{"i":2862,"t":"push_notifications.max_inactive_device_days​ This integer option configures the number of days to keep device without updates. By default Centrifugo does not remove inactive devices. push_notifications.enable_redis_delayed_scheduler​ Boolean option which enables Redis scheduler to process delayed push notifications. It's off by default since produces additional requests to Redis. When using PostgreSQL as push notifications queue engine you don't need to enable sheduler explicitly. push_notifications.dry_run​ Boolean option, when true Centrifugo PRO does not send push notifications to FCM, APNs, HMS providers but instead just print logs. Useful for development. push_notifications.dry_run_latency​ Duration. When set together with push_notifications.dry_run every dry-run request will cause some delay in workers emulating real-world latency. Useful for development.","s":"Other options","u":"/docs/pro/push_notifications","h":"#other-options","p":2837},{"i":2864,"t":"Centrifugo PRO utilizes Redis Streams as the default queue engine for push notifications. However, it also offers the option to employ PostgreSQL for queuing. It's as simple as: config.json { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"push_notifications\": { \"queue_engine\": \"database\", // rest of the options... }} tip Queue based on Redis streams is generally more efficient, so if you start with PostgreSQL based queue – you have an option to switch to a more performant implementation later. Though in-flight and currently queued push notifications will be lost during a switch.","s":"Use PostgreSQL as queue","u":"/docs/pro/push_notifications","h":"#use-postgresql-as-queue","p":2837},{"i":2867,"t":"Registers or updates device information. device_register request​ Field Type Required Description id string No ID of the device being registered (provide it when updating). provider string Yes Provider of the device token (valid choices: fcm, hms, apns). token string Yes Push notification token for the device. platform string Yes Platform of the device (valid choices: ios, android, web). user string No User associated with the device. topics array of strings No Device topic subscriptions. This should be a full list which replaces all the topics previously accociated with the device. User topics managed by UserTopic model will be automatically attached. meta map No Additional custom metadata for the device device_register result​ Field Name Type Required Description id string Yes The device ID that was registered/updated.","s":"device_register","u":"/docs/pro/push_notifications","h":"#device_register","p":2837},{"i":2869,"t":"Call this method to update device. For example, when user logs out the app and you need to detach user ID from the device. device_update request​ Field Type Required Description ids repeated string No Device ids to filter users repeated string No Device users filter user_update DeviceUserUpdate No Optional user update object meta_update DeviceMetaUpdate No Optional device meta update object topics_update DeviceTopicsUpdate No Optional topics update object DeviceUserUpdate: Field Type Required Description user string Yes User to set DeviceMetaUpdate: Field Type Required Description meta map Yes Meta to set DeviceTopicsUpdate: Field Type Required Description op string Yes Operation to make: add, remove or set topics repeated string Yes Topics for the operation device_update result​ Empty object.","s":"device_update","u":"/docs/pro/push_notifications","h":"#device_update","p":2837},{"i":2871,"t":"Removes device from storage. This may be also called when user logs out the app and you don't need its device token after that. device_remove request​ Field Name Type Required Description ids repeated string No A list of device IDs to be removed users repeated string No A list of device user IDs to filter devices to remove device_remove result​ Empty object.","s":"device_remove","u":"/docs/pro/push_notifications","h":"#device_remove","p":2837},{"i":2873,"t":"Returns a paginated list of registered devices according to request filter conditions. device_list request​ Field Type Required Description filter DeviceFilter Yes How to filter results cursor string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_total_count bool No Flag indicating whether to include total count for the current filter. include_topics bool No Flag indicating whether to include topics information for each device. include_meta bool No Flag indicating whether to include meta information for each device. DeviceFilter: Field Type Required Description ids repeated string No List of device IDs to filter results. providers repeated string No List of device token providers to filter results. platforms repeated string No List of device platforms to filter results. users repeated string No List of device users to filter results. topics repeated string No List of topics to filter results. device_list result​ Field Name Type Required Description items repeated Device Yes A list of devices next_cursor string No Cursor string for retreiving the next page, if not set - then no next page exists total_count integer No Total count value (if include_total_count used) Device: Field Name Type Required Description id string Yes The device's ID. provider string Yes The device's token provider. token string Yes The device's token. platform string Yes The device's platform. user string No The user associated with the device. topics array of strings No Only included if include_topics was true meta map No Only included if include_meta was true","s":"device_list","u":"/docs/pro/push_notifications","h":"#device_list","p":2837},{"i":2875,"t":"Manage mapping of device to topics. device_topic_update request​ Field Type Required Description device_id string Yes Device ID. op string Yes add or remove or set topics repeated string No List of topics. device_topic_update result​ Empty object.","s":"device_topic_update","u":"/docs/pro/push_notifications","h":"#device_topic_update","p":2837},{"i":2877,"t":"List device to topic mapping. device_topic_list request​ Field Type Required Description filter DeviceTopicFilter No List of device IDs to filter results. cursor string No Cursor for pagination (last device id in previous batch, empty for first page). limit int32 No Maximum number of devices to retrieve. include_device bool No Flag indicating whether to include Device information for each object. include_total_count bool No Flag indicating whether to include total count info to response. DeviceTopicFilter: Field Type Required Description device_ids repeated string No List of device IDs to filter results. device_providers repeated string No List of device token providers to filter results. device_platforms repeated string No List of device platforms to filter results. device_users repeated string No List of device users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Topic prefix to filter results. device_topic_list result​ Field Name Type Required Description items repeated DeviceTopic Yes A list of DeviceChannel objects next_cursor string No Cursor string for retreiving the next page, if not set - then no next page exists total_count integer No Total count value (if include_total_count used) DeviceTopic: Field Type Required Description id string Yes ID of DeviceTopic object device_id string Yes Device ID topic string Yes Topic","s":"device_topic_list","u":"/docs/pro/push_notifications","h":"#device_topic_list","p":2837},{"i":2879,"t":"Manage mapping of topics with users. These user topics will be automatically attached to user devices upon registering. And removed from device upon deattaching user. user_topic_update request​ Field Type Required Description user string Yes User ID. op string Yes add or remove or set topics repeated string No List of topics. user_topic_update result​ Empty object.","s":"user_topic_update","u":"/docs/pro/push_notifications","h":"#user_topic_update","p":2837},{"i":2881,"t":"List user to topic mapping. user_topic_list request​ Field Type Required Description flter UserTopicFilter No Filter object. cursor string No Cursor for pagination (last id in previous batch, empty for first page). limit int32 No Maximum number of UserTopic objects to retrieve. include_total_count bool No Flag indicating whether to include total count info to response. UserTopicFilter: Field Type Required Description users repeated string No List of users to filter results. topics repeated string No List of topics to filter results. topic_prefix string No Channel prefix to filter results. user_topic_list result​ Field Name Type Description items repeated UserTopic A list of UserTopic objects next_cursor string No total_count integer No UserTopic: Field Type Required Description id string Yes ID of UserTopic user string Yes User ID topic string Yes Topic","s":"user_topic_list","u":"/docs/pro/push_notifications","h":"#user_topic_list","p":2837},{"i":2883,"t":"Send push notification to specific device_ids, or to topics, or native provider identifiers like fcm_tokens, or to fcm_topic. Request will be queued by Centrifugo, consumed by Centrifugo built-in workers and sent to the provider API. send_push_notification request​ Field name Type Required Description recipient PushRecipient Yes Recipient of push notification notification PushNotification Yes Push notification to send uid string No Unique send id, used for Centrifugo builtin analytics or to cancel delayed push. We recommend using UUID v4 for it send_at int64 No Optional Unix time in the future (in seconds) when to send push notification, push will be queued until that time. PushRecipient (you must set only one of the following fields): Field Type Required Description filter DeviceFilter No Send to device IDs based on Centrifugo device storage filter fcm_tokens repeated string No Send to a list of FCM native tokens fcm_topic string No Send to a FCM native topic fcm_condition string No Send to a FCM native condition hms_tokens repeated string No Send to a list of HMS native tokens hms_topic string No Send to a HMS native topic hms_condition string No Send to a HMS native condition apns_tokens repeated string No Send to a list of APNs native tokens PushNotification: Field Type Required Description expire_at int64 No Unix timestamp when Centrifugo stops attempting to send this notification. Note, it's Centrifugo specific and does not relate to notification TTL fields. We generally recommend to always set this to a reasonable value to protect your app from old push notifications sending fcm FcmPushNotification No Notification for FCM hms HmsPushNotification No Notification for HMS apns ApnsPushNotification No Notification for APNs FcmPushNotification: Field Type Required Description message JSON object Yes FCM Message described in FCM docs. HmsPushNotification: Field Type Required Description message JSON object Yes HMS Message described in HMS Push Kit docs. ApnsPushNotification: Field Type Required Description headers map No APNs headers payload JSON object Yes APNs payload send_push_notification result​ Field Name Type Description uid string Unique send id, matches uid in request if it was provided","s":"send_push_notification","u":"/docs/pro/push_notifications","h":"#send_push_notification","p":2837},{"i":2885,"t":"Cancel delayed push notification (which was sent with custom send_at value). update_push_status request​ Field Type Required Description uid string Yes uid of push notification to cancel update_push_status result​ Empty object.","s":"cancel_push","u":"/docs/pro/push_notifications","h":"#cancel_push","p":2837},{"i":2887,"t":"This API call is experimental, some changes may happen here. Centrifugo PRO also allows tracking status of push notification delivery and interaction. It's possible to use update_push_status API to save the updated status of push notification to the notifications analytics table. Then it's possible to build insights into push notification effectiveness by querying the table. The update_push_status API supposes that you are using uid field with each notification sent and you are using Centrifugo PRO generated device IDs (as described in steps to integrate). This is a part of server API at the moment, so you need to proxy requests to this endpoint over your backend. We can consider making this API suitable for requests from the client side – please reach out if your use case requires it. update_push_status request​ Field Type Required Description uid string Yes uid (unique send id) from send_push_notification status string Yes Status of push notification - delivered or interacted device_id string Yes Device ID msg_id string No Message ID update_push_status result​ Empty object.","s":"update_push_status","u":"/docs/pro/push_notifications","h":"#update_push_status","p":2837},{"i":2889,"t":"Several metrics are available to monitor the state of Centrifugo push worker system: centrifugo_push_notification_count​ Type: Counter Labels: provider, recipient_type, platform, success, err_code Description: Total count of push notifications. Usage: Helps in tracking the number and success rate of push notifications sent, providing insights for optimization and troubleshooting. centrifugo_push_queue_consuming_lag​ Type: Gauge Labels: provider, queue Description: Queue consuming lag in seconds. Usage: Useful for monitoring the delay in processing jobs from the queue, helping identify potential bottlenecks and ensuring timely processing. centrifugo_push_consuming_inflight_jobs​ Type: Gauge Labels: provider, queue Description: Number of inflight jobs being consumed. Usage: Helps in tracking the load on the job processing system, ensuring that resources are being utilized efficiently. centrifugo_push_job_duration_seconds​ Type: Summary Labels: provider, recipient_type Description: Duration of push processing job in seconds. Usage: Useful for monitoring the performance of job processing, helping in performance tuning and issue resolution.","s":"Exposed metrics","u":"/docs/pro/push_notifications","h":"#exposed-metrics","p":2837},{"i":2891,"t":"Coming soon.","s":"Further reading and tutorials","u":"/docs/pro/push_notifications","h":"#further-reading-and-tutorials","p":2837},{"i":2893,"t":"On this page","s":"User blocking API","u":"/docs/pro/user_block","h":"","p":2892},{"i":2895,"t":"By default, information about user block/unblock requests shared throughout Centrifugo cluster and kept in memory. So user will be blocked until Centrifugo restart. But it's possible to enable blocking information persistence by configuring a persistence storage – in this case information will survive Centrifugo restarts. Centrifugo also automatically expires entries in the storage to keep working set of blocked users reasonably small. Keeping pool of blocked users small allows avoiding expensive database lookups on every check – information is loaded periodically from the storage and all checks performed over in-memory data structure – thus user blocking checks are cheap and have a small impact on the overall system performance.","s":"How it works","u":"/docs/pro/user_block","h":"#how-it-works","p":2892},{"i":2897,"t":"User block feature is enabled by default in Centrifugo PRO (blocking information will be stored in process memory). To keep blocking information persistently you need to configure persistence engine. There are two types of persistent engines supported at the moment: redis database","s":"Configure","u":"/docs/pro/user_block","h":"#configure","p":2892},{"i":2899,"t":"Blocking data can be kept in Redis. To enable this configuration should be: { ... \"user_block\": { \"persistence_engine\": \"redis\", \"redis_address\": \"localhost:6379\" }} danger Unlike many other Redis features in Centrifugo consistent sharding is not supported for blocking data. The reason is that we don't want to loose blocking information when additional Redis node added. So only one Redis shard can be provided for user_block feature. This should be fine given that working set of blocked users should be reasonably small and old entries expire. If you try to set several Redis shards here Centrifugo will exit with an error on start. caution One more thing you may notice is that Redis configuration here does not have use_redis_from_engine option. The reason is that since Redis is not shardable here reusing Redis configuration here could cause problems at the moment of Redis scaling – which we want to avoid thus require explicit configuration here.","s":"Redis persistence engine","u":"/docs/pro/user_block","h":"#redis-persistence-engine","p":2892},{"i":2901,"t":"Blocking data can be kept in the relational database. Only PostgreSQL is supported. To enable this configuration should be like: { ... \"database\": { \"dsn\": \"postgresql://postgres:pass@127.0.0.1:5432/postgres\" }, \"user_block\": { \"persistence_engine\": \"database\" }} tip To quickly start local PostgreSQL database: docker run -it --rm -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:15","s":"Database persistence engine","u":"/docs/pro/user_block","h":"#database-persistence-engine","p":2892},{"i":2904,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"2695\", \"expire_at\": 1635845122}' \\ http://localhost:8000/api/block_user block_user params​ Parameter name Parameter type Required Description user string yes User ID to block expire_at int no Unix time in the future when user blocking information should expire (Unix seconds). While optional we recommend to use a reasonably small expiration time to keep working set of blocked users small (since Centrifugo nodes periodically load all entries from the storage to construct in-memory cache). block_user result​ Empty object at the moment.","s":"block_user","u":"/docs/pro/user_block","h":"#block_user","p":2892},{"i":2906,"t":"Example: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"user\": \"2695\"}' \\ http://localhost:8000/api/unblock_user unblock_user params​ Parameter name Parameter type Required Description user string yes User ID to unblock unblock_user result​ Empty object at the moment.","s":"unblock_user","u":"/docs/pro/user_block","h":"#unblock_user","p":2892},{"i":2908,"t":"On this page","s":"Admin web UI","u":"/docs/server/admin_web","h":"","p":2907},{"i":2910,"t":"admin (boolean, default: false) – enables/disables admin web UI admin_password (string, default: \"\") – this is a password to log into admin web interface admin_secret (string, default: \"\") - this is a secret key for authentication token set on successful login. Make both admin_password and admin_secret strong and keep them in secret. After configuring, restart Centrifugo and go to http://localhost:8000 (by default) - you should see web interface. tip Although there is a password based authentication a good advice is to protect web interface by firewall rules in production.","s":"Options","u":"/docs/server/admin_web","h":"#options","p":2907},{"i":2912,"t":"If you want to use custom web interface you can specify path to web interface directory dist: config.json { ..., \"admin\": true, \"admin_password\": \"\", \"admin_secret\": \"\", \"admin_web_path\": \"\"} This can be useful if you want to modify official web interface code in some way and test it with Centrifugo.","s":"Using custom web interface","u":"/docs/server/admin_web","h":"#using-custom-web-interface","p":2907},{"i":2914,"t":"There is also an option to run Centrifugo in insecure admin mode. In this mode, it's unnecessary to set admin_password and admin_secret in the configuration – you will be automatically logged into the web interface without any password. Note that this mode should only be considered for production if you have protected the admin web interface with firewall rules. Without such protection, anyone on the internet would have full access to the admin functionalities described above. To enable insecure admin mode: config.json { ..., \"admin\": true, \"admin_insecure\": true, \"admin_password\": \"\", \"admin_secret\": \"\"}","s":"Admin insecure mode","u":"/docs/server/admin_web","h":"#admin-insecure-mode","p":2907},{"i":2916,"t":"On this page","s":"User status API","u":"/docs/pro/user_status","h":"","p":2915},{"i":2918,"t":"Centrifugo PRO provides a built-in RPC method of client API called update_user_status. Call it with empty parameters from a client side whenever user performs a useful action that proves it's active status in your app. For example, in Javascript: await centrifuge.rpc('update_user_status', {}); note Don't forget to debounce this method calls on a client side to avoid exposing RPC on every mouse move event for example. This RPC call sets user's last active time value in Redis (with sharding and Cluster support). Information about active status will be kept in Redis for a configured time interval, then expire.","s":"Client-side status update RPC","u":"/docs/pro/user_status","h":"#client-side-status-update-rpc","p":2915},{"i":2920,"t":"It's also possible to call update_user_status using Centrifugo server API (for example if you want to force status during application development or you want to proxy status updates over your app backend when using unidirectional transports): curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"users\": [\"42\"]}' \\ http://localhost:8000/api/update_user_status Update user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to update status for Update user status result​ Empty object at the moment.","s":"update_user_status server API","u":"/docs/pro/user_status","h":"#update_user_status-server-api","p":2915},{"i":2922,"t":"Now on a backend side you have access to a bulk API to effectively get status of particular users. Call RPC method of server API (over HTTP or GRPC): curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"users\": [\"42\"]}' \\ http://localhost:8000/api/get_user_status You should get a response like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\", \"active\":1627107289, \"online\":1627107289 } ] }} In case information about last status update time not available the response will be like this: { \"result\":{ \"statuses\":[ { \"user\":\"42\" } ] }} I.e. status object will present in a response but active field won't be set for status object. Note that Centrifugo also maintains online field inside user status object. This field updated periodically by Centrifugo itself while user has active connection with a server. So you can draw away statuses in your application: i.e. when user connected (online time) but not using application for a long time (active time). Get user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to get status for Get user status result​ Field name Field type Optional Description statuses array of UserStatus no Statuses for each user in params (same order) UserStatus​ Field name Field type Optional Description user string no User ID active integer yes Last active time (Unix seconds) online integer yes Last online time (Unix seconds)","s":"get_user_status server API","u":"/docs/pro/user_status","h":"#get_user_status-server-api","p":2915},{"i":2924,"t":"If you need to clear user status information for some reason there is a delete_user_status server API call: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"users\": [\"42\"]}' \\ http://localhost:8000/api/delete_user_status Delete user status params​ Parameter name Parameter type Required Description users array of strings yes List of users to delete status for Delete user status result​ Empty object at the moment.","s":"delete_user_status server API","u":"/docs/pro/user_status","h":"#delete_user_status-server-api","p":2915},{"i":2926,"t":"To enable Redis user status feature: config.json { ... \"user_status\": { \"enabled\": true, \"redis_address\": \"127.0.0.1:6379\" }} Redis configuration for user status feature matches Centrifugo Redis engine configuration. So Centrifugo supports client-side consistent sharding to scale Redis, Redis Sentinel, Redis Cluster for user status feature too. It's also possible to reuse Centrifugo Redis engine by setting use_redis_from_engine option instead of custom throttling Redis address declaration, like this: config.json { ... \"engine\": \"redis\", \"redis_address\": \"localhost:6379\", \"user_status\": { \"enabled\": true, \"use_redis_from_engine\": true, }} In this case Redis active status will simply connect to Redis instances configured for Centrifugo Redis engine. expire_interval is a duration for how long Redis keys will be kept for each user. Expiration time extended on every update. By default expiration time is 31 day. To set it to 1 day: config.json { ... \"user_status\": { ... \"expire_interval\": \"24h\" }}","s":"Configuration","u":"/docs/pro/user_status","h":"#configuration","p":2915},{"i":2928,"t":"On this page","s":"Channel permission model","u":"/docs/server/channel_permissions","h":"","p":2927},{"i":2930,"t":"By default, client's attempt to subscribe on a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel subscribe permissions: Provide subscription token Configure subscribe proxy Use user-limited channels Use subscribe_allowed_for_client namespace option Subscribe capabilities in connection token Subscribe capabilities in connect proxy Below, we are describing those in detail. Provide subscription token​ A client can provide a subscription token in subscribe request. See the format of the token. If client provides a valid token then subscription will be accepted. In Centrifugo PRO subscription token can additionally grant publish, history and presence permissions to a client. caution For namespaces with allow_subscribe_for_client option ON Centrifugo does not allow subscribing on channels starting with private_channel_prefix ($ by default) without token. This limitation exists to help users migrate to Centrifugo v4 without security risks. Configure subscribe proxy​ If client subscribes on a namespace with configured subscribe proxy then depending on proxy response subscription will be accepted or not. If a namespace has configured subscribe proxy, but user came with a token – then subscribe proxy is not used, we are relying on token in this case. If a namespace has subscribe proxy, but user subscribes on a user-limited channel – then subscribe proxy is not used also. Use user-limited channels​ If client subscribes on a user-limited channel and there is a user ID match then subscription will be accepted. caution User-limited channels must be enabled in a namespace using allow_user_limited_channels option. Use allow_subscribe_for_client namespace option​ allow_subscribe_for_client allows all authenticated non-anonymous connections to subscribe on all channels in a namespace. caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security. To additionally allow subscribing to anonymous connections take a look at allow_subscribe_for_anonymous option. Subscribe capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user subscribe to channels. Subscribe capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow user subscribe to channels.","s":"Subscribe permission model","u":"/docs/server/channel_permissions","h":"#subscribe-permission-model","p":2927},{"i":2932,"t":"tip In idiomatic Centrifugo use case data should be published to channels from the application backend (over server API). In this case backend can validate data, save it into persistent storage before publishing in real-time towards connections. When publishing from the client-side backend does not receive publication data at all – it just goes through Centrifugo (except using publish proxy). There are cases when direct publications from the client-side are desired (like typing indicators in chat applications) though. By default, client's attempt to publish data into a channel will be rejected by a server with 103: permission denied error. There are several approaches how to control channel publish permissions: Configure publish proxy Use allow_publish_for_subscriber namespace option Use allow_publish_for_client namespace option Publish capabilities in connection token Publish capability in subscription token Publish capabilities in connect proxy Publish capability in subscribe proxy Use allow_publish_for_client namespace option​ allow_publish_for_client allows publications to channels of a namespace for all client connections. Use allow_publish_for_subscriber namespace option​ allow_publish_for_subscriber allows publications to channels of a namespace for all connections subscribed on a channel they want to publish data into. Configure publish proxy​ If client publishes to a namespace with configured publish proxy then depending on proxy response publication will be accepted or not. Configured publish proxy always used??? (what if user has permission in token or allow_publish_for_client?) Publish capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to channels. Publish capability in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow client to publish to a channel. Publish capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client publish to certain channels. Publish capability in subscribe proxy​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber publish to channel.","s":"Publish permission model","u":"/docs/server/channel_permissions","h":"#publish-permission-model","p":2927},{"i":2934,"t":"By default, client's attempt to call history from a channel (with history retention configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel history permissions. Use allow_history_for_subscriber namespace option​ allow_history_for_subscriber allows history requests to all channels in a namespace for all client connections subscribed on a channel they want to call history for. Use allow_history_for_client namespace option​ allow_history_for_client allows history requests to all channels in a namespace for all client connections. History capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history for channels. History capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call history from a channel. History capabilities in connect proxy​ This is a Centrifugo PRO feature. Connect proxy can return capability object to allow client call history from certain channels. History capability in subscribe proxy response​ Centrifugo PRO only Subscribe proxy can return capability object to allow subscriber call history from channel.","s":"History permission model","u":"/docs/server/channel_permissions","h":"#history-permission-model","p":2927},{"i":2936,"t":"By default, client's attempt to call presence from a channel (with channel presence configured) will be rejected by a server with 103: permission denied error. There are several approaches how to control channel presence permissions. Presence capability in subscribe proxy response​ Subscribe proxy can return capability object to allow subscriber call presence from channel. Use allow_presence_for_subscriber namespace option​ allow_presence_for_subscriber allows presence requests to all channels in a namespace for all client connections subscribed on a channel they want to call presence for. Use allow_presence_for_client namespace option​ allow_presence_for_client allows presence requests to all channels in a namespace for all client connections. Presence capabilities in connection token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence for channels. Presence capabilities in subscription token​ Centrifugo PRO only Connection token can contain a capability object to allow user call presence of a channel. Presence capabilities in connect proxy​ Centrifugo PRO only Connect proxy can return capability object to allow client call presence from certain channels.","s":"Presence permission model","u":"/docs/server/channel_permissions","h":"#presence-permission-model","p":2927},{"i":2938,"t":"Server can whether turn on positioning for all channels in a namespace using \"force_positioning\": true option or client can create positioned subscriptions (but in this case client must have access to history capability).","s":"Positioning permission model","u":"/docs/server/channel_permissions","h":"#positioning-permission-model","p":2927},{"i":2940,"t":"Server can whether turn on automatic recovery for all channels in a namespace using \"force_recovery\": true option or client can create recoverable subscriptions (but in this case client must have access to history capability).","s":"Recovery permission model","u":"/docs/server/channel_permissions","h":"#recovery-permission-model","p":2927},{"i":2942,"t":"Server can whether force sending join/leave messages to all subscribers for all channels in a namespace using \"force_push_join_leave\": true option or client can ask server to include join/leave messages upon subscribing (but in this case client must have access to presence capability).","s":"Join/Leave permission model","u":"/docs/server/channel_permissions","h":"#joinleave-permission-model","p":2927},{"i":2944,"t":"On this page","s":"Channel JWT authorization","u":"/docs/server/channel_token_auth","h":"","p":2943},{"i":2946,"t":"For subscription JWT Centrifugo uses some standard claims defined in rfc7519, also some custom Centrifugo-specific.","s":"Subscription JWT claims","u":"/docs/server/channel_token_auth","h":"#subscription-jwt-claims","p":2943},{"i":2948,"t":"This is a standard JWT claim which must contain an ID of the current application user (as string). The value must match a user in connection JWT – since it's the same real-time connection. The missing claim will mean that token issued for anonymous user (i.e. with empty user ID).","s":"sub","u":"/docs/server/channel_token_auth","h":"#sub","p":2943},{"i":2950,"t":"Required. Channel that client tries to subscribe to with this token (string).","s":"channel","u":"/docs/server/channel_token_auth","h":"#channel","p":2943},{"i":2952,"t":"Optional. Additional information for connection inside this channel (valid JSON).","s":"info","u":"/docs/server/channel_token_auth","h":"#info","p":2943},{"i":2954,"t":"Optional. Additional information for connection inside this channel in base64 format (string). Will be decoded by Centrifugo to raw bytes.","s":"b64info","u":"/docs/server/channel_token_auth","h":"#b64info","p":2943},{"i":2956,"t":"Optional. This is a standard JWT claim that allows setting private channel subscription token expiration time (a UNIX timestamp in the future, in seconds, as integer) and configures subscription expiration time. At the moment if the subscription expires client connection will be closed and the client will try to reconnect. In most cases, you don't need this and should prefer using the expiration of the connection JWT to deactivate the connection (see authentication). But if you need more granular per-channel control this may fit your needs. Once exp is set in token every subscription token must be periodically refreshed. This refresh workflow happens on the client side. Refer to the specific client documentation to see how to refresh subscriptions.","s":"exp","u":"/docs/server/channel_token_auth","h":"#exp","p":2943},{"i":2958,"t":"Optional. By default, Centrifugo looks on exp claim to both check token expiration and configure subscription expiration time. In most cases this is fine, but there could be situations where you want to decouple subscription token expiration check with subscription expiration time. As soon as the expire_at claim is provided (set) in subscription JWT Centrifugo relies on it for setting subscription expiration time (JWT expiration still checked over exp though). expire_at is a UNIX timestamp seconds when the subscription should expire. Set it to the future time for expiring subscription at some point Set it to 0 to disable subscription expiration (but still check token exp claim). This allows implementing a one-time subscription token.","s":"expire_at","u":"/docs/server/channel_token_auth","h":"#expire_at","p":2943},{"i":2960,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But if you set token_audience option as described in client authentication then audience for subscription JWT will also be checked.","s":"aud","u":"/docs/server/channel_token_auth","h":"#aud","p":2943},{"i":2962,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But if you set token_issuer option as described in client authentication then issuer for subscription JWT will also be checked.","s":"iss","u":"/docs/server/channel_token_auth","h":"#iss","p":2943},{"i":2964,"t":"This is a UNIX time when token was issued (seconds). See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"iat","u":"/docs/server/channel_token_auth","h":"#iat","p":2943},{"i":2966,"t":"This is a token unique ID. See definition in RFC. This claim is optional but can be useful together with Centrifugo PRO token revocation features.","s":"jti","u":"/docs/server/channel_token_auth","h":"#jti","p":2943},{"i":2968,"t":"One more claim is override. This is an object which allows overriding channel options for the particular channel subscriber which comes with subscription token. Field Type Optional Description presence BoolValue yes override presence channel option join_leave BoolValue yes override join_leave channel option force_push_join_leave BoolValue yes override force_push_join_leave channel option force_recovery BoolValue yes override force_recovery channel option force_positioning BoolValue yes override force_positioning channel option BoolValue is an object like this: { \"value\": true/false} So for example, you want to turn off emitting a presence information for a particular subscriber in a channel: { ... \"override\": { \"presence\": { \"value\": false } }}","s":"override","u":"/docs/server/channel_token_auth","h":"#override","p":2943},{"i":2970,"t":"So to generate a subscription token you can use something like this in Python (assuming user ID is 42 and the channel is gossips): Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"channel\": \"$gossips\", \"exp\": int(time.time()) + 3600}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42', channel: '$gossips' }) .setProtectedHeader({ alg }) .setExpirationTime('1h') .sign(secret) console.log(token);})(); Where \"secret\" is the token_hmac_secret_key from Centrifugo configuration (we use HMAC tokens in this example which relies on a shared secret key, for RSA or ECDSA tokens you need to use a private key known only by your backend).","s":"Example","u":"/docs/server/channel_token_auth","h":"#example","p":2943},{"i":2972,"t":"During development you can quickly generate valid subscription token using Centrifugo gensubtoken cli command. ./centrifugo gensubtoken -u 123722 -s channel You should see an output like this: HMAC SHA-256 JWT for user \"123722\" and channel \"channel\" with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM3MjIiLCJleHAiOjE2NTU0NDg0MzgsImNoYW5uZWwiOiJjaGFubmVsIn0.JyRI3ovNV-abV8VxCmZCD556o2F2mNL1UoU58gNR-uI But in real app subscription JWT must be generated by your application backend.","s":"gensubtoken cli command","u":"/docs/server/channel_token_auth","h":"#gensubtoken-cli-command","p":2943},{"i":2974,"t":"When separate_subscription_token_config boolean option is true Centrifugo does not look at general token options at all when verifying subscription tokens and uses config options starting from subscription_token_ prefix instead. Here is an example how to use JWKS for connection tokens, but have HMAC-based verification for subscription tokens: config.json { \"token_jwks_public_endpoint\": \"https://example.com/openid-connect/certs\", \"separate_subscription_token_config\": true, \"subscription_token_hmac_secret_key\": \"separate_secret_which_must_be_strong\"} All the options which are available for connection token configuration may be re-used for a separate subscription token configuration – just prefix them with subscription_token_ instead of token_.","s":"Separate subscription token config","u":"/docs/server/channel_token_auth","h":"#separate-subscription-token-config","p":2943},{"i":2976,"t":"On this page","s":"Error and disconnect codes","u":"/docs/server/codes","h":"","p":2975},{"i":2978,"t":"Client errors are errors that can be returned to a client in replies to commands. This is specific for bidirectional client protocol only. For example, an error can be returned inside a reply to a subscribe command issued by a client. Here is the list of Centrifugo built-in client error codes (with proxy feature you have a way to use custom error codes in replies or reuse existing).","s":"Client error codes","u":"/docs/server/codes","h":"#client-error-codes","p":2975},{"i":2980,"t":"Code: 100Message: \"internal server error\"Temporary: true Error Internal means server error, if returned this is a signal that something went wrong with a server itself and client most probably not guilty.","s":"Internal","u":"/docs/server/codes","h":"#internal","p":2975},{"i":2982,"t":"Code: 101Message: \"unauthorized\" Error Unauthorized says that request is unauthorized.","s":"Unauthorized","u":"/docs/server/codes","h":"#unauthorized","p":2975},{"i":2984,"t":"Code: 102Message: \"unknown channel\" Error Unknown Channel means that channel name does not exist. Usually this is returned when client uses channel with a namespace which is not defined in Centrifugo configuration.","s":"Unknown Channel","u":"/docs/server/codes","h":"#unknown-channel","p":2975},{"i":2986,"t":"Code: 103Message: \"permission denied\" Error Permission Denied means that access to resource not allowed.","s":"Permission Denied","u":"/docs/server/codes","h":"#permission-denied","p":2975},{"i":2988,"t":"Code: 104Message: \"method not found\" Error Method Not Found means that method sent in command does not exist.","s":"Method Not Found","u":"/docs/server/codes","h":"#method-not-found","p":2975},{"i":2990,"t":"Code: 105Message: \"already subscribed\" Error Already Subscribed returned when a client attempts to subscribe to a channel to which it is already subscribed. In Centrifugo, a client can only have one subscription to a specific channel. When using client-side subscriptions, this error may signal a bug in the SDK or the SDK being used in a way that was not planned. This error may also be returned by the server when a client tries to subscribe to a channel but is already subscribed to it using server-side subscriptions.","s":"Already Subscribed","u":"/docs/server/codes","h":"#already-subscribed","p":2975},{"i":2992,"t":"Code: 106Message: \"limit exceeded\" Error Limit Exceeded says that some sort of limit exceeded, server logs should give more detailed information. See also ErrorTooManyRequests which is more specific for rate limiting purposes.","s":"Limit Exceeded","u":"/docs/server/codes","h":"#limit-exceeded","p":2975},{"i":2994,"t":"Code: 107Message: \"bad request\" Error Bad Request says that server can not process received data because it is malformed. Retrying request does not make sense.","s":"Bad Request","u":"/docs/server/codes","h":"#bad-request","p":2975},{"i":2996,"t":"Code: 108Message: \"not available\" Error Not Available means that resource is not enabled. For example, this can be returned when trying to access history or presence in a channel that is not configured for having history or presence features.","s":"Not Available","u":"/docs/server/codes","h":"#not-available","p":2975},{"i":2998,"t":"Code: 109Message: \"token expired\" Error Token Expired indicates that connection token expired. Our SDKs handle it in a special way by updating token.","s":"Token Expired","u":"/docs/server/codes","h":"#token-expired","p":2975},{"i":3000,"t":"Code: 110Message: \"expired\" Error Expired indicates that connection expired (no token involved).","s":"Expired","u":"/docs/server/codes","h":"#expired","p":2975},{"i":3002,"t":"Code: 111Message: \"too many requests\"Temporary: true Error Too Many Requests means that server rejected request due to rate limiting strategies.","s":"Too Many Requests","u":"/docs/server/codes","h":"#too-many-requests","p":2975},{"i":3004,"t":"Code: 112Message: \"unrecoverable position\" Error Unrecoverable Position means that stream does not contain required range of publications to fulfill a history query. This can happen due to wrong epoch passed.","s":"Unrecoverable Position","u":"/docs/server/codes","h":"#unrecoverable-position","p":2975},{"i":3006,"t":"Client can be disconnected by a Centrifugo server with custom code and string reason. Here is the list of Centrifugo built-in disconnect codes (with proxy feature you have a way to use custom disconnect codes). note We expect that in most situations developers don't need to programmatically deal with handling various disconnect codes, but since Centrifugo sends them and codes shown in server metrics – they are documented. We expect these codes are mostly useful for logs and metrics.","s":"Client disconnect codes","u":"/docs/server/codes","h":"#client-disconnect-codes","p":2975},{"i":3008,"t":"Code: 3000Reason: \"connection closed\" DisconnectConnectionClosed is a special Disconnect object used when client connection was closed without any advice from a server side. This can be a clean disconnect, or temporary disconnect of the client due to internet connection loss. Server can not distinguish the actual reason of disconnect.","s":"DisconnectConnectionClosed","u":"/docs/server/codes","h":"#disconnectconnectionclosed","p":2975},{"i":3010,"t":"Client will reconnect after receiving such codes. Shutdown​ Code: 3001Reason: \"shutdown\" Disconnect Shutdown may be sent when node is going to shut down. DisconnectServerError​ Code: 3004Reason: \"internal server error\" DisconnectServerError issued when internal error occurred on server. DisconnectExpired​ Code: 3005Reason: \"connection expired\" DisconnectSubExpired​ Code: 3006Reason: \"subscription expired\" DisconnectSubExpired issued when client subscription expired. DisconnectSlow​ Code: 3008Reason: \"slow\" DisconnectSlow issued when client can't read messages fast enough. DisconnectWriteError​ Code: 3009Reason: \"write error\" DisconnectWriteError issued when an error occurred while writing to client connection. DisconnectInsufficientState​ Code: 3010Reason: \"insufficient state\" DisconnectInsufficientState issued when server detects wrong client position in channel Publication stream. Disconnect allows clien to restore missed publications on reconnect. DisconnectForceReconnect​ Code: 3011Reason: \"force reconnect\" DisconnectForceReconnect issued when server disconnects connection for some reason and whants it to reconnect. DisconnectNoPong​ Code: 3012Reason: \"no pong\" DisconnectNoPong may be issued when server disconnects bidirectional connection due to no pong received to application-level server-to-client pings in a configured time. DisconnectTooManyRequests​ Code: 3013Reason: \"too many requests\" DisconnectTooManyRequests may be issued when client sends too many commands to a server.","s":"Non-terminal disconnect codes","u":"/docs/server/codes","h":"#non-terminal-disconnect-codes","p":2975},{"i":3012,"t":"Client won't reconnect upon receiving such code. DisconnectInvalidToken​ Code: 3500Reason: \"invalid token\" DisconnectInvalidToken issued when client came with invalid token. DisconnectBadRequest​ Code: 3501Reason: \"bad request\" DisconnectBadRequest issued when client uses malformed protocol frames. DisconnectStale​ Code: 3502Reason: \"stale\" DisconnectStale issued to close connection that did not become authenticated in configured interval after dialing. DisconnectForceNoReconnect​ Code: 3503Reason: \"force disconnect\" DisconnectForceNoReconnect issued when server disconnects connection and asks it to not reconnect again. DisconnectConnectionLimit​ Code: 3504Reason: \"connection limit\" DisconnectConnectionLimit can be issued when client connection exceeds a configured connection limit (per user ID or due to other rule). DisconnectChannelLimit​ Code: 3505Reason: \"channel limit\" DisconnectChannelLimit can be issued when client connection exceeds a configured channel limit. DisconnectInappropriateProtocol​ Code: 3506Reason: \"inappropriate protocol\" DisconnectInappropriateProtocol can be issued when client connection format can not handle incoming data. For example, this happens when JSON-based clients receive binary data in a channel. This is usually an indicator of programmer error, JSON clients can not handle binary. DisconnectPermissionDenied​ Code: 3507Reason: \"permission denied\" DisconnectPermissionDenied may be issued when client attempts accessing a server without enough permissions. DisconnectNotAvailable​ Code: 3508Reason: \"not available\" DisconnectNotAvailable may be issued when ErrorNotAvailable does not fit message type, for example we issue DisconnectNotAvailable when client sends asynchronous message without MessageHandler set on server side. DisconnectTooManyErrors​ Code: 3509Reason: \"too many errors\" DisconnectTooManyErrors may be issued when client generates too many errors.","s":"Terminal disconnect codes","u":"/docs/server/codes","h":"#terminal-disconnect-codes","p":2975},{"i":3014,"t":"On this page","s":"Client JWT authentication","u":"/docs/server/authentication","h":"","p":3013},{"i":3016,"t":"For connection JWT, Centrifugo uses some standard claims defined in RFC 7519, as well as custom Centrifugo-specific claims.","s":"Connection JWT Claims","u":"/docs/server/authentication","h":"#connection-jwt-claims","p":3013},{"i":3018,"t":"This standard JWT claim must contain the ID of the current application user (as a string). If a user is not authenticated in the application but you wish to allow them to connect to Centrifugo, an empty string can be used as the user ID in the sub claim. This facilitates anonymous access. In such cases, you might need to enable the corresponding channel namespace options that allow protocol features for anonymous users.","s":"sub","u":"/docs/server/authentication","h":"#sub","p":3013},{"i":3020,"t":"This claim specifies the UNIX timestamp (in seconds) when the token will expire. It is a standard JWT claim - all JWT libraries across different programming languages provide an API to set it. If the exp claim is not included, Centrifugo will not expire the connection. When included, a special algorithm will identify connections with an exp in the past and initiate the connection refresh mechanism. The refresh mechanism allows a connection to be extended. If the refresh fails, Centrifugo will eventually close the client connection, which will not be accepted again until new valid and current credentials are provided in the connection token. The connection expiration mechanism can be utilized in scenarios where you do not want users to remain subscribed to channels after being banned or deactivated in the application. It also serves to protect users from token leakage by setting a reasonably short expiration time. Choose the exp value judiciously; too short a value can lead to frequent application hits with refresh requests, whereas too long a value can result in delayed user connection deactivation. It's a matter of balance. Further details on connection expiration can be found below.","s":"exp","u":"/docs/server/authentication","h":"#exp","p":3013},{"i":3022,"t":"This represents the UNIX time when the token was issued (in seconds). Refer to the definition in RFC 7519. This claim is optional but can be advantageous in conjunction with Centrifugo PRO's token revocation features.","s":"iat","u":"/docs/server/authentication","h":"#iat","p":3013},{"i":3024,"t":"This is a unique identifier for the token. Refer to the definition in RFC 7519. This claim is optional but can be beneficial in conjunction with Centrifugo PRO's token revocation features.","s":"jti","u":"/docs/server/authentication","h":"#jti","p":3013},{"i":3026,"t":"By default, Centrifugo does not check JWT audience (rfc7519 aud claim). But you can force this check by setting token_audience string option: config.json { \"token_audience\": \"centrifugo\"} caution Setting token_audience will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.","s":"aud","u":"/docs/server/authentication","h":"#aud","p":3013},{"i":3028,"t":"By default, Centrifugo does not check JWT issuer (rfc7519 iss claim). But you can force this check by setting token_issuer string option: config.json { \"token_issuer\": \"my_app\"} caution Setting token_issuer will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.","s":"iss","u":"/docs/server/authentication","h":"#iss","p":3013},{"i":3030,"t":"This optional claim provides additional information about the client's connection for Centrifugo. This information will be included in presence data, join/leave events, and client-side channel publications.","s":"info","u":"/docs/server/authentication","h":"#info","p":3013},{"i":3032,"t":"For those utilizing the binary Protobuf protocol and requiring the info to be custom bytes, this field should be used. It contains a base64 encoded representation of your bytes. Centrifugo will decode the base64 back into bytes upon receipt and incorporate the result into the various places described above.","s":"b64info","u":"/docs/server/authentication","h":"#b64info","p":3013},{"i":3034,"t":"This is an optional array of strings identifying the server-side channels to which the client will be subscribed. Further details can be found in the documentation on server-side subscriptions. tip It's important to note that the channels claim is sometimes misinterpreted by users as a list of channel permissions. It does not serve that purpose. Instead, using this claim causes the client to be automatically subscribed to the specified channels upon connection, making it unnecessary to invoke the subscribe API from the client side. More information can be found in the server-side subscriptions documentation.","s":"channels","u":"/docs/server/authentication","h":"#channels","p":3013},{"i":3036,"t":"This optional claim is a map of channels with options, providing a more detailed approach to server-side subscriptions compared to the channels claim, as it allows for the annotation of each channel with additional information and data through options. tip The term subs is shorthand for subscriptions. It should not be confused with the sub claim mentioned earlier, which is a standard JWT claim used to provide a user ID (short for subject). Despite their similar names, these claims serve distinct purposes within a connection JWT. Example: { ... \"subs\": { \"channel1\": { \"data\": {\"welcome\": \"welcome to channel1\"} }, \"channel2\": { \"data\": {\"welcome\": \"welcome to channel2\"} } }} Subscribe options:​ Field Type Optional Description info JSON object yes Custom channel info b64info string yes Custom channel info in Base64 - to pass binary channel info data JSON object yes Custom JSON data to return in subscription context inside Connect reply b64data string yes Same as data but in Base64 to send binary data override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave position BoolValue yes Override position recover BoolValue yes Override recover BoolValue is an object like this: { \"value\": true/false}","s":"subs","u":"/docs/server/authentication","h":"#subs","p":3013},{"i":3038,"t":"meta is an additional JSON object (e.g., {\"key\": \"value\"}) that is attached to a connection. It differs from info as it is never disclosed to clients within presence and join/leave events; it is only accessible on the server side. It can be included in proxy calls from Centrifugo to the application backend (refer to the proxy_include_connection_meta option). In Centrifugo PRO, there is a connections API method that returns this metadata within the connection description object.","s":"meta","u":"/docs/server/authentication","h":"#meta","p":3013},{"i":3040,"t":"Although Centrifugo typically uses the exp claim to manage connection expiration, there may be scenarios where you want to separate the token expiration check from the connection expiration time. When the expire_at claim is included in the JWT, Centrifugo uses it to determine the connection expiration time, while the JWT expiration is still verified using the exp claim. expire_at is a UNIX timestamp indicating when the connection should expire. To expire the connection at a specific future time, set it to that time. To prevent connection expiration, set it to 0 (token exp claim will still be checked).","s":"expire_at","u":"/docs/server/authentication","h":"#expire_at","p":3013},{"i":3042,"t":"As mentioned, the exp claim in a connection token is designed to expire the client connection at some point in time. Here's a detailed look at the process when Centrifugo identifies that the connection is going to expire. First, activate the client expiration mechanism in Centrifugo by providing a connection JWT with an exp claim: import jwtimport timetoken = jwt.encode({\"sub\": \"42\", \"exp\": int(time.time()) + 10*60}, \"secret\", algorithm=\"HS256\")print(token) Assuming the exp claim is set to expire in 10 minutes, the client connects to Centrifugo with this token. Centrifugo will maintain the connection for the specified duration. Once the time elapses, Centrifugo allows a grace period (default is 25 seconds) for the client to refresh its credentials with a new valid token containing an updated exp. Upon initial connection, the client receives a ttl value in the connect response, indicating the seconds remaining before it must initiate a refresh command with new credentials. Centrifugo SDKs handle this ttl internally and automatically begin the refresh process. SDKs provide mechanisms to hook into this process and provide a function to get new token. It's up to developer to decide how to load new token from the backend – in web browser this is usually a simple fetch request and response may look like this: { \"token\": token} You should provide the same connection JWT you issued when the page was initially rendered, but with an updated and valid exp. Our SDKs will then send this token to the Centrifugo server, and the connection will be extended for the period set in the new exp. When you load new token from your app backend user authentication must be facilitated by your app's session mechanism. So you know for whom you are are going to generate an updated token.","s":"Connection expiration","u":"/docs/server/authentication","h":"#connection-expiration","p":3013},{"i":3044,"t":"Let's look at how to generate connection HS256 JWT in Python:","s":"Examples","u":"/docs/server/authentication","h":"#examples","p":3013},{"i":3046,"t":"Python NodeJS import jwttoken = jwt.encode({\"sub\": \"42\"}, \"secret\").decode()print(token) const jose = require('jose');(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42' }) .setProtectedHeader({ alg }) .sign(secret) console.log(token);})(); Note that we use the value of token_hmac_secret_key from Centrifugo config here (in this case token_hmac_secret_key value is just secret). The only two who must know the HMAC secret key is your application backend which generates JWT and Centrifugo. You should never reveal the HMAC secret key to your users. Then you can pass this token to your client side and use it when connecting to Centrifugo: Using centrifuge-js v3 var centrifuge = new Centrifuge(\"ws://localhost:8000/connection/websocket\", { token: token});centrifuge.connect(); See more details about working with connection tokens and handling token expiration on the client-side in the real-time SDK API spec.","s":"Simplest token","u":"/docs/server/authentication","h":"#simplest-token","p":3013},{"i":3048,"t":"HS256 token that will be valid for 5 minutes: Python NodeJS import jwtimport timeclaims = {\"sub\": \"42\", \"exp\": int(time.time()) + 5*60}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42' }) .setProtectedHeader({ alg }) .setExpirationTime('5m') .sign(secret) console.log(token);})();","s":"Token with expiration","u":"/docs/server/authentication","h":"#token-with-expiration","p":3013},{"i":3050,"t":"Let's attach user name: Python NodeJS import jwtclaims = {\"sub\": \"42\", \"info\": {\"name\": \"Alexander Emelin\"}}token = jwt.encode(claims, \"secret\", algorithm=\"HS256\").decode()print(token) const jose = require('jose')(async function main() { const secret = new TextEncoder().encode('secret') const alg = 'HS256' const token = await new jose.SignJWT({ sub: '42', info: {\"name\": \"Alexander Emelin\"} }) .setProtectedHeader({ alg }) .setExpirationTime('5m') .sign(secret) console.log(token);})();","s":"Token with additional connection info","u":"/docs/server/authentication","h":"#token-with-additional-connection-info","p":3013},{"i":3052,"t":"You can use jwt.io site to investigate the contents of your tokens. Also, server logs usually contain some useful information.","s":"Investigating problems with JWT","u":"/docs/server/authentication","h":"#investigating-problems-with-jwt","p":3013},{"i":3054,"t":"Centrifugo supports JSON Web Key (JWK) spec. This means that it's possible to improve JWT security by providing an endpoint to Centrifugo from where to load JWK (by looking at kid header of JWT). A mechanism can be enabled by providing token_jwks_public_endpoint string option to Centrifugo (HTTP address). As soon as token_jwks_public_endpoint set all tokens will be verified using JSON Web Key Set loaded from JWKS endpoint. This makes it impossible to use non-JWK based tokens to connect and subscribe to private channels. tip Read a tutorial in our blog about using Centrifugo with Keycloak SSO. In that case connection tokens are verified using public key loaded from the JWKS endpoint of Keycloak. At the moment Centrifugo caches keys loaded from an endpoint for one hour. Centrifugo will load keys from JWKS endpoint by issuing GET HTTP request with 1 second timeout and one retry in case of failure (not configurable at the moment). Centrifugo supports the following key types (kty) for JWKs tokens: RSA EC (since Centrifugo v5.1.0) Once enabled JWKS used for both connection and channel subscription tokens.","s":"JSON Web Key support","u":"/docs/server/authentication","h":"#json-web-key-support","p":3013},{"i":3056,"t":"It's possible to extract variables from iss and aud JWT claims using Go regexp named groups, then use variables extracted during iss or aud matching to construct a JWKS endpoint dynamically upon token validation. In this case JWKS endpoint may be set in config as template. To achieve this Centrifugo provides two additional options: token_issuer_regex - match JWT issuer (iss claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. token_audience_regex - match JWT audience (aud claim) against this regex, extract named groups to variables, variables are then available for jwks endpoint construction. Let's look at the example: { \"token_issuer_regex\": \"https://example.com/auth/realms/(?P[A-z]+)\", \"token_jwks_public_endpoint\": \"https://keycloak:443/{{realm}}/protocol/openid-connect/certs\",} To use variable in token_jwks_public_endpoint it must be wrapped in {{ }}. When using token_issuer_regex and token_audience_regex make sure token_issuer and token_audience not used in the config - otherwise and error will be returned on Centrifugo start. caution Setting token_issuer_regex and token_audience_regex will also affect subscription tokens (used for channel token authorization). If you need to separate connection token configuration and subscription token configuration check out separate subscription token config feature.","s":"Dynamic JWKs endpoint","u":"/docs/server/authentication","h":"#dynamic-jwks-endpoint","p":3013},{"i":3058,"t":"On this page","s":"Channels and namespaces","u":"/docs/server/channels","h":"","p":3057},{"i":3060,"t":"Centrifugo operates on a PUB/SUB model - it has publishers and subscribers. A channel acts as a conduit for publications. Clients can subscribe to a channel to receive all the real-time messages published there. Subscribers to a channel may also request information about the channel's online presence or its history. A channel is simply a string - names like news, comments, personal_feed are examples of valid channel names. However, there are predefined rules for these strings, as we will discuss later. You can define different behaviors for a channel using a range of available channel options. Channels are ephemeral – there is no need to create them explicitly. Channels are automatically created by Centrifugo as soon as the first client subscribes. Similarly, when the last subscriber leaves, the channel is automatically cleaned up. A channel can be part of a channel namespace. Channel namespacing is a mechanism to define different behaviors for various channels within Centrifugo. Using namespaces is the recommended approach to manage channels – enabling only those channel options which are necessary for the specific real-time feature you are implementing with Centrifugo. caution Ensure you have defined a namespace in the configuration when using channel namespaces. Attempts to subscribe to a channel within an undefined namespace will result in 102: unknown channel errors.","s":"What is a channel","u":"/docs/server/channels","h":"#what-is-a-channel","p":3057},{"i":3062,"t":"Only ASCII symbols must be used in a channel string. Channel name length limited by 255 characters by default (controlled by configuration option channel_max_length). Several symbols in channel names reserved for Centrifugo internal needs: : – for namespace channel boundary (see below) # – for user channel boundary (see below) $ – for private channel prefix (see below) / – for Channel Patterns in Centrifugo PRO * – for the future Centrifugo needs & – for the future Centrifugo needs","s":"Channel name rules","u":"/docs/server/channels","h":"#channel-name-rules","p":3057},{"i":3064,"t":": – is a channel namespace boundary. Namespaces are used to set custom options to a group of channels. Each channel belonging to the same namespace will have the same channel options. Read more about about namespaces and channel options below. If the channel is public:chat - then Centrifugo will apply options to this channel from the channel namespace with the name public. info A namespace is a inalienable component of the channel name. If a user is subscribed to a channel with a namespace, such as public:chat, then you must publish messages to the public:chat channel for them to be delivered to the user. There is often confusion among developers who try to publish messages to chat, mistakenly believing that the namespace is stripped upon subscription. This is not the case. You must publish exactly to the same channel string you used for subscribing.","s":"namespace boundary (:)","u":"/docs/server/channels","h":"#namespace-boundary-","p":3057},{"i":3066,"t":"# symbol serves as the user channel boundary. It acts as a separator to create personal channels for users—referred to as user-limited channels—without requiring a subscription token. For instance, if the channel is named news#42, then only the user with ID 42 can subscribe to this channel. Centrifugo identifies the user ID from the connection credentials provided in the connection JWT. To create a user-limited channel within the personal namespace, you might use a name such as personal:user#42. Furthermore, it's possible to specify multiple user IDs in the channel name, separated by a comma: dialog#42,43. In this case, only users with IDs 42 and 43 are permitted to subscribe to this channel. This setup is ideal for channels that have a static list of allowed users, such as channels for personal messages to a single user or dialogue channels between specific users. However, for dynamic access management of a channel for numerous users, this type of channel is not appropriate. tip User-limited channels must be enabled for a channel namespace using allow_user_limited_channels option. See below more information about channel options and channel namespaces.","s":"user channel boundary (#)","u":"/docs/server/channels","h":"#user-channel-boundary-","p":3057},{"i":3068,"t":"Centrifugo maintains compatibility with its previous versions which had concept of private channels. In earlier versions — specifically Centrifugo v1, v2, and v3—only – only channels beginning with $ required a subscription JWT for subscribing. With Centrifugo v4, this is no longer the case; clients can subscribe to any channel if they have a valid subscription token. However, for namespaces where the allow_subscribe_for_client option is activated, Centrifugo prohibits subscriptions to channels that start with the private_channel_prefix (which defaults to $) unless a subscription token is provided. This restriction is designed to facilitate a secure migration to Centrifugo v4 or later versions.","s":"private channel prefix ($)","u":"/docs/server/channels","h":"#private-channel-prefix-","p":3057},{"i":3070,"t":"Bear in mind that a channel is uniquely identified by its string representation. Do not assume that channels $news and news are the same; they differ because their strings are not identical. Thus, if a user is subscribed to $news, they will not receive messages published to news. The channels dialog#42,43 and dialog#43,42 are considered different as well. Centrifugo only applies permission checks when a user subscribes to a channel. So if user-limited channels are enabled then the user with ID 42 will be able to subscribe on both dialog#42,43 and dialog#43,42. But Centrifugo does no magic regarding channel strings when keeping channel->to->subscribers map. So if the user subscribed on dialog#42,43 you must publish messages to exactly that channel: dialog#42,43. The same reasoning applies to channels within namespaces. Channels chat:index and index are not the same — they are distinct and, moreover, they belong to different namespaces. The concept of channel namespaces in Centrifugo will be discussed shortly.","s":"Channel is just a string","u":"/docs/server/channels","h":"#channel-is-just-a-string","p":3057},{"i":3072,"t":"Centrifugo allows configuring a list of channel namespaces. Namespaces are optional but very useful. A namespace is a container for options applied for channels starting with the namespace name. This provides great control over channel behavior so you have a flexible way to define different channel options for different real-time features in the application. A namespace acts as a container for options that are applied to channels starting with the namespace name. This provides great control over channel behavior, so you have a flexible way to define different channel options for various real-time features in the application. Namespace has a name, and can contain all the channel options described below (with the same defaults). name - unique namespace name (name must consist of letters, numbers, underscores, or hyphens and be more than 2 symbols length i.e. satisfy regexp ^[-a-zA-Z0-9_]{2,}$). If you want to use namespace options for a channel - you must include namespace name into channel name with : as a separator: public:messages gossips:messages Where public and gossips are namespace names. Centrifugo looks for : symbol in the channel name, if found – extracts the namespace name, and applies namespace options while processing protocol commands from a client. All things together here is an example of config.json which includes some top-level channel options set and has 2 additional channel namespaces configured: config.json { \"token_hmac_secret_key\": \"very-long-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"30s\", \"namespaces\": [ { \"name\": \"facts\", \"history_size\": 10, \"history_ttl\": \"300s\" }, { \"name\": \"gossips\" } ]} Channel news will use globally defined channel options. Channel facts:sport will use facts namespace options. Channel gossips:sport will use gossips namespace options. Channel xxx:hello will result into subscription error since there is no xxx namespace defined in the configuration above. Channel namespaces also work with private channels and user-limited channels. For example, if you have a namespace called dialogs then the private channel can be constructed as $dialogs:gossips, user-limited channel can be constructed as dialogs:dialog#1,2. note There is no inheritance in channel options and namespaces – for example, you defined presence: true on a top level of configuration and then defined a namespace – that namespace won't have online presence enabled - you must enable it for a namespace explicitly. There are many options which can be set for channel namespace (on top-level and to named one) to modify behavior of channels belonging to a namespace. Below we describe all these options.","s":"Channel namespaces","u":"/docs/server/channels","h":"#channel-namespaces","p":3057},{"i":3074,"t":"Channel behavior can be modified by using channel options. Channel options can be defined on configuration top-level and for every namespace.","s":"Channel options","u":"/docs/server/channels","h":"#channel-options","p":3057},{"i":3076,"t":"presence (boolean, default false) – enable/disable online presence information for channels in a namespace. Online presence is information about clients currently subscribed to the channel. It contains each subscriber's client ID, user ID, connection info, and channel info. By default, this option is off so no presence information will be available for channels. Let's say you have a channel chat:index and 2 users (with ID 2694 and 56) subscribed to it. And user 2694 has 2 connections to Centrifugo in different browser tabs. In presence data you may see sth like this: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat:index\"}' \\ http://localhost:8000/api/presence{ \"result\": { \"presence\": { \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\": { \"user\": \"2694\", \"client\": \"66fdf8d1-06f0-4375-9fac-db959d6ee8d6\", \"conn_info\": {\"name\": \"Alex\"} }, \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\": { \"user\": \"2694\", \"client\": \"d4516dd3-0b6e-4cfe-84e8-0342fd2bb20c\", \"conn_info\": {\"name\": \"Alex\"} } \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\": { \"user\": \"56\", \"client\": \"g3216dd3-1b6e-tcfe-14e8-1342fd2bb20c\", \"conn_info\": {\"name\": \"Alice\"} } } }} To call presence API from the client connection side client must have permission to do so. See presence permission model. caution Enabling channel online presence adds some overhead since Centrifugo needs to maintain an additional data structure (in a process memory or in a broker memory/disk). So only use it for channels where presence is required. See more details about online presence design.","s":"presence","u":"/docs/server/channels","h":"#presence","p":3057},{"i":3078,"t":"join_leave (boolean, default false) – enable/disable sending join and leave messages when the client subscribes to a channel (unsubscribes from a channel). Join/leave event includes information about the connection that triggered an event – client ID, user ID, connection info, and channel info (similar to entry inside presence information). Enabling join_leave means that Join/Leave messages will start being emitted, but by default they are not delivered to clients subscribed to a channel. You need to force this using namespace option force_push_join_leave or explicitly provide intent from a client-side (in this case client must have permission to call presence API). caution Keep in mind that join/leave messages can generate a huge number of messages in a system if turned on for channels with a large number of active subscribers. If you have channels with a large number of subscribers consider avoiding using this feature. It's hard to say what is \"large\" for you though – just estimate the load based on the fact that each subscribe/unsubscribe event in a channel with N subscribers will result into N messages broadcasted to all. If all clients reconnect at the same time the amount of generated messages is N^2. Join/leave messages distributed only with at most once delivery guarantee.","s":"join_leave","u":"/docs/server/channels","h":"#join_leave","p":3057},{"i":3080,"t":"Boolean, default false. When on all clients will receive join/leave events for a channel in a namespace automatically – without explicit intent to consume join/leave messages from the client side. If pushing join/leave is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel presence (by having an explicit capability or if allowed on a namespace level).","s":"force_push_join_leave","u":"/docs/server/channels","h":"#force_push_join_leave","p":3057},{"i":3082,"t":"history_size (integer, default 0) – history size (amount of messages) for channels. As Centrifugo keeps all history messages in process memory (or in a broker memory) it's very important to limit the maximum amount of messages in channel history with a reasonable value. history_size defines the maximum amount of messages that Centrifugo will keep for each channel in the namespace. As soon as history has more messages than defined by history size – old messages will be evicted. Setting only history_size is not enough to enable history in channels – you also need to wisely configure history_ttl option (see below). caution Enabling channel history adds some overhead (both memory and CPU) since Centrifugo needs to maintain an additional data structure (in a process memory or a broker memory/disk). So only use history for channels where it's required.","s":"history_size","u":"/docs/server/channels","h":"#history_size","p":3057},{"i":3084,"t":"history_ttl (duration, default 0s) – interval how long to keep channel history messages (with seconds precision). As all history is storing in process memory (or in a broker memory) it is also very important to get rid of old history data for unused (inactive for a long time) channels. By default history TTL duration is zero – this means that channel history is disabled. Again – to turn on history you should wisely configure both history_size and history_ttl options. For example for top-level channels (which do not belong to a namespace): config.json { ... \"history_size\": 10, \"history_ttl\": \"60s\"} Let's look at example. You enabled history for a namespace chat and sent two messages in channel chat:index. Then history will contain sth like this: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat:index\", \"limit\": 100}' \\ http://localhost:8000/api/history{ \"result\": { \"publications\": [ { \"data\": { \"input\": \"1\" }, \"offset\": 1 }, { \"data\": { \"input\": \"2\" }, \"offset\": 2 } ], \"epoch\": \"gWuY\", \"offset\": 2 }} To call history API from the client connection side client must have permission to do so. See history permission model. See additional information about offsets and epoch in History and recovery chapter. tip History persistence properties are dictated by Centrifugo engine used. For example, when using memory engine history is only kept till Centrifugo node restart. In Redis engine case persistence is determined by a Redis server persistence configuration (same for KeyDB and Tarantool).","s":"history_ttl","u":"/docs/server/channels","h":"#history_ttl","p":3057},{"i":3086,"t":"history_meta_ttl (duration) – sets a time of history stream metadata expiration (with seconds precision). When using a history in a channel, Centrifugo keeps some metadata for each channel stream. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help to expire metadata faster (or slower if you need it). The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration. If not specified Centrifugo uses a global history_meta_ttl which is 30 days. This should be a good default for most use cases to avoid tweaking history_meta_ttl on a namespace level at all.","s":"history_meta_ttl","u":"/docs/server/channels","h":"#history_meta_ttl","p":3057},{"i":3088,"t":"force_positioning (boolean, default false) – when the force_positioning option is on Centrifugo forces all subscriptions in a namespace to be positioned. I.e. Centrifugo will try to compensate at most once delivery of PUB/SUB broker checking client position inside a stream. If Centrifugo detects a bad position of the client (i.e. potential message loss) it disconnects a client with the Insufficient state disconnect code. Also, when the position option is enabled Centrifugo exposes the current stream top offset and current epoch in subscribe reply making it possible for a client to manually recover its state upon disconnect using history API. force_positioning option must be used in conjunction with reasonably configured message history for a channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to check client position in a stream). If positioning is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level).","s":"force_positioning","u":"/docs/server/channels","h":"#force_positioning","p":3057},{"i":3090,"t":"force_recovery (boolean, default false) – when the position option is on Centrifugo forces all subscriptions in a namespace to be recoverable. When enabled Centrifugo will try to recover missed publications in channels after a client reconnects for some reason (bad internet connection for example). Also when the recovery feature is on Centrifugo automatically enables properties of the force_positioning option described above. force_recovery option must be used in conjunction with reasonably configured message history for channel i.e. history_size and history_ttl must be set (because Centrifugo uses channel history to recover messages). If recovery is not forced then client can provide a corresponding Subscription option to enable it – but it should have permissions to access channel history (by having an explicit capability or if allowed on a namespace level). tip Not all real-time events require this feature turned on so think wisely when you need this. When this option is turned on your application should be designed in a way to tolerate duplicate messages coming from a channel (currently Centrifugo returns recovered publications in order and without duplicates but this is an implementation detail that can be theoretically changed in the future). See more details about how recovery works in special chapter.","s":"force_recovery","u":"/docs/server/channels","h":"#force_recovery","p":3057},{"i":3092,"t":"allow_subscribe_for_client (boolean, default false) – when on all non-anonymous clients will be able to subscribe to any channel in a namespace. To additionally allow anonymous users to subscribe turn on allow_subscribe_for_anonymous (see below). caution Turning this option on effectively makes namespace public – no subscribe permissions will be checked (only the check that current connection is authenticated - i.e. has non-empty user ID). Make sure this is really what you want in terms of channels security.","s":"allow_subscribe_for_client","u":"/docs/server/channels","h":"#allow_subscribe_for_client","p":3057},{"i":3094,"t":"allow_subscribe_for_anonymous (boolean, default false) – turn on if anonymous clients (with empty user ID) should be able to subscribe on channels in a namespace.","s":"allow_subscribe_for_anonymous","u":"/docs/server/channels","h":"#allow_subscribe_for_anonymous","p":3057},{"i":3096,"t":"allow_publish_for_subscriber (boolean, default false) - when the allow_publish_for_subscriber option is enabled client can publish into a channel in namespace directly from the client side over real-time connection but only if client subscribed to that channel. danger Keep in mind that in this case subscriber can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage). allow_publish_for_subscriber (or allow_publish_for_client mentioned below) option still can be useful to send something without backend-side validation and saving it into a database – for example, this option may be handy for demos and quick prototyping real-time app ideas.","s":"allow_publish_for_subscriber","u":"/docs/server/channels","h":"#allow_publish_for_subscriber","p":3057},{"i":3098,"t":"allow_publish_for_client (boolean, default false) – when on allows clients to publish messages into channels directly (from a client-side). It's like allow_publish_for_subscriber – but client should not be a channel subscriber to publish. danger Keep in mind that in this case client can publish any payload to a channel – Centrifugo does not validate input at all. Your app backend won't receive those messages - publications just go through Centrifugo towards channel subscribers. Consider always validate messages which are being published to channels (i.e. using server API to publish after validating input on the backend side, or using publish proxy - see idiomatic usage).","s":"allow_publish_for_client","u":"/docs/server/channels","h":"#allow_publish_for_client","p":3057},{"i":3100,"t":"allow_publish_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to publish into channels in a namespace.","s":"allow_publish_for_anonymous","u":"/docs/server/channels","h":"#allow_publish_for_anonymous","p":3057},{"i":3102,"t":"allow_history_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call history API from that channel.","s":"allow_history_for_subscriber","u":"/docs/server/channels","h":"#allow_history_for_subscriber","p":3057},{"i":3104,"t":"allow_history_for_client (boolean, default false) – allows all clients to call history information in a namespace.","s":"allow_history_for_client","u":"/docs/server/channels","h":"#allow_history_for_client","p":3057},{"i":3106,"t":"allow_history_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call history from channels in a namespace.","s":"allow_history_for_anonymous","u":"/docs/server/channels","h":"#allow_history_for_anonymous","p":3057},{"i":3108,"t":"allow_presence_for_subscriber (boolean, default false) – allows clients who subscribed on a channel to call presence information from that channel.","s":"allow_presence_for_subscriber","u":"/docs/server/channels","h":"#allow_presence_for_subscriber","p":3057},{"i":3110,"t":"allow_presence_for_client (boolean, default false) – allows all clients to call presence information in a namespace.","s":"allow_presence_for_client","u":"/docs/server/channels","h":"#allow_presence_for_client","p":3057},{"i":3112,"t":"allow_presence_for_anonymous (boolean, default false) – turn on if anonymous clients should be able to call presence from channels in a namespace.","s":"allow_presence_for_anonymous","u":"/docs/server/channels","h":"#allow_presence_for_anonymous","p":3057},{"i":3114,"t":"allow_user_limited_channels (boolean, default false) - allows using user-limited channels in a namespace for checking subscribe permission. note If client subscribes to a user-limited channel while this option is off then server rejects subscription with 103: permission denied error.","s":"allow_user_limited_channels","u":"/docs/server/channels","h":"#allow_user_limited_channels","p":3057},{"i":3116,"t":"channel_regex (string, default \"\") – is an option to set a regular expression for channels allowed in the namespace. By default Centrifugo does not limit channel name variations. For example, if you have a namespace chat, then channel names inside this namespace are not really limited, it can be chat:index, chat:1, chat:2, chat:zzz and so on. But if you want to be strict and know possible channel patterns you can use channel_regex option. This is especially useful in namespaces where all clients can subscribe to channels. For example, let's only allow digits after chat: for channel names in a chat namespace: { \"namespaces\": [ { \"name\": \"chat\", \"allow_subscribe_for_client\": true, \"channel_regex\": \"^[\\d+]$\" } ]} danger Note, that we are skipping chat: part in regex. Since namespace prefix is the same for all channels in a namespace we only match the rest (after the prefix) of channel name. Channel regex only checked for client-side subscriptions, if you are using server-side subscriptions Centrifugo won't check the regex. Centrifugo uses Go language regexp package for regular expressions.","s":"channel_regex","u":"/docs/server/channels","h":"#channel_regex","p":3057},{"i":3118,"t":"proxy_subscribe (boolean, default false) – turns on subscribe proxy, more info in proxy chapter","s":"proxy_subscribe","u":"/docs/server/channels","h":"#proxy_subscribe","p":3057},{"i":3120,"t":"proxy_publish (boolean, default false) – turns on publish proxy, more info in proxy chapter","s":"proxy_publish","u":"/docs/server/channels","h":"#proxy_publish","p":3057},{"i":3122,"t":"proxy_sub_refresh (boolean, default false) – turns on sub refresh proxy, more info in proxy chapter","s":"proxy_sub_refresh","u":"/docs/server/channels","h":"#proxy_sub_refresh","p":3057},{"i":3124,"t":"proxy_subscribe_stream (boolean, default false) - turns on subscribe stream proxy, see subscription streams","s":"proxy_subscribe_stream","u":"/docs/server/channels","h":"#proxy_subscribe_stream","p":3057},{"i":3126,"t":"subscribe_proxy_name (string, default \"\") – turns on subscribe proxy when granular proxy mode is used. Note that proxy_subscribe option defined above is ignored in granular proxy mode.","s":"subscribe_proxy_name","u":"/docs/server/channels","h":"#subscribe_proxy_name","p":3057},{"i":3128,"t":"publish_proxy_name (string, default \"\") – turns on publish proxy when granular proxy mode is used. Note that proxy_publish option defined above is ignored in granular proxy mode.","s":"publish_proxy_name","u":"/docs/server/channels","h":"#publish_proxy_name","p":3057},{"i":3130,"t":"sub_refresh_proxy_name (string, default \"\") – turns on sub refresh proxy when granular proxy mode is used. Note that proxy_sub_refresh option defined above is ignored in granular proxy mode.","s":"sub_refresh_proxy_name","u":"/docs/server/channels","h":"#sub_refresh_proxy_name","p":3057},{"i":3132,"t":"subscribe_stream_proxy_name (string, default \"\") – turns on subscribe stream proxy when granular proxy mode is used. Note that proxy_subscribe_stream option defined above is ignored in granular proxy mode.","s":"subscribe_stream_proxy_name","u":"/docs/server/channels","h":"#subscribe_stream_proxy_name","p":3057},{"i":3134,"t":"Let's look at how to set some of these options in a config. In this example we turning on presence, history features, forcing publication recovery. Also allowing all client connections (including anonymous users) to subscribe to channels and call publish, history, presence APIs if subscribed. config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true} Here we set channel options on config top-level – these options will affect channels without namespace. In many cases defining namespaces is a recommended approach so you can manage options for every real-time feature separately. With namespaces the above config may transform to: config.json { \"token_hmac_secret_key\": \"my-secret-key\", \"api_key\": \"secret-api-key\", \"namespaces\": [ { \"name\": \"feed\", \"presence\": true, \"history_size\": 10, \"history_ttl\": \"300s\", \"force_recovery\": true, \"allow_subscribe_for_client\": true, \"allow_subscribe_for_anonymous\": true, \"allow_publish_for_subscriber\": true, \"allow_publish_for_anonymous\": true, \"allow_history_for_subscriber\": true, \"allow_history_for_anonymous\": true, \"allow_presence_for_subscriber\": true, \"allow_presence_for_anonymous\": true } ]} In this case channels should be prefixed with feed: to follow the behavior configured for a feed namespace.","s":"Channel config examples","u":"/docs/server/channels","h":"#channel-config-examples","p":3057},{"i":3136,"t":"On this page","s":"History and recovery","u":"/docs/server/history_and_recovery","h":"","p":3135},{"i":3138,"t":"History properties configured on a namespace level, to enable history both history_size and history_ttl should be set to a value greater than zero. Centrifugo is designed with an idea that history streams are ephemeral (can be created on the fly without explicit create call from Centrifugo users) and can expire or can be lost at any moment. Centrifugo provides a way for a client to understand that channel history lost. In this case, the main application database should be the source of truth and state recovery. When history is on every message published into a channel is saved into a history stream. The persistence properties of history are dictated by a Centrifugo engine used. For example, in the case of the Memory engine, all history is stored in process memory. So as soon as Centrifugo restarted all history is cleared. When using Redis engine history is kept in Redis Stream data structure - persistence properties are then inherited from Redis persistence configuration (the same for KeyDB engine). For Tarantool history is kept inside Tarantool spaces. Each publication when added to history has an offset field. This is an incremental uint64 field. Each stream is identified by the epoch field - which is an arbitrary string. As soon as the underlying engine loses data epoch field will change for a stream thus letting consumers know that stream can't be used as the source of state recovery anymore. tip History in channels is not enabled by default. See how to enable it over channel options. History is available in both server and client API.","s":"History design","u":"/docs/server/history_and_recovery","h":"#history-design","p":3135},{"i":3140,"t":"History iteration based on three fields: limit since reverse Combining these fields you can iterate over a stream in both directions. Get current stream top offset and epoch: history(limit: 0, since: null, reverse: false) Get full history from the current beginning (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: false) Get full history from the current end (but up to client_history_max_publication_limit, which is 300 by default): history(limit: -1, since: null, reverse: true) Get history from the current beginning (up to 10): history(limit: 10, since: null, reverse: false) Get history from the current end in reversed direction (up to 10): history(limit: 10, since: null, reverse: true) Get up to 10 publications since known stream position (described by offset and epoch values): history(limit: 10, since: {offset: 0, epoch: \"epoch\"}, reverse: false) Get up to 10 publications since known stream position (described by offset and epoch values) but in reversed direction (from last to earliest): history(limit: 10, since: {offset: 11, epoch: \"epoch\"}, reverse: true) Here is an example program in Go which endlessly iterates over stream both ends (using gocent API library), upon reaching the end of stream the iteration goes in reversed direction (not really useful in real world but fun): // Iterate by 10.limit := 10// Paginate in reversed order first, then invert it.reverse := true// Start with nil StreamPosition, then fill it with value while paginating.var sp *gocent.StreamPositionfor { historyResult, err = c.History( ctx, channel, gocent.WithLimit(limit), gocent.WithReverse(reverse), gocent.WithSince(sp), ) if err != nil { log.Fatalf(\"Error calling history: %v\", err) } for _, pub := range historyResult.Publications { log.Println(pub.Offset, \"=>\", string(pub.Data)) sp = &gocent.StreamPosition{ Offset: pub.Offset, Epoch: historyResult.Epoch, } } if len(historyResult.Publications) < limit { // Got all pubs, invert pagination direction. reverse = !reverse log.Println(\"end of stream reached, change iteration direction\") }}","s":"History iteration API","u":"/docs/server/history_and_recovery","h":"#history-iteration-api","p":3135},{"i":3142,"t":"One of the most interesting features of Centrifugo is automatic message recovery after short network disconnects. This mechanism allows a client to automatically restore missed publications on successful resubscribe to a channel after being disconnected for a while. In general, you could query your application backend for the actual state on every client reconnect - but the message recovery feature allows Centrifugo to deal with this and restore missed publications from the history cache thus radically reducing the load on your application backend and your main application database in some scenarios (when many clients reconnect at the same time). danger Message recovery protocol feature designed to be used together with reasonably small history stream size as all missed publications sent towards the client in one protocol frame on resubscribing to a channel. Thus, it is mostly suitable for short-time disconnects. It helps a lot to survive a reconnect storm when many clients reconnect at one moment (balancer reload, network glitch) - but it's not a good idea to recover a long list of missed messages after clients being offline for a long time. To enable recovery mechanism for channels set the force_recovery boolean configuration option to true on the configuration file top-level or for a channel namespace. Make sure to enable this option in namespaces where history is on. It's also possible to ask for enabling recovery from the client-side when configuring Subscription object – in this case client must have a permission to call history API. When re-subscribing on channels Centrifugo will return missed publications to a client in a subscribe Reply, also it will return a special recovered boolean flag to indicate whether all missed publications successfully recovered after a disconnect or not. The number of publications that is possible to automatically recover is controlled by the client_recovery_max_publication_limit option which is 300 by default. Centrifugo recovery model based on two fields in the protocol: offset and epoch. All fields are managed automatically by Centrifugo client SDKs (for bidirectional transport). The recovery process works this way: Let's suppose client subscribes on a channel with recovery on. Client receives subscribe reply from Centrifugo with two values: stream epoch and stream top offset, those values are saved by an SDK implementation. Every received publication has incremental offset, client SDK increments locally saved offset on each publication from a channel. Let's say at this point client disconnected for a while. Upon resubscribing to a channel SDK provides last seen epoch anf offset to Centrifugo. Centrifugo tries to load all the missed publications starting from the stream position provided by a client. If Centrifugo decides it can successfully recover client's state – then all missed publications returned in subscribe reply and client receives recovered: true in subscribed event context, and publication event handler of Subscription is called for every missed publication. Otherwise no publications returned and recovered flag of subscribed event context is set to false. epoch is useful for cases when history storage is temporary and can lose the history stream entirely. In this case comparing epoch values gives Centrifugo a tip that while publications exist and theoretically have same offsets as before - the stream is actually different, so it's impossible to use it for the recovery process. To summarize, here is a list of possible scenarios when Centrifugo can't recover client's state for a channel and provides recovered: false flag in subscribed event context: number of missed publications exceeds client_recovery_max_publication_limit option number of missed publications exceeds history_size namespace option client was away for a long time and history stream expired according to history_ttl namespace option storage used by Centrifugo engine lost the stream (restart, number of shards changed, cleared by the administrator, etc.) Having said this all, Centrifugo recovery is nice to keep the continuity of the connection and subscription. This speed-ups resubscribe in many cases and helps the backend to survive mass reconnect scenario since you avoid lots of requests for state loading. For setups with millions of connections this can be a life-saver. But we recommend applications to always have a way to load the state from the main application database. For example, on app reload. You can also manually implement your own recovery logic on top of the basic PUB/SUB possibilities that Centrifugo provides. As we said above you can simply ask your backend for an actual state after every client resubscribe completely bypassing the recovery mechanism described here. Also, it's possible to manually iterate over the Centrifugo stream using the history iteration API described above.","s":"Automatic message recovery","u":"/docs/server/history_and_recovery","h":"#automatic-message-recovery","p":3135},{"i":3144,"t":"On this page","s":"Infrastructure tuning","u":"/docs/server/infra_tuning","h":"","p":3143},{"i":3146,"t":"You should increase a max number of open files Centrifugo process can open if you want to handle more connections. To get the current open files limit run: ulimit -n On Linux you can check limits for a running process using: cat /proc//limits The open file limit shows approximately how many clients your server can handle. Each connection consumes one file descriptor. On most operating systems this limit is 128-256 by default. See this document to get more info on how to increase this number. If you install Centrifugo using RPM from repo then it automatically sets max open files limit to 65536. You may also need to increase max open files for Nginx (or any other proxy before Centrifugo).","s":"Open files limit","u":"/docs/server/infra_tuning","h":"#open-files-limit","p":3143},{"i":3148,"t":"Ephemeral ports exhaustion problem can happen between your load balancer and Centrifugo server. If your clients connect directly to Centrifugo without any load balancer or reverse proxy software between then you are most likely won't have this problem. But load balancing is a very common thing. The problem arises due to the fact that each TCP connection uniquely identified in the OS by the 4-part-tuple: source ip | source port | destination ip | destination port On load balancer/server boundary you are limited in 65536 possible variants by default. Actually due to some OS limits not all 65536 ports are available for usage (usually about 15k ports available by default). In order to eliminate a problem you can: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces See a post in Pusher blog about this problem and more detailed solution steps.","s":"Ephemeral port exhaustion","u":"/docs/server/infra_tuning","h":"#ephemeral-port-exhaustion","p":3143},{"i":3150,"t":"On load balancer/server boundary one more problem can arise: sockets in TIME_WAIT state. Under load when lots of connections and disconnections happen socket descriptors can stay in TIME_WAIT state. Those descriptors can not be reused for a while. So you can get various errors when using Centrifugo. For example something like (99: Cannot assign requested address) while connecting to upstream in Nginx error log and 502 on client side. Look how many socket descriptors in TIME_WAIT state. netstat -an |grep TIME_WAIT | grep | wc -l Nice article about TIME_WAIT sockets: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html The advices here are similar to ephemeral port exhaustion problem: Increase the ephemeral port range by tuning ip_local_port_range option Deploy more Centrifugo server instances to load balance across Deploy more load balancer instances Use virtual network interfaces","s":"Sockets in TIME_WAIT state","u":"/docs/server/infra_tuning","h":"#sockets-in-time_wait-state","p":3143},{"i":3152,"t":"Proxies like Nginx and Envoy have default limits on maximum number of connections which can be established. Make sure you have a reasonable limit for max number of incoming and outgoing connections in your proxy configuration.","s":"Proxy max connections","u":"/docs/server/infra_tuning","h":"#proxy-max-connections","p":3143},{"i":3154,"t":"More rare (since default limit is usually sufficient) your possible number of connections can be limited by conntrack table. Netfilter framework which is part of iptables keeps information about all connections and has limited size for this information. See how to see its limits and instructions to increase in this article.","s":"Conntrack table","u":"/docs/server/infra_tuning","h":"#conntrack-table","p":3143},{"i":3156,"t":"You should also consider adding additional protection to your Centrifugo endpoints. Centrifugo itself provides several options (described in configuration section) regarding server protection from the malicious behavior. Though an additional layer of DDOS protection on network or infrastructure level is highly recommended. For example, you may want to limit the number of connections coming from particular IP address. Here we list some possible ways you can use to protect your Centrifugo installation: Adding Nginx limit_conn_zone configuration Using stick tables of Haproxy Configuring rate limiting rules with Cloudflare The list is not exhaustive of course.","s":"Additional server protection","u":"/docs/server/infra_tuning","h":"#additional-server-protection","p":3143},{"i":3158,"t":"On this page","s":"Helper CLI commands","u":"/docs/server/console_commands","h":"","p":3157},{"i":3160,"t":"To show Centrifugo version and exit run: centrifugo version","s":"version","u":"/docs/server/console_commands","h":"#version","p":3157},{"i":3162,"t":"Another command is genconfig: centrifugo genconfig -c config.json It will automatically generate the minimal required configuration file. This is mostly useful for development. If any errors happen – program will exit with error message and exit code 1. genconfig also supports generation of YAML and TOML configuration file formats - just provide an extension to a file: centrifugo genconfig -c config.toml","s":"genconfig","u":"/docs/server/console_commands","h":"#genconfig","p":3157},{"i":3164,"t":"Centrifugo has special command to check configuration file checkconfig: centrifugo checkconfig --config=config.json If any errors found during validation – program will exit with error message and exit code 1.","s":"checkconfig","u":"/docs/server/console_commands","h":"#checkconfig","p":3157},{"i":3166,"t":"Another command is gentoken: centrifugo gentoken -c config.json -u 28282 It will automatically generate HMAC SHA-256 based token for user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gentoken","u":"/docs/server/console_commands","h":"#gentoken","p":3157},{"i":3168,"t":"Another command is gensubtoken: centrifugo gensubtoken -c config.json -u 28282 -s channel It will automatically generate HMAC SHA-256 based subscription token for channel channel and user with ID 28282 (which expires in 1 week). You can change token TTL with -t flag (number of seconds): centrifugo gentoken -c config.json -u 28282 -s channel -t 3600 This way generated token will be valid for 1 hour. If any errors happen – program will exit with error message and exit code 1. This command is mostly useful for development.","s":"gensubtoken","u":"/docs/server/console_commands","h":"#gensubtoken","p":3157},{"i":3170,"t":"One more command is checktoken: centrifugo checktoken -c config.json It will validate your connection JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checktoken","u":"/docs/server/console_commands","h":"#checktoken","p":3157},{"i":3172,"t":"One more command is checksubtoken: centrifugo checksubtoken -c config.json It will validate your subscription JWT, so you can test it before using while developing application. If any errors happen or validation failed – program will exit with error message and exit code 1. This is mostly useful for development.","s":"checksubtoken","u":"/docs/server/console_commands","h":"#checksubtoken","p":3157},{"i":3174,"t":"On this page","s":"Configure Centrifugo","u":"/docs/server/configuration","h":"","p":3173},{"i":3176,"t":"Centrifugo can be configured in several ways: using command-line flags (highest priority), environment variables (second priority after flags), configuration file (lowest priority).","s":"Configuration sources","u":"/docs/server/configuration","h":"#configuration-sources","p":3173},{"i":3178,"t":"Centrifugo supports several command-line flags. See centrifugo -h for available flags. Command-line flags limited to most frequently used. In general, we suggest to avoid using flags for configuring Centrifugo in a production environment – prefer using environment variables or configuration file. Command-line options have the highest priority when set than other ways to configure Centrifugo.","s":"Command-line flags","u":"/docs/server/configuration","h":"#command-line-flags","p":3173},{"i":3180,"t":"All Centrifugo options can be set over env in the format CENTRIFUGO_ (i.e. option name with CENTRIFUGO_ prefix, all in uppercase). Setting options over env is mostly straightforward except namespaces – see how to set namespaces via env. Environment variables have the second priority after flags. Boolean options can be set using strings according to Go language ParseBool function. I.e. to set true you can just use \"true\" value for an environment variable (or simply \"1\"). To set false use \"false\" or \"0\". Example: export CENTRIFUGO_PROMETHEUS=\"1\" Also, array options, like allowed_origins can be set over environment variables as a single string where values separated by a space. For example: export CENTRIFUGO_ALLOWED_ORIGINS=\"https://mysite1.example.com https://mysite2.example.com\" For a nested object configuration (which we have, for example, in Centrifugo PRO ClickHouse analytics) it's still possible to use environment variables to set options. In this case replace nesting with _ when constructing environment variable name. Empty environment variables are considered unset (!) and will fall back to the next configuration source.","s":"OS environment variables","u":"/docs/server/configuration","h":"#os-environment-variables","p":3173},{"i":3182,"t":"Configuration file supports all options mentioned in Centrifugo documentation and can be in one of three supported formats: JSON, YAML, or TOML. Config file options have the lowest priority among configuration sources (i.e. option set over environment variable is preferred over the same option in config file). A simple way to start with Centrifugo is to run: centrifugo genconfig This command generates config.json configuration file in a current directory. This file already has the minimal number of options set. So it's then possible to start Centrifugo: centrifugo -c config.json","s":"Configuration file","u":"/docs/server/configuration","h":"#configuration-file","p":3173},{"i":3184,"t":"Centrifugo supports three configuration file formats: JSON, YAML, or TOML.","s":"Config file formats","u":"/docs/server/configuration","h":"#config-file-formats","p":3173},{"i":3186,"t":"Here is an example of Centrifugo JSON configuration file: config.json { \"allowed_origins\": [\"http://localhost:3000\"], \"token_hmac_secret_key\": \"\", \"api_key\": \"\"} token_hmac_secret_key used to check JWT signature (more info about JWT in authentication chapter). If you are using connect proxy then you may use Centrifugo without JWT. api_key used for Centrifugo API endpoint authorization, see more in chapter about server HTTP API. Keep both values secret and never reveal them to clients. allowed_origins option described below.","s":"JSON config format","u":"/docs/server/configuration","h":"#json-config-format","p":3173},{"i":3188,"t":"Centrifugo also supports TOML format for configuration file: centrifugo --config=config.toml Where config.toml contains: config.toml allowed_origins: [ \"http://localhost:3000\" ]token_hmac_secret_key = \"\"api_key = \"\"log_level = \"debug\" In the example above we also defined logging level to be debug which is useful to have while developing an application. In the production environment debug logging can be too chatty.","s":"TOML config format","u":"/docs/server/configuration","h":"#toml-config-format","p":3173},{"i":3190,"t":"YAML format is also supported: config.yaml allowed_origins: - \"http://localhost:3000\"token_hmac_secret_key: \"\"api_key: \"\"log_level: debug With YAML remember to use spaces, not tabs when writing a configuration file.","s":"YAML config format","u":"/docs/server/configuration","h":"#yaml-config-format","p":3173},{"i":3192,"t":"Let's describe some important options you can configure when running Centrifugo.","s":"Important options","u":"/docs/server/configuration","h":"#important-options","p":3173},{"i":3194,"t":"This option allows setting an array of allowed origin patterns (array of strings) for WebSocket and SockJS endpoints to prevent CSRF or WebSocket hijacking attacks. Also, it's used for HTTP-based unidirectional transports to enable CORS for configured origins. As soon as allowed_origins is defined every connection request with Origin set will be checked against each pattern in an array. Connection requests without Origin header set are passing through without any checks (i.e. always allowed). For example, a client connects to Centrifugo from a web browser application on http://localhost:3000. In this case, allowed_origins should be configured in this way: \"allowed_origins\": [ \"http://localhost:3000\"] When connecting from https://example.com: \"allowed_origins\": [ \"https://example.com\"] Origin pattern can contain wildcard symbol * to match subdomains: \"allowed_origins\": [ \"https://*.example.com\"] – in this case requests with Origin header like https://foo.example.com or https://bar.example.com will pass the check. It's also possible to allow all origins in the following way (but this is discouraged and insecure when using connect proxy feature): \"allowed_origins\": [ \"*\"]","s":"allowed_origins","u":"/docs/server/configuration","h":"#allowed_origins","p":3173},{"i":3196,"t":"Bind your Centrifugo to a specific interface address (string, by default \"\" - listen on all available interfaces).","s":"address","u":"/docs/server/configuration","h":"#address","p":3173},{"i":3198,"t":"Port to bind Centrifugo to (string, by default \"8000\").","s":"port","u":"/docs/server/configuration","h":"#port","p":3173},{"i":3200,"t":"Engine to use - memory, redis or tarantool. It's a string option, by default memory. Read more about engines in special chapter.","s":"engine","u":"/docs/server/configuration","h":"#engine","p":3173},{"i":3202,"t":"These options allow tweaking server behavior, in most cases default values are good to start with.","s":"Advanced options","u":"/docs/server/configuration","h":"#advanced-options","p":3173},{"i":3204,"t":"Default: 128 Sets the maximum number of different channel subscriptions a single client can have. tip When designing an application avoid subscribing to an unlimited number of channels per one client. Keep number of subscriptions for each client reasonably small – this will help keeping handshake process lightweight and fast.","s":"client_channel_limit","u":"/docs/server/configuration","h":"#client_channel_limit","p":3173},{"i":3206,"t":"Default: 255 Sets the maximum length of the channel name.","s":"channel_max_length","u":"/docs/server/configuration","h":"#channel_max_length","p":3173},{"i":3208,"t":"Default: 0 The maximum number of connections from a user (with known user ID) to Centrifugo node. By default, unlimited. The important thing to emphasize is that client_user_connection_limit works only per one Centrifugo node and exists mostly to protect Centrifugo from many connections from a single user – but not for business logic limitations. This means that if you set this to 1 and scale nodes – say run 10 Centrifugo nodes – then a user will be able to create 10 connections (one to each node).","s":"client_user_connection_limit","u":"/docs/server/configuration","h":"#client_user_connection_limit","p":3173},{"i":3210,"t":"Default: 0 When set to a value > 0 client_connection_limit limits the max number of connections single Centrifugo node can handle. It acts on HTTP middleware level and stops processing request if the condition met. It logs a warning into logs in this case and increments centrifugo_node_client_connection_limit Prometheus counter. Client SDKs will attempt reconnecting. Some motivation behind this option may be found in this issue. Note, that at this point client_connection_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_limit","u":"/docs/server/configuration","h":"#client_connection_limit","p":3173},{"i":3212,"t":"Default: 0 client_connection_rate_limit sets the maximum number of HTTP requests to establish a new real-time connection a single Centrifugo node will accept per second (on real-time transport endpoints). All requests outside the limit will get 503 Service Unavailable code in response. Our SDKs handle this with backoff reconnection. By default, no limit is used. Note, that at this point client_connection_rate_limit does not affect connections coming over GRPC unidirectional transport.","s":"client_connection_rate_limit","u":"/docs/server/configuration","h":"#client_connection_rate_limit","p":3173},{"i":3214,"t":"Default: 1048576 Maximum client message queue size in bytes to close slow reader connections. By default - 1mb.","s":"client_queue_max_size","u":"/docs/server/configuration","h":"#client_queue_max_size","p":3173},{"i":3216,"t":"Default: 0 client_concurrency when set tells Centrifugo that commands from a client must be processed concurrently. By default, concurrency disabled – Centrifugo processes commands received from a client one by one. This means that if a client issues two RPC requests to a server then Centrifugo will process the first one, then the second one. If the first RPC call is slow then the client will wait for the second RPC response much longer than it could (even if the second RPC is very fast). If you set client_concurrency to some value greater than 1 then commands will be processed concurrently (in parallel) in separate goroutines (with maximum concurrency level capped by client_concurrency value). Thus, this option can effectively reduce the latency of individual requests. Since separate goroutines are involved in processing this mode adds some performance and memory overhead – though it should be pretty negligible in most cases. This option applies to all commands from a client (including subscribe, publish, presence, etc).","s":"client_concurrency","u":"/docs/server/configuration","h":"#client_concurrency","p":3173},{"i":3218,"t":"Duration, default: 10s This option allows tuning the maximum time Centrifugo will wait for the connect frame (which contains authentication information) from the client after establishing connection. Default value should be reasonable for most use cases.","s":"client_stale_close_delay","u":"/docs/server/configuration","h":"#client_stale_close_delay","p":3173},{"i":3220,"t":"String, default: \"\" Available since v5.1.1 Usually to authenticate client connections with Centrifugo you need to use JWT authentication or connect proxy. Sometimes though it may be convenient to pass user ID information in incoming HTTP request headers. This is usually the case when application backend infrastructure has some authentication proxy (like Envoy, etc). This proxy may set authenticated user ID to some header and proxy requests further to Centrifugo. When client_user_id_http_header is set to some non-empty header name Centrifugo will try to extract the authenticated user ID for client connections from that header. This mechanism works for all real-time transports based on HTTP (this also includes WebSocket since it starts with HTTP Upgrade request). Example: config.json { ... \"client_user_id_http_header\": \"X-User-Id\"} When using this way for user authentication – you can not set connection expiration and additional connection info which is possible to do using other authentication ways mentioned above. Security warning When using authentication over proxy ensure your proxy strips the header you are using for auth if it comes from the client or forbids such requests to avoid malicious usage. Only your authentication proxy must set the header with user ID.","s":"client_user_id_http_header","u":"/docs/server/configuration","h":"#client_user_id_http_header","p":3173},{"i":3222,"t":"Default: false Enable a mode when all clients can connect to Centrifugo without JWT. In this case, all connections without a token will be treated as anonymous (i.e. with empty user ID). Access to channel operations should be explicitly enabled for anonymous connections.","s":"allow_anonymous_connect_without_token","u":"/docs/server/configuration","h":"#allow_anonymous_connect_without_token","p":3173},{"i":3224,"t":"Default: false When the option is set Centrifugo won't accept connections from anonymous users even if they provided a valid JWT. I.e. if token is valid, but sub claim is empty – then Centrifugo closes connection with advice to not reconnect again.","s":"disallow_anonymous_connection_tokens","u":"/docs/server/configuration","h":"#disallow_anonymous_connection_tokens","p":3173},{"i":3226,"t":"Default: 0 By default, Centrifugo runs on all available CPU cores (also Centrifugo can look at cgroup limits when rnning in Docker/Kubernetes). To limit the number of cores Centrifugo can utilize in one moment use this option.","s":"gomaxprocs","u":"/docs/server/configuration","h":"#gomaxprocs","p":3173},{"i":3228,"t":"After Centrifugo started there are several endpoints available.","s":"Endpoint configuration","u":"/docs/server/configuration","h":"#endpoint-configuration","p":3173},{"i":3230,"t":"Bidirectional WebSocket default endpoint: ws://localhost:8000/connection/websocket Bidirectional emulation with HTTP-streaming (disabled by default): ws://localhost:8000/connection/http_stream Bidirectional emulation with SSE (EventSource) (disabled by default): ws://localhost:8000/connection/sse Bidirectional SockJS default endpoint (disabled by default): http://localhost:8000/connection/sockjs Unidirectional EventSource endpoint (disabled by default): http://localhost:8000/connection/uni_sse Unidirectional HTTP streaming endpoint (disabled by default): http://localhost:8000/connection/uni_http_stream Unidirectional WebSocket endpoint (disabled by default): http://localhost:8000/connection/uni_websocket Unidirectional SSE (EventSource) endpoint (disabled by default): http://localhost:8000/connection/uni_sse Server HTTP API endpoint: http://localhost:8000/api By default, all endpoints work on port 8000. This can be changed with port option: { \"port\": 9000} In production setup, you may have a proper domain name in endpoint addresses above instead of localhost. While domain name and port parts can differ depending on setup – URL paths stay the same: /connection/sockjs, /connection/websocket, /api etc.","s":"Default endpoints","u":"/docs/server/configuration","h":"#default-endpoints","p":3173},{"i":3232,"t":"Admin web UI endpoint works on root path by default, i.e. http://localhost:8000. For more details about admin web UI, refer to the Admin web UI documentation.","s":"Admin endpoints","u":"/docs/server/configuration","h":"#admin-endpoints","p":3173},{"i":3234,"t":"Next, when Centrifugo started in debug mode some extra debug endpoints become available. To start in debug mode add debug option to config: { ... \"debug\": true} And endpoint: http://localhost:8000/debug/pprof/ – will show useful information about the internal state of Centrifugo instance. This info is especially helpful when troubleshooting. See wiki page for more info.","s":"Debug endpoints","u":"/docs/server/configuration","h":"#debug-endpoints","p":3173},{"i":3236,"t":"Use health boolean option (by default false) to enable the health check endpoint which will be available on path /health. Also available over command-line flag: centrifugo -c config.json --health","s":"Health check endpoint","u":"/docs/server/configuration","h":"#health-check-endpoint","p":3173},{"i":3238,"t":"Use swagger boolean option (by default false) to enable Swagger UI for server HTTP API. UI will be available on path /swagger. Also available over command-line flag: centrifugo -c config.json --swagger","s":"Swagger UI for server API","u":"/docs/server/configuration","h":"#swagger-ui-for-server-api","p":3173},{"i":3240,"t":"We strongly recommend not expose API, admin, debug, health, and Prometheus endpoints to the Internet. The following Centrifugo endpoints are considered internal: API endpoint (/api) - for HTTP API requests Admin web interface endpoints (/, /admin/auth, /admin/api) - used by web interface Prometheus endpoint (/metrics) - used for exposing server metrics in Prometheus format Health check endpoint (/health) - used to do health checks Debug endpoints (/debug/pprof) - used to inspect internal server state Swagger UI endpoint (/swagger) - used for showing embedded Swagger UI for server HTTP API It's a good practice to protect all these endpoints with a firewall. For example, it's possible to configure in location section of the Nginx configuration. Though sometimes you don't have access to a per-location configuration in your proxy/load balancer software. For example when using Amazon ELB. In this case, you can change ports on which your internal endpoints work. To run internal endpoints on custom port use internal_port option: { ... \"internal_port\": 9000} So admin web interface will work on address: http://localhost:9000 Also, debug page will be available on a new custom port too: http://localhost:9000/debug/pprof/ The same for API and Prometheus endpoints.","s":"Custom internal ports","u":"/docs/server/configuration","h":"#custom-internal-ports","p":3173},{"i":3242,"t":"To disable websocket endpoint set websocket_disable boolean option to true. To disable API endpoint set api_disable boolean option to true.","s":"Disable default endpoints","u":"/docs/server/configuration","h":"#disable-default-endpoints","p":3173},{"i":3244,"t":"It's possible to customize server HTTP handler endpoints. To do this Centrifugo supports several options: admin_handler_prefix (default \"\") - to control Admin panel URL prefix websocket_handler_prefix (default \"/connection/websocket\") - to control WebSocket URL prefix http_stream_handler_prefix (default \"/connection/http_stream\") - to control HTTP-streaming URL prefix sse_handler_prefix (default \"/connection/sse\") - to control SSE/EventSource URL prefix emulation_handler_prefix (default \"/emulation\") - to control emulation endpoint prefix sockjs_handler_prefix (default \"/connection/sockjs\") - to control SockJS URL prefix uni_sse_handler_prefix (default \"/connection/uni_sse\") - to control unidirectional Eventsource URL prefix uni_http_stream_handler_prefix (default \"/connection/uni_http_stream\") - to control unidirectional HTTP streaming URL prefix uni_websocket_handler_prefix (default \"/connection/uni_websocket\") - to control unidirectional WebSocket URL prefix api_handler_prefix (default \"/api\") - to control HTTP API URL prefix prometheus_handler_prefix (default \"/metrics\") - to control Prometheus URL prefix health_handler_prefix (default \"/health\") - to control health check URL prefix","s":"Customize handler endpoints","u":"/docs/server/configuration","h":"#customize-handler-endpoints","p":3173},{"i":3246,"t":"It's possible to send HUP signal to Centrifugo to reload a configuration: kill -HUP Though at moment this will only reload token secrets and channel options (top-level and namespaces). Centrifugo tries to gracefully shut down client connections when SIGINT or SIGTERM signals are received. By default, the maximum graceful shutdown period is 30 seconds but can be changed using shutdown_timeout (integer, in seconds) configuration option.","s":"Signal handling","u":"/docs/server/configuration","h":"#signal-handling","p":3173},{"i":3249,"t":"The boolean option client_insecure (default false) allows connecting to Centrifugo without JWT token. In this mode, there is no user authentication involved. It also disables permission checks on client API level - for presence and history calls. This mode can be useful for demo projects based on Centrifugo, integration tests, local projects, or real-time application prototyping. Don't use it in production until you 100% know what you are doing.","s":"Insecure client connection","u":"/docs/server/configuration","h":"#insecure-client-connection","p":3173},{"i":3251,"t":"Available since Centrifugo v5.0.4 The boolean option client_insecure_skip_token_signature_verify (default false), if enabled – tells Centrifugo to skip JWT signature verification - for both connection and subscription tokens. This is absolutely insecure and must only be used for development and testing purposes. Token claims are parsed as usual - so token should still follow JWT format.","s":"Disable client token signature check","u":"/docs/server/configuration","h":"#disable-client-token-signature-check","p":3173},{"i":3253,"t":"This mode can be enabled using the boolean option api_insecure (default false). When on there is no need to provide API key in HTTP requests. When using this mode everyone that has access to /api endpoint can send any command to server. Enabling this option can be reasonable if /api endpoint is protected by firewall rules. The option is also useful in development to simplify sending API commands to Centrifugo using CURL for example without specifying Authorization header in requests.","s":"Insecure API mode","u":"/docs/server/configuration","h":"#insecure-api-mode","p":3173},{"i":3255,"t":"This mode can be enabled using the boolean option admin_insecure (default false). When on there is no authentication in the admin web interface. Again - this is not secure but can be justified if you protected the admin interface by firewall rules or you want to use basic authentication for the Centrifugo admin interface (configured on proxy level).","s":"Insecure admin mode","u":"/docs/server/configuration","h":"#insecure-admin-mode","p":3173},{"i":3257,"t":"Time durations in Centrifugo can be set using strings where duration value and unit are both provided. For example, to set 5 seconds duration use \"5s\". The minimal time resolution is 1ms. Some options of Centrifugo only support second precision (for example history_ttl channel option). Valid time units are \"ms\" (milliseconds), \"s\" (seconds), \"m\" (minutes), \"h\" (hours). Some examples: \"1000ms\" // 1000 milliseconds\"1s\" // 1 second\"12h\" // 12 hours\"720h\" // 30 days","s":"Setting time duration options","u":"/docs/server/configuration","h":"#setting-time-duration-options","p":3173},{"i":3259,"t":"While setting most options in Centrifugo over env is pretty straightforward setting namespaces is a bit special: CENTRIFUGO_NAMESPACES='[{\"name\": \"ns1\"}, {\"name\": \"ns2\"}]' ./centrifugo I.e. CENTRIFUGO_NAMESPACES environment variable should be a valid JSON string that represents namespaces array.","s":"Setting namespaces over env","u":"/docs/server/configuration","h":"#setting-namespaces-over-env","p":3173},{"i":3261,"t":"Centrifugo periodically sends anonymous usage information (once in 24 hours). That information is impersonal and does not include sensitive data, passwords, IP addresses, hostnames, etc. Only counters to estimate version and installation size distribution, and feature usage. Please do not disable usage stats sending without reason. If you depend on Centrifugo – sure you are interested in further project improvements. Usage stats help us understand Centrifugo use cases better, concentrate on widely-used features, and be confident we are moving in the right direction. Developing in the dark is hard, and decisions may be non-optimal. To disable sending usage stats set usage_stats_disable option: config.json { \"usage_stats_disable\": true}","s":"Anonymous usage stats","u":"/docs/server/configuration","h":"#anonymous-usage-stats","p":3173},{"i":3263,"t":"On this page","s":"Engines and scalability","u":"/docs/server/engines","h":"","p":3262},{"i":3265,"t":"Used by default. Supports only one node. Nice choice to start with. Supports all features keeping everything in Centrifugo node process memory. You don't need to install Redis when using this engine. Advantages: Super fast since it does not involve network at all Does not require separate broker setup Disadvantages: Does not allow scaling nodes (actually you still can scale Centrifugo with Memory engine but you have to publish data into each Centrifugo node and you won't have consistent history and presence state throughout Centrifugo nodes) Does not persist message history in channels between Centrifugo restarts.","s":"Memory engine","u":"/docs/server/engines","h":"#memory-engine","p":3262},{"i":3267,"t":"history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. When using a history in a channel, Centrifugo keeps some metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help to expire metadata faster (or slower if you need it). The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Memory engine options","u":"/docs/server/engines","h":"#memory-engine-options","p":3262},{"i":3269,"t":"Redis is an open-source, in-memory data structure store, used as a database, cache, and message broker. Centrifugo Redis engine allows scaling Centrifugo nodes to different machines. Nodes will use Redis as a message broker (utilizing Redis PUB/SUB for node communication) and keep presence and history data in Redis. Minimal Redis version is 5.0.1 With Redis it's possible to come to the architecture like this:","s":"Redis engine","u":"/docs/server/engines","h":"#redis-engine","p":3262},{"i":3271,"t":"Several configuration options related to Redis engine. redis_address​ String, default \"127.0.0.1:6379\" - Redis server address. redis_password​ String, default \"\" - Redis password. redis_user​ String, default \"\" - Redis user for ACL-based auth. redis_db​ Integer, default 0 - number of Redis db to use. redis_prefix​ String, default \"centrifugo\" – custom prefix to use for channels and keys in Redis. redis_use_lists​ Boolean, default false – turns on using Redis Lists instead of Stream data structure for keeping history (not recommended, keeping this for backwards compatibility mostly). redis_force_resp2​ Boolean, default false. If set to true it forces using RESP2 protocol for communicating with Redis. By default, Redis client used by Centrifugo tries to detect supported Redis protocol automatically trying RESP3 first. history_meta_ttl​ Duration, default 2160h (90 days). history_meta_ttl sets a time of history stream metadata expiration. Similar to a Memory engine Redis engine also looks at history_meta_ttl option. Meta key in Redis is a HASH that contains the current offset number in channel and the stream epoch value. When using a history in a channel, Centrifugo saves metadata for it. Metadata includes the latest stream offset and its epoch value. In some cases, when channels are created for а short time and then not used anymore, created metadata can stay in memory while not useful. For example, you can have a personal user channel but after using your app for a while user left it forever. From a long-term perspective, this can be an unwanted memory growth. Setting a reasonable value to this option can help. The rule of thumb here is to keep this value much bigger than maximum history TTL used in Centrifugo configuration.","s":"Redis engine options","u":"/docs/server/engines","h":"#redis-engine-options","p":3262},{"i":3273,"t":"Some options may help you configuring TLS-protected communication between Centrifugo and Redis. redis_tls​ Boolean, default false - enable Redis TLS connection. redis_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_tls_skip_verify – but it will be removed in v5. redis_tls_cert​ String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_tls_cert_pem option. redis_tls_key​ String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_tls_key_pem option. redis_tls_root_ca​ String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_tls_root_ca_pem option. redis_tls_server_name​ String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Configuring Redis TLS","u":"/docs/server/engines","h":"#configuring-redis-tls","p":3262},{"i":3275,"t":"Let's see how to start several Centrifugo nodes using the Redis Engine. We will start 3 Centrifugo nodes and all those nodes will be connected via Redis. First, you should have Redis running. As soon as it's running - we can launch 3 Centrifugo instances. Open your terminal and start the first one: centrifugo --config=config.json --port=8000 --engine=redis --redis_address=127.0.0.1:6379 If your Redis is on the same machine and runs on its default port you can omit redis_address option in the command above. Then open another terminal and start another Centrifugo instance: centrifugo --config=config.json --port=8001 --engine=redis --redis_address=127.0.0.1:6379 Note that we use another port number (8001) as port 8000 is already busy by our first Centrifugo instance. If you are starting Centrifugo instances on different machines then you most probably can use the same port number (8000 or whatever you want) for all instances. And finally, let's start the third instance: centrifugo --config=config.json --port=8002 --engine=redis --redis_address=127.0.0.1:6379 Now you have 3 Centrifugo instances running on ports 8000, 8001, 8002 and clients can connect to any of them. You can also send API requests to any of those nodes – as all nodes connected over Redis PUB/SUB message will be delivered to all interested clients on all nodes. To load balance clients between nodes you can use Nginx – you can find its configuration here in the documentation. tip In the production environment you will most probably run Centrifugo nodes on different hosts, so there will be no need to use different port numbers. Here is a live example where we locally start two Centrifugo nodes both connected to local Redis: Sorry, your browser doesn't support embedded video.","s":"Scaling with Redis tutorial","u":"/docs/server/engines","h":"#scaling-with-redis-tutorial","p":3262},{"i":3277,"t":"Centrifugo supports the official way to add high availability to Redis - Redis Sentinel. For this you only need to utilize 2 Redis Engine options: redis_sentinel_address and redis_sentinel_master_name: redis_sentinel_address (string, default \"\") - comma separated list of Sentinel addresses for HA. At least one known server required. redis_sentinel_master_name (string, default \"\") - name of Redis master Sentinel monitors Also: redis_sentinel_password – optional string password for your Sentinel, works with Redis Sentinel >= 5.0.1 redis_sentinel_user - optional string user (used only in Redis ACL-based auth). So you can start Centrifugo which will use Sentinels to discover Redis master instances like this: centrifugo --config=config.json Where config.json: config.json { ... \"engine\": \"redis\", \"redis_sentinel_address\": \"127.0.0.1:26379\", \"redis_sentinel_master_name\": \"mymaster\"} Sentinel configuration file may look like this (for 3-node Sentinel setup with quorum 2): port 26379sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 10000sentinel failover-timeout mymaster 60000 You can find how to properly set up Sentinels in official documentation. Note that when your Redis master instance is down there will be a small downtime interval until Sentinels discover a problem and come to a quorum decision about a new master. The length of this period depends on Sentinel configuration.","s":"Redis Sentinel for high availability","u":"/docs/server/engines","h":"#redis-sentinel-for-high-availability","p":3262},{"i":3279,"t":"To configure TLS for Redis Sentinel use the following options. redis_sentinel_tls​ Boolean, default false - enable Redis TLS connection. redis_sentinel_tls_insecure_skip_verify​ Boolean, default false - disable Redis TLS host verification. Centrifugo v4 also supports alias for this option – redis_sentinel_tls_skip_verify – but it will be removed in v5. redis_sentinel_tls_cert​ String, default \"\" – path to TLS cert file. If you prefer passing certificate as a string instead of path to the file then use redis_sentinel_tls_cert_pem option. redis_sentinel_tls_key​ String, default \"\" – path to TLS key file. If you prefer passing cert key as a string instead of path to the file then use redis_sentinel_tls_key_pem option. redis_sentinel_tls_root_ca​ String, default \"\" – path to TLS root CA file (in PEM format) to use. If you prefer passing root CA PEM as a string instead of path to the file then use redis_sentinel_tls_root_ca_pem option. redis_sentinel_tls_server_name​ String, default \"\" – used to verify the hostname on the returned certificates. It is also included in the client's handshake to support virtual hosting unless it is an IP address.","s":"Redis Sentinel TLS","u":"/docs/server/engines","h":"#redis-sentinel-tls","p":3262},{"i":3281,"t":"Alternatively, you can use Haproxy between Centrifugo and Redis to let it properly balance traffic to Redis master. In this case, you still need to configure Sentinels but you can omit Sentinel specifics from Centrifugo configuration and just use Redis address as in a simple non-HA case. For example, you can use something like this in Haproxy config: listen redis server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup bind *:16379 mode tcp option tcpka option tcplog option tcp-check tcp-check send PING\\r\\n tcp-check expect string +PONG tcp-check send info\\ replication\\r\\n tcp-check expect string role:master tcp-check send QUIT\\r\\n tcp-check expect string +OK balance roundrobin And then just point Centrifugo to this Haproxy: centrifugo --config=config.json --engine=redis --redis_address=\"localhost:16379\"","s":"Haproxy instead of Sentinel configuration","u":"/docs/server/engines","h":"#haproxy-instead-of-sentinel-configuration","p":3262},{"i":3283,"t":"Centrifugo has built-in Redis sharding support. This resolves the situation when Redis becoming a bottleneck on a large Centrifugo setup. Redis is a single-threaded server, it's very fast but its power is not infinite so when your Redis approaches 100% CPU usage then the sharding feature can help your application to scale. At moment Centrifugo supports a simple comma-based approach to configuring Redis shards. Let's just look at examples. To start Centrifugo with 2 Redis shards on localhost running on port 6379 and port 6380 use config like this: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"127.0.0.1:6379\", \"127.0.0.1:6380\", ]} To start Centrifugo with Redis instances on different hosts: config.json { ... \"engine\": \"redis\", \"redis_address\": [ \"192.168.1.34:6379\", \"192.168.1.35:6379\", ]} If you also need to customize AUTH password, Redis DB number then you can use an extended address notation. note Due to how Redis PUB/SUB works it's not possible (and it's pretty useless anyway) to run shards in one Redis instance using different Redis DB numbers. When sharding enabled Centrifugo will spread channels and history/presence keys over configured Redis instances using a consistent hashing algorithm. At moment we use Jump consistent hash algorithm (see paper and implementation).","s":"Redis sharding","u":"/docs/server/engines","h":"#redis-sharding","p":3262},{"i":3285,"t":"Running Centrifugo with Redis cluster is simple and can be achieved using redis_cluster_address option. This is an array of strings. Each element of the array is a comma-separated Redis cluster seed node. For example: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\" ]} You don't need to list all Redis cluster nodes in config – only several working nodes are enough to start. To set the same over environment variable: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001\" CENTRIFUGO_ENGINE=redis ./centrifugo If you need to shard data between several Redis clusters then simply add one more string with seed nodes of another cluster to this array: { ... \"redis_cluster_address\": [ \"localhost:30001,localhost:30002,localhost:30003\", \"localhost:30101,localhost:30102,localhost:30103\" ]} Sharding between different Redis clusters can make sense due to the fact how PUB/SUB works in the Redis cluster. It does not scale linearly when adding nodes as all PUB/SUB messages got copied to every cluster node. See this discussion for more information on topic. To spread data between different Redis clusters Centrifugo uses the same consistent hashing algorithm described above (i.e. Jump). To reproduce the same over environment variable use space to separate different clusters: CENTRIFUGO_REDIS_CLUSTER_ADDRESS=\"localhost:30001,localhost:30002 localhost:30101,localhost:30102\" CENTRIFUGO_ENGINE=redis ./centrifugo","s":"Redis cluster","u":"/docs/server/engines","h":"#redis-cluster","p":3262},{"i":3287,"t":"When using Redis engine it's possible to point Centrifugo not only to Redis itself, but also to the other Redis compatible server. Such servers may work just fine if implement Redis protocol and support all the data structures Centrifugo uses and have PUB/SUB implemented. Some known options: AWS Elasticache – it was reported to work, but we suggest you testing the setup including failover tests and work under load. KeyDB – should work fine with Centrifugo, no known problems at this point regarding Centrifugo compatibility. DragonflyDB - should work fine starting from DragonflyDB 1.3.0 and with redis_force_resp2 Centrifugo option on. We have not tested a Redis Cluster emulation mode provided by DragonflyDB yet. We suggest you testing the setup including failover tests and work under load.","s":"Other Redis compatible","u":"/docs/server/engines","h":"#other-redis-compatible","p":3262},{"i":3289,"t":"EXPERIMENTAL Tarantool is a fast and flexible in-memory storage with different persistence/replication schemes and LuaJIT for writing custom logic on the Tarantool side. It allows implementing Centrifugo engine with unique characteristics. caution EXPERIMENTAL status of Tarantool integration means that we are still going to improve it and there could be breaking changes as integration evolves. There are many ways to operate Tarantool in production and it's hard to distribute Centrifugo Tarantool engine in a way that suits everyone. Centrifugo tries to fit generic case by providing centrifugal/tarantool-centrifuge module and by providing ready-to-use centrifugal/rotor project based on centrifugal/tarantool-centrifuge and Tarantool Cartridge. info To be honest we bet on the community help to push this integration further. Tarantool provides an incredible performance boost for presence and history operations (up to 5x more RPS compared to the Redis Engine) and a pretty fast PUB/SUB (comparable to what Redis Engine provides). Let's see what we can build together. There are several supported Tarantool topologies to which Centrifugo can connect: One standalone Tarantool instance Many standalone Tarantool instances and consistently shard data between them Tarantool running in Cartridge Tarantool with replica and automatic failover in Cartridge Many Tarantool instances (or leader-follower setup) in Cartridge with consistent client-side sharding between them Tarantool with synchronous replication (Raft-based, Tarantool >= 2.7) After running Tarantool you can point Centrifugo to it (and of course scale Centrifugo nodes): config.json { ... \"engine\": \"tarantool\", \"tarantool_address\": \"127.0.0.1:3301\"} See centrifugal/rotor repo for ready-to-use engine based on Tarantool Cartridge framework. See centrifugal/tarantool-centrifuge repo for examples on how to run engine with Standalone single Tarantool instance or with Raft-based synchronous replication.","s":"Tarantool engine","u":"/docs/server/engines","h":"#tarantool-engine","p":3262},{"i":3291,"t":"tarantool_address​ String or array of strings. Default tcp://127.0.0.1:3301. Connection address to Tarantool. tarantool_mode​ String, default standalone A mode how to connect to Tarantool. Default is standalone which connects to a single Tarantool instance address. Possible values are: leader-follower (connects to a setup with Tarantool master and async replicas) and leader-follower-raft (connects to a Tarantool with synchronous Raft-based replication). All modes support client-side consistent sharding (similar to what Redis engine provides). tarantool_user​ String, default \"\". Allows setting a user. tarantool_password​ String, default \"\". Allows setting a password. history_meta_ttl​ Duration, default 2160h. Same option as for Memory engine and Redis engine also applies to Tarantool case.","s":"Tarantool engine options","u":"/docs/server/engines","h":"#tarantool-engine-options","p":3262},{"i":3293,"t":"It's possible to scale with Nats PUB/SUB server. Keep in mind, that Nats is called a broker here, not an Engine – Nats integration only implements PUB/SUB part of Engine, so carefully read limitations below. Limitations: Nats integration works only for unreliable at most once PUB/SUB. This means that history, presence, and message recovery Centrifugo features won't be available. Nats wildcard channel subscriptions with symbols * and > not supported. First start Nats server: $ nats-server[3569] 2020/07/08 20:28:44.324269 [INF] Starting nats-server version 2.1.7[3569] 2020/07/08 20:28:44.324400 [INF] Git commit [not set][3569] 2020/07/08 20:28:44.325600 [INF] Listening for client connections on 0.0.0.0:4222[3569] 2020/07/08 20:28:44.325612 [INF] Server id is NDAM7GEHUXAKS5SGMA3QE6ZSO4IQUJP6EL3G2E2LJYREVMAMIOBE7JT4[3569] 2020/07/08 20:28:44.325617 [INF] Server is ready Then start Centrifugo with broker option: centrifugo --broker=nats --config=config.json And one more Centrifugo on another port (of course in real life you will start another Centrifugo on another machine): centrifugo --broker=nats --config=config.json --port=8001 Now you can scale connections over Centrifugo instances, instances will be connected over Nats server.","s":"Nats broker","u":"/docs/server/engines","h":"#nats-broker","p":3262},{"i":3295,"t":"nats_url​ String, default nats://127.0.0.1:4222. Connection url in format nats://derek:pass@localhost:4222. nats_prefix​ String, default centrifugo. Prefix for channels used by Centrifugo inside Nats. nats_dial_timeout​ Duration, default 1s. Timeout for dialing with Nats. nats_write_timeout​ Duration, default 1s. Write (and flush) timeout for a connection to Nats.","s":"Options","u":"/docs/server/engines","h":"#options","p":3262},{"i":3297,"t":"On this page","s":"Metrics monitoring","u":"/docs/server/monitoring","h":"","p":3296},{"i":3299,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus","u":"/docs/server/monitoring","h":"#prometheus","p":3296},{"i":3301,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite","u":"/docs/server/monitoring","h":"#graphite","p":3296},{"i":3303,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/server/monitoring","h":"#grafana-dashboard","p":3296},{"i":3305,"t":"On this page","s":"Load balancing","u":"/docs/server/load_balancing","h":"","p":3304},{"i":3307,"t":"Although it's possible to use Centrifugo without any reverse proxy before it, it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with edge cases when handling HTTP/Websocket connections from the wild. Also you probably want some sort of load balancing eventually between Centrifugo nodes so that proxy can be such a balancer too. In this section we will look at Nginx configuration to deploy Centrifugo. Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections. There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).","s":"Nginx configuration","u":"/docs/server/load_balancing","h":"#nginx-configuration","p":3304},{"i":3309,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}#server {# listen 80;# server_name centrifugo.example.com;# rewrite ^(.*) https://$server_name$1 permanent;#}server { server_name centrifugo.example.com; listen 80; #listen 443 ssl; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_certificate /etc/nginx/ssl/server.crt; #ssl_certificate_key /etc/nginx/ssl/server.key; include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_min_length 1000; gzip_proxied any; # Only retry if there was a communication error, not a timeout # on the Centrifugo server (to avoid propagating \"queries of death\" # to all frontends) proxy_next_upstream error; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; location /connection { proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location / { proxy_pass http://centrifugo; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }}","s":"Separate domain for Centrifugo","u":"/docs/server/load_balancing","h":"#separate-domain-for-centrifugo","p":3304},{"i":3311,"t":"upstream centrifugo { # uncomment ip_hash if using SockJS transport with many upstream servers. #ip_hash; server 127.0.0.1:8000;}map $http_upgrade $connection_upgrade { default upgrade; '' close;}server { # ... your web site Nginx config location /centrifugo/ { rewrite ^/centrifugo/(.*) /$1 break; proxy_pass http://centrifugo; proxy_pass_header Server; proxy_set_header Host $http_host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } location /centrifugo/connection { rewrite ^/centrifugo(.*) $1 break; proxy_pass http://centrifugo; proxy_buffering off; keepalive_timeout 65; proxy_read_timeout 60s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header Host $http_host; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}","s":"Embed to a location of web site","u":"/docs/server/load_balancing","h":"#embed-to-a-location-of-web-site","p":3304},{"i":3313,"t":"You may also need to update worker_connections option of Nginx: events { worker_connections 65535;}","s":"worker_connections","u":"/docs/server/load_balancing","h":"#worker_connections","p":3304},{"i":3315,"t":"On this page","s":"Server observability","u":"/docs/server/observability","h":"","p":3314},{"i":3318,"t":"To enable Prometheus endpoint start Centrifugo with prometheus option on: config.json { ... \"prometheus\": true} This will enable /metrics endpoint so the Centrifugo instance can be monitored by your Prometheus server.","s":"Prometheus metrics","u":"/docs/server/observability","h":"#prometheus-metrics","p":3314},{"i":3320,"t":"To enable automatic export to Graphite (via TCP): config.json { ... \"graphite\": true, \"graphite_host\": \"localhost\", \"graphite_port\": 2003} By default, stats will be aggregated over 10 seconds intervals inside Centrifugo and then pushed to Graphite over TCP connection. If you need to change this aggregation interval use the graphite_interval option (in seconds, default 10).","s":"Graphite metrics","u":"/docs/server/observability","h":"#graphite-metrics","p":3314},{"i":3322,"t":"Check out Centrifugo official Grafana dashboard for Prometheus storage. You can import that dashboard to your Grafana, point to Prometheus storage – and enjoy visualized metrics.","s":"Grafana dashboard","u":"/docs/server/observability","h":"#grafana-dashboard","p":3314},{"i":3324,"t":"Here is a description of various metrics exposed by Centrifugo. centrifugo_node_messages_sent_count​ Type: Counter Labels: type Description: Tracks the number of messages sent by a node to the broker. Usage: Use this metric to monitor the outgoing message rate and detect any anomalies or spikes in the data flow. centrifugo_node_messages_received_count​ Type: Counter Labels: type Description: Measures the number of messages received from the broker. Usage: Helps in understanding the incoming message rate and ensures the node is receiving data as expected. centrifugo_node_action_count​ Type: Counter Labels: action Description: Counts the number of various actions called within the node. Usage: Useful for tracking specific actions' usage and frequency. centrifugo_node_num_clients​ Type: Gauge Description: Shows the current number of clients connected to the node. Usage: Monitor the client connections to ensure the node is not reaching its capacity. centrifugo_node_num_users​ Type: Gauge Description: Displays the number of unique users connected to the node. Usage: Helps in understanding user engagement and capacity planning. centrifugo_node_num_subscriptions​ Type: Gauge Description: Indicates the number of active subscriptions. Usage: Use this to monitor the subscription levels and identify any potential issues or required optimizations. centrifugo_node_num_nodes​ Type: Gauge Description: Shows the total number of nodes in the cluster. Usage: Essential for monitoring the size of the cluster and ensuring that all nodes are operational. centrifugo_node_build_info​ Type: Gauge Labels: version Description: Provides build information of the node. Usage: Helps in tracking the version of the application running across different environments. centrifugo_node_num_channels​ Type: Gauge Description: Counts the number of channels with one or more subscribers. Usage: Useful for monitoring the activity and utilization of channels. centrifugo_node_survey_duration_seconds​ Type: Summary Labels: op Description: Captures the duration of surveys conducted by the node. Usage: Helps in performance monitoring and identifying any delays or issues in survey operations. centrifugo_client_num_reply_errors​ Type: Counter Labels: method, code Description: Counts the number of errors in replies sent to clients. Usage: Critical for error monitoring and ensuring smooth client interactions. centrifugo_client_num_server_disconnects​ Type: Counter Labels: code Description: Tracks the number of server-initiated disconnects. Usage: Use this to monitor the health of client connections and identify potential issues with the server. centrifugo_client_command_duration_seconds​ Type: Summary Labels: method Description: Measures the duration of commands executed by clients. Usage: Essential for performance monitoring and ensuring timely responses to client commands. centrifugo_client_recover​ Type: Counter Labels: recovered Description: Counts the number of recover operations performed. Usage: Helps in tracking the system's resilience and recovery mechanisms. centrifugo_client_connection_limit_reached_total​ Type: Counter Labels: None Description: Number of refused connections due to the node client connection limit. Usage: Useful for monitoring the load on the Centrifugo node and identifying when clients are being refused connections due to reaching the connection limit. centrifugo_transport_connect_count​ Type: Counter Labels: transport Description: Measures the number of connections to specific transports. Usage: Use this to monitor the usage of different transports and ensure they are functioning as expected. centrifugo_transport_messages_sent​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Tracks the number of messages sent to client connections over specific transports. Usage: Essential for understanding the data flow and performance of different transports. centrifugo_transport_messages_sent_size​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Measures the size of messages sent to client connections over specific transports. Usage: Helps in monitoring the network bandwidth usage and optimizing the data transfer. centrifugo_transport_messages_received​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Counts the number of messages received from client connections over specific transports. Usage: Important for ensuring that messages are being successfully received and processed. centrifugo_transport_messages_received_size​ Type: Counter Labels: transport, frame_type, channel_namespace Description: Measures the size of messages received from client connections over specific transports. Usage: Use this metric to monitor the incoming data size and optimize the application's performance. centrifugo_proxy_duration_seconds​ Type: Summary & Histogram Labels: protocol, type Description: Captures the duration of proxy calls. Usage: Critical for understanding the performance of proxy calls and identifying any potential bottlenecks or issues. centrifugo_proxy_errors​ Type: Counter Labels: protocol, type Description: Counts the number of errors occurred during proxy calls. Usage: Helps in monitoring the reliability of proxy services and ensuring error-free operations. centrifugo_granular_proxy_duration_seconds​ Type: Summary & Histogram Labels: type, name Description: Measures the duration of granular proxy calls. Usage: Use this to get more detailed insights into the performance of granular proxy operations. centrifugo_granular_proxy_errors​ Type: Counter Labels: type, name Description: Counts the number of errors in granular proxy calls. Usage: Essential for error tracking and ensuring the stability of granular proxy services. centrifugo_api_command_duration_seconds​ Type: Summary Labels: protocol, method Description: Tracks the duration of API commands. Usage: Helps in monitoring the API performance and ensuring timely responses. centrifugo_api_command_duration_seconds_histogram​ Type: Histogram Labels: protocol, method Description: Tracks the duration of API commands. Usage: Helps in monitoring the API performance and ensuring timely responses.","s":"Exposed metrics","u":"/docs/server/observability","h":"#exposed-metrics","p":3314},{"i":3327,"t":"At this point Centrifugo can export traces for HTTP and GRPC server API requests in OpenTelemetry format. To enable: { ... \"opentelemetry\": true, \"opentelemetry_api\": true} OpenTelemetry must be explicitly turned on to avoid tracing overhead when it's not needed. To configure OpenTelemetry export behaviour we are relying on OpenTelemetry environment vars supporting only HTTP export endpoints for now. So a simple example to run Centrifugo with server API tracing would be running Jaeger with COLLECTOR_OTLP_ENABLED: docker run --rm -it --name jaeger \\ -e COLLECTOR_OTLP_ENABLED=true \\ -p 16686:16686 \\ -p 4318:4318 \\ jaegertracing/all-in-one:latest Then start Centrifugo: OTEL_EXPORTER_OTLP_ENDPOINT=\"http://localhost:4318\" CENTRIFUGO_OPENTELEMETRY=1 CENTRIFUGO_OPENTELEMETRY_API=1 ./centrifugo Send some API requests - and open http://localhost:16686 to see traces in Jaeger UI. By default, Centrifugo exports traces in http/protobuf format. If you want to use GRPC exporter then it's possible to turn it on by setting environment variable OTEL_EXPORTER_OTLP_PROTOCOL to grpc (GRPC exporter format supported since Centrifugo v5.0.3).","s":"OpenTelemetry","u":"/docs/server/observability","h":"#opentelemetry","p":3314},{"i":3329,"t":"Logging may be configured using log_level option. It may have the following values: none trace debug info (default) warn error We generally do not recommend anything below info to be used in production. By default Centrifugo logs to STDOUT. Usually this is what you need when running servers on modern infrastructures. Logging into file may be configured using log_file option.","s":"Logs","u":"/docs/server/observability","h":"#logs","p":3314},{"i":3331,"t":"On this page","s":"Server-side subscriptions","u":"/docs/server/server_subs","h":"","p":3330},{"i":3333,"t":"See subscribe and unsubscribe server API","s":"Dynamic server-side subscriptions","u":"/docs/server/server_subs","h":"#dynamic-server-side-subscriptions","p":3330},{"i":3335,"t":"It's possible to automatically subscribe a user to a personal server-side channel. To enable this you need to enable the user_subscribe_to_personal boolean option (by default false). As soon as you do this every connection with a non-empty user ID will be automatically subscribed to a personal user-limited channel. Anonymous users with empty user IDs won't be subscribed to any channel. For example, if you set this option and the user with ID 87334 connects to Centrifugo it will be automatically subscribed to channel #87334 and you can process personal publications on the client-side in the same way as shown above. As you can see by default generated personal channel name belongs to the default namespace (i.e. no explicit namespace used). To set custom namespace name use user_personal_channel_namespace option (string, default \"\") – i.e. the name of namespace from configured configuration namespaces array. In this case, if you set user_personal_channel_namespace to personal for example – then the automatically generated personal channel will be personal:#87334 – user will be automatically subscribed to it on connect and you can use this channel name to publish personal notifications to the online user.","s":"Automatic personal channel subscription","u":"/docs/server/server_subs","h":"#automatic-personal-channel-subscription","p":3330},{"i":3337,"t":"Usage of personal channel subscription also opens a road to enable one more feature: maintaining only a single connection for each user globally around all Centrifugo nodes. user_personal_single_connection boolean option (default false) turns on a mode in which Centrifugo will try to maintain only a single connection for each user at the same moment. As soon as the user establishes a connection other connections from the same user will be closed with connection limit reason (client won't try to automatically reconnect). This feature works with a help of presence information inside a personal channel. So presence should be turned on in a personal channel. Example config: config.json { \"user_subscribe_to_personal\": true, \"user_personal_single_connection\": true, \"user_personal_channel_namespace\": \"personal\", \"namespaces\": [ { \"name\": \"personal\", \"presence\": true } ]} note Centrifugo can't guarantee that other user connections will be closed – since Disconnect messages are distributed around Centrifugo nodes with at most once guarantee. So don't add critical business logic based on this feature to your application. Though this should work just fine most of the time if the connection between the Centrifugo node and PUB/SUB broker is OK.","s":"Maintain single user connection","u":"/docs/server/server_subs","h":"#maintain-single-user-connection","p":3330},{"i":3339,"t":"On this page","s":"Online presence","u":"/docs/server/presence","h":"","p":3338},{"i":3341,"t":"To enable online presence, you need to set the presence option to true for the specific channel namespace in your Centrifugo configuration. { \"namespaces\": [{ \"namespace\": \"public\", \"presence\": true }]} After enabling this you can query presence information over server HTTP/GRPC presence call: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: YOUR_API_KEY\" \\ --request POST \\ --data '{\"channel\": \"public:test\"}' \\ http://localhost:8000/api/presence See description of presence API. Also, a shorter version of presence which only contains two counters - number of clients and number of unique users in channel - may be called: curl --header \"Content-Type: application/json\" \\ --header \"X-API-Key: YOUR_API_KEY\" \\ --request POST \\ --data '{\"channel\": \"public:test\"}' \\ http://localhost:8000/api/presence_stats See description of presence stats API.","s":"Enabling online presence","u":"/docs/server/presence","h":"#enabling-online-presence","p":3338},{"i":3343,"t":"Once presence enabled, you can retrieve the presence information on the client side too by calling the presence method on the channel. To do this you need to give the client permission to call presence. Once done, presence may be retrieved from the subscription: const resp = await subscription.presence(channel); It's also available on the top-level of the client (for example, if you need to call presence for server-side subscription): const resp = await client.presence(channel); If the permission check has passed successfully – both methods will return an object containing information about currently subscribed clients. Also, presenceStats method is avalable: const resp = await subscription.presenceStats(channel);","s":"Retrieving presence on the client side","u":"/docs/server/presence","h":"#retrieving-presence-on-the-client-side","p":3338},{"i":3345,"t":"It's also possible to enable real-time tracking of users joining or leaving a channel by listening to join and leave events on the client side. By default, Centrifugo does not send these events and they must be explicitly turned on for channel namespace: { \"namespaces\": [{ \"namespace\": \"public\", \"presence\": true, \"join_leave\": true, \"force_push_join_leave\": true }]} Then on the client side: subscription.on('join', function(joinCtx) { console.log('client joined:', joinCtx);});subscription.on('leave', function(leaveCtx) { console.log('client left:', leaveCtx);}); And the same on client top-level for the needs of server-side subscriptions (analogous to the presence call described above). These events provide real-time updates and can be used to keep track of user activity and manage live interactions. You can combine join/leave events with presence information and maintain a list of currently active subscribers - for example show the list of online players in the game room updated in real-time.","s":"Join and leave events","u":"/docs/server/presence","h":"#join-and-leave-events","p":3338},{"i":3347,"t":"The online presence feature might increase the load on your Centrifugo server, since Centrifugo need to maintain an addition data structure. Therefore, it is recommended to use this feature judiciously based on your server's capability and the necessity of real-time presence data in your application. Always make sure to secure the presence data, as it could expose sensitive information about user activity in your application. Ensure appropriate security measures are in place. Join and leave events delivered with at most once guarantee. See more about presence design in design overview chapter. Also check out FAQ which mentions scalability concerns for presence data and join/leave events.","s":"Implementation notes","u":"/docs/server/presence","h":"#implementation-notes","p":3338},{"i":3349,"t":"The online presence feature of Centrifugo is a highly useful tool for real-time applications. It provides instant and live data about user activity, which can be critical for interactive features in chats, collaborative tools, multiplayer games, or live tracking systems. Make sure to configure and use this feature appropriately to get the most out of your real-time application.","s":"Conclusion","u":"/docs/server/presence","h":"#conclusion","p":3338},{"i":3351,"t":"On this page","s":"Configure TLS","u":"/docs/server/tls","h":"","p":3350},{"i":3353,"t":"In first way you already have cert and key files. For development you can create self-signed certificate - see this instruction as example. config.json { ... \"tls\": true, \"tls_key\": \"server.key\", \"tls_cert\": \"server.crt\"} And run: ./centrifugo --config=config.json","s":"Using crt and key files","u":"/docs/server/tls","h":"#using-crt-and-key-files","p":3350},{"i":3355,"t":"For automatic certificates from Let's Encrypt add into configuration file: config.json { ... \"tls_autocert\": true, \"tls_autocert_host_whitelist\": \"www.example.com\", \"tls_autocert_cache_dir\": \"/tmp/certs\", \"tls_autocert_email\": \"user@example.com\", \"tls_autocert_http\": true, \"tls_autocert_http_addr\": \":80\"} tls_autocert (boolean) says Centrifugo that you want automatic certificate handling using ACME provider. tls_autocert_host_whitelist (string) is a string with your app domain address. This can be comma-separated list. It's optional but recommended for extra security. tls_autocert_cache_dir (string) is a path to a folder to cache issued certificate files. This is optional but will increase performance. tls_autocert_email (string) is optional - it's an email address ACME provider will send notifications about problems with your certificates. tls_autocert_http (boolean) is an option to handle http_01 ACME challenge on non-TLS port. tls_autocert_http_addr (string) can be used to set address for handling http_01 ACME challenge (default is :80) When configured correctly and your domain is valid (localhost will not work) - certificates will be retrieved on first request to Centrifugo. Also Let's Encrypt certificates will be automatically renewed. There are two options that allow Centrifugo to support TLS client connections from older browsers such as Chrome 49 on Windows XP and IE8 on XP: tls_autocert_force_rsa - this is a boolean option, by default false. When enabled it forces autocert manager generate certificates with 2048-bit RSA keys. tls_autocert_server_name - string option, allows to set server name for client handshake hello. This can be useful to deal with old browsers without SNI support - see comment grpc_api_tls_disable boolean flag allows to disable TLS for GRPC API server but keep it on for HTTP endpoints. uni_grpc_tls_disable boolean flag allows to disable TLS for GRPC uni stream server but keep it on for HTTP endpoints.","s":"Automatic certificates","u":"/docs/server/tls","h":"#automatic-certificates","p":3350},{"i":3357,"t":"You can provide custom certificate files to configure TLS for GRPC API server. grpc_api_tls boolean flag enables TLS for GRPC API server, requires an X509 certificate and a key file grpc_api_tls_cert string provides a path to an X509 certificate file for GRPC API server grpc_api_tls_key string provides a path to an X509 certificate key for GRPC API server","s":"TLS for GRPC API","u":"/docs/server/tls","h":"#tls-for-grpc-api","p":3350},{"i":3359,"t":"You can provide custom certificate files to configure TLS for GRPC unidirectional stream endpoint. uni_grpc_tls boolean flag enables TLS for GRPC server, requires an X509 certificate and a key file uni_grpc_tls_cert string provides a path to an X509 certificate file for GRPC uni stream server uni_grpc_tls_key string provides a path to an X509 certificate key for GRPC uni stream server","s":"TLS for GRPC unidirectional stream","u":"/docs/server/tls","h":"#tls-for-grpc-unidirectional-stream","p":3350},{"i":3361,"t":"On this page","s":"Proxy subscription streams","u":"/docs/server/proxy_streams","h":"","p":3360},{"i":3363,"t":"Using proxy subscription streams increases resource usage on both Centrifugo and app backend sides because it involves more moving parts such as goroutines, additional buffers, connections, etc. The feature is quite niche. Read carefully the motivation described in this doc. If you don't really need proxy streams – prefer using Centrifugo usual approach by always publishing messages to channels over Centrifugo publish API whenever an event happens. This is efficient and Centrifugo just drops messages in case of no active subscribers in a channel. I.e. follow our idiomatic guidelines. tip Use proxy subscription streams only when really needed. Specifically, proxy subscription stream may be very useful to stream data for a limited time upon some user action in the app. At the same time proxy subscription streams should scale well horizontally with adding more servers. But scaling GRPC is more involved and using GRPC streams results into more resources utilized than with the common Centrifugo approach, so make sure the resource consumption is sufficient for your system by performing load tests with your expected load profile. The thing is that sometimes proxy streams is the only way to achieve the desired behaviour – at that point they shine even though require more resources and developer effort. Also, not every use case involves tens of thousands of subscriptions/connections to worry about – be realistic about your practical situation.","s":"Scalability concerns","u":"/docs/server/proxy_streams","h":"#scalability-concerns","p":3360},{"i":3365,"t":"Here is a diagram which shows the sequence of events happening when using subscription streams: Subscription streams generally solve a task of integrating with third-party streaming providers or external process, possibly with custom filtering. They come into play when it's not feasible to continuously stream all data to various channels, and when you need to deallocate resources on the backend side as soon as stream is not needed anymore. Subscription streams may be also considered as streaming requests – an isolated way to stream something from the backend to the client or from the client to the backend. Let's describe a real-life use case. Say you have Loki for keeping logs, it provides a streaming API for tailing logs. You decided to stream logs towards your app's clients. When client subscribes to some channel in Centrifugo and the unidirectional stream established between Centrifugo and your backend – you can make sure client has proper permissions for the requested resource and backend then starts tailing Loki logs (or other third-party system, this may be Twitter streaming API, MQTT broker, GraphQL subscription, or streaming query to the real-time database such as RethinkDB). As soon as backend receives log events from Loki it transfers them towards client over Centrifugo. Client can provide custom data upon subscribing to a channel which makes it possible to pass query filters from the frontend app to the backend. In the example with Loki above this may be a LogQL query. In case of proxy subscription streams all the client authentication may be delegated to common Centrifugo mechanisms, so when the channel stream is established you know the ID of user (obtained by Centrifugo from JWT auth process or over connect proxy). You can additionally check channel permissions at the moment of stream establishement. As soon as client unsubscribes from the channel – Centrifugo closes the unidirectional GRPC stream – so your backend will notice that. If client disconnects – stream is closed also. If for some reason connection between Centrifugo and backend is closed – then Centrifugo will unsubscribe a client with insufficient state reason and a client will soon resubscribe to a channel (managed automatically by our SDKs). You may wonder – what about the same channel name used for subscribing to such a stream by different connections. Proxy stream is an individual link between a client and a backend – Centrifugo transfers stream data published to the GRPC stream by the backend only to the client connection to whom the stream belongs. I.e. messages sent by the backend to GRPC stream are not broadcasted to other channel subscribers. But if you will use server API for publishing – then message will be broadcasted to all channel subscribers even if they are currently using proxy stream within that channel. Presence and join/leave features will work as usual for channels with subscription proxy streams. If different connections use the same channel they will be able to use presence (if enabled) to see who else is currently in the channel, and may receive join/leave messages (if enabled). Channel history for proxy subscription streams For the case of proxy subscription streams Centrifugo channel history and recovery features do not really make sense. Proxy stream is an individual direct link between client and your backend through Centrifugo which is always re-established from scratch upon re-subscription or connection drops. The benefit of the history and its semantics are not clear in this case and can only bring undesired overhead (because Centrifugo will have to use broker, now messages just go directly towards connections without broker/engine involved at all). Only for client-side subscriptions Subscription streams work only with client-side subscriptions (i.e. when client explicitly subscribes to a channel on the application's frontend side). Server-side subscriptions won't initiate a GRPC stream to the backend. Don't forget that Centrifugo namespace system is very flexible – so you can always combine different approaches using different channel namespaces. You can always use subscription streams only for some channels belonging to a specific namespace.","s":"Motivation and design","u":"/docs/server/proxy_streams","h":"#motivation-and-design","p":3360},{"i":3367,"t":"From the configuration point of view subscription streams may be enabled for channel namespace just as additional type of proxy. The important difference is that only GRPC endpoints may be used - as we are using GRPC streaming RPCs for this functionality. You can configure subscription streams for channels very similar to how subscribe proxy is configured. First, configure subscribe stream proxy, pointing it to the backend which implements our proxy stream GRPC service contract: config.json { ... \"proxy_subscribe_stream_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_stream_timeout\": \"3s\"} Only grpc:// endpoints are supported since we are heavily relying on GRPC streaming ecosystem here. In this case proxy_subscribe_stream_timeout defines a time how long Centrifugo waits for a first message from a stream which contains subscription details to transfer to a client. Then you can enable subscription streams for channels on a namespace level: config.json { ... \"proxy_subscribe_stream_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_stream_timeout\": \"3s\", \"namespaces\": [ { \"name\": \"streams\", \"proxy_subscribe_stream\": true } ]} info You can not use subscribe, publish, sub_refresh proxy configurations together with stream proxy configuration inside one channel namespace. That's it on Centrifugo side. Now on the app backend you should implement GRPC service according to the following definitions: service CentrifugoProxy { ... // SubscribeUnidirectional allows handling unidirectional subscription streams. rpc SubscribeUnidirectional(SubscribeRequest) returns (stream StreamSubscribeResponse); ...} GRPC service definitions can be found in the Centrifugo repository: proxy.proto - same as we described before, probably you already have a service which implements some methods from it. If you don't – just follow GRPC tutorials for your programming language to generate server stubs from our Protobuf schema – and you are ready to describe stream logic. Here we are looking at unidirectional subscription stream – so the next thing to do is to implement streaming handler on the application backend side which contains stream business logic, i.e. implement SubscribeUnidirectional streaming rpc handler. tip You can write GRPC handlers to handle proxy subscription streams in any language with good GRPC support. A basic example of such handler in Go may look like this (error handling skipped for brevity): package mainimport ( \"fmt\" \"log\" \"math\" \"net\" \"strconv\" \"time\" pb \"example/proxyproto\" \"google.golang.org/grpc\")type streamServer struct { pb.UnimplementedCentrifugoProxyServer}func (s *streamerServer) SubscribeUnidirectional( req *pb.SubscribeRequest, stream pb.CentrifugoProxy_SubscribeUnidirectionalServer,) error { started := time.Now() fmt.Println(\"unidirectional subscribe called with request\", req) defer func() { fmt.Println(\"unidirectional subscribe finished, elapsed\", time.Since(started)) }() _ = stream.Send(&pb.StreamSubscribeResponse{ SubscribeResponse: &pb.SubscribeResponse{}, }) // Now publish data to a stream every 1 second. for { select { case <-stream.Context().Done(): return stream.Context().Err() case <-time.After(1000 * time.Millisecond): } pub := &pb.Publication{Data: []byte(`{\"input\": \"` + strconv.Itoa(i) + `\"}`)} _ = stream.Send(&pb.StreamSubscribeResponse{Publication: pub}) }}func main() { lis, _ := net.Listen(\"tcp\", \":12000\") s := grpc.NewServer(grpc.MaxConcurrentStreams(math.MaxUint32)) pb.RegisterCentrifugoProxyServer(s, &streamServer{}) _ = s.Serve(lis)} tip Note we have increased grpc.MaxConcurrentStreams for server to handle more simultaneous streams than allowed by default. Usually default is 100 but can differ in various GRPC server implementations. If you expect more streams then you need a bigger value. Centrifugo has some rules about messages in streams. Upon stream establishement Centrifugo expects backend to send first message from a stream - this is a StreamSubscribeResponse with SubscribeResponse in it. Centrifugo waits for this message before replying to the client's subscription command. This way we can communicate initial state with a client and make sure streaming is properly established with all permission checks passed. After sending initial message you can send events (publications) as they appear in your system. Now everything should be ready to test it out from the client side: just subscribe to a channel where stream proxy is on with our SDK – and you will see your stream handler called and data streamed from it to a client. For example, with our Javascript SDK: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { getToken: getTokenImplementation});client.connect();const sub = client.newSubscription('streams:123e4567-e89b-12d3-a456-426614174000', { data: {}}).on('publication', function(ctx) { console.log(\"received publication from a channel\", ctx.data);});sub.subscribe(); Again, while we are still looking for a proper semantics of subscription streams we recommend using unique channel names for all on-demand streams you are establishing.","s":"Unidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#unidirectional-subscription-streams","p":3360},{"i":3369,"t":"In addition to unidirectional streams, Centrifugo supports bidirectional streams upon client channel subscription. In this case client gets a possibility to stream any data to the backend utilizing bidirectional communication. Client can send messages to a bidirectional stream by using .publish(data) method of a Subscription object. In terms of general design bidirectional streams behave similar to unidirectional streams as described above. When enabling subscription streams, Centrifugo uses unidirectional GRPC streams by default – as those should fit most of the use cases proxy subscription streams were introduced for. To tell Centrifugo use bidirectional streaming add proxy_subscribe_stream_bidirectional flag to the namespace configuration: config.json { ... \"proxy_subscribe_stream_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_stream_timeout\": \"3s\", \"namespaces\": [ { \"name\": \"streams\", \"proxy_subscribe_stream\": true, \"proxy_subscribe_stream_bidirectional\": true } ]} On the backend you need to implement the following streaming handler: service CentrifugoProxy { ... // SubscribeBidirectional allows handling bidirectional subscription streams. rpc SubscribeBidirectional(stream StreamSubscribeRequest) returns (stream StreamSubscribeResponse); ...} The first StreamSubscribeRequest message in stream will contain SubscribeRequest and Centrifugo expects StreamSubscribeResponse with SubscribeResponse from the backend – just like in unidirectional case described above. An example of such handler in Go language which echoes back all publications from client (error handling skipped for brevity): func (s *streamerServer) SubscribeBidirectional( stream pb.CentrifugoProxy_SubscribeBidirectionalServer,) error { started := time.Now() fmt.Println(\"bidirectional subscribe called\") defer func() { fmt.Println(\"bidirectional subscribe finished, elapsed\", time.Since(started)) }() // First message always contains SubscribeRequest. req, _ := stream.Recv() fmt.Println(\"subscribe request received\", req.SubscribeRequest) _ = stream.Send(&pb.StreamSubscribeResponse{ SubscribeResponse: &pb.SubscribeResponse{}, }) // The following messages contain publications from client. for { req, _ = stream.Recv() data := req.Publication.Data fmt.Println(\"data from client\", string(data)) var cd clientData pub := &pb.Publication{Data: data} _ = stream.Send(&pb.StreamSubscribeResponse{Publication: pub}) }}","s":"Bidirectional subscription streams","u":"/docs/server/proxy_streams","h":"#bidirectional-subscription-streams","p":3360},{"i":3371,"t":"Granular proxy mode works with subscription streams in the same manner as for other Centrifugo proxy types. Here is an example how you can define different subscribe stream proxies for different namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"stream_1\", \"endpoint\": \"grpc://localhost:3000\", \"timeout\": \"500ms\", }, { \"name\": \"stream_2\", \"endpoint\": \"grpc://localhost:3001\", \"timeout\": \"500ms\", } ], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_stream_proxy_name\": \"stream_1\" }, { \"name\": \"ns2\", \"subscribe_stream_proxy_name\": \"stream_2\" } ]}","s":"Granular proxy mode","u":"/docs/server/proxy_streams","h":"#granular-proxy-mode","p":3360},{"i":3373,"t":"Full example which demonstrates proxy subscribe stream backend implemented in Go language may be found in Centrifugo examples repo.","s":"Full example","u":"/docs/server/proxy_streams","h":"#full-example","p":3360},{"i":3375,"t":"On this page","s":"Client protocol","u":"/docs/transports/client_protocol","h":"","p":3374},{"i":3377,"t":"Centrifugo is built on top of Centrifuge library for Go. Centrifuge library uses its own framing for wrapping Centrifuge-specific messages – synchronous commands from a client to a server (which expect replies from a server) and asynchronous pushes. Centrifuge client protocol is defined by a Protobuf schema. This is the source of truth. tip At the moment Protobuf schema contains some fields which are only used in client protocol v1. This is for backwards compatibility – server supports clients connecting over both client protocol v2 and client protocol v1. Client protocol v1 is considered deprecated and will be removed at some point in the future (giving enough time to our users to migrate).","s":"Protobuf schema","u":"/docs/transports/client_protocol","h":"#protobuf-schema","p":3374},{"i":3379,"t":"In bidirectional case client sends Command to a server and server sends Reply to a client. I.e. all communication between client and server is a bidirectional exchange of Command and Reply messages. Each Command has id field. This is an incremental uint32 field. This field will be echoed in a server replies to commands so client could match a certain Reply to Command sent before. This is important since Websocket is an asynchronous transport where server and client both send messages at any moment and there is no builtin request-response matching. Having id allows matching a reply with a command send before on SDK level. In JSON case client can send command like this: {\"id\": 1, \"subscribe\": {\"channel\": \"example\"}} And client can expect something like this in response: {\"id\": 1, \"subscribe\": {}} Reply for different commands has corresponding field with command result (\"subscribe\" in example above). Reply can also contain error if Command processing resulted into an error on a server. error is optional and if Reply does not have error then it means that Command processed successfully and client can handle result object appropriately. error looks like this in JSON case: { \"code\": 100, \"message\": \"internal server error\", \"temporary\": true} I.e. reply with error may look like this: {\"id\": 1, \"error\": {\"code\": 100, \"message\": \"internal server error\"}} We will talk more about error handling below. Centrifuge library defines several command types client can issue. A well-written client must be aware of all those commands and client workflow. Current commands: connect – sent to authenticate connection, sth like hello from a client which can carry authentication token and arbitrary data. subscribe – sent to subscribe to a channel unsubscribe - sent to unsubscribe from a channel publish - sent to publish data into a channel presence - sent to request presence information from a channel presence_stats - sent to request presence stats information from a channel history - sent to request history information for a channel send - sent to send async message to a server (this command is a bit special since it must not contain id - as we don't wait for any response from a server in this case). rpc - sent to send RPC to a channel (execute arbitrary logic and wait for response) refresh - sent to refresh connection token sub_refresh - sent to refresh channel subscription token","s":"Command-Reply","u":"/docs/transports/client_protocol","h":"#command-reply","p":3374},{"i":3381,"t":"The special type of Reply is asynchronous Reply. Such replies have no id field set (or id can be equal to zero). Async replies can come to a client at any moment - not as reaction to issued Command but as a message from a server to a client at arbitrary time. For example, this can be a message published into channel. There are several types of asynchronous messages that can come from a server to a client. pub is a message published into channel join messages sent when someone joined (subscribed on) channel. leave messages sent when someone left (unsubscribed from) channel. unsubscribe message sent when a server unsubscribed current client from a channel: subscribe may be sent when a server subscribes client to a channel. disconnect may be sent be a server before closing connection and contains disconnect code/reason message may be sent when server sends asynchronous message to a client connect push can be sent in unidirectional transport case refresh may be sent when a server refreshes client credentials (useful in unidirectional transports)","s":"Asynchronous pushes","u":"/docs/transports/client_protocol","h":"#asynchronous-pushes","p":3374},{"i":3383,"t":"To reduce number of system calls one request from a client to a server and one response from a server to a client can have more than one Command or Reply. This allows reducing number of system calls for writing and reading data. When JSON format used then many Command can be sent from client to server in JSON streaming line-delimited format. I.e. each individual Command encoded to JSON and then commands joined together using new line symbol \\n: {\"id\": 1, \"subscribe\": {\"channel\": \"ch1\"}}{\"id\": 2, \"subscribe\": {\"channel\": \"ch2\"}} Here is an example how we do this in Javascript client when JSON format used: function encodeCommands(commands) { const encodedCommands = []; for (const i in commands) { if (commands.hasOwnProperty(i)) { encodedCommands.push(JSON.stringify(commands[i])); } } return encodedCommands.join('\\n');} info This doc uses JSON format for examples because it's human-readable. Everything said here for JSON is also true for Protobuf encoded case. There is a difference how several individual Command or server Reply joined into one request – see details below. Also, in JSON format bytes fields transformed into embedded JSON by Centrifugo. When Protobuf format used then many Command can be sent from a client to a server in a length-delimited format where each individual Command marshaled to bytes prepended by varint length. See existing client implementations for encoding example. The same rules relate to many Reply in one response from server to client. Line-delimited JSON and varint-length prefixed Protobuf also used there. tip Server can even send reply to a command and asynchronous message batched together in a one frame. For example here is how we read server response and extracting individual replies in Javascript client when JSON format used: function decodeReplies(data) { const replies = []; const encodedReplies = data.split('\\n'); for (const i in encodedReplies) { if (encodedReplies.hasOwnProperty(i)) { if (!encodedReplies[i]) { continue; } const reply = JSON.parse(encodedReplies[i]); replies.push(reply); } } return replies;} For Protobuf case see existing client implementations for decoding example.","s":"Top level batching","u":"/docs/transports/client_protocol","h":"#top-level-batching","p":3374},{"i":3385,"t":"To maintain connection alive and detect broken connections server periodically sends empty commands to clients and expects empty replies from them. When client does not receive ping from a server for some time it can consider connection broken and try to reconnect. Usually a server sends pings every 25 seconds.","s":"Ping Pong","u":"/docs/transports/client_protocol","h":"#ping-pong","p":3374},{"i":3387,"t":"Client should handle disconnect advices from server. In websocket case disconnect advice is sent in CLOSE Websocket frame. Disconnect advice contains uint32 code and human-readable string reason.","s":"Handle disconnects","u":"/docs/transports/client_protocol","h":"#handle-disconnects","p":3374},{"i":3389,"t":"This section contains advices to error handling in client implementations. Errors can happen during various operations and can be handled in special way in context of some commands to tolerate network and server problems. Errors during connect must result in full client reconnect with exponential backoff strategy. The special case is error with code 110 which signals that connection token already expired. As we said above client should update its connection JWT before connecting to server again. Errors during subscribe must result in full client reconnect in case of internal error (code 100). And be sent to subscribe error event handler of subscription if received error is persistent. Persistent errors are errors like permission denied, bad request, namespace not found etc. Persistent errors in most situation mean a mistake from developers side. The special corner case is client-side timeout during subscribe operation. As protocol is asynchronous it's possible in this case that server will eventually subscribe client on channel but client will think that it's not subscribed. It's possible to retry subscription request and tolerate already subscribed (code 105) error as expected. But the simplest solution is to reconnect entirely as this is simpler and gives client a chance to connect to working server instance. Errors during rpc-like operations can be just returned to caller - i.e. user javascript code. Calls like history and presence are idempotent. You should be accurate with non-idempotent operations like publish - in case of client timeout it's possible to send the same message into channel twice if retry publish after timeout - so users of libraries must care about this case – making sure they have some protection from displaying message twice on client side (maybe some sort of unique key in payload).","s":"Handle errors","u":"/docs/transports/client_protocol","h":"#handle-errors","p":3374},{"i":3391,"t":"Client protocol does not allow one client connection to subscribe to the same channel twice. In this case client will receive already subscribed error in a reply to a subscribe command.","s":"Additional notes","u":"/docs/transports/client_protocol","h":"#additional-notes","p":3374},{"i":3393,"t":"On this page","s":"Client real-time SDKs","u":"/docs/transports/client_sdk","h":"","p":3392},{"i":3395,"t":"Here is a list of SDKs maintained by Centrifugal Labs: centrifuge-js – for a browser, NodeJS and React Native centrifuge-go - for Go language centrifuge-dart - for Dart and Flutter (mobile and web) centrifuge-swift – for native iOS development centrifuge-java – for native Android development and general Java SDKs driven by the community: centrifuge-csharp - SDK in C# for .NET and Unity 2022.3+ See a description of client protocol if you want to write a custom bidirectional connector or eager to learn how Centrifugo protocol internals are structured. In case of any question how protocol works take a look at existing SDK source code or reach out in the community rooms.","s":"List of client SDKs","u":"/docs/transports/client_sdk","h":"#list-of-client-sdks","p":3392},{"i":3397,"t":"Centrifugo real-time SDKs work using two possible serialization formats: JSON and Protobuf. The entire bidirectional client protocol is described by the Protobuf schema. But those Protobuf messages may be also encoded as JSON objects (in JSON representation bytes fields in the Protobuf schema is replaced by the embedded JSON object in Centrifugo case). Our Javascript SDK - centrifuge-js - uses JSON serialization for protocol frames by default. This makes communication with Centrifugo server convenient as we are exchanging human-readable JSON frames between client and server. And it makes it possible to use centrifuge-js without extra dependency to protobuf.js library. It's possible to switch to Protobuf protocol with centrifuge-js SDK though, in case you want more compact Centrifuge protocol representation, faster decode/encode speeds on Centrifugo server side, or payloads you need to pass are custom binary. See more details on how to use centrifuge-js with Protobuf serialization in README. centrifuge-go real-time SDK for Go language also supports both JSON and Protobuf formats when communicating with Centrifugo server. Other SDKs, like centrifuge-dart, centrifuge-swift, centrifuge-java work using only Protobuf serialization for Centrifuge protocol internally. So they utilize the fastest and the most compact wire representation by default. Note, that while internally in those SDKs the serialization format is Protobuf, you can still send JSON towards these clients as JSON objects may be encoded as UTF-8 bytes. So these SDKs may work with both custom binary and JSON payloads. There are some important notes about JSON and Protobuf interoperability mentioned in our FAQ.","s":"Protobuf and JSON formats in SDKs","u":"/docs/transports/client_sdk","h":"#protobuf-and-json-formats-in-sdks","p":3392},{"i":3399,"t":"Below you can find an information regarding support of different features in our official client SDKs","s":"SDK feature matrix","u":"/docs/transports/client_sdk","h":"#sdk-feature-matrix","p":3392},{"i":3401,"t":"Client feature js dart swift go java connect to a server ✅ ✅ ✅ ✅ ✅ setting client options ✅ ✅ ✅ ✅ ✅ automatic reconnect with backoff algorithm ✅ ✅ ✅ ✅ ✅ client state changes ✅ ✅ ✅ ✅ ✅ command-reply ✅ ✅ ✅ ✅ ✅ command timeouts ✅ ✅ ✅ ✅ ✅ async pushes ✅ ✅ ✅ ✅ ✅ ping-pong ✅ ✅ ✅ ✅ ✅ connection token refresh ✅ ✅ ✅ ✅ ✅ handle disconnect advice from server ✅ ✅ ✅ ✅ ✅ server-side subscriptions ✅ ✅ ✅ ✅ ✅ batching API ✅ bidirectional WebSocket emulation ✅","s":"Connection related features","u":"/docs/transports/client_sdk","h":"#connection-related-features","p":3392},{"i":3403,"t":"Client feature js dart swift go java subscrbe to a channel ✅ ✅ ✅ ✅ ✅ setting subscription options ✅ ✅ ✅ ✅ ✅ automatic resubscribe with backoff algorithm ✅ ✅ ✅ ✅ ✅ subscription state changes ✅ ✅ ✅ ✅ ✅ subscription command-reply ✅ ✅ ✅ ✅ ✅ subscription async pushes ✅ ✅ ✅ ✅ ✅ subscription token refresh ✅ ✅ ✅ ✅ ✅ handle unsubscribe advice from server ✅ ✅ ✅ ✅ ✅ manage subscription registry ✅ ✅ ✅ ✅ ✅ optimistic subscriptions ✅","s":"Client-side subscription related features","u":"/docs/transports/client_sdk","h":"#client-side-subscription-related-features","p":3392},{"i":3405,"t":"On this page","s":"Proxy events to the backend","u":"/docs/server/proxy","h":"","p":3404},{"i":3407,"t":"HTTP proxy in Centrifugo converts client connection events into HTTP calls to the application backend.","s":"HTTP proxy","u":"/docs/server/proxy","h":"#http-proxy","p":3404},{"i":3409,"t":"All HTTP proxy calls are HTTP POST requests that will be sent from Centrifugo to configured endpoints with a configured timeout. These requests will have some headers copied from the original client request (see details below) and include JSON body which varies depending on call type (for example data sent by a client in RPC call etc, see more details about JSON bodies below).","s":"HTTP request structure","u":"/docs/server/proxy","h":"#http-request-structure","p":3404},{"i":3411,"t":"The good thing about Centrifugo HTTP proxy is that it transparently proxies original HTTP request headers in a request to app backend. In most cases this allows achieving transparent authentication on the application backend side. But it's required to provide an explicit list of HTTP headers you want to be proxied, for example: config.json { ... \"proxy_http_headers\": [ \"Origin\", \"User-Agent\", \"Cookie\", \"Authorization\", \"X-Real-Ip\", \"X-Forwarded-For\", \"X-Request-Id\" ]} Alternatively, you can set a list of headers via an environment variable (space separated): export CENTRIFUGO_PROXY_HTTP_HEADERS=\"Cookie User-Agent X-B3-TraceId X-B3-SpanId\" ./centrifugo note Centrifugo forces the Content-Type header to be application/json in all HTTP proxy requests since it sends the body in JSON format to the application backend. Starting from Centrifugo v5.0.2 it's possible to configure static set of headers to be appended to all HTTP proxy requests: config.json { ... \"proxy_static_http_headers\": { \"X-Custom-Header\": \"custom value\" }} proxy_static_http_headers is a map with string keys and string values. You may also set it over environment variable using JSON object string: export CENTRIFUGO_PROXY_STATIC_HTTP_HEADERS='{\"X-Custom-Header\": \"custom value\"}' Static headers may be overriden by the header from client connection request if you proxy the header with the same name inside proxy_http_headers option showed above.","s":"Proxy HTTP headers","u":"/docs/server/proxy","h":"#proxy-http-headers","p":3404},{"i":3413,"t":"When GRPC unidirectional stream is used as a client transport then you may want to proxy GRPC metadata from the client request. In this case you may configure proxy_grpc_metadata option. This is an array of string metadata keys which will be proxied. These metadata keys transformed to HTTP headers of proxy request. By default no metadata keys are proxied. See below the table of rules how metadata and headers proxied in transport/proxy different scenarios.","s":"Proxy GRPC metadata","u":"/docs/server/proxy","h":"#proxy-grpc-metadata","p":3404},{"i":3415,"t":"With the following options in the configuration file: { ... \"proxy_connect_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_connect_timeout\": \"1s\"} – connection requests without JWT set will be proxied to proxy_connect_endpoint URL endpoint. On your backend side, you can authenticate the incoming connection and return client credentials to Centrifugo in response to the proxied request. danger Make sure you properly configured allowed_origins Centrifugo option or check request origin on your backend side upon receiving connect request from Centrifugo. Otherwise, your site can be vulnerable to CSRF attacks if you are using WebSocket transport for client connections. Yes, this means you don't need to generate JWT and pass it to a client-side and can rely on a cookie while authenticating the user. Centrifugo should work on the same domain in this case so your site cookie could be passed to Centrifugo by browsers. In many cases your existing session mechanism will provide user authentication details to the connect proxy handler. tip If you want to pass some custom authentication token from a client side (not in Centrifugo JWT format) but force request to be proxied then you may put it in a cookie or use connection request custom data field (available in all our transports). This data can contain arbitrary payload you want to pass from a client to a server. This also means that every new connection from a user will result in an HTTP POST request to your application backend. While with JWT token you usually generate it once on application page reload, if client reconnects due to Centrifugo restart or internet connection loss it uses the same JWT it had before thus usually no additional requests are generated during reconnect process (until JWT expired). Payload example that will be sent to app backend when client without token wants to establish a connection with Centrifugo and proxy_connect_endpoint is set to non-empty URL string: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\"} Expected response example: {\"result\": {\"user\": \"56\"}} This response allows connecting and tells Centrifugo the ID of a user. See below the full list of supported fields in the result. Several app examples which use connect proxy can be found in our blog: With NodeJS With Django With Laravel Connect request fields​ This is what sent from Centrifugo to application backend in case of connect proxy request. Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) name string yes optional name of the client (this field will only be set if provided by a client on connect) version string yes optional version of the client (this field will only be set if provided by a client on connect) data JSON yes optional data from client (this field will only be set if provided by a client on connect) b64data string yes optional data from the client in base64 format (if the binary proxy mode is used) channels Array of strings yes list of server-side channels client want to subscribe to, the application server must check permissions and add allowed channels to result Connect result fields​ This is what application returns to Centrifugo inside result field in case of connect proxy request. Field Type Optional Description user string no user ID (calculated on app backend based on request cookie header for example). Return it as an empty string for accepting unauthenticated requests expire_at integer yes a timestamp (Unix seconds in the future) when connection must be considered expired. If not set or set to 0 connection won't expire at all info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages data JSON yes a custom data to send to the client in connect command response. b64data string yes a custom data to send to the client in the connect command response for binary connections, will be decoded to raw bytes on Centrifugo side before sending to client channels array of strings yes allows providing a list of server-side channels to subscribe connection to. See more details about server-side subscriptions subs map of SubscribeOptions yes map of channels with options to subscribe connection to. See more details about server-side subscriptions meta JSON object (ex. {\"key\": \"value\"}) yes a custom data to attach to connection (this won't be exposed to client-side) Options​ proxy_connect_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. Example​ Here is the simplest example of the connect handler in Tornado Python framework (note that in a real system you need to authenticate the user on your backend side, here we just return \"56\" as user ID): class CentrifugoConnectHandler(tornado.web.RequestHandler): def check_xsrf_cookie(self): pass def post(self): self.set_header('Content-Type', 'application/json; charset=\"utf-8\"') data = json.dumps({ 'result': { 'user': '56' } }) self.write(data)def main(): options.parse_command_line() app = tornado.web.Application([ (r'/centrifugo/connect', CentrifugoConnectHandler), ]) app.listen(3000) tornado.ioloop.IOLoop.instance().start()if __name__ == '__main__': main() This example should help you to implement a similar HTTP handler in any language/framework you are using on the backend side. We also have a tutorial in the blog about Centrifugo integration with NodeJS which uses connect proxy and native session middleware of Express.js to authenticate connections. Even if you are not using NodeJS on a backend a tutorial can help you understand the idea. What if connection is unauthenticated/unauthorized to connect?​ In this case return a disconnect object as a response. See Return custom disconnect section. Depending on whether you want connection to reconnect or not (usually not) you can select the appropriate disconnect code. Sth like this in response: { \"disconnect\": { \"code\": 4501, \"reason\": \"unauthorized\" }} – may be sufficient enough. Choosing codes and reason is up to the developer, but follow the rules described in Return custom disconnect section.","s":"Connect proxy","u":"/docs/server/proxy","h":"#connect-proxy","p":3404},{"i":3417,"t":"With the following options in the configuration file: { ... \"proxy_refresh_endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"proxy_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_refresh_endpoint when it's time to refresh the connection. Centrifugo itself will ask your backend about connection validity instead of refresh workflow on the client-side. The payload sent to app backend in refresh request (when the connection is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the connection as expired - the client will be disconnected expire_at integer yes a timestamp in the future when connection must be considered expired info JSON yes a connection info JSON b64info string yes binary connection info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Refresh proxy","u":"/docs/server/proxy","h":"#refresh-proxy","p":3404},{"i":3419,"t":"With the following option in the configuration file: { ... \"proxy_rpc_endpoint\": \"http://localhost:3000/centrifugo/connect\", \"proxy_rpc_timeout\": \"1s\"} RPC calls over client connection will be proxied to proxy_rpc_endpoint. This allows a developer to utilize WebSocket connection (or any other bidirectional transport Centrifugo supports) in a bidirectional way. Payload example sent to app backend in RPC request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"method\": \"getCurrentPrice\", \"data\":{\"params\": {\"object_id\": 12}}} Expected response example: {\"result\": {\"data\": {\"answer\": \"2019\"}}} RPC request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process method string yes an RPC method string, if the client does not use named RPC call then method will be omitted data JSON yes RPC custom data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) RPC result fields​ Field Type Optional Description data JSON yes RPC response - any valid JSON is supported b64data string yes can be set instead of data for binary response encoded in base64 format Options​ proxy_rpc_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. See below on how to return a custom error.","s":"RPC proxy","u":"/docs/server/proxy","h":"#rpc-proxy","p":3404},{"i":3421,"t":"With the following option in the configuration file: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\"} – subscribe requests sent over client connection will be proxied to proxy_subscribe_endpoint. This allows you to check the access of the client to a channel. info Subscribe proxy does not proxy subscriptions with token and subscriptions to user-limited channels at the moment. That's because those are already providing channel access control. Subscribe proxy assumes that all the permission management happens on the backend side when processing proxy request. So if you need to get subscribe proxy requests for all channels in the system - do not use subscription tokens and user-limited channels. Unlike proxy types described above subscribe proxy must be enabled per channel namespace. This means that every namespace (including global/default one) has a boolean option proxy_subscribe that enables subscribe proxy for channels in a namespace. So to enable subscribe proxy for channels without namespace define proxy_subscribe on a top configuration level: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} Or for channels in namespace sun: { ... \"proxy_subscribe_endpoint\": \"http://localhost:3000/centrifugo/subscribe\", \"proxy_subscribe_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_subscribe\": true }]} Payload example sent to app backend in subscribe request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\"} Expected response example if subscription is allowed: {\"result\": {}} Subscribe request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket or sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to subscribe to meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) data JSON yes custom data from client sent with subscription request (this field will only be set if provided by a client on subscribe). b64data string yes optional subscription data from the client in base64 format (if the binary proxy mode is used). Subscribe result fields​ Field Type Optional Description info JSON yes a channel info JSON b64info string yes a binary connection channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using data JSON yes a custom data to send to the client in subscribe command reply. b64data string yes a custom data to send to the client in subscribe command reply, will be decoded to raw bytes on Centrifugo side before sending to client override Override object yes Allows dynamically override some channel options defined in Centrifugo configuration on a per-connection basis (see below available fields) expire_at integer yes a timestamp (Unix seconds in the future) when subscription must be considered expired. If not set or set to 0 subscription won't expire at all. Supported since Centrifugo v5.0.4 Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} See below on how to return an error in case you don't want to allow subscribing. Options​ proxy_subscribe_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s. What if connection is not allowed to subscribe?​ In this case you can return error object as a subscribe handler response. See return custom error section. In general, frontend applications should not try to subscribe to channels for which access is not allowed. But these situations can happen or malicious user can try to subscribe to a channel. In most scenarios returning: { \"error\": { \"code\": 403, \"message\": \"permission denied\" }} – is sufficient enough. Error code may be not 403 actually, no real reason to force HTTP semantics here - so it's up to Centrifugo user to decide. Just keep it in range [400, 1999] as described here. If case of returning response above, on client side unsubscribed event of Subscription object will be called with error code 403. Subscription won't resubscribe automatically after that.","s":"Subscribe proxy","u":"/docs/server/proxy","h":"#subscribe-proxy","p":3404},{"i":3423,"t":"With the following option in the configuration file: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\"} – publish calls sent by a client will be proxied to proxy_publish_endpoint. This request happens BEFORE a message is published to a channel, so your backend can validate whether a client can publish data to a channel. An important thing here is that publication to the channel can fail after your backend successfully validated publish request (for example publish to Redis by Centrifugo returned an error). In this case, your backend won't know about the error that happened but this error will propagate to the client-side. Like the subscribe proxy, publish proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_publish that enables publish proxy for channels in the namespace. All other namespace options will be taken into account before making a proxy request, so you also need to turn on the publish option too. So to enable publish proxy for channels without namespace define proxy_publish and publish on a top configuration level: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"publish\": true, \"proxy_publish\": true} Or for channels in namespace sun: { ... \"proxy_publish_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"proxy_publish_timeout\": \"1s\", \"namespaces\": [{ \"name\": \"sun\", \"publish\": true, \"proxy_publish\": true }]} Keep in mind that this will only work if the publish channel option is on for a channel namespace (or for a global top-level namespace). Payload example sent to app backend in a publish request: { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"chat:index\", \"data\":{\"input\":\"hello\"}} Expected response example if publish is allowed: {\"result\": {}} Publish request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs) protocol string no protocol type used by the client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no a string channel client wants to publish to data JSON yes data sent by client b64data string yes will be set instead of data field for binary proxy mode meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Publish result fields​ Field Type Optional Description data JSON yes an optional JSON data to send into a channel instead of original data sent by a client b64data string yes a binary data encoded in base64 format, the meaning is the same as for data above, will be decoded to raw bytes on Centrifugo side before publishing skip_history bool yes when set to true Centrifugo won't save publication to the channel history See below on how to return an error in case you don't want to allow publishing. Options​ proxy_publish_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Publish proxy","u":"/docs/server/proxy","h":"#publish-proxy","p":3404},{"i":3425,"t":"With the following options in the configuration file: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh_timeout\": \"1s\"} – Centrifugo will call proxy_sub_refresh_endpoint when it's time to refresh the subscription. Centrifugo itself will ask your backend about subscription validity instead of subscription refresh workflow on the client-side. Like subscribe and publish proxy types, sub refresh proxy must be enabled per channel namespace. This means that every namespace (including the global/default one) has a boolean option proxy_sub_refresh that enables sub refresh proxy for channels in the namespace. Only subscriptions which have expiration time will be validated over sub refresh proxy endpoint. Sub refresh proxy may be used as a periodical Subscription liveness callback from Centrifugo to app backend. caution In the current implementation the delay of Subscription refresh requests from Centrifugo to application backend may be up to one minute (was implemented this way from a simplicity and efficiency perspective). We assume this should be enough for many scenarios. But this may be improved if needed. Please reach us out with a detailed description of your use case where you want more accurate requests to refresh subscriptions. So to enable sub refresh proxy for channels without namespace define proxy_sub_refresh on a top configuration level: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/sub_refresh\", \"proxy_sub_refresh\": true} Or for channels in namespace sun: { ... \"proxy_sub_refresh_endpoint\": \"http://localhost:3000/centrifugo/publish\", \"namespaces\": [{ \"name\": \"sun\", \"proxy_sub_refresh\": true }]} The payload sent to app backend in sub refresh request (when the subscription is going to expire): { \"client\":\"9336a229-2400-4ebc-8c50-0a643d22e8a0\", \"transport\":\"websocket\", \"protocol\": \"json\", \"encoding\":\"json\", \"user\":\"56\", \"channel\": \"channel\"} Expected response example: {\"result\": {\"expire_at\": 1565436268}} Sub refresh request fields​ Field Type Optional Description client string no unique client ID generated by Centrifugo for each incoming connection transport string no transport name (ex. websocket, sockjs, uni_sse etc.) protocol string no protocol type used by client (json or protobuf at moment) encoding string no protocol encoding type used (json or binary at moment) user string no a connection user ID obtained during authentication process channel string no channel for which Subscription is going to expire meta JSON yes a connection attached meta (off by default, enable with \"proxy_include_connection_meta\": true) Sub refresh result fields​ Field Type Optional Description expired bool yes a flag to mark the subscription as expired - the client will be disconnected expire_at integer yes a timestamp in the future (Unix seconds) when subscription must be considered expired info JSON yes a channel info JSON b64info string yes binary channel info encoded in base64 format, will be decoded to raw bytes on Centrifugo before using in messages Options​ proxy_sub_refresh_timeout (duration) config option controls timeout of HTTP POST request sent to app backend. By default 1s.","s":"Sub refresh proxy","u":"/docs/server/proxy","h":"#sub-refresh-proxy","p":3404},{"i":3427,"t":"Application backend can return JSON object that contains an error to return it to the client: { \"error\": { \"code\": 1000, \"message\": \"custom error\" }} Applications must use error codes in range [400, 1999]. Error code field is uint32 internally. note Returning custom error does not apply to response for refresh and sub refresh proxy requests as there is no sense in returning an error (will not reach client anyway). I.e. custom error is only processed for connect, subscribe, publish and rpc proxy types.","s":"Return custom error","u":"/docs/server/proxy","h":"#return-custom-error","p":3404},{"i":3429,"t":"Application backend can return JSON object that contains a custom disconnect object to disconnect client in a custom way: { \"disconnect\": { \"code\": 4500, \"reason\": \"disconnect reason\" }} Application must use numbers in the range 4000-4999 for custom disconnect codes: codes in range [4000, 4499] give client an advice to reconnect codes in range [4500, 4999] are terminal codes – client won't reconnect upon receiving it. Code is uint32 internally. Numbers outside of 4000-4999 range are reserved by Centrifugo internal protocol. Keep in mind that due to WebSocket protocol limitations and Centrifugo internal protocol needs you need to keep disconnect reason string no longer than 32 ASCII symbols (i.e. 32 bytes max). note Returning custom disconnect does not apply to response for refresh and sub refresh proxy requests as there is no way to control disconnect at moment - the client will always be disconnected with expired disconnect reason. I.e. custom disconnect is only processed for connect, subscribe, publish and rpc proxy types.","s":"Return custom disconnect","u":"/docs/server/proxy","h":"#return-custom-disconnect","p":3404},{"i":3431,"t":"Centrifugo can also proxy connection events to your backend over GRPC instead of HTTP. In this case, Centrifugo acts as a GRPC client and your backend acts as a GRPC server. GRPC service definitions can be found in the Centrifugo repository: proxy.proto. tip GRPC proxy inherits all the fields for HTTP proxy – so you can refer to field descriptions for HTTP above. Both proxy types in Centrifugo share the same Protobuf schema definitions. Every proxy call in this case is a unary GRPC call. Centrifugo puts client headers into GRPC metadata (since GRPC doesn't have headers concept). All you need to do to enable proxying over GRPC instead of HTTP is to use grpc schema in endpoint, for example for the connect proxy: config.json { ... \"proxy_connect_endpoint\": \"grpc://localhost:12000\", \"proxy_connect_timeout\": \"1s\"} Refresh proxy: config.json { ... \"proxy_refresh_endpoint\": \"grpc://localhost:12000\", \"proxy_refresh_timeout\": \"1s\"} Or for RPC proxy: config.json { ... \"proxy_rpc_endpoint\": \"grpc://localhost:12000\", \"proxy_rpc_timeout\": \"1s\"} For publish proxy in namespace chat: config.json { ... \"proxy_publish_endpoint\": \"grpc://localhost:12000\", \"proxy_publish_timeout\": \"1s\" \"namespaces\": [ { \"name\": \"chat\", \"publish\": true, \"proxy_publish\": true } ]} Use subscribe proxy for all channels without namespaces: config.json { ... \"proxy_subscribe_endpoint\": \"grpc://localhost:12000\", \"proxy_subscribe_timeout\": \"1s\", \"proxy_subscribe\": true} So the same as for HTTP, just the different endpoint scheme.","s":"GRPC proxy","u":"/docs/server/proxy","h":"#grpc-proxy","p":3404},{"i":3433,"t":"Some additional options exist to control GRPC proxy behavior. proxy_grpc_cert_file​ String, default: \"\". Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. proxy_grpc_credentials_key​ String, default \"\" (i.e. not used). Add custom key to per-RPC credentials. proxy_grpc_credentials_value​ String, default \"\" (i.e. not used). A custom value for proxy_grpc_credentials_key. proxy_grpc_compression​ Bool, default false (i.e. not used). If true then gzip compression will be used for each GRPC proxy call (available since Centrifugo v5.1.0).","s":"GRPC proxy options","u":"/docs/server/proxy","h":"#grpc-proxy-options","p":3404},{"i":3435,"t":"We have an example of backend server (written in Go language) which can react to events from Centrifugo over GRPC. For other programming languages the approach is similar, i.e.: Copy proxy Protobuf definitions Generate GRPC code Run backend service with you custom business logic Point Centrifugo to it.","s":"GRPC proxy example","u":"/docs/server/proxy","h":"#grpc-proxy-example","p":3404},{"i":3437,"t":"Centrifugo not only supports HTTP-based client transports but also GRPC-based (for example GRPC unidirectional stream). Here is a table with rules used to proxy headers/metadata in various scenarios: Client protocol type Proxy type Client headers Client metadata HTTP HTTP In proxy request headers N/A GRPC GRPC N/A In proxy request metadata HTTP GRPC In proxy request metadata N/A GRPC HTTP N/A In proxy request headers","s":"Header proxy rules","u":"/docs/server/proxy","h":"#header-proxy-rules","p":3404},{"i":3439,"t":"As you may noticed there are several fields in request/result description of various proxy calls which use base64 encoding. Centrifugo can work with binary Protobuf protocol (in case of bidirectional WebSocket transport). All our bidirectional clients support this. Most Centrifugo users use JSON for custom payloads: i.e. for data sent to a channel, for connection info attached while authenticating (which becomes part of presence response, join/leave messages and added to Publication client info when message published from a client side). But since HTTP proxy works with JSON format (i.e. sends requests with JSON body) – it can not properly pass binary data to application backend. Arbitrary binary data can't be encoded into JSON. In this case it's possible to turn Centrifugo proxy into binary mode by using: config.json { ... \"proxy_binary_encoding\": true} Once enabled this option tells Centrifugo to use base64 format in requests and utilize fields like b64data, b64info with payloads encoded to base64 instead of their JSON field analogues. While this feature is useful for HTTP proxy it's not really required if you are using GRPC proxy – since GRPC allows passing binary data just fine. Regarding b64 fields in proxy results – just use base64 fields when required – Centrifugo is smart enough to detect that you are using base64 field and will pick payload from it, decode from base64 automatically and will pass further to connections in binary format.","s":"Binary mode","u":"/docs/server/proxy","h":"#binary-mode","p":3404},{"i":3441,"t":"By default, with proxy configuration shown above, you can only define a global proxy settings and one endpoint for each type of proxy (i.e. one for connect proxy, one for subscribe proxy, and so on). Also, you can configure only one set of headers to proxy which will be used by each proxy type. This may be sufficient for many use cases, but what if you need a more granular control? For example, use different subscribe proxy endpoints for different channel namespaces (i.e. when using microservice architecture). Centrifugo v3.1.0 introduced a new mode for proxy configuration called granular proxy mode. In this mode it's possible to configure subscribe and publish proxy behaviour on per-namespace level, use different set of headers passed to the proxy endpoint in each proxy type. Also, Centrifugo v3.1.0 introduced a concept of rpc namespaces (in addition to channel namespaces) – together with granular proxy mode this allows configuring rpc proxies on per rpc namespace basis.","s":"Granular proxy mode","u":"/docs/server/proxy","h":"#granular-proxy-mode","p":3404},{"i":3443,"t":"Since the change is rather radical it requires a separate boolean option granular_proxy_mode to be enabled. As soon as this option set Centrifugo does not use proxy configuration rules described above and follows the rules described below. config.json { ... \"granular_proxy_mode\": true}","s":"Enable granular proxy mode","u":"/docs/server/proxy","h":"#enable-granular-proxy-mode","p":3404},{"i":3445,"t":"When using granular proxy mode on configuration top level you can define \"proxies\" array with a list of different proxy objects. Each proxy object in an array should have at least two required fields: name and endpoint. Here is an example: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [ { \"name\": \"connect\", \"endpoint\": \"http://localhost:3000/centrifugo/connect\", \"timeout\": \"500ms\", \"http_headers\": [\"Cookie\"] }, { \"name\": \"refresh\", \"endpoint\": \"http://localhost:3000/centrifugo/refresh\", \"timeout\": \"500ms\" }, { \"name\": \"subscribe1\", \"endpoint\": \"http://localhost:3001/centrifugo/subscribe\" }, { \"name\": \"publish1\", \"endpoint\": \"http://localhost:3001/centrifugo/publish\" }, { \"name\": \"rpc1\", \"endpoint\": \"http://localhost:3001/centrifugo/rpc\" }, { \"name\": \"subscribe2\", \"endpoint\": \"http://localhost:3002/centrifugo/subscribe\" }, { \"name\": \"publish2\", \"endpoint\": \"grpc://localhost:3002\" } { \"name\": \"rpc2\", \"endpoint\": \"grpc://localhost:3002\" } ]} Let's look at all fields for a proxy object which is possible to set for each proxy inside \"proxies\" array. Field name Field type Required Description name string yes Unique name of proxy used for referencing in configuration, must match regexp ^[-a-zA-Z0-9_.]{2,}$ endpoint string yes HTTP or GRPC endpoint in the same format as in default proxy mode. For example, http://localhost:3000/path for HTTP or grpc://localhost:3000 for GRPC. timeout duration (string) no Proxy request timeout, default \"1s\" http_headers array of strings no List of headers to proxy, by default no headers static_http_headers map[string]string no Static set of headers to add to HTTP proxy requests. Note these headers only appended to HTTP proxy requests from Centrifugo to backend. Available since Centrifugo v5.0.2 grpc_metadata array of strings no List of GRPC metadata keys to proxy, by default no metadata keys binary_encoding bool no Use base64 for payloads include_connection_meta bool no Include meta information (attached on connect) grpc_cert_file string no Path to cert file for secure TLS connection. If not set then an insecure connection with the backend endpoint is used. grpc_credentials_key string no Add custom key to per-RPC credentials. grpc_credentials_value string no A custom value for grpc_credentials_key. grpc_compression bool no If true then gzip compression will be used for each GRPC proxy call (available since Centrifugo v5.1.0)","s":"Defining a list of proxies","u":"/docs/server/proxy","h":"#defining-a-list-of-proxies","p":3404},{"i":3447,"t":"As soon as you defined a list of proxies you can reference them by a name to use a specific proxy configuration for a specific event. To enable connect proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\"} We have an example of Centrifugo integration with NodeJS which uses granular proxy mode. Even if you are not using NodeJS on a backend an example can help you understand the idea. Let's also add refresh proxy: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"connect_proxy_name\": \"connect\", \"refresh_proxy_name\": \"refresh\"}","s":"Granular connect and refresh","u":"/docs/server/proxy","h":"#granular-connect-and-refresh","p":3404},{"i":3449,"t":"Subscribe, publish and sub refresh proxies work per-namespace. This means that subscribe_proxy_name, publish_proxy_name and sub_refresh_proxy_name are just channel namespace options. So it's possible to define these options on configuration top-level (for channels in default top-level namespace) or inside namespace object. config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [ { \"name\": \"ns1\", \"subscribe_proxy_name\": \"subscribe1\", \"publish\": true, \"publish_proxy_name\": \"publish1\" }, { \"name\": \"ns2\", \"subscribe_proxy_name\": \"subscribe2\", \"publish\": true, \"publish_proxy_name\": \"publish2\" } ]} If namespace does not have \"subscribe_proxy_name\" or \"subscribe_proxy_name\" is empty then no subscribe proxy will be used for a namespace. If namespace does not have \"publish_proxy_name\" or \"publish_proxy_name\" is empty then no publish proxy will be used for a namespace. If namespace does not have \"sub_refresh_proxy_name\" or \"sub_refresh_proxy_name\" is empty then no sub refresh proxy will be used for a namespace. tip You can define subscribe_proxy_name, publish_proxy_name, sub_refresh_proxy_name on configuration top level – and in this case publish, subscribe and sub refresh requests for channels without explicit namespace will be proxied using this proxy. The same mechanics as for other channel options in Centrifugo.","s":"Granular subscribe, publish, sub refresh","u":"/docs/server/proxy","h":"#granular-subscribe-publish-sub-refresh","p":3404},{"i":3451,"t":"Analogous to channel namespaces it's possible to configure rpc namespaces: config.json { ... \"granular_proxy_mode\": true, \"proxies\": [...], \"namespaces\": [...], \"rpc_namespaces\": [ { \"name\": \"rpc_ns1\", \"rpc_proxy_name\": \"rpc1\", }, { \"name\": \"rpc_ns2\", \"rpc_proxy_name\": \"rpc2\" } ]} The mechanics is the same as for channel namespaces. RPC requests with RPC method like rpc_ns1:test will use rpc proxy rpc1, RPC requests with RPC method like rpc_ns2:test will use rpc proxy rpc2. So Centrifugo uses : as RPC namespace boundary in RPC method (just like it does for channel namespaces). Just like channel namespaces RPC namespaces should have a name which match ^[-a-zA-Z0-9_.]{2,}$ regexp pattern – this is validated on Centrifugo start. tip The same as for channel namespaces and channel options you can define rpc_proxy_name on configuration top level – and in this case RPC calls without explicit namespace in RPC method will be proxied using this proxy.","s":"Granular RPC","u":"/docs/server/proxy","h":"#granular-rpc","p":3404},{"i":3453,"t":"On this page","s":"HTTP streaming, with bidirectional emulation","u":"/docs/transports/http_stream","h":"","p":3452},{"i":3456,"t":"Boolean, default: false. Enables HTTP streaming endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"http_stream\": true} When enabling http_stream you can connect to /connection/http_stream from centrifuge-js. Note that our bidirectional emulation also uses /emulation endpoint of Centrifugo to send requests from client to server. This is required because HTTP streaming is a unidirectional transport in its nature. So we use HTTP call to send data from client to server and proxy this call to the correct Centrifugo node which handles the connection. Thus achieving bidirectional behaviour - see details about Centrifugo bidirectional emulation layer. Make sure /emulation endpoint is available for requests from the client side too. If required, you can also control both HTTP streaming connection url prefix and emulation endpoint prefix, see customizing endpoints.","s":"http_stream","u":"/docs/transports/http_stream","h":"#http_stream","p":3452},{"i":3458,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"http_stream_max_request_body_size","u":"/docs/transports/http_stream","h":"#http_stream_max_request_body_size","p":3452},{"i":3460,"t":"On this page","s":"Real-time transports","u":"/docs/transports/overview","h":"","p":3459},{"i":3462,"t":"Bidirectional transports are capable to serve all Centrifugo features. These transports are the main Centrifugo focus and where Centrifugo really shines. Bidirectional transports come with a cost that developers need to use a special client connector library (SDK) which speaks Centrifugo client protocol. The reason why we need a special client connector library is that a bidirectional connection is asynchronous – it's required to match requests to responses, properly manage connection state, handle request queueing/timeouts/errors, etc. And of course to multiplex subscriptions to different channels over a single connection. Centrifugo has several official client SDKs for popular environments. All of them work over WebSocket transport. Our Javascript SDK also offers bidirectional fallbacks over HTTP-Streaming, Server-Sent Events (SSE), SockJS, and has an experimental support for WebTransport.","s":"Bidirectional","u":"/docs/transports/overview","h":"#bidirectional","p":3459},{"i":3464,"t":"Unidirectional transports suit well for simple use-cases with stable subscriptions, usually known at connection time. The advantage is that unidirectional transports do not require special client connectors - developers can use native browser APIs (like WebSocket, EventSource/SSE, HTTP-streaming), or GRPC generated code to receive real-time updates from Centrifugo. Thus avoiding dependency to a client connector that abstracts bidirectional communication. The drawback is that with unidirectional transports you are not inheriting all Centrifugo features out of the box (like dynamic subscriptions/unsubscriptions, automatic message recovery on reconnect, possibility to send RPC calls over persistent connection). But some of the missing client APIs can be mimicked by using calls to Centrifugo server API (i.e. over client -> application backend -> Centrifugo). Learn more about unidirectional protocol and available unidirectional transports.","s":"Unidirectional","u":"/docs/transports/overview","h":"#unidirectional","p":3459},{"i":3466,"t":"Centrifugo server periodically sends pings to clients and expects pong from clients that works over bidirectional transports. Sending ping and receiving pong allows to find broken connections faster. Centrifugo sends pings on the Centrifugo client protocol level, thus it's possible for clients to handle ping messages on the client side to make sure connection is not broken (our bidirectional SDKs do this automatically). By default Centrifugo sends pings every 25 seconds. This may be changed using ping_interval option (duration, default \"25s\"). Centrifugo expects pong message from bidirectional client SDK after sending ping to it. By default, it waits no more than 8 seconds before closing a connection. This may be changed using pong_timeout option (duration, default \"8s\"). In most cases default ping/pong intervals are fine so you don't really need to tweak them. Reducing timeouts may help you to find non-gracefully closed connections faster, but will increase network traffic and CPU resource usage since ping/pongs are sent faster. caution ping_interval must be greater than pong_timeout in the current implementation. Here is a scheme how ping/pong works in bidirectional and unidirectional client scenarios:","s":"PING/PONG behavior","u":"/docs/transports/overview","h":"#pingpong-behavior","p":3459},{"i":3468,"t":"On this page","s":"SockJS","u":"/docs/transports/sockjs","h":"","p":3467},{"i":3470,"t":"caution There are several important caveats to know when using SockJS – see below.","s":"SockJS caveats","u":"/docs/transports/sockjs","h":"#sockjs-caveats","p":3467},{"i":3472,"t":"First is that you need to use sticky sessions mechanism if you have more than one Centrifugo nodes running behind a load balancer. This mechanism usually supported by load balancers (for example Nginx). Sticky sessions mean that all requests from the same client will come to the same Centrifugo node. This is necessary because SockJS maintains connection session in process memory thus allowing bidirectional communication between a client and a server. Sticky mechanism not required if you only use one Centrifugo node on a backend. For example, with Nginx sticky support can be enabled with ip_hash directive for upstream: upstream centrifugo { ip_hash; server 127.0.0.1:8000; server 127.0.0.2:8000;} With this configuration Nginx will proxy connections with the same ip address to the same upstream backend. But ip_hash; is not the best choice in this case, because there could be situations where a lot of different connections are coming with the same IP address (behind proxies) and the load balancing system won't be fair. So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for a client.","s":"Sticky sessions","u":"/docs/transports/sockjs","h":"#sticky-sessions","p":3467},{"i":3474,"t":"SockJS is only supported by centrifuge-js – i.e. our browser client. There is no much sense to use SockJS outside of a browser these days.","s":"Browser only","u":"/docs/transports/sockjs","h":"#browser-only","p":3467},{"i":3476,"t":"One more thing to be aware of is that SockJS does not support binary data, so there is no option to use Centrifugo Protobuf protocol on top of SockJS (unlike WebSocket). Only JSON payloads can be transferred.","s":"JSON only","u":"/docs/transports/sockjs","h":"#json-only","p":3467},{"i":3479,"t":"Boolean, default: false. Enables SockJS transport.","s":"sockjs","u":"/docs/transports/sockjs","h":"#sockjs","p":3467},{"i":3481,"t":"Default: https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js Link to SockJS url which is required when iframe-based HTTP fallbacks are in use.","s":"sockjs_url","u":"/docs/transports/sockjs","h":"#sockjs_url","p":3467},{"i":3483,"t":"On this page","s":"SSE (EventSource), with bidirectional emulation","u":"/docs/transports/sse","h":"","p":3482},{"i":3486,"t":"Boolean, default: false. Enables SSE (EventSource) endpoint. And enables emulation endpoint (/emulation by default) to accept emulation HTTP requests from clients. config.json { ... \"sse\": true} When enabling sse you can connect to /connection/sse from centrifuge-js. Note that our bidirectional emulation also uses /emulation endpoint of Centrifugo to send requests from client to server. This is required because SSE/EventSource is a unidirectional transport in its nature. So we use HTTP call to send data from client to server and proxy this call to the correct Centrifugo node which handles the connection. Thus achieving bidirectional behaviour - see details about Centrifugo bidirectional emulation layer. Make sure /emulation endpoint is available for requests from the client side too. If required, you can also control both SSE connection url prefix and emulation endpoint prefix, see customizing endpoints.","s":"sse","u":"/docs/transports/sse","h":"#sse","p":3482},{"i":3488,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"sse_max_request_body_size","u":"/docs/transports/sse","h":"#sse_max_request_body_size","p":3482},{"i":3490,"t":"On this page","s":"Unidirectional client protocol","u":"/docs/transports/uni_client_protocol","h":"","p":3489},{"i":3492,"t":"In case of unidirectional transports Centrifugo will send Push frames to the connection. Push frames defined by client protocol schema. I.e. Centrifugo reuses a part of its bidirectional protocol for unidirectional communication. Push message defined as: message Push { string channel = 2; Publication pub = 4; Join join = 5; Leave leave = 6; Unsubscribe unsubscribe = 7; Message message = 8; Subscribe subscribe = 9; Connect connect = 10; Disconnect disconnect = 11; Refresh refresh = 12;} tip Some numbers in Protobuf definitions skipped for backwards compatibility with previous client protocol version. So unidirectional connection will receive various pushes. Every push contains one of the following objects: Publication Join Leave Unsubscribe Message Subscribe Connect Disconnect Refresh Some pushes belong to a channel which may be set on Push top level. All you need to do is look at Push, process messages you are interested in and ignore others. In most cases you will be most interested in pushes which contain Connect or Publication messages. For example, according to protocol schema Publication message type looks like this: message Publication { bytes data = 4; ClientInfo info = 5; uint64 offset = 6; map tags = 7;} tip In JSON protocol case Centrifugo replaces bytes type with embedded JSON. Just try using any unidirectional transport and you will quickly get the idea.","s":"Unidirectional message types","u":"/docs/transports/uni_client_protocol","h":"#unidirectional-message-types","p":3489},{"i":3494,"t":"On this page","s":"Server API walkthrough","u":"/docs/server/server_api","h":"","p":3493},{"i":3496,"t":"Server HTTP API works on /api path prefix (by default). The request format is super-simple: this is an HTTP POST request to a specific method API path with application/json Content-Type, X-API-Key header and with JSON body. Instead of many words, here is an example how to call publish method: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"test\", \"data\": {\"value\": \"test_value\"}}' \\ http://localhost:8000/api/publish tip You can just use one of our available HTTP API libraries or use Centrifugo GRPC API to avoid manually constructing requests structures. Below we look at all aspects of HTTP API in detail, starting with information about authorization.","s":"HTTP API","u":"/docs/server/server_api","h":"#http-api","p":3493},{"i":3498,"t":"HTTP API is protected by api_key set in Centrifugo configuration. I.e. api_key option must be added to config, like: config.json { ... \"api_key\": \"\"} This API key must be set in the request X-API-Key header in this way: X-API-Key: It's also possible to pass API key over URL query param. Simply add ?api_key= query param to the API endpoint. Keep in mind that passing the API key in the X-API-Key header is a recommended way as it is considered more secure. To disable API key check on Centrifugo side you can use api_insecure configuration option. Use it in development only or make sure to protect the API endpoint by proxy or firewall rules in production – to prevent anyone with access to the endpoint to send commands over your unprotected Centrifugo API. API key auth is not very safe for man-in-the-middle so we also recommended protecting Centrifugo with TLS.","s":"HTTP API authorization","u":"/docs/server/server_api","h":"#http-api-authorization","p":3493},{"i":3500,"t":"Server API supports many methods. Let's describe them starting with the most important publish operation.","s":"API methods","u":"/docs/server/server_api","h":"#api-methods","p":3493},{"i":3502,"t":"Publish method allows publishing data into a channel (we call this message publication in Centrifugo). Most probably this is a command you'll use most of the time. Here is an example of publishing message to Centrifugo: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\", \"data\": {\"text\": \"hello\"}}' \\ http://localhost:8000/api/publish In case of successful publish you will get a response like this: { \"result\": {}} As an additional example, let's take a look how to publish to Centrifugo with requests library for Python: import jsonimport requestsapi_key = \"YOUR_API_KEY\"data = json.dumps({ \"channel\": \"docs\", \"data\": { \"content\": \"1\" }})headers = {'Content-type': 'application/json', 'X-API-Key': api_key}resp = requests.post(\"https://centrifuge.example.com/api/publish\", data=data, headers=headers)print(resp.json()) In case of publication error, response object will contain error field. For example, let's publish to an unknown namespace (not defined in Centrifugo configuration): curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"unknown:chat\", \"data\": {\"text\": \"hello\"}}' \\ http://localhost:8000/api/publish In response you will also get 200 OK, but payload will contain error field instead of result: { \"error\": { \"code\": 102, \"message\": \"namespace not found\" }} error object contains error code and message - this is also the same for other commands described below. Publish request​ Field name Field type Required Description channel string yes Name of channel to publish data any JSON yes Custom JSON data to publish into a channel skip_history bool no Skip adding publication to history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Publish result​ Field name Field type Optional Description offset integer yes Offset of publication in history stream epoch string yes Epoch of current stream","s":"publish","u":"/docs/server/server_api","h":"#publish","p":3493},{"i":3504,"t":"broadcast is similar to publish but allows to efficiently send the same data into many channels: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channels\": [\"user:1\", \"user:2\"], \"data\": {\"text\": \"hello\"}}' \\ http://localhost:8000/api/broadcast Broadcast request​ Field name Field type Required Description channels Array of strings yes List of channels to publish data to data any JSON yes Custom JSON data to publish into each channel skip_history bool no Skip adding publications to channels' history for this request tags map[string]string no Publication tags - map with arbitrary string keys and values which is attached to publication and will be delivered to clients b64data string no Custom binary data to publish into a channel encoded to base64 so it's possible to use HTTP API to send binary to clients. Centrifugo will decode it from base64 before publishing. In case of GRPC you can publish binary using data field. Broadcast result​ Field name Field type Optional Description responses Array of publish responses no Responses for each individual publish (with possible error and publish result)","s":"broadcast","u":"/docs/server/server_api","h":"#broadcast","p":3493},{"i":3506,"t":"subscribe allows subscribing active user's sessions to a channel. Note, it's mostly for dynamic server-side subscriptions. Subscribe request​ Field name Field type Required Description user string yes User ID to subscribe channel string yes Name of channel to subscribe user to info any JSON no Attach custom data to subscription (will be used in presence and join/leave messages) b64info string no info in base64 for binary mode (will be decoded by Centrifugo) client string no Specific client ID to subscribe (user still required to be set, will ignore other user connections with different client IDs) session string no Specific client session to subscribe (user still required to be set) data any JSON no Custom subscription data (will be sent to client in Subscribe push) b64data string no Same as data but in base64 format (will be decoded by Centrifugo) recover_since StreamPosition object no Stream position to recover from override Override object no Allows dynamically override some channel options defined in Centrifugo configuration (see below available fields) Override object​ Field Type Optional Description presence BoolValue yes Override presence join_leave BoolValue yes Override join_leave force_push_join_leave BoolValue yes Override force_push_join_leave force_positioning BoolValue yes Override force_positioning force_recovery BoolValue yes Override force_recovery BoolValue is an object like this: { \"value\": true/false} Subscribe result​ Empty object at the moment.","s":"subscribe","u":"/docs/server/server_api","h":"#subscribe","p":3493},{"i":3508,"t":"unsubscribe allows unsubscribing user from a channel. Unsubscribe request​ Field name Field type Required Description user string yes User ID to unsubscribe channel string yes Name of channel to unsubscribe user to client string no Specific client ID to unsubscribe (user still required to be set) session string no Specific client session to disconnect (user still required to be set). Unsubscribe result​ Empty object at the moment.","s":"unsubscribe","u":"/docs/server/server_api","h":"#unsubscribe","p":3493},{"i":3510,"t":"disconnect allows disconnecting a user by ID. Disconnect request​ Field name Field type Required Description user string yes User ID to disconnect client string no Specific client ID to disconnect (user still required to be set) session string no Specific client session to disconnect (user still required to be set). whitelist Array of strings no Array of client IDs to keep disconnect Disconnect object no Provide custom disconnect object, see below Disconnect object​ Field name Field type Required Description code int yes Disconnect code reason string yes Disconnect reason Disconnect result​ Empty object at the moment.","s":"disconnect","u":"/docs/server/server_api","h":"#disconnect","p":3493},{"i":3512,"t":"refresh allows refreshing user connection (mostly useful when unidirectional transports are used). Refresh request​ Field name Field type Required Description user string yes User ID to refresh client string no Client ID to refresh (user still required to be set) session string no Specific client session to refresh (user still required to be set). expired bool no Mark connection as expired and close with Disconnect Expired reason expire_at int no Unix time (in seconds) in the future when the connection will expire Refresh result​ Empty object at the moment.","s":"refresh","u":"/docs/server/server_api","h":"#refresh","p":3493},{"i":3514,"t":"presence allows getting channel online presence information (all clients currently subscribed on this channel). tip Presence in channels is not enabled by default. See how to enable it over channel options. Also check out dedicated chapter about it. curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\"}' \\ http://localhost:8000/api/presence Example response: { \"result\": { \"presence\": { \"c54313b2-0442-499a-a70c-051f8588020f\": { \"client\": \"c54313b2-0442-499a-a70c-051f8588020f\", \"user\": \"42\" }, \"adad13b1-0442-499a-a70c-051f858802da\": { \"client\": \"adad13b1-0442-499a-a70c-051f858802da\", \"user\": \"42\" } } }} Presence request​ Field name Field type Required Description channel string yes Name of channel to call presence from Presence result​ Field name Field type Optional Description presence Map of client ID (string) to ClientInfo object no Offset of publication in history stream ClientInfo​ Field name Field type Optional Description client string no Client ID user string no User ID conn_info JSON yes Optional connection info chan_info JSON yes Optional channel info","s":"presence","u":"/docs/server/server_api","h":"#presence","p":3493},{"i":3516,"t":"presence_stats allows getting short channel presence information - number of clients and number of unique users (based on user ID). curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\"}' \\ http://localhost:8000/api/presence_stats Example response: { \"result\": { \"num_clients\": 0, \"num_users\": 0 }} Presence stats request​ Field name Field type Required Description channel string yes Name of channel to call presence from Presence stats result​ Field name Field type Optional Description num_clients integer no Total number of clients in channel num_users integer no Total number of unique users in channel","s":"presence_stats","u":"/docs/server/server_api","h":"#presence_stats","p":3493},{"i":3518,"t":"history allows getting channel history information (list of last messages published into the channel). By default if no limit parameter set in request history call will only return current stream position information - i.e. offset and epoch fields. To get publications you must explicitly provide limit parameter. See also history API description in special doc chapter. tip History in channels is not enabled by default. See how to enable it over channel options. curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"channel\": \"chat\", \"limit\": 2}' \\ http://localhost:8000/api/history Example response: { \"result\": { \"epoch\": \"qFhv\", \"offset\": 4, \"publications\": [ { \"data\": { \"text\": \"hello\" }, \"offset\": 2 }, { \"data\": { \"text\": \"hello\" }, \"offset\": 3 } ] }} History request​ Field name Field type Required Description channel string yes Name of channel to call history from limit int no Limit number of returned publications, if not set in request then only current stream position information will present in result (without any publications) since StreamPosition object no To return publications after this position reverse bool no Iterate in reversed order (from latest to earliest) StreamPosition​ Field name Field type Required Description offset integer yes Offset in a stream epoch string yes Stream epoch History result​ Field name Field type Optional Description publications Array of publication objects yes List of publications in channel offset integer yes Top offset in history stream epoch string yes Epoch of current stream","s":"history","u":"/docs/server/server_api","h":"#history","p":3493},{"i":3520,"t":"history_remove allows removing publications in channel history. Current top stream position meta data kept untouched to avoid client disconnects due to insufficient state. History remove request​ Field name Field type Required Description channel string yes Name of channel to remove history History remove result​ Empty object at the moment.","s":"history_remove","u":"/docs/server/server_api","h":"#history_remove","p":3493},{"i":3522,"t":"channels return active channels (with one or more active subscribers in it). curl --header \"X-API-Key: \" \\ --request POST \\ --data '{}' \\ http://localhost:8000/api/channels Channels request​ Field name Field type Required Description pattern string no Pattern to filter channels, we are using gobwas/glob library for matching Channels result​ Field name Field type Optional Description channels Map of string to ChannelInfo no Map where key is channel and value is ChannelInfo (see below) ChannelInfo​ Field name Field type Optional Description num_clients integer no Total number of connections currently subscribed to a channel caution Keep in mind that since the channels method by default returns all active channels it can be really heavy for massive deployments. Centrifugo does not provide a way to paginate over channels list. At the moment we mostly suppose that channels API call will be used in the development process or for administrative/debug purposes, and in not very massive Centrifugo setups (with no more than 10k active channels). A better and scalable approach for huge setups could be a real-time analytics approach described here.","s":"channels","u":"/docs/server/server_api","h":"#channels","p":3493},{"i":3524,"t":"info method allows getting information about running Centrifugo nodes. Example response: { \"result\": { \"nodes\": [ { \"name\": \"Alexanders-MacBook-Pro.local_8000\", \"num_channels\": 0, \"num_clients\": 0, \"num_users\": 0, \"uid\": \"f844a2ed-5edf-4815-b83c-271974003db9\", \"uptime\": 0, \"version\": \"\" } ] }} Info request​ Empty object at the moment. Info result​ Field name Field type Optional Description nodes Array of Node objects no Information about all nodes in a cluster","s":"info","u":"/docs/server/server_api","h":"#info","p":3493},{"i":3526,"t":"Batch allows sending many commands in one request. Commands processed sequentially by Centrifugo, users should check individual error in each returned reply. Useful to avoid RTT latency penalty for each command sent, this is an analogue of pipelining. Example with two publications in one request: curl --header \"X-API-Key: \" \\ --request POST \\ --data '{\"commands\": [{\"publish\": {\"channel\": \"test1\", \"data\": {}}}, {\"publish\": {\"channel\": \"x:test2\", \"data\": {}}}]}' \\ http://localhost:8000/api/batch Example response: { \"replies\":[ {\"publish\":{}}, {\"error\":{\"code\":102,\"message\":\"unknown channel\"}} ]}","s":"batch","u":"/docs/server/server_api","h":"#batch","p":3493},{"i":3528,"t":"Sending an API request to Centrifugo is a simple task to do in any programming language - this is just a POST request with JSON payload in body and Authorization header. But we have several official HTTP API libraries for different languages, to help developers to avoid constructing proper HTTP requests manually: cent for Python phpcent for PHP gocent for Go rubycent for Ruby Also, there are API libraries created by community: crystalcent API client for Crystal language cent.js API client for NodeJS Centrifugo.AspNetCore API client for ASP.NET Core tip Also, keep in mind that Centrifugo has GRPC API so you can automatically generate client API code for your language.","s":"HTTP API libraries","u":"/docs/server/server_api","h":"#http-api-libraries","p":3493},{"i":3530,"t":"Centrifugo also supports GRPC API. With GRPC it's possible to communicate with Centrifugo using a more compact binary representation of commands and use the power of HTTP/2 which is the transport behind GRPC. GRPC API is also useful if you want to publish binary data to Centrifugo channels. tip GRPC API allows calling all commands described in HTTP API doc, actually both GRPC and HTTP API in Centrifugo based on the same Protobuf schema definition. So refer to the HTTP API description doc for the parameter and the result field description. You can enable GRPC API in Centrifugo using grpc_api option: config.json { ... \"grpc_api\": true} By default, GRPC will be served on port 10000 but you can change it using the grpc_api_port option. Now, as soon as Centrifugo started – you can send GRPC commands to it. To do this get our API Protocol Buffer definitions from this file. Then see GRPC docs specific to your language to find out how to generate client code from definitions and use generated code to communicate with Centrifugo.","s":"GRPC API","u":"/docs/server/server_api","h":"#grpc-api","p":3493},{"i":3532,"t":"For example for Python you need to run sth like this according to GRPC docs: pip install grpcio-toolspython -m grpc_tools.protoc -I ./ --python_out=. --grpc_python_out=. api.proto As soon as you run the command you will have 2 generated files: api_pb2.py and api_pb2_grpc.py. Now all you need is to write a simple program that uses generated code and sends GRPC requests to Centrifugo: import grpcimport api_pb2_grpc as api_grpcimport api_pb2 as api_pbchannel = grpc.insecure_channel('localhost:10000')stub = api_grpc.CentrifugoApiStub(channel)try: resp = stub.Info(api_pb.InfoRequest())except grpc.RpcError as err: # GRPC level error. print(err.code(), err.details())else: if resp.error.code: # Centrifugo server level error. print(resp.error.code, resp.error.message) else: print(resp.result) Note that you need to explicitly handle Centrifugo API level error which is not transformed automatically into GRPC protocol-level error.","s":"GRPC example for Python","u":"/docs/server/server_api","h":"#grpc-example-for-python","p":3493},{"i":3534,"t":"Here is a simple example of how to run Centrifugo with the GRPC Go client. You need protoc, protoc-gen-go and protoc-gen-go-grpc installed. First start Centrifugo itself with GRPC API enabled: CENTRIFUGO_GRPC_API=1 centrifugo --config config.json In another terminal tab: mkdir centrifugo_grpc_examplecd centrifugo_grpc_example/touch main.gogo mod init centrifugo_examplemkdir apiprotocd apiprotowget https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto -O api.proto Run protoc to generate code: protoc -I ./ api.proto --go_out=. --go-grpc_out=. Put the following code to main.go file (created on the last step above): package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure()) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoApiClient(conn) for { resp, err := client.Publish(context.Background(), &apiproto.PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} Then run: go run main.go The program starts and periodically publishes the same payload into chat:index channel.","s":"GRPC example for Go","u":"/docs/server/server_api","h":"#grpc-example-for-go","p":3493},{"i":3536,"t":"You can also set grpc_api_key (string) in Centrifugo configuration to protect GRPC API with key. In this case, you should set per RPC metadata with key authorization and value apikey . For example in Go language: package mainimport ( \"context\" \"log\" \"time\" \"centrifugo_example/apiproto\" \"google.golang.org/grpc\")type keyAuth struct { key string}func (t keyAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { return map[string]string{ \"authorization\": \"apikey \" + t.key, }, nil}func (t keyAuth) RequireTransportSecurity() bool { return false}func main() { conn, err := grpc.Dial(\"localhost:10000\", grpc.WithInsecure(), grpc.WithPerRPCCredentials(keyAuth{\"xxx\"})) if err != nil { log.Fatalln(err) } defer conn.Close() client := apiproto.NewCentrifugoClient(conn) for { resp, err := client.Publish(context.Background(), &PublishRequest{ Channel: \"chat:index\", Data: []byte(`{\"input\": \"hello from GRPC\"}`), }) if err != nil { log.Printf(\"Transport level error: %v\", err) } else { if resp.GetError() != nil { respError := resp.GetError() log.Printf(\"Error %d (%s)\", respError.Code, respError.Message) } else { log.Println(\"Successfully published\") } } time.Sleep(time.Second) }} For other languages refer to GRPC docs.","s":"GRPC API key authorization","u":"/docs/server/server_api","h":"#grpc-api-key-authorization","p":3493},{"i":3538,"t":"By default, Centrifugo server API never returns transport level errors - for example it always returns 200 OK for HTTP API and never returns GRPC transport-level errors. Centrifugo returns its custom errors from API calls inside optional error field of response as we showed above in this doc. This means that API call to Centrifigo API may returns 200 OK, but in the error field you may find Centrifugo-specific 100: internal error. Since Centrifugo v5.1.0 Centrifigo has an option to use transport-native error codes instead of Centrifugo error field in the response. The main motivation is make API calls friendly to integrate with the network ecosystem - for automatic retries, better logging, etc. In many situations this may be more obvious for humans also. Let's show an example. Without any special options HTTP request to Centrifigo server API which contains error in response looks like this: ❯ echo '{}' | http POST \"http://localhost:8000/api/publish\"HTTP/1.1 200 OKContent-Length: 46Content-Type: application/jsonDate: Sat, 19 Aug 2023 07:23:40 GMT{ \"error\": { \"code\": 107, \"message\": \"bad request\" }} Note - it returns 200 OK even though response contains error field. With transport error mode request-response may be transformed into the following: ❯ echo '{}' | http POST \"http://localhost:8000/api/publish\" \"X-Centrifugo-Error-Mode: transport\"HTTP/1.1 400 Bad RequestContent-Length: 36Content-Type: application/jsonDate: Sat, 19 Aug 2023 07:23:59 GMT{ \"code\": 107, \"message\": \"bad request\"} Transport error mode may be turned on globally: using \"api_error_mode\": \"transport\" option for HTTP server API using \"grpc_api_error_mode\": \"transport\" option for GRPC server API Also, this mode may be used on per-request basis: by setting custom header X-Centrifugo-Error-Mode: transport for HTTP (as we just showed in the example) adding custom metadata key x-centrifugo-error-mode: transport for GRPC caution Note, that transport error mode does not help a lot with Batch and Broadcast APIs which are quite special because these calls contain many independent operations. For these calls you still need to look at individual error objects in response. To achieve the goal we have an internal matching of Centrifugo API error codes to HTTP and GRPC error codes.","s":"Transport error mode","u":"/docs/server/server_api","h":"#transport-error-mode","p":3493},{"i":3540,"t":"func MapErrorToHTTPCode(err *Error) int { switch err.Code { case ErrorInternal.Code: // 100 -> HTTP 500 return http.StatusInternalServerError case ErrorUnknownChannel.Code, ErrorNotFound.Code: // 102, 104 -> HTTP 404 return http.StatusNotFound case ErrorBadRequest.Code, ErrorNotAvailable.Code: // 107, 108 -> HTTP 400 return http.StatusBadRequest case ErrorUnrecoverablePosition.Code: // 112 -> HTTP 416 return http.StatusRequestedRangeNotSatisfiable case ErrorConflict.Code: // 113 -> HTTP 409 return http.StatusConflict default: // Default to Internal Error for unmapped errors. // In general should be avoided - all new API errors must be explicitly described here. return http.StatusInternalServerError // HTTP 500 }}","s":"Centrifugo error code to HTTP code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-http-code","p":3493},{"i":3542,"t":"func MapErrorToGRPCCode(err *Error) codes.Code { switch err.Code { case ErrorInternal.Code: // 100 return codes.Internal case ErrorUnknownChannel.Code, ErrorNotFound.Code: // 102, 104 return codes.NotFound case ErrorBadRequest.Code, ErrorNotAvailable.Code: // 107, 108 return codes.InvalidArgument case ErrorUnrecoverablePosition.Code: // 112 return codes.OutOfRange case ErrorConflict.Code: // 113 return codes.AlreadyExists default: // Default to Internal Error for unmapped errors. // In general should be avoided - all new API errors must be explicitly described here. return codes.Internal }}","s":"Centrifugo error code to GRPC code","u":"/docs/server/server_api","h":"#centrifugo-error-code-to-grpc-code","p":3493},{"i":3544,"t":"On this page","s":"Unidirectional GRPC","u":"/docs/transports/uni_grpc","h":"","p":3543},{"i":3546,"t":"JSON and binary.","s":"Supported data formats","u":"/docs/transports/uni_grpc","h":"#supported-data-formats","p":3543},{"i":3549,"t":"Boolean, default: false. Enables unidirectional GRPC endpoint. config.json { ... \"uni_grpc\": true}","s":"uni_grpc","u":"/docs/transports/uni_grpc","h":"#uni_grpc","p":3543},{"i":3551,"t":"String, default \"11000\". Port to listen on.","s":"uni_grpc_port","u":"/docs/transports/uni_grpc","h":"#uni_grpc_port","p":3543},{"i":3553,"t":"String, default \"\" (listen on all interfaces) Address to bind uni GRPC to.","s":"uni_grpc_address","u":"/docs/transports/uni_grpc","h":"#uni_grpc_address","p":3543},{"i":3555,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from GRPC connection in bytes.","s":"uni_grpc_max_receive_message_size","u":"/docs/transports/uni_grpc","h":"#uni_grpc_max_receive_message_size","p":3543},{"i":3557,"t":"Boolean, default: false Enable custom TLS for unidirectional GRPC server.","s":"uni_grpc_tls","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls","p":3543},{"i":3559,"t":"String, default: \"\". Path to cert file.","s":"uni_grpc_tls_cert","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_cert","p":3543},{"i":3561,"t":"String, default: \"\". Path to key file.","s":"uni_grpc_tls_key","u":"/docs/transports/uni_grpc","h":"#uni_grpc_tls_key","p":3543},{"i":3563,"t":"We don't have example here yet. In general, the algorithm is like this: Copy Protobuf definitions Generate GRPC client code Use generated code to connect to Centrifugo Process Push messages, drop unknown Pushes, handle those necessary for the application.","s":"Example","u":"/docs/transports/uni_grpc","h":"#example","p":3543},{"i":3565,"t":"On this page","s":"Unidirectional HTTP streaming","u":"/docs/transports/uni_http_stream","h":"","p":3564},{"i":3567,"t":"It's possible to pass initial connect command by posting a JSON body to a streaming endpoint. Refer to the full Connect command description – it's the same as for unidirectional WebSocket.","s":"Connect command","u":"/docs/transports/uni_http_stream","h":"#connect-command","p":3564},{"i":3569,"t":"JSON","s":"Supported data formats","u":"/docs/transports/uni_http_stream","h":"#supported-data-formats","p":3564},{"i":3571,"t":"Centrifugo will send different message types to a connection. Every message is JSON encoded. A special JSON value null used as a PING message. You can simply ignore it on a client side upon receiving. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/transports/uni_http_stream","h":"#pings","p":3564},{"i":3574,"t":"Boolean, default: false. Enables unidirectional HTTP streaming endpoint. config.json { ... \"uni_http_stream\": true}","s":"uni_http_stream","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream","p":3564},{"i":3576,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes.","s":"uni_http_stream_max_request_body_size","u":"/docs/transports/uni_http_stream","h":"#uni_http_stream_max_request_body_size","p":3564},{"i":3578,"t":"Let's look how simple it is to connect to Centrifugo using HTTP streaming. We will start from scratch, generate new configuration file: centrifugo genconfig Turn on uni HTTP stream and automatically subscribe users to personal channel upon connect: config.json { ... \"uni_http_stream\": true, \"user_subscribe_to_personal\": true} Run Centrifugo: centrifugo -c config.json In separate terminal window create token for a user: ❯ go run main.go gentoken -u user12HMAC SHA-256 JWT for user user12 with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM Then connect to Centrifugo uni HTTP stream endpoint with simple CURL POST request: curl -X POST http://localhost:8000/connection/uni_http_stream \\ -d '{\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIiLCJleHAiOjE2MjUwNzMyODh9.BxmS4R-X6YXMxLfXNhYRzeHvtu_M2NCaXF6HNu7VnDM\"}' Open one more terminal window and publish message to a personal user channel: curl -X POST http://localhost:8000/api/publish \\ -d '{\"channel\": \"#user12\", \"data\": {\"input\": \"hello\"}}' \\ -H \"Authorization: apikey 9230f514-34d2-4971-ace2-851c656e81dc\" You should see this messages coming from server. {} messages are pings from a server. That's all, happy streaming!","s":"Connecting using CURL","u":"/docs/transports/uni_http_stream","h":"#connecting-using-curl","p":3564},{"i":3580,"t":"A basic browser will come soon as we update docs for v4.","s":"Browser example","u":"/docs/transports/uni_http_stream","h":"#browser-example","p":3564},{"i":3582,"t":"On this page","s":"Unidirectional SSE (EventSource)","u":"/docs/transports/uni_sse","h":"","p":3581},{"i":3584,"t":"Unfortunately SSE specification does not allow POST requests from a web browser, so the only way to pass initial connect command is over URL params. Centrifugo is looking for cf_connect URL param for connect command. Connect command value expected to be a JSON-encoded string, properly encoded into URL. For example: const url = new URL('http://localhost:8000/connection/uni_sse');url.searchParams.append(\"cf_connect\", JSON.stringify({ 'token': ''}));const eventSource = new EventSource(url); Refer to the full Connect command description – it's the same as for unidirectional WebSocket. The length of URL query should be kept less than 2048 characters to work throughout browsers. This should be more than enough for most use cases. tip Centrifugo unidirectional SSE endpoint also supports POST requests. While it's not very useful for browsers which only allow GET requests for EventSource this can be useful when connecting from a mobile device. In this case you must send the connect object as a JSON body of a POST request (instead of using cf_connect URL parameter), similar to what we have in unidirectional HTTP streaming transport case.","s":"Connect command","u":"/docs/transports/uni_sse","h":"#connect-command","p":3581},{"i":3586,"t":"JSON","s":"Supported data formats","u":"/docs/transports/uni_sse","h":"#supported-data-formats","p":3581},{"i":3589,"t":"Boolean, default: false. Enables unidirectional SSE (EventSource) endpoint. config.json { ... \"uni_sse\": true}","s":"uni_sse","u":"/docs/transports/uni_sse","h":"#uni_sse","p":3581},{"i":3591,"t":"Default: 65536 (64KB) Maximum allowed size of a initial HTTP POST request in bytes when using HTTP POST requests to connect (browsers are using GET so it's not applied).","s":"uni_sse_max_request_body_size","u":"/docs/transports/uni_sse","h":"#uni_sse_max_request_body_size","p":3581},{"i":3593,"t":"Coming soon.","s":"Browser example","u":"/docs/transports/uni_sse","h":"#browser-example","p":3581},{"i":3595,"t":"On this page","s":"Unidirectional WebSocket","u":"/docs/transports/uni_websocket","h":"","p":3594},{"i":3597,"t":"It's possible to send connect command as first WebSocket message (as JSON). Field name Field type Required Description token string no Connection JWT, not required when using the connect proxy feature. data any JSON no Custom JSON connection data name string no Application name version string no Application version subs map of channel to SubscribeRequest no Pass an information about desired subscriptions to a server","s":"Connect command","u":"/docs/transports/uni_websocket","h":"#connect-command","p":3594},{"i":3599,"t":"Field name Field type Required Description recover boolean no Whether a client wants to recover from a certain position offset integer no Known stream position offset when recover is used epoch string no Known stream position epoch when recover is used","s":"SubscribeRequest","u":"/docs/transports/uni_websocket","h":"#subscriberequest","p":3594},{"i":3601,"t":"JSON","s":"Supported data formats","u":"/docs/transports/uni_websocket","h":"#supported-data-formats","p":3594},{"i":3603,"t":"Centrifugo uses empty commands ({} in JSON case) as pings for unidirectional WS. You can ignore such messages or use them to detect broken connections (nothing received from a server for a long time).","s":"Pings","u":"/docs/transports/uni_websocket","h":"#pings","p":3594},{"i":3606,"t":"Boolean, default: false. Enables unidirectional WebSocket endpoint. config.json { ... \"uni_websocket\": true}","s":"uni_websocket","u":"/docs/transports/uni_websocket","h":"#uni_websocket","p":3594},{"i":3608,"t":"Default: 65536 (64KB) Maximum allowed size of a first connect message received from WebSocket connection in bytes.","s":"uni_websocket_message_size_limit","u":"/docs/transports/uni_websocket","h":"#uni_websocket_message_size_limit","p":3594},{"i":3610,"t":"Let's connect to a unidirectional WebSocket endpoint using wscat tool – it allows connecting to WebSocket servers interactively from a terminal. First, run Centrifugo with uni_websocket enabled. Also let's enable automatic personal channel subscriptions for users. Configuration example: config.json { \"token_hmac_secret_key\": \"secret\", \"uni_websocket\":true, \"user_subscribe_to_personal\": true} Run Centrifugo: ./centrifugo -c config.json In another terminal: ❯ ./centrifugo gentoken -c config.json -u test_userHMAC SHA-256 JWT for user test_user with expiration TTL 168h0m0s:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2MzAxMzAxNzB9.u7anX-VYXywX1p1lv9UC9CAu04vpA6LgG5gsw5lz1Iw Install wscat and run: wscat -c \"ws://localhost:8000/connection/uni_websocket\" This will establish a connection with a server and you then can send connect command to a server: ❯ wscat -c \"ws://localhost:8000/connection/uni_websocket\"Connected (press CTRL+C to quit)> {\"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjE2NTY1MDMwNDV9.3UYL-UCUBp27TybeBK7Z0OenwdsKwCMRe46fuEjJnzI\", \"subs\": {\"abc\": {}}}< {\"connect\":{\"client\":\"bfd28799-b958-4791-b9e9-b011eaef68c1\",\"version\":\"0.0.0\",\"subs\":{\"#test_user\":{}},\"expires\":true,\"ttl\":604407,\"ping\":25,\"session\":\"57b1287b-44ec-45c8-93fc-696c5294af25\"}} The connection will receive server pings (empty commands {}) periodically. You can try to publish something to #test_user or abc channels (using Centrifugo server API or using admin UI) – and the message should come to the connection we just established.","s":"Example","u":"/docs/transports/uni_websocket","h":"#example","p":3594},{"i":3612,"t":"WebTransport WebTransport is an API offering low-latency, bidirectional, client-server messaging on top of HTTP/3 (with QUIC under the hood). See Using WebTransport article that gives a good overview of it. danger WebTransport support in Centrifugo is EXPERIMENTAL and not recommended for production usage. WebTransport IETF specification is not finished yet and may have breaking changes. To use WebTransport you first need to run HTTP/3 experimental server and enable webtransport endpoint: config.json { \"http3\": true, \"tls\": true, \"tls_cert\": \"path/to/crt\", \"tls_key\": \"path/to/key\", \"webtransport\": true} In HTTP/3 and WebTransport case TLS is required. tip At the time of writing only Chrome (since v97) supports WebTransport API. If you are experimenting with self-signed certificates you may need to run Chrome with flags to force HTTP/3 on origin and ignore certificate errors: /path/to/your/Chrome --origin-to-force-quic-on=localhost:8000 --ignore-certificate-errors-spki-list=TSZTiMjLG+DNjESXdJh3f+S8C+RhsFCav7T24VNuCPQ= Where the value of --ignore-certificate-errors-spki-list is a certificate fingerprint obtained this way: openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 With not self-signed certs things should work just fine in Chrome. Here is a video tutorial that shows this in action: After starting Centrifugo with HTTP/3 and WebTransport endpoint you can connect to that endpoint (by default – /connection/webtransport) using centrifuge-js. For example, let's enable WebTransport and will use WebSocket as a fallback option: const transports = [ { transport: 'webtransport', endpoint: 'https://localhost:8000/connection/webtransport' }, { transport: 'websocket', endpoint: 'wss://localhost:8000/connection/websocket' }];const centrifuge = new Centrifuge(transports);centrifuge.connect() Note, that we are using secure schemes here – https:// and wss://. While in WebSocket case you could opt for non-TLS communication, in WebTransport case non-TLS http:// scheme is simply not supported by the specification. Also, Chrome may not automatically close WebTransport sessions upon browser reload, so consider adding: addEventListener(\"unload\", (event) => { centrifuge.disconnect() }); tip Make sure you run Centrifugo without load balancer or reverse proxy in front, or make sure your proxy can proxy HTTP/3 traffic to Centrifugo. In Centrifugo case, we utilize a single bidirectional stream of WebTransport to pass our protocol between client and server. Both JSON and Protobuf communication are supported. There are some issues with the proper passing of the disconnect advice in some cases, otherwise it's fully functional. Obviously, due to the limited WebTransport support in browsers at the moment, possible breaking changes in the WebTransport specification it's an experimental feature. And it's not recommended for production usage for now. At some point in the future, it may become a reasonable alternative to WebSocket.","s":"WebTransport","u":"/docs/transports/webtransport","h":"","p":3611},{"i":3614,"t":"On this page","s":"WebSocket","u":"/docs/transports/websocket","h":"","p":3613},{"i":3617,"t":"Default: 65536 (64KB) Maximum allowed size of a message received from WebSocket connection in bytes.","s":"websocket_message_size_limit","u":"/docs/transports/websocket","h":"#websocket_message_size_limit","p":3613},{"i":3619,"t":"In bytes, by default 0 which tells Centrifugo to reuse read buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but can increase number of system calls depending on average message size). config.json { ... \"websocket_read_buffer_size\": 512}","s":"websocket_read_buffer_size","u":"/docs/transports/websocket","h":"#websocket_read_buffer_size","p":3613},{"i":3621,"t":"In bytes, by default 0 which tells Centrifugo to reuse write buffer from HTTP server for WebSocket connection (usually 4096 bytes in size). If set to a lower value can reduce memory usage per WebSocket connection (but HTTP buffer won't be reused): config.json { ... \"websocket_write_buffer_size\": 512}","s":"websocket_write_buffer_size","u":"/docs/transports/websocket","h":"#websocket_write_buffer_size","p":3613},{"i":3623,"t":"If you have a few writes then websocket_use_write_buffer_pool (boolean, default false) option can reduce memory usage of Centrifugo a bit as there won't be separate write buffer binded to each WebSocket connection.","s":"websocket_use_write_buffer_pool","u":"/docs/transports/websocket","h":"#websocket_use_write_buffer_pool","p":3613},{"i":3625,"t":"An experimental feature for raw WebSocket endpoint - permessage-deflate compression for websocket messages. Btw look at great article about websocket compression. WebSocket compression can reduce an amount of traffic travelling over the wire. We consider this experimental because this websocket compression is experimental in Gorilla Websocket library that Centrifugo uses internally. caution Enabling WebSocket compression will result in much slower Centrifugo performance and more memory usage – depending on your message rate this can be very noticeable. To enable WebSocket compression for raw WebSocket endpoint set websocket_compression to true in a configuration file. After this clients that support permessage-deflate will negotiate compression with server automatically. Note that enabling compression does not mean that every connection will use it - this depends on client support for this feature. Another option is websocket_compression_min_size. Default 0. This is a minimal size of message in bytes for which we use deflate compression when writing it to client's connection. Default value 0 means that we will compress all messages when websocket_compression enabled and compression support negotiated with client. It's also possible to control websocket compression level defined at compress/flate By default when compression with a client negotiated Centrifugo uses compression level 1 (BestSpeed). If you want to set custom compression level use websocket_compression_level configuration option.","s":"websocket_compression","u":"/docs/transports/websocket","h":"#websocket_compression","p":3613},{"i":3627,"t":"In most cases you will use Centrifugo with JSON protocol which is used by default. It consists of simple human-readable frames that can be easily inspected. Also it's a very simple task to publish JSON encoded data to HTTP API endpoint. You may want to use binary Protobuf client protocol if: you want less traffic on wire as Protobuf is very compact you want maximum performance on server-side as Protobuf encoding/decoding is very efficient you can sacrifice human-readable JSON for your application Binary protobuf protocol only works for raw Websocket connections (as SockJS can't deal with binary). With most clients to use binary you just need to provide query parameter format to Websocket URL, so final URL look like: wss://centrifugo.example.com/connection/websocket?format=protobuf After doing this Centrifugo will use binary frames to pass data between client and server. Your application specific payload can be random bytes. tip You still can continue to encode your application specific data as JSON when using Protobuf protocol thus have a possibility to co-exist with clients that use JSON protocol on the same Centrifugo installation inside the same channels.","s":"Protobuf binary protocol","u":"/docs/transports/websocket","h":"#protobuf-binary-protocol","p":3613},{"i":3629,"t":"Centrifugo supports a special url parameter for bidirectional websocket which turns on using native WebSocket frame ping-pong mechanism instead of server-to-client application level pings Centrifugo uses by default. This simplifies debugging Centrifugo protocol with tools like Postman, wscat, websocat, etc. By default, it may be inconvenient due to the fact Centrifugo sends periodic ping message to the client ({} in JSON protocol scenario) and expects pong response back within some time period. Otherwise Centrifugo closes connection. This results in problems with mentioned tools because you had to manually send {} pong message upon ping message. So typical session in wscat could look like this: ❯ wscat --connect ws://localhost:8000/connection/websocketConnected (press CTRL+C to quit)> {\"id\": 1, \"connect\": {}}< {\"id\":1,\"connect\":{\"client\":\"9ac9de4e-5289-4ad6-9aa7-8447f007083e\",\"version\":\"0.0.0\",\"ping\":25,\"pong\":true}}< {}Disconnected (code: 3012, reason: \"no pong\") The parameter is called cf_ws_frame_ping_pong, to use it connect to Centrifugo bidirectional WebSocket endpoint like ws://localhost:8000/connection/websocket?cf_ws_frame_ping_pong=true. Here is an example which demonstrates working with Postman WebSocket where we connect to local Centrifugo and subscribe to two channels test1 and test2: You can then proceed to Centrifugo admin web UI, publish something to these channels and see publications in Postman. Note, how we sent several JSON commands in one WebSocket frame to Centrifugo from Postman in the example above - this is possible since Centrifugo protocol supports batches of commands in line-delimited format. We consider this feature to be used only for debugging, in production prefer using our SDKs without using cf_ws_frame_ping_pong parameter – because app-level ping-pong is more efficient and our SDKs detect broken connections due to it.","s":"Debugging with Postman, wscat, etc","u":"/docs/transports/websocket","h":"#debugging-with-postman-wscat-etc","p":3613},{"i":3631,"t":"On this page","s":"Client SDK API","u":"/docs/transports/client_api","h":"","p":3630},{"i":3633,"t":"Client connection has 4 states: disconnected connecting connected closed note closed state is only implemented by SDKs where it makes sense (need to clean up allocated resources when app gracefully shuts down – for example in Java SDK we close thread executors used internally). When a new Client is created it has a disconnected state. To connect to a server connect() method must be called. After calling connect Client moves to the connecting state. If a Client can't connect to a server it attempts to create a connection with an exponential backoff algorithm (with full jitter). If a connection to a server is successful then the state becomes connected. If a connection is lost (due to a missing network for example, or due to reconnect advice received from a server, or due to some client-side error that can't be recovered without reconnecting) Client goes to the connecting state again. In this state Client tries to reconnect (again, with an exponential backoff algorithm). The Client's state can become disconnected. This happens when Client's disconnect() method was called by a developer. Also, this can happen due to server advice from a server, or due to a terminal problem that happened on the client-side. Here is a program where we create a Client instance, set callbacks to listen to state updates and establish a connection with a server: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('connecting', function(ctx) { console.log('connecting', ctx);});client.on('connected', function(ctx) { console.log('connected', ctx);});client.on('disconnected', function(ctx) { console.log('disconnected', ctx);});client.connect(); final onEvent = (dynamic event) { print('client event> $event');};final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);client.connecting.listen(onEvent);client.connected.listen(onEvent);client.disconnected.listen(onEvent);await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate { func onConnecting(_ c: CentrifugeClient, _ e: CentrifugeConnectingEvent) { print(\"connecting\", e.code, e.reason) } func onConnected(_ client: CentrifugeClient, _ e: CentrifugeConnectedEvent) { print(\"connected with id\", e.client) } func onDisconnected(_ client: CentrifugeClient, _ e: CentrifugeDisconnectedEvent) { print(\"disconnected\", e.code, e.reason) }}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())client.connect() EventListener listener = new EventListener() { @Override public void onConnected(Client client, ConnectedEvent event) { System.out.println(\"connected\"); } @Override public void onConnecting(Client client, ConnectingEvent event) { System.out.printf(\"connecting: %s%n\", event.getReason()); } @Override public void onDisconnected(Client client, DisconnectedEvent event) { System.out.printf(\"disconnected %d %s\", event.getCode(), event.getReason()); }};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)defer client.Close()client.OnConnecting(func(e centrifuge.ConnectingEvent) { log.Printf(\"Connecting - %d (%s)\", e.Code, e.Reason)})client.OnConnected(func(e centrifuge.ConnectedEvent) { log.Printf(\"Connected with ID %s\", e.ClientID)})client.OnDisconnected(func(e centrifuge.DisconnectedEvent) { log.Printf(\"Disconnected: %d (%s)\", e.Code, e.Reason)})_ = client.connect() In case of successful connection Client states will transition like this: disconnected (initial) -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server and then successfully reconnected: connected -> connecting (on('connecting') called) -> connected (on('connected') called). In case of already connected Client temporary lost a connection with a server, but got a terminal error upon reconnection: connected -> connecting (on('connecting') called) -> disconnected (on('disconnected') called). In case of already connected Client came across terminal condition (for example, if during a connection token refresh application found that user has no permission to connect anymore): connected -> disconnected (on('disconnected') called). Both connecting and disconnected events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why the Client went to the connecting state or to the disconnected state. This diagram demonstrates possible Client state transitions: You can also listen for all errors happening internally (which are not reflected by state changes, for example, transport errors happening on initial connect, transport during reconnect, connection token refresh related errors, etc) while the client works by using error event: client.on('error', function(ctx) { console.log('client error', ctx);}); If you want to disconnect from a server call .disconnect() method: client.disconnect(); In this case on('disconnected') will be called. You can call connect() again when you need to establish a connection. closed state implemented in SDKs where resources like internal queues, thread executors, etc must be cleaned up when the Client is not needed anymore. All subscriptions should automatically go to the unsubscribed state upon closing. The client is not usable after going to a closed state.","s":"Client connection states","u":"/docs/transports/client_api","h":"#client-connection-states","p":3630},{"i":3635,"t":"There are several common options available when creating Client instance. option to set connection token and callback to get connection token upon expiration (see below mode details) option to set connect data option to configure operation timeout tweaks for reconnect backoff algorithm (min delay, max delay) configure max delay of server pings (to detect broken connection) configure headers to send in WebSocket upgrade request (except centrifuge-js) configure client name and version for analytics purpose","s":"Client common options","u":"/docs/transports/client_api","h":"#client-common-options","p":3630},{"i":3637,"t":"connect() – connect to a server disconnect() - disconnect from a server close() - close Client if not needed anymore send(data) - send asynchronous message to a server rpc(method, data) - send arbitrary RPC and wait for response","s":"Client methods","u":"/docs/transports/client_api","h":"#client-methods","p":3630},{"i":3639,"t":"All SDKs support connecting to Centrifugo with JWT. Initial connection token can be set in Client option upon initialization. Example: const client = new Centrifuge('ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE'}); If the token sets connection expiration then the client SDK will keep the token refreshed. It does this by calling a special callback function. This callback must return a new token. If a new token with updated connection expiration is returned from callback then it's sent to Centrifugo. In case of error returned by your callback SDK will retry the operation after some jittered time. An example: async function getToken() { if (!loggedIn) { return \"\"; // Empty token or pre-generated token for anonymous users. } // Fetch your application backend. const res = await fetch('http://localhost:8000/centrifugo/connection_token'); if (!res.ok) { if (res.status === 403) { // Return special error to not proceed with token refreshes, // client will be disconnected. throw new Centrifuge.UnauthorizedError(); } // Any other error thrown will result into token refresh re-attempts. throw new Error(`Unexpected status code ${res.status}`); } const data = await res.json(); return data.token;}const client = new Centrifuge( 'ws://localhost:8000/connection/websocket', { token: 'JWT-GENERATED-ON-BACKEND-SIDE', // Optional, getToken is enough. getToken: getToken }); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authentication. In this case SDK should attempt to get a connection token before establishing an initial connection.","s":"Client connection token","u":"/docs/transports/client_api","h":"#client-connection-token","p":3630},{"i":3641,"t":"PINGs sent by a server, a client should answer with PONGs upon receiving PING. If a client does not receive PING from a server for a long time (ping interval + configured delay) – the connection is considered broken and will be re-established.","s":"Connection PING/PONG","u":"/docs/transports/client_api","h":"#connection-pingpong","p":3630},{"i":3643,"t":"Client allows subscribing on channels. This can be done by creating Subscription object. const sub = centrifuge.newSubscription(channel);sub.subscribe(); When anewSubscription method is called Client allocates a new Subscription instance and saves it in the internal subscription registry. Having a registry of allocated subscriptions allows SDK to manage resubscribes upon reconnecting to a server. Centrifugo connectors do not allow creating two subscriptions to the same channel – in this case, newSubscription can throw an exception. Subscription has 3 states: unsubscribed subscribing subscribed When a new Subscription is created it has an unsubscribed state. To initiate the actual process of subscribing to a channel subscribe() method of Subscription instance should be called. After calling subscribe() Subscription moves to subscribing state. If subscription to a channel is not successful then depending on error type subscription can automatically resubscribe (with exponential backoff) or go to an unsubscribed state (upon non-temporary error). If subscription to a channel is successful then the state becomes subscribed. Javascript Dart Swift Java Go const sub = client.newSubscription(channel);sub.on('subscribing', function(ctx) { console.log('subscribing');});sub.on('subscribed', function(ctx) { console.log('subscribed');});sub.on('unsubscribed', function(ctx) { console.log('unsubscribed');});sub.subscribe(); final onSubscriptionEvent = (dynamic event) async { print('subscription $channel> $event');};final subscription = client.newSubscription(channel);subscription.subscribing.listen(onSubscriptionEvent);subscription.subscribed.listen(onSubscriptionEvent);subscription.unsubscribed.listen(onSubscriptionEvent);await subscription.subscribe(); class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onSubscribing(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribingEvent) { print(\"subscribing\", e.code, e.reason) } func onSubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeSubscribedEvent) { print(\"subscribed\") } func onUnsubscribed(_ s: CentrifugeSubscription, _ e: CentrifugeUnsubscribedEvent) { print(\"unsubscribed\", e.code, e.reason) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")} SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onSubscribed(Subscription sub, SubscribedEvent event) { System.out.println(\"subscribed to \" + sub.getChannel()); } @Override public void onSubscribing(Subscription sub, SubscribingEvent event) { System.out.printf(\"subscribing \" + sub.getChannel()); } @Override public void onUnsubscribed(Subscription sub, UnsubscribedEvent event) { System.out.println(\"unsubscribed \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();} sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnSubscribing(func(e centrifuge.SubscribingEvent) { log.Printf(\"Subscribing on channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})sub.OnSubscribed(func(e centrifuge.SubscribedEvent) { log.Printf(\"Subscribed on channel %s\", sub.Channel)})sub.OnUnsubscribed(func(e centrifuge.UnsubscribedEvent) { log.Printf(\"Unsubscribed from channel %s - %d (%s)\", sub.Channel, e.Code, e.Reason)})err = sub.Subscribe()if err != nil { log.Fatalln(err)} Subscriptions also go to subscribing state when Client connection (i.e. transport) becomes unavailable. Upon connection re-establishement all subscriptions which are not in unsubscribed state will resubscribe automatically. In case of successful subscription states will transition like this: unsubscribed (initial) -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). In case of connected and subscribed Client temporary lost a connection with a server and then succesfully reconnected and resubscribed: subscribed -> subscribing (on('subscribing') called) -> subscribed (on('subscribed') called). Both subscribing and unsubscribed events have numeric code and human-readable string reason in their context, so you can look at them and find the exact reason why Subscription went to subscribing state or to unsubscribed state. This diagram demonstrates possible Subscription state transitions: You can listen for all errors happening internally in Subscription (which are not reflected by state changes, for example, temporary subscribe errors, subscription token related errors, etc) by using error event: sub.on('error', function(ctx) { console.log(\"subscription error\", ctx);}); If you want to unsubscribe from a channel call .unsubscribe() method: sub.unsubscribe(); In this case on('unsubscribed') will be called. Subscription still kept in Client's registry, but no resubscription attempts will be made. You can call subscribe() again when you need Subscription again. Or you can remove Subscription from Client's registry (see below).","s":"Subscription states","u":"/docs/transports/client_api","h":"#subscription-states","p":3630},{"i":3645,"t":"The client SDK provides several methods to manage the internal registry of client-side subscriptions. newSubscription(channel, options) allocates a new Subscription in the registry or throws an exception if the Subscription is already there. We will discuss common Subscription options below. getSubscription(channel) returns the existing Subscription by a channel from the registry (or null if it does not exist). removeSubscription(sub) removes Subscription from Client's registry. Subscription is automatically unsubscribed before being removed. Use this to free resources if you don't need a Subscription to a channel anymore. subscriptions() returns all registered subscriptions, so you can iterate over all and do some action if required (for example, you want to unsubscribe/remove all subscriptions).","s":"Subscription management","u":"/docs/transports/client_api","h":"#subscription-management","p":3630},{"i":3647,"t":"Of course the main point of having Subscriptions is the ability to listen for publications (i.e. messages published to a channel). sub.on('publication', function(ctx) { console.log(\"received publication\", ctx);}); Publication context has several fields: data - publication payload, this can be JSON or binary data offset - optional offset inside history stream, this is an incremental number tags - optional tags, this is a map with string keys and string values info - optional information about client connection who published this (only exists if publication comes from client-side publish() API). So minimal code where we connect to a server and listen for messages published into example channel may look like: Javascript Dart Swift Java Go const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = client.newSubscription('example').on('publication', function(ctx) { console.log(\"received publication from a channel\", ctx.data);});sub.subscribe();client.connect(); final client = centrifuge.createClient( 'ws://localhost:8000/connection/websocket', centrifuge.ClientConfig(),);final subscription = client.newSubscription(channel);subscription.publication.listen((event) { print(event);});await subscription.subscribe();await client.connect(); import SwiftCentrifugeclass ClientDelegate : NSObject, CentrifugeClientDelegate {}let config = CentrifugeClientConfig()let endpoint = \"ws://localhost:8000/connection/websocket\"let client = CentrifugeClient(endpoint: endpoint, config: config, delegate: ClientDelegate())class SubscriptionDelegate : NSObject, CentrifugeSubscriptionDelegate { func onPublication(_ s: CentrifugeSubscription, _ e: CentrifugePublicationEvent) { print(\"publication\", e.data) }}do { sub = try self.client?.newSubscription(channel: \"example\", delegate: SubscriptionDelegate()) sub!.subscribe()} catch { print(\"Can not create subscription: \\(error)\")}client.connect() EventListener listener = new EventListener() {};Options opts = new Options();Client client = new Client(\"ws://localhost:8000/connection/websocket\", opts, listener);SubscriptionEventListener subListener = new SubscriptionEventListener() { @Override public void onPublication(Subscription sub, PublicationEvent event) { System.out.println(\"publication from \" + sub.getChannel()); }};Subscription sub;try { sub = client.newSubscription(\"example\", subListener); sub.subscribe();} catch (DuplicateSubscriptionException e) { e.printStackTrace();}client.connect(); client := centrifuge.NewJsonClient( \"ws://localhost:8000/connection/websocket\", centrifuge.Config{},)// defer client.Close()sub, err := client.NewSubscription(\"example\")if err != nil { log.Fatalln(err)}sub.OnPublication(func(e centrifuge.PublicationEvent) { log.Printf(\"Publication from channel\")})err = sub.Subscribe()if err != nil { log.Fatalln(err)}if err = client.Connect(); err != nil { log.Fatalln(err)} Note, that we can call subscribe() before making a connection to a server – and this will work just fine, subscription goes to subscribing state and will be subscribed upon succesfull connection. And of course, it's possible to call .subscribe() after .connect().","s":"Listen to channel publications","u":"/docs/transports/client_api","h":"#listen-to-channel-publications","p":3630},{"i":3649,"t":"Subscriptions to channels with recovery option enabled maintain stream position information internally. On every publication received this information updated and used to recover missed publications upon resubscribe (caused by reconnect for example). When you call unsubscribe() Subscription position state is not cleared. So it's possible to call subscribe() later and catch up a state. The recovery process result – i.e. whether all missed publications recovered or not – can be found in on('subscribed') event context. Centrifuge protocol provides two fields: wasRecovering - boolean flag that tells whether recovery was used during subscription process resulted into subscribed state. Can be useful if you want to distinguish first subscribe attempt (when subscription does not have any position information yet) recovered - boolean flag that tells whether Centrifugo thinks that all missed publications can be successfully recovered and there is no need to load state from the main application database. It's always false when wasRecovering is false.","s":"Subscription recovery state","u":"/docs/transports/client_api","h":"#subscription-recovery-state","p":3630},{"i":3651,"t":"There are several common options available when creating Subscription instance. option to set subscription token and callback to get subscription token upon expiration (see below more details) option to set subscription data (attached to every subscribe/resubscribe request) options to tweak resubscribe backoff algorithm option to start Subscription since known Stream Position (i.e. attempt recovery on first subscribe) option to ask server to make subscription positioned (if not forced by a server) option to ask server to make subscription recoverable (if not forced by a server) option to ask server to push Join/Leave messages (if not forced by a server)","s":"Subscription common options","u":"/docs/transports/client_api","h":"#subscription-common-options","p":3630},{"i":3653,"t":"subscribe() – start subscribing to a channel unsubscribe() - unsubscribe from a channel publish(data) - publish data to Subscription channel history(options) - request Subscription channel history presence() - request Subscription channel online presence information presenceStats() - request Subscription channel online presence stats information (number of client connections and unique users in a channel).","s":"Subscription methods","u":"/docs/transports/client_api","h":"#subscription-methods","p":3630},{"i":3655,"t":"All SDKs support subscribing to Centrifugo channels with JWT. Channel subscription token can be set as a Subscription option upon initialization. Example: const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE'});sub.subscribe(); If token sets subscription expiration client SDK will keep token refreshed. It does this by calling special callback function. This callback must return a new token. If new token with updated subscription expiration returned from a calbback then it's sent to Centrifugo. If your callback returns an empty string – this means user has no permission to subscribe to a channel anymore and subscription will be unsubscribed. In case of error returned by your callback SDK will retry operation after some jittered time. An example: async function getToken(ctx) { // Fetch your application backend. const res = await fetch('http://localhost:8000/centrifugo/subscription_token', { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify({ channel: ctx.channel }) }); if (!res.ok) { if (res.status === 403) { // Return special error to not proceed with token refreshes, // client will be disconnected. throw new Centrifuge.UnauthorizedError(); } // Any other error thrown will result into token refresh re-attempts. throw new Error(`Unexpected status code ${res.status}`); } const data = await res.json(); return data.token;}const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});const sub = centrifuge.newSubscription(channel, { token: 'JWT-GENERATED-ON-BACKEND-SIDE', // Optional, getToken is enough. getToken: getToken});sub.subscribe(); tip If initial token is not provided, but getToken is specified – then SDK should assume that developer wants to use token authorization for a channel subscription. In this case SDK should attempt to get a subscription token before initial subscribe.","s":"Subscription token","u":"/docs/transports/client_api","h":"#subscription-token","p":3630},{"i":3657,"t":"We encourage using client-side subscriptions where possible as they provide a better control and isolation from connection. But in some cases you may want to use server-side subscriptions (i.e. subscriptions created by server upon connection establishment). Technically, client SDK keeps server-side subscriptions in internal registry (similar to client-side subscriptions but without possibility to control them). To listen for server-side subscription events use callbacks as shown in example below: const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});client.on('subscribed', function(ctx) { // Called when subscribed to a server-side channel upon Client moving to // connected state or during connection lifetime if server sends Subscribe // push message. console.log('subscribed to server-side channel', ctx.channel);});client.on('subscribing', function(ctx) { // Called when existing connection lost (Client reconnects) or Client // explicitly disconnected. Client continue keeping server-side subscription // registry with stream position information where applicable. console.log('subscribing to server-side channel', ctx.channel);});client.on('unsubscribed', function(ctx) { // Called when server sent unsubscribe push or server-side subscription // previously existed in SDK registry disappeared upon Client reconnect. console.log('unsubscribed from server-side channel', ctx.channel);});client.on('publication', function(ctx) { // Called when server sends Publication over server-side subscription. console.log('publication receive from server-side channel', ctx.channel, ctx.data);});client.connect(); Server-side subscription events mostly mimic events of client-side subscriptions. But again – they do not provide control to the client and managed entirely by a server side. Additionally, Client has several top-level methods to call with server-side subscription related operations: publish(channel, data) history(channel, options) presence(channel) presenceStats(channel)","s":"Server-side subscriptions","u":"/docs/transports/client_api","h":"#server-side-subscriptions","p":3630},{"i":3659,"t":"Server can return error codes in range 100-1999. Error codes in interval 0-399 reserved by Centrifuge/Centrifugo server. Codes in range [400, 1999] may be returned by application code built on top of Centrifuge/Centrifugo. Server errors contain a temporary boolean flag which works as a signal that error may be fixed by a later retry. Errors with codes 0-100 can be used by client-side implementation. Client-side errors may not have code attached at all since in many languages error can be distinguished by its type.","s":"Error codes","u":"/docs/transports/client_api","h":"#error-codes","p":3630},{"i":3661,"t":"Server may return unsubscribe codes. Server unsubscribe codes must be in range [2000, 2999]. Unsubscribe codes >= 2500 coming from server to client result into automatic resubscribe attempt (i.e. client goes to subscribing state). Codes < 2500 result into going to unsubscribed state. Client implementation can use codes <2000 for client-side specific unsubscribe reasons.","s":"Unsubscribe codes","u":"/docs/transports/client_api","h":"#unsubscribe-codes","p":3630},{"i":3663,"t":"Server may send custom disconnect codes to a client. Custom disconnect codes must be in range [3000, 4999]. Client automatically reconnects upon receiving code in range 3000-3499, 4000-4499 (i.e. Client goes to connecting state). Other codes result into going to disconnected state. Client implementation can use codes <3000 for client-side specific disconnect reasons.","s":"Disconnect codes","u":"/docs/transports/client_api","h":"#disconnect-codes","p":3630},{"i":3665,"t":"An SDK provides a way to send RPC to a server. RPC is a call that is not related to channels at all. It's just a way to call the server method from the client-side over the real-time connection. RPC is only available when RPC proxy configured (so Centrifugo proxies the RPC to your application backend). const rpcRequest = {'key': 'value'};const data = await centrifuge.namedRPC('example_method', rpcRequest);","s":"RPC","u":"/docs/transports/client_api","h":"#rpc","p":3630},{"i":3667,"t":"SDK provides a method to call publication history inside a channel (only for channels where history is enabled) to get last publications in a channel. Get stream current top position: const resp = await subscription.history();console.log(resp.offset);console.log(resp.epoch); Get up to 10 publications from history since known stream position: const resp = await subscription.history({limit: 10, since: {offset: 0, epoch: '...'}});console.log(resp.publications); Get up to 10 publications from history since current stream beginning: const resp = await subscription.history({limit: 10});console.log(resp.publications); Get up to 10 publications from history since current stream end in reversed order (last to first): const resp = await subscription.history({limit: 10, reverse: true});console.log(resp.publications);","s":"Channel history API","u":"/docs/transports/client_api","h":"#channel-history-api","p":3630},{"i":3669,"t":"Once subscribed client can call presence and presence stats information inside channel (only for channels where presence configured): For presence (full information about active subscribers in channel): const resp = await subscription.presence();// resp contains presence information - a map client IDs as keys // and client information as values. For presence stats (just a number of clients and unique users in a channel): const resp = await subscription.presenceStats();// resp contains a number of clients and a number of unique users.","s":"Presence and presence stats API","u":"/docs/transports/client_api","h":"#presence-and-presence-stats-api","p":3630},{"i":3671,"t":"Callbacks must be fast. Avoid blocking operations inside event handlers. Callbacks caused by protocol messages received from a server are called synchronously and connection read loop is blocked while such callbacks are being executed. Consider doing heavy work asynchronously. Do not blindly rely on the current Client or Subscription state when making client API calls – state can change at any moment, so don't forget to handle errors. Disconnect from a server when a mobile application goes to the background since a mobile OS can kill the connection at some point without any callbacks called.","s":"SDK common best practices","u":"/docs/transports/client_api","h":"#sdk-common-best-practices","p":3630}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/2",[0,1.305,1,1.313,2,0.609,3,1.932,4,0.701,5,0.796,6,1.634,7,0.549,8,1.216,9,2.532,10,1.222,11,1.802,12,0.911,13,4.043,14,1.681,15,1.426,16,1.904,17,1.545,18,3.058,19,2.303,20,1.413,21,1.966,22,4.727,23,2.323,24,1.657,25,1.655,26,2.829,27,2.052,28,1.327,29,1.364,30,2.446,31,0.552,32,3.232,33,1.288,34,0.996,35,1.573,36,3.36,37,3.445,38,3.91,39,1.591,40,2.06,41,1.025,42,1.657,43,3.91,44,3.343,45,3.91,46,2.552,47,3.259,48,0.917,49,0.879,50,5.502,51,2.26,52,2.435,53,2.435,54,2.435,55,1.229,56,0.959,57,2.435,58,1.634,59,2.058,60,2.435,61,2.435,62,2.435,63,1.736,64,2.435,65,2.435,66,2.435,67,2.435,68,1.482,69,2.435,70,2.79,71,2.435,72,2.435,73,2.435,74,2.435,75,2.435,76,1.681,77,2.435,78,2.435,79,1.883,80,2.435,81,2.435,82,2.435,83,2.435,84,0.885,85,0.783,86,2.662,87,6.96,88,1.018,89,0.843,90,1.065,91,1.883,92,2.821,93,0.957,94,1.222,95,2.426,96,1.377,97,2.754,98,3.038,99,1.425,100,2.311,101,3.278,102,3.259,103,0.756,104,1.989,105,1.616,106,2.257,107,1.001,108,2.019,109,1.174,110,1.191,111,1.657,112,1.937,113,1.426,114,1.802,115,1.04,116,0.843,117,2.26,118,2.435,119,2.058,120,5.168,121,1.322,122,2.435,123,0.77,124,1.425,125,1.288,126,2.495,127,0.741,128,1.078,129,1.35,130,2.946,131,3.457,132,1.518,133,0.967,134,1.124,135,1.288,136,2.435,137,2.509,138,1.577,139,1.014,140,0.885,141,2.26,142,2.058,143,3.184,144,1.872,145,1.572,146,2.435,147,2.26,148,2.435,149,4.213,150,2.435,151,2.435,152,2.435,153,1.156,154,2.144,155,2.532,156,2.662,157,1.305,158,1.129,159,1.506,160,0.792,161,1.538,162,0.794,163,0.896,164,1.074,165,1.158,166,2.26,167,1.736,168,1.883,169,1.236,170,0.668,171,0.614,172,1.354,173,1.209,174,1.162,175,1.236,176,1.84,177,1.091,178,1.768,179,1.729,180,2.26,181,1.28,182,1.308,183,1.678,184,1.708,185,1.555,186,1.065,187,1.573,188,1.634,189,1.209,190,1.161,191,2.827,192,1.331,193,0.822,194,1.736,195,1.989,196,0.896,197,1.007,198,1.1,199,1.115,200,1.119,201,1.15,202,2.691,203,0.936,204,4.208,205,2.058,206,1.463,207,0.922,208,2.058,209,2.058,210,2.058]],["t/4",[16,1.01,23,4.081,27,1.288,29,1.893,31,1.929,41,3.582,48,2.202,56,1.119,84,0.588,90,2.557,103,2.642,105,1.697,123,2.691,130,3.093,133,2.321,135,3.093,189,2.904,192,3.196,200,2.688,201,2.763,211,4.038,212,5.149,213,2.208,214,5.847,215,6.927,216,2.763,217,3.093,218,5.847,219,1.68,220,3.34,221,2.916,222,5.149,223,3.055,224,2.299,225,3.327,226,3.829,227,3.581,228,1.719,229,3.219,230,4.522,231,5.71,232,1.974,233,1.964,234,5.847,235,4.777,236,1.427,237,5.79,238,3.113,239,3.113,240,2.968,241,4.245,242,3.824,243,2.968,244,3.34,245,3.241,246,2.986,247,3.47,248,2.547,249,4.038,250,5.847,251,5.847,252,4.522,253,3.265,254,1.745,255,3.654,256,2.03,257,4.02,258,5.149,259,5.847,260,5.149,261,2.75,262,3.547,263,5.427,264,5.847,265,5.847,266,5.847,267,4.64,268,5.149,269,4.943,270,5.847,271,5.847,272,2.221,273,4.327]],["t/6",[6,5.396,16,1.833,23,3.289,24,1.9,25,2.389,26,4.095,27,2.339,28,1.556,29,2.604,56,0.737,90,3.517,115,3.436,125,4.254,162,2.622,196,2.958,229,4.426,274,6.219,275,2.31,276,3.697,277,3.836,278,2.674,279,4.257,280,4.747,281,3.531,282,4.059,283,2.158,284,5.026,285,3.083,286,4.524,287,5.326,288,4.925,289,2.949,290,6.219,291,4.396,292,4.286,293,5.951,294,4.015,295,3.001,296,2.876,297,4.081]],["t/8",[9,2.761,10,2.306,16,0.793,19,4.81,21,2.56,22,2.645,23,3.722,24,0.69,34,1.086,36,1.88,48,1.73,56,0.895,84,0.872,92,1.579,96,1.502,110,2.246,115,3.034,116,2.459,131,1.934,134,2.121,143,3.471,158,2.131,165,0.955,174,1.267,200,2.112,216,2.17,221,1.574,225,3.238,226,2.067,227,2.814,236,1.121,278,1.528,282,3.583,283,1.233,294,2.293,298,2.604,299,2.736,300,3.994,301,4.594,302,4.171,303,3.335,304,0.898,305,2.712,306,2.925,307,8.677,308,4.263,309,4.263,310,4.594,311,3.471,312,3.4,313,1.967,314,3.4,315,2.025,316,1.9,317,3.293,318,2.206,319,4.263,320,2.446,321,3.222,322,1.333,323,1.719,324,2.257,325,2.968,326,2.902,327,3.222,328,2.814,329,4.263,330,4.594,331,4.594,332,2.212,333,4.594,334,4.562,335,3.552,336,1.735,337,1.986,338,1.049,339,2.15,340,2.842,341,2.345,342,2.787,343,1.516,344,3.645,345,3.645,346,3.222,347,2.814,348,2.736,349,4.594,350,4.594,351,2.384,352,4.594,353,1.163,354,4.594,355,4.594,356,3.883,357,4.594,358,3.552,359,1.88,360,3.042,361,2.871,362,3.335,363,3.042,364,3.004,365,2.16,366,2.666,367,2.17,368,3.222,369,2.645,370,3.335,371,5.633,372,1.595,373,3.083,374,4.594,375,3.829,376,2.43,377,0.953,378,3.645,379,3.552,380,4.046,381,2.017,382,2.645,383,2.968,384,3.552,385,3.753,386,3.172,387,2.968]],["t/10",[1,3.301,2,1.532,16,1.326,19,1.574,21,0.998,23,1.896,24,1.626,26,2.108,28,0.498,29,1.232,33,2.013,34,1.814,56,1.064,84,0.615,85,2.468,96,1.244,97,1.579,98,1.241,105,1.105,106,2.991,126,1.705,133,2.43,134,1.757,135,2.013,138,1.424,153,1.807,158,4.079,162,1.241,164,1.56,165,0.791,175,1.932,217,2.013,219,1.093,220,2.174,224,1.496,225,1.262,254,1.826,256,2.125,272,1.446,277,2.919,278,1.265,280,2.247,296,1.361,302,1.626,304,1.197,315,1.678,316,1.574,317,1.765,324,1.87,332,2.948,340,3.786,341,1.942,343,2.02,347,2.331,351,2.055,353,1.549,359,3.599,365,2.878,378,3.02,388,2.554,389,3.532,390,1.705,391,1.487,392,3.524,393,1.734,394,2.839,395,1.396,396,6.384,397,1.468,398,1.244,399,2.669,400,2.763,401,3.651,402,6.996,403,3.645,404,2.765,405,1.124,406,3.806,407,3.22,408,3.993,409,1.79,410,2.331,411,3.806,412,4.226,413,4.293,414,2.026,415,2.489,416,1.626,417,3.806,418,1.765,419,3.806,420,3.217,421,2.209,422,3.866,423,3.806,424,3.806,425,3.806,426,1.304,427,3.806,428,3.806,429,3.02,430,2.801,431,6.12,432,2.227,433,2.039,434,2.354,435,3.806,436,2.52,437,2.666,438,4.236,439,2.308,440,1.557,441,1.921,442,2.943,443,2.489,444,1.531,445,2.227,446,2.247,447,2.687,448,3.217,449,3.496,450,5.173,451,2.943,452,2.431,453,2.266,454,3.217,455,2.876,456,3.217,457,3.806,458,1.989,459,2.763,460,3.217,461,1.977,462,3.217,463,2.125,464,3.02,465,2.459,466,2.489,467,2.763,468,2.247,469,3.532,470,2.554]],["t/12",[2,1.117,16,1.66,23,2.978,25,2.063,26,2.392,27,2.429,28,0.585,31,1.012,34,1.055,47,2.613,48,1.681,50,3.77,56,0.636,68,1.571,84,1.047,88,2.902,89,2.405,90,1.952,103,2.156,105,1.296,107,1.061,108,2.139,116,1.546,119,3.773,128,1.976,130,2.362,131,2.923,132,1.217,134,2.061,138,2.599,143,3.373,144,1.984,153,2.119,169,2.266,175,2.266,183,1.778,189,2.217,197,1.846,200,3.192,201,2.109,213,1.686,225,2.303,228,1.312,231,2.996,239,2.376,272,1.696,274,3.452,275,1.282,278,1.484,283,1.198,296,1.596,300,3.906,315,1.968,316,1.846,317,2.071,326,2.82,338,1.019,339,2.09,341,2.278,344,5.51,345,5.51,346,3.131,351,2.332,353,2.156,359,1.827,369,2.57,371,3.542,395,1.637,399,3.131,407,1.873,416,2.967,422,2.82,433,2.392,466,2.919,468,4.099,471,1.717,472,3.138,473,4.143,474,3.373,475,4.143,476,2.296,477,3.822,478,2.511,479,2.884,480,6.073,481,2.475,482,1.852,483,2.613,484,1.755,485,3.131,486,4.464,487,4.464,488,4.464,489,2.761,490,2.919,491,1.249,492,5.041,493,3.931,494,6.944,495,2,496,2.53,497,4.464,498,2.305,499,2.884,500,1.96,501,5.869,502,1.777,503,4.143,504,2.511,505,2.67,506,2.457,507,2.253,508,2.44,509,2.347,510,5.37,511,1.321,512,1.428,513,1.952,514,3.131,515,4.464,516,4.143,517,2.511,518,4.464,519,3.452,520,3.773,521,4.464,522,3.083,523,4.143,524,2.55,525,1.952,526,2.996]],["t/14",[16,1.822,19,2.936,21,1.205,23,3.82,24,1.467,26,1.583,27,1.012,28,1.136,29,1.487,31,1.042,36,1.88,47,2.689,50,2.494,56,0.795,84,0.872,88,1.92,97,1.906,101,2.269,103,1.426,130,2.43,134,2.121,138,1.719,143,3.471,144,2.041,157,4.649,163,1.69,164,1.81,165,0.955,190,2.191,197,1.9,198,2.076,207,1.74,212,4.046,213,3.277,219,1.32,224,1.806,225,1.524,231,3.083,232,2.929,236,1.121,244,2.624,245,2.547,256,1.595,272,2.697,275,1.32,284,4.437,299,2.736,305,4.191,306,3.576,315,2.025,316,2.936,323,1.719,343,1.516,365,2.16,422,4.485,430,3.25,441,2.319,446,2.712,468,2.712,492,3.335,500,2.017,523,4.263,527,1.761,528,3.471,529,3.883,530,4.38,531,2.584,532,3.436,533,4.594,534,3.042,535,3.883,536,2.398,537,4.263,538,3.552,539,2.736,540,3.645,541,2.624,542,3.552,543,2.4,544,4.121,545,3.045,546,3.552,547,4.594,548,2.624,549,4.594,550,1.675,551,4.594,552,2.332,553,2.257,554,3.883,555,2.017,556,4.263,557,2.121,558,3.601,559,3.471,560,3.507,561,3.753,562,2.094,563,3.883,564,2.761,565,1.392,566,2.968,567,3.229,568,1.221,569,3.645,570,6.001,571,3.172,572,2.257,573,4.263,574,3.883,575,2.025,576,2.511,577,2.017,578,1.675]],["t/16",[0,0.812,1,0.817,2,1.366,4,1.096,5,0.903,6,1.017,8,0.757,12,1.034,14,1.047,15,0.513,16,1.92,17,1.013,19,1.143,20,0.88,21,0.999,22,2.703,23,2.507,24,1.085,26,0.952,27,2.45,28,0.878,29,0.491,30,3.169,31,1.918,33,0.802,34,0.358,36,1.131,40,1.351,41,1.163,46,1.063,47,2.229,48,0.571,49,0.316,50,1.5,56,0.5,84,0.674,89,1.319,90,3.158,91,1.172,92,2.639,94,0.761,96,2.002,97,2.265,98,0.901,99,2.229,101,0.749,105,1.585,106,0.94,107,1.116,109,0.432,110,2.296,115,1.181,116,0.957,121,0.823,123,2.429,127,0.841,130,2.014,131,1.163,132,0.753,133,1.512,134,2.168,135,1.462,138,0.567,140,0.551,153,0.719,154,1.335,160,0.493,162,1.242,164,0.971,165,0.315,174,0.762,179,0.471,186,1.208,188,1.017,189,1.372,190,0.723,192,0.828,193,0.512,196,0.557,197,0.627,199,0.694,201,0.716,207,0.574,211,1.908,216,2.579,221,1.305,224,0.596,228,0.446,229,0.834,230,1.172,231,2.555,235,2.257,236,0.929,238,4.995,239,0.807,240,2.771,245,2.603,252,1.172,254,0.825,255,2.38,256,1.63,262,1.676,269,3.219,272,1.05,275,1.094,276,0.697,277,0.723,278,0.504,279,0.608,280,3.958,281,0.665,282,0.765,283,0.741,288,0.928,289,0.556,292,0.612,295,1.421,298,2.661,302,1.181,305,2.248,306,1.139,313,0.863,315,2.07,316,1.575,317,1.282,320,0.807,323,1.034,334,1.453,337,1.646,338,0.631,340,0.938,341,0.774,343,1.55,351,1.577,353,1.381,359,1.558,362,1.1,365,1.299,376,0.802,377,0.573,381,0.665,388,1.854,391,1.079,395,1.013,396,1.1,403,0.903,408,0.508,416,2.333,421,0.88,422,0.957,439,1.676,440,1.131,447,1.213,466,0.991,476,0.408,477,2.096,479,0.979,490,0.991,491,1.527,499,0.979,500,0.665,505,2.1,510,1.172,525,3.358,527,0.953,528,0.741,550,0.553,552,2.011,561,1.238,563,2.336,564,1.66,565,0.459,568,1.919,570,1.281,579,1.854,580,0.488,581,1.854,582,1.172,583,0.859,584,1.172,585,1.938,586,1.238,587,1.145,588,1.407,589,1.281,590,0.757,591,0.802,592,1.646,593,0.694,594,1.407,595,1.335,596,1.963,597,3.68,598,2.763,599,4.598,600,0.938,601,0.66,602,1.238,603,1.203,604,0.928,605,0.928,606,1.081,607,0.983,608,0.73,609,0.919,610,1.081,611,0.585,612,1.66,613,1.159,614,0.802,615,0.823,616,4.808,617,4.962,618,2.763,619,4.573,620,2.41,621,2.24,622,0.709,623,0.674,624,3.808,625,2.763,626,1.281,627,1.203,628,0.726,629,0.895,630,0.947,631,3.311,632,1.379,633,1.1,634,1.97,635,0.51,636,1.444,637,0.903,638,0.846,639,1.516,640,1.516,641,1.172,642,1.335,643,1.281,644,1.238,645,1.516,646,1.407,647,0.528,648,1.516,649,1.516,650,1.727,651,0.938,652,1.516,653,0.443,654,1.238,655,0.919,656,1.516,657,1.1,658,0.674,659,0.911,660,1.908,661,1.516,662,1.49,663,0.859,664,1.031,665,1.407,666,1.1,667,2.433,668,2.763,669,1.145,670,1.49,671,1.281,672,0.828,673,1.238,674,1.516,675,0.895,676,1.238,677,0.741,678,1.335,679,1.1,680,1.1,681,0.88,682,1.172,683,0.526,684,0.734,685,0.783,686,1.1,687,1.172,688,1.335,689,1.281,690,0.873,691,0.774,692,1.122,693,0.866,694,1.395,695,1.516,696,0.991,697,1.516,698,2.555,699,1.335,700,1.516,701,1.407,702,0.947,703,1.122,704,1.238,705,0.866,706,1.004,707,0.783,708,1.1,709,0.919,710,1.1,711,1.516,712,1.379,713,1.172,714,0.979,715,0.723,716,1.238,717,1.516,718,1.203,719,0.846,720,1.281,721,1.203,722,1.516,723,0.503,724,0.685,725,0.716,726,0.919,727,1.335,728,0.979,729,1.004,730,1.145,731,0.737,732,0.919,733,1.238,734,0.622,735,1.031,736,0.6]],["t/18",[2,0.695,4,0.799,5,0.908,6,3.165,8,1.387,9,1.669,12,1.765,13,3.424,15,0.94,16,1.401,21,1.9,22,1.599,23,3.197,24,1.551,25,0.825,26,1.625,28,0.949,29,1.527,31,0.63,39,1.049,46,1.068,49,0.984,56,1.163,84,0.946,85,1.976,92,2.112,98,0.906,100,1.153,106,0.945,108,1.331,109,0.962,113,0.94,115,2.015,116,0.962,121,2.561,132,0.757,133,1.872,134,1.283,135,1.469,138,2.712,155,2.834,162,1.538,164,0.708,165,0.98,174,0.766,182,0.862,185,1.774,201,1.312,202,1.774,219,1.355,224,1.092,225,0.921,231,1.864,235,2.269,238,1.479,239,1.479,248,1.21,254,0.829,272,2.335,276,1.277,285,2.357,288,2.888,289,2.976,294,1.387,298,1.574,299,1.654,304,0.543,315,1.224,316,1.148,317,1.288,318,0.628,332,1.338,336,1.049,338,1.077,339,2.208,343,2.029,351,1.584,353,1.193,365,2.218,367,1.312,377,1.276,383,1.795,389,2.578,395,1.73,397,1.071,408,1.58,416,1.187,436,1.839,437,1.21,440,1.137,441,2.38,443,1.816,444,1.117,446,1.64,458,1.451,468,2.784,472,1.255,477,1.529,483,1.626,489,1.718,491,0.777,500,1.219,502,0.711,505,1.814,506,4.856,508,1.518,509,1.46,511,0.822,512,0.889,517,1.562,525,1.215,527,0.957,528,1.358,536,1.303,557,1.283,560,3.036,565,1.429,568,1.633,592,1.654,596,1.971,599,1.325,605,1.584,621,1.325,630,0.952,654,2.269,664,1.89,673,2.269,683,0.964,690,1.599,691,2.407,694,1.402,706,1.839,710,3.424,737,1.948,738,2.269,739,2.777,740,1.165,741,1.669,742,1.612,743,2.888,744,2.495,745,2.777,746,2.777,747,1.797,748,1.948,749,1.669,750,3.084,751,2.527,752,2.099,753,2.204,754,2.099,755,1.971,756,1.21,757,1.078,758,5.603,759,2.357,760,0.762,761,2.446,762,1.736,763,1.754,764,1.736,765,1.529,766,2.777,767,1.981,768,2.348,769,1.682,770,1.981,771,2.578,772,1.701,773,3.424,774,1.394,775,2.446,776,7.98,777,2.777,778,1.626,779,4.548,780,1.372,781,2.348,782,4.716,783,2.777,784,3.209,785,4.716,786,2.777,787,3.564,788,2.777,789,2.777,790,1.795,791,2.777,792,2.777,793,2.777,794,1.795,795,1.685,796,1.351,797,2.777,798,3.137,799,1.948,800,5.704,801,1.918,802,1.043,803,1.451,804,2.204,805,1.754,806,2.016,807,0.996,808,2.777]],["t/20",[2,1.133,6,3.039,12,2.628,21,1.187,23,2.175,24,1.292,26,2.419,27,2.309,28,1.452,31,1.592,37,2.801,39,1.71,56,0.643,92,2.955,98,2.289,105,1.314,109,1.099,115,1.935,126,2.029,129,2.51,133,1.798,134,4.473,138,3.218,162,1.477,164,1.154,190,4.621,207,2.659,221,1.552,224,4.361,240,2.298,245,2.51,247,1.846,256,2.986,257,2.139,272,2.667,278,1.506,279,1.816,281,3.082,282,2.285,284,2.83,288,2.773,289,3.845,313,1.027,315,1.996,320,3.738,323,1.695,340,2.801,351,1.521,372,1.572,377,0.94,408,2.352,430,3.214,447,1.988,468,2.673,476,1.22,477,2.492,505,2.7,541,2.586,550,2.56,552,4.033,553,3.45,554,3.827,560,2.237,563,3.827,564,2.721,565,2.127,568,2.285,595,3.988,596,2.934,599,2.16,601,3.058,607,2.497,621,2.16,628,2.17,630,1.552,632,3.505,636,2.366,662,2.442,673,5.736,691,2.311,694,3.544,706,2.999,707,2.338,712,2.261,724,2.046,765,2.492,768,3.827,796,2.203,806,5.097,809,2.892,810,3.992,811,2.607,812,4.343,813,2.721,814,1.81,815,3.699,816,2.381,817,4.528,818,2.11,819,4.591,820,2.16,821,2.926,822,2.51]],["t/22",[7,0.57,12,2.917,16,1.346,18,3.178,19,3.223,21,2.044,27,2.578,30,4.524,31,1.767,56,0.714,84,0.783,92,2.678,105,3.018,123,2.465,126,4.659,172,2.505,201,3.682,225,2.585,226,3.507,229,4.29,238,4.149,239,4.149,353,2.632,407,3.269,408,2.611,525,4.547,552,2.56,564,4.684,565,2.361,568,2.071,597,3.735,611,3.007,623,3.463,631,4.728,664,5.303,712,3.891,734,3.2,758,6.027,819,6.801,823,5.89,824,5.303,825,5.558,826,6.027,827,3.665,828,3.016,829,3.81,830,4.41,831,4.562,832,4.871]],["t/24",[2,1.806,7,0.885,16,1.705,17,2.646,18,2.942,31,2.238,89,2.499,93,2.837,123,3.122,124,4.223,160,3.66,165,1.5,174,1.99,192,3.944,232,2.435,233,2.423,236,1.761,278,2.399,315,3.181,318,1.63,323,2.701,444,2.903,492,5.238,519,5.58,536,1.994,567,2.685,568,1.918,596,3.016,620,3.704,622,3.377,677,5.501,684,3.492,694,3.642,829,3.528,833,4.719,834,5.453,835,4.51,836,5.58,837,5.725,838,6.354,839,3.841,840,5.238,841,3.095,842,5.725,843,7.215,844,7.215,845,7.215,846,4.067,847,3.704,848,5.453,849,6.923,850,4.377,851,3.51,852,2.903,853,5.061,854,3.564,855,4.719,856,4]],["t/26",[7,0.889,8,2.022,9,3.865,16,1.111,18,2.623,19,3.308,21,1.687,24,0.966,26,2.216,27,0.892,28,1.193,29,2.082,31,2.399,33,2.142,56,0.371,88,1.692,89,2.227,92,2.21,105,1.175,109,0.634,110,1.98,116,1.402,123,3.346,124,2.37,125,2.142,132,1.104,135,2.142,155,2.434,157,2.17,160,2.092,170,2.194,174,1.774,175,2.055,181,2.129,185,2.587,186,1.771,189,2.011,215,2.796,216,1.913,225,1.343,226,1.822,227,2.48,253,2.261,275,1.163,282,2.044,283,1.087,285,1.553,291,2.214,295,1.511,298,2.295,306,1.669,311,3.06,316,1.674,317,1.878,326,2.558,329,3.758,338,0.924,353,1.025,359,1.657,377,0.84,383,2.617,390,1.814,391,2.513,395,1.485,461,2.103,471,3.077,498,2.091,512,1.296,526,4.316,527,1.305,536,1.119,552,2.628,565,1.227,566,2.617,575,1.785,578,2.917,596,1.692,597,1.941,599,1.931,607,1.44,619,2.412,622,1.895,623,1.8,677,3.144,684,3.113,693,2.313,715,1.931,726,2.456,736,1.602,741,2.434,759,1.317,763,2.558,798,2.067,802,1.52,809,2.587,829,1.98,830,1.717,833,2.648,834,3.06,835,5.695,836,3.131,840,2.94,841,1.737,846,1.669,847,3.302,849,2.84,857,2.708,858,3.423,859,4.759,860,2.84,861,5.103,862,1.87,863,5.705,864,4.049,865,2.997,866,3.131,867,4.049,868,4.049,869,4.049,870,4.049,871,4.049,872,4.049,873,4.86,874,1.845,875,4.759,876,2.558,877,1.37,878,1.602,879,2.94,880,1.618,881,2.756,882,2.245,883,2.756,884,2.37,885,2.142,886,2.39,887,2.37,888,2.214,889,2.888,890,2.796,891,2.531,892,4.049,893,2.17,894,4.049,895,4.049,896,3.213,897,2.997,898,1.822]],["t/28",[7,0.754,16,1.059,21,1.608,28,1.349,49,2.149,56,0.562,84,0.885,85,1.971,89,2.123,109,0.959,132,2.809,144,2.725,162,1.999,165,1.275,172,1.971,174,1.691,177,2.747,179,1.904,197,2.535,198,2.77,206,3.684,213,2.315,236,2.149,249,4.234,253,3.423,279,3.531,298,3.475,318,1.99,353,2.228,359,2.509,394,2.844,395,2.248,458,3.204,484,2.41,527,1.244,568,1.629,595,5.399,608,2.953,611,2.365,614,1.047,622,2.87,630,2.101,659,3.684,709,3.719,712,3.061,790,3.961,799,4.3,802,4.478,814,3.519,824,4.172,835,6.44,836,4.741,847,6.374,859,4.537,882,5.712,899,3.916,900,3.719,901,7.443,902,3.961,903,3.961,904,3.077,905,6.131,906,3.449,907,3.651,908,4.865,909,3.916,910,5.182,911,4.537,912,3.961,913,5.399,914,2.831,915,5.69,916,3.306]],["t/30",[7,0.84,160,2.954,177,4.069,189,4.51,257,4.29,302,3.88,313,2.059,320,4.834,334,4.774,353,2.298,471,3.493,684,4.395,802,4.311,847,4.662,880,3.629,897,6.72,917,8.657,918,9.081,919,5.801,920,4.898,921,6.863,922,7.255,923,9.383,924,9.383,925,7.676,926,8.428]],["t/32",[0,2.07,2,1.941,7,0.876,16,1.88,18,1.576,19,2.562,21,1.013,23,1.919,24,1.459,26,1.331,27,0.851,28,1.529,31,1.758,34,1.464,48,1.455,56,0.568,84,1.094,89,1.338,93,1.519,99,2.261,103,1.2,107,1.472,109,0.969,123,1.959,129,2.142,132,2.114,145,2.894,158,1.792,164,0.985,165,0.803,167,2.755,170,1.059,174,2.139,175,3.144,181,2.031,183,1.539,193,1.304,198,1.746,203,1.376,213,1.459,217,2.044,219,1.11,228,1.136,229,2.127,232,3.82,236,0.943,262,3.758,275,1.11,276,1.776,279,1.549,283,1.037,285,1.481,294,3.093,295,1.442,306,2.553,313,1.758,318,2.005,323,1.446,338,0.882,359,2.535,377,0.802,381,1.696,390,2.776,405,1.141,430,1.769,471,1.486,491,1.081,495,1.731,500,1.696,511,1.143,513,1.69,527,1.257,528,1.889,530,1.603,536,1.712,545,1.657,548,2.207,550,1.409,552,2.035,558,2.571,562,1.761,567,2.305,593,1.769,635,1.301,663,2.19,666,2.805,677,1.889,682,2.988,683,1.342,685,1.995,698,4.157,702,2.415,757,0.884,759,1.257,769,1.378,775,3.403,780,1.909,796,1.879,802,1.45,814,1.544,835,2.415,847,1.983,849,2.71,852,4.818,861,3.066,885,3.277,886,2.281,887,5.193,914,1.784,927,4.345,928,2.988,929,2.441,930,1.961,931,2.755,932,3.864,933,2.629,934,1.71,935,2.755,936,1.534,937,2.468,938,4.497,939,2.207,940,1.342,941,2.629,942,1.514,943,3.266,944,3.864,945,1.87,946,2.647,947,1.825,948,2.344]],["t/34",[0,2.392,2,1.117,7,0.623,19,1.846,27,0.983,28,0.585,49,0.931,56,1.291,68,1.571,84,0.698,85,2.232,105,1.296,109,1.087,112,2.052,123,1.412,131,1.879,132,1.217,138,1.671,160,1.452,164,1.138,172,3.091,173,2.217,213,1.686,217,2.362,225,1.481,243,2.266,256,1.55,279,1.79,313,1.574,316,1.846,318,2.172,322,1.296,323,1.671,339,2.09,359,1.827,377,0.927,387,2.884,408,2.855,426,1.53,491,1.942,502,1.777,552,3.781,565,1.353,568,1.845,592,2.659,601,1.944,608,2.15,609,2.708,611,1.722,632,2.229,636,2.333,641,3.452,725,2.109,726,5.169,734,2.851,747,1.701,765,2.457,802,1.676,810,1.839,847,2.292,878,1.766,885,2.362,899,2.851,906,2.511,929,6.073,949,3.083,950,2.884,951,2.659,952,6.259,953,1.676,954,2.761,955,3.241,956,3.773,957,3.452,958,4.212,959,5.947,960,3.647,961,3.773,962,4.435,963,5.869,964,3.773,965,4.064,966,3.131,967,3.773,968,5.869,969,3.773,970,3.773,971,5.03,972,3.773,973,2.884,974,3.773,975,3.773,976,3.773,977,3.773,978,2.79,979,2.734,980,4.435,981,2.57,982,3.773,983,3.773,984,3.773,985,3.773,986,2.171,987,3.773,988,2.319,989,3.773,990,3.773,991,3.773,992,2.071,993,2.392,994,4.464,995,2.82]],["t/36",[7,0.646,56,0.809,84,1.134,92,4.501,93,3.471,109,1.381,140,3.21,171,2.226,172,3.626,256,3.065,279,3.539,283,2.369,304,2.206,318,1.995,338,2.015,470,5.924,476,2.378,477,4.859,552,4.083,576,4.825,707,4.558,802,3.314,815,7.212,828,3.416,847,5.791,857,3.716,885,4.67,952,6.779]],["t/38",[2,0.941,4,1.082,5,1.229,7,0.749,8,1.877,15,1.272,16,0.649,18,2.472,21,0.986,22,2.165,24,0.565,26,2.088,27,1.335,29,1.217,31,0.853,33,1.989,34,0.889,46,1.446,56,0.345,84,0.609,86,2.375,90,1.644,92,3.518,93,4.669,97,2.515,98,1.226,107,0.893,109,0.588,112,2.787,113,1.272,117,3.49,125,1.989,127,1.144,138,1.407,140,1.367,160,2.477,165,0.782,170,1.031,172,2.808,175,1.909,179,1.882,181,4.003,185,4.864,186,2.651,191,5.11,207,1.424,213,1.42,224,1.478,228,1.105,236,1.858,253,2.1,256,1.306,257,1.777,278,1.25,283,1.627,313,0.853,315,3.357,316,2.507,337,1.625,338,1.384,340,2.326,353,1.534,369,2.165,377,1.258,381,3.835,390,1.685,395,1.379,407,1.577,439,2.281,441,1.898,444,1.513,446,2.22,447,1.651,458,1.965,471,1.446,484,1.478,495,1.685,500,1.651,519,4.687,527,1.23,569,2.984,571,2.597,580,1.212,590,1.877,597,4.186,614,0.642,620,6.35,629,2.22,634,2.682,638,2.1,655,2.281,675,2.22,690,2.165,691,1.919,693,2.148,694,1.898,698,2.523,716,4.952,728,2.43,742,3.518,743,2.303,751,2.015,775,3.312,802,1.412,807,1.349,809,2.402,824,2.559,826,2.908,847,3.112,848,2.842,849,2.637,854,1.858,857,1.583,874,1.714,885,1.989,916,2.028,993,2.015,996,2.201,997,2.617,998,2.239,999,1.898,1000,3.312,1001,2.682,1002,2.783,1003,3.312,1004,3.76,1005,3.76,1006,2.877,1007,3.788,1008,3.41,1009,1.607,1010,3.49,1011,2.35,1012,1.977,1013,2.597,1014,2.26,1015,3.76,1016,3.76,1017,4.81,1018,3.76,1019,4.81,1020,4.81,1021,3.76,1022,3.76,1023,3.76,1024,2.22,1025,2.637,1026,2.984,1027,2.842,1028,2.303,1029,2.402,1030,3.179,1031,3.179,1032,3.179]],["t/40",[7,0.911,24,1.164,56,0.71,88,3.237,103,2.405,105,2.248,144,3.442,219,2.225,226,3.485,236,1.89,243,3.931,244,4.424,276,3.561,283,2.079,305,4.573,306,4.267,317,3.593,351,2.601,353,1.96,363,5.13,377,1.608,471,3.983,478,4.357,491,2.167,502,1.982,527,2.101,536,2.141,550,2.825,565,3.138,567,4.634,568,2.752,577,3.401,578,2.825,580,2.496,586,6.328,604,4.744,635,2.608,672,4.234,692,5.732,798,3.954,809,4.948,835,4.841,936,3.075,1033,4.573,1034,6.328,1035,5.99,1036,4.744]],["t/42",[2,2.115,7,0.618,16,2.014,24,1.648,28,1.107,31,1.314,56,0.531,84,0.85,92,1.991,106,1.971,107,2.771,109,1.968,127,1.763,132,2.72,140,2.107,160,3.246,161,3.66,170,1.589,171,1.461,172,2.717,248,2.524,268,5.102,275,1.664,283,2.268,292,2.339,313,1.314,316,2.396,318,2.841,323,2.168,338,1.323,372,2.012,391,2.263,408,1.941,426,2.897,471,3.251,491,1.621,495,2.596,502,2.807,528,2.833,552,1.903,565,1.755,568,1.54,587,4.378,593,2.652,612,3.482,677,2.833,705,3.309,715,2.763,723,1.922,725,2.737,755,2.422,757,1.325,796,2.818,802,2.175,814,2.315,828,2.242,841,3.625,847,2.974,852,2.331,952,3.482,953,2.175,986,2.818,988,3.009,1014,3.482,1037,2.99,1038,4.897,1039,4.733,1040,3.701,1041,3.42,1042,3.235,1043,2.79,1044,3.167,1045,3.584]],["t/44",[7,0.694,25,2.82,46,4.943,109,1.485,157,5.085,272,3.605,334,4.989,337,4.102,712,4.738,715,5.631,756,4.134,801,6.553,878,3.755,909,6.062,1046,4.118,1047,5.261,1048,7.023,1049,7.023,1050,5.703,1051,6.768,1052,6.285,1053,6.889,1054,8.022,1055,5.812,1056,6.656]],["t/46",[4,3.108,7,0.79,16,1.425,21,2.164,23,2.556,24,1.24,28,1.576,84,1.085,89,2.858,132,2.249,134,3.81,170,2.262,172,2.653,203,1.833,279,3.308,283,2.214,313,1.871,318,1.864,399,5.787,471,4.629,484,3.244,495,3.697,622,3.862,677,4.034,684,3.994,827,3.88,846,3.4,847,5.543,887,7.045,903,5.332,904,4.142,947,3.898,1057,6.489,1058,8.251,1059,4.286,1060,3.713,1061,8.251]],["t/48",[16,1.591,134,4.255,160,3.771,172,2.962,173,4.575,189,4.575,200,4.236,221,3.158,261,4.333,313,2.089,318,2.082,375,4.969,390,4.128,405,2.72,415,6.025,471,3.543,511,2.726,536,2.546,601,4.013,677,4.505,807,3.304,850,5.589,854,5.725,855,6.025,861,7.311,1062,9.214,1063,9.214,1064,8.551,1065,7.527,1066,4.217]],["t/50",[7,0.769,8,1.772,16,0.613,18,1.447,19,3.027,21,0.931,24,0.533,27,1.613,28,0.465,29,1.149,50,4.584,51,5.368,56,0.852,63,7.816,84,0.581,88,1.483,89,2.003,94,1.782,95,2.043,96,1.891,101,1.753,105,1.03,106,1.207,107,0.843,109,0.555,128,1.571,131,1.494,132,0.967,134,1.639,138,1.328,157,1.902,160,1.155,177,1.59,182,2.273,191,3.882,195,2.9,198,1.604,207,1.344,213,1.34,225,1.919,228,1.043,236,0.866,247,1.447,291,1.94,296,1.269,298,2.012,315,4.098,317,1.646,338,0.81,339,2.708,341,1.812,342,2.153,353,0.898,359,1.452,377,0.737,381,1.558,395,1.302,410,2.174,422,2.242,430,2.648,440,1.452,446,2.095,447,1.558,467,2.577,471,3.247,472,2.614,498,3.78,499,2.293,500,1.558,510,4.473,512,3.507,573,3.294,628,1.701,676,2.9,677,3.579,702,2.218,705,2.027,752,4.371,774,1.782,779,2.627,796,1.726,802,1.332,813,5.073,821,2.293,835,2.218,841,1.522,846,1.463,847,2.969,851,1.726,857,1.494,875,2.627,885,1.878,940,1.232,950,3.737,1009,3.607,1017,8.697,1019,8.697,1020,4.59,1040,2.267,1067,4.199,1068,5.784,1069,3.675,1070,3.549,1071,3.549,1072,2.745,1073,3.882,1074,4.057,1075,1.914,1076,2.531,1077,3.549,1078,4.473,1079,3.549,1080,5.981,1081,5.784,1082,5.784,1083,5.784,1084,5.784,1085,3.549,1086,5.784,1087,4.057,1088,5.784,1089,3.549,1090,5.784,1091,3.549,1092,5.784,1093,5.784,1094,3.549,1095,3.549,1096,5.784,1097,3.549,1098,3.549,1099,5.784,1100,3.549,1101,4.057,1102,3.549,1103,3.549,1104,3.294,1105,4.473,1106,3.126,1107,1.735,1108,2.218,1109,3.126,1110,2.415,1111,2.153,1112,5.784,1113,2.531,1114,1.494,1115,2.682,1116,2.174]],["t/52",[7,0.954,8,3.017,16,1.043,28,0.791,31,1.976,34,1.428,48,2.275,84,0.607,90,2.643,105,2.53,129,3.35,130,3.197,131,2.544,137,3.599,166,5.608,174,2.819,189,3.001,203,1.342,211,4.173,233,3.757,236,2.126,257,2.855,262,3.666,272,2.296,318,1.365,336,2.282,338,1.379,339,2.829,351,2.029,353,1.529,359,2.473,363,4.002,375,3.259,390,2.708,391,2.361,395,2.216,416,2.582,418,2.803,437,2.632,444,2.431,484,2.376,553,2.97,557,2.791,578,2.204,593,2.766,609,3.666,683,2.098,684,2.925,712,3.017,734,2.481,774,3.033,828,2.339,834,4.567,839,3.217,841,2.592,846,4.212,847,3.102,848,4.567,849,4.238,850,3.666,851,2.939,856,3.35,875,4.472,879,4.387,922,5.505,950,3.905,1117,4.374,1118,5.608,1119,6.043,1120,6.043,1121,6.043,1122,3.508,1123,4.002,1124,8.714,1125,5.322,1126,6.043,1127,4.937,1128,5.108,1129,3.508,1130,5.322,1131,6.043,1132,3.905,1133,3.017,1134,4.673]],["t/54",[7,0.866,9,4.124,10,4.787,18,2.799,26,2.364,31,1.556,48,3.59,84,0.69,105,2.768,124,4.017,163,2.524,196,2.524,247,2.799,249,4.739,277,4.548,306,3.929,324,3.373,372,2.383,377,1.424,507,3.464,605,2.305,611,3.679,620,3.523,628,3.289,677,4.662,683,2.383,684,3.322,802,2.576,823,5.186,836,5.307,847,6.388,882,3.805,885,3.631,920,3.701,934,4.221,1026,5.446,1113,7.815,1135,5.307,1136,5.607,1137,4.384,1138,4.813,1139,3.408,1140,6.369,1141,6.369,1142,6.863,1143,6.863,1144,6.863,1145,6.863,1146,6.044,1147,6.369,1148,6.863,1149,6.863,1150,6.863,1151,6.863,1152,4.289,1153,5.446,1154,6.863,1155,6.863,1156,5.186,1157,5.607,1158,6.863,1159,6.863]],["t/56",[7,0.679,18,4.748,19,3.838,27,2.044,29,3.005,31,2.104,56,0.85,123,2.935,181,4.879,200,4.267,221,3.991,225,4.22,226,4.176,235,7.583,351,3.117,395,3.404,505,3.569,553,5.722,621,4.427,809,5.928,885,4.91,1160,9.281,1161,9.281,1162,9.281,1163,5.221,1164,4.344]],["t/58",[7,0.89,16,1.787,17,1.614,18,4.471,19,3.945,21,1.154,23,1.364,24,1.032,26,2.366,27,1.86,28,0.576,31,1.915,56,1.005,84,0.959,103,1.367,123,2.172,144,1.956,156,2.781,165,0.915,174,2.329,179,1.367,181,3.611,213,1.662,216,3.245,217,2.329,219,1.264,220,2.514,221,2.354,225,3.165,226,1.981,227,2.696,230,3.404,232,1.486,236,1.676,247,1.795,248,1.917,276,2.024,278,2.284,313,1.915,318,0.995,323,1.648,324,2.163,327,3.087,351,2.836,353,1.114,359,1.801,365,2.07,369,3.954,370,3.195,375,3.704,377,0.914,387,4.438,390,1.972,391,1.72,408,1.475,430,2.015,440,1.801,447,1.933,458,3.589,463,4.716,471,1.693,524,2.514,525,1.925,527,0.893,530,1.827,545,1.888,555,3.015,557,2.033,562,2.006,565,2.081,584,3.404,585,3.087,608,2.12,611,2.65,638,2.458,643,3.721,684,2.131,692,3.258,693,2.514,709,2.67,726,2.67,727,3.877,802,2.578,818,2.051,835,2.751,848,3.326,857,1.853,885,2.329,899,2.812,930,2.234,952,2.645,1053,3.195,1066,2.015,1114,1.853,1165,4.402,1166,4.402,1167,4.085,1168,3.326,1169,2.599,1170,1.254,1171,3.258,1172,3.721,1173,4.085,1174,2.723,1175,3.404,1176,3.721,1177,3.493,1178,2.995,1179,2.514,1180,2.576,1181,2.12,1182,3.493,1183,4.402,1184,4.402,1185,4.402,1186,4.402,1187,3.493,1188,3.139,1189,3.721,1190,4.402,1191,2.995,1192,3.721,1193,2.576]],["t/60",[2,1.513,5,1.226,16,1.312,17,1.375,18,4.564,21,1.586,23,2.701,26,1.292,27,1.921,28,0.792,29,1.958,31,2.438,34,0.886,56,1.025,84,1.081,88,2.527,89,3.312,96,1.977,99,2.194,101,1.852,103,1.164,105,1.088,116,1.298,123,3.401,130,1.983,132,1.648,133,1.488,144,1.666,157,2.009,160,1.22,164,1.542,165,0.779,169,1.903,174,1.034,196,2.224,197,1.55,213,1.416,216,1.771,219,1.077,225,2.006,226,1.687,227,2.296,228,1.102,232,2.041,236,1.476,248,1.633,278,1.247,280,2.213,283,1.006,298,4.307,305,2.213,306,3.593,313,1.723,316,1.55,317,1.739,320,3.219,334,3.179,338,0.856,348,3.601,351,1.259,353,1.53,359,1.534,364,2.452,376,1.983,377,0.778,382,2.159,391,2.969,408,1.256,440,1.534,447,1.646,463,4.243,474,2.833,476,1.01,491,1.049,512,1.2,530,3.154,536,1.036,545,2.594,558,1.556,567,1.395,568,2.317,575,1.653,578,1.367,581,2.516,584,2.899,585,2.63,587,2.833,592,2.233,605,1.259,607,1.334,611,1.446,630,1.285,655,2.274,683,2.639,694,3.052,709,3.668,726,2.274,742,2.176,816,1.971,818,2.818,820,1.788,828,1.451,848,2.833,874,1.709,883,2.551,884,2.194,887,3.539,890,2.589,927,2.63,940,2.1,1036,2.296,1069,3.035,1114,1.578,1129,4.411,1194,3.169,1195,2.899,1196,2.775,1197,2.775,1198,3.063,1199,3.479,1200,3.302,1201,2.319,1202,1.833,1203,5.111,1204,2.775,1205,3.063,1206,3.749,1207,3.169,1208,2.775,1209,3.063,1210,2.674,1211,3.749,1212,2.296,1213,3.479,1214,2.253,1215,2.551,1216,1.771,1217,2.899,1218,3.16,1219,2.674,1220,2.899,1221,2.589,1222,3.302]],["t/62",[2,2.027,4,1.574,16,1.842,18,5.034,20,3.174,23,2.51,24,1.793,26,1.884,27,1.784,28,0.716,29,1.77,40,2.673,46,4.381,56,1.24,84,0.814,85,3.102,90,2.391,96,1.787,97,3.361,109,1.268,127,2.465,133,2.171,135,2.893,160,1.779,164,2.46,186,2.391,197,2.261,198,2.471,203,1.215,213,2.065,219,1.571,228,1.607,229,3.01,236,1.977,272,2.077,275,1.571,285,2.096,313,1.836,316,2.261,320,2.911,395,2.005,421,4.702,471,3.115,484,2.15,491,1.53,502,1.399,530,2.269,550,2.954,555,2.401,610,3.899,887,3.2,897,4.046,935,3.899,965,4.741,971,6.298,1037,1.934,1116,3.349,1223,4.259,1224,3.256,1225,5.468,1226,5.468,1227,5.468,1228,4.815,1229,5.075,1230,5.468,1231,7.518,1232,5.075,1233,5.075,1234,2.875,1235,2.503,1236,4.622,1237,3.417,1238,3.031,1239,3.123,1240,2.893]],["t/64",[2,1.844,12,1.805,16,1.272,17,1.768,18,4.399,21,1.264,24,1.709,27,1.062,28,1.413,29,1.561,34,1.14,56,1.189,84,0.485,89,1.67,103,1.497,132,1.314,158,2.236,163,1.773,169,3.74,196,2.71,197,1.994,203,1.071,221,1.653,223,3.85,224,1.896,228,2.944,246,1.692,247,1.966,254,2.199,285,4.136,286,2.712,313,1.671,334,2.535,353,1.22,377,1.529,395,1.768,426,2.525,437,3.21,463,2.692,471,1.854,479,3.115,484,1.896,491,2.061,502,1.234,527,1.815,560,2.382,576,2.636,601,2.1,683,1.674,685,2.49,747,3.408,759,2.397,760,2.452,827,3.465,831,2.822,874,2.197,940,2.559,953,1.81,1114,2.03,1116,2.953,1164,2.257,1223,2.535,1229,4.475,1231,4.475,1232,4.475,1233,4.475,1240,2.551,1241,3.644,1242,6.618,1243,5.014,1244,4.822,1245,1.48,1246,3.826,1247,3.115,1248,4.822,1249,7.369,1250,4.822,1251,4.822,1252,8.943,1253,4.822,1254,7.369,1255,4.822,1256,4.822,1257,2.6,1258,4.822,1259,4.822,1260,4.822,1261,4.822,1262,4.822,1263,2.712,1264,4.822,1265,4.822,1266,1.22,1267,4.822,1268,4.475,1269,4.822,1270,3.826,1271,3.281,1272,4.822,1273,4.822]],["t/66",[0,1.772,2,1.366,5,1.081,12,1.238,16,0.943,17,2.002,18,2.226,21,1.828,24,1.532,25,1.622,26,1.881,27,1.203,28,1.262,29,1.768,31,1.238,34,0.782,40,1.617,41,1.393,56,1.113,84,0.333,88,2.282,89,2.414,96,1.081,98,1.079,105,1.585,107,2.705,109,2.044,113,1.119,123,1.727,127,2.122,132,1.9,134,1.527,140,1.203,155,1.988,164,0.843,165,0.688,170,2.218,174,2.232,192,2.984,196,1.217,197,1.368,200,1.521,203,0.735,213,1.249,225,1.097,238,1.761,246,1.161,272,2.648,278,1.1,279,1.326,285,2.093,295,1.234,297,2.771,304,1.068,313,1.238,316,2.258,338,0.755,343,1.092,359,1.354,377,2.117,379,4.222,381,1.452,395,1.213,397,2.106,409,1.556,416,1.413,426,1.134,430,1.514,439,2.006,471,1.272,476,1.878,498,1.708,507,1.67,512,2.23,517,1.861,527,0.671,550,3.719,552,1.087,555,1.452,568,1.451,575,2.407,591,1.75,597,1.585,605,1.111,606,2.359,607,1.177,613,1.388,630,1.134,655,2.006,665,3.07,683,2.809,712,1.651,735,2.251,736,1.309,747,1.26,756,3.036,814,2.786,818,1.541,829,1.617,877,1.119,880,1.322,884,1.936,897,2.448,936,1.313,949,2.284,953,1.242,965,1.936,995,2.09,1028,2.026,1045,2.046,1170,1.555,1180,3.195,1197,2.448,1223,4.708,1237,2.067,1266,0.837,1274,1.875,1275,1.97,1276,2.284,1277,2.702,1278,2.046,1279,2.558,1280,2.067,1281,1.548,1282,2.359,1283,5.459,1284,2.477,1285,1.728,1286,3.308,1287,3.07,1288,3.07,1289,1.242,1290,1.708,1291,3.308,1292,2.913,1293,2.913,1294,2.913,1295,2.796,1296,2.913,1297,3.308,1298,4.808,1299,3.308,1300,3.312,1301,4.615,1302,1.988,1303,1.834,1304,3.308,1305,2.448,1306,1.92,1307,3.07,1308,1.821,1309,2.702,1310,2.191,1311,3.308,1312,1.128]],["t/68",[2,1.881,12,2.813,16,1.752,18,3.065,21,1.971,23,2.328,24,1.129,25,3.818,27,2.236,28,1.611,56,1.053,84,1.02,101,3.713,109,1.176,132,3.132,144,3.34,164,1.916,236,1.834,306,3.097,313,1.704,476,3.095,558,3.119,580,2.422,660,5.19,685,3.881,1037,3.59,1223,3.951,1285,3.927,1292,6.619,1306,5.89,1313,6.619,1314,4.748,1315,3.836,1316,7.516,1317,7.516,1318,7.516,1319,7.516,1320,4.437,1321,6.619,1322,6.975,1323,6.975,1324,7.516,1325,7.516,1326,7.516,1327,7.516]],["t/70",[2,1.664,16,1.61,18,2.71,23,2.059,24,0.999,25,1.975,27,1.464,28,1.411,56,1.2,96,2.173,132,2.542,163,2.445,165,1.382,182,2.064,196,2.445,203,1.476,213,2.51,219,1.909,306,2.739,313,2.114,351,2.232,376,3.516,394,3.083,426,3.196,476,1.79,491,1.859,495,2.978,500,2.918,502,1.701,545,2.851,550,2.424,562,3.029,577,4.729,580,2.142,757,2.67,778,3.89,914,3.069,936,3.702,940,2.308,965,3.89,1060,2.991,1114,3.926,1170,1.894,1207,5.618,1223,3.494,1235,3.042,1257,3.585,1295,5.618,1315,4.76,1328,3.685,1329,4.662,1330,4.199,1331,4.071,1332,3.767,1333,6.646,1334,6.646,1335,6.646,1336,6.646,1337,6.646,1338,6.646,1339,6.646,1340,6.646,1341,6.646,1342,6.646,1343,5.274]],["t/72",[2,2.651,16,1.113,18,2.628,23,1.996,24,1.592,26,2.22,27,1.419,28,1.387,48,2.426,56,1.189,85,2.071,98,2.101,107,2.516,109,1.902,116,2.231,127,1.961,132,2.487,164,1.643,165,1.897,170,2.501,196,2.37,236,1.572,313,2.068,332,3.103,377,1.337,381,2.829,394,2.989,397,2.486,405,1.902,426,2.208,525,2.818,528,3.15,550,2.35,553,3.166,560,3.183,568,1.712,605,2.164,644,5.264,724,2.911,756,3.974,781,5.446,965,3.771,1037,2.279,1038,5.446,1180,3.771,1246,5.113,1279,4.983,1280,4.027,1285,3.367,1287,5.98,1288,5.98,1292,5.674,1300,3.908,1301,5.446,1344,6.443,1345,4.324,1346,6.443,1347,9.123,1348,6.443,1349,6.443,1350,6.443,1351,5.446,1352,2.875,1353,5.264,1354,6.443,1355,5.446,1356,5.446]],["t/74",[2,1.839,12,1.336,16,0.616,18,2.997,21,1.927,23,2.277,24,0.873,27,2.054,28,1.109,29,1.156,31,1.666,37,2.208,56,1.007,68,1.256,84,0.851,85,1.147,89,2.012,103,1.108,107,0.848,109,1.15,112,1.641,116,2.012,125,1.888,126,1.599,127,1.768,134,1.648,138,2.751,140,2.113,144,1.586,153,1.694,160,1.89,162,1.164,163,1.313,164,0.91,165,1.208,169,1.812,171,0.9,172,2.723,173,5.641,174,1.603,183,1.422,190,1.702,193,2.481,221,1.223,224,2.285,228,1.049,229,1.965,236,2.067,275,1.025,276,1.641,281,1.567,282,3.709,283,0.958,289,2.695,291,1.951,313,0.809,316,2.403,322,1.036,337,1.543,343,1.178,359,1.461,362,2.591,377,2.075,383,2.306,385,4.747,404,1.613,408,2.838,409,1.678,430,1.634,433,1.912,440,1.461,441,1.801,444,1.436,445,2.089,477,1.965,490,2.334,491,1.625,502,0.913,511,1.056,525,1.561,527,0.724,541,2.039,552,3.731,553,1.754,564,2.145,568,1.544,592,2.126,601,2.531,607,1.27,608,1.719,614,1.256,631,2.165,632,2.901,638,3.245,655,2.165,725,1.686,742,2.072,743,2.186,768,3.017,806,4.218,807,1.28,810,2.394,819,2.334,820,1.702,828,1.381,841,2.492,846,1.471,877,2.487,940,1.239,952,2.145,962,3.712,965,2.089,966,4.076,986,3.575,988,3.018,1012,3.055,1123,2.364,1235,1.634,1293,3.143,1294,3.143,1295,3.017,1296,3.143,1298,3.143,1305,2.641,1306,2.072,1308,1.965,1331,2.186,1332,2.023,1343,2.832,1357,3.569,1358,3.569,1359,3.569,1360,1.888,1361,2.916,1362,2.395,1363,1.942,1364,2.395,1365,2.072,1366,1.843,1367,1.606,1368,3.569,1369,3.569,1370,3.017,1371,2.545,1372,3.143,1373,2.023,1374,3.569,1375,3.569,1376,1.656,1377,3.312,1378,3.017,1379,2.545]],["t/76",[2,1.598,6,2.692,12,1.501,18,3.242,21,1.052,24,1.195,26,1.382,27,2.527,28,1.38,31,1.447,46,1.543,50,2.178,56,1.083,84,0.799,90,2.791,98,1.308,105,1.853,106,2.704,107,2.725,109,1.969,115,4.227,120,3.722,123,1.268,125,2.122,132,1.093,157,2.149,163,2.348,164,1.023,170,1.1,171,1.011,174,2.193,175,2.036,181,2.109,203,0.891,213,1.515,216,1.895,229,2.208,239,2.135,244,2.291,254,3.144,272,1.524,279,1.608,281,1.761,295,1.497,313,1.447,318,0.906,320,2.135,322,1.853,334,2.109,337,1.734,343,1.324,353,2.012,358,3.102,359,3.253,362,2.912,372,1.393,377,1.325,426,1.375,437,1.747,444,1.614,472,1.812,489,2.481,502,1.026,507,3.222,513,1.754,527,1.295,544,2.328,550,2.328,572,1.971,585,2.813,586,3.277,611,1.547,614,0.685,635,1.35,658,4.396,683,1.393,694,2.024,734,1.647,755,1.676,757,0.917,796,1.951,936,1.592,950,2.592,965,2.348,997,4.061,1006,1.904,1037,1.419,1047,3.538,1101,2.813,1164,1.877,1218,2.096,1223,2.109,1245,1.96,1275,2.389,1278,2.481,1293,3.532,1294,3.532,1295,3.39,1296,3.532,1298,3.532,1352,1.79,1377,3.722,1380,2.348,1381,2.348,1382,5.841,1383,3.277,1384,4.011,1385,2.912,1386,3.532,1387,3.183,1388,2.813,1389,3.513,1390,2.656,1391,3.277,1392,3.031,1393,4.011,1394,2.481]],["t/78",[2,1.879,12,1.58,16,0.963,17,0.895,18,4.136,21,1.743,23,0.756,24,1.401,26,1.454,27,0.93,28,0.319,29,0.79,31,0.553,34,0.577,46,3.391,47,1.428,49,0.509,56,0.808,84,0.886,85,1.357,90,2.906,92,3.946,93,4.119,94,2.118,96,1.823,97,3.659,101,2.085,103,0.758,105,0.708,106,1.435,107,1.003,109,0.66,113,0.826,115,1.803,127,1.284,128,1.868,132,0.665,138,1.58,140,0.887,144,1.084,145,0.91,157,1.307,164,0.622,167,1.74,172,2.136,174,1.164,181,1.283,186,1.067,190,1.164,193,0.824,194,1.74,207,1.599,228,0.717,229,2.323,236,0.595,240,1.238,254,0.728,272,0.927,275,0.701,279,0.978,283,1.132,295,0.91,304,0.477,306,1.005,313,1.702,315,1.075,323,2.087,324,2.741,326,1.541,332,1.175,334,1.283,338,0.963,343,0.805,348,1.453,353,1.068,359,1.727,362,1.771,369,1.405,372,0.847,376,1.291,377,0.876,381,1.853,390,1.891,393,1.112,405,3.258,408,1.414,421,1.416,430,1.932,440,0.998,441,1.231,447,1.071,476,1.502,482,1.012,489,1.509,491,1.859,505,0.938,506,1.343,509,2.219,511,1.65,525,1.067,527,0.495,552,1.832,562,1.112,565,0.739,568,1.766,572,2.074,579,1.637,580,0.786,593,1.117,596,1.764,597,3.185,599,4.656,611,0.941,614,0.417,615,1.325,619,1.453,620,2.166,630,1.446,633,1.771,653,1.629,658,1.084,659,1.466,660,1.685,683,0.847,687,1.887,694,2.13,707,2.179,710,1.771,714,1.576,715,1.164,716,1.993,734,1.002,742,1.416,758,1.887,769,0.87,773,1.771,795,1.48,796,2.053,798,1.245,810,1.005,846,1.005,877,0.826,913,2.149,926,2.264,935,1.74,936,1.675,942,0.956,947,1.153,949,1.685,966,2.96,986,1.187,997,1.916,999,2.13,1000,2.149,1009,1.043,1010,2.264,1041,1.44,1066,1.117,1072,1.887,1108,1.525,1164,1.142,1197,1.805,1220,1.887,1266,2.852,1303,1.352,1328,1.352,1360,1.291,1395,1.771,1396,2.149,1397,1.372,1398,3.448,1399,4.819,1400,7.452,1401,2.44,1402,2.44,1403,2.44,1404,2.44,1405,1.844,1406,2.44,1407,2.44,1408,2.298,1409,4.425,1410,2.44,1411,2.44,1412,2.44,1413,2.44,1414,2.44,1415,2.44,1416,2.795,1417,2.585,1418,2.44,1419,2.44,1420,2.44,1421,2.062,1422,2.44,1423,3.917,1424,2.44,1425,2.44,1426,2.44,1427,1.805,1428,1.993,1429,1.711,1430,2.264,1431,1.805,1432,1.844,1433,1.805,1434,1.26,1435,1.525,1436,2.44,1437,2.44,1438,2.44,1439,1.993,1440,2.44,1441,1.362,1442,2.44,1443,1.685]],["t/80",[0,4.313,12,3.976,18,4.849,21,1.422,27,2.938,28,1.486,30,5.572,56,0.973,92,1.863,96,1.772,101,2.678,107,1.288,109,1.662,127,2.449,144,2.409,165,1.127,172,3.086,193,1.83,197,2.242,207,2.053,223,2.833,224,2.131,246,1.902,272,2.06,275,1.557,282,2.736,283,1.455,289,1.988,291,2.963,296,1.939,304,1.06,334,2.85,336,3.039,337,2.343,338,1.238,351,1.821,359,2.218,383,3.503,416,2.316,430,2.481,437,2.361,444,2.181,511,1.604,525,4.198,552,3.906,568,1.441,596,2.266,597,4.601,599,4.579,601,2.361,605,1.821,631,5.823,632,2.706,706,5.33,712,2.706,725,2.561,741,4.837,747,2.066,769,1.933,807,1.944,810,2.234,812,3.353,827,2.549,828,2.098,893,2.905,909,3.463,934,2.399,986,3.915,999,2.736,1012,2.85,1290,2.799,1362,3.638,1365,3.147,1379,3.866,1444,3.353,1445,3.503,1446,2.943]],["t/82",[1,1.925,4,1.027,7,0.538,9,2.145,10,2.917,16,1.003,17,2.695,18,5.199,19,2.403,21,1.927,24,0.873,25,1.061,26,2.532,28,1.109,56,0.916,84,0.359,88,1.492,89,3.462,101,1.763,103,1.108,105,2.133,110,1.745,116,2.012,134,1.648,144,2.582,156,2.255,160,1.161,164,0.91,169,1.812,174,2.757,181,3.055,183,1.422,186,1.561,189,1.772,199,1.634,211,2.465,213,1.348,221,1.223,225,2.438,226,1.606,227,3.559,253,1.993,274,2.76,275,1.669,276,1.641,278,1.187,282,4.275,283,2.273,305,2.107,315,1.573,316,1.476,317,2.695,323,1.336,324,1.754,338,0.815,340,2.208,341,1.822,351,1.952,353,0.903,359,3.007,360,2.364,362,4.218,366,2.072,375,3.964,387,2.306,390,1.599,391,1.394,437,1.555,476,0.962,481,1.979,491,0.998,511,1.056,512,1.142,522,2.465,527,1.179,530,1.481,575,1.573,605,1.199,611,1.377,621,1.702,628,3.522,632,1.782,633,2.591,669,2.697,677,1.745,683,3.238,684,3.557,685,3.795,687,2.76,721,2.832,727,3.143,733,2.916,742,2.072,747,1.36,757,0.816,765,1.965,795,2.165,796,1.736,802,1.34,816,1.876,820,2.772,825,2.545,839,1.9,846,1.471,854,1.763,859,2.641,860,2.503,883,2.429,884,2.089,887,6.648,899,2.28,900,2.165,903,4.749,939,2.039,940,2.552,995,2.255,1024,2.107,1066,1.634,1152,3.632,1156,2.697,1181,2.799,1234,1.876,1302,2.145,1308,1.965,1315,2.966,1373,2.023,1386,3.143,1447,3.143,1448,2.697,1449,2.545,1450,3.569,1451,2.832,1452,3.569,1453,3.017,1454,2.055,1455,3.312,1456,2.255,1457,2.364,1458,3.671,1459,2.429,1460,2.76,1461,3.017,1462,3.312,1463,3.569,1464,2.832,1465,2.395,1466,2.832,1467,2.591,1468,3.312,1469,2.832,1470,3.899,1471,2.832,1472,3.312,1473,1.476]],["t/84",[2,2.189,3,8.132,4,1.748,5,1.985,7,0.444,9,3.649,10,5.144,11,6.471,13,4.408,14,4.193,16,1.51,21,2.687,22,5.035,24,1.786,27,2.258,30,3.524,32,5.076,36,2.485,38,5.635,39,2.293,40,2.969,43,5.635,44,4.818,45,5.635,47,3.554,50,3.297,56,0.556,84,0.61,87,8.115,90,2.655,92,3.005,95,3.496,96,1.985,97,2.52,100,2.52,101,2.999,106,3.486,107,1.443,109,0.95,143,4.589,147,5.635,175,3.082,187,3.923,188,4.075,189,3.015,191,5.868,197,2.511,242,3.971,277,2.896,293,4.493,317,2.816,353,1.537,366,3.524,386,4.193,400,4.408,408,2.034,416,2.595,421,3.524,440,2.485,484,2.387,489,5.409,490,3.971,500,2.666,502,1.554,599,2.896,601,2.645,712,3.031,796,2.954,816,3.192,854,2.999,1108,3.795,1132,3.923,1277,4.961,1474,4.33,1475,5.132,1476,2.07,1477,4.193,1478,6.072,1479,5.132,1480,6.072]],["t/86",[4,2.239,9,4.673,12,1.938,18,4.539,23,3.217,24,1.169,50,2.811,56,1.02,84,0.782,106,1.761,107,1.23,109,0.81,131,3.274,138,2.911,140,1.883,145,1.932,164,1.32,165,1.076,170,1.419,172,1.664,173,2.571,179,1.608,181,4.088,186,3.401,197,3.216,207,1.961,213,1.955,219,1.487,224,3.672,225,1.717,227,3.171,228,1.522,232,2.625,283,2.786,284,5.837,292,2.09,305,4.591,313,2.118,316,2.141,338,1.182,344,4.108,345,4.108,346,3.631,369,2.981,371,4.108,390,2.32,405,1.528,407,3.262,440,2.118,441,2.613,468,3.056,476,1.395,480,3.27,482,2.148,500,2.273,512,1.656,527,1.578,545,4.006,546,4.003,558,2.148,559,3.912,560,2.557,562,2.359,568,2.067,575,3.428,576,2.83,601,2.255,605,1.739,610,3.692,677,2.531,705,2.957,733,4.23,795,3.14,796,2.518,807,1.856,810,2.133,811,2.981,834,3.912,837,4.108,887,3.03,893,2.774,953,1.943,1014,3.111,1066,3.56,1113,3.692,1212,4.763,1216,2.446,1385,3.758,1388,3.631,1397,2.912,1462,4.804,1470,3.474,1474,3.692,1481,3.758,1482,3.631,1483,4.003,1484,5.177,1485,5.177,1486,3.523,1487,3.202]],["t/88",[18,4.837,56,1.087,105,2.776,194,6.819,246,3.355,247,3.899,296,3.42,318,2.16,323,3.579,359,3.913,377,2.462,383,6.178,578,3.487,623,4.249,628,4.583,794,6.178,1006,4.539,1132,6.178,1381,3.517,1441,5.339,1488,9.562,1489,7.587,1490,8.874,1491,6.819]],["t/90",[4,1.934,7,0.491,16,1.871,18,5.035,20,3.899,28,0.88,31,2.66,84,0.675,89,2.326,105,1.95,123,2.971,157,3.599,164,1.712,174,2.591,177,3.01,179,2.917,181,3.531,201,4.438,211,4.638,213,2.537,216,3.174,225,2.228,226,3.022,227,4.114,228,1.975,236,1.639,244,3.837,245,3.724,262,4.075,275,1.93,298,3.807,300,3.779,306,2.768,315,2.961,318,1.518,320,3.576,323,2.514,340,4.155,348,4,353,2.742,367,3.174,430,3.075,511,1.987,527,1.363,555,2.949,568,1.785,601,2.926,630,2.302,672,3.672,683,3.262,693,3.837,696,4.393,709,4.075,856,3.724,887,3.931,913,5.916,921,5.076,1195,5.194,1396,5.916,1492,5.194,1493,3.965,1494,5.916,1495,6.717,1496,5.488,1497,6.717,1498,4.449,1499,5.916]],["t/92",[17,4.27,48,3.494,56,0.85,105,2.694,111,6.316,127,2.825,134,4.286,213,3.505,221,3.181,225,3.079,232,3.133,254,2.77,275,2.666,292,3.746,323,4.358,377,1.926,468,5.479,734,3.811,814,3.709,860,6.51,1009,3.966,1042,5.182,1212,7.131,1214,5.578,1500,6.51,1501,8.613,1502,9.239]],["t/94",[2,2.392,4,1.397,5,0.939,8,1.434,15,0.972,16,1.651,17,2.31,19,2.605,21,1.272,23,1.503,24,0.729,27,1.069,28,1.252,31,1.1,32,1.667,56,0.921,84,1.052,89,2.182,96,1.586,103,0.892,105,1.408,116,1.68,123,0.908,127,0.874,129,1.592,132,1.322,133,1.14,134,2.241,141,2.665,156,3.065,158,1.332,160,1.578,161,1.814,162,0.937,164,1.888,165,0.597,186,1.256,187,1.856,200,2.231,201,1.357,221,2.538,225,2.745,232,3.531,236,1.537,237,4.287,247,1.979,256,0.997,275,1.81,281,1.261,283,1.302,292,1.159,299,1.71,306,1.183,317,1.332,318,1.096,323,3.36,340,1.777,348,1.71,351,1.629,358,2.221,359,1.985,361,1.795,365,1.351,377,1.717,391,1.122,393,1.309,395,1.053,398,0.939,399,2.014,415,4.12,430,1.315,476,0.774,478,2.729,491,2.315,496,1.628,502,0.735,505,1.105,508,1.57,511,1.435,530,3.073,532,3.049,534,1.902,538,2.221,539,2.89,543,1.501,552,2.069,555,1.261,558,1.192,572,1.411,585,2.014,587,2.17,611,1.108,615,1.559,627,2.279,666,3.522,670,1.549,682,2.221,683,0.997,684,2.348,690,1.654,698,1.927,712,2.422,747,1.849,802,1.078,820,1.37,828,2.438,857,2.652,862,1.326,919,1.834,927,2.014,931,2.048,953,1.821,993,1.539,1059,2.52,1163,1.616,1169,1.695,1187,2.279,1194,2.428,1202,1.404,1212,7.586,1214,5.744,1215,7.573,1217,4.872,1219,7.164,1235,1.315,1289,1.821,1381,1.056,1501,5.846,1503,2.279,1504,1.878,1505,2.665,1506,3.752,1507,6.007,1508,1.474,1509,2.279,1510,2.872,1511,1.742,1512,4.273,1513,2.529,1514,2.872,1515,2.872,1516,2.872,1517,2.872,1518,2.872,1519,2.872,1520,2.872,1521,2.872,1522,2.872,1523,2.529,1524,2.872,1525,4.273,1526,4.101,1527,1.759,1528,2.529,1529,2.872,1530,1.604,1531,1.777,1532,5.548,1533,2.872,1534,4.503,1535,2.048,1536,1.954]],["t/96",[28,1.355,56,0.948,84,1.04,158,4.798,196,3.805,225,3.431,377,2.147,532,5.007,920,5.579,1163,5.819,1219,8.876,1532,9.11,1534,9.6,1537,10.344,1538,7.377,1539,6.399]],["t/98",[15,1.328,17,1.439,48,1.477,56,0.82,84,0.394,103,1.948,105,1.139,108,1.881,153,1.863,156,2.479,158,1.82,160,2.04,163,1.443,187,2.535,203,0.872,232,1.324,247,4.641,254,1.171,275,1.127,304,0.767,321,2.752,324,1.928,334,2.063,343,1.295,351,1.318,377,0.814,398,3.411,401,2.225,502,1.004,511,1.161,532,1.899,539,5.828,540,3.113,543,3.277,553,1.928,607,1.396,611,1.514,621,1.871,637,3.735,708,2.848,762,2.452,795,2.38,841,1.683,898,1.766,920,2.116,1047,2.175,1055,2.403,1129,3.64,1170,3.428,1212,2.403,1215,2.67,1278,2.427,1309,5.124,1363,2.618,1364,2.633,1441,2.191,1507,2.848,1527,2.403,1531,2.427,1535,5.587,1536,5.331,1540,2.014,1541,3.924,1542,9.619,1543,3.924,1544,6.272,1545,6.272,1546,6.272,1547,8.95,1548,10.436,1549,3.924,1550,6.18,1551,6.272,1552,3.924,1553,3.924,1554,7.102,1555,8.95,1556,9.786,1557,3.611,1558,4.094,1559,4.473,1560,5.301,1561,6.272,1562,6.272,1563,4.74,1564,3.924,1565,2.535,1566,4.154,1567,2.798,1568,3.924,1569,3.924,1570,3.034,1571,3.924,1572,3.924,1573,3.924,1574,3.924,1575,3.924,1576,3.924,1577,2.752,1578,3.924,1579,6.921,1580,3.924,1581,3.924,1582,3.924,1583,2.904,1584,3.924,1585,3.924,1586,3.924,1587,3.924,1588,3.317,1589,3.924,1590,3.924,1591,8.95,1592,3.924,1593,3.456,1594,3.924,1595,3.317,1596,3.924,1597,2.297,1598,3.924,1599,3.924,1600,3.924,1601,2.175,1602,3.924,1603,3.924,1604,3.924]],["t/100",[15,3.961,16,1.01,24,1.279,34,1.382,56,1.155,85,1.88,96,1.911,97,2.427,158,2.712,164,2.169,165,1.216,228,1.719,247,3.47,338,1.335,348,3.482,351,2.858,367,2.763,377,1.214,388,3.924,395,2.144,405,1.726,416,2.499,418,2.712,471,3.273,505,2.249,528,2.859,530,2.427,538,7.757,539,5.974,575,2.578,578,2.132,591,3.093,747,2.228,829,2.859,857,2.462,920,3.154,930,2.968,940,2.03,1059,3.037,1139,2.904,1215,3.979,1219,8.712,1366,3.019,1376,4.653,1532,9.702,1535,4.17,1536,3.979,1542,9.312,1559,4.17,1560,4.943,1563,4.419,1567,4.17,1605,3.979,1606,7.494,1607,5.162,1608,5.149,1609,5.149,1610,5.149,1611,5.149,1612,5.149,1613,5.149,1614,5.149,1615,5.149,1616,4.394,1617,5.847,1618,5.427,1619,5.847,1620,5.847,1621,5.847,1622,3.482,1623,5.149,1624,5.847,1625,4.101,1626,5.847,1627,5.847,1628,4.17]],["t/102",[16,1.757,19,5.096,84,1.023,140,3.701,225,3.376,275,2.924,323,3.809,468,6.008,471,3.914,491,2.847,906,5.725,1212,6.233,1215,6.926,1216,4.809,1507,7.388,1525,8.963,1526,8.603,1629,6.576]],["t/104",[15,2.822,16,1.705,24,1.253,56,1.298,145,2.123,160,1.851,203,1.264,225,1.887,300,3.2,338,1.299,372,1.975,395,2.087,398,1.86,401,2.959,405,2.462,426,2.859,562,2.593,601,2.478,607,2.024,651,3.519,747,2.168,898,2.56,930,2.888,965,3.33,971,4.924,1008,3.2,1212,5.108,1215,5.676,1228,5.01,1266,2.752,1289,3.707,1320,3.359,1525,5.01,1526,4.809,1555,4.648,1563,4.299,1607,5.059,1630,5.689,1631,4.299,1632,2.74,1633,5.467,1634,5.689,1635,6.45,1636,9.875,1637,9.875,1638,3.998,1639,5.689,1640,9.875,1641,9.875,1642,4.514,1643,9.787,1644,5.01,1645,5.01,1646,5.689,1647,5.689,1648,5.689,1649,8.341,1650,5.28,1651,5.28,1652,5.689,1653,5.689,1654,5.689,1655,5.689,1656,5.689]],["t/106",[5,1.459,12,2.599,15,2.884,16,0.771,23,1.383,24,1.043,28,0.909,29,1.445,49,0.931,56,1.31,84,0.698,85,1.435,89,1.546,97,1.852,103,1.386,135,2.362,138,1.671,140,1.623,164,1.138,196,2.554,225,1.481,275,1.282,283,1.198,286,2.511,323,3.189,326,2.82,336,1.686,338,1.019,359,1.827,367,2.109,368,3.131,376,2.362,395,2.547,397,1.722,401,1.583,405,1.318,426,3.295,478,2.511,511,1.321,539,2.659,543,2.333,560,3.43,568,1.845,607,1.588,781,3.773,831,2.613,931,3.183,940,1.55,965,5.627,971,7.026,979,2.734,1037,3.401,1212,5.219,1214,2.683,1215,5.799,1228,3.931,1240,5.086,1247,5.506,1266,1.757,1285,3.628,1320,5.675,1559,3.183,1607,5.169,1643,7.203,1644,3.931,1645,7.505,1650,4.143,1657,4.464,1658,4.464,1659,6.944,1660,4.464,1661,4.464,1662,4.464,1663,4.464,1664,3.038,1665,4.464,1666,4.464,1667,4.464,1668,4.464,1669,4.143,1670,3.931,1671,2.278,1672,3.647,1673,4.143,1674,4.464,1675,4.464,1676,6.944,1677,4.464,1678,6.944,1679,8.521,1680,4.464,1681,4.464,1682,4.464,1683,6.944,1684,4.464,1685,4.464,1686,3.773,1687,3.452]],["t/108",[0,2.545,2,1.823,7,0.347,15,1.607,16,1.85,19,3.012,21,1.91,24,1.331,25,2.632,28,1.301,33,2.512,34,1.721,56,0.667,105,1.379,110,2.322,116,2.522,128,2.102,135,3.853,156,3,158,4.11,164,1.211,165,0.987,183,1.892,197,1.964,200,2.183,213,1.793,220,2.713,225,3.295,228,2.141,235,3.88,236,1.777,283,2.377,295,1.772,306,3.001,313,1.077,323,4.01,353,1.202,377,1.512,394,2.203,471,1.826,476,1.962,491,1.328,492,3.448,502,1.215,505,1.826,522,3.279,528,2.322,550,1.732,552,4.309,553,2.334,558,3.677,560,2.346,568,1.262,606,3.387,607,1.689,627,5.779,638,2.652,653,1.387,669,3.589,729,3.145,756,2.069,757,1.086,820,2.265,825,3.387,827,2.233,829,2.322,833,3.106,852,4.31,921,3.589,931,3.387,935,3.387,951,2.828,993,2.545,1047,2.633,1212,2.909,1215,8.264,1217,3.673,1219,3.387,1240,3.853,1271,3.232,1274,2.692,1512,7.803,1513,8.749,1567,3.387,1628,3.387,1632,2.287,1688,4.601,1689,2.31,1690,4.749,1691,3.88,1692,4.749,1693,2.578,1694,4.749,1695,3.88,1696,2.804]],["t/110",[0,0.972,12,1.216,16,0.762,24,0.273,25,0.539,27,0.399,28,1.251,34,0.429,35,1.172,48,0.683,56,1.375,68,3.12,84,0.182,95,2.542,100,2.857,105,0.526,108,2.967,116,0.628,119,1.533,128,1.438,164,0.462,165,0.918,198,0.819,200,1.494,213,0.685,217,0.959,225,0.602,247,0.739,295,0.677,299,1.08,304,0.355,306,0.747,322,1.282,323,2.014,326,3.398,338,0.414,343,0.599,353,0.459,372,1.128,375,0.978,395,0.665,398,2.446,405,1.828,413,1.272,422,1.146,426,3.627,446,2.606,471,0.697,476,1.189,483,2.584,502,0.464,511,0.536,527,0.368,552,3.139,560,1.605,707,0.936,726,1.1,736,0.718,747,0.691,752,2.456,790,1.172,813,2.653,825,1.293,851,0.882,852,2.164,862,1.501,930,3.797,934,0.803,954,1.122,965,4.953,971,6.767,978,1.133,979,1.111,981,1.044,1008,1.828,1037,1.562,1075,0.978,1087,3.097,1164,0.849,1212,1.99,1214,2.653,1215,2.211,1223,1.708,1240,3.275,1245,0.557,1247,2.853,1266,1.361,1289,1.657,1310,1.201,1320,3.655,1367,2.786,1381,0.667,1390,1.201,1441,1.013,1635,1.402,1638,2.578,1643,4.547,1644,1.597,1645,1.597,1651,4.097,1669,1.683,1670,3.888,1673,1.683,1695,1.482,1696,6.116,1697,1.813,1698,1.252,1699,1.044,1700,1.252,1701,1.813,1702,1.813,1703,1.439,1704,2.513,1705,1.842,1706,1.813,1707,4.415,1708,1.813,1709,3.25,1710,1.813,1711,3.25,1712,3.25,1713,10.118,1714,1.813,1715,1.813,1716,5.38,1717,1.813,1718,2.01,1719,1.683,1720,4.415,1721,1.813,1722,1.813,1723,1.813,1724,3.25,1725,3.25,1726,3.25,1727,1.683,1728,1.813,1729,3.267,1730,3.336,1731,1.813,1732,1.252,1733,1.005,1734,1.597,1735,3.476,1736,1.813,1737,1.137,1738,3.25,1739,3.25,1740,3.25,1741,3.25,1742,5.38,1743,1.813,1744,1.813,1745,1.813,1746,1.813,1747,1.813,1748,1.813,1749,1.813,1750,1.813,1751,1.813,1752,1.813,1753,1.158,1754,0.715,1755,1.813,1756,4.415,1757,1.813,1758,1.813,1759,1.158,1760,1.813,1761,1.813,1762,1.813,1763,1.533,1764,1.813,1765,0.965,1766,3.25,1767,1.813]],["t/112",[16,1.505,25,3.324,26,2.082,28,1.465,56,1.251,105,2.53,129,3.35,132,1.647,134,2.791,165,1.256,199,2.766,200,2.778,201,2.855,275,1.736,306,3.591,316,2.499,323,2.262,337,2.612,338,1.379,353,2.205,367,2.855,377,1.254,405,1.784,426,2.987,447,2.653,476,1.628,502,1.546,511,2.578,552,4.282,558,4.643,560,2.985,605,2.029,607,2.149,629,3.567,690,3.479,698,5.848,730,4.567,763,5.505,852,2.431,936,2.399,939,3.452,965,5.101,971,7.294,979,3.701,1037,2.138,1180,5.101,1240,5.407,1247,3.905,1320,3.567,1628,4.309,1643,5.108,1670,5.322,1718,3.738,1719,5.608,1727,5.608,1768,3.817,1769,6.043,1770,6.043,1771,6.043,1772,6.043,1773,4.238]],["t/114",[16,1.847,17,3.922,56,0.98,275,3.072,277,5.101,324,5.255,359,4.376,377,2.22,469,9.925,511,3.164,662,5.768,1528,9.418,1774,10.694]],["t/116",[2,2.819,7,0.448,10,3.077,16,1.059,21,1.608,23,1.899,29,1.985,31,1.996,56,0.944,84,0.885,105,1.78,110,2.997,116,3.049,131,2.581,134,2.831,135,3.243,158,2.844,160,3.352,187,3.961,189,3.045,197,2.535,213,3.325,215,6.08,225,2.034,230,4.741,232,2.972,244,3.502,248,2.671,254,1.829,277,2.924,283,1.645,284,5.503,302,2.62,313,1.996,314,4.537,320,4.688,334,3.223,340,3.792,359,3.603,376,3.243,377,1.272,393,2.794,430,2.806,522,4.234,530,3.654,545,2.63,563,5.182,572,3.013,578,2.236,585,4.3,587,4.633,611,2.365,614,1.504,628,2.938,683,2.129,764,3.832,778,3.588,816,3.223,820,2.924,839,3.264,849,4.3,949,4.234,1026,4.865,1066,2.806,1212,7.305,1214,3.684,1215,4.172,1284,2.782,1512,5.399,1513,5.399,1530,3.423,1733,3.399,1775,4.741,1776,6.131,1777,6.131,1778,4.633,1779,3.832,1780,6.131]],["t/118",[7,0.943,16,1.381,23,2.478,24,0.808,27,1.762,28,1.047,31,2.166,40,2.628,55,2.713,56,0.733,84,0.804,89,2.77,103,1.669,109,1.252,123,3.021,156,3.395,158,2.493,165,1.117,187,3.473,215,3.712,216,2.539,219,1.544,225,1.783,226,3.599,227,3.292,229,2.959,232,2.7,278,1.787,281,2.36,283,2.146,284,3.359,298,3.047,313,1.813,315,2.369,332,2.589,336,2.03,338,1.227,376,2.843,476,1.448,478,3.024,491,1.503,500,2.36,527,1.623,530,2.231,545,2.305,552,1.766,555,2.36,558,2.231,562,2.45,565,1.629,567,2,568,1.428,576,2.938,578,1.96,584,4.157,585,3.77,593,2.46,596,2.247,597,2.576,604,3.292,626,4.543,630,1.842,658,2.389,683,1.866,684,2.602,719,3.001,741,3.23,818,2.504,838,4.734,839,4.258,852,2.162,856,2.98,857,2.263,881,3.658,888,2.938,903,3.473,1026,4.265,1067,3.902,1179,3.07,1188,3.833,1193,3.146,1194,4.543,1212,3.292,1224,3.201,1243,3.658,1416,3.56,1417,3.292,1453,4.543,1487,3.325,1507,3.902,1540,2.759,1688,3.395,1781,4.543,1782,3.992,1783,5.375,1784,4.265,1785,4.734,1786,4.543,1787,4.391,1788,4.157,1789,4.062,1790,4.543]],["t/120",[7,0.846,27,1.434,29,2.108,31,1.476,55,3.286,56,0.597,85,2.093,89,3.182,93,3.613,123,2.059,158,3.019,160,2.118,172,2.093,219,1.87,227,3.987,231,4.368,232,3.102,262,3.949,283,1.747,306,2.682,318,1.471,323,2.436,332,3.135,334,4.831,341,3.323,353,1.647,359,2.664,372,2.26,395,3.906,430,4.206,471,2.504,512,2.94,536,2.54,548,3.718,552,2.138,567,3.419,620,4.717,670,3.511,677,3.183,684,5.155,694,3.286,721,5.165,763,4.112,767,4.642,829,3.183,834,4.919,840,4.726,841,2.792,847,5.468,852,2.619,853,4.566,860,4.566,885,4.861,951,3.877,952,3.912,1006,3.09,1007,5.743,1046,2.824,1073,4.368,1130,5.733,1213,6.041,1363,2.175,1381,2.394,1791,6.51,1792,3.778,1793,5.733,1794,9.19,1795,6.51]],["t/122",[2,0.871,7,0.673,16,1.441,17,1.277,24,1.085,28,1.294,29,1.127,31,1.892,34,0.823,48,1.311,55,2.873,56,0.522,84,0.35,85,1.119,89,3.187,96,1.861,98,1.135,103,1.081,111,2.369,123,1.8,145,2.696,160,1.132,162,1.135,170,2.709,174,0.96,179,1.768,182,1.768,206,2.092,211,2.404,216,1.645,224,2.84,225,1.155,226,1.566,227,3.487,236,0.849,243,1.767,257,1.645,275,1,278,1.893,283,0.934,289,1.277,292,1.405,295,1.299,306,1.434,313,0.789,318,1.885,323,3.123,324,1.711,332,1.676,336,1.314,338,0.795,351,1.169,353,0.881,359,3.415,362,2.527,367,1.645,369,3.278,375,1.877,391,1.36,397,3.219,405,2.464,440,2.33,447,2.5,471,3.539,484,2.84,491,0.974,495,1.56,505,2.189,508,1.903,514,2.441,527,1.155,550,2.076,555,2.5,558,1.444,578,1.269,613,1.46,622,1.629,632,1.738,670,1.877,677,1.702,684,2.755,708,2.527,719,3.179,749,3.421,754,2.63,755,1.455,757,0.796,758,2.692,760,1.981,761,3.065,772,3.487,780,1.719,794,2.249,801,2.404,805,3.596,827,3.397,828,1.347,839,1.853,840,4.133,851,1.693,854,2.812,877,1.178,883,2.369,888,3.112,907,3.39,920,1.877,936,1.382,946,3.086,947,1.645,951,2.073,952,3.421,953,1.307,955,2.527,1009,1.487,1011,2.175,1072,2.692,1135,2.692,1152,2.175,1181,1.676,1214,3.421,1289,1.307,1308,1.916,1315,2.906,1387,2.762,1451,2.762,1456,2.199,1458,2.199,1583,2.576,1782,5.516,1796,3.065,1797,3.481,1798,2.94,1799,3.636,1800,3.23,1801,4.868,1802,2.942,1803,2.369,1804,3.23,1805,2.692,1806,5.459,1807,2.576,1808,2.942,1809,3.23,1810,2.762,1811,3.065,1812,3.065,1813,2.369,1814,2.305]],["t/124",[4,1.487,7,0.65,15,0.748,16,1.679,17,2.276,18,2.107,19,1.601,21,1.355,23,3.299,24,1.335,26,0.761,27,0.487,28,1.017,29,0.715,31,1.171,33,1.169,36,0.904,46,1.489,47,1.293,49,1.078,55,1.115,56,1.152,84,1.138,85,0.71,88,1.618,89,1.341,98,0.721,99,1.293,105,0.641,123,1.224,129,1.225,132,1.055,133,1.537,138,2.322,140,0.803,144,0.982,157,1.184,158,1.025,160,0.719,161,1.396,163,0.813,164,0.987,165,1.467,171,0.976,174,0.609,186,1.693,187,1.428,196,0.813,199,1.011,203,0.491,207,0.837,211,2.673,213,1.462,219,2.027,225,1.284,226,0.994,228,1.138,232,3.885,236,0.945,244,1.262,247,0.901,257,1.044,262,1.34,275,1.782,276,1.78,281,0.97,282,1.115,283,2.383,284,4.41,292,2.086,299,1.316,303,1.604,305,2.285,313,1.407,318,0.499,320,1.176,323,1.934,328,1.353,334,1.162,336,1.462,341,1.128,346,1.55,348,1.316,351,0.742,353,0.98,363,1.463,366,1.282,372,0.767,377,0.804,386,1.526,395,0.81,407,0.927,430,1.772,433,1.184,441,3.132,471,1.489,476,1.043,478,3.969,480,1.396,491,1.445,500,0.97,512,1.653,527,1.575,530,3.222,532,1.069,535,1.868,536,1.428,537,2.051,539,1.316,543,1.155,544,1.282,545,4.77,548,2.211,550,0.806,552,0.726,553,1.086,558,2.928,559,3.904,560,3.065,565,0.669,575,2.735,578,0.806,583,1.252,585,1.55,586,1.805,590,1.103,591,1.169,596,0.923,601,1.686,605,0.742,612,1.328,613,1.624,636,1.155,641,1.709,642,1.946,659,1.328,666,1.604,683,1.794,686,1.604,702,1.381,709,1.34,715,1.054,734,0.907,736,1.532,744,1.169,757,1.419,762,1.381,765,1.216,809,1.411,829,1.08,831,1.293,852,0.889,875,1.635,877,0.748,886,1.304,898,0.994,929,2.446,934,0.978,938,1.604,943,1.868,948,2.348,993,1.184,1009,0.944,1014,4.24,1072,1.709,1129,2.247,1164,1.034,1195,1.709,1202,1.08,1203,3.272,1216,1.044,1234,2.035,1235,1.772,1243,1.503,1308,2.844,1312,2.648,1343,1.753,1363,0.738,1366,1.141,1397,1.243,1445,2.502,1449,3.685,1451,1.753,1498,1.463,1583,1.635,1753,1.411,1782,2.16,1815,1.868,1816,1.526,1817,2.21,1818,2.21,1819,1.316,1820,2.051,1821,2.21,1822,1.234,1823,2.21,1824,2.21,1825,1.753,1826,1.526,1827,1.753,1828,2.051,1829,2.21,1830,3.871,1831,2.865,1832,2.051,1833,2.21,1834,2.21,1835,2.051,1836,1.635,1837,2.21,1838,1.526,1839,2.051,1840,1.946,1841,2.21,1842,2.21,1843,1.868,1844,2.194,1845,1.216,1846,2.21,1847,1.381,1848,1.604]],["t/126",[7,0.777,21,1.698,27,2.863,28,1.512,31,1.468,56,0.593,84,0.651,89,4.218,101,3.199,126,2.902,128,2.866,160,2.107,162,2.112,165,1.346,175,3.287,202,4.137,207,3.468,291,3.54,294,3.233,295,2.417,315,4.036,317,3.004,318,1.463,334,3.405,336,2.446,338,1.478,341,4.673,353,2.688,391,2.53,415,4.235,471,2.491,478,5.151,512,2.072,513,2.832,578,2.362,707,3.344,735,6.231,736,4.203,758,5.008,781,5.474,802,2.431,833,4.235,854,3.199,857,2.727,886,3.823,900,3.928,1069,4.596,1072,5.008,1075,4.938,1080,5.291,1164,3.031,1330,4.091,1490,6.01,1782,2.707,1798,3.344,1849,4.235,1850,4.048,1851,4.701,1852,6.476,1853,6.476,1854,5.474,1855,6.476,1856,6.476,1857,6.476]],["t/128",[6,2.894,7,0.75,16,2.089,21,1.772,23,2.094,24,1.633,25,2.803,26,2.328,27,2.078,28,1.54,31,2.323,35,2.786,49,1.41,50,3.669,55,2.176,56,0.865,84,0.433,104,3.523,108,3.239,115,1.842,128,1.908,130,2.281,131,2.845,132,1.842,156,2.724,163,1.586,164,1.099,165,1.733,168,3.334,179,1.339,182,1.339,185,2.754,190,2.057,198,1.948,213,2.552,228,1.268,232,2.281,236,1.052,276,1.982,289,1.581,313,0.978,336,2.552,337,1.864,338,1.903,339,3.163,341,3.449,343,1.423,367,2.037,377,0.895,422,2.724,437,1.878,440,1.764,458,2.253,471,1.658,472,1.948,498,2.226,501,3.645,512,2.162,532,2.087,568,2.215,591,2.281,596,1.802,635,1.452,636,2.253,653,1.26,655,2.616,677,2.108,709,2.616,723,1.43,734,2.775,736,1.706,737,3.024,747,1.643,751,2.31,753,3.421,765,2.373,805,2.724,846,1.777,847,2.213,852,1.735,861,3.421,929,4.269,1114,1.815,1180,3.955,1181,2.077,1315,2.201,1373,2.444,1446,2.341,1583,3.191,1782,1.802,1798,6.865,1799,7.726,1808,3.645,1810,3.421,1847,2.695,1858,4.312,1859,4.002,1860,4.312,1861,4.312,1862,4.312,1863,2.623,1864,2.108,1865,3.258,1866,3.191,1867,4.002]],["t/130",[1,3.674,2,2.1,7,0.753,15,1.474,16,0.752,21,1.142,24,0.655,26,1.501,28,1.099,34,1.03,39,1.645,49,1.421,55,3.439,56,0.624,84,0.843,101,2.152,107,1.035,109,1.942,127,1.326,132,1.857,134,2.012,157,3.65,160,2.216,162,2.222,164,1.111,165,0.906,170,2.3,171,1.098,175,2.211,185,2.783,193,1.47,198,1.968,213,1.645,236,1.662,246,2.391,247,1.776,249,3.008,253,2.433,275,1.957,279,1.747,295,1.626,297,2.211,304,1.641,313,0.988,314,3.224,322,2.435,338,1.555,351,2.288,353,2.605,373,2.923,377,0.904,387,2.815,403,2.594,418,2.021,471,3.649,476,1.174,477,2.398,484,1.713,514,3.055,525,1.905,527,0.884,557,2.012,578,1.589,605,1.463,614,1.865,621,2.078,628,2.088,637,2.594,644,3.559,708,3.162,709,2.643,724,1.968,755,1.821,759,1.417,760,1.194,762,2.723,802,1.635,814,1.741,820,2.078,854,2.152,857,1.834,877,1.474,880,2.723,882,2.415,883,2.964,893,2.334,912,2.815,943,3.682,1034,3.559,1036,2.668,1133,4.188,1170,1.941,1171,3.224,1221,3.008,1282,3.107,1284,1.977,1330,2.752,1343,3.457,1381,1.602,1466,3.457,1540,3.497,1558,2.276,1633,1.905,1801,2.305,1808,3.682,1826,3.008,1850,2.723,1868,4.356,1869,4.043,1870,3.919,1871,3.292,1872,3.837,1873,4.094,1874,2.815,1875,3.369,1876,2.618,1877,4.356,1878,3.837,1879,3.559,1880,3.457,1881,4.356]],["t/132",[2,1.175,4,1.352,5,1.535,6,3.151,7,0.644,15,1.589,17,1.722,22,2.704,24,1.602,26,1.618,28,1.396,33,2.484,55,3.645,56,0.977,85,2.321,103,2.242,107,2.09,108,2.251,109,1.761,125,2.484,134,2.169,135,2.484,138,1.758,139,3.664,145,1.753,160,2.349,162,1.531,164,1.197,170,3.535,183,1.871,190,2.24,193,2.437,216,2.219,217,2.484,228,1.381,229,2.585,245,2.603,279,1.883,281,2.062,283,1.26,289,3.227,303,3.409,304,0.918,313,1.065,338,1.648,351,1.577,353,1.188,359,1.922,362,3.409,365,2.208,369,2.704,383,3.034,392,2.704,407,1.97,430,2.15,437,3.146,440,2.955,447,2.062,471,3.798,496,2.662,557,2.169,565,1.423,568,1.919,608,3.478,636,3.773,677,2.296,684,2.273,757,1.074,759,3.662,760,3.215,765,2.585,801,3.243,802,2.711,841,2.014,847,2.411,875,3.475,899,3,900,4.38,934,2.078,954,2.905,1042,2.622,1181,2.262,1216,2.219,1245,1.442,1290,2.425,1364,3.151,1373,2.662,1381,1.727,1469,3.726,1540,2.411,1558,3.773,1754,1.852,1820,4.358,1870,2.332,1882,2.567,1883,3.726,1884,2.876,1885,3.837,1886,4.696,1887,4.358]],["t/134",[7,0.774,16,1.649,17,1.971,18,2.192,21,1.409,24,1.699,28,0.704,32,3.12,46,2.067,50,2.918,56,0.733,84,0.54,89,2.77,107,1.9,109,0.841,123,1.7,124,3.146,131,2.263,134,2.482,160,1.749,163,2.942,169,4.848,170,3.366,174,1.483,181,2.826,182,1.669,190,2.564,199,2.46,219,1.544,228,1.58,247,2.192,262,3.26,279,3.207,283,1.442,289,1.971,292,2.17,311,4.062,341,2.743,343,1.774,392,3.095,422,5.052,430,2.46,433,2.88,447,2.36,471,3.673,496,3.047,502,1.375,503,4.988,506,2.959,513,2.351,536,1.486,553,2.641,558,2.231,562,2.45,623,3.554,628,2.576,635,1.81,637,3.201,677,2.628,702,3.359,709,3.26,719,3.001,723,3.509,747,2.048,757,1.229,760,2.619,802,2.018,805,3.395,841,2.305,862,3.693,999,2.713,1014,3.23,1122,3.12,1215,5.442,1234,2.826,1330,3.395,1367,2.418,1373,3.047,1397,3.024,1431,5.919,1445,3.473,1500,3.77,1531,3.325,1638,2.576,1782,2.247,1847,3.359,1864,2.628,1888,8.942,1889,4.543,1890,5.375,1891,3.712,1892,4.391,1893,4.734]],["t/136",[2,1.275,7,0.753,9,3.061,24,1.548,28,0.667,31,1.742,36,2.084,49,1.602,56,1.065,84,0.772,101,2.516,107,1.21,109,2.058,115,2.176,116,1.764,123,1.611,127,1.55,132,1.388,139,2.121,157,2.729,160,3.35,164,1.299,170,2.824,172,1.637,174,2.841,200,2.342,203,1.707,213,1.923,223,2.662,233,2.58,236,1.874,261,2.395,283,1.367,313,1.155,318,1.151,328,3.12,336,1.923,338,2.351,351,1.711,353,1.289,377,1.057,447,3.373,471,1.959,502,1.303,508,5.056,517,2.865,607,2.732,634,3.632,683,1.769,756,3.346,759,3.009,765,2.804,827,2.395,846,2.099,862,2.352,880,4.116,885,2.695,914,2.352,922,3.218,980,3.253,1006,2.418,1275,3.033,1397,2.865,1464,4.042,1482,3.572,1486,3.466,1508,5.964,1826,3.517,1847,3.183,1870,2.987,1873,3.061,1891,3.517,1894,4.161,1895,5.093,1896,5.093,1897,5.093,1898,2.49,1899,5.976,1900,3.183,1901,5.941,1902,2.254,1903,5.093,1904,3.517,1905,3.769]],["t/138",[7,0.453,24,1.928,29,2.004,49,1.291,56,0.567,84,1.137,107,2.107,109,1.621,115,2.645,182,1.922,226,2.786,228,1.82,233,2.079,236,1.511,246,2.172,254,3.376,275,1.778,283,1.661,318,2.341,328,3.792,353,1.567,358,4.788,372,2.15,375,3.339,376,3.275,383,4,397,2.388,426,2.122,437,2.697,471,2.381,502,1.584,527,2.102,555,2.718,578,3.234,611,2.388,634,6.323,637,3.687,663,3.509,677,3.027,683,2.15,694,3.125,760,1.697,798,3.16,804,4.912,857,2.606,880,2.474,993,3.317,1041,5.235,1245,2.723,1330,3.911,1381,2.277,1487,3.83,1540,3.178,1691,5.058,1699,3.564,1785,5.452,1873,6.798,1906,4.475,1907,4.25,1908,6.775,1909,5.233,1910,6.191,1911,4.275,1912,6.191,1913,4.912,1914,6.191,1915,4.678,1916,3.432,1917,5.745]],["t/140",[2,2.079,7,0.608,18,4.422,56,0.994,129,4.604,193,2.803,227,5.087,246,2.915,313,1.883,318,1.877,339,3.888,358,6.423,359,3.399,392,4.782,394,3.853,405,2.452,430,3.802,471,3.194,519,6.423,550,3.955,553,4.082,578,3.029,611,3.204,653,3.168,677,4.061,683,2.884,795,5.038,886,4.903,951,4.946,1036,5.087,1179,4.744,1187,6.59,1223,4.367,1234,5.701,1332,4.708,1782,3.471,1832,7.708,1918,3.835,1919,8.306,1920,8.306,1921,8.306,1922,6.423]],["t/142",[7,0.556,16,1.313,17,2.79,23,3.17,55,3.839,84,0.765,105,2.208,113,2.574,135,4.024,164,1.939,171,1.918,196,2.798,203,1.69,213,2.872,219,2.185,225,2.523,236,1.856,275,2.939,283,2.041,313,1.724,322,2.208,323,2.847,342,4.614,398,2.486,401,2.698,421,4.415,438,3.663,479,4.915,525,3.326,543,5.346,548,4.345,552,2.498,614,1.299,703,5.629,757,1.739,828,2.943,953,2.855,1007,4.754,1217,5.882,1238,4.217,1312,2.594,1366,3.927,1427,5.629,1502,6.035,1507,9.366,1526,6.429,1607,4.614,1638,3.645,1775,5.882,1923,7.606,1924,5.748,1925,5.882,1926,6.035,1927,6.035]],["t/144",[2,1.714,5,1.434,7,0.697,9,2.636,15,1.484,16,1.183,18,2.793,19,1.814,21,1.15,23,2.95,24,0.659,28,0.574,49,0.915,55,2.214,56,1.048,84,0.957,88,1.833,89,1.519,105,1.273,116,1.519,127,1.335,131,1.847,156,2.771,160,1.427,164,1.118,165,1.752,171,1.727,189,2.178,196,1.613,213,1.657,219,1.26,220,3.912,221,2.348,225,1.455,232,2.844,236,1.07,239,2.335,283,2.772,284,2.742,292,1.771,306,3.472,313,0.995,316,1.814,322,1.273,338,1.001,347,2.687,348,2.612,351,1.473,353,1.733,359,2.803,366,3.976,367,2.072,377,0.91,386,3.029,393,1.999,395,1.609,397,3.674,407,1.84,408,1.47,440,1.795,447,1.926,471,2.634,482,1.82,512,1.404,530,1.82,543,3.579,552,1.441,558,1.82,611,1.692,614,1.17,636,2.292,647,1.527,684,2.123,734,1.801,736,1.736,747,1.672,790,2.834,795,2.661,796,2.134,802,2.571,807,1.573,820,2.092,851,2.134,857,1.847,893,2.35,1006,2.082,1007,4.281,1114,2.884,1181,3.299,1212,8.087,1215,2.985,1216,2.072,1217,3.392,1219,3.128,1234,2.306,1235,3.135,1290,2.265,1312,3.247,1378,3.708,1387,3.481,1397,2.468,1405,3.315,1473,1.814,1507,6.117,1540,2.252,1775,3.392,1828,4.071,1839,4.071,1843,3.708,1891,3.029,1898,2.145,1928,3.863,1929,3.863,1930,4.387,1931,4.387,1932,4.387,1933,2.713,1934,3.863,1935,3.481,1936,3.584]],["t/146",[2,2.306,7,0.848,16,1.591,26,3.174,28,1.518,55,5.85,132,3.159,162,3.005,249,6.362,342,5.589,446,5.439,467,6.688,511,2.726,684,4.459,802,4.351,813,5.537,814,4.632,846,5.225,882,6.425,920,4.969,1057,5.537,1381,3.389,1937,8.551]],["t/148",[7,0.486,23,2.059,28,0.87,31,1.507,32,3.858,55,4.707,56,0.609,84,0.937,89,2.302,103,2.064,105,1.929,109,1.04,123,2.102,139,2.768,156,4.199,157,3.561,162,2.167,164,1.694,165,1.382,170,3.202,225,2.205,236,1.622,311,5.023,341,4.76,353,3.112,471,2.556,512,2.127,530,2.758,558,2.758,559,5.023,560,3.283,568,1.766,605,2.232,607,2.364,636,3.473,696,4.347,702,4.154,741,3.994,756,2.895,760,2.557,764,4.154,862,3.069,880,2.656,954,4.111,993,3.561,1009,2.84,1014,3.994,1042,3.711,1073,4.46,1113,4.74,1202,3.25,1540,3.412,1792,3.858,1801,3.516,1808,5.618,1810,5.274,1811,5.853,1822,3.711,1826,6.439,1849,4.347,1850,4.154,1851,4.825,1870,2.147,1879,5.43,1888,5.618,1916,5.169,1938,6.646,1939,6.646,1940,5.023,1941,5.274,1942,6.646,1943,5.853]],["t/150",[7,0.876,18,3.959,26,3.345,55,4.9,115,4.149,160,3.158,206,5.835,291,5.307,306,4.001,507,4.9,683,3.371,684,4.699,802,4.495,1135,7.508,1136,7.932,1137,6.201,1138,6.809,1139,4.821,1224,5.782,1381,3.571,1473,4.015,1944,7.508,1945,6.006]],["t/152",[7,0.827,16,1.381,17,1.971,18,4.612,19,4.676,26,1.852,28,1.047,31,1.219,48,2.024,55,4.037,84,0.54,105,1.56,123,1.7,132,1.465,158,2.493,159,4.947,160,1.749,165,1.663,182,1.669,189,2.669,221,1.842,225,2.653,226,2.418,227,3.292,232,1.814,245,2.98,257,2.539,262,3.26,278,1.787,281,2.36,296,1.922,318,1.214,332,2.589,341,2.743,348,3.201,353,1.36,375,2.899,390,2.408,391,2.1,393,2.45,447,3.511,471,2.067,478,4.499,558,2.231,560,3.951,565,1.629,568,1.428,611,2.074,637,3.201,677,2.628,684,2.602,696,3.515,719,3.001,731,2.615,767,3.833,809,3.433,827,2.528,828,2.08,834,4.062,849,3.77,853,3.77,856,2.98,858,4.543,885,2.843,920,2.899,955,3.902,1014,3.23,1029,3.433,1036,3.292,1066,2.46,1113,5.703,1140,4.988,1141,4.988,1195,4.157,1212,4.898,1214,3.23,1221,3.712,1284,2.439,1397,3.024,1465,3.607,1672,4.391,1688,3.395,1782,3.992,1796,4.734,1801,4.231,1802,4.543,1807,3.978,1810,6.346,1891,5.523,1946,5.375,1947,5.375,1948,5.375,1949,5.375,1950,4.988,1951,5.375,1952,5.375,1953,2.713,1954,4.265,1955,5.375,1956,4.543,1957,5.375,1958,5.375]],["t/154",[7,0.95,12,2.899,16,1.788,20,4.496,23,2.4,27,1.706,31,2.348,55,3.91,56,0.71,84,1.041,123,3.275,160,2.52,197,3.203,199,3.546,213,2.925,232,2.615,285,2.97,306,3.192,351,2.601,447,3.401,463,4.325,507,3.91,527,1.572,607,3.683,611,2.988,622,3.626,630,2.655,682,5.99,684,3.749,799,5.433,818,5.436,847,3.976,920,4.178,992,3.593,1179,4.424,1224,4.613,1275,7.417,1493,6.887,1786,6.547,1959,7.746,1960,4.792,1961,6.822]],["t/156",[1,3.168,2,1.47,5,1.92,7,0.736,16,1.474,17,2.154,21,1.54,23,1.82,24,0.883,28,0.769,29,1.902,56,0.922,84,1.011,95,3.382,96,1.92,98,1.916,103,2.651,105,1.705,113,1.988,144,2.611,159,3.634,165,1.221,177,2.632,179,1.824,182,1.824,221,2.013,247,2.396,256,2.04,283,1.576,296,2.101,323,2.199,334,3.088,351,1.973,377,1.219,440,2.404,444,2.363,447,2.579,463,4.767,478,3.305,511,1.738,527,1.192,530,2.438,531,3.305,562,2.677,565,1.78,630,3.447,638,3.28,677,2.872,682,4.543,683,2.04,684,2.843,726,3.563,757,1.343,818,2.737,825,4.189,827,2.762,846,2.421,847,3.016,874,3.891,1036,3.598,1059,3.051,1146,5.173,1177,4.661,1234,3.088,1240,3.108,1266,1.487,1275,3.498,1301,4.965,1308,3.234,1397,3.305,1435,3.671,1822,3.28,1925,4.543,1960,6.222,1962,4.189,1963,3.942,1964,3.711,1965,3.942,1966,5.874,1967,5.452,1968,5.874,1969,5.874,1970,6.318,1971,4.057,1972,4.189,1973,5.874,1974,4.799,1975,4.965,1976,5.874,1977,5.874,1978,8.538,1979,5.874,1980,3.796,1981,5.452]],["t/158",[2,1.24,4,1.426,7,0.922,8,2.473,15,1.676,16,1.753,21,1.299,22,2.852,24,1.13,26,1.707,27,1.657,28,0.985,31,2.062,46,2.893,56,0.833,84,0.498,85,1.592,90,4.44,92,2.585,93,1.948,94,2.487,96,1.619,123,2.876,132,2.479,145,1.849,158,2.298,170,1.358,172,1.592,174,1.366,186,3.289,216,2.34,226,2.229,236,1.209,243,2.514,245,5.041,247,2.02,275,1.423,278,1.647,282,2.5,283,1.329,291,2.708,292,2,313,1.123,317,4.218,332,2.386,341,2.528,351,1.664,370,3.596,375,4.904,395,1.817,400,3.596,422,3.129,437,2.158,444,1.993,459,3.596,463,2.766,491,1.386,505,1.905,527,1.005,555,2.175,575,2.184,578,1.806,583,2.808,590,2.473,605,1.664,606,3.533,613,2.078,619,2.95,620,2.543,683,1.72,694,3.797,709,3.005,712,2.473,715,2.363,719,2.766,734,2.034,764,3.096,770,3.533,798,2.528,821,3.201,823,3.744,851,2.41,856,2.746,874,2.258,882,2.746,896,7.215,919,3.164,939,2.83,993,2.654,998,2.95,1036,3.034,1111,3.005,1137,3.164,1168,5.684,1193,2.899,1360,2.621,1496,4.047,1535,5.364,1597,4.403,1960,5.625,1982,4.954,1983,4.363,1984,4.954,1985,4.954,1986,4.363,1987,3.831,1988,3.931,1989,4.363]],["t/160",[7,0.453,15,3.001,24,0.93,31,1.404,34,2.096,49,1.291,56,1.097,84,0.622,85,3.33,112,4.077,128,2.74,145,2.31,153,2.939,159,3.83,182,1.922,203,1.375,225,2.054,244,3.536,273,4.581,275,1.778,300,3.483,315,2.729,359,2.533,376,3.275,377,1.84,378,4.912,383,4,498,4.579,500,2.718,532,4.292,601,2.697,630,3.039,726,3.755,744,3.275,747,2.359,794,4,798,3.16,810,2.551,818,2.885,882,3.432,1009,3.789,1116,3.792,1177,4.912,1204,6.562,1239,3.536,1243,4.213,1275,3.687,1328,3.432,1381,2.277,1536,4.213,1538,4.415,1565,4,1632,2.982,1975,5.233,1990,3.954,1991,4.988,1992,7.829,1993,5.058,1994,5.452,1995,6.191,1996,4.678,1997,6.323,1998,3.339,1999,5.233,2000,6.191,2001,8.867,2002,8.867,2003,8.867,2004,5.233,2005,6.191,2006,5.745,2007,5.452,2008,5.452,2009,3.954,2010,5.745,2011,4.678,2012,5.058]],["t/162",[7,0.757,20,6.004,292,4.175,353,2.618,378,8.208,447,4.542,568,2.749,707,5.341,820,4.934,874,4.714,897,7.655,1224,6.16,1960,7.699,2007,9.11,2013,6.054,2014,10.344]],["t/164",[29,3.462,203,2.82,376,5.657,401,3.793,568,2.842,747,4.075,839,5.693,1672,8.737,1996,8.082,2009,6.831,2010,9.925,2015,7.385]],["t/166",[26,3.597,27,2.3,34,2.468,48,2.953,84,1.05,96,2.563,105,2.276,172,2.521,192,4.287,254,2.34,256,2.723,285,3.007,297,3.98,338,1.79,353,1.984,418,3.638,461,6.097,482,3.254,507,3.958,527,2.54,568,2.084,607,2.789,630,2.688,707,4.049,741,4.713,798,4.003,810,3.231,828,3.035,877,2.654,878,3.103,888,4.287,1275,7.983,1506,4.67,1567,5.592,1628,5.592,1960,6.459,2016,5.5,2017,6.222,2018,7.842,2019,7.842,2020,7.278]],["t/168",[7,0.975,19,4.035,25,2.108,28,0.929,34,1.677,49,2.035,56,0.65,84,1.121,103,2.203,105,2.059,127,2.159,164,1.809,203,1.576,219,2.038,223,3.707,236,1.731,254,2.117,257,3.352,296,2.537,313,1.608,318,2.52,338,1.619,348,4.225,353,1.795,377,1.472,378,5.629,397,2.737,401,3.461,440,2.903,611,2.737,630,2.431,757,1.622,886,4.188,888,3.878,914,3.276,920,5.262,993,3.801,1059,3.685,1111,6.765,1271,4.827,1491,5.059,1825,5.629,1960,8.246,1990,4.531,2007,6.247,2015,4.899,2016,4.976,2021,4.827,2022,6.584,2023,7.094,2024,5.996]],["t/170",[7,0.815,12,2.24,16,1.034,21,1.57,23,3.15,24,1.674,29,1.938,31,1.357,49,1.805,56,1.249,84,1.022,93,2.353,96,1.957,98,1.952,107,1.422,109,0.937,110,4.97,123,1.893,158,2.776,174,1.651,203,1.33,207,2.267,219,1.719,232,2.02,256,2.078,285,2.295,318,1.352,338,1.976,342,3.631,376,3.166,377,1.242,395,2.195,398,1.957,401,2.123,438,2.883,491,1.674,527,1.756,536,3.078,553,2.942,630,2.966,655,3.631,685,3.091,744,3.166,757,1.369,818,2.789,831,3.503,848,4.523,855,3.915,930,3.038,1028,3.666,1170,1.705,1200,5.271,1223,3.147,1289,3.249,1312,2.041,1672,4.89,1870,3.598,1918,2.764,1960,6.288,2006,5.555,2025,4.198,2026,2.841,2027,3.474,2028,4.43,2029,5.986,2030,4.629,2031,5.354,2032,2.338,2033,3.128,2034,5.06]],["t/172",[7,0.868,16,0.839,23,1.505,24,1.627,31,1.102,56,1.121,84,1.088,90,2.125,96,1.588,98,2.417,107,1.761,109,1.857,110,4.393,123,1.537,132,1.324,160,1.581,165,1.01,170,2.032,174,2.044,182,1.509,183,1.935,186,2.125,192,2.656,193,1.64,196,1.787,197,2.009,219,2.129,223,2.539,228,1.428,241,3.527,245,2.693,275,1.396,277,2.317,280,2.868,281,2.133,285,2.842,296,2.651,313,1.102,338,1.109,342,2.947,353,1.23,372,1.687,408,1.628,426,2.54,478,2.733,507,2.452,527,2.638,536,2.483,568,1.291,578,1.772,607,1.728,613,3.109,630,1.665,635,1.636,685,2.509,724,2.195,756,4.715,757,1.695,814,1.942,818,3.454,820,2.317,827,2.285,850,2.947,851,2.363,862,2.244,936,1.929,953,1.824,962,3.103,1033,2.868,1117,2.439,1133,2.426,1216,2.295,1242,5.485,1245,2.759,1263,4.169,1266,1.23,1275,5.987,1366,2.509,1417,2.976,1448,3.672,1960,5.558,2031,3.005,2032,1.898,2035,3.855,2036,4.859,2037,4.859,2038,4.509,2039,4.859,2040,4.509,2041,3.595]],["t/174",[7,0.644,23,1.899,24,0.921,25,1.822,26,2.112,27,3.016,28,0.803,34,1.449,56,1.297,84,0.885,109,1.612,110,4.305,127,3.427,132,1.671,179,1.904,294,3.061,295,2.288,315,2.703,318,1.385,338,1.4,482,2.544,507,4.444,527,2.42,596,2.562,607,2.181,663,3.475,1037,2.169,1045,3.792,1111,3.719,1170,1.747,1245,3.164,1275,7.618,1363,2.049,2038,9.563,2042,5.69,2043,5.759,2044,6.131,2045,6.131,2046,6.131,2047,6.131,2048,6.131,2049,6.131,2050,6.131,2051,6.131,2052,6.131,2053,6.131,2054,4.172,2055,6.131,2056,6.131,2057,6.131,2058,5.69,2059,6.131,2060,6.131]],["t/176",[7,0.699,12,1.399,16,1.31,18,2.46,19,1.546,21,0.98,23,1.158,24,0.907,25,1.792,27,2.804,28,0.993,56,1.264,84,0.96,89,1.295,98,1.219,105,1.085,107,1.433,109,0.944,127,2.649,132,1.644,140,1.359,160,1.963,164,0.953,172,1.202,186,2.638,192,2.043,228,1.773,236,0.912,246,1.312,278,1.243,294,1.866,296,1.337,306,2.486,343,1.991,351,1.255,372,1.298,377,0.776,426,1.281,440,1.53,447,1.641,482,2.503,507,4.393,511,1.106,524,2.135,527,2.072,550,3.174,558,1.551,562,1.704,596,1.562,601,1.628,607,2.146,610,2.666,630,1.281,635,1.259,698,2.509,757,0.855,760,1.025,778,2.188,807,1.341,810,1.54,818,4.056,829,1.828,874,1.704,877,1.265,878,1.479,887,2.188,906,2.103,935,2.666,942,1.465,951,2.226,953,2.265,993,2.003,999,1.887,1059,1.942,1111,3.659,1191,2.544,1197,2.766,1223,3.987,1234,1.965,1235,3.472,1275,7.059,1278,2.312,1285,1.953,1312,1.275,1315,1.908,1364,2.509,1448,2.825,1579,2.891,1628,2.666,1779,2.336,1786,3.16,1884,2.289,1972,2.666,1980,2.415,1997,2.666,2016,2.622,2020,3.469,2042,3.469,2043,2.445,2054,2.544,2058,3.469,2061,1.89,2062,3.16,2063,3.738,2064,2.476,2065,3.469,2066,3.738,2067,3.292,2068,2.966,2069,3.292,2070,5.599,2071,3.738,2072,3.738,2073,3.738,2074,3.738,2075,2.891,2076,3.738,2077,3.738,2078,3.16,2079,3.469,2080,3.738,2081,3.738,2082,5.161,2083,3.469,2084,3.738,2085,3.469,2086,3.738,2087,3.738,2088,3.738,2089,3.738,2090,3.738,2091,3.738,2092,1.942,2093,3.738,2094,3.16,2095,3.054]],["t/178",[2,1.43,7,0.849,15,1.934,21,2.194,23,1.771,24,1.258,27,2.555,46,2.198,56,0.767,84,1.096,90,3.66,91,4.42,92,2.876,93,2.247,105,1.659,109,0.894,127,1.739,130,3.023,133,2.269,138,2.139,144,2.54,145,2.133,172,2.69,174,1.576,177,3.75,186,2.499,200,4.552,219,1.642,272,2.171,278,1.9,283,1.534,285,2.191,339,3.917,372,2.906,377,1.186,393,2.605,416,2.442,463,3.191,468,3.374,482,3.473,502,1.462,505,2.198,527,1.698,536,2.313,565,1.732,572,2.809,577,2.509,614,0.976,630,2.868,632,2.853,659,3.435,677,2.794,757,1.307,759,1.859,807,2.05,810,3.448,818,4.613,851,2.78,885,3.023,907,3.404,962,3.65,1033,3.374,1048,4.229,1170,1.628,1234,3.005,1275,5.896,1302,3.435,1308,3.146,1360,3.023,1473,2.363,1511,3.467,1633,2.499,1922,4.42,1960,5.177,1967,5.304,2061,1.791,2064,3.785,2096,4.535,2097,5.715,2098,4.42,2099,5.715,2100,3.835,2101,4.669,2102,4.535]],["t/180",[7,0.889,18,3.432,29,2.725,56,1.002,105,2.443,109,1.317,192,4.601,200,3.869,213,3.178,268,7.412,306,3.468,313,1.908,376,4.452,377,1.747,390,3.771,393,3.836,416,3.596,418,3.904,447,4.803,481,4.666,484,3.309,507,4.248,630,2.885,677,4.115,765,4.633,818,3.922,820,4.014,823,6.36,921,6.36,953,3.159,1033,4.968,1137,5.376,1138,5.903,1139,4.179,1197,6.228,1224,5.012,1493,4.968,1960,5.206,2103,6.002,2104,8.416,2105,7.114]],["t/182",[7,0.922,16,1.57,20,3.721,23,1.986,27,2.002,29,2.943,34,1.515,55,3.236,56,0.587,84,1.062,109,1.798,127,1.951,182,1.991,185,4.095,192,3.504,197,2.651,199,4.161,213,2.421,221,2.197,296,2.293,306,2.642,316,2.651,343,2.116,351,2.153,447,2.815,463,3.58,498,3.31,507,4.588,568,1.704,576,3.504,607,2.28,611,2.473,630,2.197,653,1.873,682,4.957,684,3.103,721,5.087,799,4.496,818,5.654,820,3.058,847,3.291,874,2.922,920,3.457,992,2.974,1164,3.001,1170,1.826,1174,7.109,1179,3.662,1224,3.818,1275,7.226,1285,3.35,1493,7.163,1880,5.087,1889,5.419,1961,5.646,2016,6.376,2061,2.849,2106,7.213,2107,6.411]],["t/184",[2,1.52,7,0.905,16,1.51,21,1.592,22,3.496,24,0.912,26,2.092,27,1.926,28,0.795,31,2.323,46,3.363,56,0.801,84,0.61,90,4.482,92,3.005,93,2.387,94,3.048,96,1.985,109,0.95,123,3.241,132,1.655,133,2.411,145,2.266,164,1.548,174,2.412,179,1.885,216,2.869,226,2.732,236,1.482,243,3.082,246,2.131,247,3.566,275,1.744,277,2.896,278,2.019,280,3.584,283,1.629,292,2.451,313,1.377,317,4.056,341,3.099,364,3.971,375,5.527,418,2.816,437,2.645,444,2.443,463,3.39,491,1.698,527,1.232,568,1.614,590,3.031,605,2.039,613,2.547,619,3.616,620,3.117,622,2.842,683,2.108,694,3.065,709,5.304,715,2.896,719,3.39,798,3.099,821,3.923,851,2.954,856,3.366,874,2.767,882,3.366,885,3.212,998,3.616,1036,3.719,1137,3.878,1168,4.589,1174,5.409,1193,3.554,1496,4.961,1986,5.347,1987,4.696,1988,4.818,1989,5.347,2108,4.589]],["t/186",[2,1.588,7,0.884,15,3.054,21,2.367,29,2.055,46,2.441,56,0.827,84,1.15,90,2.775,92,3.609,93,3.548,115,2.712,134,2.931,192,3.469,200,2.918,207,2.404,228,1.866,229,3.493,245,3.518,304,1.241,334,3.336,338,2.06,351,3.031,376,3.357,421,3.684,532,6.08,596,2.652,607,2.257,614,1.084,621,5.01,630,2.175,726,3.85,744,3.357,747,2.418,763,4.009,820,5.457,847,3.258,879,4.607,888,4.934,951,3.779,1055,3.887,1174,7.77,1224,3.779,1465,4.259,1493,6.754,1889,5.364,1991,5.909,2013,5.283,2015,4.382,2109,5.364,2110,6.346,2111,5.036,2112,4.696,2113,6.346,2114,6.346,2115,6.346,2116,5.89]],["t/188",[15,2.926,48,4.191,56,1.262,375,4.663,395,3.171,511,2.558,607,3.959,723,3.692,747,3.295,953,3.246,1043,4.164,1107,4.227,1174,6.886,1511,5.245,1565,7.954,1994,7.615,2117,8.646,2118,5.655,2119,8.646,2120,12.31,2121,7.615,2122,8.646,2123,7.309,2124,7.309,2125,8.646,2126,8.646,2127,7.615,2128,8.646]],["t/190",[12,2.501,16,1.154,29,3.03,56,1.17,164,1.703,179,2.075,203,1.484,223,3.491,247,2.725,275,1.919,351,2.244,375,3.604,392,3.847,401,3.832,482,2.773,511,1.977,532,3.234,607,2.376,608,4.508,621,3.187,630,3.208,653,1.952,704,5.459,721,5.302,723,2.216,818,6.336,882,3.704,1493,3.944,1565,7.563,2111,5.302,2121,8.243,2123,7.911,2124,9.131,2129,6.682,2130,6.682,2131,6.682,2132,6.682,2133,8.686,2134,6.682,2135,6.682,2136,6.682,2137,6.682,2138,8.243,2139,5.648,2140,6.682,2141,4.85,2142,6.682,2143,6.682,2144,6.682,2145,6.682,2146,6.682,2147,6.682,2148,6.682,2149,6.682,2150,6.682,2151,6.682,2152,6.682]],["t/192",[15,0.976,16,1.091,29,1.577,34,1.151,48,3.124,56,1.258,84,0.29,164,1.242,203,0.641,212,2.541,225,1.616,236,0.704,246,1.012,275,1.399,277,1.376,279,3.841,292,1.165,372,1.002,375,1.556,392,2.804,401,2.634,405,0.852,416,1.233,426,2.166,437,1.257,445,1.689,471,2.431,482,1.197,483,1.689,502,1.9,507,1.456,511,1.87,530,1.197,607,3.58,611,1.113,623,2.164,630,0.989,653,1.846,683,1.002,723,2.097,747,1.099,756,2.122,802,1.083,803,1.508,818,4.69,839,1.536,846,1.189,851,3.075,882,2.7,920,1.556,1027,2.18,1043,1.39,1050,1.734,1059,1.499,1170,2.868,1179,1.648,1235,2.229,1263,1.623,1275,5.705,1345,3.269,1367,1.298,1432,2.18,1473,1.193,1511,1.75,1530,1.611,1565,6.19,1566,1.911,1579,3.767,1638,1.383,1693,2.644,1698,1.992,1733,1.599,1735,1.864,1773,3.416,1816,1.992,1825,2.289,1847,3.044,1872,2.541,1906,1.456,1971,5.729,1980,4.798,2016,7.35,2033,1.508,2054,1.963,2061,1.526,2067,2.541,2068,2.289,2069,2.541,2106,2.289,2111,2.289,2121,4.289,2123,4.117,2124,4.117,2127,4.289,2133,4.52,2138,4.289,2139,4.117,2153,5.164,2154,5.342,2155,2.885,2156,4.871,2157,2.678,2158,2.678,2159,2.357,2160,2.885,2161,2.885,2162,6.959,2163,2.885,2164,4.52,2165,2.541,2166,4.871,2167,4.52,2168,2.885,2169,2.885,2170,4.871,2171,2.885,2172,2.885,2173,2.885,2174,2.885,2175,2.885,2176,5.866,2177,2.885,2178,2.885,2179,2.885,2180,2.439,2181,2.678,2182,2.678,2183,2.678,2184,2.678,2185,2.885,2186,6.32,2187,4.289,2188,4.289,2189,4.871,2190,2.541,2191,2.541,2192,2.541,2193,2.885,2194,4.871,2195,2.885,2196,2.289,2197,2.885,2198,2.885,2199,2.885,2200,2.885,2201,2.885,2202,2.885,2203,2.885,2204,2.885,2205,2.885,2206,2.541,2207,4.52,2208,4.871,2209,2.541,2210,2.885,2211,2.885,2212,2.885,2213,2.885,2214,2.885,2215,2.885,2216,2.885,2217,2.885,2218,2.885,2219,2.885,2220,2.541,2221,2.885,2222,2.885,2223,2.885,2224,2.885,2225,2.406,2226,2.678,2227,2.678,2228,2.541,2229,2.885,2230,2.678,2231,1.936]],["t/194",[7,0.495,16,0.548,18,1.295,23,2.098,24,1.018,27,2.537,29,1.709,34,1.6,39,1.199,56,1.314,84,0.319,85,2.177,96,1.038,134,1.466,158,2.448,160,1.717,221,1.088,225,1.053,279,2.715,394,1.473,401,1.126,405,0.937,426,2.704,438,2.542,471,2.604,483,1.858,505,1.221,529,2.684,530,1.318,607,3.114,630,1.088,653,1.541,683,1.102,818,4.08,820,1.514,851,4.259,973,5.658,1059,1.649,1110,2.16,1170,1.503,1235,4.799,1240,1.68,1271,2.16,1275,6.243,1306,1.843,1308,1.748,1309,2.594,1345,2.131,1432,3.988,1565,2.051,1579,5.237,1632,2.542,1638,2.529,1773,3.701,1863,1.232,1902,1.405,1906,1.602,1971,3.644,1972,2.264,1980,5.098,2016,7.353,2025,2.227,2043,2.076,2061,1.654,2065,2.946,2067,2.796,2068,2.519,2069,2.796,2070,4.898,2079,2.946,2083,2.946,2085,2.946,2106,2.519,2111,2.519,2123,2.684,2124,2.684,2127,2.796,2138,2.796,2139,4.461,2153,2.594,2154,2.684,2157,2.946,2158,2.946,2159,2.594,2162,4.081,2165,2.796,2176,4.898,2180,2.684,2181,2.946,2182,2.946,2183,2.946,2184,4.898,2187,2.796,2188,2.796,2190,2.796,2191,2.796,2192,2.796,2207,2.946,2226,2.946,2227,2.946,2228,2.796,2230,2.946,2232,3.451,2233,6.772,2234,3.175,2235,3.175,2236,3.175,2237,2.946,2238,2.946,2239,3.175,2240,5.277,2241,3.988,2242,6.772,2243,3.175,2244,3.175,2245,3.175,2246,3.175,2247,3.175,2248,3.175,2249,3.175,2250,5.277,2251,3.175,2252,2.796,2253,2.399,2254,2.455,2255,3.175,2256,3.175,2257,3.175,2258,3.175,2259,3.175,2260,3.175,2261,3.175,2262,3.175,2263,3.175,2264,3.175,2265,2.684,2266,3.175,2267,3.175,2268,3.175,2269,3.175,2270,6.772,2271,3.175,2272,3.175,2273,2.946,2274,2.946,2275,2.264,2276,2.946,2277,2.946,2278,2.946,2279,3.175,2280,3.831,2281,3.175,2282,1.964]],["t/196",[4,1.285,7,0.914,15,3.524,16,0.771,23,2.64,24,1.445,31,1.012,46,1.717,49,1.448,56,1.01,84,1.108,85,1.435,90,1.952,92,3.579,93,3.78,96,2.786,105,2.016,107,2.284,109,1.504,113,1.511,123,1.412,127,2.926,145,1.666,164,1.138,171,1.126,174,2.35,189,2.217,203,1.542,223,2.333,241,3.241,280,2.635,296,1.596,297,2.266,313,1.012,317,2.071,322,2.474,338,2.195,351,1.499,359,1.827,395,1.637,397,1.722,401,1.583,438,4.63,476,1.203,502,2.181,505,1.717,527,1.729,528,2.183,536,2.657,550,3.108,607,1.588,614,1.456,653,1.304,655,2.708,747,1.701,828,1.728,833,2.919,857,1.879,898,2.009,939,2.55,1110,3.038,1170,1.272,1174,2.761,1275,5.075,1289,1.676,1312,1.522,1531,2.761,1577,3.131,1633,1.952,1870,3.562,1882,2.44,1991,2.511,2025,3.131,2027,4.03,2028,3.304,2030,3.452,2031,2.761,2096,5.51,2225,2.205,2283,2.884,2284,4.464,2285,4.464,2286,5.869,2287,3.998,2288,4.464,2289,5.37,2290,4.464,2291,3.452,2292,4.464,2293,3.373,2294,4.464,2295,3.083,2296,3.038,2297,1.38,2298,5.37]],["t/198",[2,1.061,7,0.743,12,2.496,15,2.257,16,1.152,24,1.239,25,1.26,34,1.002,47,2.481,49,0.884,56,1.128,84,1.085,85,1.363,101,2.094,105,1.231,114,4.935,123,2.109,153,2.012,158,1.967,164,1.7,179,1.316,196,1.559,203,0.942,213,1.601,272,1.611,276,1.949,285,1.625,306,1.747,323,1.587,338,0.968,342,4.045,359,1.735,438,3.212,449,3.809,463,2.367,471,1.63,482,2.767,498,2.189,527,1.353,528,2.073,530,1.759,536,1.843,550,1.546,560,3.294,605,1.424,612,2.548,630,2.286,659,2.548,685,2.189,744,4.945,757,0.969,798,2.164,919,2.708,930,3.385,941,2.885,955,6.785,978,2.65,993,2.272,996,2.481,1059,2.202,1174,6.676,1245,1.302,1285,2.215,1312,1.446,1380,2.481,1493,3.937,1531,5.099,1577,2.973,1597,6.316,1618,3.934,1633,1.854,1638,2.032,1933,4.125,1975,3.584,1991,3.751,2031,4.125,2032,1.656,2033,2.215,2253,3.204,2283,2.739,2289,3.279,2299,6.23,2300,6.669,2301,3.584,2302,4.24,2303,6.669,2304,4.24,2305,4.24,2306,3.464,2307,4.309,2308,5.04,2309,4.24,2310,5.04,2311,9.306,2312,5.359,2313,5.04,2314,6.189,2315,5.873,2316,4.24,2317,3.279,2318,3.279,2319,3.279,2320,3.279,2321,2.65,2322,5.04,2323,3.934,2324,4.24,2325,3.584,2326,4.24,2327,4.24,2328,3.934]],["t/200",[7,0.661,15,1.137,16,0.58,23,1.041,24,1.612,27,0.74,34,0.794,39,2.662,56,1.281,84,0.338,92,1.155,105,2.046,107,1.314,109,0.526,123,1.749,127,1.683,158,3.27,161,5.705,170,1.517,177,1.506,179,1.044,182,3.016,192,1.837,193,1.867,216,1.588,256,1.167,275,0.965,277,3.362,279,3.276,285,3.132,337,2.391,338,1.263,353,1.784,395,1.232,405,0.992,418,3.27,426,3.677,471,3.142,484,2.771,498,1.735,505,2.711,527,2.322,530,3.748,536,2.965,550,2.017,560,1.66,605,1.129,607,1.195,630,1.896,638,1.876,647,1.926,653,0.982,662,1.812,685,1.735,734,1.38,736,2.789,744,1.778,747,1.281,757,1.265,795,2.038,818,3.807,850,3.355,851,1.635,936,2.798,993,1.801,1163,1.89,1174,6.009,1223,4.295,1240,1.778,1245,2.164,1274,1.905,1275,3.294,1306,3.21,1366,1.735,1466,2.667,1493,4.823,1579,2.599,1632,2.664,1638,2.651,1754,2.182,1768,2.123,1773,2.357,1863,2.147,1975,2.841,2016,2.357,2031,2.079,2032,1.313,2061,2.209,2139,4.675,2187,4.871,2188,2.96,2190,2.96,2191,2.96,2192,2.96,2273,3.119,2274,3.119,2275,2.396,2276,3.119,2277,3.119,2278,3.119,2329,3.361,2330,3.361,2331,3.361,2332,3.361,2333,3.361,2334,3.361,2335,3.361,2336,3.361,2337,3.361,2338,3.361,2339,3.361,2340,3.361,2341,3.361,2342,3.361,2343,3.361,2344,3.361,2345,5.531,2346,3.361,2347,3.119,2348,3.361,2349,3.361,2350,3.361,2351,3.361,2352,3.361,2353,1.863,2354,3.361]],["t/202",[2,1.57,3,3.113,5,1.283,7,0.763,12,1.469,16,1.083,19,1.623,21,1.029,22,2.259,23,1.216,24,1.345,25,1.166,27,2.299,29,1.27,36,1.606,56,0.575,84,1.101,90,2.743,98,2.919,105,1.139,107,1.862,109,1.633,110,1.918,127,3.176,132,1.069,145,1.464,157,3.361,165,1.629,170,1.076,172,1.261,177,1.758,181,2.063,182,1.948,196,1.443,216,1.854,219,2.571,220,2.241,221,1.345,222,3.456,225,1.302,230,3.034,246,2.201,247,1.6,272,1.491,277,1.871,285,3.432,288,2.403,289,1.439,297,1.991,298,2.224,313,0.89,315,1.73,318,1.77,337,2.711,338,1.432,353,1.983,359,1.606,372,2.178,376,2.076,392,2.259,393,1.788,463,2.191,482,1.628,484,1.543,491,1.098,496,2.224,502,2.29,507,1.98,513,2.743,527,2.381,536,3.028,553,1.928,557,1.812,560,1.938,568,1.667,578,1.431,607,2.787,613,2.631,630,1.345,635,1.321,658,1.744,659,4.708,677,1.918,685,3.238,741,3.769,757,0.897,759,2.549,774,1.97,806,2.848,818,4.17,820,1.871,880,1.568,949,2.709,950,2.535,960,3.206,997,1.348,1111,2.38,1135,3.034,1170,1.118,1174,6.777,1223,2.063,1275,5.828,1279,3.034,1473,1.623,1493,2.316,1894,3.206,1998,3.383,2031,2.427,2032,1.533,2043,4.102,2064,2.599,2355,3.924,2356,3.924,2357,3.317,2358,3.924,2359,3.924,2360,3.924,2361,3.924]],["t/204",[15,3.529,29,3.377,186,4.561,277,4.975,324,5.125,377,2.165,818,4.86,954,6.452,1009,4.457,1146,9.185,1301,8.816,1473,4.313,1779,6.518,1803,7.097,1991,5.867,1992,7.882]],["t/206",[1,3.493,2,2.292,4,1.864,7,0.925,16,1.118,18,3.734,20,3.759,23,2.006,24,1.376,56,0.839,84,1.224,90,2.832,93,2.546,105,2.658,109,1.807,132,1.765,145,2.417,162,2.112,164,1.651,165,1.903,170,1.776,171,1.633,172,2.082,174,1.786,177,4.103,192,3.54,200,2.977,213,2.446,225,2.148,232,2.186,256,2.249,275,1.86,285,2.483,306,2.669,313,2.076,323,2.424,353,2.317,377,1.344,390,2.902,393,2.952,397,2.498,507,3.269,526,4.346,536,2.531,558,2.688,597,3.104,611,2.498,630,2.22,671,5.474,759,2.107,818,4.266,823,4.894,852,2.605,949,4.472,997,2.225,1113,4.618,1137,4.137,1138,4.542,1139,3.216,1174,4.006,1224,3.857,1234,3.405,1278,4.006,1360,3.426,1493,3.823,1870,2.092,2362,5.703]],["t/208",[7,0.574,10,3.936,24,1.178,26,2.702,84,1.05,103,2.435,182,2.435,186,3.429,187,5.067,203,2.32,220,4.479,221,2.688,225,2.601,236,2.548,280,4.629,281,3.443,285,4.003,343,2.588,524,4.479,526,5.263,527,2.119,568,2.775,607,2.789,683,3.626,708,5.693,724,3.543,760,2.15,936,3.113,947,3.705,1009,4.462,1036,4.803,1241,7.891,1302,4.713,1498,5.194,1536,5.336,1540,4.026,2363,5.693,2364,5.926,2365,6.407,2366,7.842,2367,7.842,2368,9.315,2369,6.906,2370,8.826]],["t/210",[2,2.589,7,0.757,105,3.003,129,5.734,248,4.506,275,2.971,283,2.776,285,3.966,332,4.982,397,3.991,398,3.381,511,3.06,512,3.31,759,3.365,1506,6.16,2371,10.344,2372,6.399]],["t/212",[15,3.302,28,1.278,34,1.677,48,3.673,56,1.022,84,0.713,85,3.137,160,3.174,161,4.481,193,2.394,203,2.167,225,2.353,248,4.25,249,4.899,280,4.188,304,1.387,315,3.127,343,2.342,351,2.382,484,2.789,527,1.439,568,1.885,578,2.587,602,5.796,607,4.272,621,3.384,723,2.353,747,2.703,757,1.622,760,1.945,930,3.6,1170,3.177,1239,4.052,1314,4.481,1345,6.548,1577,4.976,1601,3.933,1754,2.798,1991,5.488,2228,6.247,2283,4.584,2368,7.742,2373,7.094,2374,7.094,2375,7.094,2376,7.094,2377,6.584,2378,7.094,2379,5.486,2380,6.584,2381,6.584,2382,7.094,2383,5.996,2384,8.592,2385,6.584]],["t/214",[4,2.617,7,0.841,12,2.399,15,3.574,17,2.351,24,0.963,34,1.515,48,2.414,56,1.213,84,1.155,105,1.861,107,1.523,109,1.003,110,3.134,153,3.043,171,1.616,196,2.358,223,3.35,248,3.96,292,2.588,298,3.633,332,3.087,338,1.463,398,2.096,438,3.087,511,1.897,512,2.051,527,1.301,630,2.197,723,2.126,724,2.897,760,1.758,839,3.413,1043,3.087,1044,3.504,1202,3.134,1214,3.852,1302,3.852,1312,2.186,1345,8.457,1416,4.246,1577,4.496,1597,3.752,1754,2.529,1991,5.941,2283,4.142,2328,5.949,2372,3.965,2384,5.646,2386,4.654,2387,6.411,2388,6.411,2389,5.237,2390,6.411,2391,6.411,2392,6.411,2393,6.411,2394,6.411,2395,3.118,2396,6.411,2397,6.411,2398,4.571,2399,6.411,2400,5.949,2401,6.411,2402,6.411,2403,6.411,2404,6.411]],["t/216",[2,0.575,4,0.661,7,0.581,8,1.147,10,1.153,12,1.499,15,0.778,16,0.692,18,2.172,24,1.087,27,0.883,28,0.835,29,1.725,31,0.521,34,0.543,48,0.865,49,1.111,56,1.335,84,0.91,85,1.288,97,0.954,105,0.667,107,0.546,109,0.834,110,1.123,123,0.727,127,1.219,134,1.061,140,0.836,160,2.075,167,1.639,170,0.63,196,0.845,203,0.89,224,1.575,225,0.762,236,0.561,240,1.166,247,0.937,248,1.001,275,1.53,279,2.136,285,0.881,292,0.928,295,0.858,300,2.254,304,0.783,313,0.521,316,0.95,318,0.519,332,1.93,334,2.106,351,1.789,353,0.581,359,0.94,377,0.832,395,0.843,426,2.723,447,1.009,471,1.541,482,0.954,484,0.903,505,0.884,511,1.576,527,0.813,529,1.942,532,2.578,568,1.695,572,1.129,578,0.838,591,2.119,607,1.425,621,1.911,630,3.506,632,1.147,662,1.239,683,0.798,760,1.748,762,1.436,802,0.863,807,0.824,851,3.518,862,3.668,886,1.356,946,0.982,971,1.356,973,2.589,992,1.066,993,1.231,999,1.16,1006,1.091,1009,0.982,1036,1.407,1037,0.813,1044,1.256,1059,1.194,1122,1.334,1147,2.133,1193,1.345,1214,1.381,1234,1.208,1235,3.911,1237,1.436,1239,1.313,1243,1.564,1274,1.302,1279,1.777,1285,1.201,1302,6.147,1328,1.274,1423,2.133,1531,2.478,1567,1.639,1628,5.158,1632,2.565,1696,1.356,1782,1.674,1803,2.726,1902,1.017,1971,2.766,1997,1.639,2012,4.352,2032,1.565,2061,0.72,2162,4.931,2265,1.942,2283,2.589,2368,8.357,2370,5.39,2377,2.133,2379,3.098,2380,2.133,2381,2.133,2384,2.024,2385,2.133,2400,3.718,2405,2.298,2406,2.298,2407,2.298,2408,2.298,2409,4.006,2410,2.133,2411,1.564,2412,1.942,2413,2.298,2414,2.298,2415,2.298,2416,2.298,2417,2.298,2418,6.377,2419,4.006,2420,2.298,2421,2.298,2422,2.298,2423,2.298,2424,2.298,2425,2.298,2426,2.298,2427,4.006,2428,4.025,2429,3.483,2430,2.298,2431,4.006,2432,7.233,2433,2.298,2434,2.298,2435,2.298,2436,4.006,2437,2.298,2438,1.668,2439,4.006,2440,2.298,2441,2.298,2442,4.006,2443,4.006,2444,4.006,2445,1.823,2446,2.298,2447,1.452,2448,1.823,2449,2.298,2450,1.7,2451,2.024,2452,2.298,2453,2.298,2454,2.298,2455,2.024,2456,1.823,2457,1.877,2458,2.298,2459,2.298,2460,3.528,2461,2.298,2462,2.298,2463,2.298,2464,2.298,2465,2.298,2466,4.006,2467,2.298,2468,2.298,2469,2.298,2470,2.298,2471,2.298,2472,2.298,2473,2.298,2474,2.298,2475,2.298]],["t/218",[7,0.881,16,1.085,23,1.947,24,0.944,26,2.165,27,2.301,31,1.425,40,4.381,84,0.901,92,2.159,107,1.493,109,1.635,115,2.685,123,1.987,127,3.466,167,4.481,197,2.598,219,1.805,221,2.153,236,1.533,244,3.589,275,1.805,280,5.29,281,2.759,313,1.425,316,2.598,318,2.36,343,2.074,474,4.748,476,1.693,502,2.293,513,2.748,527,1.275,540,4.986,541,3.589,550,3.809,555,2.759,562,2.864,566,4.06,567,2.338,568,1.67,572,3.088,593,2.876,605,3.01,616,5.533,617,4.65,619,3.742,650,3.927,689,5.311,705,3.589,740,2.636,757,1.437,764,5.601,802,2.359,810,2.589,874,2.864,878,2.486,886,3.709,1024,3.709,1181,3.026,1193,3.677,1280,3.927,1315,5.332,1889,5.311,1935,4.986,1945,3.887,1990,4.013,2363,4.561,2476,5.533,2477,6.283,2478,6.283,2479,4.859,2480,5.533]],["t/220",[7,0.88,34,2.312,85,3.145,246,3.433,247,3.99,286,5.504,318,2.211,562,4.459,747,3.728,757,2.237,820,4.667,823,7.394,920,5.277,1493,5.776,1632,5.795,2109,8.27,2225,4.833,2297,3.024,2481,7.774,2482,9.784,2483,9.08]],["t/222",[7,0.835,16,0.948,25,2.415,26,1.892,27,1.21,29,1.778,34,1.921,49,1.695,56,0.886,84,0.817,85,1.765,97,3.372,99,3.214,103,2.523,105,2.359,106,1.868,135,2.905,164,1.4,199,2.514,219,1.578,221,1.882,223,2.87,225,1.822,226,2.471,236,1.983,246,1.927,247,2.239,279,2.202,283,2.596,294,4.057,311,4.15,314,4.064,316,2.271,338,1.254,343,2.682,353,1.39,376,2.905,390,2.461,395,2.98,401,1.948,416,3.472,465,3.548,471,2.112,476,2.88,484,2.159,532,3.933,578,2.003,580,1.77,607,2.89,611,2.119,614,0.938,757,1.256,809,3.508,840,3.987,888,3.002,1033,3.242,1129,3.188,1179,3.137,1193,4.756,1381,2.02,1441,3.066,1449,3.916,1476,1.873,1566,7.913,1785,4.836,1980,6.25,2013,3.214,2116,5.097,2225,2.713,2476,4.836,2479,10.757,2484,5.097,2485,3.188,2486,3.397,2487,7.675,2488,3.089]],["t/224",[7,0.889,16,1.26,27,1.608,41,3.073,48,2.748,49,2.075,86,4.611,92,3.419,99,4.272,101,3.605,105,2.119,127,3.028,219,2.858,221,2.502,277,3.481,292,2.946,318,2.557,343,2.409,372,3.454,377,1.515,398,3.252,401,2.589,476,1.966,482,4.129,527,1.481,578,2.662,659,4.386,780,3.605,1059,5.167,1566,8.05,1980,7.314,2022,9.233,2026,3.465,2032,2.851,2312,3.624,2479,5.644,2484,6.774,2487,5.791,2489,8.128,2490,7.299,2491,7.299,2492,7.299,2493,6.774,2494,7.299,2495,5.644,2496,7.299,2497,5.298,2498,6.774]],["t/226",[7,0.508,15,3.251,16,1.198,24,1.444,27,2.117,34,2.271,40,3.392,56,0.636,84,0.966,85,3.089,90,3.034,92,4.77,97,3.987,99,4.061,100,2.879,107,1.648,109,1.504,115,2.965,127,2.112,164,1.769,219,1.993,247,2.829,278,2.307,296,2.481,318,1.568,332,3.342,372,3.827,405,2.048,471,2.668,476,1.869,505,2.668,723,2.301,740,2.91,818,4.477,993,3.718,1170,1.977,1577,4.866,1980,7.122,1991,5.405,2225,3.427,2479,9.661,2487,5.505,2493,6.439,2499,6.938,2500,8.122,2501,6.938,2502,5.037,2503,6.938,2504,6.938]],["t/228",[23,3.128,27,2.224,28,1.322,34,2.386,40,4.936,92,3.469,105,2.931,155,6.068,283,2.709,372,3.506,530,4.19,557,4.662,740,4.235,828,3.907,874,4.601,1477,6.972,1962,7.2,1963,6.775,1964,6.378,1965,6.775]],["t/230",[7,0.782,27,2.356,127,3.864,318,2.416,562,4.874,568,2.842,1024,6.313,1065,8.737,1315,5.458,1891,7.385,1892,8.737,2480,9.418]],["t/232",[2,1.245,7,0.667,21,1.304,27,2.409,28,1.332,30,6.682,31,2.61,32,2.887,40,3.689,56,0.456,84,0.916,103,2.343,123,3.459,127,1.514,132,2.056,134,3.484,138,2.824,153,2.361,164,1.268,165,1.568,174,2.081,175,2.524,184,3.488,185,3.177,224,2.966,228,1.462,245,2.757,254,1.484,272,1.889,275,1.429,313,1.71,318,1.124,320,2.648,323,1.861,343,1.642,366,2.887,383,3.214,394,2.307,397,1.919,407,2.086,416,2.125,430,2.276,458,2.599,479,3.214,491,1.391,500,2.183,525,3.299,530,2.064,550,1.814,565,1.507,568,1.322,596,3.153,630,1.705,631,3.017,647,3.542,650,3.108,664,3.384,690,2.863,706,3.294,729,3.294,743,3.046,750,3.252,763,3.142,802,1.867,805,3.142,810,2.049,814,1.987,831,2.911,846,4.507,862,3.484,901,4.204,907,2.962,919,3.177,937,3.177,1024,4.454,1066,3.453,1129,2.887,1163,2.798,1181,2.395,1240,2.631,1315,2.538,1366,2.568,1473,2.057,1487,3.076,1838,3.434,1907,2.384,2061,1.558,2092,2.583,2094,4.204,2505,4.973,2506,3.946,2507,4.38,2508,3.488,2509,3.384,2510,4.973,2511,8.46,2512,4.973,2513,4.204,2514,4.973,2515,4.973,2516,3.61]],["t/234",[0,7.094,7,0.742,15,2.543,21,3.013,27,2.917,28,0.984,40,3.674,56,0.93,86,4.748,97,3.119,100,3.119,105,2.182,109,1.176,127,2.287,165,2.11,174,2.073,177,3.368,179,2.334,198,3.396,203,1.67,236,1.834,256,2.61,272,2.855,302,3.211,318,1.698,343,2.481,359,3.075,377,1.56,437,4.421,478,4.228,552,2.469,617,7.51,670,4.053,678,6.619,811,4.327,1315,3.836,1329,5.271,2479,8.885,2498,6.975,2508,5.271,2517,7.516,2518,3.486,2519,9.418]],["t/236",[0,4.606,2,1.484,7,0.811,8,2.96,21,2.254,25,1.762,27,2.227,31,1.949,36,2.426,39,2.239,56,0.543,70,3.927,84,1.115,86,3.746,92,2.954,94,2.976,96,1.938,106,3.772,127,3.077,164,1.512,165,2.447,175,3.009,199,2.714,231,3.979,293,4.388,302,3.673,318,1.34,343,1.957,359,2.426,372,2.985,395,2.175,397,2.288,398,1.938,408,3.387,440,3.517,476,1.597,506,4.732,589,5.012,617,6.361,650,6.931,677,4.203,678,8.905,707,3.062,740,2.487,757,1.966,839,3.157,906,3.336,1108,6.931,1315,3.027,1431,6.361,2508,9.097,2519,7.978,2520,5.222,2521,5.503,2522,6.13,2523,5.503,2524,5.93,2525,4.705,2526,5.93,2527,4.585]],["t/238",[7,0.848,56,0.844,127,2.804,133,3.658,145,4.732,153,4.374,232,3.11,247,3.757,313,2.089,316,3.81,320,4.905,336,3.479,405,3.928,426,3.158,436,6.102,447,4.045,495,4.128,821,5.953,877,3.118,936,3.658,1006,4.374,1046,3.998,1315,4.703,1990,5.885,2479,8.963]],["t/240",[0,3.844,4,2.065,7,0.821,27,2.658,30,4.164,31,1.627,56,0.657,84,1.128,92,2.465,96,3.214,101,3.544,105,2.083,123,2.269,127,3.414,145,2.677,174,1.979,179,2.228,201,3.39,252,5.548,278,2.386,281,3.15,318,1.621,332,3.455,343,2.368,372,2.491,405,2.118,409,3.374,416,3.066,540,5.693,541,4.098,555,3.15,605,2.41,611,2.768,616,6.318,617,5.309,619,4.273,650,4.484,689,6.064,750,4.692,886,4.235,898,3.228,1065,5.862,1108,4.484,1193,4.199,1280,6.145,1315,5.019,1449,5.116,1473,2.967,1779,4.484,1980,4.636,1990,4.583,2479,7.604,2508,5.032,2511,6.658,2528,5.862,2529,7.175,2530,7.175,2531,5.309]],["t/242",[7,0.963,16,1.544,20,2.799,21,1.264,22,2.776,24,1.107,26,1.661,27,1.97,28,1.171,31,2.028,36,3.015,39,1.821,46,3.44,55,2.434,56,0.989,84,0.899,89,1.67,90,4.379,92,2.532,93,1.896,94,2.42,96,1.576,123,2.828,132,1.314,145,1.799,174,2.032,181,2.535,189,2.394,197,1.994,199,2.207,213,1.821,216,2.278,225,1.599,226,3.316,227,2.953,228,1.417,229,2.654,236,1.177,243,2.447,247,1.966,275,1.385,277,2.3,278,1.603,283,1.294,285,1.849,292,1.946,294,2.407,316,1.994,317,3.418,341,3.761,351,1.619,375,3.974,395,1.768,437,2.1,444,1.94,447,2.117,463,4.114,491,1.349,527,0.978,536,1.333,560,2.382,565,1.461,580,1.554,590,2.407,605,1.619,613,2.023,619,2.871,620,2.475,636,2.519,670,2.6,675,2.846,682,3.729,683,1.674,684,2.334,685,2.49,694,2.434,709,4.47,715,2.3,719,2.692,737,5.168,798,2.461,799,3.382,821,3.115,847,2.475,856,2.673,874,3.358,882,2.673,920,2.6,992,2.236,998,2.871,1137,3.08,1168,3.644,1179,4.209,1193,4.313,1224,2.871,1242,3.568,1454,5.149,1493,5.911,1496,3.939,1788,3.729,1961,4.246,1986,4.246,1987,3.729,1988,3.826,1989,4.246,2532,4.246]],["t/244",[2,1.837,7,0.83,14,5.069,15,2.484,24,1.706,25,2.181,26,2.529,27,1.617,29,2.377,31,1.664,56,0.673,84,0.738,105,2.899,109,1.149,110,3.589,123,2.322,182,2.28,201,3.468,285,4.353,324,3.608,332,3.536,338,1.676,358,5.677,377,1.524,390,3.289,391,2.868,511,2.172,525,3.21,527,2.027,536,2.029,560,3.626,565,2.224,568,1.951,630,3.423,644,5.998,685,3.791,712,3.665,726,4.453,757,1.679,794,4.744,825,5.235,830,3.114,853,5.149,865,5.433,874,3.346,1009,3.137,1242,5.433,1290,3.791,1397,4.13,1454,4.227,1493,4.334,1803,4.995,1991,4.13,1992,5.548,1993,5.998,2307,4.744,2532,6.465,2533,5.677]],["t/246",[7,0.529,14,2.41,15,1.181,18,1.423,23,1.081,28,0.457,34,0.825,46,1.342,56,1.311,84,0.574,85,1.122,89,1.209,105,1.656,123,1.104,144,1.551,160,1.136,186,1.526,201,1.649,219,1.003,237,2.375,247,1.423,279,1.4,285,1.338,291,1.908,292,1.409,322,1.656,351,1.172,369,2.01,377,0.724,378,2.77,395,2.093,401,2.567,438,1.681,447,1.532,471,1.342,482,1.448,500,1.532,527,1.468,532,2.762,541,4.134,558,1.448,560,2.819,568,0.928,572,1.715,607,2.973,610,2.489,630,3.158,757,0.798,810,1.438,850,2.117,865,6.185,874,2.6,935,2.489,1197,5.356,1223,3,1234,1.835,1235,2.612,1239,1.994,1285,1.824,1363,1.907,1432,2.638,1454,3.285,1489,4.527,1539,2.159,1554,5.742,1601,4.634,1816,3.94,1873,4.349,1918,1.612,1980,4.676,1981,7.757,1990,2.229,2012,4.662,2016,4.002,2031,3.53,2162,8.072,2164,5.295,2165,3.074,2237,3.239,2238,3.239,2321,3.566,2379,4.412,2445,2.77,2534,2.95,2535,3.49,2536,5.295,2537,5.706,2538,8.116,2539,3.49,2540,3.49,2541,3.49,2542,3.49,2543,3.49,2544,3.49,2545,3.49,2546,3.49,2547,3.49,2548,3.49,2549,5.706,2550,2.342,2551,3.49,2552,3.49,2553,3.49,2554,3.49,2555,3.49,2556,3.49,2557,3.49,2558,5.295,2559,3.49,2560,5.706,2561,3.49,2562,3.49,2563,3.49,2564,3.49,2565,3.49,2566,3.49,2567,3.49,2568,3.49,2569,3.49,2570,3.49,2571,3.49,2572,3.49,2573,3.49,2574,5.706,2575,3.49,2576,3.49,2577,3.49,2578,3.49,2579,3.49,2580,3.49,2581,3.49,2582,3.49,2583,3.074,2584,3.49,2585,3.49,2586,3.49,2587,3.49,2588,3.49,2589,5.295,2590,3.49,2591,5.706,2592,3.49,2593,5.706,2594,3.49,2595,5.706,2596,3.49,2597,3.239,2598,3.49,2599,3.49]],["t/248",[7,0.719,15,2.428,18,2.926,31,1.627,56,1.339,113,2.428,160,2.334,322,2.854,401,2.545,607,2.552,851,5.871,973,4.636,1043,3.455,1235,3.284,1489,5.693,1601,3.977,1918,3.313,2026,3.406,2027,4.164,2028,5.309,2030,5.548,2031,4.438,2054,4.882,2162,5.548,2225,3.544,2289,5.548,2297,3.468,2600,4.098,2601,4.815,2602,4.815,2603,7.175,2604,5.862,2605,4.815,2606,5.208,2607,7.175,2608,4.954,2609,7.175,2610,7.175,2611,7.175,2612,7.175,2613,7.175,2614,7.175,2615,7.175,2616,7.175,2617,7.175,2618,7.175,2619,7.175,2620,7.175]],["t/250",[5,2.163,7,0.786,15,2.239,16,0.725,23,2.05,24,1.751,25,1.247,29,1.359,34,0.992,41,1.767,56,1.243,84,0.823,105,1.218,114,4.896,123,2.093,158,3.069,163,1.544,164,1.687,165,1.376,174,1.158,179,1.303,182,1.303,225,1.392,238,2.234,285,2.537,289,1.539,296,1.501,323,1.571,342,4.968,343,2.184,348,2.5,407,1.761,421,2.436,437,1.828,449,3.779,471,1.614,500,1.843,505,1.614,511,1.242,527,0.852,530,2.746,536,2.569,541,2.397,544,2.436,560,2.073,568,1.115,630,3.467,662,2.264,685,2.167,734,1.724,744,3.5,757,1.513,759,1.365,807,1.505,816,2.207,930,2.13,955,6.748,959,2.596,993,2.249,996,2.457,1050,2.522,1059,2.18,1117,2.107,1223,2.207,1263,3.722,1274,2.379,1285,2.193,1312,1.431,1454,5.823,1493,2.478,1531,2.596,1554,3.33,1597,5.441,1633,1.836,1638,2.012,1737,1.469,1822,2.344,1933,4.093,2031,4.093,2032,1.64,2282,2.596,2289,3.246,2299,3.172,2306,3.429,2307,4.275,2308,5,2310,5,2311,9.279,2312,5.336,2313,5,2314,6.141,2315,5.827,2322,5,2323,3.895,2353,2.327,2532,3.696,2538,3.696,2550,2.817,2558,6.141,2621,4.197,2622,4.197,2623,2.944,2624,3.047,2625,4.197,2626,5.406,2627,3.172]],["t/252",[7,0.732,16,1.464,19,2.407,24,0.875,25,1.729,31,1.923,34,1.376,49,1.214,56,1.315,84,0.852,98,1.898,109,1.72,110,4.891,127,3.045,170,1.596,203,1.293,248,4.358,318,2.261,395,2.135,418,2.7,476,1.568,505,2.238,527,1.181,568,1.547,580,1.875,587,4.398,614,0.994,653,1.7,755,2.433,757,1.331,814,2.326,830,4.243,877,1.97,953,2.185,1008,3.274,1037,2.059,1043,2.803,1044,3.182,1060,2.619,1107,2.846,1170,1.658,1235,2.664,1284,2.641,1332,3.299,1345,3.906,1399,3.182,1454,3.351,1601,4.701,1632,2.803,1699,3.351,1847,3.638,2032,2.274,2061,1.824,2353,3.227,2536,5.402,2538,5.126,2550,3.906,2589,5.402,2604,4.755,2605,3.906,2624,4.225,2628,5.82,2629,5.82,2630,5.82,2631,5.82,2632,3.227,2633,2.971,2634,5.82,2635,5.82,2636,3.099,2637,5.82,2638,5.82,2639,4.019,2640,5.82,2641,5.82]],["t/254",[1,4.053,2,2.54,7,0.9,16,1.298,18,3.065,20,4.362,23,2.328,24,1.525,28,0.984,56,0.93,84,1.292,90,3.287,93,2.955,105,2.182,109,1.588,132,2.049,145,2.805,162,2.451,164,1.916,165,1.562,170,2.061,171,1.895,172,2.416,174,2.073,177,3.368,225,2.493,232,2.537,256,2.61,313,2.301,318,1.698,353,2.568,377,1.56,397,2.899,526,5.044,536,2.077,568,1.997,597,3.602,611,2.899,671,6.353,852,3.024,853,5.271,997,2.583,1113,5.36,1224,4.476,1234,3.951,1303,4.166,1360,3.976,1492,5.812,2362,6.619,2642,7.516]],["t/256",[7,0.633,21,2.267,29,2.799,31,1.96,85,2.78,129,4.793,174,2.385,221,3.815,224,3.399,231,7.47,233,4.366,272,3.285,278,2.875,292,4.493,313,1.96,318,1.954,320,4.603,353,2.817,684,5.388,688,7.615,702,5.404,740,3.627,820,4.124,837,6.861,838,7.615,846,4.587,940,3.002,953,3.246,1281,4.047,1804,8.024,1996,6.534,2643,5.587]],["t/258",[2,1.001,7,0.838,9,2.403,14,4.396,21,2.08,23,1.973,24,0.601,26,2.193,27,0.881,28,0.524,31,2.051,34,0.945,46,1.538,48,1.505,56,0.366,68,2.791,84,0.909,103,1.242,105,1.161,109,0.626,123,2.013,131,1.683,140,1.454,144,1.777,155,2.403,162,2.076,165,0.831,174,2.495,189,3.94,201,1.889,215,5.478,221,1.37,228,1.175,229,2.201,233,2.664,236,1.936,256,1.388,275,1.149,280,3.758,281,1.755,283,1.708,284,2.499,289,1.466,297,2.029,298,3.609,306,1.647,317,1.855,336,1.51,351,2.138,353,1.012,364,4.164,375,2.156,376,2.115,377,0.83,382,3.666,391,1.562,395,1.466,416,2.721,421,2.321,430,2.914,444,2.561,463,2.233,467,2.902,471,1.538,478,2.249,500,1.755,505,2.449,525,1.748,528,1.955,535,3.38,546,3.092,555,1.755,557,1.846,562,1.822,568,1.692,593,1.83,597,1.916,601,1.742,604,2.449,605,2.138,607,1.422,611,1.542,622,3.713,628,1.916,629,2.36,655,3.862,677,1.955,679,2.902,683,2.211,685,2.064,699,3.521,708,2.902,731,1.945,732,2.425,740,1.677,741,2.403,747,1.524,762,2.499,820,3.037,828,1.547,846,1.647,850,2.425,856,2.216,874,1.822,878,1.582,885,3.368,886,2.36,893,2.142,920,2.156,921,3.021,930,2.029,940,2.211,999,2.018,1025,2.804,1076,2.851,1127,3.267,1173,3.711,1178,2.721,1179,2.284,1180,4.643,1181,1.926,1191,2.721,1202,1.955,1270,3.172,1281,1.872,1290,2.064,1310,2.648,1417,2.449,1451,3.172,1471,3.172,1473,1.653,1482,2.804,1782,1.671,1800,5.909,1891,2.761,1916,3.529,1974,3.267,1998,2.156,2428,3.021,2533,3.092,2644,3.998,2645,3.092,2646,3.172,2647,3.998,2648,3.711,2649,3.998,2650,3.998,2651,3.998,2652,3.267,2653,2.959,2654,3.711,2655,3.521,2656,3.711,2657,3.711,2658,3.998]],["t/260",[2,1.198,7,0.536,16,0.826,23,3.09,24,1.101,25,2.177,26,1.649,27,2.197,30,4.252,31,2.437,33,2.531,34,1.731,40,2.339,56,0.438,84,1.003,85,1.538,96,1.564,98,1.56,115,2.045,123,3.154,134,2.21,162,1.56,211,3.304,216,2.261,223,2.5,232,2.473,233,1.607,236,1.788,262,2.903,275,1.375,278,1.591,279,3.569,297,2.429,306,1.972,317,2.22,318,1.655,323,2.742,353,1.211,358,3.7,367,5.079,381,2.101,393,2.181,407,2.007,415,3.129,416,2.045,471,1.84,476,1.289,478,2.692,484,1.881,491,1.338,527,0.971,568,1.947,593,3.353,609,2.903,613,3.073,630,3.418,670,2.581,683,2.544,685,2.471,725,2.261,728,3.092,729,3.169,734,1.965,740,5.107,818,2.23,839,2.547,850,2.903,851,3.564,853,3.356,857,2.015,885,2.531,903,3.092,904,2.402,938,3.474,940,1.661,947,2.261,953,1.796,999,2.415,1037,1.693,1139,2.376,1194,4.045,1198,3.909,1220,3.7,1224,2.85,1280,2.991,1281,5.699,1330,3.023,1504,3.129,1505,4.441,1557,2.755,1898,3.582,1917,4.441,1956,4.045,2041,3.541,2096,3.797,2206,4.214,2659,4.785,2660,4.785,2661,7.326,2662,4.785,2663,4.785,2664,4.785,2665,4.441,2666,4.045]],["t/262",[1,2.349,16,1.176,17,1.598,21,1.142,25,2.493,27,1.501,28,0.57,30,2.529,84,0.685,98,1.421,101,2.152,121,3.699,132,1.857,145,3.54,158,3.16,163,2.506,190,2.078,211,3.008,236,1.662,247,1.776,275,1.251,281,1.913,283,1.169,286,3.832,287,4.512,297,2.211,323,1.63,334,2.29,338,0.994,351,1.463,370,3.162,376,2.305,382,2.508,395,1.598,397,3.66,405,1.286,434,2.695,444,1.753,500,1.913,524,2.488,527,2.089,565,1.32,593,1.994,608,2.098,630,2.335,635,2.294,653,1.273,691,2.224,740,4.785,750,2.849,757,0.996,760,2.823,790,2.815,798,4.282,810,2.807,827,2.049,853,3.055,874,3.823,880,1.741,893,2.334,896,3.457,904,5.726,934,1.928,936,1.73,1009,1.861,1045,4.214,1066,1.994,1107,3.331,1132,2.815,1201,5.19,1209,3.559,1216,2.058,1218,2.276,1223,2.29,1281,6.51,1285,2.276,1417,2.668,1435,2.723,1444,2.695,1457,2.885,1459,2.964,1482,3.055,1567,3.107,1801,2.305,1813,2.964,1814,2.885,1822,2.433,1913,3.457,1997,3.107,2043,2.849,2118,2.849,2206,3.837,2485,2.529,2666,3.682,2667,4.356,2668,2.849,2669,4.778,2670,6.323,2671,3.292,2672,2.643,2673,4.356,2674,4.043,2675,4.356,2676,4.043,2677,3.162,2678,3.457,2679,2.752,2680,2.572,2681,2.594,2682,4.356,2683,2.488,2684,2.488,2685,4.356,2686,4.356]],["t/264",[2,1.216,7,0.735,10,2.439,14,3.355,16,0.839,21,1.274,23,1.505,28,1.417,31,1.102,84,0.488,98,1.584,105,1.41,123,1.537,124,2.844,132,2.74,160,1.581,162,2.417,174,2.773,197,2.009,233,1.632,236,2.784,245,2.693,247,1.981,248,2.116,282,2.452,283,1.304,294,2.426,305,2.868,306,3.054,316,2.009,336,2.799,343,1.604,351,1.632,361,3.037,375,2.62,391,1.898,408,1.628,409,2.285,447,2.133,459,3.527,562,2.214,568,1.291,579,3.26,593,2.224,601,2.116,607,1.728,655,2.947,664,3.306,666,3.527,677,2.375,683,1.687,685,4.64,698,3.26,724,2.195,740,3.769,747,1.851,778,2.844,799,3.408,814,2.962,836,3.757,841,2.084,851,2.363,854,3.661,874,3.378,885,3.921,931,3.465,940,1.687,947,2.295,1001,3.465,1009,2.076,1028,2.976,1057,4.454,1156,3.672,1201,6.219,1202,2.375,1281,4.706,1290,2.509,1457,3.218,1459,3.306,1460,3.757,1482,5.198,1493,2.868,1494,4.279,1503,3.855,1782,5.345,1796,4.279,1813,5.043,1867,4.509,1871,3.672,1891,3.355,1893,4.279,1935,5.881,1983,4.279,2141,3.527,2528,3.969,2639,3.355,2656,4.509,2680,2.868,2687,3.969,2688,4.859,2689,4.859,2690,4.859,2691,3.527,2692,4.279,2693,4.509,2694,4.859,2695,4.107]],["t/266",[7,0.751,16,1.772,30,5.955,98,3.346,132,2.797,225,3.403,233,3.446,236,2.504,275,2.947,395,3.763,524,5.861,601,4.469,740,4.304,828,3.971,920,5.534,951,6.11,1281,4.803,2696,6.413]],["t/268",[7,0.772,25,2.369,27,1.756,28,0.701,31,1.808,40,2.617,48,2.015,56,0.49,84,0.801,85,1.721,107,1.272,192,2.926,194,3.817,233,3.2,236,2.576,238,2.849,247,2.183,248,2.331,254,1.597,256,1.858,292,3.218,316,2.213,318,1.801,353,1.354,372,1.858,395,1.963,426,1.834,482,2.221,502,2.438,527,1.933,553,2.63,565,2.416,603,4.247,612,3.216,614,0.914,635,1.802,662,2.887,691,2.732,740,5.285,760,3.245,798,4.069,810,3.285,814,2.139,821,3.458,874,2.439,880,2.139,896,4.247,904,5.941,953,2.009,999,4.024,1036,4.883,1116,3.278,1163,3.011,1201,4.932,1216,2.529,1245,2.448,1281,5.54,1457,3.545,1459,3.642,1605,3.642,1822,2.989,1907,2.565,1913,4.247,2118,5.214,2671,4.045,2680,4.706,2681,4.748,2697,7.2,2698,3.642,2699,4.714,2700,4.045,2701,4.932,2702,5.352,2703,4.714,2704,6.794,2705,4.714]],["t/270",[2,1.286,7,0.898,9,3.088,10,1.543,16,0.531,17,1.884,21,0.806,22,1.77,25,3.385,27,1.703,28,0.403,30,1.785,31,1.95,32,1.785,33,1.626,36,1.258,37,1.902,56,1.108,84,1.04,85,0.988,92,1.056,96,1.005,98,2.158,103,0.955,105,0.892,113,1.04,123,1.625,129,1.704,160,1,164,2.193,165,0.639,183,2.046,191,2.063,213,1.161,219,0.883,221,1.054,223,1.607,228,1.946,233,2.889,236,2.78,238,1.637,240,1.56,241,2.232,247,1.254,278,1.022,279,1.233,281,2.256,283,2.308,313,0.697,316,1.271,318,2.101,338,0.702,343,1.015,351,1.033,372,1.068,377,1.374,398,1.005,416,1.314,422,1.942,426,1.054,437,2.883,444,2.662,445,3.874,472,2.991,477,2.828,481,1.704,502,0.787,506,1.692,509,1.616,512,0.984,527,0.624,552,1.01,565,0.932,568,0.817,605,1.726,612,3.088,614,0.525,622,1.439,647,1.07,650,4.136,681,2.982,691,2.622,694,1.552,712,1.535,740,5.527,757,1.513,760,1.409,769,1.097,814,1.229,820,1.466,821,1.987,826,2.378,854,1.519,857,1.294,874,1.401,936,2.04,937,4.227,940,1.068,945,1.488,951,1.831,1006,1.459,1041,3.907,1072,2.378,1218,1.607,1243,2.092,1281,5.896,1284,1.395,1290,1.588,1303,1.704,1306,1.785,1361,2.512,1363,1.717,1444,1.902,1482,3.604,1629,1.987,1638,1.474,1718,1.902,1734,4.525,1847,1.922,1916,2.848,2312,1.527,2372,1.902,2508,3.604,2632,1.704,2636,2.735,2680,3.033,2681,4.605,2684,1.756,2698,2.092,2701,1.902,2706,2.378,2707,3.075,2708,2.063,2709,2.44,2710,2.708,2711,1.848,2712,2.853,2713,2.512,2714,2.853,2715,3.075,2716,2.853,2717,3.117,2718,2.853,2719,2.378,2720,2.708]],["t/272",[7,0.73,12,1.791,21,1.921,25,1.422,34,1.131,56,0.816,84,0.736,96,1.564,105,1.389,106,1.627,107,1.741,111,3.256,115,2.045,132,1.997,162,1.56,170,2.441,179,2.275,197,1.979,213,1.807,228,2.154,233,3.35,236,1.788,238,6.996,246,1.679,247,1.951,272,3.382,276,3.368,281,2.101,313,1.085,318,2.429,323,1.791,324,2.351,364,3.129,365,2.25,372,1.661,382,2.755,387,3.092,390,2.144,409,2.25,482,1.986,491,2.049,502,1.224,527,2.47,541,2.733,565,1.45,568,1.272,578,1.745,607,2.606,630,1.64,632,2.389,651,5.506,660,3.304,685,2.471,712,2.389,740,2.007,814,1.912,827,2.25,877,2.479,878,1.894,880,1.912,885,3.875,893,2.564,904,6.393,947,3.461,950,4.734,1006,2.271,1024,2.825,1034,3.909,1066,2.19,1116,2.931,1164,2.24,1245,1.469,1281,4.167,1352,2.135,1473,1.979,1632,2.305,1822,2.672,1838,3.304,1940,3.616,2232,3.129,2372,2.96,2445,3.797,2680,2.825,2681,5.94,2697,3.616,2701,6.17,2721,4.214,2722,5.985,2723,4.214,2724,4.214,2725,4.045]],["t/274",[2,1.892,7,0.745,30,5.914,31,1.714,40,3.696,84,1.159,98,2.466,123,2.391,163,3.748,228,2.223,233,3.422,236,2.486,281,3.319,294,3.775,298,4.285,313,1.714,318,2.302,338,1.726,351,2.539,361,7.203,377,1.569,461,3.927,482,3.138,740,5.401,814,3.021,822,4.191,863,5.392,1132,4.885,1218,3.951,1281,5.395,1394,7.629,1483,5.847,1826,5.221,2021,6.933,2681,4.503,2684,6.583,2726,5.007,2727,6.391,2728,5.999,2729,7.017]],["t/276",[7,0.716,25,2.907,29,3.168,129,5.424,228,2.876,233,3.286,236,2.936,238,5.209,248,4.262,476,2.636,527,1.985,562,5.483,683,3.397,740,4.104,886,5.776,904,4.911,1044,5.348,1116,5.992,1281,4.58,1688,6.18,2674,9.08,2726,6.48]],["t/278",[7,0.754,21,1.608,29,1.985,30,5.111,31,1.39,34,1.449,41,2.581,84,0.616,105,2.556,157,3.285,160,1.994,165,1.275,174,2.429,181,3.223,189,3.045,194,4.372,197,2.535,200,2.819,211,4.234,216,2.897,220,3.502,221,2.101,233,3.461,236,1.496,248,2.671,262,3.719,275,1.761,317,2.844,318,2.328,351,2.957,353,2.607,359,3.603,369,3.53,376,3.243,390,2.747,421,3.559,430,4.716,482,2.544,484,2.41,527,1.244,596,2.562,611,2.365,628,2.938,637,3.651,677,2.997,740,5.209,765,3.375,820,5.372,839,3.264,850,3.719,851,2.982,854,3.029,921,4.633,996,3.588,1006,2.91,1009,2.62,1026,4.865,1044,4.813,1132,3.961,1163,3.449,1216,2.897,1281,5.272,1500,6.176,1809,5.69,1885,5.009,1909,5.182,1928,5.399,1998,3.306,2231,4.114,2684,3.502,2730,5.69,2731,6.131,2732,6.131]],["t/280",[0,2.246,2,1.049,4,0.697,7,0.852,8,1.209,14,2.895,16,0.957,18,2.261,20,1.405,21,1.454,22,1.394,23,1.299,24,0.364,25,1.647,27,1.221,28,0.317,29,0.784,31,1.499,32,2.433,33,1.281,34,0.991,36,0.991,39,0.914,56,0.685,84,0.883,93,1.648,96,1.812,97,1.74,101,1.196,105,0.703,106,2.248,107,0.996,109,0.868,110,1.184,115,1.791,123,2.091,134,1.118,135,1.281,138,0.906,153,1.99,156,1.529,160,0.788,164,1.069,165,1.153,170,0.664,172,0.778,174,0.668,177,1.085,186,1.059,187,1.564,190,1.155,207,1.588,215,3.829,216,1.981,220,2.395,221,0.83,225,0.803,236,0.591,254,1.251,256,0.841,257,1.144,262,2.543,263,3.891,275,1.204,283,1.488,285,1.607,291,1.323,292,2.238,297,1.229,298,2.376,315,1.848,321,1.698,337,1.046,338,1.266,339,1.133,351,0.813,353,1.061,359,0.991,362,1.758,375,1.306,387,2.709,390,1.878,391,0.946,393,1.911,395,0.888,416,2.369,421,1.405,440,0.991,458,1.265,467,1.758,471,0.931,476,1.129,482,1.005,484,0.952,489,1.498,491,1.173,499,1.564,526,2.813,527,1.341,528,1.184,536,1.159,550,2.022,552,2.171,578,0.883,579,1.625,581,1.625,589,2.046,594,2.247,599,2,601,1.055,605,0.813,611,0.934,620,4.508,628,1.16,630,1.437,632,1.209,638,1.352,642,3.692,655,1.469,696,1.583,708,1.758,712,1.209,719,1.352,724,1.094,730,1.83,736,0.958,737,1.698,747,0.923,757,0.554,759,0.788,761,2.132,765,1.333,770,3.954,774,2.104,798,1.236,810,1.727,814,1.675,818,1.128,825,1.726,827,1.139,840,1.758,842,3.326,856,1.342,862,1.936,874,1.911,876,4.176,885,1.281,890,1.672,898,1.089,908,1.921,910,2.046,914,1.936,934,1.072,936,0.961,939,1.383,953,0.909,993,1.297,996,3.869,1000,2.132,1009,1.035,1026,1.921,1044,1.323,1050,3.332,1066,1.108,1067,1.758,1108,1.513,1109,2.132,1113,1.726,1179,1.383,1180,1.417,1191,1.647,1199,2.247,1201,2.593,1234,1.273,1281,1.133,1284,1.099,1302,1.455,1314,1.529,1328,1.342,1361,1.978,1389,1.333,1417,1.483,1473,1.734,1487,1.498,1503,1.921,1540,1.243,1778,3.168,1782,1.012,1793,2.132,1813,1.647,1814,1.603,1822,1.352,1838,1.672,1885,3.425,1936,1.978,1945,1.498,2101,1.978,2321,1.513,2428,1.83,2520,2.132,2527,3.242,2666,2.046,2687,1.978,2693,2.247,2733,2.421,2734,4.192,2735,8.184,2736,4.192,2737,4.192,2738,4.192,2739,3.326,2740,1.978,2741,1.758,2742,2.421,2743,2.421,2744,8.184,2745,2.421,2746,2.421,2747,2.421,2748,2.421,2749,4.192,2750,1.872,2751,2.421,2752,2.421,2753,4.192,2754,1.792,2755,2.421,2756,5.544,2757,2.421,2758,4.192,2759,2.421,2760,4.192,2761,2.421,2762,2.421,2763,2.421,2764,1.978,2765,2.421,2766,2.421,2767,2.421,2768,2.247,2769,2.247,2770,2.421,2771,2.421,2772,2.421,2773,2.046,2774,4.192,2775,2.247,2776,2.421,2777,2.421,2778,3.425,2779,2.421,2780,2.132,2781,2.421,2782,1.483,2783,1.921,2784,1.872,2785,2.421,2786,2.421,2787,1.429,2788,2.421,2789,1.529,2790,2.421,2791,2.421,2792,2.421,2793,2.421]],["t/283",[7,0.898,16,1.915,17,2.175,18,5.006,19,5.076,20,3.442,26,2.961,28,1.126,31,1.344,48,2.232,84,0.864,89,2.053,90,2.593,92,2.038,93,2.331,123,1.875,164,2.578,174,1.636,199,2.714,201,2.801,221,2.032,225,2.851,226,4.55,232,2.001,237,4.035,247,2.418,248,2.583,275,1.703,291,3.241,296,2.121,298,3.361,302,3.673,318,1.34,323,2.219,353,2.175,359,2.426,377,1.784,390,2.657,391,2.316,403,3.531,407,2.487,418,2.75,447,2.603,478,3.336,484,2.331,536,1.639,541,3.387,555,2.603,557,2.738,565,1.797,568,1.576,596,2.478,664,4.035,683,2.059,719,3.311,731,2.884,762,3.706,764,3.706,821,3.831,851,2.884,854,2.929,856,3.287,874,3.918,879,4.304,887,3.471,940,2.059,1029,3.787,1043,2.856,1044,3.241,1221,4.095,1476,2.931,1491,4.228,1788,4.585,1801,3.137,1998,3.198,2725,5.012,2794,5.503]],["t/285",[7,0.758,14,5.349,21,3.059,24,1.164,29,2.508,31,2.348,56,0.71,84,0.779,101,3.826,107,1.84,109,1.949,110,3.787,123,3.275,134,3.577,200,3.561,216,3.66,221,2.655,228,3.43,267,6.146,277,3.695,294,3.867,302,3.31,395,3.798,483,4.534,527,2.101,568,2.752,580,2.496,593,3.546,601,3.374,605,2.601,611,2.988,635,2.608,683,2.69,756,3.374,770,5.524,771,7.189,854,3.826,880,3.095,1281,4.848,1385,5.623,1493,4.573,1869,7.189,2013,4.534,2795,7.189]],["t/287",[1,2.839,7,0.86,8,2.628,16,0.909,21,1.38,24,1.884,31,1.785,48,1.982,49,1.642,56,1.078,84,1.052,98,2.567,99,3.08,105,1.528,107,1.25,109,1.476,110,4.612,116,1.823,123,2.983,124,3.08,130,2.784,133,3.126,160,1.712,164,1.342,171,1.327,174,1.452,197,2.176,221,2.698,228,1.547,236,1.284,247,2.146,254,2.349,257,2.487,272,2,279,2.11,282,3.974,285,3.019,291,2.877,311,3.977,317,2.441,322,1.528,334,2.767,351,1.768,359,2.154,377,1.634,395,3.459,397,2.03,527,2.386,528,2.573,536,2.176,565,1.595,568,1.399,578,1.919,591,2.784,605,1.768,607,1.872,614,0.899,622,2.464,655,3.193,658,2.339,677,2.573,755,2.2,757,1.204,759,1.712,795,3.193,824,3.581,997,4.042,1170,1.499,1172,4.449,1245,2.896,1274,2.983,1289,3.541,1312,1.795,1870,3.62,2013,3.08,2796,5.263,2797,3.486,2798,5.263,2799,3.821]],["t/289",[7,0.758,16,1.069,28,0.811,41,2.606,49,1.291,56,1.224,84,0.622,105,1.797,107,2.461,109,1.949,110,4.335,123,1.958,124,3.623,127,3.153,132,1.687,164,2.641,165,1.287,170,1.697,171,1.561,193,2.09,219,1.778,223,3.235,228,1.82,272,2.352,291,3.384,296,2.214,315,2.729,318,1.399,322,2.574,338,1.413,408,2.971,447,2.718,517,3.483,527,2.528,567,2.304,593,2.834,596,2.588,614,1.058,636,3.235,663,3.509,814,2.474,850,3.755,934,3.925,953,2.324,997,2.127,1163,3.483,1170,2.526,1235,2.834,1245,3.181,1285,3.235,1870,3.346,1876,3.72,1902,2.74,2082,4.213,2797,5.873,2800,7.667,2801,6.191,2802,5.452,2803,5.233,2804,8.229]],["t/291",[2,0.781,5,1.7,7,0.228,12,1.168,24,0.469,28,0.876,29,1.01,31,1.769,36,1.277,56,1.219,84,0.942,103,1.615,105,1.51,107,1.236,109,1.629,110,1.525,115,1.333,127,0.95,132,1.823,138,1.168,139,2.785,145,1.941,160,1.692,164,1.326,170,3.33,172,1.003,174,0.861,196,1.148,203,1.155,224,1.227,225,1.725,228,0.917,233,1.048,236,2.116,240,1.584,246,1.095,254,1.552,257,1.474,279,1.251,292,1.259,296,1.116,313,1.179,315,1.375,317,2.413,318,0.705,338,1.187,359,1.277,377,1.08,398,1.02,407,1.309,410,1.911,426,2.292,437,1.359,502,1.711,527,1.357,530,1.295,560,1.541,565,2.027,578,1.138,593,3.061,601,1.359,607,1.11,614,0.889,630,1.069,647,1.086,685,1.611,696,2.04,712,1.558,724,1.41,731,1.518,747,1.189,756,1.359,757,0.713,759,3.047,760,3.466,769,2.385,773,2.265,827,1.467,851,4.556,862,2.402,877,1.76,880,2.673,919,1.993,942,1.223,946,2.222,953,1.952,973,2.016,997,1.787,1008,1.755,1114,1.314,1163,1.755,1235,4.547,1245,0.958,1266,0.79,1332,3.791,1352,1.392,1408,2.143,1454,1.796,1473,1.29,1508,1.602,1530,1.742,1754,1.231,1782,2.174,1822,1.742,1863,1.211,1870,1.008,1899,2.016,1902,3.454,1907,1.495,1918,3.604,2082,3.539,2092,1.621,2450,4.949,2488,1.755,2627,2.358,2797,3.445,2802,4.581,2805,2.225,2806,2.094,2807,3.12,2808,5.201,2809,3.12,2810,3.12,2811,2.476,2812,3.12,2813,6.873,2814,3.361,2815,2.016,2816,2.309,2817,2.476,2818,1.911,2819,2.309,2820,3.12,2821,2.309,2822,2.225,2823,2.476,2824,2.637,2825,2.476,2826,4.127,2827,2.225,2828,2.476,2829,2.476,2830,2.896,2831,2.476,2832,2.358,2833,2.04,2834,3.12,2835,2.549,2836,2.225,2837,2.155,2838,3.12,2839,3.286]],["t/293",[2,1.184,6,3.175,7,0.647,15,1.601,16,1.254,21,1.241,24,0.711,26,1.63,28,1.539,56,1.077,84,1.135,103,1.469,107,3.069,109,1.674,110,2.313,132,1.98,138,1.771,163,1.74,165,1.838,170,2.424,174,1.305,182,2.255,183,1.885,190,2.257,213,1.787,219,1.359,228,1.391,236,1.155,246,1.66,289,1.735,296,1.692,316,1.956,317,2.195,318,1.069,322,2.108,338,1.08,360,3.133,363,3.133,377,0.982,407,1.985,418,2.195,426,1.622,441,2.388,476,1.956,491,1.323,511,1.4,513,3.176,525,2.069,527,0.96,536,2.956,562,2.156,565,3.562,567,3.289,568,1.93,593,3.324,605,2.439,662,2.552,672,2.586,712,2.362,743,2.898,757,1.661,759,1.539,760,2.424,769,1.687,772,5.414,862,2.185,878,1.872,880,2.902,919,3.022,997,1.626,1008,2.661,1117,3.646,1163,2.661,1170,1.348,1223,2.487,1235,2.166,1289,1.776,1290,2.443,1302,2.843,1352,2.111,1363,1.581,1540,2.429,1733,2.623,1870,1.528,1902,2.094,1972,3.374,2017,3.754,2082,3.22,2092,2.457,2293,3.575,2296,3.22,2372,2.927,2802,4.167,2840,3.999,2841,4.731,2842,4.731,2843,4.731]],["t/295",[4,1.254,5,1.424,7,0.498,16,2.095,21,2.2,24,1.64,26,1.501,27,0.96,28,1.589,29,1.41,36,1.783,56,0.943,84,0.685,97,1.808,99,3.987,103,1.353,107,1.619,109,1.942,132,3.594,133,3.331,139,1.814,145,1.626,160,1.417,162,2.222,164,2.139,165,1.416,170,3.468,182,1.353,193,3.202,197,1.801,203,0.968,206,4.094,224,2.679,228,2.466,232,1.47,236,1.662,275,1.251,289,1.598,292,1.758,296,1.558,305,2.572,316,1.801,317,2.021,340,2.695,353,1.724,405,1.286,430,1.994,480,2.752,484,1.713,508,2.381,527,0.884,556,4.043,578,1.589,593,3.118,607,1.549,613,1.827,623,1.936,632,2.175,635,2.294,636,2.276,647,2.921,663,2.469,666,3.162,690,2.508,696,2.849,747,1.66,756,1.898,759,1.417,760,1.194,852,1.753,878,2.696,919,2.783,947,3.219,951,4.057,1012,2.29,1039,3.559,1107,2.13,1122,2.529,1235,1.994,1314,2.752,1353,5.566,1397,2.451,1801,2.305,1806,3.292,1814,2.885,1891,3.008,1902,1.928,2082,2.964,2095,5.566,2645,3.369,2797,2.885,2813,3.837,2844,3.008,2845,4.356,2846,3.837,2847,3.837,2848,4.356,2849,4.356]],["t/297",[2,2.273,16,1.568,21,2.381,24,1.893,28,1.189,56,0.832,84,1.155,109,1.797,132,3.13,165,1.888,177,4.069,179,2.82,182,2.82,199,4.157,228,2.67,236,2.216,285,3.482,372,3.153,446,5.361,491,2.54,511,2.687,536,3.174,614,1.551,759,2.954,802,3.409,1223,4.774,1363,3.035,1373,5.147]],["t/299",[7,0.742,16,1.298,49,1.568,56,1.177,107,2.411,109,1.925,110,6.282,132,2.049,140,3.69,170,2.061,174,2.799,236,1.834,257,3.551,285,3.891,292,3.034,296,2.688,322,2.946,343,2.481,407,3.153,511,2.224,527,2.607,557,3.471,593,3.44,603,5.964,644,6.14,877,3.434,997,2.583,1122,4.362,1170,2.141,1181,4.888,1245,3.116,1417,4.603,1863,2.918,1870,2.428,2605,5.044,2797,4.977,2804,6.975,2850,5.812,2851,4.001]],["t/301",[7,0.411,24,0.844,28,0.735,36,2.297,50,3.048,56,1.302,84,0.985,98,1.831,103,2.565,109,1.884,130,4.369,132,1.53,139,2.338,160,2.687,165,1.167,170,2.687,186,2.455,233,1.885,236,2.015,278,1.867,279,2.251,292,2.266,338,2.237,474,4.242,508,3.069,513,2.455,544,3.258,578,2.047,580,1.809,593,2.57,601,2.445,611,3.186,635,1.89,702,3.508,724,2.537,759,1.826,760,2.963,765,3.09,851,2.731,880,2.243,999,2.833,1235,4.946,1332,3.182,1381,2.065,1454,3.232,1508,4.24,1530,3.135,1539,3.473,1900,6.124,1902,4.782,1918,3.814,2797,5.47,2813,7.273,2816,4.154,2817,4.454,2818,3.438,2819,4.154,2821,4.154,2822,4.003,2823,4.454,2825,4.454,2826,6.553,2827,4.003,2828,4.454,2829,4.454,2830,5.21,2831,4.454,2832,4.242,2852,5.614,2853,5.21,2854,5.614]],["t/303",[7,0.884,24,1.817,84,0.991,174,2.72,179,3.062,182,3.062,199,4.513,213,3.724,233,3.312,236,2.406,316,4.077,536,3.341,593,4.513,759,3.208,1114,4.151,1197,7.297,1263,5.547,1373,5.589,1508,6.206]],["t/305",[2,2.292,7,0.67,16,1.581,26,2.231,28,1.391,49,1.351,56,0.593,84,1.068,98,2.112,101,3.199,103,2.011,107,2.175,109,1.662,110,3.166,132,2.895,153,3.074,156,4.091,157,3.47,160,2.107,165,1.903,170,2.511,197,3.786,213,2.446,216,3.06,221,2.22,222,5.703,228,1.904,232,2.186,239,3.448,255,4.048,283,1.738,313,1.468,317,3.004,318,1.463,351,2.175,353,2.688,369,3.729,391,2.53,500,2.843,527,1.314,558,2.688,562,2.952,568,1.721,583,3.671,593,4.191,607,3.257,628,3.104,696,4.235,712,3.233,823,4.894,852,2.605,881,4.407,888,3.54,919,4.137,1066,2.964,1157,5.291,1498,4.289,1504,4.235,1540,3.325,1782,2.707,1954,5.139,1987,5.008,2232,4.235,2799,4.701,2855,6.476,2856,6.476,2857,6.476,2858,6.476,2859,6.476,2860,6.476,2861,6.476]],["t/307",[1954,9.444]],["t/309",[9,6.537,48,4.096,84,1.094,159,6.729,683,3.777,1157,8.888,1475,9.195,2061,3.409,2862,10.878,2863,9.58,2864,10.096]],["t/311",[7,0.934,16,1.347,23,1.61,24,0.781,27,1.718,28,1.021,29,1.683,31,2.123,40,2.541,49,1.084,56,0.476,84,0.784,89,2.701,109,0.813,123,2.961,156,3.284,158,2.411,160,1.691,165,1.081,172,1.671,182,1.614,187,3.359,216,2.456,219,2.241,221,1.782,226,2.339,229,2.861,232,2.633,246,1.824,247,3.181,273,3.847,278,1.729,279,2.084,283,2.512,298,2.946,313,1.179,318,1.175,332,2.504,338,1.187,341,2.653,343,1.716,353,1.974,375,2.803,376,2.75,395,1.906,476,1.4,478,2.924,491,1.454,500,2.282,527,1.9,530,2.157,531,2.924,545,2.23,552,1.707,555,2.282,558,2.157,562,2.369,565,1.575,567,1.934,568,1.381,578,1.896,584,4.02,585,3.646,593,2.379,597,2.491,604,3.184,614,0.888,626,4.394,630,1.782,658,2.31,677,2.541,683,1.805,719,2.903,726,3.153,760,1.425,802,1.951,805,3.284,818,2.422,852,2.091,856,2.882,858,4.394,875,3.847,881,3.537,885,2.75,888,2.842,903,3.359,947,2.456,1038,4.394,1188,3.707,1193,3.042,1212,3.184,1224,3.096,1243,3.537,1416,3.443,1417,3.184,1688,3.284,1782,3.913,1786,4.394,1787,4.247,1788,4.02,1789,3.928,1790,4.394,1885,4.247,1906,2.624,1909,4.394,2643,5.04,2865,5.198,2866,4.578,2867,2.822,2868,5.198]],["t/313",[7,0.902,28,1.509,55,5.817,84,0.76,88,3.16,89,4.271,160,2.46,203,1.68,247,3.083,252,5.847,275,2.172,282,3.816,283,3.093,332,3.641,338,1.726,474,5.714,489,4.677,511,2.237,531,6.483,568,2.009,608,3.641,672,4.133,684,3.659,802,2.838,840,5.488,846,3.115,879,5.488,882,4.191,884,4.425,1057,4.544,1066,3.461,1178,5.145,1181,3.641,1210,5.392,1372,6.658,1373,4.285,1782,4.259,1893,6.658,2643,4.885,2654,7.017,2869,7.561,2870,6.391,2871,7.561,2872,7.561,2873,5.714,2874,6.177,2875,7.561]],["t/315",[2,2.217,5,1.552,7,0.533,18,1.937,24,1.095,28,1.301,33,2.512,41,1.999,56,1.165,84,0.477,105,2.114,132,1.294,134,4.091,155,2.854,160,1.545,167,3.387,182,2.262,190,4.226,197,1.964,213,1.793,228,1.396,236,1.159,241,3.448,246,1.667,247,1.937,249,3.279,283,1.274,285,1.821,313,1.651,323,2.726,338,1.084,353,1.202,365,2.233,366,2.757,376,2.512,377,0.986,392,2.734,397,1.832,405,1.402,426,3.405,440,1.943,471,3.407,484,1.867,502,1.215,513,2.077,527,2.016,541,2.713,614,0.811,724,2.146,732,2.881,759,1.545,760,3.227,763,3,805,3,807,3.177,814,1.898,828,1.838,839,2.528,851,4.833,862,2.193,878,1.879,882,2.633,897,3.514,934,2.102,935,3.387,947,2.244,1037,1.68,1040,3.033,1117,2.384,1235,2.174,1266,2.242,1300,2.881,1308,2.614,1310,3.145,1330,3,1464,3.768,1754,2.873,1782,4.478,1863,4.159,2265,4.014,2379,3.673,2445,5.779,2450,7.929,2456,5.779,2627,3.589,2643,4.706,2696,2.968,2844,3.279,2851,3.878,2866,4.182,2876,4.749,2877,7.284,2878,4.014,2879,6.76,2880,4.749,2881,4.749,2882,4.749]],["t/317",[2,1.834,5,1.564,7,0.536,21,2.334,24,0.719,27,1.054,28,1.166,31,1.085,34,1.131,36,2.998,42,3.256,68,1.684,84,1.081,93,1.881,95,2.755,98,1.56,107,1.741,109,1.993,123,1.513,144,2.126,145,1.786,165,1.523,172,3.914,177,2.144,182,1.486,190,2.282,193,1.615,203,1.063,253,2.672,254,1.428,280,4.325,281,3.908,289,1.755,302,2.045,304,0.936,313,1.661,323,1.791,326,3.023,336,2.766,338,1.092,359,1.958,361,2.991,363,3.169,377,1.52,398,1.564,403,2.85,409,2.25,418,2.22,437,2.084,439,2.903,471,2.817,476,1.289,481,2.653,484,1.881,491,1.338,527,1.486,552,3.276,592,2.85,602,3.909,613,2.007,614,1.521,677,2.339,760,2.441,774,2.402,812,2.96,846,1.972,861,3.797,876,3.023,878,1.894,897,3.541,936,1.9,941,3.256,950,3.092,953,1.796,986,2.328,988,2.485,1057,2.876,1133,2.389,1137,3.056,1274,2.712,1315,2.442,1540,2.456,1557,2.755,1689,2.328,1782,2,1826,5.059,1870,2.875,1906,5.426,1935,3.797,2527,3.7,2528,3.909,2695,4.045,2787,2.825,2867,5.837,2883,4.785,2884,4.785,2885,3.797,2886,4.785,2887,3.616,2888,4.785,2889,4.785,2890,3.797,2891,4.441,2892,3.356]],["t/319",[7,0.977,15,2.621,28,1.014,46,4.992,55,5.888,84,0.779,89,4.04,98,2.526,199,3.546,254,2.311,306,4.807,313,1.756,351,2.601,440,4.238,447,3.401,478,4.357,555,3.401,576,4.234,597,3.712,608,3.731,715,4.94,736,3.065,757,1.771,762,4.841,802,2.908,886,4.573,933,5.271,1014,4.655,1035,5.99,1069,3.888,1072,5.99,1073,5.198,1192,6.547,1447,6.822,1778,5.854,1998,4.178,2643,7.539,2893,7.746,2894,7.746]],["t/321",[7,0.671,10,2.845,19,2.344,21,1.887,25,2.138,26,1.953,28,0.453,31,0.785,33,1.831,34,0.818,41,1.457,46,1.331,55,1.747,56,0.995,84,0.57,89,1.963,96,1.132,98,1.129,100,2.352,109,0.887,127,1.054,133,1.374,134,1.599,140,1.259,160,1.844,162,1.129,164,1.445,165,0.72,174,0.955,177,1.551,182,1.075,186,1.514,187,2.237,189,1.719,196,2.085,199,2.594,203,0.769,206,2.08,213,1.307,219,2.83,221,1.942,228,1.018,275,1.628,283,2.232,292,2.288,294,1.728,295,1.292,306,1.426,316,1.431,318,2.925,323,2.121,336,1.307,351,1.163,353,1.434,367,1.636,391,1.352,397,2.186,398,2.719,440,1.417,471,2.18,472,3.758,476,2.241,478,1.947,482,1.437,506,3.961,510,2.677,512,1.813,514,2.428,527,1.15,531,1.947,566,2.237,567,3.666,578,1.262,593,1.585,611,1.335,613,2.377,623,1.538,653,2.102,675,2.044,683,1.202,703,2.562,707,2.926,723,3.45,734,1.421,736,2.243,757,2.253,802,1.3,814,1.383,846,2.335,854,1.71,882,3.142,885,2.998,898,2.55,945,2.743,954,2.141,1035,2.677,1037,3.84,1042,1.933,1060,3.238,1065,2.828,1107,2.771,1113,2.469,1245,1.063,1284,1.571,1289,1.3,1310,2.293,1312,1.933,1328,1.919,1330,2.187,1364,2.323,1366,1.787,1380,2.026,1473,2.344,1511,2.1,1638,1.659,1672,2.828,1885,2.828,2026,2.69,2032,3.584,2033,1.809,2102,2.747,2312,4.13,2372,2.141,2495,2.677,2509,2.356,2528,2.828,2632,1.919,2633,2.893,2643,4.65,2787,2.044,2895,1.977,2896,2.616,2897,3.213,2898,2.677,2899,2.747,2900,3.213,2901,3.462,2902,4.991,2903,3.462,2904,3.213]],["t/323",[2,1.756,7,0.811,13,5.093,15,2.374,17,2.573,21,2.539,24,1.054,28,1.268,33,3.711,41,2.953,84,0.705,98,2.288,103,2.178,160,2.282,179,2.178,199,3.211,219,2.781,228,2.062,275,2.781,318,2.188,321,4.92,322,2.036,381,3.08,390,3.143,397,2.706,407,2.943,438,3.379,471,2.698,495,3.143,578,2.558,614,1.199,675,4.141,702,4.384,723,2.327,724,3.17,736,2.776,800,6.511,802,2.633,884,4.106,889,5.003,890,4.844,891,4.384,953,2.633,1037,2.482,1043,3.379,1044,5.293,1268,6.511,1289,2.633,1312,3.78,1330,4.432,1380,4.106,1508,3.601,1887,6.511,1996,5.301,2410,6.511,2481,4.533,2655,6.178,2902,8.527,2905,4.646,2906,9.764,2907,7.015,2908,7.015]],["t/325",[7,0.79,16,1.425,84,0.829,171,2.08,219,3.102,261,3.88,277,3.936,283,2.898,317,3.827,318,2.44,377,1.713,437,3.594,471,4.153,562,3.761,567,3.07,578,3.009,591,4.365,736,4.273,757,2.469,798,4.212,1044,4.511,1076,5.884,1214,4.959,1289,3.097,1476,2.814,2447,7.604,2488,4.642,2909,10.022,2910,9.621,2911,8.251,2912,7.658,2913,8.251,2914,8.251,2915,8.251]],["t/327",[7,0.837,17,2.327,24,1.356,29,2.055,31,1.439,34,1.5,49,1.324,55,3.203,56,0.827,84,1.263,85,2.04,103,1.971,105,1.842,109,0.993,162,2.07,170,2.88,177,2.844,179,2.803,228,2.653,236,1.549,275,1.823,283,1.703,285,2.433,322,2.62,338,1.449,351,2.131,377,1.317,398,2.95,470,4.259,527,1.288,605,2.131,614,1.794,683,2.204,759,2.065,760,3.543,811,3.654,879,4.607,885,3.357,940,2.204,947,4.264,1214,3.814,1312,2.164,1633,2.775,1698,4.382,1754,2.503,1882,3.469,2297,1.962,2368,5.036,2389,5.185,2398,8.62,2643,4.101,2814,4.101,2916,5.364,2917,5.589,2918,5.589,2919,6.346,2920,9.748,2921,5.89,2922,4.796,2923,4.908,2924,5.89,2925,5.89,2926,5.89]],["t/329",[7,0.873,10,2.745,15,1.85,16,1.399,21,1.434,29,2.623,49,2.575,85,3.102,160,1.779,174,1.508,182,1.698,186,2.391,213,2.065,276,2.514,277,5.433,336,2.065,353,2.05,359,2.237,361,3.417,365,2.571,393,2.492,398,3.899,400,3.969,401,3.422,437,2.382,471,2.103,498,2.823,527,1.109,536,1.511,557,2.525,569,4.339,608,2.633,611,2.109,614,1.648,647,2.82,684,3.921,709,3.317,820,2.608,831,3.2,857,2.302,878,2.164,898,2.46,1009,4.123,1057,3.286,1202,3.96,1303,3.031,1373,3.099,1473,2.261,1632,2.633,1691,4.467,1698,3.776,1753,7.276,1754,2.157,1801,2.893,1870,1.766,1882,2.989,1937,5.075,2033,2.857,2395,2.66,2481,3.533,2528,4.467,2639,3.776,2643,3.533,2669,3.835,2677,5.88,2789,3.454,2900,5.075,2927,9.649,2928,4.228,2929,5.468,2930,5.88,2931,4.622,2932,5.468,2933,5.468,2934,10.668,2935,5.468,2936,5.468,2937,5.468,2938,5.468,2939,5.468,2940,5.468]],["t/331",[2,1.125,5,1.47,7,0.916,16,0.776,21,1.179,23,3.237,24,1.662,25,2.075,27,1.886,28,0.914,29,1.456,31,1.019,33,2.378,34,1.063,56,0.958,84,1.112,89,2.964,100,1.866,105,1.305,107,1.068,109,1.093,127,1.368,140,1.635,155,2.702,162,1.466,163,1.654,169,3.544,174,1.24,177,2.014,182,1.396,190,2.144,193,1.517,196,1.654,198,2.031,248,1.958,273,3.327,275,1.291,278,1.495,283,1.874,302,1.921,304,0.879,321,3.153,336,2.637,347,2.754,353,1.138,365,2.114,367,2.124,377,1.449,382,2.588,393,2.049,407,2.929,502,1.15,506,2.475,511,1.33,512,2.234,555,1.974,558,2.897,560,2.221,568,1.195,628,2.155,630,1.541,685,2.321,723,2.316,736,1.779,755,1.879,763,2.84,807,1.612,827,2.114,830,2.961,878,1.779,886,2.654,906,2.529,934,1.99,936,1.785,1002,3.327,1043,2.165,1044,2.458,1055,2.754,1059,2.335,1164,2.104,1193,4.087,1245,1.38,1312,1.533,1330,2.84,1446,2.441,1511,2.727,1773,3.153,1782,2.918,1798,5.395,1799,6.674,1819,2.677,1847,2.81,1916,2.492,1998,2.425,2481,4.511,2518,3.971,2643,2.905,2896,3.397,2941,8.956,2942,6.993,2943,4.172,2944,3.959,2945,4.172,2946,3.673,2947,4.172,2948,4.172,2949,4.172,2950,4.172,2951,4.172,2952,3.959,2953,6.48,2954,4.496,2955,3.477,2956,3.567]],["t/333",[4,2.745,7,0.802,17,2.517,18,2.799,23,2.126,28,0.899,55,4.813,56,0.874,84,0.959,162,2.238,164,1.75,165,1.427,187,4.435,213,2.592,219,1.971,221,2.352,227,4.203,232,2.317,247,2.799,257,3.242,275,2.739,277,3.273,282,3.464,283,1.842,284,4.289,302,2.933,303,4.982,315,3.025,318,1.551,320,5.077,323,2.569,341,3.503,353,2.413,484,2.698,491,1.92,500,3.013,509,3.608,522,4.739,530,2.848,545,5.079,552,3.132,558,2.848,559,7.206,560,4.711,608,3.305,684,5.304,765,3.778,794,4.435,839,3.654,893,3.677,920,3.701,948,4.163,958,4.163,1014,4.124,1129,3.983,1216,3.242,1310,4.545,1850,4.289,2811,5.446,2957,6.863,2958,5.801,2959,6.863]],["t/335",[2,1.613,7,0.889,18,3.72,31,1.461,56,0.971,85,2.071,123,2.038,129,5.058,144,2.863,160,2.096,167,4.595,174,2.516,213,2.433,215,4.449,233,3.557,240,3.27,248,2.807,275,2.621,279,2.583,291,3.522,292,2.601,318,1.456,351,2.164,361,4.027,393,2.936,440,2.637,441,3.252,444,2.592,471,3.509,498,3.327,502,1.649,505,3.509,526,4.324,527,1.307,540,7.239,591,3.409,611,2.486,628,4.373,633,4.677,677,3.15,683,2.237,733,5.264,740,2.703,763,4.07,802,2.419,834,4.869,846,4.364,855,4.214,917,4.214,922,5.763,954,3.986,993,3.452,1067,4.677,1109,5.674,1116,3.946,1118,5.98,1281,3.016,1470,4.324,1500,4.519,1593,5.674,1803,4.384,1996,4.869,2684,3.68,2960,7.454,2961,8.034,2962,6.443,2963,4.267,2964,5.98,2965,6.443,2966,4.07]],["t/337",[7,0.637,18,3.55,29,2.819,249,6.012,261,4.094,278,2.895,306,3.587,353,2.203,359,3.562,376,4.606,377,1.807,394,4.038,409,4.094,415,5.694,484,3.423,514,6.106,684,4.214,721,6.908,802,4.197,814,3.479,854,5.523,875,6.443,882,4.826,946,3.72,993,4.665,1064,8.08,1066,3.985,1137,5.561,1139,4.323,1275,5.185,1527,5.332,2141,6.32,2643,7.98,2967,8.08,2968,8.706,2969,7.667]],["t/339",[7,0.92,19,3.283,21,2.082,34,1.877,56,0.728,103,2.466,156,5.016,162,2.589,236,1.938,249,5.483,278,2.64,296,2.84,302,3.393,306,3.272,326,5.016,353,2.664,375,4.282,391,3.102,430,4.819,463,7.308,578,2.896,685,4.1,851,3.863,874,6.351,920,4.282,1111,4.817,1174,6.513,1960,7.781,2364,6.001,2365,6.487,2970,6.712,2971,6.993,2972,6.993,2973,6.487]],["t/341",[7,0.976,16,1.183,24,1.827,25,2.504,27,1.509,28,1.709,31,2.593,40,3.349,56,1.21,103,1.362,107,2.002,109,1.49,115,2.927,123,3.617,127,3.601,162,1.43,219,1.26,223,2.292,276,3.149,468,4.043,476,1.845,491,1.916,527,1.39,528,2.145,565,3.684,568,2.24,601,2.984,614,1.17,630,3.264,635,1.477,757,1.003,759,2.228,810,2.822,814,3.368,999,3.457,1114,1.847,1239,2.506,1328,2.432,1435,2.742,1473,1.814,1754,3.324,1864,2.145,2411,4.661,2636,3.646,2974,3.863,2975,4.387,2976,3.863,2977,3.863,2978,8.387,2979,6.032,2980,3.863,2981,3.863,2982,7.421,2983,3.863,2984,3.863,2985,3.863,2986,3.863,2987,6.032,2988,6.032,2989,6.032,2990,6.032,2991,6.032,2992,6.032,2993,6.032]],["t/343",[2061,3.73]],["t/345",[2,1.995,7,0.922,16,1.645,24,1.198,25,2.369,27,1.179,28,1.55,29,1.733,31,1.213,56,0.49,84,0.958,89,1.854,103,1.662,105,1.554,106,1.82,109,0.838,116,1.854,123,1.693,124,3.133,127,2.9,132,1.459,160,1.741,162,1.745,165,1.113,167,3.817,170,1.468,174,2.199,175,2.716,181,2.814,193,2.691,197,2.213,225,1.775,228,1.573,275,1.537,276,2.461,313,1.213,318,1.801,334,2.814,338,1.82,343,1.767,377,1.655,390,2.398,440,3.262,471,3.664,476,2.148,490,3.5,491,1.497,502,1.37,536,2.633,550,1.952,553,2.63,558,3.308,565,3.42,568,3,576,2.926,577,2.35,580,1.725,593,3.649,601,2.331,607,1.904,663,3.034,664,3.642,670,2.887,683,1.858,734,2.198,747,2.04,757,2.414,796,2.604,810,2.205,835,3.345,836,4.139,841,2.296,852,2.153,860,3.754,874,2.439,929,3.381,930,2.716,999,2.701,1117,2.687,1188,3.817,1754,2.111,1836,3.961,2064,3.545,2696,3.345,2994,4.714,2995,4.967]],["t/347",[2,1.129,4,1.299,5,1.475,7,0.874,27,2.13,28,0.591,31,1.945,36,1.846,42,3.07,49,2.183,56,0.413,84,0.972,92,2.948,93,3.373,106,2.381,109,1.096,131,1.9,134,2.083,138,3.917,144,2.005,153,2.142,165,1.455,171,1.138,172,3.943,173,2.241,181,2.372,188,3.028,193,1.523,207,1.709,216,2.132,224,2.753,228,2.058,236,1.101,256,1.567,276,3.219,281,1.981,288,2.763,289,2.568,292,1.821,302,1.928,318,1.019,334,2.372,337,1.95,338,1.598,339,4.016,365,2.122,376,2.387,377,1.453,409,2.122,441,2.277,468,2.663,500,3.074,502,1.155,525,1.973,527,0.915,552,1.482,555,1.981,568,2.782,578,1.645,579,3.028,580,1.454,601,4.559,620,3.594,629,2.663,632,2.253,636,3.658,638,2.519,691,5.342,715,2.152,728,2.915,732,2.737,743,2.763,774,2.265,807,1.618,810,4.313,812,5.307,816,2.372,841,1.935,862,2.083,878,3.395,907,2.687,927,3.164,952,2.711,995,2.85,1024,2.663,1025,4.911,1066,2.065,1360,2.387,1364,3.028,1365,4.064,2092,2.343,2353,5.36,2646,3.58,2805,3.217,2963,2.988,2996,3.58,2997,6.166,2998,4.187,2999,3.814,3000,3.973]],["t/349",[2,1.374,7,0.929,21,1.44,27,2.856,28,1.4,30,4.717,31,1.245,36,2.247,49,1.145,84,0.552,92,1.887,101,2.713,109,1.272,116,1.902,126,2.461,138,2.055,153,2.607,162,1.791,163,2.02,165,1.142,171,2.049,172,3.11,173,4.804,174,2.668,184,3.852,188,3.685,193,2.743,196,2.02,223,2.87,224,2.159,228,1.614,246,1.927,282,2.772,304,1.074,313,1.245,320,2.924,353,1.39,407,2.304,502,1.405,505,2.112,525,4.23,552,1.804,568,2.571,576,3.002,578,2.003,597,2.632,599,2.619,601,2.392,608,2.645,614,0.938,620,2.819,631,3.331,632,4.057,638,4.538,658,2.44,675,3.242,725,2.595,741,3.3,807,3.469,810,2.263,818,2.559,819,3.591,830,4.103,831,3.214,832,5.079,877,1.858,986,3.953,1002,4.064,1012,5.086,1024,3.242,1028,3.363,1066,2.514,1360,4.299,1365,3.188,1847,3.432,1899,3.548,2787,3.242,2789,3.469,3001,5.492,3002,4.836,3003,4.064,3004,4.247]],["t/351",[0,6.386,7,0.872,21,3.125,27,2.851,109,2.025,127,3.627,179,3.701,193,3.252,343,3.18,437,4.197,557,4.449,631,7.229,757,2.203,816,5.065,1180,5.639,2522,6.871]],["t/353",[7,0.77,84,0.798,109,1.243,133,3.152,160,3.843,165,1.651,221,3.609,228,3.095,275,3.024,320,5.605,377,1.648,426,2.722,430,3.635,445,4.648,476,2.139,491,2.221,498,4.1,502,2.032,527,2.397,565,2.406,568,2.798,635,2.673,811,4.572,936,3.152,939,4.536,1049,5.876,1066,3.635,1276,5.483,1329,5.569,1366,4.1,1416,8.668,1481,5.764,1898,3.882,1970,5.876,2043,5.193,2064,6.973,2516,5.764,3005,6.712,3006,6.301]],["t/355",[7,0.528,21,1.892,28,1.293,31,1.636,33,3.817,37,4.463,42,4.91,46,2.775,56,0.661,84,0.725,90,3.155,92,2.479,93,2.837,106,2.454,107,2.345,109,1.893,115,4.218,126,3.233,153,3.425,165,1.5,189,3.583,216,3.409,224,3.881,272,2.741,318,1.63,353,1.826,407,4.141,426,3.383,502,1.846,513,3.155,620,3.704,629,4.259,635,2.429,658,3.207,769,2.573,893,3.866,909,4.609,936,2.865,951,5.879,997,4.495,1009,3.083,1066,3.303,1398,5.895,1434,3.726,1445,4.662,1733,4,1814,4.779,1845,3.972,2668,4.719,2928,5.58,2966,4.558,3007,5.145,3008,4.983,3009,5.238,3010,4.558]],["t/357",[1,2.815,2,1.958,7,0.817,8,2.606,28,0.683,31,1.183,33,4.139,34,1.234,36,2.136,39,1.971,41,2.198,46,2.007,49,1.089,50,2.834,56,0.478,84,0.944,90,3.422,91,4.036,92,3.583,93,3.076,94,5.886,96,2.558,97,2.166,109,0.817,130,2.761,132,1.423,144,2.32,145,2.92,164,1.995,165,1.085,172,2.515,179,1.621,186,2.283,203,1.16,207,1.977,216,2.466,224,2.052,228,1.534,236,1.274,240,2.649,272,1.983,283,2.519,292,2.107,313,1.774,343,1.723,351,1.753,390,2.339,407,2.19,408,1.749,437,2.274,439,3.166,440,3.202,441,2.634,447,2.292,476,3.009,482,3.247,568,2.771,578,1.903,580,1.682,593,2.389,599,5.965,601,2.274,619,4.66,620,4.017,658,2.32,670,2.815,675,3.081,694,2.634,715,4.477,737,3.661,757,1.194,769,1.862,810,2.151,812,3.229,822,2.894,832,3.262,877,1.766,902,3.373,997,1.794,998,4.66,1009,2.23,1066,2.389,1434,4.846,1814,3.457,2353,2.894,2429,3.414,3011,4.412,3012,4.036,3013,4.597]],["t/359",[2061,3.73]],["t/361",[7,0.807,31,1.934,56,0.782,84,0.857,123,2.698,162,2.782,216,5.213,219,2.45,275,2.45,283,2.289,306,3.515,316,3.527,318,1.927,390,3.822,463,4.763,527,1.731,555,3.745,565,2.584,567,3.174,568,2.267,604,5.224,683,2.962,692,6.312,693,4.872,734,3.503,738,6.969,798,4.354,802,3.202,814,3.409,874,3.887,930,4.329,1029,5.448,1066,3.904,1138,5.983,1174,5.276,1178,5.804,1180,4.992,1189,7.21,1289,3.202,1960,5.276,3014,7.512]],["t/363",[7,0.79,10,4.142,19,3.412,22,4.751,23,2.556,24,1.24,27,1.818,34,2.552,84,0.829,88,3.449,94,4.142,103,2.562,106,3.673,123,2.61,158,3.827,181,4.338,220,4.713,221,2.828,225,2.737,226,3.713,313,1.871,332,3.974,339,3.862,341,4.212,384,6.381,387,5.332,491,2.308,495,4.839,506,4.542,511,2.441,512,2.64,513,3.608,519,6.381,599,3.936,694,5.45,773,5.99,937,5.271,1069,4.142,1105,6.381,1107,4.034,1111,5.005,1179,4.713,1431,6.106]],["t/365",[7,0.852,26,3.197,28,1.215,41,3.907,47,5.432,56,0.85,90,5.092,92,4.372,93,3.649,94,4.659,97,3.851,132,2.53,186,4.059,283,2.491,597,5.581,599,4.427,601,4.043,611,3.58,614,1.586,619,5.527,620,4.764,715,4.427,874,4.23,998,5.527,1433,6.868,1434,4.792]],["t/367",[7,0.852,16,1.603,27,2.044,28,1.525,84,0.933,88,3.879,89,4.406,145,3.464,174,2.56,244,5.301,278,3.086,283,2.491,289,3.404,294,4.634,306,3.824,318,2.097,512,2.97,558,3.851,575,4.091,780,4.585,883,6.316,884,5.432,887,5.432,1114,3.907,1243,6.316,1289,3.484,1315,4.737]],["t/369",[4,2.3,7,0.867,23,3.276,26,2.753,28,1.046,31,2.397,56,0.732,84,1.063,103,2.481,123,3.344,219,2.295,232,4.256,236,1.95,283,3.18,313,1.812,318,1.805,391,3.122,443,5.226,480,5.048,511,2.364,530,3.316,545,3.427,548,4.564,552,3.473,558,3.316,567,3.934,568,2.124,601,3.481,629,4.717,685,4.126,814,3.193,847,4.102,852,4.767,885,4.227,887,4.677,940,2.775,948,4.847,1503,6.34]],["t/371",[2,2.34,7,0.684,24,1.918,84,0.94,165,1.944,248,4.073,283,2.509,285,3.585,313,2.12,397,3.607,512,2.992,536,2.584,565,2.833,567,3.479,568,2.485,757,2.919,759,3.042,760,2.564,878,3.7,930,4.745,1117,4.693,1473,3.866,2032,3.653,2398,6.667,3015,7.639,3016,5.221]],["t/373",[7,0.739,16,1.743,24,1.843,28,1.322,56,0.925,84,1.015,132,3.343,145,3.768,228,2.968,502,2.584,565,3.059,568,2.683,683,3.506,769,3.601,946,5.241,1498,6.687,2696,6.31]],["t/375",[7,0.721,16,1.703,27,2.172,31,2.235,40,4.821,56,0.904,96,3.223,107,2.872,109,2.046,115,4.213,123,3.118,163,3.627,278,3.279,458,5.152,527,2.001,597,4.726,635,3.32,653,2.88,814,3.94,880,3.94]],["t/377",[7,0.789,16,1.862,28,1.412,33,5.706,109,1.688,132,3.478,170,2.957,283,2.894,444,4.339,576,5.896,734,4.429]],["t/379",[23,2.854,24,1.385,25,3.444,28,1.206,32,5.348,49,1.922,56,0.844,98,3.005,132,2.511,157,4.937,165,1.915,236,2.248,276,4.236,337,3.982,339,4.313,491,2.577,502,2.966,513,4.029,544,5.348,558,3.823,565,2.792,577,5.089,757,2.107,795,5.589,796,4.482,1312,3.142,1427,6.818,2032,3.599,2805,6.57]],["t/381",[2,2.568,107,2.438,109,2.082,115,4.384,163,3.774,174,2.83,254,3.062,313,2.326,507,5.179,527,2.082,550,3.742,658,4.56,755,4.288,997,3.526,1047,5.688,3017,9.036]],["t/383",[2,2.135,7,0.807,16,1.473,27,1.879,28,1.445,56,0.782,109,1.727,126,3.822,132,2.325,138,3.192,172,3.932,190,4.068,193,3.724,288,5.224,289,3.128,313,1.934,337,3.687,338,1.947,339,3.993,408,2.858,491,2.386,552,2.802,611,3.291,614,1.457,632,4.258,755,3.565,806,6.192,810,3.515,819,5.578,830,3.618,877,2.886,906,4.798,952,5.126,1012,4.484,1360,4.512,2867,4.631,3018,7.21]],["t/385",[7,0.769,27,2.317,29,3.405,109,1.646,127,3.2,224,4.135,248,4.581,338,2.401,390,4.712,434,6.505,555,4.617,715,5.016,1044,5.749,1389,5.789,3019,8.344]],["t/387",[7,0.839,58,7.697,275,3.295,564,6.893,1625,8.045]],["t/389",[7,0.684,13,6.787,84,0.94,228,2.749,283,3.139,294,4.668,300,5.259,332,4.503,395,3.429,399,6.558,476,2.519,542,7.23,543,6.112,742,5.427,863,6.667,873,7.066,877,3.164,888,5.111,889,6.667,890,6.456,891,5.844,1328,5.183,1566,6.192,1991,5.259,2683,5.341,3020,6.274,3021,6.558,3022,8.234]],["t/391",[7,0.884,11,7.297,17,3.616,18,4.021,19,4.077,28,1.291,158,5.608,332,5.822,391,3.852,846,4.063,880,3.94,887,5.771,917,8.55,922,6.229,923,8.056,924,8.056,925,10.219]],["t/393",[1,3.529,2,1.638,7,0.954,17,2.4,24,0.983,28,0.857,31,2.422,84,0.927,98,2.134,106,2.225,109,1.024,115,4.564,123,2.917,135,3.461,174,2.544,233,4.107,278,2.176,298,3.709,313,1.483,316,2.706,336,2.471,353,1.656,361,4.089,363,4.333,381,2.873,397,3.558,408,2.192,430,2.995,444,2.632,484,2.573,492,4.75,527,2.353,550,2.386,568,2.451,572,3.216,578,2.386,605,2.198,611,3.558,623,2.908,655,3.969,658,2.908,712,3.267,715,3.121,829,3.199,849,4.589,850,3.969,854,3.232,856,3.627,874,2.982,916,3.529,1218,4.819,1394,4.048,1396,5.762,1439,5.346,1696,3.863,1792,3.798,1801,3.461,2486,4.048,2684,3.738,2726,4.333,2727,5.531,2910,4.75,3023,6.276]],["t/395",[2061,3.73]],["t/397",[3,5.964,7,0.55,8,3.752,10,3.773,11,5.562,21,1.971,22,4.327,23,2.328,24,1.129,26,2.589,27,2.236,28,1.329,31,1.704,46,2.89,56,0.689,84,0.755,103,2.334,105,2.182,109,1.176,121,4.081,123,2.377,126,3.368,140,2.733,165,1.562,171,1.895,174,2.073,197,3.108,213,2.838,220,4.293,221,2.576,232,2.537,292,3.034,305,4.437,315,3.313,346,5.271,353,1.902,392,4.327,434,6.278,491,2.102,500,3.3,555,3.3,571,7.934,583,4.26,590,3.752,591,3.976,592,4.476,611,2.899,622,3.518,637,4.476,657,5.456,941,5.114,1137,4.801,1476,2.563,1504,4.915,1754,2.964,2652,6.14,3024,6.619,3025,5.456]],["t/399",[4,2.151,7,0.547,16,1.29,23,3.132,24,1.986,31,2.292,35,4.828,36,4.136,56,0.926,84,0.751,86,4.72,96,2.442,98,3.296,100,3.1,108,3.581,123,2.363,131,4.255,142,6.315,143,9.275,145,2.788,165,1.553,171,1.884,174,2.061,188,6.783,198,3.376,205,6.315,207,3.829,228,2.196,232,3.412,337,3.229,353,1.891,377,2.098,400,7.338,489,4.622,500,4.438,545,3.205,571,5.159,662,4.029,940,2.594,1028,4.576,1827,5.928,3025,5.424,3026,6.58,3027,6.58]],["t/401",[34,2.527,84,1.075,92,3.675,93,4.992,229,5.887,252,8.27,390,4.792,599,5.101,619,6.369,620,5.49,942,4.191,998,6.369]],["t/403",[34,2.761,415,7.64,1066,5.347]],["t/405",[34,2.761,415,7.64,1066,5.347]],["t/407",[12,3.15,27,2.41,28,1.591,84,1.222,107,2.599,109,2.013,172,3.907,173,4.179,174,2.321,272,3.197,304,1.646,318,1.902,468,4.968,495,3.771,502,2.154,557,3.886,564,5.058,596,3.518,607,2.993,612,5.058,747,3.207,819,5.504,829,5.348,830,3.569,841,3.61,877,4.354,1277,6.876,1352,3.756,1953,4.248,2806,5.648]],["t/409",[4,1.496,5,1.699,7,0.685,16,0.898,21,2.455,24,0.781,25,1.545,26,3.585,27,1.718,28,1.226,40,2.541,56,0.858,84,1.046,98,3.053,106,1.768,107,1.853,109,2.068,110,3.814,111,7.587,116,1.8,126,2.329,132,1.417,133,2.064,164,2.387,165,1.622,169,3.959,170,2.139,183,2.07,186,2.273,189,3.874,256,1.805,272,1.975,289,1.906,316,2.15,318,1.175,353,2.369,365,2.444,372,1.805,377,1.079,382,2.993,407,2.181,416,2.221,432,3.042,439,3.153,440,2.127,447,2.282,458,2.716,527,2.262,555,2.282,565,1.575,568,2.073,578,1.896,593,3.57,606,3.707,613,3.927,615,2.822,630,2.674,635,1.75,679,5.663,809,3.32,814,2.077,818,4.363,862,2.4,933,5.308,937,3.32,962,5.98,1111,4.732,1134,4.02,1163,2.924,1276,3.59,1279,6.032,1439,7.649,1831,3.847,1892,4.247,2016,3.646,2043,3.4,3028,4.578,3029,4.578]],["t/411",[4,1.595,5,1.811,7,0.711,15,1.875,16,0.957,27,2.803,28,1.569,56,0.891,98,1.807,105,1.608,107,1.316,109,1.68,113,1.875,127,2.489,132,1.51,165,2.021,171,1.397,172,3.126,173,2.751,182,1.72,199,2.536,228,2.404,236,1.996,257,2.617,281,2.432,289,3,292,2.236,302,2.367,320,2.949,338,1.265,343,1.829,351,1.861,365,3.846,366,3.216,369,3.19,437,2.413,445,3.242,455,6.181,458,2.895,459,4.022,490,3.623,491,1.55,502,1.418,525,2.423,550,2.02,552,1.82,568,2.174,581,5.489,604,3.393,617,4.1,633,4.022,635,2.754,636,2.895,684,2.681,734,2.275,757,1.267,798,2.828,801,3.825,815,4.526,831,3.242,851,2.695,878,2.192,906,3.116,909,3.539,933,3.77,953,3.07,1006,4.615,1041,3.27,1067,4.022,1216,2.617,1245,3.297,1290,2.86,1445,3.58,2101,4.526,2516,5.937,3008,3.825,3030,4.879,3031,4.879,3032,4.284,3033,6.053,3034,4.879,3035,4.879,3036,4.284]],["t/413",[2,1.672,7,0.79,17,2.45,25,2.781,28,1.614,56,1.073,84,1.177,88,4.515,89,4.054,109,1.046,128,2.957,133,2.653,162,2.179,164,1.703,175,3.391,275,1.919,313,1.515,315,2.945,377,1.387,405,1.973,407,2.803,440,2.734,472,4.881,476,2.521,483,3.911,491,1.869,505,2.57,512,4.282,558,2.773,575,5.781,605,2.244,698,4.484,726,4.053,887,3.911,912,4.317,1069,4.698,1366,5.578,1616,3.45,1754,2.635,1815,9.131,2783,5.302,3037,5.302,3038,5.648,3039,5.459,3040,5.302]],["t/415",[7,0.751,84,1.166,92,2.629,98,2.495,109,1.607,110,3.741,116,2.65,165,1.591,174,3.198,186,3.346,216,3.615,228,3.02,229,4.212,233,2.57,242,5.004,317,3.549,318,1.729,351,2.57,364,5.004,372,2.657,377,2.132,390,3.429,426,2.623,458,3.999,527,2.515,580,2.466,591,4.048,613,3.21,630,3.521,637,4.557,658,5.745,693,4.371,798,3.906,818,3.566,996,4.479,997,3.53,1163,4.305,1218,5.368,1444,4.734,1916,4.242,2098,5.918,3041,6.072]],["t/417",[2,2.449,5,3.198,23,3.031,24,1.47,26,3.371,158,4.538,220,5.589,246,3.433,247,3.99,276,4.498,351,3.286,365,4.601,401,3.47,408,3.278,418,4.538,420,8.27,458,5.112,466,6.399,534,6.48,628,4.689,828,3.786,958,5.935,1036,5.992,2105,8.27]],["t/419",[7,0.769,19,4.349,28,1.377,84,1.057,138,3.936,197,4.349,225,3.488,316,4.349,338,2.401,441,5.308,536,2.906,857,4.427,942,4.121,1066,4.814,1506,6.263]],["t/421",[7,0.912,15,3.008,16,1.535,23,2.754,84,0.894,144,3.951,219,3.255,232,3.825,256,3.087,275,3.255,283,2.385,351,2.986,491,2.487,545,3.813,548,5.078,552,2.92,583,5.039,615,4.826,852,3.576,942,3.484,948,5.392,1202,4.346,1848,9.054,1924,6.718,2225,4.391,2297,2.748,2395,4.324,3042,4.826,3043,7.829,3044,7.829]],["t/423",[17,3.857,23,3.258,24,1.58,84,1.057,98,3.429,160,3.421,161,6.643,219,3.021,232,3.55,275,3.021,530,4.364,607,3.74,877,3.559,927,7.376,1848,7.634]],["t/425",[5,2.074,7,0.464,14,4.382,16,1.096,25,3.399,27,2.52,28,1.645,29,2.055,31,2.381,34,1.5,84,0.638,96,2.074,105,1.842,123,3.322,158,2.944,163,2.334,164,1.618,181,3.336,200,5.26,245,5.003,246,2.227,248,2.764,256,2.204,257,2.998,281,2.786,313,1.439,318,1.434,332,3.056,377,1.317,437,3.931,440,2.597,491,1.775,530,2.634,562,2.892,565,1.923,635,2.137,658,2.82,740,5.07,810,2.615,830,2.692,904,6.067,938,4.607,1137,4.054,1171,4.696,1280,3.966,1281,6.182,1328,5.003,1379,4.526,1457,4.203,1459,4.318,1913,5.036,2100,4.259,2671,4.796,2680,3.746,2691,4.607,2719,6.98,3045,6.346]],["t/427",[7,0.897,25,3,27,2.702,28,1.73,109,1.58,272,3.836,281,4.433,318,2.281,343,3.333,565,3.059,568,2.683,596,4.22,635,3.399,1813,6.87,2719,7.808,3046,8.011]],["t/429",[2,1.211,7,0.833,12,1.812,16,1.276,21,1.269,23,2.29,27,2.38,28,1.314,40,2.366,56,0.677,84,1.009,103,1.503,109,1.691,110,2.366,113,1.638,127,3.811,131,2.038,133,1.922,153,2.298,155,2.909,162,2.923,164,1.234,165,1.864,171,1.22,174,2.038,193,1.634,197,2.001,219,2.882,221,1.659,225,1.605,228,2.635,232,1.634,248,2.108,298,2.743,313,1.097,318,1.67,338,1.105,343,1.598,375,2.61,377,1.534,393,2.206,440,1.981,472,2.187,491,1.354,502,2.294,524,2.765,527,1.499,536,2.042,538,3.743,544,2.809,550,1.765,557,2.235,558,2.009,565,3.871,567,2.75,568,2.872,577,3.245,578,1.765,580,1.56,593,2.215,601,2.108,605,1.625,614,0.827,630,1.659,635,1.63,670,2.61,690,2.787,747,1.844,756,3.219,757,2.294,796,4.361,810,1.994,931,3.451,999,2.443,1033,2.857,1114,2.038,1280,3.025,1314,4.668,1504,3.165,1558,2.529,1754,2.915,1822,2.703,1944,3.743,1990,3.092,2017,3.84,2064,3.205,3047,3.743]],["t/431",[2,1.664,5,2.173,17,2.438,26,3.213,31,1.507,34,1.571,41,2.798,56,0.855,84,0.937,85,2.137,101,3.283,103,2.064,107,2.922,109,2.049,125,3.516,129,3.685,170,2.557,174,1.833,200,3.056,216,3.14,228,2.741,236,1.622,257,3.14,261,4.385,278,2.21,302,2.84,353,1.682,369,3.827,398,2.173,408,3.124,444,2.674,527,2.587,536,2.577,553,3.266,565,2.014,601,2.895,607,3.317,630,2.278,663,3.767,693,3.796,757,1.52,810,2.739,839,3.538,999,3.355,1170,3.069,1245,3.308,1257,3.585,1536,4.523,1558,3.473,1754,2.622,1918,3.069,3046,5.274,3048,5.853,3049,5.853,3050,5.853,3051,4.154,3052,5.853]],["t/433",[4,2.423,27,2.41,28,1.102,31,2.48,84,1.1,96,2.751,109,1.902,123,3.459,125,4.452,138,3.15,164,2.789,171,2.758,174,3.017,175,4.272,179,2.613,278,2.799,297,4.272,347,5.155,377,1.747,416,3.596,544,4.885,568,2.237,607,2.993,614,2.076,630,2.885,635,2.834,818,3.922,934,3.725,1382,6.064,1504,5.504,1870,3.533,3053,5.26]],["t/435",[5,2.926,7,0.833,24,1.88,34,2.116,56,0.82,107,2.127,127,2.725,158,4.153,174,2.469,228,2.632,236,2.185,283,2.402,295,3.341,306,3.689,338,2.044,502,2.291,536,3.146,550,3.265,558,3.715,560,4.422,577,4.997,827,4.21,830,3.797,874,4.08,928,6.923,946,3.825,1302,5.38,1328,4.963,2429,7.443,3054,7.314]],["t/437",[2,1.635,5,2.649,7,0.848,16,1.128,17,1.514,18,1.684,19,1.707,20,2.396,21,1.083,22,2.377,24,1.679,26,2.25,28,1.463,31,1.481,33,2.184,41,1.738,46,1.588,56,0.742,84,0.657,85,1.327,90,1.806,92,1.419,98,1.346,103,1.282,125,3.455,132,2.209,138,1.545,140,1.501,145,1.541,164,1.665,165,2.086,168,3.193,179,1.282,181,2.171,183,1.645,189,2.05,220,2.358,225,1.37,226,1.858,243,2.095,244,2.358,245,4.492,255,2.58,257,1.951,280,2.437,283,1.753,285,1.583,289,2.395,292,1.667,320,2.198,339,1.933,343,3.312,353,1.045,360,2.734,366,2.396,376,2.184,386,2.851,408,1.383,430,1.89,444,1.661,468,2.437,498,3.372,502,1.057,505,1.588,509,2.171,527,1.644,536,1.141,550,3.891,565,2.791,566,2.668,574,3.49,593,1.89,596,1.726,622,1.933,628,1.979,630,2.238,631,2.504,632,2.061,636,3.413,672,2.257,686,2.997,719,2.305,732,2.504,747,1.573,759,1.343,770,2.944,796,2.008,807,4.47,814,1.65,816,2.171,820,1.969,826,3.193,830,2.77,832,2.58,841,1.771,856,2.289,904,2.072,909,4.172,939,2.358,946,1.764,1028,2.529,1066,2.99,1113,2.944,1180,2.417,1218,2.157,1240,2.184,1245,1.268,1278,2.554,1290,2.132,1300,2.504,1310,2.734,1355,3.49,1391,3.373,1798,2.132,1907,1.979,2232,2.7,2429,5.299,2486,2.554,2717,3.962,3032,3.193,3055,3.12,3056,3.12,3057,2.771,3058,3.832,3059,3.636,3060,3.636,3061,3.12,3062,3.373]],["t/439",[5,2.751,21,2.207,28,1.591,31,1.908,56,1.002,84,0.846,89,2.915,105,2.443,107,2.599,109,1.317,140,3.06,144,3.74,145,3.141,165,1.75,179,2.613,199,3.852,242,5.504,257,3.976,337,3.638,507,4.248,536,2.326,550,4.432,565,2.55,580,3.525,630,2.885,633,6.11,807,3.923,828,3.257,830,3.569,908,6.678,953,3.159,1047,4.666,1352,3.756,1382,4.666,1798,4.346,2429,7.154]],["t/441",[2,1.43,5,2.736,7,0.418,26,2.883,27,2.181,28,1.296,31,1.296,84,0.574,85,1.837,92,1.964,93,2.247,98,2.729,103,1.775,106,2.846,107,2.879,109,1.897,115,4.657,123,1.807,138,2.139,163,2.102,164,2.133,165,1.188,174,1.576,179,1.775,182,1.775,233,1.919,236,2.042,242,3.738,254,3.252,257,2.7,272,3.179,278,1.9,289,2.096,302,2.442,316,2.363,317,2.651,318,1.291,322,1.659,324,2.809,341,2.917,353,1.446,385,4.669,416,2.442,430,2.616,444,2.299,458,2.986,468,3.374,489,3.535,490,3.738,502,1.462,522,3.946,524,3.264,527,1.698,550,3.052,576,3.124,611,2.205,613,2.397,658,2.54,693,3.264,709,3.467,737,4.008,755,2.389,774,2.869,798,2.917,893,3.062,997,3.402,1037,2.022,1153,4.535,1218,4.373,1329,4.008,1381,2.102,1382,5.488,1388,4.008,1392,4.319,1474,4.076,1922,4.42,3010,5.286,3063,4.831,3064,5.033,3065,5.033,3066,5.033]],["t/443",[306,4.814,1275,6.957,1557,6.726]],["t/445",[2061,3.73]],["t/447",[7,0.624,16,1.473,18,4.5,24,1.838,28,1.117,34,2.016,56,1.011,84,0.857,98,2.782,103,2.649,140,3.102,160,3.59,162,2.782,164,2.175,165,1.773,203,1.895,225,2.829,248,3.715,254,2.545,261,4.011,285,4.231,338,1.947,353,2.159,377,1.77,512,2.729,536,2.357,759,3.979,760,2.339,1033,5.035,1114,3.591,1235,5.051,1236,7.21,3067,7.512,3068,7.512]],["t/449",[16,1.981,24,1.724,116,3.972,807,4.113,2456,9.101]],["t/451",[7,0.763,16,2.16,24,1.88,28,1.638,165,2.168,289,3.825,830,4.423,832,6.518,877,3.529,907,6.211,1446,5.663,1798,5.385,1819,6.211]],["t/453",[24,1.567,28,1.366,203,2.317,323,3.904,338,2.381,377,2.165,550,3.803,807,3.74,1223,5.483,2254,8.065,3069,9.185,3070,8.521,3071,9.185,3072,9.185,3073,8.521,3074,9.185]],["t/455",[2,1.958,7,0.687,12,1.954,16,1.621,20,3.03,27,2.068,28,1.535,49,1.089,56,0.86,96,2.558,107,2.786,109,1.835,127,2.381,138,1.954,153,3.714,164,1.331,165,1.085,170,2.145,173,5.823,174,2.158,193,3.77,197,2.158,217,2.761,224,2.052,236,1.909,256,1.812,257,2.466,261,2.455,272,1.983,289,2.87,313,1.774,318,1.768,322,1.515,323,3.513,353,1.321,394,2.421,397,2.014,405,1.541,407,3.282,418,2.421,426,1.789,432,4.58,433,2.797,437,2.274,484,2.052,485,3.661,491,1.46,507,2.634,550,4.073,565,2.371,568,2.494,632,5.205,635,1.757,647,1.817,743,4.792,807,1.872,812,4.84,829,2.552,851,2.539,877,1.766,878,2.065,907,3.109,916,2.815,986,3.806,995,3.297,1037,1.846,1047,2.894,1122,3.03,1302,3.137,1352,2.329,1376,2.421,1915,3.944,2033,2.727,2282,3.229,2653,3.863,3075,4.597,3076,4.142,3077,4.142]],["t/457",[2,2.149,16,2.119,18,3.502,28,1.451,56,0.787,96,2.807,97,3.564,105,2.493,107,2.633,132,3.345,145,3.205,170,3.364,193,3.741,313,1.947,318,1.94,338,1.96,405,2.535,507,4.334,550,4.728,596,3.589,862,3.966,1047,4.761,1122,4.985,1285,4.487,1290,4.434,1302,5.161,1351,7.259,1352,3.832,3076,6.814,3077,6.814]],["t/459",[16,1.983,23,2.813,24,1.365,25,2.698,28,1.504,49,1.894,56,1.052,109,1.421,132,2.475,228,2.67,297,4.609,313,2.059,398,2.969,476,2.446,491,2.54,502,2.939,536,2.51,545,3.895,577,5.812,580,2.926,1037,3.213,1235,4.157,1332,5.147,3078,9.383,3079,7.419,3080,7.419]],["t/461",[0,3.822,12,2.67,28,0.934,49,1.488,56,1.103,107,1.695,109,1.75,112,5.801,172,4.056,193,4.259,217,3.774,391,2.787,502,1.826,525,3.12,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,953,4.199,954,4.413,959,6.059,986,3.47,988,3.705,1009,4.779,1235,5.511,1331,7.374,1332,6.824,1367,3.21,3081,5.829,3082,9.138,3083,5.829,3084,5.829,3085,5.829]],["t/463",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,997,4.614,1235,4.799,1245,2.423,1331,7.685,1332,5.943,1381,3.856,1389,5.771,3086,6.447,3087,6.447]],["t/465",[2,1.317,7,0.917,19,2.176,23,1.631,24,1.573,28,1.235,31,1.193,34,1.244,46,3.028,56,0.959,92,4.042,93,3.709,101,2.6,105,1.528,107,2.241,109,1.476,123,1.664,127,2.396,162,1.716,182,1.634,207,1.993,216,2.487,217,2.784,219,1.512,221,1.804,225,1.746,229,2.897,232,1.776,236,1.921,276,2.42,280,3.107,283,1.412,294,3.931,306,2.169,313,1.193,315,2.32,318,1.189,323,3.53,361,3.289,390,2.358,407,2.208,476,2.541,491,1.472,527,1.068,528,3.849,546,4.07,552,1.729,555,2.311,562,2.399,566,3.401,590,2.628,597,3.773,599,3.755,601,2.293,613,2.208,614,0.899,623,2.339,629,3.107,647,1.832,658,2.339,677,2.573,691,2.686,693,3.006,694,2.656,707,2.718,734,2.161,814,3.146,827,2.475,828,2.037,885,2.784,947,3.72,948,3.193,1132,3.401,1212,3.223,1400,6.655,1469,4.176,1583,3.895,1879,4.3,2369,4.635,2521,4.884,2652,4.3,2754,3.895,2778,4.3,2787,3.107,2891,4.884,3018,4.449,3088,5.263,3089,3.977,3090,4.3,3091,5.263,3092,4.3,3093,4.449,3094,5.263,3095,5.263,3096,5.263]],["t/467",[7,0.9,21,1.299,23,1.535,24,1.894,26,1.707,27,1.657,28,1.33,30,2.875,31,1.123,40,2.422,46,4.421,56,0.454,84,0.914,85,1.592,90,2.166,92,3.125,96,2.459,97,2.056,103,1.538,107,2.413,109,1.926,115,2.117,127,1.508,154,4.363,170,1.358,171,1.249,172,2.418,179,1.538,183,1.973,193,2.539,225,1.643,236,1.209,254,2.245,256,1.72,282,2.5,372,1.72,395,1.817,437,3.277,447,2.175,455,3.744,476,2.45,491,1.386,525,2.166,552,2.987,576,2.708,597,3.605,599,4.843,602,4.047,604,3.034,611,1.911,632,2.473,638,2.766,658,2.201,671,4.187,694,2.5,707,3.884,769,2.683,814,3.006,830,3.19,840,3.596,857,2.086,862,2.288,878,1.96,936,2.986,947,3.554,953,1.86,986,2.41,997,3.752,1164,2.319,1289,1.86,1303,2.746,1360,2.621,1362,3.324,1379,3.533,1399,4.112,1400,6.358,1429,5.276,1430,4.597,1467,3.596,1845,2.727,1879,4.047,1906,2.5,2118,3.24,2527,3.831,3004,3.831,3097,4.954]],["t/469",[7,0.412,10,2.83,15,1.908,24,1.627,31,1.278,56,1.271,92,3.721,93,2.217,108,2.702,131,3.489,155,3.389,181,2.964,182,1.751,191,3.784,201,2.664,203,1.253,225,1.87,300,3.172,308,5.233,309,5.233,312,7.27,315,2.486,322,1.637,323,2.11,361,3.524,381,2.476,392,3.246,395,3.039,405,1.665,421,3.273,426,1.933,483,3.3,491,1.577,553,2.771,562,4.477,571,3.894,600,3.488,607,2.006,635,1.898,677,2.757,690,3.246,694,2.846,735,7.368,757,1.289,763,3.562,778,3.3,809,3.602,965,3.3,971,4.891,979,3.453,1017,4.474,1019,4.474,1020,4.474,1247,5.354,1435,3.524,1473,2.332,1664,3.837,1891,3.894,2225,2.785,3098,5.639,3099,5.639,3100,5.639,3101,5.639,3102,5.639,3103,4.766,3104,5.233,3105,5.639,3106,6.015,3107,5.639,3108,4.173,3109,5.233,3110,5.233,3111,4.173,3112,5.233,3113,4.766,3114,4.766,3115,4.766,3116,4.766,3117,4.766,3118,4.766,3119,5.639,3120,5.639,3121,4.966,3122,5.639,3123,4.766,3124,4.766,3125,4.766,3126,4.766]],["t/471",[0,1.919,2,0.506,7,0.353,10,2.419,15,1.212,16,0.618,17,0.742,19,0.836,21,1.528,24,1.001,25,1.979,26,0.697,28,0.469,29,0.655,31,0.812,32,2.078,34,0.478,36,2.384,39,2.2,42,1.376,48,0.761,50,1.944,56,1.324,84,0.74,90,0.884,92,3.44,95,2.062,96,0.661,98,1.168,100,0.839,106,0.688,108,0.969,109,0.316,115,0.864,123,0.64,124,1.184,126,0.906,140,1.302,153,0.96,155,1.215,157,1.083,164,0.516,165,1.002,175,2.957,176,1.528,177,2.159,183,0.805,184,1.418,186,2.548,191,3.91,197,1.481,200,0.93,203,0.795,204,3.642,213,0.764,216,0.955,220,1.155,221,1.227,225,0.671,236,0.493,262,1.227,275,0.581,278,0.672,283,0.543,304,0.395,311,1.528,312,3.566,313,0.812,315,1.578,322,0.587,339,1.676,343,0.667,353,0.906,370,1.468,381,1.572,384,1.564,395,1.313,398,1.171,405,1.423,408,0.677,413,2.511,421,1.174,426,1.652,430,0.926,432,1.184,440,1.465,446,1.194,477,1.113,483,2.096,485,1.418,491,1.63,506,1.971,508,1.105,511,1.059,553,1.76,566,1.307,568,0.537,569,1.605,589,1.709,590,1.01,593,2.667,596,1.497,600,1.251,612,1.215,628,0.969,630,0.693,650,1.264,653,0.591,664,1.376,677,0.989,698,1.357,702,2.238,703,1.496,710,7.473,723,3.827,725,0.955,735,5.775,750,1.322,757,1.332,758,1.564,773,1.468,778,1.184,779,3.566,780,0.999,784,2.437,787,2.706,790,1.307,796,0.984,824,1.376,828,0.783,877,0.684,878,0.8,936,1.422,937,1.292,965,2.096,971,4.703,979,1.239,1009,0.864,1017,4.623,1019,2.841,1020,1.605,1051,1.442,1055,1.239,1108,4.163,1235,0.926,1247,3.114,1320,2.114,1367,2.621,1373,1.146,1431,2.65,1435,1.264,1465,1.357,1473,0.836,1474,1.442,1635,2.769,1664,2.437,1779,1.264,2102,2.841,2167,1.877,2283,2.314,2486,3.604,2520,1.781,2522,1.442,2523,1.877,2550,2.403,2695,1.709,2716,6.836,2778,1.652,2782,1.239,2873,1.528,3103,1.709,3104,3.323,3106,4.229,3108,1.496,3109,1.877,3110,1.877,3111,1.496,3112,1.877,3113,1.709,3114,1.709,3115,1.709,3116,1.709,3117,1.709,3118,1.709,3121,1.781,3123,1.709,3124,1.709,3125,3.027,3126,1.709,3127,1.877,3128,2.022,3129,1.709,3130,2.022,3131,3.581,3132,3.581,3133,2.769,3134,2.022,3135,2.022,3136,3.581,3137,5.407,3138,2.022,3139,5.826,3140,3.581,3141,2.022,3142,3.581,3143,2.022,3144,3.581,3145,3.581,3146,2.022,3147,2.022,3148,6.182,3149,3.581,3150,4.819,3151,2.022,3152,1.564,3153,2.022,3154,1.564,3155,2.022,3156,3.581,3157,2.022,3158,1.468,3159,2.022,3160,3.581,3161,3.581,3162,2.022,3163,2.022,3164,2.022,3165,2.022,3166,2.022,3167,2.022,3168,3.581,3169,2.022,3170,2.022,3171,2.022,3172,1.781,3173,2.022]],["t/473",[2,1.722,4,0.739,7,0.503,8,2.895,16,1.001,19,3.497,21,1.521,24,1.436,25,2.302,28,0.336,29,0.832,49,0.92,50,1.395,56,0.775,84,0.91,85,0.826,89,0.89,90,1.123,92,4.567,94,1.289,97,1.831,100,2.406,105,0.746,108,2.114,112,2.028,113,0.869,115,1.098,132,0.7,162,0.838,164,1.754,165,0.534,175,1.304,177,1.151,179,0.798,182,1.37,186,1.929,192,1.404,197,1.824,198,2.62,207,1.671,213,0.97,216,1.214,228,0.755,236,0.627,246,3.749,257,1.214,275,2.225,279,1.03,282,1.296,283,1.846,292,1.037,294,1.282,306,2.389,312,6.259,313,1.315,316,1.062,318,0.58,323,1.651,337,1.11,339,1.202,340,1.589,343,1.914,348,1.53,353,1.96,359,1.051,377,0.533,380,2.262,386,1.774,388,1.724,391,1.723,405,0.758,407,1.077,422,1.623,430,2.019,437,1.119,438,2.125,440,1.051,441,1.296,443,6.247,478,3.262,480,1.623,482,1.066,500,1.937,504,1.445,511,1.305,522,1.774,526,1.724,536,1.219,541,1.467,560,2.179,568,0.683,578,0.937,605,0.863,614,1.175,622,1.202,651,1.589,653,0.75,670,1.385,680,1.865,699,2.262,702,1.605,710,4.209,714,5.006,715,4.034,723,2.805,735,3.002,756,1.119,757,1.326,796,1.249,833,4.5,851,1.249,899,1.641,900,1.558,920,1.385,933,1.748,947,1.214,955,1.865,971,1.516,1009,1.098,1029,4.948,1035,5.321,1037,0.909,1046,2.985,1051,3.146,1110,5.272,1115,3.334,1129,1.491,1170,0.732,1240,1.359,1266,1.741,1284,1.166,1285,1.342,1371,4.135,1399,3.761,1408,1.058,1417,1.573,1428,3.604,1434,1.326,1472,2.384,1473,1.824,1687,1.986,1799,1.641,1827,2.038,1864,1.256,1933,1.589,2092,1.334,2100,2.961,2372,2.729,2497,3.203,2636,3.087,2655,2.262,2668,1.68,2874,2.099,2964,2.384,3033,1.901,3040,2.038,3111,1.901,3127,2.384,3174,1.986,3175,2.038,3176,1.986,3177,1.986,3178,5.816,3179,3.729,3180,3.729,3181,3.729,3182,3.412,3183,2.171,3184,2.171,3185,2.171,3186,2.171,3187,2.171,3188,2.171,3189,2.171,3190,2.171,3191,2.171,3192,2.171,3193,4.412,3194,4.094,3195,2.569,3196,2.569,3197,2.569,3198,2.569,3199,2.038,3200,2.384,3201,2.384,3202,2.569,3203,2.569,3204,3.729,3205,2.569]],["t/475",[2,0.68,5,0.487,7,0.443,8,1.873,10,1.365,14,1.028,15,0.504,17,1.698,18,0.607,19,2.229,21,0.713,24,0.996,26,0.513,27,1.02,28,0.935,29,0.482,31,0.338,32,0.864,33,0.788,34,0.352,46,1.046,56,1.195,84,0.764,92,3.514,93,0.585,96,1.226,100,1.922,105,1.756,106,0.925,107,0.891,108,1.798,109,1.038,110,0.728,112,1.25,127,1.64,128,0.659,129,0.825,132,0.406,134,0.688,135,1.438,140,0.541,156,0.94,160,1.753,162,1.223,163,0.548,165,0.565,172,0.874,177,0.667,179,0.462,181,0.783,182,1.673,185,2.396,186,0.651,189,0.739,190,0.71,191,4.06,193,0.918,194,1.062,196,1,197,1.124,199,1.244,200,0.684,213,1.027,221,0.932,224,0.585,225,3.668,228,1.103,236,1.315,241,1.081,243,0.756,246,1.625,247,0.607,254,1.119,275,0.428,277,0.71,278,1.247,279,0.597,281,0.654,282,0.751,289,0.997,292,0.601,294,1.357,297,0.756,300,0.837,311,1.125,312,5.93,313,1.372,315,2.041,316,0.616,317,0.691,323,2.481,338,0.34,339,1.756,348,1.619,353,0.949,359,1.535,360,0.986,367,0.703,372,0.517,375,1.466,377,0.564,380,1.311,381,1.194,395,0.546,397,0.574,398,0.487,407,0.625,416,1.162,418,1.261,437,0.649,440,0.609,446,0.879,474,1.125,476,0.732,479,1.757,482,0.618,483,5.986,485,1.044,489,1.682,491,0.416,495,1.218,498,0.769,500,1.194,502,0.381,511,0.44,513,1.189,524,0.85,525,0.651,531,2.605,532,1.316,541,2.143,552,0.893,555,0.654,568,0.396,572,0.732,576,0.814,593,0.681,596,0.622,597,1.303,599,0.71,600,2.321,607,0.53,613,0.625,614,0.254,628,0.714,632,0.743,636,0.778,647,0.518,653,1.096,658,0.662,662,0.803,669,1.125,690,0.857,694,0.751,702,0.93,707,0.769,710,5.186,715,1.789,716,1.216,723,1.536,726,0.903,734,0.611,735,2.553,752,1.125,755,0.622,757,0.622,762,0.93,767,1.062,803,0.778,806,1.081,809,0.951,813,0.895,814,1.851,820,0.71,830,0.631,833,0.974,863,1.939,878,1.484,882,1.507,885,0.788,930,0.756,939,0.85,965,0.871,993,0.798,997,0.934,999,1.372,1001,1.062,1002,1.102,1012,0.783,1017,2.157,1019,2.157,1020,2.977,1036,0.912,1037,1.327,1055,0.912,1076,1.939,1087,1.907,1101,1.044,1110,3.667,1114,0.627,1115,1.125,1164,0.697,1169,0.879,1200,1.311,1201,0.921,1202,0.728,1220,1.151,1246,1.181,1247,1.757,1266,0.688,1274,1.541,1289,0.559,1310,0.986,1331,0.912,1367,4.687,1399,2.051,1400,1.258,1416,0.986,1429,1.044,1432,1.125,1445,0.962,1473,1.124,1527,0.912,1605,1.013,1642,1.181,1664,1.013,1688,0.94,1691,1.216,1779,0.93,1789,1.125,1803,1.013,1825,1.181,1827,1.181,1894,1.216,1906,0.751,1990,0.951,2061,0.467,2062,1.258,2141,1.081,2225,0.735,2486,1.682,2522,1.062,2636,0.793,2666,1.258,2676,1.382,2699,1.311,2739,1.181,2967,2.523,3008,1.028,3051,0.93,3103,1.258,3106,2.723,3113,1.258,3114,1.258,3115,1.258,3116,1.258,3117,1.258,3118,1.258,3123,1.258,3124,1.258,3125,1.258,3126,1.258,3172,2.394,3194,1.382,3206,7.951,3207,1.489,3208,1.489,3209,1.489,3210,1.489,3211,2.523,3212,3.481,3213,1.489,3214,1.489,3215,1.489,3216,1.489,3217,1.489,3218,1.311,3219,1.489,3220,1.489,3221,1.489,3222,1.382,3223,1.489,3224,1.489,3225,1.489,3226,1.489,3227,1.489,3228,1.489,3229,1.382,3230,1.382,3231,1.382,3232,1.382,3233,1.489,3234,1.489,3235,1.489,3236,1.489,3237,1.489,3238,1.489,3239,1.489,3240,1.489,3241,1.489,3242,1.382,3243,1.489,3244,1.489,3245,1.489,3246,1.489,3247,1.489,3248,1.489,3249,1.258,3250,1.382,3251,1.489,3252,2.298,3253,3.171,3254,1.311,3255,1.489,3256,1.489,3257,7.631,3258,2.523,3259,1.382,3260,1.382,3261,1.489,3262,3.304,3263,2.523,3264,1.125,3265,1.489,3266,2.523,3267,1.382,3268,1.489,3269,1.489,3270,1.489,3271,1.382,3272,1.489,3273,1.489,3274,1.382,3275,2.523,3276,1.382,3277,1.489,3278,1.489,3279,1.311,3280,1.382,3281,1.489,3282,1.489,3283,1.382,3284,1.489,3285,1.489,3286,1.382,3287,1.489,3288,1.489,3289,2.298,3290,1.382,3291,1.489,3292,1.489,3293,1.382,3294,1.489,3295,1.382,3296,1.489,3297,1.489,3298,2.523,3299,1.489,3300,1.489,3301,1.382,3302,1.489,3303,1.489,3304,1.489,3305,1.489,3306,1.489,3307,1.382,3308,1.489,3309,1.489,3310,1.382,3311,2.977,3312,1.216,3313,1.489,3314,1.489,3315,1.216]],["t/477",[2,0.422,7,0.305,8,1.521,9,1.83,15,0.571,17,1.117,19,2.436,21,0.799,22,0.972,24,0.458,25,0.905,28,0.668,31,0.383,32,2.416,34,0.72,46,0.649,50,1.654,56,1.225,84,0.592,85,0.542,92,3.95,93,2.316,98,0.55,100,3.703,103,1.293,105,0.49,108,2.443,109,0.264,112,0.776,127,0.927,131,0.71,132,0.46,140,1.514,144,0.75,158,1.413,160,1.917,163,0.621,165,0.865,177,1.365,182,1.829,183,0.672,185,1.078,186,0.738,191,2.794,193,0.57,197,1.721,198,0.763,199,0.772,200,0.776,201,1.967,206,1.014,207,0.639,221,0.578,225,1.954,227,1.034,228,0.496,231,1.132,236,0.412,243,0.856,247,0.688,272,0.641,275,0.875,277,0.805,281,0.741,282,0.852,283,1.117,295,0.63,298,1.726,304,0.33,306,0.695,312,3.081,313,0.383,315,1.835,317,0.783,323,3.202,337,0.729,338,0.695,339,1.949,343,0.557,348,1.005,351,1.023,353,1.29,359,0.691,377,0.35,381,2.238,383,1.09,391,0.659,395,0.619,407,0.708,408,1.02,418,1.413,432,0.988,433,0.904,440,0.691,446,0.996,447,2.238,471,1.171,475,1.566,477,0.929,482,0.7,483,6.373,504,0.949,505,0.649,506,0.929,511,1.232,531,2.868,541,0.964,555,0.741,562,0.769,568,0.809,576,1.665,579,1.132,582,1.305,593,1.394,596,2.131,597,1.995,602,1.379,608,1.467,611,0.651,612,1.014,621,0.805,628,0.809,629,0.996,653,0.493,659,1.014,662,1.643,669,1.275,677,1.489,684,0.817,694,0.852,710,5.58,714,1.09,715,1.453,723,2.377,734,0.693,735,1.148,740,0.708,757,0.952,787,1.275,796,1.482,809,1.946,813,1.014,820,1.453,821,1.09,830,0.716,841,0.724,853,1.184,856,0.935,857,0.71,863,1.203,877,1.031,893,0.904,909,1.078,924,1.379,947,0.797,965,1.783,971,1.798,1008,0.949,1009,0.721,1017,3.303,1019,3.303,1020,3.303,1028,1.034,1029,1.946,1074,1.184,1076,1.203,1087,1.184,1108,2.602,1110,2.833,1117,0.847,1132,1.09,1138,1.184,1167,1.566,1179,0.964,1191,1.148,1247,1.09,1284,0.766,1310,1.118,1314,1.066,1360,0.893,1367,4.636,1373,0.956,1381,0.621,1386,1.486,1397,2.342,1465,2.044,1467,1.225,1471,1.339,1473,0.698,1491,1.203,1691,1.379,1733,0.935,1779,1.055,1789,1.275,1798,0.871,1807,2.254,1916,1.689,1922,2.356,1945,1.044,1950,1.566,2096,1.339,2636,1.622,2729,1.566,2775,1.566,2787,1.798,2890,1.339,3103,1.426,3113,1.426,3114,1.426,3115,1.426,3116,1.426,3117,1.426,3118,1.426,3121,1.486,3123,1.426,3124,1.426,3125,1.426,3126,1.426,3148,2.827,3204,2.575,3206,3.666,3211,1.566,3212,1.566,3222,1.566,3229,1.566,3230,1.566,3231,1.566,3232,1.566,3242,1.566,3249,1.426,3250,1.566,3252,2.575,3253,3.519,3254,3.666,3257,8.075,3258,1.566,3259,1.566,3260,1.566,3262,1.486,3263,1.566,3264,1.275,3266,1.566,3267,1.566,3271,1.566,3274,1.566,3275,2.827,3276,1.566,3279,1.486,3280,1.566,3283,1.566,3286,1.566,3289,3.519,3290,1.566,3293,1.566,3295,2.827,3298,2.827,3301,1.566,3307,1.566,3310,1.566,3315,8.896,3316,1.688,3317,3.046,3318,3.046,3319,2.575,3320,1.688,3321,1.688,3322,1.688,3323,1.688,3324,1.688,3325,1.688,3326,1.688,3327,1.688,3328,1.688,3329,1.688,3330,1.688,3331,1.688,3332,1.688,3333,1.688,3334,1.688,3335,1.688,3336,1.688,3337,1.688,3338,1.688,3339,3.046,3340,1.688,3341,1.688,3342,1.688,3343,1.688,3344,1.275,3345,1.688,3346,1.566,3347,1.688,3348,1.688,3349,1.688,3350,1.688,3351,1.688,3352,1.688,3353,1.688,3354,1.688,3355,1.688,3356,1.688,3357,1.688,3358,1.566,3359,1.688,3360,1.688,3361,1.688,3362,1.688,3363,1.688,3364,1.688,3365,1.688,3366,1.688,3367,1.688,3368,1.688,3369,1.688,3370,1.688,3371,1.688,3372,1.688,3373,1.688,3374,1.688,3375,1.688,3376,1.566,3377,1.688,3378,1.688,3379,1.688,3380,1.688,3381,1.688,3382,1.688,3383,3.046,3384,1.688,3385,1.566,3386,1.688,3387,3.046,3388,1.688,3389,1.688]],["t/479",[1,0.812,2,0.947,5,1.237,7,0.684,10,1.378,14,1.039,15,1.281,16,0.26,17,2.637,19,1.135,21,0.72,25,1.125,31,0.622,32,3.152,33,0.796,34,0.356,35,0.972,36,2.222,39,2.051,42,1.024,44,2.178,49,0.573,50,6.024,56,1.082,63,1.958,68,1.911,84,0.847,85,0.882,92,3.859,93,1.836,96,0.897,98,1.234,99,0.881,100,2.984,101,1.356,102,3.179,103,0.467,105,1.576,106,0.934,107,0.358,109,0.235,113,0.509,121,2.055,123,0.476,124,0.881,126,1.23,131,3.22,132,1.032,134,1.268,138,0.563,140,1.376,153,2.893,155,1.65,157,2.91,160,1.231,162,1.234,163,0.553,165,0.971,175,1.393,177,0.674,181,1.443,182,1.686,185,1.753,191,5.883,195,1.229,196,1.392,197,1.565,198,0.68,199,0.689,200,0.692,201,1.788,203,0.61,221,0.516,225,1.801,236,0.67,240,0.764,246,0.963,247,0.614,253,0.84,254,0.449,260,1.325,262,0.913,278,0.5,279,0.603,293,2.801,304,0.913,312,5.321,313,1.513,316,1.135,317,1.273,323,2.498,334,1.443,338,0.627,339,1.772,340,0.931,343,0.497,351,1.824,353,1.542,359,1.123,365,0.708,369,0.866,375,0.812,377,0.57,388,1.01,392,0.866,394,0.698,395,1.007,397,0.581,407,0.631,408,1.819,416,1.617,422,3.431,430,1.256,440,1.123,471,1.056,473,5.655,476,0.405,477,0.828,479,0.972,483,3.179,491,0.768,498,1.417,500,0.661,502,0.969,504,2.129,505,0.579,506,3.958,511,0.445,517,1.544,531,1.544,555,0.661,562,0.686,568,1.774,569,1.194,570,1.272,572,2.295,576,1.501,578,1.001,587,1.137,590,0.751,607,0.535,612,0.904,614,0.647,621,0.718,628,0.721,629,0.888,630,0.941,657,1.092,658,0.669,659,0.904,672,0.823,677,2.283,687,1.164,710,4.423,712,0.751,723,1.801,734,1.127,740,0.631,755,0.629,757,1.644,758,2.123,767,1.073,770,1.073,779,3.456,784,1.024,787,2.074,794,1.774,796,2.271,802,0.565,823,1.137,824,1.024,833,0.984,841,0.645,846,0.62,857,1.156,875,2.801,877,0.929,881,1.024,893,0.806,909,0.961,919,0.961,936,0.597,950,0.972,954,0.931,992,0.698,993,0.806,1006,1.797,1037,0.532,1076,1.073,1104,2.548,1108,1.716,1110,2.576,1180,2.215,1191,1.024,1257,1.48,1367,1.235,1381,0.553,1389,0.828,1417,0.922,1447,1.325,1498,0.997,1527,0.922,1531,1.698,1554,1.194,1730,1.137,1779,0.94,1835,2.548,1872,1.325,1891,1.039,1894,1.229,1911,3.224,1915,1.137,1922,1.164,2092,0.782,2321,0.94,2597,1.396,2668,0.984,2717,4.889,2787,0.888,3025,1.092,3093,1.272,3106,1.092,3137,1.396,3158,3.39,3206,4.112,3218,1.325,3249,1.272,3252,2.32,3253,1.272,3254,1.325,3257,3.947,3264,1.137,3289,1.272,3312,3.815,3315,7.413,3358,1.396,3390,1.505,3391,1.194,3392,2.745,3393,1.505,3394,1.505,3395,1.505,3396,1.505,3397,1.505,3398,1.505,3399,1.505,3400,7.19,3401,1.055,3402,1.055,3403,1.505,3404,1.505,3405,1.505,3406,1.505,3407,1.505,3408,1.505,3409,1.505,3410,1.505,3411,1.396,3412,1.092,3413,1.01,3414,1.272,3415,1.505,3416,2.745,3417,3.785,3418,2.417,3419,1.505,3420,1.396,3421,5.431,3422,2.745,3423,1.505,3424,1.505,3425,1.505,3426,1.505,3427,1.505,3428,1.505,3429,1.505,3430,1.505,3431,1.505,3432,1.505,3433,1.505,3434,1.505,3435,1.505,3436,1.505,3437,1.505,3438,1.505,3439,1.505,3440,1.505,3441,1.505,3442,1.505,3443,1.505,3444,1.505,3445,1.505,3446,1.396,3447,1.505,3448,1.505,3449,2.745,3450,1.272,3451,1.505,3452,1.505,3453,1.505,3454,1.505,3455,1.505,3456,2.123,3457,1.505,3458,1.505,3459,1.505,3460,1.505,3461,1.505,3462,1.505,3463,1.505,3464,2.745,3465,2.745,3466,1.505,3467,1.505,3468,1.229,3469,1.505,3470,1.505]],["t/481",[7,0.574,15,1.332,16,1.086,17,2.306,19,1.628,21,1.032,32,5.689,34,0.93,56,1.147,58,2.641,84,0.789,92,2.698,93,1.547,95,2.266,96,1.287,100,1.633,103,1.222,105,2.279,107,2.483,108,1.886,109,0.616,153,1.868,157,3.369,160,2.917,162,1.284,165,1.632,170,1.079,175,3.191,179,1.222,182,2.438,183,1.568,184,2.761,191,2.641,195,3.216,225,1.306,228,1.157,246,1.381,247,1.605,253,2.198,275,1.131,277,1.877,279,1.578,292,1.589,312,7.253,315,1.735,320,2.095,323,2.353,351,1.322,359,1.611,381,1.728,394,1.826,405,1.162,426,1.349,437,1.714,438,1.896,440,1.611,446,4.635,476,1.06,482,2.609,483,6.116,502,1.007,504,2.214,517,2.214,531,3.537,536,1.088,568,1.046,576,3.437,578,2.293,583,2.231,607,2.236,623,1.749,690,2.266,693,2.248,755,1.645,778,3.68,801,2.718,802,1.478,811,2.266,813,4.718,820,1.877,829,1.924,878,1.558,933,2.678,936,1.563,1024,2.323,1050,2.365,1108,2.46,1156,2.974,1193,2.304,1281,1.842,1285,2.057,1315,2.009,1431,2.913,1531,2.435,1625,2.761,1759,2.514,1779,2.46,1801,3.326,2500,6.636,2784,3.044,3051,2.46,3158,2.857,3249,3.327,3252,5.315,3253,3.327,3257,8.285,3262,3.466,3279,3.466,3289,3.327,3315,6.414,3376,3.653,3446,3.653,3471,3.936,3472,3.936,3473,3.936,3474,3.936,3475,3.936,3476,6.288,3477,6.288,3478,3.936,3479,3.327,3480,3.936,3481,2.486,3482,3.936,3483,2.913,3484,3.936,3485,3.936,3486,3.936,3487,3.936,3488,3.936,3489,3.936,3490,3.936,3491,3.936,3492,3.936,3493,3.936,3494,3.936,3495,3.936,3496,3.936,3497,3.936,3498,3.936,3499,3.936,3500,3.936,3501,3.936,3502,3.936,3503,3.936]],["t/483",[5,1.283,7,0.573,9,2.358,10,1.97,16,0.678,19,2.594,21,1.029,23,1.216,26,1.352,29,1.27,31,0.89,32,6.057,46,1.509,50,5.666,56,0.575,84,0.63,92,4.223,93,1.543,101,1.938,105,1.139,106,2.665,126,2.81,127,1.194,130,2.076,131,2.641,133,2.49,138,2.347,153,1.863,156,2.479,157,2.102,160,1.276,165,0.816,175,3.183,183,1.563,185,2.506,186,1.716,191,4.209,200,1.804,207,1.486,221,2.15,225,2.599,226,1.766,227,2.403,243,1.991,247,1.6,275,1.127,280,2.316,293,2.904,294,1.959,296,1.403,306,1.617,312,2.904,315,1.73,318,0.887,320,3.339,323,1.469,339,4.189,351,1.318,353,1.587,359,2.566,360,2.599,367,1.854,375,2.116,376,3.318,379,3.034,381,1.723,398,1.283,407,1.646,416,3.348,422,4.949,430,1.796,437,1.709,476,1.057,477,3.452,501,3.317,502,2.005,504,2.207,505,1.509,506,2.16,511,1.856,527,0.796,538,3.034,555,1.723,557,1.812,568,1.667,571,2.709,591,3.318,611,3.023,623,1.744,628,1.881,637,2.337,653,1.146,670,2.116,677,1.918,683,1.362,703,2.904,710,2.848,716,5.124,723,1.302,732,2.38,770,2.798,796,4.354,799,2.752,802,1.473,805,2.479,811,2.259,820,1.871,821,2.535,825,2.798,840,2.848,841,1.683,882,3.477,897,2.904,939,2.241,1087,2.752,1108,5.594,1110,5.331,1180,2.297,1280,2.452,1372,3.456,1427,2.904,1432,4.74,1473,2.594,1583,2.904,1729,2.904,1911,2.709,1916,3.477,1990,2.506,2108,2.965,2486,2.427,2531,2.904,2739,3.113,2904,5.821,3036,3.034,3092,3.206,3172,3.456,3200,3.641,3201,3.641,3311,3.113,3315,5.124,3385,3.641,3504,3.924,3505,3.206,3506,3.924,3507,3.924,3508,3.034,3509,3.924]],["t/485",[2061,3.73]],["t/487",[7,0.81,16,1.911,34,2.616,164,2.822,261,5.205,484,4.352,532,5.357,747,4.218,3510,9.748]],["t/489",[4,2.559,7,0.829,15,3.008,16,1.535,34,2.101,49,2.363,56,0.815,84,0.894,103,2.76,192,4.859,203,1.975,228,2.613,261,5.328,318,2.56,401,4.019,484,3.495,607,3.162,614,1.519,723,2.949,877,3.008,1363,2.971,1476,3.031,2026,4.22,2027,5.16,2033,4.645,2297,2.748,2600,5.078,3051,5.556,3511,7.829,3512,7.829,3513,7.237]],["t/491",[7,0.937,49,1.965,203,2.092,278,3.132,304,1.842,318,3.031,398,3.842,401,3.341,527,1.911,565,2.854,568,2.503,760,2.583,898,5.288,920,5.08,1116,5.769,1373,5.339,1487,5.827,1918,5.427,2395,4.582,2895,5.38,3514,6.717]],["t/493",[7,0.881,19,3.415,21,1.472,23,2.559,24,1.92,28,1.508,34,2.846,49,1.171,84,1.158,85,1.805,88,2.346,89,2.86,103,3.043,105,1.629,116,1.944,132,2.671,162,1.831,165,1.167,196,2.065,203,1.247,228,1.65,232,3.307,236,1.37,254,1.675,261,4.608,283,1.506,285,2.152,353,2.09,372,1.949,377,1.165,397,2.166,398,2.7,471,2.159,512,2.643,527,1.676,530,2.33,536,1.551,545,3.542,565,3.489,568,1.492,575,2.475,580,2.661,613,2.355,630,1.924,757,1.284,759,3.746,760,3.157,934,3.655,1037,2.922,1114,3.477,1312,1.914,1408,3.403,1498,3.718,1550,3.876,1558,2.933,1782,2.346,1822,4.612,1918,2.592,1971,3.876,2488,3.158,2878,4.745]],["t/495",[7,0.655,16,1.965,19,3.702,24,1.88,27,1.972,28,1.639,34,2.69,107,2.973,109,2.061,116,3.1,132,3.102,135,4.736,145,3.341,170,2.455,228,3.346,236,2.185,261,5.352,353,2.266,372,3.109,528,4.377,596,3.742,934,3.962]],["t/497",[7,0.777,19,3.786,21,1.698,24,0.973,25,1.924,27,2.543,28,0.848,31,1.468,40,3.166,48,2.438,56,0.593,84,0.92,96,2.117,107,2.175,109,1.906,123,2.048,124,3.791,127,2.787,135,3.426,158,3.004,160,2.979,219,1.86,221,3.138,226,2.914,272,3.479,275,1.86,278,2.153,283,1.738,316,2.678,318,2.752,360,4.289,394,3.004,395,2.375,397,2.498,471,3.521,527,2.342,550,3.339,555,2.843,557,2.991,561,5.291,562,2.952,565,1.962,567,2.41,568,1.721,605,3.075,607,2.303,630,2.22,654,5.291,729,4.289,757,1.481,886,3.823,939,3.699,996,3.791,1053,4.701,1274,3.671,1290,3.344,1416,4.289,1476,2.208,1628,4.618,1773,4.542,2061,2.029,2636,3.448,3019,5.139,3515,5.139]],["t/499",[7,0.689,16,2.029,24,1.415,26,4.049,34,2.226,49,1.965,145,3.515,159,5.827,164,2.401,261,4.43,276,4.33,300,5.299,359,3.854,392,5.423,393,5.356,399,6.607,403,5.61,611,3.634,742,5.467,744,4.983,764,7.345,1045,5.827,1991,5.299,3020,6.321,3021,6.607]],["t/501",[7,0.832,8,5.675,133,4.513,393,5.181,434,7.032,436,7.528]],["t/503",[7,0.897,15,3.417,26,3.478,28,1.322,47,5.909,56,0.925,84,1.015,90,4.415,92,3.469,93,3.97,97,4.19,130,5.341,138,3.779,272,3.836,367,4.77,408,3.382,440,4.131,441,5.096,605,3.391]],["t/505",[7,0.716,16,1.689,88,4.089,109,1.531,133,3.884,134,4.518,189,4.859,192,5.348,194,6.977,200,4.498,229,5.386,248,4.262,283,2.625,284,6.115,353,2.476,436,6.48,545,4.197,854,4.833,993,5.242,1043,4.712,1224,5.827,1399,5.348,1558,5.112,2041,7.24]],["t/507",[2061,3.73]],["t/509",[2,1.317,4,1.515,7,1.002,15,3.192,34,1.244,49,2.338,58,6.33,68,1.852,85,1.692,97,2.184,103,2.929,113,2.664,129,2.918,160,1.712,176,5.95,178,3.821,186,3.443,228,1.547,231,3.532,244,3.006,278,1.75,296,1.882,304,1.029,313,1.193,326,3.325,334,4.959,356,4.449,381,2.311,390,2.358,392,3.03,396,3.821,398,1.72,401,4.325,416,2.249,481,2.918,484,2.069,564,3.163,575,4.158,580,1.696,607,1.872,614,0.899,636,2.75,683,1.827,684,4.565,723,1.746,736,2.083,839,2.802,846,3.244,953,1.976,1055,3.223,1057,3.163,1381,2.896,1441,2.939,1538,3.753,1565,5.087,1625,5.522,1803,3.581,1918,2.43,1940,3.977,2225,2.6,2297,1.627,2486,3.256,2608,3.634,2671,3.977,2708,3.532,2750,4.07,3051,3.289,3319,4.449,3513,6.025,3516,4.176,3517,4.635,3518,4.635,3519,6.934,3520,4.635,3521,4.635,3522,4.635,3523,4.635,3524,4.635,3525,4.635,3526,4.635,3527,4.635,3528,4.635,3529,4.635,3530,3.486,3531,3.486,3532,3.895,3533,4.822,3534,4.635,3535,4.635]],["t/511",[4,2.065,7,0.719,15,3.796,16,1.239,21,1.881,24,1.478,34,1.696,98,2.34,145,2.677,158,3.328,159,6.082,181,3.772,189,3.563,196,2.639,203,1.594,247,2.926,261,3.374,276,3.298,279,2.877,338,1.638,343,2.368,365,3.374,377,1.489,393,3.27,401,3.488,402,5.208,403,4.273,408,3.294,412,4.954,513,3.137,541,4.098,580,2.312,611,2.768,669,5.422,723,2.38,742,4.164,764,4.484,841,3.077,978,4.484,992,3.328,1011,4.484,1105,5.548,1115,5.422,1511,4.352,1991,7.346,2225,3.544,2283,4.636,2297,2.218,2386,5.208,2928,5.548,3536,7.276,3537,6.658,3538,5.693,3539,5.693,3540,5.548,3541,6.318,3542,4.484,3543,7.175]],["t/513",[7,0.901,15,2.926,49,1.803,56,0.792,159,5.349,322,2.51,369,4.978,401,3.067,412,5.971,438,4.164,607,3.959,723,2.868,1009,3.695,1043,4.164,1601,4.793,1704,8.608,1918,3.993,1991,6.262,1992,6.534,2026,4.104,2225,4.271,2297,3.441,2386,6.277,2600,4.939,2601,5.802,2602,5.802,2604,7.064,2740,7.064,3539,6.861,3544,7.615,3545,7.615,3546,7.615,3547,8.646,3548,7.615,3549,7.615]],["t/515",[7,0.92,12,3.936,13,9.125,34,2.485,48,3.959,465,6.795,715,5.016,888,5.749,889,7.499,890,8.68,1491,7.499,1925,8.132]],["t/517",[7,0.7,16,1.192,31,1.564,34,1.631,48,2.598,58,4.631,59,9.29,112,3.172,116,2.39,133,2.739,160,2.245,176,10.351,178,5.009,275,1.982,283,2.569,300,6.183,324,3.391,334,7.291,351,2.317,353,1.746,372,2.396,465,4.459,483,4.039,532,3.34,580,2.224,590,4.779,604,4.226,684,3.34,720,5.833,846,2.843,1036,4.226,1729,5.106,1730,5.215,1781,5.833,1990,4.408,3020,4.631,3021,4.84,3550,6.077,3551,6.404,3552,6.077,3553,4.84,3554,6.077,3555,9.679,3556,6.404,3557,6.404,3558,6.077,3559,6.077,3560,6.077,3561,6.077,3562,6.077,3563,8.43]],["t/519",[7,0.938,491,3.043,532,6.207,683,3.777,1625,7.63,3564,11.294,3565,9.58,3566,9.58]],["t/521",[164,2.822,225,3.672,226,4.98,532,5.357,1993,9.043,3567,8.783,3568,9.748,3569,9.748,3570,9.748]],["t/523",[2061,3.73]],["t/525",[7,0.83,16,0.936,21,1.422,24,1.596,26,2.773,27,1.194,31,2.176,34,1.281,47,3.173,56,0.738,90,2.371,116,1.877,123,3.036,124,3.173,132,1.478,145,2.023,162,2.625,165,1.673,174,1.495,189,2.692,201,2.561,215,3.743,226,4.319,228,1.594,236,1.323,240,2.751,244,3.096,245,3.005,247,2.211,256,1.882,273,4.012,275,1.557,276,4.884,278,1.803,280,3.2,283,2.576,315,2.39,316,2.242,353,1.372,359,2.218,377,1.671,390,3.606,393,2.471,409,2.549,418,2.515,430,2.481,444,2.181,458,4.206,463,5.36,467,3.935,484,2.131,493,4.774,565,2.909,568,2.551,599,2.586,604,4.93,607,1.928,611,2.091,622,2.538,628,2.598,683,2.795,718,4.301,767,3.866,802,2.035,814,2.166,821,3.503,842,4.301,885,4.258,886,3.2,999,2.736,1065,4.429,1172,4.582,1174,3.353,1175,4.192,1176,4.582,1179,3.096,1180,4.711,1181,2.611,1182,4.301,1315,2.767,1395,3.935,1435,3.388,1960,3.353,1988,4.301,2646,4.301,2963,3.59,3344,4.097,3571,4.774,3572,4.774,3573,4.774,3574,4.774,3575,7.47]],["t/527",[7,0.925,15,2.721,16,1.389,24,1.786,27,2.339,28,1.39,40,3.932,84,0.808,105,2.334,107,2.523,109,1.86,127,2.447,140,2.924,145,3.001,226,3.618,243,4.081,272,3.055,276,3.697,318,1.817,394,3.73,472,3.634,527,2.154,550,3.872,565,3.217,568,3.159,597,3.854,626,6.797,628,3.854,798,4.104,886,4.747,953,3.019,1029,5.136,1179,4.593,1329,5.64,1822,4.49,1933,4.974,3576,8.042]],["t/529",[34,2.736,1135,8.951,1136,9.457,1275,6.894]],["t/531",[2061,3.73]],["t/533",[7,0.969,9,4.959,15,2.792,24,1.623,35,5.332,39,3.116,46,4.153,49,2.252,85,2.653,138,3.088,171,2.08,207,3.125,233,2.771,248,3.594,272,3.135,275,2.37,292,3.331,365,3.88,461,4.286,691,5.512,802,3.097,810,4.45,947,3.898,1009,3.526,1043,3.974,1044,4.511,1360,4.365,1365,4.789,1373,4.677,1408,3.4,1733,4.574,1870,4.124,2782,5.054,3344,6.236,3577,6.381,3578,7.658]],["t/535",[7,0.622,48,2.202,49,1.775,56,1.268,85,1.88,98,1.907,105,2.47,113,1.979,138,2.189,171,2.778,233,1.964,254,1.745,256,2.03,278,1.944,283,1.569,304,1.144,313,1.326,322,3.546,377,1.766,401,3.558,482,2.427,524,3.34,565,1.772,653,1.708,691,2.985,736,3.97,810,4.134,828,3.293,947,4.02,1202,2.859,1638,2.802,1870,3.558,2011,4.419,2297,3.101,2395,2.844,2698,7.497,2699,5.149,3579,10.031,3580,12.217,3581,11.017,3582,11.017,3583,5.847,3584,10.031,3585,11.017,3586,5.498,3587,5.847,3588,5.847,3589,5.847,3590,5.847,3591,5.847,3592,5.847,3593,5.847]],["t/537",[2061,3.73]],["t/539",[7,0.716,15,3.311,34,2.312,84,0.983,196,3.599,203,2.173,233,4.041,295,3.651,381,4.296,398,3.198,416,4.181,575,4.313,580,3.153,613,4.104,684,6.306,794,6.322,917,6.399,1057,5.88,1538,6.977,1803,6.658,3516,7.763]],["t/541",[7,0.884,15,3.337,56,0.904,84,0.991,96,3.223,159,6.099,233,4.06,412,6.809,846,4.063,922,6.229,978,6.163,1991,5.547,2225,4.871,2283,6.371,2297,3.048,2386,7.158,3536,8.946,3537,9.151,3538,7.824,3594,9.86]],["t/543",[84,1.204,159,8.032,233,4.022,292,3.919,351,3.261,621,4.631,802,3.645,846,4.001,888,5.307,889,6.923,890,6.704,1056,6.809,1445,6.273,1991,5.461,3536,7.185,3542,7.484,3595,8.55,3596,8.55,3597,7.508,3598,9.709]],["t/545",[7,0.776,300,5.965,580,3.417,684,5.133,3021,7.438,3599,8.414,3600,8.414,3601,8.414,3602,9.842,3603,12.633,3604,10.605,3605,9.339,3606,10.605]],["t/547",[7,0.757,233,4.18,300,5.819,532,5.007,580,3.333,684,5.007,3020,6.942,3599,8.208,3600,8.208,3601,8.208,3602,9.6,3607,12.446,3608,10.344,3609,9.11,3610,10.344]],["t/549",[7,0.731,16,1.268,34,1.735,49,2.083,56,0.915,85,3.649,96,2.4,158,3.405,174,2.755,198,3.317,203,2.219,233,4.092,254,2.191,292,2.963,296,2.625,332,3.536,398,3.265,408,2.459,491,2.794,513,3.21,525,3.21,532,3.553,614,1.254,653,2.144,769,2.618,846,4.116,917,8.798,1040,4.689,1046,4.334,1114,3.091,1202,3.589,1363,2.453,1632,4.81,1754,2.896,2033,3.836,2297,2.269,2395,3.571,2930,5.329,3611,7.925,3612,6.465,3613,6.465,3614,6.465,3615,6.205,3616,6.465,3617,5.998]],["t/551",[2061,3.73]],["t/553",[1,3.082,4,1.645,7,0.724,16,0.987,24,0.859,28,1.096,29,1.85,31,2.471,36,2.339,48,2.152,50,3.103,89,1.979,109,1.31,115,3.576,123,2.647,130,3.023,131,3.523,132,1.558,138,2.139,169,2.901,172,1.837,174,3.006,207,2.165,224,2.247,227,3.5,228,1.68,233,3.325,236,1.395,254,1.705,262,3.467,287,5.542,299,3.404,315,2.519,318,2.237,339,3.917,370,4.149,372,1.984,381,2.509,416,2.442,527,2.605,560,2.823,578,2.084,599,2.726,623,2.54,658,2.54,675,3.374,759,1.859,760,2.715,802,2.145,822,3.168,846,2.355,922,5.286,939,3.264,997,1.964,1052,3.785,1073,3.835,1076,4.076,1133,2.853,1134,4.42,1156,4.319,1218,2.986,1245,3.04,1389,3.146,1392,4.319,1394,3.535,1408,2.355,1473,2.363,1483,4.42,1629,3.693,1822,3.191,1899,3.693,2669,4.008,2679,5.286,2684,3.264,2905,3.785,2910,4.149,3023,3.889,3618,5.033,3619,4.535,3620,4.669,3621,4.149,3622,4.535,3623,4.535,3624,4.076]],["t/555",[7,0.921,15,2.401,16,1.225,24,1.066,36,2.903,39,2.679,46,2.728,84,0.713,85,2.28,86,4.481,96,2.319,108,3.4,131,2.987,145,2.647,162,2.313,174,1.957,228,2.085,233,4.37,240,3.6,247,3.979,278,2.359,283,1.904,313,1.608,318,1.603,338,1.619,377,1.472,393,3.233,398,2.319,408,3.269,504,3.991,505,3.752,576,3.878,578,2.587,590,3.542,683,2.463,757,1.622,846,2.923,877,2.401,885,3.753,917,7.293,922,4.481,992,3.291,1009,4.765,1046,4.839,1180,4.152,1202,3.468,1216,3.352,1240,3.753,1538,5.059,2141,5.15,2298,5.486,3025,5.15,3611,7.742,3625,6.247]],["t/557",[4,2.672,7,0.852,15,3.141,135,6.16,144,4.124,162,3.027,174,2.56,233,3.911,292,3.746,343,3.064,372,3.223,398,3.806,408,3.109,482,3.851,491,2.596,750,6.07,917,7.615,940,3.223,996,5.432,1125,8.174,1289,3.484,1473,3.838,1527,5.684,2969,8.174,3626,8.174,3627,8.613]],["t/559",[7,0.669,29,2.961,31,2.074,39,3.454,46,3.518,50,6.264,84,0.919,98,2.983,105,2.655,123,2.893,131,3.851,207,3.465,233,3.072,248,3.984,254,2.73,278,3.042,313,2.074,318,2.067,447,4.016,572,4.495,578,3.336,580,2.947,814,3.655,1043,4.405,1044,5,1389,5.035,1392,6.912,1408,3.769,1826,6.316,2717,5.548,3623,9.155]],["t/561",[2061,3.73]],["t/563",[2,2.138,7,0.705,24,1.046,28,0.587,39,1.692,49,2.176,56,1.286,84,0.45,86,2.83,90,1.959,92,4.378,93,3.787,94,2.249,97,1.859,100,5.079,105,1.3,107,1.064,112,2.06,116,1.551,121,5.23,127,2.931,132,1.221,165,0.931,171,2.784,172,1.44,174,1.921,177,2.007,179,1.391,193,1.512,203,0.995,228,1.317,233,1.505,236,1.093,283,1.202,313,1.579,322,2.796,372,1.556,377,0.93,381,1.967,491,1.253,495,2.007,496,2.539,527,1.733,577,3.057,614,0.765,714,2.895,715,2.137,751,5.161,757,1.024,760,1.228,816,2.355,898,2.016,914,2.069,940,1.556,946,1.914,997,1.539,1037,2.463,1117,2.249,1245,1.376,1290,2.313,1434,2.313,1737,2.437,2287,4.009,2297,2.152,2851,2.385,3010,2.83,3023,8.669,3413,3.006,3481,4.398,3628,5.99,3629,2.6,3630,3.195,3631,6.963,3632,4.769,3633,8.939,3634,3.464,3635,3.787,3636,4.884,3637,3.555]],["t/565",[2061,3.73]],["t/567",[7,0.624,27,2.431,31,1.934,34,2.016,106,2.901,112,3.921,131,3.591,138,3.192,172,2.742,189,4.236,190,4.068,191,5.724,193,2.879,198,3.854,219,3.17,233,2.865,278,2.836,295,3.183,318,2.493,341,4.354,353,2.159,377,1.77,477,4.695,491,2.386,498,4.404,499,5.512,500,3.745,504,4.798,512,2.729,774,4.282,1001,6.083,1008,4.798,1009,3.645,1011,5.331,1067,6.192,1078,6.596,1108,5.331,3638,5.983,3639,7.512,3640,7.512]],["t/569",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1011,6.465,3638,7.255]],["t/571",[7,0.757,106,3.518,131,4.355,138,3.872,190,4.934,219,2.971,233,3.474,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,512,3.31,536,2.859,1011,6.465,3638,7.255]],["t/573",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1011,6.465,3638,7.255]],["t/575",[7,0.832,139,4.734,233,3.818,341,5.802,759,3.698,1075,6.131]],["t/577",[7,0.782,24,1.607,88,4.47,206,6.427,233,3.592,341,5.458,552,3.513,567,3.979,723,3.547,852,4.302,862,4.938,3641,11.181]],["t/579",[7,0.929,29,3.462,219,3.072,233,3.592,315,4.714,318,2.416,395,3.922,922,6.756,1477,7.385,1688,6.756,1945,6.615,3311,8.486]],["t/581",[34,2.817,42,6.556,96,3.149,127,2.932,155,5.79,253,5.38,283,2.585,318,2.177,353,2.438,377,2,381,4.23,472,4.354,502,2.465,530,3.998,698,6.466,839,5.129,1009,4.117,1074,6.758,1962,6.871,1963,6.466,1964,6.086,1965,7.998,2068,7.645,3402,6.758]],["t/583",[7,0.751,27,2.26,34,2.425,102,6.005,172,3.298,283,2.753,318,2.318,530,4.258,1009,4.384,1074,7.196,1101,7.196,1557,5.907,1962,7.317,1963,6.885,1964,6.481,1965,8.311,3642,9.036]],["t/585",[2061,3.73]],["t/587",[28,1.412,89,3.735,172,3.467,275,3.098,303,7.829,318,2.437,608,5.194,802,4.049,899,6.889,1192,9.117,1363,3.604,1945,6.672]],["t/589",[28,1.581,49,1.732,84,0.835,132,2.264,171,2.094,172,3.882,193,3.661,203,1.845,236,2.646,254,2.478,304,1.624,318,2.45,336,3.136,338,1.896,391,3.245,401,2.946,404,3.753,408,4.045,426,3.717,471,3.194,502,2.775,552,2.728,568,2.207,614,1.419,882,4.604,1013,5.735,1123,5.501,1238,4.604,1360,4.394,1373,4.708,1376,3.853,1801,4.394,1844,4.708,3643,7.02,3644,2.839,3645,7.314]],["t/591",[27,2.417,106,3.732,304,2.146,404,4.958,408,3.676,471,4.22,614,1.875,1012,5.769,1123,7.267,3646,9.275]],["t/593",[7,0.897,56,0.925,84,1.233,164,2.574,183,4.021,303,7.329,313,2.289,471,3.883,552,3.316,608,4.863,614,1.725,725,4.77,847,5.183,986,4.911,1042,5.638,1363,3.374,3647,10.096,3648,9.37]],["t/596",[7,0.745,31,2.307,49,2.571,84,1.023,100,4.224,236,2.483,376,5.384,377,2.112,732,6.174,751,5.453,802,4.627,847,5.225,912,6.576,1689,4.951,3632,5.683,3649,9.314]],["t/598",[7,0.705,34,2.277,49,2.01,236,2.351,313,2.184,376,5.097,471,3.705,608,4.64,614,2.31,659,5.79,732,5.844,802,3.617,847,4.946,893,5.162,911,7.13,912,6.226,1012,5.065,1133,4.81,1689,4.687,3650,8.942,3651,8.942,3652,5.639,3653,9.635]],["t/600",[7,0.716,49,2.041,56,0.897,84,0.983,106,3.328,144,4.348,316,4.046,339,4.58,393,4.459,471,3.763,491,2.737,580,3.153,683,3.397,723,3.991,742,5.679,847,5.023,1001,6.977,1376,6.044,1511,7.298,1990,6.249]],["t/602",[7,0.664,24,1.726,28,1.189,56,1.251,84,0.913,165,1.888,174,2.505,203,2.017,228,2.67,232,3.065,246,3.187,313,2.059,338,2.622,372,3.153,536,2.51,847,4.662,930,5.829,1238,5.034,1312,3.097,2027,6.666,2297,3.55,3042,4.93,3047,7.023,3654,9.081,3655,6.179]],["t/604",[2,2.135,7,0.807,29,2.762,84,1.109,88,4.612,164,2.175,182,2.649,183,3.397,300,6.88,306,3.515,313,1.934,318,2.493,418,3.957,447,3.745,471,3.28,495,3.822,511,2.524,567,4.812,577,3.745,672,6.032,847,4.379,898,3.838,911,8.166,916,4.6,1037,3.017,1057,5.126,1117,4.282,1170,2.43,1473,3.527,2944,7.512,3656,8.53,3657,7.916]],["t/606",[2,2.149,24,1.291,26,2.959,34,2.03,90,3.755,106,2.921,109,2.151,115,3.67,144,3.816,160,2.794,179,2.667,213,3.243,216,4.057,318,2.773,416,3.67,418,3.983,502,2.836,572,4.22,578,3.132,705,4.905,736,3.398,802,3.224,847,4.408,857,3.615,901,7.259,936,3.409,1037,3.92,1041,5.07,1408,3.539,1476,2.928,1874,5.549,1898,4.199,3658,7.259]],["t/608",[2,2.137,7,0.808,24,0.883,34,1.388,49,1.781,56,1.075,68,2.067,84,1.269,108,2.815,123,1.858,125,3.108,140,2.136,160,1.911,171,1.481,201,2.775,203,1.305,219,3.172,279,2.355,306,3.518,313,1.936,322,1.705,365,2.762,372,3.492,437,2.559,449,3.355,471,2.259,476,1.582,479,3.796,482,2.438,491,1.643,512,1.88,527,1.732,536,3.382,554,4.965,575,4.434,605,1.973,608,2.829,614,1.004,757,1.343,762,3.671,802,2.205,818,2.737,835,3.671,847,3.016,850,3.563,930,2.981,955,4.264,1008,3.305,1042,3.28,1046,4.364,1059,3.051,1060,2.643,1245,1.804,1284,2.666,1330,3.711,1363,1.963,1469,4.661,1616,3.033,1700,4.057,1801,4.517,1844,3.33,2030,4.543,2031,3.634,2032,3.335,2092,3.051,2297,1.816,2312,4.995,2372,3.634,3659,4.965,3660,10.058,3661,5.874,3662,4.965]],["t/610",[2,2.34,7,0.684,21,2.452,31,2.12,139,5.317,162,3.049,170,3.668,171,2.357,203,2.077,213,3.531,313,2.12,323,3.499,578,3.41,608,4.503,759,3.805,760,2.564,769,3.335,847,4.8,885,4.946,1367,4.207,2696,5.844,3663,9.35,3664,6.682]],["t/612",[2,2.164,49,2.568,56,0.792,92,4.23,93,3.399,94,4.34,106,2.941,160,2.813,165,1.797,186,3.781,228,2.542,279,3.467,334,5.852,339,4.047,351,2.904,353,2.188,359,3.538,418,4.011,484,3.399,557,3.993,613,3.627,614,1.902,659,5.196,709,5.245,847,4.439,916,4.663,919,5.523,1009,3.695,1042,4.828,1844,4.901,2287,4.978,2677,6.277,3665,8.646,3666,8.646,3667,8.646]],["t/614",[4,2.633,7,0.844,56,0.838,84,1.16,92,4.343,164,2.332,172,2.94,177,4.099,213,3.454,256,3.176,275,2.628,304,1.789,322,2.655,372,4.006,476,3.108,531,5.145,552,3.005,707,5.957,835,5.717,846,3.769,847,4.695,1169,5.4,1801,4.839,3659,7.732,3668,6.912,3669,7.732]],["t/616",[7,0.763,49,2.175,84,1.048,171,2.63,203,2.317,232,3.52,304,2.04,313,2.365,322,3.027,527,2.116,545,5.365,554,8.816,572,5.125,847,5.354,3042,5.663]],["t/618",[4,1.383,49,1.532,105,1.394,203,1.067,236,1.172,324,2.36,372,1.668,471,1.847,608,5.187,614,0.821,757,1.098,802,1.803,911,10.05,912,9.366,930,2.438,1012,2.525,1028,2.942,1284,2.18,1689,7.033,1918,2.218,2027,2.788,2032,1.876,2600,2.744,2892,7.009,3061,3.63,3648,4.458,3649,3.63,3650,4.458,3651,4.458,3652,4.3,3670,4.803,3671,4.06,3672,4.803,3673,4.803,3674,4.803,3675,7.347,3676,4.803,3677,8.28,3678,4.803,3679,8.922,3680,4.803,3681,4.803,3682,4.803,3683,8.28,3684,4.803,3685,4.803,3686,4.803,3687,4.803,3688,4.06,3689,4.06,3690,3.924,3691,4.06,3692,4.803,3693,3.714,3694,3.714,3695,3.714,3696,3.714,3697,4.803,3698,4.803,3699,4.803,3700,4.803,3701,4.803,3702,4.458,3703,4.803,3704,4.803,3705,4.803,3706,4.803,3707,4.803,3708,4.06,3709,4.803,3710,4.06,3711,4.803,3712,4.06]],["t/620",[7,0.781,9,3.932,28,0.857,41,2.755,49,2.419,56,0.6,84,1.074,92,2.248,105,3.1,132,1.783,163,2.407,164,2.351,165,1.36,213,3.483,296,2.34,313,1.483,359,3.774,364,4.279,377,1.358,440,2.678,476,1.763,512,3.418,522,4.518,611,2.524,631,3.969,641,5.06,658,2.908,709,3.969,723,3.059,736,2.589,795,3.969,802,2.456,816,3.44,835,4.089,847,4.735,862,3.022,877,3.121,882,5.113,906,5.188,912,7.494,920,3.529,1028,4.008,1169,3.863,1193,3.83,1202,3.199,1205,5.346,1376,4.278,1385,4.75,1466,5.192,1511,5.595,1632,3.151,1633,2.861,1735,4.228,2015,4.518,2017,5.192,2027,3.798,2033,3.419,2395,3.183,2750,5.06,3061,4.945,3530,7.074,3531,4.333]],["t/622",[2061,3.73]],["t/624",[2,2.149,24,1.291,84,0.863,221,2.943,313,1.947,343,2.835,401,3.046,527,1.742,530,3.564,560,4.242,612,5.161,653,2.509,677,4.199,683,2.982,760,3.364,945,5.364,953,3.224,999,4.334,1043,5.91,1193,5.026,1216,4.057,1263,4.831,1276,5.93,1284,3.897,1312,2.928,1566,5.687,1718,5.312,2009,5.485,2312,4.265,2708,5.763,2910,8.045,3713,7.563,3714,7.563,3715,6.124,3716,6.814,3717,7.563]],["t/626",[2061,3.73]],["t/628",[2,1.727,5,2.256,7,0.868,49,1.439,56,1.007,138,2.583,165,1.435,171,1.74,203,1.533,207,3.626,246,3.857,254,3.721,256,3.324,275,1.982,276,4.401,288,4.226,304,1.349,313,1.564,315,4.219,337,2.982,338,1.575,343,2.278,416,2.949,476,1.859,491,1.93,513,3.018,691,4.886,707,3.563,715,3.291,735,4.696,760,3.419,769,2.461,774,5.517,810,3.944,830,2.927,877,2.335,878,2.731,996,4.039,1101,4.84,1365,4.005,1388,4.84,1470,4.631,1845,3.798,1953,3.483,2353,5.306,2679,4.359,2873,5.215,3718,7.298,3719,5.475,3720,5.475]],["t/630",[7,0.646,31,2.001,84,0.887,92,3.033,93,3.471,116,3.057,139,3.676,171,2.226,174,2.435,207,3.344,233,2.965,254,2.634,272,3.354,276,4.058,283,2.369,287,5.846,304,2.206,318,1.995,343,2.914,527,1.791,653,2.579,734,3.625,759,2.872,760,3.409,810,3.637,1164,4.132,1245,2.71,1473,3.65,1953,4.455,3621,6.408,3718,7.667,3721,6.827]],["t/632",[4,1.748,5,3.35,7,0.64,21,2.293,26,2.092,46,2.335,49,2.339,56,1.169,85,1.952,90,2.655,92,4.696,94,5.144,105,3.449,116,2.103,133,2.411,171,1.531,174,2.412,203,1.943,236,1.482,254,1.812,275,1.744,278,2.019,283,1.629,290,4.696,337,2.624,353,1.537,359,2.485,365,2.855,405,1.793,476,2.356,496,3.442,498,3.135,505,2.335,513,2.655,531,3.416,578,2.214,601,2.645,605,2.937,614,1.037,769,2.166,774,3.048,824,4.132,992,2.816,1006,2.882,1169,3.584,1276,4.193,1366,3.135,1434,3.135,1844,3.442,1845,3.342,1953,3.065,2287,5.035,2679,3.836,3481,5.524,3636,4.259,3718,5.998,3722,6.939,3723,6.133,3724,6.939,3725,4.021,3726,3.649,3727,4.818,3728,4.132]],["t/634",[49,2.01,56,1.328,171,2.429,283,2.585,297,4.89,476,2.595,810,5.571,1953,4.863,2698,6.556,3586,6.226,3718,5.639,3722,7.645,3723,8.359,3724,7.645,3729,6.556]],["t/636",[4,1.56,5,1.772,7,0.589,24,0.815,31,2.176,39,2.047,46,2.085,56,0.973,84,0.965,115,2.316,116,1.877,138,2.029,139,3.352,165,1.127,182,1.683,203,1.788,207,2.053,221,1.858,233,1.821,236,1.323,254,2.402,256,1.882,275,1.557,318,1.819,325,3.503,338,1.238,377,1.671,414,4.285,444,3.238,461,2.816,476,1.46,479,3.503,508,2.963,527,1.1,605,1.821,614,0.926,647,1.887,653,2.351,724,2.45,757,1.24,759,3.456,760,3.467,769,3.424,774,4.04,810,2.234,830,2.299,914,2.503,942,2.125,945,2.624,992,2.515,1008,3.049,1037,1.918,1060,3.622,1107,2.65,1170,1.544,1216,2.561,1245,2.471,1266,1.372,1284,2.46,1360,2.868,1381,1.994,1446,2.943,1476,1.848,1845,2.984,1863,2.104,1898,2.65,2013,3.173,2026,2.573,2032,3.144,2232,3.545,2518,3.733,2632,3.005,2633,2.767,2636,2.886,2679,6.711,2895,3.096,3621,5.843,3664,4.597,3718,4.711,3730,4.301,3731,4.429,3732,4.774,3733,5.401,3734,7.424,3735,4.012,3736,3.388]],["t/638",[4,2.302,5,1.757,7,0.393,10,2.698,31,2.688,39,2.03,46,2.067,56,0.969,84,0.96,116,1.861,138,2.012,139,2.238,165,1.117,182,1.669,203,1.777,207,2.036,221,1.842,254,1.604,256,1.866,275,1.544,287,6.326,302,2.297,318,1.214,325,3.473,338,1.227,377,1.66,414,4.258,461,2.792,476,1.448,527,2.491,568,1.428,592,3.201,614,0.918,647,1.871,653,2.336,757,1.229,759,3.442,760,3.25,769,3.407,774,4.015,802,2.018,810,2.215,830,2.28,857,3.367,914,2.482,942,3.134,945,2.602,992,2.493,1008,3.024,1037,1.901,1060,3.599,1107,2.628,1170,1.531,1216,2.539,1245,1.65,1266,1.36,1284,2.439,1360,2.843,1381,1.977,1473,3.95,1476,1.833,1601,2.98,1845,2.959,1863,2.087,1898,2.628,2013,3.146,2026,2.551,2032,3.124,2033,2.809,2518,3.71,2632,2.98,2633,2.743,2636,2.861,2679,5.052,2895,3.07,3664,4.568,3718,4.681,3721,6.185,3735,3.978,3736,4.999,3737,4.734,3738,7.043,3739,4.734]],["t/640",[6,5.672,7,0.73,21,1.519,26,3.438,28,1.436,31,1.314,41,2.439,48,2.181,56,0.775,84,1.003,92,1.991,93,3.323,107,2.371,109,1.562,123,1.832,125,3.065,138,2.168,153,2.75,165,2.074,171,2.131,172,3.208,173,2.877,174,2.331,179,3.406,190,4.031,207,2.194,224,2.278,233,1.946,236,2.062,289,3.1,304,1.133,318,1.309,322,1.682,339,2.712,351,1.946,491,1.621,502,1.483,505,3.838,578,2.113,583,3.284,599,2.763,614,1.874,620,2.974,658,2.575,702,3.621,724,2.618,725,3.993,757,1.933,812,3.584,877,2.86,906,3.259,997,3.769,998,3.45,1202,2.833,1241,4.378,1328,3.212,1370,4.897,1389,4.653,1444,3.584,1473,2.396,1737,2.028,2092,3.009,2297,1.791,2395,2.818,2916,4.897,2963,3.837,3540,6.536,3622,4.597,3644,1.981,3740,7.444,3741,5.102]],["t/642",[2061,3.73]],["t/644",[56,0.972,220,6.057,246,4.433,247,5.152,278,4.201,353,2.684,377,2.201,794,6.852,1493,6.26,1803,7.216,2307,6.852]],["t/646",[2061,3.73]],["t/648",[2,1.746,5,2.281,7,0.872,49,1.455,56,1.013,138,2.611,165,1.45,171,1.759,203,1.55,207,3.653,246,3.879,254,3.559,256,3.349,275,2.004,276,4.434,288,4.273,299,7.455,304,1.364,313,1.582,315,4.252,337,3.015,338,1.593,416,2.981,476,1.879,491,1.951,513,3.051,527,2.54,691,5.642,707,3.602,715,3.328,735,4.747,757,1.595,769,2.488,774,5.549,810,2.875,830,2.959,877,2.361,996,4.083,1101,4.893,1388,4.893,1470,4.682,1733,3.868,1845,3.84,1953,3.521,2353,5.347,2873,5.272,3619,5.536,3719,5.536,3720,5.536]],["t/650",[7,0.694,49,1.979,92,3.261,93,4.642,116,3.287,164,2.419,171,2.393,174,2.618,207,3.595,233,3.187,254,3.523,256,3.295,276,5.909,283,2.547,299,7.654,304,1.856,343,3.132,527,1.925,653,2.772,810,3.911,1164,4.442,2118,6.206]],["t/652",[4,1.827,5,3.434,7,0.66,21,2.367,26,2.186,46,2.441,49,2.386,56,1.108,85,2.04,90,2.775,92,4.683,94,5.272,105,3.509,116,2.198,133,2.519,171,1.6,174,2.489,203,2.005,236,1.549,254,1.894,275,1.823,278,2.11,283,1.703,290,4.908,299,6.813,337,2.743,353,1.606,359,2.597,365,2.984,405,1.874,476,2.431,496,3.597,498,3.277,505,2.441,513,2.775,527,1.288,531,3.57,578,2.314,605,3.031,614,1.084,769,2.263,774,3.186,824,4.318,992,2.944,1006,3.013,1169,3.746,1366,3.277,1434,3.277,1844,3.597,1845,3.493,1953,3.203,2287,3.654,3481,4.009,3636,4.451,3723,4.451,3725,4.203,3726,3.814,3727,5.036,3728,4.318,3742,7.162]],["t/654",[49,2.089,56,1.309,171,2.525,283,2.688,297,5.084,299,5.965,476,2.698,810,5.425,1953,5.055,2698,6.816,3586,6.472,3723,7.025,3729,6.816,3742,7.948]],["t/656",[4,1.976,5,2.243,7,0.502,31,2.162,39,2.592,46,2.639,56,1.085,84,0.69,116,2.377,138,2.569,182,2.131,203,1.525,207,2.599,221,2.352,254,2.048,256,2.383,275,1.971,299,7.411,377,1.424,414,5.077,476,1.849,527,2.681,614,1.172,647,2.389,653,2.786,691,3.503,757,1.569,769,3.401,774,4.787,830,2.911,942,2.69,945,3.322,1037,2.428,1060,4.291,1107,3.355,1170,1.955,1216,3.242,1245,2.107,1266,1.737,1284,3.114,1360,3.631,1476,2.34,1845,3.778,1863,2.664,1898,3.355,2026,3.258,2032,3.725,2518,4.423,2632,3.805,2633,3.503,2636,3.654,2895,3.92,3664,5.447,3735,5.079,3736,4.289,3743,6.044,3744,5.446]],["t/658",[4,2.439,56,1.182,116,2.934,182,2.631,221,2.904,377,1.758,476,2.282,527,2.712,647,2.95,653,3.209,757,1.937,942,3.32,945,4.101,1037,2.997,1060,4.943,1107,4.142,1170,2.414,1245,2.601,1266,2.144,1284,3.845,1476,2.889,1863,3.289,2026,4.022,2032,4.292,2518,5.096,2632,4.697,2633,4.325,2895,4.84,3744,6.723,3745,7.462,3746,10.737]],["t/660",[2061,3.73]],["t/662",[2061,3.73]],["t/664",[7,0.563,17,2.824,34,1.82,49,1.606,56,1.138,203,1.71,225,2.554,239,4.099,248,5.412,256,2.673,285,3.954,296,2.753,304,2.43,317,3.571,351,2.586,393,3.509,398,2.517,731,3.745,734,3.161,760,2.111,1043,5.602,1044,4.208,1122,4.469,1133,3.844,1266,2.61,1399,6.358,1601,5.718,1632,3.708,1737,2.694,1918,4.763,2353,4.268,2379,5.954,2601,6.921,2602,6.921,2624,5.589,2887,5.818,3644,2.632,3747,6.78,3748,6.78,3749,4.975]],["t/666",[7,0.674,10,4.625,56,1.062,84,1.165,228,2.708,248,5.524,322,2.674,511,2.726,605,3.893,888,5.036,940,3.199,1043,4.437,1399,6.932,1565,5.953,1601,5.108,1629,5.953,1638,4.416,1918,4.255,2297,2.848,2411,6.27,2601,6.183,2602,6.183,3750,8.114,3751,8.114,3752,8.114]],["t/668",[7,0.585,15,2.704,56,1.086,133,3.172,140,2.906,162,2.606,164,2.037,165,1.661,171,2.015,196,2.939,203,1.775,248,4.605,255,4.994,322,3.069,324,3.927,347,4.894,393,3.642,614,1.806,724,3.611,731,3.887,828,3.092,851,3.887,877,2.704,1043,6.317,1046,4.587,1133,3.989,1399,5.779,1601,5.861,1632,3.848,1633,3.494,1918,3.69,2297,2.47,2601,7.095,2602,7.095,3655,7.194,3749,5.163,3753,6.34,3754,6.34]],["t/670",[2061,3.73]],["t/672",[2,1.004,7,0.905,16,1.102,24,1.583,28,0.525,34,2.142,48,1.51,49,1.89,56,1.083,84,0.799,85,2.913,96,1.311,97,1.664,105,1.164,113,1.357,123,1.268,140,1.459,160,2.076,164,1.023,171,2.285,174,1.106,203,1.766,213,1.515,219,1.152,223,3.335,232,1.354,233,2.144,246,1.407,254,2.952,275,1.152,295,2.382,296,1.434,304,1.772,313,0.909,322,2.308,337,1.734,338,1.457,343,1.324,372,2.216,377,0.832,381,1.761,398,1.311,401,1.423,430,1.836,449,2.291,461,4.129,476,1.08,482,1.664,491,1.122,505,1.543,508,2.193,525,1.754,527,0.814,528,1.961,567,1.492,580,2.057,614,1.8,622,1.877,628,1.922,707,3.296,715,3.044,810,1.653,830,1.701,852,1.614,862,2.947,874,1.828,884,2.348,898,1.805,930,2.036,979,2.457,1170,2.265,1202,1.961,1270,3.183,1363,2.133,1376,1.86,1394,6.836,1441,3.564,1467,2.912,1539,3.948,1700,2.77,1735,2.592,1737,2.234,1798,2.071,1873,2.41,2015,2.77,2032,1.567,2282,2.481,2297,1.24,2312,1.992,2395,1.951,2447,6.656,2509,2.729,2684,2.291,2898,4.936,3009,2.912,3016,2.24,3644,2.718,3755,5.065,3756,5.065,3757,3.183,3758,3.532,3759,3.532,3760,3.532,3761,5.065,3762,5.065,3763,3.532,3764,5.065,3765,5.065,3766,5.065,3767,3.532,3768,3.532,3769,3.532,3770,3.532,3771,5.924,3772,3.532,3773,3.183]],["t/674",[7,0.505,21,1.809,24,1.037,28,1.253,31,2.492,56,1.182,76,4.765,109,1.498,232,3.231,461,6.165,527,1.942,572,4.704,590,3.445,607,4.222,846,3.944,1170,2.727,1266,3.414,3774,6.077,3775,6.077,3776,8.883,3777,11.33,3778,9.572,3779,9.572,3780,9.572,3781,9.572,3782,6.714,3783,6.077,3784,5.475,3785,4.84,3786,4.84,3787,4.84,3788,4.84,3789,6.077,3790,6.077,3791,4.84,3792,5.215]],["t/676",[7,0.528,21,1.892,28,1.293,31,2.551,56,1.199,76,4.983,109,1.545,381,3.168,405,2.914,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,807,3.54,1037,3.492,1266,3.45,1443,6.816,1689,4.802,3776,9.161,3782,6.923,3784,5.725,3785,5.061,3786,5.061,3787,5.061,3788,5.061,3791,5.061,3792,5.453,3793,6.354,3794,6.354,3795,8.012,3796,7.039,3797,6.354,3798,6.354,3799,5.58]],["t/678",[7,0.341,21,1.222,24,1.079,26,1.606,28,0.61,31,1.628,37,4.442,56,1.35,95,5.664,106,2.442,107,1.108,109,1.124,112,2.143,127,2.665,137,2.776,179,1.447,193,1.573,242,3.049,267,3.699,381,3.152,391,1.821,397,2.77,405,2.585,440,1.908,444,2.889,461,3.729,471,2.761,502,1.193,511,1.379,514,3.269,527,2.156,572,4.835,635,1.569,757,1.066,784,7.23,813,4.315,829,3.51,830,1.977,841,1.999,1087,6.142,1117,2.34,1353,3.809,1381,1.715,1443,6.047,1845,2.566,1883,3.699,2312,2.315,3009,5.212,3023,3.172,3553,3.269,3715,3.324,3800,6.323,3801,4.105,3802,4.105,3803,4.105,3804,4.105,3805,4.105,3806,4.105,3807,7.402,3808,6.323,3809,7.712,3810,4.105,3811,4.105,3812,4.105,3813,4.105,3814,4.105,3815,4.662,3816,4.105,3817,2.752,3818,4.105,3819,4.105,3820,6.323,3821,4.105,3822,4.105,3823,4.105,3824,4.105,3825,4.105,3826,4.105,3827,4.105,3828,4.105,3829,4.105,3830,4.105,3831,4.105,3832,4.105,3833,4.105,3834,4.105]],["t/680",[7,0.427,15,3.391,16,1.274,21,0.941,24,0.877,28,0.764,56,1.369,97,1.49,107,2.019,171,0.905,186,1.57,203,1.297,228,1.055,237,2.443,322,2.142,393,1.636,527,0.728,605,1.206,683,1.246,715,2.784,784,2.443,822,1.99,841,1.54,930,1.822,978,2.243,997,1.233,1055,2.198,1170,1.663,1204,7.817,1367,5.745,1394,6.199,1473,1.484,1567,2.56,1577,4.094,1597,2.101,1700,2.479,1898,1.755,1991,3.284,2283,2.319,2297,1.11,2325,3.034,2955,2.776,2956,2.848,3586,2.319,3755,2.848,3756,2.848,3757,2.848,3761,2.848,3762,2.848,3764,2.848,3765,2.848,3766,2.848,3817,3.446,3835,3.161,3836,5.838,3837,3.161,3838,3.161,3839,9.302,3840,9.302,3841,9.302,3842,9.302,3843,9.302,3844,8.63,3845,3.161,3846,9.302,3847,8.381,3848,7.485,3849,3.161,3850,3.161,3851,3.161,3852,6.497,3853,3.161,3854,3.161,3855,3.161]],["t/682",[7,0.695,22,5.47,39,2.578,46,3.654,49,1.424,68,2.402,85,2.194,95,3.93,98,3.098,100,3.942,116,2.364,126,3.059,160,2.221,164,1.74,171,1.721,202,6.068,207,2.585,213,2.578,247,2.784,278,2.27,337,2.95,343,2.253,351,2.292,403,4.065,404,3.084,408,2.287,434,4.222,461,3.545,506,5.229,525,4.155,550,4.689,657,4.955,706,4.521,779,7.03,958,4.141,1009,2.917,1380,3.995,1390,4.521,1394,7.683,1473,2.823,1733,3.784,1845,3.757,1916,3.784,2196,8.671,2282,4.222,2447,6.001,2485,3.962,2509,4.645,2683,3.899,2684,3.899,3133,7.347,3158,4.955,3856,6.011,3857,4.411,3858,4.714,3859,6.011,3860,6.011,3861,6.011]],["t/684",[2061,3.73]],["t/686",[7,0.54,28,1.491,31,2.273,49,1.54,56,0.677,68,2.598,84,0.742,92,3.446,94,3.706,115,4.866,132,2.733,133,2.931,196,2.716,203,1.64,233,2.48,236,1.802,254,2.203,283,1.981,313,1.674,315,3.255,318,1.668,377,2.081,390,3.309,436,4.89,502,3.125,513,3.229,527,1.498,550,2.693,577,4.999,630,2.531,675,4.359,715,3.522,751,3.956,769,2.633,829,3.61,1037,3.547,1045,4.568,1218,5.24,1220,5.71,1308,4.064,1332,4.185,1380,4.322,1863,2.866,1953,3.727,2518,3.425,2669,5.179,2799,5.36,3862,5.464,3863,7.384,3864,6.503,3865,6.503]],["t/688",[2,1.786,4,2.054,7,0.522,16,1.232,56,1.103,84,0.985,116,2.471,182,2.215,221,2.445,232,2.408,313,1.617,318,1.612,372,2.477,377,1.481,476,1.922,491,1.996,502,1.826,527,2.56,536,1.972,565,2.162,568,1.896,605,3.289,630,2.445,647,2.484,653,2.861,683,2.477,757,1.631,814,4.811,841,3.06,852,2.87,942,2.796,945,3.453,1037,2.524,1060,4.407,1107,3.488,1139,3.543,1170,2.032,1218,6.593,1257,3.848,1266,1.805,1284,3.237,1476,2.433,1863,2.769,2026,3.386,2032,3.826,2518,4.543,2632,3.955,2633,3.641,2895,4.075,3862,7.248,3866,3.436]],["t/690",[0,2.444,4,1.313,7,0.516,12,1.707,16,1.219,24,1.061,31,2.522,39,2.666,41,1.92,56,1.22,84,0.458,98,1.487,113,2.389,115,4.495,132,1.243,165,0.948,179,1.416,182,2.682,196,1.677,203,1.013,219,1.31,221,1.563,254,1.361,282,2.302,318,1.595,372,1.584,414,3.758,471,1.754,476,1.229,477,2.51,491,1.276,502,1.167,527,2.636,553,2.241,565,1.382,567,1.697,568,1.876,577,2.002,580,1.47,610,3.252,614,1.206,647,3.007,653,2.841,658,4.322,670,2.46,724,2.061,757,1.043,814,2.821,829,4.223,830,1.934,936,3.429,942,3.385,945,2.207,1037,2.498,1060,3.887,1107,2.23,1170,2.461,1218,6.263,1245,1.4,1257,2.46,1266,1.787,1284,2.07,1363,3.877,1476,2.945,1671,3.603,2026,2.165,2032,2.758,2098,8.134,2232,2.983,2518,3.275,2632,2.528,2633,2.328,2895,2.605,3006,3.619,3008,3.149,3041,3.619,3866,3.4,3867,4.233,3868,6.217,3869,4.016,3870,4.016,3871,6.217]],["t/692",[4,2.243,5,2.548,16,1.346,56,1.144,116,2.699,164,1.987,182,2.42,221,2.671,254,2.326,318,1.761,372,2.706,476,2.099,502,1.994,527,2.715,647,2.713,653,3.038,757,1.782,942,3.054,945,3.772,1037,2.757,1054,9.892,1060,4.679,1107,3.81,1170,2.22,1218,6.524,1257,4.203,1266,1.972,1284,3.537,1476,2.658,1863,3.025,2026,3.7,2032,4.062,2518,4.824,2632,4.32,2633,3.978,2895,4.452,2905,5.162,3866,3.754,3872,9.158]],["t/694",[2,1.47,7,0.808,24,0.883,28,0.769,31,1.936,49,2.098,56,1.269,90,2.569,92,4.76,93,4.341,94,2.949,97,2.438,98,1.916,100,2.438,115,3.648,132,1.601,165,1.221,171,2.536,174,2.774,177,2.632,203,1.897,283,1.576,304,1.149,313,1.332,322,2.92,398,1.92,496,3.33,527,2.041,553,2.887,614,1.004,714,3.796,715,2.802,769,3.045,814,2.348,850,3.563,898,2.643,914,2.713,940,2.04,1129,4.956,1218,5.769,1290,3.033,1434,3.033,1689,2.858,1953,2.965,2287,4.916,2297,3.109,2502,4.264,3023,3.997,3481,3.711,3636,5.988,3637,4.661,3847,4.661,3873,10.058,3874,7.519,3875,5.173]],["t/696",[2061,3.73]],["t/698",[7,0.65,12,3.327,28,1.776,84,0.894,89,3.079,105,2.58,107,2.112,174,2.452,228,2.613,372,3.087,377,1.845,390,3.983,397,3.429,405,4.005,426,3.883,496,5.039,511,3.352,536,2.457,558,3.689,622,4.161,723,3.758,940,3.087,1473,3.676,1864,6.098]],["t/700",[16,2.273,27,2.206,28,1.312,102,5.863,183,3.99,405,3.887,426,3.433,495,5.469,511,2.963,670,5.402,1052,6.634,1163,5.635,1191,6.816,1998,5.402,3876,6.917,3877,8.821]],["t/702",[27,2.46,267,8.86,405,3.297,511,3.304,757,2.553,2627,10.419]],["t/704",[7,0.733,27,2.206,28,1.312,49,2.089,84,1.007,109,2.144,183,3.99,405,2.957,409,4.71,426,3.433,511,2.963,622,4.689,1170,2.854,1289,3.76,1753,7.796,1870,3.235,3878,7.143]],["t/706",[27,2.376,130,5.706,183,4.296,338,2.462,405,3.184,511,3.191,724,4.873,880,5.099,3879,7.448,3880,7.74]],["t/708",[27,2.356,163,3.933,183,4.26,277,6.056,405,3.157,511,3.164,622,5.006,723,3.547,1037,4.79,3881,9.039]],["t/710",[27,2.336,28,1.389,107,3.319,109,1.659,405,3.131,426,3.635,511,3.137,820,6.436,3882,8.964]],["t/712",[16,1.689,27,2.155,34,2.312,121,5.312,254,2.92,353,3.045,397,3.774,405,2.888,408,4.554,484,3.847,511,2.895,581,6.566,712,4.885,1632,4.712,1874,6.322,3883,9.784,3884,11.474,3885,8.616]],["t/714",[16,1.786,27,2.279,343,3.414,351,3.474,405,3.054,432,7.285,476,2.787,511,3.06,583,5.863,635,3.483,757,3.053,2485,6.004,3886,7.999,3887,7.655]],["t/716",[27,2.206,49,2.089,109,1.567,130,5.299,171,2.525,172,3.924,174,2.763,183,3.99,377,2.079,405,2.957,426,3.433,505,3.852,511,2.963,580,3.933,636,5.234,724,4.526,997,4.194,3888,8.467]],["t/718",[24,1.621,27,2.376,405,3.184,511,3.191,760,3.726,769,4.847,1696,6.367,3889,10.009]],["t/720",[24,1.635,27,2.396,405,3.211,511,3.218,690,6.263,760,2.983,769,4.864,1696,6.422,3890,9.195]],["t/722",[16,1.801,26,4.309,27,2.297,121,5.663,183,4.154,405,3.079,407,4.375,408,3.494,511,3.086,750,6.821,757,3.064,1237,6.518,3807,8.816]],["t/724",[4,2.861,27,2.189,172,3.195,183,3.958,193,3.354,394,4.61,405,2.934,407,4.169,511,2.94,552,3.264,725,5.739,822,5.509,981,5.722,988,5.162,1114,4.184,1381,3.655,3876,6.862,3891,8.4,3892,10.268,3893,8.4]],["t/726",[5,3.442,7,0.864,16,1.818,25,2.359,28,1.379,56,0.728,84,0.798,105,2.305,125,4.201,133,4.18,134,3.667,145,2.963,164,2.024,174,2.19,196,2.921,198,3.588,200,3.651,228,2.334,236,1.938,294,3.964,372,2.757,390,3.558,458,4.149,511,3.98,536,2.195,677,3.882,683,2.757,724,3.588,807,4.694,854,3.922,887,4.648,940,3.656,1129,4.609,1266,2.009,1500,5.569,1779,4.963,2683,4.536,3894,6.993]],["t/728",[16,1.743,24,1.517,28,1.606,84,1.015,144,4.487,367,4.77,511,3.629,635,3.399,807,4.398,820,4.816,1487,6.246,2677,7.329,2683,5.767,3895,8.249,3896,10.096,3897,10.096,3898,10.096]],["t/730",[5,3.496,46,4.113,163,3.933,225,3.547,289,3.922,322,3.104,511,3.164,807,3.835,1051,9.054,2668,6.994,3007,7.626,3899,10.694]],["t/732",[5,3.438,28,1.377,163,3.868,287,8.904,289,3.857,511,3.111,760,3.686,807,3.771,1737,3.681,2700,7.947,3900,10.516]],["t/734",[5,3.438,28,1.377,84,1.057,89,3.642,163,3.868,289,3.857,432,7.357,511,3.111,757,2.874,807,3.771,1737,3.681,3887,7.782,3901,10.516]],["t/736",[5,3.409,16,2.313,163,3.836,289,3.825,322,3.027,405,3.955,495,5.604,511,3.086,807,3.74,3902,10.43,3903,8.521]],["t/738",[5,3.526,24,1.621,28,1.412,163,3.967,289,3.956,322,3.131,511,3.191,769,4.847,807,3.868,3904,10.785]],["t/740",[5,3.466,28,1.389,163,3.9,170,3.7,289,3.889,322,3.078,511,3.137,769,4.812,807,3.803,3905,10.605]],["t/742",[5,3.274,24,1.505,28,1.312,49,2.089,163,3.684,183,3.99,285,3.84,289,3.674,323,3.749,367,4.732,409,4.71,511,2.963,751,5.367,807,3.592,893,5.367,1737,3.506,1819,5.965,2678,9.685,3108,7.412,3906,10.016]],["t/744",[5,3.438,27,2.317,28,1.377,163,3.868,200,4.835,240,5.337,289,3.857,322,3.053,511,3.111,694,5.308,807,3.771,816,5.529,3401,8.816,3907,10.516]],["t/746",[5,3.409,24,1.567,28,1.366,163,3.836,201,6.33,289,3.825,322,3.027,405,3.955,511,3.086,807,3.74,3903,8.521,3908,10.43]],["t/748",[5,3.149,16,1.664,28,1.561,109,1.508,163,3.544,193,4.023,224,4.686,289,4.371,322,2.797,338,2.199,511,2.85,552,3.165,632,4.81,725,4.552,807,4.274,812,5.96,1446,5.231,3876,6.653,3909,9.635,3910,8.652]],["t/750",[5,3.354,16,1.772,24,1.542,163,3.774,289,4.879,322,2.978,426,3.517,511,3.036,605,3.446,763,6.481,807,4.441,1139,6.15,3092,8.383,3911,10.26]],["t/752",[5,3.381,16,1.786,24,1.554,163,3.805,289,4.896,511,3.06,662,5.579,807,4.788,1139,6.181,1557,5.956,1737,3.62,2952,9.11]],["t/754",[5,3.249,24,2.054,28,1.301,36,4.067,49,2.073,163,3.655,289,3.645,407,4.169,408,4.395,511,2.94,527,2.016,731,4.834,807,3.564,1245,3.051,1703,7.885,1737,3.478,3912,9.938]],["t/756",[16,2.181,219,3.046,318,3.049,405,3.729,426,3.635,567,3.946,723,3.518,734,4.354,1864,5.185,3913,8.664]],["t/758",[7,0.757,16,2.149,27,2.279,102,6.054,183,4.12,405,3.674,426,3.545,495,4.635,511,3.06,670,5.579,1052,6.851,1191,7.039,1998,5.579,3876,7.143,3914,10.344]],["t/760",[27,2.356,109,2.119,183,4.26,405,3.157,511,3.164,622,5.006,1170,3.047,1753,8.11,1870,3.454,3878,7.626]],["t/762",[7,0.776,27,2.336,163,3.9,183,4.224,277,6.025,405,3.131,511,3.137,622,4.964,723,3.518,1037,4.773,3881,8.964]],["t/764",[4,3.002,7,0.763,27,2.297,405,3.079,432,7.321,476,2.81,511,3.086,632,5.207,635,3.512,757,2.86,1363,3.485,1695,8.521,3886,8.065,3887,7.718]],["t/766",[27,2.438,130,5.855,171,2.791,183,4.409,405,3.268,511,3.275,580,4.175,3888,9.356]],["t/768",[4,2.93,27,2.242,172,3.272,183,4.054,193,3.435,394,4.721,407,4.269,511,3.011,552,3.343,725,4.809,822,5.642,981,5.86,988,5.286,1381,3.743,3876,7.028,3891,8.603,3892,8.603,3893,8.603,3915,10.178]],["t/770",[2061,3.73]],["t/772",[7,0.782,48,4.026,84,1.075,109,1.674,139,4.454,759,3.479,1408,4.407,1902,5.619,2818,6.55,3621,7.763,3721,8.27,3916,6.615]],["t/774",[7,0.829,24,1.336,28,1.164,49,1.854,56,0.815,84,0.894,107,2.112,109,1.391,139,4.719,165,1.848,171,2.241,285,3.408,502,2.275,527,2.531,568,3.011,605,2.986,614,1.519,724,4.017,729,5.887,759,2.892,953,4.253,1245,3.479,1266,2.867,1381,3.27,1506,5.294,1863,3.451,1902,3.934,2851,6.032]],["t/776",[5,1.938,7,0.629,19,2.452,24,1.987,28,0.776,31,1.344,39,2.239,41,3.619,68,3.558,84,0.596,96,1.938,107,1.409,109,0.928,115,2.534,133,3.413,134,2.738,139,3.58,160,1.929,164,1.512,165,1.787,200,2.726,203,1.91,226,2.668,236,2.873,288,3.632,295,2.213,318,1.34,338,1.354,353,1.501,367,2.801,414,3.157,527,2.052,568,2.284,591,3.137,605,1.991,630,2.032,638,3.311,757,1.356,759,2.796,760,2.772,769,4.199,770,4.228,774,2.976,827,5.215,933,4.035,934,2.624,946,5.03,950,3.831,1133,2.96,1239,3.387,1240,3.137,1303,3.287,1448,4.481,1498,3.927,1506,3.531,1733,3.287,1754,2.339,1814,3.927,2818,6.193,2966,3.746,3401,4.159,3629,3.442,3730,4.705,3917,3.5,3918,4.844,3919,5.503,3920,5.503,3921,4.095,3922,4.705]],["t/778",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1473,4.277,3718,6.054]],["t/780",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,1245,3.229,3718,6.155]],["t/782",[7,0.934,56,0.857,84,0.94,109,1.463,139,3.894,145,3.489,170,2.564,203,2.599,206,5.619,246,4.105,304,1.828,614,1.597,759,3.042,760,2.564,992,4.337,1139,4.643,1266,2.366,1558,4.886,2297,2.89,3923,6.558,3924,7.066,3925,6.274,3926,7.066,3927,9.647]],["t/784",[7,0.856,56,0.857,84,0.94,109,1.463,139,3.894,145,3.489,170,2.564,203,2.599,206,5.619,246,4.105,304,1.828,614,1.597,759,3.042,760,2.564,992,4.337,1139,4.643,1266,2.366,1558,4.886,2297,2.89,3923,6.558,3925,6.274,3928,7.066,3929,7.066,3930,9.647,3931,8.234]],["t/786",[7,0.745,24,1.529,28,1.614,109,1.593,127,3.097,132,2.774,139,4.239,193,3.435,236,2.483,254,3.956,550,3.712,578,3.712,614,1.739,623,4.523,997,3.497,1382,5.642]],["t/788",[7,0.694,84,1.187,88,3.967,89,3.287,128,5.688,140,3.451,165,1.973,182,2.947,294,4.738,575,4.183,605,3.187,635,3.195,763,5.995,828,3.673,940,3.295,1075,5.118,1363,3.946,1381,3.49,1408,3.911,1616,6.096,3932,6.889,3933,7.753,3934,7.753]],["t/790",[12,2.899,16,1.788,24,1.164,28,1.356,34,1.831,56,0.71,84,0.779,85,2.49,107,2.959,109,2.196,113,2.621,132,3.18,139,3.226,164,1.975,170,2.124,213,2.925,236,1.89,338,1.768,351,2.601,353,1.96,372,2.69,470,5.198,484,3.045,502,1.982,527,2.101,614,1.769,755,3.237,759,2.52,877,2.621,880,3.095,992,3.593,1037,2.74,1133,5.17,1266,1.96,1303,4.294,1558,4.048,1870,2.502,3866,4.988,3935,7.189]],["t/792",[7,0.297,12,2.413,16,0.702,24,1.372,25,1.207,28,0.532,34,0.96,36,1.662,49,0.847,56,1.114,68,1.429,84,0.648,85,1.306,96,1.328,107,2.169,109,2.032,113,1.375,132,1.757,139,4.413,140,1.477,164,1.036,170,2.198,179,1.261,199,1.859,213,1.534,236,1.573,295,1.516,296,1.453,313,0.921,338,1.83,351,1.364,353,1.028,377,0.843,426,1.392,470,2.726,476,2.992,491,1.136,502,1.65,512,2.565,527,1.627,575,2.842,580,1.309,614,1.969,647,3.466,653,1.883,663,2.303,725,3.046,755,1.698,756,1.77,759,2.608,803,4.189,828,1.572,877,1.375,880,1.623,940,2.784,942,4.651,992,1.884,997,2.216,1012,3.39,1037,1.437,1114,1.71,1133,3.219,1170,1.157,1187,3.223,1245,1.247,1266,1.632,1289,1.525,1303,2.252,1363,2.679,1408,4.102,1476,2.199,1558,2.123,1616,3.329,1864,1.986,1870,1.312,1874,2.625,1899,2.625,1902,4.406,2488,2.285,2839,7.279,3053,4.03,3916,2.513,3935,3.77,3936,3.319,3937,5.984,3938,3.578,3939,5.678,3940,7.457,3941,5.678,3942,2.398,3943,7.099,3944,2.897]],["t/794",[7,0.84,24,1.726,28,1.189,68,3.195,132,2.475,233,3.05,313,2.059,318,2.052,328,5.562,398,2.969,426,3.112,476,2.446,502,2.324,512,2.906,527,1.843,536,2.51,565,3.48,568,2.413,578,3.312,623,4.036,647,3.998,724,4.103,1037,3.213,1380,5.315,1408,3.742,1487,5.617,1906,4.583,1907,4.352,3725,6.014,3945,9.081]],["t/796",[7,0.751,24,2,29,2.478,31,2.629,39,2.89,49,1.596,125,4.048,139,4.829,165,1.591,197,3.164,203,2.753,213,3.879,236,1.867,246,4.069,272,2.907,292,3.089,304,1.496,414,4.074,491,2.14,759,3.342,760,2.816,769,4.929,811,4.406,1188,5.457,1216,3.615,1366,3.951,1367,3.443,2818,7.102,3042,4.155,3664,5.867,3917,4.517,3946,6.739]],["t/798",[7,0.89,16,1.116,24,1.952,25,2.387,28,1.655,29,1.32,31,2.261,34,0.963,37,3.999,39,2.441,49,0.85,56,0.736,68,2.275,85,1.31,96,1.332,103,1.266,106,1.386,112,2.972,133,1.618,134,1.882,139,1.697,140,1.482,145,1.521,160,2.975,165,0.847,171,1.028,179,1.266,203,1.436,221,1.397,223,2.13,225,1.352,228,1.198,236,1.577,257,1.926,279,1.634,292,1.645,304,1.264,313,0.924,318,0.921,323,1.525,338,0.93,343,1.345,377,0.846,394,1.89,395,2.371,397,1.572,418,1.89,426,2.754,484,1.602,500,1.789,512,1.304,527,1.855,530,1.691,560,2.013,565,1.235,568,1.718,605,1.369,609,2.472,614,0.696,630,1.397,635,1.372,712,2.035,723,1.352,747,2.463,757,2.091,759,2.614,760,2.734,769,3.556,827,3.778,841,1.748,862,1.882,877,1.379,936,1.618,940,1.415,946,5.308,1059,2.117,1114,1.716,1239,5.223,1257,2.198,1308,4.422,1312,1.39,1363,2.16,1381,1.499,1446,2.213,1605,2.773,1754,2.55,1864,1.993,1953,2.057,1970,3.016,2061,1.277,2064,2.699,2429,4.228,2741,2.959,2816,3.016,2818,6.499,2819,3.016,2867,4.362,3917,2.406,3947,3.589,3948,3.234,3949,3.589,3950,3.33,3951,3.782,3952,3.234,3953,3.782]],["t/800",[24,1.693,29,3.648,103,3.498,395,4.132,759,3.665,1530,6.291,2827,8.034]],["t/802",[7,0.843,18,4.155,24,1.136,28,0.99,56,1.18,68,3.585,84,1.159,103,2.348,105,2.195,132,2.061,160,2.46,165,1.572,196,2.781,257,3.572,279,3.031,398,3.331,527,1.534,565,2.291,568,2.009,759,3.315,760,2.794,1114,3.183,1164,3.539,1234,3.975,1257,5.495,1454,4.353,1487,4.677,1530,4.222,1633,3.306,1735,6.584,1902,3.346,1918,5.322,2600,5.82,2814,6.584,3514,5.392,3948,5.999,3950,6.177,3954,6.177,3955,5.999,3956,5.999,3957,6.391,3958,7.561,3959,7.561]],["t/804",[37,5.461,56,1.306,95,6.494,279,3.539,759,2.872,760,3.093,1257,6.084,1454,5.082,1530,4.929,1735,5.704,1754,3.482,1902,4.992,1918,5.209,2321,5.517,2816,6.533,2817,7.004,2818,5.407,2819,6.533,2821,6.533,2822,6.295,2827,6.295,2832,6.671,3955,7.004,3956,7.004,3960,7.774,3961,8.192]],["t/806",[56,1.251,279,3.641,395,3.331,527,1.843,759,2.954,760,2.49,866,8.882,1170,3.589,1257,6.194,1408,4.733,1454,5.228,1530,5.071,1735,5.868,1902,5.083,1907,4.352,1918,4.194,2821,6.72,2822,6.476,3955,7.206,3956,7.206,3957,7.676,3962,7.997,3963,7.997,3964,7.997]],["t/808",[16,1.862,84,1.283,254,3.218,409,5.072,760,2.957,855,7.053,1107,5.273,1381,3.967,1632,5.194,3965,9.498,3966,8.812]],["t/810",[2,1.604,7,0.888,17,2.351,21,2.384,24,1.366,29,2.076,39,2.421,49,1.337,84,0.914,100,2.66,107,1.523,109,1.423,116,3.148,138,4.301,165,1.333,170,1.758,171,2.292,183,2.553,203,2.019,219,2.611,236,2.218,248,3.96,272,2.435,283,2.834,313,1.453,351,2.153,398,3.757,512,2.909,614,1.095,677,3.134,734,2.632,757,1.466,759,2.957,760,2.896,780,3.167,827,3.015,898,2.884,1181,3.087,1214,3.852,1266,1.622,1312,3.919,1360,3.391,1473,2.651,1540,3.291,1550,4.427,1558,4.75,1698,4.427,1814,4.246,2032,2.504,2398,8.987,2485,3.721,2645,4.957,2789,4.05,2923,7.03,3629,3.721,3967,5.646]],["t/812",[2061,3.73]],["t/814",[7,0.902,34,2.724,49,1.577,56,1.315,84,0.76,109,2.076,164,3.144,278,2.514,295,4.301,304,1.479,401,2.682,408,2.533,452,6.508,495,3.388,527,1.534,614,1.292,802,2.838,1170,2.903,1216,5.445,1266,1.913,1282,5.392,1328,4.191,1558,3.951,1705,4.285,1870,2.442,1876,6.123,1882,4.133,3624,5.392,3968,5.999,3969,5.392,3970,6.658]],["t/816",[7,0.525,27,2.166,34,1.696,40,3.508,56,1.028,84,0.721,98,2.34,107,1.705,109,2.208,111,4.882,127,2.992,164,1.829,170,1.967,179,3.053,193,2.422,200,3.298,203,1.594,295,2.677,313,1.627,320,3.819,322,2.083,353,2.488,447,3.15,452,4.583,505,2.759,527,1.995,580,2.312,614,2.062,683,2.491,818,3.343,878,2.839,940,2.491,1170,2.801,1171,5.309,1391,5.862,1408,2.956,1693,3.895,1870,4.398,1884,4.394,3971,8.901,3972,6.064]],["t/818",[4,2.674,7,0.484,12,2.475,16,1.142,34,1.563,56,0.606,84,1.08,85,2.125,107,1.571,109,2.195,110,3.233,132,1.802,164,1.686,170,3.195,174,1.824,186,2.891,193,2.232,200,3.04,203,1.469,261,3.109,295,2.467,302,4.59,348,3.938,377,1.372,408,2.215,468,3.903,476,1.781,484,2.599,527,2.179,536,1.827,565,3.53,568,1.757,578,3.388,605,2.221,638,3.692,666,4.8,724,4.198,741,3.973,756,2.88,757,1.512,759,2.151,760,1.813,880,2.642,1114,2.784,1536,4.499,1558,6.832,1870,2.136,1884,4.049,1918,3.053,2033,3.455,2890,5.246,3508,5.113,3971,5.246,3973,5.589]],["t/820",[7,0.397,24,1.443,26,1.868,27,1.194,28,0.71,56,1.04,84,1.068,107,1.912,109,2.187,110,4.693,113,1.834,162,1.768,164,2.052,165,1.127,170,1.486,174,1.495,186,2.371,189,2.692,203,1.204,216,2.561,236,2.342,257,2.561,272,2.06,278,1.803,285,2.078,296,1.939,338,1.238,372,1.882,377,1.992,408,2.696,440,2.218,452,5.141,502,1.387,527,2.816,536,1.498,565,1.643,568,1.441,583,3.073,605,1.821,607,2.863,613,3.376,653,1.584,663,3.073,724,2.45,734,2.226,759,2.618,760,1.486,881,3.689,947,2.561,1133,2.706,1163,3.049,1170,2.293,1239,3.096,1245,3.651,1257,4.341,1289,2.035,1558,2.833,1870,1.751,1882,2.963,2608,3.743,3974,4.774,3975,8.454,3976,4.429,3977,3.638,3978,4.429,3979,4.774,3980,4.429,3981,5.031]],["t/822",[49,2.23,84,1.075,109,2.119,336,4.038,391,4.178,614,2.169,1289,4.014,1801,5.657,1870,3.454,2411,7.277]],["t/824",[7,0.731,25,2.181,27,2.684,28,1.308,31,1.664,56,0.673,84,1.225,107,1.744,109,1.149,123,2.322,127,3.879,132,2.722,162,2.394,165,2.076,213,2.772,219,2.109,256,2.549,281,3.223,304,1.436,318,1.659,338,1.676,536,2.029,557,3.39,565,3.026,567,2.732,568,3.017,614,1.706,615,3.986,630,2.516,635,2.472,810,4.116,865,5.433,909,4.689,995,4.637,999,5.041,1111,4.453,1209,5.998,1314,4.637,1397,4.13,1477,5.069,1487,4.541,1737,2.569,1754,3.94,1807,5.433,1998,3.959,3644,2.51]],["t/826",[28,1.706,107,2.859,109,1.883,127,3.965,162,3.191,171,3.033,246,3.433,304,1.913,338,2.233,614,2.056,857,4.119,877,3.311,1303,5.424,1737,3.424,3644,3.345,3982,9.08]],["t/828",[12,2.83,24,1.531,31,1.714,34,1.787,56,0.693,84,1.024,109,1.183,113,2.558,123,2.391,125,4,139,3.149,162,2.466,164,1.928,171,1.906,174,2.085,193,2.552,203,1.68,236,2.486,275,2.172,285,2.899,297,3.837,304,1.479,338,1.726,444,3.042,527,2.797,536,2.09,565,2.291,568,2.009,614,1.969,724,5.208,755,3.16,759,3.315,876,4.776,1245,3.539,1266,1.913,1737,2.646,1863,3.955,1902,3.346,2851,6.135,3644,2.585,3983,6.391,3984,6.658]],["t/830",[7,0.596,24,1.224,28,1.402,56,0.746,98,2.656,107,1.935,109,2.065,164,2.077,171,2.054,207,3.085,254,3.569,282,4.111,304,2.094,343,2.689,476,2.194,480,5.145,482,3.38,527,1.653,578,2.97,580,2.625,599,3.885,614,1.392,658,5.315,707,4.206,953,3.058,997,4.535,1245,3.288,1381,2.996,1408,4.412,1737,2.851,3644,2.784,3985,6.463,3986,7.173,3987,6.463]],["t/832",[16,2.098,28,1.301,56,0.911,132,3.311,254,2.965,304,2.376,318,2.745,338,2.269,351,3.338,502,2.543,580,3.914,734,4.081,997,4.174,1737,3.478,3644,3.397,3988,9.938]],["t/834",[7,0.574,24,1.569,25,2.33,27,2.586,28,1.367,56,0.957,84,0.788,90,3.429,103,2.435,106,3.993,107,2.789,109,2.04,115,3.351,174,2.163,254,2.34,256,2.723,302,3.351,304,1.534,362,5.693,416,3.351,513,3.429,527,1.591,550,3.808,615,4.258,623,3.485,755,3.278,950,6.747,992,3.638,1245,3.206,1352,3.499,1382,5.788,1408,4.303,1737,2.745,3053,4.901,3644,2.681,3985,6.222,3989,7.842,3990,6.407]],["t/836",[5,2.196,7,0.793,27,2.818,34,1.588,49,1.401,50,3.647,56,0.993,68,2.363,98,2.19,109,1.932,126,4.209,164,2.395,171,2.369,172,4.222,203,1.492,207,4.444,240,3.409,256,3.262,272,2.552,279,2.693,282,3.39,295,2.507,304,1.314,313,1.523,316,2.778,343,3.101,353,1.7,404,4.245,408,2.25,476,1.809,480,4.243,482,2.787,489,6.702,531,3.779,578,2.45,599,4.481,614,1.148,707,3.468,734,2.758,992,3.116,1289,3.527,1367,3.022,1671,3.428,1870,2.17,3652,3.931,3921,4.638,3987,5.33,3991,6.635,3992,5.916]],["t/838",[27,1.656,31,1.704,39,2.838,49,1.568,56,1.177,109,1.925,112,3.455,172,4.256,183,2.994,207,3.844,256,2.61,279,3.013,304,1.985,316,3.108,336,2.838,343,2.481,377,1.56,391,2.936,476,2.025,531,4.228,553,4.987,599,3.585,614,1.284,662,4.053,734,3.086,751,4.027,755,3.141,1013,5.19,1509,5.964,1689,4.937,1870,2.428,1884,4.603,2118,4.915,2297,2.323,2321,4.697,2867,4.081,2885,5.964,3042,4.081,3652,6.725,3921,5.19,3991,6.216,3993,6.14,3994,5.68,3995,6.14]],["t/840",[2,1.718,5,2.243,7,0.866,12,2.569,27,2.414,28,1.63,30,3.983,49,1.431,56,0.629,84,1.102,107,1.631,109,1.492,113,3.227,171,1.73,172,3.523,174,1.893,203,1.525,224,3.749,246,3.346,304,1.342,318,1.551,351,2.305,391,2.681,432,4.017,505,2.639,511,2.03,525,3.001,552,3.6,597,3.289,614,1.629,725,6.242,807,3.93,832,4.289,878,2.716,906,3.861,953,3.58,986,3.338,988,3.565,1012,3.608,1178,4.67,1380,4.017,1446,3.726,1737,2.402,1864,3.355,3004,5.307,3057,4.606,3644,2.346,3652,4.017,3910,4.982,3991,4.203]],["t/842",[0,3.524,4,1.893,5,3.026,7,0.85,24,0.988,27,2.36,28,0.861,31,1.491,34,1.554,49,1.372,56,0.603,84,0.931,109,1.676,113,2.226,123,2.08,140,2.392,162,2.145,164,1.677,171,2.334,172,2.976,173,4.597,174,2.553,184,4.613,193,3.125,196,2.419,203,1.461,228,1.934,255,4.111,261,3.093,275,1.889,289,2.412,304,1.286,320,3.502,323,2.462,353,1.664,377,1.365,426,2.254,432,3.85,484,3.639,505,2.53,550,2.399,568,1.748,596,2.749,614,1.83,632,3.284,725,3.108,728,4.25,755,3.869,802,2.469,828,2.545,877,2.226,934,2.911,953,2.469,1012,6.441,1066,3.011,1178,4.476,1216,3.108,1737,2.302,3003,4.867,3033,6.85,3644,2.249,3652,3.85,3921,4.542,3991,4.028]],["t/844",[16,1.974,28,1.497,56,1.048,113,3.051,132,3.116,171,2.273,172,4.036,173,4.477,196,3.316,275,2.59,304,2.236,318,2.583,338,2.058,351,3.028,430,4.127,502,2.307,580,3.684,729,5.971,734,3.702,827,4.24,1737,4.001,2782,5.522,3644,3.082,3996,11.433]],["t/846",[7,0.608,12,3.109,16,1.434,24,1.248,28,1.676,56,0.761,107,2.577,109,2.131,132,2.956,165,1.727,170,2.277,304,1.624,338,1.896,372,2.884,405,2.452,408,2.782,426,2.847,527,1.685,536,2.295,670,4.479,759,2.702,831,4.861,841,3.562,936,3.297,1114,3.497,1133,5.414,1237,5.191,1622,4.946,1737,2.907,1870,3.503,1884,5.087,3644,2.839,3997,6.642,3998,8.306]],["t/848",[56,0.988,107,2.563,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1408,4.444,1737,3.775,2296,7.339,3644,3.687]],["t/850",[56,0.988,127,3.282,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1408,4.444,1737,3.775,2295,7.448,3644,3.687]],["t/852",[56,1.118,84,1.007,107,2.38,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,1046,5.296,1266,2.535,1289,3.76,1622,5.965,1792,7.085,2296,6.816,3999,7.948]],["t/854",[56,1.118,84,1.007,127,3.048,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,1046,5.296,1266,2.535,1289,3.76,1622,5.965,1792,7.085,2295,6.917,4000,7.948]],["t/856",[29,2.637,56,0.981,105,2.364,109,1.872,112,3.745,127,2.479,162,2.656,203,2.379,206,4.895,295,3.04,318,1.84,322,3.933,336,3.076,391,3.182,395,2.987,398,3.5,614,2.171,828,3.152,997,2.799,1012,4.282,1123,5.394,1289,3.058,1633,4.682,1870,3.863,1918,4.944,2026,3.866,2297,2.518,2600,4.652,2851,4.336,3053,5.091,3652,4.767,3982,7.559,3991,4.989]],["t/858",[2,0.927,4,1.067,7,0.271,29,1.2,31,0.84,35,2.394,41,1.56,49,1.806,56,1.129,84,0.956,85,1.191,96,1.958,105,1.076,106,1.26,108,2.871,109,2.12,112,2.754,113,1.254,123,1.172,127,1.823,140,2.178,160,1.205,170,1.016,171,1.51,174,1.022,179,1.86,182,1.151,193,2.545,196,1.363,203,1.331,228,1.089,236,0.904,262,2.248,275,1.064,304,0.725,313,0.84,316,2.477,318,0.837,322,3.431,336,2.262,338,0.846,353,0.938,359,1.516,372,1.287,377,1.565,391,2.34,398,1.958,405,1.094,408,2.007,444,1.491,491,1.036,502,0.948,509,1.948,527,1.215,553,1.821,568,0.985,578,1.351,605,1.244,613,1.554,614,2.065,623,2.662,658,1.647,696,2.423,756,1.614,828,1.434,876,2.341,940,1.287,997,2.975,1012,1.948,1040,2.367,1123,2.454,1170,3.51,1238,2.054,1289,3.569,1434,1.913,1558,3.13,1588,3.132,1693,2.012,1705,2.1,1801,1.96,1870,4.402,1876,3.6,1918,2.766,2026,1.759,2275,2.642,2297,1.852,2600,2.116,2636,3.189,2799,2.69,2837,2.559,2851,3.189,3053,3.744,3652,3.506,3734,2.865,3980,3.027,3991,3.668,4001,3.263,4002,3.263,4003,3.263,4004,2.559,4005,2.742,4006,3.263,4007,3.263,4008,3.263,4009,5.982,4010,3.705,4011,3.705,4012,3.263,4013,3.263,4014,3.263]],["t/860",[2061,3.73]],["t/862",[7,0.958,15,3.812,572,5.536,846,5.394,3726,6.77]],["t/864",[7,0.889,27,2.189,49,2.073,56,0.911,100,4.124,243,5.044,246,3.487,277,4.74,401,3.525,405,3.586,511,2.94,723,3.296,841,4.262,934,4.398,1754,3.92,3533,6.086,3726,7.3,4015,10.698]],["t/866",[4,2.541,7,0.825,27,1.945,49,2.353,56,0.809,100,3.663,103,3.503,236,2.154,243,4.48,283,2.369,394,4.095,401,4.41,405,3.33,437,3.845,481,4.894,492,6.408,511,2.612,723,2.928,736,3.493,877,2.987,2225,5.572,2297,2.729,3513,8.368,3530,5.846,3531,5.846,3532,6.533,3726,6.779]],["t/868",[7,0.773,17,2.931,27,1.76,39,3.018,56,0.732,100,4.917,103,3.283,106,2.718,228,2.349,243,4.056,394,3.707,405,3.121,410,4.894,437,3.481,511,2.364,527,1.621,723,2.651,760,3.249,769,2.85,802,3,877,2.704,1245,2.454,1376,3.707,1754,3.152,1759,6.753,2225,5.223,2297,3.268,2789,5.048,2814,5.163,2815,5.163,2867,4.338,3483,7.824,3726,6.354,4016,8.063,4017,9.681,4018,6.528,4019,6.528]],["t/870",[7,0.689,10,4.728,21,2.47,24,1.415,27,2.075,56,0.863,84,0.947,100,3.909,243,4.781,353,2.384,394,4.369,405,3.469,511,2.787,568,2.503,683,3.271,723,3.124,759,3.064,760,2.583,857,3.966,1754,4.635,2225,4.653,2282,5.827,2297,2.912,3726,7.062,4020,10.35]],["t/872",[2061,3.73]],["t/874",[7,0.847,49,2.414,228,3.403,278,3.849]],["t/876",[7,0.951,34,2.043,49,2.568,56,0.792,84,1.119,103,2.685,203,1.921,228,2.542,278,2.875,283,2.32,332,4.164,382,4.978,393,3.941,401,3.067,408,2.897,513,3.781,580,2.786,614,1.477,723,4.083,1001,6.166,1202,5.442,1376,6.03,1504,5.655,1511,7.467,2708,5.802,4021,7.309,4022,6.277]],["t/878",[2,1.463,7,0.622,34,1.382,39,2.208,49,1.775,56,0.78,68,2.994,84,1.107,100,2.427,113,2.879,144,2.599,165,1.216,186,2.557,203,2.87,213,2.208,225,1.94,226,2.631,233,1.964,302,2.499,313,1.326,322,2.47,332,2.816,373,3.924,377,2.082,491,2.806,613,2.453,614,2.087,647,2.036,659,3.514,736,2.314,763,3.694,839,3.113,851,2.844,979,3.581,1008,3.289,1170,2.424,1202,5.973,1266,2.153,1290,3.019,1328,3.241,1367,2.631,1376,2.712,1394,3.617,1395,4.245,1737,2.978,1863,2.27,1870,2.748,1882,3.196,2015,6.927,2027,3.394,2395,5.943,2447,5.375,2636,3.113,2684,3.34,2898,6.58,2931,4.943,3182,4.522,3644,1.999,3857,3.778,3866,2.816,4022,4.245,4023,5.149,4024,5.149,4025,5.149,4026,5.149,4027,5.149,4028,5.149,4029,5.149]],["t/880",[2,1.915,7,0.946,15,2.589,21,2.007,49,2.418,85,3.302,103,2.376,106,2.603,113,2.589,179,2.376,203,2.282,228,2.249,283,2.756,313,1.735,332,3.685,401,4.586,481,4.242,491,2.873,512,2.448,562,3.487,614,2.209,628,3.667,723,2.538,736,3.028,820,3.65,833,5.004,854,3.78,953,2.873,1202,3.741,1565,4.944,1633,4.492,2225,3.78,2297,3.175,2395,3.722,3513,4.888,3530,5.068,3531,5.068,4022,5.555,4030,6.468,4031,7.102]],["t/882",[7,0.81,49,2.309,283,2.97,401,3.926,512,3.542,736,4.38,833,7.239,3530,7.33,3531,7.33]],["t/884",[7,0.853,16,1.338,24,1.164,28,1.014,34,1.831,49,1.616,56,0.71,68,2.725,84,1.252,105,3.386,162,2.526,219,2.225,246,2.718,256,2.69,261,4.87,285,2.97,295,2.891,318,2.636,353,2.621,377,1.608,398,2.532,401,2.748,512,2.478,536,2.141,614,1.323,734,3.181,759,3.795,828,2.998,1266,1.96,1284,3.515,1312,2.641,1408,3.192,1487,4.792,1918,4.782,2026,4.916,2027,6.011,2297,2.394,2600,5.915,2623,5.433,3514,5.524,4032,6.547]],["t/886",[7,0.822,49,1.828,56,1.197,84,0.881,140,3.188,283,2.352,318,1.981,336,3.31,377,1.819,393,3.995,398,2.866,401,3.11,568,2.33,683,3.044,736,3.469,1202,4.286,1266,2.218,1289,3.291,1381,3.224,1632,5.409,1918,4.048,2027,5.088,2481,8.006,2600,5.007,2623,6.148,3531,5.806,3532,8.311,4033,7.72,4034,6.956,4035,6.956,4036,7.72]],["t/888",[49,2.089,84,1.007,161,6.327,201,4.732,283,2.688,318,2.263,398,3.274,401,3.553,736,3.964,1266,2.535,1387,7.948,1918,4.625,2027,5.814,2481,6.472,2487,7.948,3530,8.084,3857,6.472,4034,7.948,4035,7.948,4037,8.821]],["t/890",[7,0.824,15,3.812,49,2.35,279,4.517,395,4.132,614,1.925,828,4.36]],["t/892",[2,1.491,7,0.436,17,2.185,23,2.672,24,1.772,28,0.78,48,2.243,49,1.799,56,1.162,84,0.867,98,1.943,113,2.016,162,2.813,165,1.793,171,1.502,174,1.643,203,2.252,219,1.711,228,2.536,232,2.011,246,3.558,248,2.595,272,2.263,313,1.956,338,2.537,377,1.236,433,3.192,524,3.403,530,2.472,536,1.647,545,2.555,568,1.583,614,1.018,705,5.791,757,2.318,831,3.487,852,2.397,914,2.751,930,6.436,1114,3.631,1266,1.508,1289,2.236,1312,2.031,1381,2.191,1486,4.054,1876,3.58,2027,7.137,2032,3.37,2034,5.036,2075,4.607,2623,6.05,2754,4.409,3047,4.607,3655,4.054,3866,4.883,4038,4.727,4039,5.247,4040,7.596,4041,5.247,4042,5.247,4043,5.247,4044,5.247]],["t/894",[7,0.796,56,0.997,304,2.127,397,4.197,580,3.505,651,6.729,898,4.895,1266,2.753,1285,5.684,1399,7.011]],["t/896",[7,0.832,304,2.223,438,5.475,651,7.032,1266,2.877,2025,7.973]],["t/898",[84,1.057,92,3.614,93,4.942,200,4.835,207,4.761,261,4.945,304,2.057,313,2.384,353,2.661,614,1.797,620,5.399,934,4.654,1266,2.661]],["t/900",[16,1.895,68,3.861,85,3.527,165,2.281,304,2.146,317,5.09,338,2.505,614,1.875,1801,5.805,2103,7.825]],["t/902",[5,2.947,21,2.364,28,1.644,36,3.69,56,0.826,96,2.947,98,2.94,106,4.27,107,2.142,109,1.789,170,3.135,186,3.943,203,2.003,256,3.97,296,3.225,304,1.763,343,2.976,368,6.324,404,4.074,513,3.943,568,2.396,606,6.43,611,3.478,694,4.551,774,4.526,1066,4.127,3106,6.545,4045,6.814]],["t/904",[109,1.763,203,2.503,304,2.203,404,5.091,1170,3.21,1705,6.385,3968,8.939]],["t/906",[7,0.925,15,2.721,21,2.784,24,1.786,26,2.77,36,3.291,46,5.055,56,1.089,90,3.517,98,2.622,100,3.337,106,2.735,112,4.881,144,3.574,183,3.203,186,3.517,203,1.786,217,4.254,275,2.31,279,3.224,304,2.076,359,3.291,404,3.634,408,2.694,527,2.565,607,2.86,622,3.764,719,4.49,856,4.458,1133,4.015,1198,6.57,1245,2.469,1367,3.618,4045,6.077,4046,7.082]],["t/908",[24,1.594,27,2.336,28,1.389,126,4.752,128,4.693,304,2.47,367,5.01,404,4.792,650,6.628,825,7.562,3401,7.438,4047,9.339,4048,9.339]],["t/910",[7,0.716,24,1.808,28,1.281,107,2.325,109,1.531,113,3.311,162,3.924,165,2.034,171,3.033,304,1.913,527,1.985,614,1.672,759,3.183,760,2.683,1046,4.245,1245,3.004,1737,3.424,1863,3.798,2803,8.27,2851,6.405]],["t/912",[7,0.762,16,1.08,21,2.98,28,1.574,32,3.629,39,3.934,56,0.818,68,3.142,100,2.594,107,1.485,127,1.903,165,1.3,181,3.287,183,2.49,197,2.585,203,1.984,207,2.368,223,3.267,253,3.491,304,1.746,315,2.756,316,2.585,336,2.361,337,2.702,339,2.927,343,3.967,379,6.906,404,2.825,430,2.862,477,3.441,480,3.949,482,2.594,502,1.6,508,3.418,555,2.745,577,4.989,613,3.746,614,1.526,623,2.778,635,2.105,690,3.6,694,3.156,723,3.769,747,3.403,757,2.042,778,3.659,936,2.482,997,2.148,1046,2.713,1164,2.927,1180,6.651,1367,2.813,1473,2.585,1693,3.394,1865,4.725,1900,3.907,2522,4.458,3042,3.394,3401,4.385,4049,9.174,4050,4.725,4051,5.285]],["t/914",[7,0.91,15,3.5,20,7.224,21,2.713,50,5.616,84,1.04,106,3.518,116,3.582,304,2.434,408,3.465,580,3.333,614,1.767,796,5.032,1367,4.654]],["t/916",[7,0.839,85,3.687,278,3.814,580,3.696,1312,3.911]],["t/918",[8,4.743,16,1.179,23,2.943,56,0.871,96,2.231,140,2.482,177,3.059,179,2.12,219,2.729,275,1.961,304,2.631,318,2.147,366,3.962,393,3.111,438,5.69,447,2.997,500,2.997,545,2.928,548,5.426,552,2.242,555,2.997,558,3.942,614,1.166,802,2.562,852,4.753,898,3.071,902,4.411,948,4.141,1059,3.545,1170,2.707,1312,4.659,1531,4.222,1638,3.271,2025,4.788,2253,5.158,2280,4.955,2299,5.158,2895,3.899,3042,6.743,4052,6.011,4053,8.367,4054,5.158,4055,6.011,4056,6.663,4057,5.416]],["t/920",[113,3.417,248,5.755,275,2.9,304,1.974,353,2.555,484,3.97,854,4.987,916,5.445,1043,6.363,1044,7.222,1312,3.443,1638,4.839,1816,6.972,2624,7.329]],["t/922",[7,0.818,34,2.058,56,1.132,84,0.875,85,3.594,97,3.613,224,3.423,254,2.598,322,2.527,353,2.203,482,3.613,495,3.901,572,4.278,580,2.805,611,3.359,614,1.487,709,5.281,762,5.441,839,4.635,893,4.665,1046,4.851,1312,3.812,1408,4.607,1633,3.807,2061,2.728,2481,8.71,4058,7.667,4059,7.359,4060,7.667]],["t/924",[7,0.727,84,0.999,171,2.506,246,3.487,304,1.943,491,2.78,580,3.914,614,1.698,723,3.296,1312,3.389,1376,4.61,1511,6.028,1638,4.763,1737,3.478,2225,4.909,2297,3.072,3644,3.397,3858,9.437]],["t/926",[2,1.418,7,0.414,15,1.917,16,1.436,36,2.318,48,2.132,49,2.054,56,0.903,84,1.214,133,2.249,160,1.843,165,1.177,179,1.759,197,2.342,219,1.627,224,2.227,246,2.917,248,4.29,255,3.54,275,2.388,302,2.42,313,1.885,317,2.627,318,2.611,377,1.725,438,4.744,441,2.859,495,4.862,580,1.825,614,0.968,655,3.436,724,2.559,736,2.241,744,2.996,757,1.295,802,2.126,891,6.782,898,2.549,938,4.112,940,2.887,1043,4.744,1133,2.828,1280,3.54,1312,4.592,1380,4.866,1399,5.384,1441,3.163,1898,2.769,1944,4.38,2061,1.775,2289,4.38,2299,4.28,2307,5.372,2481,6.364,2683,4.749,2726,3.751,3749,3.66,3858,7.493,4061,4.988,4062,4.988,4063,4.988,4064,4.988,4065,4.988,4066,4.988,4067,7.321,4068,4.988]],["t/928",[23,3.231,203,2.779,318,2.357,322,3.631,614,2.137,1312,4.265,3042,6.791,3644,4.276,4069,9.185,4070,9.185]],["t/930",[2,1.699,7,0.497,16,1.172,23,2.932,56,0.622,219,3.13,246,2.382,278,2.258,283,1.822,304,2.669,313,1.539,318,2.139,545,2.912,548,3.878,552,2.23,614,1.16,756,5.945,852,4.383,891,4.243,940,2.357,1043,3.27,1059,7.089,1223,3.569,1312,2.315,1882,7.46,2253,5.131,2683,3.878,3858,6.536,4057,5.387,4071,5.979,4072,5.547,4073,5.979,4074,5.979,4075,5.979,4076,5.979,4077,5.979,4078,5.979,4079,5.979,4080,5.979,4081,5.979,4082,5.979,4083,5.979]],["t/932",[2,2.093,7,0.797,24,1.256,25,2.484,28,1.095,35,5.402,39,4.113,49,2.272,84,0.84,109,1.308,116,2.895,197,3.457,304,1.635,313,1.896,336,3.157,391,3.266,404,3.778,505,3.215,614,1.861,635,2.815,743,6.671,760,2.292,802,3.138,830,3.546,1048,6.187,1049,6.187,1051,5.962,1052,7.213,1671,4.267,1870,2.7,1918,3.861,2004,7.067,2668,5.468,3007,5.962,3055,6.318,3691,7.067,4084,9.592,4085,7.363,4086,7.363]],["t/935",[7,0.856,17,3.429,24,1.405,31,2.12,84,1.176,123,2.957,133,3.712,162,3.049,285,3.585,304,1.828,338,2.134,375,6.309,393,4.261,527,1.897,568,2.485,614,1.597,690,5.383,759,3.042,760,2.564,865,6.919,1046,5.076,1055,5.726,1737,3.272,1807,6.919,3644,3.196,4087,8.234]],["t/937",[5,2.715,7,0.608,16,1.434,25,3.222,84,1.287,162,2.708,164,2.117,171,2.734,219,2.386,236,2.027,304,1.624,318,2.892,377,1.724,398,2.715,614,2.063,683,2.884,723,3.597,724,3.753,731,4.04,757,2.48,886,4.903,945,4.02,1046,4.705,1133,4.147,1284,3.769,1312,3.698,1629,5.367,1737,2.907,1871,6.277,2032,3.245,3644,2.839,3749,5.367,4088,6.59]],["t/939",[7,0.684,49,1.95,84,1.176,171,2.357,192,5.111,248,4.073,285,4.485,304,1.828,336,3.531,536,2.584,605,3.14,614,1.597,662,5.043,731,4.548,1043,6.148,1046,4.057,1127,7.639,1133,4.668,1399,6.979,1540,4.8,1737,3.272,3644,3.196,3749,6.041,3754,7.419]],["t/941",[7,0.764,31,2.661,35,5.067,37,4.851,39,4.433,56,1.147,63,5.592,68,2.759,84,1.05,95,4.515,100,3.254,109,1.227,203,2.32,236,1.914,283,2.104,377,2.436,481,4.347,614,1.784,734,3.22,1129,4.552,1266,1.984,1320,4.629,1595,8.826,1689,5.71,1754,3.093,2112,5.803,2497,5.693,2550,5.263,2708,5.263,2789,4.954,3412,5.693,3413,5.263,3652,4.59,3994,5.926,4089,6.906,4090,6.906,4091,6.906,4092,6.906,4093,6.906,4094,6.906]],["t/943",[7,0.872,113,3.26,181,5.065,203,2.648,334,5.065,491,2.695,512,3.083,614,1.646,934,4.264,1170,2.745,1202,4.711,1266,2.438,1395,6.994,1642,7.645,1754,3.8,1870,3.85,2015,6.653,2395,4.687,3866,4.64,4095,8.485,4096,7.451,4097,7.451,4098,8.485]],["t/945",[1,3.783,5,2.293,7,0.811,25,3.294,56,0.887,84,0.973,126,3.143,131,5.281,135,3.711,157,3.759,162,2.288,165,1.458,174,2.671,203,1.558,254,2.889,314,5.191,322,2.036,341,3.581,369,4.039,370,5.093,375,3.783,449,4.007,476,1.89,500,3.08,525,3.068,532,3.395,555,3.08,590,3.502,611,2.706,614,1.199,621,3.346,623,3.118,675,4.141,677,3.43,683,2.436,830,2.975,846,2.891,898,3.157,929,4.432,931,5.003,1181,3.379,1389,6.103,1390,4.646,1435,4.384,1474,5.003,1482,4.92,1527,4.297,1601,3.889,1936,5.732,2297,2.169,2789,4.432,2851,3.735,3042,5.257,3264,5.301,3508,5.425,4099,6.178,4100,5.425,4101,6.178,4102,8.527]],["t/947",[2061,3.73]],["t/949",[7,0.811,15,3.276,16,1.211,21,1.84,24,1.455,26,2.417,28,0.919,34,1.658,58,4.708,70,7.343,84,0.973,98,2.288,99,4.106,106,3.293,145,3.613,158,6.016,164,1.788,175,5.627,203,1.558,242,4.588,246,2.462,304,1.372,343,3.196,353,2.45,381,3.08,401,4.6,402,5.093,408,4.202,410,4.297,412,4.844,413,4.92,416,2.998,453,4.178,532,3.395,536,1.939,572,3.447,605,2.356,744,3.711,794,4.533,854,3.465,857,2.953,877,2.374,953,2.633,1408,2.891,3020,4.708,3106,5.093,4103,6.178,4104,6.178]],["t/951",[0,3.177,2,1.484,7,0.74,16,1.484,24,1.292,28,0.776,33,3.137,34,1.401,41,2.496,56,0.927,84,0.596,97,3.567,98,1.934,131,2.496,134,2.738,138,4.595,153,2.815,162,1.934,175,3.009,220,3.387,221,2.032,245,3.287,304,1.681,316,2.452,332,4.14,353,2.559,359,2.426,365,5.536,394,2.75,396,6.24,403,3.531,407,3.606,408,2.88,409,2.788,438,5.912,439,5.214,440,3.517,441,5.942,442,4.585,443,3.878,444,2.385,445,3.471,446,3.5,447,2.603,448,5.012,449,4.91,450,7.266,451,4.585,452,3.787,453,5.119,454,5.012,459,4.304,484,2.331,536,1.639,580,2.77,614,1.013,637,3.531,742,4.989,920,3.198,938,4.304,959,3.668,981,3.414,1314,3.746,1399,3.241,2874,4.844,4105,4.481,4106,5.222,4107,4.705,4108,4.304]],["t/953",[7,0.653,16,1.08,21,1.639,24,1.342,26,2.154,28,0.819,29,2.024,31,1.417,44,4.961,56,0.818,84,0.898,97,3.706,105,1.815,132,1.704,138,4.253,153,2.968,175,3.173,189,3.105,199,2.862,224,4.468,239,3.328,294,3.121,340,3.867,353,2.636,365,4.199,377,1.298,394,2.9,402,7.562,403,3.723,405,2.636,438,4.301,439,5.417,441,4.507,442,4.835,443,4.089,451,4.835,452,3.993,455,8.587,456,10.159,466,4.089,496,3.544,504,3.517,557,2.887,614,1.068,742,5.183,744,3.307,757,1.43,778,3.659,807,2.242,898,2.813,902,4.04,981,3.6,1399,3.418,1632,3.011,1998,3.372,3479,5.285,4105,4.725,4107,4.961,4108,4.539,4109,5.506,4110,4.385,4111,4.961,4112,5.506,4113,7.864,4114,5.506,4115,5.506,4116,5.506,4117,5.506]],["t/955",[5,3.3,24,1.843,49,2.106,70,6.687,106,4.172,286,5.679,304,1.974,351,3.391,404,4.562,408,4.109,528,4.936,536,3.39,621,4.816,744,5.341,804,8.011,4118,8.534]],["t/957",[2,2.375,24,1.774,34,2.79,106,3.228,126,4.252,175,4.817,254,3.523,256,3.295,304,1.856,353,2.402,408,4.505,409,4.463,447,4.167,460,8.022,461,4.929,462,8.022,463,5.299,464,7.53,465,6.132,466,6.206,629,5.602,2784,7.339]],["t/959",[2061,3.73]],["t/961",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,891,8.588,1312,4.182,2297,3.121,2683,5.767]],["t/963",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,877,2.966,1328,4.86,1389,4.825,1531,5.423,2297,2.71,2447,5.538,3690,7.162,4119,10.474,4120,7.41,4121,7.41,4122,7.41,4123,9.492]],["t/965",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,863,8.832,873,9.359,888,5.609,891,7.741,1138,7.196,1789,7.754,2683,5.861]],["t/967",[2061,3.73]],["t/969",[1,2.758,7,0.678,12,1.914,16,0.883,28,1.009,34,1.209,49,1.607,68,1.799,84,0.932,92,3.186,93,4.353,96,1.672,98,2.513,99,2.993,109,1.451,116,1.771,127,1.556,165,1.602,171,2.338,172,4.336,173,2.54,179,1.588,193,3.129,203,1.136,207,2.919,224,2.011,228,1.503,236,1.248,256,1.776,257,2.416,272,2.927,276,3.542,296,1.829,304,1,318,1.156,323,1.914,332,3.711,336,1.931,337,2.21,343,1.688,377,1.061,439,3.102,445,2.993,476,2.076,491,1.431,500,2.245,552,4.082,568,2.048,580,1.648,601,2.228,614,0.874,620,2.625,686,3.712,707,2.641,728,4.979,734,3.164,769,1.824,802,1.92,810,2.107,816,2.689,927,3.587,986,2.488,988,4.002,998,3.046,999,2.581,1006,2.428,1013,3.531,1040,3.267,1042,2.856,1066,2.341,1266,1.294,1362,3.432,1363,3.449,1365,4.472,1805,5.959,1870,1.652,1953,3.889,2106,4.058,2118,3.345,2353,2.835,2905,3.387,3652,2.993,3796,3.647,3857,3.304,3991,3.132,3997,3.132,4050,3.865,4124,3.865]],["t/971",[0,2.096,17,1.435,19,1.617,56,1.245,68,2.751,84,0.629,85,1.257,100,2.596,105,1.135,109,0.612,112,5.389,123,1.237,172,3.35,193,2.112,217,3.31,225,1.297,243,1.985,304,1.224,318,0.884,322,2.593,324,3.075,377,0.812,387,2.528,391,1.528,408,2.096,491,1.75,502,1.001,552,3.594,609,2.373,641,3.025,725,2.956,726,5.42,734,2.569,747,1.491,765,2.153,802,1.468,828,2.421,829,1.912,833,2.558,878,1.548,929,6.583,952,6.263,953,3.668,954,3.87,956,3.306,957,3.025,958,3.795,959,7.96,960,3.196,961,3.306,962,3.996,963,5.288,964,3.306,965,3.662,966,2.743,967,3.306,968,5.288,969,3.306,970,3.306,971,4.616,972,3.306,973,2.528,974,3.306,975,3.306,976,3.306,977,3.306,978,2.445,979,2.396,980,3.996,981,2.252,982,3.306,983,3.306,984,3.306,985,3.306,986,5.069,987,3.306,988,5.904,989,3.306,990,3.306,991,3.306,1009,4.453,1123,4.143,1363,2.091,1367,2.815,1417,2.396,1737,3.127,2438,7.093,3553,2.743,3643,5.288,4125,9.636,4126,3.104]],["t/973",[0,1.482,1,1.491,2,1.533,5,0.904,7,0.723,16,0.477,17,1.014,19,1.143,21,1.607,26,1.619,27,1.939,28,1.507,31,1.389,34,0.654,49,0.98,56,0.253,68,1.653,84,0.814,85,1.51,89,2.502,92,2.105,93,2.409,98,1.532,103,1.459,105,0.803,106,0.94,107,1.923,109,1.377,116,1.627,126,1.239,134,2.17,135,1.463,138,1.035,140,2.228,144,1.229,145,1.032,153,2.23,160,0.9,163,1.728,164,0.705,165,0.575,169,1.403,171,1.185,172,2.602,173,5.295,174,1.993,179,0.859,189,1.373,190,1.319,192,1.512,193,3.8,203,1.361,207,1.78,224,2.409,232,0.933,236,0.675,257,1.306,275,0.794,276,1.271,288,1.694,289,2.969,304,0.541,313,1.389,317,2.179,318,0.625,322,1.364,323,1.035,336,1.044,337,2.648,338,1.073,339,1.294,343,1.551,351,0.929,372,0.96,377,0.574,383,1.787,391,1.836,401,0.981,426,2.1,441,1.396,458,1.445,491,0.773,499,1.787,502,0.708,504,1.556,505,1.063,509,1.454,541,1.579,552,1.543,553,2.309,558,1.148,565,1.856,568,1.628,591,1.463,597,1.325,601,1.205,611,1.067,614,1.047,621,1.319,631,1.677,632,5.142,635,0.931,638,2.624,653,0.808,667,2.435,672,1.512,691,1.411,723,0.917,741,2.824,743,1.694,750,1.808,756,1.205,772,2.878,774,1.388,795,1.677,807,2.903,810,1.139,811,1.592,812,2.907,822,1.533,827,2.881,828,2.371,829,1.352,877,2.739,906,2.643,934,1.224,947,1.306,949,1.909,952,2.824,986,2.98,988,4.204,1012,5.415,1066,1.266,1114,1.164,1122,1.605,1123,1.831,1164,1.294,1169,1.632,1223,1.454,1274,1.567,1290,1.428,1303,2.605,1360,2.486,1363,2.047,1365,1.605,1376,3.351,1379,1.972,1397,1.556,1445,3.036,1557,1.592,1693,1.501,1696,1.632,1864,2.995,1870,1.518,1915,2.09,1935,2.194,2092,1.436,2353,4.005,2653,3.477,2687,2.259,2783,3.728,2787,1.632,2963,1.831,2996,2.194,2999,3.972,3089,3.551,3629,1.605,3644,2.094,3646,2.337,4127,2.435,4128,2.566,4129,4.361,4130,2.259]],["t/975",[2061,3.73]],["t/977",[2,1.827,7,0.96,15,2.47,16,1.26,23,2.261,24,1.495,29,2.363,46,2.807,49,1.522,56,0.669,84,0.734,108,3.498,138,2.732,145,2.724,162,2.38,165,1.517,213,2.756,228,2.146,248,3.179,256,2.534,281,3.204,313,2.256,317,3.386,377,1.515,440,2.987,441,5.021,458,3.814,481,4.046,534,4.834,536,3.359,546,5.644,581,4.898,605,2.451,613,3.062,742,4.236,744,5.263,747,2.781,798,3.725,846,4.099,855,4.773,857,3.073,959,6.154,1163,4.106,1397,4.106,1441,4.076,1784,5.791,2307,4.716,2887,5.516,2966,4.611,3933,5.963,4056,5.119,4131,6.428,4132,6.428,4133,6.428]],["t/979",[7,0.572,16,1.621,24,1.176,26,1.798,56,1.289,84,0.525,114,3.863,123,2.475,232,1.762,304,1.021,306,2.151,342,4.746,405,2.31,449,4.469,513,2.283,545,2.239,552,1.715,623,2.32,780,2.578,822,2.894,1285,4.905,1597,4.58,1816,3.604,1822,2.915,1933,6.45,2108,3.944,2306,6.393,2307,6.065,2308,5.913,2310,3.944,2311,9.45,2312,5.178,2313,5.913,2317,4.036,2318,4.036,2319,4.036,2320,4.036,2321,3.262,2322,5.913,2485,3.03,2550,3.503,3456,6.051,3577,4.036,4110,5.488,4111,4.142,4134,4.142,4135,5.79,4136,4.142,4137,6.209,4138,4.597,4139,6.891,4140,4.597,4141,4.597,4142,4.597,4143,4.597,4144,4.597,4145,4.597,4146,4.597,4147,4.597,4148,4.597,4149,4.597,4150,4.597,4151,4.597,4152,4.597,4153,4.597,4154,4.597,4155,4.597,4156,4.597,4157,4.597,4158,4.036,4159,4.597,4160,4.597,4161,4.597,4162,4.597,4163,6.209,4164,6.209,4165,4.597,4166,4.412,4167,4.264,4168,4.597,4169,6.891,4170,4.597]],["t/981",[7,0.846,16,1.839,24,0.978,26,2.243,56,1.27,84,0.654,100,3.813,114,4.817,123,2.906,232,2.197,248,2.836,304,1.273,342,5.574,449,5.249,545,2.792,744,3.444,790,5.938,855,4.257,1597,5.379,1633,2.847,1933,7.158,2108,6.944,2307,5.938,2308,6.944,2310,4.919,2311,10.046,2312,5.746,2313,6.944,2317,5.034,2318,5.034,2319,5.034,2320,5.034,2321,4.068,2322,6.944,4110,6.445,4134,5.165,4135,6.8,4136,5.165,4137,7.292,4163,7.292,4164,7.292,4171,5.733,4172,5.733,4173,5.733,4174,5.733]],["t/983",[56,1.014,164,2.822,550,4.036,614,1.891,744,5.855,814,4.423,1704,8.56,2301,10.952]],["t/985",[2061,3.73]],["t/987",[28,1.377,109,1.967,139,4.38,170,2.883,183,4.189,395,3.857,484,4.135,760,2.883,828,4.07,1408,4.333,1558,5.495,2818,6.441,3664,6.007,3916,6.505]],["t/989",[4,2.243,7,0.57,16,1.346,18,3.178,19,3.223,24,1.171,28,1.817,84,1.045,98,2.542,103,2.42,107,2.471,109,1.954,165,2.162,170,2.851,196,2.867,221,2.671,286,4.384,289,2.858,397,3.007,444,3.135,527,1.581,550,2.842,565,2.361,568,2.071,757,1.782,760,2.137,854,3.85,877,2.637,916,4.203,1006,3.7,1234,4.097,1245,4.108,1266,1.972,1558,5.434,2064,5.162,2488,4.384,3657,7.233]],["t/991",[4,3.272,28,1.488,107,2.701,109,1.779,505,4.372,1266,2.877]],["t/993",[12,4.179,24,1.678,109,1.747,254,3.332,512,3.573,578,4.072,614,1.908,1754,4.404]],["t/995",[7,0.782,12,4.003,24,1.607,109,1.674,128,4.733,254,3.191,578,3.9,614,1.827,681,6.207,736,4.232,1075,5.768,1266,2.706,1616,5.522]],["t/997",[24,1.691,28,1.584,31,2.238,34,2.333,36,2.953,39,2.725,49,1.505,84,0.725,109,1.545,116,2.499,132,1.967,133,2.865,139,3.005,164,2.868,165,1.5,170,3.474,203,2.193,285,2.766,289,2.646,338,1.647,353,1.826,367,3.409,382,4.154,394,3.347,397,2.784,414,3.841,505,2.775,525,3.155,614,1.233,693,4.121,756,3.143,759,3.211,760,3.085,769,4.314,830,3.06,854,3.564,916,3.891,946,4.808,1216,3.409,1506,4.297,1558,3.77,1671,3.683,1792,4.188,2696,4.51,2818,4.419,3917,4.259,3922,5.725]],["t/999",[7,0.711,21,1.85,29,2.284,31,2.849,39,2.664,49,1.471,125,3.732,139,4.631,165,1.467,170,3.774,197,2.917,203,2.662,213,3.67,236,1.721,246,4.205,272,2.68,292,2.848,304,1.38,323,2.64,338,1.61,414,3.756,491,1.973,605,2.369,614,1.205,734,2.897,759,3.162,760,3.285,769,4.909,811,4.062,1188,5.031,1216,3.333,1366,3.642,1367,3.174,2818,6.81,3042,3.83,3664,5.552,3917,4.164]],["t/1001",[7,0.893,28,1.312,139,4.171,145,3.738,170,2.746,203,2.225,246,4.283,285,3.84,304,1.959,614,1.711,759,3.971,828,3.876,3923,7.025,3924,9.224,3925,6.722,3926,7.569,3927,7.569]],["t/1003",[7,0.893,28,1.312,139,4.171,145,3.738,170,2.746,203,2.225,246,4.283,285,3.84,304,1.959,614,1.711,759,3.971,828,3.876,3923,7.025,3925,6.722,3928,9.224,3929,7.569,3930,7.569]],["t/1005",[7,0.596,28,1.402,49,1.699,56,0.746,84,1.202,103,2.529,109,1.676,164,2.077,170,2.233,217,4.309,279,3.266,377,1.691,398,3.5,565,2.468,760,3.28,811,4.689,996,4.767,1245,2.501,1530,4.548,1550,5.624,1558,5.595,1918,5.523,1998,4.393,2013,4.767,2600,4.652,2814,5.263,2822,5.808,3734,9.825,3954,6.655,4009,8.496,4175,12.705,4176,7.559,4177,7.173]],["t/1007",[2061,3.73]],["t/1009",[7,0.796,24,1.635,28,1.424,219,3.684,502,2.784,536,3.006,550,3.967,565,3.296,568,2.891,912,7.029]],["t/1011",[7,0.637,28,1.464,34,2.642,49,2.332,163,4.112,188,5.842,219,2.501,221,2.984,295,4.172,353,2.203,377,1.807,476,2.345,484,4.396,500,3.822,502,3.16,512,3.577,536,2.406,555,3.822,577,3.822,623,3.869,653,2.543,757,2.824,780,4.301,930,4.419,1312,2.969,2009,5.561,2032,3.401,2711,6.719]],["t/1013",[4,1.893,7,0.784,25,1.954,56,1.065,101,3.249,123,2.08,132,1.793,165,1.367,196,2.419,203,1.461,219,3.339,236,1.605,285,2.522,313,1.491,317,3.051,338,1.501,348,3.917,359,2.691,372,3.214,377,1.365,449,3.757,512,2.104,527,1.878,536,3.213,565,3.245,568,2.46,605,2.209,613,2.759,630,2.254,653,1.921,736,2.603,757,2.658,930,4.698,955,4.775,1107,3.216,1139,3.266,1202,3.216,1245,2.02,1284,2.985,1328,3.646,1700,6.392,1844,3.728,2030,5.086,2031,4.069,2032,4.541,2297,2.033,2312,6.084,2395,3.199,2447,4.155,2633,3.357,2711,3.953,3037,7.345,3857,4.25,4178,5.792,4179,8.152,4180,5.792,4181,5.792]],["t/1015",[28,1.451,34,2.03,49,1.791,84,0.863,96,2.807,165,1.785,190,4.096,219,2.467,232,2.899,295,3.205,304,1.679,398,4.011,461,4.46,536,3.709,552,2.821,567,4.124,605,2.884,614,1.467,731,4.177,757,2.534,852,3.455,1033,5.07,1266,2.173,2032,4.329,3016,7.495,3866,4.136,4182,7.563,4183,7.563]],["t/1017",[1,0.992,7,0.762,12,0.689,16,1.2,17,0.675,23,1.02,24,1.89,25,1.327,27,0.405,28,1.526,34,1.056,39,0.695,41,1.386,48,0.693,49,0.687,56,0.852,68,0.647,84,0.934,88,0.769,89,1.883,98,0.6,103,1.688,105,0.956,107,1.061,109,1.087,116,1.14,123,0.582,128,1.457,132,2.621,133,0.73,160,0.599,162,1.073,163,1.643,164,0.839,165,0.929,170,0.903,179,1.022,182,2.343,183,1.311,196,0.677,198,0.831,199,0.842,203,1.676,219,1.283,220,1.88,221,1.128,223,0.961,232,1.835,236,1.327,246,1.155,255,1.15,275,0.528,277,0.878,278,0.612,283,0.494,285,2.397,286,1.852,289,1.207,295,0.687,296,0.658,302,0.786,323,0.689,324,0.904,328,2.016,337,0.795,338,1.02,343,0.607,347,1.127,351,0.618,353,0.833,372,1.551,377,1.566,392,1.059,398,0.601,401,0.653,407,0.772,409,1.548,416,0.786,426,1.864,430,0.842,444,0.74,463,1.027,472,1.487,476,2.406,482,0.763,483,1.077,484,1.294,512,2.581,525,0.805,527,1.73,528,0.899,530,0.763,536,2.23,545,0.789,555,0.808,560,0.909,565,2.706,568,1.662,575,2.397,578,0.671,580,0.593,601,1.434,605,1.5,611,1.27,614,1.588,621,0.878,630,2.143,635,0.619,647,0.64,653,1.827,681,1.911,685,0.95,723,1.092,736,1.768,743,1.127,756,0.801,757,2.327,759,2.26,760,1.225,769,1.593,780,0.909,803,1.72,811,1.059,832,1.15,841,0.789,846,1.356,855,2.153,857,1.881,874,0.838,878,0.728,880,0.735,930,0.934,936,2.482,940,2.171,942,3.988,1046,0.798,1059,1.71,1069,2.242,1075,1.775,1114,2.633,1139,0.914,1169,1.086,1170,1.273,1174,1.138,1181,0.886,1223,1.731,1239,1.051,1240,0.973,1242,1.361,1245,1.67,1266,2.575,1312,0.627,1363,2.984,1367,0.828,1380,1.077,1381,0.677,1397,1.035,1408,1.841,1454,2.572,1476,1.122,1493,1.943,1530,1.027,1616,1.7,1633,0.805,1671,0.939,1733,1.02,1863,1.278,1902,0.814,1906,0.929,1907,0.882,1960,1.138,2027,1.068,2028,3.306,2031,3.364,2032,0.719,2033,0.961,2035,1.46,2061,0.576,2075,1.423,2159,1.503,2353,1.02,2606,1.336,2633,0.939,3051,1.15,3632,1.027,3649,2.488,3664,1.051,3715,3.186,3866,1.585,3916,1.138,3917,1.086,3942,1.943,3983,1.555,3997,1.127,4038,1.46,4056,1.29,4184,1.62,4185,1.203,4186,1.203,4187,1.203,4188,1.203,4189,1.203,4190,1.203,4191,1.203,4192,1.312,4193,1.555,4194,1.62,4195,1.503,4196,1.62,4197,1.62,4198,2.899,4199,1.62,4200,1.62,4201,1.62,4202,1.555,4203,1.62,4204,1.62,4205,1.62,4206,1.62,4207,1.62,4208,1.62,4209,1.62,4210,1.62,4211,1.62,4212,1.62,4213,1.62]],["t/1019",[7,0.703,23,1.383,24,1.878,27,0.983,28,1.364,31,1.012,39,1.686,48,1.681,49,0.931,56,0.881,84,0.857,88,1.866,89,3.329,98,1.456,103,1.386,116,2.405,128,1.976,132,1.217,163,2.554,171,1.126,177,2,182,2.156,198,2.017,219,1.282,221,1.53,225,1.481,232,2.344,285,1.712,286,2.511,302,1.907,304,0.873,313,1.012,322,1.296,328,2.734,343,1.473,377,0.927,401,1.583,444,1.796,472,2.017,502,1.142,512,3.528,527,1.409,545,1.915,555,1.96,565,2.582,575,3.061,614,1.779,630,2.38,653,2.808,670,2.408,681,2.591,736,1.766,756,1.944,757,1.949,769,3.429,780,2.205,807,1.601,841,1.915,857,1.879,936,1.772,942,4.082,946,4.45,1069,4.277,1075,2.408,1170,1.272,1216,2.109,1245,2.132,1266,2.791,1363,3.213,1376,2.071,1408,3.511,1476,2.368,1557,2.57,1616,2.305,1633,1.952,1671,2.278,1754,1.761,1765,2.376,1906,3.505,1907,2.139,2100,2.996,2672,2.708,2696,2.79,3632,2.493,3664,3.966,3693,5.37,3916,2.761,3917,2.635,4185,2.919,4186,2.919,4187,2.919,4188,2.919,4189,2.919,4190,2.919,4191,2.919,4192,3.183,4195,3.647,4214,5.51,4215,3.542,4216,3.038,4217,3.647,4218,2.956]],["t/1021",[7,0.311,23,2.072,24,1.53,28,1.415,34,1.005,39,1.606,48,1.602,49,0.887,56,0.757,63,3.033,84,0.83,88,1.778,89,3.243,98,1.387,103,1.321,116,2.316,163,3.039,171,1.072,177,2.996,182,2.076,198,1.922,203,1.485,219,1.222,221,1.458,228,1.25,232,2.257,283,1.141,285,1.631,286,2.393,295,1.587,304,0.832,322,1.235,328,2.605,338,0.971,343,1.404,377,1.388,401,1.509,405,1.256,426,1.458,444,1.711,472,1.922,476,2.911,491,1.19,502,1.711,512,3.617,527,1.357,536,1.848,545,2.868,558,1.765,565,2.026,575,3.642,577,5.141,614,1.739,630,2.292,653,2.735,683,1.477,736,1.683,756,1.853,757,1.889,780,2.101,796,2.069,841,1.825,936,3.28,940,2.322,941,2.895,942,4.235,1037,3.313,1046,1.846,1069,4.147,1170,1.905,1245,2.053,1266,3.048,1363,3.402,1476,2.28,1616,2.196,1633,1.86,1754,1.678,1906,3.375,1907,2.039,2606,3.088,2672,2.58,2836,3.033,3632,2.375,3694,5.171,3942,3.947,4185,2.782,4186,2.782,4187,2.782,4188,2.782,4189,2.782,4190,2.782,4191,2.782,4195,3.475,4216,2.895,4218,2.817,4219,5.306,4220,3.746,4221,3.746,4222,3.746,4223,3.746]],["t/1023",[4,0.709,7,0.648,21,0.646,23,0.763,24,1.242,25,1.669,28,1.415,31,0.559,34,1.006,36,1.741,39,0.93,48,0.928,49,1.395,56,1.036,68,0.867,84,0.891,88,1.03,89,2.317,98,1.388,103,0.765,107,2.824,109,1.86,116,1.946,128,1.884,132,0.671,133,0.978,134,1.138,140,0.896,162,0.803,163,2.46,165,0.885,170,2.071,171,1.904,182,1.321,183,0.981,198,1.113,203,0.547,219,0.708,221,0.844,232,1.436,236,1.038,246,1.972,285,0.945,286,1.386,295,1.588,296,0.881,304,0.482,322,1.631,328,1.509,336,1.607,338,1.527,343,0.813,377,0.883,382,1.418,391,0.962,401,0.874,405,0.727,408,1.426,426,0.844,444,0.991,472,1.113,476,2.226,491,0.689,505,0.947,512,2.997,527,1.677,536,3.06,545,1.057,557,1.138,565,2.027,575,2.477,580,1.81,605,0.827,614,1.675,623,1.095,630,1.459,647,1.956,653,2.414,659,1.48,663,1.396,681,2.47,723,1.412,724,1.113,725,2.011,736,1.684,756,1.073,757,1.727,760,0.675,780,1.217,820,1.175,827,1.158,828,0.953,841,1.057,857,1.792,878,0.975,880,0.984,936,0.978,940,1.951,942,4.236,953,0.925,993,1.32,997,1.462,1012,2.237,1046,1.069,1069,2.82,1075,2.295,1170,1.212,1245,1.725,1266,2.577,1289,1.598,1363,2.961,1408,2.315,1473,1.019,1476,1.916,1540,1.265,1558,2.936,1616,2.197,1633,1.077,1754,0.972,1864,2.081,1870,2.669,1882,1.347,1899,1.592,1906,2.148,1907,1.181,2033,1.287,2231,1.653,2293,5.055,2296,4.552,2672,1.494,2839,5.917,3053,2.66,3632,3.137,3644,0.842,3695,5.173,3725,1.632,3771,3.95,3817,1.454,3916,1.524,3942,2.512,3943,5.386,3944,1.757,4185,1.611,4186,1.611,4187,1.611,4188,1.611,4189,1.611,4190,1.611,4191,1.611,4195,2.013,4216,1.676,4218,1.632,4224,4.948,4225,1.862,4226,3.216]],["t/1025",[7,0.549,21,0.769,23,0.908,24,0.961,25,0.871,27,0.646,28,1.381,34,0.693,36,1.2,39,1.107,41,2.079,48,1.104,49,1.03,56,1.027,84,0.496,88,1.225,89,2.598,92,1.007,98,0.956,103,0.91,105,0.851,107,0.697,109,1.867,116,1.71,127,3.908,128,1.298,132,1.346,133,1.164,140,1.066,162,0.956,163,3.08,164,0.747,165,1.026,171,1.892,172,0.943,177,2.212,179,0.91,182,1.533,183,1.967,193,1.666,198,1.325,203,1.097,219,0.842,221,1.005,232,1.666,256,1.018,257,1.385,275,0.842,279,1.176,285,1.124,286,1.649,295,1.094,304,0.573,322,2.634,328,1.796,336,1.864,338,1.127,343,0.968,351,0.985,359,1.2,377,1.327,391,1.929,394,2.29,401,1.04,405,2.215,426,1.692,444,1.18,472,1.325,476,2.842,502,0.75,512,3.088,527,1.002,536,2.668,545,1.258,565,2.537,575,2.819,605,0.985,614,1.863,615,1.592,623,1.303,630,1.692,653,2.192,681,1.702,732,1.778,736,1.16,755,1.225,756,1.277,757,2.075,780,1.448,841,1.258,857,2.692,876,1.852,930,1.488,936,1.164,942,3.556,999,1.48,1046,1.272,1069,3.21,1075,1.581,1170,1.407,1245,1.516,1266,2.669,1274,1.662,1289,1.101,1363,2.798,1476,1.683,1616,1.514,1633,1.282,1754,1.947,1765,1.561,1870,3.407,1906,2.492,1907,1.405,2092,1.523,2282,1.814,2291,5.802,2295,5.181,2672,1.778,3577,2.267,3632,3.571,3644,1.002,3696,5.802,3817,1.731,3942,2.915,4185,1.917,4186,1.917,4187,1.917,4188,1.917,4189,1.917,4190,1.917,4191,1.917,4195,2.395,4216,1.995,4218,1.942,4225,2.216,4226,3.731,4227,5.225,4228,2.395,4229,2.582,4230,2.17]],["t/1027",[7,0.588,27,1.771,28,1.39,56,1.158,84,0.808,89,2.785,196,2.958,405,4.125,426,4.333,495,4.757,511,3.74,512,2.573,565,2.437,568,2.822,583,4.558,647,2.8,729,5.326,757,1.839,936,3.193,940,3.687,946,3.436,958,4.878,981,4.63,1363,2.687,1367,3.618,1381,2.958,1693,4.366,2550,7.125,3624,5.735,3795,5.326,4231,8.042]],["t/1029",[5,3.582,7,0.698,23,2.126,28,1.249,56,1.004,84,0.69,89,3.795,106,3.243,116,2.377,164,2.431,178,4.982,196,2.524,228,2.803,256,3.311,289,2.517,295,2.561,407,2.879,408,2.299,426,3.268,433,3.677,495,4.91,511,3.242,512,2.196,565,2.079,568,2.534,615,3.726,647,3.32,756,2.989,757,1.569,769,2.448,807,4.968,936,2.725,940,4.321,946,2.933,981,3.951,1266,1.737,1381,2.524,1693,3.726,1737,2.402,1865,5.186,1876,4.124,2525,8.696,3624,4.894,3795,4.545,4232,5.607]],["t/1031",[7,0.807,16,0.876,24,1.151,28,1.003,56,1.257,84,0.77,88,2.12,96,1.658,107,1.205,109,0.794,127,2.331,140,1.845,162,1.654,164,1.293,165,1.592,171,1.279,177,3.432,179,2.378,219,3.169,277,2.42,296,1.814,322,2.678,377,1.053,392,2.921,491,2.142,502,1.96,536,3.631,550,1.85,565,2.321,567,4.812,577,2.227,653,1.482,672,4.187,734,2.083,798,2.589,818,3.569,884,4.483,916,2.736,946,2.168,996,2.969,1040,3.24,1170,1.445,1224,3.021,1312,2.611,1363,2.559,1476,1.73,1870,2.98,1933,3.138,1963,3.404,2028,3.754,2032,2.992,2291,3.923,2293,3.834,2295,3.503,2296,3.452,2297,3.41,3016,2.833,3597,3.923,3632,6.16,3649,3.834,3693,3.923,3694,3.923,3695,3.923,3696,3.923,4214,4.025,4219,4.025,4233,5.668,4234,4.145,4235,4.468,4236,9.014]],["t/1033",[24,1.651,36,3.467,56,1.117,68,2.981,84,1.226,113,3.718,203,1.882,304,2.384,398,2.77,401,3.005,482,3.516,536,2.342,543,4.427,565,2.567,567,3.153,577,3.72,578,3.09,614,1.447,622,3.966,756,3.691,940,3.815,1239,4.84,1266,3.085,1312,2.889,1540,4.349,1607,5.139,1638,4.061,1801,4.482,3655,5.765,4237,7.462,4238,9.675,4239,7.462]],["t/1035",[7,0.852,15,3.141,16,1.603,88,3.879,103,2.882,113,3.141,199,4.248,225,3.079,226,5.239,243,4.711,292,3.746,377,1.926,491,2.596,511,2.746,536,2.565,550,3.385,565,3.528,567,4.332,593,4.248,672,5.073,719,5.182,798,4.737,856,5.145,940,3.223,1179,5.301,1302,5.578,2009,5.928]],["t/1037",[7,0.585,17,3.878,28,1.651,84,0.803,89,2.767,105,2.319,190,3.811,219,3.768,232,2.697,283,2.144,294,3.989,377,1.658,461,4.15,536,3.724,552,2.625,567,5.014,653,3.088,731,3.887,757,2.884,852,3.215,2032,4.629,3016,6.616,4240,7.037,4241,11.105]],["t/1039",[2,1.225,4,2.146,7,0.795,23,1.517,24,1.12,25,1.455,27,1.642,28,1.182,56,1.049,84,1.233,88,2.046,89,1.696,109,0.766,113,2.523,127,1.49,132,1.335,163,1.801,165,1.55,171,1.234,174,1.351,177,2.194,182,1.52,193,1.653,219,2.142,223,2.558,232,1.653,240,2.485,275,2.142,278,1.628,283,1.314,285,1.877,294,2.444,313,1.69,322,1.421,338,1.118,447,2.15,472,4.08,476,2.719,498,2.528,502,1.253,512,3.475,525,2.141,527,0.993,536,3.163,558,3.094,565,1.484,567,2.774,568,1.301,575,5.044,614,0.836,736,3.573,755,2.046,757,1.705,765,2.695,773,3.554,796,2.382,816,2.574,877,1.657,893,2.623,936,1.944,940,1.7,997,1.682,1006,2.324,1046,2.124,1069,4.532,1075,2.641,1114,3.801,1196,3.623,1363,3.824,1366,2.528,1382,2.714,1408,3.072,1435,3.06,1446,2.658,1476,1.67,1616,5.908,1907,2.347,1945,3.029,2033,2.558,2297,1.514,2711,2.942,3662,4.139,3916,3.029,3942,2.89,3997,2.999,4242,4.312,4243,4.312,4244,4.312]],["t/1041",[2,1.484,7,0.74,21,2.908,24,0.891,26,2.043,36,3.517,49,2.455,56,0.543,84,1.183,96,3.305,98,3.297,107,2.402,109,1.345,113,2.909,127,1.805,140,2.156,160,2.796,164,1.512,165,1.233,203,2.246,304,1.16,313,1.344,336,2.239,338,1.354,353,1.501,377,1.231,502,1.517,509,3.117,536,3.893,577,4.439,578,2.162,604,3.632,629,3.5,653,2.954,692,4.388,756,2.583,805,3.746,876,3.746,885,4.547,1046,4.812,1114,2.496,1224,3.531,1289,2.226,1312,3.448,1779,3.706,1792,5.869,1870,3.802,1899,3.831,2032,3.358,4245,8.905]],["t/1043",[4,2.752,7,0.699,48,3.6,49,1.994,56,1.087,84,0.961,140,3.477,171,2.411,203,2.124,272,3.633,295,3.568,322,2.776,536,2.643,613,4.011,614,2.027,731,5.77,802,3.589,828,4.591,2297,2.956,2963,6.333,3644,3.269,4246,7.856]],["t/1045",[2,1.03,4,1.875,12,1.54,24,1.382,29,1.332,36,1.684,49,1.359,56,1.263,68,1.448,84,0.924,96,1.345,98,2.124,105,1.195,179,1.278,203,1.447,219,1.871,254,1.228,304,1.798,322,1.195,336,1.554,372,2.807,377,1.352,391,1.608,395,1.509,398,2.643,401,1.46,444,1.656,472,1.86,482,1.708,536,3.553,543,2.15,565,1.247,567,3.008,577,1.807,623,1.829,647,2.815,653,1.202,736,1.628,757,0.941,780,4.542,914,1.9,940,2.262,942,2.553,946,1.758,1046,2.827,1164,1.926,1170,3.609,1239,2.351,1266,2.929,1289,1.545,1312,4.32,1363,3.072,1476,1.403,1540,2.113,1607,2.496,1616,2.125,1638,1.972,1689,2.002,1765,3.468,1792,2.389,1906,2.077,1907,1.972,2031,2.546,2032,2.545,2275,2.935,2297,1.272,2606,2.987,3016,3.638,3632,2.298,3655,2.8,3866,4.825,4004,2.842,4005,3.045,4215,3.265,4246,2.725,4247,5.506,4248,5.737,4249,3.265,4250,3.624,4251,3.265,4252,3.624,4253,3.265,4254,3.624,4255,3.265,4256,3.624,4257,3.265,4258,5.737,4259,3.265,4260,3.624,4261,3.265,4262,3.624,4263,3.479,4264,3.624,4265,3.624,4266,3.624,4267,3.624,4268,5.737,4269,3.624]],["t/1047",[1,4.256,7,0.577,24,1.77,49,1.646,56,1.23,84,1.184,171,1.99,272,2.998,322,3.043,372,2.74,377,2.176,395,2.894,397,4.045,430,3.612,482,3.275,536,3.787,550,2.878,565,2.391,611,3.044,874,3.596,916,4.256,946,4.48,1046,3.424,1170,2.248,1289,2.962,1397,4.439,1454,6.037,1792,4.58,2297,3.241,4246,6.944,4270,9.234,4271,6.949]],["t/1049",[2,1.565,7,0.457,12,2.34,36,2.558,49,1.862,56,1.245,84,1.047,107,2.475,109,1.778,127,3.658,162,2.039,165,1.3,179,1.941,183,2.49,275,1.796,296,2.236,304,1.223,313,1.417,322,3.024,336,3.934,391,4.07,536,3.454,614,1.78,647,2.177,757,1.43,827,2.94,1170,2.544,1289,3.352,1844,3.544,1863,3.467,1870,4.388,2297,1.933,3999,9.917,4000,9.917,4096,4.835,4097,4.835,4246,4.141,4249,4.961,4251,4.961,4255,4.961,4257,4.961]],["t/1051",[2,1.52,7,0.64,49,1.824,56,1.287,84,1.127,85,1.952,109,1.937,162,1.98,165,1.262,179,2.715,296,2.171,313,1.377,322,1.762,336,2.293,391,2.372,452,3.878,502,1.554,536,3.284,577,6.063,614,1.037,705,3.468,757,2,827,2.855,914,2.804,1037,3.966,1170,2.92,1289,2.279,1754,2.395,1844,3.442,1870,4.36,2275,4.33,2297,1.877,4004,4.193,4005,4.493,4246,4.021,4253,6.939,4259,6.939,4261,4.818,4272,4.818,4273,5.347,4274,5.347,4275,9.025,4276,5.347,4277,5.347,4278,5.347]],["t/1053",[2061,3.73]],["t/1055",[7,0.705,15,3.26,34,2.277,56,1.092,228,2.832,322,2.797,377,2,398,3.149,401,3.418,465,6.226,539,5.738,543,5.035,607,3.427,683,3.345,747,3.671,820,4.595,1535,6.871,1536,6.556,1555,7.872,1563,7.281,1607,5.844,2297,2.978,3533,5.901,4279,7.872,4280,7.872]],["t/1057",[7,0.618,16,1.458,24,0.661,25,1.308,28,0.899,34,1.04,49,1.432,56,0.629,84,0.849,103,1.367,145,3.152,162,1.435,171,1.11,175,2.234,203,1.526,219,1.973,236,1.676,256,2.385,275,1.264,281,1.933,283,1.843,304,1.343,313,1.557,315,1.94,318,0.995,322,1.994,334,2.314,338,2.178,365,2.07,368,3.087,372,1.528,395,2.519,398,1.439,401,2.436,438,2.12,458,2.3,482,1.827,530,2.85,531,2.476,539,6.82,542,5.311,543,4.986,552,1.446,564,2.645,567,2.556,605,1.478,613,1.846,614,1.956,630,1.509,708,4.986,747,1.677,757,1.007,762,2.751,877,2.858,898,3.8,947,2.08,1139,2.186,1164,2.06,1170,1.254,1181,2.12,1219,3.139,1266,2.774,1281,2.06,1306,2.555,1312,2.342,1360,2.329,1376,3.186,1416,2.915,1473,1.82,1489,3.493,1531,2.723,1540,2.26,1550,3.04,1570,3.404,1638,2.11,1732,3.04,1737,1.541,1754,1.736,1831,3.258,1898,2.152,1926,7.571,1927,3.493,2297,1.361,2806,2.954,3042,3.729,3456,5.311,3644,3.537,3671,3.721,3773,3.493,3977,2.954,4056,4.817,4281,6.049,4282,6.049,4283,3.877,4284,6.049,4285,3.877,4286,6.049,4287,3.877,4288,6.049,4289,6.049,4290,3.721,4291,6.049,4292,3.877,4293,3.877,4294,6.049,4295,3.877,4296,3.877,4297,3.877,4298,3.877,4299,3.877]],["t/1059",[4,2.472,16,2.239,49,1.791,171,2.165,236,2.994,318,2.93,398,3.623,401,4.353,539,7.722,543,5.791,567,4.824,940,2.982,1266,2.805,1376,3.983,1559,8.75,1638,5.312,3644,2.936,4300,7.563,4301,7.563,4302,7.563]],["t/1061",[4,2.391,7,0.608,16,2.085,49,1.732,85,2.67,171,2.094,236,2.947,398,3.545,401,4.283,539,7.624,543,5.667,552,3.966,567,4.763,852,3.341,940,2.884,1266,2.744,1312,2.832,1376,3.853,1559,8.611,1638,5.198,1732,7.489,3644,2.839,4303,7.314,4304,7.314,4305,7.314,4306,7.314]],["t/1063",[7,0.682,18,5.34,19,2.748,23,2.059,24,0.999,28,1.67,31,1.507,34,2.204,48,2.502,56,1.07,84,0.937,89,3.229,96,2.173,103,2.064,105,2.707,123,2.102,133,2.639,164,2.377,201,3.14,225,3.573,226,2.991,274,5.14,318,1.502,338,1.517,372,2.308,530,2.758,558,3.87,568,1.766,593,4.268,605,2.232,683,3.238,685,5.562,852,3.751,887,5.458,903,4.295,940,2.308,1152,5.828,1234,3.494,1302,3.994,1454,3.827,1455,6.168,1456,4.199,1457,4.402,1458,5.891,1459,4.523,1460,5.14,1476,2.266,1506,3.958,1782,3.898,1822,3.711,1894,5.43,1899,4.295,4307,6.646]],["t/1065",[2061,3.73]],["t/1067",[16,1.981,34,2.711,107,2.725,318,2.592,1352,5.118]],["t/1069",[2,1.45,7,0.424,16,1,24,1.5,28,0.759,34,1.369,41,2.439,49,1.763,56,1.005,84,1.003,103,2.625,107,2.771,109,1.968,110,6.147,113,2.86,127,1.763,132,2.304,140,2.107,164,1.477,165,1.204,171,2.131,179,1.799,193,1.956,203,2.217,228,1.703,272,2.201,304,2.145,313,1.314,343,1.912,377,1.754,408,1.941,527,2.615,614,1.705,658,2.575,877,3.947,940,2.012,1170,3.125,1181,2.79,1245,3.064,1266,1.466,1281,2.712,1737,2.028,1779,3.621,1844,3.284,1863,3.281,1870,3.767,1884,3.548,2605,3.888,2850,6.536,2851,3.084,3644,1.981,3866,2.79,4308,7.444,4309,5.102]],["t/1071",[5,1.868,7,0.849,12,2.139,17,2.096,21,1.499,24,1.881,27,1.259,28,0.748,31,1.296,41,2.406,46,3.808,56,1.111,98,2.729,109,1.549,110,5.671,116,1.979,131,2.406,133,2.269,158,2.651,170,1.567,171,1.441,174,2.731,179,2.599,186,3.66,196,2.102,197,2.363,254,1.705,272,2.171,275,2.404,282,4.224,289,2.096,304,1.118,322,2.874,353,1.446,367,3.954,377,1.186,386,5.779,388,3.835,408,1.915,440,2.339,482,2.372,505,3.218,525,2.499,527,2.353,528,2.794,568,1.519,590,2.853,597,2.739,599,2.726,611,2.205,614,0.976,631,5.076,719,3.191,755,3.498,807,2.05,816,3.005,856,3.168,876,3.61,877,1.934,928,4.42,997,3.402,1046,2.48,1170,1.628,1366,2.951,1633,2.499,1737,2,1870,1.846,2605,3.835,2691,4.149,2850,4.42,3644,1.954,4310,7.37]],["t/1073",[2061,3.73]],["t/1075",[34,2.593,56,1.005,84,1.103,257,5.184,278,3.649,279,4.4,295,4.095,545,4.706,992,5.09,4311,9.663]],["t/1077",[4,1.668,7,0.802,15,1.961,16,1.894,21,2.216,24,1.5,28,1.307,41,2.439,46,3.838,49,1.208,56,0.531,84,1.103,96,1.894,125,3.065,138,3.735,164,1.477,165,1.204,171,1.461,179,3.622,183,2.308,189,2.877,203,1.287,207,2.194,245,3.212,282,2.924,283,2.268,305,3.42,306,2.387,337,2.504,338,1.323,343,1.912,353,1.466,377,1.754,409,2.725,416,2.476,440,2.371,441,5.036,449,4.828,534,5.598,536,2.336,545,2.485,558,2.404,559,8.814,560,4.929,565,2.561,596,3.533,687,4.48,744,5.803,747,2.208,757,1.325,827,4.693,898,3.803,929,3.66,934,2.564,1027,4.378,1028,5.177,1444,3.584,1997,4.132,1998,3.125,2031,3.584,4110,7.693,4135,7.384,4312,5.102,4313,5.102]],["t/1079",[18,4.288,28,1.377,56,0.964,84,1.057,113,3.559,253,5.872,283,2.822,530,5.216,545,5.391,583,5.961,1129,6.104,1234,5.529,4314,8.132]],["t/1081",[7,0.739,21,2.648,23,3.128,84,1.015,88,4.22,89,3.497,283,2.709,353,2.555,359,4.131,391,3.944,472,4.562,476,2.72,512,3.23,545,5.261,575,4.451,614,1.725,1385,7.329,1449,7.2,3725,6.687]],["t/1084",[171,2.866,232,3.837,304,2.223,545,4.876,1737,3.978,3644,3.886]],["t/1086",[4,3.105,17,3.956,84,1.084,219,3.098,237,7.339,284,6.741,304,2.109,545,4.626,1059,5.602,2958,9.117,4315,9.498,4316,9.498]],["t/1088",[2061,3.73]],["t/1090",[4,2.3,5,2.612,7,0.922,19,4.372,24,1.589,28,1.651,56,0.732,84,0.803,89,2.767,164,2.695,174,2.204,224,3.142,232,3.999,274,6.179,306,3.293,313,1.812,400,5.801,421,4.638,558,5.233,596,3.34,601,3.481,683,2.775,757,2.418,887,6.188,914,3.69,934,4.679,936,3.172,947,3.775,1036,4.894,1202,3.907,1315,4.079,1508,4.102,1782,3.34,1974,6.528,2033,4.175,4317,7.037]],["t/1092",[2,1.672,4,1.923,7,0.856,16,1.154,23,2.07,24,1.406,25,1.985,27,1.472,28,1.533,31,2.122,56,0.99,84,1.086,103,2.075,113,2.261,123,2.113,165,1.389,170,1.832,173,3.318,174,1.843,216,3.157,217,3.535,219,1.919,232,3.646,247,2.725,276,3.072,289,2.45,306,2.753,318,2.441,337,2.888,363,4.425,409,3.142,491,2.618,500,2.934,502,2.395,511,1.977,513,2.922,530,2.773,548,3.817,552,2.195,558,2.773,562,3.045,565,2.024,567,2.486,568,1.776,577,2.934,632,3.336,635,2.25,663,3.787,666,4.85,683,2.32,685,3.45,814,2.67,821,4.317,852,4.346,877,2.261,881,4.547,887,5.478,934,2.957,935,4.765,1040,4.268,1461,5.648,4318,5.884,4319,5.884]],["t/1094",[7,0.719,23,1.747,24,1.627,25,1.675,27,2.164,28,0.738,29,1.826,56,1.289,84,0.567,89,3.402,95,3.246,100,3.439,107,1.34,108,3.971,109,0.882,113,1.908,128,3.667,135,2.983,164,1.438,165,2.042,169,4.206,177,2.527,179,1.751,193,2.797,219,1.62,232,2.797,275,1.62,294,2.815,296,2.017,306,2.323,343,1.861,446,3.329,447,2.476,476,2.232,483,3.3,496,3.196,505,2.169,507,2.846,512,3.143,548,3.221,552,1.852,558,3.439,596,2.357,635,1.898,653,2.87,662,3.041,740,5.23,807,2.022,813,4.98,827,2.652,852,4.641,884,3.3,946,2.409,953,2.117,1047,3.126,1087,3.955,1266,1.427,1289,3.11,1352,2.516,1363,1.884,1367,2.537,1397,3.172,1465,3.784,1539,3.488,1729,4.173,1964,3.562,4320,5.233,4321,8.286]],["t/1096",[2061,3.73]],["t/1098",[7,0.629,23,1.837,24,1.845,25,1.762,29,1.92,56,0.927,68,2.086,84,1.115,160,2.796,165,2.102,177,2.657,179,1.841,198,2.679,199,2.714,219,1.703,221,3.466,228,1.743,232,2.001,240,3.009,248,2.583,275,1.703,283,1.591,296,2.121,313,1.949,316,2.452,324,2.914,338,1.962,353,1.501,377,1.231,397,2.288,422,3.746,491,1.659,512,2.75,522,4.095,530,4.196,548,4.91,552,1.948,647,2.064,723,3.678,757,2.536,760,1.626,822,3.287,852,4.068,862,2.738,903,3.831,904,2.976,916,3.198,948,5.214,1059,6.376,1060,3.868,1069,4.315,1114,2.496,1235,2.714,1266,1.501,1312,2.022,1470,3.979,1476,2.022,1570,4.585,1705,3.361,1953,2.993,2033,3.098,2518,2.75,2711,3.563,3969,4.228,4322,7.57,4323,5.222,4324,5.012,4325,5.222,4326,5.222]],["t/1100",[512,3.808]],["t/1102",[7,0.71,16,1.677,24,1.459,25,2.885,27,2.139,31,2.201,84,0.976,113,3.285,281,4.263,476,3.226,550,4.367,553,4.771,635,3.269,948,5.889,1170,2.766,1271,6.607,1446,5.271,1622,5.782,1798,6.183,1819,5.782,1863,3.769,4327,9.709]],["t/1105",[56,1.163,171,2.696,304,2.091,322,3.104,548,6.108,852,4.302,948,6.487,1312,3.647,1737,3.743,2297,3.306,3644,3.656,4192,7.626]],["t/1107",[24,1.567,84,1.048,126,4.673,128,4.616,219,3.593,221,4.287,304,2.04,338,2.381,404,4.713,453,6.211,757,2.86,862,4.816,4328,6.662]],["t/1109",[105,3.329,192,6.27,277,5.471,377,2.381,530,4.76]],["t/1111",[2061,3.73]],["t/1113",[512,3.773,575,5.198]],["t/1116",[56,1.169,171,2.719,304,2.109,322,3.131,567,4.013,852,4.339,1312,3.678,1737,3.775,2297,3.334,3644,3.687,4329,9.498]],["t/1118",[304,2.243,438,5.524,1266,2.903,1285,5.994,4330,10.102]],["t/1120",[56,1.014,304,2.165,567,4.119,651,6.847,898,4.98,1266,2.801,1285,5.784,1399,6.051,1732,7.643]],["t/1122",[24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,567,3.979,635,3.601,747,4.075,4328,6.831]],["t/1124",[16,1.911,171,2.791,304,2.165,543,5.784,567,4.119,852,4.453,940,3.843,1737,3.874,3644,3.784]],["t/1126",[56,1.042,304,2.223,401,4.032,1266,2.877,1607,6.895,1638,5.448]],["t/1128",[56,1.042,304,2.223,398,3.716,401,4.032,1266,2.877,1638,5.448]],["t/1130",[7,0.664,24,1.365,27,2,28,1.189,84,1.155,88,3.796,103,3.566,105,2.636,144,4.036,145,3.389,179,2.82,192,4.964,225,3.012,226,5.168,277,4.331,343,2.998,377,1.885,511,3.398,557,4.194,567,3.379,568,2.413,593,4.157,653,2.653,672,4.964,706,6.014,740,4.818,1753,5.801,1997,6.476,2009,5.801]],["t/1132",[2061,3.73]],["t/1134",[2,2.079,4,3.122,16,1.434,23,2.573,24,1.924,25,2.468,27,1.83,84,0.835,109,1.3,170,2.277,174,2.291,254,2.478,313,1.883,476,2.921,512,3.864,536,2.295,568,2.882,653,2.426,723,2.755,747,3.165,759,2.702,760,2.277,803,4.34,846,4.469,940,2.884,1114,3.497,1170,3.44,1266,3.056,1363,3.624,1476,2.832,1838,5.735,1902,3.676,4130,6.786]],["t/1136",[4,2.711,28,1.233,84,1.181,217,6.217,552,3.86,592,5.61,653,2.751,725,6.052,986,5.717,988,6.104,1012,7.052,1170,2.684,1266,2.384,1363,3.927,1476,3.212,1671,4.808,2092,4.892,3644,3.22]],["t/1138",[512,3.808]],["t/1140",[7,0.733,16,1.73,24,1.505,27,2.689,31,2.271,84,1.227,169,5.084,281,4.398,472,4.526,553,4.922,635,3.372,852,4.03,1271,6.816,1446,5.438,1622,5.965,1798,5.172,1819,5.965,1863,3.888,2078,8.467]],["t/1143",[23,3.342,56,1.169,171,2.719,304,2.109,322,3.131,852,4.339,1312,3.678,1737,3.775,2297,3.334,3644,3.687,4331,8.558]],["t/1145",[23,3.313,24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,635,3.601,747,4.075,4328,6.831]],["t/1147",[7,0.855,15,3.386,16,1.898,23,2.627,24,1.833,25,1.729,27,1.868,49,1.214,56,1.118,84,1.006,109,1.327,110,2.846,127,1.771,170,1.596,171,2.138,318,1.315,321,4.082,322,1.689,338,1.329,377,1.208,395,3.11,410,3.565,437,2.535,505,2.238,527,1.721,528,4.146,532,2.817,596,2.433,635,1.96,723,1.931,747,2.218,759,1.893,760,1.596,769,2.076,830,2.469,852,2.342,877,1.97,1043,2.803,1044,3.182,1050,5.097,1193,3.407,1312,1.985,1759,3.718,1773,4.082,1798,3.005,1863,2.259,1902,2.576,1918,2.688,1998,3.139,2021,3.961,2225,5.43,2297,3.093,2600,3.325,2605,3.906,2815,3.761,2867,3.16,2942,8.981,2946,4.755,4331,4.618,4332,5.126,4333,5.126,4334,7.469,4335,5.126,4336,5.126,4337,5.126,4338,5.126,4339,5.82,4340,7.469,4341,5.82,4342,5.82,4343,5.82,4344,5.82,4345,5.82]],["t/1149",[2061,3.73]],["t/1152",[23,3.37,24,1.635,27,2.396,126,4.874,128,4.815,304,2.127,338,2.483,404,4.915,453,6.478,635,3.663,4328,6.948]],["t/1154",[7,0.646,16,1.524,23,3.495,24,1.695,27,1.945,36,3.612,56,1.034,68,3.106,79,6.827,106,3.002,126,5.054,128,4.992,131,3.716,175,4.48,200,4.058,202,5.639,203,1.961,204,6.671,207,3.344,209,7.462,219,2.536,223,4.613,304,1.726,335,6.827,339,4.132,409,4.151,416,3.772,496,5.003,500,3.876,502,2.259,1367,3.972,2297,2.729,3414,7.462]],["t/1156",[7,0.664,16,1.568,23,3.558,24,1.726,36,3.716,41,3.823,56,1.052,68,3.195,79,7.023,126,4.069,128,5.083,131,3.823,201,4.29,202,7.336,203,2.017,204,6.863,207,3.44,210,7.676,219,3.299,223,4.745,304,1.776,335,7.023,339,4.251,409,4.27,496,6.51,1367,4.086,2297,2.807]],["t/1158",[7,0.739,23,3.128,24,1.517,41,4.251,98,3.292,131,4.251,201,5.795,202,6.449,207,3.824,304,1.974,334,5.308,339,4.726,613,4.235,614,1.725,651,6.246,1583,7.472,1737,3.534,3644,3.452,4346,8.892]],["t/1160",[2,1.312,7,0.688,16,0.905,19,2.167,23,4.039,24,1.179,27,2.301,28,1.368,29,1.697,49,1.637,56,0.48,68,1.844,84,1.125,100,2.175,121,2.846,126,2.349,128,2.32,131,2.207,171,2.634,174,2.165,182,1.628,183,3.126,196,1.928,201,2.476,203,1.744,207,1.985,253,2.927,262,3.179,283,2.525,302,2.24,304,1.84,313,1.188,315,2.31,316,2.167,322,1.521,336,3.553,339,2.453,344,6.228,345,7.466,346,9.678,353,1.326,371,4.159,401,1.859,437,2.283,466,3.428,479,3.387,481,2.906,489,3.242,490,3.428,491,1.466,495,2.349,498,2.706,500,3.446,544,3.042,605,1.76,614,1.341,681,4.556,756,2.283,877,1.774,940,1.82,992,2.431,1007,5.881,1208,3.879,1289,1.968,1312,2.676,1367,3.532,1686,7.953,1699,3.018,4347,4.159,4348,6.912,4349,4.616,4350,4.616,4351,4.616,4352,4.616]],["t/1162",[2,1.424,7,0.722,12,2.129,16,1.44,23,2.584,24,0.855,28,1.424,29,1.842,84,1.257,88,4.839,89,4.01,109,0.89,127,1.731,128,2.518,129,3.154,132,1.551,164,1.45,165,1.183,169,4.233,179,2.59,213,2.148,219,1.634,236,1.388,241,4.13,275,1.634,280,3.359,296,2.035,304,1.113,313,1.29,315,2.508,316,4.083,318,1.286,337,2.459,397,3.218,404,2.571,422,3.594,440,2.328,458,2.973,470,3.818,472,2.571,476,2.66,512,3.705,532,2.754,544,3.302,545,2.44,562,3.801,568,2.624,575,5.104,605,3.316,622,2.663,681,3.302,736,2.251,753,4.514,816,2.991,822,3.154,1059,4.332,1069,4.187,1080,4.648,1114,2.395,1208,4.21,1312,1.94,1434,2.938,1849,5.455,1850,5.213,1851,4.13,1916,3.154,2518,2.639,2726,3.768,2782,3.485,4353,5.01,4354,5.01,4355,5.01]],["t/1164",[2061,3.73]],["t/1166",[2,2.507,23,3.103,24,1.834,56,0.918,179,3.11,221,3.433,313,2.767,324,4.922,512,3.205,552,3.29,723,4.049,852,4.03,862,4.625,916,5.402,1114,4.217,1312,3.415,1476,3.415,2711,6.019]],["t/1168",[512,3.808]],["t/1170",[7,0.664,16,1.568,24,1.726,25,2.698,27,2.916,28,1.189,31,2.059,68,3.195,84,1.155,96,2.969,132,2.475,281,3.987,512,3.675,553,4.463,635,3.867,653,2.653,878,3.594,934,4.019,1069,4.558,1271,6.179,1290,4.689,1446,4.93,1622,6.84,1798,4.689,1819,5.408,2438,6.592]],["t/1173",[56,1.163,171,2.696,219,3.072,304,2.091,322,3.104,552,3.513,852,4.302,1312,3.647,1737,3.743,2297,3.306,3644,3.656,4356,8.082]],["t/1175",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,862,5.023,4328,6.948]],["t/1177",[7,0.806,15,2.584,16,0.873,21,1.325,24,1.541,27,2.027,29,1.636,34,1.194,49,1.054,56,1.135,84,0.508,85,1.624,103,1.569,107,1.2,109,1.44,110,3.733,127,2.324,158,2.344,160,1.644,219,2.946,221,3.516,225,1.676,322,2.216,353,1.279,395,1.853,401,1.792,410,3.095,476,1.361,527,2.081,528,2.47,552,3.618,562,3.48,564,5.531,607,1.797,613,2.12,635,1.701,755,2.112,757,1.155,759,1.644,760,2.524,769,1.802,837,4.009,877,1.71,878,1.999,945,4.965,993,2.707,1037,1.787,1050,5.531,1060,2.274,1284,2.293,1306,4.432,1312,2.604,1718,5.693,1732,5.273,1759,3.227,1798,2.609,1980,3.265,2225,2.496,2297,2.36,2312,4.571,2438,3.668,2605,3.391,2632,2.801,2653,3.739,2708,3.391,2815,3.265,2867,2.743,2895,2.886,3513,3.227,4016,3.438,4054,5.77,4356,3.818,4357,4.009,4358,4.45,4359,6.724,4360,4.45,4361,6.724,4362,6.724,4363,4.45,4364,4.45,4365,4.45,4366,4.45,4367,4.45,4368,5.053,4369,5.053,4370,5.053,4371,5.053,4372,5.053]],["t/1179",[105,3.329,192,6.27,277,5.471,377,2.381,530,4.76]],["t/1181",[2061,3.73]],["t/1183",[4,2.189,7,0.944,8,3.797,21,1.995,27,1.675,41,3.202,46,4.962,84,1.028,85,2.445,90,4.474,92,2.614,93,4.022,98,2.48,109,1.19,127,2.315,133,3.02,134,3.512,153,3.611,164,1.939,172,3.289,174,2.098,207,3.875,213,2.872,224,2.99,256,2.641,276,3.497,283,2.745,304,1.487,305,4.49,338,1.736,340,4.705,343,2.511,363,5.037,440,3.112,476,2.049,532,3.681,599,3.628,613,3.191,690,4.379,694,3.839,853,5.335,939,4.345,997,2.614,1434,3.927,1470,5.104,2353,4.217,4373,6.698]],["t/1185",[5,2.319,31,2.212,39,2.679,56,1.022,68,3.433,84,1.121,106,2.413,109,1.971,110,3.468,165,2.028,172,3.137,197,2.933,203,2.167,207,3.696,253,3.961,254,2.117,304,1.908,377,1.472,409,3.336,527,1.98,552,3.205,553,3.486,576,3.878,607,3.47,611,2.737,614,1.212,630,2.431,638,3.961,769,3.977,819,4.639,830,3.009,902,4.584,953,2.663,986,3.451,988,3.685,1042,3.961,1210,5.059,1278,4.388,1689,3.451,2892,6.843,2966,4.481,3016,7.032,3402,4.976,3993,5.796,3995,5.796,4124,5.361,4374,5.25,4375,6.584]],["t/1187",[7,0.818,27,2.463,46,4.75,47,5.096,84,1.124,90,3.807,92,4.74,93,3.423,96,2.846,158,4.038,172,2.799,207,3.298,256,3.023,306,3.587,332,4.193,338,1.987,476,3.012,481,4.826,597,4.173,599,5.333,707,4.495,796,4.235,810,3.587,846,3.587,997,2.992,1360,4.606,2118,5.694,3668,6.579]],["t/1189",[5,1.485,7,0.515,16,0.785,17,1.667,24,0.683,29,1.471,31,1.596,39,1.716,49,0.948,56,1.062,68,2.477,84,1.117,92,4.461,93,3.816,106,2.395,109,1.738,110,2.222,144,2.019,165,1.464,171,1.146,172,2.771,177,2.036,197,1.879,199,2.08,203,1.564,207,3.264,253,2.537,256,2.445,278,1.511,297,2.306,304,2.456,372,1.578,377,0.943,398,2.301,409,2.137,476,1.224,527,1.749,543,3.679,552,2.831,553,2.233,576,2.484,580,1.464,607,2.504,611,1.753,614,1.472,630,1.557,638,2.537,707,2.346,755,1.899,769,3.074,819,2.972,830,1.927,898,2.045,899,2.903,900,2.757,902,2.936,940,1.578,953,1.706,986,2.21,988,2.36,1042,2.537,1210,3.241,1263,2.556,1266,2.456,1278,2.811,1367,3.168,1381,1.671,1429,3.187,1597,2.66,1601,2.519,1671,2.319,1689,2.21,1737,3.016,1882,2.484,1898,2.222,1906,2.294,2011,3.434,2287,2.616,2502,3.299,2892,4.938,2922,3.434,2966,2.871,3016,5.42,3042,2.467,3402,3.187,3413,3.05,3644,2.946,3669,3.841,3923,3.187,3995,3.713,4124,3.434,4374,3.363,4375,4.217,4376,4.002,4377,4.002,4378,3.606,4379,4.002,4380,4.002,4381,3.841,4382,3.713]],["t/1191",[7,0.953,15,3.577,21,1.209,24,1.47,25,1.37,27,1.016,28,1.139,34,1.09,40,2.254,46,4.748,47,4.167,49,0.962,56,0.652,84,1.063,85,3.744,92,3.839,93,1.813,96,2.843,97,4.833,105,2.067,106,2.958,129,2.556,135,2.439,138,1.726,140,1.677,158,3.302,164,1.175,179,2.211,196,1.696,272,1.752,278,1.533,283,1.237,296,1.649,304,0.902,313,1.045,318,1.042,377,0.957,393,2.101,395,2.611,437,3.788,438,5.381,440,1.887,441,2.327,471,1.773,491,1.29,530,1.913,557,3.288,591,2.439,597,2.21,605,1.548,614,0.788,723,1.529,730,3.484,734,1.893,744,2.439,747,3.314,757,1.054,813,5.227,820,2.199,854,2.278,856,2.556,1050,4.279,1055,4.36,1163,4.005,1164,2.158,1202,2.254,1328,2.556,1597,2.699,1688,2.912,1962,3.288,1963,3.094,1964,2.912,1965,3.094,1996,3.484,2025,6.1,2286,6.018,2287,2.654,2364,3.484,2836,3.288,3533,5.327,4383,4.06,4384,6.901,4385,7.659,4386,3.658,4387,4.06,4388,4.06]],["t/1193",[4,1.541,7,0.697,16,0.924,17,1.963,21,1.403,29,1.733,49,1.663,56,1.085,84,0.801,85,1.721,92,4.424,93,3.135,97,3.308,108,2.565,160,1.741,164,1.364,196,1.969,203,1.189,217,2.831,228,1.573,275,1.537,283,1.436,304,1.559,365,2.517,372,1.858,401,1.898,434,4.932,438,2.578,482,2.221,500,2.35,527,1.086,579,3.592,580,2.569,591,2.831,596,2.237,613,2.245,614,1.628,680,3.885,713,4.139,714,8.468,751,2.868,774,2.687,780,2.644,796,2.604,830,2.27,854,2.644,888,4.358,898,2.408,1009,2.287,1170,1.525,1263,3.011,1266,2.672,1371,7.528,1482,3.754,1601,2.967,1705,3.034,1733,2.967,2033,2.797,2297,2.464,2412,4.524,2668,5.214,2739,6.326,3111,3.961,3412,3.885,3533,3.278,3668,4.045,3710,8.053,3712,8.053,3923,3.754,3977,3.592,4378,4.247,4389,4.714,4390,4.714,4391,4.714,4392,9.297,4393,4.714,4394,4.714,4395,4.714,4396,4.524,4397,4.714,4398,4.714]],["t/1195",[7,0.784,16,1.408,25,2.882,49,1.7,84,0.975,92,4.112,100,3.383,108,3.907,112,3.748,164,1.406,165,1.695,198,4.382,213,2.083,246,4.699,292,2.226,348,3.285,377,1.145,388,3.701,397,2.128,438,3.926,440,2.257,441,4.115,443,8.312,544,3.201,562,2.514,614,1.657,651,3.412,713,4.265,714,5.268,898,2.482,1035,8.285,1046,2.393,1181,2.656,1266,2.454,1285,2.882,1371,3.933,1408,2.273,1428,4.506,1633,2.412,1687,4.265,1799,3.523,1998,2.975,2033,2.882,2497,5.918,2836,3.933,3175,7.695,3176,4.265,3177,4.265,3178,9.056,3179,6.891,3180,6.891,3181,6.891,3182,6.305,3183,4.662,3184,4.662,3185,4.662,3186,4.662,3187,4.662,3188,4.662,3189,4.662,3190,4.662,3191,4.662,3192,4.662,3533,3.378,4384,4.377,4399,4.857,4400,4.857]],["t/1197",[2,1.29,7,0.854,8,2.574,15,2.623,16,0.89,17,1.891,21,1.352,29,1.669,34,1.219,49,1.617,50,2.799,56,1.142,84,1.117,85,2.492,90,2.255,92,4.688,93,3.048,94,5.859,96,2.534,97,3.866,102,3.018,106,2.637,108,2.471,109,0.807,116,2.685,125,2.727,131,2.171,164,1.314,171,1.3,174,1.422,181,2.711,186,2.255,196,1.896,275,1.481,283,2.08,313,2.112,316,2.132,323,1.93,377,1.07,390,2.31,395,1.891,398,1.685,407,2.163,438,3.734,491,1.442,526,3.46,562,2.35,568,1.37,593,3.549,597,2.471,611,1.989,694,2.602,729,3.415,850,3.127,893,2.762,898,2.32,940,1.79,950,3.331,1263,2.9,1429,5.437,1433,3.815,1434,4.003,1531,3.189,1597,3.018,1601,2.858,1633,2.255,1866,3.815,2011,5.859,2241,3.896,2287,4.463,2297,2.396,2502,3.743,3111,3.815,3176,3.987,3177,3.987,3344,3.896,3629,4.5,3977,3.46,4401,4.541,4402,4.541,4403,4.541,4404,4.541,4405,4.541,4406,4.541,4407,4.091,4408,4.541]],["t/1199",[7,0.776,15,1.827,21,1.415,27,1.189,33,2.856,34,1.276,46,4.566,56,1.183,84,0.963,85,1.735,90,2.361,92,4.225,94,4.027,96,3.13,98,1.76,101,2.666,113,1.827,133,2.143,140,1.963,164,2.045,179,2.973,203,1.199,208,4.563,238,2.874,240,2.74,254,1.611,275,2.305,278,2.668,351,1.813,353,2.03,372,1.874,377,1.12,407,2.264,437,2.351,440,3.918,476,2.161,482,2.24,491,2.244,562,2.46,566,3.488,583,3.06,597,3.845,613,3.366,614,0.922,715,6.264,828,2.089,1006,2.562,1202,3.923,1266,2.03,1290,2.787,1429,3.786,1434,2.787,1479,4.563,1633,2.361,1866,3.995,1999,6.782,2009,3.448,2395,3.903,2457,4.41,3629,3.133,3708,8.094,3857,3.488,3866,4.612,3977,3.622,4407,4.283,4409,7.066,4410,4.754,4411,7.066,4412,4.754,4413,4.754,4414,4.754]],["t/1201",[7,0.806,15,3.73,16,1.659,20,4.027,36,2.839,49,1.447,84,0.966,92,4.641,93,3.778,94,3.483,97,2.879,98,2.263,157,3.718,164,2.45,174,2.65,177,4.305,230,5.366,236,2.345,275,2.76,278,3.195,302,2.965,315,3.058,359,2.839,407,2.91,430,3.176,440,2.839,578,2.53,621,3.309,684,3.358,712,3.464,795,4.209,816,3.648,900,6.686,992,3.218,993,3.718,998,8.044,1007,6.005,1024,4.096,1216,3.278,1271,4.721,1366,3.583,1464,5.505,2096,5.505,2231,4.656,2241,5.243,2672,4.209,4415,6.11]],["t/1203",[7,0.796,8,2.16,15,2.826,17,3.467,18,3.406,21,1.135,24,0.65,26,2.878,28,0.567,34,1.974,46,1.664,48,1.629,56,0.765,84,0.681,90,1.892,92,2.328,93,4.473,94,3.401,96,1.414,97,3.922,103,1.343,132,1.847,165,0.899,172,1.391,181,2.275,183,1.723,185,4.328,186,1.892,201,2.044,207,1.639,213,1.634,225,1.435,228,1.992,236,2.306,244,3.871,278,1.439,283,1.161,291,2.365,292,1.746,298,2.452,306,1.783,313,0.981,315,1.907,323,1.619,338,0.988,353,1.095,369,2.491,375,2.333,377,0.898,381,2.975,393,1.972,395,1.587,440,2.773,444,1.741,463,2.416,476,1.166,505,1.664,509,2.275,590,2.16,597,2.074,611,1.669,620,7.05,676,3.535,677,2.115,680,3.141,691,2.208,693,2.471,694,3.42,696,2.829,719,2.416,740,1.815,790,2.796,794,4.378,802,1.624,860,3.035,874,3.807,877,1.464,881,2.944,940,1.502,992,2.007,997,1.487,1007,4.235,1009,1.849,1024,4,1025,4.753,1027,3.27,1029,5.335,1030,7.06,1031,8.675,1032,5.728,1078,3.346,1218,2.261,1408,1.783,1434,3.499,1435,2.704,1871,3.27,1933,2.676,2297,1.337,2533,3.346,2805,3.085,3090,3.535,3174,3.346,3450,3.657,4416,3.81,4417,3.81,4418,3.81,4419,3.81,4420,3.433,4421,5.377,4422,3.81,4423,3.433,4424,3.81]],["t/1205",[2,1.718,8,3.426,17,2.517,24,1.87,28,0.899,48,3.59,56,0.874,68,2.415,92,3.277,93,4.309,94,3.445,97,2.848,132,1.871,165,1.427,179,2.131,186,3.001,199,3.141,203,2.118,207,2.599,236,1.675,283,1.842,304,2.519,338,2.177,527,1.392,614,1.172,620,6.612,898,4.291,1024,4.051,1025,4.813,1029,6.091,1046,4.138,1266,3.149,1371,4.894,1434,3.544,1601,3.805,1689,3.338,1693,3.726,2833,4.488,2892,4.813,3174,5.307,3866,3.305,3993,5.607,4420,7.567,4421,7.567,4423,5.446,4425,6.044,4426,6.044,4427,6.044,4428,6.044]],["t/1207",[2,1.393,7,0.908,16,2.073,21,1.459,24,1.465,27,1.226,28,0.729,41,2.343,47,3.257,56,0.986,85,3.459,90,3.588,93,3.226,97,3.405,105,1.615,109,0.871,123,1.76,124,3.257,170,1.526,172,1.789,173,2.763,174,1.535,183,2.216,200,2.558,244,3.178,256,1.932,275,1.598,283,1.493,291,3.042,295,2.076,313,1.262,323,2.083,353,1.408,408,2.749,437,4.246,438,2.68,447,2.443,471,2.14,491,2.295,502,1.424,525,2.433,580,1.793,597,4.672,599,3.914,614,0.951,615,3.021,619,7.747,627,4.415,747,2.12,846,2.293,874,3.74,997,1.912,1245,1.708,1285,2.907,1486,3.786,1876,3.344,2531,4.118,3533,5.026,3567,4.415,4386,4.415,4429,4.9,4430,10.104,4431,4.9,4432,9.374,4433,4.9,4434,4.9,4435,4.9,4436,4.9,4437,4.9,4438,4.9,4439,4.9,4440,4.9,4441,4.9,4442,7.226]],["t/1209",[7,0.84,12,3.399,24,1.726,84,0.913,109,1.421,201,4.29,304,2.589,619,7.502,736,3.594,780,5.673,1059,4.717,1266,2.906,1689,5.587,1882,4.964,3108,6.72,3158,6.592,3632,6.413,3688,7.676,3689,7.676,4443,7.997,4444,7.997,4445,7.997,4446,7.997]],["t/1211",[7,0.976,16,1.183,24,1.827,25,2.504,27,1.509,28,1.709,31,2.593,40,3.349,56,1.21,103,1.362,107,2.002,109,1.49,115,2.927,123,3.617,127,3.601,162,1.43,219,1.26,223,2.292,276,3.149,468,4.043,476,1.845,491,1.916,527,1.39,528,2.145,565,3.684,568,2.24,601,2.984,614,1.17,630,3.264,635,1.477,757,1.003,759,2.228,810,2.822,814,3.368,999,3.457,1114,1.847,1239,2.506,1328,2.432,1435,2.742,1473,1.814,1754,3.324,1864,2.145,2411,4.661,2636,3.646,2974,3.863,2976,3.863,2977,3.863,2978,8.387,2979,6.032,2980,3.863,2981,3.863,2982,7.421,2983,3.863,2984,3.863,2985,3.863,2986,3.863,2987,6.032,2988,6.032,2989,6.032,2990,6.032,2991,6.032,2992,6.032,2993,6.032,4447,4.071]],["t/1213",[1954,9.444]],["t/1215",[9,6.537,48,4.096,84,1.094,159,6.729,1128,9.195,1157,8.888,1475,9.195,2061,3.409,2863,9.58,3418,9.58,4448,10.096]],["t/1217",[7,0.818,27,1.918,31,1.974,34,2.058,56,0.798,109,1.362,115,3.72,123,2.753,135,4.606,194,6.208,238,4.635,258,7.667,269,7.359,306,5.089,505,3.348,507,4.394,557,4.02,591,5.915,607,3.097,732,5.281,795,5.281,854,4.301,941,5.924,996,5.096,1135,6.733,1136,7.113,1137,5.561,1275,6.658,1397,4.897,1527,5.332,2232,5.694,3940,7.113,4449,8.08,4450,7.667,4451,8.08]],["t/1219",[2061,3.73]],["t/1221",[3,5.964,7,0.55,8,3.752,10,3.773,11,5.562,21,1.971,22,4.327,23,2.328,24,1.129,26,2.589,27,2.236,28,1.329,31,1.704,46,2.89,56,0.689,84,0.755,103,2.334,105,2.182,109,1.176,121,4.081,123,2.377,126,3.368,140,2.733,165,1.562,171,1.895,174,2.073,197,3.108,213,2.838,220,4.293,221,2.576,232,2.537,292,3.034,305,4.437,315,3.313,346,5.271,353,1.902,392,4.327,434,6.278,491,2.102,500,3.3,555,3.3,571,7.934,583,4.26,590,3.752,591,3.976,592,4.476,611,2.899,622,3.518,637,4.476,657,5.456,941,5.114,1137,4.801,1476,2.563,1504,4.915,1754,2.964,2652,6.14,3024,6.619,3025,5.456]],["t/1223",[4,2.151,7,0.547,16,1.29,23,3.132,24,1.986,31,2.292,35,4.828,36,4.136,56,0.926,84,0.751,86,4.72,96,2.442,98,3.296,100,3.1,108,3.581,123,2.363,131,4.255,142,6.315,143,9.275,145,2.788,165,1.553,171,1.884,174,2.061,188,6.783,198,3.376,205,6.315,207,3.829,228,2.196,232,3.412,337,3.229,353,1.891,377,2.098,400,7.338,489,4.622,500,4.438,545,3.205,571,5.159,662,4.029,940,2.594,1028,4.576,1827,5.928,3025,5.424,3026,6.58,3027,6.58]],["t/1225",[34,2.527,84,1.075,92,3.675,93,4.992,229,5.887,252,8.27,390,4.792,599,5.101,619,6.369,620,5.49,942,4.191,998,6.369]],["t/1227",[34,2.761,415,7.64,1066,5.347]],["t/1229",[34,2.761,415,7.64,1066,5.347]],["t/1231",[12,3.15,27,2.41,28,1.591,84,1.222,107,2.599,109,2.013,172,3.907,173,4.179,174,2.321,272,3.197,304,1.646,318,1.902,468,4.968,495,3.771,502,2.154,557,3.886,564,5.058,596,3.518,607,2.993,612,5.058,747,3.207,819,5.504,829,5.348,830,3.569,841,3.61,877,4.354,1277,6.876,1352,3.756,1953,4.248,2806,5.648]],["t/1233",[4,1.496,5,1.699,7,0.685,16,0.898,21,2.455,24,0.781,25,1.545,26,3.585,27,1.718,28,1.226,40,2.541,56,0.858,84,1.046,98,3.053,106,1.768,107,1.853,109,2.068,110,3.814,111,7.587,116,1.8,126,2.329,132,1.417,133,2.064,164,2.387,165,1.622,169,3.959,170,2.139,183,2.07,186,2.273,189,3.874,256,1.805,272,1.975,289,1.906,316,2.15,318,1.175,353,2.369,365,2.444,372,1.805,377,1.079,382,2.993,407,2.181,416,2.221,432,3.042,439,3.153,440,2.127,447,2.282,458,2.716,527,2.262,555,2.282,565,1.575,568,2.073,578,1.896,593,3.57,606,3.707,613,3.927,615,2.822,630,2.674,635,1.75,679,5.663,809,3.32,814,2.077,818,4.363,862,2.4,933,5.308,937,3.32,962,5.98,1111,4.732,1134,4.02,1163,2.924,1276,3.59,1279,6.032,1439,7.649,1831,3.847,1892,4.247,2016,3.646,2043,3.4,3028,4.578,3029,4.578]],["t/1235",[4,1.595,5,1.811,7,0.711,15,1.875,16,0.957,27,2.803,28,1.569,56,0.891,98,1.807,105,1.608,107,1.316,109,1.68,113,1.875,127,2.489,132,1.51,165,2.021,171,1.397,172,3.126,173,2.751,182,1.72,199,2.536,228,2.404,236,1.996,257,2.617,281,2.432,289,3,292,2.236,302,2.367,320,2.949,338,1.265,343,1.829,351,1.861,365,3.846,366,3.216,369,3.19,437,2.413,445,3.242,455,6.181,458,2.895,459,4.022,490,3.623,491,1.55,502,1.418,525,2.423,550,2.02,552,1.82,568,2.174,581,5.489,604,3.393,617,4.1,633,4.022,635,2.754,636,2.895,684,2.681,734,2.275,757,1.267,798,2.828,801,3.825,815,4.526,831,3.242,851,2.695,878,2.192,906,3.116,909,3.539,933,3.77,953,3.07,1006,4.615,1041,3.27,1067,4.022,1216,2.617,1245,3.297,1290,2.86,1445,3.58,2101,4.526,2516,5.937,3008,3.825,3030,4.879,3031,4.879,3032,4.284,3033,6.053,3034,4.879,3035,4.879,3036,4.284]],["t/1237",[2,1.672,7,0.79,17,2.45,25,2.781,28,1.614,56,1.073,84,1.177,88,4.515,89,4.054,109,1.046,128,2.957,133,2.653,162,2.179,164,1.703,175,3.391,275,1.919,313,1.515,315,2.945,377,1.387,405,1.973,407,2.803,440,2.734,472,4.881,476,2.521,483,3.911,491,1.869,505,2.57,512,4.282,558,2.773,575,5.781,605,2.244,698,4.484,726,4.053,887,3.911,912,4.317,1069,4.698,1366,5.578,1616,3.45,1754,2.635,1815,9.131,2783,5.302,3037,5.302,3038,5.648,3039,5.459,3040,5.302]],["t/1239",[7,0.751,84,1.166,92,2.629,98,2.495,109,1.607,110,3.741,116,2.65,165,1.591,174,3.198,186,3.346,216,3.615,228,3.02,229,4.212,233,2.57,242,5.004,317,3.549,318,1.729,351,2.57,364,5.004,372,2.657,377,2.132,390,3.429,426,2.623,458,3.999,527,2.515,580,2.466,591,4.048,613,3.21,630,3.521,637,4.557,658,5.745,693,4.371,798,3.906,818,3.566,996,4.479,997,3.53,1163,4.305,1218,5.368,1444,4.734,1916,4.242,2098,5.918,3041,6.072]],["t/1241",[2,2.449,5,3.198,23,3.031,24,1.47,26,3.371,158,4.538,220,5.589,246,3.433,247,3.99,276,4.498,351,3.286,365,4.601,401,3.47,408,3.278,418,4.538,420,8.27,458,5.112,466,6.399,534,6.48,628,4.689,828,3.786,958,5.935,1036,5.992,2105,8.27]],["t/1243",[7,0.769,19,4.349,28,1.377,84,1.057,138,3.936,197,4.349,225,3.488,316,4.349,338,2.401,441,5.308,536,2.906,857,4.427,942,4.121,1066,4.814,1506,6.263]],["t/1245",[7,0.895,15,2.886,16,1.905,23,2.643,84,0.857,144,3.791,219,3.513,225,2.829,232,3.724,236,2.081,256,2.962,275,3.17,283,2.289,323,3.192,351,2.865,390,3.822,491,2.386,545,3.659,548,4.872,552,2.802,583,4.835,615,4.631,852,3.431,942,3.343,948,5.174,1202,4.17,1848,8.879,1924,6.446,2225,4.214,2297,2.637,2395,4.149,3042,4.631,3043,7.512,3044,7.512]],["t/1247",[7,0.505,16,1.192,17,2.531,23,2.966,55,3.483,85,2.218,105,2.003,113,2.335,116,2.39,135,3.65,164,1.759,171,1.74,196,2.538,213,2.606,219,1.982,225,2.289,232,2.329,236,2.682,272,2.622,275,3.157,283,2.949,313,1.564,322,2.003,323,2.583,342,4.186,366,4.005,398,2.256,401,2.448,421,4.005,438,3.323,471,2.654,479,4.459,491,1.93,543,5.002,548,3.941,552,2.267,614,1.179,702,4.313,703,5.106,757,1.578,828,2.67,920,3.722,1006,3.276,1007,5.982,1212,5.863,1217,5.336,1238,3.825,1312,2.353,1366,3.563,1427,5.106,1502,5.475,1507,9.605,1607,4.186,1638,3.307,1775,5.336,1924,5.215,1925,5.336,1926,5.475,1927,5.475,2787,4.073,4452,6.404,4453,6.404]],["t/1249",[17,3.857,23,3.258,24,1.58,84,1.057,98,3.429,160,3.421,161,6.643,219,3.021,232,3.55,275,3.021,530,4.364,607,3.74,877,3.559,927,7.376,1848,7.634]],["t/1251",[5,1.994,7,0.642,16,1.054,25,3.339,27,2.475,28,1.623,29,1.975,31,2.33,34,1.442,84,0.613,96,1.994,105,1.771,123,3.25,158,2.83,163,2.244,164,1.555,181,3.208,197,2.523,200,5.166,233,2.049,245,4.864,246,2.141,248,2.658,256,2.118,257,2.883,281,2.679,313,1.383,318,1.983,332,2.938,377,1.266,399,4.279,437,2.658,440,2.497,447,2.679,491,1.707,504,3.432,530,2.532,562,2.781,565,1.849,635,2.054,658,2.711,740,5.2,810,2.514,830,2.588,846,2.514,904,5.976,1137,3.897,1171,4.515,1280,3.813,1281,6.231,1328,4.864,1379,4.351,1457,4.041,1459,4.152,1913,4.841,2100,4.094,2636,3.248,2671,4.611,2680,3.602,2681,3.634,2691,4.429,2719,6.786]],["t/1253",[7,0.897,25,3,27,2.702,28,1.73,109,1.58,272,3.836,281,4.433,318,2.281,343,3.333,565,3.059,568,2.683,596,4.22,635,3.399,1813,6.87,2719,7.808,3046,8.011]],["t/1255",[2,1.211,7,0.833,12,1.812,16,1.276,21,1.269,23,2.29,27,2.38,28,1.314,40,2.366,56,0.677,84,1.009,103,1.503,109,1.691,110,2.366,113,1.638,127,3.811,131,2.038,133,1.922,153,2.298,155,2.909,162,2.923,164,1.234,165,1.864,171,1.22,174,2.038,193,1.634,197,2.001,219,2.882,221,1.659,225,1.605,228,2.635,232,1.634,248,2.108,298,2.743,313,1.097,318,1.67,338,1.105,343,1.598,375,2.61,377,1.534,393,2.206,440,1.981,472,2.187,491,1.354,502,2.294,524,2.765,527,1.499,536,2.042,538,3.743,544,2.809,550,1.765,557,2.235,558,2.009,565,3.871,567,2.75,568,2.872,577,3.245,578,1.765,580,1.56,593,2.215,601,2.108,605,1.625,614,0.827,630,1.659,635,1.63,670,2.61,690,2.787,747,1.844,756,3.219,757,2.294,796,4.361,810,1.994,931,3.451,999,2.443,1033,2.857,1114,2.038,1280,3.025,1314,4.668,1504,3.165,1558,2.529,1754,2.915,1822,2.703,1944,3.743,1990,3.092,2017,3.84,2064,3.205,3047,3.743]],["t/1257",[2,1.664,5,2.173,17,2.438,26,3.213,31,1.507,34,1.571,41,2.798,56,0.855,84,0.937,85,2.137,101,3.283,103,2.064,107,2.922,109,2.049,125,3.516,129,3.685,170,2.557,174,1.833,200,3.056,216,3.14,228,2.741,236,1.622,257,3.14,261,4.385,278,2.21,302,2.84,353,1.682,369,3.827,398,2.173,408,3.124,444,2.674,527,2.587,536,2.577,553,3.266,565,2.014,601,2.895,607,3.317,630,2.278,663,3.767,693,3.796,757,1.52,810,2.739,839,3.538,999,3.355,1170,3.069,1245,3.308,1257,3.585,1536,4.523,1558,3.473,1754,2.622,1918,3.069,3046,5.274,3048,5.853,3049,5.853,3050,5.853,3051,4.154,3052,5.853]],["t/1259",[4,2.423,27,2.41,28,1.102,31,2.48,84,1.1,96,2.751,109,1.902,123,3.459,125,4.452,138,3.15,164,2.789,171,2.758,174,3.017,175,4.272,179,2.613,278,2.799,297,4.272,347,5.155,377,1.747,416,3.596,544,4.885,568,2.237,607,2.993,614,2.076,630,2.885,635,2.834,818,3.922,934,3.725,1382,6.064,1504,5.504,1870,3.533,3053,5.26]],["t/1261",[5,2.926,7,0.833,24,1.88,34,2.116,56,0.82,107,2.127,127,2.725,158,4.153,174,2.469,228,2.632,236,2.185,283,2.402,295,3.341,306,3.689,338,2.044,502,2.291,536,3.146,550,3.265,558,3.715,560,4.422,577,4.997,827,4.21,830,3.797,874,4.08,928,6.923,946,3.825,1302,5.38,1328,4.963,2429,7.443,3054,7.314]],["t/1263",[2,1.63,5,2.643,7,0.847,16,1.125,17,1.509,18,1.678,19,1.702,20,2.389,21,1.079,22,2.369,24,1.677,26,2.244,28,1.462,31,1.477,33,2.177,41,1.733,46,1.583,56,0.741,84,0.655,85,1.323,90,1.8,92,1.414,98,1.342,103,1.278,125,3.446,132,2.204,138,1.54,140,1.497,145,1.536,164,1.661,165,2.083,168,3.182,179,1.278,181,2.164,183,1.639,189,2.044,220,2.351,225,1.365,226,1.852,243,2.089,244,2.351,245,4.482,255,2.572,257,1.944,280,2.429,283,1.748,285,1.578,289,2.389,292,1.661,320,2.191,339,1.926,343,3.307,353,1.041,360,2.725,366,2.389,376,2.177,386,2.842,408,1.379,430,1.884,444,1.656,468,2.429,498,3.364,502,1.053,505,1.583,509,2.164,527,1.64,536,1.137,550,3.886,565,2.786,566,2.659,574,3.479,593,1.884,596,1.72,622,1.926,628,1.972,630,2.233,631,2.496,632,2.055,636,3.404,672,2.25,686,2.987,719,2.298,732,2.496,747,1.568,759,1.339,770,2.935,796,2.002,807,4.466,814,1.645,816,2.164,820,1.963,826,3.182,827,1.935,830,2.763,832,2.572,841,1.765,856,2.281,904,2.066,909,4.161,939,2.351,946,1.758,1028,2.52,1066,2.982,1113,2.935,1180,2.409,1218,2.15,1240,2.177,1245,1.264,1278,2.546,1290,2.125,1300,2.496,1310,2.725,1355,3.479,1391,3.362,1798,2.125,1907,1.972,2232,2.691,2429,5.288,2486,2.546,2717,3.952,3032,3.182,3055,3.11,3056,3.11,3057,2.762,3058,3.819,3059,3.624,3060,3.624,3061,3.11,3062,3.362]],["t/1265",[5,2.751,21,2.207,28,1.591,31,1.908,56,1.002,84,0.846,89,2.915,105,2.443,107,2.599,109,1.317,140,3.06,144,3.74,145,3.141,165,1.75,179,2.613,199,3.852,242,5.504,257,3.976,337,3.638,507,4.248,536,2.326,550,4.432,565,2.55,580,3.525,630,2.885,633,6.11,807,3.923,828,3.257,830,3.569,908,6.678,953,3.159,1047,4.666,1352,3.756,1382,4.666,1798,4.346,2429,7.154]],["t/1267",[2,1.43,5,2.736,7,0.418,26,2.883,27,2.181,28,1.296,31,1.296,84,0.574,85,1.837,92,1.964,93,2.247,98,2.729,103,1.775,106,2.846,107,2.879,109,1.897,115,4.657,123,1.807,138,2.139,163,2.102,164,2.133,165,1.188,174,1.576,179,1.775,182,1.775,233,1.919,236,2.042,242,3.738,254,3.252,257,2.7,272,3.179,278,1.9,289,2.096,302,2.442,316,2.363,317,2.651,318,1.291,322,1.659,324,2.809,341,2.917,353,1.446,385,4.669,416,2.442,430,2.616,444,2.299,458,2.986,468,3.374,489,3.535,490,3.738,502,1.462,522,3.946,524,3.264,527,1.698,550,3.052,576,3.124,611,2.205,613,2.397,658,2.54,693,3.264,709,3.467,737,4.008,755,2.389,774,2.869,798,2.917,893,3.062,997,3.402,1037,2.022,1153,4.535,1218,4.373,1329,4.008,1381,2.102,1382,5.488,1388,4.008,1392,4.319,1474,4.076,1922,4.42,3010,5.286,3063,4.831,3064,5.033,3065,5.033,3066,5.033]],["t/1269",[7,0.761,24,1.171,25,3.09,34,1.842,84,1.045,103,2.42,107,3.09,109,1.627,113,2.637,162,2.542,164,1.987,174,2.15,224,3.064,228,2.291,236,1.902,291,4.26,433,4.176,447,3.422,476,3.504,502,1.994,517,4.384,536,2.154,550,2.842,565,2.361,568,2.764,690,4.487,757,1.782,857,3.281,862,4.802,1114,3.281,1122,4.524,1280,4.871,1290,4.024,1363,2.604,1864,3.81,1907,3.735,1970,5.768,1971,5.382,2061,2.442,2488,4.384,2787,4.601,3942,4.601,4454,7.233]],["t/1271",[2,2.273,7,0.921,8,5.734,21,2.381,84,0.913,92,3.121,96,3.754,98,2.961,157,4.866,165,1.888,197,3.755,203,2.017,313,2.059,336,3.429,364,5.939,375,6.194,383,5.868,444,3.653,532,4.395,582,7.023,593,4.157,605,3.05,636,4.745,764,5.676,996,5.315,1898,5.615,4382,7.419,4455,8.428]],["t/1273",[306,4.814,1275,6.957,1557,6.726]],["t/1275",[2061,3.73]],["t/1277",[2,1.995,7,0.922,16,1.645,24,1.198,25,2.369,27,1.179,28,1.55,29,1.733,31,1.213,56,0.49,84,0.958,89,1.854,103,1.662,105,1.554,106,1.82,109,0.838,116,1.854,123,1.693,124,3.133,127,2.9,132,1.459,160,1.741,162,1.745,165,1.113,167,3.817,170,1.468,174,2.199,175,2.716,181,2.814,193,2.691,197,2.213,225,1.775,228,1.573,275,1.537,276,2.461,313,1.213,318,1.801,334,2.814,338,1.82,343,1.767,377,1.655,390,2.398,440,3.262,471,3.664,476,2.148,490,3.5,491,1.497,502,1.37,536,2.633,550,1.952,553,2.63,558,3.308,565,3.42,568,3,576,2.926,577,2.35,580,1.725,593,3.649,601,2.331,607,1.904,663,3.034,664,3.642,670,2.887,683,1.858,734,2.198,747,2.04,757,2.414,796,2.604,810,2.205,835,3.345,836,4.139,841,2.296,852,2.153,860,3.754,874,2.439,929,3.381,930,2.716,999,2.701,1117,2.687,1188,3.817,1754,2.111,1836,3.961,2064,3.545,2696,3.345,2994,4.714,2995,4.967]],["t/1279",[2,1.125,4,1.294,5,1.47,7,0.873,27,2.126,28,0.589,31,1.941,36,1.84,42,3.059,49,2.179,56,0.412,84,0.97,92,2.941,93,3.365,106,2.375,109,1.093,131,1.893,134,2.076,138,3.911,144,1.998,153,2.134,165,1.451,171,1.134,172,3.939,173,2.233,181,2.364,188,3.017,193,1.517,207,1.703,216,2.124,224,2.745,228,2.052,236,1.097,256,1.561,276,3.21,281,1.974,288,2.754,289,2.561,292,1.815,302,1.921,318,1.016,334,2.364,337,1.943,338,1.594,339,4.007,365,2.114,376,2.378,377,1.449,409,2.114,441,2.269,468,2.654,500,3.065,502,1.15,525,1.966,527,0.912,552,1.477,555,1.974,568,2.777,578,1.639,579,3.017,580,1.449,601,4.552,620,3.584,629,2.654,632,2.245,636,3.648,638,2.51,691,5.333,715,2.144,728,2.905,732,2.727,743,2.754,774,2.257,807,1.612,810,4.306,812,5.295,816,2.364,841,1.928,862,2.076,878,3.387,906,2.529,907,2.677,927,3.153,952,2.702,995,2.84,1024,2.654,1025,4.897,1066,2.058,1360,2.378,1364,3.017,1365,4.053,2092,2.335,2353,5.35,2646,3.567,2805,3.206,2963,2.977,2996,3.567,2997,6.149,2998,4.172,2999,3.8,3000,3.959]],["t/1281",[2,1.29,7,0.911,21,1.352,27,2.808,28,1.455,30,4.5,31,1.169,36,2.11,49,1.075,84,0.518,92,1.772,101,2.547,109,1.458,116,1.786,126,2.31,138,2.902,153,2.447,162,1.681,163,1.896,165,1.072,171,1.955,172,3.572,173,4.627,174,2.57,184,3.616,188,3.46,193,3.145,196,1.896,223,2.694,224,3.048,228,1.516,246,1.809,282,2.602,304,1.008,313,1.169,320,2.745,353,1.305,407,2.163,416,2.203,478,2.9,502,1.319,505,1.983,525,4.074,552,1.694,568,2.476,576,2.818,578,1.88,597,3.716,599,2.459,601,2.246,608,2.483,614,0.881,620,2.647,631,3.127,632,4.651,638,4.329,658,2.291,675,3.044,725,2.436,741,3.098,807,3.341,810,2.124,818,2.402,819,3.372,830,3.951,831,3.018,832,5.823,877,1.745,986,3.771,1002,3.815,1009,2.203,1012,4.898,1024,3.044,1028,3.158,1066,2.36,1360,2.727,1365,2.993,1446,2.799,1847,3.222,1899,3.331,2639,3.56,2787,3.044,2789,3.257,2966,3.257,3002,4.541,3003,3.815,3004,3.987,4456,4.785]],["t/1283",[0,6.386,7,0.872,21,3.125,27,2.851,109,2.025,127,3.627,179,3.701,193,3.252,343,3.18,437,4.197,557,4.449,631,7.229,757,2.203,816,5.065,1180,5.639,2522,6.871]],["t/1285",[7,0.77,84,0.798,109,1.243,133,3.152,160,3.843,165,1.651,221,3.609,228,3.095,275,3.024,320,5.605,377,1.648,426,2.722,430,3.635,445,4.648,476,2.139,491,2.221,498,4.1,502,2.032,527,2.397,565,2.406,568,2.798,635,2.673,811,4.572,936,3.152,939,4.536,1049,5.876,1066,3.635,1276,5.483,1329,5.569,1366,4.1,1416,8.668,1481,5.764,1898,3.882,1970,5.876,2043,5.193,2064,6.973,2516,5.764,3005,6.712,3006,6.301]],["t/1287",[7,0.528,21,1.892,28,1.293,31,1.636,33,3.817,37,4.463,42,4.91,46,2.775,56,0.661,84,0.725,90,3.155,92,2.479,93,2.837,106,2.454,107,2.345,109,1.893,115,4.218,126,3.233,153,3.425,165,1.5,189,3.583,216,3.409,224,3.881,272,2.741,318,1.63,353,1.826,407,4.141,426,3.383,502,1.846,513,3.155,620,3.704,629,4.259,635,2.429,658,3.207,769,2.573,893,3.866,909,4.609,936,2.865,951,5.879,997,4.495,1009,3.083,1066,3.303,1398,5.895,1434,3.726,1445,4.662,1733,4,1814,4.779,1845,3.972,2668,4.719,2928,5.58,2966,4.558,3007,5.145,3008,4.983,3009,5.238,3010,4.558]],["t/1289",[1,2.815,2,1.958,7,0.817,8,2.606,28,0.683,31,1.183,33,4.139,34,1.234,36,2.136,39,1.971,41,2.198,46,2.007,49,1.089,50,2.834,56,0.478,84,0.944,90,3.422,91,4.036,92,3.583,93,3.076,94,5.886,96,2.558,97,2.166,109,0.817,130,2.761,132,1.423,144,2.32,145,2.92,164,1.995,165,1.085,172,2.515,179,1.621,186,2.283,203,1.16,207,1.977,216,2.466,224,2.052,228,1.534,236,1.274,240,2.649,272,1.983,283,2.519,292,2.107,313,1.774,343,1.723,351,1.753,390,2.339,407,2.19,408,1.749,437,2.274,439,3.166,440,3.202,441,2.634,447,2.292,476,3.009,482,3.247,568,2.771,578,1.903,580,1.682,593,2.389,599,5.965,601,2.274,619,4.66,620,4.017,658,2.32,670,2.815,675,3.081,694,2.634,715,4.477,737,3.661,757,1.194,769,1.862,810,2.151,812,3.229,822,2.894,832,3.262,877,1.766,902,3.373,997,1.794,998,4.66,1009,2.23,1066,2.389,1434,4.846,1814,3.457,2353,2.894,2429,3.414,3011,4.412,3012,4.036,3013,4.597]],["t/1291",[2061,3.73]],["t/1293",[7,0.884,16,1.701,17,2.077,18,4.926,19,4.996,20,3.288,26,2.864,28,1.089,31,1.284,48,2.132,56,0.519,84,0.836,85,1.821,89,1.962,90,2.477,92,1.946,93,2.227,123,1.791,164,2.511,165,1.177,174,1.562,199,2.593,201,2.676,221,1.941,225,3.267,226,4.883,227,3.469,232,1.912,237,3.854,240,2.875,247,2.31,248,2.467,275,1.627,291,3.096,296,2.026,298,4.712,302,4.209,318,1.28,323,2.12,353,2.104,359,2.318,377,1.725,390,2.538,391,2.213,403,3.373,407,2.376,418,2.627,447,2.487,478,3.186,484,2.227,536,1.565,541,3.235,555,2.487,557,2.615,568,1.505,596,2.367,630,1.941,664,3.854,683,1.967,719,3.163,731,2.755,762,3.54,764,3.54,821,3.66,851,2.755,854,2.798,856,3.14,874,3.789,879,4.112,940,1.967,1029,3.618,1043,2.728,1044,3.096,1195,4.38,1196,4.191,1221,3.911,1476,2.835,1491,4.039,1782,2.367,1788,4.38,1801,2.996,1998,3.055,2725,4.788,2794,5.256]],["t/1295",[7,0.887,19,3.001,21,1.903,34,1.715,56,0.665,84,0.729,103,2.253,156,4.584,162,3.232,174,2.002,236,1.771,249,5.011,278,2.413,296,2.595,302,3.101,306,2.99,326,4.584,338,1.657,353,2.508,375,3.914,408,2.431,430,3.322,463,7.317,685,3.747,874,6.383,885,3.839,920,3.914,1111,4.402,1174,6.13,1314,4.584,1825,5.758,1960,7.501,2364,7.489,2365,8.096,2780,6.391,2970,6.134,2971,6.391,2972,6.391,2973,5.929,4457,9.197,4458,6.735,4459,6.735,4460,6.735]],["t/1297",[2061,3.73]],["t/1299",[7,0.624,16,1.473,18,4.5,24,1.838,28,1.117,34,2.016,56,1.011,84,0.857,98,2.782,103,2.649,140,3.102,160,3.59,162,2.782,164,2.175,165,1.773,203,1.895,225,2.829,248,3.715,254,2.545,261,4.011,285,4.231,338,1.947,353,2.159,377,1.77,512,2.729,536,2.357,759,3.979,760,2.339,1033,5.035,1114,3.591,1235,5.051,1236,7.21,3067,7.512,3068,7.512]],["t/1301",[16,1.981,24,1.724,116,3.972,807,4.113,2456,9.101]],["t/1303",[7,0.763,16,2.16,24,1.88,28,1.638,165,2.168,289,3.825,830,4.423,832,6.518,877,3.529,907,6.211,1446,5.663,1798,5.385,1819,6.211]],["t/1305",[24,1.567,28,1.366,203,2.317,323,3.904,338,2.381,377,2.165,550,3.803,807,3.74,1223,5.483,2254,8.065,3069,9.185,3070,8.521,3071,9.185,3072,9.185,3073,8.521,3074,9.185]],["t/1307",[2,1.958,7,0.687,12,1.954,16,1.621,20,3.03,27,2.068,28,1.535,49,1.089,56,0.86,96,2.558,107,2.786,109,1.835,127,2.381,138,1.954,153,3.714,164,1.331,165,1.085,170,2.145,173,5.823,174,2.158,193,3.77,197,2.158,217,2.761,224,2.052,236,1.909,256,1.812,257,2.466,261,2.455,272,1.983,289,2.87,313,1.774,318,1.768,322,1.515,323,3.513,353,1.321,394,2.421,397,2.014,405,1.541,407,3.282,418,2.421,426,1.789,432,4.58,433,2.797,437,2.274,484,2.052,485,3.661,491,1.46,507,2.634,550,4.073,565,2.371,568,2.494,632,5.205,635,1.757,647,1.817,743,4.792,807,1.872,812,4.84,829,2.552,851,2.539,877,1.766,878,2.065,907,3.109,916,2.815,986,3.806,995,3.297,1037,1.846,1047,2.894,1122,3.03,1302,3.137,1352,2.329,1376,2.421,1915,3.944,2033,2.727,2282,3.229,2653,3.863,3075,4.597,3076,4.142,3077,4.142]],["t/1309",[2,2.149,16,2.119,18,3.502,28,1.451,56,0.787,96,2.807,97,3.564,105,2.493,107,2.633,132,3.345,145,3.205,170,3.364,193,3.741,313,1.947,318,1.94,338,1.96,405,2.535,507,4.334,550,4.728,596,3.589,862,3.966,1047,4.761,1122,4.985,1285,4.487,1290,4.434,1302,5.161,1351,7.259,1352,3.832,3076,6.814,3077,6.814]],["t/1311",[16,1.983,23,2.813,24,1.365,25,2.698,28,1.504,49,1.894,56,1.052,109,1.421,132,2.475,228,2.67,297,4.609,313,2.059,398,2.969,476,2.446,491,2.54,502,2.939,536,2.51,545,3.895,577,5.812,580,2.926,1037,3.213,1235,4.157,1332,5.147,3078,9.383,3079,7.419,3080,7.419]],["t/1313",[0,3.822,12,2.67,28,0.934,49,1.488,56,1.103,107,1.695,109,1.75,112,5.801,172,4.056,193,4.259,217,3.774,391,2.787,502,1.826,525,3.12,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,953,4.199,954,4.413,959,6.059,986,3.47,988,3.705,1009,4.779,1235,5.511,1331,7.374,1332,6.824,1367,3.21,3081,5.829,3082,9.138,3083,5.829,3084,5.829,3085,5.829]],["t/1315",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,997,4.614,1235,4.799,1245,2.423,1331,7.685,1332,5.943,1381,3.856,1389,5.771,3086,6.447,3087,6.447]],["t/1317",[2061,3.73]],["t/1319",[7,0.804,31,1.921,56,0.776,84,0.852,123,2.679,162,2.763,215,5.851,216,4.003,219,2.434,275,2.434,283,2.274,306,3.491,318,1.914,340,5.241,390,3.796,463,4.731,467,6.151,527,1.719,555,3.72,565,2.567,567,3.153,568,2.252,604,5.189,683,2.942,692,6.27,693,4.84,734,3.479,738,6.922,798,4.325,802,3.181,814,3.386,874,3.861,930,4.3,1029,5.412,1066,3.878,1138,5.942,1174,5.241,1178,5.765,1180,4.959,1189,7.162,1289,3.181,1960,5.241,3014,7.462]],["t/1321",[7,0.787,10,4.115,19,3.39,22,4.72,23,2.54,24,1.232,27,1.806,34,2.541,88,3.426,94,4.115,103,2.546,106,3.657,123,2.593,158,3.803,220,4.683,221,2.81,225,2.719,226,3.689,313,1.859,332,3.948,339,3.837,341,4.184,384,6.34,387,5.297,390,3.673,391,3.203,491,2.293,495,3.673,506,4.513,511,2.425,512,2.623,513,3.585,519,6.34,599,3.91,694,5.427,773,7.806,937,5.236,1069,4.115,1105,6.34,1106,7.22,1107,4.008,1111,4.973,1179,4.683,1431,6.067]],["t/1323",[7,0.881,24,1.623,26,2.843,28,1.414,34,1.95,41,3.474,46,3.173,47,4.83,56,0.99,90,4.722,92,4.136,93,3.244,94,4.142,96,2.697,97,3.424,132,2.249,186,3.608,216,3.898,272,3.135,283,2.214,353,2.088,418,3.827,491,2.308,509,4.338,568,2.193,597,5.175,599,3.936,601,3.594,611,3.183,614,1.41,619,4.914,620,4.236,715,5.15,874,3.761,998,4.914,1433,6.106,1434,4.261,1866,6.106]],["t/1325",[7,0.818,16,1.503,27,1.918,28,1.617,31,1.974,34,2.058,84,0.875,88,3.639,89,4.277,123,2.753,145,3.249,174,2.401,232,2.939,244,4.973,254,2.598,278,2.895,283,2.336,289,3.193,294,4.346,306,3.587,318,1.967,484,3.423,485,6.106,512,2.786,558,3.613,575,3.838,780,4.301,883,5.924,884,5.096,1114,3.665,1243,5.924,1289,3.268,1315,4.444,1441,4.861,1782,3.639]],["t/1327",[4,2.077,7,0.823,10,3.622,17,2.646,23,3.748,26,2.486,28,1.293,31,2.551,34,1.705,56,0.661,84,0.992,103,2.241,123,3.558,219,2.836,232,4.083,236,1.761,254,2.153,283,3.019,384,5.58,478,4.059,484,2.837,485,5.061,511,2.135,530,2.994,545,3.095,548,5.638,552,3.696,558,2.994,567,3.673,568,1.918,601,3.143,614,1.233,629,4.259,685,3.726,814,2.883,852,4.526,948,5.988,1007,4.51,1014,4.336,1197,5.34,1203,6.099,1212,4.419,1441,4.029,1506,4.297,1782,3.016,1831,5.34]],["t/1329",[2,2.323,7,0.852,24,1.912,28,1.215,84,0.933,165,1.929,248,4.043,283,2.491,285,3.558,313,2.104,397,3.58,512,2.97,536,2.565,565,2.812,567,3.453,568,2.467,757,2.909,759,3.019,760,2.545,878,3.673,930,4.711,1473,3.838,2032,3.626,2398,6.619,3015,7.583,3016,5.182]],["t/1331",[7,0.739,16,1.743,24,1.843,28,1.322,56,0.925,84,1.015,132,3.343,145,3.768,228,2.968,502,2.584,565,3.059,568,2.683,683,3.506,769,3.601,946,5.241,1498,6.687,2696,6.31]],["t/1333",[7,0.751,16,1.772,27,2.26,31,2.326,40,5.016,56,0.94,107,2.943,109,2.082,123,3.245,163,3.774,527,2.082,597,4.917,635,3.455,658,4.56,814,4.1]],["t/1335",[7,0.789,16,2.203,28,1.671,109,1.688,132,3.478,170,2.957,283,2.894,862,4.981,1139,5.356]],["t/1337",[23,2.875,24,1.395,25,3.46,28,1.215,32,5.387,49,1.936,56,0.85,98,3.027,132,2.53,157,4.973,165,1.929,236,2.265,337,4.011,339,4.344,491,2.596,502,2.98,513,4.059,544,5.387,558,3.851,565,2.812,577,5.112,757,2.122,795,5.63,796,4.515,1312,3.165,1427,6.868,2032,3.626,2805,6.619]],["t/1339",[28,1.344,107,2.438,109,2.082,115,4.384,132,2.797,174,2.83,236,2.504,254,3.062,360,6.795,507,5.179,550,3.742,635,3.455,658,4.56,997,3.526,1047,5.688,3017,9.036]],["t/1341",[2,2.106,7,0.8,16,1.453,28,1.432,56,0.771,109,1.712,126,3.771,132,2.294,138,3.15,172,3.907,190,4.014,193,3.692,224,3.309,288,5.155,289,3.087,313,1.908,337,3.638,338,1.921,339,3.94,408,2.819,491,2.354,552,2.765,611,3.247,614,1.438,632,4.202,694,4.248,755,3.518,806,6.11,810,3.468,819,5.504,830,3.569,877,2.848,906,4.734,952,5.058,1009,3.596,1360,4.452,2639,5.812,2867,4.569,3018,7.114]],["t/1343",[7,0.789,27,2.376,29,3.492,109,1.688,127,3.282,254,3.218,338,2.462,353,2.729,390,4.833,715,5.144,1043,5.194,1044,5.896]],["t/1345",[7,0.839,58,7.697,275,3.295,564,6.893,1625,8.045]],["t/1347",[7,0.684,13,6.787,84,0.94,228,2.749,283,3.139,294,4.668,300,5.259,332,4.503,395,3.429,399,6.558,476,2.519,542,7.23,543,6.112,742,5.427,863,6.667,873,7.066,877,3.164,888,5.111,889,6.667,890,6.456,891,5.844,1328,5.183,1566,6.192,1991,5.259,2683,5.341,3020,6.274,3021,6.558,3022,8.234]],["t/1349",[7,0.872,11,7.13,17,3.534,18,3.929,19,4.928,28,1.262,158,5.528,318,2.177,332,5.74,391,3.764,846,3.97,880,3.85,888,5.267,917,8.463,922,6.086,923,7.872,924,7.872,925,10.074,1782,4.027]],["t/1351",[7,0.973,135,5.428,174,2.83,233,4.16,278,3.412,363,6.795,444,4.128,484,4.034,578,3.742,712,5.122,849,7.196,850,6.224,854,5.068,856,5.688,916,5.534]],["t/1353",[2061,3.73]],["t/1355",[7,0.867,16,0.836,21,1.269,24,1.624,26,2.546,27,1.628,31,2.45,46,1.861,47,2.833,56,0.444,96,1.582,107,1.15,109,0.757,123,3.418,124,2.833,132,1.319,145,2.758,162,2.41,165,1.536,174,1.335,187,3.127,189,2.404,193,1.634,201,2.287,213,1.828,215,5.103,216,2.287,226,4.034,228,1.423,229,2.664,232,1.634,236,1.181,244,2.765,245,2.683,256,1.681,273,3.582,275,1.39,276,4.613,280,2.857,283,2.406,315,2.134,351,1.625,353,1.87,359,1.981,377,1.534,390,4.017,393,2.206,409,2.276,430,2.215,444,1.947,458,3.862,463,5.006,467,5.365,478,2.723,493,4.262,565,2.716,568,2.872,604,5.49,607,1.721,611,1.867,621,2.309,622,2.266,628,2.32,635,1.63,683,2.566,693,2.765,718,3.84,765,2.664,767,3.451,802,1.817,814,1.934,842,3.84,854,2.391,885,3.909,886,2.857,920,3.986,999,2.443,1129,2.809,1172,4.091,1174,2.994,1175,3.743,1176,4.091,1179,2.765,1180,4.325,1181,2.331,1182,3.84,1315,2.47,1395,3.513,1960,2.994,1987,3.743,1988,3.84,2428,3.658,2646,3.84,2963,3.205,3344,3.658,3571,4.262,3572,4.262,3573,4.262,3574,4.262,3575,6.858,4461,4.492,4462,4.492,4463,4.492,4464,4.492]],["t/1357",[2061,3.73]],["t/1359",[2,1.058,4,1.216,7,0.9,15,1.43,16,0.73,18,4.138,21,1.108,24,1.402,28,1.694,31,0.958,48,1.591,49,1.387,55,5.439,56,1.127,84,1.083,85,2.138,89,3.732,103,2.554,109,1.287,132,1.813,160,3.301,170,1.159,171,1.065,187,2.73,200,1.943,203,0.939,223,2.208,225,1.402,272,1.605,275,1.911,278,1.405,279,1.694,283,1.134,295,1.577,296,2.379,313,0.958,318,1.503,322,1.226,342,4.035,347,2.588,359,1.729,383,2.73,393,1.926,408,2.228,416,1.805,482,1.753,527,1.35,531,2.377,605,1.419,608,3.203,614,1.405,630,1.448,684,2.045,755,2.78,760,1.159,802,2.497,814,2.658,839,2.249,841,1.812,846,3.844,847,3.414,849,4.665,859,3.127,882,4.56,900,2.563,903,2.73,953,1.586,1057,2.539,1059,2.195,1060,1.901,1114,1.779,1129,2.453,1152,2.641,1163,2.377,1169,2.494,1214,2.539,1221,2.918,1234,2.221,1303,2.342,1312,2.268,1381,1.554,1456,2.669,1458,2.669,1500,2.964,1558,2.208,1633,1.848,1782,4.883,2013,2.473,2531,3.127,2782,2.588,3644,1.444,4303,3.721,4465,6.173,4466,3.921,4467,3.921,4468,6.72,4469,3.921,4470,6.173,4471,3.452,4472,3.721]],["t/1361",[7,0.872,28,1.396,55,5.381,84,0.814,89,3.692,96,2.645,140,3.877,160,2.633,165,1.682,169,4.107,171,2.04,179,2.513,213,3.056,232,4.278,275,3.063,283,2.172,317,3.754,351,2.718,491,2.264,531,4.553,593,3.704,614,1.383,622,3.788,736,4.219,802,3.038,828,4.126,852,5.298,882,4.486,920,4.365,1366,4.179,1373,4.587,1693,4.394,1782,3.383,2782,4.957,4468,6.612]],["t/1363",[7,0.95,34,2.295,55,6.044,219,2.789,221,3.328,254,2.897,283,2.605,303,7.048,361,6.068,391,3.793,471,4.605,478,5.461,545,4.164,548,5.546,552,3.189,558,4.029,578,3.541,684,4.699,829,4.747,879,7.048,1014,5.835]],["t/1365",[7,0.811,16,1.483,28,1.125,33,4.543,56,0.787,84,1.114,109,1.734,134,3.966,165,2.304,245,4.761,247,3.502,320,4.572,353,2.173,366,4.985,405,2.535,418,3.983,426,2.943,432,5.026,471,3.303,509,4.515,566,5.549,628,4.116,757,1.964,835,5.367,847,4.408,883,5.844,949,5.93,958,5.209,1181,4.136,1282,7.903,1381,3.159,1527,5.26,1801,4.543,1878,7.563,3886,6.641,3969,7.903]],["t/1367",[4,2.393,7,0.72,16,0.978,21,1.485,24,0.851,28,1.089,49,1.734,55,5.833,56,0.903,84,0.569,85,3.166,107,2.34,109,1.301,139,4.813,160,1.843,162,2.711,165,1.728,170,3.169,196,2.083,203,1.847,246,1.988,272,2.152,279,2.271,283,1.52,285,2.172,313,1.284,322,1.644,326,3.578,338,1.293,353,1.433,359,2.318,416,2.42,429,4.494,430,2.593,471,2.178,482,2.35,517,3.186,527,2.202,580,2.679,608,2.728,614,1.42,623,2.517,628,2.715,703,4.191,760,3.312,790,3.66,802,2.126,831,3.315,841,3.566,847,2.908,880,2.263,882,4.609,899,3.618,900,3.436,910,4.788,953,3.697,1006,2.689,1245,3.332,1330,3.578,1381,3.058,1539,3.504,1540,2.908,1870,1.829,1902,4.803,2100,3.801,3057,3.801,3644,1.936,4473,7.321,4474,3.854]],["t/1369",[34,2.386,109,1.919,283,2.709,295,3.768,338,2.305,376,5.341,408,3.382,471,3.883,491,2.824,527,2.048,613,4.235,614,2.095,732,6.124,802,3.79,912,6.524,1870,3.962,2800,7.472]],["t/1371",[2,2.189,7,0.75,9,3.649,10,4.389,21,1.592,28,0.795,41,2.556,48,2.286,49,2.581,55,4.414,56,0.939,84,0.61,105,2.538,109,1.368,113,2.959,131,2.556,132,1.655,160,3.334,163,2.233,165,1.262,179,1.885,190,2.896,200,2.792,211,4.193,225,2.014,236,1.482,256,2.108,296,2.171,304,1.187,313,1.377,351,2.039,353,2.213,359,3.578,393,2.767,416,2.595,418,2.816,471,2.335,476,1.636,505,2.335,524,3.468,531,3.416,611,2.342,614,1.494,677,2.969,742,5.076,747,3.332,795,3.683,802,3.283,805,3.836,857,2.556,880,2.426,883,4.132,899,3.878,900,3.683,912,7.245,992,2.816,1033,3.584,1133,3.031,1170,1.73,1540,4.489,1566,4.021,1632,2.924,1633,2.655,1850,3.795,1870,3.998,1945,3.756,2750,4.696,3057,4.075,3505,4.961,3514,4.33,4475,5.635]],["t/1373",[5,3.126,23,2.962,34,2.26,68,3.364,84,0.961,169,4.853,239,5.09,289,4.35,338,2.183,353,2.42,367,4.518,418,5.502,471,3.677,484,3.759,511,3.509,512,3.059,513,4.181,536,2.643,608,4.605,807,4.254,940,3.32,1376,4.435,2899,7.587]],["t/1375",[7,0.669,56,0.838,84,0.919,140,3.326,157,4.901,160,2.976,278,3.042,297,4.642,376,4.839,608,5.557,611,3.529,614,2.267,732,5.548,828,3.54,835,5.717,847,4.695,882,5.071,911,8.538,912,5.91,1170,2.606,1181,4.405,1330,5.778,1870,2.954,1874,5.91,2905,6.058,3645,8.055,3984,8.055,4476,8.489]],["t/1377",[2,2.487,7,0.727,24,1.493,56,0.911,103,3.086,233,3.338,318,2.245,353,2.515,471,3.822,580,3.202,790,6.421,802,3.73,911,7.354,914,4.589,953,3.73,1037,4.297,1170,2.831,1373,5.633,1801,5.257,3731,8.119,4477,9.223]],["t/1379",[2061,3.73]],["t/1381",[7,0.81,16,1.911,34,2.616,164,2.822,261,5.205,484,4.352,532,5.357,747,4.218,3510,9.748]],["t/1383",[7,0.822,15,2.966,16,1.514,34,2.072,49,2.342,56,0.803,84,0.881,85,2.818,103,2.722,192,4.792,203,2.495,228,2.577,261,5.281,318,2.537,401,3.983,481,4.86,484,3.447,607,3.118,614,1.498,723,2.908,877,2.966,1363,2.929,1476,2.989,2026,4.161,2027,5.088,2033,4.581,2297,2.71,2600,5.007,3051,5.479,3511,7.72,3512,7.72,3513,7.173]],["t/1385",[7,0.918,49,1.881,203,2.003,278,2.998,285,3.457,304,1.763,318,2.983,398,3.737,401,3.198,527,1.829,565,2.732,568,2.396,759,2.933,760,3.135,898,5.144,919,5.759,920,4.863,1116,5.522,1312,3.074,1373,5.11,1487,5.577,1918,5.28,2395,4.386,2895,5.15,3514,6.43,4478,7.621]],["t/1387",[7,0.913,19,3.68,21,1.631,24,1.974,28,1.571,34,2.68,49,1.298,84,1.206,85,2,103,3.226,105,1.806,132,2.832,162,2.029,165,1.293,196,2.288,203,1.382,228,1.829,232,2.1,236,1.518,254,1.856,261,4.185,285,2.385,372,2.16,377,1.291,397,2.4,398,2.909,471,2.393,527,1.805,530,2.582,536,1.719,565,3.635,568,1.653,580,2.867,613,2.61,630,2.132,757,1.423,759,3.902,760,3.289,934,3.938,1037,3.148,1114,3.746,1312,2.121,1550,4.296,1558,3.251,1782,2.6,1822,4.969,1918,2.873,1971,4.296,2488,3.5,2878,5.259]],["t/1389",[2,2.093,7,0.612,16,1.881,24,1.82,27,1.842,28,1.743,34,2.574,107,2.878,109,2.008,116,2.895,132,2.969,135,4.423,145,3.12,170,2.292,228,3.202,236,2.04,261,5.122,318,1.889,353,2.756,372,2.903,528,4.088,558,3.469,596,3.494,799,5.864,934,3.7,1214,5.024,1782,5.063]],["t/1391",[7,0.771,19,3.746,21,1.673,24,0.958,25,2.691,27,2.526,28,0.835,31,1.446,40,3.118,48,2.401,56,0.584,84,0.911,96,2.085,107,2.502,109,1.969,123,2.017,124,3.733,127,2.757,135,3.374,158,2.959,160,2.947,219,1.832,221,3.104,226,2.87,272,3.441,275,1.832,278,2.121,283,1.712,316,2.637,318,2.736,360,4.224,394,2.959,395,2.339,397,2.461,471,3.484,527,2.326,550,3.303,555,2.8,557,2.945,561,5.211,562,2.907,565,1.933,567,2.373,568,1.695,605,3.042,607,2.269,630,2.186,654,5.211,658,2.834,729,4.224,757,1.459,886,3.765,939,3.643,1053,4.63,1274,3.615,1290,3.293,1416,4.224,1476,2.175,1628,4.548,1773,4.474,2061,1.999,2636,3.396,3019,5.061,3515,5.061]],["t/1393",[7,0.689,16,2.029,24,1.415,26,4.049,34,2.226,49,1.965,145,3.515,159,5.827,164,2.401,261,4.43,276,4.33,300,5.299,359,3.854,392,5.423,393,5.356,399,6.607,403,5.61,611,3.634,742,5.467,744,4.983,764,7.345,1045,5.827,1991,5.299,3020,6.321,3021,6.607]],["t/1395",[1,6.551,7,0.889,8,6.064,24,1.493,32,5.768,49,2.073,106,3.38,133,3.945,225,3.296,381,4.363,393,4.529,434,6.147,436,6.581,555,4.363,590,4.961,611,3.834,1469,7.885,2683,5.676,4479,6.862]],["t/1397",[7,0.852,15,3.141,26,3.197,28,1.215,47,5.432,56,0.85,84,0.933,90,4.059,92,3.189,93,4.578,97,3.851,130,4.91,138,3.474,229,5.109,261,4.365,272,3.526,367,4.385,408,3.109,440,3.798,441,4.684,484,3.649,580,2.991,599,4.427,605,3.117,614,1.586,619,5.527,620,4.764,828,3.592,998,5.527]],["t/1399",[7,0.689,16,1.627,56,0.863,84,0.947,88,3.937,109,1.474,133,3.74,134,4.35,189,5.836,192,5.149,194,6.717,200,4.33,229,5.185,248,4.103,254,2.811,283,2.528,284,5.887,353,2.384,436,6.238,545,4.04,854,4.653,993,5.047,1043,4.537,1224,5.61,1381,3.464,1399,5.149,1558,4.922,2041,6.971]],["t/1401",[2061,3.73]],["t/1403",[2,1.397,5,1.11,7,0.602,16,1.927,17,2.046,18,3.704,19,3.4,23,1.729,24,1.726,25,1.658,27,2.373,28,1.279,29,1.807,84,0.827,88,2.332,89,3.381,105,0.986,107,1.954,109,1.421,127,2.161,132,2.241,145,3.644,153,1.612,162,1.108,165,1.709,168,2.627,169,1.724,170,3.149,172,1.794,173,2.771,174,1.539,179,1.055,193,1.147,203,0.755,225,1.127,228,1.64,232,1.147,236,2.872,275,1.603,283,1.497,289,2.604,294,1.696,313,0.77,323,2.088,336,3.687,343,1.121,365,1.597,372,1.937,391,3.814,397,1.31,405,1.647,476,1.503,491,0.95,507,1.714,510,2.627,511,1.005,512,2.631,530,1.41,545,2.393,550,1.239,558,1.41,575,2.46,577,1.491,580,1.095,591,1.797,596,1.42,647,1.183,653,0.992,723,1.127,734,2.291,736,3.254,747,1.294,748,2.382,749,2.041,757,0.777,759,1.105,760,2.677,762,2.123,769,1.99,780,1.678,807,2.001,827,3.867,828,2.159,854,1.678,877,1.888,940,1.179,946,2.384,953,1.275,997,2.825,1037,4.063,1047,1.883,1133,1.696,1223,1.786,1266,0.86,1281,1.59,1284,1.541,1303,1.883,1315,3.625,1352,3.169,1380,1.988,1389,3.071,1408,1.4,1449,3.979,1468,3.152,1558,2.916,1819,2.023,2032,1.327,2054,2.311,2353,1.883,2509,2.311,2837,2.346,4480,3.397]],["t/1405",[7,0.18,16,2.078,18,1.73,19,1.015,21,1.976,23,0.76,24,0.369,25,2.239,26,1.93,27,1.948,28,1.629,29,0.795,31,0.556,34,1.577,56,1.14,84,0.94,85,0.789,88,3.149,89,1.469,96,1.386,98,1.827,100,2.769,102,1.436,105,1.626,106,0.835,108,2.033,109,0.877,113,1.435,116,1.469,127,0.747,128,1.877,145,0.916,160,0.798,163,2.06,165,1.387,179,0.762,182,2.561,183,0.977,198,1.109,200,1.95,201,1.159,203,0.545,216,1.159,231,1.647,278,1.41,279,1.701,281,1.077,293,1.816,294,1.225,295,2.09,297,1.245,306,1.748,313,0.556,322,0.712,323,1.588,332,1.182,338,0.968,339,1.149,343,1.4,353,1.073,377,1.711,405,2.889,413,1.721,416,1.049,418,1.138,426,1.454,440,1.004,472,1.109,476,0.661,484,0.965,495,1.1,502,0.628,507,2.141,508,3.647,509,1.29,511,0.726,512,3.359,520,2.074,552,0.806,557,1.133,558,1.76,592,1.461,596,1.026,614,0.419,622,1.985,636,1.282,647,1.477,653,1.239,723,4.127,731,1.194,734,1.008,736,3.7,757,1.281,828,0.95,851,2.063,857,1.786,884,3.279,914,2.587,934,1.086,936,2.991,939,1.402,1013,1.695,1033,1.449,1037,1.5,1060,1.908,1069,2.812,1075,1.323,1164,1.149,1179,1.402,1235,3.775,1245,2.871,1274,1.391,1289,1.592,1308,2.335,1315,2.165,1362,1.647,1363,2.756,1381,1.56,1385,1.781,1408,1.011,1473,2.316,1476,0.837,1492,1.898,1511,3.398,1695,2.005,1705,3.175,1768,1.55,1778,1.854,1805,1.898,1849,1.605,1850,1.534,1864,5.955,1876,1.475,1882,1.341,1964,1.55,1998,1.323,2696,1.534,2782,1.503,2833,1.605,2837,1.695,2896,4.233,3515,1.947,3795,1.625,3997,1.503,4481,2.161,4482,2.161,4483,2.161,4484,2.161,4485,2.161,4486,2.161,4487,2.161,4488,2.161,4489,3.735,4490,2.161,4491,3.735,4492,2.161,4493,2.161,4494,2.161,4495,2.161,4496,2.161,4497,2.454,4498,1.75,4499,2.161]],["t/1407",[7,0.323,16,1.896,17,1.62,23,2.134,24,1.964,25,2.84,27,0.973,28,1.438,29,2.23,31,1.001,39,1.668,56,1.295,68,1.554,84,0.851,88,1.846,95,2.543,100,3.967,105,1.282,107,1.049,108,3.301,112,2.031,127,1.344,128,1.955,165,0.918,172,1.42,182,2.139,203,0.981,228,1.299,292,1.783,304,0.864,377,0.917,444,1.777,446,2.608,476,2.28,483,2.585,543,2.308,577,1.939,614,1.446,670,2.382,723,3.438,747,1.683,759,1.437,760,2.621,769,3.019,813,4.139,846,3.488,884,2.585,936,1.754,940,1.534,946,1.888,997,1.518,1037,2.994,1060,3.809,1087,3.098,1122,2.564,1245,2.935,1263,2.485,1266,2.142,1284,2.004,1289,1.658,1312,2.348,1352,1.971,1363,2.301,1367,3.809,1381,2.533,1473,1.827,1729,3.269,1730,3.338,1733,2.449,1765,2.352,1798,2.281,1864,2.16,2092,2.294,2836,3.15,2867,2.398,3010,2.79,3108,5.097,3553,3.098,3630,3.15,3795,2.925,4320,6.391,4500,4.417,4501,4.417,4502,6.887,4503,4.417,4504,4.417,4505,4.417,4506,4.417,4507,4.417,4508,4.417,4509,3.416]],["t/1409",[12,2.718,16,1.254,24,1.091,25,2.158,27,2.797,28,1.298,34,1.716,39,1.787,48,1.781,56,0.98,98,1.543,100,1.963,103,1.469,106,1.609,107,2.682,108,3.481,109,1.898,127,1.44,170,2.719,173,3.607,182,2.255,183,2.893,193,3.968,203,1.613,244,2.703,261,2.225,272,1.797,278,2.415,295,1.766,297,4.486,320,2.519,322,1.373,347,2.898,353,1.838,418,2.195,444,1.903,502,1.211,507,2.388,550,1.725,614,2.008,632,3.626,635,3.338,653,1.382,723,2.409,769,3.536,934,2.094,936,1.878,953,2.726,986,3.533,988,3.772,1012,4.647,1037,1.674,1047,2.623,1060,3.268,1122,2.746,1223,2.487,1245,2.714,1266,1.838,1274,4.116,1315,3.707,1352,2.111,1363,3.313,1376,2.195,1696,2.793,1733,2.623,1737,1.656,1765,4.706,1864,4.848,2092,2.457,2867,2.569,3795,3.133,3796,3.374,3866,4.775,4510,4.391,4511,3.501]],["t/1411",[16,1.473,24,1.282,27,2.431,28,1.445,56,1.121,108,4.088,109,2.096,134,3.939,170,3.025,183,3.397,189,4.236,297,5.6,323,3.192,476,2.298,495,3.822,502,2.183,608,4.108,635,2.872,707,4.404,723,2.829,813,5.126,936,3.386,1037,3.017,1053,6.192,1060,3.838,1245,2.619,1315,4.354,1352,4.924,1622,5.08,2231,5.724,3129,7.21,4510,7.916]],["t/1413",[2,1.38,16,1.85,24,2.092,25,2.422,28,1.699,29,1.786,31,1.251,39,3.079,49,1.15,56,0.889,68,2.869,112,2.536,124,3.228,140,2.006,165,2.016,171,1.391,199,2.525,203,1.225,256,1.915,285,2.115,289,2.023,296,1.973,302,2.357,313,1.251,322,1.601,323,2.064,367,5.062,405,1.628,426,1.89,446,3.256,511,1.632,564,3.315,596,2.305,712,2.754,723,3.554,726,3.346,748,3.869,749,3.315,755,2.305,760,3.281,769,4.268,814,2.204,820,2.631,827,3.834,831,3.228,946,5.431,1037,1.951,1038,4.662,1039,4.506,1060,2.482,1114,2.322,1239,3.151,1245,1.694,1278,3.412,1363,2.724,1444,3.412,1733,3.058,1779,3.447,1864,3.986,2092,2.865,2867,5.817,3889,5.119,4512,5.119]],["t/1415",[7,0.592,16,2.059,25,2.405,27,1.783,28,1.66,49,1.688,109,1.983,127,3.857,171,2.688,172,3.427,198,3.657,203,1.798,254,2.415,296,2.894,322,2.349,338,2.893,444,3.256,502,2.071,527,1.642,562,3.688,614,1.383,638,4.519,723,3.537,827,3.806,936,3.213,997,4.097,1408,3.335,1557,6.865,3010,5.112,4513,8.093]],["t/1417",[2,1.306,7,0.382,16,1.351,27,2.913,28,1.229,29,1.69,56,1.253,96,1.706,107,1.24,109,1.748,127,1.589,129,2.894,163,2.878,170,1.431,171,1.973,182,3.238,183,2.079,193,3.168,196,1.92,203,1.16,278,1.736,279,2.093,296,1.867,297,2.649,313,1.183,338,1.192,360,5.182,440,2.136,476,2.809,507,5.262,596,2.182,614,0.892,653,2.742,723,1.731,839,2.779,953,2.937,1047,4.338,1245,1.603,1278,3.229,1315,3.994,1352,4.188,1363,3.137,1364,5.251,1367,2.349,1408,3.868,1864,2.552,1870,1.686,3053,3.262,4514,10.426,4515,5.22,4516,9.386,4517,9.386,4518,9.386,4519,9.386,4520,9.386,4521,9.386,4522,9.386,4523,7.934,4524,5.22,4525,5.22,4526,5.22,4527,5.22]],["t/1419",[16,1.849,24,1.797,25,2.42,28,1.402,29,2.637,31,1.847,39,4.043,56,0.746,95,4.689,100,3.38,106,2.77,188,5.466,197,3.368,198,3.68,282,4.111,289,2.987,302,3.48,505,3.133,557,3.761,635,2.742,723,3.968,751,4.364,763,5.145,778,4.767,830,4.541,1446,4.422,1467,5.913,1768,5.145,1798,6.56,1799,5.203,1819,6.376,1864,5.235,1898,5.235,2741,5.913,4528,8.145,4529,8.145]],["t/1421",[5,4.209,16,1.759,23,3.157,24,1.136,28,1.509,29,2.448,56,0.934,88,3.16,145,2.821,153,3.589,162,2.466,163,2.781,165,2.396,169,3.837,190,3.606,216,3.572,217,4,239,6.566,289,4.523,322,2.195,365,3.555,367,4.814,376,4,430,3.461,433,4.051,505,2.908,512,2.419,647,3.547,732,4.586,748,5.303,751,4.051,753,5.999,807,4.423,1051,5.392,1069,3.795,1266,2.578,1363,2.527,1381,3.748]],["t/1423",[2,2.1,16,1.638,19,1.801,24,1.547,25,1.294,27,1.501,28,1.625,56,0.399,89,1.509,97,1.808,102,2.55,107,2.594,109,1.066,113,1.474,125,2.305,127,2.073,132,2.287,140,1.584,145,2.542,153,2.068,163,1.602,165,2.269,170,1.868,172,1.4,179,1.353,182,2.115,183,1.735,198,1.968,228,1.281,239,2.319,245,2.415,275,1.251,276,3.857,277,2.078,289,3.077,294,2.175,313,2.151,320,2.319,323,1.63,324,3.348,351,1.463,365,2.049,381,4.165,394,2.021,398,1.424,405,3.846,426,1.493,432,2.55,444,1.753,468,2.572,472,1.968,495,1.952,502,1.115,511,2.807,527,1.382,550,1.589,555,1.913,577,1.913,581,2.923,621,2.078,635,1.467,662,2.349,683,1.513,712,2.175,723,1.445,748,3.055,749,2.618,750,2.849,757,1.558,759,1.417,760,1.194,769,1.554,780,4.144,814,1.741,820,3.249,841,4.416,857,1.834,880,1.741,909,2.783,934,3.713,951,2.594,997,1.497,1052,2.885,1133,2.175,1181,2.098,1205,3.559,1223,2.29,1308,2.398,1315,2.224,1329,3.055,1381,1.602,1441,2.433,1471,3.457,1481,4.946,1870,1.407,2043,2.849,2485,3.954,2488,2.451,2516,4.946,2890,3.457,2966,2.752,3003,5.042,3051,2.723,3199,3.457,3880,2.643,3882,3.682,3890,3.682,3913,3.559]],["t/1425",[2,0.883,4,1.016,7,0.258,16,1.811,17,1.294,18,2.348,21,1.51,24,1.911,25,1.049,27,1.606,28,1.643,31,1.653,49,1.201,56,0.911,84,0.355,95,2.032,97,1.465,98,1.151,105,2.117,107,1.733,108,1.692,109,0.901,115,1.508,145,1.317,157,1.891,160,1.148,165,1.197,169,2.922,170,2.726,179,1.096,193,1.943,203,2.513,224,3.308,226,1.588,228,1.038,239,1.879,278,2.425,283,0.947,289,4.149,313,0.8,315,2.538,318,0.797,322,2.117,323,1.321,338,0.806,377,0.733,405,1.042,426,1.21,502,1.866,505,1.357,506,1.943,507,1.781,525,1.543,550,4.126,607,2.594,609,2.141,611,1.362,612,2.121,635,2.455,647,2.005,662,1.903,683,1.226,731,1.717,741,2.121,749,4.383,750,3.766,756,1.537,757,0.807,760,0.968,807,4.057,812,2.183,830,1.497,839,3.065,877,2.468,881,2.402,1028,2.162,1045,2.183,1047,1.957,1053,2.562,1117,4.224,1122,3.342,1223,5.948,1235,1.616,1308,1.943,1315,1.801,1352,2.569,1365,2.049,1376,4.3,1735,2.281,1737,2.552,1798,1.822,1799,2.254,1814,3.813,1864,1.726,1954,2.801,2254,2.729,2265,2.983,2282,3.562,2448,2.801,2485,2.049,2741,2.562,2782,2.162,2879,3.275,3056,6.358,3913,2.884,4530,3.529,4531,3.529,4532,5.758,4533,3.529,4534,3.529,4535,3.529,4536,3.275,4537,3.529,4538,3.529,4539,5.344,4540,3.529]],["t/1427",[2,1.737,4,1.997,16,2.157,24,1.656,27,1.528,28,1.735,107,2.283,109,1.504,116,2.403,127,2.112,132,3.523,145,2.589,160,2.257,162,2.263,163,2.552,164,1.769,165,1.997,170,3.022,193,2.342,199,3.176,203,1.541,289,2.545,313,1.573,336,2.62,391,2.711,394,3.218,502,2.459,507,3.502,550,4.02,596,2.9,607,2.468,613,2.91,632,3.464,740,2.91,909,4.432,1012,3.648,1047,3.846,1223,5.052,1315,3.541,1352,3.096,1363,2.319,1381,2.552,1864,3.392,1902,4.878,1953,3.502,2488,3.903,4541,11.023]],["t/1429",[16,1.354,24,1.178,27,2.3,28,1.027,39,2.961,84,0.788,107,2.481,153,4.957,170,2.15,203,1.742,224,3.083,257,3.705,279,3.144,320,4.175,322,2.276,365,3.688,407,3.29,502,2.007,527,2.119,550,3.808,747,2.988,772,6.395,807,2.812,862,3.621,877,2.654,907,4.67,1012,6.172,1037,3.694,1164,3.671,1303,4.347,1352,3.499,1363,4.183,1365,4.552,1737,2.745,2488,5.874,2696,4.901,3728,5.336,4498,5.592,4542,7.842,4543,7.842,4544,10.442,4545,7.842]],["t/1431",[5,3.126,16,1.651,23,3.675,28,1.252,48,3.6,163,3.517,165,1.988,169,4.853,239,5.09,289,4.35,367,4.518,512,3.059,527,1.94,550,3.487,647,3.329,712,4.774,724,4.321,807,3.429,877,3.236,1266,3.002,1363,3.964,1381,3.517,1765,5.09]],["t/1433",[21,2.669,24,1.529,28,1.736,89,3.525,107,3.15,109,1.593,165,2.116,179,3.16,338,2.323,405,3.005,635,3.427,723,3.376,820,4.854,1481,7.388,1864,4.976]],["t/1435",[2061,3.73]],["t/1437",[2,1.216,4,1.399,7,0.996,15,3.041,34,1.148,48,1.829,49,2.258,56,0.445,58,6.746,68,1.71,84,0.488,85,1.562,97,2.016,103,2.79,113,2.508,129,2.693,160,1.581,164,1.239,176,5.601,178,3.527,186,3.241,231,3.26,244,2.775,275,1.396,278,1.616,296,1.738,304,0.95,313,1.102,326,3.069,334,4.724,356,4.107,381,2.133,390,2.177,392,2.797,396,3.527,398,1.588,401,4.209,416,3.167,481,2.693,484,1.91,564,2.92,575,4.432,580,1.566,590,2.426,607,1.728,614,0.83,636,2.539,683,1.687,684,4.349,723,2.459,736,1.923,839,2.587,846,3.054,945,2.352,953,2.782,1055,2.976,1057,2.92,1381,2.726,1441,2.713,1538,6.408,1565,5.806,1625,6.302,1803,3.306,1918,2.244,1940,3.672,1943,4.279,2225,2.4,2297,1.502,2486,3.005,2608,3.355,2671,3.672,2750,3.757,3319,4.107,3513,5.74,3516,5.881,3517,4.279,3518,4.279,3519,6.527,3520,4.279,3521,4.279,3522,4.279,3523,4.279,3524,4.279,3525,4.279,3526,4.279,3527,4.279,3528,4.279,3529,4.279,3530,3.218,3531,3.218,3532,3.595,3533,4.539,3534,4.279,3535,4.279,4546,4.509,4547,4.279,4548,4.509,4549,4.279]],["t/1439",[4,2.065,7,0.719,15,3.796,16,1.239,21,1.881,24,1.478,34,1.696,98,2.34,145,2.677,158,3.328,159,6.082,181,3.772,189,3.563,196,2.639,203,1.594,247,2.926,261,3.374,276,3.298,279,2.877,338,1.638,343,2.368,365,3.374,377,1.489,393,3.27,401,3.488,402,5.208,403,4.273,408,3.294,412,4.954,513,3.137,541,4.098,580,2.312,611,2.768,669,5.422,723,2.38,742,4.164,764,4.484,841,3.077,978,4.484,992,3.328,1011,4.484,1105,5.548,1115,5.422,1511,4.352,1991,7.346,2225,3.544,2283,4.636,2297,2.218,2386,5.208,2928,5.548,3536,7.276,3538,5.693,3539,5.693,3540,5.548,3541,6.318,3542,4.484,4550,6.064,4551,7.175]],["t/1441",[7,0.895,15,2.886,49,1.779,56,0.782,159,5.276,322,2.476,369,4.911,401,3.026,412,5.89,438,4.108,607,3.925,723,2.829,846,3.515,1009,3.645,1043,4.108,1601,4.729,1704,8.533,1918,3.939,1991,6.207,1992,6.446,2026,4.049,2225,4.214,2297,3.411,2386,6.192,2600,4.872,2601,5.724,2602,5.724,2604,6.969,2740,6.969,3539,6.768,3544,7.512,3545,7.512,3546,7.512,3548,7.512,3549,7.512,4552,7.916,4553,8.53]],["t/1443",[7,0.92,12,3.936,13,9.125,34,2.485,48,3.959,465,6.795,715,5.016,888,5.749,889,7.499,890,8.68,1491,7.499,1925,8.132]],["t/1445",[7,0.687,16,1.16,31,1.523,34,1.588,48,2.529,58,4.508,59,9.917,112,3.088,116,2.326,133,2.667,160,2.185,176,10.423,178,4.876,275,1.93,283,2.521,300,6.094,324,3.301,334,7.329,351,2.256,353,1.7,372,2.332,465,4.34,483,5.498,532,3.251,580,2.164,590,4.69,604,4.114,684,3.251,720,5.678,846,2.768,1036,4.114,1729,4.971,1730,5.076,1781,5.678,1990,4.29,3020,4.508,3021,4.711,3550,5.916,3551,6.234,3552,5.916,3553,4.711,3554,5.916,3555,9.541,3556,6.234,3557,6.234,3558,5.916,3559,5.916,3560,5.916,3561,5.916,3562,5.916,3563,8.273,4554,6.717]],["t/1447",[7,0.938,491,3.043,532,6.207,683,3.777,1625,7.63,3564,11.294,3565,9.58,3566,9.58]],["t/1449",[164,2.822,225,3.672,226,4.98,532,5.357,1993,9.043,3567,8.783,3568,9.748,3569,9.748,3570,9.748]],["t/1451",[2061,3.73]],["t/1453",[7,0.637,16,1.503,19,3.6,21,2.283,29,2.819,84,1.124,105,2.527,165,1.81,219,3.548,221,2.984,275,2.501,296,3.113,304,1.703,318,2.945,512,2.786,513,3.807,562,3.968,567,3.239,580,3.603,653,2.543,723,2.888,736,3.445,757,2.824,906,4.897,1037,3.08,1107,4.256,1312,2.969,2518,4.038,2633,4.444,2636,4.635,2711,5.232]],["t/1455",[2,2.212,7,0.725,15,1.601,25,1.406,29,1.532,49,1.515,56,1.035,84,0.73,98,2.368,105,2.108,113,1.601,132,1.29,165,1.51,203,1.613,219,1.359,221,1.622,228,2.135,246,1.66,255,2.957,256,1.643,283,1.27,313,2.004,316,1.956,318,2.998,364,3.094,377,0.982,395,1.735,398,3.965,471,1.82,476,1.275,482,1.963,484,1.86,491,2.032,502,1.858,512,1.514,530,1.963,543,2.472,580,1.525,605,1.589,614,1.241,615,2.569,621,2.257,647,2.528,723,3.745,728,3.057,731,2.301,757,1.661,822,4.026,831,2.769,857,1.992,945,3.515,1006,2.246,1037,3.784,1059,2.457,1060,5.08,1114,3.058,1115,3.575,1133,3.626,1164,2.215,1170,1.348,1263,2.661,1284,4.854,1290,2.443,1312,3.014,1356,3.999,1408,2.993,1633,2.069,1688,2.989,1784,3.754,1898,3.551,1965,3.175,2026,4.707,2032,3.873,2297,1.463,2632,4.026,2895,2.703,3036,3.659,3042,2.569,3542,2.957,3749,3.057,3753,3.754,3966,3.866,4088,3.754,4555,4.391,4556,4.167,4557,4.167]],["t/1457",[4,1.005,7,0.255,19,1.443,25,1.695,27,1.257,28,0.457,29,1.13,40,1.707,56,1.257,68,1.228,84,0.727,100,1.448,105,1.013,109,1.814,127,3.751,140,1.269,165,0.726,172,1.834,179,1.772,182,1.772,193,2.821,195,2.852,219,1.639,254,1.042,256,1.212,277,1.665,279,1.4,295,1.303,318,0.789,327,4.002,337,2.466,338,0.797,395,1.28,398,1.141,405,2.721,418,1.619,476,2.934,511,1.688,512,1.826,552,1.874,580,1.125,614,0.596,647,1.986,653,2.889,723,2.773,757,1.655,803,1.824,818,3.372,828,1.351,936,1.386,940,1.212,942,3.276,953,1.31,986,2.776,988,2.964,1006,1.657,1037,3.26,1060,4.451,1069,1.752,1107,2.79,1163,1.963,1170,2.062,1193,2.043,1240,1.846,1266,1.831,1284,1.584,1306,4.852,1309,2.852,1363,2.418,1381,2.099,1476,1.946,1530,1.949,1539,2.159,1671,1.782,1693,1.895,1700,2.41,1705,4.102,1753,2.229,1765,1.858,1768,3.604,1870,1.843,1907,1.673,1924,2.638,2026,1.657,2054,5.688,2518,2.647,2632,3.163,2633,2.912,2797,3.779,3542,3.566,3878,2.489,3923,2.448,3997,2.138,4158,2.699,4230,2.583,4558,3.074,4559,3.074,4560,3.239,4561,3.074,4562,3.239,4563,3.074,4564,3.239,4565,3.074,4566,3.074,4567,3.239,4568,4.002,4569,4.002,4570,2.77,4571,3.239,4572,3.239,4573,3.239,4574,3.239,4575,3.239,4576,3.239,4577,3.239,4578,3.239,4579,3.239,4580,3.883,4581,3.94,4582,3.239,4583,3.94,4584,3.239,4585,3.94,4586,3.074,4587,3.239,4588,3.239,4589,3.239,4590,2.448]],["t/1459",[2,1.621,4,1.864,25,1.924,26,2.231,28,0.848,40,3.166,56,1.116,68,2.279,98,2.986,109,1.979,127,3.848,172,2.082,179,2.011,182,2.843,193,3.585,199,2.964,338,1.478,372,2.249,398,2.117,405,1.912,476,3.281,508,3.54,512,2.93,580,2.087,614,1.106,653,2.675,757,1.481,803,3.384,936,4.217,940,2.249,942,3.588,1006,3.074,1037,2.291,1060,4.12,1111,6.443,1170,2.609,1266,2.317,1306,3.759,1363,3.06,1476,3.122,1539,4.006,1765,3.448,1907,3.104,2054,4.407,2518,4.247,3542,5.723,3866,4.41,3923,4.542,3997,3.966,4230,4.793,4590,4.542,4591,6.01,4592,6.01]],["t/1461",[4,2.637,7,0.747,24,0.753,27,1.104,28,1.437,34,1.185,49,1.045,56,0.459,68,1.764,84,0.504,96,1.639,107,3.005,109,1.599,116,1.736,163,1.844,170,2.081,179,1.557,182,1.557,203,1.686,213,2.866,295,1.871,338,1.733,397,2.928,476,2.752,512,2.428,527,2.434,552,1.647,560,3.749,575,2.21,580,2.446,614,1.297,647,3.821,653,2.217,663,2.841,725,4.327,736,1.984,740,2.103,940,2.635,942,4.526,966,3.516,997,3.147,1012,4.815,1046,2.175,1060,2.255,1075,4.093,1170,2.162,1245,2.812,1266,2.922,1289,1.882,1363,2.536,1382,2.779,1408,3.127,1476,2.588,1616,3.919,1622,2.985,1863,1.946,1907,2.402,2518,3.521,2839,7.803,3053,4.744,3916,3.101,3923,3.516,3942,2.959,3943,7.827,3944,3.575,4593,4.415]],["t/1463",[4,3.278,7,0.54,28,1.491,56,1.043,109,1.999,116,2.557,182,2.293,203,2.228,213,3.787,338,1.686,397,3.868,527,2.781,560,4.954,580,2.379,647,2.571,653,2.157,807,2.648,942,3.93,1037,2.612,1060,4.512,1170,3.244,1245,3.496,1266,3.091,1352,6.117,1476,2.518,1863,2.866,2518,4.651,3923,5.179]],["t/1465",[4,3.293,5,2.95,7,0.464,28,1.498,34,1.5,56,0.963,116,2.198,182,1.971,203,2.005,213,3.408,236,1.549,239,3.379,256,2.204,289,3.31,295,2.368,338,1.449,397,3.482,511,2.67,527,2.622,560,4.458,580,2.045,647,3.983,653,2.636,807,5.072,940,2.204,942,4.116,1037,2.245,1060,4.061,1170,2.571,1245,3.711,1266,3.059,1363,3.016,1476,3.078,1765,3.379,1863,2.464,2518,4.187,3736,3.966,3866,4.347,3923,4.451,4594,5.589]],["t/1467",[4,3.258,5,2.386,7,0.534,24,1.701,28,1.482,31,1.655,39,2.756,84,1,116,2.528,144,3.244,182,2.266,203,2.21,213,3.757,232,2.464,338,1.666,367,3.448,397,2.816,414,3.886,527,2.581,560,4.915,580,2.352,647,2.541,653,2.132,769,4.335,807,2.617,852,2.936,942,2.86,946,5.74,1060,3.284,1170,2.079,1216,3.448,1245,3.055,1266,2.864,1476,2.489,1765,3.886,1863,2.833,2100,4.898,2518,4.615,3664,4.169,3736,4.562,3923,5.119]],["t/1469",[4,1.426,24,0.744,28,1.505,34,1.171,56,1.214,107,1.177,109,1.972,171,1.897,172,1.592,182,2.336,193,1.672,219,1.423,254,1.478,296,1.772,304,0.969,327,3.474,337,2.141,338,1.131,377,1.028,491,1.386,502,1.268,512,2.407,527,2.06,552,1.627,600,3.064,614,1.865,647,1.725,653,2.966,658,2.201,803,2.589,818,3.505,942,3.564,953,1.86,986,2.41,997,4.416,1037,2.661,1060,4.092,1170,2.893,1245,2.792,1257,4.057,1266,2.57,1363,3.393,1408,3.1,1409,5.969,1476,3.101,1705,2.808,1768,3.129,2518,3.489,2797,3.281,4523,4.187,4568,3.474,4569,3.474,4580,3.371,4581,3.421,4583,3.421,4585,3.421,4595,4.597,4596,3.596,4597,4.597,4598,4.597,4599,6.624,4600,8.942,4601,8.942,4602,8.942,4603,6.624,4604,6.624,4605,6.624,4606,4.047]],["t/1471",[4,1.855,17,2.363,28,1.195,56,1.157,106,3.918,109,2.031,137,5.433,182,2.833,219,1.851,254,1.923,327,4.519,337,2.785,338,1.471,377,1.337,444,3.67,502,1.649,527,2.149,600,3.986,614,1.101,638,3.598,653,3.094,818,3.002,942,2.525,997,3.958,1037,3.227,1060,4.766,1170,3.018,1245,1.978,1266,1.631,1363,3.049,1367,4.105,1389,5.022,1476,3.111,1671,4.657,1705,3.652,1768,4.07,2518,4.232,2797,4.267,3010,6.691,3971,5.113,4568,4.519,4569,4.519,4580,4.384,4581,4.449,4583,4.449,4585,4.449,4596,4.677,4607,5.446,4608,5.446,4609,6.751,4610,7.239]],["t/1473",[0,2.205,4,1.875,27,0.907,34,1.54,56,1.218,106,1.4,108,3.875,109,1.865,113,1.393,127,1.252,162,1.342,171,1.642,172,4.004,182,2.511,193,3.907,203,1.447,219,1.182,236,1.004,254,2.413,261,1.935,296,1.472,304,1.274,318,0.93,338,0.939,372,2.262,377,0.854,391,1.608,408,3.57,418,1.909,426,2.771,446,2.429,476,1.755,491,1.151,502,1.667,552,3.5,600,2.546,614,1.113,647,2.268,653,2.686,725,3.82,757,1.49,813,2.473,818,3.035,829,2.012,934,1.821,942,4.176,952,2.473,953,3.035,959,4.03,966,4.569,986,5.631,988,5.534,1037,2.304,1057,2.473,1060,3.638,1170,2.619,1204,3.045,1238,2.281,1266,2.046,1306,3.781,1363,3.348,1476,3.135,1671,3.325,1705,2.333,1765,2.191,1768,2.6,2054,4.433,2220,3.624,2518,4.265,3008,2.842,3736,2.572,3866,1.982,4126,3.265,4568,2.886,4569,2.886,4580,2.8,4581,2.842,4583,2.842,4585,2.842,4596,2.987,4611,3.819,4612,3.819,4613,3.819,4614,3.819,4615,3.624]],["t/1475",[4,2.019,28,0.919,56,1.149,109,1.963,116,2.429,172,3.843,182,3.006,193,2.368,219,2.015,224,2.758,327,4.92,337,3.032,338,1.601,377,1.456,391,2.741,407,2.943,476,1.89,513,3.068,552,2.304,608,5.757,647,2.442,653,2.828,725,3.314,807,2.516,818,4.511,942,2.749,953,2.633,1037,3.425,1060,4.989,1170,2.758,1266,1.775,1476,2.392,1705,3.976,1768,4.432,1863,2.723,1906,3.541,1953,3.541,2518,4.491,2797,4.646,3910,5.093,4568,4.92,4569,4.92,4580,4.774,4581,4.844,4583,4.844,4585,4.844,4596,5.093,4607,5.93,4608,5.93,4616,9.764,4617,6.178]],["t/1477",[4,1.691,6,5.73,7,0.625,8,4.263,21,1.54,24,0.883,31,1.332,34,1.388,56,0.782,68,2.067,84,0.591,104,4.799,105,1.705,106,1.998,107,2.029,109,2.185,115,4.718,116,2.034,123,1.858,137,3.498,144,2.611,157,3.148,182,1.824,228,1.727,229,3.234,236,1.433,256,2.04,295,2.192,304,1.149,316,2.429,343,1.939,353,2.161,372,2.04,398,1.92,426,2.926,491,1.643,492,4.264,577,2.579,593,3.908,614,1.459,615,3.189,653,2.938,679,4.264,683,2.04,705,4.877,742,3.41,765,3.234,803,4.461,809,3.752,828,2.273,953,2.205,962,3.752,992,2.725,1037,3.02,1041,3.468,1060,3.842,1170,2.865,1266,2.161,1363,3.69,1476,3.43,1605,3.997,1671,2.998,1874,3.796,2518,3.96,2684,3.355,3658,4.965,4609,4.347,4618,8.858]],["t/1479",[7,0.505,15,2.335,46,4.795,56,1.255,116,2.39,182,2.972,219,1.982,254,2.856,327,4.84,337,2.982,338,1.575,377,1.432,600,4.268,614,1.179,647,3.332,653,2.796,715,3.291,846,2.843,866,5.336,1037,3.386,1060,4.307,1170,2.727,1363,3.199,1367,5.341,1408,4.891,1476,2.353,1705,3.911,1768,4.359,1863,2.679,2797,4.57,3733,4.631,3866,3.323,4568,4.84,4569,4.84,4580,4.696,4581,4.765,4583,4.765,4585,4.765,4596,5.009,4609,5.106,4610,5.475,4619,6.404,4620,6.404,4621,6.077,4622,6.077,4623,6.077,4624,6.077,4625,6.077,4626,6.077,4627,6.077,4628,6.077,4629,6.077]],["t/1481",[2,2.547,7,0.745,16,1.757,21,2.669,84,1.023,278,3.384,313,2.307,339,4.764,386,7.028,490,6.656,512,3.256,544,5.907,552,3.343,677,4.976,723,3.376,736,4.027,757,2.327,1108,6.361,1417,6.233]],["t/1483",[7,0.728,19,4.114,20,4.236,25,2.169,28,1.592,96,2.386,103,2.266,219,2.858,221,2.502,225,2.421,226,5.469,243,3.704,256,2.534,278,2.427,280,4.309,296,2.61,306,3.008,318,3.089,366,4.236,472,3.298,511,2.159,512,2.335,513,3.192,562,3.326,567,2.716,607,2.596,611,2.816,615,3.963,683,2.534,757,2.588,877,2.47,888,3.99,906,4.106,957,5.644,1175,5.644,1177,5.791,1284,3.312,1454,4.202,1530,4.076,2024,6.169,2032,2.851,2357,6.169,2636,3.886,2711,4.386,4630,6.428,4631,6.428,4632,6.428,4633,6.428,4634,6.428,4635,6.428]],["t/1485",[2,1.47,7,0.924,17,2.154,25,1.745,28,0.769,34,1.388,56,0.922,84,1.23,85,1.888,88,2.455,89,2.034,103,2.651,109,0.919,127,1.788,134,2.713,171,1.481,179,1.824,182,1.824,202,3.752,219,2.889,226,2.643,232,1.983,247,2.396,272,2.232,283,1.576,296,2.101,304,1.149,306,3.518,313,1.332,318,2.923,322,1.705,338,1.341,353,1.487,397,2.266,401,2.084,418,4.665,421,3.41,438,2.829,471,2.259,476,1.582,502,1.503,511,2.526,526,3.942,534,3.89,567,4.985,575,3.764,591,3.108,605,1.973,614,1.459,672,5.498,723,3.336,734,2.412,802,2.205,828,2.273,884,3.438,916,3.168,1363,1.963,1476,2.911,1848,4.264,1851,4.264,2297,1.816,2518,2.725,3932,4.264,4636,7.519,4637,5.173,4638,5.173]],["t/1487",[7,0.795,15,3.191,16,1.166,25,2.006,56,1.174,84,0.679,89,2.339,103,2.928,108,3.237,145,2.52,164,2.77,196,2.484,201,3.19,243,3.427,272,2.566,279,2.708,318,1.526,336,4.441,377,1.402,401,2.395,405,3.472,418,3.132,471,2.597,511,1.998,517,3.799,532,3.268,562,3.078,567,4.376,723,2.24,757,1.544,877,2.285,971,3.986,1008,3.799,1033,3.986,1238,3.744,1331,4.136,1530,3.771,2112,4.997,4639,5.947,4640,5.947,4641,5.947,4642,5.947,4643,5.947,4644,5.947,4645,5.358,4646,5.947,4647,5.947,4648,5.947,4649,5.947,4650,5.947,4651,5.947,4652,5.947,4653,5.947,4654,5.947,4655,5.947,4656,5.947,4657,5.947,4658,5.947,4659,5.947,4660,5.947,4661,5.947,4662,5.947]],["t/1489",[7,0.673,15,3.471,28,1,31,1.146,48,1.902,56,1.312,85,2.455,103,1.569,105,1.467,109,1.195,127,2.324,140,1.837,161,3.192,164,1.288,171,1.274,179,1.569,225,3.653,243,2.564,300,2.842,318,1.142,336,1.908,377,1.049,392,2.909,401,1.792,405,1.492,437,2.201,472,2.283,476,1.361,511,2.259,532,2.446,562,2.303,567,3.817,601,2.201,607,1.797,670,2.725,747,1.925,829,2.47,830,2.143,920,2.725,965,5.387,971,6.501,978,3.158,1050,3.036,1305,3.739,1306,2.933,1320,2.983,1331,3.095,1631,3.818,1632,2.434,1633,2.21,1664,3.438,1718,3.126,2297,1.562,2488,2.842,2534,4.271,3152,3.907,3154,3.907,3716,4.009,3817,4.507,4357,6.059,4645,6.059,4663,9.699,4664,6.724,4665,4.45,4666,4.45,4667,4.45,4668,4.45,4669,4.45,4670,4.45,4671,4.45,4672,4.45,4673,4.45,4674,4.45,4675,4.45,4676,4.45,4677,4.009,4678,4.45,4679,4.009,4680,4.009,4681,3.668,4682,4.45,4683,4.009,4684,4.45,4685,4.009,4686,6.059,4687,4.009,4688,4.009,4689,4.009,4690,4.009,4691,4.009,4692,4.009]],["t/1491",[7,0.398,28,0.713,31,1.234,36,2.228,49,1.136,56,1.342,68,1.916,109,0.852,127,1.657,165,1.132,203,1.794,225,1.806,226,3.633,300,3.063,318,1.23,336,2.056,377,1.13,398,3.48,405,2.384,418,2.525,426,2.767,476,1.467,567,3.581,577,2.39,601,2.372,916,2.936,965,5.633,971,6.712,978,3.403,1133,2.718,1246,4.32,1266,2.043,1284,3.664,1305,4.029,1306,3.16,1320,3.214,1331,3.334,1631,4.114,1632,2.622,1635,4.21,1664,3.705,1718,3.368,1763,4.602,1765,2.898,2383,4.602,2488,3.063,2632,4.476,3016,3.04,3152,4.21,3154,4.21,3483,5.975,3817,3.214,4590,5.663,4677,4.32,4679,4.32,4680,4.32,4681,3.952,4683,4.32,4685,4.32,4686,6.407,4687,4.32,4688,4.32,4689,4.32,4690,4.32,4691,4.32,4692,4.32,4693,4.795,4694,4.602,4695,8.476,4696,4.795,4697,4.795,4698,4.795,4699,4.795,4700,4.795,4701,4.795,4702,4.795,4703,4.795]],["t/1493",[2061,3.73]],["t/1495",[2,1.557,7,0.651,24,1.704,29,2.014,49,1.298,56,1.204,84,0.894,107,1.478,109,1.774,139,2.591,140,2.262,170,2.44,179,1.932,182,1.932,203,1.382,236,1.518,254,1.856,275,1.787,296,2.225,313,1.41,322,1.806,377,1.291,381,3.907,395,2.282,418,2.886,484,2.446,512,1.991,517,3.5,536,1.719,578,2.269,601,2.71,614,1.063,628,2.982,634,6.346,647,3.098,683,2.16,731,3.026,759,2.024,857,2.619,940,2.16,1043,5.461,1117,3.123,1170,1.772,1240,3.291,1289,2.335,1605,4.233,1693,3.378,1754,2.454,1870,3.875,1873,7.499,1906,6.054,1907,4.979,1908,6.794,2297,1.923,3391,4.936,4704,8.781,4705,5.259]],["t/1497",[7,0.337,12,1.726,21,1.209,24,1.589,28,1.281,29,1.493,56,1.176,84,0.716,107,2.858,109,2.039,162,1.504,165,1.48,182,2.211,217,2.439,246,1.618,254,3.155,285,1.768,313,1.045,338,1.625,353,1.167,377,1.478,381,2.024,395,1.691,433,2.47,446,2.722,491,1.29,505,1.773,517,2.594,527,2.505,536,1.968,565,1.397,580,1.486,634,3.288,653,1.347,683,1.601,705,5.587,759,2.316,880,1.842,914,4.517,953,3.265,1033,2.722,1043,5.091,1170,1.314,1181,2.221,1245,3.003,1266,1.802,1289,1.731,1476,1.572,1735,2.979,1737,2.492,1765,2.454,1863,1.79,1870,1.489,1873,5.227,1906,4.937,1907,3.412,1908,4.656,2013,2.699,2092,2.395,2395,5.142,2636,2.454,2669,3.234,2837,3.184,3391,3.658,4590,3.234,4704,6.018,4705,6.018,4706,4.279,4707,3.897,4708,8.072,4709,6.607,4710,4.279,4711,4.279,4712,4.279,4713,6.607,4714,4.279,4715,6.607,4716,4.279,4717,4.279,4718,4.279]],["t/1499",[109,1.717,127,3.339,179,3.407,246,3.851,580,3.536,880,4.385,1873,7.746,1908,7.176,2395,5.337]],["t/1501",[109,1.717,172,3.527,179,3.407,246,3.851,580,3.536,880,4.385,1873,7.746,1908,7.176,2395,5.337]],["t/1503",[109,1.717,179,3.407,246,3.851,580,3.536,880,4.385,997,3.77,1873,7.746,1908,7.176,2395,5.337]],["t/1505",[4,0.328,7,0.811,12,0.427,15,1.01,16,1.364,17,0.418,18,1.799,21,0.557,23,1.159,24,1.187,25,0.631,27,0.657,28,1.14,29,0.966,31,1.136,34,1.426,46,0.438,47,0.667,48,0.429,49,1.435,56,1.099,68,2.421,84,0.692,85,1.784,96,0.695,97,0.473,100,0.473,101,0.563,103,2.011,105,0.866,107,1.722,108,0.546,109,1.495,112,1.372,113,0.386,116,0.395,123,1.909,124,0.667,127,1.837,132,0.311,133,0.453,138,0.427,140,0.773,145,0.793,157,0.611,158,2.324,160,1.435,161,3.165,164,1.125,165,0.62,169,0.579,170,0.818,171,0.287,174,0.314,177,0.511,193,1.007,196,0.781,200,0.977,203,0.831,213,1.127,219,0.61,221,0.391,224,0.835,225,0.378,228,0.625,236,0.728,246,0.4,247,0.866,248,2.821,249,0.787,253,1.186,254,0.634,261,0.536,272,0.433,275,1.074,278,0.706,285,1.144,286,0.641,287,0.755,296,0.408,298,0.646,304,0.415,305,0.673,313,0.482,316,0.879,317,0.529,318,0.845,322,1.086,338,0.485,343,0.701,348,0.679,351,1.002,353,0.755,359,0.466,366,0.662,377,1.152,382,0.656,387,0.737,388,0.765,390,1.337,395,1.618,401,3,405,0.337,407,0.478,410,1.301,418,0.986,421,2.171,437,0.925,438,1.023,470,0.765,471,2.321,476,1.008,479,0.737,481,0.632,482,0.473,484,0.835,491,1.552,498,0.589,502,0.764,512,0.955,517,0.641,525,0.498,527,0.895,528,1.039,530,2.855,532,1.028,557,0.526,562,2.284,564,0.685,565,1.133,568,1.604,572,0.56,575,0.937,580,0.367,600,0.705,605,0.383,607,1.061,613,0.478,614,0.639,621,1.423,630,1.512,635,1.259,653,0.621,655,0.691,670,0.615,683,1.036,684,0.552,712,1.061,723,1.662,730,0.861,736,0.451,743,1.301,747,0.81,756,0.496,757,0.855,759,1.435,760,2.451,769,0.758,784,0.776,807,0.762,813,0.685,814,0.849,828,0.441,829,0.557,839,0.607,846,0.47,850,0.691,851,3.151,854,0.563,874,0.968,878,0.451,880,0.849,898,0.513,921,0.861,930,1.514,939,0.651,945,1.028,946,0.487,953,0.428,954,0.705,973,3.9,993,1.138,995,0.72,1006,0.541,1037,0.752,1043,3.491,1045,1.314,1050,0.685,1053,0.827,1057,0.685,1060,0.513,1107,1.039,1114,0.894,1132,0.737,1163,0.641,1170,0.325,1193,1.747,1204,0.844,1214,1.277,1234,0.599,1235,1.712,1240,0.603,1245,0.35,1266,0.288,1274,2.12,1284,1.354,1289,0.428,1300,0.691,1308,1.643,1312,0.724,1352,0.509,1366,0.589,1376,0.986,1381,0.419,1390,2.922,1399,1.161,1432,1.606,1441,0.636,1465,0.765,1476,0.389,1477,0.787,1493,2.208,1523,1.004,1530,0.636,1540,0.585,1565,1.373,1566,1.407,1567,0.813,1579,0.881,1628,0.813,1632,1.437,1633,0.498,1638,0.546,1687,0.881,1754,0.838,1759,1.357,1782,0.888,1792,0.662,1801,0.603,1806,1.606,1863,0.442,1870,0.686,1902,0.94,1971,0.787,2009,0.728,2012,0.931,2026,1.776,2027,3.503,2032,1.166,2033,0.596,2043,0.745,2061,0.666,2064,0.755,2109,0.963,2111,0.904,2112,0.844,2154,3.162,2162,1.643,2180,0.963,2196,1.686,2231,0.765,2254,0.881,2297,1.364,2347,1.058,2353,1.178,2395,0.554,2411,1.446,2448,1.686,2533,0.881,2583,1.004,2600,2.137,2601,2.51,2602,2.002,2608,1.467,2623,2.093,2624,1.542,2632,0.632,2633,0.582,2710,1.004,2814,1.373,2815,1.373,2835,0.931,2867,0.619,2895,1.214,2917,1.004,3051,0.712,3513,0.728,3533,1.301,3880,1.289,3895,2.438,3918,0.931,4016,1.446,4072,3.604,4432,3.056,4474,1.446,4719,1.058,4720,1.058,4721,2.769,4722,2.769,4723,2.769,4724,2.769,4725,2.769,4726,3.471,4727,3.471,4728,3.471,4729,3.471,4730,3.471,4731,2.769,4732,2.769,4733,2.769,4734,2.769,4735,2.769,4736,3.471,4737,3.471,4738,3.471,4739,3.471,4740,3.471,4741,1.058,4742,1.058,4743,1.058,4744,1.14,4745,1.058,4746,1.058,4747,1.972,4748,3.471,4749,3.471,4750,0.963,4751,1.058,4752,0.963,4753,1.058,4754,1.058,4755,1.058,4756,1.972,4757,1.058,4758,1.686,4759,1.058,4760,0.963,4761,0.963,4762,1.058,4763,1.14,4764,1.058,4765,1.972,4766,1.058,4767,1.058,4768,1.058,4769,1.058,4770,1.972,4771,2.769,4772,1.972,4773,1.058,4774,1.972,4775,1.972,4776,1.972,4777,1.972,4778,1.058,4779,1.058,4780,1.058,4781,1.058,4782,1.058,4783,1.058,4784,1.796,4785,1.058,4786,1.972,4787,1.058,4788,1.058,4789,1.058,4790,1.058,4791,1.058,4792,1.058,4793,1.058,4794,1.058,4795,1.058,4796,1.058,4797,1.058,4798,1.058,4799,1.058,4800,1.058,4801,1.058,4802,1.058,4803,1.058]],["t/1507",[2061,3.73]],["t/1509",[4,1.541,21,1.403,26,1.844,28,0.701,29,1.733,49,1.663,56,1.312,84,0.801,85,2.563,105,2.314,109,2.015,113,1.811,140,1.946,144,2.379,155,3.216,160,1.741,165,1.113,174,1.476,192,2.926,219,2.29,225,2.645,254,1.597,272,2.033,295,1.997,302,2.287,313,1.213,322,1.554,353,1.354,377,1.977,395,1.963,405,1.58,426,1.834,447,2.35,463,2.989,517,3.011,614,1.628,705,6.03,755,2.237,801,3.696,824,3.642,827,2.517,914,5.466,954,3.311,1170,3.216,1234,2.814,1235,3.649,1289,3.576,1434,2.764,1633,2.341,1638,2.565,1693,2.906,1753,3.419,1870,3.96,1876,3.216,1902,2.369,1972,3.817,2252,4.714,2395,2.604,2896,4.045,4804,4.045,4805,4.967,4806,7.399,4807,7.399,4808,6.739,4809,7.399,4810,4.967,4811,4.967,4812,4.967]],["t/1511",[7,0.6,12,3.071,16,0.961,49,1.711,56,0.986,84,0.825,85,3.134,109,2.124,113,1.883,162,1.814,164,1.419,165,1.157,174,1.535,182,2.548,199,2.547,219,1.598,246,1.953,257,2.629,297,2.824,313,1.262,323,2.083,336,2.101,338,1.873,391,2.174,405,2.422,426,1.907,447,3.603,484,2.188,491,2.727,508,3.042,613,3.442,614,0.951,660,3.842,705,7.663,841,2.387,914,4.968,951,4.887,1042,3.107,1170,2.777,1196,4.118,1238,4.549,1282,3.968,1289,4.307,1381,2.047,1506,3.314,1753,3.554,1754,3.236,1844,3.154,1870,3.877,1876,3.344,1904,3.842,1972,3.968,2307,3.595,2395,5.581,2608,3.842,3033,4.118,3878,3.968,3969,3.968,3973,4.703,4707,4.703,4813,4.703,4814,5.164,4815,4.703,4816,5.164]],["t/1513",[2,1.881,48,2.83,56,1.313,109,2.118,113,2.543,186,3.287,283,2.017,377,1.56,437,3.274,527,1.525,611,2.899,705,7.563,914,6.114,1170,2.891,1289,2.821,2102,5.964,2395,5.589,2518,3.486,2754,5.562,4808,8.578,4817,6.975,4818,6.975,4819,6.975,4820,6.975,4821,6.975,4822,6.975,4823,6.975,4824,6.975,4825,6.975,4826,6.975,4827,6.975,4828,6.975]],["t/1515",[7,0.733,12,3.749,84,1.007,101,4.948,109,1.91,228,2.945,233,3.364,509,5.266,578,3.653,580,3.228,634,7.143,702,6.26,705,6.972,940,3.478,1873,7.335,1908,7.982,2395,4.872]],["t/1517",[2061,3.73]],["t/1519",[7,0.675,12,3.452,16,1.13,24,1.743,25,1.944,27,2.694,28,0.857,31,1.483,36,3.774,39,3.483,50,3.553,56,1.12,70,4.333,84,0.658,98,2.134,101,3.232,102,3.83,105,1.899,106,2.225,113,2.214,121,3.553,131,2.755,165,1.917,171,2.325,197,2.706,201,3.091,240,3.321,304,1.28,339,3.063,353,1.656,377,1.914,394,3.035,404,2.957,407,2.745,408,2.192,416,2.796,502,1.674,505,2.516,508,3.577,579,4.391,614,1.118,630,2.243,734,2.687,752,4.945,779,4.842,787,4.945,848,4.945,857,2.755,1108,5.764,1633,2.861,1689,3.183,1822,3.654,2297,2.023,2486,4.048,2550,4.391,2787,5.445,3312,5.346,4804,4.945,4829,11.348,4830,6.073,4831,8.559,4832,5.531,4833,6.073]],["t/1521",[24,1.542,27,2.26,28,1.344,84,1.031,162,3.346,225,3.403,232,3.463,322,2.978,524,5.861,614,1.753,650,7.741,723,3.403,1179,5.861,1864,6.055,3644,3.508,4834,9.522]],["t/1523",[7,0.751,21,2.691,27,2.26,84,1.031,100,4.258,106,4.212,169,5.207,232,3.463,304,2.007,404,4.636,507,5.179,614,1.753,752,7.754,756,4.469,1671,5.237,4045,7.754,4832,8.673]],["t/1525",[2061,3.73]],["t/1527",[1,2.399,4,1.28,7,0.622,16,1.196,17,1.631,24,1.442,25,1.322,27,0.98,28,0.907,31,2.357,36,3.479,46,1.711,48,1.675,49,0.928,50,2.415,56,1.226,89,1.541,109,1.799,115,2.959,123,2.689,130,2.353,131,2.915,132,1.212,138,1.665,169,2.258,170,1.22,172,1.43,174,2.647,201,3.272,207,1.685,219,1.278,224,1.749,227,2.724,233,2.855,262,2.698,287,4.586,299,2.649,315,1.961,318,2.488,338,1.941,339,3.241,370,3.229,372,1.545,381,3.04,403,2.649,416,1.901,506,2.449,527,2.234,530,1.846,599,2.122,658,1.977,675,2.626,705,2.541,718,3.53,731,2.164,740,2.905,756,3.016,759,1.447,760,2.331,802,1.67,805,2.81,822,2.466,846,1.833,880,2.767,903,2.874,904,2.233,922,4.374,939,2.541,940,1.545,947,2.102,997,1.529,1041,2.626,1043,2.142,1044,2.432,1052,2.946,1073,2.985,1076,3.172,1133,2.221,1134,3.44,1156,3.362,1218,2.324,1245,2.611,1281,3.241,1289,1.67,1389,2.449,1394,2.752,1408,1.833,1473,1.839,1483,3.44,1508,2.284,1822,2.484,1873,2.673,1908,2.909,1916,2.466,1972,3.172,2209,3.917,2518,2.063,2669,3.12,2679,4.374,2684,2.541,2726,2.946,2905,2.946,2910,3.229,3023,3.027,3618,3.917,3619,3.53,3620,3.634,3622,3.53,3623,3.53,3624,3.172,4835,4.128]],["t/1529",[7,0.941,15,2.31,16,1.179,18,2.784,24,1.026,36,2.793,39,2.578,46,2.625,84,0.686,85,2.194,86,4.312,96,2.231,108,3.271,112,3.138,131,2.874,145,2.547,162,2.226,174,1.883,228,2.007,233,4.43,240,3.464,247,3.874,278,2.27,283,1.832,313,1.548,318,1.542,338,1.558,377,1.417,393,3.111,398,2.231,408,3.183,504,3.84,505,3.654,576,3.731,578,2.489,590,3.408,683,2.37,757,1.561,846,2.813,877,2.31,885,3.611,917,7.727,922,4.312,1009,4.669,1046,4.741,1180,3.995,1202,3.337,1216,3.225,1240,3.611,1538,4.868,2141,4.955,2298,5.279,2960,5.577,2961,6.011,3611,7.538,3625,8.367,4836,6.335]],["t/1531",[4,2.595,7,0.836,15,3.051,56,0.826,135,6.048,144,4.007,162,2.94,174,2.487,233,4.217,292,3.639,343,2.976,372,3.131,398,3.737,408,3.021,482,3.742,491,2.522,590,4.501,741,5.418,750,5.897,917,7.477,940,3.131,996,5.277,1125,7.94,1289,3.385,1473,3.728,1527,5.522,2969,7.94,3626,7.94,3627,8.368]],["t/1533",[2061,3.73]],["t/1535",[7,0.624,27,2.431,31,1.934,34,2.016,106,2.901,112,3.921,131,3.591,138,3.192,172,2.742,189,4.236,190,4.068,191,5.724,193,2.879,198,3.854,219,3.17,233,2.865,278,2.836,295,3.183,318,2.493,341,4.354,353,2.159,377,1.77,477,4.695,491,2.386,498,4.404,499,5.512,500,3.745,504,4.798,512,2.729,774,4.282,1001,6.083,1008,4.798,1009,3.645,1011,5.331,1067,6.192,1078,6.596,1108,5.331,3638,5.983,3639,7.512,3640,7.512]],["t/1537",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1011,6.465,3638,7.255]],["t/1539",[7,0.757,106,3.518,131,4.355,138,3.872,190,4.934,219,2.971,233,3.474,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,512,3.31,536,2.859,1011,6.465,3638,7.255]],["t/1541",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1011,6.465,3638,7.255]],["t/1543",[7,0.832,139,4.734,233,3.818,341,5.802,759,3.698,1075,6.131]],["t/1545",[7,0.782,24,1.607,88,4.47,206,6.427,233,3.592,341,5.458,552,3.513,567,3.979,723,3.547,852,4.302,862,4.938,3641,11.181]],["t/1547",[7,0.929,29,3.462,219,3.072,233,3.592,315,4.714,318,2.416,395,3.922,922,6.756,1477,7.385,1688,6.756,1945,6.615,3311,8.486]],["t/1549",[34,2.817,42,6.556,96,3.149,127,2.932,155,5.79,253,5.38,283,2.585,318,2.177,353,2.438,377,2,381,4.23,472,4.354,502,2.465,530,3.998,698,6.466,839,5.129,1009,4.117,1074,6.758,1962,6.871,1963,6.466,1964,6.086,1965,7.998,2068,7.645,3402,6.758]],["t/1551",[7,0.751,27,2.26,34,2.425,102,6.005,172,3.298,283,2.753,318,2.318,530,4.258,1009,4.384,1074,7.196,1101,7.196,1557,5.907,1962,7.317,1963,6.885,1964,6.481,1965,8.311,3642,9.036]],["t/1553",[2061,3.73]],["t/1555",[7,0.716,15,3.311,34,2.312,84,0.983,196,3.599,203,2.173,233,4.041,295,3.651,381,4.296,398,3.198,416,4.181,575,4.313,580,3.153,613,4.104,684,6.306,794,6.322,917,6.399,1057,5.88,1538,6.977,1803,6.658,3516,7.763]],["t/1557",[7,0.88,15,3.311,56,0.897,84,0.983,96,3.198,159,6.052,233,4.041,412,6.756,846,4.032,922,6.18,978,6.115,1991,5.504,2225,4.833,2283,6.322,2297,3.024,2386,7.102,3536,8.904,3538,7.763,4550,8.27,4837,9.784,4838,9.08]],["t/1559",[84,1.198,159,8.005,233,4.003,292,3.889,351,3.236,621,4.595,802,3.617,846,3.97,888,5.267,889,6.871,890,6.653,1056,6.758,1445,6.226,1991,5.42,3536,7.13,3542,7.449,3595,8.485,3596,8.485,3597,7.451,4838,8.942,4839,8.942]],["t/1561",[7,0.769,300,5.916,580,3.389,684,5.09,3021,7.376,3599,8.344,3600,8.344,3601,8.344,3605,9.261,4840,9.76,4841,9.76,4842,12.57,4843,10.516,4844,10.516]],["t/1563",[7,0.751,233,4.16,300,5.772,532,4.966,580,3.306,684,4.966,3020,6.885,3599,8.141,3600,8.141,3601,8.141,3609,9.036,4840,9.522,4841,9.522,4845,12.385,4846,10.26,4847,10.26]],["t/1565",[7,0.731,16,1.268,34,1.735,49,2.083,56,0.915,85,3.649,96,2.4,158,3.405,174,2.755,198,3.317,203,2.219,233,4.092,254,2.191,292,2.963,296,2.625,332,3.536,398,3.265,408,2.459,491,2.794,513,3.21,525,3.21,532,3.553,614,1.254,653,2.144,769,2.618,846,4.116,917,8.798,1040,4.689,1046,4.334,1114,3.091,1202,3.589,1363,2.453,1632,4.81,1754,2.896,2033,3.836,2297,2.269,2395,3.571,2930,5.329,3611,7.925,3612,6.465,3613,6.465,3614,6.465,3615,6.205,3616,6.465,3617,5.998]],["t/1567",[2061,3.73]],["t/1569",[24,1.696,28,0.95,41,3.055,56,1.163,99,4.247,105,2.106,107,2.881,109,1.986,127,2.208,139,3.022,160,3.224,165,1.509,170,1.99,172,2.333,182,2.253,193,3.345,203,1.612,217,3.839,318,2.239,338,1.657,343,2.395,351,2.437,377,1.506,502,2.536,536,2.006,550,2.646,565,2.199,635,2.443,725,3.429,759,2.361,878,3.921,880,2.9,980,4.635,997,3.405,1363,2.425,1382,4.023,1389,3.995,1473,3.001,1508,5.087,1900,7.053,1901,7.663,1902,4.386,1905,5.37,4511,5.37,4848,5.484]],["t/1571",[0,2.115,1,2.129,2,0.988,7,0.576,12,1.478,21,1.035,28,1.03,34,0.933,56,1.285,84,0.397,96,1.291,105,1.146,107,1.498,109,2.042,123,1.249,124,2.311,127,1.202,133,1.567,144,1.755,160,3.404,170,1.083,171,0.995,172,1.269,197,1.633,198,1.784,213,1.491,228,1.161,277,1.883,279,1.583,291,2.158,292,1.594,295,2.352,297,2.004,302,1.687,313,0.895,318,0.892,322,1.146,338,2.507,343,3.454,365,1.857,377,1.308,395,1.448,397,1.523,426,1.353,430,1.807,437,1.72,502,1.01,513,1.727,527,0.801,557,1.823,591,3.334,605,1.326,614,0.675,628,1.892,647,4.093,712,1.971,747,2.998,765,2.173,801,2.726,880,3.144,885,2.089,893,2.115,914,5.071,980,5.737,997,1.357,1117,1.982,1164,1.848,1237,2.468,1241,2.984,1381,1.452,1383,3.226,1445,4.073,1486,4.289,1508,5.039,1693,2.144,1845,3.47,1870,1.275,1900,8.06,1901,8.092,1902,4.861,1904,2.726,1905,5.822,2231,4.23,2839,2.494,3057,2.65,3876,4.353,3879,2.726,3880,2.395,4228,3.226,4813,3.337,4848,5.945,4849,2.866,4850,3.664,4851,3.226]],["t/1573",[5,2.645,24,1.792,28,1.06,31,1.835,56,0.977,68,2.848,84,0.814,95,4.659,112,3.721,165,2.216,203,1.798,236,1.975,256,2.81,275,3.063,279,3.245,291,4.424,313,1.835,317,3.754,367,3.824,397,3.122,517,4.553,536,2.947,568,2.151,759,3.468,760,2.923,769,4.252,774,4.062,807,2.902,814,3.234,946,4.556,993,4.336,1263,4.553,1508,6.121,1733,4.486,1898,3.957,3990,6.612,4849,5.875,4852,6.422]],["t/1575",[7,0.758,21,1.623,24,1.799,28,1.568,56,0.949,84,1.041,109,1.388,138,2.317,160,3.68,164,1.578,165,1.843,174,1.708,182,1.922,228,1.82,233,2.079,236,2.164,289,4.149,292,2.499,314,6.562,405,1.828,407,3.72,505,2.381,511,2.623,527,2.102,536,2.863,557,4.095,635,2.084,759,2.014,760,3.102,769,2.208,778,3.623,807,3.18,814,3.544,878,4.1,880,3.544,946,3.789,1042,4.951,1263,4.988,1352,3.957,1373,5.026,1508,5.807,1557,3.564,1900,7.07,2672,5.379,2679,6.545,3051,3.869]],["t/1577",[2,2.121,56,1.225,84,1.104,96,3.985,107,2.013,109,2.091,130,4.482,160,3.574,164,2.16,196,3.116,313,1.921,338,1.934,377,1.758,395,3.107,647,3.825,653,2.475,712,4.23,880,3.386,914,5.63,1170,2.414,1486,7.476,1801,4.482,1870,2.737,1900,7.62,1902,3.75,4851,6.922]],["t/1579",[56,1.311,109,1.674,338,2.441,914,4.938,1900,6.684,1902,4.733,1904,8.767,4853,9.039]],["t/1581",[2,2.358,12,3.525,21,2.47,56,1.308,96,3.079,109,1.839,291,5.149,313,2.136,338,2.683,653,2.751,914,5.427,1417,5.769,1900,7.345,1901,7.284,1902,5.201,1904,6.504,3866,4.537,4853,7.962]],["t/1583",[28,1.518,29,2.983,56,1.302,109,1.442,302,3.937,324,5.696,338,2.646,395,3.379,628,4.416,712,4.6,724,5.237,880,3.682,914,4.255,980,5.885,1486,6.27,1900,5.758,1902,4.078,1905,6.818,1998,4.969,2930,6.688,3921,6.362,4848,6.963]],["t/1585",[2,1.915,7,0.56,56,0.941,84,0.769,107,2.441,109,1.939,127,2.329,139,4.829,170,3.649,172,2.46,182,2.376,198,3.458,203,1.7,246,2.685,253,4.273,313,1.735,338,2.829,351,2.57,392,4.406,502,1.958,527,2.084,536,2.115,583,4.337,759,2.489,760,3.398,820,3.65,880,4.634,980,4.888,997,2.629,1363,2.557,1508,3.928,1754,4.052,1884,4.687,1902,3.387,1905,5.663,2372,4.734,2764,6.252,4848,5.783]],["t/1587",[5,2.788,24,1.282,28,1.117,31,1.934,56,1.011,68,3.001,84,0.857,95,4.911,107,2.027,112,3.921,165,2.294,170,2.339,256,2.962,275,3.17,279,3.42,291,4.663,313,1.934,317,3.957,367,4.03,397,3.291,517,4.798,536,2.357,568,2.267,759,3.59,760,3.025,769,4.362,774,4.282,993,4.57,1263,4.798,1352,3.806,1508,5.664,1733,4.729,1898,4.17,4849,6.192,4852,6.768]],["t/1589",[7,0.734,21,1.936,28,1.491,56,0.919,84,1.008,107,1.754,109,1.156,138,2.764,160,3.704,164,1.882,165,2.085,170,2.749,174,2.037,203,1.64,228,2.171,233,2.48,236,1.802,289,2.708,292,2.981,405,2.18,505,2.84,511,3.369,527,2.31,536,2.041,635,2.486,759,2.402,760,3.122,769,2.633,772,4.522,778,4.322,807,2.648,814,2.951,878,4.506,946,3.155,1009,3.155,1263,4.154,1352,3.295,1508,5.148,1900,6.267,2679,7.193,3051,4.615,3054,6.033,4854,9.306,4855,6.853]],["t/1591",[6,5.235,7,0.685,21,2.455,26,3.226,28,1.362,31,1.179,41,2.188,48,1.957,56,0.858,84,1.046,92,1.786,93,3.067,107,2.225,109,1.465,123,1.644,125,2.75,138,1.946,153,2.468,163,1.912,165,1.946,171,1.967,172,3.01,173,2.581,174,2.152,179,3.231,190,3.721,199,2.379,207,2.955,224,2.044,233,1.746,236,1.903,289,2.861,296,1.859,304,1.017,318,1.175,322,1.509,339,2.433,351,1.746,437,2.264,491,1.454,502,1.996,505,3.601,578,1.896,583,2.946,596,2.173,599,3.721,613,2.181,614,1.778,620,2.668,658,2.31,691,2.653,702,3.249,724,2.349,725,3.685,757,2.379,812,3.216,877,2.64,906,2.924,997,3.831,998,3.096,1202,2.541,1241,5.895,1328,2.882,1360,2.75,1370,4.394,1389,4.294,1417,3.184,1444,3.216,1473,2.15,1737,1.819,1891,3.59,2092,2.7,2297,1.607,2395,2.529,2428,3.928,2522,3.707,2916,4.394,2963,3.443,3540,6.032,3622,7.429,3644,1.777,3740,6.87,3741,4.578]],["t/1593",[7,0.669,29,2.961,31,2.074,39,3.454,46,3.518,50,6.264,84,0.919,98,2.983,105,2.655,123,2.893,131,3.851,207,3.465,233,3.072,248,3.984,254,2.73,278,3.042,313,2.074,318,2.067,447,4.016,572,4.495,578,3.336,580,2.947,814,3.655,1043,4.405,1044,5,1389,5.035,1392,6.912,1408,3.769,1826,6.316,2717,5.548,3623,9.155]],["t/1595",[2061,3.73]],["t/1597",[7,0.721,15,2.437,21,1.227,24,1.902,29,1.515,34,1.106,56,1.238,84,0.47,89,2.494,98,1.526,103,2.236,105,1.358,108,2.242,139,1.949,160,1.522,171,1.18,182,2.726,203,1.039,219,1.344,221,3.008,232,2.963,254,1.396,318,1.057,338,1.068,353,1.184,377,1.495,395,2.641,410,2.866,426,1.604,437,2.038,471,1.799,476,2.365,491,2.014,502,1.197,512,2.809,527,2.159,528,2.287,536,1.293,564,2.812,607,2.561,630,1.604,653,1.367,747,1.783,757,1.07,759,2.856,760,2.407,769,1.669,841,2.007,945,4.771,1037,1.655,1050,7.264,1060,2.105,1107,2.287,1170,2.051,1257,4.734,1284,2.123,1477,3.231,1759,2.989,1906,4.43,2026,2.221,2032,2.813,2225,2.311,2297,1.446,2312,3.576,2632,2.594,2633,2.388,2815,3.023,2867,2.54,2895,2.673,3513,2.989,3936,3.823,4016,3.184,4054,5.442,4356,6.633,4856,4.342,4857,4.342,4858,8.146,4859,4.342,4860,6.683,4861,4.342,4862,4.342,4863,4.342,4864,4.342,4865,4.342,4866,6.087,4867,4.342,4868,4.342,4869,4.342,4870,4.342,4871,4.342]],["t/1599",[4,1.241,24,1.86,28,1.584,31,1.532,39,2.552,68,1.517,84,0.433,88,1.802,89,2.34,107,1.606,109,1.476,115,2.888,170,1.182,182,1.339,224,2.657,232,2.281,236,1.649,254,2.815,332,3.255,398,1.409,414,3.598,426,1.478,444,2.719,512,2.162,527,2.205,614,1.857,630,2.316,647,3.567,653,3.319,694,2.176,757,0.986,760,2.98,803,2.253,846,1.777,942,5.094,1008,2.426,1041,3.989,1060,1.94,1170,3.351,1245,3.146,1266,3.061,1363,4.284,1473,2.795,1476,3.875,1873,4.061,1906,3.411,1907,2.067,1908,2.82,2460,5.952,2518,3.135,3621,4.906,3733,4.535,3736,5.209,4866,3.645,4872,4.002,4873,6.272,4874,4.002,4875,4.002,4876,4.002,4877,6.272,4878,4.002,4879,3.024,4880,6.272,4881,4.002,4882,6.272]],["t/1601",[2061,3.73]],["t/1603",[2,0.893,7,0.892,16,1.003,24,1.584,28,0.467,34,2.362,48,1.344,49,1.767,56,1.041,84,0.739,85,2.723,96,1.167,97,1.481,105,1.036,109,0.559,113,1.208,123,1.129,140,1.298,164,0.91,170,2.015,171,2.136,174,0.984,193,1.205,203,1.633,213,1.348,219,1.025,223,4.872,232,1.205,246,1.253,254,3.144,275,1.025,295,2.743,296,1.276,304,1.657,313,0.809,322,2.901,337,1.543,338,1.326,343,1.178,372,2.018,377,0.741,381,1.567,398,1.167,401,1.266,430,1.634,449,2.039,461,5.192,476,0.962,482,1.481,491,0.998,505,1.373,508,1.951,525,2.541,527,0.724,528,2.841,567,1.328,580,2.368,613,2.437,614,1.708,622,1.671,628,1.711,707,3.795,715,2.772,740,1.497,810,1.471,830,2.464,852,1.436,862,2.683,874,1.627,884,2.089,898,1.606,930,1.812,979,2.186,1170,2.094,1202,1.745,1270,2.832,1281,1.671,1363,2.456,1376,1.656,1394,7.026,1441,3.245,1467,4.218,1539,3.594,1700,2.465,1735,2.306,1737,2.034,1798,1.843,1873,2.145,2015,2.465,2032,1.394,2282,2.208,2297,1.103,2312,1.772,2395,1.736,2447,7.377,2509,2.429,2684,2.039,2898,4.494,3009,4.218,3016,1.993,3644,2.512,3755,4.611,3756,4.611,3757,2.832,3758,3.143,3759,3.143,3760,3.143,3761,4.611,3762,4.611,3763,3.143,3764,4.611,3765,4.611,3766,4.611,3767,3.143,3768,3.143,3769,3.143,3770,3.143,3772,3.143,3773,2.832,4883,4.912,4884,4.912,4885,5.393]],["t/1605",[7,0.528,21,1.892,24,1.084,28,1.293,31,2.551,56,1.199,76,4.983,232,3.332,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,846,4.067,1170,2.812,1266,3.54,2032,3.856,3016,5.512,3774,6.354,3775,6.354,3777,9.884,3782,6.923,3783,6.354,3784,5.725,3785,5.061,3786,5.061,3787,5.061,3788,5.061,3789,6.354,3790,6.354,3791,5.061,3792,5.453,4886,11.227]],["t/1607",[7,0.6,21,2.15,28,1.408,31,2.72,56,1.244,76,5.661,109,1.683,170,2.248,461,6.616,527,2.182,572,5.284,590,4.093,607,4.53,1266,3.223,3777,8.785,3782,7.542,3785,5.75,3786,5.75,3787,5.75,3788,5.75,3791,5.75,3792,6.195,4887,7.608,4888,7.608,4889,6.505,4890,7.608,4891,7.608]],["t/1609",[7,0.528,21,1.892,28,1.293,31,2.551,56,1.199,76,4.983,109,1.545,381,3.168,405,2.914,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,807,3.54,1037,3.492,1266,3.54,1443,6.816,1689,4.802,3782,6.923,3784,5.725,3785,5.061,3786,5.061,3787,5.061,3788,5.061,3791,5.061,3792,5.453,3793,6.354,3794,6.354,3795,8.012,3796,7.039,3797,6.354,3798,6.354,3799,5.58]],["t/1611",[7,0.57,21,2.044,28,1.362,31,2.653,56,1.226,76,5.382,109,1.627,126,4.659,193,2.631,332,5.008,461,6.485,527,2.11,572,5.11,590,3.891,607,4.441,1266,3.512,3782,7.293,3785,5.466,3786,5.466,3787,5.466,3788,5.466,3791,5.466,3796,7.416,3799,6.027,4889,6.184,4892,7.233,4893,7.233,4894,7.233,4895,7.233]],["t/1613",[7,0.474,21,1.698,31,2.408,56,1.191,76,4.472,174,1.786,233,2.175,236,2.234,318,1.463,461,5.997,504,3.643,509,3.405,527,1.858,572,4.5,590,3.233,607,4.106,653,2.675,740,2.717,1201,5.664,1218,4.784,1266,3.714,1281,4.286,2636,3.448,2704,6.529,2709,7.265,3733,6.145,3782,6.422,3785,4.542,3786,4.542,3787,4.542,3788,4.542,3791,4.542,3799,5.008,4889,5.139,4896,6.01,4897,6.01,4898,7.739,4899,8.497,4900,8.497,4901,6.01,4902,6.01]],["t/1615",[7,0.313,21,1.123,24,0.644,26,1.475,28,0.561,31,1.881,37,4.158,56,1.346,95,5.412,106,2.822,107,1.018,109,1.052,112,1.969,127,2.525,137,4.003,163,1.575,170,1.174,179,1.33,193,1.446,242,2.801,267,3.398,381,2.951,391,1.673,397,2.593,405,2.45,440,1.752,444,2.704,461,3.492,471,3.191,502,1.096,511,1.267,514,3.004,527,2.072,572,5.019,635,1.442,740,1.796,757,0.979,784,7.375,813,4.04,829,4.057,830,1.816,841,2.883,904,2.15,1087,5.82,1117,2.15,1201,2.649,1281,2.005,1352,1.911,1381,1.575,1443,5.73,1457,2.836,1845,2.357,1883,3.398,2312,2.127,2789,2.705,3009,4.88,3023,2.914,3264,3.236,3553,3.004,3715,3.054,3800,5.92,3801,3.772,3802,3.772,3803,3.772,3804,3.772,3805,3.772,3806,3.772,3807,7.014,3808,7.308,3809,7.308,3810,3.772,3811,3.772,3812,3.772,3813,3.772,3814,3.772,3816,3.772,3817,2.528,3818,3.772,3819,3.772,3820,5.92,3821,3.772,3822,3.772,3823,3.772,3824,3.772,3825,3.772,3826,3.772,3827,3.772,3828,3.772,3829,3.772,3830,3.772,3831,3.772,3832,3.772,3833,3.772,3834,3.772,4903,3.974,4904,3.974,4905,3.974,4906,3.974,4907,3.974]],["t/1617",[7,0.413,9,2.069,15,3.325,16,1.432,21,0.903,24,0.848,28,0.739,34,0.814,49,0.718,56,1.366,97,1.429,107,1.97,171,0.868,186,1.506,203,1.253,228,1.012,237,2.343,281,1.512,322,2.656,377,0.715,393,1.569,527,0.699,605,1.156,683,1.195,715,3.419,784,2.343,822,1.909,841,1.477,930,1.747,978,2.152,997,1.183,1055,2.109,1170,1.607,1204,7.679,1367,5.687,1394,6.419,1473,1.424,1567,2.455,1577,3.957,1597,2.015,1700,2.377,1898,1.683,1991,4.033,1992,2.602,2008,3.032,2283,2.225,2297,1.064,2325,2.91,2955,2.662,2956,2.732,3586,2.225,3755,2.732,3756,2.732,3757,2.732,3761,2.732,3762,2.732,3764,2.732,3765,2.732,3766,2.732,3817,3.331,3835,3.032,3837,3.032,3838,3.032,3839,9.139,3840,9.139,3841,9.139,3842,9.139,3843,9.139,3844,8.478,3845,3.032,3846,9.139,3847,8.234,3848,7.301,3849,3.032,3850,3.032,3851,3.032,3852,6.313,3853,3.032,3854,3.032,3855,3.032,4883,2.91,4884,2.91,4908,5.236]],["t/1619",[7,0.845,22,5.272,39,2.446,46,4.085,49,1.351,84,0.651,85,2.082,98,2.986,100,3.8,112,2.977,126,2.902,160,2.107,171,1.633,174,1.786,201,3.06,202,5.848,207,2.453,213,2.446,233,2.175,278,2.153,316,2.678,337,2.799,343,2.138,351,2.175,404,2.926,408,2.17,434,4.006,461,3.364,476,1.745,480,4.091,482,2.688,506,5.04,511,1.916,525,4.004,550,4.611,643,5.474,657,4.701,706,4.289,779,6.776,1009,2.767,1380,3.791,1390,4.289,1394,7.822,1473,2.678,1733,3.59,1845,3.565,1916,5.076,2196,8.428,2282,4.006,2447,5.784,2485,3.759,2509,4.407,2683,3.699,2684,5.23,3133,7.081,3158,4.701,3856,5.703,3857,4.185,3858,4.472,3859,5.703,3860,5.703,3861,5.703]],["t/1621",[2061,3.73]],["t/1623",[2,2.106,24,1.265,84,0.846,221,2.885,233,2.827,313,1.908,343,2.778,401,2.985,527,1.708,530,3.493,560,4.157,612,5.058,653,2.458,677,4.115,683,2.922,760,3.332,846,3.468,860,5.903,945,5.294,953,3.159,999,4.248,1043,5.853,1193,4.926,1216,3.976,1263,4.734,1276,5.812,1284,3.819,1312,2.87,1566,5.574,1718,5.206,2009,5.376,2312,4.179,2708,5.648,2910,7.941,3713,7.412,3714,7.412,3715,6.002,3716,6.678,3717,7.412]],["t/1625",[2061,3.73]],["t/1627",[2,1.727,5,2.256,7,0.868,49,1.439,56,1.007,138,2.583,165,1.435,171,1.74,203,1.533,207,3.626,246,3.857,254,3.721,256,3.324,275,1.982,276,4.401,288,4.226,304,1.349,313,1.564,315,4.219,337,2.982,338,1.575,343,2.278,416,2.949,476,1.859,491,1.93,513,3.018,691,4.886,707,3.563,715,3.291,735,4.696,760,3.419,769,2.461,774,5.517,810,3.944,830,2.927,877,2.335,878,2.731,996,4.039,1101,4.84,1365,4.005,1388,4.84,1470,4.631,1845,3.798,1953,3.483,2353,5.306,2679,4.359,2873,5.215,3718,7.298,3719,5.475,3720,5.475]],["t/1629",[7,0.646,31,2.001,84,0.887,92,3.033,93,3.471,116,3.057,139,3.676,171,2.226,174,2.435,207,3.344,233,2.965,254,2.634,272,3.354,276,4.058,283,2.369,287,5.846,304,2.206,318,1.995,343,2.914,527,1.791,653,2.579,734,3.625,759,2.872,760,3.409,810,3.637,1164,4.132,1245,2.71,1473,3.65,1953,4.455,3621,6.408,3718,7.667,3721,6.827]],["t/1631",[4,1.748,5,3.35,7,0.64,21,2.293,26,2.092,46,2.335,49,2.339,56,1.169,85,1.952,90,2.655,92,4.696,94,5.144,105,3.449,116,2.103,133,2.411,171,1.531,174,2.412,203,1.943,236,1.482,254,1.812,275,1.744,278,2.019,283,1.629,290,4.696,337,2.624,353,1.537,359,2.485,365,2.855,405,1.793,476,2.356,496,3.442,498,3.135,505,2.335,513,2.655,531,3.416,578,2.214,601,2.645,605,2.937,614,1.037,769,2.166,774,3.048,824,4.132,992,2.816,1006,2.882,1169,3.584,1276,4.193,1366,3.135,1434,3.135,1844,3.442,1845,3.342,1953,3.065,2287,5.035,2679,3.836,3481,5.524,3636,4.259,3718,5.998,3722,6.939,3723,6.133,3724,6.939,3725,4.021,3726,3.649,3727,4.818,3728,4.132]],["t/1633",[49,2.01,56,1.328,171,2.429,283,2.585,297,4.89,476,2.595,810,5.571,1953,4.863,2698,6.556,3586,6.226,3718,5.639,3722,7.645,3723,8.359,3724,7.645,3729,6.556]],["t/1636",[4,1.574,5,1.787,7,0.593,24,0.822,31,2.188,39,2.065,46,2.103,56,0.978,84,0.97,115,2.336,116,1.894,138,2.047,139,3.373,165,1.137,182,1.698,203,1.799,207,2.071,221,1.874,233,1.836,236,1.334,254,2.417,256,1.899,275,1.571,318,1.83,325,3.533,338,1.248,377,1.681,414,4.312,444,3.259,461,2.84,476,1.473,479,3.533,508,2.989,527,1.109,605,1.836,614,0.934,647,1.904,653,2.366,724,2.471,757,1.25,759,3.471,760,3.271,769,3.441,774,4.066,810,2.253,830,2.319,914,2.525,942,2.143,945,2.647,992,2.536,1008,3.076,1037,1.934,1060,3.645,1107,2.673,1170,1.558,1216,2.583,1245,2.487,1266,1.384,1284,2.481,1360,2.893,1381,2.011,1446,2.969,1476,1.864,1845,3.01,1863,2.123,1898,2.673,2013,3.2,2026,2.596,2032,3.164,2232,3.576,2518,3.757,2632,3.031,2633,2.791,2636,2.911,2679,5.117,2895,3.123,3621,5.88,3664,4.627,3718,4.741,3730,4.339,3731,4.467,3732,8.497,3733,5.436,3734,7.461,3735,4.046,3736,3.417]],["t/1639",[4,2.302,5,1.757,7,0.393,10,2.698,16,0.928,31,2.783,39,2.03,46,2.067,56,0.969,84,1.063,138,2.012,139,2.238,165,1.117,182,1.669,203,1.777,207,2.036,221,1.842,236,1.312,254,1.604,256,1.866,275,1.544,287,3.56,302,2.297,318,1.214,325,3.473,338,1.227,377,1.66,414,5.085,461,2.792,476,1.448,527,2.295,568,1.428,592,3.201,614,0.918,647,1.871,653,2.336,757,1.229,759,3.442,760,2.901,769,3.407,774,4.015,802,2.018,810,2.215,830,2.28,857,3.367,914,2.482,942,2.106,945,2.602,953,2.018,992,2.493,1008,3.024,1037,1.901,1060,3.599,1107,2.628,1170,1.531,1216,2.539,1245,1.65,1266,1.36,1284,2.439,1360,2.843,1381,1.977,1473,3.95,1476,1.833,1601,2.98,1845,2.959,1863,2.087,1898,2.628,2013,3.146,2026,2.551,2032,3.124,2033,2.809,2518,3.71,2632,2.98,2633,2.743,2636,2.861,2679,5.052,2895,3.07,3664,4.568,3718,4.681,3721,6.185,3735,3.978,3736,4.999,3737,8.412,3738,7.043,3739,4.734]],["t/1641",[2061,3.73]],["t/1643",[2,1.746,5,2.281,7,0.872,49,1.455,56,1.013,138,2.611,165,1.45,171,1.759,203,1.55,207,3.653,246,3.879,254,3.559,256,3.349,275,2.004,276,4.434,288,4.273,299,7.455,304,1.364,313,1.582,315,4.252,337,3.015,338,1.593,416,2.981,476,1.879,491,1.951,513,3.051,527,2.54,691,5.642,707,3.602,715,3.328,735,4.747,757,1.595,769,2.488,774,5.549,810,2.875,830,2.959,877,2.361,996,4.083,1101,4.893,1388,4.893,1470,4.682,1733,3.868,1845,3.84,1953,3.521,2353,5.347,2873,5.272,3619,5.536,3719,5.536,3720,5.536]],["t/1645",[7,0.694,49,1.979,92,3.261,93,4.642,116,3.287,164,2.419,171,2.393,174,2.618,207,3.595,233,3.187,254,3.523,256,3.295,276,5.909,283,2.547,299,7.654,304,1.856,343,3.132,527,1.925,653,2.772,810,3.911,1164,4.442,2118,6.206]],["t/1647",[4,1.827,5,3.434,7,0.66,21,2.367,26,2.186,46,2.441,49,2.386,56,1.108,85,2.04,90,2.775,92,4.683,94,5.272,105,3.509,116,2.198,133,2.519,171,1.6,174,2.489,203,2.005,236,1.549,254,1.894,275,1.823,278,2.11,283,1.703,290,4.908,299,6.813,337,2.743,353,1.606,359,2.597,365,2.984,405,1.874,476,2.431,496,3.597,498,3.277,505,2.441,513,2.775,527,1.288,531,3.57,578,2.314,605,3.031,614,1.084,769,2.263,774,3.186,824,4.318,992,2.944,1006,3.013,1169,3.746,1366,3.277,1434,3.277,1844,3.597,1845,3.493,1953,3.203,2287,3.654,3481,4.009,3636,4.451,3723,4.451,3725,4.203,3726,3.814,3727,5.036,3728,4.318,3742,7.162]],["t/1649",[15,3.008,49,1.854,56,1.271,85,2.858,171,2.241,283,2.385,296,3.179,297,4.512,299,5.294,476,2.395,810,5.412,1055,5.445,1345,7.604,1465,5.966,1577,6.235,1953,4.487,1991,5.001,2283,5.744,2698,7.71,3586,5.744,3723,6.235,3729,6.049,3742,7.054,4909,8.25,4910,8.25,4911,8.25,4912,8.25]],["t/1652",[4,1.997,5,2.268,7,0.508,31,2.178,39,2.62,46,2.668,56,1.09,84,0.697,116,2.403,138,2.597,182,2.154,203,1.541,207,2.628,221,2.378,254,2.07,256,2.409,275,1.993,299,6.564,377,1.44,414,5.115,476,1.869,527,2.535,614,1.185,647,2.415,653,2.807,691,3.541,757,1.587,769,3.427,774,4.823,830,2.943,942,2.719,945,3.358,1037,2.454,1060,4.323,1107,3.392,1170,1.977,1216,3.278,1245,2.13,1266,1.756,1284,3.149,1360,3.67,1476,2.366,1845,3.819,1863,2.693,1898,3.392,2026,3.294,2032,3.754,2518,4.457,2632,3.846,2633,3.541,2636,3.694,2895,3.963,3664,5.488,3735,5.135,3736,4.336,3743,9.707,3744,5.505]],["t/1654",[4,2.472,56,1.188,116,2.974,182,2.667,221,2.943,377,1.782,476,2.313,527,2.49,647,2.99,653,3.237,757,1.964,942,3.366,945,4.157,1037,3.038,1060,4.987,1107,4.199,1170,2.447,1245,2.637,1266,2.173,1284,3.897,1476,2.928,1863,3.334,2026,4.077,2032,4.329,2518,5.141,2632,4.761,2633,4.383,2895,4.905,3744,6.814,3745,10.806,3746,7.563]],["t/1656",[2061,3.73]],["t/1658",[7,0.563,17,2.824,34,1.82,49,1.606,56,1.138,203,1.71,225,2.554,239,4.099,248,5.412,256,2.673,285,3.954,296,2.753,304,2.43,317,3.571,351,2.586,393,3.509,398,2.517,731,3.745,734,3.161,760,2.111,1043,5.602,1044,4.208,1122,4.469,1133,3.844,1266,2.61,1399,6.358,1601,5.718,1632,3.708,1737,2.694,1918,4.763,2353,4.268,2379,5.954,2601,6.921,2602,6.921,2624,5.589,2887,5.818,3644,2.632,3747,6.78,3748,6.78,3749,4.975]],["t/1660",[7,0.674,10,4.625,56,1.062,84,1.165,228,2.708,248,5.524,322,2.674,511,2.726,605,3.893,888,5.036,940,3.199,1043,4.437,1399,6.932,1565,5.953,1601,5.108,1629,5.953,1638,4.416,1918,4.255,2297,2.848,2411,6.27,2601,6.183,2602,6.183,3750,8.114,3751,8.114,3752,8.114]],["t/1662",[7,0.585,15,2.704,56,1.086,133,3.172,140,2.906,162,2.606,164,2.037,165,1.661,171,2.015,196,2.939,203,1.775,248,4.605,255,4.994,322,3.069,324,3.927,347,4.894,393,3.642,614,1.806,724,3.611,731,3.887,828,3.092,851,3.887,877,2.704,1043,6.317,1046,4.587,1133,3.989,1399,5.779,1601,5.861,1632,3.848,1633,3.494,1918,3.69,2297,2.47,2601,7.095,2602,7.095,3655,7.194,3749,5.163,3753,6.34,3754,6.34]],["t/1664",[2061,3.73]],["t/1666",[7,0.446,12,1.416,16,0.653,24,1.317,28,1.148,31,1.381,36,3.129,39,1.429,49,1.27,56,1.334,84,0.612,86,3.847,98,1.234,99,2.214,100,2.528,106,2.072,107,1.816,109,0.592,112,1.739,113,2.061,121,5.574,123,1.926,127,1.854,132,1.031,133,1.502,137,5.219,145,1.412,163,1.391,164,0.964,165,0.786,170,1.67,171,0.954,172,1.216,179,1.175,197,1.564,207,1.433,228,2.248,246,1.327,278,1.258,289,1.387,296,1.353,304,1.495,322,1.098,323,1.416,336,1.429,338,1.39,372,1.314,381,2.674,405,2.257,408,2.936,409,1.779,433,3.263,495,1.695,505,1.455,508,2.068,527,0.768,577,2.674,578,1.38,605,1.27,614,0.646,630,1.297,635,1.274,723,3.581,749,3.66,751,5.784,757,0.865,760,1.67,841,1.623,877,1.28,878,1.497,906,2.128,934,1.674,946,3.267,997,1.3,1037,2.155,1164,1.771,1179,2.161,1238,3.376,1289,3.29,1381,1.391,1448,2.859,1487,2.34,1498,2.505,1782,2.546,1899,2.444,1916,2.097,2094,3.198,2297,1.169,2321,3.807,2485,4.438,3010,2.39,3023,5.964,3025,2.746,3413,2.539,3628,8.69,3629,3.535,3630,2.698,3632,4.894,3634,2.925,3635,3.198,4479,2.612,4913,3.783]],["t/1668",[7,0.379,21,1.358,24,1.169,28,1.018,36,3.822,49,1.08,56,1.344,84,0.52,86,3.27,96,1.692,100,3.227,107,1.23,112,2.38,113,1.752,121,5.638,127,2.367,137,3.083,140,1.883,171,1.305,172,1.664,177,2.32,199,3.56,203,1.15,207,3.538,233,1.739,246,1.817,285,1.985,304,1.521,316,3.216,322,1.503,372,1.798,376,2.739,381,2.273,437,2.255,508,2.83,527,2.258,577,3.414,578,1.888,596,2.164,614,0.884,653,2.728,723,1.717,732,3.14,751,5.563,757,1.184,760,1.419,816,2.722,828,2.003,946,2.212,997,1.779,1037,3.304,1181,2.493,1245,2.868,1863,2.01,1916,2.87,2297,1.6,2321,3.236,2851,2.756,3010,3.27,3023,8.683,3413,3.474,3628,7.806,3630,3.692,3632,5.215,3634,4.003,3635,4.376,4914,5.177]],["t/1670",[2,1.444,7,0.801,10,1.777,21,0.928,24,0.867,28,0.463,32,2.054,36,2.361,39,1.337,46,1.361,49,1.936,56,1.31,84,0.356,86,2.236,90,2.524,92,4.372,93,3.65,94,1.777,96,1.157,97,2.395,99,2.072,100,2.395,107,0.841,112,1.627,113,1.198,121,4.576,127,1.756,132,0.965,137,2.108,140,1.287,145,1.321,162,1.154,165,1.2,171,1.455,172,1.138,174,1.592,177,1.586,179,1.792,186,1.548,197,1.464,199,2.642,203,1.282,207,1.341,224,1.392,228,1.04,233,1.189,236,0.864,246,2.564,283,0.95,285,1.357,304,1.129,313,1.308,316,1.464,317,1.642,322,2.121,323,2.16,343,1.168,353,0.896,372,1.229,381,1.554,408,2.448,482,1.469,495,1.586,496,2.006,505,1.361,527,2.02,577,2.534,590,1.767,596,1.479,609,2.147,614,0.986,653,1.686,657,2.569,691,1.807,714,2.287,715,2.753,723,2.796,751,4.516,757,1.32,760,1.582,816,1.861,828,1.37,839,1.884,876,2.236,914,1.634,934,1.566,940,1.229,946,1.512,951,2.108,997,1.216,1037,2.042,1108,2.212,1164,2.701,1181,1.705,1245,1.772,1290,1.827,1434,1.827,1863,1.374,1953,1.786,2287,3.323,2297,1.784,2321,2.212,2486,2.189,2805,2.524,2851,1.884,3010,2.236,3023,8.112,3413,2.375,3481,3.646,3628,7.679,3629,2.054,3630,2.524,3632,4.08,3634,2.737,3635,2.992,3636,4.048,3637,2.808,4915,5.771]],["t/1672",[2,1.221,5,1.594,7,0.737,12,1.825,16,1.284,17,1.789,23,2.303,24,1.841,26,1.68,28,0.973,29,1.579,36,3.686,48,1.836,49,1.879,56,1.224,84,0.49,85,1.568,86,3.081,103,1.514,121,2.648,130,2.58,137,2.905,138,1.825,140,1.774,165,1.545,171,1.23,189,2.422,190,2.326,193,1.646,207,1.847,219,2.587,228,2.185,232,1.646,233,2.497,236,1.19,239,2.597,289,2.726,296,1.744,313,1.685,322,1.416,324,2.397,336,1.842,342,2.959,373,3.273,381,2.141,405,3.616,407,3.778,408,3.375,409,3.496,438,2.349,458,3.884,464,3.87,495,3.331,502,1.248,508,2.666,527,0.99,567,1.815,572,2.397,580,1.572,592,2.905,600,3.017,673,3.985,683,1.694,723,2.988,731,2.373,744,2.58,750,3.19,751,2.613,757,1.115,807,1.749,811,2.808,831,2.855,852,1.962,929,3.081,936,1.936,1133,3.711,1193,2.855,1289,1.831,1509,3.87,1699,2.808,1782,2.039,1819,2.905,2297,1.508,2486,3.017,2497,3.541,3023,5.058,3620,3.985,3628,7.065,3887,3.609,4374,3.609,4479,3.368,4916,4.526,4917,4.526,4918,4.123,4919,4.526,4920,4.526]],["t/1674",[2061,3.73]],["t/1676",[29,3.492,101,5.328,305,6.367,318,2.437,323,4.037,395,3.956,505,4.148,655,6.542,728,6.969,740,4.524,1066,4.937,1281,5.049]],["t/1678",[4,2.243,7,0.57,17,2.858,25,2.316,27,1.717,40,3.81,48,2.934,85,2.505,164,1.987,203,1.731,233,2.617,236,3.047,275,2.239,281,3.422,282,3.934,323,2.917,343,2.573,405,2.301,409,3.665,565,2.361,568,2.764,608,3.754,655,4.728,683,2.706,691,5.308,740,4.909,760,3.423,798,3.978,810,3.211,830,3.305,874,4.739,886,4.601,904,5.874,947,3.682,1002,5.768,1028,4.773,1281,3.648,1302,4.684,1898,3.81,2678,6.184,2680,4.601,2681,4.641,2701,4.821,4921,7.233]],["t/1680",[7,0.912,10,4.462,25,3.706,32,5.16,39,3.357,84,0.894,101,4.391,229,4.893,233,2.986,236,2.169,318,2.009,343,2.934,390,3.983,481,4.928,565,2.694,630,3.047,683,3.087,737,6.235,740,4.753,827,4.18,937,5.678,1180,5.203,1281,5.304,1916,4.928,2372,5.499,2508,6.235,2680,5.248,2681,5.294,2701,5.499,3011,7.514,4922,8.25]],["t/1682",[7,0.644,12,1.758,21,1.894,25,2.146,27,1.034,28,0.615,31,1.065,56,0.977,84,0.726,96,1.535,106,1.597,107,1.716,109,0.735,111,3.196,115,2.007,123,1.485,132,1.968,162,1.531,165,0.976,170,2.708,179,2.242,197,2.986,213,1.773,228,2.123,233,2.955,236,1.762,238,7.053,246,1.648,247,1.915,272,3.343,276,3.32,281,2.062,296,1.679,313,1.637,318,1.988,323,1.758,324,2.308,364,3.071,365,2.208,372,1.631,382,2.704,387,3.034,390,2.104,409,2.208,482,1.949,491,1.314,502,1.202,527,2.518,541,2.682,565,1.423,568,1.248,578,1.713,607,1.67,630,1.61,632,2.345,651,5.442,658,2.087,660,3.243,685,2.425,734,1.928,814,1.877,827,2.208,877,2.444,878,1.858,880,1.877,885,3.82,893,2.516,904,6.361,914,2.169,947,3.412,950,4.666,1006,2.229,1009,2.007,1024,2.772,1034,3.837,1066,2.15,1116,2.876,1164,2.198,1170,1.338,1245,1.442,1281,3.38,1352,2.096,1379,3.349,1473,1.942,1632,2.262,1822,2.622,1838,3.243,2372,2.905,2445,3.726,2680,2.772,2681,5.882,2697,3.549,2701,6.109,2722,3.837,2723,4.136,2724,4.136]],["t/1684",[2,1.915,7,0.848,25,3.841,84,0.769,96,2.501,98,2.495,113,2.589,233,2.57,236,3.155,238,5.468,245,4.242,313,1.735,318,2.62,338,1.747,372,3.567,472,3.458,536,2.115,557,3.534,628,3.667,681,4.442,683,2.657,685,5.304,736,3.028,740,4.309,760,2.098,904,3.841,1006,3.632,1181,3.685,1182,6.072,1207,6.468,1245,2.35,1281,4.808,1289,2.873,1417,6.291,1810,6.072,2680,4.517,2681,6.117,2701,6.354,2713,6.252,3725,5.068]],["t/1686",[7,0.84,30,6.666,84,1.155,163,3.34,228,2.67,233,3.857,236,2.803,281,3.987,318,2.052,361,7.873,740,5.284,1132,5.868,1281,5.896,1394,5.617,1826,6.271,2021,6.179,2681,5.408,2684,6.56,2726,6.014,2727,7.676,2728,7.206,2773,7.676]],["t/1688",[7,0.869,31,1.377,34,1.435,40,2.969,48,2.286,56,0.556,84,0.61,103,1.885,107,1.443,116,2.103,132,1.655,145,2.266,179,1.885,233,2.937,236,2.501,238,3.232,247,2.476,248,2.645,313,1.377,318,2.685,372,2.108,377,1.26,426,2.081,447,2.666,465,5.65,482,2.52,502,2.869,509,3.192,525,2.655,527,1.774,530,2.52,565,2.65,568,1.614,608,2.924,614,1.037,630,2.081,635,2.044,647,2.114,685,3.135,691,4.463,740,3.668,760,2.81,904,6.553,999,3.065,1114,2.556,1116,5.356,1201,3.756,1245,2.685,1281,2.842,1457,4.021,1459,4.132,1460,4.696,1632,2.924,1782,3.655,1822,6.261,2672,5.304,2680,3.584,2681,5.208,2697,4.589,2701,3.756,2706,4.696,2726,4.021,2799,4.408,4923,5.132,4924,5.132,4925,5.132,4926,5.132]],["t/1690",[7,0.803,21,2.877,49,2.289,56,1.005,84,1.103,233,3.685,236,2.677,305,6.477,740,4.603,1009,4.689]],["t/1692",[7,0.616,21,1.512,25,2.957,29,1.867,34,1.363,41,2.428,49,2.076,56,1.151,84,1.1,85,1.854,92,2.895,105,1.674,113,1.952,129,3.197,131,2.428,134,2.663,140,2.097,162,1.881,165,1.199,171,1.454,233,1.937,236,1.407,238,4.485,256,2.003,296,2.063,318,1.903,324,2.834,351,1.937,372,2.003,377,1.197,401,2.046,465,3.727,491,1.613,536,1.594,580,1.858,607,2.051,614,0.985,621,2.751,628,2.764,681,3.348,685,2.978,691,2.944,740,4.884,757,1.319,760,2.729,810,4.51,820,2.751,874,2.628,898,2.595,904,2.895,937,3.684,947,2.725,1239,3.294,1245,1.771,1281,5.691,1289,2.165,1441,3.22,1638,2.764,1911,3.982,1916,3.197,2287,3.32,2298,4.46,2680,3.405,2681,7.241,2698,5.732,2701,5.211,3481,3.643,3586,3.727,3729,3.924,4927,5.352,4928,5.352,4929,4.358,4930,4.576,4931,5.352,4932,5.352,4933,5.352]],["t/1694",[34,2.277,56,1.297,105,2.797,296,3.446,377,2,630,4.085,810,3.97,1245,2.958,1918,4.449,2287,5.547,2701,5.96,3481,6.086,3586,6.226,3729,6.556,4929,7.281,4930,7.645,4934,8.942,4935,8.942,4936,8.942,4937,8.942]],["t/1696",[48,3.371,56,1.273,283,2.402,491,2.504,539,5.332,614,1.529,683,3.109,760,2.455,810,3.689,1263,5.036,2287,5.154,2680,5.285,3481,5.655,3586,5.785,3729,6.092,4929,6.765,4930,7.104,4938,8.308,4939,8.308,4940,8.308,4941,8.308,4942,8.308,4943,8.308,4944,8.308,4945,8.308,4946,8.308,4947,8.308,4948,8.308,4949,8.308,4950,8.308,4951,8.308]],["t/1698",[7,0.776,49,2.212,106,3.607,162,3.458,256,3.682,304,2.074,608,5.107,614,1.812,814,4.238,904,6.341,1129,6.155,2885,8.414,4952,9.842]],["t/1700",[7,0.56,17,3.767,56,1.283,85,2.46,92,3.53,93,3.009,115,3.27,197,3.164,233,2.57,272,2.907,296,2.737,304,1.496,313,1.735,315,3.373,323,2.864,353,1.936,361,4.783,552,3.374,562,3.487,596,3.198,612,4.599,614,1.981,650,7.247,740,4.309,796,3.722,810,4.233,841,3.282,937,4.888,1073,5.135,1281,4.808,1310,6.803,1365,4.442,2297,2.365,2698,6.99,2778,6.252,3586,4.944,3729,5.207,4804,5.783,4929,5.783,4953,7.102]],["t/1703",[4,2.535,68,2.157,170,1.681,182,1.904,236,2.514,238,5.996,248,2.671,254,1.829,305,5.198,324,3.013,372,2.129,398,2.004,476,1.652,527,2.091,578,3.211,653,2.572,659,3.684,740,2.572,741,3.684,757,1.402,760,2.825,814,3.519,877,2.075,904,6.873,942,4.414,947,2.897,1116,5.393,1170,1.747,1201,5.447,1245,3.458,1266,3.373,1281,2.87,1303,3.399,1330,3.873,1363,2.943,1457,4.06,1459,4.172,1476,3.003,1754,3.473,1906,3.094,1907,2.938,2153,7.194,2680,3.619,2681,3.651,2697,6.654,2701,3.792,3016,3.423,3542,5.503,3866,2.953,4879,6.176,4954,5.182,4955,5.69,4956,5.182,4957,5.69]],["t/1705",[4,3.37,109,0.915,164,1.491,182,1.816,203,2.447,236,1.427,238,4.53,247,2.384,377,1.214,502,1.496,527,2.558,614,1.882,630,2.004,647,4.076,653,3.42,757,1.337,760,1.603,814,4.678,904,6.132,942,4.318,1037,2.069,1041,5.922,1245,3.08,1266,3.092,1363,3.912,1476,3.992,1632,2.816,1863,2.27,1906,5.063,3542,6.27,4879,5.968,4924,4.943,4925,7.193,4958,7.296,4959,5.149,4960,5.847,4961,5.427,4962,7.897,4963,5.427,4964,7.897,4965,5.847,4966,8.509,4967,5.427,4968,8.509]],["t/1707",[4,2.423,133,3.341,164,2.146,182,2.613,236,2.054,247,3.432,372,3.798,502,2.154,527,2.466,608,6.197,630,2.885,647,2.93,653,2.458,691,4.296,757,1.925,760,2.999,904,6.695,1041,4.968,1170,2.398,1245,3.731,1266,2.768,1363,2.812,1476,2.87,1632,4.053,1863,3.267,4926,9.246,4958,7.244,4959,7.412,4969,7.412]],["t/1709",[4,2.049,98,2.836,106,1.568,182,3.469,236,2.58,238,3.79,254,2.595,322,2.525,353,1.167,372,3.88,404,2.083,408,1.545,426,1.58,506,2.538,527,1.985,580,1.486,623,4.965,653,2.541,747,1.757,757,1.628,760,2.899,776,3.565,829,2.254,904,6.764,942,2.79,962,4.548,1008,2.594,1041,6.596,1116,2.824,1170,2.028,1201,6.05,1245,3.246,1266,3.339,1363,2.906,1373,2.613,1376,4.537,1476,2.966,1696,5.774,1765,5.207,1863,1.79,1906,3.593,1911,3.184,2061,1.445,2092,5.08,2705,8.614,2806,3.094,3542,4.45,3866,2.221,4509,3.565,4879,4.994,4923,6.018,4954,3.897,4958,7.4,4959,4.06,4969,4.06,4970,3.658,4971,6.607,4972,7.12,4973,6.607,4974,4.06]],["t/1711",[4,2.691,182,2.903,203,2.077,238,6.796,372,3.246,482,3.88,608,4.503,647,3.255,653,2.731,757,2.138,803,4.886,904,5.872,942,4.584,947,4.417,1245,2.871,1266,3.231,1363,3.124,1443,6.456,1476,3.188,1863,3.63,2704,6.667,4958,6.192,4975,10.856]],["t/1713",[4,2.736,98,1.738,106,1.813,109,1.244,182,3.907,236,1.939,238,5.608,254,1.59,353,1.349,372,4.251,404,2.408,408,1.785,506,2.934,527,1.081,580,1.717,623,2.368,647,2.767,653,2.776,747,2.031,757,1.219,760,2.179,776,4.122,803,2.785,829,2.606,904,6.572,942,4.416,962,3.404,1041,7.227,1170,1.518,1201,3.297,1245,3.46,1266,3.313,1363,3.176,1373,3.021,1376,3.686,1476,3.241,1696,4.692,1765,4.231,1863,2.069,1882,2.913,2061,1.67,2092,4.128,2704,5.668,2806,3.577,4509,4.122,4958,8.108,4969,4.694,4970,4.229,4974,4.694,4976,7.376,4977,4.946,4978,5.33,4979,4.946,4980,4.946,4981,4.505,4982,4.946,4983,9.777]],["t/1715",[4,2.455,182,2.649,203,1.895,238,6.885,372,2.962,482,3.54,527,2.784,608,5.314,647,2.97,653,2.492,757,1.951,803,4.457,877,2.886,878,4.366,904,5.539,942,4.324,947,4.03,1116,5.224,1245,2.619,1266,3.095,1363,2.85,1443,5.89,1476,2.909,1863,3.311,1907,4.088,2722,9.015,4958,5.649,4984,7.916]],["t/1717",[4,2.674,106,2.249,109,1.454,182,3.618,238,6.203,353,1.673,372,4.046,404,2.988,408,2.215,506,3.639,527,2.491,580,2.13,647,3.234,653,3.138,747,2.52,757,1.512,776,5.113,803,3.455,829,3.233,904,3.319,942,4.21,962,4.223,1041,6.341,1170,1.884,1245,3.578,1266,3.309,1363,3.59,1373,3.747,1376,3.067,1476,3.663,1696,3.903,1765,3.52,1863,2.567,1882,3.614,2061,2.072,2092,3.434,2721,8.182,2806,4.437,4509,5.113,4956,10.376,4958,7.114,4970,5.246,4974,5.823,4981,5.589]],["t/1719",[4,3.021,7,0.768,21,1.042,25,3.838,27,1.986,48,1.496,84,0.399,99,2.325,163,1.461,182,1.234,203,0.883,236,1.928,238,5.242,297,2.016,318,0.898,372,3.13,390,1.78,397,1.533,414,2.115,418,2.938,444,2.548,445,2.325,472,2.862,512,2.528,647,2.75,653,3.216,685,5.902,740,3.781,757,1.807,760,2.166,828,2.451,904,1.994,914,1.835,937,2.538,942,3.859,947,2.993,1117,1.994,1170,1.805,1245,2.426,1266,3.177,1281,5.515,1363,3.937,1476,3.754,1492,3.072,1911,4.374,2032,2.475,2231,2.666,2508,2.787,2680,5.813,2681,6.557,2684,2.269,2701,6.81,2703,3.499,2704,4.517,2706,4.899,2709,5.027,2867,2.157,3664,2.269,3733,5.302,3917,2.345,4879,2.787,4958,6.521,4985,5.879,4986,5.879,4987,5.879,4988,5.879,4989,3.687,4990,3.687,4991,3.687,4992,3.687,4993,3.687,4994,3.687,4995,5.879,4996,5.879,4997,5.879]],["t/1721",[2,2.108,4,2.425,7,0.616,16,0.996,25,1.714,27,1.27,28,0.755,30,3.348,40,2.82,56,0.528,84,1.1,98,1.881,103,1.791,105,1.674,116,1.997,132,1.572,163,2.121,164,1.47,165,1.199,182,1.791,233,2.829,247,2.352,298,3.269,302,2.464,313,1.91,318,2.631,338,1.317,351,1.937,394,2.675,444,2.32,447,2.532,461,4.376,477,3.175,491,1.613,502,1.476,536,1.594,565,1.747,647,2.008,653,1.685,740,4.591,757,2.276,802,2.165,814,2.305,822,3.197,828,2.232,874,2.628,904,4.229,920,3.11,942,3.9,949,3.982,958,3.498,999,2.911,1007,3.604,1132,3.727,1218,5.719,1245,3.361,1266,2.77,1281,5.691,1312,1.967,1363,2.815,1444,3.568,1476,1.967,1527,3.532,1605,3.924,1863,2.239,2021,5.732,2684,3.294,2704,4.113,2706,4.46,2728,8.685,3733,6.679,4898,4.875]],["t/1723",[7,0.553,31,2.31,106,2.571,137,4.503,163,2.781,224,2.973,236,3.236,278,2.514,367,3.572,405,2.232,416,3.231,434,4.677,511,2.237,572,5.664,580,2.436,650,7.203,653,2.976,740,4.834,1013,5.221,1053,5.488,1122,4.388,1132,4.885,1170,2.903,1201,4.677,1281,3.539,1390,5.007,1689,3.678,2507,6.658,2508,8.083,2683,4.319,2709,8.085,3133,5.847,4479,5.221,4851,10.076,4998,7.017,4999,7.017,5000,8.973,5001,7.017,5002,7.561,5003,7.017,5004,7.017]],["t/1725",[272,4.48,596,4.928]],["t/1727",[2061,3.73]],["t/1729",[7,0.54,28,1.491,31,2.273,49,1.54,56,0.677,68,2.598,84,0.742,92,3.446,94,3.706,115,4.866,132,2.733,133,2.931,196,2.716,203,1.64,233,2.48,236,1.802,254,2.203,283,1.981,313,1.674,315,3.255,318,1.668,377,2.081,390,3.309,436,4.89,502,3.125,513,3.229,527,1.498,550,2.693,577,4.999,630,2.531,675,4.359,715,3.522,751,3.956,769,2.633,829,3.61,1037,3.547,1045,4.568,1218,5.24,1220,5.71,1308,4.064,1332,4.185,1380,4.322,1863,2.866,1953,3.727,2518,3.425,2669,5.179,2799,5.36,3862,5.464,3864,6.503,3865,6.503,5005,6.853]],["t/1731",[2,1.786,4,2.054,7,0.522,16,1.232,56,1.103,84,0.985,116,2.471,182,2.215,221,2.445,232,2.408,313,1.617,318,1.612,372,2.477,377,1.481,476,1.922,491,1.996,502,1.826,527,2.56,536,1.972,565,2.162,568,1.896,605,3.289,630,2.445,647,2.484,653,2.861,683,2.477,757,1.631,814,4.811,841,3.06,852,2.87,942,2.796,945,3.453,1037,2.524,1060,4.407,1107,3.488,1139,3.543,1170,2.032,1218,6.593,1257,3.848,1266,1.805,1284,3.237,1476,2.433,1863,2.769,2026,3.386,2032,3.826,2518,4.543,2632,3.955,2633,3.641,2895,4.075,3862,7.248,3866,3.436]],["t/1733",[0,2.444,4,1.313,7,0.516,12,1.707,16,1.219,24,1.061,31,2.522,39,2.666,41,1.92,56,1.22,84,0.458,98,1.487,113,2.389,115,4.495,132,1.243,165,0.948,179,1.416,182,2.682,196,1.677,203,1.013,219,1.31,221,1.563,254,1.361,282,2.302,318,1.595,372,1.584,414,3.758,471,1.754,476,1.229,477,2.51,491,1.276,502,1.167,527,2.636,553,2.241,565,1.382,567,1.697,568,1.876,577,2.002,580,1.47,610,3.252,614,1.206,647,3.007,653,2.841,658,4.322,670,2.46,724,2.061,757,1.043,814,2.821,829,4.223,830,1.934,936,3.429,942,3.385,945,2.207,1037,2.498,1060,3.887,1107,2.23,1170,2.461,1218,6.263,1245,1.4,1257,2.46,1266,1.787,1284,2.07,1363,3.877,1476,2.945,1671,3.603,2026,2.165,2032,2.758,2098,8.134,2232,2.983,2518,3.275,2632,2.528,2633,2.328,2895,2.605,3006,3.619,3008,3.149,3041,3.619,3866,3.4,3867,4.233,3868,6.217,3869,4.016,3870,4.016,3871,6.217]],["t/1735",[4,2.243,5,2.548,16,1.346,56,1.144,116,2.699,164,1.987,182,2.42,221,2.671,254,2.326,318,1.761,372,2.706,476,2.099,502,1.994,527,2.715,647,2.713,653,3.038,757,1.782,942,3.054,945,3.772,1037,2.757,1054,9.892,1060,4.679,1107,3.81,1170,2.22,1218,6.524,1257,4.203,1266,1.972,1284,3.537,1476,2.658,1863,3.025,2026,3.7,2032,4.062,2518,4.824,2632,4.32,2633,3.978,2895,4.452,2905,5.162,3866,3.754,3872,9.158]],["t/1737",[2,1.47,7,0.808,24,0.883,28,0.769,31,1.936,49,2.098,56,1.269,90,2.569,92,4.76,93,4.341,94,2.949,97,2.438,98,1.916,100,2.438,115,2.51,132,1.601,165,1.221,171,2.536,174,2.774,177,2.632,203,1.897,283,1.576,304,1.149,313,1.332,322,2.92,398,1.92,496,3.33,527,2.24,553,2.887,614,1.004,714,3.796,715,2.802,769,3.045,814,2.348,850,3.563,898,2.643,914,2.713,940,2.04,1129,4.956,1218,5.769,1290,3.033,1434,3.033,1689,2.858,1953,2.965,2287,4.916,2297,3.109,2502,4.264,3023,3.997,3481,3.711,3636,5.988,3637,4.661,3847,4.661,3874,7.519,3875,5.173,5006,9.334]],["t/1739",[2061,3.73]],["t/1741",[7,0.938,84,1.094,139,4.53,170,2.983,397,4.197,759,3.539,940,3.777,1289,4.084,1506,6.478,3925,7.3]],["t/1743",[24,1.75,31,2.104,56,0.85,68,3.266,113,3.141,123,2.935,139,4.849,179,2.882,183,3.697,313,2.104,527,2.707,568,2.467,632,4.634,759,3.788,760,2.545,914,4.286,953,3.484,1245,3.575,1266,2.349,1381,3.414,1473,3.838,1506,5.527,1863,3.603,2851,4.941]],["t/1745",[4,3.243,28,1.475,107,2.677,109,1.763,505,4.333,760,3.089,1266,2.851]],["t/1747",[12,4.179,24,1.678,109,1.747,254,3.332,512,3.573,578,4.072,614,1.908,1754,4.404]],["t/1749",[7,0.782,12,4.003,24,1.607,109,1.674,128,4.733,254,3.191,578,3.9,614,1.827,681,6.207,736,4.232,1075,5.768,1266,2.706,1616,5.522]],["t/1751",[24,1.691,28,1.584,31,2.238,34,2.333,36,2.953,39,2.725,49,1.505,84,0.725,109,1.545,116,2.499,132,1.967,133,2.865,139,3.005,164,2.868,165,1.5,170,3.474,203,2.193,285,2.766,289,2.646,338,1.647,353,1.826,367,3.409,382,4.154,394,3.347,397,2.784,414,3.841,505,2.775,525,3.155,614,1.233,693,4.121,756,3.143,759,3.211,760,3.085,769,4.314,830,3.06,854,3.564,916,3.891,946,4.808,1216,3.409,1506,4.297,1558,3.77,1671,3.683,1792,4.188,2696,4.51,2818,4.419,3917,4.259,3922,5.725]],["t/1753",[7,0.711,21,1.85,29,2.284,31,2.849,39,2.664,49,1.471,125,3.732,139,4.631,165,1.467,170,3.774,197,2.917,203,2.662,213,3.67,236,1.721,246,4.205,272,2.68,292,2.848,304,1.38,323,2.64,338,1.61,414,3.756,491,1.973,605,2.369,614,1.205,734,2.897,759,3.162,760,3.285,769,4.909,811,4.062,1188,5.031,1216,3.333,1366,3.642,1367,3.174,2818,6.81,3042,3.83,3664,5.552,3917,4.164]],["t/1755",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3924,9.359,3925,6.885,3926,7.754,3927,7.754]],["t/1757",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3925,6.885,3928,9.359,3929,7.754,3930,7.754]],["t/1759",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1473,4.277,3718,6.054]],["t/1761",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,1245,3.229,3718,6.155]],["t/1763",[21,1.809,56,1.212,68,3.368,107,2.274,109,2.111,139,2.874,170,1.892,254,2.059,338,1.575,353,1.746,377,1.432,596,2.884,605,2.317,614,2.261,647,3.332,653,2.016,755,2.884,760,1.892,942,4.887,997,4.079,1363,2.306,1476,2.353,1737,2.415,2232,6.26,2839,8.521,3053,5.982,3056,5.215,3943,9.201,3944,4.921,5007,6.826,5008,6.514,5009,6.714]],["t/1765",[7,0.624,49,1.779,56,0.782,84,1.229,103,2.649,109,1.727,164,2.175,170,2.339,217,4.512,279,3.42,377,1.77,398,3.607,527,1.731,565,2.584,760,3.354,811,4.911,996,4.992,1245,2.619,1257,5.951,1530,4.763,1550,5.89,1558,4.457,1902,3.775,1918,5.648,1998,4.6,2013,4.992,2600,4.872,2814,5.512,2822,6.083,3954,6.969,4009,8.755,4176,7.916,4177,7.512]],["t/1767",[7,0.818,34,2.058,84,0.875,103,3.472,109,1.933,123,2.753,170,3.066,410,5.332,527,1.766,565,2.638,568,2.314,630,2.984,683,3.023,723,2.888,759,3.637,760,2.387,769,3.105,841,3.734,1044,4.759,1320,5.139,1465,5.842,1566,5.766,1754,3.434,1759,5.561,2500,7.359,2814,5.625,2815,5.625,2867,4.727,4784,9.45,5010,9.45,5011,8.08,5012,8.08]],["t/1769",[2061,3.73]],["t/1771",[7,0.693,16,0.636,24,1.726,28,1.241,29,1.193,34,0.871,49,1.8,55,1.859,56,0.791,84,1.073,85,1.184,107,3.253,109,1.925,113,1.247,127,1.121,162,1.944,165,0.766,170,2.928,171,0.929,172,1.184,193,1.243,233,2.522,236,2.106,246,2.092,278,1.225,285,2.286,295,1.375,304,1.166,338,2.164,351,2.002,376,1.949,405,1.088,408,3.398,426,1.263,429,2.923,477,2.028,484,1.448,500,1.617,527,2.495,536,3.335,557,1.701,576,2.014,593,1.686,605,1.237,611,1.421,614,1.733,621,1.757,622,1.724,647,2.075,736,1.458,755,1.54,756,1.605,757,0.842,760,3.372,765,2.028,811,2.121,828,1.426,880,3.449,882,2.042,914,1.701,936,1.462,953,1.383,992,3.483,997,1.266,1117,1.849,1181,2.871,1237,2.302,1240,3.972,1245,1.83,1381,1.355,1508,5.207,1540,3.06,1699,2.121,1754,1.453,1863,1.43,1870,3.71,2372,3.688,2700,2.784,2764,3.01,2800,2.726,2851,3.174,3879,2.544,3880,2.234,4474,5.11,5013,3.419,5014,3.114,5015,2.784]],["t/1773",[7,0.774,16,1.111,24,1.826,28,1.452,31,0.918,49,1.9,56,0.371,84,1.115,107,2.362,109,1.901,123,1.281,127,4.248,132,1.753,165,1.663,170,1.763,193,3.355,197,1.674,233,3.06,276,1.862,278,1.347,296,1.448,304,0.792,318,0.915,338,2.415,373,2.717,405,1.195,426,2.204,433,2.17,476,2.678,491,1.133,500,1.778,524,2.313,527,0.822,536,3.487,565,2.424,568,1.709,592,2.412,593,1.854,605,1.36,609,2.456,614,1.557,635,1.363,647,3.172,653,1.183,691,2.067,756,1.764,760,3.041,818,1.887,857,1.705,880,3.197,929,2.558,936,1.608,995,2.558,999,2.044,1117,2.033,1237,2.531,1240,2.142,1381,2.366,1508,6.476,1696,2.39,1699,2.331,1754,1.597,1838,2.796,1870,3.583,1875,7.046,2787,2.39,3879,2.796,3880,2.456,5016,6.046]],["t/1775",[7,0.796,16,0.982,24,1.879,28,1.293,49,1.187,84,0.838,107,2.584,109,2.047,170,1.56,172,4.465,174,1.569,233,3.652,278,1.892,304,1.113,338,2.762,405,1.68,426,2.859,502,3.096,527,1.692,536,3.005,592,3.388,593,2.604,605,1.911,614,1.425,647,3.786,756,2.478,757,1.907,760,2.982,819,3.721,880,3.333,936,2.259,1117,2.856,1237,3.556,1381,3.068,1508,6.582,1699,3.276,1870,3.512,3879,3.929,3880,3.451,5017,6.618,5018,7.05]],["t/1777",[7,0.728,16,0.996,24,1.887,28,1.303,49,1.203,84,0.847,107,2.6,109,2.088,170,1.581,233,3.342,278,1.918,304,1.128,338,2.775,405,1.703,426,2.887,502,3.111,527,1.709,536,3.025,592,3.435,593,2.64,605,1.937,614,1.439,647,3.81,756,2.512,757,1.926,760,3.001,880,3.367,936,2.29,997,4.784,1117,2.895,1237,3.604,1381,3.098,1508,6.607,1699,3.32,1870,3.535,3879,3.982,3880,3.498,5019,6.684,5020,7.121]],["t/1779",[16,1.757,28,1.614,84,1.023,109,1.593,165,2.116,170,2.791,172,3.272,322,2.954,607,3.62,614,1.739,724,4.599,725,5.823,755,4.254,1508,5.225,1870,3.287,2092,5.286,5009,7.138]],["t/1781",[16,1.743,28,1.606,84,1.015,109,1.58,165,2.099,170,2.768,172,3.246,173,5.014,322,2.931,607,3.591,614,1.725,724,4.562,755,4.22,877,3.417,1508,5.183,1870,3.261,2092,5.244,4511,7.472,5008,6.87]],["t/1783",[16,2.058,25,2.863,27,2.625,28,1.561,84,0.969,107,2.832,109,1.508,165,2.003,322,2.797,614,1.646,623,4.282,724,4.354,878,3.813,997,3.311,1139,4.785,1382,6.607,1508,4.946,1557,5.547,1870,3.112,2092,5.004,5007,6.871]],["t/1785",[2061,3.73]],["t/1787",[7,0.938,24,1.635,84,1.094,139,4.53,397,4.197,759,3.539,940,3.777,1289,4.084,3925,7.3,3938,9.58]],["t/1789",[7,0.633,24,1.299,56,0.792,84,0.869,89,2.994,109,1.353,139,4.636,164,2.204,165,1.797,171,2.806,174,2.385,285,3.315,502,2.213,527,2.637,568,2.958,605,2.904,614,1.477,724,5.03,729,5.726,759,2.813,953,4.179,1245,3.418,1266,2.817,1381,3.18,1506,5.149,1863,3.357,1870,2.793,1902,3.827,2851,5.926,5021,6.277]],["t/1791",[5,1.938,7,0.629,19,2.452,24,1.987,28,0.776,31,1.344,39,2.239,41,3.619,68,3.558,84,0.596,96,1.938,107,1.409,109,0.928,115,2.534,133,3.413,134,2.738,139,3.58,160,1.929,164,1.512,165,1.787,200,2.726,203,1.91,226,2.668,236,2.873,288,3.632,295,2.213,318,1.34,338,1.354,353,1.501,367,2.801,414,3.157,527,2.052,568,2.284,591,3.137,605,1.991,630,2.032,638,3.311,757,1.356,759,2.796,760,2.772,769,4.199,770,4.228,774,2.976,827,5.215,933,4.035,934,2.624,946,5.03,950,3.831,1133,2.96,1239,3.387,1240,3.137,1303,3.287,1448,4.481,1498,3.927,1506,3.531,1733,3.287,1754,2.339,1814,3.927,2818,6.193,2966,3.746,3401,4.159,3629,3.442,3730,4.705,3917,3.5,3918,4.844,3919,5.503,3920,5.503,3921,4.095,3922,4.705]],["t/1793",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1473,4.277,3718,6.054]],["t/1795",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,1245,3.229,3718,6.155]],["t/1797",[4,2.506,7,0.818,49,1.816,56,0.798,84,1.124,109,1.362,139,3.626,165,1.81,170,3.066,200,4.002,203,2.484,206,5.232,246,3.923,247,3.55,304,1.703,613,3.652,614,1.487,759,2.832,760,3.386,958,5.281,992,4.038,1139,4.323,1266,2.203,1284,3.951,1473,3.6,1527,5.332,2297,2.691,3924,6.579,3925,5.842,3926,6.579,3927,9.333]],["t/1799",[4,2.506,7,0.637,49,1.816,56,0.798,84,1.124,109,1.362,139,3.626,165,1.81,170,3.066,200,4.002,203,2.484,206,5.232,246,3.923,247,3.55,304,1.703,613,3.652,614,1.487,759,2.832,760,3.386,958,5.281,992,4.038,1139,4.323,1266,2.203,1284,3.951,1473,3.6,1527,5.332,2297,2.691,3925,5.842,3928,6.579,3929,6.579,3930,9.333,3931,7.667]],["t/1801",[7,0.745,24,1.529,28,1.614,109,1.593,127,3.097,132,2.774,139,4.239,193,3.435,236,2.483,254,3.956,550,3.712,578,3.712,614,1.739,623,4.523,997,3.497,1382,5.642]],["t/1803",[7,0.694,84,1.187,88,3.967,89,3.287,128,5.688,140,3.451,165,1.973,182,2.947,294,4.738,575,4.183,605,3.187,635,3.195,763,5.995,828,3.673,940,3.295,1075,5.118,1363,3.946,1381,3.49,1408,3.911,1616,6.096,3932,6.889,3933,7.753,3934,7.753]],["t/1805",[16,2.181,28,1.389,34,2.506,107,2.52,109,1.659,132,3.443,170,2.908,353,2.684,484,4.169,614,1.812,1266,2.684,3866,5.107]],["t/1807",[7,0.347,12,1.777,16,0.82,24,1.331,25,1.411,34,1.122,36,1.943,49,0.991,56,1.141,68,1.671,84,0.477,96,1.552,107,1.128,109,1.842,132,1.294,139,4.462,140,1.727,170,2.429,179,1.475,199,2.174,236,1.159,295,1.772,296,1.698,313,1.077,338,1.663,353,1.202,377,0.986,426,1.628,476,3.171,491,1.328,502,1.215,512,2.835,527,0.964,575,3.211,580,1.53,614,2.011,647,3.73,653,2.128,663,2.692,725,3.441,756,2.069,759,2.37,803,2.482,828,1.838,940,3.076,942,4.88,997,2.503,1012,3.829,1114,1.999,1170,1.353,1187,3.768,1245,1.458,1266,1.843,1289,1.783,1363,2.961,1408,4.415,1476,2.484,1616,3.761,1864,2.322,1874,3.069,1899,3.069,1902,3.921,2488,2.671,2839,7.672,3053,4.552,3916,2.938,3936,3.88,3937,6.76,3938,4.182,3939,6.414,3940,8.117,3941,6.414,3942,2.804,3943,7.641,3944,3.387]],["t/1809",[7,0.814,12,3.236,24,1.85,28,1.132,34,2.043,68,3.042,132,2.357,233,2.904,313,1.96,318,1.954,328,5.296,398,2.826,426,2.963,472,3.907,476,2.329,502,2.213,512,2.767,536,2.39,565,3.373,568,2.298,578,3.153,614,1.477,623,3.842,647,3.875,724,3.907,997,2.971,1037,3.059,1380,5.061,1382,4.793,1408,3.563,1476,2.948,1487,5.349,1906,4.364,1907,4.144,3725,5.726,4218,5.726]],["t/1811",[7,0.751,24,2,29,2.478,31,2.629,39,2.89,49,1.596,125,4.048,139,4.829,165,1.591,197,3.164,203,2.753,213,3.879,236,1.867,246,4.069,272,2.907,292,3.089,304,1.496,414,4.074,491,2.14,759,3.342,760,2.816,769,4.929,811,4.406,1188,5.457,1216,3.615,1366,3.951,1367,3.443,2818,7.102,3042,4.155,3664,5.867,3917,4.517,3946,6.739]],["t/1813",[7,0.89,16,1.116,24,1.952,25,2.387,28,1.655,29,1.32,31,2.261,34,0.963,37,3.999,39,2.441,49,0.85,56,0.736,68,2.275,85,1.31,96,1.332,103,1.266,106,1.386,112,2.972,133,1.618,134,1.882,139,1.697,140,1.482,145,1.521,160,2.975,165,0.847,171,1.028,179,1.266,203,1.436,221,1.397,223,2.13,225,1.352,228,1.198,236,1.577,257,1.926,279,1.634,292,1.645,304,1.264,313,0.924,318,0.921,323,1.525,338,0.93,343,1.345,377,0.846,394,1.89,395,2.371,397,1.572,418,1.89,426,2.754,484,1.602,500,1.789,512,1.304,527,1.855,530,1.691,560,2.013,565,1.235,568,1.718,605,1.369,609,2.472,614,0.696,630,1.397,635,1.372,712,2.035,723,1.352,747,2.463,757,2.091,759,2.614,760,2.734,769,3.556,827,3.778,841,1.748,862,1.882,877,1.379,936,1.618,940,1.415,946,5.308,1059,2.117,1114,1.716,1239,5.223,1257,2.198,1308,4.422,1312,1.39,1363,2.16,1381,1.499,1446,2.213,1605,2.773,1754,2.55,1864,1.993,1953,2.057,1970,3.016,2061,1.277,2064,2.699,2429,4.228,2741,2.959,2816,3.016,2818,6.499,2819,3.016,2867,4.362,3917,2.406,3947,3.589,3948,3.234,3949,3.589,3950,3.33,3951,3.782,3952,3.234,3953,3.782]],["t/1815",[24,1.693,29,3.648,103,3.498,395,4.132,759,3.665,1530,6.291,2827,8.034]],["t/1817",[7,0.797,18,3.86,24,1.422,28,1.239,31,1.539,34,1.605,56,1.176,68,3.33,84,1.095,103,2.108,105,1.971,123,2.147,132,2.58,145,2.534,160,2.209,165,1.411,196,2.497,257,3.208,275,1.95,279,2.722,318,1.534,353,1.718,398,3.094,484,2.669,527,1.377,565,2.057,568,1.804,759,3.079,760,3.399,769,2.421,847,3.485,1114,2.858,1164,3.178,1214,4.08,1234,3.569,1257,5.104,1454,3.909,1487,4.2,1530,3.791,1633,2.969,1735,6.116,1782,2.838,1902,3.005,1918,5.032,2448,5.387,2600,5.406,2814,6.116,3514,4.842,3948,5.387,3950,5.547,3954,5.547,3955,5.387,3956,5.387,3957,5.739,5022,6.301]],["t/1819",[37,5.461,56,1.306,95,6.494,279,3.539,759,2.872,760,3.093,1257,6.084,1454,5.082,1530,4.929,1735,5.704,1754,3.482,1902,4.992,1918,5.209,2321,5.517,2816,6.533,2817,7.004,2818,5.407,2819,6.533,2821,6.533,2822,6.295,2827,6.295,2832,6.671,3955,7.004,3956,7.004,3960,7.774,3961,8.192]],["t/1821",[56,1.251,279,3.641,395,3.331,527,1.843,759,2.954,760,2.49,866,8.882,1170,3.589,1257,6.194,1408,4.733,1454,5.228,1530,5.071,1735,5.868,1902,5.083,1907,4.352,1918,4.194,2821,6.72,2822,6.476,3955,7.206,3956,7.206,3957,7.676,3962,7.997,3963,7.997,3964,7.997]],["t/1823",[16,1.862,84,1.283,254,3.218,409,5.072,760,2.957,855,7.053,1107,5.273,1381,3.967,1632,5.194,3965,9.498,3966,8.812]],["t/1825",[2,1.463,7,0.894,17,2.144,21,2.231,24,1.507,29,1.893,39,2.208,49,1.22,84,1.177,100,2.427,107,1.389,109,1.332,116,2.947,138,4.382,165,1.769,170,1.603,171,2.145,183,2.329,193,1.974,200,2.688,203,1.89,219,2.444,220,3.34,236,2.076,248,3.707,272,2.221,283,2.283,296,2.091,313,1.326,351,1.964,398,3.827,512,2.723,525,2.557,614,0.999,677,2.859,734,2.401,757,1.337,759,2.768,760,3.021,780,2.888,827,2.75,898,2.631,1181,2.816,1214,3.514,1266,1.48,1312,3.992,1360,3.093,1473,2.418,1493,3.452,1540,3.002,1550,4.038,1558,3.055,1698,5.876,1814,3.872,2032,2.284,2368,6.752,2370,4.943,2398,8.992,2485,3.394,2645,4.522,2789,3.694,2923,6.58,3629,3.394,3967,5.149]],["t/1827",[2,1.351,4,1.554,7,0.7,29,1.748,49,1.126,56,0.877,84,1.236,85,1.735,101,2.666,109,0.845,111,6.516,139,3.988,165,1.668,170,2.2,200,2.482,203,1.782,206,3.244,225,1.791,236,1.317,247,2.201,313,1.224,347,3.306,351,1.813,377,1.12,395,1.98,405,1.594,426,1.85,578,1.968,580,3.086,613,2.264,614,0.922,621,2.575,663,3.06,759,3.115,760,2.907,841,2.315,878,2.136,914,4.422,958,3.274,992,2.504,1164,2.527,1170,2.728,1243,3.673,1284,2.45,1312,3.615,1473,2.232,1527,3.306,1633,3.509,1754,2.129,1904,5.54,1971,3.727,2389,4.41,2395,5.98,2398,7.56,2636,5.098,2754,5.937,2837,7.32,2923,6.205,3924,4.079,3926,7.236,3927,4.079,3928,4.079,3929,7.236,3930,4.079,4004,3.727,5023,5.01,5024,9.838,5025,8.887,5026,5.01,5027,5.01,5028,5.01]],["t/1829",[2061,3.73]],["t/1831",[4,1.567,7,0.779,27,1.199,28,1.057,31,1.831,34,1.287,49,1.136,56,0.74,84,0.967,96,3.146,107,2.701,109,2.261,123,2.554,127,2.457,133,2.161,160,1.771,164,1.388,170,1.493,172,1.75,174,1.502,182,1.691,193,1.838,197,2.251,203,1.209,228,1.6,254,1.625,272,3.068,295,2.032,313,1.234,323,2.038,351,1.828,391,2.127,397,2.1,405,1.607,416,2.326,439,3.303,557,2.514,580,1.754,597,2.609,607,2.872,614,1.379,621,2.597,635,1.833,658,2.419,731,2.648,747,2.075,755,2.276,827,2.56,829,2.662,877,2.732,947,2.572,992,2.525,997,1.871,1009,2.326,1047,3.018,1117,2.733,1170,1.551,1181,2.622,1238,3.018,1266,2.043,1289,3.996,1416,3.606,1557,3.135,1753,3.478,1754,2.147,1801,4.271,1847,3.403,1870,3.847,1884,3.334,1898,2.662,1972,3.883,2677,3.952,3878,3.883,5029,5.053,5030,5.053]],["t/1833",[7,0.907,34,2.74,49,1.596,56,1.317,84,0.769,109,2.083,164,3.16,278,2.545,295,4.327,304,1.496,408,2.564,452,6.561,495,3.429,527,1.553,614,1.307,756,3.333,1170,2.926,1216,5.478,1266,1.936,1282,5.457,1558,3.999,1705,4.337,1870,2.472,1876,6.173,1882,4.183,3624,5.457,3968,6.072,3969,5.457,3970,6.739]],["t/1835",[7,0.531,27,2.183,34,1.715,40,3.548,56,1.034,84,0.729,98,2.366,107,1.724,109,2.213,111,4.938,127,3.016,164,1.85,170,1.99,179,3.077,193,2.449,200,3.336,203,1.612,295,2.708,313,1.645,320,3.863,322,2.106,353,1.836,447,3.186,452,4.635,505,2.791,527,2.011,614,2.072,683,2.52,818,3.381,878,2.872,940,2.52,1170,2.823,1171,5.37,1391,5.929,1408,2.99,1693,3.94,1870,4.41,1884,4.445,3971,8.954,3972,6.134]],["t/1837",[7,0.418,24,1.258,26,1.969,27,1.259,28,0.748,34,1.351,56,0.907,84,0.995,107,1.988,109,2.218,110,4.84,162,1.864,164,2.133,165,1.188,170,1.567,171,1.441,186,2.499,216,2.7,236,2.416,254,1.705,257,2.7,272,2.171,278,1.9,295,2.133,296,2.044,338,1.305,353,1.446,372,1.984,377,2.055,408,3.317,440,2.339,452,3.65,502,1.462,527,2.82,605,1.919,607,2.976,613,3.51,614,1.43,653,1.669,663,3.239,724,2.582,759,1.859,760,1.567,881,3.889,947,2.7,1170,2.384,1239,3.264,1245,3.562,1257,4.513,1289,2.145,1870,3.198,2608,3.946,2800,4.229,3974,5.033,3975,5.033,3976,4.669,3977,3.835,3978,4.669,3979,5.033,3980,4.669,3981,5.304]],["t/1839",[7,0.972,28,0.996,55,5.835,56,1.133,85,3.289,101,3.757,107,2.747,109,1.935,162,3.336,165,1.581,170,3.39,171,1.918,304,1.487,338,1.736,408,2.548,429,6.035,527,1.543,611,2.934,614,1.748,622,3.56,759,2.474,760,3.17,835,4.754,846,3.134,847,3.905,859,5.629,882,4.217,900,4.614,993,4.075,1042,4.247,1240,4.024,1330,4.805,1373,4.311,1540,3.905,1754,3,1870,2.457,4474,5.176,5014,6.429]],["t/1841",[7,0.784,27,2.039,29,2.13,41,2.769,56,0.603,96,3.8,107,2.911,109,2.193,127,2.817,160,3.782,171,1.658,179,3.326,198,4.183,246,2.308,256,3.214,293,4.867,408,2.203,444,2.646,445,3.85,527,2.578,603,5.219,615,3.571,635,2.215,734,2.701,803,3.437,880,2.628,1153,5.219,1164,3.079,1224,3.917,1245,2.02,1257,3.547,1266,2.711,1693,5.026,1870,3.46,1884,4.028,1945,4.069,2153,5.374,2369,5.792,3092,5.374,3817,3.883,3932,4.775,3976,5.374,3978,9.497,5031,8.591]],["t/1843",[2,0.914,7,0.267,21,0.958,26,1.258,28,0.478,29,1.182,31,0.828,33,3.132,35,2.36,41,1.537,49,1.791,56,1.143,84,0.948,85,1.174,89,1.265,96,1.935,105,1.06,106,1.242,108,2.837,109,2.139,112,2.721,113,1.236,123,1.155,140,2.153,160,1.188,170,1.001,171,1.492,174,1.007,179,1.838,182,1.134,193,1.233,196,1.343,203,1.658,228,1.073,236,0.891,262,2.215,275,1.049,277,1.742,295,1.363,304,0.714,313,0.828,316,2.448,318,0.825,322,1.718,336,2.819,338,0.834,343,1.205,353,0.924,359,1.494,372,1.268,377,1.549,391,2.916,398,1.935,405,1.078,408,1.983,444,1.469,491,1.021,502,0.934,509,1.92,527,1.201,553,1.795,568,0.97,578,1.332,605,1.226,613,1.532,614,2.131,623,2.631,658,1.623,696,2.388,723,1.211,756,1.591,828,2.291,876,2.307,940,1.268,997,2.565,1040,2.333,1123,2.418,1170,3.554,1238,2.024,1289,3.542,1434,1.886,1558,3.093,1588,3.087,1693,1.983,1705,2.07,1801,3.132,1870,4.439,1876,3.557,1884,2.237,1918,2.734,2026,1.733,2275,2.604,2297,1.83,2411,2.485,2600,2.086,2636,3.151,2837,2.522,3652,3.465,3734,2.824,3980,2.983,3991,3.626,4001,3.216,4002,3.216,4003,3.216,4004,2.522,4005,2.702,4006,3.216,4007,3.216,4008,3.216,4009,5.923,4012,3.216,4013,3.216,4014,3.216,5032,3.389,5033,3.389]],["t/1845",[49,2.23,84,1.075,109,2.119,336,4.038,391,4.178,614,2.169,1289,4.014,1801,5.657,1870,3.454,2411,7.277]],["t/1847",[4,1.29,7,0.509,24,1.283,28,1.509,34,2.018,56,1.228,84,0.45,96,1.464,98,1.461,107,1.654,108,3.337,109,1.865,132,1.221,161,2.83,164,1.142,171,1.129,182,1.391,207,1.697,221,1.535,254,2.549,282,2.261,304,1.362,318,1.012,343,1.479,353,1.134,395,1.643,476,2.301,480,2.83,482,1.859,484,1.761,502,1.146,517,2.52,527,2.241,530,1.859,578,1.634,580,1.444,599,2.137,614,0.765,653,1.309,658,4.28,707,2.313,741,2.692,757,1.024,880,2.783,945,2.168,953,1.682,992,2.078,997,4.446,1037,1.585,1060,2.016,1066,2.051,1107,2.19,1170,2.433,1245,2.622,1284,2.033,1381,1.648,1408,2.869,1737,1.568,1870,1.447,2026,2.127,2032,2.72,2632,2.483,2633,2.287,2895,2.559,3644,1.531,3715,4.965,3817,4.11,3844,7.869,3985,3.555,3986,3.945,3987,3.555,4606,6.978,5034,6.462,5035,6.462,5036,6.462,5037,6.462,5038,6.462,5039,6.462,5040,6.462,5041,6.462,5042,6.462,5043,6.462,5044,6.462,5045,6.462,5046,6.462,5047,6.462,5048,6.462,5049,6.462,5050,4.158]],["t/1849",[12,2.073,17,2.032,24,1.229,25,1.646,27,2.731,28,1.569,30,3.216,31,1.256,33,2.931,40,2.708,56,0.891,84,0.822,85,1.781,98,1.807,103,2.54,106,3.307,107,2.721,109,1.94,115,2.367,132,1.51,138,2.073,164,1.412,165,1.152,171,1.397,174,1.528,179,1.72,182,1.72,183,2.207,197,2.291,199,2.536,236,1.352,254,2.441,256,1.924,289,2.032,302,2.367,304,1.599,313,1.256,318,1.252,369,3.19,413,5.736,416,2.367,489,3.427,502,1.418,507,2.796,513,2.423,525,2.423,527,1.124,550,3.546,590,2.766,614,0.946,615,3.008,623,2.462,631,3.36,755,2.315,809,3.539,880,2.214,950,6.282,992,2.57,997,2.81,1047,3.071,1111,3.36,1139,2.751,1238,3.071,1245,2.511,1303,3.071,1352,2.472,1353,4.526,1382,5.952,1408,3.37,1474,3.951,1737,1.939,1845,3.049,1870,1.789,1880,4.396,3053,5.112,3056,4.186,3644,1.894,3985,4.396,3990,4.526,5007,3.951,5051,5.141]],["t/1851",[27,1.931,28,1.623,56,1.029,99,5.131,109,1.757,132,2.389,162,2.859,170,2.404,171,2.21,236,2.139,304,1.714,336,3.31,338,2.001,550,3.197,614,1.498,635,2.952,636,4.581,724,3.961,740,3.677,877,2.966,880,3.503,997,3.012,1139,4.353,1382,6.869,1508,4.5,1737,3.068,1844,6.365,1870,3.627,1880,6.956,3644,2.997,5021,6.364]],["t/1853",[4,1.884,5,2.139,7,0.781,27,2.795,34,1.546,49,1.365,50,3.553,56,0.979,68,2.302,84,0.658,98,2.134,109,1.985,126,4.133,164,2.351,171,2.325,172,4.279,203,1.454,207,4.393,240,3.321,256,3.202,272,2.486,279,2.624,282,3.303,295,2.442,304,1.28,313,2.091,316,2.706,343,3.044,353,1.656,404,4.168,408,2.192,476,1.763,480,4.133,482,2.715,489,6.607,531,3.681,578,2.386,599,4.399,614,1.118,707,3.379,734,2.687,992,3.035,1289,3.462,1367,2.944,1671,3.34,1870,2.113,3652,3.83,3921,4.518,3987,5.192,3991,6.542,3992,5.762]],["t/1855",[7,0.505,16,0.765,24,0.666,27,1.521,28,0.904,29,1.435,31,1.005,34,1.632,39,1.674,46,1.705,49,1.44,56,1.278,84,0.694,92,2.373,93,3.335,100,2.866,102,2.594,108,4.066,109,1.721,112,2.038,132,1.208,163,1.63,165,0.922,171,1.118,172,4.254,173,2.201,179,1.376,182,1.376,183,1.766,193,1.496,207,3.213,221,1.519,254,1.323,256,1.539,261,2.085,276,3.9,279,1.777,296,1.585,304,1.351,316,1.833,318,1.002,336,1.674,343,1.463,377,1.761,391,1.732,395,1.626,408,1.485,476,2.58,502,1.134,517,2.494,531,2.494,553,3.394,578,1.616,599,2.114,614,0.757,620,2.276,653,1.295,662,2.391,728,2.864,734,1.82,741,2.664,751,2.375,755,1.853,757,1.014,818,2.065,880,2.76,945,2.145,986,4.659,988,3.587,998,2.64,1013,3.061,1037,1.568,1060,1.995,1107,2.167,1164,2.075,1221,3.061,1284,2.012,1381,1.63,1509,3.517,1689,3.359,1870,2.231,1884,2.715,1953,2.237,1980,4.462,2026,2.104,2032,2.698,2118,2.899,2297,1.37,2321,2.77,2353,2.457,2632,2.457,2633,2.263,2867,2.407,2885,3.517,2895,2.532,2899,3.517,3042,2.407,3652,4.965,3817,4.077,3921,3.061,3991,4.23,3993,3.622,3994,3.35,3995,3.622,4849,3.218,5052,4.114]],["t/1857",[2,1.512,5,1.975,7,0.868,12,2.262,27,1.92,28,1.618,30,3.508,49,1.26,56,0.799,84,1.027,107,1.436,109,1.599,113,3.459,170,2.389,171,2.197,172,3.596,203,1.342,224,3.426,236,1.475,246,3.058,304,1.182,318,1.365,336,2.282,338,1.379,351,2.029,391,2.361,432,3.537,505,2.324,511,1.788,525,2.643,552,3.357,597,2.896,599,2.882,614,1.911,636,3.158,724,2.731,725,5.838,807,3.665,832,3.777,878,2.391,880,2.415,906,3.399,953,3.271,986,2.939,988,3.139,1012,3.177,1139,4.328,1178,4.112,1380,3.537,1446,3.281,1508,3.102,1737,2.115,1844,3.425,1864,2.954,1870,2.815,3004,4.673,3057,4.055,3644,2.066,3652,3.537,3910,4.387,3991,3.701,5009,7.168,5021,4.387]],["t/1859",[0,3.09,4,1.66,5,2.754,7,0.852,24,0.867,27,2.192,28,1.103,31,1.308,34,1.363,49,1.203,56,0.772,84,0.847,109,1.822,113,1.952,123,1.824,140,2.097,162,1.881,164,1.47,170,2.31,171,2.509,172,3.199,173,4.942,174,2.324,184,4.045,193,2.843,203,1.281,228,1.695,236,1.407,255,3.604,261,2.712,275,1.657,289,2.115,296,2.063,304,1.128,320,3.07,323,2.159,336,2.178,338,1.317,353,1.459,377,1.197,426,1.977,432,3.376,484,3.312,505,2.218,550,2.103,568,1.533,596,2.41,614,1.989,632,2.879,636,3.014,724,2.606,725,2.725,728,3.727,755,3.521,802,2.165,828,2.232,877,1.952,880,2.305,934,2.553,953,2.165,1012,5.232,1066,2.64,1139,4.183,1178,3.924,1216,2.725,1508,2.961,1737,2.018,1844,3.269,1870,2.721,3003,4.268,3033,6.234,3644,1.972,3652,3.376,3921,3.982,3991,3.532,4511,4.268,5008,5.732,5009,4.045,5021,4.187]],["t/1861",[24,1.248,28,1.088,34,1.963,56,0.994,107,2.869,109,1.697,113,2.811,193,2.803,246,3.806,285,3.184,295,3.099,304,1.624,338,1.896,351,3.642,477,4.572,527,2.2,576,4.54,605,2.789,614,1.419,621,3.961,755,4.533,765,4.572,880,3.319,953,3.118,992,3.853,1181,5.223,1245,2.55,1540,4.264,1737,2.907,1863,3.224,1870,3.503,2372,5.138,2851,5.773,3644,2.839,4474,5.652,5015,6.277]],["t/1863",[28,1.389,56,0.972,107,2.52,109,1.659,304,2.074,527,2.152,755,4.432,1245,3.256,1737,3.711,1863,4.117,1870,3.425,2851,5.645,3644,3.625,5015,8.014]],["t/1865",[7,0.66,16,1.096,24,0.954,25,1.886,27,1.988,28,1.375,31,2.046,34,1.5,41,2.672,56,0.827,84,1.056,107,2.496,109,1.892,113,2.148,123,2.854,127,3.679,131,2.672,132,2.863,162,2.07,165,1.319,171,1.6,193,2.142,213,2.397,225,2.105,256,2.204,295,2.368,302,2.712,304,1.241,318,1.434,377,1.317,433,3.4,472,2.868,491,1.775,524,3.625,536,1.754,557,2.931,565,3.183,614,1.794,615,3.446,628,3.042,630,3.093,635,2.137,810,2.615,865,4.696,995,4.009,999,3.203,1169,3.746,1209,5.185,1303,3.518,1314,4.009,1397,3.57,1477,4.382,1737,2.221,1754,4.513,1807,4.696,1870,2.05,1875,4.908,1980,5.832,1998,3.423,2787,3.746,3644,2.17,5016,7.938]],["t/1867",[7,0.731,16,1.268,27,2.501,28,1.596,34,1.735,41,3.091,56,1.04,84,1.004,107,2.373,109,1.994,113,2.484,127,4.002,131,3.091,132,2.722,165,1.526,193,2.478,225,2.435,256,2.549,302,3.137,304,1.436,313,1.664,318,1.659,338,1.676,433,3.933,472,3.317,524,4.193,536,2.029,557,3.39,565,3.026,615,3.986,630,2.516,635,2.472,995,4.637,1169,4.334,1303,4.07,1314,4.637,1737,2.569,1754,4.478,1875,5.677,1980,6.454,2787,4.334,3644,2.51,5016,5.548]],["t/1869",[28,1.424,56,0.997,109,1.702,127,3.311,304,2.127,755,4.547,1737,3.807,1870,3.514,2851,5.791,3644,3.719,5053,9.195]],["t/1871",[28,1.4,56,0.98,107,2.541,109,1.987,172,3.438,304,2.091,318,2.416,338,2.441,502,2.737,1737,3.743,3644,3.656,5017,8.486]],["t/1873",[28,1.424,56,0.997,172,3.497,254,3.246,304,2.127,338,2.483,502,2.784,1737,3.807,1870,3.514,3644,3.719,5018,9.195]],["t/1875",[28,1.412,56,0.988,109,1.688,172,3.467,304,2.109,502,2.76,755,4.508,1737,3.775,1870,3.484,2851,5.742,3644,3.687,5054,9.117]],["t/1877",[28,1.4,56,0.98,107,2.541,109,1.987,254,3.191,304,2.091,338,2.441,502,2.737,997,3.675,1737,3.743,3644,3.656,5019,8.486]],["t/1879",[28,1.424,56,0.997,254,3.246,304,2.127,338,2.483,502,2.784,997,3.738,1737,3.807,1870,3.514,3644,3.719,5020,9.195]],["t/1881",[28,1.412,56,0.988,109,1.688,304,2.109,502,2.76,755,4.508,997,3.706,1737,3.775,1870,3.484,2851,5.742,3644,3.687,5055,9.117]],["t/1883",[16,1.651,28,1.252,84,0.961,107,2.818,109,1.856,170,2.622,196,3.517,246,3.355,304,1.87,338,2.183,405,2.823,408,3.974,527,2.407,614,1.634,880,4.74,1237,5.976,1737,3.347,1870,3.088,2800,7.076,3644,3.269,3879,6.603,3880,5.8]],["t/1885",[2,1.505,7,0.746,12,2.251,16,1.039,28,1.137,41,2.532,56,1.165,84,1.122,107,1.429,109,2.076,132,2.367,170,2.381,179,1.868,196,2.212,203,1.336,225,1.995,226,2.706,246,3.048,257,2.841,300,3.383,304,1.698,322,1.746,338,1.982,377,1.802,395,2.206,408,2.909,447,2.64,605,2.02,614,1.484,705,3.435,709,3.648,765,3.31,818,5.516,883,4.092,914,2.777,1169,3.55,1170,3.373,1266,1.522,1343,6.891,1539,3.72,1718,3.72,1870,4.206,1873,5.219,1882,4.747,1904,7.039,2062,5.084,3817,3.55,4004,4.153,4474,4.092,4804,4.545,4815,8.616,5056,5.581,5057,5.581,5058,5.581,5059,5.581]],["t/1887",[56,0.988,107,2.563,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1408,4.444,1737,3.775,2296,7.339,3644,3.687]],["t/1889",[56,0.988,127,3.282,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1408,4.444,1737,3.775,2295,7.448,3644,3.687]],["t/1891",[56,0.98,261,5.029,304,2.091,353,2.706,536,3.509,755,4.47,946,4.57,1408,4.407,1737,3.743,1902,4.733,3644,3.656,5060,8.486]],["t/1893",[56,1.118,84,1.007,107,2.38,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,1046,5.296,1266,2.535,1289,3.76,1622,5.965,1792,7.085,2296,6.816,3999,7.948]],["t/1895",[56,1.118,84,1.007,127,3.048,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,1046,5.296,1266,2.535,1289,3.76,1622,5.965,1792,7.085,2295,6.917,4000,7.948]],["t/1897",[56,1.113,84,0.999,140,3.614,196,3.655,304,1.943,536,3.626,614,1.698,755,4.154,946,4.246,1046,5.271,1266,2.515,1289,3.73,1622,5.918,1792,7.051,1902,4.398,5060,7.885,5061,8.4]],["t/1899",[24,0.781,26,1.791,28,0.681,29,1.683,31,1.179,48,1.957,49,1.084,56,1.113,105,1.509,107,1.853,109,1.628,112,3.586,123,1.644,127,1.582,140,1.89,162,1.695,165,1.622,172,2.508,173,2.581,174,2.152,193,1.755,203,1.733,206,3.124,318,2.116,322,4.121,336,1.963,338,1.187,377,1.079,391,2.031,395,1.906,398,3.401,502,1.33,527,1.055,593,2.379,613,2.181,614,1.778,623,2.31,755,2.173,947,2.456,997,3.575,1033,3.069,1123,5.166,1139,2.581,1170,1.481,1289,1.951,1633,4.094,1801,2.75,1870,3.601,1882,2.842,1898,2.541,1918,4.805,2026,3.703,2297,2.411,2600,4.456,2851,2.767,3652,4.566,3991,4.777,4474,5.308,5008,5.308,5015,5.895,5016,5.895,5017,6.189,5019,6.189,5053,6.593,5054,6.593,5055,6.593,5062,8.689]],["t/1901",[2061,3.73]],["t/1903",[7,0.958,15,3.812,572,5.536,846,5.394,3726,6.77]],["t/1905",[4,2.489,7,0.814,27,1.905,49,2.322,56,0.792,84,0.869,100,3.588,103,3.456,144,3.842,236,2.11,243,4.388,283,2.32,394,4.011,401,4.367,405,3.286,437,3.766,481,4.793,492,6.277,511,2.558,683,3.002,723,2.868,736,3.422,877,2.926,2225,5.499,2297,2.673,3513,8.303,3530,5.726,3531,5.726,3532,6.399,3726,6.689]],["t/1907",[7,0.889,27,2.189,49,2.073,56,0.911,100,4.124,243,5.044,246,3.487,277,4.74,401,3.525,405,3.586,511,2.94,723,3.296,841,4.262,934,4.398,1754,3.92,3533,6.086,3726,7.3,4015,10.698]],["t/1909",[7,0.761,17,2.858,27,1.717,39,2.943,56,0.714,84,0.783,100,4.856,103,3.229,106,2.651,144,3.463,228,2.291,243,3.956,394,3.615,405,3.07,410,4.773,437,3.395,511,2.306,527,1.581,683,2.706,723,3.449,760,3.209,769,2.78,802,2.926,877,2.637,1245,2.393,1376,3.615,1754,3.074,1759,6.642,2225,5.137,2297,3.214,2789,4.923,2814,5.036,2815,5.036,2867,4.231,3483,7.695,3726,6.249,4016,7.963,4017,9.561,4018,6.367,4019,6.367]],["t/1911",[7,0.739,17,2.74,27,1.646,39,2.821,56,0.685,84,0.751,100,4.754,103,3.139,106,2.541,109,1.921,144,3.32,170,2.049,228,2.196,243,3.792,394,3.466,405,2.984,410,4.576,437,3.254,511,2.21,527,1.516,683,2.594,723,3.353,760,3.141,769,2.665,802,2.805,877,2.528,1245,2.294,1320,5.967,1376,3.466,1754,2.947,1759,6.456,2225,4.993,2297,3.125,2789,4.72,2814,4.828,2815,4.828,2867,4.056,3483,7.48,3726,6.074,4016,5.084,4017,9.359,4018,6.104,4019,6.104,5010,8.544]],["t/1913",[7,0.674,10,4.625,21,2.416,24,1.385,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,857,3.879,1754,4.572,2225,4.551,2282,5.699,2297,2.848,3726,6.965,4020,10.207]],["t/1915",[7,0.674,10,4.625,21,2.416,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,170,2.526,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,857,3.879,1754,4.572,2225,4.551,2282,5.699,2297,2.848,3726,6.965,5063,10.757]],["t/1917",[2061,3.73]],["t/1919",[7,0.811,15,3.276,16,1.211,21,1.84,24,1.455,26,2.417,28,0.919,34,1.658,58,4.708,70,7.343,84,0.973,98,2.288,99,4.106,106,3.293,145,3.613,158,6.016,164,1.788,175,5.627,203,1.558,242,4.588,246,2.462,304,1.372,343,3.196,353,2.45,381,3.08,401,4.6,402,5.093,408,4.202,410,4.297,412,4.844,413,4.92,416,2.998,453,4.178,532,3.395,536,1.939,572,3.447,605,2.356,744,3.711,794,4.533,854,3.465,857,2.953,877,2.374,953,2.633,1408,2.891,3020,4.708,3106,5.093,4103,6.178,4104,6.178]],["t/1921",[0,3.177,2,1.484,7,0.74,16,1.484,24,1.292,28,0.776,33,3.137,34,1.401,41,2.496,56,0.927,84,0.596,97,3.567,98,1.934,131,2.496,134,2.738,138,4.595,153,2.815,162,1.934,175,3.009,220,3.387,221,2.032,245,3.287,304,1.681,316,2.452,332,4.14,353,2.559,359,2.426,365,5.536,394,2.75,396,6.24,403,3.531,407,3.606,408,2.88,409,2.788,438,5.912,439,5.214,440,3.517,441,5.942,442,4.585,443,3.878,444,2.385,445,3.471,446,3.5,447,2.603,448,5.012,449,4.91,450,7.266,451,4.585,452,3.787,453,5.119,454,5.012,459,4.304,484,2.331,536,1.639,580,2.77,614,1.013,637,3.531,742,4.989,920,3.198,938,4.304,959,3.668,981,3.414,1314,3.746,1399,3.241,2874,4.844,4105,4.481,4106,5.222,4107,4.705,4108,4.304]],["t/1923",[7,0.653,16,1.08,21,1.639,24,1.342,26,2.154,28,0.819,29,2.024,31,1.417,44,4.961,56,0.818,84,0.898,97,3.706,105,1.815,132,1.704,138,4.253,153,2.968,175,3.173,189,3.105,199,2.862,224,4.468,239,3.328,294,3.121,340,3.867,353,2.636,365,4.199,377,1.298,394,2.9,402,7.562,403,3.723,405,2.636,438,4.301,439,5.417,441,4.507,442,4.835,443,4.089,451,4.835,452,3.993,455,8.587,456,10.159,466,4.089,496,3.544,504,3.517,557,2.887,614,1.068,742,5.183,744,3.307,757,1.43,778,3.659,807,2.242,898,2.813,902,4.04,981,3.6,1399,3.418,1632,3.011,1998,3.372,3479,5.285,4105,4.725,4107,4.961,4108,4.539,4109,5.506,4110,4.385,4111,4.961,4112,5.506,4113,7.864,4114,5.506,4115,5.506,4116,5.506,4117,5.506]],["t/1925",[5,3.3,24,1.843,49,2.106,70,6.687,106,4.172,286,5.679,304,1.974,351,3.391,404,4.562,408,4.109,528,4.936,536,3.39,621,4.816,744,5.341,804,8.011,4118,8.534]],["t/1927",[2,2.375,24,1.774,34,2.79,106,3.228,126,4.252,175,4.817,254,3.523,256,3.295,304,1.856,353,2.402,408,4.505,409,4.463,447,4.167,460,8.022,461,4.929,462,8.022,463,5.299,464,7.53,465,6.132,466,6.206,629,5.602,2784,7.339]],["t/1929",[2,1.892,7,0.843,16,1.306,24,1.136,49,2.404,84,1.024,105,2.195,106,2.571,121,4.105,153,3.589,197,3.126,228,2.223,236,1.845,278,2.514,291,4.133,302,3.231,336,2.855,372,3.538,377,1.569,408,3.413,449,4.319,461,3.927,478,4.253,532,3.659,578,3.716,596,3.16,605,2.539,614,1.292,670,4.078,731,3.678,744,4,764,4.725,828,2.926,898,3.402,1006,4.837,1133,6.157,1312,2.578,1441,4.222,1801,4,1898,3.696,1945,4.677,2232,4.945,3012,5.847,3175,5.999,4105,5.714,4478,6.391,4918,6.391,5064,6.177,5065,7.017,5066,7.017]],["t/1931",[2061,3.73]],["t/1933",[7,0.65,12,3.327,28,1.776,84,0.894,89,3.079,105,2.58,107,2.112,174,2.452,228,2.613,372,3.087,377,1.845,390,3.983,397,3.429,405,4.005,426,3.883,496,5.039,511,3.352,536,2.457,558,3.689,622,4.161,723,3.758,940,3.087,1473,3.676,1864,6.098]],["t/1935",[16,2.273,28,1.312,183,3.99,322,2.907,405,3.603,426,3.433,495,5.469,511,2.963,670,5.402,1052,6.634,1163,5.635,1191,6.816,1998,5.402,3876,6.917,3877,8.821,5067,9.296,5068,9.296]],["t/1937",[405,3.326,511,3.333,757,2.576,2627,10.457,5069,10.455]],["t/1939",[7,0.739,28,1.322,49,2.106,84,1.015,109,2.15,183,4.021,405,2.981,409,4.748,426,3.46,511,2.987,622,4.726,1170,2.876,1289,3.79,1753,7.834,1870,3.261,5070,9.37]],["t/1941",[130,5.755,183,4.333,338,2.483,405,3.211,511,3.218,724,4.915,880,5.125,3880,7.779,5071,10.096]],["t/1943",[163,3.967,183,4.296,277,6.086,405,3.184,511,3.191,622,5.049,723,3.578,1037,4.807,5072,10.009]],["t/1945",[28,1.4,107,3.328,109,1.674,405,3.157,426,3.665,511,3.164,820,6.459,5073,9.925]],["t/1947",[16,1.703,34,2.33,121,5.353,254,2.942,353,3.059,397,3.804,405,2.911,408,4.566,484,3.877,511,2.917,581,6.617,712,4.923,1632,4.749,1874,6.371,3884,11.514,3885,8.684,5074,9.151]],["t/1949",[16,1.801,343,3.443,351,3.503,405,3.079,432,7.321,476,2.81,511,3.086,583,5.911,635,3.512,757,3.064,2485,6.054,3887,7.718,5075,9.679]],["t/1951",[49,2.106,109,1.58,130,5.341,171,2.546,172,3.943,174,2.785,183,4.021,377,2.096,405,2.981,426,3.46,505,3.883,511,2.987,580,3.952,636,5.276,724,4.562,997,4.215,5076,9.37]],["t/1953",[24,1.554,145,3.86,228,3.041,405,3.054,511,3.06,760,3.798,769,4.761,814,4.134,934,4.578,1696,6.106,1782,4.323,5077,9.6]],["t/1955",[24,1.649,405,3.239,511,3.246,690,6.317,760,3.009,769,4.882,1696,6.477,5078,10.183]],["t/1957",[16,1.801,26,4.309,121,5.663,183,4.154,322,3.027,405,3.079,407,4.375,408,3.494,511,3.086,750,6.821,757,2.86,1237,6.518,5079,9.679,5080,9.679]],["t/1959",[4,2.883,172,3.22,183,3.99,193,3.381,394,4.646,405,2.957,407,4.202,511,2.963,552,3.29,725,5.767,822,5.553,981,5.767,988,5.203,1114,4.217,1381,3.684,3876,6.917,3892,10.317,3893,8.467,5081,9.296]],["t/1961",[5,2.715,7,0.883,16,1.873,25,2.468,28,1.088,56,0.761,84,1.09,105,2.411,125,4.394,133,3.297,144,3.691,145,3.099,164,2.117,174,2.291,196,3.055,198,4.9,228,2.442,294,4.147,372,2.884,390,3.722,458,4.34,511,4.03,536,2.295,683,2.884,807,4.591,854,4.103,940,3.766,1266,2.102,1632,4,1779,5.191,2683,6.194,3894,7.314]],["t/1963",[5,3.056,16,2.02,24,1.918,28,1.532,84,0.94,132,2.548,134,4.318,162,3.049,239,4.977,255,5.844,367,5.527,407,3.922,511,2.766,647,3.255,807,4.797,832,5.844,907,5.568,934,4.138,1364,6.274,2677,6.787,5082,8.677,5083,8.677]],["t/1965",[5,1.728,16,2.108,24,1.577,25,1.57,26,2.721,27,1.164,28,1.599,31,1.198,46,2.033,49,1.102,109,0.827,163,1.944,170,2.166,193,2.666,200,2.43,201,3.731,224,2.078,225,1.753,240,2.682,281,2.32,289,3.848,336,1.996,338,1.206,405,3.097,407,2.217,495,3.539,511,3.924,552,1.736,558,2.193,568,1.405,632,2.639,635,2.659,694,2.667,723,1.753,725,2.497,757,1.209,769,3.373,807,3.762,812,3.269,816,2.779,1051,6.743,1139,2.625,1446,2.869,1473,5.19,1798,2.729,1799,5.045,2668,3.456,3007,3.769,3401,3.707,3876,3.65,3903,6.453,3910,3.837,5084,4.905,5085,7.33,5086,4.905,5087,4.905,5088,4.905,5089,7.33,5090,4.905,5091,7.33,5092,4.905,5093,7.33,5094,4.905,5095,7.33,5096,4.905,5097,4.905,5098,7.33,5099,4.905,5100,4.905,5101,7.33,5102,4.905,5103,7.33,5104,4.905]],["t/1967",[16,1.519,17,1.722,24,1.762,25,1.395,26,2.488,27,1.591,28,1.688,36,1.922,41,1.977,49,1.835,84,0.472,89,2.501,103,1.458,109,1.377,132,1.28,145,2.695,162,2.355,169,2.383,203,1.043,240,2.383,281,2.062,285,1.801,286,2.642,287,4.782,289,2.648,367,2.219,377,1.499,394,2.178,405,2.597,407,1.97,408,3.309,409,2.208,432,2.749,476,1.945,511,3.815,512,2.311,527,0.953,575,3.183,580,1.513,635,2.431,653,1.372,662,2.533,693,2.682,724,2.122,731,2.284,736,1.858,751,2.516,757,1.074,760,1.98,807,2.59,878,1.858,880,2.886,893,2.516,1117,2.357,1139,2.332,1245,1.442,1315,2.397,1473,5.332,1557,2.704,1696,2.772,1703,5.73,2678,3.726,2700,3.549,3108,3.475,3880,2.849,3887,3.475,5105,6.702,5106,4.358,5107,6.702,5108,4.358,5109,6.702,5110,4.358,5111,6.702,5112,4.358,5113,6.702,5114,4.358,5115,6.702,5116,4.358,5117,6.702,5118,4.358,5119,4.358,5120,4.358,5121,6.702,5122,4.358,5123,8.165,5124,4.358,5125,4.358,5126,4.358,5127,6.702,5128,4.358]],["t/1969",[2061,3.73]],["t/1971",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,891,8.588,1312,4.182,2297,3.121,2683,5.767]],["t/1973",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,877,2.966,1328,4.86,1389,4.825,1531,5.423,2297,2.71,2447,5.538,3690,7.162,4119,10.474,4120,7.41,4121,7.41,4122,7.41,4123,9.492]],["t/1975",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,863,8.832,873,9.359,888,5.609,891,7.741,1138,7.196,1789,7.754,2683,5.861]],["t/1977",[2061,3.73]],["t/1979",[1,2.672,7,0.799,12,1.854,16,0.855,27,1.091,28,0.985,34,1.171,49,1.569,68,1.743,84,0.914,92,3.125,93,4.293,98,2.453,99,2.899,109,1.589,116,1.716,127,1.508,162,1.615,165,1.564,171,2.293,172,4.352,173,3.735,179,1.538,193,1.672,203,1.1,207,2.849,224,2.957,228,1.456,236,1.209,257,2.34,272,2.858,276,4.181,296,1.772,304,0.969,318,1.119,332,3.623,336,1.871,337,2.141,343,1.635,377,1.028,439,3.005,445,2.899,476,2.026,491,1.386,502,1.268,527,1.005,552,3.776,568,1.317,580,1.596,601,2.158,607,2.675,614,0.846,620,3.861,686,3.596,707,2.558,728,5.876,734,3.089,769,1.767,802,1.86,810,2.041,816,2.604,927,3.474,986,2.41,988,3.907,998,2.95,999,2.5,1006,2.352,1013,3.421,1040,3.164,1042,2.766,1066,2.267,1221,3.421,1266,1.254,1362,3.324,1363,3.393,1365,4.366,1397,2.787,1805,3.831,1844,2.808,1870,1.6,1953,3.797,2106,3.931,2118,3.24,2353,2.746,2513,4.187,2905,3.281,3652,2.899,3796,3.533,3857,3.201,3991,3.034,3997,3.034,4050,3.744]],["t/1981",[0,2.096,17,1.435,19,1.617,56,1.245,68,2.751,84,0.629,85,1.257,100,2.596,105,1.135,109,0.612,112,5.389,123,1.237,172,3.35,193,2.112,217,3.31,225,1.297,243,1.985,304,1.224,318,0.884,322,2.593,324,3.075,377,0.812,387,2.528,391,1.528,408,2.096,491,1.75,502,1.001,552,3.594,609,2.373,641,3.025,725,2.956,726,5.42,734,2.569,747,1.491,765,2.153,802,1.468,828,2.421,829,1.912,833,2.558,878,1.548,929,6.583,952,6.263,953,3.668,954,3.87,956,3.306,957,3.025,958,3.795,959,7.96,960,3.196,961,3.306,962,3.996,963,5.288,964,3.306,965,3.662,966,2.743,967,3.306,968,5.288,969,3.306,970,3.306,971,4.616,972,3.306,973,2.528,974,3.306,975,3.306,976,3.306,977,3.306,978,2.445,979,2.396,980,3.996,981,2.252,982,3.306,983,3.306,984,3.306,985,3.306,986,5.069,987,3.306,988,5.904,989,3.306,990,3.306,991,3.306,1009,4.453,1123,4.143,1363,2.091,1367,2.815,1417,2.396,1737,3.127,2438,7.093,3553,2.743,3643,5.288,4125,9.636,4126,3.104]],["t/1983",[2,1.77,5,0.725,7,0.814,8,1.107,17,0.813,21,1.359,22,1.277,24,0.584,26,1.785,27,1.37,28,1.531,31,1.41,49,1.081,56,0.356,68,1.823,84,0.711,85,0.713,89,1.794,93,0.872,94,1.113,96,0.725,98,0.723,103,0.688,105,1.127,106,2.116,107,2.114,109,1.462,116,0.768,124,1.298,126,0.993,132,0.604,134,2.392,135,1.173,138,2.328,140,1.412,144,0.985,153,1.843,163,0.816,165,1.293,169,1.125,170,1.421,171,1.306,172,2.86,173,5.158,174,1.429,179,1.206,184,1.555,185,1.416,189,1.928,190,2.471,192,1.212,193,3.827,203,0.863,224,3.059,228,1.142,232,0.748,236,1.518,256,0.77,275,0.637,288,2.378,289,2.593,292,0.895,296,0.793,304,0.434,313,1.41,317,1.028,318,0.877,322,1.127,323,1.453,336,0.837,337,1.678,338,0.886,339,1.038,340,1.372,343,1.282,347,1.358,351,0.745,372,1.348,377,0.46,383,1.433,391,2.024,395,1.424,401,0.786,426,2.132,433,1.188,441,1.119,458,1.159,491,0.62,502,0.994,505,0.853,509,1.166,513,0.97,541,1.266,550,2.268,552,3.069,553,2.546,555,0.973,557,1.024,558,0.92,565,1.885,568,1.879,597,1.063,601,1.692,610,1.581,611,1.498,614,1.329,621,1.058,622,1.038,630,0.76,632,5.033,635,1.744,636,1.159,638,2.168,647,0.772,686,1.61,691,1.982,712,1.107,719,1.238,725,1.048,741,1.332,743,2.378,756,0.966,757,0.507,769,0.791,772,4.331,774,1.113,802,0.832,806,1.61,807,2.536,810,1.6,812,2.402,816,1.166,822,1.229,827,2.436,828,1.503,829,1.084,857,0.933,877,2.105,878,0.877,880,0.886,906,2.184,907,1.32,919,1.416,934,0.981,947,1.048,949,1.531,951,1.32,952,2.334,986,3.785,988,3.673,999,1.96,1008,1.247,1009,0.947,1012,4.913,1055,1.358,1066,1.015,1074,1.555,1122,1.287,1123,1.468,1164,1.818,1169,1.309,1223,1.166,1274,2.201,1290,1.145,1303,2.153,1360,1.173,1362,2.606,1363,1.298,1365,1.287,1376,2.403,1379,1.581,1397,1.247,1499,1.953,1557,2.236,1605,1.509,1696,1.309,1699,2.236,1703,3.081,1737,1.359,1782,2.6,1864,2.533,1870,2.009,1898,1.084,1915,1.676,2092,1.152,2353,1.229,2488,2.914,2645,1.715,2687,1.812,2782,1.358,2787,1.309,2905,1.468,2963,1.468,2996,1.759,3019,1.759,3040,1.759,3644,1.328,3646,3.282,3652,1.298,3991,1.358,4127,1.953,5008,1.509,5129,2.058]],["t/1985",[2061,3.73]],["t/1987",[7,0.847,49,2.414,228,3.403,278,3.849]],["t/1989",[7,0.951,34,2.043,49,2.568,56,0.792,84,1.119,103,2.685,203,1.921,228,2.542,278,2.875,283,2.32,332,4.164,382,4.978,393,3.941,401,3.067,408,2.897,513,3.781,580,2.786,614,1.477,723,4.083,1001,6.166,1202,5.442,1376,6.03,1504,5.655,1511,7.467,2708,5.802,4021,7.309,4022,6.277]],["t/1991",[2,1.463,7,0.622,34,1.382,39,2.208,49,1.775,56,0.78,68,2.994,84,1.107,100,2.427,113,2.879,144,2.599,165,1.216,186,2.557,203,2.87,213,2.208,225,1.94,226,2.631,233,1.964,302,2.499,313,1.326,322,2.47,332,2.816,373,3.924,377,2.082,491,2.806,613,2.453,614,2.087,647,2.036,659,3.514,736,2.314,763,3.694,839,3.113,851,2.844,979,3.581,1008,3.289,1170,2.424,1202,5.973,1266,2.153,1290,3.019,1328,3.241,1367,2.631,1376,2.712,1394,3.617,1395,4.245,1737,2.978,1863,2.27,1870,2.748,1882,3.196,2015,6.927,2027,3.394,2395,5.943,2447,5.375,2636,3.113,2684,3.34,2898,6.58,2931,4.943,3182,4.522,3644,1.999,3857,3.778,3866,2.816,4022,4.245,4023,5.149,4024,5.149,4025,5.149,4026,5.149,4027,5.149,4028,5.149,4029,5.149]],["t/1993",[2,1.915,7,0.946,15,2.589,21,2.007,49,2.418,85,3.302,103,2.376,106,2.603,113,2.589,179,2.376,203,2.282,228,2.249,283,2.756,313,1.735,332,3.685,401,4.586,481,4.242,491,2.873,512,2.448,562,3.487,614,2.209,628,3.667,723,2.538,736,3.028,820,3.65,833,5.004,854,3.78,953,2.873,1202,3.741,1565,4.944,1633,4.492,2225,3.78,2297,3.175,2395,3.722,3513,4.888,3530,5.068,3531,5.068,4022,5.555,4030,6.468,4031,7.102]],["t/1995",[7,0.81,49,2.309,283,2.97,401,3.926,512,3.542,736,4.38,833,7.239,3530,7.33,3531,7.33]],["t/1997",[7,0.853,16,1.338,24,1.164,28,1.014,34,1.831,49,1.616,56,0.71,68,2.725,84,1.252,105,3.386,162,2.526,219,2.225,246,2.718,256,2.69,261,4.87,285,2.97,295,2.891,318,2.636,353,2.621,377,1.608,398,2.532,401,2.748,512,2.478,536,2.141,614,1.323,734,3.181,759,3.795,828,2.998,1266,1.96,1284,3.515,1312,2.641,1408,3.192,1487,4.792,1918,4.782,2026,4.916,2027,6.011,2297,2.394,2600,5.915,2623,5.433,3514,5.524,4032,6.547]],["t/1999",[7,0.822,49,1.828,56,1.197,84,0.881,140,3.188,283,2.352,318,1.981,336,3.31,377,1.819,393,3.995,398,2.866,401,3.11,568,2.33,683,3.044,736,3.469,1202,4.286,1266,2.218,1289,3.291,1381,3.224,1632,5.409,1918,4.048,2027,5.088,2481,8.006,2600,5.007,2623,6.148,3531,5.806,3532,8.311,4033,7.72,4034,6.956,4035,6.956,4036,7.72]],["t/2001",[49,2.089,84,1.007,161,6.327,201,4.732,283,2.688,318,2.263,398,3.274,401,3.553,736,3.964,1266,2.535,1387,7.948,1918,4.625,2027,5.814,2481,6.472,2487,7.948,3530,8.084,3857,6.472,4034,7.948,4035,7.948,4037,8.821]],["t/2003",[7,0.824,15,3.812,49,2.35,279,4.517,395,4.132,614,1.925,828,4.36]],["t/2005",[2,1.491,7,0.436,17,2.185,23,2.672,24,1.772,28,0.78,48,2.243,49,1.799,56,1.162,84,0.867,98,1.943,113,2.016,162,2.813,165,1.793,171,1.502,174,1.643,203,2.252,219,1.711,228,2.536,232,2.011,246,3.558,248,2.595,272,2.263,313,1.956,338,2.537,377,1.236,433,3.192,524,3.403,530,2.472,536,1.647,545,2.555,568,1.583,614,1.018,705,5.791,757,2.318,831,3.487,852,2.397,914,2.751,930,6.436,1114,3.631,1266,1.508,1289,2.236,1312,2.031,1381,2.191,1486,4.054,1876,3.58,2027,7.137,2032,3.37,2034,5.036,2075,4.607,2623,6.05,2754,4.409,3047,4.607,3655,4.054,3866,4.883,4038,4.727,4039,5.247,4040,7.596,4041,5.247,4042,5.247,4043,5.247,4044,5.247]],["t/2007",[7,0.796,56,0.997,304,2.127,397,4.197,580,3.505,651,6.729,898,4.895,1266,2.753,1285,5.684,1399,7.011]],["t/2009",[7,0.832,304,2.223,438,5.475,651,7.032,1266,2.877,2025,7.973]],["t/2011",[84,1.057,92,3.614,93,4.942,200,4.835,207,4.761,261,4.945,304,2.057,313,2.384,353,2.661,614,1.797,620,5.399,934,4.654,1266,2.661]],["t/2013",[16,1.895,68,3.861,85,3.527,165,2.281,304,2.146,317,5.09,338,2.505,614,1.875,1801,5.805,2103,7.825]],["t/2015",[5,2.947,21,2.364,28,1.644,36,3.69,56,0.826,96,2.947,98,2.94,106,4.27,107,2.142,109,1.789,170,3.135,186,3.943,203,2.003,256,3.97,296,3.225,304,1.763,343,2.976,368,6.324,404,4.074,513,3.943,568,2.396,606,6.43,611,3.478,694,4.551,774,4.526,1066,4.127,3106,6.545,4045,6.814]],["t/2017",[109,1.763,203,2.503,304,2.203,404,5.091,1170,3.21,1705,6.385,3968,8.939]],["t/2019",[7,0.925,15,2.721,21,2.784,24,1.786,26,2.77,36,3.291,46,5.055,56,1.089,90,3.517,98,2.622,100,3.337,106,2.735,112,4.881,144,3.574,183,3.203,186,3.517,203,1.786,217,4.254,275,2.31,279,3.224,304,2.076,359,3.291,404,3.634,408,2.694,527,2.565,607,2.86,622,3.764,719,4.49,856,4.458,1133,4.015,1198,6.57,1245,2.469,1367,3.618,4045,6.077,4046,7.082]],["t/2021",[7,0.77,24,1.582,28,1.04,46,3.054,56,0.728,68,2.794,70,5.259,106,2.701,145,2.963,165,1.651,186,3.472,196,2.921,203,1.764,206,4.772,219,2.281,232,2.68,277,3.787,289,2.912,292,3.205,304,1.553,336,2.999,343,2.621,408,2.66,491,2.221,534,5.259,567,2.955,596,3.319,614,1.357,757,1.816,852,3.194,891,4.963,1006,3.769,1117,3.986,1168,6.001,1242,5.876,1362,5.329,1367,4.737,1390,5.259,1473,3.283,1632,5.071,1782,3.319,1911,5.483,2231,5.329,2930,5.764,4233,5.876,5130,7.941,5131,9.771,5132,7.369,5133,7.369]],["t/2023",[7,0.78,24,1.602,31,2.417,36,3.312,39,3.056,46,3.112,84,0.814,106,2.753,123,3.372,145,3.02,160,2.633,186,3.539,196,2.977,203,1.798,206,4.864,219,2.325,232,3.599,289,2.968,292,3.267,304,2.085,404,3.657,408,3.572,491,2.264,511,2.394,528,3.957,567,3.011,596,3.383,749,4.864,757,2.438,798,4.131,852,3.256,936,3.213,1006,3.842,1240,4.281,1312,2.76,1367,3.641,1782,3.383,2509,5.507,4167,6.612,4314,6.258,4471,6.612,5134,9.895]],["t/2025",[24,1.594,27,2.336,28,1.389,126,4.752,128,4.693,304,2.47,367,5.01,404,4.792,650,6.628,825,7.562,3401,7.438,4047,9.339,4048,9.339]],["t/2027",[7,0.762,16,1.08,21,2.98,28,1.574,32,3.629,39,3.934,56,0.818,68,3.142,100,2.594,107,1.485,127,1.903,165,1.3,181,3.287,183,2.49,197,2.585,203,1.984,207,2.368,223,3.267,253,3.491,304,1.746,315,2.756,316,2.585,336,2.361,337,2.702,339,2.927,343,3.967,379,6.906,404,2.825,430,2.862,477,3.441,480,3.949,482,2.594,502,1.6,508,3.418,555,2.745,577,4.989,613,3.746,614,1.526,623,2.778,635,2.105,690,3.6,694,3.156,723,3.769,747,3.403,757,2.042,778,3.659,936,2.482,997,2.148,1046,2.713,1164,2.927,1180,6.651,1367,2.813,1473,2.585,1693,3.394,1865,4.725,1900,3.907,2522,4.458,3042,3.394,3401,4.385,4049,9.174,4050,4.725,4051,5.285]],["t/2029",[5,3.223,7,0.721,24,1.817,28,1.291,31,2.235,68,3.469,84,0.991,112,4.533,165,2.05,169,5.004,254,2.942,285,3.78,304,2.364,338,2.251,403,5.872,404,4.455,528,4.821,614,1.685,778,5.771,1381,3.627,1689,4.796]],["t/2031",[7,0.705,24,1.945,28,1.262,109,1.508,113,3.26,162,3.887,165,2.003,171,3.005,304,1.884,381,4.23,527,1.955,724,4.354,759,3.134,760,2.642,1046,4.18,1238,5.341,1245,2.958,1737,3.372,1863,3.74,2803,8.144,2851,6.345]],["t/2033",[7,0.934,24,1.758,41,3.936,56,0.857,113,3.164,139,3.894,203,2.077,236,2.281,239,4.977,289,3.429,304,1.828,367,4.417,430,4.28,527,1.897,614,1.597,662,5.043,759,3.042,760,2.564,1006,4.438,1240,4.946,1737,3.272,1754,4.614,1863,3.63,1902,4.138,2851,4.977,4471,7.639]],["t/2035",[7,0.956,15,3.337,20,7.017,21,2.586,29,3.192,50,5.353,84,0.991,106,3.354,116,3.415,304,2.364,408,4.05,580,3.177,614,1.685,796,4.796,1367,4.437,5135,9.151,5136,9.151,5137,9.151]],["t/2037",[7,0.839,85,3.687,278,3.814,580,3.696,1312,3.911]],["t/2039",[8,4.506,16,1.096,23,2.796,56,0.827,96,2.074,140,2.308,177,2.844,179,1.971,219,3.017,275,1.823,304,2.665,318,2.039,366,3.684,393,2.892,438,5.51,447,2.786,500,2.786,545,2.722,548,6,552,2.965,555,2.786,558,4.748,614,1.084,802,2.382,852,4.602,898,2.856,902,4.101,948,5.475,1014,5.424,1059,3.296,1170,2.571,1312,4.582,1531,3.926,1638,3.042,2025,4.451,2253,4.796,2280,4.607,2299,4.796,2895,3.625,3042,7.016,4052,5.589,4053,7.948,4054,4.796,4055,5.589,4056,6.33,4057,5.036,5138,5.89,5139,5.89]],["t/2041",[113,3.417,248,5.755,275,2.9,304,1.974,353,2.555,484,3.97,854,4.987,916,5.445,1043,6.363,1044,7.222,1312,3.443,1638,4.839,1816,6.972,2624,7.329]],["t/2043",[7,0.818,34,2.058,56,1.132,84,0.875,85,3.594,97,3.613,224,3.423,254,2.598,322,2.527,353,2.203,482,3.613,495,3.901,572,4.278,580,2.805,611,3.359,614,1.487,709,5.281,762,5.441,839,4.635,893,4.665,1046,4.851,1312,3.812,1408,4.607,1633,3.807,2061,2.728,2481,8.71,4058,7.667,4059,7.359,4060,7.667]],["t/2045",[7,0.727,84,0.999,171,2.506,246,3.487,304,1.943,491,2.78,580,3.914,614,1.698,723,3.296,1312,3.389,1376,4.61,1511,6.028,1638,4.763,1737,3.478,2225,4.909,2297,3.072,3644,3.397,3858,9.437]],["t/2047",[2,1.418,7,0.414,15,1.917,16,1.436,36,2.318,48,2.132,49,2.054,56,0.903,84,1.214,133,2.249,160,1.843,165,1.177,179,1.759,197,2.342,219,1.627,224,2.227,246,2.917,248,4.29,255,3.54,275,2.388,302,2.42,313,1.885,317,2.627,318,2.611,377,1.725,438,4.744,441,2.859,495,4.862,580,1.825,614,0.968,655,3.436,724,2.559,736,2.241,744,2.996,757,1.295,802,2.126,891,6.782,898,2.549,938,4.112,940,2.887,1043,4.744,1133,2.828,1280,3.54,1312,4.592,1380,4.866,1399,5.384,1441,3.163,1898,2.769,1944,4.38,2061,1.775,2289,4.38,2299,4.28,2307,5.372,2481,6.364,2683,4.749,2726,3.751,3749,3.66,3858,7.493,4061,4.988,4062,4.988,4063,4.988,4064,4.988,4065,4.988,4066,4.988,4067,7.321,4068,4.988]],["t/2049",[23,3.231,203,2.779,318,2.357,322,3.631,614,2.137,1312,4.265,3042,6.791,3644,4.276,4069,9.185,4070,9.185]],["t/2051",[2,1.573,7,0.46,16,1.085,23,2.776,56,0.576,219,3.272,246,2.205,278,2.089,283,1.686,304,2.691,313,1.425,318,2.025,545,2.695,548,3.589,552,2.944,614,1.073,756,5.994,852,4.202,891,3.927,940,2.182,1043,3.026,1059,7.148,1223,3.303,1312,2.142,1787,5.133,1882,7.522,2253,4.748,2683,3.589,3858,6.188,4057,4.986,4071,5.533,4072,5.133,4073,5.533,4074,5.533,4075,5.533,4076,5.533,4077,5.533,4078,5.533,4079,5.533,4080,5.533,4081,5.533,4082,5.533,4083,5.533,5140,5.831,5141,5.533,5142,5.831,5143,5.533]],["t/2053",[2,2.093,7,0.797,24,1.256,25,2.484,28,1.095,35,5.402,39,4.113,49,2.272,84,0.84,109,1.308,116,2.895,197,3.457,304,1.635,313,1.896,336,3.157,391,3.266,404,3.778,505,3.215,614,1.861,635,2.815,743,6.671,760,2.292,802,3.138,830,3.546,1048,6.187,1049,6.187,1051,5.962,1052,7.213,1671,4.267,1870,2.7,1918,3.861,2004,7.067,2668,5.468,3007,5.962,3055,6.318,3691,7.067,4084,9.592,4085,7.363,4086,7.363]],["t/2056",[7,0.8,10,4.225,17,3.087,24,1.265,28,1.102,31,1.908,84,1.1,102,4.926,123,2.662,133,3.341,162,2.745,172,2.706,246,2.953,257,3.976,285,3.227,304,1.646,318,1.902,336,3.178,338,1.921,375,5.899,393,3.836,470,5.648,502,2.154,527,1.708,568,2.237,614,1.438,690,4.846,759,2.738,760,2.308,865,6.228,874,3.836,880,3.363,997,2.892,1046,4.746,1055,5.155,1733,4.666,1737,2.946,1807,6.228,3042,4.569,3644,2.877,4087,7.412]],["t/2058",[5,2.715,7,0.608,16,1.434,25,3.222,84,1.287,162,2.708,164,2.117,171,2.734,219,2.386,236,2.027,304,1.624,318,2.892,377,1.724,398,2.715,614,2.063,683,2.884,723,3.597,724,3.753,731,4.04,757,2.48,886,4.903,945,4.02,1046,4.705,1133,4.147,1284,3.769,1312,3.698,1629,5.367,1737,2.907,1871,6.277,2032,3.245,3644,2.839,3749,5.367,4088,6.59]],["t/2060",[7,0.684,49,1.95,84,1.176,171,2.357,192,5.111,248,4.073,285,4.485,304,1.828,336,3.531,536,2.584,605,3.14,614,1.597,662,5.043,731,4.548,1043,6.148,1046,4.057,1127,7.639,1133,4.668,1399,6.979,1540,4.8,1737,3.272,3644,3.196,3749,6.041,3754,7.419]],["t/2062",[7,0.764,31,2.661,35,5.067,37,4.851,39,4.433,56,1.147,63,5.592,68,2.759,84,1.05,95,4.515,100,3.254,109,1.227,203,2.32,236,1.914,283,2.104,377,2.436,481,4.347,614,1.784,734,3.22,1129,4.552,1266,1.984,1320,4.629,1595,8.826,1689,5.71,1754,3.093,2112,5.803,2497,5.693,2550,5.263,2708,5.263,2789,4.954,3412,5.693,3413,5.263,3652,4.59,3994,5.926,4089,6.906,4090,6.906,4091,6.906,4092,6.906,4093,6.906,4094,6.906]],["t/2064",[7,0.872,113,3.26,181,5.065,203,2.648,334,5.065,491,2.695,512,3.083,614,1.646,934,4.264,1170,2.745,1202,4.711,1266,2.438,1395,6.994,1642,7.645,1754,3.8,1870,3.85,2015,6.653,2395,4.687,3866,4.64,4095,8.485,4096,7.451,4097,7.451,4098,8.485]],["t/2066",[1,3.783,5,2.293,7,0.811,25,3.294,56,0.887,84,0.973,126,3.143,131,5.281,135,3.711,157,3.759,162,2.288,165,1.458,174,2.671,203,1.558,254,2.889,314,5.191,322,2.036,341,3.581,369,4.039,370,5.093,375,3.783,449,4.007,476,1.89,500,3.08,525,3.068,532,3.395,555,3.08,590,3.502,611,2.706,614,1.199,621,3.346,623,3.118,675,4.141,677,3.43,683,2.436,830,2.975,846,2.891,898,3.157,929,4.432,931,5.003,1181,3.379,1389,6.103,1390,4.646,1435,4.384,1474,5.003,1482,4.92,1527,4.297,1601,3.889,1936,5.732,2297,2.169,2789,4.432,2851,3.735,3042,5.257,3264,5.301,3508,5.425,4099,6.178,4100,5.425,4101,6.178,4102,8.527]],["t/2068",[2061,3.73]],["t/2070",[2,1.827,7,0.96,15,2.47,16,1.26,23,2.261,24,1.495,29,2.363,46,2.807,49,1.522,56,0.669,84,0.734,108,3.498,138,2.732,145,2.724,162,2.38,165,1.517,213,2.756,228,2.146,248,3.179,256,2.534,281,3.204,313,2.256,317,3.386,377,1.515,440,2.987,441,5.021,458,3.814,481,4.046,534,4.834,536,3.359,546,5.644,581,4.898,605,2.451,613,3.062,742,4.236,744,5.263,747,2.781,798,3.725,846,4.099,855,4.773,857,3.073,959,6.154,1163,4.106,1397,4.106,1441,4.076,1784,5.791,2307,4.716,2887,5.516,2966,4.611,3933,5.963,4056,5.119,4131,6.428,4132,6.428,4133,6.428]],["t/2072",[7,0.572,16,1.621,24,1.176,26,1.798,56,1.289,84,0.525,114,3.863,123,2.475,232,1.762,304,1.021,306,2.151,342,4.746,405,2.31,449,4.469,513,2.283,545,2.239,552,1.715,623,2.32,780,2.578,822,2.894,1285,4.905,1597,4.58,1816,3.604,1822,2.915,1933,6.45,2108,3.944,2306,6.393,2307,6.065,2308,5.913,2310,3.944,2311,9.45,2312,5.178,2313,5.913,2317,4.036,2318,4.036,2319,4.036,2320,4.036,2321,3.262,2322,5.913,2485,3.03,2550,3.503,3456,6.051,3577,4.036,4110,5.488,4111,4.142,4134,4.142,4135,5.79,4136,4.142,4137,6.209,4138,4.597,4139,6.891,4140,4.597,4141,4.597,4142,4.597,4143,4.597,4144,4.597,4145,4.597,4146,4.597,4147,4.597,4148,4.597,4149,4.597,4150,4.597,4151,4.597,4152,4.597,4153,4.597,4154,4.597,4155,4.597,4156,4.597,4157,4.597,4158,4.036,4159,4.597,4160,4.597,4161,4.597,4162,4.597,4163,6.209,4164,6.209,4165,4.597,4166,4.412,4167,4.264,4168,4.597,4169,6.891,4170,4.597]],["t/2074",[7,0.846,16,1.839,24,0.978,26,2.243,56,1.27,84,0.654,100,3.813,114,4.817,123,2.906,232,2.197,248,2.836,304,1.273,342,5.574,449,5.249,545,2.792,744,3.444,790,5.938,855,4.257,1597,5.379,1633,2.847,1933,7.158,2108,6.944,2307,5.938,2308,6.944,2310,4.919,2311,10.046,2312,5.746,2313,6.944,2317,5.034,2318,5.034,2319,5.034,2320,5.034,2321,4.068,2322,6.944,4110,6.445,4134,5.165,4135,6.8,4136,5.165,4137,7.292,4163,7.292,4164,7.292,4171,5.733,4172,5.733,4173,5.733,4174,5.733]],["t/2076",[56,1.014,164,2.822,550,4.036,614,1.891,744,5.855,814,4.423,1704,8.56,2301,10.952]],["t/2078",[2061,3.73]],["t/2080",[4,2.189,7,0.944,8,3.797,21,1.995,27,1.675,41,3.202,46,4.962,84,1.028,85,2.445,90,4.474,92,2.614,93,4.022,98,2.48,109,1.19,127,2.315,133,3.02,134,3.512,153,3.611,164,1.939,172,3.289,174,2.098,207,3.875,213,2.872,224,2.99,256,2.641,276,3.497,283,2.745,304,1.487,305,4.49,338,1.736,340,4.705,343,2.511,363,5.037,440,3.112,476,2.049,532,3.681,599,3.628,613,3.191,690,4.379,694,3.839,853,5.335,939,4.345,997,2.614,1434,3.927,1470,5.104,2353,4.217,4373,6.698]],["t/2082",[5,2.268,7,0.703,31,2.178,49,1.447,56,0.636,68,3.878,84,1.256,105,2.014,109,1.725,110,3.392,164,1.769,165,1.442,172,3.543,203,2.134,207,3.639,253,3.874,256,3.336,304,1.357,377,1.44,404,3.135,527,1.95,552,3.156,553,3.41,576,3.793,607,3.418,611,2.677,614,1.185,623,3.083,630,2.378,638,3.874,731,3.375,769,3.427,902,4.483,986,3.375,988,3.604,1042,3.874,1057,4.17,1073,4.656,1129,4.027,1210,4.948,1278,4.292,1689,3.375,2506,5.505,2867,3.767,2892,6.739,3016,6.976,3402,4.866,3468,5.669,4124,5.243,4347,5.505,4374,5.135,5144,5.505,5145,5.669]],["t/2084",[2,2.106,7,0.8,27,2.41,46,4.674,47,4.926,84,1.1,90,3.68,92,4.783,93,3.309,96,2.751,158,3.904,172,2.706,207,3.188,256,2.922,306,3.468,313,1.908,332,4.053,338,1.921,476,2.947,481,4.666,596,3.518,597,4.034,599,5.217,604,5.155,707,4.346,796,4.094,810,3.468,846,3.468,997,2.892,1360,4.452,2118,5.504,3668,6.36]],["t/2086",[5,1.409,7,0.75,16,0.745,17,1.581,28,0.565,29,1.396,31,1.532,49,1.41,56,0.939,68,3.32,84,1.244,89,2.34,92,4.405,93,3.277,105,1.252,106,2.299,109,1.603,110,2.108,144,1.916,165,0.896,172,3.033,177,1.932,199,1.974,203,1.851,207,3.157,253,2.408,256,2.894,278,1.434,283,1.157,297,2.188,304,2.365,306,1.777,322,1.252,372,1.497,377,0.895,398,2.209,404,1.948,476,1.162,505,2.599,527,1.691,552,3.099,553,2.119,576,2.357,580,1.389,607,2.404,611,1.663,614,1.424,623,1.916,630,1.478,638,2.408,707,2.226,731,2.097,747,1.643,755,1.802,769,1.538,877,1.459,898,1.94,899,2.754,900,2.616,902,2.786,940,1.497,953,1.619,986,3.287,988,3.51,999,2.176,1042,2.408,1057,2.591,1129,2.503,1139,2.141,1210,3.075,1263,2.426,1266,2.387,1278,2.667,1367,1.94,1381,1.586,1429,3.024,1446,2.341,1601,2.39,1671,2.201,1689,2.097,1737,2.365,1882,2.357,1898,2.108,1906,2.176,2011,3.258,2287,2.482,2502,3.13,2506,3.421,2867,2.341,2892,5.845,3016,5.268,3204,3.645,3402,3.024,3468,3.523,3644,2.31,3669,3.645,4124,3.258,4374,3.191,4376,3.797,4377,3.797,4378,3.421,4379,3.797,4382,3.523,5023,4.002,5144,3.421,5145,3.523,5146,3.797,5147,4.002]],["t/2088",[7,0.866,24,0.804,49,1.116,55,2.701,56,1.201,84,1.135,92,3.274,171,1.349,177,4.269,283,2.139,304,2.315,306,2.205,368,3.754,382,5.485,398,2.606,401,4.198,426,1.834,440,2.19,449,3.057,539,4.748,543,6.185,608,2.578,611,2.065,614,1.929,623,2.379,736,2.118,898,2.408,1006,5.011,1114,4.011,1133,2.672,1266,3.103,1597,4.666,1607,4.836,1638,5.673,1698,3.696,1699,3.081,1737,2.79,1816,5.505,1941,4.247,2643,3.458,2922,4.045,3042,2.906,3093,8.923,3644,2.726,4100,4.139,4108,3.885,4380,4.714,4381,4.524,5148,4.967,5149,4.524,5150,4.967,5151,4.967,5152,4.967,5153,4.967,5154,4.967,5155,6.739,5156,6.739,5157,4.967,5158,4.967]],["t/2090",[7,0.953,15,3.577,21,1.209,24,1.47,25,1.37,27,1.016,28,1.139,34,1.09,40,2.254,46,4.748,47,4.167,49,0.962,56,0.652,84,1.063,85,3.744,92,3.839,93,1.813,96,2.843,97,4.833,105,2.067,106,2.958,129,2.556,135,2.439,138,1.726,140,1.677,158,3.302,164,1.175,179,2.211,196,1.696,272,1.752,278,1.533,283,1.237,296,1.649,304,0.902,313,1.045,318,1.042,377,0.957,393,2.101,395,2.611,437,3.788,438,5.381,440,1.887,441,2.327,471,1.773,491,1.29,530,1.913,557,3.288,591,2.439,597,2.21,605,1.548,614,0.788,723,1.529,730,3.484,734,1.893,744,2.439,747,3.314,757,1.054,813,5.227,820,2.199,854,2.278,856,2.556,1050,4.279,1055,4.36,1163,4.005,1164,2.158,1202,2.254,1328,2.556,1597,2.699,1688,2.912,1962,3.288,1963,3.094,1964,2.912,1965,3.094,1996,3.484,2025,6.1,2286,6.018,2287,2.654,2364,3.484,2836,3.288,3533,5.327,4383,4.06,4384,6.901,4385,7.659,4386,3.658,4387,4.06,4388,4.06]],["t/2092",[4,1.515,7,0.69,8,2.628,16,0.909,17,1.93,21,1.38,29,1.704,46,2.024,49,1.642,56,1.078,84,0.791,85,1.692,92,4.404,93,3.096,97,3.267,108,3.773,160,1.712,164,1.342,196,1.936,203,1.169,217,2.784,228,1.547,275,1.512,283,1.412,304,1.54,365,2.475,372,1.827,401,1.867,434,4.87,438,2.535,482,2.184,500,2.311,527,1.068,579,3.532,580,1.696,591,2.784,596,2.2,613,2.208,614,1.612,680,3.821,713,4.07,714,8.561,751,2.82,774,2.642,780,2.6,796,2.56,813,3.163,830,2.232,854,2.6,888,4.304,898,2.368,1009,2.249,1170,1.499,1263,2.961,1266,2.649,1371,7.466,1482,3.691,1601,2.918,1705,2.983,1733,2.918,2033,2.75,2297,2.434,2412,4.449,2668,5.149,2739,6.247,3111,3.895,3412,3.821,3533,3.223,3668,3.977,3710,7.973,3712,7.973,3977,3.532,4378,4.176,4389,4.635,4390,4.635,4391,4.635,4392,9.22,4393,4.635,4394,4.635,4395,4.635,4396,4.449,4397,4.635,4398,6.934]],["t/2094",[7,0.829,24,0.811,48,2.032,49,1.126,55,2.724,56,1.203,84,1.193,92,3.29,171,1.361,177,4.291,283,2.153,304,2.322,368,3.786,382,5.513,398,2.623,401,4.211,426,1.85,449,3.083,539,4.778,543,6.204,608,2.6,614,1.936,623,2.399,714,3.488,736,2.136,898,2.429,1006,5.032,1114,4.031,1266,3.111,1597,4.696,1607,4.867,1638,5.69,1698,3.727,1699,3.108,1737,2.808,1816,5.54,1941,4.283,2643,3.488,2922,4.079,3042,2.931,3093,8.961,3644,2.743,4100,4.174,4108,3.919,5149,4.563,5155,6.782,5156,6.782,5159,5.01,5160,5.01,5161,5.01,5162,5.01,5163,5.01,5164,5.01,5165,5.01,5166,5.01,5167,5.01,5168,5.01]],["t/2096",[7,0.784,16,1.408,25,2.882,49,1.7,84,0.975,92,4.112,100,3.383,108,3.907,112,3.748,164,1.406,165,1.695,198,4.382,213,2.083,246,4.699,292,2.226,348,3.285,377,1.145,388,3.701,397,2.128,438,3.926,440,2.257,441,4.115,443,8.312,544,3.201,562,2.514,614,1.657,651,3.412,713,4.265,714,5.268,898,2.482,1035,8.285,1046,2.393,1181,2.656,1266,2.454,1285,2.882,1371,3.933,1408,2.273,1428,4.506,1633,2.412,1687,4.265,1799,3.523,1998,2.975,2033,2.882,2497,5.918,2836,3.933,3175,7.695,3176,4.265,3177,4.265,3178,9.056,3179,6.891,3180,6.891,3181,6.891,3182,6.305,3183,4.662,3184,4.662,3185,4.662,3186,4.662,3187,4.662,3188,4.662,3189,4.662,3190,4.662,3191,4.662,3192,4.662,3533,3.378,4384,4.377,4399,4.857,4400,4.857]],["t/2098",[2,1.29,7,0.854,8,2.574,15,2.623,16,0.89,17,1.891,21,1.352,29,1.669,34,1.219,49,1.617,50,2.799,56,1.142,84,1.117,85,2.492,90,2.255,92,4.688,93,3.048,94,5.859,96,2.534,97,3.866,102,3.018,106,2.637,108,2.471,109,0.807,116,2.685,125,2.727,131,2.171,164,1.314,171,1.3,174,1.422,181,2.711,186,2.255,196,1.896,275,1.481,283,2.08,313,2.112,316,2.132,323,1.93,377,1.07,390,2.31,395,1.891,398,1.685,407,2.163,438,3.734,491,1.442,526,3.46,562,2.35,568,1.37,593,3.549,597,2.471,611,1.989,694,2.602,729,3.415,850,3.127,893,2.762,898,2.32,940,1.79,950,3.331,1263,2.9,1429,5.437,1433,3.815,1434,4.003,1531,3.189,1597,3.018,1601,2.858,1633,2.255,1866,3.815,2011,5.859,2241,3.896,2287,4.463,2297,2.396,2502,3.743,3111,3.815,3176,3.987,3177,3.987,3344,3.896,3629,4.5,3977,3.46,4401,4.541,4402,4.541,4403,4.541,4404,4.541,4405,4.541,4406,4.541,4407,4.091,4408,4.541]],["t/2100",[7,0.776,15,1.827,21,1.415,27,1.189,33,2.856,34,1.276,46,4.566,56,1.183,84,0.963,85,1.735,90,2.361,92,4.225,94,4.027,96,3.13,98,1.76,101,2.666,113,1.827,133,2.143,140,1.963,164,2.045,179,2.973,203,1.199,208,4.563,238,2.874,240,2.74,254,1.611,275,2.305,278,2.668,351,1.813,353,2.03,372,1.874,377,1.12,407,2.264,437,2.351,440,3.918,476,2.161,482,2.24,491,2.244,562,2.46,566,3.488,583,3.06,597,3.845,613,3.366,614,0.922,715,6.264,828,2.089,1006,2.562,1202,3.923,1266,2.03,1290,2.787,1429,3.786,1434,2.787,1479,4.563,1633,2.361,1866,3.995,1999,6.782,2009,3.448,2395,3.903,2457,4.41,3629,3.133,3708,8.094,3857,3.488,3866,4.612,3977,3.622,4407,4.283,4409,7.066,4410,4.754,4411,7.066,4412,4.754,4413,4.754,4414,4.754]],["t/2102",[7,0.806,15,3.73,16,1.659,20,4.027,36,2.839,49,1.447,84,0.966,92,4.641,93,3.778,94,3.483,97,2.879,98,2.263,157,3.718,164,2.45,174,2.65,177,4.305,230,5.366,236,2.345,275,2.76,278,3.195,302,2.965,315,3.058,359,2.839,407,2.91,430,3.176,440,2.839,578,2.53,621,3.309,684,3.358,712,3.464,795,4.209,816,3.648,900,6.686,992,3.218,993,3.718,998,8.044,1007,6.005,1024,4.096,1216,3.278,1271,4.721,1366,3.583,1464,5.505,2096,5.505,2231,4.656,2241,5.243,2672,4.209,4415,6.11]],["t/2104",[0,3.866,7,0.722,8,4.928,10,6.722,56,0.905,84,0.725,89,2.499,92,3.392,138,3.695,144,3.207,172,2.32,196,2.654,200,3.317,213,2.725,236,1.761,240,3.662,275,3.475,283,1.936,295,2.693,313,1.636,316,2.984,351,2.423,365,3.393,504,5.553,605,2.423,623,4.387,637,4.297,680,7.166,691,5.038,715,3.442,795,4.377,860,5.061,900,5.988,952,4.336,959,4.463,1014,4.336,1046,3.131,1076,5.145,1483,5.58,1504,6.456,1527,4.419,1945,4.463,2282,4.463,2531,5.34,2918,6.354,5169,6.696,5170,10.442,5171,7.215]],["t/2106",[7,0.796,8,2.16,15,2.826,17,3.467,18,3.406,21,1.135,24,0.65,26,2.878,28,0.567,34,1.974,46,1.664,48,1.629,56,0.765,84,0.681,90,1.892,92,2.328,93,4.473,94,3.401,96,1.414,97,3.922,103,1.343,132,1.847,165,0.899,172,1.391,181,2.275,183,1.723,185,4.328,186,1.892,201,2.044,207,1.639,213,1.634,225,1.435,228,1.992,236,2.306,244,3.871,278,1.439,283,1.161,291,2.365,292,1.746,298,2.452,306,1.783,313,0.981,315,1.907,323,1.619,338,0.988,353,1.095,369,2.491,375,2.333,377,0.898,381,2.975,393,1.972,395,1.587,440,2.773,444,1.741,463,2.416,476,1.166,505,1.664,509,2.275,590,2.16,597,2.074,611,1.669,620,7.05,676,3.535,677,2.115,680,3.141,691,2.208,693,2.471,694,3.42,696,2.829,719,2.416,740,1.815,790,2.796,794,4.378,802,1.624,860,3.035,874,3.807,877,1.464,881,2.944,940,1.502,992,2.007,997,1.487,1007,4.235,1009,1.849,1024,4,1025,4.753,1027,3.27,1029,5.335,1030,7.06,1031,8.675,1032,5.728,1078,3.346,1218,2.261,1408,1.783,1434,3.499,1435,2.704,1871,3.27,1933,2.676,2297,1.337,2533,3.346,2805,3.085,3090,3.535,3174,3.346,3450,3.657,4416,3.81,4417,3.81,4418,3.81,4419,3.81,4420,3.433,4421,5.377,4422,3.81,4423,3.433,4424,3.81]],["t/2108",[2,1.718,8,3.426,17,2.517,24,1.87,28,0.899,48,3.59,56,0.874,68,2.415,92,3.277,93,4.309,94,3.445,97,2.848,132,1.871,165,1.427,179,2.131,186,3.001,199,3.141,203,2.118,207,2.599,236,1.675,283,1.842,304,2.519,338,2.177,527,1.392,614,1.172,620,6.612,898,4.291,1024,4.051,1025,4.813,1029,6.091,1046,4.138,1266,3.149,1371,4.894,1434,3.544,1601,3.805,1689,3.338,1693,3.726,2833,4.488,2892,4.813,3174,5.307,3866,3.305,4420,7.567,4421,7.567,4423,5.446,4425,6.044,4426,6.044,4427,6.044,4428,6.044,5144,5.446]],["t/2110",[2,1.393,7,0.908,16,2.073,21,1.459,24,1.465,27,1.226,28,0.729,41,2.343,47,3.257,56,0.986,85,3.459,90,3.588,93,3.226,97,3.405,105,1.615,109,0.871,123,1.76,124,3.257,170,1.526,172,1.789,173,2.763,174,1.535,183,2.216,200,2.558,244,3.178,256,1.932,275,1.598,283,1.493,291,3.042,295,2.076,313,1.262,323,2.083,353,1.408,408,2.749,437,4.246,438,2.68,447,2.443,471,2.14,491,2.295,502,1.424,525,2.433,580,1.793,597,4.672,599,3.914,614,0.951,615,3.021,619,7.747,627,4.415,747,2.12,846,2.293,874,3.74,997,1.912,1245,1.708,1285,2.907,1486,3.786,1876,3.344,2531,4.118,3533,5.026,3567,4.415,4386,4.415,4429,4.9,4430,10.104,4431,4.9,4432,9.374,4433,4.9,4434,4.9,4435,4.9,4436,4.9,4437,4.9,4438,4.9,4439,4.9,4440,4.9,4441,4.9,4442,7.226]],["t/2112",[7,0.84,12,3.399,24,1.726,84,0.913,109,1.421,201,4.29,304,2.589,619,7.502,736,3.594,780,5.673,1059,4.717,1266,2.906,1689,5.587,1882,4.964,3108,6.72,3158,6.592,3632,6.413,3688,7.676,3689,7.676,4443,7.997,4444,7.997,4445,7.997,4446,7.997]],["t/2114",[2061,3.73]],["t/2116",[24,1.87,109,1.619,236,2.524,254,3.087,397,3.991,444,4.161,527,2.099,623,4.597,658,4.597,953,3.883,997,3.555,1245,3.176,2669,7.255,3009,7.509,3917,6.106,5172,10.344]],["t/2118",[16,1.811,34,1.865,49,1.646,56,1.256,109,1.641,164,2.012,171,2.644,193,2.663,203,1.753,221,2.705,254,2.355,318,1.783,322,3.043,397,3.044,476,2.126,491,2.207,502,2.019,614,1.348,653,2.305,658,3.507,757,1.804,822,4.374,945,3.819,997,4.709,1037,2.791,1060,3.551,1107,3.858,1284,3.581,1476,2.691,1870,3.387,2032,4.096,2632,4.374,2633,4.028,2895,4.507,3015,6.447,4570,6.261,5173,7.323]],["t/2120",[16,1.329,28,1.627,56,1.066,107,1.829,109,1.205,132,2.811,164,2.629,170,2.828,171,1.941,246,2.702,254,3.078,313,1.745,326,4.863,336,2.907,377,1.598,391,3.008,426,2.639,502,2.976,525,4.51,580,2.481,647,2.68,712,3.844,734,3.161,880,4.122,953,2.89,997,4.582,1037,3.649,1114,3.241,1235,4.721,1274,4.364,1332,5.846,1381,2.832,2806,6.921,5174,10.314,5175,7.145,5176,7.145]],["t/2122",[16,1.389,28,1.053,31,2.407,56,0.973,84,0.808,107,1.911,109,1.258,115,3.436,123,3.358,132,2.192,140,2.924,164,2.05,170,2.205,174,2.218,179,2.497,236,1.962,256,2.792,336,3.037,338,1.836,391,3.141,502,2.058,507,5.999,527,2.154,550,3.872,658,3.574,814,3.214,828,3.112,947,3.799,997,3.648,1047,5.886,1278,4.974,1444,6.568,1688,5.08,2021,5.472,4272,6.381,5177,7.463,5178,7.463,5179,8.974,5180,7.463,5181,7.463,5182,7.463]],["t/2124",[7,0.751,16,1.321,17,2.806,31,1.735,34,1.809,40,3.741,84,0.769,115,3.27,123,2.42,138,2.864,164,1.951,174,2.833,175,3.884,246,2.685,247,3.12,254,2.283,261,3.598,282,3.862,351,2.57,353,1.936,415,5.004,433,4.1,476,3.124,507,3.862,525,3.346,527,1.553,550,2.791,568,2.73,571,5.284,578,2.791,621,3.65,631,4.642,658,3.401,707,3.951,997,4.259,1047,4.242,1066,4.702,1380,4.479,1508,3.928,1540,5.273,1813,5.207,1898,3.741,2041,5.663,3129,6.468,3508,5.918,3934,6.252,4498,5.457,5183,6.739,5184,6.739,5185,7.102]],["t/2126",[7,0.65,31,2.569,49,1.854,84,1.139,115,3.799,123,3.583,174,3.44,236,2.169,247,3.625,351,2.986,416,3.799,476,2.395,527,1.804,568,3.011,584,6.875,585,6.235,621,4.24,658,3.951,818,4.142,997,3.055,1193,6.632,1444,5.499,1688,7.158,1790,7.514,2021,6.049,2691,6.453,2973,7.263,3012,6.875,4498,6.339]],["t/2128",[2061,3.73]],["t/2130",[16,1.981,34,2.711,107,2.725,318,2.592,1352,5.118]],["t/2132",[2,1.45,7,0.424,16,1,24,1.5,28,0.759,34,1.369,41,2.439,49,1.763,56,1.005,84,1.003,103,2.625,107,2.771,109,1.968,110,6.147,113,2.86,127,1.763,132,2.304,140,2.107,164,1.477,165,1.204,171,2.131,179,1.799,193,1.956,203,2.217,228,1.703,272,2.201,304,2.145,313,1.314,343,1.912,377,1.754,408,1.941,527,2.615,614,1.705,658,2.575,877,3.947,940,2.012,1170,3.125,1181,2.79,1245,3.064,1266,1.466,1281,2.712,1737,2.028,1779,3.621,1844,3.284,1863,3.281,1870,3.767,1884,3.548,2605,3.888,2850,6.536,2851,3.084,3644,1.981,3866,2.79,4308,7.444,4309,5.102]],["t/2134",[5,1.86,7,0.847,12,2.129,17,2.087,21,1.492,24,1.879,27,1.253,28,0.745,31,1.29,41,2.395,46,3.798,56,1.109,98,2.72,109,1.545,110,5.661,116,1.97,131,2.395,133,2.259,158,2.639,170,1.56,171,1.434,174,2.724,179,2.59,186,3.647,196,2.093,197,2.353,254,1.698,272,2.161,275,2.396,282,4.21,289,2.087,304,1.113,322,2.866,353,1.44,367,3.941,377,1.181,386,5.76,388,3.818,408,1.906,440,2.328,482,2.361,505,3.208,525,2.488,527,2.349,528,2.782,568,1.512,590,2.84,597,2.727,599,2.714,611,2.195,614,0.972,631,5.059,719,3.177,755,3.486,807,2.04,816,2.991,856,3.154,876,3.594,877,1.925,928,4.4,997,3.393,1046,2.469,1170,1.621,1366,2.938,1633,2.488,1737,1.991,1870,1.838,2297,1.759,2605,3.818,2691,4.13,2850,4.4,3644,1.945,4310,7.345]],["t/2136",[2061,3.73]],["t/2138",[7,0.705,15,3.26,34,2.277,56,1.092,228,2.832,322,2.797,377,2,398,3.149,401,3.418,465,6.226,539,5.738,543,5.035,607,3.427,683,3.345,747,3.671,820,4.595,1535,6.871,1536,6.556,1555,7.872,1563,7.281,1607,5.844,2297,2.978,3533,5.901,4279,7.872,4280,7.872]],["t/2140",[7,0.618,16,1.458,24,0.661,25,1.308,28,0.899,34,1.04,49,1.432,56,0.629,84,0.849,103,1.367,145,3.152,162,1.435,171,1.11,175,2.234,203,1.526,219,1.973,236,1.676,256,2.385,275,1.264,281,1.933,283,1.843,304,1.343,313,1.557,315,1.94,318,0.995,322,1.994,334,2.314,338,2.178,365,2.07,368,3.087,372,1.528,395,2.519,398,1.439,401,2.436,438,2.12,458,2.3,482,1.827,530,2.85,531,2.476,539,6.82,542,5.311,543,4.986,552,1.446,564,2.645,567,2.556,605,1.478,613,1.846,614,1.956,630,1.509,708,4.986,747,1.677,757,1.007,762,2.751,877,2.858,898,3.8,947,2.08,1139,2.186,1164,2.06,1170,1.254,1181,2.12,1219,3.139,1266,2.774,1281,2.06,1306,2.555,1312,2.342,1360,2.329,1376,3.186,1416,2.915,1473,1.82,1489,3.493,1531,2.723,1540,2.26,1550,3.04,1570,3.404,1638,2.11,1732,3.04,1737,1.541,1754,1.736,1831,3.258,1898,2.152,1926,7.571,1927,3.493,2297,1.361,2806,2.954,3042,3.729,3456,5.311,3644,3.537,3671,3.721,3773,3.493,3977,2.954,4056,4.817,4281,6.049,4282,6.049,4283,3.877,4284,6.049,4285,3.877,4286,6.049,4287,3.877,4288,6.049,4289,6.049,4290,3.721,4291,6.049,4292,3.877,4293,3.877,4294,6.049,4295,3.877,4296,3.877,4297,3.877,4298,3.877,4299,3.877]],["t/2142",[4,2.472,16,2.239,49,1.791,171,2.165,236,2.994,318,2.93,398,3.623,401,4.353,539,7.722,543,5.791,567,4.824,940,2.982,1266,2.805,1376,3.983,1559,8.75,1638,5.312,3644,2.936,4300,7.563,4301,7.563,4302,7.563]],["t/2144",[4,2.439,16,2.105,49,1.767,171,2.136,236,2.975,398,3.591,401,4.325,539,7.682,543,5.741,552,4.005,567,4.8,852,3.408,940,2.942,1266,2.78,1312,2.889,1376,3.93,1559,8.694,1638,5.265,1732,7.586,3644,2.896,4304,7.462,4305,7.462,4306,7.462]],["t/2146",[2061,3.73]],["t/2148",[7,0.528,16,1.943,18,4.933,19,4.082,24,1.084,27,1.589,28,1.754,31,1.636,56,0.905,84,0.992,88,4.126,89,4.387,116,2.499,169,3.662,198,3.26,225,2.393,240,3.662,283,1.936,292,2.913,296,2.58,302,3.083,303,5.238,332,3.475,390,3.233,391,2.819,397,2.784,491,2.018,527,1.464,608,3.475,712,3.602,723,2.393,734,2.963,740,3.027,835,4.51,859,8.326,882,4,884,5.777,899,4.609,900,4.377,1024,4.259,1216,3.409,1243,4.91,1289,2.709,1315,3.683,1363,2.411,1381,2.654,1805,5.58,1864,3.528]],["t/2150",[16,2.026,18,1.476,19,1.497,23,1.122,24,1.115,25,3.282,27,2.069,28,1.603,29,1.903,56,0.783,96,1.183,98,1.181,100,3.08,102,3.441,107,2.232,109,1.786,113,1.989,116,1.254,127,1.789,133,1.437,140,1.317,145,2.194,163,4.502,165,1.776,170,0.993,172,1.89,182,2.304,183,1.442,198,1.636,216,1.71,231,2.43,232,1.222,254,2.215,278,1.204,279,1.452,285,2.254,295,1.351,306,1.492,322,1.051,334,1.903,336,1.367,338,0.826,343,1.94,353,0.916,377,1.22,405,3.463,440,1.481,476,1.584,495,2.634,511,1.739,512,1.881,517,2.037,557,1.672,558,2.439,577,2.581,592,2.156,614,0.619,636,1.892,647,1.26,653,1.058,719,2.022,723,4.244,734,1.487,757,1.953,760,2.035,778,3.441,857,2.475,907,2.156,914,3.427,934,1.602,936,3.391,946,3.171,953,1.359,997,2.55,1179,2.068,1245,3.089,1274,2.052,1289,1.359,1306,2.101,1315,1.848,1352,2.623,1362,2.43,1363,2.855,1381,2.162,1385,2.628,1389,1.993,1473,1.497,1492,2.8,1768,2.287,1778,2.736,1782,1.513,1864,5.581,1998,1.953,2486,2.24,2696,2.263,2833,2.368,3010,2.287,3630,2.582,3795,2.398,3997,3.6,4498,2.582,5021,2.628,5186,3.36]],["t/2152",[16,2.212,24,1.328,25,1.83,27,2.939,28,1.677,31,1.397,84,0.619,107,2.455,109,1.946,116,2.134,127,2.689,163,4.82,165,1.281,203,1.369,232,2.983,278,2.049,281,2.705,293,4.559,360,5.852,367,2.911,377,1.279,507,4.46,596,3.693,653,2.581,723,2.044,740,2.584,807,3.169,852,3.555,857,2.594,934,2.727,946,3.776,953,2.313,980,3.935,1013,4.254,1047,3.415,1239,3.519,1245,2.713,1278,3.811,1315,5.274,1352,4.61,1363,2.059,1381,2.266,1473,2.548,1864,5.518,2833,4.029,3515,4.888,3997,3.773,5187,5.717]],["t/2154",[7,0.28,16,1.948,21,2.707,25,1.138,26,2.655,27,0.843,28,1.524,34,1.822,56,1.232,84,1.09,88,3.689,96,1.252,98,2.005,100,1.589,105,2.237,106,2.092,107,1.461,108,1.835,109,0.962,113,1.296,128,2.722,160,1.246,163,2.262,165,1.279,169,1.943,179,1.189,200,2.827,201,1.809,278,1.273,295,1.429,296,1.369,297,1.943,313,0.868,322,1.111,323,2.302,338,0.874,339,2.879,353,0.969,377,2.006,413,2.685,416,2.628,418,1.776,426,2.108,430,1.752,476,1.031,484,1.505,502,1.574,506,2.107,507,3.104,508,4.824,509,3.233,512,3.821,520,3.236,552,1.258,622,2.879,723,3.86,731,1.862,736,4.463,757,1.406,851,2.992,936,3.06,939,2.187,1033,2.26,1069,3.869,1075,2.065,1235,4.725,1245,1.176,1308,3.385,1315,1.954,1363,1.279,1408,1.578,1511,4.675,1705,4.369,1849,2.504,1850,2.393,1864,5.514,1876,2.301,1882,2.093,1964,2.419,2782,2.345,2837,2.644,2896,5.825,4481,3.372,4482,3.372,4483,3.372,4484,3.372,4485,3.372,4486,3.372,4487,3.372,4488,3.372,4489,5.416,4490,3.372,4491,5.416,4492,3.372,4493,3.372,4494,3.372,4495,3.372,4496,3.372]],["t/2156",[16,2.197,24,1.912,25,3.46,28,1.525,31,2.104,39,3.505,198,4.194,282,4.684,289,3.404,302,3.966,409,4.365,505,3.569,635,3.125,723,3.079,830,3.936,1446,5.039,1467,6.737,1798,6.012,1819,6.934,1863,4.52,1864,4.538,2741,6.737]],["t/2158",[5,3.249,16,1.716,23,3.763,28,1.301,145,3.709,163,3.655,165,2.066,169,5.044,239,6.984,367,4.695,511,2.94,807,4.705,1266,2.515,1381,3.655,1849,6.499,1850,6.211,3795,6.581]],["t/2160",[2,2.1,16,1.638,19,1.801,24,1.547,25,1.294,27,1.501,28,1.625,56,0.399,89,1.509,97,1.808,102,2.55,107,2.594,109,1.066,113,1.474,125,2.305,127,2.073,132,2.287,140,1.584,145,2.542,153,2.068,163,1.602,165,2.269,170,1.868,172,1.4,179,1.353,182,2.115,183,1.735,198,1.968,228,1.281,239,2.319,245,2.415,275,1.251,276,3.857,277,2.078,289,3.077,294,2.175,313,2.151,320,2.319,323,1.63,324,3.348,351,1.463,365,2.049,381,4.165,394,2.021,398,1.424,405,3.846,426,1.493,432,2.55,444,1.753,468,2.572,472,1.968,495,1.952,502,1.115,511,2.807,527,1.382,550,1.589,555,1.913,577,1.913,581,2.923,621,2.078,635,1.467,662,2.349,683,1.513,712,2.175,723,1.445,748,3.055,749,2.618,750,2.849,757,1.558,759,1.417,760,1.194,769,1.554,780,4.144,814,1.741,820,3.249,841,4.416,857,1.834,880,1.741,909,2.783,934,3.713,951,2.594,997,1.497,1052,2.885,1133,2.175,1181,2.098,1205,3.559,1223,2.29,1308,2.398,1315,2.224,1329,3.055,1381,1.602,1441,2.433,1471,3.457,1481,4.946,1870,1.407,2043,2.849,2485,3.954,2488,2.451,2516,4.946,2890,3.457,2966,2.752,3003,5.042,3051,2.723,3199,3.457,3880,2.643,3882,3.682,3890,3.682,3913,3.559]],["t/2162",[21,2.669,24,1.529,28,1.736,89,3.525,107,3.15,109,1.593,165,2.116,179,3.16,338,2.323,405,3.005,635,3.427,723,3.376,820,4.854,1481,7.388,1864,4.976]],["t/2164",[2061,3.73]],["t/2166",[7,0.62,18,5.45,28,1.109,34,2.002,56,1.117,89,3.805,103,2.631,201,4.003,225,3.644,226,3.812,495,3.796,530,3.516,558,3.516,605,2.845,683,3.815,685,6.295,707,4.375,799,5.942,887,4.959,940,2.942,1152,6.866,1234,4.454,1302,5.092,1454,4.878,1456,5.352,1457,5.611,1458,6.94,1459,5.765,1460,6.552,1476,2.889,5188,7.863]],["t/2168",[2,2.157,7,0.768,16,1.676,18,5.121,19,1.879,25,1.35,27,1.001,28,1.129,31,1.596,34,1.074,84,1.056,88,5.249,89,3.637,123,2.227,128,3.116,132,1.239,162,1.482,164,1.159,165,1.464,169,3.573,196,2.589,197,1.879,213,1.716,225,2.335,226,2.045,275,2.476,279,1.822,283,1.219,304,1.377,306,2.901,313,1.03,351,2.364,353,1.782,440,1.86,472,3.181,483,2.66,484,1.787,495,3.155,500,1.995,510,8.121,512,4.091,557,3.251,558,1.886,575,3.103,605,1.526,628,2.178,647,3,659,2.731,734,2.891,736,3.41,762,2.84,796,2.21,828,1.759,884,4.121,940,2.445,951,2.706,1069,3.534,1073,3.05,1074,3.187,1114,1.913,1152,2.84,1164,2.127,1208,3.363,1234,5.103,1308,2.501,1310,3.01,1363,1.519,1456,2.871,1458,2.871,1491,3.241,1778,3.434,1782,4.842,1849,2.972,1850,2.84,1851,5.111,1964,2.871,1990,2.903,2041,3.363,2787,2.683,3039,3.713,3932,6.256,5189,4.217,5190,4.217,5191,4.217,5192,4.217]],["t/2170",[28,1.437,96,3.587,174,3.027,254,3.274,283,2.944,295,4.095,591,5.805,888,5.998,1782,4.586,1945,6.788]],["t/2172",[16,1.789,23,1.918,24,1.333,28,1.357,56,1.394,132,1.687,145,2.31,170,1.697,174,1.708,203,1.375,224,2.434,225,2.054,239,3.296,289,2.27,318,1.399,506,3.408,558,2.569,614,1.058,723,2.941,740,2.597,749,3.72,760,1.697,780,3.058,802,2.324,807,2.22,877,2.095,946,2.645,1014,3.72,1152,3.869,1234,3.255,1456,3.911,1458,3.911,1798,3.197,1799,3.954,1864,3.027,2833,4.049,3629,3.593]],["t/2174",[16,1.16,28,0.88,56,1.391,109,1.051,145,2.507,170,3.601,174,1.853,203,1.492,224,2.641,225,2.228,239,3.576,614,1.148,723,2.228,740,2.818,749,4.037,760,1.842,772,4.114,802,2.522,877,2.273,946,2.87,947,3.174,1056,4.711,1152,4.198,1234,3.531,1352,2.997,1456,4.243,1458,4.243,1864,3.284,1888,5.678,2833,4.393,3629,3.899,5193,6.234]],["t/2176",[2061,3.73]],["t/2179",[28,1.301,56,1.113,171,3.063,219,3.489,304,2.376,322,2.885,552,3.264,757,2.272,1014,7.884,1240,5.257,1312,4.142,1737,3.478,1840,8.752,2297,3.072,3644,3.397]],["t/2181",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,862,5.023,4328,6.948]],["t/2183",[2061,3.73]],["t/2185",[4,2.042,5,2.319,7,0.879,16,1.225,19,4.035,23,2.198,24,1.466,28,1.573,56,0.65,84,0.713,89,2.457,145,2.647,163,2.609,164,2.487,174,1.957,219,2.038,224,2.789,232,4.054,274,5.486,275,2.038,278,2.359,284,4.434,313,1.608,361,4.434,400,5.15,421,4.118,491,2.729,545,3.043,550,2.587,552,2.33,555,3.115,558,4.984,596,2.965,601,3.09,683,2.463,757,2.231,887,5.711,888,3.878,914,3.276,934,4.318,936,2.816,947,3.352,948,4.303,1036,4.345,1202,3.468,1308,3.905,1315,3.621,1508,3.642,1782,4.661,1974,5.796,2033,3.707,4317,6.247]],["t/2187",[2,1.681,4,1.934,7,0.858,16,1.16,23,2.081,24,1.412,25,1.996,27,1.48,28,1.536,31,2.13,56,0.861,84,1.089,103,2.086,113,2.273,123,2.124,165,1.396,170,1.842,173,3.336,174,1.853,216,3.174,217,3.553,219,1.93,232,3.657,247,2.739,276,3.088,289,2.463,306,2.768,318,2.448,337,2.903,363,4.449,409,3.159,491,2.628,500,2.949,502,2.404,511,1.987,513,2.937,530,2.787,552,2.206,558,2.787,562,3.061,565,2.035,567,2.499,568,1.785,577,2.949,632,3.354,635,2.262,663,3.807,666,4.876,683,2.332,685,3.468,814,2.684,821,4.34,852,4.358,877,2.273,881,4.571,887,5.498,934,2.973,935,4.79,1040,4.29,1461,5.678,4318,5.916,4319,5.916,5194,6.234]],["t/2189",[7,0.644,21,1.231,24,1.691,25,1.395,27,2.734,28,0.946,29,2.338,48,1.768,56,1.25,63,3.349,84,0.472,88,1.963,89,3.692,95,4.158,106,1.597,107,2.09,108,2.251,109,1.13,112,2.159,113,1.589,128,3.196,135,3.82,164,1.197,165,1.829,169,3.665,193,3.598,203,1.043,232,2.437,294,2.345,296,2.583,306,1.935,336,1.773,343,1.55,377,0.975,391,1.835,446,4.263,447,2.062,476,1.265,483,2.749,496,2.662,505,1.806,507,4.441,512,2.311,558,1.949,635,1.581,647,1.635,653,2.109,659,2.822,672,2.567,740,5.316,767,3.349,807,3.155,846,1.935,852,3.973,884,4.227,899,3,900,2.849,946,3.759,980,3,986,2.284,1008,2.642,1047,4.877,1087,5.065,1266,1.827,1289,2.711,1352,3.926,1373,2.662,1381,2.656,1397,2.642,1408,1.935,1409,3.726,1465,3.151,1539,2.905,1622,2.797,1729,5.344,1730,3.549,1884,2.876,1964,2.967,3542,2.935,3553,3.294,3796,3.349,4879,3.294]],["t/2191",[2,1.387,7,0.838,16,0.957,24,1.613,25,3.403,27,1.802,28,1.623,39,3.089,50,3.008,84,0.822,89,1.919,105,1.608,130,2.931,131,2.332,132,1.51,133,2.199,145,2.067,153,2.63,163,2.038,164,1.412,165,1.152,168,7.519,175,2.812,190,2.642,198,3.696,232,1.87,275,2.349,304,2.24,313,1.256,323,2.073,336,2.092,337,2.394,338,1.265,339,2.593,351,1.861,353,1.402,367,3.864,483,3.242,491,1.55,544,3.216,558,4.455,591,4.327,611,2.137,614,1.397,621,2.642,635,1.865,751,2.968,765,3.049,778,3.242,780,2.737,802,3.07,830,2.35,852,2.229,857,2.332,877,1.875,953,2.08,992,2.57,1048,4.1,1073,6.525,1181,2.668,1366,2.86,1689,3.979,1782,3.418,1798,6.189,1799,6.21,1819,4.871,1933,3.427,2103,3.951,2741,5.937,4050,4.186,5195,7.59,5196,7.59,5197,5.141]],["t/2193",[2061,3.73]],["t/2195",[34,2.593,56,1.005,84,1.103,257,5.184,278,3.649,279,4.4,295,4.095,545,4.706,992,5.09,4311,9.663]],["t/2197",[4,1.668,7,0.802,15,1.961,16,1.894,21,2.216,24,1.5,28,1.307,41,2.439,46,3.838,49,1.208,56,0.531,84,1.103,96,1.894,125,3.065,138,3.735,164,1.477,165,1.204,171,1.461,179,3.622,183,2.308,189,2.877,203,1.287,207,2.194,245,3.212,282,2.924,283,2.268,305,3.42,306,2.387,337,2.504,338,1.323,343,1.912,353,1.466,377,1.754,409,2.725,416,2.476,440,2.371,441,5.036,449,4.828,534,5.598,536,2.336,545,2.485,558,2.404,559,8.814,560,4.929,565,2.561,596,3.533,687,4.48,744,5.803,747,2.208,757,1.325,827,4.693,898,3.803,929,3.66,934,2.564,1027,4.378,1028,5.177,1444,3.584,1997,4.132,1998,3.125,2031,3.584,4110,7.693,4135,7.384,4312,5.102,4313,5.102]],["t/2199",[18,4.288,28,1.377,56,0.964,84,1.057,113,3.559,253,5.872,283,2.822,530,5.216,545,5.391,583,5.961,1129,6.104,1234,5.529,4314,8.132]],["t/2201",[7,0.739,21,2.648,23,3.128,84,1.015,88,4.22,89,3.497,283,2.709,353,2.555,359,4.131,391,3.944,472,4.562,476,2.72,512,3.23,545,5.261,575,4.451,614,1.725,1385,7.329,1449,7.2,3725,6.687]],["t/2204",[171,2.866,232,3.837,304,2.223,545,4.876,1737,3.978,3644,3.886]],["t/2206",[4,3.105,17,3.956,84,1.084,219,3.098,237,7.339,284,6.741,304,2.109,545,4.626,1059,5.602,2958,9.117,4315,9.498,4316,9.498]],["t/2208",[2061,3.73]],["t/2211",[28,1.301,56,1.113,171,3.063,219,2.855,304,2.376,322,2.885,548,5.676,757,2.272,948,7.368,1014,7.884,1240,5.257,1312,4.142,1737,3.478,2297,3.072,3644,3.397]],["t/2213",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,862,4.662,1693,5.482,4328,6.449]],["t/2215",[2061,3.73]],["t/2217",[2,2.507,23,3.103,24,1.834,56,0.918,179,3.11,221,3.433,313,2.767,324,4.922,512,3.205,552,3.29,723,4.049,852,4.03,862,4.625,916,5.402,1114,4.217,1312,3.415,1476,3.415,2711,6.019]],["t/2219",[512,3.808]],["t/2221",[7,0.664,16,1.568,24,1.726,25,2.698,27,2.916,28,1.189,31,2.059,68,3.195,84,1.155,96,2.969,132,2.475,281,3.987,512,3.675,553,4.463,635,3.867,653,2.653,878,3.594,934,4.019,1069,4.558,1271,6.179,1290,4.689,1446,4.93,1622,6.84,1798,4.689,1819,5.408,2438,6.592]],["t/2224",[56,1.163,171,2.696,219,3.072,304,2.091,322,3.104,552,3.513,852,4.302,1312,3.647,1737,3.743,2297,3.306,3644,3.656,4356,8.082]],["t/2226",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,862,5.023,4328,6.948]],["t/2228",[7,0.836,15,2.759,16,1.408,21,1.446,24,1.457,27,2.136,29,1.786,34,1.304,49,1.15,56,1.134,84,0.554,85,1.773,103,1.713,107,1.31,109,1.518,110,3.986,127,2.481,158,2.558,160,1.794,219,2.786,221,3.324,225,1.83,322,2.366,353,1.396,395,2.023,401,1.956,410,3.378,476,1.486,527,2.174,552,3.519,562,3.715,564,4.899,596,2.305,607,1.962,613,2.314,755,2.305,757,1.261,759,1.794,760,2.235,769,1.967,837,4.377,877,1.866,878,2.183,945,4.694,993,2.955,1037,1.951,1050,4.899,1060,2.482,1284,2.503,1306,3.201,1312,1.881,1718,5.043,1732,5.63,1759,3.523,1798,2.848,1980,3.564,2225,2.725,2297,2.52,2312,4.048,2605,3.701,2632,3.058,2653,4.082,2708,3.701,2815,3.564,2867,2.995,2895,3.151,3513,3.523,4016,3.753,4054,4.168,4356,4.168,4357,4.377,4358,4.857,4359,7.18,4360,4.857,4361,7.18,4362,4.857,4363,4.857,4364,4.857,4365,4.857,4366,4.857,4367,4.857]],["t/2230",[55,5.686,192,6.158,272,4.28,418,5.226,530,4.675,596,4.709,814,4.502]],["t/2232",[2061,3.73]],["t/2234",[7,0.629,23,1.837,24,1.845,25,1.762,29,1.92,56,0.927,68,2.086,84,1.115,160,2.796,165,2.102,177,2.657,179,1.841,198,2.679,199,2.714,219,1.703,221,3.466,228,1.743,232,2.001,240,3.009,248,2.583,275,1.703,283,1.591,296,2.121,313,1.949,316,2.452,324,2.914,338,1.962,353,1.501,377,1.231,397,2.288,422,3.746,491,1.659,512,2.75,522,4.095,530,4.196,548,4.91,552,1.948,647,2.064,723,3.678,757,2.536,760,1.626,822,3.287,852,4.068,862,2.738,903,3.831,904,2.976,916,3.198,948,5.214,1059,6.376,1060,3.868,1069,4.315,1114,2.496,1235,2.714,1266,1.501,1312,2.022,1470,3.979,1476,2.022,1570,4.585,1705,3.361,1953,2.993,2033,3.098,2518,2.75,2711,3.563,3969,4.228,4322,7.57,4323,5.222,4324,5.012,4325,5.222,4326,5.222]],["t/2236",[512,3.808]],["t/2239",[56,1.163,171,2.696,304,2.091,322,3.104,548,6.108,852,4.302,948,6.487,1312,3.647,1737,3.743,2297,3.306,3644,3.656,4192,7.626]],["t/2241",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,862,4.662,1693,5.482,4328,6.449]],["t/2243",[55,5.686,192,6.158,272,4.28,418,5.226,530,4.675,596,4.709,814,4.502]],["t/2245",[2061,3.73]],["t/2247",[2,2.079,4,3.122,16,1.434,23,2.573,24,1.924,25,2.468,27,1.83,84,0.835,109,1.3,170,2.277,174,2.291,254,2.478,313,1.883,476,2.921,512,3.864,536,2.295,568,2.882,653,2.426,723,2.755,747,3.165,759,2.702,760,2.277,803,4.34,846,4.469,940,2.884,1114,3.497,1170,3.44,1266,3.056,1363,3.624,1476,2.832,1838,5.735,1902,3.676,4130,6.786]],["t/2249",[4,2.711,28,1.233,84,1.181,217,6.217,552,3.86,592,5.61,653,2.751,725,6.052,986,5.717,988,6.104,1012,7.052,1170,2.684,1266,2.384,1363,3.927,1476,3.212,1671,4.808,2092,4.892,3644,3.22]],["t/2251",[512,3.808]],["t/2253",[7,0.727,16,1.716,24,1.493,27,2.189,31,2.253,56,0.911,84,1.221,165,2.066,281,4.363,512,3.18,553,4.884,635,3.346,723,3.296,852,3.998,1271,6.762,1446,5.396,1622,5.918,1798,5.131,1819,5.918,1863,3.858,2078,8.4]],["t/2256",[23,3.342,56,1.169,171,2.719,304,2.109,322,3.131,852,4.339,1312,3.678,1737,3.775,2297,3.334,3644,3.687,4331,8.558]],["t/2258",[23,3.313,24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,635,3.601,747,4.075,4328,6.831]],["t/2260",[7,0.842,15,3.316,16,1.988,23,2.559,24,1.81,25,1.668,27,1.237,49,1.171,56,1.141,84,0.985,109,1.292,110,2.745,127,1.708,170,1.539,171,2.082,318,1.268,321,3.937,322,1.629,338,1.281,377,1.165,395,3.029,410,3.438,437,2.445,505,2.159,527,1.676,528,4.038,532,2.717,596,2.346,635,1.89,723,2.74,747,2.139,759,1.826,760,1.539,769,2.002,830,2.381,852,2.258,877,1.9,1043,2.704,1044,3.069,1050,4.963,1193,3.286,1312,1.914,1759,3.586,1773,3.937,1798,2.899,1863,2.179,1902,2.485,1918,2.592,1998,3.028,2021,3.82,2225,5.337,2297,3.029,2600,3.206,2605,3.767,2815,3.627,2867,3.048,2942,8.828,2946,4.586,4016,3.82,4331,4.454,4332,4.944,4333,4.944,4334,7.273,4335,4.944,4336,4.944,4337,4.944,4338,4.944,4340,7.273,5198,5.21,5199,5.21,5200,5.21,5201,5.21,5202,5.21,5203,5.21,5204,5.21,5205,5.21,5206,5.21,5207,5.21,5208,5.21]],["t/2262",[2061,3.73]],["t/2264",[512,3.773,575,5.198]],["t/2267",[56,1.169,171,2.719,304,2.109,322,3.131,567,4.013,852,4.339,1312,3.678,1737,3.775,2297,3.334,3644,3.687,4329,9.498]],["t/2269",[304,2.243,438,5.524,1266,2.903,1285,5.994,4330,10.102]],["t/2271",[56,1.014,304,2.165,567,4.119,651,6.847,898,4.98,1266,2.801,1285,5.784,1399,6.051,1732,7.643]],["t/2273",[24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,567,3.979,635,3.601,747,4.075,4328,6.831]],["t/2275",[16,1.911,171,2.791,304,2.165,543,5.784,567,4.119,852,4.453,940,3.843,1737,3.874,3644,3.784]],["t/2277",[56,1.042,304,2.223,401,4.032,1266,2.877,1607,6.895,1638,5.448]],["t/2279",[56,1.042,304,2.223,398,3.716,401,4.032,1266,2.877,1638,5.448]],["t/2281",[7,0.674,24,1.385,27,2.03,28,1.206,55,4.65,84,0.926,88,3.851,103,3.938,145,3.438,192,5.036,272,3.5,343,3.041,377,1.912,418,4.274,511,3.429,557,4.255,567,3.428,568,2.449,596,3.851,653,2.691,672,5.036,706,6.102,740,4.862,814,3.682,1753,5.885,1997,6.57,2009,5.885,3629,5.348]],["t/2283",[2061,3.73]],["t/2285",[7,0.633,16,1.493,19,3.575,21,2.267,29,2.799,84,1.119,105,2.51,165,1.797,219,3.536,221,2.963,275,2.484,296,3.092,304,1.691,318,2.937,512,2.767,513,3.781,562,3.941,567,3.217,580,3.587,653,2.526,723,3.692,736,3.422,757,2.815,906,4.864,1037,3.059,1107,4.227,1312,2.948,2518,4.011,2633,4.413,2636,4.603,2711,5.196]],["t/2287",[2,2.207,7,0.724,15,1.595,25,1.401,29,1.526,49,1.51,56,1.076,84,0.728,98,2.362,105,2.102,113,1.595,132,1.285,165,1.506,203,1.609,219,1.354,221,1.616,228,2.129,246,1.654,255,2.946,256,1.637,283,1.265,313,1.999,316,1.949,318,2.996,364,3.083,377,0.978,395,1.729,398,3.961,471,1.813,476,1.27,482,1.956,484,1.853,491,2.026,502,1.853,512,1.508,530,1.956,543,2.463,580,1.519,605,1.583,614,1.237,615,2.559,621,2.248,647,2.521,723,3.739,728,3.046,731,2.293,757,1.656,822,4.015,831,2.759,857,1.984,945,3.505,1006,2.238,1037,3.778,1059,2.448,1060,5.072,1114,3.049,1115,3.562,1133,3.616,1164,2.206,1170,1.343,1263,2.652,1284,4.846,1290,2.434,1312,3.007,1356,3.984,1408,2.984,1633,2.061,1688,2.978,1784,3.74,1898,3.541,1965,3.163,2026,4.697,2032,3.866,2297,1.457,2632,4.015,2895,2.692,3036,3.645,3042,2.559,3542,2.946,3749,3.046,3753,3.74,3966,3.851,4088,3.74,4555,4.375,4556,4.151,4557,4.151]],["t/2289",[2,0.805,4,0.926,7,0.5,19,1.33,25,2.03,27,1.505,28,0.698,29,1.041,31,0.729,40,1.572,56,1.233,68,1.131,84,0.886,100,1.334,109,1.81,127,3.83,140,1.169,165,1.109,172,1.714,179,1.656,182,1.656,193,2.976,219,1.963,254,0.959,256,1.116,277,1.534,279,1.289,295,1.2,313,0.729,318,1.205,327,3.74,337,2.305,338,0.734,377,0.667,395,1.956,398,1.051,405,2.603,418,1.491,476,3.036,502,0.823,511,1.578,512,1.706,552,1.752,567,1.196,575,3.012,614,0.549,647,1.857,653,2.777,723,2.637,757,1.562,803,1.68,818,3.184,828,1.244,857,1.354,936,1.276,940,1.852,942,3.116,953,1.207,986,2.594,988,2.77,1006,1.526,1037,3.119,1060,4.278,1069,2.677,1075,1.734,1107,2.607,1163,1.809,1170,1.947,1193,1.882,1240,1.701,1266,2.012,1284,1.459,1306,4.615,1309,2.627,1363,2.657,1381,1.961,1476,1.818,1530,1.795,1539,1.989,1616,2.754,1671,1.641,1693,1.746,1700,2.22,1705,3.873,1753,2.054,1765,1.712,1768,3.369,1870,1.722,1907,1.541,1924,2.43,2026,1.526,2054,5.41,2518,2.474,2632,2.956,2633,2.722,2797,3.532,3542,3.333,3878,2.293,3942,1.898,3997,1.969,4158,2.486,4230,2.379,4558,2.832,4559,2.832,4560,2.984,4561,2.832,4562,2.984,4563,2.832,4564,2.984,4565,2.832,4566,2.832,4567,2.984,4568,3.74,4569,3.74,4570,2.551,4571,2.984,4572,2.984,4573,2.984,4574,2.984,4575,2.984,4576,2.984,4577,2.984,4578,2.984,4579,2.984,4580,3.629,4581,3.683,4582,2.984,4583,3.683,4584,2.984,4585,3.683,4586,2.832,4587,2.984,4588,2.984,4589,2.984,4590,2.255]],["t/2291",[2,2.088,4,1.638,7,0.416,25,2.478,26,1.96,28,1.092,40,2.782,56,1.061,68,2.002,84,0.838,98,2.72,109,1.956,127,3.982,165,1.183,172,1.829,179,1.767,182,2.59,193,3.333,199,2.604,219,1.634,313,1.29,318,1.286,338,1.299,372,1.975,398,1.86,405,1.68,476,3.368,508,3.11,512,2.669,567,2.117,575,4.353,614,0.972,653,2.436,757,1.301,803,2.973,857,2.395,936,3.92,940,2.896,942,3.269,1006,2.701,1037,2.013,1060,3.753,1069,2.856,1075,3.068,1111,5.99,1170,2.376,1266,2.499,1306,3.302,1363,3.3,1476,2.844,1539,3.519,1616,4.307,1765,3.029,1907,2.727,2054,3.871,2518,3.869,3542,5.213,3866,4.017,3942,3.359,3997,3.485,4230,4.21,4590,3.99,4591,5.28,4592,5.28]],["t/2293",[4,2.624,7,0.667,24,0.747,27,1.096,28,1.432,34,1.175,49,1.037,56,0.456,68,1.75,84,0.5,96,1.626,107,2.998,109,1.592,116,1.722,163,1.829,170,2.069,179,1.544,182,1.544,203,1.676,213,2.849,295,1.856,338,1.722,397,2.911,476,2.741,512,2.414,527,2.427,552,1.634,560,3.727,575,2.192,580,1.603,614,1.289,647,3.808,653,2.204,663,2.819,725,2.35,736,1.968,740,2.086,940,2.62,942,4.688,966,3.488,997,3.132,1012,2.615,1046,2.158,1060,2.238,1075,4.069,1170,2.149,1245,2.799,1266,2.913,1289,1.867,1363,2.521,1382,2.757,1408,3.109,1476,2.573,1616,3.896,1622,2.962,1863,1.931,1907,2.384,2518,3.5,2839,7.971,3053,4.715,3916,3.076,3942,2.936,3943,8.21,3944,3.547,4593,4.38,5007,5.38,5008,5.134,5009,5.291]],["t/2295",[4,3.307,28,1.505,56,1.053,109,2.011,116,2.603,182,2.334,203,2.254,213,3.832,338,1.716,397,3.915,527,2.793,560,5.013,647,2.617,653,2.195,807,2.695,942,3.977,1037,2.659,1060,4.566,1170,3.273,1245,3.528,1266,3.113,1352,6.142,1476,2.563,1863,2.918,2518,4.707]],["t/2297",[4,3.346,5,3.026,28,1.522,34,1.554,56,0.982,116,2.278,182,2.042,203,2.056,213,3.496,236,1.605,256,2.284,295,2.455,338,1.501,397,3.571,511,2.739,527,2.649,560,4.573,647,4.047,653,2.704,807,5.101,940,2.284,942,4.198,1037,2.327,1060,4.165,1170,2.637,1245,3.762,1266,3.1,1363,3.093,1476,3.156,1863,2.553,2518,4.294,3736,4.111,3866,4.458,4594,5.792]],["t/2299",[4,3.288,5,2.428,24,1.716,28,1.496,31,1.684,39,2.805,84,1.012,116,2.572,144,3.301,182,2.306,203,2.236,213,3.802,232,2.507,338,1.696,367,3.509,397,2.865,414,3.954,527,2.596,560,4.973,647,2.586,653,2.17,769,4.367,807,2.664,852,2.988,942,2.911,946,5.766,1060,3.342,1170,2.116,1216,3.509,1245,3.091,1266,2.89,1476,2.533,1765,3.954,1863,2.883,2100,4.984,2518,4.67,3664,4.242,3736,4.642]],["t/2301",[4,1.404,24,0.733,28,1.496,34,1.153,56,1.209,107,1.159,109,1.964,171,1.874,172,1.568,182,2.308,193,1.646,219,1.401,246,1.712,247,1.989,254,1.455,261,2.294,296,1.744,304,0.954,327,3.421,337,2.108,338,1.113,377,1.012,485,3.421,491,1.364,502,1.248,512,2.378,527,2.044,552,1.602,600,3.017,614,1.853,647,1.698,653,2.942,658,2.167,803,2.549,818,3.464,942,3.53,953,1.831,986,2.373,997,4.399,1037,2.63,1060,4.053,1170,2.87,1245,2.766,1257,4.009,1266,2.549,1363,3.366,1408,3.063,1409,5.898,1476,3.071,1705,2.764,1768,3.081,2518,3.448,2797,3.23,4523,4.123,4568,3.421,4569,3.421,4580,3.319,4581,3.368,4583,3.368,4585,3.368,4595,4.526,4596,3.541,4597,4.526,4598,4.526,4599,6.546,4600,8.871,4601,8.871,4602,8.871,4603,6.546,4604,6.546,4605,6.546,4606,3.985]],["t/2303",[4,1.855,17,2.363,28,1.195,56,1.157,106,3.918,109,2.031,137,5.433,182,2.833,219,1.851,254,1.923,327,4.519,337,2.785,338,1.471,377,1.337,444,3.67,502,1.649,527,2.149,600,3.986,614,1.101,638,3.598,653,3.094,818,3.002,942,2.525,997,3.958,1037,3.227,1060,4.766,1170,3.018,1245,1.978,1266,1.631,1363,3.049,1367,4.105,1389,5.022,1476,3.111,1671,4.657,1705,3.652,1768,4.07,2518,4.232,2797,4.267,3010,6.691,3971,5.113,4568,4.519,4569,4.519,4580,4.384,4581,4.449,4583,4.449,4585,4.449,4596,4.677,4607,5.446,4608,5.446,4609,6.751,4610,7.239]],["t/2305",[0,2.205,4,1.875,27,0.907,34,1.54,56,1.218,106,1.4,108,3.875,109,1.865,113,1.393,127,1.252,162,1.342,171,1.642,172,4.004,182,2.511,193,3.907,203,1.447,219,1.182,236,1.004,254,2.413,261,1.935,296,1.472,304,1.274,318,0.93,338,0.939,372,2.262,377,0.854,391,1.608,408,3.57,418,1.909,426,2.771,446,2.429,476,1.755,491,1.151,502,1.667,552,3.5,600,2.546,614,1.113,647,2.268,653,2.686,725,3.82,757,1.49,813,2.473,818,3.035,829,2.012,934,1.821,942,4.176,952,2.473,953,3.035,959,4.03,966,4.569,986,5.631,988,5.534,1037,2.304,1057,2.473,1060,3.638,1170,2.619,1204,3.045,1238,2.281,1266,2.046,1306,3.781,1363,3.348,1476,3.135,1671,3.325,1705,2.333,1765,2.191,1768,2.6,2054,4.433,2220,3.624,2518,4.265,3008,2.842,3736,2.572,3866,1.982,4126,3.265,4568,2.886,4569,2.886,4580,2.8,4581,2.842,4583,2.842,4585,2.842,4596,2.987,4611,3.819,4612,3.819,4613,3.819,4614,3.819,4615,3.624]],["t/2307",[4,2.019,28,0.919,56,1.149,109,1.963,116,2.429,172,3.843,182,3.006,193,2.368,219,2.015,224,2.758,327,4.92,337,3.032,338,1.601,377,1.456,391,2.741,407,2.943,476,1.89,513,3.068,552,2.304,608,5.757,647,2.442,653,2.828,725,3.314,807,2.516,818,4.511,942,2.749,953,2.633,1037,3.425,1060,4.989,1170,2.758,1266,1.775,1476,2.392,1705,3.976,1768,4.432,1863,2.723,1906,3.541,1953,3.541,2518,4.491,2797,4.646,3910,5.093,4568,4.92,4569,4.92,4580,4.774,4581,4.844,4583,4.844,4585,4.844,4596,5.093,4607,5.93,4608,5.93,4616,9.764,4617,6.178]],["t/2309",[4,1.66,6,5.653,7,0.616,8,4.206,19,2.385,21,1.512,24,0.867,31,1.308,34,1.363,56,0.772,68,2.029,84,0.847,104,4.712,105,1.674,106,1.962,107,2.001,109,2.179,115,4.677,116,1.997,123,1.824,137,3.435,144,2.563,157,3.09,182,1.791,228,1.695,229,3.175,236,1.407,256,2.003,295,2.152,304,1.128,316,2.385,318,1.303,343,1.904,353,2.132,372,2.003,398,1.885,426,2.887,491,1.613,502,1.476,593,3.856,614,1.439,615,3.131,653,2.907,679,4.187,683,2.003,705,4.812,742,3.348,765,3.175,803,4.402,809,3.684,828,2.232,914,2.663,953,2.165,962,3.684,992,2.675,1037,2.98,1041,3.405,1060,3.79,1170,2.835,1266,2.132,1363,3.658,1476,3.394,1605,3.924,1671,2.944,1874,3.727,2518,3.908,2684,3.294,3658,4.875,4609,4.268,4618,8.765,5209,5.352]],["t/2311",[7,0.505,15,2.335,46,4.795,56,1.255,116,2.39,182,2.972,219,1.982,254,2.856,327,4.84,337,2.982,338,1.575,377,1.432,600,4.268,614,1.179,647,3.332,653,2.796,715,3.291,846,2.843,866,5.336,1037,3.386,1060,4.307,1170,2.727,1363,3.199,1367,5.341,1408,4.891,1476,2.353,1705,3.911,1768,4.359,1863,2.679,2797,4.57,3733,4.631,3866,3.323,4568,4.84,4569,4.84,4580,4.696,4581,4.765,4583,4.765,4585,4.765,4596,5.009,4609,5.106,4610,5.475,4619,6.404,4620,6.404,4621,6.077,4622,6.077,4623,6.077,4624,6.077,4625,6.077,4626,6.077,4627,6.077,4628,6.077,4629,6.077]],["t/2313",[2,1.939,7,0.567,10,5.198,16,1.338,21,2.031,25,2.302,56,1.141,84,1.041,108,3.712,109,1.621,127,3.152,160,2.52,196,2.849,221,2.655,278,2.576,313,1.756,339,3.626,377,1.608,386,5.349,413,5.433,476,3.355,490,5.066,512,2.478,544,4.496,552,2.544,677,3.787,723,2.569,736,3.065,757,2.368,945,5.012,1037,2.74,1060,4.66,1108,4.841,1284,3.515,1417,4.744,1511,4.699,2026,3.677,2032,3.026,2033,4.048,2632,4.294,2955,5.99,2956,6.146,3969,5.524,5210,7.746,5211,7.746]],["t/2315",[7,0.728,19,4.114,20,4.236,25,2.169,28,1.592,96,2.386,103,2.266,219,2.858,221,2.502,225,2.421,226,5.469,243,3.704,256,2.534,278,2.427,280,4.309,296,2.61,306,3.008,318,3.089,366,4.236,472,3.298,511,2.159,512,2.335,513,3.192,562,3.326,567,2.716,607,2.596,611,2.816,615,3.963,683,2.534,757,2.588,877,2.47,888,3.99,906,4.106,957,5.644,1175,5.644,1177,5.791,1284,3.312,1454,4.202,1530,4.076,2024,6.169,2032,2.851,2357,6.169,2636,3.886,2711,4.386,4630,6.428,4631,6.428,4632,6.428,4633,6.428,4634,6.428,4635,6.428]],["t/2317",[2,1.47,7,0.924,17,2.154,25,1.745,28,0.769,34,1.388,56,0.922,84,1.23,85,1.888,88,2.455,89,2.034,103,2.651,109,0.919,127,1.788,134,2.713,171,1.481,179,1.824,182,1.824,202,3.752,219,2.889,226,2.643,232,1.983,247,2.396,272,2.232,283,1.576,296,2.101,304,1.149,306,3.518,313,1.332,318,2.923,322,1.705,338,1.341,353,1.487,397,2.266,401,2.084,418,4.665,421,3.41,438,2.829,471,2.259,476,1.582,502,1.503,511,2.526,526,3.942,534,3.89,567,4.985,575,3.764,591,3.108,605,1.973,614,1.459,672,5.498,723,3.336,734,2.412,802,2.205,828,2.273,884,3.438,916,3.168,1363,1.963,1476,2.911,1848,4.264,1851,4.264,2297,1.816,2518,2.725,3932,4.264,4636,7.519,4637,5.173,4638,5.173]],["t/2319",[7,0.795,15,3.191,16,1.166,25,2.006,56,1.174,84,0.679,89,2.339,103,2.928,108,3.237,145,2.52,164,2.77,196,2.484,201,3.19,243,3.427,272,2.566,279,2.708,318,1.526,336,4.441,377,1.402,401,2.395,405,3.472,418,3.132,471,2.597,511,1.998,517,3.799,532,3.268,562,3.078,567,4.376,723,2.24,757,1.544,877,2.285,971,3.986,1008,3.799,1033,3.986,1238,3.744,1331,4.136,1530,3.771,2112,4.997,4639,5.947,4640,5.947,4641,5.947,4642,5.947,4643,5.947,4644,5.947,4645,5.358,4646,5.947,4647,5.947,4648,5.947,4649,5.947,4650,5.947,4651,5.947,4652,5.947,4653,5.947,4654,5.947,4655,5.947,4656,5.947,4657,5.947,4658,5.947,4659,5.947,4660,5.947,4661,5.947,4662,5.947]],["t/2321",[7,0.673,15,3.471,28,1,31,1.146,48,1.902,56,1.312,85,2.455,103,1.569,105,1.467,109,1.195,127,2.324,140,1.837,161,3.192,164,1.288,171,1.274,179,1.569,225,3.653,243,2.564,300,2.842,318,1.142,336,1.908,377,1.049,392,2.909,401,1.792,405,1.492,437,2.201,472,2.283,476,1.361,511,2.259,532,2.446,562,2.303,567,3.817,601,2.201,607,1.797,670,2.725,747,1.925,829,2.47,830,2.143,920,2.725,965,5.387,971,6.501,978,3.158,1050,3.036,1305,3.739,1306,2.933,1320,2.983,1331,3.095,1631,3.818,1632,2.434,1633,2.21,1664,3.438,1718,3.126,2297,1.562,2488,2.842,2534,4.271,3152,3.907,3154,3.907,3716,4.009,3817,4.507,4357,6.059,4645,6.059,4663,9.699,4664,6.724,4665,4.45,4666,4.45,4667,4.45,4668,4.45,4669,4.45,4670,4.45,4671,4.45,4672,4.45,4673,4.45,4674,4.45,4675,4.45,4676,4.45,4677,4.009,4678,4.45,4679,4.009,4680,4.009,4681,3.668,4682,4.45,4683,4.009,4684,4.45,4685,4.009,4686,6.059,4687,4.009,4688,4.009,4689,4.009,4690,4.009,4691,4.009,4692,4.009]],["t/2323",[7,0.398,28,0.713,31,1.234,36,2.228,49,1.136,56,1.342,68,1.916,109,0.852,127,1.657,165,1.132,203,1.794,225,1.806,226,3.633,300,3.063,318,1.23,336,2.056,377,1.13,398,3.48,405,2.384,418,2.525,426,2.767,476,1.467,567,3.581,577,2.39,601,2.372,916,2.936,965,5.633,971,6.712,978,3.403,1133,2.718,1246,4.32,1266,2.043,1284,3.664,1305,4.029,1306,3.16,1320,3.214,1331,3.334,1631,4.114,1632,2.622,1635,4.21,1664,3.705,1718,3.368,1763,4.602,1765,2.898,2383,4.602,2488,3.063,2632,4.476,3016,3.04,3152,4.21,3154,4.21,3483,5.975,3817,3.214,4590,5.663,4677,4.32,4679,4.32,4680,4.32,4681,3.952,4683,4.32,4685,4.32,4686,6.407,4687,4.32,4688,4.32,4689,4.32,4690,4.32,4691,4.32,4692,4.32,4693,4.795,4694,4.602,4695,8.476,4696,4.795,4697,4.795,4698,4.795,4699,4.795,4700,4.795,4701,4.795,4702,4.795,4703,4.795]],["t/2325",[1954,9.444]],["t/2327",[9,6.537,48,4.096,84,1.094,159,6.729,1128,9.195,1157,8.888,1475,9.195,2061,3.409,2863,9.58,3418,9.58,4448,10.096]],["t/2329",[2061,3.73]],["t/2332",[23,3.37,24,1.635,27,2.396,126,4.874,128,4.815,304,2.127,338,2.483,404,4.915,453,6.478,635,3.663,4328,6.948]],["t/2334",[7,0.646,16,1.524,23,3.495,24,1.695,27,1.945,36,3.612,56,1.034,68,3.106,79,6.827,106,3.002,126,5.054,128,4.992,131,3.716,175,4.48,200,4.058,202,5.639,203,1.961,204,6.671,207,3.344,209,7.462,219,2.536,223,4.613,304,1.726,335,6.827,339,4.132,409,4.151,416,3.772,496,5.003,500,3.876,502,2.259,1367,3.972,2297,2.729,3414,7.462]],["t/2336",[7,0.664,16,1.568,23,3.558,24,1.726,36,3.716,41,3.823,56,1.052,68,3.195,79,7.023,126,4.069,128,5.083,131,3.823,201,4.29,202,7.336,203,2.017,204,6.863,207,3.44,210,7.676,219,3.299,223,4.745,304,1.776,335,7.023,339,4.251,409,4.27,496,6.51,1367,4.086,2297,2.807]],["t/2338",[7,0.739,23,3.128,24,1.517,41,4.251,98,3.292,131,4.251,201,5.795,202,6.449,207,3.824,304,1.974,334,5.308,339,4.726,613,4.235,614,1.725,651,6.246,1583,7.472,1737,3.534,3644,3.452,4346,8.892]],["t/2340",[2,1.312,7,0.688,16,0.905,19,2.167,23,4.039,24,1.179,27,2.301,28,1.368,29,1.697,49,1.637,56,0.48,68,1.844,84,1.125,100,2.175,121,2.846,126,2.349,128,2.32,131,2.207,171,2.634,174,2.165,182,1.628,183,3.126,196,1.928,201,2.476,203,1.744,207,1.985,253,2.927,262,3.179,283,2.525,302,2.24,304,1.84,313,1.188,315,2.31,316,2.167,322,1.521,336,3.553,339,2.453,344,6.228,345,7.466,346,9.678,353,1.326,371,4.159,401,1.859,437,2.283,466,3.428,479,3.387,481,2.906,489,3.242,490,3.428,491,1.466,495,2.349,498,2.706,500,3.446,544,3.042,605,1.76,614,1.341,681,4.556,756,2.283,877,1.774,940,1.82,992,2.431,1007,5.881,1208,3.879,1289,1.968,1312,2.676,1367,3.532,1686,7.953,1699,3.018,4347,4.159,4348,6.912,4349,4.616,4350,4.616,4351,4.616,4352,4.616]],["t/2342",[2,1.424,7,0.722,12,2.129,16,1.44,23,2.584,24,0.855,28,1.424,29,1.842,84,1.257,88,4.839,89,4.01,109,0.89,127,1.731,128,2.518,129,3.154,132,1.551,164,1.45,165,1.183,169,4.233,179,2.59,213,2.148,219,1.634,236,1.388,241,4.13,275,1.634,280,3.359,296,2.035,304,1.113,313,1.29,315,2.508,316,4.083,318,1.286,337,2.459,397,3.218,404,2.571,422,3.594,440,2.328,458,2.973,470,3.818,472,2.571,476,2.66,512,3.705,532,2.754,544,3.302,545,2.44,562,3.801,568,2.624,575,5.104,605,3.316,622,2.663,681,3.302,736,2.251,753,4.514,816,2.991,822,3.154,1059,4.332,1069,4.187,1080,4.648,1114,2.395,1208,4.21,1312,1.94,1434,2.938,1849,5.455,1850,5.213,1851,4.13,1916,3.154,2518,2.639,2726,3.768,2782,3.485,4353,5.01,4354,5.01,4355,5.01]],["t/2344",[2061,3.73]],["t/2346",[7,0.796,24,1.635,28,1.424,219,3.684,502,2.784,536,3.006,550,3.967,565,3.296,568,2.891,912,7.029]],["t/2348",[7,0.637,28,1.464,34,2.642,49,2.332,163,4.112,188,5.842,219,2.501,221,2.984,295,4.172,353,2.203,377,1.807,476,2.345,484,4.396,500,3.822,502,3.16,512,3.577,536,2.406,555,3.822,577,3.822,623,3.869,653,2.543,757,2.824,780,4.301,930,4.419,1312,2.969,2009,5.561,2032,3.401,2711,6.719]],["t/2350",[4,1.893,7,0.784,25,1.954,56,1.065,101,3.249,123,2.08,132,1.793,165,1.367,196,2.419,203,1.461,219,3.339,236,1.605,285,2.522,313,1.491,317,3.051,338,1.501,348,3.917,359,2.691,372,3.214,377,1.365,449,3.757,512,2.104,527,1.878,536,3.213,565,3.245,568,2.46,605,2.209,613,2.759,630,2.254,653,1.921,736,2.603,757,2.658,930,4.698,955,4.775,1107,3.216,1139,3.266,1202,3.216,1245,2.02,1284,2.985,1328,3.646,1700,6.392,1844,3.728,2030,5.086,2031,4.069,2032,4.541,2297,2.033,2312,6.084,2395,3.199,2447,4.155,2633,3.357,2711,3.953,3037,7.345,3857,4.25,4178,5.792,4179,8.152,4180,5.792,4181,5.792]],["t/2352",[28,1.451,34,2.03,49,1.791,84,0.863,96,2.807,165,1.785,190,4.096,219,2.467,232,2.899,295,3.205,304,1.679,398,4.011,461,4.46,536,3.709,552,2.821,567,4.124,605,2.884,614,1.467,731,4.177,757,2.534,852,3.455,1033,5.07,1266,2.173,2032,4.329,3016,7.495,3866,4.136,4182,7.563,4183,7.563]],["t/2354",[1,0.911,5,0.997,7,0.737,12,0.632,16,1.136,17,0.62,23,0.945,24,1.9,25,1.238,26,0.582,27,0.372,28,1.488,34,1.206,41,1.284,48,1.148,49,0.636,56,0.924,68,0.595,84,0.898,88,0.706,89,1.767,98,0.551,103,1.585,105,0.885,107,0.99,109,1.03,116,1.056,123,0.534,128,1.35,132,2.524,133,0.671,160,0.55,162,0.994,163,1.533,164,0.777,165,1.226,170,0.836,179,0.947,182,2.227,183,1.215,196,0.621,198,0.764,199,0.773,203,1.593,219,1.197,220,1.742,221,1.045,223,0.883,232,1.722,236,1.439,240,0.858,246,1.07,255,1.056,275,0.485,277,0.806,278,0.562,283,0.453,285,2.523,286,1.716,289,1.528,295,0.631,296,0.604,302,0.722,304,0.33,323,0.632,324,0.83,328,1.868,337,0.73,338,0.951,343,0.558,347,1.035,351,0.568,353,0.772,372,1.447,377,1.489,392,0.973,398,0.552,401,0.599,407,0.709,409,1.96,416,0.722,426,2.458,430,0.773,444,0.68,463,0.944,472,1.378,476,2.306,482,0.701,483,0.989,484,1.639,500,0.742,511,1.233,512,2.461,517,0.951,525,0.739,527,1.737,528,0.826,530,0.701,536,2.253,545,0.725,555,0.742,560,1.506,565,2.594,568,1.567,575,2.249,578,0.616,580,0.544,601,1.328,605,1.714,611,1.176,614,1.525,621,0.806,622,0.791,629,0.998,630,2.021,635,0.569,647,1.062,653,1.723,681,1.77,683,0.587,685,0.873,723,1.012,731,0.822,736,1.649,743,1.035,756,0.736,757,2.248,759,2.14,760,1.143,769,1.486,780,0.835,784,1.15,803,1.594,807,2.115,811,0.973,827,0.795,828,0.654,832,1.056,841,0.725,846,1.257,855,1.994,857,1.755,874,0.77,878,0.669,880,0.675,930,0.858,936,2.848,940,2.491,942,3.853,1009,0.722,1046,0.733,1059,1.584,1069,2.092,1075,1.645,1114,2.483,1139,0.839,1169,0.998,1170,1.187,1174,1.045,1181,0.814,1223,2.191,1239,0.965,1240,0.894,1242,1.25,1245,1.567,1266,2.488,1312,0.576,1363,2.861,1367,0.76,1380,0.989,1381,0.621,1397,0.951,1408,1.717,1441,1.703,1454,2.399,1476,1.04,1493,1.8,1498,1.119,1530,0.944,1616,1.575,1633,0.739,1671,0.862,1689,0.822,1733,0.937,1863,1.184,1902,0.748,1906,0.853,1907,0.81,1960,1.045,2027,0.981,2028,3.084,2031,3.157,2032,0.66,2033,0.883,2035,1.341,2061,0.529,2075,1.307,2092,0.878,2159,1.381,2353,0.937,2606,1.227,2627,1.277,2633,0.862,3051,1.056,3632,1.703,3649,2.305,3664,0.965,3715,2.972,3866,1.469,3916,1.045,3917,0.998,3942,1.8,3983,1.428,3997,1.035,4038,1.341,4056,1.185,4184,1.488,4185,1.105,4186,1.105,4187,1.105,4188,1.105,4189,1.105,4190,1.105,4191,1.105,4192,1.205,4193,1.428,4194,1.488,4196,1.488,4197,1.488,4198,2.686,4199,1.488,4200,1.488,4201,1.488,4202,1.428,4203,1.488,4204,1.488,4205,1.488,4206,1.488,4207,1.488,4208,1.488,4209,1.488,4210,1.488,4211,1.488,4212,1.488,4213,1.488,4498,1.205,5212,1.568,5213,1.568]],["t/2356",[7,0.702,23,1.378,24,1.876,27,0.98,28,1.362,31,1.009,48,1.675,49,0.928,56,0.879,84,0.855,88,1.859,89,3.323,98,1.451,103,1.381,116,2.398,128,1.969,132,1.212,163,2.547,171,1.122,177,1.993,182,2.15,198,2.01,219,1.278,221,1.525,225,1.476,232,2.337,285,1.706,286,2.502,302,1.901,304,1.354,313,1.009,322,1.291,328,2.724,343,1.468,377,0.923,401,1.578,444,1.79,472,2.01,502,1.138,512,3.523,527,1.405,545,1.908,555,1.953,565,2.576,575,3.052,614,1.776,630,2.373,653,2.803,670,2.399,681,2.582,736,1.76,756,1.938,757,1.944,769,3.422,780,2.197,807,1.595,841,1.908,857,1.873,936,1.766,942,4.075,946,4.443,1069,4.268,1075,2.399,1170,1.267,1216,2.102,1245,2.126,1266,2.786,1363,3.207,1376,2.063,1408,3.503,1476,2.361,1557,2.561,1616,2.297,1633,1.945,1671,2.27,1689,2.164,1754,1.755,1765,2.368,1906,3.495,1907,2.132,2100,2.985,2672,2.698,2696,2.78,3632,3.867,3664,3.955,3693,5.355,3916,2.752,3917,2.626,4185,2.909,4186,2.909,4187,2.909,4188,2.909,4189,2.909,4190,2.909,4191,2.909,4192,3.172,4214,5.495,4215,3.53,4216,3.027,4217,3.634,4218,2.946]],["t/2358",[7,0.31,23,2.066,24,1.527,28,1.413,34,1.002,48,1.596,49,0.884,56,0.755,63,3.023,84,0.829,88,1.772,89,3.237,98,1.383,103,1.316,116,2.31,163,3.032,171,1.069,177,2.988,182,2.071,198,1.916,203,1.481,219,1.218,221,1.453,228,1.246,232,2.251,283,1.138,285,1.625,286,2.385,295,1.582,304,1.304,322,1.231,328,2.597,338,0.968,343,1.399,377,1.384,401,1.504,405,1.252,426,1.453,444,1.706,472,1.916,476,2.907,491,1.186,502,1.707,512,3.612,527,1.353,536,1.843,545,2.86,558,1.759,565,2.021,575,3.634,577,5.135,614,1.736,630,2.286,653,2.73,683,1.472,736,1.678,756,1.847,757,1.885,780,2.094,796,2.062,841,1.818,936,3.273,940,2.316,941,2.885,942,4.229,1037,3.307,1046,1.839,1069,4.138,1170,1.9,1245,2.048,1266,3.045,1363,3.396,1476,2.274,1616,2.189,1633,1.854,1689,2.062,1754,1.672,1906,3.366,1907,2.032,2606,3.078,2672,2.572,2836,3.023,3632,3.724,3694,5.157,3942,3.937,4185,2.773,4186,2.773,4187,2.773,4188,2.773,4189,2.773,4190,2.773,4191,2.773,4216,2.885,4218,2.808,4219,5.292,4220,3.734,4221,3.734,4222,3.734,4223,3.734]],["t/2360",[4,0.61,5,0.693,7,0.554,21,0.556,23,0.657,24,1.229,25,1.487,27,0.467,28,1.373,31,0.481,34,1.182,36,1.528,41,0.893,48,0.798,49,1.257,56,1.057,68,0.746,84,0.822,88,0.886,89,2.087,98,1.218,103,1.16,105,1.084,107,2.848,109,1.831,116,1.733,123,0.671,125,1.122,128,1.653,132,1.018,133,0.842,134,1.724,140,1.358,162,0.692,163,2.217,165,1.253,170,2.243,171,1.732,182,1.16,183,0.845,190,1.011,198,0.958,203,0.471,219,1.073,221,0.727,232,1.26,236,0.911,240,1.076,246,1.756,256,0.736,285,0.813,286,1.193,295,1.394,296,0.758,304,0.73,313,0.481,322,1.452,328,1.299,336,1.41,338,1.73,343,0.7,377,0.775,382,1.221,391,0.828,394,0.984,401,0.752,405,2.237,408,1.251,426,2.355,444,0.853,472,0.958,476,2.042,491,0.593,502,0.543,505,1.924,511,1.48,512,2.783,527,1.765,536,2.898,545,0.91,550,0.773,557,0.979,565,1.826,568,0.564,575,2.205,580,0.683,605,0.712,614,1.563,623,0.942,629,1.252,630,1.28,647,2.392,653,2.214,659,1.274,660,1.464,663,1.202,681,2.167,723,1.239,724,1.687,736,1.478,756,0.924,757,1.571,760,0.581,772,1.299,780,1.048,820,1.011,827,0.997,828,1.445,841,0.91,857,1.572,877,0.718,878,0.839,880,1.492,919,1.354,936,1.986,940,2.093,942,4.109,953,0.796,981,1.221,993,1.136,997,1.283,1009,0.906,1046,0.92,1069,2.511,1075,2.014,1139,1.053,1170,1.064,1223,1.115,1245,1.536,1266,2.415,1289,1.402,1352,0.946,1363,2.734,1408,2.062,1441,1.184,1473,0.877,1476,1.706,1540,1.089,1558,2.614,1616,1.928,1633,0.927,1689,1.032,1754,0.836,1822,1.184,1864,1.826,1870,2.448,1882,1.159,1899,1.37,1906,1.885,1907,1.016,2033,1.108,2231,1.423,2293,4.555,2296,4.101,2672,1.286,2839,5.779,3053,2.334,3632,3.366,3644,0.725,3695,4.661,3725,1.404,3817,1.252,3880,1.286,3916,1.312,3942,2.204,3943,5.405,3944,1.512,3952,3.97,4185,1.387,4186,1.387,4187,1.387,4188,1.387,4189,1.387,4190,1.387,4191,1.387,4216,1.443,4218,1.404,4224,4.406,4225,1.602,4226,2.822,5007,2.663,5008,2.541,5009,2.619,5064,1.733,5214,1.602,5215,1.683]],["t/2362",[7,0.548,21,0.767,23,0.906,24,0.959,25,0.869,27,0.644,28,1.38,34,0.691,36,1.197,41,2.075,48,1.101,49,1.028,56,1.026,84,0.495,88,1.223,89,2.595,92,1.005,98,0.954,103,0.908,105,0.849,107,0.695,109,1.866,116,1.706,127,3.906,128,1.295,132,1.343,133,1.161,140,1.064,162,0.954,163,3.076,164,0.746,165,1.024,171,1.889,172,0.94,177,2.208,179,0.908,182,1.53,183,1.963,193,1.663,198,1.322,203,1.095,219,0.84,221,1.003,232,1.663,256,1.016,257,1.382,275,0.84,279,1.173,285,1.122,286,1.645,295,1.092,304,0.964,322,2.631,328,1.792,336,1.861,338,1.125,343,0.966,351,0.982,359,1.197,377,1.325,391,1.925,394,2.286,401,1.038,405,2.212,426,1.689,444,1.177,472,1.322,476,2.839,502,0.749,512,3.085,527,1,536,2.665,545,1.255,565,2.534,575,2.814,605,0.982,614,1.861,615,1.588,623,1.3,630,1.689,653,2.188,681,1.698,732,1.774,736,1.158,755,1.223,756,1.274,757,2.073,780,1.445,841,1.255,857,2.688,876,1.848,930,1.485,936,1.161,942,3.552,999,1.476,1046,1.269,1069,3.205,1075,1.578,1170,1.404,1245,1.513,1266,2.667,1274,1.658,1289,1.098,1363,2.794,1476,1.68,1616,1.51,1633,1.279,1689,1.423,1754,1.944,1765,1.557,1870,3.404,1906,2.487,1907,1.402,2092,1.519,2282,1.809,2291,5.794,2295,5.173,2672,1.774,3577,2.262,3632,4.183,3644,1,3696,5.794,3817,1.727,3942,2.909,4185,1.913,4186,1.913,4187,1.913,4188,1.913,4189,1.913,4190,1.913,4191,1.913,4216,1.99,4218,1.937,4225,2.211,4226,3.724,4227,5.216,4228,2.39,4229,2.576,4230,2.165]],["t/2364",[7,0.72,21,1.342,23,0.948,24,1.289,26,1.054,27,0.674,28,1.123,31,1.16,36,1.252,37,1.893,48,1.152,49,1.067,56,1.015,84,0.862,88,1.279,89,2.67,98,0.998,103,0.95,107,0.727,109,1.733,116,1.772,127,0.931,128,1.354,132,0.834,162,0.998,163,1.882,164,0.78,165,0.636,170,2.941,171,1.944,177,1.371,182,1.589,183,1.219,190,1.459,198,1.383,219,0.879,221,1.049,225,1.697,228,0.899,232,1.727,240,1.553,247,1.248,285,1.173,286,1.721,302,1.307,304,1.001,313,0.694,322,1.914,323,1.915,328,1.874,336,1.155,343,1.01,353,0.774,377,0.635,391,1.195,401,1.085,444,1.231,472,1.383,484,1.203,491,0.856,502,0.783,512,2.967,527,1.038,536,2.563,545,1.312,555,1.343,565,2.599,568,0.813,575,2.256,605,1.028,614,1.584,623,1.36,630,2.26,653,2.506,670,1.65,677,1.496,681,1.776,736,1.211,756,1.333,757,1.961,769,3.308,780,1.511,807,1.097,830,1.298,841,1.312,857,1.288,936,1.215,942,3.362,946,4.973,953,1.149,958,1.856,992,1.419,1006,1.452,1009,1.307,1069,3.311,1075,1.65,1170,1.458,1210,2.182,1216,1.446,1245,1.571,1266,2.492,1289,1.149,1300,1.856,1312,1.043,1363,2.576,1376,1.419,1408,2.718,1453,2.586,1476,2.249,1527,1.874,1557,1.762,1616,1.58,1633,1.338,1671,1.562,1688,1.933,1689,1.488,1754,2.018,1765,1.629,1870,2.996,1902,4.569,1906,2.583,1907,1.466,1916,1.696,2013,1.791,2100,2.053,2672,1.856,2696,1.912,2717,1.856,3199,2.428,3632,2.857,3644,1.046,3664,2.923,3916,1.893,3917,1.806,4185,2.001,4186,2.001,4187,2.001,4188,2.001,4189,2.001,4190,2.001,4191,2.001,4192,2.182,4216,2.082,4217,2.5,4218,2.026,4225,2.312,4226,3.867,4227,2.5,4471,2.5,5060,6.117,5216,7.154,5217,4.749,5218,4.749]],["t/2366",[27,1.83,28,1.42,56,1.107,84,0.835,196,3.055,405,4.096,426,4.388,495,3.722,511,3.573,512,2.657,536,2.295,565,2.517,568,2.882,583,4.708,647,2.892,729,5.501,757,1.899,936,3.297,940,3.766,946,4.634,958,5.038,981,4.782,1363,2.776,1381,3.055,1693,4.509,1902,3.676,2550,5.574,3795,5.501,5214,6.277,5215,6.59]],["t/2368",[5,3.633,7,0.631,23,1.846,28,1.455,41,2.508,56,1.018,70,3.945,84,0.599,89,3.511,106,2.934,113,2.016,116,2.063,128,2.637,164,2.199,178,6.261,196,2.191,228,2.536,239,3.172,256,2.995,289,3.163,407,2.499,408,1.996,426,2.956,433,3.192,495,4.543,511,3.638,512,1.906,536,1.647,565,1.805,568,2.292,615,3.235,635,2.006,647,3.003,712,2.974,756,2.595,757,1.362,769,2.125,807,4.82,878,2.357,936,2.365,940,3.859,946,3.686,981,6.399,1050,3.58,1266,1.508,1282,4.248,1381,2.191,1693,3.235,1865,4.502,1876,3.58,1902,2.637,2525,8.045,3624,4.248,3795,3.945,4232,8.283,4314,4.607,5219,8.005,5220,5.036]],["t/2370",[7,0.807,16,0.876,24,1.151,28,1.003,56,1.257,84,0.77,88,2.12,96,1.658,107,1.205,109,0.794,127,2.331,140,1.845,162,1.654,164,1.293,165,1.592,171,1.279,177,3.432,179,2.378,219,3.169,277,2.42,296,1.814,322,2.678,377,1.053,392,2.921,491,2.142,502,1.96,536,3.631,550,1.85,565,2.321,567,4.812,577,2.227,653,1.482,672,4.187,734,2.083,798,2.589,818,3.569,884,4.483,916,2.736,946,2.168,996,2.969,1040,3.24,1170,1.445,1224,3.021,1312,2.611,1363,2.559,1476,1.73,1870,2.98,1933,3.138,1963,3.404,2028,3.754,2032,2.992,2291,3.923,2293,3.834,2295,3.503,2296,3.452,2297,3.41,3016,2.833,3597,3.923,3632,6.16,3649,3.834,3693,3.923,3694,3.923,3695,3.923,3696,3.923,4214,4.025,4219,4.025,4233,5.668,4234,4.145,4235,4.468,4236,9.014]],["t/2372",[24,1.651,36,3.467,56,1.117,68,2.981,84,1.226,113,3.718,203,1.882,304,2.384,398,2.77,401,3.005,482,3.516,536,2.342,543,4.427,565,2.567,567,3.153,577,3.72,578,3.09,614,1.447,622,3.966,756,3.691,940,3.815,1239,4.84,1266,3.085,1312,2.889,1540,4.349,1607,5.139,1638,4.061,1801,4.482,3655,5.765,4237,7.462,4238,9.675,4239,7.462]],["t/2374",[7,0.852,15,3.141,16,1.603,88,3.879,103,2.882,113,3.141,199,4.248,225,3.079,226,5.239,243,4.711,292,3.746,377,1.926,491,2.596,511,2.746,536,2.565,550,3.385,565,3.528,567,4.332,593,4.248,672,5.073,719,5.182,798,4.737,856,5.145,940,3.223,1179,5.301,1302,5.578,2009,5.928]],["t/2376",[7,0.585,17,3.878,28,1.651,84,0.803,89,2.767,105,2.319,190,3.811,219,3.768,232,2.697,283,2.144,294,3.989,377,1.658,461,4.15,536,3.724,552,2.625,567,5.014,653,3.088,731,3.887,757,2.884,852,3.215,2032,4.629,3016,6.616,4240,7.037,4241,11.105]],["t/2378",[2,1.225,4,2.146,7,0.795,23,1.517,24,1.12,25,1.455,27,1.642,28,1.182,56,1.049,84,1.233,88,2.046,89,1.696,109,0.766,113,2.523,127,1.49,132,1.335,163,1.801,165,1.55,171,1.234,174,1.351,177,2.194,182,1.52,193,1.653,219,2.142,223,2.558,232,1.653,240,2.485,275,2.142,278,1.628,283,1.314,285,1.877,294,2.444,313,1.69,322,1.421,338,1.118,447,2.15,472,4.08,476,2.719,498,2.528,502,1.253,512,3.475,525,2.141,527,0.993,536,3.163,558,3.094,565,1.484,567,2.774,568,1.301,575,5.044,614,0.836,736,3.573,755,2.046,757,1.705,765,2.695,773,3.554,796,2.382,816,2.574,877,1.657,893,2.623,936,1.944,940,1.7,997,1.682,1006,2.324,1046,2.124,1069,4.532,1075,2.641,1114,3.801,1196,3.623,1363,3.824,1366,2.528,1382,2.714,1408,3.072,1435,3.06,1446,2.658,1476,1.67,1616,5.908,1907,2.347,1945,3.029,2033,2.558,2297,1.514,2711,2.942,3662,4.139,3916,3.029,3942,2.89,3997,2.999,4242,4.312,4243,4.312,4244,4.312]],["t/2380",[2,1.505,7,0.635,21,2.927,24,0.904,26,2.072,36,3.554,49,2.469,56,0.551,84,1.19,96,3.332,98,3.324,107,2.422,109,1.359,113,2.939,127,1.83,140,2.187,160,1.957,164,1.533,165,1.25,203,2.264,304,1.176,313,1.364,336,2.271,338,1.373,353,1.522,377,1.248,502,1.539,509,3.162,536,3.902,577,4.475,578,2.193,604,3.683,629,3.55,653,2.977,692,4.451,756,2.62,805,3.799,876,3.799,885,4.594,1046,4.843,1114,2.532,1224,3.582,1289,2.258,1312,3.476,1779,3.759,1792,5.916,1870,3.824,1899,3.886,2032,3.393,4245,7.648]],["t/2382",[4,2.752,7,0.699,48,3.6,49,1.994,56,1.087,84,0.961,140,3.477,171,2.411,203,2.124,272,3.633,295,3.568,322,2.776,536,2.643,613,4.011,614,2.027,731,5.77,802,3.589,828,4.591,2297,2.956,2963,6.333,3644,3.269,4246,7.856]],["t/2384",[2,1.03,4,1.875,12,1.54,24,1.382,29,1.332,36,1.684,49,1.359,56,1.263,68,1.448,84,0.924,96,1.345,98,2.124,105,1.195,179,1.278,203,1.447,219,1.871,254,1.228,304,1.798,322,1.195,336,1.554,372,2.807,377,1.352,391,1.608,395,1.509,398,2.643,401,1.46,444,1.656,472,1.86,482,1.708,536,3.553,543,2.15,565,1.247,567,3.008,577,1.807,623,1.829,647,2.815,653,1.202,736,1.628,757,0.941,780,4.542,914,1.9,940,2.262,942,2.553,946,1.758,1046,2.827,1164,1.926,1170,3.609,1239,2.351,1266,2.929,1289,1.545,1312,4.32,1363,3.072,1476,1.403,1540,2.113,1607,2.496,1616,2.125,1638,1.972,1689,2.002,1765,3.468,1792,2.389,1906,2.077,1907,1.972,2031,2.546,2032,2.545,2275,2.935,2297,1.272,2606,2.987,3016,3.638,3632,2.298,3655,2.8,3866,4.825,4004,2.842,4005,3.045,4215,3.265,4246,2.725,4247,5.506,4248,5.737,4249,3.265,4250,3.624,4251,3.265,4252,3.624,4253,3.265,4254,3.624,4255,3.265,4256,3.624,4257,3.265,4258,5.737,4259,3.265,4260,3.624,4261,3.265,4262,3.624,4263,3.479,4264,3.624,4265,3.624,4266,3.624,4267,3.624,4268,5.737,4269,3.624]],["t/2386",[1,4.256,7,0.577,24,1.77,49,1.646,56,1.23,84,1.184,171,1.99,272,2.998,322,3.043,372,2.74,377,2.176,395,2.894,397,4.045,430,3.612,482,3.275,536,3.787,550,2.878,565,2.391,611,3.044,874,3.596,916,4.256,946,4.48,1046,3.424,1170,2.248,1289,2.962,1397,4.439,1454,6.037,1792,4.58,2297,3.241,4246,6.944,4270,9.234,4271,6.949]],["t/2388",[2,1.457,7,0.426,12,2.178,36,2.382,49,1.769,56,1.225,84,1.105,107,2.377,109,1.72,127,3.556,162,1.898,165,1.21,179,1.807,183,2.318,275,1.672,296,2.082,304,1.138,313,1.32,322,2.904,336,3.778,391,3.909,536,3.48,614,1.709,647,2.026,757,1.331,827,2.737,946,4.275,1170,2.416,1289,3.184,1844,3.299,1863,3.884,1870,4.424,1902,4.428,2297,1.799,3999,9.678,4000,9.678,4096,4.501,4097,4.501,4246,3.855,4249,4.618,4251,4.618,4255,4.618,4257,4.618,5061,9.292]],["t/2390",[2,1.52,7,0.64,49,1.824,56,1.287,84,1.127,85,1.952,109,1.937,162,1.98,165,1.262,179,2.715,296,2.171,313,1.377,322,1.762,336,2.293,391,2.372,452,3.878,502,1.554,536,3.284,577,6.063,614,1.037,705,3.468,757,2,827,2.855,914,2.804,1037,3.966,1170,2.92,1289,2.279,1754,2.395,1844,3.442,1870,4.36,2275,4.33,2297,1.877,4004,4.193,4005,4.493,4246,4.021,4253,6.939,4259,6.939,4261,4.818,4272,4.818,4273,5.347,4274,5.347,4275,9.025,4276,5.347,4277,5.347,4278,5.347]],["t/2392",[2,0.843,4,0.97,5,1.101,7,0.662,15,2.39,16,1.219,18,2.261,23,2.535,24,0.506,27,0.742,28,0.726,31,0.764,32,1.956,34,0.796,56,1.099,68,1.186,84,0.909,85,1.083,88,1.408,89,1.167,105,1.609,116,1.167,131,2.334,138,1.261,156,2.129,160,1.096,162,1.099,164,1.413,165,1.88,171,1.398,174,0.929,186,1.474,196,1.239,201,1.592,219,1.592,228,0.991,232,2.384,239,1.794,275,0.968,283,2.427,284,2.106,292,1.36,296,1.982,304,0.659,306,2.284,313,1.601,317,1.563,318,1.252,322,2.05,347,2.064,348,2.007,351,1.862,359,1.379,361,2.106,366,1.956,372,1.17,377,0.699,391,1.316,393,2.526,395,1.236,397,2.724,405,2.085,407,1.413,408,1.129,415,2.204,440,1.379,441,1.701,466,2.204,471,1.296,512,1.078,530,1.398,536,1.952,539,5.386,543,4.276,544,1.956,552,1.107,558,2.3,572,1.656,575,1.485,614,0.576,621,2.644,712,1.682,734,1.384,747,1.284,790,3.582,795,2.044,796,1.639,802,2.081,807,1.208,851,1.639,893,1.805,930,2.813,959,2.084,1007,4.414,1045,2.084,1114,2.334,1139,2.753,1169,1.989,1181,2.67,1212,7.881,1215,2.293,1216,1.592,1219,5.036,1222,2.967,1234,1.771,1235,2.537,1290,1.74,1312,3.084,1366,1.74,1376,1.563,1378,2.848,1405,2.546,1473,1.393,1493,1.989,1502,2.674,1503,2.674,1507,7.061,1535,3.953,1536,3.772,1540,1.73,1542,6.917,1559,2.403,1560,2.848,1563,2.546,1606,2.967,1607,2.044,1608,2.967,1609,2.967,1610,2.967,1611,2.967,1612,2.967,1613,2.967,1614,2.967,1615,2.967,1616,1.74,1622,4.206,1623,4.882,1775,2.606,1843,2.848,1898,2.71,1929,2.967,1933,3.429,1934,2.967,1965,2.261,2297,1.042,2672,2.044,4279,2.753,4280,2.753,5221,2.848,5222,3.127,5223,3.127,5224,3.127,5225,3.127,5226,3.127,5227,3.127,5228,3.127,5229,3.127]],["t/2394",[7,0.976,16,1.183,24,1.827,25,2.504,27,1.509,28,1.709,31,2.593,40,3.349,56,1.21,103,1.362,107,2.002,109,1.49,115,2.927,123,3.617,127,3.601,162,1.43,219,1.26,223,2.292,276,3.149,468,4.043,476,1.845,491,1.916,527,1.39,528,2.145,565,3.684,568,2.24,601,2.984,614,1.17,630,3.264,635,1.477,757,1.003,759,2.228,810,2.822,814,3.368,999,3.457,1114,1.847,1239,2.506,1328,2.432,1435,2.742,1473,1.814,1754,3.324,1864,2.145,2411,4.661,2636,3.646,2974,3.863,2976,3.863,2977,3.863,2978,8.387,2979,6.032,2980,3.863,2981,3.863,2982,7.421,2983,3.863,2984,3.863,2985,3.863,2986,3.863,2987,6.032,2988,6.032,2989,6.032,2990,6.032,2991,6.032,2992,6.032,2993,6.032,4447,4.071]],["t/2396",[7,0.825,27,1.945,31,2.001,34,2.086,109,1.381,115,3.772,123,2.792,135,4.67,194,6.295,238,4.699,258,7.774,269,7.462,306,5.123,507,5.693,591,5.967,607,3.14,732,5.355,795,5.355,854,4.361,941,6.007,996,5.167,1135,6.827,1136,7.212,1137,5.639,1275,6.718,1397,4.966,2232,5.773,2657,8.192,3940,7.212,4449,8.192,4450,7.774,4451,8.192]],["t/2398",[2061,3.73]],["t/2400",[2,1.4,7,0.91,16,0.966,24,1.621,25,1.662,26,1.173,27,1.814,28,1.191,29,1.103,30,1.977,31,2.342,33,1.802,34,0.805,40,2.734,41,1.434,48,1.282,56,0.946,84,0.715,98,2.32,101,1.682,103,1.737,107,1.957,109,1.53,113,1.153,115,3.04,123,3.411,127,2.506,132,0.928,140,1.239,158,1.58,160,1.819,161,3.533,162,2.32,165,1.479,172,1.798,174,2.272,190,1.624,193,1.15,225,1.13,228,1.001,232,1.15,236,1.365,275,2.044,276,2.571,278,1.86,313,1.268,318,1.264,332,1.64,337,2.417,343,1.846,365,1.602,377,0.707,409,2.63,421,1.977,476,1.506,478,1.916,502,1.431,527,2.096,530,1.413,536,2.514,550,3.767,552,1.119,553,1.674,558,2.321,565,3.384,568,2.745,572,1.674,576,1.862,577,2.455,605,1.144,607,2.531,608,1.64,610,2.429,613,2.984,615,1.849,630,3.118,635,1.147,662,1.837,664,2.318,690,1.961,698,2.286,705,1.945,706,2.256,747,1.298,757,2.08,795,2.066,796,2.72,798,1.738,810,1.403,874,1.552,886,2.011,903,2.201,904,1.71,930,1.729,935,2.429,936,1.352,940,1.183,995,2.151,999,1.719,1029,2.175,1049,2.52,1066,1.559,1107,2.734,1111,3.392,1117,1.71,1164,1.594,1188,2.429,1221,2.352,1361,2.783,1754,2.206,1836,4.138,1916,3.1,1933,2.107,1953,1.719,2013,1.993,2527,2.634,2696,2.129,2782,2.086,2994,2.999,3005,2.879,3391,2.702,3578,3.161,3976,2.783,4233,2.52,5230,3.406,5231,3.406]],["t/2402",[2,1.09,4,1.254,5,1.424,7,0.864,27,2.09,28,0.892,31,1.902,36,1.783,42,2.964,49,2.147,56,0.624,84,1.035,92,2.883,93,3.298,106,2.317,109,1.066,131,1.834,134,2.012,138,3.854,144,1.936,153,2.068,165,1.416,171,1.098,172,3.99,173,3.383,181,2.29,188,2.923,193,1.47,207,1.65,216,2.058,224,2.679,228,1.281,236,1.063,256,1.513,276,3.132,281,1.913,288,2.668,289,2.499,292,1.758,302,1.861,318,0.984,334,2.29,337,1.883,338,1.555,339,3.189,365,2.049,376,2.305,377,1.414,404,1.968,409,2.049,441,2.199,468,2.572,500,2.991,502,1.115,525,1.905,527,0.884,552,1.431,555,1.913,568,2.521,578,1.589,579,2.923,580,1.404,601,4.485,620,3.497,629,2.572,632,2.175,636,3.56,638,2.433,691,4.842,715,2.078,728,2.815,732,2.643,743,2.668,774,2.187,807,1.562,810,4.243,812,5.19,841,1.869,862,2.012,877,1.474,878,3.32,906,2.451,907,2.594,927,3.055,952,2.618,995,2.752,1024,2.572,1025,4.778,1066,1.994,1274,2.469,1360,3.604,1364,2.923,1365,3.954,1376,2.021,2092,2.263,2353,5.259,2646,3.457,2712,4.043,2805,3.107,2963,2.885,2996,3.457,2997,6,2999,3.682,3000,3.837,3312,3.559,4129,4.043,5232,3.682,5233,4.356]],["t/2404",[2,1.527,7,0.871,27,2.731,28,1.471,30,5.093,31,1.383,84,0.613,92,2.097,101,3.014,109,0.955,126,2.734,138,2.284,153,2.896,162,1.99,163,2.244,171,2.212,172,3.612,173,4.358,174,2.834,184,4.279,193,3.469,196,2.244,224,2.399,228,1.794,246,2.141,282,3.079,304,1.193,313,1.383,351,2.049,407,2.559,416,2.607,478,3.432,502,1.561,525,4.494,552,2.004,568,1.621,578,2.225,597,4.206,599,2.91,614,1.042,620,3.132,631,3.701,632,5.13,638,3.407,658,2.711,675,3.602,725,2.883,741,3.667,807,3.147,819,3.99,830,2.588,831,3.571,832,6.422,877,2.065,986,4.268,1009,2.607,1012,4.613,1024,3.602,1028,3.737,1365,3.541,1446,3.313,1847,3.813,2639,4.213,2787,3.602,2966,3.854,3002,5.373,3003,4.515,3004,4.718,3061,4.611,4456,5.662]],["t/2406",[0,6.416,7,0.71,21,3.14,27,2.86,109,2.032,127,3.644,179,3.718,193,3.277,343,3.205,437,4.229,557,4.483,631,7.264,757,2.22,816,5.104,2522,8.539]],["t/2408",[7,0.754,84,0.774,109,1.614,133,3.056,160,3.784,165,1.6,179,2.391,221,3.535,228,3.032,275,2.963,320,5.491,377,1.598,426,2.639,430,3.524,445,4.506,476,2.778,491,2.154,498,3.975,502,1.97,527,2.36,565,2.333,568,2.741,635,3.473,811,4.432,936,3.056,939,4.397,1049,5.697,1066,3.524,1276,5.316,1329,5.4,1366,3.975,1416,8.579,1481,5.589,1898,3.764,1970,5.697,2043,5.035,2064,6.831,2516,5.589,2665,7.145,3005,6.507,3006,6.109]],["t/2410",[7,0.531,21,1.903,28,1.298,31,1.645,33,3.839,37,4.489,42,4.938,46,2.791,56,0.665,84,0.729,90,3.173,92,2.494,93,2.853,106,2.468,107,2.354,109,1.898,115,4.234,126,3.252,153,3.445,165,1.509,189,3.604,216,3.429,224,3.896,272,2.757,318,1.64,353,1.836,407,4.157,426,3.396,502,1.857,513,3.173,620,3.725,629,4.284,635,2.443,658,3.225,769,2.588,893,3.888,909,4.635,936,2.881,951,5.902,997,4.503,1009,3.101,1051,5.175,1066,3.322,1398,5.929,1434,3.747,1445,4.689,1733,4.023,1814,4.806,1845,3.995,2928,5.612,2966,4.584,3008,5.011,3009,5.268,3010,4.584]],["t/2412",[1,2.747,2,1.923,7,0.753,8,2.543,28,0.667,31,1.155,33,4.064,34,1.204,36,2.084,39,1.923,41,2.144,46,1.959,49,1.062,50,2.765,56,0.467,84,0.93,90,3.359,91,3.939,92,3.539,93,3.02,94,5.832,96,2.511,97,2.114,109,0.797,113,1.724,130,2.695,132,1.388,144,2.264,145,2.867,164,1.958,165,1.059,172,2.47,179,1.582,186,2.227,203,1.131,207,1.929,216,2.406,224,2.003,228,1.497,236,1.874,240,2.585,272,1.935,283,2.482,292,2.056,313,1.742,343,1.681,351,1.711,390,2.282,407,2.137,408,1.706,437,2.219,439,3.09,440,3.143,441,2.571,447,2.236,476,2.977,482,3.188,568,2.737,578,1.857,580,1.641,593,2.331,599,5.921,601,2.219,619,4.575,620,3.944,658,2.264,670,2.747,675,3.007,694,2.571,715,4.411,737,3.572,757,1.165,769,1.817,810,2.099,812,3.151,822,2.824,832,3.183,877,1.724,902,3.291,997,1.75,998,4.575,1009,2.176,1012,2.678,1066,2.331,1376,2.363,1434,4.775,1737,1.783,1782,2.129,1814,3.373,2353,2.824,2429,3.331,2527,3.939,3011,4.305,3012,3.939,3013,4.486]],["t/2414",[2061,3.73]],["t/2416",[7,0.804,16,2.105,19,3.504,84,0.852,144,3.765,164,2.16,201,5.19,216,4.003,226,4.943,244,4.84,247,3.455,281,3.72,313,2.491,353,2.144,391,3.31,408,3.68,511,2.507,565,3.329,596,3.541,696,5.541,712,4.23,731,4.121,827,3.984,874,5.007,1029,7.017,1066,3.878,1179,4.84,1195,8.496,1308,4.664,1788,6.552,1987,8.496,2457,6.922]],["t/2418",[7,0.78,15,2.739,16,1.397,23,2.507,49,1.688,56,0.742,84,0.814,93,3.182,96,2.645,153,3.842,164,2.718,167,5.771,203,1.798,215,5.588,245,4.486,253,4.519,275,2.325,313,2.417,326,6.735,353,2.048,369,4.659,474,6.116,476,2.872,524,4.623,525,3.539,601,3.525,628,3.879,702,5.058,764,5.058,798,4.131,874,3.688,896,8.46,902,5.229,1009,3.458,1128,6.841,1201,5.006,1388,5.676,1427,5.989,1535,5.771,1593,7.127,1597,4.737,1847,5.058,5234,8.093,5235,8.093,5236,8.093]],["t/2420",[7,0.944,27,1.675,28,1.34,31,1.724,56,1.059,84,0.765,89,2.634,92,4.433,109,1.19,113,2.574,123,2.405,164,1.939,172,2.445,179,2.362,185,4.858,189,3.777,199,3.482,201,4.833,229,4.187,232,2.567,236,2.496,247,3.102,256,2.641,298,4.311,323,2.847,391,2.971,416,3.25,495,3.408,511,2.25,541,4.345,555,3.339,597,4.903,658,3.38,816,3.999,821,4.915,874,3.466,939,4.345,941,5.176,997,2.614,1025,5.335,1036,4.658,1137,4.858,1312,2.594,1314,4.805,1782,3.179,1916,4.217,2795,7.059]],["t/2422",[4,1.63,7,0.845,17,2.077,22,3.261,24,0.851,27,2.39,28,0.742,31,2.46,41,2.385,56,0.519,84,0.569,96,1.851,109,1.698,119,4.788,123,3.115,126,2.538,130,2.996,172,2.672,197,2.342,199,3.805,207,2.145,216,4.653,224,2.227,236,2.403,238,5.243,241,4.112,253,3.163,275,1.627,276,3.822,306,3.426,319,5.256,353,1.433,408,1.897,416,2.42,439,3.436,504,3.186,527,1.687,565,1.716,568,1.505,597,2.715,601,2.467,606,5.928,607,2.015,617,8.552,630,1.941,631,3.436,659,3.404,660,3.911,667,4.988,693,4.749,740,2.376,747,2.158,756,2.467,798,4.243,807,2.031,810,2.334,816,2.978,819,3.704,832,3.54,923,4.627,1003,4.988,1012,2.978,1066,3.805,1224,3.373,1280,3.54,1380,4.866,1557,3.261,1822,5.5,1874,3.66,1891,3.911,1940,4.28,2092,2.942,2429,3.704,2648,5.256,2769,5.256,2840,4.788,2966,3.578,3728,3.854,5237,5.664,5238,5.664,5239,5.664]],["t/2424",[2,1.207,7,0.866,21,1.264,23,2.283,24,1.107,26,1.661,27,1.062,28,1.311,29,1.561,30,2.799,31,1.093,42,3.281,56,0.675,84,0.741,89,1.67,109,1.78,113,1.632,123,1.525,132,2.008,134,2.226,157,2.583,170,1.322,172,1.55,174,2.762,203,1.071,213,1.821,224,1.896,225,1.599,226,3.316,229,2.654,232,1.627,233,2.475,236,2.632,238,2.567,247,1.966,262,2.925,280,2.846,283,2.4,284,3.013,285,2.825,294,4.465,306,1.987,313,1.093,315,2.125,316,1.994,353,1.865,390,2.16,416,3.822,430,2.207,439,2.925,444,2.964,491,1.349,508,2.636,525,2.108,526,3.236,536,1.333,550,2.687,552,1.584,555,2.117,565,2.233,567,1.794,568,2.377,569,3.826,597,3.532,599,2.3,607,2.621,614,0.824,619,7.048,623,2.143,631,2.925,658,2.143,689,6.228,709,2.925,734,3.026,740,2.023,757,1.103,827,2.267,846,1.987,874,3.358,930,2.447,934,2.134,997,1.657,1003,4.246,1036,2.953,1066,2.207,1179,2.754,1270,3.826,1281,2.257,1284,3.344,1360,2.551,1380,2.822,1394,2.983,1782,2.015,1822,2.692,1940,3.644,1990,3.08,2363,3.5,2513,4.075,2684,2.754,3090,3.939,3311,3.826,5240,4.246,5241,4.246,5242,4.475]],["t/2426",[2061,3.73]],["t/2428",[7,0.885,16,1.705,17,2.087,18,4.934,19,4.788,20,3.302,26,2.874,28,1.092,31,1.29,48,2.142,56,0.521,84,0.838,85,1.829,89,1.97,90,2.488,92,1.955,93,2.237,123,1.799,164,2.517,165,1.183,174,1.569,199,2.604,201,2.688,221,1.95,225,3.275,226,4.893,227,3.485,232,1.92,237,3.871,240,2.888,247,2.32,248,2.478,275,1.634,291,3.11,296,2.035,298,4.728,302,4.219,318,1.286,323,2.129,353,1.44,359,2.328,377,1.731,390,2.549,391,2.223,403,3.388,407,2.387,418,2.639,447,2.498,478,3.2,484,2.237,536,1.572,541,3.25,555,2.498,557,2.627,568,1.512,596,2.378,630,1.95,664,3.871,683,1.975,719,3.177,731,2.768,762,3.556,764,3.556,821,3.676,851,2.768,854,2.81,856,3.154,874,3.801,879,4.13,940,1.975,1029,3.634,1043,2.74,1044,3.11,1195,4.4,1196,4.21,1221,3.929,1476,2.844,1491,4.057,1782,2.378,1788,4.4,1801,3.01,1998,3.068,2725,4.809,5242,5.28,5243,5.689]],["t/2430",[7,0.887,19,3.001,21,1.903,34,1.715,56,0.665,84,0.729,103,2.253,156,4.584,162,3.232,174,2.002,236,1.771,249,5.011,278,2.413,296,2.595,302,3.101,306,2.99,326,4.584,338,1.657,353,2.508,375,3.914,408,2.431,430,3.322,463,7.317,685,3.747,874,6.383,885,3.839,920,3.914,1111,4.402,1174,6.13,1314,4.584,1825,5.758,1960,7.501,2364,7.489,2365,8.096,2780,6.391,2970,6.134,2971,6.391,2972,6.391,2973,5.929,4457,9.197,4458,6.735,4459,6.735,4460,6.735]],["t/2432",[2061,3.73]],["t/2434",[7,0.804,31,1.921,56,0.776,84,0.852,123,2.679,162,2.763,215,5.851,216,4.003,219,2.434,275,2.434,283,2.274,306,3.491,318,1.914,340,5.241,390,3.796,463,4.731,467,6.151,527,1.719,555,3.72,565,2.567,567,3.153,568,2.252,604,5.189,683,2.942,692,6.27,693,4.84,734,3.479,738,6.922,798,4.325,802,3.181,814,3.386,874,3.861,930,4.3,1029,5.412,1066,3.878,1138,5.942,1174,5.241,1178,5.765,1180,4.959,1189,7.162,1289,3.181,1960,5.241,3014,7.462]],["t/2436",[7,0.783,10,4.089,19,3.368,22,4.689,23,2.523,24,1.224,27,1.794,34,2.53,88,3.404,94,4.089,103,2.529,106,3.642,123,2.576,158,3.778,220,4.652,221,2.792,225,2.702,226,3.665,313,1.847,332,3.923,339,3.813,341,4.157,384,6.299,387,5.263,390,3.65,391,3.182,495,3.65,506,4.483,511,2.41,512,2.606,513,3.562,519,6.299,524,4.652,599,3.885,694,5.404,767,5.808,773,7.773,937,5.203,1069,4.089,1105,6.299,1106,7.173,1107,3.982,1111,4.941,1179,4.652,1431,6.028]],["t/2438",[7,0.875,24,1.609,26,2.806,28,1.402,34,1.925,41,3.429,46,3.133,47,4.767,56,0.981,90,4.682,92,4.11,93,4.21,94,4.089,96,2.663,97,3.38,132,2.22,186,3.562,216,3.848,272,3.094,283,2.186,353,2.061,418,3.778,491,2.278,509,4.282,568,2.165,580,2.625,597,5.132,599,3.885,601,3.548,611,3.142,614,1.392,619,4.851,620,4.181,715,5.107,874,3.712,998,4.851,1433,6.028,1434,4.206,1866,6.028]],["t/2440",[7,0.818,16,1.503,27,1.918,28,1.617,31,1.974,34,2.058,84,0.875,88,3.639,89,4.277,123,2.753,145,3.249,174,2.401,232,2.939,244,4.973,254,2.598,278,2.895,283,2.336,289,3.193,294,4.346,306,3.587,318,1.967,484,3.423,485,6.106,512,2.786,558,3.613,575,3.838,780,4.301,883,5.924,884,5.096,1114,3.665,1243,5.924,1289,3.268,1315,4.444,1441,4.861,1782,3.639]],["t/2442",[4,1.944,7,0.795,10,3.39,17,2.477,23,3.643,26,2.327,28,1.235,31,2.463,34,1.596,56,0.619,84,1.092,103,2.097,123,3.436,219,2.709,232,3.969,236,1.648,254,2.015,283,3.156,318,1.526,384,5.222,478,3.799,484,2.655,485,4.736,511,1.998,530,3.913,545,2.896,548,5.386,552,3.863,558,3.913,567,3.509,568,1.795,578,2.463,593,3.091,601,2.942,614,1.154,629,3.986,685,3.487,796,3.285,814,2.699,852,4.371,948,5.72,1007,4.221,1014,4.058,1197,4.997,1203,5.708,1212,4.136,1441,3.771,1506,4.022,1782,2.822,1831,4.997,1850,4.221,2372,4.177,2811,5.358]],["t/2444",[2,2.323,7,0.852,24,1.912,28,1.215,165,1.929,246,3.257,248,4.043,283,2.491,285,3.558,313,2.104,397,3.58,512,2.97,536,2.565,565,2.812,567,3.453,568,2.467,757,2.909,759,3.019,760,2.545,878,3.673,930,4.711,1473,3.838,2032,3.626,2398,6.619,3015,7.583,3016,5.182]],["t/2446",[7,0.852,16,1.603,24,1.75,28,1.215,56,0.85,84,0.933,109,1.822,115,3.966,132,3.174,145,3.464,228,2.728,236,2.265,318,2.097,502,2.375,527,2.362,565,2.812,568,2.467,683,3.223,769,3.31,807,3.328,946,4.975,1352,4.141,1498,6.147,2696,5.801,2726,6.147]],["t/2448",[7,0.751,16,1.772,27,2.26,31,2.326,40,5.016,56,0.94,107,2.943,109,2.082,123,3.245,163,3.774,527,2.082,597,4.917,635,3.455,658,4.56,814,4.1]],["t/2450",[7,0.789,16,2.203,28,1.671,109,1.688,132,3.478,170,2.957,283,2.894,862,4.981,1139,5.356]],["t/2452",[23,2.985,24,1.448,25,3.541,28,1.262,49,2.01,56,0.883,98,3.142,132,2.626,286,5.42,337,4.164,339,4.51,491,2.695,502,3.05,513,4.213,544,5.592,558,3.998,565,2.919,577,5.233,757,2.203,795,5.844,796,4.687,1312,3.285,2032,3.764]],["t/2454",[28,1.344,107,2.438,109,2.082,115,4.384,132,2.797,174,2.83,236,2.504,254,3.062,360,6.795,507,5.179,550,3.742,635,3.455,658,4.56,997,3.526,1047,5.688,3017,9.036]],["t/2456",[2,2.079,7,0.793,28,1.42,56,0.761,109,1.697,126,3.722,132,2.264,138,3.109,172,4.115,190,3.961,193,3.661,224,3.265,288,5.087,289,3.046,313,1.883,337,3.59,338,1.896,339,3.888,408,2.782,491,2.323,552,2.728,565,2.517,568,2.207,611,3.204,614,1.419,632,4.147,694,4.192,755,3.471,806,6.029,810,3.422,819,5.432,830,3.523,877,2.811,906,4.672,952,4.991,1009,3.549,1360,4.394,2639,5.735,2867,4.509,3018,7.02]],["t/2458",[7,0.789,27,2.376,29,3.492,109,1.688,127,3.282,254,3.218,338,2.462,353,2.729,390,4.833,715,5.144,1043,5.194,1044,5.896]],["t/2460",[7,0.839,58,7.697,275,3.295,564,6.893,1625,8.045]],["t/2462",[7,0.684,13,6.787,84,0.94,283,3.139,294,4.668,300,5.259,332,4.503,395,3.429,399,6.558,476,2.519,542,7.23,543,6.112,742,5.427,863,6.667,873,7.066,877,3.164,888,5.111,889,6.667,890,6.456,891,5.844,1037,3.308,1328,5.183,1566,6.192,1991,5.259,2683,5.341,3020,6.274,3021,6.558,3022,8.234]],["t/2464",[7,0.872,11,7.13,17,3.534,18,3.929,19,4.928,28,1.262,158,5.528,318,2.177,332,5.74,391,3.764,846,3.97,880,3.85,888,5.267,917,8.463,922,6.086,923,7.872,924,7.872,925,10.074,1782,4.027]],["t/2466",[7,0.973,135,5.428,174,2.83,233,4.16,236,2.504,278,3.412,363,6.795,444,4.128,484,4.034,578,3.742,849,7.196,850,6.224,854,5.068,856,5.688,916,5.534]],["t/2468",[2061,3.73]],["t/2470",[3,5.964,7,0.55,8,3.752,10,3.773,11,5.562,21,1.971,22,4.327,23,2.328,24,1.129,26,2.589,27,2.236,28,1.329,31,1.704,46,2.89,56,0.689,84,0.755,103,2.334,105,2.182,109,1.176,121,4.081,123,2.377,126,3.368,140,2.733,165,1.562,171,1.895,174,2.073,197,3.108,213,2.838,220,4.293,221,2.576,232,2.537,292,3.034,305,4.437,315,3.313,346,5.271,353,1.902,392,4.327,434,6.278,491,2.102,500,3.3,555,3.3,571,7.934,583,4.26,590,3.752,591,3.976,592,4.476,611,2.899,622,3.518,637,4.476,657,5.456,941,5.114,1137,4.801,1476,2.563,1504,4.915,1754,2.964,2652,6.14,3024,6.619,3025,5.456]],["t/2472",[4,2.151,7,0.547,16,1.29,23,3.132,24,1.986,31,2.292,35,4.828,36,4.136,56,0.926,84,0.751,86,4.72,96,2.442,98,3.296,100,3.1,108,3.581,123,2.363,131,4.255,142,6.315,143,9.275,145,2.788,165,1.553,171,1.884,174,2.061,188,6.783,198,3.376,205,6.315,207,3.829,228,2.196,232,3.412,337,3.229,353,1.891,377,2.098,400,7.338,489,4.622,500,4.438,545,3.205,571,5.159,662,4.029,940,2.594,1028,4.576,1827,5.928,3025,5.424,3026,6.58,3027,6.58]],["t/2474",[34,2.527,84,1.075,92,3.675,93,4.992,229,5.887,252,8.27,390,4.792,599,5.101,619,6.369,620,5.49,942,4.191,998,6.369]],["t/2476",[34,2.761,415,7.64,1066,5.347]],["t/2478",[34,2.761,415,7.64,1066,5.347]],["t/2480",[12,3.15,27,2.41,28,1.591,84,1.222,107,2.599,109,2.013,172,3.907,173,4.179,174,2.321,272,3.197,304,1.646,318,1.902,468,4.968,495,3.771,502,2.154,557,3.886,564,5.058,596,3.518,607,2.993,612,5.058,747,3.207,819,5.504,829,5.348,830,3.569,841,3.61,877,4.354,1277,6.876,1352,3.756,1953,4.248,2806,5.648]],["t/2482",[4,1.496,5,1.699,7,0.685,16,0.898,21,2.455,24,0.781,25,1.545,26,3.585,27,1.718,28,1.226,40,2.541,56,0.858,84,1.046,98,3.053,106,1.768,107,1.853,109,2.068,110,3.814,111,7.587,116,1.8,126,2.329,132,1.417,133,2.064,164,2.387,165,1.622,169,3.959,170,2.139,183,2.07,186,2.273,189,3.874,256,1.805,272,1.975,289,1.906,316,2.15,318,1.175,353,2.369,365,2.444,372,1.805,377,1.079,382,2.993,407,2.181,416,2.221,432,3.042,439,3.153,440,2.127,447,2.282,458,2.716,527,2.262,555,2.282,565,1.575,568,2.073,578,1.896,593,3.57,606,3.707,613,3.927,615,2.822,630,2.674,635,1.75,679,5.663,809,3.32,814,2.077,818,4.363,862,2.4,933,5.308,937,3.32,962,5.98,1111,4.732,1134,4.02,1163,2.924,1276,3.59,1279,6.032,1439,7.649,1831,3.847,1892,4.247,2016,3.646,2043,3.4,3028,4.578,3029,4.578]],["t/2484",[4,1.595,5,1.811,7,0.711,15,1.875,16,0.957,27,2.803,28,1.569,56,0.891,98,1.807,105,1.608,107,1.316,109,1.68,113,1.875,127,2.489,132,1.51,165,2.021,171,1.397,172,3.126,173,2.751,182,1.72,199,2.536,228,2.404,236,1.996,257,2.617,281,2.432,289,3,292,2.236,302,2.367,320,2.949,338,1.265,343,1.829,351,1.861,365,3.846,366,3.216,369,3.19,437,2.413,445,3.242,455,6.181,458,2.895,459,4.022,490,3.623,491,1.55,502,1.418,525,2.423,550,2.02,552,1.82,568,2.174,581,5.489,604,3.393,617,4.1,633,4.022,635,2.754,636,2.895,684,2.681,734,2.275,757,1.267,798,2.828,801,3.825,815,4.526,831,3.242,851,2.695,878,2.192,906,3.116,909,3.539,933,3.77,953,3.07,1006,4.615,1041,3.27,1067,4.022,1216,2.617,1245,3.297,1290,2.86,1445,3.58,2101,4.526,2516,5.937,3008,3.825,3030,4.879,3031,4.879,3032,4.284,3033,6.053,3034,4.879,3035,4.879,3036,4.284]],["t/2486",[2,1.672,7,0.79,17,2.45,25,2.781,28,1.614,56,1.073,84,1.177,88,4.515,89,4.054,109,1.046,128,2.957,133,2.653,162,2.179,164,1.703,175,3.391,275,1.919,313,1.515,315,2.945,377,1.387,405,1.973,407,2.803,440,2.734,472,4.881,476,2.521,483,3.911,491,1.869,505,2.57,512,4.282,558,2.773,575,5.781,605,2.244,698,4.484,726,4.053,887,3.911,912,4.317,1069,4.698,1366,5.578,1616,3.45,1754,2.635,1815,9.131,2783,5.302,3037,5.302,3038,5.648,3039,5.459,3040,5.302]],["t/2488",[7,0.751,84,1.166,92,2.629,98,2.495,109,1.607,110,3.741,116,2.65,165,1.591,174,3.198,186,3.346,216,3.615,228,3.02,229,4.212,233,2.57,242,5.004,317,3.549,318,1.729,351,2.57,364,5.004,372,2.657,377,2.132,390,3.429,426,2.623,458,3.999,527,2.515,580,2.466,591,4.048,613,3.21,630,3.521,637,4.557,658,5.745,693,4.371,798,3.906,818,3.566,996,4.479,997,3.53,1163,4.305,1218,5.368,1444,4.734,1916,4.242,2098,5.918,3041,6.072]],["t/2490",[2,2.449,5,3.198,23,3.031,24,1.47,26,3.371,158,4.538,220,5.589,246,3.433,247,3.99,276,4.498,351,3.286,365,4.601,401,3.47,408,3.278,418,4.538,420,8.27,458,5.112,466,6.399,534,6.48,628,4.689,828,3.786,958,5.935,1036,5.992,2105,8.27]],["t/2492",[7,0.769,19,4.349,28,1.377,84,1.057,138,3.936,197,4.349,225,3.488,316,4.349,338,2.401,441,5.308,536,2.906,857,4.427,942,4.121,1066,4.814,1506,6.263]],["t/2494",[7,0.895,15,2.886,16,1.905,23,2.643,84,0.857,144,3.791,219,3.513,225,2.829,232,3.724,236,2.081,256,2.962,275,3.17,283,2.289,323,3.192,351,2.865,390,3.822,491,2.386,545,3.659,548,4.872,552,2.802,583,4.835,615,4.631,852,3.431,942,3.343,948,5.174,1202,4.17,1848,8.879,1924,6.446,2225,4.214,2297,2.637,2395,4.149,3042,4.631,3043,7.512,3044,7.512]],["t/2496",[7,0.505,16,1.192,17,2.531,23,2.966,55,3.483,85,2.218,105,2.003,113,2.335,116,2.39,135,3.65,164,1.759,171,1.74,196,2.538,213,2.606,219,1.982,225,2.289,232,2.329,236,2.682,272,2.622,275,3.157,283,2.949,313,1.564,322,2.003,323,2.583,342,4.186,366,4.005,398,2.256,401,2.448,421,4.005,438,3.323,471,2.654,479,4.459,491,1.93,543,5.002,548,3.941,552,2.267,614,1.179,702,4.313,703,5.106,757,1.578,828,2.67,920,3.722,1006,3.276,1007,5.982,1212,5.863,1217,5.336,1238,3.825,1312,2.353,1366,3.563,1427,5.106,1502,5.475,1507,9.605,1607,4.186,1638,3.307,1775,5.336,1924,5.215,1925,5.336,1926,5.475,1927,5.475,2787,4.073,4452,6.404,4453,6.404]],["t/2498",[17,3.857,23,3.258,24,1.58,84,1.057,98,3.429,160,3.421,161,6.643,219,3.021,232,3.55,275,3.021,530,4.364,607,3.74,877,3.559,927,7.376,1848,7.634]],["t/2500",[5,1.994,7,0.642,16,1.054,25,3.339,27,2.475,28,1.623,29,1.975,31,2.33,34,1.442,84,0.613,96,1.994,105,1.771,123,3.25,158,2.83,163,2.244,164,1.555,181,3.208,197,2.523,200,5.166,233,2.049,245,4.864,246,2.141,248,2.658,256,2.118,257,2.883,281,2.679,313,1.383,318,1.983,332,2.938,377,1.266,399,4.279,437,2.658,440,2.497,447,2.679,491,1.707,504,3.432,530,2.532,562,2.781,565,1.849,635,2.054,658,2.711,740,5.2,810,2.514,830,2.588,846,2.514,904,5.976,1137,3.897,1171,4.515,1280,3.813,1281,6.231,1328,4.864,1379,4.351,1457,4.041,1459,4.152,1913,4.841,2100,4.094,2636,3.248,2671,4.611,2680,3.602,2681,3.634,2691,4.429,2719,6.786]],["t/2502",[7,0.897,25,3,27,2.702,28,1.73,109,1.58,272,3.836,281,4.433,318,2.281,343,3.333,565,3.059,568,2.683,596,4.22,635,3.399,1813,6.87,2719,7.808,3046,8.011]],["t/2504",[2,1.211,7,0.833,12,1.812,16,1.276,21,1.269,23,2.29,27,2.38,28,1.314,40,2.366,56,0.677,84,1.009,103,1.503,109,1.691,110,2.366,113,1.638,127,3.811,131,2.038,133,1.922,153,2.298,155,2.909,162,2.923,164,1.234,165,1.864,171,1.22,174,2.038,193,1.634,197,2.001,219,2.882,221,1.659,225,1.605,228,2.635,232,1.634,248,2.108,298,2.743,313,1.097,318,1.67,338,1.105,343,1.598,375,2.61,377,1.534,393,2.206,440,1.981,472,2.187,491,1.354,502,2.294,524,2.765,527,1.499,536,2.042,538,3.743,544,2.809,550,1.765,557,2.235,558,2.009,565,3.871,567,2.75,568,2.872,577,3.245,578,1.765,580,1.56,593,2.215,601,2.108,605,1.625,614,0.827,630,1.659,635,1.63,670,2.61,690,2.787,747,1.844,756,3.219,757,2.294,796,4.361,810,1.994,931,3.451,999,2.443,1033,2.857,1114,2.038,1280,3.025,1314,4.668,1504,3.165,1558,2.529,1754,2.915,1822,2.703,1944,3.743,1990,3.092,2017,3.84,2064,3.205,3047,3.743]],["t/2506",[2,1.664,5,2.173,17,2.438,26,3.213,31,1.507,34,1.571,41,2.798,56,0.855,84,0.937,85,2.137,101,3.283,103,2.064,107,2.922,109,2.049,125,3.516,129,3.685,170,2.557,174,1.833,200,3.056,216,3.14,228,2.741,236,1.622,257,3.14,261,4.385,278,2.21,302,2.84,353,1.682,369,3.827,398,2.173,408,3.124,444,2.674,527,2.587,536,2.577,553,3.266,565,2.014,601,2.895,607,3.317,630,2.278,663,3.767,693,3.796,757,1.52,810,2.739,839,3.538,999,3.355,1170,3.069,1245,3.308,1257,3.585,1536,4.523,1558,3.473,1754,2.622,1918,3.069,3046,5.274,3048,5.853,3049,5.853,3050,5.853,3051,4.154,3052,5.853]],["t/2508",[4,2.423,27,2.41,28,1.102,31,2.48,84,1.1,96,2.751,109,1.902,123,3.459,125,4.452,138,3.15,164,2.789,171,2.758,174,3.017,175,4.272,179,2.613,278,2.799,297,4.272,347,5.155,377,1.747,416,3.596,544,4.885,568,2.237,607,2.993,614,2.076,630,2.885,635,2.834,818,3.922,934,3.725,1382,6.064,1504,5.504,1870,3.533,3053,5.26]],["t/2510",[5,2.926,7,0.833,24,1.88,34,2.116,56,0.82,107,2.127,127,2.725,158,4.153,174,2.469,228,2.632,236,2.185,283,2.402,295,3.341,306,3.689,338,2.044,502,2.291,536,3.146,550,3.265,558,3.715,560,4.422,577,4.997,827,4.21,830,3.797,874,4.08,928,6.923,946,3.825,1302,5.38,1328,4.963,2429,7.443,3054,7.314]],["t/2512",[1,2.071,2,1.543,5,2.525,7,0.874,16,0.663,18,1.566,19,1.588,20,2.229,22,2.211,24,1.632,26,2.124,28,1.158,31,0.871,33,2.032,46,2.371,49,0.801,56,0.887,84,0.889,85,1.235,90,1.679,92,1.32,103,1.192,105,1.789,109,0.601,115,1.641,121,2.085,125,3.261,132,1.047,140,1.397,164,0.979,165,2.258,174,1.059,179,1.192,189,1.907,190,1.832,197,2.549,220,2.194,224,2.424,225,1.274,226,1.728,233,1.29,243,1.949,244,2.194,245,4.281,257,1.814,281,1.686,283,1.654,289,2.833,292,1.55,302,1.641,320,2.044,343,2.035,359,1.571,360,2.543,364,2.512,366,2.229,376,2.032,386,2.652,395,1.408,408,1.287,430,1.758,437,1.673,471,1.477,498,1.983,502,0.983,505,1.477,509,2.019,527,0.779,536,1.704,550,3.961,565,1.868,566,2.481,574,3.246,593,1.758,596,1.605,600,2.376,622,1.798,628,2.955,630,2.113,632,1.917,636,3.221,655,2.33,672,2.099,719,3.442,732,2.33,751,2.058,757,0.878,770,2.739,796,1.868,807,4.461,811,2.211,814,3.086,816,2.019,820,1.832,826,2.97,828,1.486,830,3.276,831,2.248,856,2.129,862,1.773,898,1.728,908,6.128,909,2.453,933,4.195,937,2.453,939,2.194,946,2.634,1006,1.823,1028,2.352,1066,2.822,1180,2.248,1218,2.007,1240,2.032,1300,2.33,1352,1.714,1355,3.246,1465,2.577,1467,2.788,1693,2.085,2033,2.007,2429,6.76,2480,6.802,2717,2.33,2784,2.97,2840,3.246,3032,2.97,3055,2.902,3056,2.902,3057,2.577,3059,3.382,3060,3.382,3061,2.902,3062,3.138,5244,6.165,5245,3.84,5246,3.84,5247,3.84,5248,3.84]],["t/2514",[5,2.751,21,2.207,28,1.591,31,1.908,56,1.002,84,0.846,89,2.915,105,2.443,107,2.599,109,1.317,140,3.06,144,3.74,145,3.141,165,1.75,179,2.613,199,3.852,242,5.504,257,3.976,337,3.638,507,4.248,536,2.326,550,4.432,565,2.55,580,3.525,630,2.885,633,6.11,807,3.923,828,3.257,830,3.569,908,6.678,953,3.159,1047,4.666,1352,3.756,1382,4.666,1798,4.346,2429,7.154]],["t/2516",[2,1.43,5,2.736,7,0.418,26,2.883,27,2.181,28,1.296,31,1.296,84,0.574,85,1.837,92,1.964,93,2.247,98,2.729,103,1.775,106,2.846,107,2.879,109,1.897,115,4.657,123,1.807,138,2.139,163,2.102,164,2.133,165,1.188,174,1.576,179,1.775,182,1.775,233,1.919,236,2.042,242,3.738,254,3.252,257,2.7,272,3.179,278,1.9,289,2.096,302,2.442,316,2.363,317,2.651,318,1.291,322,1.659,324,2.809,341,2.917,353,1.446,385,4.669,416,2.442,430,2.616,444,2.299,458,2.986,468,3.374,489,3.535,490,3.738,502,1.462,522,3.946,524,3.264,527,1.698,550,3.052,576,3.124,611,2.205,613,2.397,658,2.54,693,3.264,709,3.467,737,4.008,755,2.389,774,2.869,798,2.917,893,3.062,997,3.402,1037,2.022,1153,4.535,1218,4.373,1329,4.008,1381,2.102,1382,5.488,1388,4.008,1392,4.319,1474,4.076,1922,4.42,3010,5.286,3063,4.831,3064,5.033,3065,5.033,3066,5.033]],["t/2518",[7,0.761,24,1.171,25,3.09,34,1.842,84,1.045,103,2.42,107,3.09,109,1.627,113,2.637,162,2.542,164,1.987,174,2.15,224,3.064,228,2.291,236,1.902,291,4.26,433,4.176,447,3.422,476,3.504,502,1.994,517,4.384,536,2.154,550,2.842,565,2.361,568,2.764,690,4.487,757,1.782,857,3.281,862,4.802,1114,3.281,1122,4.524,1280,4.871,1290,4.024,1363,2.604,1864,3.81,1907,3.735,1970,5.768,1971,5.382,2061,2.442,2488,4.384,2787,4.601,3942,4.601,4454,7.233]],["t/2520",[2,2.273,7,0.921,8,5.734,21,2.381,84,0.913,92,3.121,96,3.754,98,2.961,157,4.866,165,1.888,197,3.755,203,2.017,313,2.059,336,3.429,364,5.939,375,6.194,383,5.868,444,3.653,532,4.395,582,7.023,593,4.157,605,3.05,636,4.745,764,5.676,996,5.315,1898,5.615,4382,7.419,4455,8.428]],["t/2522",[306,4.814,1275,6.957,1557,6.726]],["t/2524",[2061,3.73]],["t/2526",[7,0.827,11,3.148,16,0.735,21,1.115,23,1.318,24,1.624,26,1.465,27,1.473,31,2.563,33,2.25,46,1.636,47,2.49,84,0.428,90,1.86,96,1.391,97,1.765,105,1.235,107,1.011,109,0.666,123,3.418,132,1.159,145,1.587,162,1.387,165,0.884,170,1.166,174,1.844,180,3.948,187,2.749,193,1.436,215,4.617,216,2.01,226,3.717,228,1.25,232,1.436,236,1.632,244,2.43,245,3.707,256,1.477,275,1.222,276,4.305,280,2.511,281,1.868,283,2.217,289,1.56,348,2.533,353,2.091,359,1.741,361,4.179,377,0.883,382,2.449,390,4.196,393,1.939,408,1.425,409,2,421,2.469,430,1.947,444,1.711,458,2.223,459,3.088,463,5.229,467,4.854,478,2.393,491,1.19,511,1.258,555,1.868,565,2.503,568,1.777,604,5.06,611,1.641,622,1.991,628,2.039,635,1.432,683,2.869,693,2.43,701,3.948,718,3.375,719,2.375,767,3.033,802,1.597,814,1.7,826,3.29,834,3.215,842,3.375,854,2.101,861,3.375,885,2.25,886,2.511,896,3.375,920,3.606,943,3.596,947,3.159,999,2.147,1129,2.469,1174,2.631,1175,3.29,1176,3.596,1179,2.43,1180,3.914,1181,2.049,1182,3.375,1315,2.171,1395,3.088,1451,3.375,1464,3.375,1535,3.033,1597,2.49,1782,1.778,1798,2.196,1799,2.717,1813,2.895,1960,2.631,1983,3.746,1987,3.29,1988,3.375,2040,3.948,2428,3.215,2527,3.29,2782,2.605,2897,3.948,2963,2.817,3089,5.053,3344,3.215,3571,3.746,3572,3.746,3573,3.746,3574,3.746,4461,3.948,4462,3.948,4463,3.948,4464,3.948,4512,3.948,5232,6.983,5249,4.254,5250,4.254,5251,4.254]],["t/2528",[2061,3.73]],["t/2530",[2,1.058,4,1.216,7,0.9,15,1.43,16,0.73,18,4.138,21,1.108,24,1.402,28,1.694,31,0.958,48,1.591,49,1.387,55,5.439,56,1.127,84,1.083,85,2.138,89,3.732,103,2.554,109,1.287,132,1.813,160,3.301,170,1.159,171,1.065,187,2.73,200,1.943,203,0.939,223,2.208,225,1.402,272,1.605,275,1.911,278,1.405,279,1.694,283,1.134,295,1.577,296,2.379,313,0.958,318,1.503,322,1.226,342,4.035,347,2.588,359,1.729,383,2.73,393,1.926,408,2.228,416,1.805,482,1.753,527,1.35,531,2.377,605,1.419,608,3.203,614,1.405,630,1.448,684,2.045,755,2.78,760,1.159,802,2.497,814,2.658,839,2.249,841,1.812,846,3.844,847,3.414,849,4.665,859,3.127,882,4.56,900,2.563,903,2.73,953,1.586,1057,2.539,1059,2.195,1060,1.901,1114,1.779,1129,2.453,1152,2.641,1163,2.377,1169,2.494,1214,2.539,1221,2.918,1234,2.221,1303,2.342,1312,2.268,1381,1.554,1456,2.669,1458,2.669,1500,2.964,1558,2.208,1633,1.848,1782,4.883,2013,2.473,2531,3.127,2782,2.588,3644,1.444,4303,3.721,4465,6.173,4466,3.921,4467,3.921,4468,6.72,4469,3.921,4470,6.173,4471,3.452,4472,3.721]],["t/2532",[7,0.872,28,1.396,55,5.381,84,0.814,89,3.692,96,2.645,140,3.877,160,2.633,165,1.682,169,4.107,171,2.04,179,2.513,213,3.056,232,4.278,275,3.063,283,2.172,317,3.754,351,2.718,491,2.264,531,4.553,593,3.704,614,1.383,622,3.788,736,4.219,802,3.038,828,4.126,852,5.298,882,4.486,920,4.365,1366,4.179,1373,4.587,1693,4.394,1782,3.383,2782,4.957,4468,6.612]],["t/2534",[7,0.95,34,2.295,55,6.044,219,2.789,221,3.328,254,2.897,283,2.605,303,7.048,361,6.068,391,3.793,471,4.605,478,5.461,545,4.164,548,5.546,552,3.189,558,4.029,578,3.541,684,4.699,829,4.747,879,7.048,1014,5.835]],["t/2536",[7,0.811,16,1.483,28,1.125,33,4.543,56,0.787,84,1.114,109,1.734,134,3.966,165,2.304,245,4.761,247,3.502,320,4.572,353,2.173,366,4.985,405,2.535,418,3.983,426,2.943,432,5.026,471,3.303,509,4.515,566,5.549,628,4.116,757,1.964,835,5.367,847,4.408,883,5.844,949,5.93,958,5.209,1181,4.136,1282,7.903,1381,3.159,1527,5.26,1801,4.543,1878,7.563,3886,6.641,3969,7.903]],["t/2538",[4,2.393,7,0.72,16,0.978,21,1.485,24,0.851,28,1.089,49,1.734,55,5.833,56,0.903,84,0.569,85,3.166,107,2.34,109,1.301,139,4.813,160,1.843,162,2.711,165,1.728,170,3.169,196,2.083,203,1.847,246,1.988,272,2.152,279,2.271,283,1.52,285,2.172,313,1.284,322,1.644,326,3.578,338,1.293,353,1.433,359,2.318,416,2.42,429,4.494,430,2.593,471,2.178,482,2.35,517,3.186,527,2.202,580,2.679,608,2.728,614,1.42,623,2.517,628,2.715,703,4.191,760,3.312,790,3.66,802,2.126,831,3.315,841,3.566,847,2.908,880,2.263,882,4.609,899,3.618,900,3.436,910,4.788,953,3.697,1006,2.689,1245,3.332,1330,3.578,1381,3.058,1539,3.504,1540,2.908,1870,1.829,1902,4.803,2100,3.801,3057,3.801,3644,1.936,4473,7.321,4474,3.854]],["t/2540",[34,2.386,109,1.919,283,2.709,295,3.768,338,2.305,376,5.341,408,3.382,471,3.883,491,2.824,527,2.048,613,4.235,614,2.095,732,6.124,802,3.79,912,6.524,1870,3.962,2800,7.472]],["t/2542",[2,2.189,7,0.75,9,3.649,10,4.389,21,1.592,28,0.795,41,2.556,48,2.286,49,2.581,55,4.414,56,0.939,84,0.61,105,2.538,109,1.368,113,2.959,131,2.556,132,1.655,160,3.334,163,2.233,165,1.262,179,1.885,190,2.896,200,2.792,211,4.193,225,2.014,236,1.482,256,2.108,296,2.171,304,1.187,313,1.377,351,2.039,353,2.213,359,3.578,393,2.767,416,2.595,418,2.816,471,2.335,476,1.636,505,2.335,524,3.468,531,3.416,611,2.342,614,1.494,677,2.969,742,5.076,747,3.332,795,3.683,802,3.283,805,3.836,857,2.556,880,2.426,883,4.132,899,3.878,900,3.683,912,7.245,992,2.816,1033,3.584,1133,3.031,1170,1.73,1540,4.489,1566,4.021,1632,2.924,1633,2.655,1850,3.795,1870,3.998,1945,3.756,2750,4.696,3057,4.075,3505,4.961,3514,4.33,4475,5.635]],["t/2544",[5,3.126,23,2.962,34,2.26,68,3.364,84,0.961,169,4.853,239,5.09,289,4.35,338,2.183,353,2.42,367,4.518,418,5.502,471,3.677,484,3.759,511,3.509,512,3.059,513,4.181,536,2.643,608,4.605,807,4.254,940,3.32,1376,4.435,2899,7.587]],["t/2546",[7,0.669,56,0.838,84,0.919,140,3.326,157,4.901,160,2.976,278,3.042,297,4.642,376,4.839,608,5.557,611,3.529,614,2.267,732,5.548,828,3.54,835,5.717,847,4.695,882,5.071,911,8.538,912,5.91,1170,2.606,1181,4.405,1330,5.778,1870,2.954,1874,5.91,2905,6.058,3645,8.055,3984,8.055,4476,8.489]],["t/2548",[2,2.487,7,0.727,24,1.493,56,0.911,103,3.086,233,3.338,318,2.245,353,2.515,471,3.822,580,3.202,790,6.421,802,3.73,911,7.354,914,4.589,953,3.73,1037,4.297,1170,2.831,1373,5.633,1801,5.257,3731,8.119,4477,9.223]],["t/2550",[2061,3.73]],["t/2552",[7,0.803,16,1.895,34,2.593,164,2.797,261,5.16,359,4.49,484,4.314,532,5.311,747,4.181,3510,9.663]],["t/2554",[7,0.822,15,2.966,16,1.514,34,2.072,49,2.342,56,0.803,84,0.881,85,2.818,103,2.722,192,4.792,203,2.495,228,2.577,261,5.281,318,2.537,401,3.983,481,4.86,484,3.447,607,3.118,614,1.498,723,2.908,877,2.966,1363,2.929,1476,2.989,2026,4.161,2027,5.088,2033,4.581,2297,2.71,2600,5.007,3051,5.479,3511,7.72,3512,7.72,3513,7.173]],["t/2556",[7,0.915,49,1.867,203,1.989,216,4.23,278,2.977,285,3.432,304,1.751,318,2.975,398,3.721,401,3.176,527,1.816,565,2.713,568,2.379,759,2.912,760,3.121,898,5.121,919,5.718,920,4.828,1116,5.483,1312,3.053,1373,5.074,1487,5.538,1918,5.256,2395,4.355,2895,5.114,3514,6.384,4478,7.567]],["t/2558",[7,0.911,19,3.667,21,1.623,24,1.972,28,1.568,34,2.674,49,1.291,84,1.204,85,1.99,103,3.217,105,1.797,132,2.824,162,2.019,165,1.287,196,2.277,203,1.375,228,1.82,232,2.09,236,1.511,254,1.847,261,4.17,285,2.374,372,2.15,377,1.285,397,2.388,398,2.899,471,2.381,527,1.799,530,2.569,536,1.711,565,3.628,568,1.645,580,2.857,613,2.597,630,2.122,757,1.416,759,3.895,760,3.283,878,2.45,934,3.925,1037,3.137,1114,3.733,1312,2.111,1550,4.275,1558,3.235,1782,2.588,1822,4.951,1918,2.859,1971,4.275,2488,3.483,2878,5.233]],["t/2560",[2,2.093,7,0.612,16,1.881,24,1.82,27,1.842,28,1.743,34,2.574,107,2.878,109,2.008,116,2.895,132,2.969,135,4.423,145,3.12,170,2.292,228,3.202,236,2.04,261,5.122,318,1.889,353,2.756,372,2.903,528,4.088,558,3.469,596,3.494,799,5.864,934,3.7,1214,5.024,1782,5.063]],["t/2562",[7,0.768,19,3.732,21,1.664,24,0.954,25,2.682,27,2.52,28,0.831,31,1.439,40,3.103,48,2.389,56,0.582,84,0.907,96,2.074,107,2.496,109,1.966,123,2.007,124,3.714,127,2.747,135,3.357,158,2.944,160,2.936,219,1.823,221,3.093,226,2.856,272,3.429,275,1.823,278,2.11,283,1.703,316,2.624,318,2.731,360,4.203,394,2.944,395,2.327,397,2.448,471,3.471,527,2.321,550,3.291,555,2.786,557,2.931,561,5.185,562,2.892,565,1.923,567,2.361,568,1.687,605,3.031,607,2.257,630,2.175,635,2.137,654,5.185,658,2.82,729,4.203,757,1.451,886,3.746,939,3.625,1053,4.607,1274,3.597,1290,3.277,1416,4.203,1476,2.164,1628,4.526,1773,4.451,2061,1.989,2636,3.379,3019,5.036,3515,5.036]],["t/2564",[7,0.689,16,2.029,24,1.415,26,4.049,34,2.226,49,1.965,145,3.515,159,5.827,164,2.401,261,4.43,276,4.33,300,5.299,359,3.854,392,5.423,393,5.356,399,6.607,403,5.61,611,3.634,742,5.467,744,4.983,764,7.345,1045,5.827,1991,5.299,3020,6.321,3021,6.607]],["t/2566",[1,6.551,7,0.889,8,6.064,24,1.493,32,5.768,49,2.073,106,3.38,133,3.945,225,3.296,381,4.363,393,4.529,434,6.147,436,6.581,555,4.363,590,4.961,611,3.834,1469,7.885,2683,5.676,4479,6.862]],["t/2568",[7,0.852,15,3.141,26,3.197,28,1.215,47,5.432,56,0.85,84,0.933,90,4.059,92,3.189,93,4.578,97,3.851,130,4.91,138,3.474,229,5.109,261,4.365,272,3.526,367,4.385,408,3.109,440,3.798,441,4.684,484,3.649,580,2.991,599,4.427,605,3.117,614,1.586,619,5.527,620,4.764,828,3.592,998,5.527]],["t/2570",[7,0.689,16,1.627,56,0.863,84,0.947,88,3.937,109,1.474,133,3.74,134,4.35,189,5.836,192,5.149,194,6.717,200,4.33,229,5.185,248,4.103,254,2.811,283,2.528,284,5.887,353,2.384,436,6.238,545,4.04,854,4.653,993,5.047,1043,4.537,1224,5.61,1381,3.464,1399,5.149,1558,4.922,2041,6.971]],["t/2572",[2061,3.73]],["t/2574",[2,1.549,7,0.649,16,1.069,18,4.882,20,3.593,28,1.568,41,2.606,55,3.125,56,1.142,105,1.797,132,2.417,160,2.014,179,1.922,182,1.922,190,2.953,213,2.338,225,2.054,275,2.547,313,1.404,351,2.079,353,1.567,372,2.15,397,2.388,405,1.828,426,3.039,470,4.155,651,3.83,684,2.996,747,2.359,760,1.697,802,3.329,805,3.911,807,3.18,814,3.544,831,3.623,840,4.494,846,3.654,851,5.04,875,4.581,882,3.432,934,2.74,1008,3.483,1040,3.954,1152,3.869,1214,5.329,1234,3.255,1266,1.567,1271,4.213,1308,3.408,1322,5.745,1323,5.745,1456,3.911,1458,3.911,1754,2.442,1782,5.207,1801,3.275,1863,2.403,1945,3.83,2450,6.562,2643,5.73,2866,7.809,4272,4.912,4839,5.745,5252,8.867,5253,6.191,5254,6.191]],["t/2576",[7,0.91,15,3.5,46,4.787,55,6.282,56,0.948,89,3.582,179,3.212,306,4.262,513,4.524,715,4.934,899,6.607,1192,8.744,2643,8.042]],["t/2578",[2,1.527,4,1.756,19,2.523,21,2.301,28,0.799,56,0.804,84,1.13,103,1.895,133,2.422,160,3.656,171,1.538,177,3.932,187,3.942,213,2.304,219,3.561,275,1.753,282,3.079,292,2.463,304,1.193,318,2.953,336,2.304,382,5.052,391,2.384,398,2.868,436,4.041,471,3.375,472,3.965,516,5.662,531,5.781,608,2.938,623,2.711,636,3.188,659,5.273,684,2.953,736,5.172,757,2.007,846,2.514,858,5.157,882,4.864,916,3.291,1037,3.104,1042,3.407,1060,3.948,1179,3.485,1214,3.667,1284,2.769,1500,4.279,1527,3.737,1638,2.924,2032,2.384,2312,3.03,2362,5.373,2476,5.373,2632,3.382,2643,3.942,2902,5.373,2966,3.854,4415,5.373,4570,6.963,5255,6.101,5256,6.101,5257,6.101]],["t/2580",[289,3.355,318,2.067,502,2.341,536,2.528,608,6.912,614,2.387,707,4.723,807,4.137,911,6.769,940,3.176,1376,4.243,3677,8.489,3683,8.489,3702,8.489,4381,7.732,4468,7.473,4472,8.055,4473,8.055,5258,9.147,5259,9.147]],["t/2582",[7,0.88,41,4.119,55,4.938,105,2.84,183,3.897,213,3.695,277,4.667,580,3.153,684,4.735,835,6.115,847,5.023,854,6.436,855,6.399,1042,5.463,1597,5.726,1803,6.658,2643,7.774,2668,6.399,3007,6.977,5260,9.784]],["t/2584",[2061,3.73]],["t/2586",[7,0.624,16,1.473,18,4.5,24,1.838,28,1.117,34,2.016,56,1.011,84,0.857,98,2.782,103,2.649,140,3.102,160,3.59,162,2.782,164,2.175,165,1.773,203,1.895,225,2.829,248,3.715,254,2.545,261,4.011,285,4.231,338,1.947,353,2.159,377,1.77,512,2.729,536,2.357,759,3.979,760,2.339,1033,5.035,1114,3.591,1235,5.051,1236,7.21,3067,7.512,3068,7.512]],["t/2588",[16,1.981,24,1.724,116,3.972,807,4.113,2456,9.101]],["t/2590",[7,0.763,16,2.16,24,1.88,28,1.638,165,2.168,289,3.825,830,4.423,832,6.518,877,3.529,907,6.211,1446,5.663,1798,5.385,1819,6.211]],["t/2592",[24,1.567,28,1.366,203,2.317,323,3.904,338,2.381,377,2.165,550,3.803,807,3.74,1223,5.483,2254,8.065,3069,9.185,3070,8.521,3071,9.185,3072,9.185,3073,8.521,3074,9.185]],["t/2594",[2,1.964,7,0.688,12,1.962,16,1.625,20,3.042,27,2.073,28,1.538,49,1.093,56,0.862,96,2.566,107,2.79,109,1.838,127,2.389,138,1.962,153,3.726,164,1.336,165,1.632,170,2.152,173,5.832,174,2.165,193,3.777,217,2.773,224,2.061,236,1.915,256,1.82,257,2.476,261,2.465,272,1.991,277,2.5,289,2.879,313,1.188,318,1.773,323,3.522,353,1.326,394,2.431,397,2.022,405,1.547,407,3.292,418,2.431,426,1.796,432,4.594,433,2.808,437,2.283,484,2.061,485,3.676,491,1.466,507,2.645,550,4.08,565,2.378,568,2.501,632,5.216,635,1.765,647,1.825,743,4.807,807,1.88,812,4.855,829,2.563,851,2.55,877,1.774,878,2.074,907,3.121,916,2.827,986,3.818,995,3.311,1037,1.854,1047,2.906,1122,3.042,1302,3.15,1352,2.339,1376,2.431,1915,3.961,2033,2.739,2282,3.242,2653,3.879,3075,4.616,3076,4.159,3077,4.159]],["t/2596",[2,2.149,16,2.119,18,3.502,28,1.451,56,0.787,96,2.807,97,3.564,105,2.493,107,2.633,132,3.345,145,3.205,170,3.364,193,3.741,313,1.947,318,1.94,338,1.96,405,2.535,507,4.334,550,4.728,596,3.589,862,3.966,1047,4.761,1122,4.985,1285,4.487,1290,4.434,1302,5.161,1351,7.259,1352,3.832,3076,6.814,3077,6.814]],["t/2598",[16,1.983,23,2.813,24,1.365,25,2.698,28,1.504,49,1.894,56,1.052,109,1.421,132,2.475,228,2.67,297,4.609,313,2.059,398,2.969,476,2.446,491,2.54,502,2.939,536,2.51,545,3.895,577,5.812,580,2.926,1037,3.213,1235,4.157,1332,5.147,3078,9.383,3079,7.419,3080,7.419]],["t/2600",[0,3.822,12,2.67,28,0.934,49,1.488,56,1.103,107,1.695,109,1.75,112,5.801,172,4.056,193,4.259,217,3.774,391,2.787,502,1.826,525,3.12,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,953,4.199,954,4.413,959,6.059,986,3.47,988,3.705,1009,4.779,1235,5.511,1331,7.374,1332,6.824,1367,3.21,3081,5.829,3082,9.138,3083,5.829,3084,5.829,3085,5.829]],["t/2602",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,997,4.614,1235,4.799,1245,2.423,1331,7.685,1332,5.943,1381,3.856,1389,5.771,3086,6.447,3087,6.447]],["t/2604",[2061,3.73]],["t/2606",[2,1.221,4,1.404,7,0.996,15,3.048,34,1.153,48,1.836,49,2.262,56,0.447,58,6.76,68,1.716,84,0.49,85,1.568,97,2.024,103,2.797,113,1.65,129,2.704,160,1.587,164,1.243,176,5.617,178,3.541,186,3.251,231,3.273,244,2.786,275,1.401,278,1.622,296,1.744,304,0.954,313,1.106,326,3.081,334,4.735,356,4.123,377,1.012,381,2.141,390,2.185,392,2.808,396,3.541,398,1.594,401,4.215,416,3.176,481,2.704,484,1.918,564,2.931,575,4.44,580,1.572,590,2.435,607,1.735,614,0.833,683,1.694,684,4.36,723,2.466,736,1.93,839,2.597,846,3.063,945,2.361,953,2.79,1055,2.987,1057,2.931,1381,2.734,1441,2.723,1538,6.423,1565,5.82,1625,6.317,1803,3.319,1918,2.252,1940,3.686,2225,2.409,2297,1.508,2486,3.017,2608,3.368,2671,3.686,2750,3.772,3319,4.123,3513,5.753,3516,5.898,3517,4.295,3518,4.295,3519,6.546,3520,4.295,3521,4.295,3522,4.295,3523,4.295,3524,4.295,3525,4.295,3526,4.295,3527,4.295,3528,4.295,3529,4.295,3530,3.23,3531,3.23,3532,3.609,3533,4.553,3534,4.295,3535,4.295,4546,4.526,4547,4.295,4548,4.526,4549,4.295,5261,4.877]],["t/2608",[4,2.077,7,0.722,15,3.807,16,1.246,21,1.892,24,1.483,26,2.486,34,1.705,97,2.994,98,2.353,145,2.693,158,3.347,159,6.106,196,2.654,203,1.603,247,2.942,261,3.393,276,3.317,279,2.893,321,5.061,338,1.647,343,2.382,365,3.393,393,3.288,401,3.501,402,5.238,403,4.297,408,3.307,412,4.983,513,3.155,580,2.325,611,2.784,669,5.453,723,2.393,764,4.51,841,3.095,915,6.696,978,4.51,992,3.347,1011,4.51,1105,5.58,1115,5.453,1511,4.377,1991,7.358,2225,3.564,2283,4.662,2297,2.23,2386,5.238,2928,5.58,3536,5.34,3538,5.725,3539,5.725,3540,5.58,3541,6.354,3542,4.51,4550,6.099,5262,6.696,5263,7.215]],["t/2610",[7,0.895,15,2.886,49,1.779,56,0.782,159,5.276,322,2.476,369,4.911,401,3.026,412,5.89,438,4.108,607,3.925,723,2.829,846,3.515,1009,3.645,1043,4.108,1601,4.729,1704,8.533,1918,3.939,1991,6.207,1992,6.446,2026,4.049,2225,4.214,2297,3.411,2386,6.192,2600,4.872,2601,5.724,2602,5.724,2604,6.969,2740,6.969,3539,6.768,3544,7.512,3545,7.512,3546,7.512,3548,7.512,3549,7.512,4552,7.916,5262,7.916]],["t/2612",[7,0.92,12,3.936,13,9.125,34,2.485,48,3.959,465,6.795,715,5.016,888,5.749,889,7.499,890,8.68,1491,7.499,1925,8.132]],["t/2614",[7,0.725,16,1.253,31,1.645,34,1.715,48,2.732,58,4.87,59,8.376,112,3.336,116,2.513,133,2.881,160,2.361,176,10.135,178,5.268,275,2.085,283,2.659,300,6.348,324,3.566,334,7.178,351,2.437,353,1.836,372,2.52,465,4.689,532,3.512,580,2.338,590,4.947,604,4.445,684,3.512,720,6.134,846,2.99,1036,4.445,1729,5.37,1781,6.134,1990,4.635,3020,4.87,3021,5.09,3550,6.391,3552,6.391,3553,5.09,3554,6.391,3555,9.938,3558,6.391,3559,6.391,3560,8.727,3561,6.391,3562,6.391,3563,6.391,5264,7.257]],["t/2616",[7,0.938,491,3.043,532,6.207,683,3.777,1625,7.63,3564,11.294,3565,9.58,3566,9.58]],["t/2618",[164,2.822,225,3.672,226,4.98,532,5.357,1993,9.043,3567,8.783,3568,9.748,3569,9.748,3570,9.748]],["t/2620",[2061,3.73]],["t/2622",[2,0.533,5,1.224,16,1.651,24,1.996,28,1.668,29,0.689,56,1.235,84,0.376,97,0.883,105,0.618,129,1.18,130,1.981,132,1.021,153,1.01,155,1.279,160,2.24,162,0.694,164,1.279,165,1.432,170,0.583,193,1.693,196,0.783,203,0.473,224,4.473,225,1.664,232,1.264,239,1.994,243,1.08,275,0.611,277,1.015,279,0.853,289,2.785,297,1.08,323,1.402,324,1.046,351,0.715,365,1.001,367,3.588,377,1.254,394,3.194,405,2.42,407,2.889,446,1.256,495,2.248,502,2.687,505,0.818,511,0.629,514,1.492,527,0.432,528,1.831,550,2.989,555,1.644,568,0.565,583,1.206,591,1.126,605,0.715,607,1.784,609,1.291,614,0.364,630,0.729,632,1.062,635,0.716,650,1.33,662,2.705,663,1.206,675,1.256,683,0.739,734,0.874,748,2.627,749,2.251,754,1.608,760,1.027,802,0.799,807,3.553,814,0.85,816,1.969,820,2.393,841,1.606,862,1.729,877,0.72,878,1.482,880,0.85,893,2.007,907,2.23,946,1.6,973,1.375,979,3.072,1009,1.6,1011,1.33,1012,1.119,1037,1.775,1042,2.091,1048,1.575,1050,3.015,1110,1.448,1117,1.068,1122,2.174,1152,1.33,1191,1.448,1235,0.974,1245,1.15,1247,3.241,1266,0.538,1274,1.206,1285,2.621,1300,1.291,1308,1.171,1312,1.277,1320,3.567,1345,3.366,1352,0.949,1365,2.912,1405,2.83,1456,1.344,1458,2.366,1633,2.194,1699,2.156,1718,3.103,1782,2.097,1806,2.83,1836,1.575,1849,1.391,1850,1.33,1911,1.469,2082,4.111,2225,1.051,2241,2.83,2280,2.719,2363,1.545,2488,1.197,2668,1.391,2677,2.719,2700,1.608,2839,3.169,2844,1.469,3007,1.517,3070,3.06,3073,1.738,3089,1.608,3629,2.174,4750,5.821,4752,4.24,5179,1.798,5265,3.476,5266,1.975,5267,1.975,5268,1.975,5269,1.975,5270,1.975,5271,1.975,5272,1.798,5273,1.798,5274,1.975,5275,3.166,5276,1.798,5277,1.798,5278,1.688,5279,1.798,5280,1.975,5281,4.656,5282,1.975,5283,3.476,5284,5.107,5285,3.166,5286,1.975,5287,1.975,5288,1.975,5289,1.975,5290,1.975,5291,1.975,5292,1.798,5293,1.798,5294,1.798,5295,1.975,5296,3.166,5297,3.98,5298,1.975,5299,1.975,5300,1.975,5301,1.975,5302,1.975,5303,1.975,5304,1.975,5305,3.476,5306,1.975,5307,1.975,5308,1.975,5309,1.975,5310,1.798,5311,1.798,5312,1.975,5313,1.798,5314,1.975,5315,1.975,5316,1.975,5317,3.476,5318,1.975,5319,1.975,5320,1.975,5321,1.975,5322,1.975,5323,1.975,5324,1.975,5325,1.975,5326,1.975]],["t/2624",[16,1.329,18,3.139,23,2.385,24,1.867,25,2.287,28,1.351,34,1.82,49,2.591,70,6.831,97,3.195,203,2.291,278,2.56,289,2.824,295,2.873,342,4.67,373,5.166,381,3.38,476,2.074,484,3.027,580,2.481,607,2.738,614,2.123,637,4.585,749,4.627,757,1.76,760,2.828,769,2.746,780,3.803,846,3.172,878,3.046,1046,3.34,1170,2.193,1234,4.048,1300,4.67,1446,4.18,1798,3.975,1819,4.585,1874,4.975,2032,3.008,2103,5.49,2684,4.397,2717,7.055,3629,4.469,4852,6.109]],["t/2626",[16,2.25,24,1.808,25,3.575,27,2.155,28,1.281,56,0.897,164,2.494,367,5.684,476,2.636,577,4.296,778,5.726,807,4.315,936,3.884,1042,5.463,1315,4.994,3997,5.992,5327,9.08,5328,9.08]],["t/2628",[7,0.638,18,1.887,24,1.753,28,1.387,31,1.049,56,1.312,84,0.465,103,2.217,132,1.946,160,3.794,163,1.702,165,1.484,183,1.843,203,1.586,221,1.586,224,1.819,236,1.129,256,1.607,283,1.242,285,1.774,296,1.655,313,1.049,377,1.482,381,2.032,405,1.366,426,3.997,502,1.184,511,1.369,527,0.939,528,2.262,565,2.163,614,0.791,653,1.352,675,2.732,683,1.607,754,3.497,759,2.836,760,3.461,769,2.546,807,1.659,814,1.849,851,4.24,857,1.948,862,4.525,878,1.831,880,1.849,934,2.048,946,1.977,973,5.633,1037,1.637,1107,2.262,1117,2.323,1218,2.418,1235,3.268,1237,2.892,1266,1.171,1300,6.423,1629,2.99,1782,4.426,1863,1.796,2013,2.708,2032,2.789,2280,3.359,2450,5.284,2451,4.075,2455,4.075,2485,2.686,2626,3.781,2633,2.362,2711,2.781,2844,3.195,5329,4.295,5330,4.295,5331,4.295,5332,4.295,5333,3.911,5334,3.497,5335,3.911,5336,3.911,5337,4.295,5338,4.295,5339,4.295,5340,4.627]],["t/2630",[16,2.058,24,1.448,28,1.561,31,2.184,49,2.01,56,1.092,163,3.544,302,4.117,528,4.711,541,5.503,553,4.735,635,4.013,751,5.162,878,3.813,941,6.556,1798,6.981,1799,6.154,1819,5.738,2717,5.844]],["t/2632",[2,0.601,5,1.361,7,0.176,9,1.444,16,0.719,24,0.988,28,0.861,29,0.778,34,0.568,56,1.287,84,0.242,97,1.728,107,2.878,109,1.577,113,0.813,129,1.332,134,1.109,155,1.444,160,1.793,164,0.613,165,1.367,170,3.51,179,0.746,193,1.861,213,0.907,224,4.211,225,1.828,232,0.811,289,1.527,295,0.897,297,1.219,326,1.518,338,1.258,343,0.793,373,1.612,377,0.864,394,1.114,405,2.732,495,1.866,500,1.055,502,2.579,505,0.924,511,0.711,514,1.685,528,1.175,541,1.372,550,2.972,555,1.055,591,1.271,605,0.807,607,2.339,608,1.157,636,1.255,647,0.836,653,0.702,662,2.246,663,1.362,675,1.418,734,0.987,748,1.685,749,1.444,760,0.659,772,4.028,802,0.902,813,1.444,862,1.923,877,1.409,878,2.181,887,1.406,893,2.231,907,3.283,947,1.135,965,2.438,971,3.254,973,1.552,979,3.376,999,1.213,1011,1.502,1037,1.95,1056,4.612,1110,2.834,1117,1.206,1122,3.199,1152,1.502,1164,1.125,1181,1.157,1191,1.635,1235,1.906,1247,3.562,1266,0.608,1285,1.255,1308,1.323,1320,5.464,1345,4.413,1352,4.326,1365,1.395,1456,1.518,1458,1.518,1699,2.398,1718,2.576,1782,1.004,1806,3.147,1812,2.116,1836,1.778,1849,1.571,1850,1.502,1902,4.096,1953,1.213,2035,1.906,2082,4.475,2363,1.744,2488,1.352,2509,1.635,2639,2.876,2833,1.571,2839,3.482,3089,1.816,4681,1.744,4758,5.218,4761,2.031,5272,2.031,5278,1.906,5284,5.559,5285,3.52,5297,4.374,5334,1.816,5341,2.23,5342,2.23,5343,2.23,5344,2.23,5345,2.23,5346,2.23,5347,2.23,5348,2.23,5349,2.23,5350,2.23,5351,2.23,5352,3.52,5353,2.031,5354,2.23,5355,4.659,5356,2.23,5357,3.865,5358,2.23,5359,2.23,5360,2.23,5361,2.23,5362,2.23,5363,2.031,5364,2.031,5365,2.031,5366,3.52,5367,3.52,5368,2.23,5369,2.23,5370,2.23,5371,4.659,5372,2.23,5373,2.23,5374,2.23,5375,2.23,5376,2.23,5377,2.23,5378,2.031,5379,2.031,5380,1.963,5381,2.031,5382,2.23,5383,2.031,5384,2.23,5385,2.23,5386,3.865,5387,3.865,5388,2.23,5389,2.23,5390,2.23,5391,2.23,5392,2.23,5393,2.23,5394,2.031,5395,2.23,5396,2.23,5397,2.23,5398,2.23]],["t/2634",[4,2.138,28,1.318,84,0.747,109,1.575,130,3.929,132,2.024,133,2.949,160,2.416,164,1.894,170,3.892,236,1.812,278,2.47,295,2.772,373,4.984,377,1.542,426,3.451,491,2.078,495,3.328,566,4.799,605,2.494,608,4.849,614,1.72,622,4.713,637,4.423,820,3.543,857,3.127,877,2.513,947,3.509,952,4.464,1037,2.627,1042,4.147,1045,4.594,1056,8.587,1110,5.054,1116,4.549,1303,4.117,1352,3.314,1699,4.276,1782,3.104,2141,5.392,2438,5.392,5334,5.613,5399,6.893,5400,6.893,5401,6.893,5402,6.893]],["t/2636",[2,0.881,12,1.317,16,0.992,24,1.391,27,1.265,28,1.372,29,1.14,56,1.318,68,1.238,106,1.197,107,1.996,109,1.139,113,1.191,127,2.556,129,1.951,132,0.959,160,3.011,170,2.538,172,1.131,193,3.536,196,1.295,224,1.384,225,1.167,254,1.05,275,1.011,278,1.17,279,1.411,291,3.14,292,1.421,313,0.798,318,0.795,351,1.182,377,1.192,398,1.151,472,1.59,476,1.548,481,1.951,502,1.47,505,1.354,511,1.041,512,1.126,550,1.283,552,1.156,575,1.551,596,1.471,601,1.533,607,1.252,609,2.135,614,1.435,622,1.647,636,1.839,803,1.839,857,1.482,878,1.393,965,4.26,971,5.464,973,2.274,979,2.156,986,2.794,1152,2.2,1235,2.63,1247,2.274,1266,1.454,1285,3.803,1308,1.937,1312,1.959,1320,2.078,1321,3.1,1345,3.855,1362,2.362,1363,1.176,1366,1.817,1405,4.341,1408,1.45,1456,2.223,1458,2.223,1633,3.183,1664,2.395,1826,2.43,1902,3.718,2082,3.909,2280,4.17,2363,4.17,2488,1.98,2639,3.967,2835,2.876,2839,2.223,2844,2.43,3542,3.59,4681,2.555,5273,2.975,5275,4.856,5276,2.975,5277,2.975,5278,4.558,5279,2.975,5292,2.975,5293,2.975,5294,2.975,5296,4.856,5297,2.793,5310,2.975,5311,2.975,5313,2.975,5352,4.856,5353,2.975,5355,2.975,5363,2.975,5364,2.975,5365,2.975,5366,2.975,5367,4.856,5371,2.975,5378,2.975,5379,2.975,5380,2.876,5381,2.975,5383,2.975,5394,2.975,5403,5.059,5404,3.266,5405,3.266,5406,3.266,5407,3.266,5408,3.266,5409,3.266,5410,3.266,5411,3.266,5412,3.266,5413,3.266,5414,3.266,5415,3.266,5416,3.266,5417,3.266,5418,3.266,5419,3.266,5420,3.266,5421,3.266,5422,3.266,5423,3.266,5424,3.266,5425,3.266,5426,3.266,5427,3.266]],["t/2638",[2,1.542,7,0.451,18,2.512,56,0.81,84,1.039,89,2.134,107,2.455,109,0.964,113,2.085,138,2.306,164,1.571,170,3.095,171,1.553,173,5.131,182,2.744,193,3.81,223,4.617,224,4.438,236,1.503,254,3.083,277,2.938,282,3.109,289,2.259,313,2.003,320,3.28,343,2.917,377,1.279,433,3.301,495,2.76,502,2.261,550,2.247,552,2.024,568,1.637,601,2.684,605,2.069,612,3.702,614,1.053,632,5.158,635,2.074,725,4.881,747,2.348,772,3.773,810,2.539,814,2.462,841,2.642,878,2.438,1009,2.632,1012,5.934,1117,3.092,1164,2.884,1274,3.492,1352,2.749,1363,2.059,1364,4.134,1376,4.099,1737,3.093,2092,5.366,2488,3.466,2639,4.254,2905,4.08,3644,3.021,3728,4.192,4758,4.888,5428,8.201]],["t/2640",[16,2.271,27,1.617,34,1.735,85,2.36,97,3.046,107,1.744,113,2.484,170,3.689,173,3.646,203,2.219,217,3.884,278,2.441,295,2.74,351,3.354,353,1.858,476,1.978,484,2.886,552,2.411,580,2.366,607,2.611,614,2.339,637,4.372,725,4.719,740,3.079,747,2.797,749,4.412,757,1.679,760,2.739,769,2.618,772,4.496,878,2.905,1117,3.685,1139,5.638,1300,4.453,1382,4.07,1557,6.536,1907,3.518,2103,5.235,3629,4.261,4511,5.433,5429,6.813]],["t/2642",[24,1.317,28,1.148,56,0.803,85,2.818,106,2.982,107,2.668,109,2.199,127,2.668,170,3.583,172,2.818,254,3.351,444,3.527,476,2.362,527,1.779,658,4.991,757,2.833,997,4.258,1352,5.011,1389,4.825,1421,7.41,5430,7.72,5431,8.136]],["t/2644",[7,0.511,28,0.914,31,1.019,56,1.307,84,0.452,103,2.168,107,2.034,109,1.635,132,1.225,160,3.598,163,1.654,165,1.78,170,3.271,183,1.791,203,1.551,221,1.541,236,1.097,256,1.561,283,1.206,296,1.608,313,1.019,377,1.449,381,1.974,405,1.327,426,3.955,502,1.15,511,1.33,527,0.912,565,2.116,614,0.768,653,1.313,683,1.561,754,3.397,759,2.785,760,3.496,769,2.49,814,1.797,851,4.164,857,1.893,862,3.953,878,1.779,880,1.797,934,1.99,946,1.921,973,6.236,1037,1.59,1042,2.51,1107,2.198,1117,2.257,1218,2.349,1235,3.918,1237,2.81,1266,1.138,1284,2.04,1300,5.854,1352,2.006,1629,2.905,1782,4.367,1863,1.745,1902,3.09,2013,2.631,2032,2.728,2450,5.167,2451,3.959,2455,3.959,2485,2.61,2626,3.673,2633,2.295,2711,2.702,2844,3.104,5329,4.172,5330,4.172,5331,4.172,5332,4.172,5333,3.8,5334,3.397,5335,3.8,5336,3.8,5337,4.172,5338,4.172,5339,4.172,5380,3.673,5432,6.48,5433,4.172,5434,4.172,5435,4.496]],["t/2646",[2,1.665,16,2.346,24,1.525,25,1.976,27,0.931,28,1.67,56,1.213,84,0.827,107,1.58,109,1.588,113,1.43,132,3.675,144,1.878,157,2.264,160,1.375,162,1.378,163,1.554,165,0.878,170,3.497,193,1.426,199,1.934,224,1.661,236,1.623,254,1.261,256,2.31,278,1.405,289,2.439,295,1.577,297,2.145,336,1.596,377,0.877,381,1.855,391,1.651,476,1.138,491,1.182,495,1.893,502,2.597,528,2.066,550,3,552,1.388,568,1.123,582,3.268,605,1.419,607,1.503,614,0.722,622,3.113,635,1.423,662,2.279,675,2.494,725,1.996,740,2.79,756,3.583,807,1.515,841,1.812,878,3.255,947,1.996,951,2.516,1037,1.495,1039,3.452,1056,5.769,1235,1.934,1238,2.342,1285,2.208,1300,2.563,1330,2.669,1352,1.885,1365,2.453,1779,2.641,1782,2.78,2082,6.349,2095,3.452,2372,2.614,2782,2.588,2844,2.918,2846,3.721,2847,3.721,3062,3.452,3617,3.452,3659,3.572,4760,5.622,5436,3.921,5437,3.921,5438,3.921,5439,3.921,5440,3.921,5441,3.921,5442,3.921,5443,3.921,5444,3.921,5445,3.921]],["t/2648",[16,2.033,26,2.719,28,1.373,84,0.793,102,6.137,132,2.858,226,3.551,275,2.267,323,2.953,390,3.536,391,3.083,405,4.045,426,3.594,511,3.973,568,2.097,612,4.742,653,2.305,751,4.228,907,4.699,981,6.037,1052,5.226,1364,5.295,1367,4.718,1376,3.66,1381,2.902,1466,6.261,1907,3.782,2485,4.58,3624,5.627,3644,2.698,4499,9.234,5214,5.963,5215,8.32,5446,7.323]],["t/2650",[5,2.788,16,2.112,28,1.693,56,1.011,84,0.857,107,2.027,113,2.886,132,2.325,182,3.426,224,4.338,225,2.829,323,3.192,397,3.291,426,2.923,511,3.962,596,3.565,609,5.174,772,5.224,877,2.886,981,4.911,1117,4.282,1352,5.976,5447,10.24,5448,7.916,5449,10.24]],["t/2652",[5,2.788,16,1.473,24,1.282,25,2.534,28,1.754,84,0.857,113,2.886,132,2.325,182,2.649,224,4.338,225,2.829,289,3.128,323,3.192,397,3.291,511,3.962,609,5.174,635,2.872,807,4.638,877,2.886,878,3.375,940,3.831,981,6.353,2525,6.768,3895,9.993,4232,6.969,5220,7.21,5450,7.916]],["t/2654",[7,0.633,16,1.922,24,1.299,25,2.569,28,1.132,31,1.96,49,1.803,56,1.02,109,1.353,123,2.734,132,2.357,228,3.272,236,2.11,297,4.388,313,1.96,398,2.826,476,2.329,491,2.419,502,2.848,536,3.076,565,2.62,568,2.298,577,5.905,580,2.786,1037,3.059,1235,3.958,1332,4.901,1782,3.614,3078,9.094,3079,7.064,3080,7.064]],["t/2656",[0,3.822,12,2.67,56,1.103,109,1.75,112,5.801,171,1.799,172,4.056,193,4.259,217,3.774,236,1.741,391,2.787,502,1.826,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,953,4.199,954,4.413,959,6.059,986,3.47,988,3.705,1009,4.779,1037,2.524,1235,5.511,1331,7.374,1332,6.824,1367,3.21,1782,2.982,3081,5.829,3082,9.138,3083,5.829,3084,5.829,3085,5.829]],["t/2658",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,997,4.614,1235,4.799,1245,2.423,1331,7.685,1332,5.943,1381,3.856,1389,5.771,3086,6.447,3087,6.447]],["t/2660",[12,2.847,16,1.767,24,1.537,27,1.675,28,1.34,56,0.697,89,2.634,116,2.634,133,3.02,145,2.838,162,2.48,170,2.085,200,3.497,224,4.022,275,2.185,281,3.339,292,3.07,299,6.093,302,3.25,318,1.719,351,2.554,381,3.339,396,5.522,405,2.245,436,5.037,470,5.104,502,2.958,513,3.326,550,2.774,568,2.021,609,4.614,635,2.561,679,5.522,694,3.839,787,5.748,802,2.855,807,2.728,811,4.379,903,6.611,953,2.855,1024,4.49,1223,3.999,1300,7.5,1303,4.217,1315,3.882,1956,6.429,2486,4.705,3055,5.748,3505,6.214,3728,5.176]],["t/2662",[2061,3.73]],["t/2664",[24,1.696,28,0.95,41,3.055,56,1.163,99,4.247,105,2.106,107,2.881,109,1.986,127,2.208,139,3.022,160,3.224,165,1.509,170,1.99,172,2.333,182,2.253,193,3.345,203,1.612,217,3.839,318,2.239,338,1.657,343,2.395,351,2.437,377,1.506,502,2.536,536,2.006,550,2.646,565,2.199,635,2.443,725,3.429,759,2.361,878,3.921,880,2.9,980,4.635,997,3.405,1363,2.425,1382,4.023,1389,3.995,1473,3.001,1508,5.087,1900,7.053,1901,7.663,1902,4.386,1905,5.37,4511,5.37,4848,5.484]],["t/2666",[0,2.115,1,2.129,2,0.988,7,0.576,12,1.478,21,1.035,28,1.03,34,0.933,56,1.285,84,0.397,96,1.291,105,1.146,107,1.498,109,2.042,123,1.249,124,2.311,127,1.202,133,1.567,144,1.755,160,3.404,170,1.083,171,0.995,172,1.269,197,1.633,198,1.784,213,1.491,228,1.161,277,1.883,279,1.583,291,2.158,292,1.594,295,2.352,297,2.004,302,1.687,313,0.895,318,0.892,322,1.146,338,2.507,343,3.454,365,1.857,377,1.308,395,1.448,397,1.523,426,1.353,430,1.807,437,1.72,502,1.01,513,1.727,527,0.801,557,1.823,591,3.334,605,1.326,614,0.675,628,1.892,647,4.093,712,1.971,747,2.998,765,2.173,801,2.726,880,3.144,885,2.089,893,2.115,914,5.071,980,5.737,997,1.357,1117,1.982,1164,1.848,1237,2.468,1241,2.984,1381,1.452,1383,3.226,1445,4.073,1486,4.289,1508,5.039,1693,2.144,1845,3.47,1870,1.275,1900,8.06,1901,8.092,1902,4.861,1904,2.726,1905,5.822,2231,4.23,2839,2.494,3057,2.65,3876,4.353,3879,2.726,3880,2.395,4228,3.226,4813,3.337,4848,5.945,4849,2.866,4850,3.664,4851,3.226]],["t/2668",[5,2.645,24,1.792,28,1.06,31,1.835,56,0.977,68,2.848,84,0.814,95,4.659,112,3.721,165,2.216,203,1.798,236,1.975,256,2.81,275,3.063,279,3.245,291,4.424,313,1.835,317,3.754,367,3.824,397,3.122,517,4.553,536,2.947,568,2.151,759,3.468,760,2.923,769,4.252,774,4.062,807,2.902,814,3.234,946,4.556,993,4.336,1263,4.553,1508,6.121,1733,4.486,1898,3.957,3990,6.612,4849,5.875,4852,6.422]],["t/2670",[7,0.758,21,1.623,24,1.799,28,1.568,56,0.949,84,1.041,109,1.388,138,2.317,160,3.68,164,1.578,165,1.843,174,1.708,182,1.922,228,1.82,233,2.079,236,2.164,289,4.149,292,2.499,314,6.562,405,1.828,407,3.72,505,2.381,511,2.623,527,2.102,536,2.863,557,4.095,635,2.084,759,2.014,760,3.102,769,2.208,778,3.623,807,3.18,814,3.544,878,4.1,880,3.544,946,3.789,1042,4.951,1263,4.988,1352,3.957,1373,5.026,1508,5.807,1557,3.564,1900,7.07,2672,5.379,2679,6.545,3051,3.869]],["t/2672",[2,2.121,56,1.225,84,1.104,96,3.985,107,2.013,109,2.091,130,4.482,160,3.574,164,2.16,196,3.116,313,1.921,338,1.934,377,1.758,395,3.107,647,3.825,653,2.475,712,4.23,880,3.386,914,5.63,1170,2.414,1486,7.476,1801,4.482,1870,2.737,1900,7.62,1902,3.75,4851,6.922]],["t/2674",[56,1.311,109,1.674,338,2.441,914,4.938,1900,6.684,1902,4.733,1904,8.767,4853,9.039]],["t/2676",[2,2.358,12,3.525,21,2.47,56,1.308,96,3.079,109,1.839,291,5.149,313,2.136,338,2.683,653,2.751,914,5.427,1417,5.769,1900,7.345,1901,7.284,1902,5.201,1904,6.504,3866,4.537,4853,7.962]],["t/2678",[28,1.518,29,2.983,56,1.302,109,1.442,302,3.937,324,5.696,338,2.646,395,3.379,628,4.416,712,4.6,724,5.237,880,3.682,914,4.255,980,5.885,1486,6.27,1900,5.758,1902,4.078,1905,6.818,1998,4.969,2930,6.688,3921,6.362,4848,6.963]],["t/2680",[2,1.915,7,0.56,56,0.941,84,0.769,107,2.441,109,1.939,127,2.329,139,4.829,170,3.649,172,2.46,182,2.376,198,3.458,203,1.7,246,2.685,253,4.273,313,1.735,338,2.829,351,2.57,392,4.406,502,1.958,527,2.084,536,2.115,583,4.337,759,2.489,760,3.398,820,3.65,880,4.634,980,4.888,997,2.629,1363,2.557,1508,3.928,1754,4.052,1884,4.687,1902,3.387,1905,5.663,2372,4.734,2764,6.252,4848,5.783]],["t/2682",[5,2.788,24,1.282,28,1.117,31,1.934,56,1.011,68,3.001,84,0.857,95,4.911,107,2.027,112,3.921,165,2.294,170,2.339,256,2.962,275,3.17,279,3.42,291,4.663,313,1.934,317,3.957,367,4.03,397,3.291,517,4.798,536,2.357,568,2.267,759,3.59,760,3.025,769,4.362,774,4.282,993,4.57,1263,4.798,1352,3.806,1508,5.664,1733,4.729,1898,4.17,4849,6.192,4852,6.768]],["t/2684",[7,0.734,21,1.936,28,1.491,56,0.919,84,1.008,107,1.754,109,1.156,138,2.764,160,3.704,164,1.882,165,2.085,170,2.749,174,2.037,203,1.64,228,2.171,233,2.48,236,1.802,289,2.708,292,2.981,405,2.18,505,2.84,511,3.369,527,2.31,536,2.041,635,2.486,759,2.402,760,3.122,769,2.633,772,4.522,778,4.322,807,2.648,814,2.951,878,4.506,946,3.155,1009,3.155,1263,4.154,1352,3.295,1508,5.148,1900,6.267,2679,7.193,3051,4.615,3054,6.033,4854,9.306,4855,6.853]],["t/2686",[4,0.33,7,0.812,12,0.429,15,1.014,16,1.313,17,0.42,18,1.805,21,0.559,23,1.163,24,1.19,25,0.634,27,0.66,28,1.143,29,0.97,31,1.14,34,1.431,46,0.44,47,0.67,48,0.431,49,1.439,56,1.106,68,2.427,84,0.693,85,1.79,96,0.979,97,0.475,100,0.475,101,0.566,103,2.017,105,0.87,107,1.727,108,0.549,109,1.498,112,1.377,113,0.387,116,0.397,123,1.914,124,0.67,127,1.694,132,0.312,133,0.455,138,0.429,140,0.776,145,0.796,158,2.332,160,1.44,161,3.176,164,1.129,165,0.623,169,0.581,170,0.821,171,0.289,174,0.316,177,0.513,193,1.011,196,0.785,200,0.981,203,0.834,213,1.131,219,0.613,221,0.392,224,0.839,225,0.38,228,0.627,236,0.731,246,0.402,247,0.467,248,2.829,249,0.791,253,0.639,254,0.637,261,0.538,272,0.435,275,1.078,278,0.709,285,1.149,286,0.644,287,0.758,296,0.409,298,0.649,304,0.417,305,0.676,313,0.484,316,0.882,317,0.531,318,1,322,1.09,338,0.487,343,0.704,348,0.682,351,1.006,353,0.758,359,0.469,366,0.665,377,1.155,382,0.659,387,0.74,388,0.768,390,1.342,395,1.624,398,0.374,401,3.007,405,0.338,407,0.48,410,1.307,418,0.99,421,2.179,437,0.499,438,1.028,470,0.768,471,2.328,476,1.011,479,0.74,481,0.635,482,0.475,484,0.839,491,1.557,498,0.591,502,0.767,512,0.959,525,0.501,527,0.898,528,1.043,530,2.863,532,1.033,557,0.529,562,2.291,564,0.688,565,1.138,568,1.609,572,0.563,575,0.941,580,0.369,600,0.708,605,0.385,607,1.066,613,0.48,614,0.641,621,1.429,630,1.517,635,1.264,653,0.623,655,0.695,670,0.618,683,1.04,684,0.554,712,1.065,723,1.668,736,0.453,743,1.307,747,0.813,756,0.499,757,0.859,759,1.44,760,2.456,769,0.761,784,0.779,807,0.765,813,0.688,814,0.853,828,0.443,829,0.56,839,0.61,846,0.472,850,0.695,851,3.16,854,0.566,862,0.529,874,0.972,878,0.453,880,0.853,898,0.515,921,0.865,930,1.52,939,0.654,945,1.033,946,0.489,953,0.43,954,0.708,973,3.912,993,1.143,995,0.723,1006,0.544,1037,0.405,1043,3.5,1045,1.32,1050,0.688,1053,0.831,1057,0.688,1107,1.043,1114,0.898,1132,0.74,1170,0.326,1193,1.753,1204,0.847,1214,1.282,1234,0.602,1235,1.718,1240,0.606,1245,0.352,1266,0.29,1274,1.698,1284,0.968,1289,0.43,1300,0.695,1308,1.649,1312,0.728,1352,0.511,1366,0.591,1376,0.99,1381,0.421,1390,2.932,1399,1.166,1432,1.612,1441,0.639,1465,0.768,1476,0.39,1477,0.791,1493,2.216,1523,1.008,1530,0.639,1540,0.588,1565,1.379,1566,1.413,1567,0.817,1579,0.885,1628,0.817,1632,1.443,1633,0.501,1638,0.549,1687,0.885,1754,0.842,1759,1.363,1782,0.892,1792,0.665,1801,0.606,1806,1.612,1863,0.444,1870,0.689,1902,0.944,1963,0.768,1971,0.791,1998,0.618,2009,0.731,2012,0.935,2026,1.782,2027,3.514,2032,1.17,2033,0.598,2043,0.749,2061,0.359,2064,0.758,2109,0.968,2111,0.909,2112,0.847,2154,3.173,2162,1.65,2180,0.968,2196,1.693,2231,0.768,2254,0.885,2297,1.368,2312,0.569,2353,1.183,2395,0.557,2411,1.452,2448,1.693,2495,0.885,2533,0.885,2583,1.008,2600,2.144,2601,2.519,2602,2.01,2608,1.473,2623,2.101,2624,1.549,2633,0.584,2710,1.008,2814,1.379,2815,1.379,2835,0.935,2867,0.622,2895,0.654,2917,1.008,3051,0.716,3513,0.731,3533,1.307,3668,0.865,3880,1.294,3895,2.448,3918,0.935,4016,1.452,4072,3.617,4432,3.067,4474,1.452,4719,1.063,4720,1.063,4721,2.78,4722,2.78,4723,2.78,4724,2.78,4725,2.78,4726,3.484,4727,3.484,4728,3.484,4729,3.484,4730,3.484,4731,2.78,4732,2.78,4733,2.78,4734,2.78,4735,2.78,4736,3.484,4737,3.484,4738,3.484,4739,3.484,4740,3.484,4741,1.063,4742,1.063,4743,1.063,4745,1.063,4746,1.063,4747,1.98,4748,3.484,4749,3.484,4750,0.968,4751,1.063,4752,0.968,4753,1.063,4754,1.063,4755,1.063,4756,1.98,4757,1.063,4758,1.693,4759,1.063,4760,0.968,4761,0.968,4762,1.063,4764,1.063,4765,1.98,4766,1.063,4767,1.063,4768,1.063,4769,1.063,4770,1.98,4771,2.78,4772,1.98,4773,1.063,4774,1.98,4775,1.98,4776,1.98,4777,1.98,4778,1.063,4779,1.063,4780,1.063,4781,1.063,4782,1.063,4783,1.063,4784,1.803,4785,1.063,4786,1.98,4787,1.063,4788,1.063,4789,1.063,4790,1.063,4791,1.063,4792,1.063,4793,1.063,4794,1.063,4795,1.063,4796,1.063,4797,1.063,4798,1.063,4799,1.063,4800,1.063,4801,1.063,4802,1.063,4803,1.063,5184,1.008,5451,1.145,5452,1.145,5453,1.145]],["t/2688",[2061,3.73]],["t/2690",[2,1.557,7,0.651,24,1.704,29,2.014,49,1.298,56,1.204,84,0.894,107,1.478,109,1.774,139,2.591,140,2.262,170,2.44,179,1.932,182,1.932,203,1.382,236,1.518,254,1.856,275,1.787,296,2.225,313,1.41,322,1.806,377,1.291,381,3.907,395,2.282,418,2.886,484,2.446,512,1.991,517,3.5,536,1.719,578,2.269,601,2.71,614,1.063,628,2.982,634,6.346,647,3.098,683,2.16,731,3.026,759,2.024,857,2.619,940,2.16,1043,5.461,1117,3.123,1170,1.772,1240,3.291,1289,2.335,1605,4.233,1693,3.378,1754,2.454,1870,3.875,1873,7.499,1906,6.054,1907,4.979,1908,6.794,2297,1.923,3391,4.936,4704,8.781,4705,5.259]],["t/2692",[7,0.337,12,1.726,21,1.209,24,1.589,28,1.281,29,1.493,56,1.176,84,0.716,107,2.858,109,2.039,162,1.504,165,1.48,182,2.211,217,2.439,246,1.618,254,3.155,285,1.768,313,1.045,338,1.625,353,1.167,377,1.478,381,2.024,395,1.691,433,2.47,446,2.722,491,1.29,505,1.773,517,2.594,527,2.505,536,1.968,565,1.397,580,1.486,634,3.288,653,1.347,683,1.601,705,5.587,759,2.316,880,1.842,914,4.517,953,3.265,1033,2.722,1043,5.091,1170,1.314,1181,2.221,1245,3.003,1266,1.802,1289,1.731,1476,1.572,1735,2.979,1737,2.492,1765,2.454,1863,1.79,1870,1.489,1873,5.227,1906,4.937,1907,3.412,1908,4.656,2013,2.699,2092,2.395,2395,5.142,2636,2.454,2669,3.234,2837,3.184,3391,3.658,4590,3.234,4704,6.018,4705,6.018,4706,4.279,4707,3.897,4708,8.072,4709,6.607,4710,4.279,4711,4.279,4712,4.279,4713,6.607,4714,4.279,4715,6.607,4716,4.279,4717,4.279,4718,4.279]],["t/2694",[109,1.717,127,3.339,179,3.407,246,3.851,580,3.536,880,4.385,1873,7.746,1908,7.176,2395,5.337]],["t/2696",[109,1.717,172,3.527,179,3.407,246,3.851,580,3.536,880,4.385,1873,7.746,1908,7.176,2395,5.337]],["t/2698",[109,1.717,179,3.407,246,3.851,580,3.536,880,4.385,997,3.77,1873,7.746,1908,7.176,2395,5.337]],["t/2700",[2061,3.73]],["t/2702",[7,0.675,12,3.452,16,1.13,24,1.743,25,1.944,27,2.694,28,0.857,31,1.483,36,3.774,39,3.483,50,3.553,56,1.12,70,4.333,84,0.658,98,2.134,101,3.232,102,3.83,105,1.899,106,2.225,113,2.214,121,3.553,131,2.755,165,1.917,171,2.325,197,2.706,201,3.091,240,3.321,304,1.28,339,3.063,353,1.656,377,1.914,394,3.035,404,2.957,407,2.745,408,2.192,416,2.796,502,1.674,505,2.516,508,3.577,579,4.391,614,1.118,630,2.243,734,2.687,752,4.945,779,4.842,787,4.945,848,4.945,857,2.755,1108,5.764,1633,2.861,1689,3.183,1822,3.654,2297,2.023,2486,4.048,2550,4.391,2787,5.445,3312,5.346,4804,4.945,4829,11.348,4830,6.073,4831,8.559,4832,5.531,4833,6.073]],["t/2704",[24,1.542,27,2.26,28,1.344,84,1.031,162,3.346,225,3.403,232,3.463,322,2.978,524,5.861,614,1.753,650,7.741,723,3.403,1179,5.861,1864,6.055,3644,3.508,4834,9.522]],["t/2706",[7,0.751,21,2.691,27,2.26,84,1.031,100,4.258,106,4.212,169,5.207,232,3.463,304,2.007,404,4.636,507,5.179,614,1.753,752,7.754,756,4.469,1671,5.237,4045,7.754,4832,8.673]],["t/2708",[7,0.808,16,0.57,25,0.98,28,0.713,29,1.764,31,0.748,34,0.78,40,3.402,41,1.389,46,1.269,49,1.136,56,1.112,84,0.812,85,1.751,92,2.776,93,2.142,95,3.136,101,1.63,107,1.294,109,1.982,121,1.791,131,1.389,160,1.073,162,1.076,163,2.559,164,0.841,165,1.447,171,2.254,174,2.228,179,1.024,190,2.598,197,1.364,203,0.733,213,1.246,224,4.34,228,0.97,233,3.421,240,1.674,244,1.884,254,0.985,256,1.146,257,1.559,277,1.574,279,2.184,281,2.392,288,2.021,289,1.21,318,0.745,322,0.958,323,2.039,343,1.089,347,2.021,351,1.108,377,0.685,393,1.504,398,1.078,401,1.17,405,0.974,433,1.768,437,1.437,471,1.269,472,2.462,476,0.889,481,1.829,484,1.297,492,2.395,500,1.448,502,1.394,506,1.816,513,1.443,536,1.923,550,4.717,565,2.448,577,1.448,579,2.214,593,1.51,607,1.173,634,2.353,635,1.111,647,1.149,650,2.062,653,0.964,672,1.803,694,2.75,704,2.695,706,2.185,747,1.257,748,2.314,749,1.983,757,0.754,772,3.337,818,2.538,829,2.663,830,1.399,841,2.337,857,1.389,922,2.084,993,1.768,997,2.776,1009,1.41,1028,2.021,1047,1.829,1108,2.062,1170,0.94,1223,1.734,1238,1.829,1274,1.87,1281,2.55,1312,2.373,1365,3.162,1381,1.213,1398,2.695,1477,2.278,1633,1.443,1859,3.062,1863,1.281,1870,2.247,1898,1.613,2297,1.02,2315,2.905,2485,1.915,2509,2.245,2717,2.001,2740,2.695,2784,2.551,2799,3.955,2966,2.084,3032,2.551,3061,2.493,3817,1.948,3866,1.589,4234,2.695,4539,5.056,5240,7.114,5241,7.875,5454,5.448,5455,3.299,5456,5.448,5457,2.905,5458,3.062,5459,5.448,5460,5.448,5461,3.299,5462,3.299,5463,3.299,5464,3.299,5465,3.299,5466,3.299]],["t/2710",[2061,3.73]],["t/2712",[1,2.975,2,2.04,7,0.784,17,2.023,31,1.848,49,2.023,56,1.227,92,1.895,121,4.426,133,2.19,165,1.147,182,3.011,183,2.197,221,1.89,233,2.738,236,1.346,246,2.861,315,2.431,318,1.246,322,1.601,338,2.732,377,1.145,398,2.665,408,2.731,422,3.484,426,1.89,447,2.422,502,1.411,511,1.632,579,3.701,590,2.754,611,2.128,630,1.89,751,2.955,757,1.864,760,2.235,839,2.936,945,2.67,1013,5.63,1045,7.071,1164,2.582,1216,2.606,1278,3.412,1284,2.503,1363,2.724,1367,2.482,1376,2.558,1718,3.412,1730,4.168,1734,4.857,1737,1.93,1854,4.662,2312,2.739,2486,5.999,2632,3.058,2708,3.701,2717,3.346,3412,5.918,3628,7.514,3917,3.256,3994,4.168,4396,4.662,4849,4.004,5467,5.516,5468,5.516,5469,5.516,5470,5.516,5471,9,5472,9,5473,5.516,5474,9,5475,5.516,5476,5.516,5477,5.516,5478,4.857]],["t/2714",[2,2.393,7,0.699,49,1.994,56,1.27,84,0.961,92,4.076,94,4.8,105,2.776,121,5.191,171,2.991,174,2.637,196,3.517,313,2.168,322,2.776,408,3.203,590,4.774,715,4.561,2287,5.505,2297,2.956,3481,6.04,5479,9.562]],["t/2716",[29,3.714,318,2.592,395,4.207,471,4.411,1476,3.911]],["t/2718",[4,2.577,36,3.663,84,0.9,100,3.715,121,6.18,228,2.632,236,2.777,304,1.751,338,2.044,398,4.091,653,2.615,730,6.765,751,6.705,942,4.904,953,3.361,1045,5.538,1266,2.266,1363,2.992,1476,3.053,1671,6.387,2636,4.766,3412,6.499,3628,6.279,5478,11.02]],["t/2720",[4,2.523,16,1.514,26,3.02,31,2.546,84,0.881,182,2.722,292,3.539,338,2.829,414,4.667,653,2.561,662,4.728,760,2.404,942,4.401,988,4.553,1045,5.423,1170,2.498,1278,5.423,1363,2.929,1476,2.989,1671,6.324,1765,4.667,1838,7.755,2092,4.553,3412,8.152,3628,6.148,4193,7.41,4849,6.364,5471,8.136,5472,8.136,5474,8.136,5478,7.72]],["t/2722",[2061,3.73]],["t/2724",[7,0.804,15,2.867,34,2.002,48,3.19,56,0.776,58,5.686,84,1.104,164,2.16,196,3.116,203,1.882,233,3.69,275,2.434,295,3.162,381,3.72,398,2.77,416,3.62,575,4.843,580,2.73,590,4.23,613,3.554,684,5.901,723,2.81,794,5.475,917,5.541,945,4.101,953,3.181,1057,5.092,1538,7.834,1565,5.475,1625,5.942,1803,5.765,1943,7.462,3516,8.717,4547,7.462,4549,7.462,5480,8.473]],["t/2726",[7,0.884,15,3.337,56,0.904,84,0.991,96,3.223,159,6.099,233,4.06,412,6.809,846,4.063,922,6.229,978,6.163,1991,5.547,2225,4.871,2283,6.371,2297,3.048,2386,7.158,3536,8.946,3538,7.824,4550,8.335,5481,9.86]],["t/2728",[84,1.204,159,8.032,233,4.022,292,3.919,351,3.261,621,4.631,802,3.645,846,4.001,888,5.307,889,6.923,890,6.704,1056,6.809,1445,6.273,1991,5.461,3536,7.185,3542,7.484,3595,8.55,3596,8.55,3597,7.508,5482,9.709]],["t/2730",[7,0.789,300,6.067,580,3.475,684,5.22,3021,7.565,3599,8.558,3600,8.558,3601,8.558,3605,9.498,5483,10.009,5484,10.785,5485,10.785]],["t/2732",[7,0.769,233,4.222,300,5.916,532,5.09,580,3.389,684,5.09,3020,7.057,3599,8.344,3600,8.344,3601,8.344,3609,9.261,5483,9.76,5486,10.516,5487,10.516]],["t/2734",[7,0.731,16,1.268,34,1.735,49,2.083,56,0.915,85,3.649,96,2.4,158,3.405,174,2.755,198,3.317,203,2.219,233,4.092,254,2.191,292,2.963,296,2.625,332,3.536,398,3.265,408,2.459,491,2.794,513,3.21,525,3.21,532,3.553,614,1.254,653,2.144,769,2.618,846,4.116,917,8.798,1040,4.689,1046,4.334,1114,3.091,1202,3.589,1363,2.453,1632,4.81,1754,2.896,2033,3.836,2297,2.269,2395,3.571,2930,5.329,3611,7.925,3612,6.465,3613,6.465,3614,6.465,3615,6.205,3616,6.465,3617,5.998]],["t/2736",[4,2.176,7,0.843,39,2.855,48,2.847,56,0.934,84,0.76,109,1.804,116,2.618,165,1.572,171,2.906,197,3.126,233,3.871,236,1.845,322,2.958,480,4.776,482,3.138,580,2.436,614,1.969,747,2.881,1076,5.392,1164,3.539,1238,4.191,1380,4.425,1633,3.306,1870,3.723,2297,2.337,2489,6.177,2683,5.82,2692,8.973,3413,6.838,3644,2.585,4051,6.391,5457,6.658,5488,9.456,5489,9.456,5490,9.456,5491,9.456,5492,10.189,5493,10.189]],["t/2738",[2061,3.73]],["t/2740",[7,0.624,27,2.431,31,1.934,34,2.016,106,2.901,112,3.921,131,3.591,138,3.192,172,2.742,189,4.236,190,4.068,191,5.724,193,2.879,198,3.854,219,3.17,233,2.865,278,2.836,295,3.183,318,2.493,341,4.354,353,2.159,377,1.77,477,4.695,491,2.386,498,4.404,499,5.512,500,3.745,504,4.798,512,2.729,774,4.282,1001,6.083,1008,4.798,1009,3.645,1011,5.331,1067,6.192,1078,6.596,1108,5.331,3638,5.983,3639,7.512,3640,7.512]],["t/2742",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1011,6.465,3638,7.255]],["t/2744",[7,0.757,106,3.518,131,4.355,138,3.872,190,4.934,219,2.971,233,3.474,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,512,3.31,536,2.859,1011,6.465,3638,7.255]],["t/2746",[7,0.757,88,4.323,106,3.518,131,4.355,138,3.872,190,4.934,233,3.474,318,2.337,341,5.28,477,5.694,498,5.341,499,6.684,500,4.542,504,5.819,567,3.849,1011,6.465,3638,7.255]],["t/2748",[7,0.832,139,4.734,233,3.818,341,5.802,759,3.698,1075,6.131]],["t/2750",[7,0.782,24,1.607,88,4.47,206,6.427,233,3.592,341,5.458,552,3.513,567,3.979,723,3.547,852,4.302,862,4.938,3641,11.181]],["t/2752",[7,0.929,29,3.462,219,3.072,233,3.592,315,4.714,318,2.416,395,3.922,922,6.756,1477,7.385,1688,6.756,1945,6.615,3311,8.486]],["t/2754",[34,2.817,42,6.556,96,3.149,127,2.932,155,5.79,253,5.38,283,2.585,318,2.177,353,2.438,377,2,381,4.23,472,4.354,502,2.465,530,3.998,698,6.466,839,5.129,1009,4.117,1074,6.758,1962,6.871,1963,6.466,1964,6.086,1965,7.998,2068,7.645,3402,6.758]],["t/2756",[7,0.751,27,2.26,34,2.425,102,6.005,172,3.298,283,2.753,318,2.318,530,4.258,1009,4.384,1074,7.196,1101,7.196,1557,5.907,1962,7.317,1963,6.885,1964,6.481,1965,8.311,3642,9.036]],["t/2758",[2061,3.73]],["t/2760",[1,2.249,7,0.678,16,1.137,17,1.529,24,1.392,25,1.239,27,0.918,28,0.862,31,2.1,36,3.337,46,1.604,48,1.57,49,0.87,50,2.264,56,1.232,89,1.444,109,1.873,115,2.813,121,2.264,123,2.579,130,2.206,131,2.771,132,1.137,138,1.561,169,2.116,170,1.143,172,1.34,174,2.555,201,3.11,207,1.579,219,1.198,224,3.206,233,2.211,262,2.529,287,4.359,299,2.483,315,1.838,318,2.421,338,1.862,339,3.081,353,1.055,372,1.448,381,2.89,403,2.483,408,1.397,415,2.727,416,2.813,506,2.295,527,2.174,530,1.73,550,2.4,565,1.263,578,1.521,599,1.989,658,1.853,688,3.672,705,2.382,718,3.308,731,2.028,740,2.761,756,2.867,759,1.356,760,2.236,805,2.634,822,2.311,880,2.63,903,2.694,904,2.093,922,2.634,939,2.382,940,1.448,947,1.97,953,1.565,997,1.433,1041,2.461,1043,2.008,1044,2.279,1073,2.798,1076,4.694,1132,2.694,1133,2.082,1134,3.224,1156,3.151,1216,1.97,1218,2.179,1245,2.504,1281,3.081,1289,1.565,1389,2.295,1394,2.579,1473,1.724,1483,3.224,1500,4.617,1508,2.14,1528,3.672,1792,2.42,1822,2.328,1873,2.506,1908,2.727,1916,2.311,1928,3.672,1972,2.973,2209,3.672,2518,1.934,2669,2.924,2679,4.158,2684,2.382,2692,5.797,2726,2.761,2840,3.524,2910,3.027,3346,3.87,3618,3.672,3619,3.308,3620,3.407,3622,3.308,3623,3.308,4835,3.87,5240,3.672,5241,3.672,5494,4.17]],["t/2762",[4,2.589,7,0.766,15,2.137,18,2.575,28,0.827,31,1.432,56,0.824,68,2.222,170,1.731,233,3.518,236,1.541,237,4.297,247,2.575,254,1.884,275,1.814,281,2.772,342,3.83,397,2.436,398,4.099,525,2.761,535,5.337,540,5.01,580,2.898,623,2.806,658,2.806,684,3.056,686,4.584,696,4.13,724,2.853,741,3.795,757,1.444,769,3.736,796,3.072,798,3.223,814,2.523,842,5.01,846,2.602,856,3.501,898,2.841,917,7.465,936,2.507,939,3.607,958,3.83,996,3.696,1037,2.234,1170,1.799,1201,3.906,1476,2.153,1500,4.429,1527,3.867,1557,3.636,1733,3.501,2118,4.13,2717,3.83,2782,3.867,2960,5.159,3615,7.602,3626,5.561,4290,5.337,5495,6.315,5496,6.315,5497,6.315,5498,10.475,5499,6.315,5500,10.475,5501,6.315,5502,6.315,5503,6.315,5504,6.315,5505,6.315,5506,6.315,5507,6.315,5508,6.315]],["t/2764",[7,0.941,15,2.31,16,1.179,18,2.784,24,1.026,36,2.793,39,2.578,46,2.625,84,0.686,85,2.194,86,4.312,95,3.93,96,2.231,108,3.271,131,2.874,145,2.547,162,2.226,174,1.883,228,2.007,233,4.43,240,3.464,247,3.874,278,2.27,283,1.832,313,1.548,318,1.542,338,1.558,377,1.417,393,3.111,398,2.231,408,3.183,504,3.84,505,3.654,576,3.731,578,2.489,590,3.408,683,2.37,757,1.561,846,2.813,877,2.31,885,3.611,917,7.727,922,4.312,1009,4.669,1046,4.741,1180,3.995,1202,3.337,1216,3.225,1240,3.611,1538,4.868,2141,4.955,2298,5.279,2960,5.577,2961,6.011,3611,7.538,3625,8.367,4836,6.335]],["t/2766",[2061,3.73]],["t/2768",[4,1.541,21,1.403,26,1.844,28,0.701,29,1.733,49,1.663,56,1.312,84,0.801,85,2.563,105,2.314,109,2.015,113,1.811,140,1.946,144,2.379,155,3.216,160,1.741,165,1.113,174,1.476,192,2.926,219,2.29,225,2.645,254,1.597,272,2.033,295,1.997,302,2.287,313,1.213,322,1.554,353,1.354,377,1.977,395,1.963,405,1.58,426,1.834,447,2.35,463,2.989,517,3.011,614,1.628,705,6.03,755,2.237,801,3.696,824,3.642,827,2.517,914,5.466,954,3.311,1170,3.216,1234,2.814,1235,3.649,1289,3.576,1434,2.764,1633,2.341,1638,2.565,1693,2.906,1753,3.419,1870,3.96,1876,3.216,1902,2.369,1972,3.817,2252,4.714,2395,2.604,2896,4.045,4804,4.045,4805,4.967,4806,7.399,4807,7.399,4808,6.739,4809,7.399,4810,4.967,4811,4.967,4812,4.967]],["t/2770",[7,0.6,12,3.071,16,0.961,49,1.711,56,0.986,84,0.825,85,3.134,109,2.124,113,1.883,162,1.814,164,1.419,165,1.157,174,1.535,182,2.548,199,2.547,219,1.598,246,1.953,257,2.629,297,2.824,313,1.262,323,2.083,336,2.101,338,1.873,391,2.174,405,2.422,426,1.907,447,3.603,484,2.188,491,2.727,508,3.042,613,3.442,614,0.951,660,3.842,705,7.663,841,2.387,914,4.968,951,4.887,1042,3.107,1170,2.777,1196,4.118,1238,4.549,1282,3.968,1289,4.307,1381,2.047,1506,3.314,1753,3.554,1754,3.236,1844,3.154,1870,3.877,1876,3.344,1904,3.842,1972,3.968,2307,3.595,2395,5.581,2608,3.842,3033,4.118,3878,3.968,3969,3.968,3973,4.703,4707,4.703,4813,4.703,4814,5.164,4815,4.703,4816,5.164]],["t/2772",[2,1.881,48,2.83,56,1.313,109,2.118,113,2.543,186,3.287,283,2.017,377,1.56,437,3.274,527,1.525,611,2.899,705,7.563,914,6.114,1170,2.891,1289,2.821,2102,5.964,2395,5.589,2518,3.486,2754,5.562,4808,8.578,4817,6.975,4818,6.975,4819,6.975,4820,6.975,4821,6.975,4822,6.975,4823,6.975,4824,6.975,4825,6.975,4826,6.975,4827,6.975,4828,6.975]],["t/2774",[7,0.733,12,3.749,84,1.007,101,4.948,109,1.91,228,2.945,233,3.364,509,5.266,578,3.653,580,3.228,634,7.143,702,6.26,705,6.972,940,3.478,1873,7.335,1908,7.982,2395,4.872]],["t/2776",[7,0.669,29,2.961,31,2.074,39,3.454,46,3.518,50,6.264,84,0.919,98,2.983,105,2.655,123,2.893,131,3.851,207,3.465,233,3.072,248,3.984,254,2.73,278,3.042,313,2.074,318,2.067,447,4.016,572,4.495,578,3.336,580,2.947,814,3.655,1043,4.405,1044,5,1389,5.035,1392,6.912,1408,3.769,1826,6.316,2717,5.548,3623,9.155]],["t/2778",[2061,3.73]],["t/2780",[7,0.724,15,2.451,21,1.236,24,1.867,29,1.526,34,1.114,56,1.24,84,0.474,89,2.508,98,1.537,103,2.249,105,1.368,108,2.259,139,1.963,160,1.533,171,1.188,182,2.738,203,1.047,219,1.354,221,3.022,232,2.977,254,1.407,318,1.636,338,1.076,353,1.193,377,1.503,395,2.656,398,1.541,410,2.887,426,1.616,437,2.053,471,1.813,476,2.376,491,2.026,502,1.206,512,2.822,527,2.167,528,2.305,536,1.303,564,2.833,607,2.576,630,1.616,653,1.377,747,1.796,757,1.078,759,2.869,760,2.418,769,1.681,841,2.022,945,4.79,1050,7.281,1107,2.305,1170,2.063,1257,4.756,1477,3.255,1759,3.011,1906,4.451,2026,2.238,2032,2.829,2225,2.328,2297,1.457,2312,4.379,2633,2.406,2815,3.046,2867,2.559,3513,3.011,3936,3.851,4016,3.208,4054,5.473,4356,6.664,4856,4.375,4857,4.375,4858,8.184,4859,4.375,4860,6.721,4861,4.375,4862,4.375,4863,4.375,4864,4.375,4865,4.375,4866,6.121,4867,4.375,4868,4.375,4869,4.375,4870,4.375,4871,4.375,5509,4.714]],["t/2782",[4,1.241,24,1.86,28,1.584,31,1.532,39,2.552,68,1.517,84,0.433,88,1.802,89,2.34,107,1.606,109,1.476,115,2.888,170,1.182,182,1.339,224,2.657,232,2.281,236,1.649,254,2.815,332,3.255,398,1.409,414,3.598,426,1.478,444,2.719,512,2.162,527,2.205,614,1.857,630,2.316,647,3.567,653,3.319,694,2.176,757,0.986,760,2.98,803,2.253,846,1.777,942,5.094,1008,2.426,1041,3.989,1060,1.94,1170,3.351,1245,3.146,1266,3.061,1363,4.284,1473,2.795,1476,3.875,1873,4.061,1906,3.411,1907,2.067,1908,2.82,2460,5.952,2518,3.135,3621,4.906,3733,4.535,3736,5.209,4866,3.645,4872,4.002,4873,6.272,4874,4.002,4875,4.002,4876,4.002,4877,6.272,4878,4.002,4879,3.024,4880,6.272,4881,4.002,4882,6.272]],["t/2784",[6,5.235,7,0.685,21,2.455,26,3.226,28,1.362,31,1.179,41,2.188,48,1.957,56,0.858,84,1.046,92,1.786,93,3.067,107,2.225,109,1.465,123,1.644,125,2.75,138,1.946,153,2.468,163,1.912,165,1.946,171,1.967,172,3.01,173,2.581,174,2.152,179,3.231,190,3.721,199,2.379,207,2.955,224,2.044,233,1.746,236,1.903,289,2.861,296,1.859,304,1.017,318,1.175,322,1.509,339,2.433,351,1.746,437,2.264,491,1.454,502,1.996,505,3.601,578,1.896,583,2.946,596,2.173,599,3.721,613,2.181,614,1.778,620,2.668,658,2.31,691,2.653,702,3.249,724,2.349,725,3.685,757,2.379,812,3.216,877,2.64,906,2.924,997,3.831,998,3.096,1202,2.541,1241,5.895,1328,2.882,1360,2.75,1370,4.394,1389,4.294,1417,3.184,1444,3.216,1473,2.15,1737,1.819,1891,3.59,2092,2.7,2297,1.607,2395,2.529,2428,3.928,2522,3.707,2916,4.394,2963,3.443,3540,6.032,3622,7.429,3644,1.777,3740,6.87,3741,4.578]],["t/2786",[2061,3.73]],["t/2788",[2,0.893,7,0.892,16,1.003,24,1.584,28,0.467,34,2.362,48,1.344,49,1.767,56,1.041,84,0.739,85,2.723,96,1.167,97,1.481,105,1.036,109,0.559,113,1.208,123,1.129,140,1.298,164,0.91,170,2.015,171,2.136,174,0.984,193,1.205,203,1.633,213,1.348,219,1.025,223,4.872,232,1.205,246,1.253,254,3.144,275,1.025,295,2.743,296,1.276,304,1.657,313,0.809,322,2.901,337,1.543,338,1.326,343,1.178,372,2.018,377,0.741,381,1.567,398,1.167,401,1.266,430,1.634,449,2.039,461,5.192,476,0.962,482,1.481,491,0.998,505,1.373,508,1.951,525,2.541,527,0.724,528,2.841,567,1.328,580,2.368,613,2.437,614,1.708,622,1.671,628,1.711,707,3.795,715,2.772,740,1.497,810,1.471,830,2.464,852,1.436,862,2.683,874,1.627,884,2.089,898,1.606,930,1.812,979,2.186,1170,2.094,1202,1.745,1270,2.832,1281,1.671,1363,2.456,1376,1.656,1394,7.026,1441,3.245,1467,4.218,1539,3.594,1700,2.465,1735,2.306,1737,2.034,1798,1.843,1873,2.145,2015,2.465,2032,1.394,2282,2.208,2297,1.103,2312,1.772,2395,1.736,2447,7.377,2509,2.429,2684,2.039,2898,4.494,3009,4.218,3016,1.993,3644,2.512,3755,4.611,3756,4.611,3757,2.832,3758,3.143,3759,3.143,3760,3.143,3761,4.611,3762,4.611,3763,3.143,3764,4.611,3765,4.611,3766,4.611,3767,3.143,3768,3.143,3769,3.143,3770,3.143,3772,3.143,3773,2.832,4883,4.912,4884,4.912,4885,5.393]],["t/2790",[7,0.528,21,1.892,24,1.084,28,1.293,31,2.551,56,1.199,76,4.983,232,3.332,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,846,4.067,1170,2.812,1266,3.54,2032,3.856,3016,5.512,3774,6.354,3775,6.354,3777,9.884,3782,6.923,3783,6.354,3784,5.725,3785,5.061,3786,5.061,3787,5.061,3788,5.061,3789,6.354,3790,6.354,3791,5.061,3792,5.453,4886,11.227]],["t/2792",[7,0.6,21,2.15,28,1.408,31,2.72,56,1.244,76,5.661,109,1.683,170,2.248,461,6.616,527,2.182,572,5.284,590,4.093,607,4.53,1266,3.223,3777,8.785,3782,7.542,3785,5.75,3786,5.75,3787,5.75,3788,5.75,3791,5.75,3792,6.195,4887,7.608,4888,7.608,4889,6.505,4890,7.608,4891,7.608]],["t/2794",[7,0.528,21,1.892,28,1.293,31,2.551,56,1.199,76,4.983,109,1.545,381,3.168,405,2.914,461,6.283,527,2.003,572,4.851,590,3.602,607,4.303,807,3.54,1037,3.492,1266,3.54,1443,6.816,1689,4.802,3782,6.923,3784,5.725,3785,5.061,3786,5.061,3787,5.061,3788,5.061,3791,5.061,3792,5.453,3793,6.354,3794,6.354,3795,8.012,3796,7.039,3797,6.354,3798,6.354,3799,5.58]],["t/2796",[7,0.57,21,2.044,28,1.362,31,2.653,56,1.226,76,5.382,109,1.627,126,4.659,193,2.631,332,5.008,461,6.485,527,2.11,572,5.11,590,3.891,607,4.441,1266,3.512,3782,7.293,3785,5.466,3786,5.466,3787,5.466,3788,5.466,3791,5.466,3796,7.416,3799,6.027,4889,6.184,4892,7.233,4893,7.233,4894,7.233,4895,7.233]],["t/2798",[7,0.474,21,1.698,31,2.408,56,1.191,76,4.472,174,1.786,233,2.175,236,2.234,318,1.463,461,5.997,504,3.643,509,3.405,527,1.858,572,4.5,590,3.233,607,4.106,653,2.675,740,2.717,1201,5.664,1218,4.784,1266,3.714,1281,4.286,2636,3.448,2704,6.529,2709,7.265,3733,6.145,3782,6.422,3785,4.542,3786,4.542,3787,4.542,3788,4.542,3791,4.542,3799,5.008,4889,5.139,4896,6.01,4897,6.01,4898,7.739,4899,8.497,4900,8.497,4901,6.01,4902,6.01]],["t/2800",[7,0.313,21,1.123,24,0.644,26,1.475,28,0.561,31,1.881,37,4.158,56,1.346,95,5.412,106,2.822,107,1.018,109,1.052,112,1.969,127,2.525,137,4.003,163,1.575,170,1.174,179,1.33,193,1.446,242,2.801,267,3.398,381,2.951,391,1.673,397,2.593,405,2.45,440,1.752,444,2.704,461,3.492,471,3.191,502,1.096,511,1.267,514,3.004,527,2.072,572,5.019,635,1.442,740,1.796,757,0.979,784,7.375,813,4.04,829,4.057,830,1.816,841,2.883,904,2.15,1087,5.82,1117,2.15,1201,2.649,1281,2.005,1352,1.911,1381,1.575,1443,5.73,1457,2.836,1845,2.357,1883,3.398,2312,2.127,2789,2.705,3009,4.88,3023,2.914,3264,3.236,3553,3.004,3715,3.054,3800,5.92,3801,3.772,3802,3.772,3803,3.772,3804,3.772,3805,3.772,3806,3.772,3807,7.014,3808,7.308,3809,7.308,3810,3.772,3811,3.772,3812,3.772,3813,3.772,3814,3.772,3816,3.772,3817,2.528,3818,3.772,3819,3.772,3820,5.92,3821,3.772,3822,3.772,3823,3.772,3824,3.772,3825,3.772,3826,3.772,3827,3.772,3828,3.772,3829,3.772,3830,3.772,3831,3.772,3832,3.772,3833,3.772,3834,3.772,4903,3.974,4904,3.974,4905,3.974,4906,3.974,4907,3.974]],["t/2802",[7,0.413,9,2.069,15,3.325,16,1.432,21,0.903,24,0.848,28,0.739,34,0.814,49,0.718,56,1.366,97,1.429,107,1.97,171,0.868,186,1.506,203,1.253,228,1.012,237,2.343,281,1.512,322,2.656,377,0.715,393,1.569,527,0.699,605,1.156,683,1.195,715,3.419,784,2.343,822,1.909,841,1.477,930,1.747,978,2.152,997,1.183,1055,2.109,1170,1.607,1204,7.679,1367,5.687,1394,6.419,1473,1.424,1567,2.455,1577,3.957,1597,2.015,1700,2.377,1898,1.683,1991,4.033,1992,2.602,2008,3.032,2283,2.225,2297,1.064,2325,2.91,2955,2.662,2956,2.732,3586,2.225,3755,2.732,3756,2.732,3757,2.732,3761,2.732,3762,2.732,3764,2.732,3765,2.732,3766,2.732,3817,3.331,3835,3.032,3837,3.032,3838,3.032,3839,9.139,3840,9.139,3841,9.139,3842,9.139,3843,9.139,3844,8.478,3845,3.032,3846,9.139,3847,8.234,3848,7.301,3849,3.032,3850,3.032,3851,3.032,3852,6.313,3853,3.032,3854,3.032,3855,3.032,4883,2.91,4884,2.91,4908,5.236]],["t/2804",[7,0.87,22,5.532,39,2.62,46,4.239,49,1.447,84,0.697,85,2.23,98,3.133,100,3.987,112,3.19,126,3.109,160,2.257,171,1.749,174,1.914,201,3.278,202,6.137,207,2.628,213,2.62,233,2.33,316,2.869,337,2.999,351,2.33,404,3.135,408,2.324,461,3.604,476,1.869,480,4.383,482,2.879,506,5.289,511,2.053,525,4.202,550,4.713,643,5.865,657,5.037,706,4.595,779,7.111,1009,2.965,1394,7.995,1473,2.869,1733,3.846,1845,3.819,1916,5.327,2196,8.746,2282,4.292,2447,4.383,2485,4.027,2509,4.721,2684,5.488,3158,5.037,3856,6.11,3857,4.483]],["t/2806",[1,3.701,39,2.592,84,0.959,106,2.334,126,4.273,131,4.614,137,4.087,182,2.131,278,2.282,315,4.831,341,3.503,343,3.148,381,3.013,403,4.087,434,5.899,445,4.017,476,3.352,506,5.249,590,3.426,611,4.228,653,3.763,706,6.315,1303,3.805,1380,4.017,1390,4.545,1394,7.698,1444,4.245,1473,3.943,1476,3.737,1689,3.338,2447,4.335,2485,3.983,2489,8.953,2683,3.92,2684,3.92,3057,4.606,3133,7.374,3158,8.597,3413,4.606,3858,4.739,3859,6.044,3860,6.044,3861,6.044,4479,4.739,5510,6.863]],["t/2808",[2061,3.73]],["t/2810",[2,2.106,24,1.265,84,0.846,221,2.885,233,2.827,313,1.908,343,2.778,401,2.985,527,1.708,530,3.493,560,4.157,612,5.058,653,2.458,677,4.115,683,2.922,760,3.332,846,3.468,860,5.903,945,5.294,953,3.159,999,4.248,1043,5.853,1193,4.926,1216,3.976,1263,4.734,1276,5.812,1284,3.819,1312,2.87,1566,5.574,1718,5.206,2009,5.376,2312,4.179,2708,5.648,2910,7.941,3713,7.412,3714,7.412,3715,6.002,3716,6.678,3717,7.412]],["t/2812",[2061,3.73]],["t/2814",[2,1.727,5,2.256,7,0.868,49,1.439,56,1.007,138,2.583,165,1.435,171,1.74,203,1.533,207,3.626,246,3.857,254,3.721,256,3.324,275,1.982,276,4.401,288,4.226,304,1.349,313,1.564,315,4.219,337,2.982,338,1.575,343,2.278,416,2.949,476,1.859,491,1.93,513,3.018,691,4.886,707,3.563,715,3.291,735,4.696,760,3.419,769,2.461,774,5.517,810,3.944,830,2.927,877,2.335,878,2.731,996,4.039,1101,4.84,1365,4.005,1388,4.84,1470,4.631,1845,3.798,1953,3.483,2353,5.306,2679,4.359,2873,5.215,3718,7.298,3719,5.475,3720,5.475]],["t/2816",[7,0.646,31,2.001,84,0.887,92,3.033,93,3.471,116,3.057,139,3.676,171,2.226,174,2.435,207,3.344,233,2.965,254,2.634,272,3.354,276,4.058,283,2.369,287,5.846,304,2.206,318,1.995,343,2.914,527,1.791,653,2.579,734,3.625,759,2.872,760,3.409,810,3.637,1164,4.132,1245,2.71,1473,3.65,1953,4.455,3621,6.408,3718,7.667,3721,6.827]],["t/2818",[4,1.748,5,3.35,7,0.64,21,2.293,26,2.092,46,2.335,49,2.339,56,1.169,85,1.952,90,2.655,92,4.696,94,5.144,105,3.449,116,2.103,133,2.411,171,1.531,174,2.412,203,1.943,236,1.482,254,1.812,275,1.744,278,2.019,283,1.629,290,4.696,337,2.624,353,1.537,359,2.485,365,2.855,405,1.793,476,2.356,496,3.442,498,3.135,505,2.335,513,2.655,531,3.416,578,2.214,601,2.645,605,2.937,614,1.037,769,2.166,774,3.048,824,4.132,992,2.816,1006,2.882,1169,3.584,1276,4.193,1366,3.135,1434,3.135,1844,3.442,1845,3.342,1953,3.065,2287,5.035,2679,3.836,3481,5.524,3636,4.259,3718,5.998,3722,6.939,3723,6.133,3724,6.939,3725,4.021,3726,3.649,3727,4.818,3728,4.132]],["t/2820",[49,2.01,56,1.328,171,2.429,283,2.585,297,4.89,476,2.595,810,5.571,1953,4.863,2698,6.556,3586,6.226,3718,5.639,3722,7.645,3723,8.359,3724,7.645,3729,6.556]],["t/2823",[4,1.588,5,1.803,7,0.596,24,0.829,31,2.199,39,2.083,46,2.121,56,0.982,84,0.975,115,2.357,116,1.91,138,2.064,139,3.395,165,1.147,182,1.713,203,1.811,207,2.089,221,1.89,233,1.852,236,1.346,254,2.433,256,1.915,275,1.584,318,2.191,325,3.564,338,1.259,377,1.692,398,1.803,414,4.34,444,3.28,461,2.865,476,1.486,479,3.564,508,3.015,527,1.119,605,1.852,614,0.942,647,1.92,653,2.381,724,2.492,757,1.261,759,3.485,760,3.281,769,3.459,774,4.092,810,2.273,830,2.339,914,2.547,942,2.162,945,2.67,992,2.558,1008,3.103,1060,2.482,1107,2.697,1170,1.571,1216,2.606,1245,2.503,1266,1.396,1360,2.918,1381,2.029,1446,2.995,1476,1.881,1845,3.036,1863,2.141,1898,2.697,2013,3.228,2026,2.618,2032,3.185,2232,3.607,2312,2.739,2518,3.782,2633,2.815,2636,2.936,2679,5.15,3621,5.918,3664,4.657,3718,4.772,3730,4.377,3731,4.506,3732,7.18,3733,5.471,3734,7.5,3735,4.082,3736,3.447,5511,5.516]],["t/2826",[4,2.317,5,1.772,7,0.397,10,2.721,16,0.936,31,2.791,39,2.047,46,2.085,56,0.973,84,1.068,138,2.029,139,2.258,165,1.127,182,1.683,203,1.788,207,2.053,221,1.858,236,1.323,254,1.618,256,1.882,275,1.557,287,3.59,302,2.316,318,1.819,325,3.503,338,1.238,377,1.671,398,1.772,414,5.111,461,2.816,476,1.46,527,2.303,568,1.441,592,3.228,614,0.926,647,1.887,653,2.351,757,1.24,759,3.456,760,2.913,769,3.424,774,4.04,802,2.035,810,2.234,830,2.299,857,3.389,914,2.503,942,2.125,945,2.624,953,2.035,992,2.515,1008,3.049,1060,2.439,1107,2.65,1170,1.544,1216,2.561,1245,1.665,1266,1.372,1360,2.868,1381,1.994,1473,3.97,1476,1.848,1601,3.005,1845,2.984,1863,2.104,1898,2.65,2013,3.173,2026,2.573,2032,3.144,2033,2.833,2312,2.692,2518,3.733,2633,2.767,2636,2.886,2679,5.084,3664,4.597,3718,4.711,3721,6.224,3735,4.012,3736,5.03,3737,7.088,3738,7.088,3739,4.774,5512,5.421]],["t/2828",[2061,3.73]],["t/2830",[7,0.441,12,1.399,16,0.646,24,1.308,28,1.14,31,1.368,36,3.104,39,1.412,49,1.258,56,1.333,84,0.606,86,3.811,98,1.219,99,2.188,100,2.503,106,2.052,107,1.802,109,0.585,112,1.719,113,2.041,121,6.435,123,1.908,127,1.836,132,1.019,133,1.484,137,5.183,145,1.395,163,1.375,164,0.953,165,0.777,170,1.654,171,0.942,172,1.202,179,1.161,197,1.546,207,1.416,228,2.23,246,1.312,278,1.243,289,1.371,296,1.337,304,1.483,322,1.085,323,1.399,336,1.412,338,1.377,372,1.298,381,2.649,405,2.239,408,3.745,409,1.758,433,3.232,495,1.675,505,1.438,508,2.043,527,0.758,577,2.649,578,1.363,605,1.255,614,0.639,630,1.281,635,1.259,723,3.563,749,3.625,751,5.756,757,0.855,760,1.654,841,1.603,877,1.265,878,1.479,906,2.103,934,1.654,946,3.241,997,1.285,1037,2.134,1164,1.75,1179,2.135,1238,3.344,1289,3.267,1381,1.375,1448,2.825,1487,2.312,1498,2.476,1782,2.521,1899,2.415,1916,2.072,2094,3.16,2297,1.156,2321,3.77,2485,4.402,3010,2.361,3025,2.714,3413,2.509,3628,8.661,3629,3.502,3630,2.666,3632,4.86,3634,2.891,3862,2.766,4479,2.581,5513,3.738]],["t/2832",[7,0.365,21,1.309,24,1.137,28,0.991,36,3.739,49,1.041,56,1.339,84,0.502,86,3.154,96,1.632,100,3.14,107,1.186,112,2.295,113,1.69,121,7.287,127,2.303,137,2.973,140,1.816,171,1.259,172,1.605,177,2.237,199,3.464,203,1.109,207,3.461,233,1.677,246,1.752,285,1.914,304,1.48,316,3.129,322,1.449,372,1.734,376,2.641,381,2.192,408,4.232,437,2.175,508,2.729,527,2.223,577,3.322,578,1.821,596,2.087,614,0.853,653,2.669,723,1.656,732,3.029,751,5.463,757,1.142,760,1.369,816,2.625,828,1.932,946,2.133,997,1.716,1037,3.232,1181,2.405,1245,2.806,1863,1.938,1916,2.768,2297,1.543,2321,3.121,2851,2.658,3010,3.154,3413,3.351,3628,7.684,3630,3.561,3632,5.102,3634,3.861,3862,3.695,5514,4.993]],["t/2834",[2,1.409,7,0.792,10,1.724,21,0.9,24,0.846,28,0.45,32,1.993,36,2.304,39,1.297,46,1.321,49,1.905,56,1.306,84,0.345,86,2.169,90,2.462,92,4.34,93,3.592,94,1.724,96,1.122,97,2.336,99,2.01,100,2.336,107,0.816,112,1.579,113,1.162,121,6.858,127,1.713,132,0.936,137,2.045,140,1.249,145,1.281,162,1.12,165,1.17,171,1.419,172,1.104,174,1.553,177,1.539,179,1.748,186,1.502,197,1.42,199,2.577,203,1.251,207,1.301,224,1.35,228,1.009,233,1.153,236,0.838,246,2.511,283,0.921,285,1.316,304,1.101,313,1.276,316,1.42,317,1.593,322,2.077,323,2.107,343,1.133,353,0.869,372,1.192,381,1.507,408,4.175,482,1.425,495,1.539,496,1.946,505,1.321,527,1.991,577,2.472,590,1.714,596,1.435,609,2.083,614,0.962,653,1.644,657,2.493,691,1.753,714,2.219,715,2.685,723,2.745,751,4.434,757,1.287,760,1.544,816,1.805,828,1.329,839,1.828,876,2.169,914,1.586,934,1.52,940,1.192,946,1.467,951,2.045,997,1.18,1037,1.992,1108,2.146,1164,2.635,1181,1.654,1245,1.729,1290,1.773,1434,1.773,1863,1.333,1953,1.733,2287,3.241,2297,1.74,2321,2.146,2486,2.124,2805,2.449,2851,1.828,3010,2.169,3413,2.304,3481,3.556,3628,7.589,3629,1.993,3630,2.449,3632,3.995,3634,2.655,3636,3.948,3637,2.724,3862,2.541,5515,5.63]],["t/2836",[2,1.211,5,1.582,7,0.734,12,1.812,16,1.276,17,1.775,23,2.29,24,1.836,26,1.667,28,0.968,29,1.567,36,3.668,48,1.822,49,1.87,56,1.222,84,0.487,85,1.556,86,3.057,103,1.503,121,4.867,130,2.56,137,2.882,138,1.812,140,1.76,165,1.536,171,1.22,189,2.404,190,2.309,193,1.634,207,1.833,219,2.575,228,2.172,232,1.634,233,2.482,236,1.181,239,2.577,289,2.71,296,1.731,313,1.675,322,1.405,324,2.378,336,1.828,342,2.936,373,3.248,381,2.125,405,3.607,407,3.76,408,3.816,409,3.475,438,2.331,458,3.862,464,3.84,495,3.311,502,1.239,508,2.646,527,0.982,567,1.801,572,2.378,580,1.56,592,2.882,600,2.994,673,3.954,683,1.681,723,2.974,731,2.354,744,2.56,750,3.165,751,2.593,757,1.107,807,1.736,811,2.787,831,2.833,852,1.947,929,3.057,936,1.922,1133,3.69,1193,2.833,1289,1.817,1509,3.84,1699,2.787,1782,2.023,1819,2.882,2297,1.496,2486,2.994,2497,3.513,3620,3.954,3628,7.037,3887,3.582,4374,3.582,4479,3.342,4916,4.492,4917,4.492,4918,4.091,4919,4.492,4920,4.492]],["t/2838",[2061,3.73]],["t/2840",[29,3.492,101,5.328,305,6.367,318,2.437,323,4.037,395,3.956,505,4.148,655,6.542,728,6.969,740,4.524,1066,4.937,1281,5.049]],["t/2842",[4,2.243,7,0.57,17,2.858,25,2.316,27,1.717,40,3.81,48,2.934,85,2.505,164,1.987,203,1.731,233,2.617,236,3.047,275,2.239,281,3.422,282,3.934,323,2.917,343,2.573,405,2.301,409,3.665,565,2.361,568,2.764,608,3.754,655,4.728,683,2.706,691,5.308,740,4.909,760,3.423,798,3.978,810,3.211,830,3.305,874,4.739,886,4.601,904,5.874,947,3.682,1002,5.768,1028,4.773,1281,3.648,1302,4.684,1898,3.81,2678,6.184,2680,4.601,2681,4.641,2701,4.821,4921,7.233]],["t/2844",[7,0.946,10,3.841,17,2.806,22,4.406,25,3.841,30,4.442,31,1.735,32,4.442,36,3.131,37,4.734,56,0.701,84,0.769,101,3.78,164,1.951,174,2.111,229,4.212,233,3.894,236,1.867,278,2.545,283,2.053,318,1.729,343,2.526,377,1.588,390,3.429,481,4.242,527,1.553,565,2.319,612,4.599,630,2.623,650,4.783,683,2.657,740,5.199,827,3.598,937,4.888,1180,4.479,1281,5.801,1916,4.242,2372,4.734,2508,5.367,2680,4.517,2681,4.557,2701,4.734,2717,4.642,2718,7.102]],["t/2846",[7,0.644,12,1.758,21,1.894,25,2.146,27,1.034,28,0.615,31,1.065,56,0.977,84,0.726,96,1.535,106,1.597,107,1.716,109,0.735,111,3.196,115,2.007,123,1.485,132,1.968,162,1.531,165,0.976,170,2.708,179,2.242,197,2.986,213,1.773,228,2.123,233,2.955,236,1.762,238,7.053,246,1.648,247,1.915,272,3.343,276,3.32,281,2.062,296,1.679,313,1.637,318,1.988,323,1.758,324,2.308,364,3.071,365,2.208,372,1.631,382,2.704,387,3.034,390,2.104,409,2.208,482,1.949,491,1.314,502,1.202,527,2.518,541,2.682,565,1.423,568,1.248,578,1.713,607,1.67,630,1.61,632,2.345,651,5.442,658,2.087,660,3.243,685,2.425,734,1.928,814,1.877,827,2.208,877,2.444,878,1.858,880,1.877,885,3.82,893,2.516,904,6.361,914,2.169,947,3.412,950,4.666,1006,2.229,1009,2.007,1024,2.772,1034,3.837,1066,2.15,1116,2.876,1164,2.198,1170,1.338,1245,1.442,1281,3.38,1352,2.096,1379,3.349,1473,1.942,1632,2.262,1822,2.622,1838,3.243,2372,2.905,2445,3.726,2680,2.772,2681,5.882,2697,3.549,2701,6.109,2722,3.837,2723,4.136,2724,4.136]],["t/2848",[2,1.915,7,0.848,25,3.841,84,0.769,96,2.501,98,2.495,113,2.589,233,2.57,236,3.155,238,5.468,245,4.242,313,1.735,318,2.62,338,1.747,372,3.567,472,3.458,536,2.115,557,3.534,628,3.667,681,4.442,683,2.657,685,5.304,736,3.028,740,4.309,760,2.098,904,3.841,1006,3.632,1181,3.685,1182,6.072,1207,6.468,1245,2.35,1281,4.808,1289,2.873,1417,6.291,1810,6.072,2680,4.517,2681,6.117,2701,6.354,2713,6.252,3725,5.068]],["t/2850",[7,0.84,30,6.666,84,1.155,163,3.34,228,2.67,233,3.857,236,2.803,281,3.987,318,2.052,361,7.873,740,5.284,1132,5.868,1281,5.896,1394,5.617,1826,6.271,2021,6.179,2681,5.408,2684,6.56,2726,6.014,2727,7.676,2728,7.206,2773,7.676]],["t/2852",[7,0.869,31,1.377,34,1.435,40,2.969,48,2.286,56,0.556,84,0.61,103,1.885,107,1.443,116,2.103,132,1.655,145,2.266,179,1.885,233,2.937,236,2.501,238,3.232,247,2.476,248,2.645,313,1.377,318,2.685,372,2.108,377,1.26,426,2.081,447,2.666,465,5.65,482,2.52,502,2.869,509,3.192,525,2.655,527,1.774,530,2.52,565,2.65,568,1.614,608,2.924,614,1.037,630,2.081,635,2.044,647,2.114,685,3.135,691,4.463,740,3.668,760,2.81,904,6.553,999,3.065,1114,2.556,1116,5.356,1201,3.756,1245,2.685,1281,2.842,1457,4.021,1459,4.132,1460,4.696,1632,2.924,1782,3.655,1822,6.261,2672,5.304,2680,3.584,2681,5.208,2697,4.589,2701,3.756,2706,4.696,2726,4.021,2799,4.408,4923,5.132,4924,5.132,4925,5.132,4926,5.132]],["t/2854",[7,0.803,21,2.877,49,2.289,56,1.005,84,1.103,233,3.685,236,2.677,305,6.477,740,4.603,1009,4.689]],["t/2856",[7,0.616,21,1.512,25,2.957,29,1.867,34,1.363,41,2.428,49,2.076,56,1.151,84,1.1,85,1.854,92,2.895,105,1.674,113,1.952,129,3.197,131,2.428,134,2.663,140,2.097,162,1.881,165,1.199,171,1.454,233,1.937,236,1.407,238,4.485,256,2.003,296,2.063,318,1.903,324,2.834,351,1.937,372,2.003,377,1.197,401,2.046,465,3.727,491,1.613,536,1.594,580,1.858,607,2.051,614,0.985,621,2.751,628,2.764,681,3.348,685,2.978,691,2.944,740,4.884,757,1.319,760,2.729,810,4.51,820,2.751,874,2.628,898,2.595,904,2.895,937,3.684,947,2.725,1239,3.294,1245,1.771,1281,5.691,1289,2.165,1441,3.22,1638,2.764,1911,3.982,1916,3.197,2287,3.32,2298,4.46,2680,3.405,2681,7.241,2698,5.732,2701,5.211,3481,3.643,3586,3.727,3729,3.924,4927,5.352,4928,5.352,4929,4.358,4930,4.576,4931,5.352,4932,5.352,4933,5.352]],["t/2858",[34,2.277,56,1.297,105,2.797,296,3.446,377,2,630,4.085,810,3.97,1245,2.958,1918,4.449,2287,5.547,2701,5.96,3481,6.086,3586,6.226,3729,6.556,4929,7.281,4930,7.645,4934,8.942,4935,8.942,4936,8.942,4937,8.942]],["t/2860",[48,3.371,56,1.273,283,2.402,491,2.504,539,5.332,614,1.529,683,3.109,760,2.455,810,3.689,1263,5.036,2287,5.154,2680,5.285,3481,5.655,3586,5.785,3729,6.092,4929,6.765,4930,7.104,4938,8.308,4939,8.308,4940,8.308,4941,8.308,4942,8.308,4943,8.308,4944,8.308,4945,8.308,4946,8.308,4947,8.308,4948,8.308,4949,8.308,4950,8.308,4951,8.308]],["t/2862",[7,0.703,15,2.348,25,2.062,32,4.027,49,1.447,84,1.108,92,3.302,93,2.728,106,2.36,123,2.194,133,2.755,162,2.263,164,1.769,171,2.423,177,3.109,203,1.541,233,2.33,236,1.693,256,2.409,304,1.879,313,1.573,322,2.014,343,2.29,387,4.483,509,3.648,578,2.53,608,3.342,614,1.883,650,4.336,683,3.336,740,4.624,757,2.197,814,2.773,904,4.823,1014,4.17,1129,4.027,1238,3.846,1281,5.16,1307,6.439,1632,3.342,1671,3.541,1689,3.375,2508,4.866,2680,4.096,2681,4.132,2698,4.721,2701,4.292,2717,5.828,2885,5.505,3218,6.11,3644,3.285,3728,4.721,4536,6.439,4952,6.439,5516,6.938,5517,6.938,5518,9.609,5519,6.938,5520,6.938]],["t/2864",[7,0.556,17,3.752,56,1.265,85,2.445,92,3.515,93,2.99,103,2.362,197,3.145,233,2.554,296,2.72,304,1.487,313,1.724,315,3.353,323,2.847,353,2.589,361,4.754,552,3.36,562,3.466,612,4.571,614,1.975,650,7.225,740,4.291,796,3.7,810,4.216,841,3.262,937,6.535,953,2.855,1281,4.789,1310,6.775,1365,4.415,1916,4.217,2297,2.351,2698,6.962,2778,6.214,3586,4.915,3729,5.176,4804,5.748,4929,5.748,4953,7.059,5521,7.606]],["t/2867",[4,2.626,170,1.767,182,2.001,236,2.584,238,6.132,248,2.807,254,1.923,305,5.386,324,3.166,372,2.237,527,2.149,578,2.35,653,2.665,659,3.872,740,2.703,741,3.872,757,1.473,760,2.904,814,3.646,877,2.18,904,6.863,940,2.237,942,4.514,947,3.044,1116,5.588,1170,1.836,1201,5.643,1245,3.537,1266,3.355,1281,3.016,1303,3.572,1330,4.07,1363,3.049,1457,4.267,1459,4.384,1476,3.111,1754,3.598,1906,3.252,1907,3.088,2680,3.804,2681,3.837,2697,6.894,2701,3.986,3016,3.598,3866,3.103,4879,4.519,4954,5.446,4955,5.98,4956,5.446,4957,5.98]],["t/2869",[4,3.286,164,1.61,182,1.961,203,2.327,238,5.576,247,2.575,351,2.121,377,1.311,381,3.949,482,2.62,502,1.616,527,2.618,608,3.041,614,1.79,630,2.164,647,3.974,653,3.334,757,1.444,814,4.562,904,6.056,942,4.473,1037,2.234,1041,5.309,1245,3.216,1266,3.173,1363,3.814,1443,4.36,1476,3.892,1632,3.041,1863,2.451,1906,5.287,4879,4.429,4924,5.337,4925,7.602,4958,6.937,4961,5.86,4962,8.346,4963,5.86,4964,8.346,4967,5.86,5522,8.993]],["t/2871",[4,2.506,133,3.456,164,2.219,182,2.703,247,3.55,372,3.882,502,2.228,527,2.506,608,5.948,630,2.984,647,3.031,653,2.543,691,4.444,757,1.991,760,2.387,904,6.766,1041,5.139,1170,2.48,1245,3.792,1266,2.829,1363,2.909,1476,2.969,1632,4.193,1863,3.38,4926,9.45,4958,7.404]],["t/2873",[4,2.716,68,1.517,84,0.433,98,2.204,106,1.467,137,4.025,182,3.528,203,0.958,236,2.302,238,5.023,254,2.017,322,1.962,372,3.945,404,1.948,408,1.444,426,1.478,506,2.373,527,1.914,622,2.018,623,4.553,653,2.756,747,1.643,757,1.545,760,2.587,776,3.334,829,2.108,839,3.598,904,6.524,942,4.26,953,1.619,962,4.317,1008,2.426,1041,7.14,1116,2.641,1170,1.925,1201,5.836,1245,3.146,1266,3.365,1363,3.153,1373,2.444,1376,3.866,1476,3.217,1671,2.201,1696,4.92,1765,4.437,1863,1.674,1906,3.411,1911,2.977,2061,2.612,2092,4.329,2705,8.308,2806,2.894,3866,2.077,4479,4.667,4509,3.334,4879,3.024,4923,5.712,4954,3.645,4958,7.198,4970,6.613,4971,6.272,4973,6.272,5523,6.272,5524,5.952,5525,3.797,5526,4.002,5527,3.797]],["t/2875",[4,2.691,182,2.903,203,2.077,238,6.796,372,3.246,482,3.88,608,4.503,647,3.255,653,2.731,757,2.138,803,4.886,904,5.872,942,4.584,947,4.417,1245,2.871,1266,3.231,1363,3.124,1443,6.456,1476,3.188,1863,3.63,2704,6.667,4958,6.192,4975,10.856]],["t/2877",[4,2.877,68,1.69,84,0.483,98,1.566,106,1.634,137,4.376,182,3.785,203,1.067,236,1.172,238,6.046,254,1.433,372,4.232,404,2.17,408,1.609,506,2.644,527,0.975,622,2.248,623,3.265,647,3.106,653,2.919,747,1.83,757,1.098,760,1.317,776,3.714,803,2.51,829,2.348,839,3.911,904,6.399,936,1.907,942,3.917,962,3.068,1041,7.195,1170,1.368,1201,2.971,1245,3.487,1266,3.328,1363,3.34,1373,2.722,1376,3.408,1408,1.979,1476,3.408,1671,2.452,1696,4.337,1765,3.911,1863,1.865,1882,2.626,2061,2.796,2092,3.816,2704,5.239,2806,3.223,4479,5.073,4509,3.714,4958,7.522,4970,7.08,4976,6.819,4977,4.458,4979,4.458,4980,4.458,4981,4.06,4982,4.458,4983,4.458,5524,6.47,5525,4.23,5526,4.458,5527,4.23,5528,7.347,5529,8.922]],["t/2879",[4,2.455,182,2.649,203,1.895,238,6.885,372,2.962,482,3.54,527,2.784,608,5.314,647,2.97,653,2.492,757,1.951,803,4.457,877,2.886,878,4.366,904,5.539,942,4.324,947,4.03,1116,5.224,1245,2.619,1266,3.095,1363,2.85,1443,5.89,1476,2.909,1863,3.311,1907,4.088,2722,9.015,4958,5.649,4984,7.916]],["t/2881",[4,2.958,106,2.075,109,0.955,137,3.634,182,3.489,238,6.338,372,3.902,404,2.757,408,2.044,506,3.358,527,2.415,623,2.711,647,3.577,653,3.282,747,2.325,757,1.395,776,4.718,803,3.188,829,2.983,936,2.422,942,4.027,962,3.897,1041,6.634,1170,1.738,1245,3.45,1266,3.308,1363,3.755,1373,3.458,1376,2.83,1408,2.514,1476,3.832,1671,3.114,1696,3.602,1765,3.248,1863,2.368,1882,3.335,2061,1.912,2092,3.169,2721,7.728,2806,4.094,4479,4.213,4509,4.718,4956,10.063,4958,6.805,4970,6.963,4981,5.157,5524,5.373,5525,5.373,5527,5.373,5530,6.101,5531,8.775]],["t/2883",[4,2.896,5,1.183,7,0.736,17,1.328,21,0.949,25,3.801,27,1.882,31,1.333,39,1.367,48,1.363,55,1.827,68,1.274,84,0.591,99,2.119,103,1.124,163,1.332,182,1.124,196,1.332,203,1.306,236,1.811,238,3.951,297,1.837,313,0.821,318,0.818,372,2.577,390,1.622,397,2.268,414,3.129,418,2.727,433,1.94,444,2.365,445,2.119,472,2.656,512,2.374,531,2.037,614,0.619,630,1.241,647,2.584,653,3.097,685,5.703,691,1.848,740,4.633,757,1.697,760,2.035,828,2.275,904,2.951,914,1.672,937,3.755,942,3.681,1041,3.47,1117,1.817,1133,1.807,1170,1.675,1216,1.71,1245,2.279,1266,2.969,1281,5.619,1363,3.815,1476,3.615,1492,2.8,1733,2.007,1898,2.874,1911,4.059,2032,2.296,2231,2.43,2508,2.539,2680,5.545,2681,6.313,2684,2.068,2701,6.557,2703,3.188,2704,2.582,2706,4.546,2709,4.664,2717,2.196,2720,3.188,2867,1.966,3664,2.068,3733,4.98,3917,2.137,4879,2.539,4958,4.915,4985,5.455,4986,5.455,4987,5.455,4988,5.455,4989,3.36,4990,3.36,4991,3.36,4992,3.36,4993,3.36,4994,5.455,4995,5.455,4996,5.455,4997,5.455,5523,3.36,5532,3.62,5533,3.36]],["t/2885",[4,2.795,68,3.416,163,3.571,182,3.015,647,3.38,653,2.836,740,5.023,757,2.22,940,3.371,942,3.805,1266,2.457,1281,5.605,1363,3.244,1476,3.311,1863,3.769,2717,5.889,2720,10.546,2728,9.502,3733,8.036,5533,9.01]],["t/2887",[2,2.108,4,2.425,7,0.616,16,0.996,25,1.714,27,1.27,28,0.755,30,3.348,40,2.82,56,0.528,84,1.1,98,1.881,103,1.791,105,1.674,116,1.997,132,1.572,163,2.121,164,1.47,165,1.199,182,1.791,233,2.829,247,2.352,298,3.269,302,2.464,313,1.91,318,2.631,338,1.317,351,1.937,394,2.675,444,2.32,447,2.532,461,4.376,477,3.175,491,1.613,502,1.476,536,1.594,565,1.747,647,2.008,653,1.685,740,4.591,757,2.276,802,2.165,814,2.305,822,3.197,828,2.232,874,2.628,904,4.229,920,3.11,942,3.9,949,3.982,958,3.498,999,2.911,1007,3.604,1132,3.727,1218,5.719,1245,3.361,1266,2.77,1281,5.691,1312,1.967,1363,2.815,1444,3.568,1476,1.967,1527,3.532,1605,3.924,1863,2.239,2021,5.732,2684,3.294,2704,4.113,2706,4.46,2728,8.685,3733,6.679,4898,4.875]],["t/2889",[7,0.434,31,1.344,39,3.246,84,0.864,99,5.031,106,2.924,121,3.219,130,3.137,131,4.669,137,3.531,138,2.219,163,2.181,224,2.331,236,2.873,278,1.972,315,3.789,341,3.027,343,3.886,403,3.531,416,3.673,434,6.255,445,3.471,580,1.911,611,4.278,650,6.931,653,3.239,740,4.652,796,2.884,1122,4.989,1132,3.831,1201,3.668,1281,4.024,1303,4.765,1390,3.927,1433,4.388,1444,5.317,1473,2.452,1476,3.781,1689,2.884,1813,5.849,1916,3.287,2489,9.06,2507,5.222,2508,4.159,2683,3.387,2717,3.597,3057,3.979,3133,4.585,3413,3.979,4059,5.012,4479,4.095,4998,5.503,4999,5.503,5000,7.57,5001,5.503,5003,5.503,5004,10.925,5534,8.596,5535,5.93,5536,5.93]],["t/2891",[272,4.48,596,4.928]],["t/2893",[2061,3.73]],["t/2895",[2,1.746,5,2.281,7,0.872,49,1.455,56,1.013,138,2.611,165,1.45,171,1.759,203,1.55,207,3.653,246,3.879,254,3.559,256,3.349,275,2.004,276,4.434,288,4.273,299,7.455,304,1.364,313,1.582,315,4.252,337,3.015,338,1.593,416,2.981,476,1.879,491,1.951,513,3.051,527,2.54,691,5.642,707,3.602,715,3.328,735,4.747,757,1.595,769,2.488,774,5.549,810,2.875,830,2.959,877,2.361,996,4.083,1101,4.893,1388,4.893,1470,4.682,1733,3.868,1845,3.84,1953,3.521,2353,5.347,2873,5.272,3619,5.536,3719,5.536,3720,5.536]],["t/2897",[7,0.694,49,1.979,92,3.261,93,4.642,116,3.287,164,2.419,171,2.393,174,2.618,207,3.595,233,3.187,254,3.523,256,3.295,276,5.909,283,2.547,299,7.654,304,1.856,343,3.132,527,1.925,653,2.772,810,3.911,1164,4.442,2118,6.206]],["t/2899",[4,1.827,5,3.434,7,0.66,21,2.367,26,2.186,46,2.441,49,2.386,56,1.108,85,2.04,90,2.775,92,4.683,94,5.272,105,3.509,116,2.198,133,2.519,171,1.6,174,2.489,203,2.005,236,1.549,254,1.894,275,1.823,278,2.11,283,1.703,290,4.908,299,6.813,337,2.743,353,1.606,359,2.597,365,2.984,405,1.874,476,2.431,496,3.597,498,3.277,505,2.441,513,2.775,527,1.288,531,3.57,578,2.314,605,3.031,614,1.084,769,2.263,774,3.186,824,4.318,992,2.944,1006,3.013,1169,3.746,1366,3.277,1434,3.277,1844,3.597,1845,3.493,1953,3.203,2287,3.654,3481,4.009,3636,4.451,3723,4.451,3725,4.203,3726,3.814,3727,5.036,3728,4.318,3742,7.162]],["t/2901",[15,3.008,49,1.854,56,1.271,85,2.858,171,2.241,283,2.385,296,3.179,297,4.512,299,5.294,476,2.395,810,5.412,1055,5.445,1345,7.604,1465,5.966,1577,6.235,1953,4.487,1991,5.001,2283,5.744,2698,7.71,3586,5.744,3723,6.235,3729,6.049,3742,7.054,4909,8.25,4910,8.25,4911,8.25,4912,8.25]],["t/2904",[4,2.019,5,2.293,7,0.513,31,2.195,39,2.649,46,2.698,56,1.095,84,0.705,116,2.429,138,2.626,182,2.178,203,1.558,207,2.657,221,2.404,254,2.093,256,2.436,275,2.015,299,6.603,318,1.585,377,1.456,398,2.293,414,5.154,476,1.89,527,2.545,614,1.199,647,2.442,653,2.828,691,3.581,757,1.604,769,3.453,774,4.86,830,2.975,942,2.749,945,3.395,1060,3.157,1107,3.43,1170,1.999,1216,3.314,1245,2.154,1266,1.775,1360,3.711,1476,2.392,1845,3.862,1863,2.723,1898,3.43,2026,3.33,2032,3.782,2312,3.484,2518,4.491,2633,3.581,2636,3.735,3664,5.53,3735,5.191,3736,4.384,3743,8.527,3744,5.566,5537,7.015]],["t/2906",[4,2.506,56,1.194,116,3.015,182,2.703,221,2.984,318,1.967,377,1.807,398,2.846,476,2.345,527,2.506,647,3.031,653,3.266,757,1.991,942,3.412,945,4.214,1060,3.917,1107,4.256,1170,2.48,1245,2.673,1266,2.203,1476,2.969,1863,3.38,2026,4.133,2032,4.368,2312,4.323,2518,5.186,2633,4.444,3744,6.908,3745,9.846,3746,7.667,5538,8.706]],["t/2908",[2061,3.73]],["t/2910",[7,0.563,17,2.824,34,1.82,49,1.606,56,1.138,203,1.71,225,2.554,239,4.099,248,5.412,256,2.673,285,3.954,296,2.753,304,2.43,317,3.571,351,2.586,393,3.509,398,2.517,731,3.745,734,3.161,760,2.111,1043,5.602,1044,4.208,1122,4.469,1133,3.844,1266,2.61,1399,6.358,1601,5.718,1632,3.708,1737,2.694,1918,4.763,2353,4.268,2379,5.954,2601,6.921,2602,6.921,2624,5.589,2887,5.818,3644,2.632,3747,6.78,3748,6.78,3749,4.975]],["t/2912",[7,0.674,10,4.625,56,1.062,84,1.165,228,2.708,248,5.524,322,2.674,511,2.726,605,3.893,888,5.036,940,3.199,1043,4.437,1399,6.932,1565,5.953,1601,5.108,1629,5.953,1638,4.416,1918,4.255,2297,2.848,2411,6.27,2601,6.183,2602,6.183,3750,8.114,3751,8.114,3752,8.114]],["t/2914",[7,0.574,15,2.654,49,1.636,56,1.076,140,2.852,162,3.405,171,1.977,196,2.884,203,1.742,248,4.548,255,4.901,281,3.443,302,3.351,313,1.778,322,3.031,324,3.854,393,3.574,614,1.34,704,6.407,724,3.543,731,3.815,828,3.035,851,3.815,877,2.654,1043,6.278,1046,5.431,1133,5.213,1399,5.708,1601,5.788,1632,3.777,1918,3.621,2297,2.424,2601,7.007,2602,7.007,3655,7.105,3749,5.067,3753,6.222,3754,6.222]],["t/2916",[2061,3.73]],["t/2918",[7,0.54,28,1.491,31,2.273,49,1.54,56,0.677,68,2.598,84,0.742,92,3.446,94,3.706,115,4.866,132,2.733,133,2.931,196,2.716,203,1.64,233,2.48,236,1.802,254,2.203,283,1.981,313,1.674,315,3.255,318,1.668,377,2.081,390,3.309,436,4.89,502,3.125,513,3.229,527,1.498,550,2.693,577,4.999,630,2.531,675,4.359,715,3.522,751,3.956,769,2.633,829,3.61,1037,3.547,1045,4.568,1218,5.24,1220,5.71,1308,4.064,1332,4.185,1380,4.322,1863,2.866,1953,3.727,2518,3.425,2669,5.179,2799,5.36,3862,5.464,3864,6.503,3865,6.503,5005,6.853]],["t/2920",[2,1.806,4,2.077,7,0.528,16,1.246,56,1.109,84,0.992,116,2.499,182,2.241,221,2.473,232,2.435,313,1.636,318,2.23,372,2.505,377,1.498,398,2.359,476,1.944,491,2.018,502,1.846,527,2.57,536,1.994,565,2.186,568,1.918,605,3.315,630,2.473,647,2.512,653,2.883,683,2.505,757,1.65,814,4.834,841,3.095,852,2.903,942,2.828,945,3.492,1060,3.247,1107,3.528,1139,3.583,1170,2.056,1218,6.62,1257,3.891,1266,1.826,1476,2.46,1863,2.801,2026,3.425,2032,3.856,2312,3.583,2518,4.579,2633,3.683,3862,5.34,3866,3.475,5539,7.215]],["t/2922",[0,2.461,4,1.322,7,0.519,12,1.719,16,1.226,24,1.067,31,2.529,39,2.681,41,1.934,56,1.222,84,0.462,98,1.498,113,2.402,115,4.51,132,1.252,165,0.955,179,1.426,182,2.694,196,1.69,203,1.02,219,1.32,221,1.574,254,1.371,282,2.319,318,1.961,372,1.595,398,1.502,414,3.78,471,1.767,476,1.238,477,2.529,491,1.285,502,1.176,527,2.641,553,2.257,565,1.392,567,1.709,568,1.887,577,2.017,580,1.48,610,3.276,614,1.213,647,3.021,653,2.852,658,4.338,670,2.478,724,2.076,757,1.05,814,2.837,829,4.242,830,1.948,936,3.445,942,3.401,945,2.223,1037,1.625,1060,3.195,1107,2.246,1170,2.472,1218,6.278,1245,1.411,1257,2.478,1266,1.797,1363,3.887,1476,2.959,1671,3.624,2026,2.181,2032,2.774,2098,8.161,2232,3.004,2312,2.281,2518,3.293,2633,2.345,3006,3.645,3008,3.172,3041,3.645,3866,3.419,3868,6.252,3869,4.046,3870,4.046,3871,6.252,5540,4.594]],["t/2924",[4,2.271,5,2.579,16,1.363,56,1.15,116,2.733,164,2.012,182,2.45,221,2.705,254,2.355,318,2.369,372,2.74,398,2.579,476,2.126,502,2.019,527,2.724,647,2.747,653,3.063,757,1.804,942,3.093,945,3.819,1054,9.953,1060,3.551,1107,3.858,1170,2.248,1218,6.556,1257,4.256,1266,1.997,1476,2.691,1863,3.063,2026,3.746,2032,4.096,2312,3.919,2518,4.864,2633,4.028,2905,5.226,3866,3.8,3872,6.949,5541,7.891]],["t/2926",[2,1.47,7,0.808,24,0.883,28,0.769,31,1.936,49,2.098,56,1.269,90,2.569,92,4.76,93,4.341,94,2.949,97,2.438,98,1.916,100,2.438,115,2.51,132,1.601,165,1.221,171,2.536,174,2.774,177,2.632,203,1.897,283,1.576,304,1.149,313,1.332,322,2.92,398,1.92,496,3.33,527,2.24,553,2.887,614,1.004,714,3.796,715,2.802,769,3.045,814,2.348,850,3.563,898,2.643,914,2.713,940,2.04,1129,4.956,1218,5.769,1290,3.033,1434,3.033,1689,2.858,1953,2.965,2287,4.916,2297,3.109,2502,4.264,3023,3.997,3481,3.711,3636,5.988,3637,4.661,3847,4.661,3874,7.519,3875,5.173,5006,9.334]],["t/2928",[2061,3.73]],["t/2930",[7,0.693,16,0.636,24,1.726,28,1.241,29,1.193,34,0.871,49,1.8,55,1.859,56,0.791,84,1.073,85,1.184,107,3.253,109,1.925,113,1.247,127,1.121,162,1.944,165,0.766,170,2.928,171,0.929,172,1.184,193,1.243,233,2.522,236,2.106,246,2.092,278,1.225,285,2.286,295,1.375,304,1.166,338,2.164,351,2.002,376,1.949,405,1.088,408,3.398,426,1.263,429,2.923,477,2.028,484,1.448,500,1.617,527,2.495,536,3.335,557,1.701,576,2.014,593,1.686,605,1.237,611,1.421,614,1.733,621,1.757,622,1.724,647,2.075,736,1.458,755,1.54,756,1.605,757,0.842,760,3.372,765,2.028,811,2.121,828,1.426,880,3.449,882,2.042,914,1.701,936,1.462,953,1.383,992,3.483,997,1.266,1117,1.849,1181,2.871,1237,2.302,1240,3.972,1245,1.83,1381,1.355,1508,5.207,1540,3.06,1699,2.121,1754,1.453,1863,1.43,1870,3.71,2372,3.688,2700,2.784,2764,3.01,2800,2.726,2851,3.174,3879,2.544,3880,2.234,4474,5.11,5013,3.419,5014,3.114,5015,2.784]],["t/2932",[7,0.774,16,1.111,24,1.826,28,1.452,31,0.918,49,1.9,56,0.371,84,1.115,107,2.362,109,1.901,123,1.281,127,4.248,132,1.753,165,1.663,170,1.763,193,3.355,197,1.674,233,3.06,276,1.862,278,1.347,296,1.448,304,0.792,318,0.915,338,2.415,373,2.717,405,1.195,426,2.204,433,2.17,476,2.678,491,1.133,500,1.778,524,2.313,527,0.822,536,3.487,565,2.424,568,1.709,592,2.412,593,1.854,605,1.36,609,2.456,614,1.557,635,1.363,647,3.172,653,1.183,691,2.067,756,1.764,760,3.041,818,1.887,857,1.705,880,3.197,929,2.558,936,1.608,995,2.558,999,2.044,1117,2.033,1237,2.531,1240,2.142,1381,2.366,1508,6.476,1696,2.39,1699,2.331,1754,1.597,1838,2.796,1870,3.583,1875,7.046,2787,2.39,3879,2.796,3880,2.456,5016,6.046]],["t/2934",[7,0.796,16,0.982,24,1.879,28,1.293,49,1.187,84,0.838,107,2.584,109,2.047,170,1.56,172,4.465,174,1.569,233,3.652,278,1.892,304,1.113,338,2.762,405,1.68,426,2.859,502,3.096,527,1.692,536,3.005,592,3.388,593,2.604,605,1.911,614,1.425,647,3.786,756,2.478,757,1.907,760,2.982,819,3.721,880,3.333,936,2.259,1117,2.856,1237,3.556,1381,3.068,1508,6.582,1699,3.276,1870,3.512,3879,3.929,3880,3.451,5017,6.618,5018,7.05]],["t/2936",[7,0.728,16,0.996,24,1.887,28,1.303,49,1.203,84,0.847,107,2.6,109,2.088,170,1.581,233,3.342,278,1.918,304,1.128,338,2.775,405,1.703,426,2.887,502,3.111,527,1.709,536,3.025,592,3.435,593,2.64,605,1.937,614,1.439,647,3.81,756,2.512,757,1.926,760,3.001,880,3.367,936,2.29,997,4.784,1117,2.895,1237,3.604,1381,3.098,1508,6.607,1699,3.32,1870,3.535,3879,3.982,3880,3.498,5019,6.684,5020,7.121]],["t/2938",[16,1.757,28,1.614,84,1.023,109,1.593,165,2.116,170,2.791,172,3.272,322,2.954,607,3.62,614,1.739,724,4.599,725,5.823,755,4.254,1508,5.225,1870,3.287,2092,5.286,5009,7.138]],["t/2940",[16,1.743,28,1.606,84,1.015,109,1.58,165,2.099,170,2.768,172,3.246,173,5.014,322,2.931,607,3.591,614,1.725,724,4.562,755,4.22,877,3.417,1508,5.183,1870,3.261,2092,5.244,4511,7.472,5008,6.87]],["t/2942",[16,2.058,25,2.863,27,2.625,28,1.561,84,0.969,107,2.832,109,1.508,165,2.003,322,2.797,614,1.646,623,4.282,724,4.354,878,3.813,997,3.311,1139,4.785,1382,6.607,1508,4.946,1557,5.547,1870,3.112,2092,5.004,5007,6.871]],["t/2944",[2061,3.73]],["t/2946",[7,0.938,84,1.094,139,4.53,170,2.983,397,4.197,759,3.539,940,3.777,1289,4.084,1506,6.478,3925,7.3]],["t/2948",[24,1.75,31,2.104,56,0.85,68,3.266,113,3.141,123,2.935,139,4.849,179,2.882,183,3.697,313,2.104,527,2.707,568,2.467,632,4.634,759,3.788,760,2.545,914,4.286,953,3.484,1245,3.575,1266,2.349,1381,3.414,1473,3.838,1506,5.527,1863,3.603,2851,4.941]],["t/2950",[4,3.243,28,1.475,107,2.677,109,1.763,505,4.333,760,3.089,1266,2.851]],["t/2952",[12,4.179,24,1.678,109,1.747,254,3.332,512,3.573,578,4.072,614,1.908,1754,4.404]],["t/2954",[7,0.782,12,4.003,24,1.607,109,1.674,128,4.733,254,3.191,578,3.9,614,1.827,681,6.207,736,4.232,1075,5.768,1266,2.706,1616,5.522]],["t/2956",[24,1.691,28,1.584,31,2.238,34,2.333,36,2.953,39,2.725,49,1.505,84,0.725,109,1.545,116,2.499,132,1.967,133,2.865,139,3.005,164,2.868,165,1.5,170,3.474,203,2.193,285,2.766,289,2.646,338,1.647,353,1.826,367,3.409,382,4.154,394,3.347,397,2.784,414,3.841,505,2.775,525,3.155,614,1.233,693,4.121,756,3.143,759,3.211,760,3.085,769,4.314,830,3.06,854,3.564,916,3.891,946,4.808,1216,3.409,1506,4.297,1558,3.77,1671,3.683,1792,4.188,2696,4.51,2818,4.419,3917,4.259,3922,5.725]],["t/2958",[7,0.711,21,1.85,29,2.284,31,2.849,39,2.664,49,1.471,125,3.732,139,4.631,165,1.467,170,3.774,197,2.917,203,2.662,213,3.67,236,1.721,246,4.205,272,2.68,292,2.848,304,1.38,323,2.64,338,1.61,414,3.756,491,1.973,605,2.369,614,1.205,734,2.897,759,3.162,760,3.285,769,4.909,811,4.062,1188,5.031,1216,3.333,1366,3.642,1367,3.174,2818,6.81,3042,3.83,3664,5.552,3917,4.164]],["t/2960",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3924,9.359,3925,6.885,3926,7.754,3927,7.754]],["t/2962",[7,0.751,28,1.344,139,4.273,170,2.813,203,2.279,246,4.346,285,3.934,304,2.007,614,1.753,759,4.029,828,3.971,3925,6.885,3928,9.359,3929,7.754,3930,7.754]],["t/2964",[7,0.757,31,2.345,34,2.445,39,3.906,84,1.04,139,4.308,174,2.853,233,3.474,325,6.684,414,5.507,509,5.438,614,1.767,672,5.655,760,3.413,1473,4.277,3718,6.054]],["t/2966",[7,0.769,34,2.485,84,1.057,139,4.38,174,2.901,233,3.532,325,6.795,444,4.231,509,5.529,614,1.797,672,5.749,760,3.446,1245,3.229,3718,6.155]],["t/2968",[21,1.809,56,1.212,68,3.368,107,2.274,109,2.111,139,2.874,170,1.892,254,2.059,338,1.575,353,1.746,377,1.432,596,2.884,605,2.317,614,2.261,647,3.332,653,2.016,755,2.884,760,1.892,942,4.887,997,4.079,1363,2.306,1476,2.353,1737,2.415,2232,6.26,2839,8.521,3053,5.982,3056,5.215,3943,9.201,3944,4.921,5007,6.826,5008,6.514,5009,6.714]],["t/2970",[7,0.489,49,1.394,56,1.262,84,1.086,103,2.075,109,1.691,160,3.045,164,1.703,170,1.832,217,3.535,279,2.679,377,1.387,398,3.06,527,1.356,565,2.024,601,2.91,760,3.209,811,3.847,851,3.25,996,3.911,1235,5.358,1245,2.052,1257,5.826,1332,3.787,1454,3.847,1530,5.226,1550,4.614,1558,3.491,1902,4.142,1918,5.405,1998,3.604,2013,3.911,2600,3.817,2814,4.317,2816,4.945,2817,5.302,2818,4.092,2819,4.945,2821,4.945,2822,4.765,2823,5.302,2824,5.648,2825,5.302,2826,7.427,2827,4.765,2828,5.302,2829,5.302,2831,5.302,2832,5.049,4009,8.572,4177,5.884,5542,6.682,5543,6.682]],["t/2972",[7,0.818,34,2.058,84,0.875,103,3.472,109,1.933,123,2.753,170,3.066,410,5.332,527,1.766,565,2.638,568,2.314,630,2.984,683,3.023,723,2.888,759,3.637,760,2.387,769,3.105,841,3.734,1044,4.759,1320,5.139,1465,5.842,1566,5.766,1754,3.434,1759,5.561,2500,7.359,2814,5.625,2815,5.625,2867,4.727,4784,9.45,5010,9.45,5011,8.08,5012,8.08]],["t/2974",[7,0.577,17,2.894,24,1.576,29,2.555,49,2.187,56,0.961,84,1.184,85,2.537,103,2.45,105,2.29,170,3.229,177,4.698,322,3.043,377,1.638,541,4.507,580,2.543,613,3.31,614,2.144,760,3.682,1633,3.451,1698,5.449,1882,5.732,2297,2.439,2389,6.447,2398,5.627,2814,5.099,2920,9.731,2921,9.731,2922,5.963,2923,6.102,2924,7.323,2925,7.323,2926,7.323,3644,2.698,5544,7.891]],["t/2976",[2061,3.73]],["t/2978",[7,0.65,12,3.327,28,1.776,84,0.894,89,3.079,105,2.58,107,2.112,174,2.452,228,2.613,372,3.087,377,1.845,390,3.983,397,3.429,405,4.005,426,3.883,496,5.039,511,3.352,536,2.457,558,3.689,622,4.161,723,3.758,940,3.087,1473,3.676,1864,6.098]],["t/2980",[16,2.273,28,1.312,183,3.99,322,2.907,405,3.603,426,3.433,495,5.469,511,2.963,670,5.402,1052,6.634,1163,5.635,1191,6.816,1998,5.402,3876,6.917,3877,8.821,5067,9.296,5068,9.296]],["t/2982",[405,3.326,511,3.333,757,2.576,2627,10.457,5069,10.455]],["t/2984",[7,0.739,28,1.322,49,2.106,84,1.015,109,2.15,183,4.021,405,2.981,409,4.748,426,3.46,511,2.987,622,4.726,1170,2.876,1289,3.79,1753,7.834,1870,3.261,5070,9.37]],["t/2986",[130,5.755,183,4.333,338,2.483,405,3.211,511,3.218,724,4.915,880,5.125,3880,7.779,5071,10.096]],["t/2988",[163,3.967,183,4.296,277,6.086,405,3.184,511,3.191,622,5.049,723,3.578,1037,4.807,5072,10.009]],["t/2990",[7,0.612,16,1.881,21,2.192,28,1.681,84,1.218,107,3.242,109,1.896,132,2.969,170,3.321,228,2.458,397,3.225,405,3.576,426,3.733,505,3.215,511,2.473,820,6.121,1052,5.537,1117,4.197,1156,6.318,1303,4.635,1500,5.864,1782,4.552,5073,7.759]],["t/2992",[16,1.703,34,2.33,121,5.353,254,2.942,353,3.059,397,3.804,405,2.911,408,4.566,484,3.877,511,2.917,581,6.617,712,4.923,1632,4.749,1874,6.371,3884,11.514,3885,8.684,5074,9.151]],["t/2994",[16,1.801,343,3.443,351,3.503,405,3.079,432,7.321,476,2.81,511,3.086,583,5.911,635,3.512,757,3.064,2485,6.054,3887,7.718,5075,9.679]],["t/2996",[49,2.106,109,1.58,130,5.341,171,2.546,172,3.943,174,2.785,183,4.021,377,2.096,405,2.981,426,3.46,505,3.883,511,2.987,580,3.952,636,5.276,724,4.562,997,4.215,5076,9.37]],["t/2998",[24,1.554,145,3.86,228,3.041,405,3.054,511,3.06,760,3.798,769,4.761,814,4.134,934,4.578,1696,6.106,1782,4.323,5077,9.6]],["t/3000",[24,1.649,405,3.239,511,3.246,690,6.317,760,3.009,769,4.882,1696,6.477,5078,10.183]],["t/3002",[16,1.801,26,4.309,121,5.663,183,4.154,322,3.027,405,3.079,407,4.375,408,3.494,511,3.086,750,6.821,757,2.86,1237,6.518,5079,9.679,5080,9.679]],["t/3004",[4,2.883,172,3.22,183,3.99,193,3.381,394,4.646,405,2.957,407,4.202,511,2.963,552,3.29,725,5.767,822,5.553,981,5.767,988,5.203,1114,4.217,1381,3.684,3876,6.917,3892,10.317,3893,8.467,5081,9.296]],["t/3006",[5,2.715,7,0.883,16,1.873,25,2.468,28,1.088,56,0.761,84,1.09,105,2.411,125,4.394,133,3.297,144,3.691,145,3.099,164,2.117,174,2.291,196,3.055,198,4.9,228,2.442,294,4.147,372,2.884,390,3.722,458,4.34,511,4.03,536,2.295,683,2.884,807,4.591,854,4.103,940,3.766,1266,2.102,1632,4,1779,5.191,2683,6.194,3894,7.314]],["t/3008",[5,3.056,16,2.02,24,1.918,28,1.532,84,0.94,132,2.548,134,4.318,162,3.049,239,4.977,255,5.844,367,5.527,407,3.922,511,2.766,647,3.255,807,4.797,832,5.844,907,5.568,934,4.138,1364,6.274,2677,6.787,5082,8.677,5083,8.677]],["t/3010",[5,1.728,16,2.108,24,1.577,25,1.57,26,2.721,27,1.164,28,1.599,31,1.198,46,2.033,49,1.102,109,0.827,163,1.944,170,2.166,193,2.666,200,2.43,201,3.731,224,2.078,225,1.753,240,2.682,281,2.32,289,3.848,336,1.996,338,1.206,405,3.097,407,2.217,495,3.539,511,3.924,552,1.736,558,2.193,568,1.405,632,2.639,635,2.659,694,2.667,723,1.753,725,2.497,757,1.209,769,3.373,807,3.762,812,3.269,816,2.779,1051,6.743,1139,2.625,1446,2.869,1473,5.19,1798,2.729,1799,5.045,2668,3.456,3007,3.769,3401,3.707,3876,3.65,3903,6.453,3910,3.837,5084,4.905,5085,7.33,5086,4.905,5087,4.905,5088,4.905,5089,7.33,5090,4.905,5091,7.33,5092,4.905,5093,7.33,5094,4.905,5095,7.33,5096,4.905,5097,4.905,5098,7.33,5099,4.905,5100,4.905,5101,7.33,5102,4.905,5103,7.33,5104,4.905]],["t/3012",[16,1.519,17,1.722,24,1.762,25,1.395,26,2.488,27,1.591,28,1.688,36,1.922,41,1.977,49,1.835,84,0.472,89,2.501,103,1.458,109,1.377,132,1.28,145,2.695,162,2.355,169,2.383,203,1.043,240,2.383,281,2.062,285,1.801,286,2.642,287,4.782,289,2.648,367,2.219,377,1.499,394,2.178,405,2.597,407,1.97,408,3.309,409,2.208,432,2.749,476,1.945,511,3.815,512,2.311,527,0.953,575,3.183,580,1.513,635,2.431,653,1.372,662,2.533,693,2.682,724,2.122,731,2.284,736,1.858,751,2.516,757,1.074,760,1.98,807,2.59,878,1.858,880,2.886,893,2.516,1117,2.357,1139,2.332,1245,1.442,1315,2.397,1473,5.332,1557,2.704,1696,2.772,1703,5.73,2678,3.726,2700,3.549,3108,3.475,3880,2.849,3887,3.475,5105,6.702,5106,4.358,5107,6.702,5108,4.358,5109,6.702,5110,4.358,5111,6.702,5112,4.358,5113,6.702,5114,4.358,5115,6.702,5116,4.358,5117,6.702,5118,4.358,5119,4.358,5120,4.358,5121,6.702,5122,4.358,5123,8.165,5124,4.358,5125,4.358,5126,4.358,5127,6.702,5128,4.358]],["t/3014",[2061,3.73]],["t/3016",[7,0.924,24,1.594,84,1.066,139,5.261,216,5.01,325,6.852,397,4.091,759,3.45,940,3.682,1289,3.981,1506,6.315,5545,9.339]],["t/3018",[7,0.641,24,1.317,84,0.881,89,3.036,109,1.372,139,4.677,164,2.235,165,1.822,171,2.21,174,2.418,281,3.849,285,3.361,338,2.564,527,2.651,568,2.985,614,1.498,724,3.961,759,2.852,953,3.291,1245,3.448,1266,2.842,1381,3.224,1506,5.221,1863,3.403,1870,2.831,1902,3.88,2851,5.978,2870,7.41,3946,7.72,5021,6.364]],["t/3020",[5,1.92,7,0.625,19,2.429,24,1.982,28,0.769,31,1.332,39,2.218,68,3.539,96,1.92,107,1.396,109,0.919,139,4.189,160,1.911,182,1.824,190,2.802,203,1.897,226,2.643,236,2.083,243,2.981,277,2.802,295,2.192,313,1.332,318,1.327,338,1.341,367,2.775,414,3.127,421,3.41,441,2.965,445,3.438,484,2.31,527,2.041,553,2.887,568,2.269,605,1.973,623,3.794,638,4.767,662,3.168,757,1.343,759,2.778,760,2.758,769,4.183,796,2.858,827,4.73,850,3.563,862,2.713,933,3.997,934,2.6,946,4.718,953,2.205,1001,4.189,1133,2.933,1239,3.355,1240,3.108,1303,3.257,1383,4.799,1435,3.671,1448,4.439,1496,4.799,1498,3.89,1506,3.498,1629,3.796,1733,3.257,1754,2.317,2282,3.634,2717,3.563,2818,6.16,2966,3.711,3629,3.41,3730,4.661,3917,3.468,3918,4.799,3922,6.775,5183,5.173,5546,5.874,5547,5.874]],["t/3022",[7,0.745,31,2.307,39,3.843,139,4.239,174,2.807,325,6.576,363,6.74,414,5.418,614,1.739,672,5.564,760,3.379,916,5.489,1178,6.926,1473,4.209,1642,8.076,2713,8.315,3718,5.957,5545,8.963]],["t/3024",[7,0.763,139,4.344,174,2.877,325,6.739,444,4.196,445,6.104,614,1.782,672,5.701,760,3.429,916,5.625,1178,7.097,2695,8.816,2713,8.521,3718,6.104,5545,9.185]],["t/3026",[7,0.807,24,1.282,49,2.301,56,0.782,84,0.857,109,1.335,139,3.552,164,2.175,170,3.354,174,2.353,203,2.451,206,5.126,246,4.292,247,3.478,304,1.668,613,4.629,614,1.457,759,2.775,760,3.672,992,3.957,1139,4.236,1266,2.159,1284,3.871,1633,3.73,2297,2.637,3924,6.446,3925,5.724,3926,6.446,3927,9.243]],["t/3028",[7,0.624,24,1.282,49,2.301,56,0.782,84,0.857,109,1.335,139,3.552,164,2.175,170,3.354,174,2.353,203,2.451,206,5.126,246,4.292,247,3.478,304,1.668,613,4.629,614,1.457,759,2.775,760,3.672,992,3.957,1139,4.236,1266,2.159,1284,3.871,1633,3.73,2297,2.637,3925,5.724,3928,6.446,3929,6.446,3930,9.243,3931,7.512]],["t/3030",[7,0.751,24,1.542,28,1.344,109,1.606,132,2.797,139,4.273,193,3.463,236,2.504,254,3.696,476,2.764,550,3.742,578,3.742,614,1.753,623,4.56,997,3.526,1382,5.688,1699,5.907]],["t/3032",[4,2.711,7,0.689,84,0.947,88,3.937,89,3.262,128,5.669,140,3.425,182,2.925,294,4.703,557,4.35,575,4.152,763,5.95,796,4.582,828,3.645,878,3.727,940,3.271,1069,4.728,1075,5.08,1363,3.148,1381,3.464,1408,3.881,1616,6.068,3932,6.838,3934,7.696,5548,9.419,5549,9.419]],["t/3034",[16,2.033,24,1.186,28,1.542,84,0.793,107,2.798,109,1.964,132,3.42,139,4.367,170,2.875,177,3.536,196,2.902,254,2.355,277,5.001,279,3.164,296,2.822,313,1.789,318,1.783,351,2.65,353,1.997,372,2.74,421,4.58,445,4.619,484,3.102,527,1.601,614,1.348,704,6.447,854,5.179,877,2.67,878,3.123,880,3.153,1266,1.997,1280,4.932,1435,4.932,1629,5.099,1874,5.099,3728,5.37,3866,3.8,5550,7.891,5551,7.891]],["t/3036",[7,0.337,12,1.726,16,0.796,24,1.307,25,1.37,34,1.09,36,1.887,49,0.962,56,1.131,68,1.622,84,0.463,98,1.504,107,1.095,109,1.823,132,1.257,139,4.403,170,2.385,179,1.432,185,2.945,199,2.11,236,1.737,254,1.376,295,1.721,296,1.649,338,1.625,353,1.167,377,0.957,421,2.676,426,1.58,476,3.138,484,1.813,512,2.783,524,2.634,527,0.935,575,3.138,576,2.52,578,1.681,580,1.486,593,2.11,614,1.99,628,2.21,638,2.574,647,3.68,653,2.08,663,2.613,725,3.364,759,2.316,803,2.409,940,3.02,942,4.838,997,2.446,1012,3.743,1114,1.941,1170,1.314,1171,3.412,1187,3.658,1245,1.416,1266,1.802,1289,1.731,1363,2.906,1408,4.03,1476,2.428,1506,2.746,1616,3.676,1802,3.897,1847,2.882,1864,2.254,1874,2.979,1883,3.658,1899,2.979,1902,3.849,2488,2.594,2839,7.599,3053,4.45,3420,4.279,3916,2.852,3936,3.767,3939,6.27,3940,7.991,3941,6.27,3942,2.722,3943,7.539,3944,3.288,5552,4.611]],["t/3038",[7,0.814,16,1.493,24,1.85,28,1.132,68,3.042,96,2.826,132,2.357,233,2.904,318,1.954,398,2.826,426,2.963,502,2.213,512,2.767,536,2.39,550,3.153,565,2.62,568,2.298,578,3.153,614,1.477,623,3.842,647,3.875,724,3.907,916,4.663,997,2.971,1037,3.059,1382,4.793,1408,3.563,1476,2.948,1487,5.349,1847,6.957,1906,4.364,1907,4.144,2670,8.024,3016,4.828,4218,5.726,5553,8.646]],["t/3040",[7,0.758,24,2.007,31,2.824,84,1.173,139,5.187,190,3.695,203,2.301,213,3.911,246,3.634,397,2.988,414,4.124,605,2.601,613,3.249,623,3.442,759,3.369,760,2.84,769,4.943,831,4.534,947,3.66,1002,5.732,1216,3.66,1367,3.485,1696,4.573,1698,5.349,2818,7.145,2887,5.854,2899,6.146,3664,5.915,3917,4.573]],["t/3042",[7,0.849,16,0.788,24,1.889,25,1.355,28,1.377,29,2.286,31,1.601,37,2.821,39,2.666,56,0.891,68,1.605,103,1.416,112,2.097,115,1.949,138,2.642,139,3.597,145,1.702,160,3.619,179,1.416,203,1.568,225,2.342,236,2.373,248,1.987,257,2.155,279,1.829,282,2.302,285,1.749,292,1.841,304,0.892,313,1.034,338,1.041,343,2.851,409,2.145,445,2.669,484,1.793,495,2.044,525,1.994,527,0.925,530,1.893,560,2.253,562,2.078,565,2.139,628,2.186,630,1.563,635,1.536,683,1.584,723,1.513,747,1.738,757,1.043,759,2.297,760,3.447,769,3.468,814,3.452,827,4.062,830,2.994,850,2.766,851,2.218,857,1.92,862,3.989,877,1.543,878,1.805,919,2.913,936,2.803,946,3.691,954,2.821,1009,1.949,1049,3.375,1066,2.088,1239,4.033,1257,2.46,1381,1.677,1383,3.726,1473,1.886,1629,2.947,1689,2.218,1696,2.692,1754,2.785,1782,3.61,1918,2.106,1970,3.375,2013,2.669,2061,1.429,2429,2.983,2741,3.311,2811,3.619,2816,3.375,2818,7.105,2819,3.375,2867,3.833,2870,3.855,3947,4.016,3948,3.619,3949,4.016,5554,4.561,5555,4.561,5556,4.016,5557,4.233]],["t/3044",[24,1.693,29,3.648,103,3.498,395,4.132,759,3.665,1530,6.291,2827,8.034]],["t/3046",[7,0.756,18,3.603,24,1.328,28,1.157,31,1.397,34,1.456,56,1.241,68,3.109,84,1.039,103,1.913,105,1.788,123,1.948,132,2.408,145,2.299,160,3.361,165,1.281,196,2.266,257,2.911,275,1.77,279,2.47,318,1.392,353,1.559,398,2.888,484,2.422,527,1.25,565,1.867,568,1.637,601,2.684,759,2.004,760,3.277,769,2.197,847,3.163,851,2.997,1114,2.594,1164,2.884,1214,3.702,1234,3.239,1235,5.167,1257,4.766,1332,3.492,1454,3.547,1487,3.811,1530,3.44,1633,2.694,1735,3.981,1782,2.575,1902,2.727,1918,5.212,2448,4.888,2600,5.047,2814,5.71,2823,4.888,2825,4.888,2826,7.011,2827,4.393,2828,4.888,2829,4.888,2831,4.888,2832,4.656,2853,5.717,3514,4.393,3948,4.888,3950,5.033,3954,5.033,5022,5.717]],["t/3048",[37,4.974,56,1.308,95,4.63,160,3.454,279,3.224,601,3.503,760,2.911,851,3.912,1235,5.786,1257,5.726,1332,4.558,1454,4.63,1530,4.49,1754,3.172,1902,4.699,1918,4.903,2816,5.951,2817,6.381,2818,4.925,2819,5.951,2821,5.951,2822,5.735,2823,6.381,2824,6.797,2825,6.381,2826,8.424,2827,7.571,2828,6.381,2829,6.381,2831,6.381,2832,6.077,3960,7.082,5558,7.463]],["t/3050",[56,1.292,160,3.425,279,3.184,395,2.912,527,1.611,601,3.459,760,2.177,851,3.863,866,8.142,1170,3.365,1235,5.758,1257,5.678,1332,4.501,1408,4.338,1454,4.572,1530,4.434,1902,4.66,1907,3.806,1918,4.862,2821,5.876,2822,5.663,2823,6.301,2824,6.712,2825,6.301,2826,8.354,2827,5.663,2828,6.301,2829,6.301,2831,6.301,2832,6.001,3962,6.993,3963,6.993,3964,6.993,5558,7.369]],["t/3052",[16,1.862,84,1.283,254,3.218,409,5.072,760,2.957,855,7.053,1107,5.273,1381,3.967,1632,5.194,3965,9.498,3966,8.812]],["t/3054",[2,1.405,7,0.934,17,2.059,21,2.166,24,1.472,29,1.817,39,2.12,48,2.114,49,1.171,84,1.158,100,2.33,107,1.334,109,1.292,116,2.86,138,4.31,165,1.717,170,1.539,171,2.082,183,2.236,193,1.895,200,2.581,203,1.835,219,2.372,220,3.206,236,2.015,248,3.598,272,2.133,283,2.216,296,2.008,313,1.273,351,1.885,398,3.937,512,2.643,525,2.455,614,0.959,653,1.64,677,2.745,734,2.305,757,1.284,759,2.687,760,3.157,780,2.773,827,2.64,898,2.526,1181,2.704,1214,3.374,1266,1.421,1312,3.926,1360,2.97,1473,2.321,1493,3.314,1540,2.882,1550,3.876,1558,2.933,1698,5.703,1814,3.718,2032,2.193,2368,6.553,2370,4.745,2398,9.109,2485,3.258,2645,4.341,2789,3.546,2923,6.387,3967,4.944,5559,5.614,5560,5.614,5561,4.745]],["t/3056",[2,1.351,7,0.587,24,0.811,29,1.748,49,1.673,56,0.877,84,1.193,85,1.735,101,2.666,109,0.845,111,6.516,139,3.988,164,1.376,165,1.122,170,2.626,174,1.489,203,1.782,206,3.244,225,1.791,236,1.317,246,1.894,247,2.201,313,1.224,347,3.306,351,1.813,377,1.12,395,1.98,405,1.594,426,1.85,578,1.968,580,2.585,613,3.366,614,0.922,621,2.575,663,3.06,759,3.115,760,3.255,841,2.315,878,2.136,914,4.422,992,2.504,1164,2.527,1170,2.728,1243,3.673,1284,2.45,1312,3.615,1633,4.187,1754,2.129,1904,5.54,1971,3.727,2389,4.41,2395,5.98,2398,7.56,2636,5.098,2754,5.937,2837,7.32,2923,6.205,3924,4.079,3926,7.236,3927,4.079,3928,4.079,3929,7.236,3930,4.079,4004,3.727,5024,9.838,5025,8.887,5026,5.01,5027,5.01,5028,5.01]],["t/3058",[2061,3.73]],["t/3060",[7,0.784,27,1.215,28,1.068,31,1.848,49,1.15,56,0.747,84,0.975,96,2.665,107,2.843,109,2.229,123,2.578,127,2.481,160,1.794,164,1.406,171,1.391,172,1.773,174,1.521,182,1.713,193,1.862,254,1.646,272,2.095,294,2.754,323,2.064,377,1.145,381,2.422,397,2.128,405,1.628,439,3.346,447,2.422,557,2.547,566,3.564,580,1.777,593,2.525,597,2.644,607,2.9,612,3.315,614,1.393,635,1.857,658,2.451,731,2.683,741,3.315,747,2.102,757,1.261,827,2.594,829,2.697,877,2.759,947,2.606,981,3.176,992,2.558,997,1.895,1009,2.357,1047,3.058,1117,2.769,1170,2.323,1238,3.058,1266,2.063,1289,3.64,1290,2.848,1416,3.653,1753,3.523,1754,2.176,1801,4.313,1813,3.753,1847,5.095,1870,3.865,1898,2.697,1997,3.933,2677,4.004,3878,3.933,4233,4.082,5029,5.119,5030,5.119,5562,5.516,5563,5.516,5564,5.516,5565,5.516]],["t/3062",[7,0.904,34,2.732,49,1.586,56,1.316,84,0.765,109,2.125,164,2.947,233,2.554,278,2.529,295,4.314,304,1.487,408,2.548,452,6.535,495,3.408,527,1.543,614,1.299,705,4.345,756,3.313,1170,2.915,1216,4.833,1266,1.925,1282,5.424,1558,3.974,1705,4.311,1870,2.457,1876,6.148,1882,4.158,3624,5.424,3968,6.035,3969,5.424,3970,6.698]],["t/3064",[7,0.516,27,2.141,40,3.449,56,0.891,84,0.977,98,2.3,107,2.309,109,2.22,111,4.8,127,3.384,165,1.467,170,1.934,179,3.453,193,2.381,200,3.243,203,1.567,211,4.871,281,3.097,295,2.633,353,1.785,452,4.506,505,2.713,527,1.972,603,5.597,614,2.047,683,2.449,818,3.287,878,2.792,940,2.449,1170,2.769,1171,5.22,1266,1.785,1408,2.907,1693,3.83,1870,4.38,1884,4.321,2428,5.331,3971,8.823,3972,5.963,5566,7.054,5567,7.054]],["t/3066",[2,1.457,4,1.675,7,0.426,8,2.906,24,1.274,27,1.282,34,1.376,56,0.533,84,0.852,97,2.415,107,2.015,109,2.212,110,4.891,165,1.21,170,1.596,171,1.467,186,2.545,236,1.42,254,1.737,281,3.723,295,2.172,296,2.082,313,1.32,338,1.329,353,1.473,372,2.021,397,2.245,408,3.352,421,3.378,440,2.382,445,3.407,452,3.718,527,2.779,607,3.016,613,3.557,614,1.449,653,1.7,663,3.299,724,2.63,733,4.755,759,1.893,760,1.596,947,2.75,1170,2.85,1239,3.325,1245,3.375,1257,4.574,1629,3.761,1847,3.638,1870,3.232,1876,3.498,2102,4.618,2608,4.019,2773,4.92,2800,4.307,3089,4.398,3974,5.126,3975,5.126,3977,3.906,3978,4.755,3979,5.126,4233,4.307,4498,4.151,5568,5.82,5569,5.82,5570,5.82,5571,5.82]],["t/3068",[4,2.216,7,0.948,28,1.008,55,5.206,56,1.138,85,2.475,107,2.45,109,1.944,115,3.29,165,1.6,170,3.407,236,1.879,282,3.886,304,1.506,397,2.97,612,4.627,614,1.315,759,2.505,760,2.828,835,4.812,846,4.793,859,5.697,882,4.268,900,4.67,954,4.762,1066,3.524,1224,4.585,1373,4.364,1540,3.952,1558,4.023,1754,3.037,1802,6.507,1865,5.818,1870,2.487,1941,6.109,2870,6.507,3973,6.507,4474,5.239,5014,6.507,5572,7.699,5573,7.699]],["t/3070",[5,2.161,7,0.786,27,2.047,56,0.606,96,3.511,107,2.916,109,2.175,127,2.827,160,3.79,171,1.667,179,3.335,216,3.124,246,2.32,256,2.296,302,2.825,337,2.858,408,2.215,444,2.66,445,3.87,527,2.491,566,4.272,603,5.246,615,3.59,635,2.226,734,2.715,803,3.455,880,2.642,1153,5.246,1224,3.938,1241,4.996,1245,2.03,1257,3.566,1266,2.718,1693,5.044,1847,4.132,1870,3.47,1883,5.246,1884,4.049,1945,4.09,2013,3.87,2153,5.402,3092,5.402,3817,3.903,3932,4.8,3976,5.402,3978,9.519,5031,8.622,5574,6.612]],["t/3072",[7,0.409,21,0.893,26,1.173,28,0.446,29,1.103,31,1.268,33,2.958,35,2.201,41,1.434,49,1.718,56,1.122,84,0.914,85,1.798,89,1.179,96,2.326,105,0.989,106,1.158,108,2.68,109,2.148,112,2.571,113,1.153,123,1.769,140,1.239,160,1.108,170,0.934,171,1.41,174,1.542,179,1.058,182,1.058,193,1.15,196,1.253,203,1.242,228,1.644,236,1.365,262,3.392,275,0.978,277,1.624,294,1.7,295,2.087,304,0.666,316,2.312,318,0.77,322,1.623,336,2.687,338,0.777,343,1.124,353,0.862,359,1.394,372,1.183,377,1.477,391,2.779,398,1.828,405,1.005,408,1.873,444,1.37,491,1.564,502,0.872,509,1.791,527,1.135,553,1.674,568,1.486,578,1.242,605,1.144,613,1.429,614,2.154,623,2.485,658,1.514,696,3.657,723,1.13,756,2.436,828,2.164,876,2.151,997,2.445,1040,2.175,1123,2.256,1170,3.543,1238,1.888,1289,3.67,1381,2.617,1434,1.759,1558,2.922,1588,2.879,1693,3.863,1705,1.93,1801,3.764,1870,4.434,1876,3.361,1884,2.086,1918,2.582,2026,1.617,2275,2.429,2297,1.729,2411,2.318,2600,1.945,2636,2.977,2837,2.352,3652,3.273,3734,2.634,3980,2.783,3991,3.425,4001,2.999,4002,2.999,4003,2.999,4004,2.352,4005,2.52,4006,2.999,4007,2.999,4008,2.999,4009,5.645,4012,2.999,4013,2.999,4014,2.999,4233,2.52,5032,3.161,5033,3.161]],["t/3074",[49,2.23,84,1.075,109,2.119,336,4.038,391,4.178,614,2.169,1289,4.014,1801,5.657,1870,3.454,2411,7.277]],["t/3076",[4,1.299,7,0.512,24,1.289,28,1.513,34,2.028,56,1.23,84,0.454,96,1.475,98,1.471,107,1.663,108,3.356,109,1.869,132,1.23,161,2.85,164,1.15,171,1.138,182,1.401,207,1.709,221,1.546,254,2.56,282,2.277,304,1.369,318,1.582,343,1.489,353,1.142,395,1.655,398,1.475,476,2.311,480,2.85,482,1.872,484,1.774,502,1.155,517,2.538,527,2.247,530,1.872,578,1.645,580,1.454,599,2.152,614,0.771,653,1.318,658,4.297,707,2.33,741,2.711,757,1.032,880,2.798,945,2.184,953,1.694,992,2.093,997,4.386,1066,2.065,1107,2.206,1170,2.444,1245,2.634,1381,1.659,1408,2.885,1737,1.579,1870,1.457,2026,2.142,2032,2.735,2312,2.241,2633,2.303,3644,1.542,3715,4.993,3817,4.133,3844,7.899,3985,3.58,3986,3.973,3987,3.58,4606,7.009,5034,6.498,5035,6.498,5036,6.498,5037,6.498,5038,6.498,5039,6.498,5040,6.498,5041,6.498,5042,6.498,5043,6.498,5044,6.498,5045,6.498,5046,6.498,5047,6.498,5048,6.498,5049,6.498,5050,4.187,5575,3.973]],["t/3078",[12,2.073,17,2.032,24,1.229,25,1.646,27,2.731,28,1.569,30,3.216,31,1.256,33,2.931,40,2.708,56,0.891,84,0.822,85,1.781,98,1.807,103,2.54,106,3.307,107,2.721,109,1.94,115,2.367,132,1.51,138,2.073,164,1.412,165,1.152,171,1.397,174,1.528,179,1.72,182,1.72,183,2.207,197,2.291,199,2.536,236,1.352,254,2.441,256,1.924,289,2.032,302,2.367,304,1.599,313,1.256,318,1.252,369,3.19,413,5.736,416,2.367,489,3.427,502,1.418,507,2.796,513,2.423,525,2.423,527,1.124,550,3.546,590,2.766,614,0.946,615,3.008,623,2.462,631,3.36,755,2.315,809,3.539,880,2.214,950,6.282,992,2.57,997,2.81,1047,3.071,1111,3.36,1139,2.751,1238,3.071,1245,2.511,1303,3.071,1352,2.472,1353,4.526,1382,5.952,1408,3.37,1474,3.951,1737,1.939,1845,3.049,1870,1.789,1880,4.396,3053,5.112,3056,4.186,3644,1.894,3985,4.396,3990,4.526,5007,3.951,5051,5.141]],["t/3080",[27,1.931,28,1.623,56,1.029,99,5.131,109,1.757,132,2.389,162,2.859,170,2.404,171,2.21,236,2.139,304,1.714,336,3.31,338,2.001,550,3.197,614,1.498,635,2.952,636,4.581,724,3.961,740,3.677,877,2.966,880,3.503,997,3.012,1139,4.353,1382,6.869,1508,4.5,1737,3.068,1844,6.365,1870,3.627,1880,6.956,3644,2.997,5021,6.364]],["t/3082",[4,1.884,5,2.139,7,0.781,27,2.795,34,1.546,49,1.365,50,3.553,56,0.979,68,2.302,84,0.658,98,2.134,109,1.985,126,4.133,164,2.351,171,2.325,172,4.279,203,1.454,207,4.393,240,3.321,256,3.202,272,2.486,279,2.624,282,3.303,295,2.442,304,1.28,313,2.091,316,2.706,343,3.044,353,1.656,404,4.168,408,2.192,476,1.763,480,4.133,482,2.715,489,6.607,531,3.681,578,2.386,599,4.399,614,1.118,707,3.379,734,2.687,992,3.035,1289,3.462,1367,2.944,1671,3.34,1870,2.113,3652,3.83,3921,4.518,3987,5.192,3991,6.542,3992,5.762]],["t/3084",[7,0.508,16,0.771,24,0.671,27,1.53,28,0.909,29,1.445,31,1.012,34,1.641,39,1.686,46,1.717,49,1.448,56,1.279,84,0.698,92,2.386,93,3.35,100,2.881,102,2.613,108,4.084,109,1.726,112,2.052,132,1.217,163,1.642,165,0.928,171,1.126,172,4.212,173,2.217,179,1.386,182,1.386,183,1.778,193,1.507,207,3.228,221,1.53,254,1.332,256,1.55,261,2.099,276,3.918,279,1.79,296,1.596,304,1.358,316,1.846,318,1.569,336,1.686,343,1.473,377,1.769,391,1.744,395,1.637,398,1.459,408,1.495,476,2.59,502,1.142,517,2.511,531,2.511,553,3.412,578,1.628,599,2.129,614,0.763,620,2.292,653,1.304,662,2.408,728,2.884,734,1.833,741,2.683,751,2.392,755,1.866,757,1.021,818,2.08,880,2.775,945,2.161,986,4.676,988,3.607,998,2.659,1013,3.083,1107,2.183,1164,2.09,1221,3.083,1381,1.642,1509,3.542,1689,3.378,1870,2.243,1884,2.734,1953,2.253,1980,4.487,2026,2.119,2032,2.713,2118,2.919,2297,1.38,2312,2.217,2321,2.79,2353,2.475,2633,2.278,2867,2.424,2885,3.542,2899,3.542,3042,2.424,3652,4.988,3817,4.099,3921,3.083,3991,4.253,3993,3.647,3994,3.373,3995,3.647,4849,3.241,5052,4.143,5576,4.143]],["t/3086",[5,2.074,7,0.768,31,2.046,35,4.101,39,2.397,49,1.324,56,0.827,68,3.696,84,1.299,98,2.07,105,1.842,109,1.79,110,3.103,164,1.618,165,1.876,172,3.377,203,2.005,207,3.419,253,3.544,256,3.134,304,1.241,317,2.944,336,2.397,377,1.317,404,2.868,513,2.775,527,1.831,552,3.45,553,3.119,576,3.469,607,3.21,611,2.448,614,1.084,623,2.82,630,2.175,638,3.544,731,3.087,769,3.219,876,4.009,902,4.101,986,3.087,988,3.296,1042,3.544,1057,3.814,1073,4.259,1129,3.684,1210,4.526,1278,3.926,1629,4.101,1689,3.087,1870,2.05,2103,4.526,2506,5.036,2867,3.446,2892,7.367,3016,6.749,3402,4.451,3994,4.796,4124,4.796,4347,5.036,4374,4.696,5145,5.185]],["t/3088",[2,1.512,5,1.975,7,0.868,12,2.262,27,1.92,28,1.618,30,3.508,49,1.26,56,0.799,84,1.027,107,1.436,109,1.599,113,3.459,170,2.389,171,2.197,172,3.596,203,1.342,224,3.426,236,1.475,246,3.058,304,1.182,318,1.365,336,2.282,338,1.379,351,2.029,391,2.361,432,3.537,505,2.324,511,1.788,525,2.643,552,3.357,597,2.896,599,2.882,614,1.911,636,3.158,724,2.731,725,5.838,807,3.665,832,3.777,878,2.391,880,2.415,906,3.399,953,3.271,986,2.939,988,3.139,1012,3.177,1139,4.328,1178,4.112,1380,3.537,1446,3.281,1508,3.102,1737,2.115,1844,3.425,1864,2.954,1870,2.815,3004,4.673,3057,4.055,3644,2.066,3652,3.537,3910,4.387,3991,3.701,5009,7.168,5021,4.387]],["t/3090",[0,3.09,4,1.66,5,2.754,7,0.852,24,0.867,27,2.192,28,1.103,31,1.308,34,1.363,49,1.203,56,0.772,84,0.847,109,1.822,113,1.952,123,1.824,140,2.097,162,1.881,164,1.47,170,2.31,171,2.509,172,3.199,173,4.942,174,2.324,184,4.045,193,2.843,203,1.281,228,1.695,236,1.407,255,3.604,261,2.712,275,1.657,289,2.115,296,2.063,304,1.128,320,3.07,323,2.159,336,2.178,338,1.317,353,1.459,377,1.197,426,1.977,432,3.376,484,3.312,505,2.218,550,2.103,568,1.533,596,2.41,614,1.989,632,2.879,636,3.014,724,2.606,725,2.725,728,3.727,755,3.521,802,2.165,828,2.232,877,1.952,880,2.305,934,2.553,953,2.165,1012,5.232,1066,2.64,1139,4.183,1178,3.924,1216,2.725,1508,2.961,1737,2.018,1844,3.269,1870,2.721,3003,4.268,3033,6.234,3644,1.972,3652,3.376,3921,3.982,3991,3.532,4511,4.268,5008,5.732,5009,4.045,5021,4.187]],["t/3092",[24,1.248,28,1.088,34,1.963,56,0.994,107,2.869,109,1.697,113,2.811,193,2.803,246,3.806,285,3.184,295,3.099,304,1.624,338,1.896,351,3.642,477,4.572,527,2.2,576,4.54,605,2.789,614,1.419,621,3.961,755,4.533,765,4.572,880,3.319,953,3.118,992,3.853,1181,5.223,1245,2.55,1540,4.264,1737,2.907,1863,3.224,1870,3.503,2372,5.138,2851,5.773,3644,2.839,4474,5.652,5015,6.277]],["t/3094",[28,1.389,56,0.972,107,2.52,109,1.659,304,2.074,527,2.152,755,4.432,1245,3.256,1737,3.711,1863,4.117,1870,3.425,2851,5.645,3644,3.625,5015,8.014]],["t/3096",[7,0.66,16,1.096,24,0.954,25,1.886,27,1.988,28,1.375,31,2.046,34,1.5,41,2.672,56,0.827,84,1.056,107,2.496,109,1.892,113,2.148,123,2.854,127,3.679,131,2.672,132,2.863,162,2.07,165,1.319,171,1.6,193,2.142,213,2.397,225,2.105,256,2.204,295,2.368,302,2.712,304,1.241,318,1.434,377,1.317,433,3.4,472,2.868,491,1.775,524,3.625,536,1.754,557,2.931,565,3.183,614,1.794,615,3.446,628,3.042,630,3.093,635,2.137,810,2.615,865,4.696,995,4.009,999,3.203,1169,3.746,1209,5.185,1303,3.518,1314,4.009,1397,3.57,1477,4.382,1737,2.221,1754,4.513,1807,4.696,1870,2.05,1875,4.908,1980,5.832,1998,3.423,2787,3.746,3644,2.17,5016,7.938]],["t/3098",[7,0.731,16,1.268,27,2.501,28,1.596,34,1.735,41,3.091,56,1.04,84,1.004,107,2.373,109,1.994,113,2.484,127,4.002,131,3.091,132,2.722,165,1.526,193,2.478,225,2.435,256,2.549,302,3.137,304,1.436,313,1.664,318,1.659,338,1.676,433,3.933,472,3.317,524,4.193,536,2.029,557,3.39,565,3.026,615,3.986,630,2.516,635,2.472,995,4.637,1169,4.334,1303,4.07,1314,4.637,1737,2.569,1754,4.478,1875,5.677,1980,6.454,2787,4.334,3644,2.51,5016,5.548]],["t/3100",[28,1.424,56,0.997,109,1.702,127,3.311,304,2.127,755,4.547,1737,3.807,1870,3.514,2851,5.791,3644,3.719,5053,9.195]],["t/3102",[28,1.4,56,0.98,107,2.541,109,1.987,172,3.438,304,2.091,318,2.416,338,2.441,502,2.737,1737,3.743,3644,3.656,5017,8.486]],["t/3104",[28,1.424,56,0.997,172,3.497,254,3.246,304,2.127,338,2.483,502,2.784,1737,3.807,1870,3.514,3644,3.719,5018,9.195]],["t/3106",[28,1.412,56,0.988,109,1.688,172,3.467,304,2.109,502,2.76,755,4.508,1737,3.775,1870,3.484,2851,5.742,3644,3.687,5054,9.117]],["t/3108",[28,1.4,56,0.98,107,2.541,109,1.987,254,3.191,304,2.091,338,2.441,502,2.737,997,3.675,1737,3.743,3644,3.656,5019,8.486]],["t/3110",[28,1.424,56,0.997,254,3.246,304,2.127,338,2.483,502,2.784,997,3.738,1737,3.807,1870,3.514,3644,3.719,5020,9.195]],["t/3112",[28,1.412,56,0.988,109,1.688,304,2.109,502,2.76,755,4.508,997,3.706,1737,3.775,1870,3.484,2851,5.742,3644,3.687,5055,9.117]],["t/3114",[16,1.651,28,1.252,84,0.961,107,2.818,109,1.856,170,2.622,196,3.517,246,3.355,304,1.87,338,2.183,405,2.823,408,3.974,527,2.407,614,1.634,880,4.74,1237,5.976,1737,3.347,1870,3.088,2800,7.076,3644,3.269,3879,6.603,3880,5.8]],["t/3116",[2,1.505,7,0.746,12,2.251,16,1.039,28,1.137,41,2.532,56,1.165,84,1.122,107,1.429,109,2.076,132,2.367,170,2.381,179,1.868,196,2.212,203,1.336,225,1.995,226,2.706,246,3.048,257,2.841,300,3.383,304,1.698,322,1.746,338,1.982,377,1.802,395,2.206,408,2.909,447,2.64,605,2.02,614,1.484,705,3.435,709,3.648,765,3.31,818,5.516,883,4.092,914,2.777,1169,3.55,1170,3.373,1266,1.522,1343,6.891,1539,3.72,1718,3.72,1870,4.206,1873,5.219,1882,4.747,1904,7.039,2062,5.084,3817,3.55,4004,4.153,4474,4.092,4804,4.545,4815,8.616,5056,5.581,5057,5.581,5058,5.581,5059,5.581]],["t/3118",[56,0.988,107,2.563,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1408,4.444,1737,3.775,2296,7.339,3644,3.687]],["t/3120",[56,0.988,127,3.282,261,5.072,304,2.109,353,2.729,536,3.527,755,4.508,1408,4.444,1737,3.775,2295,7.448,3644,3.687]],["t/3122",[56,0.98,261,5.029,304,2.091,353,2.706,536,3.509,755,4.47,946,4.57,1408,4.407,1737,3.743,1902,4.733,3644,3.656,5060,8.486]],["t/3124",[34,2.571,107,2.585,170,2.983,304,2.127,536,3.006,552,4.213,755,4.547,1737,3.807,3644,3.719,5577,9.195]],["t/3126",[56,1.118,84,1.007,107,2.38,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,1046,5.296,1266,2.535,1289,3.76,1622,5.965,1792,7.085,2296,6.816,3999,7.948]],["t/3128",[56,1.118,84,1.007,127,3.048,140,3.642,196,3.684,304,1.959,536,3.638,614,1.711,755,4.186,1046,5.296,1266,2.535,1289,3.76,1622,5.965,1792,7.085,2295,6.917,4000,7.948]],["t/3130",[56,1.113,84,0.999,140,3.614,196,3.655,304,1.943,536,3.626,614,1.698,755,4.154,946,4.246,1046,5.271,1266,2.515,1289,3.73,1622,5.918,1792,7.051,1902,4.398,5060,7.885,5061,8.4]],["t/3132",[56,1.113,84,0.999,107,2.361,140,3.614,196,3.655,304,1.943,536,3.626,552,3.264,614,1.698,755,4.154,1046,5.271,1266,2.515,1289,3.73,1622,5.918,1792,7.051,5577,8.4,5578,9.223]],["t/3134",[24,0.781,26,1.791,28,0.681,29,1.683,31,1.179,48,1.957,49,1.084,56,1.113,105,1.509,107,1.853,109,1.628,112,3.586,123,1.644,127,1.582,140,1.89,162,1.695,165,1.622,172,2.508,173,2.581,174,2.152,193,1.755,203,1.733,206,3.124,318,2.116,322,4.121,336,1.963,338,1.187,377,1.079,391,2.031,395,1.906,398,3.401,502,1.33,527,1.055,593,2.379,613,2.181,614,1.778,623,2.31,755,2.173,947,2.456,997,3.575,1033,3.069,1123,5.166,1139,2.581,1170,1.481,1289,1.951,1633,4.094,1801,2.75,1870,3.601,1882,2.842,1898,2.541,1918,4.805,2026,3.703,2297,2.411,2600,4.456,2851,2.767,3652,4.566,3991,4.777,4474,5.308,5008,5.308,5015,5.895,5016,5.895,5017,6.189,5019,6.189,5053,6.593,5054,6.593,5055,6.593,5062,8.689]],["t/3136",[2061,3.73]],["t/3138",[1,2.672,7,0.799,12,1.854,16,0.855,27,1.091,28,0.985,34,1.171,49,1.569,68,1.743,84,0.914,92,3.125,93,4.293,98,2.453,99,2.899,109,1.589,116,1.716,127,1.508,162,1.615,165,1.564,171,2.293,172,4.352,173,3.735,179,1.538,193,1.672,203,1.1,207,2.849,224,2.957,228,1.456,236,1.209,257,2.34,272,2.858,276,4.181,296,1.772,304,0.969,318,1.119,332,3.623,336,1.871,337,2.141,343,1.635,377,1.028,439,3.005,445,2.899,476,2.026,491,1.386,502,1.268,527,1.005,552,3.776,568,1.317,580,1.596,601,2.158,607,2.675,614,0.846,620,3.861,686,3.596,707,2.558,728,5.876,734,3.089,769,1.767,802,1.86,810,2.041,816,2.604,927,3.474,986,2.41,988,3.907,998,2.95,999,2.5,1006,2.352,1013,3.421,1040,3.164,1042,2.766,1066,2.267,1221,3.421,1266,1.254,1362,3.324,1363,3.393,1365,4.366,1397,2.787,1805,3.831,1844,2.808,1870,1.6,1953,3.797,2106,3.931,2118,3.24,2353,2.746,2513,4.187,2905,3.281,3652,2.899,3796,3.533,3857,3.201,3991,3.034,3997,3.034,4050,3.744]],["t/3140",[0,2.096,17,1.435,19,1.617,56,1.245,68,2.751,84,0.629,85,1.257,100,2.596,105,1.135,109,0.612,112,5.389,123,1.237,172,3.35,193,2.112,217,3.31,225,1.297,243,1.985,304,1.224,318,0.884,322,2.593,324,3.075,377,0.812,387,2.528,391,1.528,408,2.096,491,1.75,502,1.001,552,3.594,609,2.373,641,3.025,725,2.956,726,5.42,734,2.569,747,1.491,765,2.153,802,1.468,828,2.421,829,1.912,833,2.558,878,1.548,929,6.583,952,6.263,953,3.668,954,3.87,956,3.306,957,3.025,958,3.795,959,7.96,960,3.196,961,3.306,962,3.996,963,5.288,964,3.306,965,3.662,966,2.743,967,3.306,968,5.288,969,3.306,970,3.306,971,4.616,972,3.306,973,2.528,974,3.306,975,3.306,976,3.306,977,3.306,978,2.445,979,2.396,980,3.996,981,2.252,982,3.306,983,3.306,984,3.306,985,3.306,986,5.069,987,3.306,988,5.904,989,3.306,990,3.306,991,3.306,1009,4.453,1123,4.143,1363,2.091,1367,2.815,1417,2.396,1737,3.127,2438,7.093,3553,2.743,3643,5.288,4125,9.636,4126,3.104]],["t/3142",[2,1.77,5,0.725,7,0.814,8,1.107,17,0.813,21,1.359,22,1.277,24,0.584,26,1.785,27,1.37,28,1.531,31,1.41,49,1.081,56,0.356,68,1.823,84,0.711,85,0.713,89,1.794,93,0.872,94,1.113,96,0.725,98,0.723,103,0.688,105,1.127,106,2.116,107,2.114,109,1.462,116,0.768,124,1.298,126,0.993,132,0.604,134,2.392,135,1.173,138,2.328,140,1.412,144,0.985,153,1.843,163,0.816,165,1.293,169,1.125,170,1.421,171,1.306,172,2.86,173,5.158,174,1.429,179,1.206,184,1.555,185,1.416,189,1.928,190,2.471,192,1.212,193,3.827,203,0.863,224,3.059,228,1.142,232,0.748,236,1.518,256,0.77,275,0.637,288,2.378,289,2.593,292,0.895,296,0.793,304,0.434,313,1.41,317,1.028,318,0.877,322,1.127,323,1.453,336,0.837,337,1.678,338,0.886,339,1.038,340,1.372,343,1.282,347,1.358,351,0.745,372,1.348,377,0.46,383,1.433,391,2.024,395,1.424,401,0.786,426,2.132,433,1.188,441,1.119,458,1.159,491,0.62,502,0.994,505,0.853,509,1.166,513,0.97,541,1.266,550,2.268,552,3.069,553,2.546,555,0.973,557,1.024,558,0.92,565,1.885,568,1.879,597,1.063,601,1.692,610,1.581,611,1.498,614,1.329,621,1.058,622,1.038,630,0.76,632,5.033,635,1.744,636,1.159,638,2.168,647,0.772,686,1.61,691,1.982,712,1.107,719,1.238,725,1.048,741,1.332,743,2.378,756,0.966,757,0.507,769,0.791,772,4.331,774,1.113,802,0.832,806,1.61,807,2.536,810,1.6,812,2.402,816,1.166,822,1.229,827,2.436,828,1.503,829,1.084,857,0.933,877,2.105,878,0.877,880,0.886,906,2.184,907,1.32,919,1.416,934,0.981,947,1.048,949,1.531,951,1.32,952,2.334,986,3.785,988,3.673,999,1.96,1008,1.247,1009,0.947,1012,4.913,1055,1.358,1066,1.015,1074,1.555,1122,1.287,1123,1.468,1164,1.818,1169,1.309,1223,1.166,1274,2.201,1290,1.145,1303,2.153,1360,1.173,1362,2.606,1363,1.298,1365,1.287,1376,2.403,1379,1.581,1397,1.247,1499,1.953,1557,2.236,1605,1.509,1696,1.309,1699,2.236,1703,3.081,1737,1.359,1782,2.6,1864,2.533,1870,2.009,1898,1.084,1915,1.676,2092,1.152,2353,1.229,2488,2.914,2645,1.715,2687,1.812,2782,1.358,2787,1.309,2905,1.468,2963,1.468,2996,1.759,3019,1.759,3040,1.759,3644,1.328,3646,3.282,3652,1.298,3991,1.358,4127,1.953,5008,1.509,5129,2.058]],["t/3144",[2061,3.73]],["t/3146",[7,0.811,15,3.276,16,1.211,21,1.84,24,1.455,26,2.417,28,0.919,34,1.658,58,4.708,70,7.343,84,0.973,98,2.288,99,4.106,106,3.293,145,3.613,158,6.016,164,1.788,175,5.627,203,1.558,242,4.588,246,2.462,304,1.372,343,3.196,353,2.45,381,3.08,401,4.6,402,5.093,408,4.202,410,4.297,412,4.844,413,4.92,416,2.998,453,4.178,532,3.395,536,1.939,572,3.447,605,2.356,744,3.711,794,4.533,854,3.465,857,2.953,877,2.374,953,2.633,1408,2.891,3020,4.708,3106,5.093,4103,6.178,4104,6.178]],["t/3148",[0,3.177,2,1.484,7,0.74,16,1.484,24,1.292,28,0.776,33,3.137,34,1.401,41,2.496,56,0.927,84,0.596,97,3.567,98,1.934,131,2.496,134,2.738,138,4.595,153,2.815,162,1.934,175,3.009,220,3.387,221,2.032,245,3.287,304,1.681,316,2.452,332,4.14,353,2.559,359,2.426,365,5.536,394,2.75,396,6.24,403,3.531,407,3.606,408,2.88,409,2.788,438,5.912,439,5.214,440,3.517,441,5.942,442,4.585,443,3.878,444,2.385,445,3.471,446,3.5,447,2.603,448,5.012,449,4.91,450,7.266,451,4.585,452,3.787,453,5.119,454,5.012,459,4.304,484,2.331,536,1.639,580,2.77,614,1.013,637,3.531,742,4.989,920,3.198,938,4.304,959,3.668,981,3.414,1314,3.746,1399,3.241,2874,4.844,4105,4.481,4106,5.222,4107,4.705,4108,4.304]],["t/3150",[7,0.653,16,1.08,21,1.639,24,1.342,26,2.154,28,0.819,29,2.024,31,1.417,44,4.961,56,0.818,84,0.898,97,3.706,105,1.815,132,1.704,138,4.253,153,2.968,175,3.173,189,3.105,199,2.862,224,4.468,239,3.328,294,3.121,340,3.867,353,2.636,365,4.199,377,1.298,394,2.9,402,7.562,403,3.723,405,2.636,438,4.301,439,5.417,441,4.507,442,4.835,443,4.089,451,4.835,452,3.993,455,8.587,456,10.159,466,4.089,496,3.544,504,3.517,557,2.887,614,1.068,742,5.183,744,3.307,757,1.43,778,3.659,807,2.242,898,2.813,902,4.04,981,3.6,1399,3.418,1632,3.011,1998,3.372,3479,5.285,4105,4.725,4107,4.961,4108,4.539,4109,5.506,4110,4.385,4111,4.961,4112,5.506,4113,7.864,4114,5.506,4115,5.506,4116,5.506,4117,5.506]],["t/3152",[5,3.3,24,1.843,49,2.106,70,6.687,106,4.172,286,5.679,304,1.974,351,3.391,404,4.562,408,4.109,528,4.936,536,3.39,621,4.816,744,5.341,804,8.011,4118,8.534]],["t/3154",[2,2.375,24,1.774,34,2.79,106,3.228,126,4.252,175,4.817,254,3.523,256,3.295,304,1.856,353,2.402,408,4.505,409,4.463,447,4.167,460,8.022,461,4.929,462,8.022,463,5.299,464,7.53,465,6.132,466,6.206,629,5.602,2784,7.339]],["t/3156",[2,1.892,7,0.843,16,1.306,24,1.136,49,2.404,84,1.024,105,2.195,106,2.571,121,4.105,153,3.589,197,3.126,228,2.223,236,1.845,278,2.514,291,4.133,302,3.231,336,2.855,372,3.538,377,1.569,408,3.413,449,4.319,461,3.927,478,4.253,532,3.659,578,3.716,596,3.16,605,2.539,614,1.292,670,4.078,731,3.678,744,4,764,4.725,828,2.926,898,3.402,1006,4.837,1133,6.157,1312,2.578,1441,4.222,1801,4,1898,3.696,1945,4.677,2232,4.945,3012,5.847,3175,5.999,4105,5.714,4478,6.391,4918,6.391,5064,6.177,5065,7.017,5066,7.017]],["t/3158",[2061,3.73]],["t/3160",[7,0.958,15,3.812,572,5.536,846,5.394,3726,6.77]],["t/3162",[4,2.489,7,0.814,27,1.905,49,2.322,56,0.792,84,0.869,100,3.588,103,3.456,144,3.842,236,2.11,243,4.388,283,2.32,394,4.011,401,4.367,405,3.286,437,3.766,481,4.793,492,6.277,511,2.558,683,3.002,723,2.868,736,3.422,877,2.926,2225,5.499,2297,2.673,3513,8.303,3530,5.726,3531,5.726,3532,6.399,3726,6.689]],["t/3164",[7,0.889,27,2.189,49,2.073,56,0.911,100,4.124,243,5.044,246,3.487,277,4.74,401,3.525,405,3.586,511,2.94,723,3.296,841,4.262,934,4.398,1754,3.92,3533,6.086,3726,7.3,4015,10.698]],["t/3166",[7,0.761,17,2.858,27,1.717,39,2.943,56,0.714,84,0.783,100,4.856,103,3.229,106,2.651,144,3.463,228,2.291,243,3.956,394,3.615,405,3.07,410,4.773,437,3.395,511,2.306,527,1.581,683,2.706,723,3.449,760,3.209,769,2.78,802,2.926,877,2.637,1245,2.393,1376,3.615,1754,3.074,1759,6.642,2225,5.137,2297,3.214,2789,4.923,2814,5.036,2815,5.036,2867,4.231,3483,7.695,3726,6.249,4016,7.963,4017,9.561,4018,6.367,4019,6.367]],["t/3168",[7,0.739,17,2.74,27,1.646,39,2.821,56,0.685,84,0.751,100,4.754,103,3.139,106,2.541,109,1.921,144,3.32,170,2.049,228,2.196,243,3.792,394,3.466,405,2.984,410,4.576,437,3.254,511,2.21,527,1.516,683,2.594,723,3.353,760,3.141,769,2.665,802,2.805,877,2.528,1245,2.294,1320,5.967,1376,3.466,1754,2.947,1759,6.456,2225,4.993,2297,3.125,2789,4.72,2814,4.828,2815,4.828,2867,4.056,3483,7.48,3726,6.074,4016,5.084,4017,9.359,4018,6.104,4019,6.104,5010,8.544]],["t/3170",[7,0.674,10,4.625,21,2.416,24,1.385,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,857,3.879,1754,4.572,2225,4.551,2282,5.699,2297,2.848,3726,6.965,4020,10.207]],["t/3172",[7,0.674,10,4.625,21,2.416,27,2.03,56,0.844,84,1.165,100,3.823,144,4.094,170,2.526,243,4.676,353,2.332,394,4.274,405,3.422,511,2.726,568,2.449,683,4.024,723,3.056,759,2.997,760,2.526,857,3.879,1754,4.572,2225,4.551,2282,5.699,2297,2.848,3726,6.965,5063,10.757]],["t/3174",[2061,3.73]],["t/3176",[7,0.745,39,3.843,49,2.571,84,1.023,228,2.992,278,3.384,401,3.61,723,3.376,1202,4.976,1376,5.717,1511,6.174,2395,4.951,4021,8.603,4022,9.624,4030,8.603]],["t/3178",[7,0.948,34,2.03,49,2.559,56,0.787,84,1.233,103,2.667,203,1.908,228,2.525,278,2.856,283,2.304,382,4.944,393,3.914,401,3.046,408,2.877,513,3.755,580,2.767,614,1.467,723,4.07,1001,6.124,1202,5.418,1376,6.014,1504,5.616,1511,7.443,2395,4.177,2708,5.763,4021,7.259,4022,6.234]],["t/3180",[2,1.463,7,0.622,34,1.382,39,2.208,49,1.775,56,0.78,68,2.994,84,1.107,100,2.427,113,2.879,144,2.599,165,1.216,186,2.557,203,2.87,213,2.208,225,1.94,226,2.631,233,1.964,302,2.499,313,1.326,322,2.47,332,2.816,373,3.924,377,2.082,491,2.806,613,2.453,614,2.087,647,2.036,659,3.514,736,2.314,763,3.694,839,3.113,851,2.844,979,3.581,1008,3.289,1170,2.424,1202,5.973,1266,2.153,1290,3.019,1328,3.241,1367,2.631,1376,2.712,1394,3.617,1395,4.245,1737,2.978,1863,2.27,1870,2.748,1882,3.196,2015,6.927,2027,3.394,2395,5.943,2447,5.375,2636,3.113,2684,3.34,2898,6.58,2931,4.943,3182,4.522,3644,1.999,3857,3.778,3866,2.816,4022,4.245,4023,5.149,4024,5.149,4025,5.149,4026,5.149,4027,5.149,4028,5.149,4029,5.149]],["t/3182",[2,1.915,7,0.946,15,2.589,21,2.007,49,2.418,85,3.302,103,2.376,106,2.603,113,2.589,179,2.376,203,2.282,228,2.249,283,2.756,313,1.735,332,3.685,382,4.406,401,4.586,481,4.242,491,2.873,512,2.448,562,3.487,614,2.209,628,3.667,723,2.538,736,3.028,820,3.65,833,5.004,854,3.78,953,2.873,1202,3.741,1565,4.944,1633,4.492,2225,3.78,2297,3.175,2395,3.722,3513,4.888,3530,5.068,3531,5.068,4022,5.555,4030,6.468]],["t/3184",[7,0.81,49,2.309,283,2.97,401,3.926,512,3.542,736,4.38,833,7.239,3530,7.33,3531,7.33]],["t/3186",[7,0.853,16,1.338,24,1.164,28,1.014,34,1.831,49,1.616,56,0.71,68,2.725,84,1.252,105,3.386,162,2.526,219,2.225,246,2.718,256,2.69,261,4.87,285,2.97,295,2.891,318,2.636,353,2.621,377,1.608,398,2.532,401,2.748,512,2.478,536,2.141,614,1.323,734,3.181,759,3.795,828,2.998,1266,1.96,1284,3.515,1312,2.641,1408,3.192,1487,4.792,1918,4.782,2026,4.916,2027,6.011,2297,2.394,2600,5.915,2623,5.433,3514,5.524,4032,6.547]],["t/3188",[7,0.822,49,1.828,56,1.197,84,0.881,140,3.188,283,2.352,318,1.981,336,3.31,377,1.819,393,3.995,398,2.866,401,3.11,568,2.33,683,3.044,736,3.469,1202,4.286,1266,2.218,1289,3.291,1381,3.224,1632,5.409,1918,4.048,2027,5.088,2481,8.006,2600,5.007,2623,6.148,3531,5.806,3532,8.311,4033,7.72,4034,6.956,4035,6.956,4036,7.72]],["t/3190",[49,2.089,84,1.007,161,6.327,201,4.732,283,2.688,318,2.263,398,3.274,401,3.553,736,3.964,1266,2.535,1387,7.948,1918,4.625,2027,5.814,2481,6.472,2487,7.948,3530,8.084,3857,6.472,4034,7.948,4035,7.948,4037,8.821]],["t/3192",[7,0.824,15,3.812,49,2.35,279,4.517,395,4.132,614,1.925,828,4.36]],["t/3194",[2,1.491,7,0.436,17,2.185,23,2.672,24,1.772,28,0.78,48,2.243,49,1.799,56,1.162,84,0.867,98,1.943,113,2.016,162,2.813,165,1.793,171,1.502,174,1.643,203,2.252,219,1.711,228,2.536,232,2.011,246,3.558,248,2.595,272,2.263,313,1.956,338,2.537,377,1.236,433,3.192,524,3.403,530,2.472,536,1.647,545,2.555,568,1.583,614,1.018,705,5.791,757,2.318,831,3.487,852,2.397,914,2.751,930,6.436,1114,3.631,1266,1.508,1289,2.236,1312,2.031,1381,2.191,1486,4.054,1876,3.58,2027,7.137,2032,3.37,2034,5.036,2075,4.607,2623,6.05,2754,4.409,3047,4.607,3655,4.054,3866,4.883,4038,4.727,4039,5.247,4040,7.596,4041,5.247,4042,5.247,4043,5.247,4044,5.247]],["t/3196",[7,0.796,56,0.997,304,2.127,397,4.197,580,3.505,651,6.729,898,4.895,1266,2.753,1285,5.684,1399,7.011]],["t/3198",[7,0.832,304,2.223,438,5.475,651,7.032,1266,2.877,2025,7.973]],["t/3200",[84,1.057,92,3.614,93,4.942,200,4.835,207,4.761,261,4.945,304,2.057,313,2.384,353,2.661,614,1.797,620,5.399,934,4.654,1266,2.661]],["t/3202",[16,1.895,68,3.861,85,3.527,165,2.281,304,2.146,317,5.09,338,2.505,614,1.875,1801,5.805,2103,7.825]],["t/3204",[5,2.947,21,2.364,28,1.644,36,3.69,56,0.826,96,2.947,98,2.94,106,4.27,107,2.142,109,1.789,170,3.135,186,3.943,203,2.003,256,3.97,296,3.225,304,1.763,343,2.976,368,6.324,404,4.074,513,3.943,568,2.396,606,6.43,611,3.478,694,4.551,774,4.526,1066,4.127,3106,6.545,4045,6.814]],["t/3206",[109,1.763,203,2.503,304,2.203,404,5.091,1170,3.21,1705,6.385,3968,8.939]],["t/3208",[7,0.925,15,2.721,21,2.784,24,1.786,26,2.77,36,3.291,46,5.055,56,1.089,90,3.517,98,2.622,100,3.337,106,2.735,112,4.881,144,3.574,183,3.203,186,3.517,203,1.786,217,4.254,275,2.31,279,3.224,304,2.076,359,3.291,404,3.634,408,2.694,527,2.565,607,2.86,622,3.764,719,4.49,856,4.458,1133,4.015,1198,6.57,1245,2.469,1367,3.618,4045,6.077,4046,7.082]],["t/3210",[7,0.592,24,1.602,28,1.06,46,3.112,56,0.742,68,2.848,70,5.36,106,2.753,145,3.02,165,1.682,186,3.539,196,2.977,203,1.798,206,4.864,219,2.325,232,2.732,277,3.86,289,2.968,292,3.267,304,1.583,336,3.056,343,2.671,408,2.711,491,2.264,534,5.36,567,3.011,596,3.383,614,1.383,757,1.851,852,3.256,891,5.058,1117,4.062,1168,6.116,1242,5.989,1362,5.431,1367,4.797,1390,5.36,1473,3.347,1632,5.135,1782,3.383,1911,5.588,2231,5.431,2930,5.875,4233,5.989,5131,9.895,5132,7.511,5133,7.511]],["t/3212",[7,0.604,24,1.623,31,2.448,36,3.376,39,3.116,46,3.173,84,0.829,106,2.806,123,3.415,145,3.079,160,2.684,186,3.608,196,3.035,203,1.833,206,4.959,219,2.37,232,3.645,289,3.026,292,3.331,304,2.112,404,3.728,408,3.618,491,2.308,511,2.441,528,4.034,567,3.07,596,3.449,749,4.959,757,2.469,798,4.212,852,3.32,936,3.276,1240,4.365,1312,2.814,1367,3.713,1782,3.449,2509,5.615,4167,6.741,4314,6.381,5134,10.022]],["t/3214",[24,1.594,27,2.336,28,1.389,126,4.752,128,4.693,304,2.47,367,5.01,404,4.792,650,6.628,825,7.562,3401,7.438,4047,9.339,4048,9.339]],["t/3216",[7,0.762,16,1.08,21,2.98,28,1.574,32,3.629,39,3.934,56,0.818,68,3.142,100,2.594,107,1.485,127,1.903,165,1.3,181,3.287,183,2.49,197,2.585,203,1.984,207,2.368,223,3.267,253,3.491,304,1.746,315,2.756,316,2.585,336,2.361,337,2.702,339,2.927,343,3.967,379,6.906,404,2.825,430,2.862,477,3.441,480,3.949,482,2.594,502,1.6,508,3.418,555,2.745,577,4.989,613,3.746,614,1.526,623,2.778,635,2.105,690,3.6,694,3.156,723,3.769,747,3.403,757,2.042,778,3.659,936,2.482,997,2.148,1046,2.713,1164,2.927,1180,6.651,1367,2.813,1473,2.585,1693,3.394,1865,4.725,1900,3.907,2522,4.458,3042,3.394,3401,4.385,4049,9.174,4050,4.725,4051,5.285]],["t/3218",[5,3.223,7,0.721,24,1.817,28,1.291,31,2.235,68,3.469,84,0.991,112,4.533,165,2.05,169,5.004,254,2.942,285,3.78,304,2.364,338,2.251,403,5.872,404,4.455,528,4.821,614,1.685,778,5.771,1381,3.627,1689,4.796]],["t/3220",[2,1.374,7,0.708,17,2.014,23,1.701,24,1.715,28,1.267,31,1.245,56,0.98,84,1.147,85,1.765,123,1.737,131,2.312,140,1.997,164,1.4,165,1.142,197,2.271,203,2.375,219,2.779,228,2.389,232,1.854,254,1.639,275,1.578,281,2.411,285,4.97,286,3.089,304,1.074,342,3.331,377,1.14,409,3.821,491,1.536,505,2.112,513,2.402,527,2.424,536,3.417,555,2.411,565,1.664,568,1.459,578,2.003,580,1.77,596,2.295,623,2.44,628,2.632,757,2.445,759,1.787,764,3.432,769,1.959,804,4.358,827,2.583,1114,2.312,1170,1.565,1181,2.645,1245,3.504,1263,3.089,1266,1.39,1280,3.432,1408,2.263,1435,3.432,1540,2.819,1813,3.737,1863,2.132,1990,3.508,2032,4.667,2297,1.698,2312,2.727,2837,3.792,2930,3.987,3972,4.642,5064,4.487,5457,4.836,5579,8.126,5580,5.492]],["t/3222",[7,0.705,24,1.945,28,1.262,109,1.508,113,3.26,162,3.887,165,2.003,171,3.005,304,1.884,381,4.23,527,1.955,724,4.354,759,3.134,760,2.642,1046,4.18,1238,5.341,1245,2.958,1737,3.372,1863,3.74,2803,8.144,2851,6.345]],["t/3224",[7,0.868,24,1.783,41,4.026,56,0.876,113,3.236,139,3.982,203,2.124,236,2.333,239,5.09,289,3.507,304,1.87,367,4.518,430,4.377,527,1.94,614,1.634,662,5.157,759,3.111,760,2.622,1240,5.058,1737,3.347,1754,4.679,1863,3.712,1902,4.232,2851,5.09]],["t/3226",[7,0.956,15,3.337,20,7.017,21,2.586,29,3.192,50,5.353,84,0.991,106,3.354,116,3.415,304,2.364,408,4.05,580,3.177,614,1.685,796,4.796,1367,4.437,5135,9.151,5136,9.151,5137,9.151]],["t/3228",[7,0.839,85,3.687,278,3.814,580,3.696,1312,3.911]],["t/3230",[8,4.506,16,1.096,23,2.796,56,0.827,96,2.074,140,2.308,177,2.844,179,1.971,219,3.017,275,1.823,304,2.665,318,2.039,366,3.684,393,2.892,438,5.51,447,2.786,500,2.786,545,2.722,548,6,552,2.965,555,2.786,558,4.748,614,1.084,802,2.382,852,4.602,898,2.856,902,4.101,948,5.475,1014,5.424,1059,3.296,1170,2.571,1312,4.582,1531,3.926,1638,3.042,2025,4.451,2253,4.796,2280,4.607,2299,4.796,2895,3.625,3042,7.016,4052,5.589,4053,7.948,4054,4.796,4055,5.589,4056,6.33,4057,5.036,5138,5.89,5139,5.89]],["t/3232",[113,3.417,248,5.755,275,2.9,304,1.974,353,2.555,484,3.97,854,4.987,916,5.445,1043,6.363,1044,7.222,1312,3.443,1638,4.839,1816,6.972,2624,7.329]],["t/3234",[7,0.818,34,2.058,56,1.132,84,0.875,85,3.594,97,3.613,224,3.423,254,2.598,322,2.527,353,2.203,482,3.613,495,3.901,572,4.278,580,2.805,611,3.359,614,1.487,709,5.281,762,5.441,839,4.635,893,4.665,1046,4.851,1312,3.812,1408,4.607,1633,3.807,2061,2.728,2481,8.71,4058,7.667,4059,7.359,4060,7.667]],["t/3236",[7,0.727,84,0.999,171,2.506,246,3.487,304,1.943,491,2.78,580,3.914,614,1.698,723,3.296,1312,3.389,1376,4.61,1511,6.028,1638,4.763,1737,3.478,2225,4.909,2297,3.072,3644,3.397,3858,9.437]],["t/3238",[7,0.71,16,1.677,84,0.976,171,2.448,219,2.789,304,1.899,318,2.194,491,2.716,580,3.859,614,1.659,723,3.22,1044,6.546,1376,4.503,1511,5.889,1638,4.653,1737,3.398,2225,4.796,2297,3.001,2906,11.939,3644,3.319]],["t/3240",[2,1.345,7,0.393,15,1.819,16,1.649,36,2.199,48,2.024,49,1.992,56,0.875,84,1.234,133,2.134,160,1.749,165,1.117,179,1.669,197,2.223,219,2.297,224,2.113,246,2.807,248,4.161,255,3.359,275,2.297,302,2.297,313,1.813,317,2.493,318,2.678,377,1.66,438,4.6,441,2.713,495,4.74,572,2.641,580,1.732,614,0.918,655,3.26,724,2.429,736,2.127,744,2.843,757,1.229,802,2.018,891,6.612,898,2.418,938,3.902,940,2.777,1043,4.6,1044,4.372,1133,2.683,1280,3.359,1312,4.596,1380,4.681,1399,5.221,1441,3.001,1898,2.628,1944,4.157,1964,3.395,2061,1.684,2289,4.157,2299,4.062,2307,5.168,2481,6.172,2683,4.568,2726,3.56,2906,8.412,3749,3.473,3858,7.305,4061,4.734,4062,4.734,4063,4.734,4064,4.734,4065,4.734,4066,4.734,4067,7.043,4068,4.734]],["t/3242",[23,3.231,203,2.779,318,2.357,322,3.631,614,2.137,1312,4.265,3042,6.791,3644,4.276,4069,9.185,4070,9.185]],["t/3244",[2,1.52,7,0.444,16,1.049,23,2.709,56,0.556,219,3.221,246,2.131,278,2.019,283,1.629,304,2.701,313,1.377,318,1.976,545,2.604,548,3.468,552,2.872,614,1.037,756,6.016,852,4.123,891,3.795,940,2.108,1014,5.255,1043,2.924,1059,7.098,1223,3.192,1312,2.982,1787,4.961,1882,7.549,2253,4.589,2683,3.468,3858,6.038,4057,4.818,4071,5.347,4072,4.961,4073,5.347,4074,5.347,4075,5.347,4076,5.347,4077,5.347,4078,5.347,4079,5.347,4080,5.347,4081,5.347,4082,5.347,4083,5.347,5140,5.635,5141,5.347,5142,5.635,5143,5.347,5581,6.072]],["t/3246",[2,2.093,7,0.797,24,1.256,25,2.484,28,1.095,35,5.402,39,4.113,49,2.272,84,0.84,109,1.308,116,2.895,197,3.457,304,1.635,313,1.896,336,3.157,391,3.266,404,3.778,505,3.215,614,1.861,635,2.815,743,6.671,760,2.292,802,3.138,830,3.546,1048,6.187,1049,6.187,1051,5.962,1052,7.213,1671,4.267,1870,2.7,1918,3.861,2004,7.067,2668,5.468,3007,5.962,3055,6.318,3691,7.067,4084,9.592,4085,7.363,4086,7.363]],["t/3249",[7,0.8,10,4.225,17,3.087,24,1.265,28,1.102,31,1.908,84,1.1,102,4.926,123,2.662,133,3.341,162,2.745,172,2.706,246,2.953,257,3.976,285,3.227,304,1.646,318,1.902,336,3.178,338,1.921,375,5.899,393,3.836,470,5.648,502,2.154,527,1.708,568,2.237,614,1.438,690,4.846,759,2.738,760,2.308,865,6.228,874,3.836,880,3.363,997,2.892,1046,4.746,1055,5.155,1733,4.666,1737,2.946,1807,6.228,3042,4.569,3644,2.877,4087,7.412]],["t/3251",[7,0.836,10,4.526,24,1.355,48,3.395,56,0.826,84,0.906,139,3.755,170,2.472,171,2.273,213,3.405,223,4.711,304,1.763,409,4.24,580,2.905,614,1.54,646,8.368,683,3.131,734,3.702,736,3.568,759,3.719,760,3.442,1539,5.577,1695,7.366,1737,3.156,1874,5.826,2922,6.814,3644,3.082,3655,6.135,4032,7.621,5582,8.368,5583,9.016]],["t/3253",[5,2.715,7,0.608,16,1.434,25,3.222,84,1.287,162,2.708,164,2.117,171,2.734,219,2.386,236,2.027,304,1.624,318,2.892,377,1.724,398,2.715,614,2.063,683,2.884,723,3.597,724,3.753,731,4.04,757,2.48,886,4.903,945,4.02,1046,4.705,1133,4.147,1284,3.769,1312,3.698,1629,5.367,1737,2.907,1871,6.277,2032,3.245,3644,2.839,3749,5.367,4088,6.59]],["t/3255",[7,0.684,49,1.95,84,1.176,171,2.357,192,5.111,248,4.073,285,4.485,304,1.828,336,3.531,536,2.584,605,3.14,614,1.597,662,5.043,731,4.548,1043,6.148,1046,4.057,1127,7.639,1133,4.668,1399,6.979,1540,4.8,1737,3.272,3644,3.196,3749,6.041,3754,7.419]],["t/3257",[7,0.764,31,2.661,35,5.067,37,4.851,39,4.433,56,1.147,63,5.592,68,2.759,84,1.05,95,4.515,100,3.254,109,1.227,203,2.32,236,1.914,283,2.104,377,2.436,481,4.347,614,1.784,734,3.22,1129,4.552,1266,1.984,1320,4.629,1595,8.826,1689,5.71,1754,3.093,2112,5.803,2497,5.693,2550,5.263,2708,5.263,2789,4.954,3412,5.693,3413,5.263,3652,4.59,3994,5.926,4089,6.906,4090,6.906,4091,6.906,4092,6.906,4093,6.906,4094,6.906]],["t/3259",[7,0.872,113,3.26,181,5.065,203,2.648,334,5.065,491,2.695,512,3.083,614,1.646,934,4.264,1170,2.745,1202,4.711,1266,2.438,1395,6.994,1642,7.645,1754,3.8,1870,3.85,2015,6.653,2395,4.687,3866,4.64,4095,8.485,4096,7.451,4097,7.451,4098,8.485]],["t/3261",[1,3.783,5,2.293,7,0.811,25,3.294,56,0.887,84,0.973,126,3.143,131,5.281,135,3.711,157,3.759,162,2.288,165,1.458,174,2.671,203,1.558,254,2.889,314,5.191,322,2.036,341,3.581,369,4.039,370,5.093,375,3.783,449,4.007,476,1.89,500,3.08,525,3.068,532,3.395,555,3.08,590,3.502,611,2.706,614,1.199,621,3.346,623,3.118,675,4.141,677,3.43,683,2.436,830,2.975,846,2.891,898,3.157,929,4.432,931,5.003,1181,3.379,1389,6.103,1390,4.646,1435,4.384,1474,5.003,1482,4.92,1527,4.297,1601,3.889,1936,5.732,2297,2.169,2789,4.432,2851,3.735,3042,5.257,3264,5.301,3508,5.425,4099,6.178,4100,5.425,4101,6.178,4102,8.527]],["t/3263",[2061,3.73]],["t/3265",[4,2.189,7,0.944,8,3.797,21,1.995,27,1.675,41,3.202,46,4.962,84,1.028,85,2.445,90,4.474,92,2.614,93,4.022,98,2.48,109,1.19,127,2.315,133,3.02,134,3.512,153,3.611,164,1.939,172,3.289,174,2.098,207,3.875,213,2.872,224,2.99,256,2.641,276,3.497,283,2.745,304,1.487,305,4.49,338,1.736,340,4.705,343,2.511,363,5.037,440,3.112,476,2.049,532,3.681,599,3.628,613,3.191,690,4.379,694,3.839,853,5.335,939,4.345,997,2.614,1434,3.927,1470,5.104,2353,4.217,4373,6.698]],["t/3267",[5,2.268,7,0.703,31,2.178,49,1.447,56,0.636,68,3.878,84,1.256,105,2.014,109,1.725,110,3.392,164,1.769,165,1.442,172,3.543,203,2.134,207,3.639,253,3.874,256,3.336,304,1.357,377,1.44,404,3.135,527,1.95,552,3.156,553,3.41,576,3.793,607,3.418,611,2.677,614,1.185,623,3.083,630,2.378,638,3.874,731,3.375,769,3.427,902,4.483,986,3.375,988,3.604,1042,3.874,1057,4.17,1073,4.656,1129,4.027,1210,4.948,1278,4.292,1689,3.375,2506,5.505,2867,3.767,2892,6.739,3016,6.976,3402,4.866,3468,5.669,4124,5.243,4347,5.505,4374,5.135,5144,5.505,5145,5.669]],["t/3269",[2,2.106,7,0.8,27,2.41,46,4.674,47,4.926,84,1.1,90,3.68,92,4.783,93,3.309,96,2.751,158,3.904,172,2.706,207,3.188,256,2.922,306,3.468,313,1.908,332,4.053,338,1.921,476,2.947,481,4.666,596,3.518,597,4.034,599,5.217,604,5.155,707,4.346,796,4.094,810,3.468,846,3.468,997,2.892,1360,4.452,2118,5.504,3668,6.36]],["t/3271",[5,1.424,7,0.694,16,0.752,17,1.598,28,0.57,29,1.41,31,1.545,49,1.421,56,0.943,68,3.338,84,1.248,89,2.359,92,4.416,93,3.298,105,1.265,106,2.317,109,1.611,110,2.13,144,1.936,165,0.906,172,3.05,177,1.952,199,1.994,203,1.864,207,3.178,253,2.433,256,2.913,278,1.449,283,1.169,297,2.211,304,2.373,306,1.795,322,1.265,372,1.513,377,0.904,398,2.227,404,1.968,476,1.174,505,2.62,527,1.702,552,3.116,553,2.141,576,2.381,607,2.423,611,1.681,614,1.433,623,1.936,630,1.493,638,2.433,707,2.249,731,2.119,747,1.66,755,1.821,769,1.554,877,1.474,898,1.96,899,2.783,900,2.643,902,2.815,940,1.513,953,1.635,986,3.314,988,3.539,999,2.199,1042,2.433,1057,2.618,1129,2.529,1139,2.163,1210,3.107,1263,2.451,1266,2.401,1278,2.695,1367,1.96,1381,1.602,1429,3.055,1446,2.365,1601,2.415,1671,2.224,1689,2.119,1737,2.384,1882,2.381,1898,2.13,1906,2.199,2011,3.292,2287,2.508,2502,3.162,2506,3.457,2867,2.365,2892,5.884,3016,5.298,3204,3.682,3402,3.055,3468,3.559,3644,2.329,3669,3.682,4124,3.292,4374,3.224,4376,3.837,4377,3.837,4378,3.457,4379,3.837,4382,3.559,5144,3.457,5145,3.559,5146,3.837,5147,4.043]],["t/3273",[7,0.606,24,0.847,49,1.176,55,2.846,56,1.216,84,1.16,92,3.376,171,1.422,177,4.402,283,2.223,304,2.359,306,2.323,368,3.955,382,5.656,398,2.709,401,4.278,426,1.933,440,2.307,449,3.221,539,4.935,543,6.303,608,2.716,611,2.175,614,1.971,623,2.506,736,2.231,898,2.537,1114,4.136,1133,2.815,1266,3.155,1597,4.85,1607,5.026,1638,5.781,1698,3.894,1699,3.246,1737,2.9,1816,5.722,1941,4.474,2643,3.643,2922,4.261,3042,3.061,3644,2.833,4100,4.36,4108,4.093,4380,4.966,4381,4.766,5148,5.233,5149,4.766,5150,5.233,5151,5.233,5152,5.233,5153,5.233,5154,5.233,5155,7.004,5156,7.004,5157,5.233,5158,5.233]],["t/3275",[7,0.953,15,3.577,21,1.209,24,1.47,25,1.37,27,1.016,28,1.139,34,1.09,40,2.254,46,4.748,47,4.167,49,0.962,56,0.652,84,1.063,85,3.744,92,3.839,93,1.813,96,2.843,97,4.833,105,2.067,106,2.958,129,2.556,135,2.439,138,1.726,140,1.677,158,3.302,164,1.175,179,2.211,196,1.696,272,1.752,278,1.533,283,1.237,296,1.649,304,0.902,313,1.045,318,1.042,377,0.957,393,2.101,395,2.611,437,3.788,438,5.381,440,1.887,441,2.327,471,1.773,491,1.29,530,1.913,557,3.288,591,2.439,597,2.21,605,1.548,614,0.788,723,1.529,730,3.484,734,1.893,744,2.439,747,3.314,757,1.054,813,5.227,820,2.199,854,2.278,856,2.556,1050,4.279,1055,4.36,1163,4.005,1164,2.158,1202,2.254,1328,2.556,1597,2.699,1688,2.912,1962,3.288,1963,3.094,1964,2.912,1965,3.094,1996,3.484,2025,6.1,2286,6.018,2287,2.654,2364,3.484,2836,3.288,3533,5.327,4383,4.06,4384,6.901,4385,7.659,4386,3.658,4387,4.06,4388,4.06]],["t/3277",[4,1.515,7,0.69,8,2.628,16,0.909,17,1.93,21,1.38,29,1.704,46,2.024,49,1.642,56,1.078,84,0.791,85,1.692,92,4.404,93,3.096,97,3.267,108,3.773,160,1.712,164,1.342,196,1.936,203,1.169,217,2.784,228,1.547,275,1.512,283,1.412,304,1.54,365,2.475,372,1.827,401,1.867,434,4.87,438,2.535,482,2.184,500,2.311,527,1.068,579,3.532,580,1.696,591,2.784,596,2.2,613,2.208,614,1.612,680,3.821,713,4.07,714,8.561,751,2.82,774,2.642,780,2.6,796,2.56,813,3.163,830,2.232,854,2.6,888,4.304,898,2.368,1009,2.249,1170,1.499,1263,2.961,1266,2.649,1371,7.466,1482,3.691,1601,2.918,1705,2.983,1733,2.918,2033,2.75,2297,2.434,2412,4.449,2668,5.149,2739,6.247,3111,3.895,3412,3.821,3533,3.223,3668,3.977,3710,7.973,3712,7.973,3977,3.532,4378,4.176,4389,4.635,4390,4.635,4391,4.635,4392,9.22,4393,4.635,4394,4.635,4395,4.635,4396,4.449,4397,4.635,4398,6.934]],["t/3279",[7,0.416,24,0.855,48,2.142,49,1.187,55,2.872,56,1.219,84,1.216,92,3.393,171,1.434,177,4.425,283,2.238,304,2.366,368,3.99,382,5.685,398,2.726,401,4.292,426,1.95,449,3.25,539,4.967,543,6.323,608,2.74,614,1.978,623,2.528,714,3.676,736,2.251,898,2.56,1114,4.157,1266,3.164,1597,4.882,1607,5.059,1638,5.799,1698,3.929,1699,3.276,1737,2.919,1816,5.76,1941,4.514,2643,3.676,2922,4.299,3042,3.089,3644,2.851,4100,4.4,4108,4.13,5149,4.809,5155,7.05,5156,7.05,5159,5.28,5160,5.28,5161,5.28,5162,5.28,5163,5.28,5164,5.28,5165,5.28,5166,5.28,5167,5.28,5168,5.28]],["t/3281",[7,0.784,16,1.408,25,2.882,49,1.7,84,0.975,92,4.112,100,3.383,108,3.907,112,3.748,164,1.406,165,1.695,198,4.382,213,2.083,246,4.699,292,2.226,348,3.285,377,1.145,388,3.701,397,2.128,438,3.926,440,2.257,441,4.115,443,8.312,544,3.201,562,2.514,614,1.657,651,3.412,713,4.265,714,5.268,898,2.482,1035,8.285,1046,2.393,1181,2.656,1266,2.454,1285,2.882,1371,3.933,1408,2.273,1428,4.506,1633,2.412,1687,4.265,1799,3.523,1998,2.975,2033,2.882,2497,5.918,2836,3.933,3175,7.695,3176,4.265,3177,4.265,3178,9.056,3179,6.891,3180,6.891,3181,6.891,3182,6.305,3183,4.662,3184,4.662,3185,4.662,3186,4.662,3187,4.662,3188,4.662,3189,4.662,3190,4.662,3191,4.662,3192,4.662,3533,3.378,4384,4.377,4399,4.857,4400,4.857]],["t/3283",[2,1.29,7,0.854,8,2.574,15,2.623,16,0.89,17,1.891,21,1.352,29,1.669,34,1.219,49,1.617,50,2.799,56,1.142,84,1.117,85,2.492,90,2.255,92,4.688,93,3.048,94,5.859,96,2.534,97,3.866,102,3.018,106,2.637,108,2.471,109,0.807,116,2.685,125,2.727,131,2.171,164,1.314,171,1.3,174,1.422,181,2.711,186,2.255,196,1.896,275,1.481,283,2.08,313,2.112,316,2.132,323,1.93,377,1.07,390,2.31,395,1.891,398,1.685,407,2.163,438,3.734,491,1.442,526,3.46,562,2.35,568,1.37,593,3.549,597,2.471,611,1.989,694,2.602,729,3.415,850,3.127,893,2.762,898,2.32,940,1.79,950,3.331,1263,2.9,1429,5.437,1433,3.815,1434,4.003,1531,3.189,1597,3.018,1601,2.858,1633,2.255,1866,3.815,2011,5.859,2241,3.896,2287,4.463,2297,2.396,2502,3.743,3111,3.815,3176,3.987,3177,3.987,3344,3.896,3629,4.5,3977,3.46,4401,4.541,4402,4.541,4403,4.541,4404,4.541,4405,4.541,4406,4.541,4407,4.091,4408,4.541]],["t/3285",[7,0.776,15,1.827,21,1.415,27,1.189,33,2.856,34,1.276,46,4.566,56,1.183,84,0.963,85,1.735,90,2.361,92,4.225,94,4.027,96,3.13,98,1.76,101,2.666,113,1.827,133,2.143,140,1.963,164,2.045,179,2.973,203,1.199,208,4.563,238,2.874,240,2.74,254,1.611,275,2.305,278,2.668,351,1.813,353,2.03,372,1.874,377,1.12,407,2.264,437,2.351,440,3.918,476,2.161,482,2.24,491,2.244,562,2.46,566,3.488,583,3.06,597,3.845,613,3.366,614,0.922,715,6.264,828,2.089,1006,2.562,1202,3.923,1266,2.03,1290,2.787,1429,3.786,1434,2.787,1479,4.563,1633,2.361,1866,3.995,1999,6.782,2009,3.448,2395,3.903,2457,4.41,3629,3.133,3708,8.094,3857,3.488,3866,4.612,3977,3.622,4407,4.283,4409,7.066,4410,4.754,4411,7.066,4412,4.754,4413,4.754,4414,4.754]],["t/3287",[2,1.727,7,0.912,8,4.779,10,6.259,16,1.653,56,0.877,84,0.962,85,2.218,89,2.39,92,4.285,93,2.713,138,3.583,217,5.064,236,1.684,275,3.706,281,3.03,283,1.852,292,3.864,313,1.564,323,3.583,365,3.245,476,1.859,504,5.385,597,3.307,614,1.635,623,4.254,670,3.722,680,6.949,707,3.563,715,3.291,900,5.806,998,4.109,1014,4.147,1046,2.994,1366,5.675,1483,5.336,1504,6.26,1945,4.268,2918,6.077,5146,6.077,5169,6.404,5170,10.2,5584,6.9]],["t/3289",[7,0.796,8,2.16,15,2.826,17,3.467,18,3.406,21,1.135,24,0.65,26,2.878,28,0.567,34,1.974,46,1.664,48,1.629,56,0.765,84,0.681,90,1.892,92,2.328,93,4.473,94,3.401,96,1.414,97,3.922,103,1.343,132,1.847,165,0.899,172,1.391,181,2.275,183,1.723,185,4.328,186,1.892,201,2.044,207,1.639,213,1.634,225,1.435,228,1.992,236,2.306,244,3.871,278,1.439,283,1.161,291,2.365,292,1.746,298,2.452,306,1.783,313,0.981,315,1.907,323,1.619,338,0.988,353,1.095,369,2.491,375,2.333,377,0.898,381,2.975,393,1.972,395,1.587,440,2.773,444,1.741,463,2.416,476,1.166,505,1.664,509,2.275,590,2.16,597,2.074,611,1.669,620,7.05,676,3.535,677,2.115,680,3.141,691,2.208,693,2.471,694,3.42,696,2.829,719,2.416,740,1.815,790,2.796,794,4.378,802,1.624,860,3.035,874,3.807,877,1.464,881,2.944,940,1.502,992,2.007,997,1.487,1007,4.235,1009,1.849,1024,4,1025,4.753,1027,3.27,1029,5.335,1030,7.06,1031,8.675,1032,5.728,1078,3.346,1218,2.261,1408,1.783,1434,3.499,1435,2.704,1871,3.27,1933,2.676,2297,1.337,2533,3.346,2805,3.085,3090,3.535,3174,3.346,3450,3.657,4416,3.81,4417,3.81,4418,3.81,4419,3.81,4420,3.433,4421,5.377,4422,3.81,4423,3.433,4424,3.81]],["t/3291",[2,1.718,8,3.426,17,2.517,24,1.87,28,0.899,48,3.59,56,0.874,68,2.415,92,3.277,93,4.309,94,3.445,97,2.848,132,1.871,165,1.427,179,2.131,186,3.001,199,3.141,203,2.118,207,2.599,236,1.675,283,1.842,304,2.519,338,2.177,527,1.392,614,1.172,620,6.612,898,4.291,1024,4.051,1025,4.813,1029,6.091,1046,4.138,1266,3.149,1371,4.894,1434,3.544,1601,3.805,1689,3.338,1693,3.726,2833,4.488,2892,4.813,3174,5.307,3866,3.305,4420,7.567,4421,7.567,4423,5.446,4425,6.044,4426,6.044,4427,6.044,4428,6.044,5144,5.446]],["t/3293",[2,1.393,7,0.908,16,2.073,21,1.459,24,1.465,27,1.226,28,0.729,41,2.343,47,3.257,56,0.986,85,3.459,90,3.588,93,3.226,97,3.405,105,1.615,109,0.871,123,1.76,124,3.257,170,1.526,172,1.789,173,2.763,174,1.535,183,2.216,200,2.558,244,3.178,256,1.932,275,1.598,283,1.493,291,3.042,295,2.076,313,1.262,323,2.083,353,1.408,408,2.749,437,4.246,438,2.68,447,2.443,471,2.14,491,2.295,502,1.424,525,2.433,580,1.793,597,4.672,599,3.914,614,0.951,615,3.021,619,7.747,627,4.415,747,2.12,846,2.293,874,3.74,997,1.912,1245,1.708,1285,2.907,1486,3.786,1876,3.344,2531,4.118,3533,5.026,3567,4.415,4386,4.415,4429,4.9,4430,10.104,4431,4.9,4432,9.374,4433,4.9,4434,4.9,4435,4.9,4436,4.9,4437,4.9,4438,4.9,4439,4.9,4440,4.9,4441,4.9,4442,7.226]],["t/3295",[7,0.84,12,3.399,24,1.726,84,0.913,109,1.421,201,4.29,304,2.589,619,7.502,736,3.594,780,5.673,1059,4.717,1266,2.906,1689,5.587,1882,4.964,3108,6.72,3158,6.592,3632,6.413,3688,7.676,3689,7.676,4443,7.997,4444,7.997,4445,7.997,4446,7.997]],["t/3297",[2061,3.73]],["t/3299",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,891,8.588,1312,4.182,2297,3.121,2683,5.767]],["t/3301",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,877,2.966,1328,4.86,1389,4.825,1531,5.423,2297,2.71,2447,5.538,3690,7.162,4119,10.474,4120,7.41,4121,7.41,4122,7.41,4123,9.492]],["t/3303",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,863,8.832,873,9.359,888,5.609,891,7.741,1138,7.196,1789,7.754,2683,5.861]],["t/3305",[2061,3.73]],["t/3307",[2,1.827,7,0.96,15,2.47,16,1.26,23,2.261,24,1.495,29,2.363,46,2.807,49,1.522,56,0.669,84,0.734,108,3.498,138,2.732,145,2.724,162,2.38,165,1.517,213,2.756,228,2.146,248,3.179,256,2.534,281,3.204,313,2.256,317,3.386,377,1.515,440,2.987,441,5.021,458,3.814,481,4.046,534,4.834,536,3.359,546,5.644,581,4.898,605,2.451,613,3.062,742,4.236,744,5.263,747,2.781,798,3.725,846,4.099,855,4.773,857,3.073,959,6.154,1163,4.106,1397,4.106,1441,4.076,1784,5.791,2307,4.716,2887,5.516,2966,4.611,3933,5.963,4056,5.119,4131,6.428,4132,6.428,4133,6.428]],["t/3309",[7,0.572,16,1.621,24,1.176,26,1.798,56,1.289,84,0.525,114,3.863,123,2.475,232,1.762,304,1.021,306,2.151,342,4.746,405,2.31,449,4.469,513,2.283,545,2.239,552,1.715,623,2.32,780,2.578,822,2.894,1285,4.905,1597,4.58,1816,3.604,1822,2.915,1933,6.45,2108,3.944,2306,6.393,2307,6.065,2308,5.913,2310,3.944,2311,9.45,2312,5.178,2313,5.913,2317,4.036,2318,4.036,2319,4.036,2320,4.036,2321,3.262,2322,5.913,2485,3.03,2550,3.503,3456,6.051,3577,4.036,4110,5.488,4111,4.142,4134,4.142,4135,5.79,4136,4.142,4137,6.209,4138,4.597,4139,6.891,4140,4.597,4141,4.597,4142,4.597,4143,4.597,4144,4.597,4145,4.597,4146,4.597,4147,4.597,4148,4.597,4149,4.597,4150,4.597,4151,4.597,4152,4.597,4153,4.597,4154,4.597,4155,4.597,4156,4.597,4157,4.597,4158,4.036,4159,4.597,4160,4.597,4161,4.597,4162,4.597,4163,6.209,4164,6.209,4165,4.597,4166,4.412,4167,4.264,4168,4.597,4169,6.891,4170,4.597]],["t/3311",[7,0.846,16,1.839,24,0.978,26,2.243,56,1.27,84,0.654,100,3.813,114,4.817,123,2.906,232,2.197,248,2.836,304,1.273,342,5.574,449,5.249,545,2.792,744,3.444,790,5.938,855,4.257,1597,5.379,1633,2.847,1933,7.158,2108,6.944,2307,5.938,2308,6.944,2310,4.919,2311,10.046,2312,5.746,2313,6.944,2317,5.034,2318,5.034,2319,5.034,2320,5.034,2321,4.068,2322,6.944,4110,6.445,4134,5.165,4135,6.8,4136,5.165,4137,7.292,4163,7.292,4164,7.292,4171,5.733,4172,5.733,4173,5.733,4174,5.733]],["t/3313",[56,1.014,164,2.822,550,4.036,614,1.891,744,5.855,814,4.423,1704,8.56,2301,10.952]],["t/3315",[2061,3.73]],["t/3318",[7,0.897,16,1.743,56,1.124,85,3.246,97,4.19,171,3.092,322,2.931,434,6.246,614,1.725,891,8.588,1312,4.182,2297,3.121,2683,5.767]],["t/3320",[7,0.641,12,3.281,24,1.317,39,4.241,56,1.029,84,0.881,112,5.163,164,2.235,171,2.21,304,2.196,322,2.545,443,7.344,491,3.141,614,1.498,740,3.677,751,6.017,802,3.291,877,2.966,1328,4.86,1389,4.825,1531,5.423,2297,2.71,2447,5.538,3690,7.162,4119,10.474,4120,7.41,4121,7.41,4122,7.41,4123,9.492]],["t/3322",[7,0.751,56,0.94,246,3.601,247,4.184,279,4.114,292,4.142,691,6.322,863,8.832,873,9.359,888,5.609,891,7.741,1138,7.196,1789,7.754,2683,5.861]],["t/3324",[1,3.01,4,0.549,7,0.249,15,0.645,16,0.587,21,0.5,24,1.511,27,1.912,28,1.317,31,1.042,46,3.756,56,0.311,84,0.873,89,1.933,96,1.503,105,0.553,106,3.733,107,0.453,109,0.532,115,1.452,121,1.845,126,2.501,131,5.269,137,1.135,138,0.713,153,0.905,163,2.053,167,2.423,170,0.932,173,0.947,198,1.535,232,3.296,236,0.465,254,0.569,279,0.764,286,1.911,294,1.696,298,1.081,315,3.827,318,1.261,336,0.72,341,2.346,343,0.629,353,0.86,381,2.812,397,2.74,405,2.097,407,1.425,408,1.138,434,6.665,444,0.767,445,3.749,476,1.725,484,0.75,491,1.561,502,1.818,511,1.005,527,0.689,536,2.273,568,0.507,571,4.905,572,1.67,599,1.621,611,3.349,635,2.156,653,3.725,715,1.621,723,1.851,768,1.611,796,0.927,798,1.734,807,0.684,827,0.896,841,0.818,846,1.4,851,0.927,862,0.88,936,1.825,953,0.716,958,2.061,1012,1.786,1037,1.975,1045,2.844,1132,1.232,1170,0.968,1202,0.932,1274,1.081,1303,1.884,1380,1.116,1390,6.833,1433,1.411,1443,1.316,1444,5.37,1446,1.035,1449,1.359,1473,2.308,1476,4.206,1494,1.679,1500,1.337,1689,3.455,1696,1.125,1792,3.24,1811,1.679,1813,6.426,1847,1.191,1864,0.932,1935,1.513,2021,1.297,2043,1.247,2141,1.384,2486,1.179,2489,9.505,2683,2.626,2691,2.467,2714,1.769,2717,1.156,2730,1.769,2768,5.18,3057,3.085,3063,1.611,3133,4.953,3411,1.769,3633,3.154,3858,1.316,3903,1.557,4118,1.611,4479,5.319,5000,6.256,5232,1.611,5488,1.769,5489,1.769,5490,1.769,5491,1.769,5585,1.906,5586,1.906,5587,1.906,5588,1.906,5589,1.906,5590,1.906,5591,1.906,5592,1.906,5593,1.906,5594,1.906,5595,1.906,5596,1.906,5597,3.398,5598,3.398,5599,1.906,5600,1.906,5601,1.906,5602,1.906,5603,1.906,5604,1.906,5605,1.769,5606,3.398,5607,1.906,5608,5.582,5609,5.582,5610,1.906,5611,4.597,5612,1.906,5613,1.906,5614,1.906,5615,1.906,5616,1.906]],["t/3327",[2,1.505,7,0.902,15,3.449,16,1.5,25,1.787,34,1.421,49,1.254,56,1.13,84,0.605,85,1.933,158,2.79,164,1.533,171,1.516,203,1.336,219,2.495,283,2.33,292,2.428,304,1.176,313,1.969,318,2.303,322,2.521,377,1.248,471,2.313,480,3.799,513,2.63,562,2.741,567,4.153,605,2.02,736,4.034,755,3.63,757,1.986,805,3.799,811,3.463,1044,3.288,1170,1.713,1202,4.246,1238,3.334,1312,2.051,1345,4.036,1577,4.218,1735,3.886,1991,3.383,2283,5.612,2395,2.926,2447,7.793,2909,10.987,2910,8.594,2912,9.46,5617,6.014,5618,6.014,5619,6.014,5620,6.014,5621,6.014,5622,6.014,5623,6.014,5624,6.014,5625,6.014,5626,6.014,5627,6.014,5628,6.014,5629,6.014,5630,6.014]],["t/3329",[7,0.65,15,3.008,16,1.535,48,3.347,49,2.363,68,3.128,84,1.254,103,2.76,164,2.266,187,5.744,295,3.317,304,2.216,393,4.051,401,3.153,405,2.624,409,4.18,614,1.936,764,5.556,1408,4.669,1632,6.007,1793,7.829,1898,4.346,2481,5.744,2483,8.25,2910,6.453,2930,6.453,5605,8.25,5631,8.89,5632,8.89]],["t/3331",[2061,3.73]],["t/3333",[16,1.981,34,2.711,107,2.725,318,2.592,1352,5.118]],["t/3335",[2,1.45,7,0.424,16,1,24,1.5,28,0.759,34,1.369,41,2.439,49,1.763,56,1.005,84,1.003,103,2.625,107,2.771,109,1.968,110,6.147,113,2.86,127,1.763,132,2.304,140,2.107,164,1.477,165,1.204,171,2.131,179,1.799,193,1.956,203,2.217,228,1.703,272,2.201,304,2.145,313,1.314,343,1.912,377,1.754,408,1.941,527,2.615,614,1.705,658,2.575,877,3.947,940,2.012,1170,3.125,1181,2.79,1245,3.064,1266,1.466,1281,2.712,1737,2.028,1779,3.621,1844,3.284,1863,3.281,1870,3.767,1884,3.548,2605,3.888,2850,6.536,2851,3.084,3644,1.981,3866,2.79,4308,7.444,4309,5.102]],["t/3337",[5,1.86,7,0.847,12,2.129,17,2.087,21,1.492,24,1.879,27,1.253,28,0.745,31,1.29,41,2.395,46,3.798,56,1.109,98,2.72,109,1.545,110,5.661,116,1.97,131,2.395,133,2.259,158,2.639,170,1.56,171,1.434,174,2.724,179,2.59,186,3.647,196,2.093,197,2.353,254,1.698,272,2.161,275,2.396,282,4.21,289,2.087,304,1.113,322,2.866,353,1.44,367,3.941,377,1.181,386,5.76,388,3.818,408,1.906,440,2.328,482,2.361,505,3.208,525,2.488,527,2.349,528,2.782,568,1.512,590,2.84,597,2.727,599,2.714,611,2.195,614,0.972,631,5.059,719,3.177,755,3.486,807,2.04,816,2.991,856,3.154,876,3.594,877,1.925,928,4.4,997,3.393,1046,2.469,1170,1.621,1366,2.938,1633,2.488,1737,1.991,1870,1.838,2297,1.759,2605,3.818,2691,4.13,2850,4.4,3644,1.945,4310,7.345]],["t/3339",[2061,3.73]],["t/3341",[7,0.469,16,1.107,28,0.839,34,2.148,49,1.337,56,1.266,106,3.092,109,1.798,164,1.634,171,2.292,193,2.164,203,1.424,221,3.116,254,1.913,318,2.597,322,2.639,397,2.473,398,2.972,444,2.579,476,2.449,491,1.793,502,2.326,527,1.301,614,1.095,653,2.655,658,2.849,757,2.079,822,3.554,846,2.642,945,4.4,997,4.55,1107,4.444,1164,3.001,1381,2.358,1389,3.529,1390,4.246,1476,3.1,1870,3.412,2032,4.49,2312,4.514,2633,4.64,3015,5.237,4570,7.213,5173,8.436,5575,5.646,5633,6.411,5634,5.949]],["t/3343",[16,1.268,28,1.596,56,1.117,107,1.744,109,1.149,132,2.722,164,2.546,170,2.739,171,1.851,246,2.576,254,2.981,313,1.664,326,4.637,336,2.772,377,1.524,391,2.868,426,2.516,502,2.905,525,4.368,580,2.366,647,2.556,712,3.665,734,3.014,880,3.991,953,2.756,997,4.518,1037,4.016,1114,3.091,1235,5.196,1274,4.161,1331,6.953,1332,6.435,1381,2.7,1421,6.205,2806,6.703,5175,6.813,5176,6.813,5458,6.813,5635,7.341]],["t/3345",[2,1.58,7,0.462,16,1.09,25,1.876,28,1.372,31,2.375,56,1.106,84,0.635,107,1.5,109,1.407,115,3.843,123,3.313,132,2.855,140,2.296,164,1.61,170,1.731,171,1.592,179,1.961,193,2.131,236,1.541,254,1.884,256,2.193,282,3.187,304,1.235,313,1.432,322,3.04,336,2.385,372,3.123,377,1.311,391,2.467,502,1.616,507,5.287,527,1.825,550,4.163,572,3.103,585,4.429,586,5.159,658,2.806,755,2.639,814,3.594,828,2.444,947,2.983,953,2.37,997,3.599,1047,4.986,1238,3.501,1275,3.761,1278,3.906,1285,3.3,1382,3.501,1417,3.867,1444,5.563,1688,3.989,1870,3.383,2021,4.297,3053,3.947,4272,5.01,5007,4.503,5177,5.86,5178,5.86,5179,7.602,5180,5.86,5181,5.86,5182,5.86]],["t/3347",[7,0.731,16,1.268,17,2.692,31,1.664,34,1.735,40,3.589,84,0.738,115,3.137,123,2.322,138,2.748,164,1.872,174,2.755,175,3.726,229,4.041,246,2.576,247,2.994,254,2.191,261,3.452,282,3.705,351,2.465,353,1.858,415,4.801,433,3.933,476,3.282,507,3.705,525,3.21,527,1.489,550,3.642,568,2.654,571,5.069,578,2.677,621,3.501,628,3.518,631,4.453,658,3.262,707,3.791,901,6.205,997,4.38,1047,4.07,1066,4.572,1380,4.297,1382,4.07,1508,3.769,1540,5.127,1813,4.995,1898,3.589,2041,5.433,3129,6.205,3508,5.677,3934,5.998,4498,5.235,5183,6.465,5184,6.465,5185,6.813]],["t/3349",[7,0.65,31,2.569,49,1.854,84,1.139,115,3.799,123,3.583,174,3.44,236,2.169,247,3.625,351,2.986,416,3.799,476,2.395,527,1.804,568,3.011,584,6.875,585,6.235,621,4.24,658,3.951,818,4.142,997,3.055,1193,6.632,1444,5.499,1688,7.158,1790,7.514,2021,6.049,2691,6.453,2973,7.263,3012,6.875,4498,6.339]],["t/3351",[2061,3.73]],["t/3353",[7,0.705,15,3.26,34,2.277,56,1.092,228,2.832,322,2.797,377,2,398,3.149,401,3.418,465,6.226,539,5.738,543,5.035,607,3.427,683,3.345,747,3.671,820,4.595,1535,6.871,1536,6.556,1555,7.872,1563,7.281,1607,5.844,2297,2.978,3533,5.901,4279,7.872,4280,7.872]],["t/3355",[7,0.618,16,1.458,24,0.661,25,1.308,28,0.899,34,1.04,49,1.432,56,0.629,84,0.849,103,1.367,145,3.152,162,1.435,171,1.11,175,2.234,203,1.526,219,1.973,236,1.676,256,2.385,275,1.264,281,1.933,283,1.843,304,1.343,313,1.557,315,1.94,318,0.995,322,1.994,334,2.314,338,2.178,365,2.07,368,3.087,372,1.528,395,2.519,398,1.439,401,2.436,438,2.12,458,2.3,482,1.827,530,2.85,531,2.476,539,6.82,542,5.311,543,4.986,552,1.446,564,2.645,567,2.556,605,1.478,613,1.846,614,1.956,630,1.509,708,4.986,747,1.677,757,1.007,762,2.751,877,2.858,898,3.8,947,2.08,1139,2.186,1164,2.06,1170,1.254,1181,2.12,1219,3.139,1266,2.774,1281,2.06,1306,2.555,1312,2.342,1360,2.329,1376,3.186,1416,2.915,1473,1.82,1489,3.493,1531,2.723,1540,2.26,1550,3.04,1570,3.404,1638,2.11,1732,3.04,1737,1.541,1754,1.736,1831,3.258,1898,2.152,1926,7.571,1927,3.493,2297,1.361,2806,2.954,3042,3.729,3456,5.311,3644,3.537,3671,3.721,3773,3.493,3977,2.954,4056,4.817,4281,6.049,4282,6.049,4283,3.877,4284,6.049,4285,3.877,4286,6.049,4287,3.877,4288,6.049,4289,6.049,4290,3.721,4291,6.049,4292,3.877,4293,3.877,4294,6.049,4295,3.877,4296,3.877,4297,3.877,4298,3.877,4299,3.877]],["t/3357",[4,2.472,16,2.239,49,1.791,171,2.165,236,2.994,318,2.93,398,3.623,401,4.353,539,7.722,543,5.791,567,4.824,940,2.982,1266,2.805,1376,3.983,1559,8.75,1638,5.312,3644,2.936,4300,7.563,4301,7.563,4302,7.563]],["t/3359",[4,2.439,16,2.105,49,1.767,171,2.136,236,2.975,398,3.591,401,4.325,539,7.682,543,5.741,552,4.005,567,4.8,852,3.408,940,2.942,1266,2.78,1312,2.889,1376,3.93,1559,8.694,1638,5.265,1732,7.586,3644,2.896,4304,7.462,4305,7.462,4306,7.462]],["t/3361",[2061,3.73]],["t/3363",[4,1.42,7,0.797,10,2.477,16,0.852,24,0.742,27,1.652,31,1.7,48,1.858,56,0.831,84,1.154,90,3.28,91,3.816,101,2.438,107,1.172,109,1.174,113,1.67,115,2.108,125,2.61,127,2.282,130,5.361,131,2.077,132,1.345,133,1.959,138,2.807,164,1.912,165,1.559,170,2.779,174,1.361,175,2.504,179,1.532,182,1.532,197,2.04,198,2.23,200,2.269,202,3.152,216,2.331,228,1.451,281,2.166,292,1.992,296,1.765,315,2.175,316,2.04,318,1.115,321,3.461,351,1.657,353,2.758,359,2.019,379,3.816,382,2.841,394,2.289,397,1.904,408,1.653,409,2.32,416,2.108,418,2.289,430,2.259,433,2.644,447,2.166,476,1.329,491,1.38,501,4.171,527,1.001,536,3.172,550,1.799,552,4.038,555,2.166,565,1.495,567,2.791,570,4.171,578,1.799,593,3.433,621,2.354,629,2.913,630,2.57,637,2.939,655,2.993,675,2.913,683,1.713,690,5.223,706,3.268,734,2.026,737,3.461,765,4.128,796,2.4,805,3.117,828,1.91,878,1.953,995,3.117,1006,2.342,1045,3.052,1168,3.729,1280,3.084,1313,4.346,1838,3.407,1879,4.031,1916,2.735,2531,3.652,2799,3.582,3061,3.729,4922,4.579,5636,4.934,5637,4.934,5638,4.934,5639,4.934]],["t/3365",[2,1.078,5,1.408,7,0.814,16,0.744,24,1.341,27,1.487,28,1.583,31,0.566,34,0.59,41,1.051,56,0.938,84,1.029,85,0.803,96,2.207,103,0.776,105,0.725,107,1.979,109,1.915,113,1.457,116,0.865,123,1.362,124,1.462,127,1.311,130,2.278,132,2.076,133,0.992,140,0.908,162,0.814,163,0.919,164,1.098,165,1.404,170,3.114,171,1.086,172,1.825,173,1.24,174,1.188,179,1.337,224,0.982,225,0.828,228,0.734,236,1.385,237,2.93,246,0.876,256,0.867,257,1.18,260,2.2,272,2.565,275,1.237,280,1.474,281,1.891,285,0.958,294,1.247,302,1.067,313,0.566,316,1.033,318,1.283,343,1.421,351,1.907,364,1.633,366,1.45,367,2.682,377,0.518,394,1.159,395,0.916,397,0.964,409,1.174,416,1.84,430,1.143,433,3.042,436,1.654,440,2.763,471,0.961,476,1.529,480,1.578,491,1.205,498,1.29,508,2.354,524,1.427,527,0.507,528,3.301,536,2.606,541,2.46,550,1.57,552,4.407,565,3.441,567,2.512,572,1.227,582,1.931,583,1.416,587,1.887,593,1.143,596,1.044,599,2.054,621,1.191,630,0.856,635,1.45,637,1.487,660,1.725,690,1.438,696,1.633,702,1.561,706,1.654,757,0.985,759,0.813,765,1.375,772,1.53,807,0.896,810,1.029,822,3.147,827,1.174,828,0.967,852,1.733,862,1.153,874,1.138,877,0.845,878,1.704,880,1.721,919,1.595,929,1.578,940,1.495,947,1.18,953,1.617,997,1.48,1041,2.542,1042,1.395,1111,2.612,1114,1.051,1130,2.2,1170,0.712,1238,1.385,1245,0.767,1263,1.405,1314,1.578,1352,1.922,1382,2.387,1417,1.53,1427,1.848,1449,3.071,1632,3.668,1782,1.044,1822,2.405,1836,1.848,1847,1.561,1870,1.834,1884,2.638,1998,1.347,2363,1.813,2364,3.254,2365,3.518,2372,1.545,2653,1.848,2672,1.515,2782,1.53,2783,1.982,2787,3.351,2905,1.654,3910,1.813,4450,2.2,5232,2.111,5557,2.318,5640,2.498,5641,2.498,5642,6.752,5643,4.307,5644,2.498,5645,2.498,5646,2.498,5647,2.498,5648,2.498,5649,2.498]],["t/3367",[7,0.579,10,1.147,12,0.855,16,0.916,21,0.599,25,1.576,27,1.588,28,0.944,29,1.717,31,0.904,34,0.54,39,0.863,48,1.501,49,1.652,56,1.27,68,0.804,84,0.639,85,0.735,88,0.955,96,1.303,100,0.948,102,1.338,103,0.71,105,1.157,107,1.881,109,1.333,113,0.773,127,1.213,132,1.446,133,0.907,145,2.371,160,0.743,164,0.583,165,1.103,170,2.598,171,1.005,175,1.16,179,0.71,192,1.249,193,1.346,196,0.841,198,1.802,199,1.046,201,1.08,213,0.863,224,0.899,225,0.758,226,1.794,227,1.4,228,0.672,243,1.16,244,2.278,246,0.802,247,0.932,277,1.09,279,0.916,281,1.003,283,1.07,292,1.61,294,1.141,296,1.426,300,1.286,304,0.78,306,0.942,316,0.945,317,1.06,322,0.663,323,2.964,336,0.863,338,0.91,351,0.768,353,1.009,359,0.935,366,1.327,377,0.828,405,1.177,409,1.075,416,0.977,426,1.367,444,0.919,471,2.04,476,1.429,484,0.899,499,1.477,502,1.02,509,1.202,528,2.593,529,1.932,536,2.496,550,0.833,552,4.318,553,1.123,565,1.925,567,3.798,568,0.607,577,2.329,578,0.833,601,0.995,621,1.09,630,0.783,653,0.668,660,1.578,662,1.233,672,2.18,719,2.226,723,0.758,731,1.112,738,1.867,747,2.021,757,0.523,778,2.334,784,1.555,798,3.679,811,1.316,820,1.09,828,1.543,839,1.217,851,1.112,852,1.604,856,1.267,857,1.679,862,1.841,878,0.904,880,0.913,884,1.338,939,1.305,979,3.891,980,2.547,993,1.225,1008,1.286,1037,0.808,1114,0.962,1163,1.286,1170,1.136,1223,3.79,1235,1.046,1247,2.576,1289,0.858,1305,1.691,1308,1.258,1312,1.36,1320,2.354,1381,1.467,1408,0.942,1449,1.63,1493,1.349,1539,1.414,1554,3.164,1631,1.727,1632,1.101,1635,1.767,1664,1.555,1699,1.316,1782,1.667,1864,1.117,1870,2.052,1898,1.117,1902,1.011,2016,1.603,2033,1.194,2082,1.555,2297,1.233,2450,1.691,2844,1.578,3402,1.603,3540,1.767,3597,1.767,3630,1.63,4234,1.867,4236,3.258,4694,1.932,5221,1.932,5403,2.013,5556,2.013,5577,1.932,5650,3.7,5651,4.922,5652,3.7,5653,2.121,5654,5.304,5655,2.285,5656,3.7,5657,2.285,5658,2.121,5659,2.285,5660,2.285,5661,2.121,5662,2.285,5663,2.285,5664,2.285,5665,3.987,5666,2.285,5667,2.121,5668,2.285,5669,2.285,5670,2.121,5671,3.987,5672,2.121,5673,2.121,5674,3.7,5675,2.121,5676,2.285,5677,2.285,5678,2.285,5679,2.285,5680,2.121,5681,2.285,5682,2.121,5683,2.285,5684,2.285,5685,2.285,5686,2.285,5687,2.285,5688,2.285,5689,2.285,5690,2.285,5691,2.285,5692,2.285,5693,2.285,5694,2.285,5695,2.285,5696,2.285,5697,2.285,5698,2.285]],["t/3369",[2,0.994,7,0.659,25,1.18,27,1.986,28,1.374,48,2.385,49,0.829,56,1.291,84,0.906,85,1.277,103,1.234,107,1.505,109,0.622,140,2.304,145,2.364,164,1.013,165,1.642,170,2.7,171,1.002,193,2.138,198,1.795,199,1.819,223,2.076,225,1.318,226,1.788,281,1.744,283,1.066,304,0.777,306,1.637,322,1.839,323,1.487,338,0.907,377,0.825,405,1.87,426,1.362,433,2.129,476,2.128,482,1.649,502,1.017,536,1.098,552,4.336,557,1.835,558,4.354,565,2.394,567,1.478,576,2.172,577,1.744,578,1.449,600,2.458,635,1.338,647,1.383,693,2.269,747,2.414,757,0.909,763,2.51,796,1.933,798,2.028,801,2.744,828,2.451,852,3.626,878,1.572,885,2.102,979,5.521,980,4.046,1037,1.405,1066,1.819,1170,1.132,1223,3.33,1247,4.093,1320,2.345,1376,1.843,1381,2.906,1539,2.458,1554,5.027,1664,2.704,1735,2.567,1768,2.51,1870,2.046,1994,3.499,2297,1.228,4130,5.176,4236,3.246,5221,3.358,5430,3.499,5556,3.499,5577,3.358,5650,3.687,5651,3.687,5652,3.687,5653,3.687,5656,5.879,5658,3.687,5667,3.687,5670,3.687,5672,3.687,5673,3.687,5674,5.879,5675,3.687,5680,3.687,5682,3.687,5699,6.335,5700,6.335,5701,3.973,5702,6.335,5703,3.973,5704,6.335,5705,6.335,5706,3.973,5707,3.973,5708,3.973,5709,3.973,5710,3.973,5711,3.973]],["t/3371",[7,0.563,56,1.333,96,3.372,105,2.235,107,1.829,170,2.111,179,2.391,275,2.212,322,2.235,377,1.598,536,3.434,552,3.388,653,2.249,780,5.095,1046,3.34,1170,3.54,1289,2.89,1312,3.517,1792,4.469,1870,3.331,2297,2.38,4096,5.954,4097,5.954,4246,5.099,4247,8.718,4263,6.507,5578,9.572,5712,7.699,5713,10.314,5714,10.314,5715,7.699]],["t/3373",[7,0.776,107,2.52,155,6.373,225,3.518,226,4.772,277,5.058,323,3.969,324,5.211,377,2.622,536,2.931,552,3.483,565,3.213,794,6.852]],["t/3375",[2061,3.73]],["t/3377",[7,0.528,16,1.943,18,4.933,19,4.082,24,1.084,27,1.589,28,1.754,31,1.636,56,0.905,84,0.992,88,4.126,89,4.387,116,2.499,169,3.662,198,3.26,225,2.393,240,3.662,283,1.936,292,2.913,296,2.58,302,3.083,303,5.238,332,3.475,390,3.233,391,2.819,397,2.784,491,2.018,527,1.464,608,3.475,712,3.602,723,2.393,734,2.963,740,3.027,835,4.51,859,8.326,882,4,884,5.777,899,4.609,900,4.377,1024,4.259,1216,3.409,1243,4.91,1289,2.709,1315,3.683,1363,2.411,1381,2.654,1805,5.58,1864,3.528]],["t/3379",[16,2.026,18,1.476,19,1.497,23,1.122,24,1.115,25,3.282,27,2.069,28,1.603,29,1.903,56,0.783,96,1.183,98,1.181,100,3.08,102,3.441,107,2.232,109,1.786,113,1.989,116,1.254,127,1.789,133,1.437,140,1.317,145,2.194,163,4.502,165,1.776,170,0.993,172,1.89,182,2.304,183,1.442,198,1.636,216,1.71,231,2.43,232,1.222,254,2.215,278,1.204,279,1.452,285,2.254,295,1.351,306,1.492,322,1.051,334,1.903,336,1.367,338,0.826,343,1.94,353,0.916,377,1.22,405,3.463,440,1.481,476,1.584,495,2.634,511,1.739,512,1.881,517,2.037,557,1.672,558,2.439,577,2.581,592,2.156,614,0.619,636,1.892,647,1.26,653,1.058,719,2.022,723,4.244,734,1.487,757,1.953,760,2.035,778,3.441,857,2.475,907,2.156,914,3.427,934,1.602,936,3.391,946,3.171,953,1.359,997,2.55,1179,2.068,1245,3.089,1274,2.052,1289,1.359,1306,2.101,1315,1.848,1352,2.623,1362,2.43,1363,2.855,1381,2.162,1385,2.628,1389,1.993,1473,1.497,1492,2.8,1768,2.287,1778,2.736,1782,1.513,1864,5.581,1998,1.953,2486,2.24,2696,2.263,2833,2.368,3010,2.287,3630,2.582,3795,2.398,3997,3.6,4498,2.582,5021,2.628,5186,3.36]],["t/3381",[16,2.212,24,1.328,25,1.83,27,2.939,28,1.677,31,1.397,84,0.619,107,2.455,109,1.946,116,2.134,127,2.689,163,4.82,165,1.281,203,1.369,232,2.983,278,2.049,281,2.705,293,4.559,360,5.852,367,2.911,377,1.279,507,4.46,596,3.693,653,2.581,723,2.044,740,2.584,807,3.169,852,3.555,857,2.594,934,2.727,946,3.776,953,2.313,980,3.935,1013,4.254,1047,3.415,1239,3.519,1245,2.713,1278,3.811,1315,5.274,1352,4.61,1363,2.059,1381,2.266,1473,2.548,1864,5.518,2833,4.029,3515,4.888,3997,3.773,5187,5.717]],["t/3383",[7,0.28,16,1.948,21,2.707,25,1.138,26,2.655,27,0.843,28,1.524,34,1.822,56,1.232,84,1.09,88,3.689,96,1.252,98,2.005,100,1.589,105,2.237,106,2.092,107,1.461,108,1.835,109,0.962,113,1.296,128,2.722,160,1.246,163,2.262,165,1.279,169,1.943,179,1.189,200,2.827,201,1.809,278,1.273,295,1.429,296,1.369,297,1.943,313,0.868,322,1.111,323,2.302,338,0.874,339,2.879,353,0.969,377,2.006,413,2.685,416,2.628,418,1.776,426,2.108,430,1.752,476,1.031,484,1.505,502,1.574,506,2.107,507,3.104,508,4.824,509,3.233,512,3.821,520,3.236,552,1.258,622,2.879,723,3.86,731,1.862,736,4.463,757,1.406,851,2.992,936,3.06,939,2.187,1033,2.26,1069,3.869,1075,2.065,1235,4.725,1245,1.176,1308,3.385,1315,1.954,1363,1.279,1408,1.578,1511,4.675,1705,4.369,1849,2.504,1850,2.393,1864,5.514,1876,2.301,1882,2.093,1964,2.419,2782,2.345,2837,2.644,2896,5.825,4481,3.372,4482,3.372,4483,3.372,4484,3.372,4485,3.372,4486,3.372,4487,3.372,4488,3.372,4489,5.416,4490,3.372,4491,5.416,4492,3.372,4493,3.372,4494,3.372,4495,3.372,4496,3.372]],["t/3385",[16,2.197,24,1.912,25,3.46,28,1.525,31,2.104,39,3.505,198,4.194,282,4.684,289,3.404,302,3.966,409,4.365,505,3.569,635,3.125,723,3.079,830,3.936,1446,5.039,1467,6.737,1798,6.012,1819,6.934,1863,4.52,1864,4.538,2741,6.737]],["t/3387",[5,3.249,16,1.716,23,3.763,28,1.301,145,3.709,163,3.655,165,2.066,169,5.044,239,6.984,367,4.695,511,2.94,807,4.705,1266,2.515,1381,3.655,1849,6.499,1850,6.211,3795,6.581]],["t/3389",[2,2.1,16,1.638,19,1.801,24,1.547,25,1.294,27,1.501,28,1.625,56,0.399,89,1.509,97,1.808,102,2.55,107,2.594,109,1.066,113,1.474,125,2.305,127,2.073,132,2.287,140,1.584,145,2.542,153,2.068,163,1.602,165,2.269,170,1.868,172,1.4,179,1.353,182,2.115,183,1.735,198,1.968,228,1.281,239,2.319,245,2.415,275,1.251,276,3.857,277,2.078,289,3.077,294,2.175,313,2.151,320,2.319,323,1.63,324,3.348,351,1.463,365,2.049,381,4.165,394,2.021,398,1.424,405,3.846,426,1.493,432,2.55,444,1.753,468,2.572,472,1.968,495,1.952,502,1.115,511,2.807,527,1.382,550,1.589,555,1.913,577,1.913,581,2.923,621,2.078,635,1.467,662,2.349,683,1.513,712,2.175,723,1.445,748,3.055,749,2.618,750,2.849,757,1.558,759,1.417,760,1.194,769,1.554,780,4.144,814,1.741,820,3.249,841,4.416,857,1.834,880,1.741,909,2.783,934,3.713,951,2.594,997,1.497,1052,2.885,1133,2.175,1181,2.098,1205,3.559,1223,2.29,1308,2.398,1315,2.224,1329,3.055,1381,1.602,1441,2.433,1471,3.457,1481,4.946,1870,1.407,2043,2.849,2485,3.954,2488,2.451,2516,4.946,2890,3.457,2966,2.752,3003,5.042,3051,2.723,3199,3.457,3880,2.643,3882,3.682,3890,3.682,3913,3.559]],["t/3391",[21,2.669,24,1.529,28,1.736,89,3.525,107,3.15,109,1.593,165,2.116,179,3.16,338,2.323,405,3.005,635,3.427,723,3.376,820,4.854,1481,7.388,1864,4.976]],["t/3393",[2061,3.73]],["t/3395",[7,0.51,18,5.412,28,0.914,29,2.259,34,1.649,56,1.013,89,3.828,103,2.166,105,2.025,165,1.45,201,3.296,225,3.199,226,3.139,247,2.845,248,3.039,275,2.004,282,3.521,306,3.974,332,3.36,372,2.422,376,3.691,495,3.126,511,2.064,530,2.895,558,2.895,605,2.343,622,3.266,683,3.349,685,5.708,707,3.602,799,4.893,887,4.083,903,4.508,940,2.422,958,4.232,1137,4.456,1152,6.028,1234,3.668,1275,4.155,1302,4.193,1454,4.017,1456,4.407,1457,4.62,1458,6.093,1459,4.747,1460,5.395,1476,2.379,1782,4.985,2225,3.446,2960,5.7,2970,5.897,5188,6.475,5661,6.475,5716,6.977,5717,6.977,5718,6.977]],["t/3397",[2,2.157,7,0.768,16,1.676,18,5.121,19,1.879,25,1.35,27,1.001,28,1.129,31,1.596,34,1.074,84,1.056,88,5.249,89,3.637,123,2.227,128,3.116,132,1.239,162,1.482,164,1.159,165,1.464,169,3.573,196,2.589,197,1.879,213,1.716,225,2.335,226,2.045,275,2.476,279,1.822,283,1.219,304,1.377,306,2.901,313,1.03,351,2.364,353,1.782,440,1.86,472,3.181,483,2.66,484,1.787,495,3.155,500,1.995,510,8.121,512,4.091,557,3.251,558,1.886,575,3.103,605,1.526,628,2.178,647,3,659,2.731,734,2.891,736,3.41,762,2.84,796,2.21,828,1.759,884,4.121,940,2.445,951,2.706,1069,3.534,1073,3.05,1074,3.187,1114,1.913,1152,2.84,1164,2.127,1208,3.363,1234,5.103,1308,2.501,1310,3.01,1363,1.519,1456,2.871,1458,2.871,1491,3.241,1778,3.434,1782,4.842,1849,2.972,1850,2.84,1851,5.111,1964,2.871,1990,2.903,2041,3.363,2787,2.683,3039,3.713,3932,6.256,5189,4.217,5190,4.217,5191,4.217,5192,4.217]],["t/3399",[28,1.437,96,3.587,174,3.027,254,3.274,283,2.944,295,4.095,591,5.805,888,5.998,1782,4.586,1945,6.788]],["t/3401",[16,1.789,23,1.918,24,1.333,28,1.357,56,1.394,132,1.687,145,2.31,170,1.697,174,1.708,203,1.375,224,2.434,225,2.054,239,3.296,289,2.27,318,1.399,506,3.408,558,2.569,614,1.058,723,2.941,740,2.597,749,3.72,760,1.697,780,3.058,802,2.324,807,2.22,877,2.095,946,2.645,1014,3.72,1152,3.869,1234,3.255,1456,3.911,1458,3.911,1798,3.197,1799,3.954,1864,3.027,2833,4.049,3629,3.593]],["t/3403",[16,1.16,28,0.88,56,1.391,109,1.051,145,2.507,170,3.601,174,1.853,203,1.492,224,2.641,225,2.228,239,3.576,614,1.148,723,2.228,740,2.818,749,4.037,760,1.842,772,4.114,802,2.522,877,2.273,946,2.87,947,3.174,1056,4.711,1152,4.198,1234,3.531,1352,2.997,1456,4.243,1458,4.243,1864,3.284,1888,5.678,2833,4.393,3629,3.899,5193,6.234]],["t/3405",[2061,3.73]],["t/3407",[7,0.796,24,1.635,28,1.424,219,3.684,502,2.784,536,3.006,550,3.967,565,3.296,568,2.891,912,7.029]],["t/3409",[7,0.633,28,1.458,34,2.631,49,2.322,163,4.094,188,5.802,219,3.198,221,2.963,295,4.154,353,2.188,377,1.795,476,2.329,484,4.376,500,3.796,502,3.15,512,3.562,536,2.39,555,3.796,577,3.796,623,3.842,653,2.526,757,2.815,780,4.271,930,4.388,1312,2.948,2009,5.523,2032,3.378,2711,6.689]],["t/3411",[2,1.255,4,1.443,7,0.747,12,1.876,24,0.753,25,1.489,28,0.656,49,1.045,56,1.132,68,3.223,84,0.504,85,1.611,101,2.476,123,1.585,132,1.366,140,1.823,165,1.042,179,1.557,196,1.844,203,2.035,219,3.153,236,1.223,285,1.922,313,1.721,317,2.325,338,1.144,348,2.985,359,2.051,372,2.635,377,1.04,398,1.639,449,2.863,491,1.402,512,2.428,527,1.54,536,3.191,565,2.775,568,2.017,572,2.463,605,1.683,613,2.103,614,0.856,630,1.718,647,1.745,653,1.464,736,1.984,757,2.641,803,2.619,930,3.852,940,3.547,955,3.639,1107,2.451,1139,2.489,1170,1.428,1202,3.711,1245,1.539,1266,2.318,1284,2.275,1328,2.779,1700,5.241,1844,2.841,1854,4.237,2030,5.869,2031,3.101,2032,5.036,2297,2.346,2312,5.734,2395,3.692,2447,4.794,2608,5.241,2633,2.559,2711,3.012,3037,6.022,3857,3.239,4178,4.415,4179,6.684,4180,4.415,4181,4.415,5719,4.652,5720,7.59,5721,5.013,5722,5.013]],["t/3413",[28,1.451,34,2.03,49,1.791,84,0.863,96,2.807,165,1.785,190,4.096,219,2.467,232,2.899,295,3.205,304,1.679,398,4.011,461,4.46,536,3.709,552,2.821,567,4.124,605,2.884,614,1.467,731,4.177,757,2.534,852,3.455,1033,5.07,1266,2.173,2032,4.329,3016,7.495,3866,4.136,4182,7.563,4183,7.563]],["t/3415",[1,0.908,5,0.993,7,0.736,12,0.63,16,1.133,17,0.617,23,0.941,24,1.898,25,1.234,26,0.58,27,0.371,28,1.486,34,1.202,39,0.636,41,1.279,48,1.144,49,0.634,56,0.922,68,0.592,84,0.896,88,0.703,89,1.762,98,0.549,103,1.58,105,0.882,107,0.987,109,1.027,116,1.052,123,0.532,128,1.345,132,2.519,133,0.668,160,0.548,162,0.991,163,1.528,164,0.775,165,1.223,170,0.833,179,0.944,182,2.222,183,1.21,196,0.619,198,0.76,199,0.77,203,1.59,219,1.193,220,1.736,221,1.041,223,0.879,232,1.717,236,1.435,240,0.854,246,1.066,255,1.052,275,0.483,277,0.803,278,0.56,283,0.452,285,2.516,286,1.709,289,1.523,295,0.628,296,0.602,302,0.719,304,0.329,323,0.63,324,0.827,328,1.861,337,0.727,338,0.948,343,0.556,347,1.031,351,0.565,353,0.769,372,1.442,377,1.485,392,0.969,398,0.55,401,0.597,407,0.706,409,1.953,414,0.896,416,0.719,426,2.452,430,0.77,444,0.677,463,0.94,472,1.373,476,2.302,482,0.698,483,0.985,484,1.633,500,0.739,511,1.229,512,2.456,517,0.947,525,0.736,527,1.734,528,0.823,530,0.698,536,2.248,545,0.722,555,0.739,560,1.501,565,2.589,568,1.563,575,2.243,578,0.614,580,0.542,601,1.324,605,1.709,611,1.172,614,1.522,621,0.803,622,0.788,629,0.994,630,2.016,635,0.567,647,1.058,653,1.718,681,1.764,683,0.584,685,0.869,723,1.008,731,0.819,736,1.644,743,1.031,756,0.733,757,2.244,759,2.135,760,1.139,769,1.481,780,0.831,784,1.145,803,1.588,807,2.109,811,0.969,827,0.791,828,0.651,832,1.052,841,0.722,846,1.252,855,1.987,857,1.749,874,0.767,878,0.666,880,0.673,930,0.854,936,2.841,940,2.485,942,3.846,1009,0.719,1046,0.73,1059,1.578,1069,2.085,1075,1.639,1114,2.476,1139,0.836,1169,0.994,1170,1.183,1174,1.041,1181,0.811,1216,0.795,1223,2.184,1239,0.961,1240,0.89,1242,1.245,1245,1.562,1266,2.484,1312,0.574,1363,2.855,1367,0.757,1380,0.985,1381,0.619,1397,0.947,1408,1.712,1441,1.697,1454,2.392,1476,1.036,1493,1.794,1498,1.115,1530,0.94,1616,1.569,1633,0.736,1671,0.859,1689,0.819,1733,0.933,1863,1.18,1902,0.745,1906,0.849,1907,0.807,1960,1.041,2027,0.977,2028,3.074,2031,3.147,2032,0.657,2033,0.879,2035,1.335,2061,0.527,2075,1.302,2092,0.874,2159,1.375,2353,0.933,2606,1.222,2627,1.272,2633,0.859,3051,1.052,3632,1.697,3649,2.296,3664,0.961,3715,2.962,3866,1.463,3916,1.041,3917,0.994,3942,1.794,3983,1.423,3997,1.031,4038,1.335,4056,1.18,4184,1.482,4185,1.101,4186,1.101,4187,1.101,4188,1.101,4189,1.101,4190,1.101,4191,1.101,4192,1.2,4193,1.423,4194,1.482,4196,1.482,4197,1.482,4198,2.676,4199,1.482,4200,1.482,4201,1.482,4202,1.423,4203,1.482,4204,1.482,4205,1.482,4206,1.482,4207,1.482,4208,1.482,4209,1.482,4210,1.482,4211,1.482,4212,1.482,4213,1.482,4498,1.2,5212,1.562,5213,1.562]],["t/3417",[7,0.702,23,1.378,24,1.876,27,0.98,28,1.362,31,1.009,48,1.675,49,0.928,56,0.879,84,0.855,88,1.859,89,3.323,98,1.451,103,1.381,116,2.398,128,1.969,132,1.212,163,2.547,171,1.122,177,1.993,182,2.15,198,2.01,219,1.278,221,1.525,225,1.476,232,2.337,285,1.706,286,2.502,302,1.901,304,1.354,313,1.009,322,1.291,328,2.724,343,1.468,377,0.923,401,1.578,444,1.79,472,2.01,502,1.138,512,3.523,527,1.405,545,1.908,555,1.953,565,2.576,575,3.052,614,1.776,630,2.373,653,2.803,670,2.399,681,2.582,736,1.76,756,1.938,757,1.944,769,3.422,780,2.197,807,1.595,841,1.908,857,1.873,936,1.766,942,4.075,946,4.443,1069,4.268,1075,2.399,1170,1.267,1216,2.102,1245,2.126,1266,2.786,1363,3.207,1376,2.063,1408,3.503,1476,2.361,1557,2.561,1616,2.297,1633,1.945,1671,2.27,1689,2.164,1754,1.755,1765,2.368,1906,3.495,1907,2.132,2100,2.985,2672,2.698,2696,2.78,3632,3.867,3664,3.955,3693,5.355,3916,2.752,3917,2.626,4185,2.909,4186,2.909,4187,2.909,4188,2.909,4189,2.909,4190,2.909,4191,2.909,4192,3.172,4214,5.495,4215,3.53,4216,3.027,4217,3.634,4218,2.946]],["t/3419",[7,0.484,23,2.05,24,1.52,28,1.407,34,0.992,48,1.58,49,0.875,56,0.751,63,2.993,84,0.823,88,1.754,89,3.219,98,1.369,103,1.303,116,2.291,163,3.012,171,1.058,177,2.965,182,2.055,198,1.897,203,1.47,219,1.206,221,1.439,228,1.234,232,2.765,283,1.776,285,1.609,286,2.361,295,1.566,304,1.294,322,1.218,328,2.571,338,0.958,343,1.385,377,1.373,401,1.489,405,1.239,426,1.439,444,1.689,472,1.897,476,2.895,491,1.174,502,1.693,512,3.599,527,1.342,536,1.829,545,1.8,558,2.746,565,2.005,575,3.61,577,5.117,614,1.728,630,2.268,653,2.715,683,1.457,736,1.661,756,1.828,757,1.873,780,2.073,796,2.042,841,1.8,936,3.252,940,2.297,941,2.856,942,4.211,1037,3.288,1046,1.821,1069,4.111,1170,1.885,1245,2.032,1266,3.035,1363,3.38,1476,2.256,1616,2.167,1633,1.836,1689,2.042,1754,1.656,1906,3.34,1907,2.012,2606,3.047,2672,2.546,2836,2.993,3632,3.695,3694,5.117,3942,3.906,4185,2.745,4186,2.745,4187,2.745,4188,2.745,4189,2.745,4190,2.745,4191,2.745,4216,2.856,4218,2.78,4219,5.25,4220,3.696,4221,3.696,4222,3.696,4223,3.696]],["t/3421",[5,0.703,7,0.559,21,0.564,23,0.666,24,1.238,25,1.502,27,0.473,28,1.38,34,1.195,36,1.546,39,0.812,41,1.591,48,0.809,49,1.055,56,1.028,68,0.756,84,0.697,88,0.898,89,2.107,98,0.701,103,1.173,105,1.097,107,2.791,109,1.786,116,1.751,123,0.68,125,1.137,128,1.672,132,1.378,133,0.853,134,0.993,140,1.374,162,0.701,163,2.238,164,0.548,165,1.051,170,2.633,171,1.534,182,1.173,183,0.856,190,1.025,198,0.971,203,1.123,219,1.085,221,0.737,232,1.275,236,0.922,240,1.091,246,0.754,256,0.746,283,0.577,285,0.824,286,1.209,295,1.41,302,0.918,304,0.739,313,0.487,322,1.468,328,1.316,336,0.812,338,1.744,343,1.247,377,0.784,391,0.84,394,1.753,401,0.762,405,2.255,408,1.266,414,1.144,416,0.918,426,2.376,444,0.865,472,0.971,476,2.058,491,0.601,502,0.55,505,1.453,511,1.496,512,2.802,527,1.55,536,2.75,545,0.922,550,0.784,557,0.993,565,1.532,568,0.571,575,2.229,580,0.693,605,0.722,614,1.573,623,0.955,629,1.269,630,1.295,647,2.413,653,2.232,660,1.484,663,1.218,681,2.193,723,1.253,724,2.285,736,1.495,756,1.646,757,1.885,760,1.036,769,1.348,772,1.316,780,1.062,820,1.025,828,1.462,841,0.922,857,1.591,877,0.727,880,1.51,919,1.373,936,2.007,940,2.113,942,4.229,947,1.015,981,1.237,993,1.152,997,1.298,1009,0.918,1046,0.933,1069,2.538,1075,2.038,1139,1.067,1170,1.077,1216,1.015,1223,1.13,1245,1.16,1266,2.43,1289,1.419,1352,0.959,1363,2.754,1367,0.967,1408,2.508,1441,1.2,1476,1.724,1616,1.951,1633,0.94,1671,1.097,1689,1.046,1822,1.2,1864,1.847,1870,2.468,1899,1.389,1906,1.907,1907,1.03,2013,1.258,2293,4.599,2296,4.141,2672,1.304,2839,5.817,3053,2.362,3632,3.398,3644,0.735,3664,1.228,3695,4.706,3725,1.423,3817,1.269,3880,1.304,3916,1.33,3917,1.269,3942,2.231,3943,5.448,3944,1.533,3952,4.012,4185,1.406,4186,1.406,4187,1.406,4188,1.406,4189,1.406,4190,1.406,4191,1.406,4216,1.463,4218,1.423,4224,4.453,4225,1.624,4226,2.856,5007,2.695,5008,2.571,5009,2.65,5064,1.756,5214,1.624,5215,1.705,5582,1.995]],["t/3423",[7,0.548,21,0.767,23,0.906,24,0.959,25,0.869,27,0.644,28,1.38,34,0.691,36,1.197,41,2.075,48,1.101,49,1.028,56,1.026,84,0.495,88,1.223,89,2.595,92,1.005,98,0.954,103,0.908,105,0.849,107,0.695,109,1.866,116,1.706,127,3.906,128,1.295,132,1.343,133,1.161,140,1.064,162,0.954,163,3.076,164,0.746,165,1.024,171,1.889,172,0.94,177,2.208,179,0.908,182,1.53,183,1.963,193,1.663,198,1.322,203,1.095,219,0.84,221,1.003,232,1.663,256,1.016,257,1.382,275,0.84,279,1.173,285,1.122,286,1.645,295,1.092,304,0.964,322,2.631,328,1.792,336,1.861,338,1.125,343,0.966,351,0.982,359,1.197,377,1.325,391,1.925,394,2.286,401,1.038,405,2.212,426,1.689,444,1.177,472,1.322,476,2.839,502,0.749,512,3.085,527,1,536,2.665,545,1.255,565,2.534,575,2.814,605,0.982,614,1.861,615,1.588,623,1.3,630,1.689,653,2.188,681,1.698,732,1.774,736,1.158,755,1.223,756,1.274,757,2.073,780,1.445,841,1.255,857,2.688,876,1.848,930,1.485,936,1.161,942,3.552,999,1.476,1046,1.269,1069,3.205,1075,1.578,1170,1.404,1245,1.513,1266,2.667,1274,1.658,1289,1.098,1363,2.794,1476,1.68,1616,1.51,1633,1.279,1689,1.423,1754,1.944,1765,1.557,1870,3.404,1906,2.487,1907,1.402,2092,1.519,2282,1.809,2291,5.794,2295,5.173,2672,1.774,3577,2.262,3632,4.183,3644,1,3696,5.794,3817,1.727,3942,2.909,4185,1.913,4186,1.913,4187,1.913,4188,1.913,4189,1.913,4190,1.913,4191,1.913,4216,1.99,4218,1.937,4225,2.211,4226,3.724,4227,5.216,4228,2.39,4229,2.576,4230,2.165]],["t/3425",[7,0.679,21,1.345,23,0.95,24,0.993,26,1.057,27,0.676,28,1.125,31,1.162,36,1.255,37,1.897,39,1.158,48,1.155,49,1.069,56,1.016,84,0.863,88,1.282,89,2.674,98,1,103,0.952,107,0.729,109,1.734,116,1.776,127,0.933,128,1.357,132,0.836,162,1,163,1.886,164,0.782,165,0.638,170,3.121,171,1.947,177,1.374,182,1.592,183,1.222,190,1.463,198,1.386,219,0.881,221,1.051,225,1.701,228,0.902,232,1.731,240,1.557,247,1.251,285,1.176,286,1.725,302,1.311,304,1.003,313,0.695,322,1.918,323,1.919,328,1.878,336,1.158,343,1.012,353,0.776,377,0.637,391,1.198,401,1.088,414,1.633,444,1.234,472,1.386,484,1.206,491,0.858,502,0.785,512,2.971,527,1.04,536,2.566,545,1.316,555,1.347,565,2.602,568,0.815,575,2.26,605,1.03,614,1.586,623,1.363,630,2.264,653,2.509,670,1.654,677,1.5,681,1.78,736,1.214,756,1.336,757,1.964,769,3.312,780,1.515,807,1.1,830,1.301,841,1.316,857,1.291,936,1.218,942,3.366,946,4.977,953,1.151,958,1.86,992,1.423,1009,1.311,1069,3.316,1075,1.654,1170,1.461,1210,2.187,1216,1.449,1245,1.574,1266,2.494,1289,1.151,1300,1.86,1312,1.046,1363,2.58,1376,1.423,1408,2.722,1453,2.593,1476,2.253,1527,1.878,1557,1.766,1616,1.584,1633,1.341,1671,1.565,1688,1.937,1689,1.492,1754,2.022,1765,1.633,1870,2.999,1902,4.573,1906,2.588,1907,1.47,1916,1.7,2013,1.795,2100,2.058,2672,1.86,2696,1.917,2717,1.86,3199,2.434,3632,2.863,3644,1.049,3664,2.929,3916,1.897,3917,1.811,4185,2.006,4186,2.006,4187,2.006,4188,2.006,4189,2.006,4190,2.006,4191,2.006,4192,2.187,4216,2.087,4217,2.506,4218,2.031,4225,2.318,4226,3.875,4227,2.506,5060,6.126,5216,7.165,5217,4.758,5218,4.758]],["t/3427",[24,1.171,27,1.717,28,1.362,56,1.072,84,0.783,107,1.852,113,2.637,127,2.372,196,2.867,343,2.573,405,4.097,426,4.279,495,3.492,511,3.462,512,2.494,536,2.874,565,2.361,568,2.764,577,3.422,583,4.417,647,2.713,653,2.277,729,5.162,757,1.782,936,3.094,940,4.063,946,4.443,958,4.728,981,4.487,1363,2.604,1381,2.867,1693,4.231,1902,3.449,2550,5.23,3795,5.162,5214,5.89,5215,6.184]],["t/3429",[5,3.555,7,0.61,23,1.763,24,0.855,28,1.424,41,2.395,56,0.997,70,3.768,84,0.572,89,3.42,106,2.837,107,1.352,113,2.822,116,1.97,127,1.731,128,2.518,164,2.126,178,6.055,196,2.093,228,2.452,239,3.029,256,2.896,289,3.059,343,1.878,407,2.387,408,1.906,426,2.859,433,3.048,495,4.425,511,3.58,512,1.82,536,2.305,565,1.724,568,2.217,577,2.498,615,3.089,635,1.916,647,2.904,653,1.662,712,2.84,756,2.478,757,1.301,769,2.029,807,4.834,878,2.251,936,2.259,940,4.02,946,3.564,981,6.261,1050,3.419,1266,1.44,1282,4.057,1381,2.093,1693,3.089,1865,4.299,1876,3.419,1902,2.518,2525,7.835,3624,4.057,3795,3.768,4232,8.068,4314,4.4,5219,7.741,5220,4.809]],["t/3431",[7,0.807,16,0.876,24,1.151,28,1.003,56,1.257,84,0.77,88,2.12,96,1.658,107,1.205,109,0.794,127,2.331,140,1.845,162,1.654,164,1.293,165,1.592,171,1.279,177,3.432,179,2.378,219,3.169,277,2.42,296,1.814,322,2.678,377,1.053,392,2.921,491,2.142,502,1.96,536,3.631,550,1.85,565,2.321,567,4.812,577,2.227,653,1.482,672,4.187,734,2.083,798,2.589,818,3.569,884,4.483,916,2.736,946,2.168,996,2.969,1040,3.24,1170,1.445,1224,3.021,1312,2.611,1363,2.559,1476,1.73,1870,2.98,1933,3.138,1963,3.404,2028,3.754,2032,2.992,2291,3.923,2293,3.834,2295,3.503,2296,3.452,2297,3.41,3016,2.833,3597,3.923,3632,6.16,3649,3.834,3693,3.923,3694,3.923,3695,3.923,3696,3.923,4214,4.025,4219,4.025,4233,5.668,4234,4.145,4235,4.468,4236,9.014]],["t/3433",[7,0.556,24,1.537,36,3.112,56,1.059,68,2.676,84,1.297,98,2.48,113,3.912,203,1.69,304,2.418,322,2.208,346,5.335,398,2.486,401,2.698,482,3.156,502,1.946,536,2.827,543,3.974,565,2.305,567,3.807,577,3.339,578,2.774,580,2.451,614,1.299,622,3.56,756,3.313,940,3.552,1239,4.345,1266,2.925,1312,2.594,1540,3.905,1607,4.614,1638,3.645,1737,2.662,1765,4.049,1801,4.024,3655,5.176,4158,5.882,4237,6.698,4238,9.01,4239,6.698,5561,6.429,5723,7.606]],["t/3435",[7,0.852,15,3.141,16,1.603,88,3.879,103,2.882,113,3.141,199,4.248,225,3.079,226,5.239,243,4.711,292,3.746,377,1.926,491,2.596,511,2.746,536,2.565,550,3.385,565,3.528,567,4.332,593,4.248,672,5.073,719,5.182,798,4.737,856,5.145,940,3.223,1179,5.301,1302,5.578,2009,5.928]],["t/3437",[7,0.585,17,3.878,28,1.651,84,0.803,89,2.767,105,2.319,190,3.811,219,3.768,232,2.697,283,2.144,294,3.989,377,1.658,461,4.15,536,3.724,552,2.625,567,5.014,653,3.088,731,3.887,757,2.884,852,3.215,2032,4.629,3016,6.616,4240,7.037,4241,11.105]],["t/3439",[2,1.225,4,2.146,7,0.795,23,1.517,24,1.12,25,1.455,27,1.642,28,1.182,56,1.049,84,1.233,88,2.046,89,1.696,109,0.766,113,2.523,127,1.49,132,1.335,163,1.801,165,1.55,171,1.234,174,1.351,177,2.194,182,1.52,193,1.653,219,2.142,223,2.558,232,1.653,240,2.485,275,2.142,278,1.628,283,1.314,285,1.877,294,2.444,313,1.69,322,1.421,338,1.118,447,2.15,472,4.08,476,2.719,498,2.528,502,1.253,512,3.475,525,2.141,527,0.993,536,3.163,558,3.094,565,1.484,567,2.774,568,1.301,575,5.044,614,0.836,736,3.573,755,2.046,757,1.705,765,2.695,773,3.554,796,2.382,816,2.574,877,1.657,893,2.623,936,1.944,940,1.7,997,1.682,1006,2.324,1046,2.124,1069,4.532,1075,2.641,1114,3.801,1196,3.623,1363,3.824,1366,2.528,1382,2.714,1408,3.072,1435,3.06,1446,2.658,1476,1.67,1616,5.908,1907,2.347,1945,3.029,2033,2.558,2297,1.514,2711,2.942,3662,4.139,3916,3.029,3942,2.89,3997,2.999,4242,4.312,4243,4.312,4244,4.312]],["t/3441",[2,1.505,7,0.635,21,2.927,24,0.904,26,2.072,36,3.554,49,2.469,56,0.551,84,1.19,96,3.332,98,3.324,107,2.422,109,1.359,113,2.939,127,1.83,140,2.187,160,1.957,164,1.533,165,1.25,203,2.264,304,1.176,313,1.364,336,2.271,338,1.373,353,1.522,377,1.248,502,1.539,509,3.162,536,3.902,577,4.475,578,2.193,604,3.683,629,3.55,653,2.977,692,4.451,756,2.62,805,3.799,876,3.799,885,4.594,1046,4.843,1114,2.532,1224,3.582,1289,2.258,1312,3.476,1779,3.759,1792,5.916,1870,3.824,1899,3.886,2032,3.393,4245,7.648]],["t/3443",[4,2.752,7,0.699,48,3.6,49,1.994,56,1.087,84,0.961,140,3.477,171,2.411,203,2.124,272,3.633,295,3.568,322,2.776,536,2.643,613,4.011,614,2.027,731,5.77,802,3.589,828,4.591,2297,2.956,2963,6.333,3644,3.269,4246,7.856]],["t/3445",[2,0.93,4,1.728,7,0.553,12,1.391,24,1.303,29,1.203,36,1.521,49,1.252,56,1.242,68,1.308,84,0.957,96,1.215,98,2.463,105,1.079,179,1.154,196,1.367,203,1.678,219,2.492,254,1.109,304,1.696,322,1.743,336,1.403,346,2.606,372,2.623,377,1.246,391,1.452,395,1.363,398,2.469,401,1.318,444,1.495,472,1.679,482,2.492,502,0.951,536,3.597,543,1.942,565,1.819,567,3.227,577,1.632,580,1.935,623,1.651,647,2.63,653,1.086,736,1.471,757,1.727,780,4.285,914,1.716,940,2.085,942,2.353,946,1.588,1046,2.605,1164,1.739,1170,3.512,1239,2.123,1266,2.823,1289,1.395,1312,4.204,1363,2.899,1476,1.267,1540,1.908,1607,2.254,1616,1.919,1638,1.781,1689,1.808,1765,4.022,1792,2.157,1854,3.141,1906,1.876,1907,1.781,2031,2.299,2032,3.389,2275,2.65,2297,1.149,2606,2.698,2608,2.566,3016,3.352,3632,2.075,3655,2.529,3866,4.585,4004,2.566,4005,2.75,4158,2.874,4215,2.949,4246,2.461,4247,5.075,4248,5.287,4249,2.949,4250,3.273,4251,2.949,4252,3.273,4253,2.949,4254,3.273,4255,2.949,4256,3.273,4257,2.949,4258,5.287,4259,2.949,4260,3.273,4261,2.949,4262,3.273,4263,3.141,4264,3.273,4265,3.273,4266,3.273,4267,3.273,4268,5.287,4269,3.273,4590,2.606,5561,3.141,5719,3.449,5724,3.716,5725,3.716]],["t/3447",[1,4.256,7,0.577,24,1.77,49,1.646,56,1.23,84,1.184,171,1.99,272,2.998,322,3.043,372,2.74,377,2.176,395,2.894,397,4.045,430,3.612,482,3.275,536,3.787,550,2.878,565,2.391,611,3.044,874,3.596,916,4.256,946,4.48,1046,3.424,1170,2.248,1289,2.962,1397,4.439,1454,6.037,1792,4.58,2297,3.241,4246,6.944,4270,9.234,4271,6.949]],["t/3449",[2,1.457,7,0.426,12,2.178,36,2.382,49,1.769,56,1.225,84,1.105,107,2.377,109,1.72,127,3.556,162,1.898,165,1.21,179,1.807,183,2.318,275,1.672,296,2.082,304,1.138,313,1.32,322,2.904,336,3.778,391,3.909,536,3.48,614,1.709,647,2.026,757,1.331,827,2.737,946,4.275,1170,2.416,1289,3.184,1844,3.299,1863,3.884,1870,4.424,1902,4.428,2297,1.799,3999,9.678,4000,9.678,4096,4.501,4097,4.501,4246,3.855,4249,4.618,4251,4.618,4255,4.618,4257,4.618,5061,9.292]],["t/3451",[2,1.52,7,0.64,49,1.824,56,1.287,84,1.127,85,1.952,109,1.937,162,1.98,165,1.262,179,2.715,296,2.171,313,1.377,322,1.762,336,2.293,391,2.372,452,3.878,502,1.554,536,3.284,577,6.063,614,1.037,705,3.468,757,2,827,2.855,914,2.804,1037,3.966,1170,2.92,1289,2.279,1754,2.395,1844,3.442,1870,4.36,2275,4.33,2297,1.877,4004,4.193,4005,4.493,4246,4.021,4253,6.939,4259,6.939,4261,4.818,4272,4.818,4273,5.347,4274,5.347,4275,9.025,4276,5.347,4277,5.347,4278,5.347]],["t/3453",[2061,3.73]],["t/3456",[4,2.626,7,0.775,16,1.575,18,2.628,24,1.592,25,2.711,28,1.508,34,2.156,46,2.478,56,0.836,84,0.917,101,3.183,132,1.756,145,2.404,171,2.67,196,2.37,219,3.492,232,2.175,304,1.784,322,1.87,337,2.785,351,2.164,476,1.736,478,3.624,484,2.533,502,2.335,536,1.781,552,3.479,558,4.395,580,2.076,621,3.073,664,4.384,734,2.646,756,2.807,757,2.422,805,4.07,852,2.592,940,2.237,1014,7.968,1059,3.347,1234,3.387,1240,3.409,1312,4.304,1445,4.163,1737,2.255,1840,8.034,1882,4.987,2297,1.992,3644,2.203,5141,5.674]],["t/3458",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,862,5.023,4328,6.948]],["t/3460",[2061,3.73]],["t/3462",[4,1.893,5,2.15,7,0.896,16,1.136,19,3.828,23,2.038,24,1.61,28,1.522,56,0.603,84,0.661,89,2.278,96,2.15,109,1.029,145,2.455,163,2.419,164,2.36,170,1.803,174,1.814,186,2.876,219,1.889,224,2.586,232,3.924,274,5.086,275,1.889,278,2.187,283,1.765,284,4.111,291,3.595,313,1.491,361,4.111,400,4.775,421,3.818,491,2.996,545,2.821,550,2.399,552,2.16,555,2.888,558,4.824,596,2.749,601,2.865,683,2.284,757,2.117,765,3.62,887,5.418,888,3.595,914,3.037,934,4.097,936,2.611,947,3.108,948,3.99,1007,4.111,1036,4.028,1202,3.216,1212,4.028,1308,3.62,1313,5.792,1315,3.357,1451,5.219,1508,3.376,1782,4.477,1974,5.374,2033,3.437,4317,5.792]],["t/3464",[2,1.621,4,1.864,7,0.845,16,1.118,23,2.006,24,1.376,25,1.924,27,1.427,28,1.512,31,2.076,56,0.839,84,1.068,89,2.243,103,2.011,113,2.192,123,2.048,165,1.346,170,1.776,173,3.216,174,1.786,216,3.06,217,3.426,219,1.86,232,3.897,247,2.641,276,2.977,289,2.375,306,2.669,318,2.4,337,2.799,353,1.639,363,4.289,409,3.045,491,2.561,500,2.843,502,2.343,511,1.916,513,2.832,530,2.688,552,2.127,558,2.688,562,2.952,565,1.962,567,2.41,568,1.721,577,2.843,580,2.087,632,3.233,635,2.18,663,3.671,666,4.701,683,2.249,685,3.344,799,4.542,814,2.588,821,4.185,852,4.9,877,2.192,881,4.407,887,5.359,934,2.866,935,4.618,1040,4.137,1461,5.474,4318,5.703,4319,5.703,5194,6.01]],["t/3466",[2,1.387,7,0.838,16,0.957,24,1.613,25,3.403,27,1.802,28,1.623,39,3.089,50,3.008,84,0.822,89,1.919,105,1.608,130,2.931,131,2.332,132,1.51,133,2.199,145,2.067,153,2.63,163,2.038,164,1.412,165,1.152,168,7.519,175,2.812,190,2.642,198,3.696,232,1.87,275,2.349,304,2.24,313,1.256,323,2.073,336,2.092,337,2.394,338,1.265,339,2.593,351,1.861,353,1.402,367,3.864,483,3.242,491,1.55,544,3.216,558,4.455,591,4.327,611,2.137,614,1.397,621,2.642,635,1.865,751,2.968,765,3.049,778,3.242,780,2.737,802,3.07,830,2.35,852,2.229,857,2.332,877,1.875,953,2.08,992,2.57,1048,4.1,1073,6.525,1181,2.668,1366,2.86,1689,3.979,1782,3.418,1798,6.189,1799,6.21,1819,4.871,1933,3.427,2103,3.951,2741,5.937,4050,4.186,5195,7.59,5196,7.59,5197,5.141]],["t/3468",[2061,3.73]],["t/3470",[34,2.593,56,1.005,84,1.103,257,5.184,278,3.649,279,4.4,295,4.095,545,4.706,992,5.09,4311,9.663]],["t/3472",[4,1.668,7,0.802,15,1.961,16,1.894,21,2.216,24,1.5,28,1.307,41,2.439,46,3.838,49,1.208,56,0.531,84,1.103,96,1.894,125,3.065,138,3.735,164,1.477,165,1.204,171,1.461,179,3.622,183,2.308,189,2.877,203,1.287,207,2.194,245,3.212,282,2.924,283,2.268,305,3.42,306,2.387,337,2.504,338,1.323,343,1.912,353,1.466,377,1.754,409,2.725,416,2.476,440,2.371,441,5.036,449,4.828,534,5.598,536,2.336,545,2.485,558,2.404,559,8.814,560,4.929,565,2.561,596,3.533,687,4.48,744,5.803,747,2.208,757,1.325,827,4.693,898,3.803,929,3.66,934,2.564,1027,4.378,1028,5.177,1444,3.584,1997,4.132,1998,3.125,2031,3.584,4110,7.693,4135,7.384,4312,5.102,4313,5.102]],["t/3474",[18,4.288,28,1.377,56,0.964,84,1.057,113,3.559,253,5.872,283,2.822,530,5.216,545,5.391,583,5.961,1129,6.104,1234,5.529,4314,8.132]],["t/3476",[7,0.739,21,2.648,23,3.128,84,1.015,88,4.22,89,3.497,283,2.709,353,2.555,359,4.131,391,3.944,472,4.562,476,2.72,512,3.23,545,5.261,575,4.451,614,1.725,1385,7.329,1449,7.2,3725,6.687]],["t/3479",[171,2.866,232,3.837,304,2.223,545,4.876,1737,3.978,3644,3.886]],["t/3481",[4,3.105,17,3.956,84,1.084,219,3.098,237,7.339,284,6.741,304,2.109,545,4.626,1059,5.602,2958,9.117,4315,9.498,4316,9.498]],["t/3483",[2061,3.73]],["t/3486",[4,2.645,7,0.779,16,1.587,18,2.655,24,1.601,25,2.73,28,1.515,34,2.172,46,2.504,56,0.842,84,0.924,101,3.216,132,1.774,145,2.429,171,2.685,196,2.394,219,2.64,232,2.197,304,1.797,322,1.89,337,2.814,351,2.186,476,1.754,478,3.662,484,2.559,502,2.352,536,1.799,548,3.718,558,4.42,580,2.098,621,3.105,664,4.43,734,2.673,756,2.836,757,2.436,805,4.112,852,2.619,940,2.26,948,7.019,1014,7.989,1059,3.381,1234,3.422,1240,3.444,1312,4.319,1445,4.206,1737,2.278,1787,5.318,1882,5.023,2297,2.012,3644,2.225,5143,5.733]],["t/3488",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,862,4.662,1693,5.482,4328,6.449]],["t/3490",[2061,3.73]],["t/3492",[7,0.644,21,1.231,24,1.691,25,1.395,27,2.734,28,0.946,29,2.338,48,1.768,56,1.25,63,3.349,84,0.472,88,1.963,89,3.692,95,4.158,106,1.597,107,2.09,108,2.251,109,1.13,112,2.159,113,1.589,128,3.196,135,3.82,164,1.197,165,1.829,169,3.665,193,3.598,203,1.043,232,2.437,294,2.345,296,2.583,306,1.935,336,1.773,343,1.55,377,0.975,391,1.835,446,4.263,447,2.062,476,1.265,483,2.749,496,2.662,505,1.806,507,4.441,512,2.311,558,1.949,635,1.581,647,1.635,653,2.109,659,2.822,672,2.567,740,5.316,767,3.349,807,3.155,846,1.935,852,3.973,884,4.227,899,3,900,2.849,946,3.759,980,3,986,2.284,1008,2.642,1047,4.877,1087,5.065,1266,1.827,1289,2.711,1352,3.926,1373,2.662,1381,2.656,1397,2.642,1408,1.935,1409,3.726,1465,3.151,1539,2.905,1622,2.797,1729,5.344,1730,3.549,1884,2.876,1964,2.967,3542,2.935,3553,3.294,3796,3.349,4879,3.294]],["t/3494",[2061,3.73]],["t/3496",[7,0.502,10,3.445,16,1.185,19,2.838,21,1.8,26,2.364,29,2.222,56,1.004,68,2.415,84,0.959,85,2.206,105,1.992,109,1.074,127,2.089,177,3.075,219,3.402,221,3.268,254,2.048,275,1.971,295,2.561,296,2.454,304,1.342,318,3.044,377,1.424,397,2.648,398,3.117,476,2.569,484,2.698,502,1.756,512,2.196,513,3.001,562,3.128,567,2.554,580,2.211,653,2.005,707,3.544,736,2.716,757,2.708,853,4.813,897,5.079,906,3.861,945,3.322,1037,3.373,1107,3.355,1284,3.114,1361,5.607,1638,4.57,1882,3.752,2026,3.258,2032,3.725,2312,4.736,2495,5.307,2633,3.503,2636,3.654,2711,4.124,5726,6.863]],["t/3498",[2,1.52,7,0.82,25,1.804,49,1.824,56,0.939,84,0.879,113,2.055,132,1.655,203,1.943,219,1.744,228,2.571,246,2.131,256,2.108,302,2.595,313,1.377,316,2.511,318,3.12,351,2.039,353,1.537,393,2.767,398,4.267,482,2.52,491,2.446,536,1.678,543,3.173,614,1.494,615,3.297,621,2.896,683,2.108,723,2.014,724,2.744,731,2.954,757,1.388,822,4.848,831,3.554,1006,2.882,1059,3.154,1060,3.935,1114,3.682,1133,5.116,1263,3.416,1290,3.135,1312,3.494,1356,5.132,1540,3.117,1633,2.655,1898,4.275,2026,4.865,2032,3.416,2297,1.877,2312,5.089,3036,4.696,3042,3.297,3749,3.923,3753,4.818,4088,4.818,4556,5.347,4557,5.347,4570,6.939,5727,6.072]],["t/3500",[16,1.878,26,3.748,85,3.497,127,3.311,279,4.362,283,2.919,318,2.458,381,4.776,395,3.99,828,4.21,1037,3.848]],["t/3502",[2,0.93,4,1.07,7,0.696,19,1.537,25,1.104,27,1.911,28,0.786,29,1.203,31,0.843,40,1.817,49,0.775,56,1.193,68,1.308,84,0.759,100,1.542,105,1.079,109,1.745,127,3.814,165,1.57,172,1.93,177,1.665,179,1.154,182,2.346,193,3.437,219,1.067,221,2.058,277,1.772,279,1.49,295,1.387,313,0.843,318,1.96,338,0.848,376,1.966,377,1.568,388,2.494,395,2.202,398,2.835,405,3.006,418,1.724,472,1.679,476,3.187,502,0.951,511,1.776,512,1.921,552,1.972,567,1.383,575,3.33,578,1.355,614,0.635,647,2.09,653,2.207,723,1.992,757,2.177,803,1.942,818,1.732,828,1.438,857,1.565,936,2.999,940,2.085,942,3.399,945,2.906,953,1.395,986,2.921,988,3.118,1006,1.764,1037,1.315,1069,1.865,1075,2.004,1107,2.935,1122,2.157,1163,2.09,1170,2.152,1266,2.195,1289,1.395,1306,3.485,1363,3.581,1381,2.778,1476,2.047,1530,2.075,1539,2.299,1616,3.1,1671,1.897,1753,2.374,1765,1.978,1870,1.939,1907,1.781,2026,2.85,2032,2.951,2054,4.085,2312,3.751,2495,4.643,2633,1.897,3542,3.752,3878,2.65,3942,2.194,3997,2.276,4202,3.141,4230,2.75,4558,3.273,4559,3.273,4561,3.273,4563,3.273,4565,3.273,4566,3.273,4580,2.529,4586,3.273,4590,2.606,5728,3.716,5729,3.716]],["t/3504",[2,2.04,4,1.588,7,0.403,25,2.422,26,1.9,28,1.068,40,2.697,56,0.889,68,1.941,84,0.82,98,2.659,109,1.937,127,3.95,165,1.147,172,1.773,179,1.713,182,2.532,193,3.273,199,2.525,219,1.584,221,1.89,313,1.251,318,1.842,338,1.259,372,1.915,398,2.665,405,1.628,476,3.424,508,3.015,512,2.608,567,2.052,575,4.275,614,0.942,653,2.381,757,2.218,803,2.882,857,2.322,936,3.85,940,2.831,942,3.195,945,2.67,1006,2.618,1069,2.769,1075,2.975,1111,5.883,1170,2.323,1266,2.454,1306,3.201,1363,3.82,1476,2.78,1539,3.412,1616,4.21,1765,2.936,1907,2.644,1916,3.058,2026,2.618,2032,2.155,2054,3.753,2312,2.739,3542,5.095,3866,3.926,3942,3.256,3997,3.378,4230,4.082,4590,3.869,5730,5.516,5731,5.516,5732,5.516]],["t/3506",[4,2.568,7,0.653,16,0.829,24,0.722,27,1.058,28,1.41,34,1.135,49,1.002,56,0.44,68,1.69,84,0.483,96,1.57,107,2.968,109,1.564,115,2.052,116,1.663,132,1.309,144,2.135,163,1.767,170,2.446,179,1.491,182,1.491,196,1.767,203,1.632,213,2.775,295,1.792,313,1.089,338,1.677,397,2.834,476,2.692,512,2.351,527,2.304,552,1.578,560,4.407,575,2.117,580,1.548,614,1.255,647,3.749,653,2.146,663,4.164,725,2.269,736,1.901,740,2.015,757,1.098,940,2.551,942,4.632,966,3.369,997,3.066,1012,2.525,1046,2.084,1075,3.962,1170,2.093,1245,2.74,1266,2.874,1289,1.803,1363,3.34,1382,2.663,1408,3.027,1476,2.505,1616,3.794,1622,2.861,1863,1.865,1907,2.302,2669,3.369,2839,7.892,3053,4.592,3916,2.971,3942,2.835,3943,8.099,3944,3.425,4593,4.23,5007,5.239,5008,5,5009,5.153]],["t/3508",[4,3.432,28,1.561,109,1.866,116,2.803,182,2.513,203,2.368,213,4.026,338,1.847,397,4.113,527,2.744,560,5.267,647,2.817,653,2.364,757,1.851,807,2.902,942,4.178,1170,3.038,1245,3.274,1266,3.207,1352,6.153,1363,3.563,1476,2.76,1863,3.142]],["t/3510",[4,3.417,5,3.129,28,1.554,34,1.631,116,2.39,182,2.143,203,2.126,213,3.615,236,1.684,256,2.396,295,2.575,338,1.575,397,3.693,511,2.832,527,2.53,560,4.728,647,4.132,653,2.796,757,1.578,807,5.105,940,2.396,942,4.307,1170,2.727,1245,3.645,1266,3.155,1363,3.967,1476,3.264,1863,2.679,3736,4.313,3866,4.61,4594,6.077]],["t/3512",[4,3.288,5,2.428,24,1.716,28,1.496,31,1.684,39,2.805,84,1.012,116,2.572,144,3.301,182,2.306,203,2.236,213,3.802,232,2.507,338,1.696,367,3.509,397,2.865,414,3.954,527,2.596,560,4.973,647,2.586,653,2.17,757,1.698,769,4.367,807,2.664,852,2.988,942,2.911,946,5.766,1170,2.116,1216,3.509,1245,3.091,1266,2.89,1363,3.364,1476,2.533,1765,3.954,1863,2.883,2100,4.984,3664,4.242,3736,4.642]],["t/3514",[4,1.484,24,0.775,28,1.528,34,1.219,56,1.224,107,1.225,109,1.95,171,1.955,172,1.657,182,2.407,193,1.74,221,1.767,246,1.809,247,2.102,254,1.539,261,2.425,296,1.844,304,1.008,318,1.165,338,1.177,377,1.07,398,1.685,476,1.389,485,3.616,491,1.442,502,1.319,512,2.481,527,2.102,552,1.694,600,3.189,614,1.898,647,1.795,653,2.722,658,2.291,757,1.773,803,2.694,818,2.402,936,2.047,942,3.651,945,2.495,953,1.935,986,2.508,997,4.281,1170,2.952,1245,2.861,1257,4.181,1266,2.622,1363,3.9,1408,3.195,1409,6.152,1476,3.177,2026,2.447,2032,2.014,2312,2.56,4523,4.358,4599,6.827,4600,9.126,4601,9.126,4602,9.126,4603,6.827,4604,6.827,4605,6.827,4606,4.212,5575,4.541]],["t/3516",[4,1.986,17,2.531,28,1.253,56,1.182,106,4.037,109,2.019,137,5.701,182,2.972,221,2.365,254,2.059,318,1.559,338,1.575,377,1.432,398,2.256,444,3.851,476,1.859,502,1.766,527,2.23,600,4.268,614,1.179,638,3.853,653,2.796,757,2.189,818,3.215,936,2.739,942,2.704,945,3.34,997,4.079,1170,3.131,1245,2.119,1266,1.746,1363,3.967,1367,4.307,1389,5.269,1476,3.264,1671,4.886,2026,3.276,2032,2.696,2312,3.427,3010,4.359,4609,7.084,4610,7.595,5634,6.404]],["t/3518",[0,2.326,4,1.956,27,0.956,34,1.606,56,1.231,106,1.477,108,3.256,109,1.845,113,1.469,127,1.321,162,1.416,171,1.713,172,3.897,182,2.599,193,3.98,203,1.509,221,1.488,236,1.059,254,2.498,261,2.042,296,1.553,304,1.329,318,1.535,338,0.991,372,2.359,377,0.901,391,1.696,398,1.419,408,3.444,418,2.014,426,2.869,446,2.563,476,2.255,491,1.214,502,1.739,552,3.58,600,2.686,614,1.161,647,2.365,653,2.445,725,3.955,757,2.165,813,2.609,818,2.023,829,2.123,934,1.921,936,1.724,942,4.272,945,2.101,952,2.609,953,3.142,959,4.203,966,4.765,986,5.736,988,5.662,1057,2.609,1170,2.698,1238,2.407,1266,2.118,1306,3.944,1363,3.807,1476,3.229,1671,3.468,1765,2.311,2026,2.061,2032,1.696,2054,4.623,2312,2.156,2518,3.152,3008,2.998,3736,2.713,3866,2.091,4126,3.445,4615,3.823,5576,4.029]],["t/3520",[4,2.472,28,1.125,109,1.92,116,2.974,172,4.168,182,2.667,193,2.899,224,3.376,338,1.96,391,3.355,407,3.602,476,2.313,513,3.755,552,2.821,608,6.245,647,2.99,653,2.509,725,4.057,757,1.964,807,3.08,942,3.366,953,3.224,1170,3.157,1266,2.173,1363,3.703,1476,2.928,1863,3.334,1906,4.334,1953,4.334,3910,6.234,4616,7.563,4617,7.563]],["t/3522",[4,1.595,6,5.489,7,0.598,8,4.083,19,2.291,21,1.453,24,0.832,31,1.256,34,1.309,56,0.984,68,1.949,84,0.822,104,4.526,105,1.608,106,1.884,107,1.943,109,2.144,115,4.588,116,1.919,123,1.752,137,3.299,144,2.462,157,2.968,182,1.72,221,1.899,228,1.629,229,3.049,236,1.352,256,1.924,295,2.067,304,1.083,316,2.291,318,1.848,343,1.829,353,2.07,372,1.924,398,2.674,426,2.803,476,1.492,491,1.55,502,1.418,593,3.744,614,1.397,615,3.008,653,2.84,679,4.022,683,1.924,705,4.672,742,3.216,757,1.87,765,3.049,803,4.274,809,3.539,828,2.144,914,2.558,945,2.681,953,2.08,962,3.539,992,2.57,1037,1.96,1041,3.27,1170,2.77,1266,2.07,1363,4.005,1476,3.315,1605,3.77,1671,2.828,1874,3.58,2026,2.63,2032,2.164,2312,2.751,2684,3.164,3658,4.683,4609,4.1,4618,8.563,5209,5.141,5733,5.54]],["t/3524",[7,0.563,15,2.605,46,4.982,56,1.268,116,2.666,182,3.203,254,3.078,338,1.757,377,1.598,600,4.762,614,1.315,647,3.591,653,2.249,715,3.672,757,1.76,846,3.172,866,5.954,936,3.056,1037,2.723,1170,2.938,1363,3.447,1367,5.59,1408,4.793,1476,2.625,1863,2.989,3733,5.166,3866,3.708,4609,5.697,4610,6.109,4621,6.78,4622,6.78,4623,6.78,4624,6.78,4625,6.78,4626,6.78,4627,6.78,4628,6.78,4629,6.78]],["t/3526",[7,0.543,21,2.64,25,2.207,26,2.559,32,4.311,56,1.236,84,0.747,98,3.283,109,1.787,127,3.476,163,2.732,193,2.507,221,2.546,246,2.606,318,1.678,338,1.696,343,2.452,377,2.09,398,2.428,405,2.193,426,2.546,476,3.077,506,4.088,508,4.06,513,3.248,527,1.507,710,5.392,723,4.061,757,2.612,936,2.949,945,3.595,1164,3.477,1196,5.496,1431,5.496,1864,4.922,2026,3.526,2032,2.902,2312,3.688,2955,5.744,3038,6.278,5734,7.427,5735,7.427,5736,7.427,5737,7.427]],["t/3528",[7,0.728,19,4.114,20,4.236,25,2.169,28,1.592,96,2.386,103,2.266,219,2.858,221,2.502,225,2.421,226,5.469,243,3.704,256,2.534,278,2.427,280,4.309,296,2.61,306,3.008,318,3.089,366,4.236,472,3.298,511,2.159,512,2.335,513,3.192,562,3.326,567,2.716,607,2.596,611,2.816,615,3.963,683,2.534,757,2.588,877,2.47,888,3.99,906,4.106,957,5.644,1175,5.644,1177,5.791,1284,3.312,1454,4.202,1530,4.076,2024,6.169,2032,2.851,2357,6.169,2636,3.886,2711,4.386,4630,6.428,4631,6.428,4632,6.428,4633,6.428,4634,6.428,4635,6.428]],["t/3530",[2,1.47,7,0.924,17,2.154,25,1.745,28,0.769,34,1.388,56,0.922,84,1.23,85,1.888,88,2.455,89,2.034,103,2.651,109,0.919,127,1.788,134,2.713,171,1.481,179,1.824,182,1.824,202,3.752,219,2.889,226,2.643,232,1.983,247,2.396,272,2.232,283,1.576,296,2.101,304,1.149,306,3.518,313,1.332,318,2.923,322,1.705,338,1.341,353,1.487,397,2.266,401,2.084,418,4.665,421,3.41,438,2.829,471,2.259,476,1.582,502,1.503,511,2.526,526,3.942,534,3.89,567,4.985,575,3.764,591,3.108,605,1.973,614,1.459,672,5.498,723,3.336,734,2.412,802,2.205,828,2.273,884,3.438,916,3.168,1363,1.963,1476,2.911,1848,4.264,1851,4.264,2297,1.816,2518,2.725,3932,4.264,4636,7.519,4637,5.173,4638,5.173]],["t/3532",[7,0.795,15,3.191,16,1.166,25,2.006,56,1.174,84,0.679,89,2.339,103,2.928,108,3.237,145,2.52,164,2.77,196,2.484,201,3.19,243,3.427,272,2.566,279,2.708,318,1.526,336,4.441,377,1.402,401,2.395,405,3.472,418,3.132,471,2.597,511,1.998,517,3.799,532,3.268,562,3.078,567,4.376,723,2.24,757,1.544,877,2.285,971,3.986,1008,3.799,1033,3.986,1238,3.744,1331,4.136,1530,3.771,2112,4.997,4639,5.947,4640,5.947,4641,5.947,4642,5.947,4643,5.947,4644,5.947,4645,5.358,4646,5.947,4647,5.947,4648,5.947,4649,5.947,4650,5.947,4651,5.947,4652,5.947,4653,5.947,4654,5.947,4655,5.947,4656,5.947,4657,5.947,4658,5.947,4659,5.947,4660,5.947,4661,5.947,4662,5.947]],["t/3534",[7,0.673,15,3.471,28,1,31,1.146,48,1.902,56,1.312,85,2.455,103,1.569,105,1.467,109,1.195,127,2.324,140,1.837,161,3.192,164,1.288,171,1.274,179,1.569,225,3.653,243,2.564,300,2.842,318,1.142,336,1.908,377,1.049,392,2.909,401,1.792,405,1.492,437,2.201,472,2.283,476,1.361,511,2.259,532,2.446,562,2.303,567,3.817,601,2.201,607,1.797,670,2.725,747,1.925,829,2.47,830,2.143,920,2.725,965,5.387,971,6.501,978,3.158,1050,3.036,1305,3.739,1306,2.933,1320,2.983,1331,3.095,1631,3.818,1632,2.434,1633,2.21,1664,3.438,1718,3.126,2297,1.562,2488,2.842,2534,4.271,3152,3.907,3154,3.907,3716,4.009,3817,4.507,4357,6.059,4645,6.059,4663,9.699,4664,6.724,4665,4.45,4666,4.45,4667,4.45,4668,4.45,4669,4.45,4670,4.45,4671,4.45,4672,4.45,4673,4.45,4674,4.45,4675,4.45,4676,4.45,4677,4.009,4678,4.45,4679,4.009,4680,4.009,4681,3.668,4682,4.45,4683,4.009,4684,4.45,4685,4.009,4686,6.059,4687,4.009,4688,4.009,4689,4.009,4690,4.009,4691,4.009,4692,4.009]],["t/3536",[7,0.398,28,0.713,31,1.234,36,2.228,49,1.136,56,1.342,68,1.916,109,0.852,127,1.657,165,1.132,203,1.794,225,1.806,226,3.633,300,3.063,318,1.23,336,2.056,377,1.13,398,3.48,405,2.384,418,2.525,426,2.767,476,1.467,567,3.581,577,2.39,601,2.372,916,2.936,965,5.633,971,6.712,978,3.403,1133,2.718,1246,4.32,1266,2.043,1284,3.664,1305,4.029,1306,3.16,1320,3.214,1331,3.334,1631,4.114,1632,2.622,1635,4.21,1664,3.705,1718,3.368,1763,4.602,1765,2.898,2383,4.602,2488,3.063,2632,4.476,3016,3.04,3152,4.21,3154,4.21,3483,5.975,3817,3.214,4590,5.663,4677,4.32,4679,4.32,4680,4.32,4681,3.952,4683,4.32,4685,4.32,4686,6.407,4687,4.32,4688,4.32,4689,4.32,4690,4.32,4691,4.32,4692,4.32,4693,4.795,4694,4.602,4695,8.476,4696,4.795,4697,4.795,4698,4.795,4699,4.795,4700,4.795,4701,4.795,4702,4.795,4703,4.795]],["t/3538",[7,0.825,12,1.31,14,2.417,16,1.446,26,1.97,27,1.26,29,1.852,36,1.432,48,1.318,56,0.999,84,0.842,101,1.729,102,2.049,125,1.852,140,1.273,153,1.661,157,1.875,162,1.141,164,0.892,177,1.568,183,1.394,189,1.738,196,2.103,197,1.447,203,0.778,213,1.322,219,3.002,221,1.96,227,2.144,232,3.916,304,0.684,318,2.684,321,2.455,336,2.16,351,1.175,353,0.886,377,1.505,381,1.537,388,4.866,395,1.284,397,1.35,398,1.144,405,3.931,418,1.624,426,3.395,430,1.602,432,4.244,433,1.875,474,2.645,495,2.562,502,2.362,506,1.927,508,1.913,511,2.73,555,1.537,567,3.115,572,3.563,588,3.248,591,1.852,601,1.525,611,1.35,614,1.577,647,1.219,653,1.67,685,1.807,755,1.463,757,2.11,874,1.595,876,2.211,877,1.184,914,1.616,934,2.531,936,3.932,940,2.518,992,1.624,1006,1.661,1033,2.066,1046,4.534,1111,2.123,1168,2.645,1363,2.798,1381,2.667,1487,3.538,1632,1.686,1705,3.241,1768,3.613,1849,2.289,1899,2.262,1909,2.959,2032,1.367,2298,5.607,2312,3.601,2485,2.032,2495,2.707,2864,5.307,3016,1.954,3886,4.422,4580,5.697,4581,2.417,4583,3.949,4585,3.949,5214,2.645,5561,2.959,5738,3.5,5739,3.5,5740,5.719,5741,5.719,5742,5.719,5743,3.5,5744,3.5,5745,3.5,5746,3.5,5747,3.5,5748,3.5,5749,3.5]],["t/3540",[56,1.326,102,4.017,103,2.131,105,1.992,160,2.233,165,2.587,219,3.7,304,1.865,318,1.551,405,3.496,426,4.415,495,3.075,513,3.001,828,2.656,1238,3.805,1247,4.435,1310,4.545,3736,4.289,3878,4.894,3881,5.801,3886,5.307,3888,5.801,3891,5.801,4166,8.061,5214,5.186,5750,6.863,5751,6.369,5752,6.369,5753,9.536,5754,6.369,5755,6.369,5756,6.863,5757,6.863,5758,6.369,5759,6.369,5760,6.863,5761,6.369,5762,6.863,5763,6.863,5764,6.369,5765,6.369,5766,6.863,5767,6.863,5768,6.369]],["t/3542",[56,1.295,102,4.452,103,2.362,105,2.208,160,2.474,165,2.682,304,2.001,318,1.719,405,3.65,426,4.554,495,3.408,513,3.326,828,2.943,1238,4.217,1247,4.915,1310,5.037,3878,5.424,3881,6.429,3886,5.882,3888,6.429,3891,6.429,5751,7.059,5752,7.059,5754,7.059,5755,7.059,5758,7.059,5759,7.059,5761,7.059,5764,7.059,5765,7.059,5768,7.059,5769,7.606,5770,7.606,5771,10.231,5772,7.606,5773,7.606,5774,7.606,5775,7.606]],["t/3544",[2061,3.73]],["t/3546",[512,3.773,575,5.198]],["t/3549",[56,1.169,171,2.719,304,2.109,322,3.131,567,4.013,852,4.339,1312,3.678,1737,3.775,2297,3.334,3644,3.687,4329,9.498]],["t/3551",[304,2.243,438,5.524,1266,2.903,1285,5.994,4330,10.102]],["t/3553",[56,1.014,304,2.165,567,4.119,651,6.847,898,4.98,1266,2.801,1285,5.784,1399,6.051,1732,7.643]],["t/3555",[24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,567,3.979,635,3.601,747,4.075,4328,6.831]],["t/3557",[16,1.911,171,2.791,304,2.165,543,5.784,567,4.119,852,4.453,940,3.843,1737,3.874,3644,3.784]],["t/3559",[56,1.042,304,2.223,401,4.032,1266,2.877,1607,6.895,1638,5.448]],["t/3561",[56,1.042,304,2.223,398,3.716,401,4.032,1266,2.877,1638,5.448]],["t/3563",[7,0.699,24,1.437,27,2.106,28,1.252,84,0.961,88,3.997,103,4.005,105,2.776,133,3.796,145,3.568,343,3.156,377,1.985,511,3.509,557,4.416,567,3.558,568,2.541,672,5.227,706,6.333,740,4.976,1753,6.108,1997,6.819,2009,6.108,3629,5.55]],["t/3565",[2061,3.73]],["t/3567",[2,2.507,23,3.103,24,1.834,56,0.918,179,3.11,221,3.433,313,2.767,324,4.922,512,3.205,552,3.29,723,4.049,852,4.03,862,4.625,916,5.402,1114,4.217,1312,3.415,1476,3.415,2711,6.019]],["t/3569",[512,3.808]],["t/3571",[7,0.664,16,1.568,24,1.726,25,2.698,27,2.916,28,1.189,31,2.059,68,3.195,84,1.155,96,2.969,132,2.475,281,3.987,512,3.675,553,4.463,635,3.867,653,2.653,878,3.594,934,4.019,1069,4.558,1271,6.179,1290,4.689,1446,4.93,1622,6.84,1798,4.689,1819,5.408,2438,6.592]],["t/3574",[56,1.163,171,2.696,219,3.072,304,2.091,322,3.104,552,3.513,852,4.302,1312,3.647,1737,3.743,2297,3.306,3644,3.656,4356,8.082]],["t/3576",[126,4.874,128,4.815,219,3.125,221,3.728,304,2.127,338,2.483,404,4.915,453,6.478,757,2.488,862,5.023,4328,6.948]],["t/3578",[7,0.841,15,2.786,16,1.422,21,1.466,24,1.469,27,2.153,29,1.809,34,1.321,49,1.166,56,1.139,84,0.562,85,1.797,103,1.735,107,1.328,109,1.529,110,4.025,127,1.701,158,2.592,160,1.818,219,2.807,221,3.35,225,1.854,322,2.39,353,1.414,395,2.05,401,1.982,410,3.423,476,1.506,527,2.188,552,3.542,562,3.752,564,4.947,596,2.336,607,1.988,613,2.344,755,2.336,757,1.278,759,1.818,760,2.257,769,1.993,837,4.435,877,1.891,878,2.212,945,4.73,993,2.994,1050,4.947,1284,2.536,1306,3.244,1312,1.906,1718,5.092,1732,5.685,1759,3.57,1798,2.886,1980,3.611,2225,2.761,2297,2.545,2312,4.088,2495,4.322,2605,3.751,2632,3.098,2653,4.136,2708,3.751,2815,3.611,2867,3.034,3513,3.57,4016,3.803,4054,4.223,4356,4.223,4357,4.435,4358,4.922,4359,7.25,4360,4.922,4361,7.25,4362,4.922,4363,4.922,4364,4.922,4365,4.922,4366,4.922,4367,4.922]],["t/3580",[55,5.686,192,6.158,272,4.28,418,5.226,530,4.675,596,4.709,814,4.502]],["t/3582",[2061,3.73]],["t/3584",[7,0.629,23,1.837,24,1.845,25,1.762,29,1.92,56,0.927,68,2.086,84,1.115,160,2.796,165,2.102,177,2.657,179,1.841,198,2.679,199,2.714,219,1.703,221,3.466,228,1.743,232,2.001,240,3.009,248,2.583,275,1.703,283,1.591,296,2.121,313,1.949,316,2.452,324,2.914,338,1.962,353,1.501,377,1.231,397,2.288,422,3.746,491,1.659,512,2.75,522,4.095,530,4.196,548,4.91,552,1.948,647,2.064,723,3.678,757,2.536,760,1.626,822,3.287,852,4.068,862,2.738,903,3.831,904,2.976,916,3.198,948,5.214,1059,6.376,1060,3.868,1069,4.315,1114,2.496,1235,2.714,1266,1.501,1312,2.022,1470,3.979,1476,2.022,1570,4.585,1705,3.361,1953,2.993,2033,3.098,2518,2.75,2711,3.563,3969,4.228,4322,7.57,4323,5.222,4324,5.012,4325,5.222,4326,5.222]],["t/3586",[512,3.808]],["t/3589",[56,1.163,171,2.696,304,2.091,322,3.104,548,6.108,852,4.302,948,6.487,1312,3.647,1737,3.743,2297,3.306,3644,3.656,4192,7.626]],["t/3591",[24,1.517,84,1.233,126,4.524,128,4.469,219,3.523,221,4.204,304,1.974,313,2.289,338,2.305,404,4.562,453,6.013,530,4.19,757,2.805,862,4.662,1693,5.482,4328,6.449]],["t/3593",[272,4.48,596,4.928]],["t/3595",[2061,3.73]],["t/3597",[2,2.079,4,3.122,16,1.434,23,2.573,24,1.924,25,2.468,27,1.83,84,0.835,109,1.3,170,2.277,174,2.291,254,2.478,313,1.883,476,2.921,512,3.864,536,2.295,568,2.882,653,2.426,723,2.755,747,3.165,759,2.702,760,2.277,803,4.34,846,4.469,940,2.884,1114,3.497,1170,3.44,1266,3.056,1363,3.624,1476,2.832,1838,5.735,1902,3.676,4130,6.786]],["t/3599",[4,2.711,28,1.233,84,1.181,217,6.217,552,3.86,592,5.61,653,2.751,725,6.052,986,5.717,988,6.104,1012,7.052,1170,2.684,1266,2.384,1363,3.927,1476,3.212,1671,4.808,2092,4.892,3644,3.22]],["t/3601",[512,3.808]],["t/3603",[7,0.727,16,1.716,24,1.493,27,2.189,31,2.253,56,0.911,84,1.221,165,2.066,281,4.363,512,3.18,553,4.884,635,3.346,723,3.296,852,3.998,1271,6.762,1446,5.396,1622,5.918,1798,5.131,1819,5.918,1863,3.858,2078,8.4]],["t/3606",[23,3.342,56,1.169,171,2.719,304,2.109,322,3.131,852,4.339,1312,3.678,1737,3.775,2297,3.334,3644,3.687,4331,8.558]],["t/3608",[23,3.313,24,1.908,27,2.356,126,4.792,128,4.733,304,2.091,338,2.441,404,4.832,453,6.369,635,3.601,747,4.075,4328,6.831]],["t/3610",[7,0.842,15,3.316,16,1.988,23,2.559,24,1.81,25,1.668,27,1.237,49,1.171,56,1.141,84,0.985,109,1.292,110,2.745,127,1.708,170,1.539,171,2.082,318,1.268,321,3.937,322,1.629,338,1.281,377,1.165,395,3.029,410,3.438,437,2.445,505,2.159,527,1.676,528,4.038,532,2.717,596,2.346,635,1.89,723,2.74,747,2.139,759,1.826,760,1.539,769,2.002,830,2.381,852,2.258,877,1.9,1043,2.704,1044,3.069,1050,4.963,1193,3.286,1312,1.914,1759,3.586,1773,3.937,1798,2.899,1863,2.179,1902,2.485,1918,2.592,1998,3.028,2021,3.82,2225,5.337,2297,3.029,2600,3.206,2605,3.767,2815,3.627,2867,3.048,2942,8.828,2946,4.586,4016,3.82,4331,4.454,4332,4.944,4333,4.944,4334,7.273,4335,4.944,4336,4.944,4337,4.944,4338,4.944,4340,7.273,5198,5.21,5199,5.21,5200,5.21,5201,5.21,5202,5.21,5203,5.21,5204,5.21,5205,5.21,5206,5.21,5207,5.21,5208,5.21]],["t/3612",[2,0.803,4,0.923,5,1.048,7,0.644,15,2.308,16,1.178,18,2.17,23,2.46,24,0.482,27,0.706,28,0.697,31,0.727,32,1.862,34,0.758,56,1.138,68,1.128,84,0.885,85,1.031,88,1.34,89,1.111,105,1.545,116,1.111,131,2.24,138,1.2,156,2.026,160,1.043,162,1.046,164,1.357,165,1.83,171,1.342,174,0.885,186,1.402,196,1.18,201,1.515,219,1.529,228,0.943,232,2.302,239,1.707,275,0.921,283,2.363,284,2.004,292,1.295,296,1.903,302,1.37,304,0.627,306,2.193,313,1.546,317,1.488,318,1.202,322,1.98,347,1.964,348,1.91,351,1.787,359,1.312,361,2.004,366,1.862,367,1.515,372,1.114,377,0.666,391,1.253,393,2.425,395,1.176,397,2.631,405,2.014,407,1.345,408,1.074,415,2.097,440,1.312,441,1.619,466,2.097,471,1.233,504,1.804,512,1.026,530,2.208,536,1.885,539,5.243,543,4.148,544,1.862,550,1.17,552,1.053,558,2.208,560,1.584,572,1.576,575,1.414,614,0.548,621,2.538,712,1.601,734,1.317,743,1.964,747,1.222,790,3.439,795,1.945,796,1.56,802,1.998,807,1.15,851,1.56,877,1.085,878,1.269,893,1.718,930,2.701,959,1.984,1006,1.522,1007,4.263,1045,1.984,1114,2.24,1139,2.643,1169,1.893,1181,2.563,1212,7.874,1215,3.621,1216,1.515,1219,5.661,1222,2.824,1234,1.686,1235,2.436,1290,1.656,1312,3.002,1366,1.656,1376,1.488,1378,2.711,1405,2.424,1473,1.326,1493,1.893,1502,2.545,1503,2.545,1507,6.893,1535,3.795,1536,3.621,1540,1.646,1542,6.71,1559,2.287,1560,2.711,1563,2.424,1606,2.824,1607,1.945,1608,2.824,1609,2.824,1610,2.824,1611,2.824,1612,2.824,1613,2.824,1614,2.824,1615,2.824,1616,1.656,1622,4.062,1623,4.687,1775,2.48,1843,2.711,1898,2.602,1929,2.824,1933,3.292,1934,2.824,1965,2.152,2297,0.991,2456,2.545,2672,1.945,4128,2.976,4279,2.62,4280,2.62,5221,2.711,5222,2.976,5223,2.976,5224,2.976,5225,2.976,5226,2.976,5227,2.976,5228,2.976,5229,2.976,5776,3.207]],["t/3614",[2061,3.73]],["t/3617",[23,3.37,24,1.635,27,2.396,126,4.874,128,4.815,304,2.127,338,2.483,404,4.915,453,6.478,635,3.663,4328,6.948]],["t/3619",[7,0.646,16,1.524,23,3.495,24,1.695,27,1.945,36,3.612,56,1.034,68,3.106,79,6.827,106,3.002,126,5.054,128,4.992,131,3.716,175,4.48,200,4.058,202,5.639,203,1.961,204,6.671,207,3.344,209,7.462,219,2.536,223,4.613,304,1.726,335,6.827,339,4.132,409,4.151,416,3.772,496,5.003,500,3.876,502,2.259,1367,3.972,2297,2.729,3414,7.462]],["t/3621",[7,0.664,16,1.568,23,3.558,24,1.726,36,3.716,41,3.823,56,1.052,68,3.195,79,7.023,126,4.069,128,5.083,131,3.823,201,4.29,202,7.336,203,2.017,204,6.863,207,3.44,210,7.676,219,3.299,223,4.745,304,1.776,335,7.023,339,4.251,409,4.27,496,6.51,1367,4.086,2297,2.807]],["t/3623",[7,0.739,23,3.128,24,1.517,41,4.251,98,3.292,131,4.251,201,5.795,202,6.449,207,3.824,304,1.974,334,5.308,339,4.726,613,4.235,614,1.725,651,6.246,1583,7.472,1737,3.534,3644,3.452,4346,8.892]],["t/3625",[2,1.312,7,0.688,16,0.905,19,2.167,23,4.039,24,1.179,27,2.301,28,1.368,29,1.697,49,1.637,56,0.48,68,1.844,84,1.125,100,2.175,121,2.846,126,2.349,128,2.32,131,2.207,171,2.634,174,2.165,182,1.628,183,3.126,196,1.928,201,2.476,203,1.744,207,1.985,253,2.927,262,3.179,283,2.525,302,2.24,304,1.84,313,1.188,315,2.31,316,2.167,322,1.521,336,3.553,339,2.453,344,6.228,345,7.466,346,9.678,353,1.326,371,4.159,401,1.859,437,2.283,466,3.428,479,3.387,481,2.906,489,3.242,490,3.428,491,1.466,495,2.349,498,2.706,500,3.446,544,3.042,605,1.76,614,1.341,681,4.556,756,2.283,877,1.774,940,1.82,992,2.431,1007,5.881,1208,3.879,1289,1.968,1312,2.676,1367,3.532,1686,7.953,1699,3.018,4347,4.159,4348,6.912,4349,4.616,4350,4.616,4351,4.616,4352,4.616]],["t/3627",[2,1.424,7,0.722,12,2.129,16,1.44,23,2.584,24,0.855,28,1.424,29,1.842,84,1.257,88,4.839,89,4.01,109,0.89,127,1.731,128,2.518,129,3.154,132,1.551,164,1.45,165,1.183,169,4.233,179,2.59,213,2.148,219,1.634,236,1.388,241,4.13,275,1.634,280,3.359,296,2.035,304,1.113,313,1.29,315,2.508,316,4.083,318,1.286,337,2.459,397,3.218,404,2.571,422,3.594,440,2.328,458,2.973,470,3.818,472,2.571,476,2.66,512,3.705,532,2.754,544,3.302,545,2.44,562,3.801,568,2.624,575,5.104,605,3.316,622,2.663,681,3.302,736,2.251,753,4.514,816,2.991,822,3.154,1059,4.332,1069,4.187,1080,4.648,1114,2.395,1208,4.21,1312,1.94,1434,2.938,1849,5.455,1850,5.213,1851,4.13,1916,3.154,2518,2.639,2726,3.768,2782,3.485,4353,5.01,4354,5.01,4355,5.01]],["t/3629",[2,1.129,5,1.475,7,0.917,16,0.779,21,1.183,23,3.243,24,1.664,25,2.08,27,1.89,28,0.917,29,1.461,31,1.023,33,2.387,34,1.066,56,0.959,84,1.113,89,2.971,100,1.872,105,1.31,107,1.072,109,1.096,127,1.373,140,1.641,155,2.711,162,1.471,163,1.659,169,3.553,174,1.244,177,2.022,182,1.401,190,2.152,193,1.523,196,1.659,198,2.039,248,1.965,273,3.339,275,1.296,278,1.5,283,1.879,302,1.928,304,1.369,321,3.164,336,2.644,347,2.763,353,1.142,365,2.122,367,2.132,377,1.453,382,2.598,393,2.056,407,2.937,502,1.155,506,2.484,511,1.335,512,2.24,555,1.981,558,2.905,560,2.229,568,1.199,628,2.162,630,1.546,685,2.33,723,2.322,736,1.785,755,1.886,763,2.85,807,1.618,827,2.122,830,2.969,878,1.785,886,2.663,906,2.538,934,1.997,936,1.791,1002,3.339,1043,2.173,1044,2.466,1055,2.763,1059,2.343,1164,2.112,1193,4.098,1245,1.385,1312,1.538,1446,2.45,1511,2.737,1773,3.164,1782,2.926,1798,5.404,1799,6.685,1819,2.687,1847,2.82,1916,2.501,1998,2.433,2481,4.524,2518,3.98,2896,3.41,2941,8.973,2942,7.009,2943,4.187,2944,3.973,2945,4.187,2946,3.686,2947,4.187,2948,4.187,2949,4.187,2950,4.187,2951,4.187,2952,3.973,2953,6.498,2955,3.489,2956,3.58,5777,4.512]],["t/3631",[2061,3.73]],["t/3633",[2,0.533,5,1.224,16,1.651,24,1.996,28,1.668,29,0.689,56,1.235,84,0.376,97,0.883,105,0.618,129,1.18,130,1.981,132,1.021,153,1.01,155,1.279,160,2.24,162,0.694,164,1.279,165,1.432,170,0.583,193,1.693,196,0.783,203,0.473,224,4.473,225,1.664,232,1.264,239,1.994,243,1.08,275,0.611,277,1.015,279,0.853,289,2.785,297,1.08,323,1.402,324,1.046,351,0.715,365,1.001,367,3.588,377,1.254,394,3.194,405,2.42,407,2.889,446,1.256,495,2.248,502,2.687,505,0.818,511,0.629,514,1.492,527,0.432,528,1.831,550,2.989,555,1.644,568,0.565,583,1.206,591,1.126,605,0.715,607,1.784,609,1.291,614,0.364,630,0.729,632,1.062,635,0.716,650,1.33,662,2.705,663,1.206,675,1.256,683,0.739,734,0.874,748,2.627,749,2.251,754,1.608,760,1.027,802,0.799,807,3.553,814,0.85,816,1.969,820,2.393,841,1.606,862,1.729,877,0.72,878,1.482,880,0.85,893,2.007,907,2.23,946,1.6,973,1.375,979,3.072,1009,1.6,1011,1.33,1012,1.119,1037,1.775,1042,2.091,1048,1.575,1050,3.015,1110,1.448,1117,1.068,1122,2.174,1152,1.33,1191,1.448,1235,0.974,1245,1.15,1247,3.241,1266,0.538,1274,1.206,1285,2.621,1300,1.291,1308,1.171,1312,1.277,1320,3.567,1345,3.366,1352,0.949,1365,2.912,1405,2.83,1456,1.344,1458,2.366,1633,2.194,1699,2.156,1718,3.103,1782,2.097,1806,2.83,1836,1.575,1849,1.391,1850,1.33,1911,1.469,2082,4.111,2225,1.051,2241,2.83,2280,2.719,2363,1.545,2488,1.197,2668,1.391,2677,2.719,2700,1.608,2839,3.169,2844,1.469,3007,1.517,3070,3.06,3073,1.738,3089,1.608,3629,2.174,4750,5.821,4752,4.24,5179,1.798,5265,3.476,5266,1.975,5267,1.975,5268,1.975,5269,1.975,5270,1.975,5271,1.975,5272,1.798,5273,1.798,5274,1.975,5275,3.166,5276,1.798,5277,1.798,5278,1.688,5279,1.798,5280,1.975,5281,4.656,5282,1.975,5283,3.476,5284,5.107,5285,3.166,5286,1.975,5287,1.975,5288,1.975,5289,1.975,5290,1.975,5291,1.975,5292,1.798,5293,1.798,5294,1.798,5295,1.975,5296,3.166,5297,3.98,5298,1.975,5299,1.975,5300,1.975,5301,1.975,5302,1.975,5303,1.975,5304,1.975,5305,3.476,5306,1.975,5307,1.975,5308,1.975,5309,1.975,5310,1.798,5311,1.798,5312,1.975,5313,1.798,5314,1.975,5315,1.975,5316,1.975,5317,3.476,5318,1.975,5319,1.975,5320,1.975,5321,1.975,5322,1.975,5323,1.975,5324,1.975,5325,1.975,5326,1.975]],["t/3635",[16,1.329,18,3.139,23,2.385,24,1.867,25,2.287,28,1.351,34,1.82,49,2.591,70,6.831,97,3.195,203,2.291,278,2.56,289,2.824,295,2.873,342,4.67,373,5.166,381,3.38,476,2.074,484,3.027,580,2.481,607,2.738,614,2.123,637,4.585,749,4.627,757,1.76,760,2.828,769,2.746,780,3.803,846,3.172,878,3.046,1046,3.34,1170,2.193,1234,4.048,1300,4.67,1446,4.18,1798,3.975,1819,4.585,1874,4.975,2032,3.008,2103,5.49,2684,4.397,2717,7.055,3629,4.469,4852,6.109]],["t/3637",[16,2.25,24,1.808,25,3.575,27,2.155,28,1.281,56,0.897,164,2.494,367,5.684,476,2.636,577,4.296,778,5.726,807,4.315,936,3.884,1042,5.463,1315,4.994,3997,5.992,5327,9.08,5328,9.08]],["t/3639",[7,0.519,18,1.873,24,1.677,28,1.382,31,1.042,56,1.296,84,0.462,103,2.694,132,1.935,160,3.63,163,1.69,165,1.476,182,1.426,203,1.577,236,1.121,240,2.332,256,1.595,273,3.4,283,1.233,285,1.761,296,1.643,313,1.042,377,1.474,381,2.017,405,2.562,426,3.824,476,1.238,502,1.176,511,1.359,527,0.932,528,2.246,541,4.055,565,2.629,568,1.221,614,1.213,683,1.595,754,3.471,759,2.823,760,3.656,769,2.532,807,1.647,814,1.836,851,3.454,857,1.934,862,4.508,878,1.818,934,3.142,946,3.708,1117,3.564,1218,2.4,1235,3.972,1300,5.922,1332,4.024,1629,2.968,1782,4.411,1863,1.783,1940,3.471,2013,2.689,2280,3.335,2450,7.81,2485,2.666,2626,3.753,2811,3.645,2833,3.004,2844,3.172,2851,2.446,3952,3.645,5333,3.883,5334,5.365,5335,3.883,5336,6.001,5778,4.594,5779,4.594,5780,4.263,5781,4.263,5782,4.263]],["t/3641",[16,2.058,24,1.448,28,1.561,31,2.184,49,2.01,56,1.092,163,3.544,302,4.117,528,4.711,541,5.503,553,4.735,635,4.013,751,5.162,878,3.813,941,6.556,1798,6.981,1799,6.154,1819,5.738,2717,5.844]],["t/3643",[2,0.601,5,1.361,7,0.176,9,1.444,16,0.719,24,0.988,28,0.861,29,0.778,34,0.568,56,1.287,84,0.242,97,1.728,107,2.878,109,1.577,113,0.813,129,1.332,134,1.109,155,1.444,160,1.793,164,0.613,165,1.367,170,3.51,179,0.746,193,1.861,213,0.907,224,4.211,225,1.828,232,0.811,289,1.527,295,0.897,297,1.219,326,1.518,338,1.258,343,0.793,373,1.612,377,0.864,394,1.114,405,2.732,495,1.866,500,1.055,502,2.579,505,0.924,511,0.711,514,1.685,528,1.175,541,1.372,550,2.972,555,1.055,591,1.271,605,0.807,607,2.339,608,1.157,636,1.255,647,0.836,653,0.702,662,2.246,663,1.362,675,1.418,734,0.987,748,1.685,749,1.444,760,0.659,772,4.028,802,0.902,813,1.444,862,1.923,877,1.409,878,2.181,887,1.406,893,2.231,907,3.283,947,1.135,965,2.438,971,3.254,973,1.552,979,3.376,999,1.213,1011,1.502,1037,1.95,1056,4.612,1110,2.834,1117,1.206,1122,3.199,1152,1.502,1164,1.125,1181,1.157,1191,1.635,1235,1.906,1247,3.562,1266,0.608,1285,1.255,1308,1.323,1320,5.464,1345,4.413,1352,4.326,1365,1.395,1456,1.518,1458,1.518,1699,2.398,1718,2.576,1782,1.004,1806,3.147,1812,2.116,1836,1.778,1849,1.571,1850,1.502,1902,4.096,1953,1.213,2035,1.906,2082,4.475,2363,1.744,2488,1.352,2509,1.635,2639,2.876,2833,1.571,2839,3.482,3089,1.816,4681,1.744,4758,5.218,4761,2.031,5272,2.031,5278,1.906,5284,5.559,5285,3.52,5297,4.374,5334,1.816,5341,2.23,5342,2.23,5343,2.23,5344,2.23,5345,2.23,5346,2.23,5347,2.23,5348,2.23,5349,2.23,5350,2.23,5351,2.23,5352,3.52,5353,2.031,5354,2.23,5355,4.659,5356,2.23,5357,3.865,5358,2.23,5359,2.23,5360,2.23,5361,2.23,5362,2.23,5363,2.031,5364,2.031,5365,2.031,5366,3.52,5367,3.52,5368,2.23,5369,2.23,5370,2.23,5371,4.659,5372,2.23,5373,2.23,5374,2.23,5375,2.23,5376,2.23,5377,2.23,5378,2.031,5379,2.031,5380,1.963,5381,2.031,5382,2.23,5383,2.031,5384,2.23,5385,2.23,5386,3.865,5387,3.865,5388,2.23,5389,2.23,5390,2.23,5391,2.23,5392,2.23,5393,2.23,5394,2.031,5395,2.23,5396,2.23,5397,2.23,5398,2.23]],["t/3645",[4,2.138,28,1.318,84,0.747,109,1.575,130,3.929,132,2.024,133,2.949,160,2.416,164,1.894,170,3.892,236,1.812,278,2.47,295,2.772,373,4.984,377,1.542,426,3.451,491,2.078,495,3.328,566,4.799,605,2.494,608,4.849,614,1.72,622,4.713,637,4.423,820,3.543,857,3.127,877,2.513,947,3.509,952,4.464,1037,2.627,1042,4.147,1045,4.594,1056,8.587,1110,5.054,1116,4.549,1303,4.117,1352,3.314,1699,4.276,1782,3.104,2141,5.392,2438,5.392,5334,5.613,5399,6.893,5400,6.893,5401,6.893,5402,6.893]],["t/3647",[2,0.881,12,1.317,16,0.992,24,1.391,27,1.265,28,1.372,29,1.14,56,1.318,68,1.238,106,1.197,107,1.996,109,1.139,113,1.191,127,2.556,129,1.951,132,0.959,160,3.011,170,2.538,172,1.131,193,3.536,196,1.295,224,1.384,225,1.167,254,1.05,275,1.011,278,1.17,279,1.411,291,3.14,292,1.421,313,0.798,318,0.795,351,1.182,377,1.192,398,1.151,472,1.59,476,1.548,481,1.951,502,1.47,505,1.354,511,1.041,512,1.126,550,1.283,552,1.156,575,1.551,596,1.471,601,1.533,607,1.252,609,2.135,614,1.435,622,1.647,636,1.839,803,1.839,857,1.482,878,1.393,965,4.26,971,5.464,973,2.274,979,2.156,986,2.794,1152,2.2,1235,2.63,1247,2.274,1266,1.454,1285,3.803,1308,1.937,1312,1.959,1320,2.078,1321,3.1,1345,3.855,1362,2.362,1363,1.176,1366,1.817,1405,4.341,1408,1.45,1456,2.223,1458,2.223,1633,3.183,1664,2.395,1826,2.43,1902,3.718,2082,3.909,2280,4.17,2363,4.17,2488,1.98,2639,3.967,2835,2.876,2839,2.223,2844,2.43,3542,3.59,4681,2.555,5273,2.975,5275,4.856,5276,2.975,5277,2.975,5278,4.558,5279,2.975,5292,2.975,5293,2.975,5294,2.975,5296,4.856,5297,2.793,5310,2.975,5311,2.975,5313,2.975,5352,4.856,5353,2.975,5355,2.975,5363,2.975,5364,2.975,5365,2.975,5366,2.975,5367,4.856,5371,2.975,5378,2.975,5379,2.975,5380,2.876,5381,2.975,5383,2.975,5394,2.975,5403,5.059,5404,3.266,5405,3.266,5406,3.266,5407,3.266,5408,3.266,5409,3.266,5410,3.266,5411,3.266,5412,3.266,5413,3.266,5414,3.266,5415,3.266,5416,3.266,5417,3.266,5418,3.266,5419,3.266,5420,3.266,5421,3.266,5422,3.266,5423,3.266,5424,3.266,5425,3.266,5426,3.266,5427,3.266]],["t/3649",[2,1.542,7,0.451,18,2.512,56,0.81,84,1.039,89,2.134,107,2.455,109,0.964,113,2.085,138,2.306,164,1.571,170,3.095,171,1.553,173,5.131,182,2.744,193,3.81,223,4.617,224,4.438,236,1.503,254,3.083,277,2.938,282,3.109,289,2.259,313,2.003,320,3.28,343,2.917,377,1.279,433,3.301,495,2.76,502,2.261,550,2.247,552,2.024,568,1.637,601,2.684,605,2.069,612,3.702,614,1.053,632,5.158,635,2.074,725,4.881,747,2.348,772,3.773,810,2.539,814,2.462,841,2.642,878,2.438,1009,2.632,1012,5.934,1117,3.092,1164,2.884,1274,3.492,1352,2.749,1363,2.059,1364,4.134,1376,4.099,1737,3.093,2092,5.366,2488,3.466,2639,4.254,2905,4.08,3644,3.021,3728,4.192,4758,4.888,5428,8.201]],["t/3651",[16,2.271,27,1.617,34,1.735,85,2.36,97,3.046,107,1.744,113,2.484,170,3.689,173,3.646,203,2.219,217,3.884,278,2.441,295,2.74,351,3.354,353,1.858,476,1.978,484,2.886,552,2.411,580,2.366,607,2.611,614,2.339,637,4.372,725,4.719,740,3.079,747,2.797,749,4.412,757,1.679,760,2.739,769,2.618,772,4.496,878,2.905,1117,3.685,1139,5.638,1300,4.453,1382,4.07,1557,6.536,1907,3.518,2103,5.235,3629,4.261,4511,5.433,5429,6.813]],["t/3653",[24,1.317,28,1.148,56,0.803,85,2.818,106,2.982,107,2.668,109,2.199,127,2.668,170,3.583,172,2.818,254,3.351,444,3.527,476,2.362,527,1.779,658,4.991,757,2.833,997,4.258,1352,5.011,1389,4.825,1421,7.41,5430,7.72,5431,8.136]],["t/3655",[7,0.493,28,1.089,31,0.974,56,1.29,84,0.432,103,2.093,107,1.976,109,1.601,132,1.171,160,3.531,163,1.58,165,1.401,170,3.112,182,1.334,183,1.712,203,1.497,221,1.473,236,1.049,240,2.181,256,1.492,273,3.18,283,1.153,296,1.537,313,0.974,377,1.399,381,1.887,405,2.455,426,3.72,476,1.158,502,1.1,511,1.271,527,0.872,541,3.85,565,2.52,568,1.142,614,1.152,653,1.255,683,1.492,754,3.247,759,2.705,760,3.512,769,2.404,807,1.541,814,1.717,851,3.279,857,1.809,862,3.84,878,1.7,880,1.717,934,2.983,946,3.553,1037,1.52,1042,2.399,1107,2.101,1117,3.383,1218,2.245,1235,4.31,1266,1.087,1284,1.95,1300,5.712,1332,3.82,1352,1.917,1629,2.777,1782,4.275,1863,1.668,1902,2.983,2013,2.515,2032,2.633,2095,3.511,2450,6.154,2485,2.494,2626,3.511,2633,2.193,2711,2.582,2811,3.41,2833,2.81,2844,2.967,3952,3.41,4324,3.632,5333,3.632,5334,5.093,5335,3.632,5336,5.697,5432,6.255,5433,3.988,5434,3.988,5780,3.988,5781,3.988,5782,3.988,5783,4.297,5784,4.297,5785,4.297]],["t/3657",[2,1.665,16,2.346,24,1.525,25,1.976,27,0.931,28,1.67,56,1.213,84,0.827,107,1.58,109,1.588,113,1.43,132,3.675,144,1.878,157,2.264,160,1.375,162,1.378,163,1.554,165,0.878,170,3.497,193,1.426,199,1.934,224,1.661,236,1.623,254,1.261,256,2.31,278,1.405,289,2.439,295,1.577,297,2.145,336,1.596,377,0.877,381,1.855,391,1.651,476,1.138,491,1.182,495,1.893,502,2.597,528,2.066,550,3,552,1.388,568,1.123,582,3.268,605,1.419,607,1.503,614,0.722,622,3.113,635,1.423,662,2.279,675,2.494,725,1.996,740,2.79,756,3.583,807,1.515,841,1.812,878,3.255,947,1.996,951,2.516,1037,1.495,1039,3.452,1056,5.769,1235,1.934,1238,2.342,1285,2.208,1300,2.563,1330,2.669,1352,1.885,1365,2.453,1779,2.641,1782,2.78,2082,6.349,2095,3.452,2372,2.614,2782,2.588,2844,2.918,2846,3.721,2847,3.721,3062,3.452,3617,3.452,3659,3.572,4760,5.622,5436,3.921,5437,3.921,5438,3.921,5439,3.921,5440,3.921,5441,3.921,5442,3.921,5443,3.921,5444,3.921,5445,3.921]],["t/3659",[16,2.033,26,2.719,28,1.373,84,0.793,102,6.137,132,2.858,226,3.551,275,2.267,323,2.953,390,3.536,391,3.083,405,4.045,426,3.594,511,3.973,568,2.097,612,4.742,653,2.305,751,4.228,907,4.699,981,6.037,1052,5.226,1364,5.295,1367,4.718,1376,3.66,1381,2.902,1466,6.261,1907,3.782,2485,4.58,3624,5.627,3644,2.698,4499,9.234,5214,5.963,5215,8.32,5446,7.323]],["t/3661",[5,2.788,16,2.112,28,1.693,56,1.011,84,0.857,107,2.027,113,2.886,132,2.325,182,3.426,224,4.338,225,2.829,323,3.192,397,3.291,426,2.923,511,3.962,596,3.565,609,5.174,772,5.224,877,2.886,981,4.911,1117,4.282,1352,5.976,5447,10.24,5448,7.916,5449,10.24]],["t/3663",[5,2.788,16,1.473,24,1.282,25,2.534,28,1.754,84,0.857,113,2.886,132,2.325,182,2.649,224,4.338,225,2.829,289,3.128,323,3.192,397,3.291,511,3.962,609,5.174,635,2.872,807,4.638,877,2.886,878,3.375,940,3.831,981,6.353,2525,6.768,3895,9.993,4232,6.969,5220,7.21,5450,7.916]],["t/3665",[7,0.633,16,1.922,24,1.299,25,2.569,28,1.132,31,1.96,49,1.803,56,1.02,109,1.353,123,2.734,132,2.357,228,3.272,236,2.11,297,4.388,313,1.96,398,2.826,476,2.329,491,2.419,502,2.848,536,3.076,565,2.62,568,2.298,577,5.905,580,2.786,1037,3.059,1235,3.958,1332,4.901,1782,3.614,3078,9.094,3079,7.064,3080,7.064]],["t/3667",[0,3.822,12,2.67,56,1.103,109,1.75,112,5.801,171,1.799,172,4.056,193,4.259,217,3.774,236,1.741,391,2.787,502,1.826,552,3.955,725,4.628,726,4.327,747,2.719,829,4.789,953,4.199,954,4.413,959,6.059,986,3.47,988,3.705,1009,4.779,1037,2.524,1235,5.511,1331,7.374,1332,6.824,1367,3.21,1782,2.982,3081,5.829,3082,9.138,3083,5.829,3084,5.829,3085,5.829]],["t/3669",[12,2.953,28,1.71,49,1.646,56,1.079,68,2.776,106,4.005,107,2.491,109,1.964,115,3.372,254,3.744,324,3.878,398,2.579,444,4.218,502,2.019,525,3.451,527,2.127,803,4.123,997,4.614,1235,4.799,1245,2.423,1331,7.685,1332,5.943,1381,3.856,1389,5.771,3086,6.447,3087,6.447]],["t/3671",[12,2.847,16,1.767,24,1.537,27,1.675,28,1.34,56,0.697,89,2.634,116,2.634,133,3.02,145,2.838,162,2.48,170,2.085,200,3.497,224,4.022,275,2.185,281,3.339,292,3.07,299,6.093,302,3.25,318,1.719,351,2.554,381,3.339,396,5.522,405,2.245,436,5.037,470,5.104,502,2.958,513,3.326,550,2.774,568,2.021,609,4.614,635,2.561,679,5.522,694,3.839,787,5.748,802,2.855,807,2.728,811,4.379,903,6.611,953,2.855,1024,4.49,1223,3.999,1300,7.5,1303,4.217,1315,3.882,1956,6.429,2486,4.705,3055,5.748,3505,6.214,3728,5.176]]],"invertedIndex":[["",{"_index":56,"t":{"2":{"position":[[690,1],[791,1],[811,1],[837,1],[880,1],[921,1],[962,1],[990,1],[2335,1],[3666,1],[3668,3]]},"4":{"position":[[343,1],[586,1],[883,1],[951,1],[1055,1],[1148,1]]},"6":{"position":[[85,1]]},"8":{"position":[[331,1],[386,1],[493,1],[1017,1]]},"10":{"position":[[754,1],[805,2],[818,2],[841,2],[850,1],[914,1],[1452,1],[1466,1],[1483,1]]},"12":{"position":[[109,1],[1017,1]]},"14":{"position":[[201,1],[1317,1],[1530,1]]},"16":{"position":[[1622,1],[4978,1],[6339,1],[6863,2],[6970,1]]},"18":{"position":[[337,1],[1633,1],[1843,1],[2527,2],[2536,1],[2545,1],[2557,2],[2560,1],[2578,2],[2617,1],[2632,1],[2641,1],[2653,2],[2656,1],[2674,1],[2716,1],[2718,1],[2720,2],[2781,2],[3173,1]]},"20":{"position":[[1191,1],[1593,1]]},"22":{"position":[[260,1]]},"26":{"position":[[805,1]]},"28":{"position":[[42,1]]},"32":{"position":[[286,1],[1504,1]]},"34":{"position":[[66,1],[332,1],[804,2],[827,2],[892,2],[1007,1],[1028,1],[1131,1],[1140,2],[1147,1],[1194,1],[1207,2],[1243,1],[1269,5],[1296,1],[1370,1],[1372,1],[1409,1],[1417,1],[1419,2],[1473,1],[1549,2],[1638,1]]},"36":{"position":[[283,1]]},"38":{"position":[[1897,1]]},"40":{"position":[[413,1]]},"42":{"position":[[328,1]]},"50":{"position":[[454,1],[715,1],[1085,1],[1478,1],[1786,1]]},"56":{"position":[[218,1]]},"58":{"position":[[56,1],[366,1],[927,1],[1323,1],[1336,1],[1459,1]]},"60":{"position":[[8,3],[124,1],[331,1],[428,1],[1349,1],[1555,1],[1664,1],[2191,1]]},"62":{"position":[[104,1],[133,1],[564,2],[614,2],[621,1],[645,2],[664,2],[671,1],[737,1],[827,2],[1067,1],[1252,1]]},"64":{"position":[[475,1],[546,1],[553,2],[595,1],[604,2],[654,1],[696,3],[811,2],[983,1],[1192,1],[1631,1]]},"66":{"position":[[437,1],[514,1],[924,1],[1105,1],[1645,1],[1733,1],[1748,2],[1761,1],[1836,1],[1875,4],[2367,2],[2469,1],[2937,1]]},"68":{"position":[[122,1],[448,1],[695,1]]},"70":{"position":[[311,1],[352,4],[497,1],[511,2],[526,1],[598,1],[792,1]]},"72":{"position":[[463,1],[563,3],[567,2],[570,2],[634,1],[708,1],[930,1]]},"74":{"position":[[622,1],[994,1],[1029,1],[1519,1],[2251,1],[2336,2],[2339,2],[2526,1]]},"76":{"position":[[767,1],[870,2],[873,2],[1196,1],[1321,1],[1442,1],[1619,1],[1672,1],[2037,1]]},"78":{"position":[[710,1],[1312,1],[1698,1],[2045,1],[2352,1],[2401,1],[3970,1]]},"80":{"position":[[312,1],[408,1],[855,1],[1215,1]]},"82":{"position":[[39,1],[444,1],[818,1],[1361,1],[2418,1],[2555,1]]},"84":{"position":[[101,1]]},"86":{"position":[[144,1],[382,1],[1083,1],[1233,1],[1429,1]]},"88":{"position":[[166,1],[193,1]]},"92":{"position":[[191,1]]},"94":{"position":[[235,1],[992,1],[1245,1],[1359,1],[1444,1],[1762,1],[2006,1],[2868,1]]},"96":{"position":[[152,2]]},"98":{"position":[[216,1],[862,3],[1679,4],[2074,4]]},"100":{"position":[[326,1],[361,1],[392,1],[660,1],[741,1],[784,1],[1184,2]]},"104":{"position":[[73,1],[182,1],[184,2],[251,2],[319,2],[384,2],[654,1],[804,1],[854,4],[915,1],[966,1],[975,2],[1158,2],[1168,2],[1189,2],[1196,1],[1213,2]]},"106":{"position":[[206,2],[265,1],[281,2],[356,2],[363,1],[376,1],[382,1],[394,2],[442,2],[449,1],[462,1],[537,1],[552,1],[556,1],[617,3],[643,3],[715,1],[717,2],[741,1],[1099,2],[1196,1],[1274,1],[1286,2],[1359,2],[1366,1],[1383,1],[1485,2]]},"108":{"position":[[97,1],[1496,1]]},"110":{"position":[[675,2],[845,1],[927,1],[975,1],[1003,1],[1066,1],[1068,2],[1117,2],[1307,1],[1321,2],[1374,2],[1381,1],[1407,1],[1486,2],[1528,2],[1535,1],[1561,1],[1619,2],[1666,2],[1673,1],[1699,1],[1701,2],[1731,2],[1759,2],[1766,1],[1869,1],[2025,1],[2138,1],[2516,1],[2518,1],[2520,1],[2522,1],[2524,1],[2526,1],[2528,1],[2530,1],[2532,1],[2534,1],[2536,1],[2538,1],[2540,1],[2542,1],[2544,1],[2546,1],[2548,1],[2550,1],[2552,1],[2554,1],[2556,1],[2558,1],[2560,1],[2562,1],[2564,1],[2566,1],[2568,1],[2570,1],[2572,1],[2574,1],[2576,1],[2578,2],[2590,1],[2604,2],[2607,1],[2609,1],[2611,1],[2613,1],[2615,1],[2617,1],[2619,1],[2621,1],[2623,1],[2625,1],[2627,1],[2629,1],[2631,1],[2633,1],[2635,1],[2637,1],[2639,1],[2641,1],[2643,1],[2645,1],[2647,1],[2649,1],[2651,1],[2653,1],[2655,1],[2657,1],[2659,1],[2661,1],[2663,1],[2665,1],[2667,1],[2669,2],[2678,3],[2682,4],[2687,1],[2689,1],[2691,1],[2693,1],[2695,1],[2697,1],[2699,1],[2701,1],[2703,1],[2705,1],[2707,1],[2709,1],[2711,1],[2713,1],[2715,1],[2717,1],[2719,1],[2721,1],[2723,1],[2725,1],[2727,1],[2729,1],[2731,1],[2733,1],[2735,1],[2737,1],[2739,1],[2741,1],[2743,1],[2745,1],[2747,1],[2749,1],[2980,1],[3020,2],[3081,2],[3139,2],[3152,1],[3162,1],[3170,1],[3190,2],[3244,2],[3251,1],[3260,2],[3270,1],[3277,1],[3284,1],[3291,1],[3322,1],[3324,1],[3352,1],[3413,2],[3420,1],[3451,1],[3457,2],[3493,2],[3520,2],[3527,1],[3536,2],[3546,1],[3548,2],[3596,1],[3603,1],[3610,1],[3641,1],[3643,1],[3657,2],[3672,1],[3737,1],[3745,2],[3792,1],[3850,1],[3910,1],[3996,1],[3998,1],[4125,1],[4244,1],[4253,2],[4292,2],[4299,1],[4321,1],[4374,1],[4396,1],[4458,1],[4469,2],[4483,1],[4490,2],[4495,1],[4509,1],[4511,1],[4588,2]]},"112":{"position":[[448,1],[454,1],[468,2],[518,2],[525,1],[538,1],[603,2],[635,2],[642,1],[655,1],[657,2]]},"114":{"position":[[47,1]]},"116":{"position":[[167,1],[391,1],[1052,1]]},"118":{"position":[[67,1],[701,1]]},"120":{"position":[[111,1]]},"122":{"position":[[1107,1],[2113,1]]},"124":{"position":[[553,1],[833,1],[958,1],[1252,1],[1704,1],[1897,1],[2149,1],[2483,1],[2489,1],[2529,1],[2535,1],[2571,1],[2673,1],[2918,1],[3212,1],[3344,1],[3557,1],[3559,1],[3561,1],[3646,2],[3649,1],[3740,2],[3743,1],[3838,1],[4040,1]]},"126":{"position":[[614,1]]},"128":{"position":[[1375,1],[1425,3],[1454,1],[1462,2]]},"130":{"position":[[304,1],[1472,1]]},"132":{"position":[[53,1],[450,2],[477,1],[706,1],[1472,1]]},"134":{"position":[[221,1],[785,1]]},"136":{"position":[[844,1],[854,1],[856,1],[908,1],[910,2],[1380,1]]},"138":{"position":[[623,1]]},"140":{"position":[[116,1],[171,2]]},"144":{"position":[[902,1],[904,1],[906,1],[999,2],[1002,1],[1107,1],[1201,1]]},"148":{"position":[[417,1]]},"154":{"position":[[493,1]]},"156":{"position":[[478,1],[665,1],[697,1]]},"158":{"position":[[507,1],[913,1],[1219,1]]},"160":{"position":[[405,1],[451,1],[511,1],[601,1],[669,1]]},"168":{"position":[[631,1]]},"170":{"position":[[173,1],[289,1],[311,2],[398,1],[409,2],[426,1],[428,1],[449,1],[451,2],[642,1],[732,1]]},"172":{"position":[[336,1],[373,2],[376,1],[385,2],[426,2],[462,1],[464,3],[468,2]]},"174":{"position":[[348,1],[380,2],[411,2],[449,2],[465,1],[508,1],[545,1],[559,1],[574,1],[627,1],[636,2],[670,2],[724,2],[743,2],[777,2]]},"176":{"position":[[597,1],[611,1],[627,3],[635,1],[671,1],[707,1],[717,1],[731,1],[784,1],[793,1],[904,4],[928,1],[930,3],[934,3],[1185,1],[1195,1],[1218,1],[1261,1],[1322,1],[1413,1],[1506,1],[1539,3],[1558,1],[1605,1]]},"178":{"position":[[435,1],[566,1]]},"180":{"position":[[328,1],[408,1]]},"182":{"position":[[614,1]]},"184":{"position":[[610,1],[878,1]]},"186":{"position":[[210,1],[609,1]]},"188":{"position":[[274,1],[317,3],[333,3],[345,3],[361,3],[373,3]]},"190":{"position":[[227,1],[296,3],[570,1],[774,1],[809,1],[811,1]]},"192":{"position":[[383,1],[428,3],[438,3],[604,2],[743,2],[880,1],[943,1],[1027,1],[1042,1],[1058,3],[1066,1],[1068,2],[1105,1],[1107,2],[1129,1],[1144,1],[1179,1],[1213,1],[1223,1],[1250,1],[1266,1],[1277,1],[1279,4],[1284,2],[1706,1],[1751,3],[1761,3],[1898,1],[1924,1],[1926,1],[2281,1],[2283,1]]},"194":{"position":[[238,3],[248,3],[264,3],[458,2],[779,2],[789,2],[826,2],[853,1],[934,1],[995,1],[1061,1],[1086,1],[1109,1],[1177,1],[1210,3],[1257,1],[1293,3],[1307,1],[1339,1],[1366,1],[1388,1],[1441,1],[1589,1],[1616,3],[1685,1],[1700,1],[1716,3],[1724,1],[1726,2],[1777,1],[1813,1],[1823,1],[1858,3],[1881,1],[1883,3],[1887,1],[1889,2],[2158,1],[2183,2],[2238,1],[2294,1],[2320,1],[2322,1],[2398,6]]},"196":{"position":[[210,1],[612,1],[614,1],[697,1],[699,2],[1240,1]]},"198":{"position":[[475,1],[675,1],[734,1],[767,1],[814,1],[816,1],[1062,1],[1095,1],[1521,1],[1523,2]]},"200":{"position":[[382,1],[408,1],[410,1],[486,6],[864,1],[918,1],[988,1],[1059,1],[1153,1],[1155,1],[1167,1],[1193,1],[1195,1],[1260,1],[1339,1],[1372,1],[1401,1],[1472,1],[1539,1],[1541,1],[1553,2],[1556,1],[1623,1],[1702,1],[1735,1],[1765,1],[1838,1],[1907,1],[1909,1],[1921,2],[1924,1]]},"202":{"position":[[341,1],[1455,1]]},"206":{"position":[[346,1],[476,1]]},"212":{"position":[[89,1],[115,1],[150,1]]},"214":{"position":[[90,1],[146,1],[269,1],[318,1],[345,1],[381,1],[418,1],[724,1]]},"216":{"position":[[662,1],[686,1],[813,1],[1037,2],[1427,1],[1449,1],[1513,1],[1526,1],[1553,1],[1567,1],[1628,1],[1636,1],[1660,1],[1662,1],[1696,1],[1711,1],[1720,1],[1803,1],[1805,1],[1910,2],[1957,1],[1959,1],[1961,1],[1997,2],[2035,2],[2054,1],[2394,2],[2397,1],[2416,2],[2444,1],[2454,1],[2473,1],[2534,1],[2578,2],[2581,1],[2620,2],[2623,1],[2663,2],[2666,1],[2717,1],[2751,3],[2755,2],[2758,1],[2760,3],[2793,2],[2796,2],[2799,1],[2826,4],[3200,1],[3246,1],[3293,1],[3295,1],[3313,5],[3333,5],[3352,4],[3357,1],[3359,3],[3376,2],[3379,1],[3381,3],[3423,1],[3459,2],[3462,3],[3473,1],[3475,3],[3513,1],[3515,1],[3800,1],[3802,3],[3846,1],[3862,3],[3879,2],[3882,1],[3884,3],[3906,1],[3908,3],[3912,1],[3951,1],[4031,1],[4093,3]]},"222":{"position":[[350,1],[720,1],[967,1]]},"226":{"position":[[532,1]]},"232":{"position":[[547,1]]},"234":{"position":[[172,1],[352,1]]},"236":{"position":[[920,1]]},"238":{"position":[[284,1]]},"240":{"position":[[626,1]]},"242":{"position":[[644,1],[783,1],[1224,1],[1349,1],[1542,1]]},"244":{"position":[[289,1]]},"246":{"position":[[161,1],[201,1],[243,1],[285,1],[317,1],[346,1],[369,1],[413,1],[420,1],[425,1],[430,1],[520,1],[537,2],[732,2],[735,1],[761,1],[794,1],[812,3],[816,1],[856,1],[896,2],[899,2],[902,1],[1933,1],[1963,1],[2006,2],[2009,1],[2033,2],[2047,2],[2068,2],[2083,1],[2104,1],[2144,1],[2151,1],[2195,4],[2406,1],[2530,2],[2533,1]]},"248":{"position":[[55,1],[216,1],[242,2],[373,1],[384,2],[496,1],[508,1],[555,1],[629,1],[724,2],[727,1],[805,2],[808,1],[894,1],[924,1],[926,1],[928,1]]},"250":{"position":[[174,1],[472,1],[743,1],[790,1],[792,1],[1027,1],[1050,1],[1398,2],[1674,2],[1677,1],[1703,1],[1724,1],[1751,1],[1753,3],[1757,1],[1787,1],[1842,1],[1844,6]]},"252":{"position":[[382,1],[447,1],[544,2],[547,2],[550,1],[556,1],[593,1],[622,1],[633,3],[637,1],[651,2],[693,1],[733,1],[742,2],[745,2],[748,3],[752,1],[764,1],[792,3]]},"254":{"position":[[248,1],[383,1]]},"258":{"position":[[2153,1]]},"260":{"position":[[334,1]]},"268":{"position":[[303,1]]},"270":{"position":[[340,1],[374,1],[378,1],[403,1],[415,1],[436,1],[438,2],[457,1],[466,1],[479,1],[540,1],[542,1],[1488,1],[2138,1]]},"272":{"position":[[733,1],[1043,1],[1256,1]]},"280":{"position":[[54,1],[362,4],[2318,1],[2882,1],[4320,1]]},"285":{"position":[[623,2]]},"287":{"position":[[752,1],[754,3],[772,1],[774,1],[813,1],[815,2]]},"289":{"position":[[140,1],[156,1],[158,1],[234,1],[236,2],[364,1],[445,1],[608,1],[688,1]]},"291":{"position":[[413,1],[467,1],[511,1],[564,1],[931,1],[1240,1],[1242,1],[1306,1],[1320,1],[1402,1],[1442,1],[1457,1],[1504,1],[1526,1],[1591,2],[1620,2],[1833,1],[1877,1],[1910,1],[1924,1],[2023,1],[2224,1],[2763,1]]},"293":{"position":[[1141,1],[1231,1],[1233,1],[1297,1],[1299,2],[1661,1],[1741,1]]},"295":{"position":[[572,1],[633,1],[700,1],[1245,1],[1868,1]]},"297":{"position":[[246,1]]},"299":{"position":[[278,1],[377,1],[379,1],[418,1],[420,2]]},"301":{"position":[[284,1],[286,1],[325,1],[342,1],[344,1],[392,1],[402,1],[484,1],[525,1],[540,1],[587,1],[609,1],[653,1],[655,1],[703,1],[705,1],[707,2],[736,2]]},"305":{"position":[[199,1]]},"311":{"position":[[708,1]]},"315":{"position":[[873,1],[1074,1],[1135,1],[1137,2],[1205,2],[1353,1],[1380,2],[1479,1],[1506,2],[1605,2]]},"321":{"position":[[292,1],[335,1],[352,1],[434,1],[1430,1],[1462,1],[1479,1],[1528,1]]},"327":{"position":[[373,1],[876,1]]},"331":{"position":[[412,3],[618,2],[703,1],[816,4],[1784,1]]},"333":{"position":[[130,1],[368,1]]},"335":{"position":[[232,1],[721,1],[761,1]]},"339":{"position":[[126,1]]},"341":{"position":[[39,1],[141,1],[253,1],[320,2],[387,1],[475,1],[656,1],[695,1],[779,1],[1034,1],[1088,2],[1221,1],[1476,1],[1530,2]]},"345":{"position":[[578,1]]},"347":{"position":[[1144,1]]},"355":{"position":[[539,1]]},"357":{"position":[[147,1]]},"361":{"position":[[355,1]]},"365":{"position":[[190,1]]},"369":{"position":[[478,1]]},"373":{"position":[[81,1]]},"375":{"position":[[31,1]]},"379":{"position":[[209,1]]},"383":{"position":[[297,1]]},"397":{"position":[[552,1]]},"399":{"position":[[384,1],[462,1]]},"409":{"position":[[339,1],[678,1],[942,1]]},"411":{"position":[[250,1],[454,1],[972,1]]},"413":{"position":[[265,1],[621,1],[712,1],[790,1]]},"429":{"position":[[670,1],[1087,1]]},"431":{"position":[[75,2],[254,2]]},"435":{"position":[[349,1]]},"437":{"position":[[1674,1],[1791,1],[1991,1]]},"439":{"position":[[155,1],[322,1]]},"447":{"position":[[71,1],[284,1]]},"455":{"position":[[190,1],[1180,1],[1311,1]]},"457":{"position":[[186,1]]},"459":{"position":[[259,1],[289,1]]},"461":{"position":[[195,1],[355,1],[545,1],[729,1]]},"463":{"position":[[214,1],[311,2],[436,1]]},"465":{"position":[[183,1],[463,1],[965,2],[975,1]]},"467":{"position":[[379,1]]},"469":{"position":[[561,1],[568,2],[660,1],[697,2],[700,1],[810,1],[826,1],[830,2],[851,2],[891,2],[898,1],[913,1],[925,1],[927,3]]},"471":{"position":[[702,1],[914,1],[1034,1],[1367,1],[1391,1],[1416,1],[1440,1],[1478,1],[1557,1],[1561,2],[1612,1],[1624,1],[1630,1],[1638,2],[1646,1],[1672,1],[1697,1],[1699,1],[1701,3],[1744,1],[1752,2],[1779,2],[1805,1],[1807,2],[1871,1],[1885,1],[1931,1],[1938,2],[1977,1],[1986,1],[1997,2],[2000,1],[2020,2],[2079,2],[2143,2],[2151,1],[2180,1],[2189,1],[2200,2],[2203,1],[2223,1],[2302,1],[2304,1],[2306,2],[2358,2],[2381,2],[2388,1],[2396,2],[2404,1],[2425,1],[2445,1],[2452,1],[2463,1],[2465,2],[2522,2],[2530,1],[2551,1],[2560,2],[2596,1],[2603,1],[2605,1],[2655,1],[2662,2],[2724,1],[2761,2],[2764,1],[2792,2],[2900,1],[2916,1],[2922,2],[2946,2],[2953,1],[2968,1],[2970,1],[2972,3],[3464,1],[3800,1],[3808,1],[3813,1],[3927,1],[3935,1],[3940,1],[4282,1]]},"473":{"position":[[469,1],[473,1],[475,1],[1526,1],[1616,1],[1635,1]]},"475":{"position":[[1631,1],[1826,1],[1835,2],[1929,2],[2042,1],[2058,1],[2065,2],[2137,2],[2144,1],[2166,1],[2168,1],[2170,3],[2510,1],[3305,2],[4566,1],[4678,1],[4738,1],[4749,1],[4820,1],[4861,1],[4872,1],[4927,1],[4939,1],[5024,1],[5034,1],[5092,1],[5104,1],[5156,1],[5166,1],[5218,1],[5230,1],[5284,1],[5294,1],[5379,1],[5388,1],[5446,1],[5456,1],[5508,1],[5518,1],[5566,1],[5576,1],[5630,1],[5640,1],[5776,1],[6033,1],[6374,1],[6769,1],[7056,1]]},"477":{"position":[[843,1],[857,2],[932,2],[942,2],[949,1],[964,1],[1055,1],[1071,1],[1077,2],[1138,2],[1193,2],[1200,1],[1223,1],[1225,1],[1227,3],[1596,1],[2727,1],[3115,1],[3549,1],[3661,1],[3720,1],[3731,1],[3796,1],[3801,1],[3841,1],[3852,1],[3908,1],[3920,1],[4005,1],[4014,1],[4073,1],[4085,1],[4136,1],[4146,1],[4198,1],[4210,1],[4264,1],[4274,1],[4360,1],[4369,1],[4427,1],[4437,1],[4489,1],[4498,1],[4547,1],[4556,1],[4610,1],[4619,1],[4878,1]]},"479":{"position":[[727,1],[2405,2],[2463,1],[2475,4],[2560,1],[2848,1],[2871,1],[3121,1],[3143,1],[3506,1],[4032,3],[4282,1],[4388,1],[4399,1],[4457,1],[4469,1],[4520,1],[4532,1],[4583,1],[4595,1],[4650,1],[4662,1],[4996,1],[5058,1],[5082,1],[5928,1],[6038,1],[6665,1],[6882,2]]},"481":{"position":[[644,1],[766,1],[777,1],[836,1],[848,1],[902,1],[914,1],[966,1],[977,1],[1033,1],[1044,1],[1262,1]]},"483":{"position":[[147,1],[1402,1]]},"489":{"position":[[350,1]]},"497":{"position":[[30,1]]},"503":{"position":[[85,1]]},"513":{"position":[[39,1]]},"525":{"position":[[873,1],[1297,1]]},"535":{"position":[[48,1],[249,1],[251,3],[272,1],[384,2],[719,1],[721,3],[742,1],[934,2],[991,1],[993,3],[1014,1],[1180,2]]},"541":{"position":[[55,1]]},"549":{"position":[[381,1],[383,3]]},"563":{"position":[[418,1],[420,3],[444,1],[510,1],[523,1],[585,2],[595,1],[678,1],[680,1],[758,1],[760,1],[762,1],[764,1],[766,2],[1305,1],[1307,3],[1385,1],[1447,1],[1460,1],[1522,1],[1524,1],[1526,2]]},"593":{"position":[[158,1]]},"600":{"position":[[152,1]]},"602":{"position":[[141,1],[143,3],[321,1],[323,3],[346,6]]},"608":{"position":[[166,1],[190,1],[290,2],[967,1],[1019,1]]},"612":{"position":[[347,1]]},"614":{"position":[[112,2]]},"620":{"position":[[608,1]]},"628":{"position":[[285,1],[557,1],[667,1]]},"632":{"position":[[78,1],[80,3],[100,1],[167,2],[196,1],[263,2],[1032,1]]},"634":{"position":[[131,1],[133,3],[149,1],[211,2],[230,1],[265,2],[294,1],[329,2]]},"636":{"position":[[464,1],[509,1],[526,1],[623,1]]},"638":{"position":[[341,1],[386,1],[403,1],[533,1]]},"640":{"position":[[999,1],[1001,3]]},"644":{"position":[[44,1]]},"648":{"position":[[263,1],[534,1],[643,1]]},"652":{"position":[[76,1],[78,3],[96,1],[163,2],[890,1]]},"654":{"position":[[129,1],[131,3],[147,1],[209,2],[226,1],[261,2]]},"656":{"position":[[56,1],[101,1],[118,1],[207,1]]},"658":{"position":[[56,1],[101,1],[118,1],[184,1]]},"664":{"position":[[32,1],[97,3],[101,1],[185,3]]},"666":{"position":[[106,1],[108,4]]},"668":{"position":[[144,1],[461,1],[463,4]]},"672":{"position":[[101,1],[103,3],[131,1],[168,1],[265,2],[427,1],[466,1],[468,2],[2114,1]]},"674":{"position":[[444,1],[581,1],[626,1],[633,100],[1223,1],[1312,100]]},"676":{"position":[[500,1],[636,1],[681,1],[688,255],[1489,1],[1577,255]]},"678":{"position":[[121,2],[131,2],[141,2],[151,2],[161,13],[368,1],[383,1],[414,12],[523,1],[538,1],[561,1],[593,12],[822,1],[952,1],[957,1],[994,2],[1053,20],[1228,1],[1251,2],[1354,1],[1365,2],[1371,1],[1381,2],[1386,1],[1396,2],[1401,1],[1411,2],[1416,1],[1426,2],[1431,1],[1441,2],[1446,1],[1456,2],[1461,1],[1471,2],[1476,1],[1486,2],[1491,1],[1501,23]]},"680":{"position":[[236,1],[238,3],[266,1],[303,1],[328,2],[390,3],[472,1],[497,1],[499,2],[775,2],[785,1],[990,1],[997,1],[1010,1],[1012,1],[1014,1],[1018,1],[1022,1],[1031,1],[1053,2],[1093,1],[1100,1],[1110,1],[1112,1],[1114,1],[1118,1],[1122,1],[1126,1],[1148,2],[1188,1],[1195,1],[1207,1],[1221,1],[1223,1],[1227,1],[1231,1],[1239,1],[1261,2],[1301,1],[1308,1],[1319,1],[1333,1],[1335,1],[1339,1],[1343,1],[1350,1],[1372,2],[1412,1],[1419,1],[1431,1],[1439,1],[1441,1],[1445,1],[1449,1],[1456,1],[1478,2],[1518,1],[1525,1],[1537,1],[1545,1],[1547,1],[1551,1],[1555,1],[1561,1],[1583,2],[1623,1],[1630,1],[1642,1],[1650,1],[1652,1],[1656,1],[1660,1],[1666,1],[1688,139]]},"686":{"position":[[295,4]]},"688":{"position":[[290,1],[335,1],[352,1],[425,1]]},"690":{"position":[[197,1],[242,1],[259,1],[329,1],[394,1],[420,1],[476,1],[478,1],[480,2],[579,1],[605,1],[619,1],[621,1],[623,2]]},"692":{"position":[[155,1],[200,1],[217,1],[290,1]]},"694":{"position":[[51,1],[53,3],[80,1],[133,2],[528,1],[530,3],[611,1],[661,2],[970,1],[972,3],[999,1],[1001,3],[1030,2]]},"726":{"position":[[403,1]]},"774":{"position":[[212,1]]},"782":{"position":[[186,1]]},"784":{"position":[[182,1]]},"790":{"position":[[555,2]]},"792":{"position":[[874,2],[939,1],[941,3],[953,1],[967,1],[1012,2],[1027,1],[1072,1],[1074,2],[1826,1]]},"798":{"position":[[348,1],[399,1],[1388,1]]},"802":{"position":[[30,1],[97,1],[133,1],[156,2],[629,1]]},"804":{"position":[[88,1],[128,1],[141,1],[220,1],[256,1],[279,2],[292,1],[307,1]]},"806":{"position":[[55,1],[114,1],[193,1],[229,1],[288,2]]},"814":{"position":[[248,1],[250,1],[295,1],[297,1],[338,1],[340,1],[380,1],[382,1],[416,1],[418,1],[452,1],[454,1]]},"816":{"position":[[0,1],[2,1],[504,1]]},"818":{"position":[[27,1]]},"820":{"position":[[0,1],[2,1],[578,1],[606,1],[904,1]]},"824":{"position":[[33,1]]},"828":{"position":[[35,1]]},"830":{"position":[[34,1]]},"832":{"position":[[53,1]]},"834":{"position":[[36,1],[236,1]]},"836":{"position":[[34,1],[450,1],[552,1]]},"838":{"position":[[35,1],[327,1],[380,1],[557,1],[559,3]]},"840":{"position":[[34,1]]},"842":{"position":[[33,1]]},"844":{"position":[[52,1],[131,1]]},"846":{"position":[[35,1]]},"848":{"position":[[41,1]]},"850":{"position":[[39,1]]},"852":{"position":[[38,3],[42,1]]},"854":{"position":[[36,3],[40,1]]},"856":{"position":[[72,1],[357,1]]},"858":{"position":[[596,2],[726,1],[848,1],[1131,1],[1341,1],[1343,1],[1458,2],[1461,1],[1519,1],[1521,2],[2131,1],[2234,1]]},"864":{"position":[[154,1]]},"866":{"position":[[159,1]]},"868":{"position":[[362,1]]},"870":{"position":[[219,1]]},"876":{"position":[[238,1]]},"878":{"position":[[213,1],[1100,3]]},"884":{"position":[[70,1]]},"886":{"position":[[150,1],[199,1],[236,1],[269,1]]},"892":{"position":[[683,1],[771,1],[839,1],[881,1],[908,1],[1181,1],[1183,4]]},"894":{"position":[[73,2]]},"902":{"position":[[263,1]]},"906":{"position":[[307,1],[402,1],[432,1]]},"912":{"position":[[149,1],[886,1]]},"918":{"position":[[740,1],[924,1]]},"922":{"position":[[140,1],[142,3],[210,1]]},"926":{"position":[[236,3],[948,1],[950,3]]},"930":{"position":[[136,3]]},"941":{"position":[[396,2],[421,2],[438,2],[456,2]]},"945":{"position":[[362,1],[714,1]]},"951":{"position":[[417,1],[431,1],[448,1]]},"953":{"position":[[519,1],[543,1]]},"961":{"position":[[86,1],[88,3]]},"963":{"position":[[62,1],[64,3]]},"965":{"position":[[147,1]]},"971":{"position":[[1366,2],[1389,2],[1454,2],[1569,1],[1590,1],[1693,1],[1702,2],[1709,1],[1756,1],[1769,2],[1805,1],[1831,5],[1858,1],[1932,1],[1934,1],[1971,1],[1979,1],[1981,2],[2035,1],[2111,2]]},"973":{"position":[[2668,1]]},"977":{"position":[[453,1]]},"979":{"position":[[20,1],[22,1],[167,1],[186,2],[204,2],[264,5],[312,1],[671,1],[734,1],[802,1],[980,1],[1302,1],[1313,1],[1315,1],[1347,1],[1396,1],[1408,1],[1438,2]]},"981":{"position":[[20,1],[22,1],[167,1],[186,2],[203,1],[205,1],[207,3],[260,1],[485,1],[519,1],[876,2]]},"983":{"position":[[71,1]]},"1005":{"position":[[173,1]]},"1013":{"position":[[342,1],[344,3],[370,1],[470,2]]},"1017":{"position":[[54,1],[56,3],[161,1],[1855,1],[5143,1],[5168,1],[5183,1],[5185,2],[5249,1],[5329,2],[5401,2]]},"1019":{"position":[[54,1],[56,3],[161,1],[456,1]]},"1021":{"position":[[53,1],[55,3],[368,1]]},"1023":{"position":[[53,1],[55,3],[166,1],[725,1],[743,1],[1324,1],[1326,3],[1496,1],[1498,3],[1623,2],[1665,3],[1727,1],[1945,3],[3755,1]]},"1025":{"position":[[53,1],[55,3],[160,1],[1184,1],[1186,3],[1365,1],[1367,3],[1486,2],[1543,3],[1742,1],[1981,3]]},"1027":{"position":[[94,1],[105,1],[147,2],[185,2]]},"1029":{"position":[[122,1],[138,1],[204,2]]},"1031":{"position":[[309,1],[732,1],[734,3],[848,1],[850,3],[967,1],[969,3],[1097,1],[1099,3],[1199,1],[1201,1],[1258,1],[1260,2],[1332,1],[1334,3]]},"1033":{"position":[[101,3],[268,2],[373,2]]},"1039":{"position":[[625,1],[829,1],[831,3],[1150,1],[1239,1],[1278,1]]},"1041":{"position":[[913,1]]},"1043":{"position":[[252,1],[254,3]]},"1045":{"position":[[252,1],[254,3],[298,1],[300,1],[424,2],[427,1],[523,2],[526,1],[607,2],[610,1],[687,2],[690,1],[759,2],[762,1],[843,2],[846,1],[904,1],[906,1],[960,1],[962,2],[1210,2]]},"1047":{"position":[[171,1],[173,3],[217,6],[479,1],[481,3],[525,6]]},"1049":{"position":[[300,1],[302,3],[346,6],[367,1],[369,1],[474,2],[477,1],[582,1],[584,2],[949,1]]},"1051":{"position":[[87,1],[89,3],[133,6],[154,6],[179,1],[181,1],[228,2],[231,1],[277,1],[279,2],[498,1],[659,2],[695,1],[852,1]]},"1055":{"position":[[152,1],[154,3]]},"1057":{"position":[[87,1],[89,3]]},"1063":{"position":[[307,1],[372,1],[576,1],[621,1]]},"1069":{"position":[[837,3],[841,1],[999,1],[1075,1]]},"1071":{"position":[[679,1],[819,1],[821,1],[860,1],[862,2],[940,1]]},"1075":{"position":[[70,1]]},"1077":{"position":[[646,1]]},"1079":{"position":[[42,1]]},"1090":{"position":[[374,1]]},"1092":{"position":[[363,1],[826,1],[849,1]]},"1094":{"position":[[268,1],[284,1],[298,1],[308,1],[319,1],[336,1],[349,1],[364,1],[377,1],[393,1],[406,1],[411,1],[427,1],[447,1],[463,1],[851,1]]},"1098":{"position":[[327,1],[474,1],[544,1]]},"1105":{"position":[[88,1],[90,3]]},"1116":{"position":[[75,1],[77,3]]},"1120":{"position":[[16,2]]},"1126":{"position":[[17,3]]},"1128":{"position":[[17,3]]},"1143":{"position":[[80,1],[82,3]]},"1147":{"position":[[70,1],[300,1],[460,1],[874,1],[1007,5],[1438,1]]},"1154":{"position":[[301,1],[303,3]]},"1156":{"position":[[260,1],[262,3]]},"1160":{"position":[[497,1]]},"1166":{"position":[[140,1]]},"1173":{"position":[[85,1],[87,3]]},"1177":{"position":[[261,1],[263,3],[423,1],[791,1],[1054,1],[1145,1],[1325,1],[1389,1]]},"1185":{"position":[[286,1],[318,1],[357,1]]},"1189":{"position":[[159,2],[242,2],[529,1],[624,1],[1161,1],[1193,1],[1232,1]]},"1191":{"position":[[1235,1],[1401,1]]},"1193":{"position":[[242,3],[379,3],[454,1],[526,2],[802,1],[804,3]]},"1197":{"position":[[565,1],[567,3],[607,1],[645,2],[721,1],[723,3],[763,1],[807,2]]},"1199":{"position":[[218,1],[220,3],[249,1],[301,2],[361,1],[681,1],[683,3],[712,1],[815,2]]},"1203":{"position":[[1536,2],[1651,1],[1653,3]]},"1205":{"position":[[581,3],[644,3]]},"1207":{"position":[[112,1],[431,1],[437,1],[479,1]]},"1211":{"position":[[39,1],[141,1],[253,1],[320,2],[387,1],[475,1],[656,1],[695,1],[779,1],[1034,1],[1088,2],[1221,1],[1476,1],[1530,2]]},"1217":{"position":[[50,1]]},"1221":{"position":[[552,1]]},"1223":{"position":[[384,1],[462,1]]},"1233":{"position":[[339,1],[678,1],[942,1]]},"1235":{"position":[[250,1],[454,1],[972,1]]},"1237":{"position":[[265,1],[621,1],[712,1],[790,1]]},"1255":{"position":[[670,1],[1087,1]]},"1257":{"position":[[75,2],[254,2]]},"1261":{"position":[[349,1]]},"1263":{"position":[[1678,1],[1795,1],[1995,1]]},"1265":{"position":[[155,1],[322,1]]},"1277":{"position":[[578,1]]},"1279":{"position":[[1149,1]]},"1287":{"position":[[539,1]]},"1289":{"position":[[147,1]]},"1293":{"position":[[929,1]]},"1295":{"position":[[213,1]]},"1299":{"position":[[71,1],[284,1]]},"1307":{"position":[[190,1],[1180,1],[1311,1]]},"1309":{"position":[[186,1]]},"1311":{"position":[[259,1],[289,1]]},"1313":{"position":[[195,1],[355,1],[545,1],[729,1]]},"1315":{"position":[[214,1],[311,2],[436,1]]},"1319":{"position":[[371,1]]},"1323":{"position":[[138,1],[364,1]]},"1327":{"position":[[588,1]]},"1331":{"position":[[81,1]]},"1333":{"position":[[31,1]]},"1337":{"position":[[198,1]]},"1341":{"position":[[233,1]]},"1355":{"position":[[899,1]]},"1359":{"position":[[212,2],[236,2],[262,2],[289,2],[315,2],[374,1],[581,2],[621,2],[872,1],[1102,1]]},"1365":{"position":[[345,1]]},"1367":{"position":[[906,1],[1085,1],[1176,1]]},"1371":{"position":[[352,1],[692,1],[788,1]]},"1375":{"position":[[147,1]]},"1377":{"position":[[193,1]]},"1383":{"position":[[350,1]]},"1391":{"position":[[27,1]]},"1397":{"position":[[85,1]]},"1399":{"position":[[233,1]]},"1405":{"position":[[952,1],[976,1],[978,3],[1008,1],[1042,1],[1095,1],[1097,1],[1375,1],[2019,1],[2035,1],[2037,3],[2062,1],[2114,1],[2154,1],[2180,1],[2192,1],[2206,1],[2260,1],[2262,1],[2915,3],[3366,1]]},"1407":{"position":[[385,1],[419,1],[445,2],[448,2],[507,1],[519,1],[540,1],[550,1],[565,1],[582,1],[595,1],[609,1],[629,1],[642,1],[652,1],[662,1],[671,1],[684,1],[702,1],[708,1],[728,1],[746,1],[1069,1],[1160,2]]},"1409":{"position":[[161,1],[195,1],[214,2],[348,1],[369,3]]},"1411":{"position":[[181,1],[215,1],[234,2]]},"1413":{"position":[[437,1],[472,1],[503,2]]},"1417":{"position":[[189,1],[372,1],[374,1],[376,2],[568,1],[739,1],[741,1],[743,2],[988,1],[1159,1],[1161,1],[1163,2],[1261,1],[1311,2]]},"1419":{"position":[[164,1]]},"1421":{"position":[[269,1],[311,1]]},"1423":{"position":[[1709,1]]},"1425":{"position":[[256,1],[317,4],[558,1],[630,1],[766,1],[1630,1]]},"1437":{"position":[[818,1]]},"1441":{"position":[[39,1]]},"1455":{"position":[[126,1],[128,3],[1267,1],[1284,1],[1323,4],[1328,1]]},"1457":{"position":[[124,1],[157,1],[186,1],[204,1],[206,1],[208,1],[385,1],[387,1],[420,1],[449,1],[466,1],[478,1],[499,1],[528,1],[592,1],[594,1],[609,1],[832,1],[1151,1],[1180,1],[1198,1],[1311,3],[1538,1],[1712,1],[1760,2]]},"1459":{"position":[[72,1],[107,1],[157,1],[175,1],[177,2]]},"1461":{"position":[[1290,1]]},"1463":{"position":[[54,1],[91,1],[138,2]]},"1465":{"position":[[46,1],[82,1],[102,2]]},"1469":{"position":[[207,1],[241,1],[261,2],[349,1],[514,1],[528,1],[570,1],[635,2],[678,1],[743,1],[745,1],[747,2]]},"1471":{"position":[[132,1],[172,1],[192,2],[278,1],[442,1],[477,2]]},"1473":{"position":[[465,1],[498,1],[530,2],[614,1],[779,1],[827,1],[829,1],[839,1],[857,2],[872,2],[875,1],[885,1],[903,2],[918,1],[920,1],[922,2]]},"1475":{"position":[[172,1],[212,1],[232,2],[311,1],[475,3]]},"1477":{"position":[[77,1],[111,3]]},"1479":{"position":[[115,4],[120,1],[285,1],[296,1],[298,1],[469,2],[472,1],[474,1],[476,2]]},"1485":{"position":[[618,1],[620,3],[777,1]]},"1487":{"position":[[132,2],[460,1],[507,1],[554,1],[616,1],[694,1]]},"1489":{"position":[[545,2],[677,1],[769,1],[781,2],[841,2],[848,1],[867,1],[895,2],[940,1],[952,2],[1087,2],[1097,2],[1104,1],[1151,1],[1158,1],[1179,2],[1186,1],[1198,2],[1280,1],[1287,1],[1327,1],[1329,1],[1355,2]]},"1491":{"position":[[237,1],[337,1],[449,1],[502,1],[504,1],[513,2],[569,1],[596,1],[608,2],[712,2],[719,1],[738,1],[766,2],[808,1],[820,2],[946,2],[956,2],[963,1],[1010,1],[1017,1],[1038,2],[1045,1],[1057,2],[1139,1],[1146,1],[1186,1],[1188,1],[1214,2]]},"1495":{"position":[[364,1],[380,1],[382,1],[442,1],[444,2],[836,1],[858,1],[1040,3]]},"1497":{"position":[[1154,1],[1156,3],[1188,2],[1520,1],[1536,1],[1538,1],[1602,2],[1617,2],[1631,2],[1666,1],[1668,2]]},"1505":{"position":[[20,2],[791,1],[1042,3],[1325,1],[1591,3],[2330,1],[2385,1],[2446,1],[2465,3],[2512,1],[2603,1],[2690,1],[2773,1],[2851,1],[2873,1],[2906,1],[2960,1],[3053,1],[3124,1],[4078,1],[4282,1],[5028,1],[5030,3],[5159,1],[5970,1],[6197,1],[6449,1],[6884,1],[6945,1],[7116,1],[7828,1],[8414,1],[8689,1],[9353,1],[9434,1],[9511,1],[9847,1],[9919,1],[9936,1],[10025,1],[10053,1],[10466,1]]},"1509":{"position":[[27,1],[29,2],[51,3],[81,2],[131,1],[133,1],[158,2],[191,3],[195,2],[198,1],[233,2],[266,3],[270,1],[272,2],[313,1],[412,2],[419,1],[477,1],[585,1],[1207,2],[1251,1]]},"1511":{"position":[[96,1],[200,1],[778,2],[955,1]]},"1513":{"position":[[0,1],[179,1],[198,1],[222,1],[240,1],[399,1],[429,1],[448,1],[464,1],[612,1],[639,1],[662,1]]},"1519":{"position":[[603,1],[610,1],[797,1],[799,2],[822,3]]},"1527":{"position":[[85,2],[192,2],[289,3],[383,2],[480,2],[564,2],[632,1],[690,2],[800,2],[896,2],[987,2],[1073,2],[1136,2],[1214,2],[1300,2]]},"1531":{"position":[[260,1]]},"1557":{"position":[[55,1]]},"1565":{"position":[[381,1],[383,3]]},"1569":{"position":[[214,1],[224,1],[226,1],[278,1],[280,2]]},"1571":{"position":[[184,1],[356,1],[366,1],[368,1],[409,2],[412,1],[453,2],[456,2],[605,1],[782,1],[912,1],[922,1],[924,1],[976,2],[979,1],[1037,2],[1040,2],[1246,1],[1628,1],[1638,1],[1640,1],[1681,2],[1684,1],[1749,2],[1752,2],[1897,1]]},"1573":{"position":[[17,1],[283,1]]},"1575":{"position":[[154,1],[625,1],[766,1]]},"1577":{"position":[[143,1],[153,1],[155,1],[219,1],[221,2]]},"1579":{"position":[[10,1],[20,1],[22,1],[90,1],[92,2]]},"1581":{"position":[[83,1],[93,1],[95,1],[163,1],[165,1],[209,1],[211,2]]},"1583":{"position":[[53,1],[63,1],[65,1],[79,6],[145,1],[147,2],[254,2]]},"1585":{"position":[[395,1],[503,1]]},"1587":{"position":[[17,1],[298,1]]},"1589":{"position":[[251,1],[444,1]]},"1591":{"position":[[504,1],[1281,1],[1283,3]]},"1597":{"position":[[68,1],[159,1],[1156,1],[1201,1],[1218,1],[1281,1],[1321,1],[1333,1],[1350,1],[1392,1],[1469,2],[1512,1],[1589,1],[1591,1],[1593,2]]},"1603":{"position":[[101,1],[103,3],[131,1],[168,1],[265,2],[516,1],[554,1],[556,2],[2618,1]]},"1605":{"position":[[364,1],[501,1],[546,1],[553,100],[1063,1],[1152,100]]},"1607":{"position":[[418,1],[480,1],[525,1],[532,255],[1097,1],[1188,100]]},"1609":{"position":[[491,1],[627,1],[672,1],[679,255],[1471,1],[1559,255]]},"1611":{"position":[[440,1],[502,1],[547,1],[554,255],[1294,1],[1385,255]]},"1613":{"position":[[0,2],[640,1],[702,1],[747,1],[754,255],[1614,1],[1702,255]]},"1615":{"position":[[121,2],[131,2],[141,2],[151,2],[161,13],[368,1],[383,1],[414,12],[523,1],[538,1],[561,1],[593,12],[814,1],[946,1],[951,1],[988,2],[1047,20],[1222,1],[1245,2],[1348,1],[1359,2],[1365,1],[1375,2],[1380,1],[1390,2],[1395,1],[1405,2],[1410,1],[1420,2],[1425,1],[1435,2],[1440,1],[1450,2],[1455,1],[1465,2],[1470,1],[1480,2],[1485,1],[1495,23],[1657,1],[1703,1],[1730,12]]},"1617":{"position":[[307,1],[309,3],[337,1],[374,1],[399,2],[461,3],[602,1],[627,1],[629,2],[944,2],[954,1],[1159,1],[1166,1],[1179,1],[1181,1],[1183,1],[1187,1],[1191,1],[1200,1],[1222,2],[1262,1],[1269,1],[1279,1],[1281,1],[1283,1],[1287,1],[1291,1],[1295,1],[1317,2],[1357,1],[1364,1],[1376,1],[1390,1],[1392,1],[1396,1],[1400,1],[1408,1],[1430,2],[1470,1],[1477,1],[1488,1],[1502,1],[1504,1],[1508,1],[1512,1],[1519,1],[1541,2],[1581,1],[1588,1],[1600,1],[1608,1],[1610,1],[1614,1],[1618,1],[1625,1],[1647,2],[1687,1],[1694,1],[1706,1],[1714,1],[1716,1],[1720,1],[1724,1],[1730,1],[1752,2],[1792,1],[1799,1],[1811,1],[1819,1],[1821,1],[1825,1],[1829,1],[1835,1],[1857,139]]},"1627":{"position":[[285,1],[557,1],[667,1]]},"1631":{"position":[[78,1],[80,3],[100,1],[167,2],[196,1],[263,2],[1032,1]]},"1633":{"position":[[131,1],[133,3],[149,1],[211,2],[230,1],[265,2],[294,1],[329,2]]},"1636":{"position":[[464,1],[509,1],[526,1],[623,1]]},"1639":{"position":[[341,1],[386,1],[403,1],[533,1]]},"1643":{"position":[[263,1],[534,1],[643,1]]},"1647":{"position":[[76,1],[78,3],[96,1],[163,2],[890,1]]},"1649":{"position":[[129,1],[131,3],[147,1],[209,2],[226,1],[261,2]]},"1652":{"position":[[56,1],[101,1],[118,1],[207,1]]},"1654":{"position":[[56,1],[101,1],[118,1],[184,1]]},"1658":{"position":[[32,1],[97,3],[101,1],[185,3]]},"1660":{"position":[[106,1],[108,4]]},"1662":{"position":[[144,1],[461,1],[463,4]]},"1666":{"position":[[105,1],[489,1],[890,1],[892,3],[925,1],[955,1],[968,1],[970,1],[1001,2],[1004,1],[1006,2],[1018,1],[1031,1],[1033,1],[1064,2],[1067,1],[1099,2],[1102,1],[1104,2],[1118,1],[1131,1],[1133,1],[1163,2],[1166,1],[1168,2],[1178,1],[1191,1],[1193,1],[1224,1],[1226,2],[1248,1],[1250,1],[1295,1],[1297,1],[1328,1],[1330,1],[1332,1],[1334,1],[1336,1],[1338,2],[1436,1],[1572,1],[1824,1]]},"1668":{"position":[[380,1],[788,1],[790,3],[821,1],[851,1],[864,1],[866,1],[897,2],[900,1],[902,2],[916,1],[929,1],[931,1],[961,1],[963,1],[965,2],[975,1],[988,1],[990,1],[1021,1],[1023,2],[1045,1],[1047,1],[1092,1],[1094,1],[1125,1],[1127,1],[1129,1],[1131,1],[1133,1],[1135,2]]},"1670":{"position":[[496,1],[751,1],[1228,1],[1230,3],[1267,1],[1332,1],[1345,1],[1347,1],[1378,2],[1381,1],[1383,2],[1397,1],[1410,1],[1412,1],[1442,1],[1444,1],[1446,2],[1456,1],[1469,1],[1471,1],[1502,1],[1504,2],[1526,1],[1528,1],[1573,1],[1575,1],[1606,1],[1608,1],[1610,1],[1612,1],[1614,1],[1616,2],[2015,1],[2017,3],[2108,1],[2158,3],[2162,2]]},"1672":{"position":[[1063,1],[1282,1],[1417,1],[1419,3],[1446,1],[1474,1],[1486,1],[1488,1],[1490,1],[1521,1],[1523,1],[1525,1],[1527,2]]},"1682":{"position":[[539,1],[834,1],[871,1],[1116,1],[1329,1]]},"1688":{"position":[[227,1]]},"1690":{"position":[[73,1]]},"1692":{"position":[[535,1],[537,3],[553,1],[615,2],[640,1],[780,2],[848,1]]},"1694":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[242,2]]},"1696":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[402,2]]},"1700":{"position":[[12,2],[205,1],[207,3],[223,1],[285,2],[310,1],[340,2],[366,2],[457,1]]},"1721":{"position":[[748,1]]},"1729":{"position":[[290,4]]},"1731":{"position":[[290,1],[335,1],[352,1],[425,1]]},"1733":{"position":[[197,1],[242,1],[259,1],[329,1],[394,1],[420,1],[476,1],[478,1],[480,2],[579,1],[605,1],[619,1],[621,1],[623,2]]},"1735":{"position":[[155,1],[200,1],[217,1],[290,1]]},"1737":{"position":[[49,1],[51,3],[70,1],[123,2],[518,1],[520,3],[593,1],[643,2],[952,1],[954,3],[973,1],[975,3],[1004,2]]},"1743":{"position":[[145,1]]},"1763":{"position":[[567,1],[701,1],[703,3],[719,1],[733,1],[750,1],[752,2]]},"1765":{"position":[[147,1]]},"1771":{"position":[[905,2],[1261,1],[1417,1],[1938,1]]},"1773":{"position":[[332,1]]},"1789":{"position":[[212,1]]},"1797":{"position":[[154,1]]},"1799":{"position":[[150,1]]},"1807":{"position":[[466,1],[468,3],[480,1],[494,1],[539,2],[554,1],[599,1],[601,2],[1353,1]]},"1813":{"position":[[348,1],[399,1],[1388,1]]},"1817":{"position":[[30,1],[97,1],[133,1],[156,2],[627,1],[688,1]]},"1819":{"position":[[88,1],[128,1],[141,1],[220,1],[256,1],[279,2],[292,1],[307,1]]},"1821":{"position":[[55,1],[114,1],[193,1],[229,1],[288,2]]},"1827":{"position":[[731,1],[971,2],[974,3]]},"1831":{"position":[[550,1],[975,1]]},"1833":{"position":[[240,1],[242,1],[287,1],[289,1],[329,1],[331,1],[372,1],[374,1],[408,1],[410,1],[444,1],[446,1]]},"1835":{"position":[[0,1],[2,1],[489,1]]},"1837":{"position":[[0,1],[2,1],[597,1]]},"1839":{"position":[[159,1],[251,1],[339,1],[539,2]]},"1841":{"position":[[866,1]]},"1843":{"position":[[596,2],[726,1],[844,1],[883,1],[1162,1],[1316,1],[1318,1],[1379,2],[1382,1],[1402,1],[1404,2],[2018,1],[2121,1]]},"1847":{"position":[[34,1],[621,1],[666,1],[683,1],[754,1],[793,1],[807,1],[849,1],[947,2],[990,1],[1088,1],[1130,1],[1227,1],[1229,1],[1231,2]]},"1849":{"position":[[36,1],[239,1],[983,1]]},"1851":{"position":[[118,1],[306,1]]},"1853":{"position":[[34,1],[450,1],[552,1]]},"1855":{"position":[[35,1],[327,1],[380,1],[557,1],[559,3],[802,1],[847,1],[864,1],[948,1],[987,1],[1005,1],[1007,1],[1017,1],[1032,2],[1047,2],[1050,1],[1060,1],[1075,2],[1090,1],[1092,2],[1124,2]]},"1857":{"position":[[43,1],[988,1]]},"1859":{"position":[[40,1],[743,1]]},"1861":{"position":[[52,1],[313,1]]},"1863":{"position":[[55,1]]},"1865":{"position":[[348,1],[889,1]]},"1867":{"position":[[50,1],[179,1],[321,1]]},"1869":{"position":[[53,1]]},"1871":{"position":[[54,1]]},"1873":{"position":[[50,1]]},"1875":{"position":[[53,1]]},"1877":{"position":[[55,1]]},"1879":{"position":[[51,1]]},"1881":{"position":[[54,1]]},"1885":{"position":[[31,3],[35,1],[616,1],[632,1],[634,1],[715,1],[717,2]]},"1887":{"position":[[41,1]]},"1889":{"position":[[39,1]]},"1891":{"position":[[43,1]]},"1893":{"position":[[38,3],[42,1]]},"1895":{"position":[[36,3],[40,1]]},"1897":{"position":[[40,3],[44,1]]},"1899":{"position":[[306,1],[814,1],[1059,1],[1146,1],[1148,1],[1552,1],[1554,2]]},"1905":{"position":[[198,1]]},"1907":{"position":[[154,1]]},"1909":{"position":[[362,1]]},"1911":{"position":[[423,1]]},"1913":{"position":[[219,1]]},"1915":{"position":[[227,1]]},"1921":{"position":[[417,1],[431,1],[448,1]]},"1923":{"position":[[519,1],[543,1]]},"1961":{"position":[[403,1]]},"1971":{"position":[[86,1],[88,3]]},"1973":{"position":[[62,1],[64,3]]},"1975":{"position":[[147,1]]},"1981":{"position":[[1366,2],[1389,2],[1454,2],[1569,1],[1590,1],[1693,1],[1702,2],[1709,1],[1756,1],[1769,2],[1805,1],[1831,5],[1858,1],[1932,1],[1934,1],[1971,1],[1979,1],[1981,2],[2035,1],[2111,2]]},"1983":{"position":[[1500,1],[2855,1]]},"1989":{"position":[[238,1]]},"1991":{"position":[[213,1],[1100,3]]},"1997":{"position":[[70,1]]},"1999":{"position":[[150,1],[199,1],[236,1],[269,1]]},"2005":{"position":[[683,1],[771,1],[839,1],[881,1],[908,1],[1181,1],[1183,4]]},"2007":{"position":[[73,2]]},"2015":{"position":[[263,1]]},"2019":{"position":[[307,1],[402,1],[432,1]]},"2021":{"position":[[58,1]]},"2027":{"position":[[149,1],[886,1]]},"2033":{"position":[[204,1]]},"2039":{"position":[[955,1],[1139,1]]},"2043":{"position":[[140,1],[142,3],[210,1]]},"2047":{"position":[[236,3],[948,1],[950,3]]},"2051":{"position":[[136,3]]},"2062":{"position":[[396,2],[421,2],[438,2],[456,2]]},"2066":{"position":[[362,1],[714,1]]},"2070":{"position":[[453,1]]},"2072":{"position":[[20,1],[22,1],[167,1],[186,2],[204,2],[264,5],[312,1],[671,1],[734,1],[802,1],[980,1],[1302,1],[1313,1],[1315,1],[1347,1],[1396,1],[1408,1],[1438,2]]},"2074":{"position":[[20,1],[22,1],[167,1],[186,2],[203,1],[205,1],[207,3],[260,1],[485,1],[519,1],[876,2]]},"2076":{"position":[[71,1]]},"2082":{"position":[[304,1]]},"2086":{"position":[[159,2],[208,2],[345,1],[440,1],[1347,1]]},"2088":{"position":[[305,1],[329,1],[420,2],[423,1],[616,2],[619,1],[811,2],[814,1],[1044,2],[1047,1]]},"2090":{"position":[[1235,1],[1401,1]]},"2092":{"position":[[242,3],[379,3],[454,1],[526,2],[777,1],[779,3]]},"2094":{"position":[[290,1],[323,1],[423,2],[426,1],[637,2],[640,1],[850,2],[853,1],[1101,2],[1104,1]]},"2098":{"position":[[565,1],[567,3],[607,1],[645,2],[721,1],[723,3],[763,1],[807,2]]},"2100":{"position":[[218,1],[220,3],[249,1],[301,2],[361,1],[681,1],[683,3],[712,1],[815,2]]},"2104":{"position":[[230,1],[287,1]]},"2106":{"position":[[1536,2],[1651,1],[1653,3]]},"2108":{"position":[[581,3],[644,3]]},"2110":{"position":[[112,1],[431,1],[437,1],[479,1]]},"2118":{"position":[[126,1],[142,2],[185,3],[328,1],[376,1],[393,1],[465,1]]},"2120":{"position":[[282,1],[466,1],[548,1]]},"2122":{"position":[[176,1],[264,1]]},"2132":{"position":[[837,3],[841,1],[999,1],[1075,1]]},"2134":{"position":[[691,1],[831,1],[833,1],[872,1],[874,2],[952,1]]},"2138":{"position":[[152,1],[154,3]]},"2140":{"position":[[87,1],[89,3]]},"2148":{"position":[[139,1],[473,1]]},"2150":{"position":[[802,3],[1186,1],[1586,1],[1717,1]]},"2154":{"position":[[646,1],[670,1],[672,3],[702,1],[736,1],[789,1],[791,1],[1065,1],[1817,1],[1833,1],[1835,3],[1860,1],[1912,1],[1952,1],[1978,1],[1990,1],[2004,1],[2058,1],[2060,1]]},"2160":{"position":[[1709,1]]},"2166":{"position":[[14,1],[141,1],[186,1]]},"2172":{"position":[[57,1],[59,1],[61,1],[63,1],[65,1],[90,1],[92,1],[94,1],[96,1],[98,1],[143,1],[145,1],[147,1],[149,1],[151,1],[174,1],[176,1],[178,1],[180,1],[182,1],[198,1],[200,1],[202,1],[204,1],[206,1],[225,1],[227,1],[229,1],[231,1],[233,1],[248,1],[250,1],[252,1],[254,1],[256,1],[268,1],[270,1],[272,1],[274,1],[276,1],[303,1],[305,1],[307,1],[309,1],[311,1],[350,1],[352,1],[354,1],[356,1],[358,1],[386,1],[388,1],[390,1],[392,1],[394,1],[409,1],[445,1]]},"2174":{"position":[[59,1],[61,1],[63,1],[65,1],[67,1],[98,1],[100,1],[102,1],[104,1],[106,1],[153,1],[155,1],[157,1],[159,1],[161,1],[190,1],[192,1],[194,1],[196,1],[198,1],[227,1],[229,1],[231,1],[233,1],[235,1],[263,1],[265,1],[267,1],[269,1],[271,1],[300,1],[302,1],[304,1],[306,1],[308,1],[348,1],[350,1],[352,1],[354,1],[356,1],[387,1],[389,1],[391,1],[393,1],[395,1],[422,1]]},"2179":{"position":[[173,1],[175,3]]},"2185":{"position":[[380,1]]},"2187":{"position":[[829,1],[852,1]]},"2189":{"position":[[268,1],[285,1],[306,1],[321,1],[338,1],[367,1],[388,1],[413,1],[434,1],[462,1],[484,1],[1160,1],[1173,1],[1194,1],[1213,1],[1243,1]]},"2195":{"position":[[70,1]]},"2197":{"position":[[646,1]]},"2199":{"position":[[42,1]]},"2211":{"position":[[176,1],[178,3]]},"2217":{"position":[[140,1]]},"2224":{"position":[[85,1],[87,3]]},"2228":{"position":[[261,1],[263,3],[423,1],[791,1],[1054,1],[1145,1],[1260,2]]},"2234":{"position":[[327,1],[474,1],[544,1]]},"2239":{"position":[[88,1],[90,3]]},"2253":{"position":[[31,3]]},"2256":{"position":[[80,1],[82,3]]},"2260":{"position":[[70,1],[300,1],[460,1],[883,1],[1133,5],[1388,3],[1517,1]]},"2267":{"position":[[75,1],[77,3]]},"2271":{"position":[[16,2]]},"2277":{"position":[[17,3]]},"2279":{"position":[[17,3]]},"2287":{"position":[[129,1],[131,3],[734,1],[1270,1],[1287,1],[1326,4],[1331,1]]},"2289":{"position":[[185,1],[218,1],[247,1],[265,1],[267,2],[445,1],[447,1],[480,1],[509,1],[526,1],[538,1],[559,1],[588,1],[652,1],[654,1],[669,1],[892,1],[1211,1],[1240,1],[1258,1],[1371,3],[1592,1],[1766,1],[1814,2]]},"2291":{"position":[[72,1],[107,1],[157,1],[175,1],[177,2]]},"2293":{"position":[[1354,1]]},"2295":{"position":[[54,1],[91,1],[138,2]]},"2297":{"position":[[46,1],[82,1],[102,2]]},"2301":{"position":[[250,1],[284,1],[304,2],[392,1],[557,1],[571,1],[613,1],[678,2],[721,1],[786,1],[788,1],[790,2]]},"2303":{"position":[[132,1],[172,1],[192,2],[278,1],[442,1],[477,2]]},"2305":{"position":[[465,1],[498,1],[530,2],[614,1],[779,1],[827,1],[829,1],[839,1],[857,2],[872,2],[875,1],[885,1],[903,2],[918,1],[920,1],[922,2]]},"2307":{"position":[[172,1],[212,1],[232,2],[311,1],[475,3]]},"2309":{"position":[[77,1],[111,3]]},"2311":{"position":[[115,4],[120,1],[285,1],[296,1],[298,1],[469,2],[472,1],[474,1],[476,2]]},"2313":{"position":[[241,1],[258,1],[421,1],[509,1]]},"2317":{"position":[[618,1],[620,3],[777,1]]},"2319":{"position":[[132,2],[460,1],[507,1],[554,1],[616,1],[694,1]]},"2321":{"position":[[545,2],[677,1],[769,1],[781,2],[841,2],[848,1],[867,1],[895,2],[940,1],[952,2],[1087,2],[1097,2],[1104,1],[1151,1],[1158,1],[1179,2],[1186,1],[1198,2],[1280,1],[1287,1],[1327,1],[1329,1],[1355,2]]},"2323":{"position":[[237,1],[337,1],[449,1],[502,1],[504,1],[513,2],[569,1],[596,1],[608,2],[712,2],[719,1],[738,1],[766,2],[808,1],[820,2],[946,2],[956,2],[963,1],[1010,1],[1017,1],[1038,2],[1045,1],[1057,2],[1139,1],[1146,1],[1186,1],[1188,1],[1214,2]]},"2334":{"position":[[301,1],[303,3]]},"2336":{"position":[[260,1],[262,3]]},"2340":{"position":[[497,1]]},"2350":{"position":[[342,1],[344,3],[370,1],[470,2]]},"2354":{"position":[[54,1],[56,3],[161,1],[1972,1],[5266,1],[5291,1],[5306,1],[5308,2],[5372,1],[5452,2],[5524,2],[6245,1],[6261,1],[6302,2],[6305,1]]},"2356":{"position":[[54,1],[56,3],[161,1],[456,1]]},"2358":{"position":[[53,1],[55,3],[368,1]]},"2360":{"position":[[53,1],[55,3],[166,1],[725,1],[743,1],[1324,1],[1326,3],[1496,1],[1498,3],[1623,2],[1665,3],[1727,1],[1945,3],[3786,1],[4399,1],[4410,1],[4456,2],[4459,1]]},"2362":{"position":[[53,1],[55,3],[160,1],[1184,1],[1186,3],[1365,1],[1367,3],[1486,2],[1543,3],[1742,1],[1981,3]]},"2364":{"position":[[81,1],[83,3],[200,1],[1445,1],[1447,3],[1590,1],[1592,3],[1684,2],[1728,3],[1831,1]]},"2366":{"position":[[94,1],[105,1],[147,2]]},"2368":{"position":[[122,1],[138,1],[184,2],[378,1]]},"2370":{"position":[[309,1],[732,1],[734,3],[848,1],[850,3],[967,1],[969,3],[1097,1],[1099,3],[1199,1],[1201,1],[1258,1],[1260,2],[1332,1],[1334,3]]},"2372":{"position":[[101,3],[268,2],[373,2]]},"2378":{"position":[[625,1],[829,1],[831,3],[1150,1],[1239,1],[1278,1]]},"2380":{"position":[[887,1]]},"2382":{"position":[[252,1],[254,3]]},"2384":{"position":[[252,1],[254,3],[298,1],[300,1],[424,2],[427,1],[523,2],[526,1],[607,2],[610,1],[687,2],[690,1],[759,2],[762,1],[843,2],[846,1],[904,1],[906,1],[960,1],[962,2],[1210,2]]},"2386":{"position":[[171,1],[173,3],[217,6],[479,1],[481,3],[525,6]]},"2388":{"position":[[337,1],[339,3],[383,6],[404,1],[406,1],[511,2],[514,1],[619,1],[621,2],[1152,1]]},"2390":{"position":[[87,1],[89,3],[133,6],[154,6],[179,1],[181,1],[228,2],[231,1],[277,1],[279,2],[498,1],[659,2],[695,1],[852,1]]},"2392":{"position":[[478,1],[1153,1],[1188,1],[1219,1],[1469,1],[1619,1],[1621,1],[1623,1],[1711,2],[1714,1],[1814,1],[1908,1]]},"2394":{"position":[[39,1],[141,1],[253,1],[320,2],[387,1],[475,1],[656,1],[695,1],[779,1],[1034,1],[1088,2],[1221,1],[1476,1],[1530,2]]},"2400":{"position":[[884,1],[1042,1],[1699,1],[1774,1],[1923,1],[2041,1],[2736,1]]},"2402":{"position":[[1215,1],[1710,1]]},"2410":{"position":[[544,1]]},"2412":{"position":[[151,1]]},"2418":{"position":[[264,1]]},"2420":{"position":[[34,1],[335,1],[447,1]]},"2422":{"position":[[897,1]]},"2424":{"position":[[887,1],[1148,1]]},"2428":{"position":[[982,1]]},"2430":{"position":[[213,1]]},"2434":{"position":[[375,1]]},"2438":{"position":[[140,1],[371,1]]},"2442":{"position":[[714,1]]},"2446":{"position":[[81,1]]},"2448":{"position":[[31,1]]},"2452":{"position":[[198,1]]},"2456":{"position":[[232,1]]},"2470":{"position":[[552,1]]},"2472":{"position":[[384,1],[462,1]]},"2482":{"position":[[339,1],[678,1],[942,1]]},"2484":{"position":[[250,1],[454,1],[972,1]]},"2486":{"position":[[265,1],[621,1],[712,1],[790,1]]},"2504":{"position":[[670,1],[1087,1]]},"2506":{"position":[[75,2],[254,2]]},"2510":{"position":[[349,1]]},"2512":{"position":[[506,1],[1690,1],[1923,1],[2037,1],[2237,1]]},"2514":{"position":[[155,1],[322,1]]},"2530":{"position":[[212,2],[236,2],[262,2],[289,2],[315,2],[374,1],[581,2],[621,2],[872,1],[1102,1]]},"2536":{"position":[[345,1]]},"2538":{"position":[[906,1],[1085,1],[1176,1]]},"2542":{"position":[[352,1],[692,1],[788,1]]},"2546":{"position":[[147,1]]},"2548":{"position":[[193,1]]},"2554":{"position":[[365,1]]},"2562":{"position":[[27,1]]},"2568":{"position":[[86,1]]},"2570":{"position":[[242,1]]},"2574":{"position":[[520,2],[544,2],[571,2],[599,2],[625,2],[853,1]]},"2576":{"position":[[73,1]]},"2578":{"position":[[197,1],[424,1]]},"2586":{"position":[[71,1],[284,1]]},"2594":{"position":[[190,1],[1234,1],[1369,1]]},"2596":{"position":[[186,1]]},"2598":{"position":[[268,1],[298,1]]},"2600":{"position":[[195,1],[355,1],[545,1],[729,1]]},"2602":{"position":[[214,1],[311,2],[436,1]]},"2606":{"position":[[842,1]]},"2610":{"position":[[39,1]]},"2622":{"position":[[209,1],[1402,1],[1505,1],[1578,1],[1653,1],[1723,1],[1741,1],[1788,1],[2057,1],[2094,1],[2169,1],[2209,1],[2287,1],[2326,1],[2410,1],[2465,1],[2504,1],[2559,1],[2681,1],[2703,1],[2776,1],[2811,1],[2886,1],[2946,1],[3025,1],[3120,1],[3150,1],[3248,2],[3417,1],[3526,1],[3635,1],[3694,1],[3811,1],[3851,1],[4013,1],[4053,1],[4225,1],[4265,1],[4505,1],[5154,1]]},"2626":{"position":[[10,1]]},"2628":{"position":[[148,1],[209,1],[583,1],[836,1],[875,2],[878,1],[891,1],[967,3],[997,2],[1010,2],[1013,1],[1028,1],[1087,1],[1108,2],[1122,2],[1125,1],[1148,2],[1162,2],[1165,1],[1180,3],[1201,1],[1263,1],[1330,1],[1386,1],[1388,3],[1456,1]]},"2630":{"position":[[158,1],[178,1]]},"2632":{"position":[[99,1],[482,1],[1177,1],[1247,1],[1314,1],[1382,1],[1458,1],[1482,1],[1544,1],[1796,1],[1839,1],[1922,1],[1963,1],[2044,1],[2066,1],[2151,1],[2198,1],[2204,1],[2317,1],[2405,1],[2439,1],[2517,1],[2553,1],[2555,1],[2576,1],[2656,1],[2689,1],[2691,1],[2712,1],[2794,1],[2829,1],[2831,1],[2876,1],[2882,1],[2991,1],[3024,2],[3067,2],[3074,1],[3147,1],[3283,1],[3394,1],[3485,1],[3509,2],[3516,1],[3853,1],[3895,1],[4083,1],[4125,1],[4704,1]]},"2636":{"position":[[167,1],[759,1],[834,1],[902,1],[1015,1],[1133,1],[1207,1],[1323,1],[1373,1],[1412,1],[1467,1],[1576,1],[1619,1],[1702,1],[1738,1],[1744,1],[1857,1],[1946,1],[1984,1],[2014,1],[2125,1],[2159,1],[2239,1],[2278,1],[2280,1],[2325,1],[2331,1],[2440,1],[2488,2],[2614,2],[2657,2],[2664,1],[2737,1],[2783,1],[2807,2],[2814,1],[2840,1],[2864,2],[2871,1],[2965,1]]},"2638":{"position":[[416,1],[472,1]]},"2642":{"position":[[12,1]]},"2644":{"position":[[165,1],[203,1],[575,1],[818,1],[857,2],[860,1],[873,1],[949,3],[979,2],[992,2],[995,1],[1010,1],[1069,1],[1090,2],[1104,2],[1107,1],[1130,2],[1144,2],[1147,1],[1162,3],[1183,1],[1258,1],[1296,1],[1363,1],[1365,2],[1558,1]]},"2646":{"position":[[515,1],[618,1],[620,2],[693,2],[768,2],[889,1],[891,2],[961,2],[1038,2],[1207,1],[1209,2],[1281,2],[1462,1],[1464,2],[1731,1]]},"2650":{"position":[[111,2],[237,1]]},"2654":{"position":[[321,1],[351,1]]},"2656":{"position":[[195,1],[355,1],[545,1],[729,1]]},"2658":{"position":[[214,1],[311,2],[436,1]]},"2660":{"position":[[371,1]]},"2664":{"position":[[214,1],[224,1],[226,1],[278,1],[280,2]]},"2666":{"position":[[184,1],[356,1],[366,1],[368,1],[409,2],[412,1],[453,2],[456,2],[605,1],[782,1],[912,1],[922,1],[924,1],[976,2],[979,1],[1037,2],[1040,2],[1246,1],[1628,1],[1638,1],[1640,1],[1681,2],[1684,1],[1749,2],[1752,2],[1897,1]]},"2668":{"position":[[17,1],[283,1]]},"2670":{"position":[[154,1],[625,1],[766,1]]},"2672":{"position":[[143,1],[153,1],[155,1],[219,1],[221,2]]},"2674":{"position":[[10,1],[20,1],[22,1],[90,1],[92,2]]},"2676":{"position":[[83,1],[93,1],[95,1],[163,1],[165,1],[209,1],[211,2]]},"2678":{"position":[[53,1],[63,1],[65,1],[79,6],[145,1],[147,2],[254,2]]},"2680":{"position":[[395,1],[503,1]]},"2682":{"position":[[17,1],[298,1]]},"2684":{"position":[[251,1],[444,1]]},"2686":{"position":[[20,2],[806,1],[1057,3],[1340,1],[1606,3],[2345,1],[2400,1],[2461,1],[2480,3],[2527,1],[2618,1],[2705,1],[2788,1],[2866,1],[2888,1],[2921,1],[2975,1],[3068,1],[3139,1],[4136,1],[4343,1],[5090,1],[5092,3],[5221,1],[6033,1],[6266,1],[6518,1],[6965,1],[7026,1],[7197,1],[7925,1],[8515,1],[8798,1],[9500,1],[9542,1],[9590,1],[9667,1],[10003,1],[10064,1],[10081,1],[10137,1],[10173,1],[10594,1]]},"2690":{"position":[[364,1],[380,1],[382,1],[442,1],[444,2],[836,1],[858,1],[1040,3]]},"2692":{"position":[[1154,1],[1156,3],[1188,2],[1520,1],[1536,1],[1538,1],[1602,2],[1617,2],[1631,2],[1666,1],[1668,2]]},"2702":{"position":[[603,1],[610,1],[797,1],[799,2],[822,3]]},"2708":{"position":[[1047,1],[1049,3],[1169,1],[1171,1],[1255,1],[1257,2],[1479,1],[1491,1],[1566,2],[1638,1],[2050,1],[2582,1],[2730,1]]},"2712":{"position":[[228,1],[347,1],[381,1],[385,1],[473,1],[485,1],[521,2],[561,1],[573,1],[661,2],[1016,1]]},"2714":{"position":[[48,1],[50,3],[80,1],[133,1],[135,1]]},"2724":{"position":[[433,1]]},"2726":{"position":[[55,1]]},"2734":{"position":[[381,1],[383,3]]},"2736":{"position":[[554,1],[556,3]]},"2760":{"position":[[85,2],[192,2],[289,3],[384,2],[481,2],[565,2],[633,1],[691,2],[801,2],[898,2],[994,2],[1085,2],[1171,2],[1234,2],[1312,2],[1398,2],[1480,2]]},"2762":{"position":[[125,1],[980,1]]},"2768":{"position":[[27,1],[29,2],[51,3],[81,2],[131,1],[133,1],[158,2],[191,3],[195,2],[198,1],[233,2],[266,3],[270,1],[272,2],[313,1],[412,2],[419,1],[477,1],[585,1],[1207,2],[1251,1]]},"2770":{"position":[[96,1],[200,1],[778,2],[955,1]]},"2772":{"position":[[0,1],[179,1],[198,1],[222,1],[240,1],[399,1],[429,1],[448,1],[464,1],[612,1],[639,1],[662,1]]},"2780":{"position":[[68,1],[159,1],[1156,1],[1190,1],[1207,1],[1233,1],[1285,1],[1297,1],[1314,1],[1356,1],[1433,2],[1476,1],[1553,1],[1555,1],[1557,2]]},"2784":{"position":[[504,1],[1281,1],[1283,3]]},"2788":{"position":[[101,1],[103,3],[131,1],[168,1],[265,2],[516,1],[554,1],[556,2],[2618,1]]},"2790":{"position":[[364,1],[501,1],[546,1],[553,100],[1063,1],[1152,100]]},"2792":{"position":[[418,1],[480,1],[525,1],[532,255],[1097,1],[1188,100]]},"2794":{"position":[[491,1],[627,1],[672,1],[679,255],[1471,1],[1559,255]]},"2796":{"position":[[440,1],[502,1],[547,1],[554,255],[1294,1],[1385,255]]},"2798":{"position":[[0,2],[640,1],[702,1],[747,1],[754,255],[1614,1],[1702,255]]},"2800":{"position":[[121,2],[131,2],[141,2],[151,2],[161,13],[368,1],[383,1],[414,12],[523,1],[538,1],[561,1],[593,12],[814,1],[946,1],[951,1],[988,2],[1047,20],[1222,1],[1245,2],[1348,1],[1359,2],[1365,1],[1375,2],[1380,1],[1390,2],[1395,1],[1405,2],[1410,1],[1420,2],[1425,1],[1435,2],[1440,1],[1450,2],[1455,1],[1465,2],[1470,1],[1480,2],[1485,1],[1495,23],[1657,1],[1703,1],[1730,12]]},"2802":{"position":[[307,1],[309,3],[337,1],[374,1],[399,2],[461,3],[602,1],[627,1],[629,2],[944,2],[954,1],[1159,1],[1166,1],[1179,1],[1181,1],[1183,1],[1187,1],[1191,1],[1200,1],[1222,2],[1262,1],[1269,1],[1279,1],[1281,1],[1283,1],[1287,1],[1291,1],[1295,1],[1317,2],[1357,1],[1364,1],[1376,1],[1390,1],[1392,1],[1396,1],[1400,1],[1408,1],[1430,2],[1470,1],[1477,1],[1488,1],[1502,1],[1504,1],[1508,1],[1512,1],[1519,1],[1541,2],[1581,1],[1588,1],[1600,1],[1608,1],[1610,1],[1614,1],[1618,1],[1625,1],[1647,2],[1687,1],[1694,1],[1706,1],[1714,1],[1716,1],[1720,1],[1724,1],[1730,1],[1752,2],[1792,1],[1799,1],[1811,1],[1819,1],[1821,1],[1825,1],[1829,1],[1835,1],[1857,139]]},"2814":{"position":[[285,1],[557,1],[667,1]]},"2818":{"position":[[78,1],[80,3],[100,1],[167,2],[196,1],[263,2],[1032,1]]},"2820":{"position":[[131,1],[133,3],[149,1],[211,2],[230,1],[265,2],[294,1],[329,2]]},"2823":{"position":[[464,1],[498,1],[515,1],[574,1]]},"2826":{"position":[[341,1],[375,1],[392,1],[474,1]]},"2830":{"position":[[105,1],[492,1],[893,1],[895,3],[928,1],[958,1],[971,1],[973,1],[1004,2],[1007,1],[1009,2],[1021,1],[1034,1],[1036,1],[1067,2],[1070,1],[1102,2],[1105,1],[1107,2],[1121,1],[1134,1],[1136,1],[1166,2],[1169,1],[1171,2],[1181,1],[1194,1],[1196,1],[1227,1],[1229,2],[1251,1],[1253,1],[1298,1],[1300,1],[1331,1],[1333,1],[1335,1],[1337,1],[1339,1],[1341,2],[1439,1],[1575,1],[1827,1]]},"2832":{"position":[[380,1],[794,1],[796,3],[827,1],[857,1],[870,1],[872,1],[903,2],[906,1],[908,2],[922,1],[935,1],[937,1],[967,1],[969,1],[971,2],[981,1],[994,1],[996,1],[1027,1],[1029,2],[1051,1],[1053,1],[1098,1],[1100,1],[1131,1],[1133,1],[1135,1],[1137,1],[1139,1],[1141,2]]},"2834":{"position":[[496,1],[751,1],[1234,1],[1236,3],[1273,1],[1338,1],[1351,1],[1353,1],[1384,2],[1387,1],[1389,2],[1403,1],[1416,1],[1418,1],[1448,1],[1450,1],[1452,2],[1462,1],[1475,1],[1477,1],[1508,1],[1510,2],[1532,1],[1534,1],[1579,1],[1581,1],[1612,1],[1614,1],[1616,1],[1618,1],[1620,1],[1622,2],[2021,1],[2023,3],[2114,1],[2164,3],[2168,2]]},"2836":{"position":[[1063,1],[1282,1],[1417,1],[1419,3],[1446,1],[1474,1],[1486,1],[1488,1],[1490,1],[1521,1],[1523,1],[1525,1],[1527,2]]},"2844":{"position":[[421,1]]},"2846":{"position":[[539,1],[834,1],[871,1],[1116,1],[1329,1]]},"2852":{"position":[[227,1]]},"2854":{"position":[[73,1]]},"2856":{"position":[[535,1],[537,3],[553,1],[615,2],[640,1],[780,2],[848,1]]},"2858":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[242,2]]},"2860":{"position":[[0,1],[2,3],[18,1],[80,2],[105,1],[402,2]]},"2864":{"position":[[190,1],[192,3],[208,1],[270,2],[295,1],[325,2],[351,2],[460,1]]},"2887":{"position":[[748,1]]},"2895":{"position":[[263,1],[534,1],[643,1]]},"2899":{"position":[[76,1],[78,3],[96,1],[163,2],[890,1]]},"2901":{"position":[[129,1],[131,3],[147,1],[209,2],[226,1],[261,2]]},"2904":{"position":[[56,1],[90,1],[107,1],[160,1]]},"2906":{"position":[[56,1],[90,1],[107,1],[135,1]]},"2910":{"position":[[32,1],[97,3],[101,1],[185,3]]},"2912":{"position":[[106,1],[108,4]]},"2914":{"position":[[157,1],[525,1],[527,4]]},"2918":{"position":[[290,4]]},"2920":{"position":[[290,1],[324,1],[341,1],[370,1]]},"2922":{"position":[[197,1],[231,1],[248,1],[277,1],[358,1],[384,1],[440,1],[442,1],[444,2],[543,1],[569,1],[583,1],[585,1],[587,2]]},"2924":{"position":[[155,1],[189,1],[206,1],[235,1]]},"2926":{"position":[[49,1],[51,3],[70,1],[123,2],[518,1],[520,3],[593,1],[643,2],[952,1],[954,3],[973,1],[975,3],[1004,2]]},"2930":{"position":[[905,2],[1261,1],[1417,1],[1938,1]]},"2932":{"position":[[332,1]]},"2948":{"position":[[145,1]]},"2968":{"position":[[567,1],[701,1],[703,3],[719,1],[733,1],[750,1],[752,2]]},"2970":{"position":[[173,1],[236,1],[249,1],[331,1],[371,1],[386,1],[433,1],[455,1],[513,2],[542,2]]},"2974":{"position":[[357,1],[689,1]]},"3006":{"position":[[403,1]]},"3026":{"position":[[154,1]]},"3028":{"position":[[150,1]]},"3036":{"position":[[544,1],[546,3],[558,1],[572,1],[617,2],[632,1],[677,1],[679,2],[1431,1]]},"3042":{"position":[[360,1],[411,1],[1236,1],[1325,1]]},"3046":{"position":[[30,1],[100,1],[141,1],[156,1],[203,1],[225,1],[262,2],[291,2],[771,1],[832,1]]},"3048":{"position":[[88,1],[128,1],[141,1],[223,1],[263,1],[278,1],[325,1],[347,1],[384,2],[413,2]]},"3050":{"position":[[55,1],[114,1],[196,1],[236,1],[251,1],[298,1],[320,1],[393,2],[422,2]]},"3056":{"position":[[697,1],[937,2],[940,3]]},"3060":{"position":[[624,1],[1059,1]]},"3062":{"position":[[240,1],[242,1],[287,1],[289,1],[329,1],[331,1],[372,1],[374,1],[415,1],[417,1],[451,1],[453,1]]},"3064":{"position":[[0,1],[2,1]]},"3066":{"position":[[0,1]]},"3068":{"position":[[121,1],[167,1],[198,1],[562,2]]},"3070":{"position":[[877,1]]},"3072":{"position":[[884,2],[1014,1],[1132,1],[1171,1],[1450,1],[1604,1],[1606,1],[1667,2],[1670,1],[1690,1],[1692,2],[2306,1],[2409,1]]},"3076":{"position":[[34,1],[621,1],[655,1],[672,1],[709,1],[757,1],[771,1],[813,1],[911,2],[954,1],[1052,1],[1094,1],[1191,1],[1193,1],[1195,2]]},"3078":{"position":[[36,1],[239,1],[983,1]]},"3080":{"position":[[118,1],[306,1]]},"3082":{"position":[[34,1],[450,1],[552,1]]},"3084":{"position":[[35,1],[327,1],[380,1],[557,1],[559,3],[802,1],[836,1],[853,1],[904,1],[951,1],[969,1],[971,1],[981,1],[996,2],[1011,2],[1014,1],[1024,1],[1039,2],[1054,1],[1056,2],[1088,2]]},"3086":{"position":[[28,1],[306,1]]},"3088":{"position":[[43,1],[988,1]]},"3090":{"position":[[40,1],[743,1]]},"3092":{"position":[[52,1],[313,1]]},"3094":{"position":[[55,1]]},"3096":{"position":[[348,1],[889,1]]},"3098":{"position":[[50,1],[179,1],[321,1]]},"3100":{"position":[[53,1]]},"3102":{"position":[[54,1]]},"3104":{"position":[[50,1]]},"3106":{"position":[[53,1]]},"3108":{"position":[[55,1]]},"3110":{"position":[[51,1]]},"3112":{"position":[[54,1]]},"3116":{"position":[[31,3],[35,1],[616,1],[632,1],[634,1],[715,1],[717,2]]},"3118":{"position":[[41,1]]},"3120":{"position":[[39,1]]},"3122":{"position":[[43,1]]},"3126":{"position":[[38,3],[42,1]]},"3128":{"position":[[36,3],[40,1]]},"3130":{"position":[[40,3],[44,1]]},"3132":{"position":[[45,3],[49,1]]},"3134":{"position":[[306,1],[814,1],[1059,1],[1146,1],[1148,1],[1552,1],[1554,2]]},"3140":{"position":[[1366,2],[1389,2],[1454,2],[1569,1],[1590,1],[1693,1],[1702,2],[1709,1],[1756,1],[1769,2],[1805,1],[1831,5],[1858,1],[1932,1],[1934,1],[1971,1],[1979,1],[1981,2],[2035,1],[2111,2]]},"3142":{"position":[[1500,1],[2855,1]]},"3148":{"position":[[417,1],[431,1],[448,1]]},"3150":{"position":[[519,1],[543,1]]},"3162":{"position":[[198,1]]},"3164":{"position":[[154,1]]},"3166":{"position":[[362,1]]},"3168":{"position":[[423,1]]},"3170":{"position":[[219,1]]},"3172":{"position":[[227,1]]},"3178":{"position":[[238,1]]},"3180":{"position":[[213,1],[1100,3]]},"3186":{"position":[[70,1]]},"3188":{"position":[[150,1],[199,1],[236,1],[269,1]]},"3194":{"position":[[683,1],[771,1],[839,1],[881,1],[908,1],[1181,1],[1183,4]]},"3196":{"position":[[73,2]]},"3204":{"position":[[263,1]]},"3208":{"position":[[307,1],[402,1],[432,1]]},"3210":{"position":[[31,1]]},"3216":{"position":[[149,1],[886,1]]},"3220":{"position":[[17,2],[793,1],[795,3],[886,1]]},"3224":{"position":[[177,1]]},"3230":{"position":[[955,1],[1139,1]]},"3234":{"position":[[140,1],[142,3],[210,1]]},"3240":{"position":[[236,3],[1038,1],[1040,3]]},"3244":{"position":[[136,3]]},"3251":{"position":[[125,1]]},"3257":{"position":[[396,2],[421,2],[438,2],[456,2]]},"3261":{"position":[[362,1],[714,1]]},"3267":{"position":[[304,1]]},"3271":{"position":[[159,2],[208,2],[345,1],[440,1],[1313,1]]},"3273":{"position":[[305,1],[329,1],[393,2],[396,1],[562,2],[565,1],[730,2],[733,1],[936,2],[939,1]]},"3275":{"position":[[1235,1],[1401,1]]},"3277":{"position":[[242,3],[379,3],[454,1],[526,2],[777,1],[779,3]]},"3279":{"position":[[290,1],[323,1],[396,2],[399,1],[583,2],[586,1],[769,2],[772,1],[993,2],[996,1]]},"3283":{"position":[[565,1],[567,3],[607,1],[645,2],[721,1],[723,3],[763,1],[807,2]]},"3285":{"position":[[218,1],[220,3],[249,1],[301,2],[361,1],[681,1],[683,3],[712,1],[815,2]]},"3287":{"position":[[309,1],[425,1]]},"3289":{"position":[[1536,2],[1651,1],[1653,3]]},"3291":{"position":[[581,3],[644,3]]},"3293":{"position":[[112,1],[431,1],[437,1],[479,1]]},"3299":{"position":[[86,1],[88,3]]},"3301":{"position":[[62,1],[64,3]]},"3303":{"position":[[147,1]]},"3307":{"position":[[453,1]]},"3309":{"position":[[20,1],[22,1],[167,1],[186,2],[204,2],[264,5],[312,1],[671,1],[734,1],[802,1],[980,1],[1302,1],[1313,1],[1315,1],[1347,1],[1396,1],[1408,1],[1438,2]]},"3311":{"position":[[20,1],[22,1],[167,1],[186,2],[203,1],[205,1],[207,3],[260,1],[485,1],[519,1],[876,2]]},"3313":{"position":[[71,1]]},"3318":{"position":[[86,1],[88,3]]},"3320":{"position":[[62,1],[64,3]]},"3322":{"position":[[147,1]]},"3324":{"position":[[4867,1],[5358,1]]},"3327":{"position":[[117,1],[119,3],[553,1],[586,1],[603,1],[618,1]]},"3335":{"position":[[837,3],[841,1],[999,1],[1075,1]]},"3337":{"position":[[691,1],[831,1],[833,1],[872,1],[874,2],[952,1]]},"3341":{"position":[[140,1],[156,2],[199,3],[342,1],[379,1],[396,1],[434,1],[695,1],[732,1],[749,1],[787,1]]},"3343":{"position":[[274,1],[450,1],[532,1],[679,1]]},"3345":{"position":[[255,1],[271,2],[365,3],[436,1],[524,1]]},"3353":{"position":[[152,1],[154,3]]},"3355":{"position":[[87,1],[89,3]]},"3363":{"position":[[315,1],[1232,1],[1412,1]]},"3365":{"position":[[510,1],[919,1],[1894,1],[1945,1],[2003,1],[2099,1],[2274,1],[2439,1],[2721,1],[4046,1]]},"3367":{"position":[[496,1],[498,3],[971,1],[973,3],[1092,1],[1094,1],[1146,1],[1148,2],[1447,1],[1449,3],[1453,2],[1621,4],[1825,1],[1934,1],[2038,1],[2453,1],[2571,1],[2751,1],[2761,2],[2853,1],[2934,3],[2940,1],[3027,2],[3030,2],[3082,1],[3091,1],[3098,1],[3160,1],[3178,1],[3199,1],[3205,2],[3248,2],[3251,1],[3269,1],[3271,6],[3280,1],[3355,1],[3364,2],[3397,2],[3513,1],[4455,1],[4591,1],[4652,1],[4716,1],[4789,1],[4834,1]]},"3369":{"position":[[536,1],[765,1],[767,3],[886,1],[888,1],[986,1],[988,2],[1085,1],[1087,3],[1091,2],[1269,4],[1442,1],[1733,1],[1743,2],[1816,1],[1896,3],[1900,2],[1958,2],[2041,1],[2128,2],[2131,2],[2195,1],[2204,1],[2225,2],[2317,2],[2350,1],[2411,2]]},"3371":{"position":[[219,1],[221,3],[265,1],[267,1],[346,2],[349,1],[428,1],[430,2],[447,1],[449,1],[508,2],[511,1],[570,1],[572,2]]},"3377":{"position":[[139,1],[473,1]]},"3379":{"position":[[802,3],[1186,1],[1586,1],[1717,1]]},"3383":{"position":[[646,1],[670,1],[672,3],[702,1],[736,1],[789,1],[791,1],[1065,1],[1817,1],[1833,1],[1835,3],[1860,1],[1912,1],[1952,1],[1978,1],[1990,1],[2004,1],[2058,1],[2060,1]]},"3389":{"position":[[1709,1]]},"3395":{"position":[[69,1],[215,1],[260,1]]},"3401":{"position":[[57,1],[59,1],[61,1],[63,1],[65,1],[90,1],[92,1],[94,1],[96,1],[98,1],[143,1],[145,1],[147,1],[149,1],[151,1],[174,1],[176,1],[178,1],[180,1],[182,1],[198,1],[200,1],[202,1],[204,1],[206,1],[225,1],[227,1],[229,1],[231,1],[233,1],[248,1],[250,1],[252,1],[254,1],[256,1],[268,1],[270,1],[272,1],[274,1],[276,1],[303,1],[305,1],[307,1],[309,1],[311,1],[350,1],[352,1],[354,1],[356,1],[358,1],[386,1],[388,1],[390,1],[392,1],[394,1],[409,1],[445,1]]},"3403":{"position":[[59,1],[61,1],[63,1],[65,1],[67,1],[98,1],[100,1],[102,1],[104,1],[106,1],[153,1],[155,1],[157,1],[159,1],[161,1],[190,1],[192,1],[194,1],[196,1],[198,1],[227,1],[229,1],[231,1],[233,1],[235,1],[263,1],[265,1],[267,1],[269,1],[271,1],[300,1],[302,1],[304,1],[306,1],[308,1],[348,1],[350,1],[352,1],[354,1],[356,1],[387,1],[389,1],[391,1],[393,1],[395,1],[422,1]]},"3411":{"position":[[342,1],[344,3],[370,1],[470,2],[960,1],[962,3],[995,1],[1031,2]]},"3415":{"position":[[54,1],[56,3],[161,1],[1972,1],[5295,1],[5320,1],[5335,1],[5337,2],[5401,1],[5481,2],[5553,2],[6274,1],[6290,1],[6331,2],[6334,1]]},"3417":{"position":[[54,1],[56,3],[161,1],[456,1]]},"3419":{"position":[[53,1],[55,3],[415,1]]},"3421":{"position":[[53,1],[55,3],[166,1],[1127,1],[1129,3],[1299,1],[1301,3],[1426,2],[1468,3],[1530,1],[1748,3],[3789,1],[4402,1],[4413,1],[4459,2],[4462,1]]},"3423":{"position":[[53,1],[55,3],[160,1],[1184,1],[1186,3],[1365,1],[1367,3],[1486,2],[1543,3],[1742,1],[1981,3]]},"3425":{"position":[[54,1],[56,3],[173,1],[1418,1],[1420,3],[1563,1],[1565,3],[1657,2],[1701,3],[1804,1]]},"3427":{"position":[[94,1],[105,1],[147,2]]},"3429":{"position":[[122,1],[138,1],[184,2],[378,1]]},"3431":{"position":[[309,1],[732,1],[734,3],[848,1],[850,3],[967,1],[969,3],[1097,1],[1099,3],[1199,1],[1201,1],[1258,1],[1260,2],[1332,1],[1334,3]]},"3433":{"position":[[101,3],[268,2],[373,2]]},"3439":{"position":[[625,1],[829,1],[831,3],[1150,1],[1239,1],[1278,1]]},"3441":{"position":[[887,1]]},"3443":{"position":[[252,1],[254,3]]},"3445":{"position":[[252,1],[254,3],[298,1],[300,1],[424,2],[427,1],[523,2],[526,1],[607,2],[610,1],[687,2],[690,1],[759,2],[762,1],[843,2],[846,1],[904,1],[906,1],[960,1],[962,2],[1210,2]]},"3447":{"position":[[171,1],[173,3],[217,6],[479,1],[481,3],[525,6]]},"3449":{"position":[[337,1],[339,3],[383,6],[404,1],[406,1],[511,2],[514,1],[619,1],[621,2],[1152,1]]},"3451":{"position":[[87,1],[89,3],[133,6],[154,6],[179,1],[181,1],[228,2],[231,1],[277,1],[279,2],[498,1],[659,2],[695,1],[852,1]]},"3456":{"position":[[173,1],[175,3]]},"3462":{"position":[[415,1]]},"3464":{"position":[[829,1],[852,1]]},"3470":{"position":[[70,1]]},"3472":{"position":[[646,1]]},"3474":{"position":[[42,1]]},"3486":{"position":[[176,1],[178,3]]},"3492":{"position":[[268,1],[285,1],[306,1],[321,1],[338,1],[367,1],[388,1],[413,1],[434,1],[462,1],[484,1],[1160,1],[1173,1],[1194,1],[1213,1],[1243,1]]},"3496":{"position":[[331,1],[348,1],[412,1]]},"3498":{"position":[[129,1],[131,3],[701,1]]},"3502":{"position":[[257,1],[274,1],[332,1],[433,1],[445,3],[590,1],[611,1],[653,1],[681,1],[746,1],[1057,1],[1074,1],[1140,1],[1270,1],[1281,1],[1329,2]]},"3504":{"position":[[134,1],[151,1],[224,1]]},"3506":{"position":[[1422,1]]},"3514":{"position":[[287,1],[304,1],[335,1],[390,1],[402,1],[416,1],[458,1],[523,2],[566,1],[631,1],[633,1],[635,2]]},"3516":{"position":[[169,1],[186,1],[217,1],[278,1],[290,1],[325,2]]},"3518":{"position":[[502,1],[519,1],[562,1],[616,1],[628,1],[676,1],[678,1],[688,1],[706,2],[721,2],[724,1],[734,1],[752,2],[767,1],[769,1],[771,2]]},"3522":{"position":[[114,1],[131,1],[140,4],[145,1]]},"3524":{"position":[[89,1],[101,1],[112,1],[114,1],[285,2],[288,1],[290,1],[292,2]]},"3526":{"position":[[335,1],[352,1],[417,5],[466,7],[474,1],[526,1],[607,2]]},"3530":{"position":[[618,1],[620,3],[777,1]]},"3532":{"position":[[132,2],[460,1],[507,1],[554,1],[616,1],[694,1]]},"3534":{"position":[[545,2],[677,1],[769,1],[781,2],[841,2],[848,1],[867,1],[895,2],[940,1],[952,2],[1087,2],[1097,2],[1104,1],[1151,1],[1158,1],[1179,2],[1186,1],[1198,2],[1280,1],[1287,1],[1327,1],[1329,1],[1355,2]]},"3536":{"position":[[237,1],[337,1],[449,1],[502,1],[504,1],[513,2],[569,1],[596,1],[608,2],[712,2],[719,1],[738,1],[766,2],[808,1],[820,2],[946,2],[956,2],[963,1],[1010,1],[1017,1],[1038,2],[1045,1],[1057,2],[1139,1],[1146,1],[1186,1],[1188,1],[1214,2]]},"3538":{"position":[[903,1],[910,4],[915,1],[1071,1],[1111,2],[1264,1],[1271,4],[1276,1]]},"3540":{"position":[[40,1],[58,1],[85,2],[93,1],[193,2],[206,1],[295,2],[308,1],[386,2],[394,1],[477,2],[485,1],[532,2],[582,2],[708,2],[720,2]]},"3542":{"position":[[47,1],[65,1],[92,2],[172,2],[257,2],[336,2],[392,2],[435,2],[485,2],[595,2]]},"3549":{"position":[[75,1],[77,3]]},"3553":{"position":[[16,2]]},"3559":{"position":[[17,3]]},"3561":{"position":[[17,3]]},"3567":{"position":[[140,1]]},"3574":{"position":[[85,1],[87,3]]},"3578":{"position":[[261,1],[263,3],[423,1],[791,1],[1062,1],[1120,1],[1235,2]]},"3584":{"position":[[327,1],[474,1],[544,1]]},"3589":{"position":[[88,1],[90,3]]},"3603":{"position":[[31,3]]},"3606":{"position":[[80,1],[82,3]]},"3610":{"position":[[70,1],[300,1],[460,1],[883,1],[1133,5],[1388,3],[1517,1]]},"3612":{"position":[[505,1],[1180,1],[1215,1],[1246,1],[1496,1],[1646,1],[1648,1],[1650,1],[1738,2],[1741,1],[1841,1],[1935,1],[2252,2],[2255,1],[2281,3]]},"3619":{"position":[[301,1],[303,3]]},"3621":{"position":[[260,1],[262,3]]},"3625":{"position":[[497,1]]},"3629":{"position":[[413,3],[619,2],[704,1],[817,4],[1785,1]]},"3633":{"position":[[209,1],[1402,1],[1505,1],[1578,1],[1653,1],[1723,1],[1741,1],[1788,1],[2057,1],[2094,1],[2169,1],[2209,1],[2287,1],[2326,1],[2410,1],[2465,1],[2504,1],[2559,1],[2681,1],[2703,1],[2776,1],[2811,1],[2886,1],[2946,1],[3025,1],[3120,1],[3150,1],[3248,2],[3417,1],[3526,1],[3635,1],[3694,1],[3811,1],[3851,1],[4013,1],[4053,1],[4225,1],[4265,1],[4505,1],[5154,1]]},"3637":{"position":[[10,1]]},"3639":{"position":[[148,1],[209,1],[678,1],[695,1],[704,3],[708,2],[767,1],[769,2],[814,1],[895,1],[912,3],[921,1],[923,2],[984,2],[1058,1],[1060,2],[1187,1],[1200,1],[1252,1],[1314,1],[1356,2],[1408,3],[1476,1]]},"3641":{"position":[[158,1],[178,1]]},"3643":{"position":[[99,1],[482,1],[1177,1],[1247,1],[1314,1],[1382,1],[1458,1],[1482,1],[1544,1],[1796,1],[1839,1],[1922,1],[1963,1],[2044,1],[2066,1],[2151,1],[2198,1],[2204,1],[2317,1],[2405,1],[2439,1],[2517,1],[2553,1],[2555,1],[2576,1],[2656,1],[2689,1],[2691,1],[2712,1],[2794,1],[2829,1],[2831,1],[2876,1],[2882,1],[2991,1],[3024,2],[3067,2],[3074,1],[3147,1],[3283,1],[3394,1],[3485,1],[3509,2],[3516,1],[3853,1],[3895,1],[4083,1],[4125,1],[4704,1]]},"3647":{"position":[[167,1],[759,1],[834,1],[902,1],[1015,1],[1133,1],[1207,1],[1323,1],[1373,1],[1412,1],[1467,1],[1576,1],[1619,1],[1702,1],[1738,1],[1744,1],[1857,1],[1946,1],[1984,1],[2014,1],[2125,1],[2159,1],[2239,1],[2278,1],[2280,1],[2325,1],[2331,1],[2440,1],[2488,2],[2614,2],[2657,2],[2664,1],[2737,1],[2783,1],[2807,2],[2814,1],[2840,1],[2864,2],[2871,1],[2965,1]]},"3649":{"position":[[416,1],[472,1]]},"3653":{"position":[[12,1]]},"3655":{"position":[[165,1],[203,1],[575,1],[819,1],[821,2],[866,1],[935,1],[1011,3],[1059,2],[1062,3],[1079,1],[1096,3],[1105,1],[1107,2],[1168,2],[1242,1],[1244,2],[1371,1],[1384,1],[1436,1],[1511,1],[1549,1],[1591,2],[1726,1]]},"3657":{"position":[[515,1],[618,1],[620,2],[693,2],[768,2],[889,1],[891,2],[961,2],[1038,2],[1207,1],[1209,2],[1281,2],[1462,1],[1464,2],[1731,1]]},"3661":{"position":[[111,2],[237,1]]},"3665":{"position":[[321,1],[351,1]]},"3667":{"position":[[195,1],[355,1],[545,1],[729,1]]},"3669":{"position":[[214,1],[311,2],[436,1]]},"3671":{"position":[[371,1]]}}}],["0",{"_index":1367,"t":{"74":{"position":[[1007,2]]},"110":{"position":[[977,1],[2444,1],[2471,1],[2491,1],[2511,1]]},"134":{"position":[[179,1]]},"192":{"position":[[2608,2]]},"461":{"position":[[412,2]]},"471":{"position":[[1641,2],[2146,2],[2399,2],[2525,2]]},"475":{"position":[[2120,2],[4874,2],[5026,2],[5036,2],[5094,2],[5106,2],[5232,2],[5286,2],[5296,2],[5381,2],[5390,2],[5448,2],[5458,2],[5510,2],[5520,2],[5568,2],[5578,2],[5632,2],[5642,2]]},"477":{"position":[[4007,2],[4016,2],[4075,2],[4087,2],[4148,2],[4266,2],[4362,2],[4371,2],[4429,2],[4439,2],[4491,2],[4500,2],[4549,2],[4558,2],[4612,2],[4621,2]]},"479":{"position":[[4401,2],[4597,2]]},"610":{"position":[[239,2]]},"680":{"position":[[1016,1],[1020,1],[1116,1],[1120,1],[1124,1],[1225,1],[1229,1],[1337,1],[1341,1],[1443,1],[1447,1],[1549,1],[1553,1],[1654,1],[1658,1]]},"796":{"position":[[533,1]]},"836":{"position":[[31,2]]},"878":{"position":[[516,4]]},"906":{"position":[[9,1]]},"912":{"position":[[9,1]]},"914":{"position":[[9,1]]},"971":{"position":[[190,2],[891,2]]},"999":{"position":[[616,1]]},"1017":{"position":[[3740,1]]},"1027":{"position":[[219,1]]},"1094":{"position":[[300,2]]},"1154":{"position":[[21,1]]},"1156":{"position":[[21,1]]},"1160":{"position":[[970,2],[1107,1]]},"1189":{"position":[[305,1],[889,2]]},"1313":{"position":[[412,2]]},"1407":{"position":[[406,2],[552,2],[798,4]]},"1417":{"position":[[1453,3]]},"1471":{"position":[[459,2],[475,1]]},"1479":{"position":[[361,2],[379,2],[395,2],[455,2]]},"1617":{"position":[[1185,1],[1189,1],[1285,1],[1289,1],[1293,1],[1394,1],[1398,1],[1506,1],[1510,1],[1612,1],[1616,1],[1718,1],[1722,1],[1823,1],[1827,1]]},"1753":{"position":[[616,1]]},"1811":{"position":[[533,1]]},"1853":{"position":[[31,2]]},"1981":{"position":[[190,2],[891,2]]},"1991":{"position":[[516,4]]},"2019":{"position":[[9,1]]},"2021":{"position":[[36,1],[60,1]]},"2023":{"position":[[36,1]]},"2027":{"position":[[9,1]]},"2035":{"position":[[9,1]]},"2086":{"position":[[271,1]]},"2303":{"position":[[459,2],[475,1]]},"2311":{"position":[[361,2],[379,2],[395,2],[455,2]]},"2334":{"position":[[21,1]]},"2336":{"position":[[21,1]]},"2340":{"position":[[970,2],[1107,1]]},"2354":{"position":[[3857,1]]},"2600":{"position":[[412,2]]},"2648":{"position":[[73,1],[354,1]]},"2656":{"position":[[412,2]]},"2712":{"position":[[608,2]]},"2802":{"position":[[1185,1],[1189,1],[1285,1],[1289,1],[1293,1],[1394,1],[1398,1],[1506,1],[1510,1],[1612,1],[1616,1],[1718,1],[1722,1],[1823,1],[1827,1]]},"2958":{"position":[[616,1]]},"3040":{"position":[[566,1]]},"3082":{"position":[[31,2]]},"3140":{"position":[[190,2],[891,2]]},"3180":{"position":[[516,4]]},"3208":{"position":[[9,1]]},"3210":{"position":[[9,1],[33,1]]},"3212":{"position":[[9,1]]},"3216":{"position":[[9,1]]},"3226":{"position":[[9,1]]},"3271":{"position":[[271,1]]},"3415":{"position":[[3886,1]]},"3421":{"position":[[3371,1]]},"3516":{"position":[[307,2],[323,1]]},"3524":{"position":[[177,2],[195,2],[211,2],[271,2]]},"3619":{"position":[[21,1]]},"3621":{"position":[[21,1]]},"3625":{"position":[[970,2],[1107,1]]},"3659":{"position":[[73,1],[354,1]]},"3667":{"position":[[412,2]]}}}],["0.0.0.0:4222[3569",{"_index":4438,"t":{"1207":{"position":[[702,18]]},"2110":{"position":[[702,18]]},"3293":{"position":[[702,18]]}}}],["0.0.0.0:4433",{"_index":1653,"t":{"104":{"position":[[1024,15]]}}}],["0.3",{"_index":661,"t":{"16":{"position":[[3696,3]]}}}],["0.32",{"_index":148,"t":{"2":{"position":[[2280,4]]}}}],["0.36kb",{"_index":3367,"t":{"477":{"position":[[4203,6]]}}}],["0.39kb",{"_index":3362,"t":{"477":{"position":[[4078,6]]}}}],["0.52",{"_index":3435,"t":{"479":{"position":[[4475,5]]}}}],["0.56µ",{"_index":3349,"t":{"477":{"position":[[3654,6]]}}}],["0.59µ",{"_index":3386,"t":{"477":{"position":[[4880,8]]}}}],["0.x86_64.rpm",{"_index":3610,"t":{"547":{"position":[[188,12]]}}}],["0.x86_64.rpmsudo",{"_index":3608,"t":{"547":{"position":[[138,16]]}}}],["0083c8836529",{"_index":4779,"t":{"1505":{"position":[[5627,12]]},"2686":{"position":[[5689,12]]}}}],["0083c88365292022",{"_index":4778,"t":{"1505":{"position":[[5524,16]]},"2686":{"position":[[5586,16]]}}}],["01",{"_index":1687,"t":{"106":{"position":[[1734,2]]},"473":{"position":[[760,2]]},"1195":{"position":[[379,2]]},"1505":{"position":[[4804,2]]},"2096":{"position":[[379,2]]},"2686":{"position":[[4866,2]]},"3281":{"position":[[379,2]]}}}],["01\"func",{"_index":1677,"t":{"106":{"position":[[1206,7]]}}}],["0120",{"_index":4732,"t":{"1505":{"position":[[942,4],[1491,4],[7994,4]]},"2686":{"position":[[957,4],[1506,4],[8091,4]]}}}],["02",{"_index":3183,"t":{"473":{"position":[[939,2]]},"1195":{"position":[[487,2]]},"2096":{"position":[[487,2]]},"3281":{"position":[[487,2]]}}}],["02#section",{"_index":1673,"t":{"106":{"position":[[1158,10]]},"110":{"position":[[799,10]]}}}],["02.type",{"_index":1646,"t":{"104":{"position":[[616,7]]}}}],["0342fd2bb20c",{"_index":5044,"t":{"1847":{"position":[[975,14],[1043,14]]},"3076":{"position":[[939,14],[1007,14]]}}}],["0442",{"_index":4600,"t":{"1469":{"position":[[540,4],[592,4],[648,4],[700,4]]},"2301":{"position":[[583,4],[635,4],[691,4],[743,4]]},"3514":{"position":[[428,4],[480,4],[536,4],[588,4]]}}}],["051f8588020f",{"_index":4603,"t":{"1469":{"position":[[555,14],[607,14]]},"2301":{"position":[[598,14],[650,14]]},"3514":{"position":[[443,14],[495,14]]}}}],["051f858802da",{"_index":4605,"t":{"1469":{"position":[[663,14],[715,14]]},"2301":{"position":[[706,14],[758,14]]},"3514":{"position":[[551,14],[603,14]]}}}],["05:30:48",{"_index":4614,"t":{"1473":{"position":[[755,8]]},"2305":{"position":[[755,8]]}}}],["06",{"_index":4771,"t":{"1505":{"position":[[5398,2],[5541,2],[7225,2]]},"2686":{"position":[[5460,2],[5603,2],[7305,2]]}}}],["06a8236543f9",{"_index":4725,"t":{"1505":{"position":[[843,14],[1377,14],[7880,14]]},"2686":{"position":[[858,14],[1392,14],[7977,14]]}}}],["06f0",{"_index":5035,"t":{"1847":{"position":[[819,4],[887,4]]},"3076":{"position":[[783,4],[851,4]]}}}],["07",{"_index":3846,"t":{"680":{"position":[[1038,2],[1133,2],[1246,2],[1357,2],[1463,2],[1568,2],[1673,2]]},"1617":{"position":[[1207,2],[1302,2],[1415,2],[1526,2],[1632,2],[1737,2],[1842,2]]},"2802":{"position":[[1207,2],[1302,2],[1415,2],[1526,2],[1632,2],[1737,2],[1842,2]]}}}],["07:23:40",{"_index":5743,"t":{"3538":{"position":[[1048,8]]}}}],["07:23:59",{"_index":5747,"t":{"3538":{"position":[[1455,8]]}}}],["08:15:09",{"_index":3848,"t":{"680":{"position":[[1044,8],[1139,8],[1252,8],[1363,8]]},"1617":{"position":[[1213,8],[1308,8],[1421,8],[1532,8]]},"2802":{"position":[[1213,8],[1308,8],[1421,8],[1532,8]]}}}],["08:15:12",{"_index":3852,"t":{"680":{"position":[[1469,8],[1574,8],[1679,8]]},"1617":{"position":[[1638,8],[1743,8],[1848,8]]},"2802":{"position":[[1638,8],[1743,8],[1848,8]]}}}],["08:25:33",{"_index":2933,"t":{"329":{"position":[[914,8]]}}}],["09",{"_index":4767,"t":{"1505":{"position":[[4801,2]]},"2686":{"position":[[4863,2]]}}}],["0916",{"_index":4727,"t":{"1505":{"position":[[886,4],[1435,4],[1866,4],[7938,4]]},"2686":{"position":[[901,4],[1450,4],[1881,4],[8035,4]]}}}],["09:44:21",{"_index":4772,"t":{"1505":{"position":[[5404,8],[5547,8]]},"2686":{"position":[[5466,8],[5609,8]]}}}],["09:45:49",{"_index":4790,"t":{"1505":{"position":[[7231,8]]},"2686":{"position":[[7311,8]]}}}],["0_amd64.deb",{"_index":3606,"t":{"545":{"position":[[183,11]]}}}],["0_amd64.debsudo",{"_index":3604,"t":{"545":{"position":[[138,15]]}}}],["0a643d22e8a0",{"_index":4189,"t":{"1017":{"position":[[1891,14]]},"1019":{"position":[[492,14]]},"1021":{"position":[[404,14]]},"1023":{"position":[[1763,14]]},"1025":{"position":[[1778,14]]},"2354":{"position":[[2008,14]]},"2356":{"position":[[492,14]]},"2358":{"position":[[404,14]]},"2360":{"position":[[1763,14]]},"2362":{"position":[[1778,14]]},"2364":{"position":[[1867,14]]},"3415":{"position":[[2008,14]]},"3417":{"position":[[492,14]]},"3419":{"position":[[451,14]]},"3421":{"position":[[1566,14]]},"3423":{"position":[[1778,14]]},"3425":{"position":[[1840,14]]}}}],["0b6e",{"_index":5041,"t":{"1847":{"position":[[960,4],[1028,4]]},"3076":{"position":[[924,4],[992,4]]}}}],["0e08a686b727",{"_index":4730,"t":{"1505":{"position":[[901,14],[1450,14],[1881,13],[7953,14]]},"2686":{"position":[[916,14],[1465,14],[1896,13],[8050,14]]}}}],["0s",{"_index":3993,"t":{"838":{"position":[[31,3]]},"1185":{"position":[[36,3]]},"1205":{"position":[[711,3]]},"1855":{"position":[[31,3]]},"3084":{"position":[[31,3]]}}}],["0x10001",{"_index":1573,"t":{"98":{"position":[[1179,10]]}}}],["1",{"_index":100,"t":{"2":{"position":[[1282,1],[1448,1],[2216,1]]},"18":{"position":[[571,2]]},"84":{"position":[[716,1]]},"110":{"position":[[1005,4],[2446,1],[2453,1],[2473,1],[2493,1],[2513,2]]},"226":{"position":[[388,1]]},"234":{"position":[[470,1]]},"321":{"position":[[428,5],[1523,4]]},"331":{"position":[[802,2]]},"399":{"position":[[217,1]]},"471":{"position":[[1772,2]]},"473":{"position":[[471,1],[816,1],[995,1]]},"475":{"position":[[4929,2],[4941,2],[5220,2],[6025,1]]},"477":{"position":[[1401,1],[2445,2],[3663,2],[3733,2],[3798,2],[3854,2],[3910,2],[3922,2],[4200,2],[4212,2],[4276,2]]},"479":{"position":[[4390,2],[4459,2],[4471,2],[4522,2],[4534,2],[4585,2],[4652,2],[4664,2]]},"481":{"position":[[768,2]]},"563":{"position":[[568,2],[583,1],[656,2],[741,2],[756,1],[857,1],[886,1],[1505,2],[1520,1]]},"596":{"position":[[110,1]]},"682":{"position":[[385,1],[454,1]]},"694":{"position":[[951,1]]},"810":{"position":[[679,1]]},"864":{"position":[[207,2]]},"866":{"position":[[212,2]]},"868":{"position":[[170,1],[333,1],[415,2]]},"870":{"position":[[272,2]]},"878":{"position":[[482,5]]},"906":{"position":[[384,1]]},"912":{"position":[[551,1]]},"941":{"position":[[424,1]]},"971":{"position":[[357,2],[518,2]]},"981":{"position":[[288,3],[546,2]]},"1094":{"position":[[310,2],[429,2]]},"1160":{"position":[[1404,1]]},"1195":{"position":[[435,1],[543,1]]},"1223":{"position":[[217,1]]},"1405":{"position":[[741,2],[754,2],[806,2],[2879,2]]},"1407":{"position":[[393,2],[521,2],[567,2],[1077,2]]},"1409":{"position":[[182,2]]},"1419":{"position":[[421,1]]},"1457":{"position":[[462,3]]},"1505":{"position":[[9445,2]]},"1523":{"position":[[181,1]]},"1619":{"position":[[386,1],[455,1]]},"1666":{"position":[[1161,1],[1326,1]]},"1668":{"position":[[959,1],[1123,1]]},"1670":{"position":[[1440,1],[1604,1]]},"1737":{"position":[[933,1]]},"1825":{"position":[[857,1]]},"1855":{"position":[[1028,3],[1045,1]]},"1905":{"position":[[251,2]]},"1907":{"position":[[207,2]]},"1909":{"position":[[170,1],[333,1],[415,2]]},"1911":{"position":[[220,1],[394,1],[476,2]]},"1913":{"position":[[272,2]]},"1915":{"position":[[280,2]]},"1981":{"position":[[357,2],[518,2]]},"1991":{"position":[[482,5]]},"2019":{"position":[[384,1]]},"2027":{"position":[[551,1]]},"2062":{"position":[[424,1]]},"2074":{"position":[[288,3],[546,2]]},"2096":{"position":[[435,1],[543,1]]},"2150":{"position":[[684,2],[786,2],[1305,2]]},"2154":{"position":[[456,2]]},"2289":{"position":[[522,3]]},"2340":{"position":[[1404,1]]},"2472":{"position":[[217,1]]},"2686":{"position":[[9601,2]]},"2706":{"position":[[181,1]]},"2718":{"position":[[290,1]]},"2804":{"position":[[386,1],[455,1]]},"2830":{"position":[[1164,1],[1329,1]]},"2832":{"position":[[965,1],[1129,1]]},"2834":{"position":[[1446,1],[1610,1]]},"2926":{"position":[[933,1]]},"3054":{"position":[[857,1]]},"3084":{"position":[[992,3],[1009,1]]},"3140":{"position":[[357,2],[518,2]]},"3162":{"position":[[251,2]]},"3164":{"position":[[207,2]]},"3166":{"position":[[170,1],[333,1],[415,2]]},"3168":{"position":[[220,1],[394,1],[476,2]]},"3170":{"position":[[272,2]]},"3172":{"position":[[280,2]]},"3180":{"position":[[482,5]]},"3208":{"position":[[384,1]]},"3216":{"position":[[551,1]]},"3257":{"position":[[424,1]]},"3281":{"position":[[435,1],[543,1]]},"3311":{"position":[[288,3],[546,2]]},"3367":{"position":[[3068,1]]},"3379":{"position":[[684,2],[786,2],[1305,2]]},"3383":{"position":[[456,2]]},"3502":{"position":[[666,3]]},"3625":{"position":[[1404,1]]},"3629":{"position":[[803,2]]}}}],["1)type",{"_index":3138,"t":{"471":{"position":[[1418,6]]}}}],["1.00kb",{"_index":3287,"t":{"475":{"position":[[5223,6]]}}}],["1.08kb",{"_index":3281,"t":{"475":{"position":[[5097,6]]}}}],["1.1",{"_index":114,"t":{"2":{"position":[[1612,3]]},"198":{"position":[[882,4],[1161,4]]},"250":{"position":[[847,4],[1105,4]]},"979":{"position":[[1098,4]]},"981":{"position":[[782,4]]},"2072":{"position":[[1098,4]]},"2074":{"position":[[782,4]]},"3309":{"position":[[1098,4]]},"3311":{"position":[[782,4]]}}}],["1.17",{"_index":1112,"t":{"50":{"position":[[2116,4],[2290,5]]}}}],["1.25kb",{"_index":3286,"t":{"475":{"position":[[5211,6]]},"477":{"position":[[4191,6]]}}}],["1.27µ",{"_index":3438,"t":{"479":{"position":[[4525,6]]}}}],["1.3.0",{"_index":5584,"t":{"3287":{"position":[[586,5]]}}}],["1.3.13",{"_index":4133,"t":{"977":{"position":[[455,6]]},"2070":{"position":[[455,6]]},"3307":{"position":[[455,6]]}}}],["1.30kb",{"_index":3280,"t":{"475":{"position":[[5085,6]]},"477":{"position":[[4066,6]]}}}],["1.45µ",{"_index":3254,"t":{"475":{"position":[[4659,6]]},"477":{"position":[[3642,6],[3789,6],[4869,7]]},"479":{"position":[[4513,6]]}}}],["1.47µ",{"_index":3263,"t":{"475":{"position":[[4796,6],[4808,6]]},"477":{"position":[[3777,6]]}}}],["1.52µ",{"_index":3494,"t":{"481":{"position":[[895,6]]}}}],["1.7",{"_index":3384,"t":{"477":{"position":[[4785,3]]}}}],["1.88µ",{"_index":3255,"t":{"475":{"position":[[4671,6]]}}}],["10",{"_index":112,"t":{"2":{"position":[[1571,2],[2592,2]]},"34":{"position":[[830,4]]},"38":{"position":[[1060,3],[1628,2]]},"74":{"position":[[1080,5]]},"160":{"position":[[453,3],[513,3]]},"461":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"473":{"position":[[845,2],[1024,2]]},"475":{"position":[[4666,4],[4815,4]]},"477":{"position":[[3649,4]]},"517":{"position":[[228,2]]},"563":{"position":[[640,3]]},"567":{"position":[[217,2]]},"678":{"position":[[1083,2]]},"798":{"position":[[510,2],[584,2]]},"838":{"position":[[579,3]]},"856":{"position":[[265,3]]},"858":{"position":[[1301,3],[1415,3]]},"906":{"position":[[412,2],[469,2]]},"963":{"position":[[181,2],[371,4]]},"971":{"position":[[595,4],[615,3],[710,4],[730,3],[772,2],[871,3],[937,2],[1086,3],[1392,4]]},"1195":{"position":[[464,2],[572,2]]},"1313":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"1407":{"position":[[686,3]]},"1413":{"position":[[458,3]]},"1445":{"position":[[251,2]]},"1505":{"position":[[5401,2],[5544,2],[7228,2]]},"1529":{"position":[[294,2]]},"1535":{"position":[[217,2]]},"1573":{"position":[[181,2]]},"1587":{"position":[[194,2]]},"1615":{"position":[[1077,2]]},"1619":{"position":[[133,2]]},"1666":{"position":[[1221,2]]},"1668":{"position":[[1018,2]]},"1670":{"position":[[1499,2]]},"1813":{"position":[[510,2],[584,2]]},"1843":{"position":[[1276,3],[1353,3]]},"1855":{"position":[[579,3]]},"1899":{"position":[[413,3],[1200,3]]},"1973":{"position":[[181,2],[371,4]]},"1981":{"position":[[595,4],[615,3],[710,4],[730,3],[772,2],[871,3],[937,2],[1086,3],[1392,4]]},"2019":{"position":[[412,2],[469,2]]},"2029":{"position":[[19,3]]},"2096":{"position":[[464,2],[572,2]]},"2189":{"position":[[436,3]]},"2600":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"2614":{"position":[[204,2]]},"2656":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]},"2668":{"position":[[181,2]]},"2682":{"position":[[194,2]]},"2686":{"position":[[5463,2],[5606,2],[7308,2]]},"2740":{"position":[[217,2]]},"2800":{"position":[[1077,2]]},"2804":{"position":[[133,2]]},"2830":{"position":[[1224,2]]},"2832":{"position":[[1024,2]]},"2834":{"position":[[1505,2]]},"3042":{"position":[[505,2]]},"3072":{"position":[[1564,3],[1641,3]]},"3084":{"position":[[579,3]]},"3134":{"position":[[413,3],[1200,3]]},"3140":{"position":[[595,4],[615,3],[710,4],[730,3],[772,2],[871,3],[937,2],[1086,3],[1392,4]]},"3208":{"position":[[412,2],[469,2]]},"3218":{"position":[[19,3]]},"3281":{"position":[[464,2],[572,2]]},"3301":{"position":[[181,2],[371,4]]},"3320":{"position":[[181,2],[371,4]]},"3492":{"position":[[436,3]]},"3667":{"position":[[286,2],[392,3],[473,2],[629,2],[766,3]]}}}],["10*60",{"_index":3949,"t":{"798":{"position":[[401,7]]},"1813":{"position":[[401,7]]},"3042":{"position":[[413,7]]}}}],["10.0",{"_index":3295,"t":{"475":{"position":[[5374,4]]},"477":{"position":[[4355,4],[4432,4]]}}}],["10.00",{"_index":3297,"t":{"475":{"position":[[5394,6]]}}}],["10.6µ",{"_index":3492,"t":{"481":{"position":[[841,6]]}}}],["10.limit",{"_index":961,"t":{"34":{"position":[[818,8]]},"971":{"position":[[1380,8]]},"1981":{"position":[[1380,8]]},"3140":{"position":[[1380,8]]}}}],["100",{"_index":102,"t":{"2":{"position":[[1337,3],[1943,3],[2319,3]]},"479":{"position":[[3038,3],[3772,3],[4193,3],[5391,3],[5728,3]]},"583":{"position":[[121,3]]},"700":{"position":[[6,4]]},"758":{"position":[[6,4]]},"1197":{"position":[[248,4]]},"1405":{"position":[[3376,4]]},"1423":{"position":[[594,5]]},"1519":{"position":[[605,4]]},"1551":{"position":[[121,3]]},"1855":{"position":[[941,6]]},"2056":{"position":[[420,4]]},"2098":{"position":[[248,4]]},"2150":{"position":[[1196,4],[1326,4]]},"2160":{"position":[[594,5]]},"2648":{"position":[[39,3],[356,3]]},"2702":{"position":[[605,4]]},"2756":{"position":[[121,3]]},"3084":{"position":[[898,5]]},"3249":{"position":[[420,4]]},"3283":{"position":[[248,4]]},"3367":{"position":[[3673,3]]},"3379":{"position":[[1196,4],[1326,4]]},"3389":{"position":[[594,5]]},"3538":{"position":[[413,4]]},"3540":{"position":[[88,3]]},"3542":{"position":[[95,3]]},"3659":{"position":[[39,3],[356,3]]}}}],["1000",{"_index":2550,"t":{"246":{"position":[[415,4]]},"250":{"position":[[1795,5]]},"252":{"position":[[735,6]]},"471":{"position":[[3802,5],[3929,5]]},"941":{"position":[[399,4]]},"979":{"position":[[647,5]]},"1027":{"position":[[115,5],[188,5]]},"1519":{"position":[[597,5]]},"2062":{"position":[[399,4]]},"2072":{"position":[[647,5]]},"2366":{"position":[[115,5]]},"2702":{"position":[[597,5]]},"3257":{"position":[[399,4]]},"3309":{"position":[[647,5]]},"3427":{"position":[[115,5]]}}}],["10000",{"_index":4637,"t":{"1485":{"position":[[682,5]]},"2317":{"position":[[682,5]]},"3530":{"position":[[682,5]]}}}],["100000",{"_index":3131,"t":{"471":{"position":[[935,6],[1055,6]]}}}],["10000sentinel",{"_index":4395,"t":{"1193":{"position":[[1057,13]]},"2092":{"position":[[1073,13]]},"3277":{"position":[[1073,13]]}}}],["1000m",{"_index":4091,"t":{"941":{"position":[[387,8]]},"2062":{"position":[[387,8]]},"3257":{"position":[[387,8]]}}}],["1009",{"_index":1089,"t":{"50":{"position":[[804,4]]}}}],["100k",{"_index":657,"t":{"16":{"position":[[3585,4]]},"397":{"position":[[348,4]]},"479":{"position":[[2352,4]]},"682":{"position":[[251,4]]},"1221":{"position":[[348,4]]},"1619":{"position":[[252,4]]},"1670":{"position":[[554,4]]},"2470":{"position":[[348,4]]},"2804":{"position":[[252,4]]},"2834":{"position":[[554,4]]}}}],["100m",{"_index":4831,"t":{"1519":{"position":[[548,5],[848,8]]},"2702":{"position":[[548,5],[848,8]]}}}],["100messag",{"_index":5067,"t":{"1935":{"position":[[6,11]]},"2980":{"position":[[6,11]]}}}],["100mk",{"_index":3451,"t":{"479":{"position":[[5797,6]]}}}],["101",{"_index":267,"t":{"4":{"position":[[1050,4]]},"285":{"position":[[619,3]]},"678":{"position":[[1049,3]]},"702":{"position":[[6,4]]},"1615":{"position":[[1043,3]]},"2800":{"position":[[1043,3]]}}}],["101messag",{"_index":5069,"t":{"1937":{"position":[[6,11]]},"2982":{"position":[[6,11]]}}}],["102",{"_index":3878,"t":{"704":{"position":[[6,4]]},"760":{"position":[[6,4]]},"1457":{"position":[[1722,4]]},"1511":{"position":[[868,4]]},"1831":{"position":[[1283,4]]},"2289":{"position":[[1776,4]]},"2770":{"position":[[868,4]]},"3060":{"position":[[1366,4]]},"3502":{"position":[[1291,4]]},"3540":{"position":[[196,4]]},"3542":{"position":[[175,4]]}}}],["1024",{"_index":411,"t":{"10":{"position":[[497,4]]}}}],["102400",{"_index":3171,"t":{"471":{"position":[[3942,6]]}}}],["1024;}error_log",{"_index":2302,"t":{"198":{"position":[[696,15]]}}}],["102messag",{"_index":5070,"t":{"1939":{"position":[[6,11]]},"2984":{"position":[[6,11]]}}}],["103",{"_index":3879,"t":{"706":{"position":[[6,4]]},"1571":{"position":[[191,3]]},"1771":{"position":[[89,4]]},"1773":{"position":[[618,4]]},"1775":{"position":[[130,4]]},"1777":{"position":[[130,4]]},"1883":{"position":[[255,4]]},"2666":{"position":[[191,3]]},"2930":{"position":[[89,4]]},"2932":{"position":[[618,4]]},"2934":{"position":[[130,4]]},"2936":{"position":[[130,4]]},"3114":{"position":[[255,4]]}}}],["103messag",{"_index":5071,"t":{"1941":{"position":[[6,11]]},"2986":{"position":[[6,11]]}}}],["104",{"_index":3881,"t":{"708":{"position":[[6,4]]},"762":{"position":[[6,4]]},"3540":{"position":[[201,3]]},"3542":{"position":[[180,3]]}}}],["1048576",{"_index":4047,"t":{"908":{"position":[[9,7]]},"2025":{"position":[[9,7]]},"3214":{"position":[[9,7]]}}}],["1048576net.ipv4.tcp_mem",{"_index":72,"t":{"2":{"position":[[813,23]]}}}],["104messag",{"_index":5072,"t":{"1943":{"position":[[6,11]]},"2988":{"position":[[6,11]]}}}],["105",{"_index":3882,"t":{"710":{"position":[[6,4]]},"1423":{"position":[[1174,4]]},"2160":{"position":[[1174,4]]},"3389":{"position":[[1174,4]]}}}],["105messag",{"_index":5073,"t":{"1945":{"position":[[6,11]]},"2990":{"position":[[6,11]]}}}],["106",{"_index":3883,"t":{"712":{"position":[[6,4]]}}}],["106messag",{"_index":5074,"t":{"1947":{"position":[[6,11]]},"2992":{"position":[[6,11]]}}}],["107",{"_index":3886,"t":{"714":{"position":[[6,4]]},"764":{"position":[[6,4]]},"1365":{"position":[[219,4]]},"2536":{"position":[[219,4]]},"3538":{"position":[[1081,4],[1477,4]]},"3540":{"position":[[298,4]]},"3542":{"position":[[260,4]]}}}],["107.2",{"_index":1100,"t":{"50":{"position":[[1290,5]]}}}],["107messag",{"_index":5075,"t":{"1949":{"position":[[6,11]]},"2994":{"position":[[6,11]]}}}],["108",{"_index":3888,"t":{"716":{"position":[[6,4]]},"766":{"position":[[6,4]]},"3540":{"position":[[303,3]]},"3542":{"position":[[265,3]]}}}],["108messag",{"_index":5076,"t":{"1951":{"position":[[6,11]]},"2996":{"position":[[6,11]]}}}],["109",{"_index":3889,"t":{"718":{"position":[[6,4]]},"1413":{"position":[[1139,5]]}}}],["109messag",{"_index":5077,"t":{"1953":{"position":[[6,11]]},"2998":{"position":[[6,11]]}}}],["10:17:33",{"_index":4768,"t":{"1505":{"position":[[4807,8]]},"2686":{"position":[[4869,8]]}}}],["10;┌─num_ops─┬─us",{"_index":3824,"t":{"678":{"position":[[1324,26]]},"1615":{"position":[[1318,26]]},"2800":{"position":[[1318,26]]}}}],["10k",{"_index":104,"t":{"2":{"position":[[1379,3]]},"128":{"position":[[1206,3]]},"1477":{"position":[[936,3]]},"2309":{"position":[[978,3]]},"3522":{"position":[[1038,3]]}}}],["10x",{"_index":1010,"t":{"38":{"position":[[970,3]]},"78":{"position":[[4085,3]]}}}],["10});console.log(resp.publ",{"_index":3084,"t":{"461":{"position":[[582,36]]},"1313":{"position":[[582,36]]},"2600":{"position":[[582,36]]},"2656":{"position":[[582,36]]},"3667":{"position":[[582,36]]}}}],["10}eof",{"_index":5470,"t":{"2712":{"position":[[450,6]]}}}],["11",{"_index":3553,"t":{"517":{"position":[[252,2]]},"678":{"position":[[1368,2]]},"971":{"position":[[1106,3]]},"1407":{"position":[[704,3]]},"1445":{"position":[[275,2]]},"1615":{"position":[[1362,2]]},"1981":{"position":[[1106,3]]},"2189":{"position":[[464,3]]},"2614":{"position":[[228,2]]},"2800":{"position":[[1362,2]]},"3140":{"position":[[1106,3]]},"3492":{"position":[[464,3]]}}}],["110",{"_index":3890,"t":{"720":{"position":[[6,4]]},"1423":{"position":[[352,3]]},"2160":{"position":[[352,3]]},"3389":{"position":[[352,3]]}}}],["11000",{"_index":4330,"t":{"1118":{"position":[[16,8]]},"2269":{"position":[[16,8]]},"3551":{"position":[[16,8]]}}}],["110messag",{"_index":5078,"t":{"1955":{"position":[[6,11]]},"3000":{"position":[[6,11]]}}}],["111",{"_index":3807,"t":{"678":{"position":[[296,5],[370,4],[525,4]]},"722":{"position":[[6,4]]},"1615":{"position":[[296,5],[370,4],[525,4]]},"2800":{"position":[[296,5],[370,4],[525,4]]}}}],["111messag",{"_index":5079,"t":{"1957":{"position":[[6,11]]},"3002":{"position":[[6,11]]}}}],["112",{"_index":3891,"t":{"724":{"position":[[6,4]]},"768":{"position":[[6,4]]},"3540":{"position":[[389,3]]},"3542":{"position":[[339,3]]}}}],["1121",{"_index":1086,"t":{"50":{"position":[[755,4],[815,4]]}}}],["112app",{"_index":2005,"t":{"160":{"position":[[504,6]]}}}],["112messag",{"_index":5081,"t":{"1959":{"position":[[6,11]]},"3004":{"position":[[6,11]]}}}],["113",{"_index":5765,"t":{"3540":{"position":[[480,3]]},"3542":{"position":[[395,3]]}}}],["114",{"_index":3466,"t":{"479":{"position":[[6160,5]]}}}],["116",{"_index":3416,"t":{"479":{"position":[[2856,3],[3213,4]]}}}],["12",{"_index":63,"t":{"2":{"position":[[735,2]]},"50":{"position":[[741,2],[801,2],[855,2],[918,2],[1111,2],[1171,2],[1225,2],[1287,2]]},"479":{"position":[[3151,2],[3272,3]]},"941":{"position":[[441,2]]},"1021":{"position":[[557,5]]},"2062":{"position":[[441,2]]},"2189":{"position":[[486,4]]},"2358":{"position":[[557,5]]},"3257":{"position":[[441,2]]},"3419":{"position":[[604,5]]},"3492":{"position":[[486,4]]}}}],["12.49",{"_index":3439,"t":{"479":{"position":[[4538,6]]}}}],["12.5µ",{"_index":3259,"t":{"475":{"position":[[4731,6]]},"477":{"position":[[3713,6]]}}}],["12000",{"_index":5685,"t":{"3367":{"position":[[3385,9]]}}}],["1204",{"_index":1018,"t":{"38":{"position":[[1448,4]]}}}],["1214",{"_index":3813,"t":{"678":{"position":[[588,4]]},"1615":{"position":[[588,4]]},"2800":{"position":[[588,4]]}}}],["123",{"_index":4708,"t":{"1497":{"position":[[315,3],[546,5],[1279,3]]},"2692":{"position":[[315,3],[546,5],[1279,3]]}}}],["123722",{"_index":4784,"t":{"1505":{"position":[[6190,6],[6285,8]]},"1767":{"position":[[139,6],[219,8]]},"2686":{"position":[[6259,6],[6354,8]]},"2972":{"position":[[139,6],[219,8]]}}}],["127.0.0.1",{"_index":2412,"t":{"216":{"position":[[473,9]]},"1193":{"position":[[999,9]]},"2092":{"position":[[1015,9]]},"3277":{"position":[[1015,9]]}}}],["127.0.0.1:26379",{"_index":4391,"t":{"1193":{"position":[[853,18]]},"2092":{"position":[[828,18]]},"3277":{"position":[[828,18]]}}}],["127.0.0.1:3301",{"_index":4424,"t":{"1203":{"position":[[1701,17]]},"2106":{"position":[[1701,17]]},"3289":{"position":[[1701,17]]}}}],["127.0.0.1:6379",{"_index":2502,"t":{"226":{"position":[[289,15]]},"694":{"position":[[116,16]]},"1189":{"position":[[86,16]]},"1197":{"position":[[609,17]]},"1737":{"position":[[106,16]]},"2086":{"position":[[86,16]]},"2098":{"position":[[609,17]]},"2926":{"position":[[106,16]]},"3271":{"position":[[86,16]]},"3283":{"position":[[609,17]]}}}],["127.0.0.1:6380",{"_index":3176,"t":{"473":{"position":[[763,14]]},"1195":{"position":[[382,14]]},"1197":{"position":[[627,17]]},"2096":{"position":[[382,14]]},"2098":{"position":[[627,17]]},"3281":{"position":[[382,14]]},"3283":{"position":[[627,17]]}}}],["127.0.0.1:6381",{"_index":3184,"t":{"473":{"position":[[942,14]]},"1195":{"position":[[490,14]]},"2096":{"position":[[490,14]]},"3281":{"position":[[490,14]]}}}],["127.0.0.1:8000",{"_index":4312,"t":{"1077":{"position":[[664,15]]},"2197":{"position":[[664,15]]},"3472":{"position":[[664,15]]}}}],["127.0.0.1:8000;}map",{"_index":4136,"t":{"979":{"position":[[113,19]]},"981":{"position":[[113,19]]},"2072":{"position":[[113,19]]},"2074":{"position":[[113,19]]},"3309":{"position":[[113,19]]},"3311":{"position":[[113,19]]}}}],["127.0.0.2:8000",{"_index":4313,"t":{"1077":{"position":[[687,16]]},"2197":{"position":[[687,16]]},"3472":{"position":[[687,16]]}}}],["127content",{"_index":4597,"t":{"1469":{"position":[[434,10]]},"2301":{"position":[[477,10]]}}}],["128",{"_index":3106,"t":{"469":{"position":[[593,4],[609,4]]},"471":{"position":[[3760,3],[3796,3],[3846,3],[4106,5]]},"475":{"position":[[1924,4],[4561,4],[5965,4]]},"479":{"position":[[4932,4]]},"902":{"position":[[9,3]]},"949":{"position":[[415,3]]},"1919":{"position":[[415,3]]},"2015":{"position":[[9,3]]},"3146":{"position":[[415,3]]},"3204":{"position":[[9,3]]}}}],["129content",{"_index":4611,"t":{"1473":{"position":[[699,10]]},"2305":{"position":[[699,10]]}}}],["12:25:05",{"_index":2002,"t":{"160":{"position":[[466,9],[526,9]]}}}],["12d3",{"_index":5693,"t":{"3367":{"position":[[4764,4]]}}}],["13",{"_index":2068,"t":{"176":{"position":[[631,3]]},"192":{"position":[[1062,3]]},"194":{"position":[[1720,3]]},"581":{"position":[[80,3]]},"1549":{"position":[[80,3]]},"2754":{"position":[[80,3]]}}}],["13.79",{"_index":3300,"t":{"475":{"position":[[5462,6]]}}}],["1328",{"_index":1092,"t":{"50":{"position":[[869,4],[933,4]]}}}],["1342fd2bb20c",{"_index":5049,"t":{"1847":{"position":[[1115,14],[1181,14]]},"3076":{"position":[[1079,14],[1145,14]]}}}],["136",{"_index":1099,"t":{"50":{"position":[[1240,3],[1302,3]]}}}],["14",{"_index":3461,"t":{"479":{"position":[[6048,3]]}}}],["14.0",{"_index":3302,"t":{"475":{"position":[[5513,4]]}}}],["14e8",{"_index":5048,"t":{"1847":{"position":[[1110,4],[1176,4]]},"3076":{"position":[[1074,4],[1140,4]]}}}],["15",{"_index":3639,"t":{"567":{"position":[[220,3]]},"1535":{"position":[[220,3]]},"2740":{"position":[[220,3]]}}}],["150",{"_index":151,"t":{"2":{"position":[[2337,3]]}}}],["151b",{"_index":3369,"t":{"477":{"position":[[4269,4]]}}}],["1565436268",{"_index":4217,"t":{"1019":{"position":[[636,12]]},"2356":{"position":[[636,12]]},"2364":{"position":[[2033,12]]},"3417":{"position":[[636,12]]},"3425":{"position":[[2006,12]]}}}],["15k",{"_index":4106,"t":{"951":{"position":[[645,3]]},"1921":{"position":[[645,3]]},"3148":{"position":[[645,3]]}}}],["16",{"_index":752,"t":{"18":{"position":[[583,2]]},"50":{"position":[[879,2],[943,2]]},"110":{"position":[[2585,4],[2599,4]]},"475":{"position":[[6030,2]]},"1519":{"position":[[641,3]]},"1523":{"position":[[172,3]]},"2702":{"position":[[641,3]]},"2706":{"position":[[172,3]]}}}],["16.04",{"_index":3556,"t":{"517":{"position":[[278,5]]},"1445":{"position":[[301,5]]}}}],["16.35",{"_index":3431,"t":{"479":{"position":[[4405,6]]}}}],["16.67",{"_index":3282,"t":{"475":{"position":[[5110,6]]}}}],["160",{"_index":4833,"t":{"1519":{"position":[[663,3]]},"2702":{"position":[[663,3]]}}}],["1635845022",{"_index":3739,"t":{"638":{"position":[[494,11]]},"1639":{"position":[[494,11]]},"2826":{"position":[[436,11]]}}}],["1635845122",{"_index":3735,"t":{"636":{"position":[[609,13]]},"638":{"position":[[519,13]]},"656":{"position":[[193,13]]},"1636":{"position":[[609,13]]},"1639":{"position":[[519,13]]},"1652":{"position":[[193,13]]},"2823":{"position":[[561,12]]},"2826":{"position":[[461,12]]},"2904":{"position":[[147,12]]}}}],["16379",{"_index":4399,"t":{"1195":{"position":[[594,7]]},"2096":{"position":[[594,7]]},"3281":{"position":[[594,7]]}}}],["16686:16686",{"_index":5620,"t":{"3327":{"position":[[591,11]]}}}],["16777216net.core.rmem_max",{"_index":81,"t":{"2":{"position":[[936,25]]}}}],["16777216net.ipv4.tcp_wmem",{"_index":78,"t":{"2":{"position":[[895,25]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioii0miisimv4cci6mtyynzcxmzmznx0.s3eohujiybjc4u21nuhkbcwjll4um0qqgu3pf",{"_index":4857,"t":{"1597":{"position":[[235,123]]},"2780":{"position":[[235,123]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioiixmjm3mjiilcjlehaioje2ntu0ndg0mzgsimnoyw5uzwwioijjagfubmvsin0.jyri3ovnv",{"_index":5011,"t":{"1767":{"position":[[270,127]]},"2972":{"position":[[270,127]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioiixmjm3mjiilcjlehaioje2ntu0ndgyotl9.muu9s5kj3yqp",{"_index":4785,"t":{"1505":{"position":[[6314,103]]},"2686":{"position":[[6383,103]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioij0zxn0x3vzzxiilcjlehaioje2mzaxmzaxnzb9.u7anx",{"_index":4335,"t":{"1147":{"position":[[558,100]]},"2260":{"position":[[567,100]]},"3610":{"position":[[567,100]]}}}],["168h0m0s:eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyjzdwiioij1c2vymtiilcjlehaioje2mjuwnzmyodh9.bxms4r",{"_index":4360,"t":{"1177":{"position":[[511,97]]},"2228":{"position":[[511,97]]},"3578":{"position":[[511,97]]}}}],["1694627573210",{"_index":5475,"t":{"2712":{"position":[[646,14]]}}}],["1697206286533",{"_index":5464,"t":{"2708":{"position":[[1550,15]]}}}],["17",{"_index":2797,"t":{"287":{"position":[[151,5]]},"289":{"position":[[863,5],[903,4]]},"291":{"position":[[1251,5],[1560,5]]},"295":{"position":[[1254,5]]},"299":{"position":[[573,4]]},"301":{"position":[[295,5],[641,5]]},"1457":{"position":[[1275,2],[1677,2]]},"1469":{"position":[[478,2]]},"1471":{"position":[[406,2]]},"1475":{"position":[[439,2]]},"1479":{"position":[[249,2]]},"2289":{"position":[[1335,2],[1731,2]]},"2301":{"position":[[521,2]]},"2303":{"position":[[406,2]]},"2307":{"position":[[439,2]]},"2311":{"position":[[249,2]]}}}],["17.0",{"_index":3308,"t":{"475":{"position":[[5635,4]]}}}],["1717",{"_index":1091,"t":{"50":{"position":[[858,4]]}}}],["176",{"_index":3169,"t":{"471":{"position":[[3386,3]]}}}],["18",{"_index":2213,"t":{"192":{"position":[[2630,2]]}}}],["18.0",{"_index":3307,"t":{"475":{"position":[[5625,4]]},"477":{"position":[[4605,4]]}}}],["18.04",{"_index":3558,"t":{"517":{"position":[[305,5]]},"1445":{"position":[[328,5]]},"2614":{"position":[[254,5]]}}}],["18.4µ",{"_index":3267,"t":{"475":{"position":[[4854,6]]},"477":{"position":[[3834,6]]}}}],["1840758",{"_index":3167,"t":{"471":{"position":[[3366,7]]}}}],["184content",{"_index":4619,"t":{"1479":{"position":[[205,10]]},"2311":{"position":[[205,10]]}}}],["18:49:39django",{"_index":2221,"t":{"192":{"position":[[2835,14]]}}}],["19",{"_index":2864,"t":{"309":{"position":[[126,2]]},"3538":{"position":[[1036,2],[1443,2]]}}}],["19.91",{"_index":3288,"t":{"475":{"position":[[5236,6]]}}}],["192.168.1.34:6379",{"_index":4402,"t":{"1197":{"position":[[765,20]]},"2098":{"position":[[765,20]]},"3283":{"position":[[765,20]]}}}],["192.168.1.35:6379",{"_index":4403,"t":{"1197":{"position":[[786,20]]},"2098":{"position":[[786,20]]},"3283":{"position":[[786,20]]}}}],["1999",{"_index":5215,"t":{"2360":{"position":[[4634,5]]},"2366":{"position":[[199,6]]},"2648":{"position":[[43,5],[142,5]]},"3421":{"position":[[4637,5]]},"3427":{"position":[[199,6]]},"3659":{"position":[[43,5],[142,5]]}}}],["1b6e",{"_index":5046,"t":{"1847":{"position":[[1100,4],[1166,4]]},"3076":{"position":[[1064,4],[1130,4]]}}}],["1m",{"_index":4089,"t":{"941":{"position":[[183,4]]},"2062":{"position":[[183,4]]},"3257":{"position":[[183,4]]}}}],["1mb",{"_index":4048,"t":{"908":{"position":[[107,4]]},"2025":{"position":[[107,4]]},"3214":{"position":[[107,4]]}}}],["1s",{"_index":3632,"t":{"563":{"position":[[554,5],[626,5],[1491,5]]},"596":{"position":[[158,5]]},"1017":{"position":[[155,5]]},"1019":{"position":[[155,5]]},"1021":{"position":[[146,5]]},"1023":{"position":[[160,5],[1431,5],[1603,5]]},"1025":{"position":[[154,5],[1285,5],[1466,5]]},"1031":{"position":[[815,5],[931,5],[1042,5],[1180,4],[1419,5]]},"1045":{"position":[[1462,4]]},"1209":{"position":[[238,3],[311,3]]},"1666":{"position":[[984,5],[1047,5],[1147,5],[1207,5]]},"1668":{"position":[[880,5],[945,5],[1004,5]]},"1670":{"position":[[1361,5],[1426,5],[1485,5]]},"2112":{"position":[[238,3],[311,3]]},"2354":{"position":[[155,5],[4867,3]]},"2356":{"position":[[155,5],[1737,3]]},"2358":{"position":[[146,5],[1721,3]]},"2360":{"position":[[160,5],[1431,5],[1603,5],[4018,3]]},"2362":{"position":[[154,5],[1285,5],[1466,5],[3323,3]]},"2364":{"position":[[194,5],[3208,3]]},"2370":{"position":[[815,5],[931,5],[1042,5],[1180,4],[1419,5]]},"2384":{"position":[[1462,4]]},"2830":{"position":[[987,5],[1050,5],[1150,5],[1210,5]]},"2832":{"position":[[886,5],[951,5],[1010,5]]},"2834":{"position":[[1367,5],[1432,5],[1491,5]]},"3295":{"position":[[238,3],[311,3]]},"3415":{"position":[[155,5],[4896,3]]},"3417":{"position":[[155,5],[1737,3]]},"3419":{"position":[[146,5],[1768,3]]},"3421":{"position":[[160,5],[1234,5],[1406,5],[4021,3]]},"3423":{"position":[[154,5],[1285,5],[1466,5],[3323,3]]},"3425":{"position":[[167,5],[3200,3]]},"3431":{"position":[[815,5],[931,5],[1042,5],[1180,4],[1419,5]]},"3445":{"position":[[1462,4]]}}}],["1}}}\\n{\"method",{"_index":5210,"t":{"2313":{"position":[[339,16]]}}}],["2",{"_index":108,"t":{"2":{"position":[[1510,1],[2168,2]]},"12":{"position":[[219,1]]},"18":{"position":[[574,2]]},"98":{"position":[[1731,2]]},"110":{"position":[[276,2],[2448,1],[2455,1],[2475,1],[2495,1]]},"128":{"position":[[1041,1],[1409,1]]},"132":{"position":[[327,1]]},"399":{"position":[[386,1]]},"469":{"position":[[989,1]]},"471":{"position":[[3326,1]]},"473":{"position":[[853,1],[1032,1]]},"475":{"position":[[2232,1],[4863,2],[5158,2]]},"477":{"position":[[1287,1],[2474,2],[3843,2],[4138,2]]},"481":{"position":[[1046,2]]},"555":{"position":[[263,1]]},"608":{"position":[[873,2]]},"858":{"position":[[559,1],[1075,1]]},"977":{"position":[[543,1]]},"1094":{"position":[[321,2],[449,2]]},"1193":{"position":[[123,1]]},"1195":{"position":[[472,1],[580,1]]},"1197":{"position":[[468,1]]},"1223":{"position":[[386,1]]},"1405":{"position":[[793,2],[2902,2]]},"1407":{"position":[[584,2],[730,2]]},"1409":{"position":[[169,2],[356,2]]},"1411":{"position":[[202,2]]},"1425":{"position":[[442,1]]},"1473":{"position":[[528,1],[609,4],[870,1]]},"1487":{"position":[[225,1]]},"1505":{"position":[[10019,5]]},"1529":{"position":[[263,1]]},"1597":{"position":[[1626,1]]},"1843":{"position":[[559,1],[1106,1]]},"1847":{"position":[[409,1],[475,1]]},"1855":{"position":[[1071,3],[1088,1],[1122,1]]},"2070":{"position":[[543,1]]},"2092":{"position":[[123,1],[975,3]]},"2096":{"position":[[472,1],[580,1]]},"2098":{"position":[[468,1]]},"2154":{"position":[[498,2]]},"2189":{"position":[[287,2]]},"2305":{"position":[[528,1],[609,4],[870,1]]},"2313":{"position":[[415,5]]},"2319":{"position":[[225,1]]},"2472":{"position":[[386,1]]},"2686":{"position":[[10132,4]]},"2764":{"position":[[263,1]]},"2780":{"position":[[1590,1]]},"3072":{"position":[[847,1],[1394,1]]},"3076":{"position":[[409,1],[475,1]]},"3084":{"position":[[1035,3],[1052,1],[1086,1]]},"3277":{"position":[[123,1],[975,3]]},"3281":{"position":[[472,1],[580,1]]},"3283":{"position":[[468,1]]},"3307":{"position":[[543,1]]},"3383":{"position":[[498,2]]},"3492":{"position":[[287,2]]},"3518":{"position":[[558,3],[719,1]]},"3532":{"position":[[225,1]]}}}],["2.0",{"_index":924,"t":{"30":{"position":[[241,3],[268,3]]},"391":{"position":[[159,5]]},"477":{"position":[[4365,3]]},"1349":{"position":[[159,5]]},"2464":{"position":[[163,5]]}}}],["2.0.0",{"_index":4508,"t":{"1407":{"position":[[1152,7]]}}}],["2.1.7[3569",{"_index":4433,"t":{"1207":{"position":[[561,11]]},"2110":{"position":[[561,11]]},"3293":{"position":[[561,11]]}}}],["2.40ghz",{"_index":57,"t":{"2":{"position":[[692,7]]}}}],["2.5x",{"_index":3381,"t":{"477":{"position":[[4676,4]]}}}],["2.60ghzbenchmarkmarsh",{"_index":1084,"t":{"50":{"position":[[717,23],[1087,23]]}}}],["2.6mb",{"_index":1777,"t":{"116":{"position":[[601,7]]}}}],["2.7",{"_index":4422,"t":{"1203":{"position":[[1539,4]]},"2106":{"position":[[1539,4]]},"3289":{"position":[[1539,4]]}}}],["20",{"_index":86,"t":{"2":{"position":[[1057,2],[2158,3]]},"38":{"position":[[1064,3]]},"224":{"position":[[581,2]]},"234":{"position":[[483,2]]},"236":{"position":[[781,2]]},"399":{"position":[[246,2]]},"555":{"position":[[294,2]]},"563":{"position":[[726,6]]},"1223":{"position":[[246,2]]},"1529":{"position":[[231,2]]},"1666":{"position":[[1061,2],[1311,6]]},"1668":{"position":[[1108,6]]},"1670":{"position":[[1589,6]]},"1672":{"position":[[1518,2]]},"2472":{"position":[[246,2]]},"2764":{"position":[[231,2]]},"2830":{"position":[[1064,2],[1314,6]]},"2832":{"position":[[1114,6]]},"2834":{"position":[[1595,6]]},"2836":{"position":[[1518,2]]}}}],["20*(1000/5",{"_index":2524,"t":{"236":{"position":[[908,11]]}}}],["20.04",{"_index":3560,"t":{"517":{"position":[[332,5]]},"1445":{"position":[[355,5]]},"2614":{"position":[[281,5],[313,5]]}}}],["20.69",{"_index":3306,"t":{"475":{"position":[[5582,6]]}}}],["200",{"_index":4580,"t":{"1457":{"position":[[1211,3],[1612,3]]},"1469":{"position":[[412,3]]},"1471":{"position":[[341,3]]},"1473":{"position":[[677,3]]},"1475":{"position":[[374,3]]},"1479":{"position":[[183,3]]},"2289":{"position":[[1271,3],[1666,3]]},"2301":{"position":[[455,3]]},"2303":{"position":[[341,3]]},"2305":{"position":[[677,3]]},"2307":{"position":[[374,3]]},"2311":{"position":[[183,3]]},"3502":{"position":[[1206,3]]},"3538":{"position":[[103,3],[349,3],[971,3],[1132,3]]}}}],["2000",{"_index":5447,"t":{"2650":{"position":[[79,6],[321,5]]},"3661":{"position":[[79,6],[321,5]]}}}],["2003",{"_index":4122,"t":{"963":{"position":[[133,5]]},"1973":{"position":[[133,5]]},"3301":{"position":[[133,5]]},"3320":{"position":[[133,5]]}}}],["200k",{"_index":118,"t":{"2":{"position":[[1732,4]]}}}],["200m",{"_index":43,"t":{"2":{"position":[[557,5],[1858,5]]},"84":{"position":[[783,5]]}}}],["2017",{"_index":62,"t":{"2":{"position":[[729,5]]}}}],["2018",{"_index":327,"t":{"8":{"position":[[599,4]]},"58":{"position":[[1237,5]]},"1457":{"position":[[1282,4],[1684,4]]},"1469":{"position":[[485,4]]},"1471":{"position":[[413,4]]},"1475":{"position":[[446,4]]},"1479":{"position":[[256,4]]},"2289":{"position":[[1342,4],[1738,4]]},"2301":{"position":[[528,4]]},"2303":{"position":[[413,4]]},"2307":{"position":[[446,4]]},"2311":{"position":[[256,4]]}}}],["2019",{"_index":4223,"t":{"1021":{"position":[[621,9]]},"2358":{"position":[[621,9]]},"3419":{"position":[[668,9]]}}}],["2020",{"_index":212,"t":{"4":{"position":[[18,4]]},"14":{"position":[[8,4]]},"192":{"position":[[2828,4]]}}}],["2020/07/08",{"_index":4430,"t":{"1207":{"position":[[499,10],[573,10],[633,10],[721,10],[830,10]]},"2110":{"position":[[499,10],[573,10],[633,10],[721,10],[830,10]]},"3293":{"position":[[499,10],[573,10],[633,10],[721,10],[830,10]]}}}],["2021",{"_index":1204,"t":{"60":{"position":[[720,4]]},"160":{"position":[[461,4],[521,4]]},"680":{"position":[[1033,4],[1128,4],[1241,4],[1352,4],[1458,4],[1563,4],[1668,4]]},"1473":{"position":[[750,4]]},"1505":{"position":[[4796,4]]},"1617":{"position":[[1202,4],[1297,4],[1410,4],[1521,4],[1627,4],[1732,4],[1837,4]]},"2305":{"position":[[750,4]]},"2686":{"position":[[4858,4]]},"2802":{"position":[[1202,4],[1297,4],[1410,4],[1521,4],[1627,4],[1732,4],[1837,4]]}}}],["2022",{"_index":4770,"t":{"1505":{"position":[[5393,4],[7220,4]]},"2686":{"position":[[5455,4],[7300,4]]}}}],["2022.3",{"_index":5718,"t":{"3395":{"position":[[389,7]]}}}],["2023",{"_index":5742,"t":{"3538":{"position":[[1043,4],[1450,4]]}}}],["2048",{"_index":1570,"t":{"98":{"position":[[1029,4]]},"1057":{"position":[[1542,4]]},"1098":{"position":[[642,4]]},"2140":{"position":[[1542,4]]},"2234":{"position":[[642,4]]},"3355":{"position":[[1542,4]]},"3584":{"position":[[642,4]]}}}],["2048gener",{"_index":1569,"t":{"98":{"position":[[997,14]]}}}],["2048openssl",{"_index":1549,"t":{"98":{"position":[[316,11]]}}}],["20:28:44.324269",{"_index":4431,"t":{"1207":{"position":[[510,15]]},"2110":{"position":[[510,15]]},"3293":{"position":[[510,15]]}}}],["20:28:44.324400",{"_index":4434,"t":{"1207":{"position":[[584,15]]},"2110":{"position":[[584,15]]},"3293":{"position":[[584,15]]}}}],["20:28:44.325600",{"_index":4437,"t":{"1207":{"position":[[644,15]]},"2110":{"position":[[644,15]]},"3293":{"position":[[644,15]]}}}],["20:28:44.325612",{"_index":4439,"t":{"1207":{"position":[[732,15]]},"2110":{"position":[[732,15]]},"3293":{"position":[[732,15]]}}}],["20:28:44.325617",{"_index":4441,"t":{"1207":{"position":[[841,15]]},"2110":{"position":[[841,15]]},"3293":{"position":[[841,15]]}}}],["20x",{"_index":2751,"t":{"280":{"position":[[736,3]]}}}],["21",{"_index":2220,"t":{"192":{"position":[[2824,3]]},"1473":{"position":[[743,2]]},"2305":{"position":[[743,2]]}}}],["2160h",{"_index":5144,"t":{"2082":{"position":[[36,5]]},"2086":{"position":[[898,5]]},"2108":{"position":[[711,6]]},"3267":{"position":[[36,5]]},"3271":{"position":[[864,5]]},"3291":{"position":[[711,6]]}}}],["217894",{"_index":3845,"t":{"680":{"position":[[1024,6]]},"1617":{"position":[[1193,6]]},"2802":{"position":[[1193,6]]}}}],["22.0",{"_index":3301,"t":{"475":{"position":[[5503,4]]},"477":{"position":[[4484,4]]}}}],["22.43",{"_index":3352,"t":{"477":{"position":[[3737,6]]}}}],["22.77",{"_index":3261,"t":{"475":{"position":[[4755,6]]}}}],["228804",{"_index":3124,"t":{"469":{"position":[[963,6]]},"471":{"position":[[3300,6]]},"475":{"position":[[2206,6]]},"477":{"position":[[1261,6]]}}}],["2292",{"_index":4737,"t":{"1505":{"position":[[993,4],[1542,4],[8045,4],[9890,4]]},"2686":{"position":[[1008,4],[1557,4],[8142,4],[10035,4]]}}}],["22:01:42",{"_index":4584,"t":{"1457":{"position":[[1287,8]]},"2289":{"position":[[1347,8]]}}}],["22:03:09",{"_index":4589,"t":{"1457":{"position":[[1689,8]]},"2289":{"position":[[1743,8]]}}}],["22:07:58",{"_index":4620,"t":{"1479":{"position":[[261,8]]},"2311":{"position":[[261,8]]}}}],["22:09:44",{"_index":4608,"t":{"1471":{"position":[[418,8]]},"1475":{"position":[[451,8]]},"2303":{"position":[[418,8]]},"2307":{"position":[[451,8]]}}}],["22:13:17",{"_index":4598,"t":{"1469":{"position":[[490,8]]},"2301":{"position":[[533,8]]}}}],["23",{"_index":64,"t":{"2":{"position":[[738,3]]}}}],["23.0",{"_index":3305,"t":{"475":{"position":[[5571,4]]}}}],["24",{"_index":3264,"t":{"475":{"position":[[4803,4]]},"477":{"position":[[3784,4]]},"479":{"position":[[5853,3]]},"945":{"position":[[67,2]]},"1615":{"position":[[1591,2]]},"2066":{"position":[[67,2]]},"2800":{"position":[[1591,2]]},"3261":{"position":[[67,2]]}}}],["24/7",{"_index":2748,"t":{"280":{"position":[[678,4]]}}}],["2400",{"_index":4186,"t":{"1017":{"position":[[1876,4]]},"1019":{"position":[[477,4]]},"1021":{"position":[[389,4]]},"1023":{"position":[[1748,4]]},"1025":{"position":[[1763,4]]},"2354":{"position":[[1993,4]]},"2356":{"position":[[477,4]]},"2358":{"position":[[389,4]]},"2360":{"position":[[1748,4]]},"2362":{"position":[[1763,4]]},"2364":{"position":[[1852,4]]},"3415":{"position":[[1993,4]]},"3417":{"position":[[477,4]]},"3419":{"position":[[436,4]]},"3421":{"position":[[1551,4]]},"3423":{"position":[[1763,4]]},"3425":{"position":[[1825,4]]}}}],["2402",{"_index":3851,"t":{"680":{"position":[[1451,4]]},"1617":{"position":[[1620,4]]},"2802":{"position":[[1620,4]]}}}],["244",{"_index":3232,"t":{"475":{"position":[[2280,3]]},"477":{"position":[[1335,3]]}}}],["24;app.use(sess",{"_index":2551,"t":{"246":{"position":[[432,21]]}}}],["24h",{"_index":3875,"t":{"694":{"position":[[1024,5]]},"1737":{"position":[[998,5]]},"2926":{"position":[[998,5]]}}}],["25",{"_index":2741,"t":{"280":{"position":[[340,2]]},"798":{"position":[[715,2]]},"1419":{"position":[[370,2]]},"1425":{"position":[[2375,2]]},"1813":{"position":[[715,2]]},"2156":{"position":[[301,2]]},"2191":{"position":[[464,2],[542,7]]},"3042":{"position":[[704,2]]},"3385":{"position":[[301,2]]},"3466":{"position":[[464,2],[542,7]]}}}],["25.0",{"_index":3299,"t":{"475":{"position":[[5451,4]]}}}],["25.83",{"_index":3285,"t":{"475":{"position":[[5172,6]]}}}],["250.6",{"_index":1097,"t":{"50":{"position":[[1174,5]]}}}],["2500",{"_index":5449,"t":{"2650":{"position":[[114,4],[239,4]]},"3661":{"position":[[114,4],[239,4]]}}}],["255",{"_index":3968,"t":{"814":{"position":[[82,3]]},"904":{"position":[[9,3]]},"1833":{"position":[[84,3]]},"2017":{"position":[[9,3]]},"3062":{"position":[[84,3]]},"3206":{"position":[[9,3]]}}}],["256",{"_index":410,"t":{"10":{"position":[[493,3]]},"50":{"position":[[596,3]]},"291":{"position":[[1137,3]]},"868":{"position":[[113,3]]},"949":{"position":[[419,3]]},"1147":{"position":[[511,3]]},"1177":{"position":[[467,3]]},"1505":{"position":[[6268,3],[6761,3]]},"1597":{"position":[[195,3]]},"1767":{"position":[[202,3]]},"1909":{"position":[[113,3]]},"1911":{"position":[[130,3]]},"1919":{"position":[[419,3]]},"2228":{"position":[[467,3]]},"2260":{"position":[[520,3]]},"2686":{"position":[[6337,3],[6842,3]]},"2780":{"position":[[195,3]]},"2972":{"position":[[202,3]]},"3146":{"position":[[419,3]]},"3166":{"position":[[113,3]]},"3168":{"position":[[130,3]]},"3578":{"position":[[467,3]]},"3610":{"position":[[520,3]]}}}],["25600",{"_index":3170,"t":{"471":{"position":[[3815,5]]}}}],["26379",{"_index":3478,"t":{"481":{"position":[[437,6]]}}}],["26379sentinel",{"_index":4393,"t":{"1193":{"position":[[968,13]]},"2092":{"position":[[984,13]]},"3277":{"position":[[984,13]]}}}],["2640",{"_index":54,"t":{"2":{"position":[[682,4]]}}}],["2654",{"_index":4862,"t":{"1597":{"position":[[1362,4]]},"2780":{"position":[[1326,4]]}}}],["268444",{"_index":3230,"t":{"475":{"position":[[2262,6]]},"477":{"position":[[1317,6]]}}}],["26883",{"_index":1016,"t":{"38":{"position":[[1436,5]]}}}],["2694",{"_index":3844,"t":{"680":{"position":[[992,4],[1095,4],[1190,4],[1303,4],[1414,4],[1520,4],[1625,4]]},"1617":{"position":[[1161,4],[1264,4],[1359,4],[1472,4],[1583,4],[1689,4],[1794,4]]},"1847":{"position":[[426,4],[466,4],[859,7],[1000,7]]},"2802":{"position":[[1161,4],[1264,4],[1359,4],[1472,4],[1583,4],[1689,4],[1794,4]]},"3076":{"position":[[426,4],[466,4],[823,7],[964,7]]}}}],["2695",{"_index":3744,"t":{"656":{"position":[[172,7]]},"658":{"position":[[174,9]]},"1652":{"position":[[172,7]]},"1654":{"position":[[174,9]]},"2904":{"position":[[126,7]]},"2906":{"position":[[126,8]]}}}],["27",{"_index":141,"t":{"2":{"position":[[2181,2]]},"94":{"position":[[2909,3]]}}}],["271974003db9",{"_index":4628,"t":{"1479":{"position":[[430,14]]},"2311":{"position":[[430,14]]},"3524":{"position":[[246,14]]}}}],["28",{"_index":117,"t":{"2":{"position":[[1656,2]]},"38":{"position":[[1458,2]]}}}],["28282",{"_index":4017,"t":{"868":{"position":[[67,5],[146,5],[276,5]]},"1909":{"position":[[67,5],[146,5],[276,5]]},"1911":{"position":[[73,5],[196,5],[326,5]]},"3166":{"position":[[67,5],[146,5],[276,5]]},"3168":{"position":[[73,5],[196,5],[326,5]]}}}],["29",{"_index":1533,"t":{"94":{"position":[[2852,2]]}}}],["29.0",{"_index":3298,"t":{"475":{"position":[[5441,4],[5561,4]]},"477":{"position":[[4422,4],[4542,4]]}}}],["29.32",{"_index":3256,"t":{"475":{"position":[[4683,7]]}}}],["2908591",{"_index":3337,"t":{"477":{"position":[[1374,7]]}}}],["298.70",{"_index":3496,"t":{"481":{"position":[[919,8]]}}}],["2999",{"_index":5448,"t":{"2650":{"position":[[86,6]]},"3661":{"position":[[86,6]]}}}],["2s",{"_index":3178,"t":{"473":{"position":[[806,2],[824,2],[985,2],[1003,2]]},"1195":{"position":[[425,2],[443,2],[533,2],[551,2]]},"2096":{"position":[[425,2],[443,2],[533,2],[551,2]]},"3281":{"position":[[425,2],[443,2],[533,2],[551,2]]}}}],["2sentinel",{"_index":4394,"t":{"1193":{"position":[[1014,9]]},"2092":{"position":[[1030,9]]},"3277":{"position":[[1030,9]]}}}],["2x",{"_index":3642,"t":{"583":{"position":[[88,2]]},"1551":{"position":[[88,2]]},"2756":{"position":[[88,2]]}}}],["3",{"_index":813,"t":{"20":{"position":[[503,1]]},"50":{"position":[[1135,1],[1195,1],[1249,1],[1311,1]]},"110":{"position":[[2457,1],[2477,1],[2497,1]]},"146":{"position":[[75,1]]},"475":{"position":[[4751,2]]},"477":{"position":[[2511,2]]},"481":{"position":[[779,2],[838,2],[968,2]]},"678":{"position":[[1474,1],[1489,1]]},"1094":{"position":[[338,2],[465,3]]},"1191":{"position":[[86,1],[232,1],[1085,1]]},"1407":{"position":[[597,2],[748,3]]},"1411":{"position":[[189,2]]},"1473":{"position":[[916,1]]},"1505":{"position":[[4437,1]]},"1615":{"position":[[1468,1],[1483,1]]},"2090":{"position":[[86,1],[232,1],[1085,1]]},"2092":{"position":[[941,1]]},"2305":{"position":[[916,1]]},"2632":{"position":[[555,1]]},"2686":{"position":[[4499,1]]},"2800":{"position":[[1468,1],[1483,1]]},"3275":{"position":[[86,1],[232,1],[1085,1]]},"3277":{"position":[[941,1]]},"3518":{"position":[[765,1]]},"3643":{"position":[[555,1]]}}}],["3+deb9u1",{"_index":61,"t":{"2":{"position":[[720,8]]}}}],["3,499",{"_index":5495,"t":{"2762":{"position":[[77,5]]}}}],["3.0",{"_index":3379,"t":{"477":{"position":[[4615,3]]}}}],["3.1.0",{"_index":4763,"t":{"1505":{"position":[[3260,5]]}}}],["3.1.2",{"_index":2222,"t":{"192":{"position":[[2858,6]]}}}],["3.1const",{"_index":1674,"t":{"106":{"position":[[1169,8]]}}}],["3.2",{"_index":1697,"t":{"110":{"position":[[21,3]]}}}],["3.2.2",{"_index":3607,"t":{"547":{"position":[[132,5],[182,5]]}}}],["3.2const",{"_index":1706,"t":{"110":{"position":[[810,8]]}}}],["3.33µ",{"_index":3444,"t":{"479":{"position":[[4655,6]]}}}],["3.34",{"_index":3359,"t":{"477":{"position":[[3926,5]]}}}],["3.37",{"_index":3278,"t":{"475":{"position":[[5039,6]]}}}],["3.40µ",{"_index":3272,"t":{"475":{"position":[[4932,6]]}}}],["3.60µ",{"_index":3358,"t":{"477":{"position":[[3913,6]]},"479":{"position":[[4643,6]]}}}],["3.72µ",{"_index":3271,"t":{"475":{"position":[[4920,6]]},"477":{"position":[[3901,6]]}}}],["3.9\"servic",{"_index":4552,"t":{"1441":{"position":[[211,14]]},"2610":{"position":[[211,14]]}}}],["30",{"_index":35,"t":{"2":{"position":[[427,2]]},"110":{"position":[[2450,2]]},"128":{"position":[[1255,3]]},"399":{"position":[[181,2]]},"479":{"position":[[6017,3]]},"533":{"position":[[243,2]]},"858":{"position":[[1320,6]]},"932":{"position":[[348,2]]},"941":{"position":[[459,2]]},"1223":{"position":[[181,2]]},"1843":{"position":[[1295,6]]},"2053":{"position":[[348,2]]},"2062":{"position":[[459,2]]},"2472":{"position":[[181,2]]},"3072":{"position":[[1583,6]]},"3086":{"position":[[867,2]]},"3246":{"position":[[348,2]]},"3257":{"position":[[459,2]]}}}],["30*60",{"_index":2852,"t":{"301":{"position":[[327,6]]}}}],["30*60}token",{"_index":2820,"t":{"291":{"position":[[1308,11]]}}}],["300",{"_index":1123,"t":{"52":{"position":[[409,5]]},"74":{"position":[[483,4]]},"589":{"position":[[331,5]]},"591":{"position":[[136,4]]},"856":{"position":[[284,7]]},"858":{"position":[[1434,7]]},"971":{"position":[[324,3],[485,3]]},"973":{"position":[[1794,3]]},"1843":{"position":[[1372,6]]},"1899":{"position":[[432,7],[1219,7]]},"1981":{"position":[[324,3],[485,3]]},"1983":{"position":[[1966,3]]},"3072":{"position":[[1660,6]]},"3134":{"position":[[432,7],[1219,7]]},"3140":{"position":[[324,3],[485,3]]},"3142":{"position":[[1966,3]]}}}],["3000",{"_index":3895,"t":{"728":{"position":[[6,5]]},"1505":{"position":[[4398,4],[4473,4],[4573,5]]},"2652":{"position":[[94,6],[169,4],[319,5]]},"2686":{"position":[[4460,4],[4535,4],[4635,5]]},"3663":{"position":[[94,6],[169,4],[319,5]]}}}],["3000;app.use(express.json());const",{"_index":2548,"t":{"246":{"position":[[371,34]]}}}],["3000reason",{"_index":5082,"t":{"1963":{"position":[[6,11]]},"3008":{"position":[[6,11]]}}}],["3001",{"_index":3899,"t":{"730":{"position":[[6,5]]}}}],["3001reason",{"_index":5084,"t":{"1965":{"position":[[66,11]]},"3010":{"position":[[66,11]]}}}],["3002",{"_index":3900,"t":{"732":{"position":[[6,5]]}}}],["3003",{"_index":3901,"t":{"734":{"position":[[6,5]]}}}],["3004",{"_index":3902,"t":{"736":{"position":[[6,5]]}}}],["3004reason",{"_index":5086,"t":{"1965":{"position":[[183,11]]},"3010":{"position":[[183,11]]}}}],["3005",{"_index":3904,"t":{"738":{"position":[[6,5]]}}}],["3005reason",{"_index":5088,"t":{"1965":{"position":[[313,11]]},"3010":{"position":[[313,11]]}}}],["3006",{"_index":3905,"t":{"740":{"position":[[6,5]]}}}],["3006reason",{"_index":5090,"t":{"1965":{"position":[[374,11]]},"3010":{"position":[[374,11]]}}}],["3007",{"_index":3906,"t":{"742":{"position":[[6,5]]}}}],["3008",{"_index":3907,"t":{"744":{"position":[[6,5]]}}}],["3008reason",{"_index":5092,"t":{"1965":{"position":[[493,11]]},"3010":{"position":[[493,11]]}}}],["3009",{"_index":3908,"t":{"746":{"position":[[6,5]]}}}],["3009reason",{"_index":5094,"t":{"1965":{"position":[[607,11]]},"3010":{"position":[[607,11]]}}}],["3010",{"_index":3909,"t":{"748":{"position":[[6,5]]}}}],["3010reason",{"_index":5096,"t":{"1965":{"position":[[755,11]]},"3010":{"position":[[755,11]]}}}],["3011",{"_index":3911,"t":{"750":{"position":[[6,5]]}}}],["3011reason",{"_index":5099,"t":{"1965":{"position":[[997,11]]},"3010":{"position":[[997,11]]}}}],["3012",{"_index":2952,"t":{"331":{"position":[[953,5]]},"752":{"position":[[6,5]]},"3629":{"position":[[954,5]]}}}],["3012reason",{"_index":5102,"t":{"1965":{"position":[[1162,11]]},"3010":{"position":[[1162,11]]}}}],["3013",{"_index":3912,"t":{"754":{"position":[[6,5]]}}}],["3013reason",{"_index":5104,"t":{"1965":{"position":[[1387,11]]},"3010":{"position":[[1387,11]]}}}],["3086496",{"_index":73,"t":{"2":{"position":[[839,7]]}}}],["30k",{"_index":3026,"t":{"399":{"position":[[249,3]]},"1223":{"position":[[249,3]]},"2472":{"position":[[249,3]]}}}],["30kb",{"_index":147,"t":{"2":{"position":[[2256,4]]},"84":{"position":[[620,4]]}}}],["30x",{"_index":1077,"t":{"50":{"position":[[474,3]]}}}],["31",{"_index":3847,"t":{"680":{"position":[[1041,2],[1136,2],[1249,2],[1360,2],[1466,2],[1571,2],[1676,2]]},"694":{"position":[[930,2]]},"1617":{"position":[[1210,2],[1305,2],[1418,2],[1529,2],[1635,2],[1740,2],[1845,2]]},"1737":{"position":[[912,2]]},"2802":{"position":[[1210,2],[1305,2],[1418,2],[1529,2],[1635,2],[1740,2],[1845,2]]},"2926":{"position":[[912,2]]}}}],["31.5µ",{"_index":3488,"t":{"481":{"position":[[759,6]]}}}],["31200",{"_index":4907,"t":{"1615":{"position":[[1724,5]]},"2800":{"position":[[1724,5]]}}}],["32",{"_index":178,"t":{"2":{"position":[[2884,3]]},"509":{"position":[[305,2]]},"517":{"position":[[474,2]]},"1029":{"position":[[532,2]]},"1437":{"position":[[315,2]]},"1445":{"position":[[497,2]]},"2368":{"position":[[682,2],[705,2]]},"2606":{"position":[[330,2]]},"2614":{"position":[[433,2]]},"3429":{"position":[[682,2],[705,2]]}}}],["3276750fs.nr_open",{"_index":71,"t":{"2":{"position":[[793,17]]}}}],["33554432",{"_index":83,"t":{"2":{"position":[[992,8]]}}}],["33554432net.core.wmem_max",{"_index":82,"t":{"2":{"position":[[964,25]]}}}],["3499",{"_index":5450,"t":{"2652":{"position":[[174,5]]},"3663":{"position":[[174,5]]}}}],["34d2",{"_index":4364,"t":{"1177":{"position":[[1182,4]]},"2228":{"position":[[1182,4]]},"3578":{"position":[[1157,4]]}}}],["35",{"_index":3418,"t":{"479":{"position":[[2876,2],[3148,2]]},"1215":{"position":[[130,2]]},"2327":{"position":[[130,2]]}}}],["3500reason",{"_index":5106,"t":{"1967":{"position":[[79,11]]},"3012":{"position":[[79,11]]}}}],["3501reason",{"_index":5108,"t":{"1967":{"position":[[202,11]]},"3012":{"position":[[202,11]]}}}],["3502reason",{"_index":5110,"t":{"1967":{"position":[[323,11]]},"3012":{"position":[[323,11]]}}}],["3503reason",{"_index":5112,"t":{"1967":{"position":[[492,11]]},"3012":{"position":[[492,11]]}}}],["3504reason",{"_index":5114,"t":{"1967":{"position":[[661,11]]},"3012":{"position":[[661,11]]}}}],["3505reason",{"_index":5116,"t":{"1967":{"position":[[859,11]]},"3012":{"position":[[859,11]]}}}],["3506reason",{"_index":5118,"t":{"1967":{"position":[[1022,11]]},"3012":{"position":[[1022,11]]}}}],["3507reason",{"_index":5122,"t":{"1967":{"position":[[1369,11]]},"3012":{"position":[[1369,11]]}}}],["3508reason",{"_index":5124,"t":{"1967":{"position":[[1540,11]]},"3012":{"position":[[1540,11]]}}}],["3509reason",{"_index":5128,"t":{"1967":{"position":[[1812,11]]},"3012":{"position":[[1812,11]]}}}],["350f",{"_index":4793,"t":{"1505":{"position":[[7309,4]]},"2686":{"position":[[7389,4]]}}}],["3539",{"_index":3850,"t":{"680":{"position":[[1345,4]]},"1617":{"position":[[1514,4]]},"2802":{"position":[[1514,4]]}}}],["36",{"_index":3417,"t":{"479":{"position":[[2873,2],[3145,2],[6125,3]]}}}],["36.36",{"_index":3303,"t":{"475":{"position":[[5524,6]]}}}],["3600",{"_index":4019,"t":{"868":{"position":[[285,4]]},"1909":{"position":[[285,4]]},"1911":{"position":[[346,4]]},"3166":{"position":[[285,4]]},"3168":{"position":[[346,4]]}}}],["3600}token",{"_index":5542,"t":{"2970":{"position":[[238,10]]}}}],["360b",{"_index":3365,"t":{"477":{"position":[[4141,4]]}}}],["365",{"_index":1561,"t":{"98":{"position":[[619,3],[2111,3]]}}}],["36content",{"_index":5746,"t":{"3538":{"position":[[1400,9]]}}}],["399",{"_index":5446,"t":{"2648":{"position":[[75,3]]},"3659":{"position":[[75,3]]}}}],["3content",{"_index":4582,"t":{"1457":{"position":[[1233,8]]},"2289":{"position":[[1293,8]]}}}],["3s",{"_index":5652,"t":{"3367":{"position":[[597,5],[1072,5]]},"3369":{"position":[[866,5]]}}}],["3x",{"_index":573,"t":{"14":{"position":[[1501,2]]},"50":{"position":[[1907,2]]}}}],["4",{"_index":446,"t":{"10":{"position":[[1428,1]]},"14":{"position":[[1586,1]]},"18":{"position":[[577,2]]},"38":{"position":[[968,1]]},"50":{"position":[[373,1]]},"110":{"position":[[2459,1],[2479,1],[2499,1]]},"146":{"position":[[88,2]]},"297":{"position":[[243,2]]},"471":{"position":[[3395,1]]},"475":{"position":[[4680,2]]},"477":{"position":[[1394,1]]},"481":{"position":[[850,2],[979,2],[1035,2]]},"951":{"position":[[393,1]]},"1094":{"position":[[351,2]]},"1407":{"position":[[611,2]]},"1413":{"position":[[445,2]]},"1473":{"position":[[808,2]]},"1497":{"position":[[870,4]]},"1921":{"position":[[393,1]]},"2189":{"position":[[308,2],[1175,2]]},"2305":{"position":[[808,2]]},"2622":{"position":[[22,1]]},"2692":{"position":[[870,4]]},"3148":{"position":[[393,1]]},"3492":{"position":[[308,2],[1175,2]]},"3518":{"position":[[657,2]]},"3633":{"position":[[22,1]]}}}],["4.0.0",{"_index":4845,"t":{"1563":{"position":[[140,5],[196,5]]}}}],["4.9.65",{"_index":60,"t":{"2":{"position":[[713,6]]}}}],["40",{"_index":136,"t":{"2":{"position":[[2088,2]]}}}],["400",{"_index":5214,"t":{"2360":{"position":[[4628,5]]},"2366":{"position":[[193,5]]},"2648":{"position":[[136,5]]},"3421":{"position":[[4631,5]]},"3427":{"position":[[193,5]]},"3538":{"position":[[1369,3]]},"3540":{"position":[[315,3]]},"3659":{"position":[[136,5]]}}}],["4000",{"_index":2525,"t":{"236":{"position":[[922,4]]},"1029":{"position":[[148,5],[249,4],[329,4]]},"2368":{"position":[[229,4],[283,6],[468,4]]},"2652":{"position":[[180,4]]},"3429":{"position":[[229,4],[283,6],[468,4]]},"3663":{"position":[[180,4]]}}}],["403",{"_index":3952,"t":{"798":{"position":[[1896,3]]},"1813":{"position":[[1896,3]]},"2360":{"position":[[4420,4],[4505,3],[4784,4]]},"3421":{"position":[[4423,4],[4508,3],[4787,4]]},"3639":{"position":[[916,4]]},"3655":{"position":[[1100,4]]}}}],["404",{"_index":5756,"t":{"3540":{"position":[[213,3]]}}}],["409",{"_index":5766,"t":{"3540":{"position":[[492,3]]}}}],["4094",{"_index":4723,"t":{"1505":{"position":[[833,4],[1367,4],[7870,4]]},"2686":{"position":[[848,4],[1382,4],[7967,4]]}}}],["4096",{"_index":79,"t":{"2":{"position":[[923,4]]},"1154":{"position":[[118,4]]},"1156":{"position":[[119,4]]},"2334":{"position":[[118,4]]},"2336":{"position":[[119,4]]},"3619":{"position":[[118,4]]},"3621":{"position":[[119,4]]}}}],["4115330",{"_index":74,"t":{"2":{"position":[[847,7]]}}}],["412",{"_index":3855,"t":{"680":{"position":[[1662,3]]},"1617":{"position":[[1831,3]]},"2802":{"position":[[1831,3]]}}}],["416",{"_index":5762,"t":{"3540":{"position":[[401,3]]}}}],["418.5",{"_index":3338,"t":{"477":{"position":[[1382,5]]}}}],["4194394",{"_index":80,"t":{"2":{"position":[[928,7]]}}}],["42",{"_index":1257,"t":{"64":{"position":[[589,5]]},"70":{"position":[[576,9]]},"431":{"position":[[361,2]]},"479":{"position":[[2879,2],[3260,3]]},"688":{"position":[[415,9]]},"690":{"position":[[319,9]]},"692":{"position":[[280,9]]},"798":{"position":[[369,5]]},"802":{"position":[[51,6],[151,4]]},"804":{"position":[[98,5],[274,4]]},"806":{"position":[[65,5],[247,5]]},"820":{"position":[[243,2],[941,2]]},"1257":{"position":[[361,2]]},"1469":{"position":[[630,4],[738,4]]},"1597":{"position":[[64,3],[212,2],[1273,7]]},"1731":{"position":[[415,9]]},"1733":{"position":[[319,9]]},"1735":{"position":[[280,9]]},"1765":{"position":[[99,2],[169,5]]},"1813":{"position":[[369,5]]},"1817":{"position":[[51,6],[151,4]]},"1819":{"position":[[98,5],[274,4]]},"1821":{"position":[[65,5],[247,5]]},"1837":{"position":[[243,2],[634,2]]},"1841":{"position":[[485,2]]},"2301":{"position":[[673,4],[781,4]]},"2506":{"position":[[361,2]]},"2780":{"position":[[64,3],[212,2],[1226,6]]},"2920":{"position":[[361,8]]},"2922":{"position":[[268,8]]},"2924":{"position":[[226,8]]},"2970":{"position":[[99,2],[183,5],[487,5]]},"3042":{"position":[[381,5]]},"3046":{"position":[[51,6],[257,4]]},"3048":{"position":[[98,5],[379,4]]},"3050":{"position":[[65,5],[352,5]]},"3066":{"position":[[258,2],[648,2]]},"3070":{"position":[[499,2]]},"3514":{"position":[[518,4],[626,4]]}}}],["421bf374",{"_index":4503,"t":{"1407":{"position":[[1101,9]]}}}],["421d",{"_index":4776,"t":{"1505":{"position":[[5514,4],[5617,4]]},"2686":{"position":[[5576,4],[5679,4]]}}}],["4254",{"_index":4343,"t":{"1147":{"position":[[1055,4]]}}}],["426614174000",{"_index":5695,"t":{"3367":{"position":[[4774,14]]}}}],["4283",{"_index":4863,"t":{"1597":{"position":[[1367,4]]},"2780":{"position":[[1331,4]]}}}],["42hmac",{"_index":4856,"t":{"1597":{"position":[[184,6]]},"2780":{"position":[[184,6]]}}}],["43",{"_index":3979,"t":{"820":{"position":[[961,2]]},"1837":{"position":[[654,2]]},"3066":{"position":[[655,2]]}}}],["4318:4318",{"_index":5621,"t":{"3327":{"position":[[608,9]]}}}],["4375",{"_index":5036,"t":{"1847":{"position":[[824,4],[892,4]]},"3076":{"position":[[788,4],[856,4]]}}}],["439d",{"_index":4869,"t":{"1597":{"position":[[1487,4]]},"2780":{"position":[[1451,4]]}}}],["43content",{"_index":4607,"t":{"1471":{"position":[[363,9]]},"1475":{"position":[[396,9]]},"2303":{"position":[[363,9]]},"2307":{"position":[[396,9]]}}}],["44",{"_index":3454,"t":{"479":{"position":[[5870,4]]}}}],["443",{"_index":4142,"t":{"979":{"position":[[369,3]]},"2072":{"position":[[369,3]]},"3309":{"position":[[369,3]]}}}],["447f",{"_index":4794,"t":{"1505":{"position":[[7314,4]]},"2686":{"position":[[7394,4]]}}}],["4499",{"_index":5220,"t":{"2368":{"position":[[290,5]]},"2652":{"position":[[185,4]]},"3429":{"position":[[290,5]]},"3663":{"position":[[185,4]]}}}],["44ec",{"_index":5205,"t":{"2260":{"position":[[1300,4]]},"3610":{"position":[[1300,4]]}}}],["45",{"_index":3460,"t":{"479":{"position":[[6034,3]]}}}],["4500",{"_index":5219,"t":{"2368":{"position":[[148,5],[346,6]]},"3429":{"position":[[148,5],[346,6]]}}}],["4501",{"_index":5213,"t":{"2354":{"position":[[6271,5]]},"3415":{"position":[[6300,5]]}}}],["4502",{"_index":3811,"t":{"678":{"position":[[409,4]]},"1615":{"position":[[409,4]]},"2800":{"position":[[409,4]]}}}],["4561",{"_index":3231,"t":{"475":{"position":[[2269,4]]},"477":{"position":[[1324,4]]}}}],["45c8",{"_index":5206,"t":{"2260":{"position":[[1305,4]]},"3610":{"position":[[1305,4]]}}}],["461.3",{"_index":1095,"t":{"50":{"position":[[1114,5]]}}}],["4648",{"_index":3125,"t":{"469":{"position":[[970,4]]},"471":{"position":[[3307,4],[3438,4]]},"475":{"position":[[2213,4]]},"477":{"position":[[1268,4]]}}}],["468n",{"_index":3430,"t":{"479":{"position":[[4393,5]]}}}],["46content",{"_index":5739,"t":{"3538":{"position":[[993,9]]}}}],["476.5",{"_index":1098,"t":{"50":{"position":[[1228,5]]}}}],["4791",{"_index":5202,"t":{"2260":{"position":[[1175,4]]},"3610":{"position":[[1175,4]]}}}],["47cb",{"_index":3841,"t":{"680":{"position":[[967,4],[1070,4],[1165,4],[1278,4],[1389,4],[1495,4],[1600,4]]},"1617":{"position":[[1136,4],[1239,4],[1334,4],[1447,4],[1558,4],[1664,4],[1769,4]]},"2802":{"position":[[1136,4],[1239,4],[1334,4],[1447,4],[1558,4],[1664,4],[1769,4]]}}}],["48.3µ",{"_index":3497,"t":{"481":{"position":[[959,6]]}}}],["4815",{"_index":4626,"t":{"1479":{"position":[[420,4]]},"2311":{"position":[[420,4]]},"3524":{"position":[[236,4]]}}}],["483b",{"_index":3276,"t":{"475":{"position":[[5019,4]]},"477":{"position":[[4000,4]]}}}],["49",{"_index":4293,"t":{"1057":{"position":[[1374,2]]},"2140":{"position":[[1374,2]]},"3355":{"position":[[1374,2]]}}}],["490b22231e74",{"_index":4521,"t":{"1417":{"position":[[308,14],[675,14],[1095,14]]}}}],["4911",{"_index":4738,"t":{"1505":{"position":[[998,4],[1547,4],[8050,4],[9895,4]]},"2686":{"position":[[1013,4],[1562,4],[8147,4],[10040,4]]}}}],["492.2",{"_index":1094,"t":{"50":{"position":[[921,5]]}}}],["4971",{"_index":4365,"t":{"1177":{"position":[[1187,4]]},"2228":{"position":[[1187,4]]},"3578":{"position":[[1162,4]]}}}],["4999",{"_index":4232,"t":{"1029":{"position":[[254,4]]},"2368":{"position":[[234,4],[353,5],[473,4]]},"2652":{"position":[[101,6]]},"3429":{"position":[[234,4],[353,5],[473,4]]},"3663":{"position":[[101,6]]}}}],["499a",{"_index":4601,"t":{"1469":{"position":[[545,4],[597,4],[653,4],[705,4]]},"2301":{"position":[[588,4],[640,4],[696,4],[748,4]]},"3514":{"position":[[433,4],[485,4],[541,4],[593,4]]}}}],["499b",{"_index":3277,"t":{"475":{"position":[[5029,4]]}}}],["4ad6",{"_index":2949,"t":{"331":{"position":[[864,4]]},"3629":{"position":[[865,4]]}}}],["4bc3ca70",{"_index":4867,"t":{"1597":{"position":[[1472,9]]},"2780":{"position":[[1436,9]]}}}],["4c49",{"_index":4728,"t":{"1505":{"position":[[891,4],[1440,4],[1871,4],[7943,4]]},"2686":{"position":[[906,4],[1455,4],[1886,4],[8040,4]]}}}],["4c7b",{"_index":4342,"t":{"1147":{"position":[[1050,4]]}}}],["4cfe",{"_index":5042,"t":{"1847":{"position":[[965,4],[1033,4]]},"3076":{"position":[[929,4],[997,4]]}}}],["4d0f",{"_index":4370,"t":{"1177":{"position":[[1576,4]]}}}],["4ddd",{"_index":4733,"t":{"1505":{"position":[[947,4],[1496,4],[7999,4]]},"2686":{"position":[[962,4],[1511,4],[8096,4]]}}}],["4e9eafcf",{"_index":4731,"t":{"1505":{"position":[[932,9],[1481,9],[7984,9]]},"2686":{"position":[[947,9],[1496,9],[8081,9]]}}}],["4ebc",{"_index":4187,"t":{"1017":{"position":[[1881,4]]},"1019":{"position":[[482,4]]},"1021":{"position":[[394,4]]},"1023":{"position":[[1753,4]]},"1025":{"position":[[1768,4]]},"2354":{"position":[[1998,4]]},"2356":{"position":[[482,4]]},"2358":{"position":[[394,4]]},"2360":{"position":[[1753,4]]},"2362":{"position":[[1768,4]]},"2364":{"position":[[1857,4]]},"3415":{"position":[[1998,4]]},"3417":{"position":[[482,4]]},"3419":{"position":[[441,4]]},"3421":{"position":[[1556,4]]},"3423":{"position":[[1768,4]]},"3425":{"position":[[1830,4]]}}}],["4f82",{"_index":4505,"t":{"1407":{"position":[[1116,4]]}}}],["4fa8",{"_index":4519,"t":{"1417":{"position":[[298,4],[665,4],[1085,4]]}}}],["4x",{"_index":1102,"t":{"50":{"position":[[1513,2]]}}}],["5",{"_index":95,"t":{"2":{"position":[[1224,1],[2632,2]]},"50":{"position":[[2143,3]]},"84":{"position":[[419,1]]},"110":{"position":[[2461,1],[2481,1],[2501,1]]},"156":{"position":[[367,2]]},"317":{"position":[[127,2]]},"471":{"position":[[3810,2],[3937,2]]},"481":{"position":[[904,2]]},"678":{"position":[[675,1],[1429,1],[1444,1],[1459,1]]},"682":{"position":[[133,1]]},"804":{"position":[[35,1],[305,1]]},"941":{"position":[[123,1]]},"1094":{"position":[[366,2]]},"1407":{"position":[[631,2]]},"1419":{"position":[[423,1]]},"1425":{"position":[[2461,1]]},"1573":{"position":[[179,1]]},"1587":{"position":[[192,1]]},"1615":{"position":[[675,1],[1423,1],[1438,1],[1453,1]]},"1819":{"position":[[35,1],[305,1]]},"2062":{"position":[[123,1]]},"2189":{"position":[[323,2],[1196,2]]},"2668":{"position":[[179,1]]},"2682":{"position":[[192,1]]},"2708":{"position":[[2042,1],[2137,1]]},"2764":{"position":[[294,1]]},"2800":{"position":[[675,1],[1423,1],[1438,1],[1453,1]]},"3048":{"position":[[35,1]]},"3257":{"position":[[123,1]]},"3492":{"position":[[323,2],[1196,2]]}}}],["5*60}token",{"_index":3960,"t":{"804":{"position":[[130,10]]},"1819":{"position":[[130,10]]},"3048":{"position":[[130,10]]}}}],["5.0.1",{"_index":3668,"t":{"614":{"position":[[115,5]]},"1187":{"position":[[343,5]]},"1193":{"position":[[529,5]]},"2084":{"position":[[343,5]]},"2092":{"position":[[529,5]]},"2686":{"position":[[3275,5]]},"3269":{"position":[[343,5]]},"3277":{"position":[[529,5]]}}}],["5.1.2.x86_64.rpm",{"_index":5487,"t":{"2732":{"position":[[180,16]]}}}],["5.1.2.x86_64.rpmsudo",{"_index":5486,"t":{"2732":{"position":[[132,20]]}}}],["5.56",{"_index":3309,"t":{"475":{"position":[[5646,5]]}}}],["5.6µ",{"_index":3489,"t":{"481":{"position":[[771,5]]}}}],["5.85µ",{"_index":3442,"t":{"479":{"position":[[4588,6]]}}}],["5.8µ",{"_index":3501,"t":{"481":{"position":[[1038,5]]}}}],["50",{"_index":3025,"t":{"397":{"position":[[345,2]]},"399":{"position":[[184,2]]},"479":{"position":[[5968,3]]},"555":{"position":[[231,2]]},"1221":{"position":[[345,2]]},"1223":{"position":[[184,2]]},"1666":{"position":[[1096,2]]},"2470":{"position":[[345,2]]},"2472":{"position":[[184,2]]},"2830":{"position":[[1099,2]]}}}],["500",{"_index":4166,"t":{"979":{"position":[[1360,3]]},"2072":{"position":[[1360,3]]},"3309":{"position":[[1360,3]]},"3540":{"position":[[100,3],[716,3]]}}}],["5000",{"_index":2641,"t":{"252":{"position":[[796,6]]}}}],["500k",{"_index":38,"t":{"2":{"position":[[458,5],[1694,4]]},"84":{"position":[[743,4]]}}}],["500m",{"_index":4247,"t":{"1045":{"position":[[388,8],[515,7]]},"2384":{"position":[[388,8],[515,7]]},"3371":{"position":[[337,8],[419,8]]},"3445":{"position":[[388,8],[515,7]]}}}],["502",{"_index":4111,"t":{"953":{"position":[[418,3]]},"979":{"position":[[1364,3]]},"1923":{"position":[[418,3]]},"2072":{"position":[[1364,3]]},"3150":{"position":[[418,3]]},"3309":{"position":[[1364,3]]}}}],["503",{"_index":4167,"t":{"979":{"position":[[1368,3]]},"2023":{"position":[[272,3]]},"2072":{"position":[[1368,3]]},"3212":{"position":[[245,3]]},"3309":{"position":[[1368,3]]}}}],["504",{"_index":4168,"t":{"979":{"position":[[1372,3]]},"2072":{"position":[[1372,3]]},"3309":{"position":[[1372,3]]}}}],["50for",{"_index":783,"t":{"18":{"position":[[2530,5]]}}}],["50k",{"_index":3449,"t":{"479":{"position":[[5641,4],[5759,3]]}}}],["50x.html",{"_index":4169,"t":{"979":{"position":[[1376,10],[1398,9]]},"2072":{"position":[[1376,10],[1398,9]]},"3309":{"position":[[1376,10],[1398,9]]}}}],["51",{"_index":3464,"t":{"479":{"position":[[6095,3],[6112,4]]}}}],["512",{"_index":204,"t":{"2":{"position":[[3519,3],[3702,4],[3738,4]]},"471":{"position":[[1393,3],[3923,3],[4091,3]]},"1154":{"position":[[337,4]]},"1156":{"position":[[297,4]]},"2334":{"position":[[337,4]]},"2336":{"position":[[297,4]]},"3619":{"position":[[337,4]]},"3621":{"position":[[297,4]]}}}],["52.3µ",{"_index":3500,"t":{"481":{"position":[[1026,6]]}}}],["5208",{"_index":5473,"t":{"2712":{"position":[[625,5]]}}}],["5289",{"_index":2948,"t":{"331":{"position":[[859,4]]},"3629":{"position":[[860,4]]}}}],["5432:5432",{"_index":4911,"t":{"1649":{"position":[[387,9]]},"2901":{"position":[[387,9]]}}}],["55",{"_index":3458,"t":{"479":{"position":[[5955,4]]}}}],["559n",{"_index":3429,"t":{"479":{"position":[[4382,5]]}}}],["55content",{"_index":4588,"t":{"1457":{"position":[[1634,9]]},"2289":{"position":[[1688,9]]}}}],["56",{"_index":3715,"t":{"624":{"position":[[280,6]]},"678":{"position":[[1351,2]]},"1017":{"position":[[2017,6],[4941,4],[5178,4]]},"1615":{"position":[[1345,2]]},"1623":{"position":[[280,6]]},"1847":{"position":[[435,3],[1140,5]]},"2354":{"position":[[2134,6],[5064,4],[5301,4]]},"2800":{"position":[[1345,2]]},"2810":{"position":[[280,6]]},"3076":{"position":[[435,3],[1104,5]]},"3415":{"position":[[2134,6],[5093,4],[5330,4]]}}}],["563",{"_index":1023,"t":{"38":{"position":[[1619,3]]}}}],["567",{"_index":3049,"t":{"431":{"position":[[381,4]]},"1257":{"position":[[381,4]]},"2506":{"position":[[381,4]]}}}],["5883",{"_index":1085,"t":{"50":{"position":[[744,4]]}}}],["59",{"_index":3422,"t":{"479":{"position":[[3129,2],[3221,3]]}}}],["59.66",{"_index":3366,"t":{"477":{"position":[[4152,6]]}}}],["5edf",{"_index":4625,"t":{"1479":{"position":[[415,4]]},"2311":{"position":[[415,4]]},"3524":{"position":[[231,4]]}}}],["5kb",{"_index":1079,"t":{"50":{"position":[[513,3]]}}}],["5m",{"_index":2523,"t":{"236":{"position":[[835,3]]},"471":{"position":[[3699,3]]}}}],["5s",{"_index":2497,"t":{"224":{"position":[[563,2]]},"473":{"position":[[837,2],[1016,2]]},"941":{"position":[[146,5]]},"1195":{"position":[[456,2],[564,2]]},"1672":{"position":[[1504,5]]},"2062":{"position":[[146,5]]},"2096":{"position":[[456,2],[564,2]]},"2836":{"position":[[1504,5]]},"3257":{"position":[[146,5]]},"3281":{"position":[[456,2],[564,2]]}}}],["5x",{"_index":676,"t":{"16":{"position":[[4413,2]]},"50":{"position":[[375,2]]},"1203":{"position":[[926,2]]},"2106":{"position":[[926,2]]},"3289":{"position":[[926,2]]}}}],["6",{"_index":1087,"t":{"50":{"position":[[765,1],[825,1]]},"110":{"position":[[2463,1],[2483,1],[2503,1]]},"475":{"position":[[4740,2],[5168,2]]},"477":{"position":[[3722,2]]},"483":{"position":[[1844,1]]},"678":{"position":[[1384,1],[1399,1],[1414,1]]},"1094":{"position":[[379,2]]},"1407":{"position":[[644,2]]},"1615":{"position":[[1378,1],[1393,1],[1408,1]]},"2189":{"position":[[340,2],[1215,2]]},"2800":{"position":[[1378,1],[1393,1],[1408,1]]},"3492":{"position":[[340,2],[1215,2]]}}}],["6.0",{"_index":3374,"t":{"477":{"position":[[4494,3]]}}}],["6.05µ",{"_index":3495,"t":{"481":{"position":[[907,6]]}}}],["6.25µ",{"_index":3441,"t":{"479":{"position":[[4576,6]]}}}],["6.2µ",{"_index":3356,"t":{"477":{"position":[[3846,5]]}}}],["6.32",{"_index":3443,"t":{"479":{"position":[[4601,5]]}}}],["6.3µ",{"_index":3268,"t":{"475":{"position":[[4866,5]]}}}],["60",{"_index":2321,"t":{"198":{"position":[[1228,4]]},"246":{"position":[[422,2],[427,2]]},"280":{"position":[[3602,2]]},"479":{"position":[[6204,3]]},"804":{"position":[[309,2]]},"838":{"position":[[598,6]]},"979":{"position":[[1074,4]]},"981":{"position":[[648,4]]},"1666":{"position":[[998,2],[1081,6]]},"1668":{"position":[[894,2]]},"1670":{"position":[[1375,2]]},"1819":{"position":[[309,2]]},"1855":{"position":[[598,6]]},"2072":{"position":[[1074,4]]},"2074":{"position":[[648,4]]},"2830":{"position":[[1001,2],[1084,6]]},"2832":{"position":[[900,2]]},"2834":{"position":[[1381,2]]},"3084":{"position":[[598,6]]},"3309":{"position":[[1074,4]]},"3311":{"position":[[648,4]]}}}],["60000",{"_index":4396,"t":{"1193":{"position":[[1097,5]]},"2092":{"position":[[1113,5]]},"2712":{"position":[[435,6]]},"3277":{"position":[[1113,5]]}}}],["604.7",{"_index":3168,"t":{"471":{"position":[[3374,5],[3452,5]]}}}],["61.53",{"_index":3350,"t":{"477":{"position":[[3667,6]]}}}],["615c0ed3eebb",{"_index":4740,"t":{"1505":{"position":[[1008,14],[1557,14],[8060,14],[9905,13]]},"2686":{"position":[[1023,14],[1572,14],[8157,14],[10050,13]]}}}],["6172992net.ipv4.tcp_rmem",{"_index":75,"t":{"2":{"position":[[855,24]]}}}],["62",{"_index":3126,"t":{"469":{"position":[[981,2]]},"471":{"position":[[3318,2]]},"475":{"position":[[2224,2]]},"477":{"position":[[1279,2]]}}}],["62.8µ",{"_index":3491,"t":{"481":{"position":[[829,6]]}}}],["6292",{"_index":1022,"t":{"38":{"position":[[1608,4]]}}}],["634",{"_index":3853,"t":{"680":{"position":[[1557,3]]},"1617":{"position":[[1726,3]]},"2802":{"position":[[1726,3]]}}}],["6379",{"_index":3111,"t":{"469":{"position":[[688,8]]},"471":{"position":[[2752,8]]},"473":{"position":[[1046,6]]},"1193":{"position":[[1009,4]]},"1197":{"position":[[512,4]]},"2092":{"position":[[1025,4]]},"2098":{"position":[[512,4]]},"3277":{"position":[[1025,4]]},"3283":{"position":[[512,4]]}}}],["6379:6379",{"_index":2284,"t":{"196":{"position":[[143,9]]}}}],["6380",{"_index":3177,"t":{"473":{"position":[[789,4]]},"1195":{"position":[[408,4]]},"1197":{"position":[[526,4]]},"2096":{"position":[[408,4]]},"2098":{"position":[[526,4]]},"3281":{"position":[[408,4]]},"3283":{"position":[[526,4]]}}}],["6381",{"_index":3185,"t":{"473":{"position":[[968,4]]},"1195":{"position":[[516,4]]},"2096":{"position":[[516,4]]},"3281":{"position":[[516,4]]}}}],["64",{"_index":176,"t":{"2":{"position":[[2869,3]]},"471":{"position":[[1065,2]]},"509":{"position":[[278,2],[334,2]]},"517":{"position":[[190,2],[214,2],[238,2],[264,2],[291,2],[318,2],[350,2],[366,2],[539,2]]},"1437":{"position":[[288,2],[344,2]]},"1445":{"position":[[190,2],[213,2],[237,2],[261,2],[287,2],[314,2],[341,2],[373,2],[389,2],[562,2]]},"2606":{"position":[[303,2],[359,2]]},"2614":{"position":[[190,2],[214,2],[240,2],[267,2],[299,2],[325,2],[498,2]]}}}],["64kb",{"_index":4328,"t":{"1107":{"position":[[15,6]]},"1122":{"position":[[15,6]]},"1145":{"position":[[15,6]]},"1152":{"position":[[15,6]]},"1175":{"position":[[15,6]]},"2181":{"position":[[15,6]]},"2213":{"position":[[15,6]]},"2226":{"position":[[15,6]]},"2241":{"position":[[15,6]]},"2258":{"position":[[15,6]]},"2273":{"position":[[15,6]]},"2332":{"position":[[15,6]]},"3458":{"position":[[15,6]]},"3488":{"position":[[15,6]]},"3555":{"position":[[15,6]]},"3576":{"position":[[15,6]]},"3591":{"position":[[15,6]]},"3608":{"position":[[15,6]]},"3617":{"position":[[15,6]]}}}],["65",{"_index":2319,"t":{"198":{"position":[[1205,3]]},"979":{"position":[[1051,3]]},"981":{"position":[[625,3]]},"2072":{"position":[[1051,3]]},"2074":{"position":[[625,3]]},"3309":{"position":[[1051,3]]},"3311":{"position":[[625,3]]}}}],["65.52",{"_index":3373,"t":{"477":{"position":[[4443,6]]}}}],["65.78",{"_index":3269,"t":{"475":{"position":[[4878,6]]}}}],["65500",{"_index":425,"t":{"10":{"position":[[844,5]]}}}],["65535",{"_index":1704,"t":{"110":{"position":[[718,5],[847,7]]},"513":{"position":[[399,5],[411,5]]},"983":{"position":[[92,7]]},"1441":{"position":[[423,5],[435,5]]},"2076":{"position":[[92,7]]},"2610":{"position":[[423,5],[435,5]]},"3313":{"position":[[92,7]]}}}],["65535if",{"_index":423,"t":{"10":{"position":[[821,7]]}}}],["65536",{"_index":453,"t":{"10":{"position":[[1549,5]]},"949":{"position":[[599,6]]},"951":{"position":[[519,5],[594,5]]},"1107":{"position":[[9,5]]},"1122":{"position":[[9,5]]},"1145":{"position":[[9,5]]},"1152":{"position":[[9,5]]},"1175":{"position":[[9,5]]},"1919":{"position":[[599,6]]},"1921":{"position":[[519,5],[594,5]]},"2181":{"position":[[9,5]]},"2213":{"position":[[9,5]]},"2226":{"position":[[9,5]]},"2241":{"position":[[9,5]]},"2258":{"position":[[9,5]]},"2273":{"position":[[9,5]]},"2332":{"position":[[9,5]]},"3146":{"position":[[599,6]]},"3148":{"position":[[519,5],[594,5]]},"3458":{"position":[[9,5]]},"3488":{"position":[[9,5]]},"3555":{"position":[[9,5]]},"3576":{"position":[[9,5]]},"3591":{"position":[[9,5]]},"3608":{"position":[[9,5]]},"3617":{"position":[[9,5]]}}}],["65537",{"_index":1572,"t":{"98":{"position":[[1173,5]]}}}],["66.08",{"_index":3357,"t":{"477":{"position":[[3858,6]]}}}],["662b",{"_index":3284,"t":{"475":{"position":[[5161,4]]}}}],["666880.00",{"_index":3135,"t":{"471":{"position":[[1110,9]]}}}],["66fdf8d1",{"_index":5034,"t":{"1847":{"position":[[809,9],[877,9]]},"3076":{"position":[[773,9],[841,9]]}}}],["67",{"_index":3457,"t":{"479":{"position":[[5938,3]]}}}],["69",{"_index":3467,"t":{"479":{"position":[[6174,3]]}}}],["696c5294af25",{"_index":5208,"t":{"2260":{"position":[[1315,15]]},"3610":{"position":[[1315,15]]}}}],["6a36",{"_index":4775,"t":{"1505":{"position":[[5509,4],[5612,4]]},"2686":{"position":[[5571,4],[5674,4]]}}}],["6mf7i",{"_index":4858,"t":{"1597":{"position":[[359,5],[765,7],[1038,7]]},"2780":{"position":[[359,5],[765,7],[1038,7]]}}}],["7",{"_index":1729,"t":{"110":{"position":[[2465,1],[2485,1],[2505,1]]},"483":{"position":[[1795,1]]},"517":{"position":[[364,1]]},"1094":{"position":[[395,2]]},"1407":{"position":[[654,2]]},"1445":{"position":[[387,1]]},"2189":{"position":[[369,2],[1245,3]]},"2614":{"position":[[339,1]]},"3492":{"position":[[369,2],[1245,3]]}}}],["7.0",{"_index":3377,"t":{"477":{"position":[[4552,3]]}}}],["7.3µ",{"_index":3498,"t":{"481":{"position":[[971,5]]}}}],["7.52",{"_index":3445,"t":{"479":{"position":[[4668,5]]}}}],["70",{"_index":656,"t":{"16":{"position":[[3540,2]]}}}],["70.08",{"_index":3363,"t":{"477":{"position":[[4091,6]]}}}],["700k",{"_index":3382,"t":{"477":{"position":[[4746,4]]}}}],["71.52",{"_index":3368,"t":{"477":{"position":[[4216,6]]}}}],["72.73",{"_index":3375,"t":{"477":{"position":[[4504,6]]}}}],["75",{"_index":3453,"t":{"479":{"position":[[5840,4]]}}}],["75.86",{"_index":3378,"t":{"477":{"position":[[4562,6]]}}}],["7519",{"_index":5545,"t":{"3016":{"position":[[72,5]]},"3022":{"position":[[101,5]]},"3024":{"position":[[74,5]]}}}],["8",{"_index":483,"t":{"12":{"position":[[221,1]]},"18":{"position":[[580,2]]},"110":{"position":[[2467,1],[2487,1],[2507,1]]},"192":{"position":[[601,2]]},"194":{"position":[[455,2]]},"285":{"position":[[599,1]]},"413":{"position":[[494,1]]},"469":{"position":[[961,1]]},"471":{"position":[[3298,1],[3364,1]]},"475":{"position":[[2204,1],[2260,1],[2289,1],[4657,1],[4729,1],[4794,1],[4852,1],[4918,1],[5017,1],[5083,1],[5149,1],[5209,1],[5277,1],[5372,1],[5439,1],[5501,1],[5559,1],[5623,1]]},"477":{"position":[[1259,1],[1315,1],[1344,1],[1372,1],[3640,1],[3711,1],[3775,1],[3832,1],[3899,1],[3998,1],[4064,1],[4129,1],[4189,1],[4257,1],[4353,1],[4420,1],[4482,1],[4540,1],[4603,1]]},"479":{"position":[[4380,1],[4448,1],[4511,1],[4574,1],[4641,1]]},"481":{"position":[[757,1],[827,1],[893,1],[916,2],[957,1],[1024,1]]},"517":{"position":[[380,1]]},"1017":{"position":[[5133,4]]},"1094":{"position":[[408,2]]},"1237":{"position":[[494,1]]},"1407":{"position":[[664,2]]},"1445":{"position":[[204,1],[403,1]]},"2168":{"position":[[1526,1]]},"2189":{"position":[[390,2]]},"2191":{"position":[[672,1]]},"2354":{"position":[[5256,4]]},"2486":{"position":[[494,1]]},"3397":{"position":[[1526,1]]},"3415":{"position":[[5285,4]]},"3466":{"position":[[672,1]]},"3492":{"position":[[390,2]]}}}],["8.65",{"_index":1966,"t":{"156":{"position":[[131,5]]}}}],["8.74",{"_index":3273,"t":{"475":{"position":[[4945,5]]}}}],["8.82",{"_index":3292,"t":{"475":{"position":[[5300,5]]}}}],["80",{"_index":3456,"t":{"479":{"position":[[5924,3],[6004,4]]},"979":{"position":[[214,4],[357,3]]},"1057":{"position":[[300,6],[1051,4]]},"2072":{"position":[[214,4],[357,3]]},"2140":{"position":[[300,6],[1051,4]]},"3309":{"position":[[214,4],[357,3]]},"3355":{"position":[[300,6],[1051,4]]}}}],["80.00",{"_index":3372,"t":{"477":{"position":[[4375,6]]}}}],["8000",{"_index":2025,"t":{"170":{"position":[[183,5]]},"194":{"position":[[3237,4]]},"196":{"position":[[967,7]]},"896":{"position":[[47,8]]},"918":{"position":[[696,5]]},"1191":{"position":[[720,4],[890,5],[1125,5]]},"2009":{"position":[[47,8]]},"2039":{"position":[[911,5]]},"2090":{"position":[[720,4],[890,5],[1125,5]]},"3198":{"position":[[47,8]]},"3230":{"position":[[911,5]]},"3275":{"position":[[720,4],[890,5],[1125,5]]}}}],["8000:8000",{"_index":2386,"t":{"214":{"position":[[80,9]]},"511":{"position":[[185,9]]},"513":{"position":[[366,9]]},"541":{"position":[[168,9]]},"1439":{"position":[[187,9]]},"1441":{"position":[[390,9]]},"1557":{"position":[[170,9]]},"2608":{"position":[[187,9]]},"2610":{"position":[[390,9]]},"2726":{"position":[[170,9]]}}}],["8001",{"_index":2286,"t":{"196":{"position":[[220,5],[914,6]]},"1191":{"position":[[705,6],[1131,5]]},"2090":{"position":[[705,6],[1131,5]]},"3275":{"position":[[705,6],[1131,5]]}}}],["8002",{"_index":4388,"t":{"1191":{"position":[[1137,4]]},"2090":{"position":[[1137,4]]},"3275":{"position":[[1137,4]]}}}],["800k",{"_index":1438,"t":{"78":{"position":[[3444,4]]}}}],["8080:8080",{"_index":2373,"t":{"212":{"position":[[79,9]]}}}],["80content",{"_index":4576,"t":{"1457":{"position":[[1042,9]]},"2289":{"position":[[1102,9]]}}}],["81.16",{"_index":3361,"t":{"477":{"position":[[4020,6]]}}}],["814a",{"_index":4777,"t":{"1505":{"position":[[5519,4],[5622,4]]},"2686":{"position":[[5581,4],[5684,4]]}}}],["8192",{"_index":76,"t":{"2":{"position":[[882,4]]},"674":{"position":[[628,4]]},"676":{"position":[[683,4]]},"1605":{"position":[[548,4]]},"1607":{"position":[[527,4]]},"1609":{"position":[[674,4]]},"1611":{"position":[[549,4]]},"1613":{"position":[[749,4]]},"2790":{"position":[[548,4]]},"2792":{"position":[[527,4]]},"2794":{"position":[[674,4]]},"2796":{"position":[[549,4]]},"2798":{"position":[[749,4]]}}}],["82.26",{"_index":3490,"t":{"481":{"position":[[783,6]]}}}],["827b",{"_index":3291,"t":{"475":{"position":[[5289,4]]}}}],["82e1",{"_index":4739,"t":{"1505":{"position":[[1003,4],[1552,4],[8055,4],[9900,4]]},"2686":{"position":[[1018,4],[1567,4],[8152,4],[10045,4]]}}}],["83.05",{"_index":3493,"t":{"481":{"position":[[854,6]]}}}],["83.33",{"_index":3380,"t":{"477":{"position":[[4625,6]]}}}],["83.34",{"_index":3370,"t":{"477":{"position":[[4280,6]]}}}],["8388608",{"_index":77,"t":{"2":{"position":[[887,7]]}}}],["83ac",{"_index":4369,"t":{"1177":{"position":[[1571,4]]}}}],["84",{"_index":3463,"t":{"479":{"position":[[6082,4]]}}}],["84.80",{"_index":3499,"t":{"481":{"position":[[983,6]]}}}],["8447f007083e\",\"version\":\"0.0.0\",\"ping\":25,\"pong\":tru",{"_index":2951,"t":{"331":{"position":[[874,56]]},"3629":{"position":[[875,56]]}}}],["84674.01",{"_index":3134,"t":{"471":{"position":[[984,8]]}}}],["84e8",{"_index":5043,"t":{"1847":{"position":[[970,4],[1038,4]]},"3076":{"position":[[934,4],[1002,4]]}}}],["85",{"_index":1534,"t":{"94":{"position":[[2870,2],[2964,2]]},"96":{"position":[[155,2]]}}}],["851a",{"_index":4864,"t":{"1597":{"position":[[1372,4]]},"2780":{"position":[[1336,4]]}}}],["851c656e81dc",{"_index":4367,"t":{"1177":{"position":[[1197,13]]},"2228":{"position":[[1197,13]]},"3578":{"position":[[1172,13]]}}}],["87334",{"_index":4308,"t":{"1069":{"position":[[446,5],[522,6]]},"2132":{"position":[[446,5],[522,6]]},"3335":{"position":[[446,5],[522,6]]}}}],["88.94",{"_index":3502,"t":{"481":{"position":[[1050,6]]}}}],["892b",{"_index":3283,"t":{"475":{"position":[[5151,4]]},"477":{"position":[[4131,4]]}}}],["8c31697e956f",{"_index":4507,"t":{"1407":{"position":[[1126,14]]}}}],["8c50",{"_index":4188,"t":{"1017":{"position":[[1886,4]]},"1019":{"position":[[487,4]]},"1021":{"position":[[399,4]]},"1023":{"position":[[1758,4]]},"1025":{"position":[[1773,4]]},"2354":{"position":[[2003,4]]},"2356":{"position":[[487,4]]},"2358":{"position":[[399,4]]},"2360":{"position":[[1758,4]]},"2362":{"position":[[1773,4]]},"2364":{"position":[[1862,4]]},"3415":{"position":[[2003,4]]},"3417":{"position":[[487,4]]},"3419":{"position":[[446,4]]},"3421":{"position":[[1561,4]]},"3423":{"position":[[1773,4]]},"3425":{"position":[[1835,4]]}}}],["8dc40072c78e",{"_index":4735,"t":{"1505":{"position":[[957,14],[1506,14],[8009,14]]},"2686":{"position":[[972,14],[1521,14],[8106,14]]}}}],["8s",{"_index":5197,"t":{"2191":{"position":[[776,6]]},"3466":{"position":[[776,6]]}}}],["9",{"_index":1730,"t":{"110":{"position":[[2469,1],[2489,1],[2509,1]]},"479":{"position":[[5883,2]]},"517":{"position":[[204,1]]},"1407":{"position":[[673,2]]},"1445":{"position":[[227,1]]},"2189":{"position":[[415,2]]},"2712":{"position":[[519,1]]},"3492":{"position":[[415,2]]}}}],["9.0",{"_index":3296,"t":{"475":{"position":[[5384,3]]}}}],["9.67µ",{"_index":3434,"t":{"479":{"position":[[4462,6]]}}}],["9.72µ",{"_index":3433,"t":{"479":{"position":[[4450,6]]}}}],["9.7µ",{"_index":3260,"t":{"475":{"position":[[4743,5]]},"477":{"position":[[3725,5]]}}}],["90",{"_index":3468,"t":{"479":{"position":[[6191,4]]},"2082":{"position":[[42,3]]},"2086":{"position":[[904,3]]},"3267":{"position":[[42,3]]},"3271":{"position":[[870,3]]}}}],["9000",{"_index":2299,"t":{"198":{"position":[[149,5],[776,5],[1744,4]]},"250":{"position":[[752,5]]},"918":{"position":[[750,5]]},"926":{"position":[[971,5]]},"2039":{"position":[[965,5]]},"2047":{"position":[[971,5]]},"3230":{"position":[[965,5]]},"3240":{"position":[[1061,5]]}}}],["9000:9000",{"_index":2325,"t":{"198":{"position":[[1652,9]]},"680":{"position":[[588,9]]},"1617":{"position":[[718,9]]},"2802":{"position":[[718,9]]}}}],["907b",{"_index":3290,"t":{"475":{"position":[[5279,4]]},"477":{"position":[[4259,4]]}}}],["91b",{"_index":3360,"t":{"477":{"position":[[4010,3]]}}}],["9230f514",{"_index":4363,"t":{"1177":{"position":[[1173,8]]},"2228":{"position":[[1173,8]]},"3578":{"position":[[1148,8]]}}}],["92714",{"_index":3849,"t":{"680":{"position":[[1233,5]]},"1617":{"position":[[1402,5]]},"2802":{"position":[[1402,5]]}}}],["928",{"_index":1096,"t":{"50":{"position":[[1126,3],[1186,3]]}}}],["93fc",{"_index":5207,"t":{"2260":{"position":[[1310,4]]},"3610":{"position":[[1310,4]]}}}],["95",{"_index":1835,"t":{"124":{"position":[[2998,3]]},"479":{"position":[[2850,2],[3123,2]]}}}],["9733d7f7b61b\",\"version\":\"dev\",\"subs\":{\"#user12\":{}}}}nullnullnullnullnull{\"channel\":\"#user12\",\"data\":{\"data\":{\"input",{"_index":4372,"t":{"1177":{"position":[[1586,118]]}}}],["9750h",{"_index":1083,"t":{"50":{"position":[[705,5],[1075,5]]}}}],["9759",{"_index":4724,"t":{"1505":{"position":[[838,4],[1372,4],[7875,4]]},"2686":{"position":[[853,4],[1387,4],[7972,4]]}}}],["979f",{"_index":4729,"t":{"1505":{"position":[[896,4],[1445,4],[1876,4],[7948,4]]},"2686":{"position":[[911,4],[1460,4],[1891,4],[8045,4]]}}}],["99",{"_index":44,"t":{"2":{"position":[[566,2],[1884,2]]},"84":{"position":[[809,2]]},"479":{"position":[[2853,2],[3126,2]]},"953":{"position":[[328,4]]},"1923":{"position":[[328,4]]},"3150":{"position":[[328,4]]}}}],["999",{"_index":4231,"t":{"1027":{"position":[[221,3]]}}}],["9_",{"_index":2276,"t":{"194":{"position":[[2395,2]]},"200":{"position":[[483,2]]}}}],["9_.]{2",{"_index":4261,"t":{"1045":{"position":[[1221,9]]},"1051":{"position":[[670,9]]},"2384":{"position":[[1221,9]]},"2390":{"position":[[670,9]]},"3445":{"position":[[1221,9]]},"3451":{"position":[[670,9]]}}}],["9_]{2",{"_index":4006,"t":{"858":{"position":[[607,10]]},"1843":{"position":[[607,10]]},"3072":{"position":[[895,10]]}}}],["9aa7",{"_index":2950,"t":{"331":{"position":[[869,4]]},"3629":{"position":[[870,4]]}}}],["9d65",{"_index":4344,"t":{"1147":{"position":[[1060,4]]}}}],["9def",{"_index":4506,"t":{"1407":{"position":[[1121,4]]}}}],["9fac",{"_index":5037,"t":{"1847":{"position":[[829,4],[897,4]]},"3076":{"position":[[793,4],[861,4]]}}}],["9ff3",{"_index":4795,"t":{"1505":{"position":[[7319,4]]},"2686":{"position":[[7399,4]]}}}],["_",{"_index":979,"t":{"34":{"position":[[1200,2]]},"106":{"position":[[554,1]]},"110":{"position":[[4464,2]]},"112":{"position":[[596,2]]},"469":{"position":[[844,2]]},"471":{"position":[[2553,2]]},"672":{"position":[[864,1]]},"878":{"position":[[1004,1]]},"971":{"position":[[1762,2]]},"1603":{"position":[[952,1]]},"1981":{"position":[[1762,2]]},"1991":{"position":[[1004,1]]},"2622":{"position":[[2137,1],[2256,1],[2376,1]]},"2632":{"position":[[1889,1],[2012,1],[2117,1]]},"2636":{"position":[[1669,1]]},"2788":{"position":[[952,1]]},"3140":{"position":[[1762,2]]},"3180":{"position":[[1004,1]]},"3367":{"position":[[2938,1],[3278,1],[3362,1],[3511,1]]},"3369":{"position":[[1956,1],[2039,1],[2202,1],[2348,1]]},"3633":{"position":[[2137,1],[2256,1],[2376,1]]},"3643":{"position":[[1889,1],[2012,1],[2117,1]]},"3647":{"position":[[1669,1]]}}}],["__dirnam",{"_index":2560,"t":{"246":{"position":[[802,9],[864,9]]}}}],["__init__.pi",{"_index":2124,"t":{"188":{"position":[[321,11]]},"190":{"position":[[246,14],[300,14],[589,14]]},"192":{"position":[[402,14],[1725,14]]},"194":{"position":[[212,14]]}}}],["__main__",{"_index":4211,"t":{"1017":{"position":[[5404,11]]},"2354":{"position":[[5527,11]]},"3415":{"position":[[5556,11]]}}}],["__name__",{"_index":4210,"t":{"1017":{"position":[[5392,8]]},"2354":{"position":[[5515,8]]},"3415":{"position":[[5544,8]]}}}],["_exampl",{"_index":1488,"t":{"88":{"position":[[24,9]]}}}],["a253",{"_index":4722,"t":{"1505":{"position":[[828,4],[1362,4],[7865,4]]},"2686":{"position":[[843,4],[1377,4],[7962,4]]}}}],["a456",{"_index":5694,"t":{"3367":{"position":[[4769,4]]}}}],["a4cc",{"_index":3842,"t":{"680":{"position":[[972,4],[1075,4],[1170,4],[1283,4],[1394,4],[1500,4],[1605,4]]},"1617":{"position":[[1141,4],[1244,4],[1339,4],[1452,4],[1563,4],[1669,4],[1774,4]]},"2802":{"position":[[1141,4],[1244,4],[1339,4],[1452,4],[1563,4],[1669,4],[1774,4]]}}}],["a70c",{"_index":4602,"t":{"1469":{"position":[[550,4],[602,4],[658,4],[710,4]]},"2301":{"position":[[593,4],[645,4],[701,4],[753,4]]},"3514":{"position":[[438,4],[490,4],[546,4],[598,4]]}}}],["a78ae18e31c7",{"_index":4871,"t":{"1597":{"position":[[1497,14]]},"2780":{"position":[[1461,14]]}}}],["a9b2",{"_index":4520,"t":{"1417":{"position":[[303,4],[670,4],[1090,4]]}}}],["ab61c9341ef",{"_index":4796,"t":{"1505":{"position":[[7324,12]]},"2686":{"position":[[7404,12]]}}}],["abc",{"_index":4340,"t":{"1147":{"position":[[999,7],[1377,3]]},"2260":{"position":[[1125,7],[1456,3]]},"3610":{"position":[[1125,7],[1456,3]]}}}],["abil",{"_index":1826,"t":{"124":{"position":[[1087,7]]},"130":{"position":[[1053,7]]},"136":{"position":[[1285,7]]},"148":{"position":[[462,7],[549,7]]},"274":{"position":[[26,7]]},"317":{"position":[[198,7],[1237,7]]},"559":{"position":[[60,7]]},"1593":{"position":[[60,7]]},"1686":{"position":[[39,7]]},"2636":{"position":[[56,7]]},"2776":{"position":[[60,7]]},"2850":{"position":[[39,7]]},"3647":{"position":[[56,7]]}}}],["abli",{"_index":1983,"t":{"158":{"position":[[349,5]]},"264":{"position":[[77,4]]},"2526":{"position":[[1958,5]]}}}],["ably.com",{"_index":895,"t":{"26":{"position":[[1705,8]]}}}],["abnorm",{"_index":3002,"t":{"349":{"position":[[221,8]]},"1281":{"position":[[224,8]]},"2404":{"position":[[236,8]]}}}],["abov",{"_index":140,"t":{"2":{"position":[[2152,5]]},"16":{"position":[[2346,5]]},"36":{"position":[[277,5]]},"38":{"position":[[813,5]]},"42":{"position":[[737,6]]},"66":{"position":[[2664,5]]},"74":{"position":[[402,6],[503,7]]},"78":{"position":[[63,5]]},"86":{"position":[[1173,5]]},"102":{"position":[[162,5]]},"106":{"position":[[1558,6]]},"124":{"position":[[3942,5]]},"176":{"position":[[431,6]]},"216":{"position":[[551,6]]},"258":{"position":[[1416,6]]},"299":{"position":[[43,5],[543,5]]},"321":{"position":[[1819,6]]},"331":{"position":[[1540,5]]},"397":{"position":[[531,5]]},"437":{"position":[[1861,6]]},"439":{"position":[[254,7]]},"447":{"position":[[246,5]]},"471":{"position":[[3892,5],[4045,5]]},"475":{"position":[[6424,5]]},"477":{"position":[[800,6],[2170,5],[5465,6]]},"479":{"position":[[484,5],[1004,6],[4113,5]]},"527":{"position":[[13,6]]},"608":{"position":[[394,5]]},"668":{"position":[[411,6]]},"672":{"position":[[831,6]]},"788":{"position":[[284,6]]},"792":{"position":[[293,5]]},"798":{"position":[[8,5]]},"842":{"position":[[294,6]]},"852":{"position":[[144,5]]},"854":{"position":[[138,5]]},"858":{"position":[[442,6],[1803,6]]},"886":{"position":[[294,5]]},"918":{"position":[[833,5]]},"973":{"position":[[2120,6],[3489,5],[3746,6]]},"1023":{"position":[[992,5]]},"1025":{"position":[[2948,6]]},"1031":{"position":[[359,6]]},"1041":{"position":[[69,6]]},"1043":{"position":[[195,5]]},"1069":{"position":[[615,6]]},"1191":{"position":[[505,6]]},"1199":{"position":[[1197,5]]},"1221":{"position":[[531,5]]},"1263":{"position":[[1865,6]]},"1265":{"position":[[254,7]]},"1299":{"position":[[246,5]]},"1361":{"position":[[238,5],[436,5]]},"1375":{"position":[[244,5]]},"1413":{"position":[[605,5]]},"1423":{"position":[[420,5]]},"1457":{"position":[[243,5]]},"1489":{"position":[[650,7]]},"1495":{"position":[[914,5]]},"1505":{"position":[[3464,5],[10145,6]]},"1509":{"position":[[782,6]]},"1603":{"position":[[919,6]]},"1662":{"position":[[411,6]]},"1668":{"position":[[568,6]]},"1670":{"position":[[1016,6]]},"1672":{"position":[[0,5]]},"1692":{"position":[[13,5]]},"1803":{"position":[[284,6]]},"1807":{"position":[[293,5]]},"1813":{"position":[[8,5]]},"1843":{"position":[[442,6],[1690,6]]},"1859":{"position":[[423,6]]},"1893":{"position":[[144,5]]},"1895":{"position":[[138,5]]},"1897":{"position":[[150,5]]},"1899":{"position":[[1016,5]]},"1983":{"position":[[4633,5],[4891,6]]},"1999":{"position":[[294,5]]},"2039":{"position":[[1048,5]]},"2090":{"position":[[505,6]]},"2100":{"position":[[1197,5]]},"2122":{"position":[[426,7]]},"2132":{"position":[[615,6]]},"2150":{"position":[[903,7]]},"2160":{"position":[[420,5]]},"2289":{"position":[[303,5]]},"2321":{"position":[[650,7]]},"2360":{"position":[[992,5],[4689,6]]},"2362":{"position":[[2948,6]]},"2370":{"position":[[359,6]]},"2380":{"position":[[43,6]]},"2382":{"position":[[195,5]]},"2400":{"position":[[1934,5]]},"2470":{"position":[[531,5]]},"2512":{"position":[[2107,6]]},"2514":{"position":[[254,7]]},"2532":{"position":[[238,5],[436,5]]},"2546":{"position":[[244,5]]},"2586":{"position":[[246,5]]},"2686":{"position":[[3487,6],[10265,6]]},"2690":{"position":[[914,5]]},"2768":{"position":[[782,6]]},"2788":{"position":[[919,6]]},"2832":{"position":[[574,6]]},"2834":{"position":[[1022,6]]},"2836":{"position":[[0,5]]},"2856":{"position":[[13,5]]},"2914":{"position":[[475,6]]},"3032":{"position":[[302,6]]},"3072":{"position":[[1978,6]]},"3090":{"position":[[423,6]]},"3126":{"position":[[144,5]]},"3128":{"position":[[138,5]]},"3130":{"position":[[150,5]]},"3132":{"position":[[165,5]]},"3134":{"position":[[1016,5]]},"3142":{"position":[[4633,5],[4891,6]]},"3188":{"position":[[294,5]]},"3220":{"position":[[1023,6]]},"3230":{"position":[[1048,5]]},"3275":{"position":[[505,6]]},"3285":{"position":[[1197,5]]},"3335":{"position":[[615,6]]},"3345":{"position":[[686,7]]},"3365":{"position":[[1472,5]]},"3369":{"position":[[438,6],[1487,6]]},"3379":{"position":[[903,7]]},"3389":{"position":[[420,5]]},"3411":{"position":[[1419,6]]},"3421":{"position":[[795,5],[4692,6]]},"3423":{"position":[[2948,6]]},"3431":{"position":[[359,6]]},"3441":{"position":[[43,6]]},"3443":{"position":[[195,5]]},"3534":{"position":[[650,7]]},"3538":{"position":[[275,5]]},"3629":{"position":[[1541,5]]}}}],["absolut",{"_index":646,"t":{"16":{"position":[[3037,10]]},"3251":{"position":[[234,10]]}}}],["abstract",{"_index":935,"t":{"32":{"position":[[799,10]]},"62":{"position":[[261,9]]},"78":{"position":[[514,9]]},"108":{"position":[[368,11]]},"176":{"position":[[1757,9]]},"246":{"position":[[1803,9]]},"315":{"position":[[948,8]]},"1092":{"position":[[417,9]]},"2187":{"position":[[420,9]]},"2400":{"position":[[39,9]]},"3464":{"position":[[420,9]]}}}],["abus",{"_index":3620,"t":{"553":{"position":[[561,7]]},"1527":{"position":[[669,7]]},"1672":{"position":[[198,7]]},"2760":{"position":[[670,7]]},"2836":{"position":[[198,7]]}}}],["abv8vxcmzcd556o2f2mnl1uou58gnr",{"_index":5012,"t":{"1767":{"position":[[398,30]]},"2972":{"position":[[398,30]]}}}],["accept",{"_index":1240,"t":{"62":{"position":[[1319,6]]},"64":{"position":[[92,6]]},"106":{"position":[[57,6],[155,7],[173,6],[484,9]]},"108":{"position":[[1335,9],[1423,9]]},"110":{"position":[[415,6],[1188,6],[1432,9],[2200,6],[2299,9]]},"112":{"position":[[23,9],[347,6],[559,9]]},"156":{"position":[[199,6]]},"194":{"position":[[3021,6]]},"200":{"position":[[1974,6]]},"232":{"position":[[1318,10]]},"437":{"position":[[1690,10]]},"473":{"position":[[2318,7]]},"555":{"position":[[654,6]]},"776":{"position":[[520,8]]},"1017":{"position":[[3606,9]]},"1263":{"position":[[1694,10]]},"1457":{"position":[[946,9]]},"1495":{"position":[[982,8]]},"1505":{"position":[[8755,9]]},"1529":{"position":[[703,6]]},"1771":{"position":[[628,9],[1170,8],[1587,9]]},"1773":{"position":[[1492,8]]},"1791":{"position":[[520,8]]},"1839":{"position":[[375,10]]},"2023":{"position":[[178,6]]},"2033":{"position":[[82,6]]},"2179":{"position":[[116,6]]},"2211":{"position":[[119,6]]},"2289":{"position":[[1006,9]]},"2354":{"position":[[3723,9]]},"2512":{"position":[[1939,10]]},"2686":{"position":[[8872,9]]},"2690":{"position":[[982,8]]},"2764":{"position":[[702,6]]},"2930":{"position":[[628,9],[1170,8],[1587,9]]},"2932":{"position":[[1492,8]]},"3020":{"position":[[556,8]]},"3212":{"position":[[151,6]]},"3224":{"position":[[55,6]]},"3415":{"position":[[3723,9]]},"3456":{"position":[[116,6]]},"3486":{"position":[[119,6]]}}}],["acceptstream",{"_index":1769,"t":{"112":{"position":[[312,13]]}}}],["acceptunistream",{"_index":1728,"t":{"110":{"position":[[2252,16]]}}}],["access",{"_index":724,"t":{"16":{"position":[[6740,6]]},"20":{"position":[[1309,6]]},"72":{"position":[[810,6]]},"130":{"position":[[1693,6]]},"172":{"position":[[673,6]]},"208":{"position":[[492,6]]},"214":{"position":[[1087,6]]},"264":{"position":[[1143,6]]},"280":{"position":[[683,6]]},"291":{"position":[[3082,6]]},"301":{"position":[[953,6]]},"315":{"position":[[429,7]]},"323":{"position":[[381,6]]},"636":{"position":[[127,6]]},"640":{"position":[[462,6]]},"668":{"position":[[371,6]]},"690":{"position":[[31,6]]},"706":{"position":[[76,6]]},"716":{"position":[[142,6]]},"726":{"position":[[472,6]]},"774":{"position":[[294,7]]},"794":{"position":[[147,10]]},"818":{"position":[[226,6],[900,7]]},"820":{"position":[[1209,6]]},"828":{"position":[[72,6],[350,6],[386,6]]},"926":{"position":[[695,6]]},"937":{"position":[[181,6]]},"1023":{"position":[[289,6]]},"1431":{"position":[[184,6]]},"1583":{"position":[[155,6],[202,6]]},"1591":{"position":[[744,6]]},"1636":{"position":[[127,6]]},"1662":{"position":[[371,6]]},"1733":{"position":[[31,6]]},"1779":{"position":[[191,6]]},"1781":{"position":[[196,6]]},"1783":{"position":[[255,6]]},"1789":{"position":[[294,7],[392,6]]},"1809":{"position":[[187,10]]},"1837":{"position":[[902,6]]},"1851":{"position":[[342,6]]},"1857":{"position":[[1024,6]]},"1859":{"position":[[779,6]]},"1941":{"position":[[73,6]]},"1951":{"position":[[139,6]]},"1967":{"position":[[1463,9]]},"2031":{"position":[[187,6]]},"2047":{"position":[[695,6]]},"2058":{"position":[[181,6]]},"2360":{"position":[[289,6],[4262,6]]},"2678":{"position":[[155,6],[202,6]]},"2762":{"position":[[237,6]]},"2784":{"position":[[744,6]]},"2823":{"position":[[127,6]]},"2914":{"position":[[429,6]]},"2922":{"position":[[31,6]]},"2938":{"position":[[191,6]]},"2940":{"position":[[196,6]]},"2942":{"position":[[255,6]]},"2986":{"position":[[73,6]]},"2996":{"position":[[139,6]]},"3012":{"position":[[1463,9]]},"3018":{"position":[[282,7]]},"3038":{"position":[[200,10]]},"3066":{"position":[[899,6]]},"3080":{"position":[[342,6]]},"3088":{"position":[[1024,6]]},"3090":{"position":[[779,6]]},"3222":{"position":[[187,6]]},"3240":{"position":[[785,6]]},"3253":{"position":[[181,6]]},"3421":{"position":[[289,6],[494,6],[4265,6]]},"3498":{"position":[[726,6]]}}}],["access_log",{"_index":2305,"t":{"198":{"position":[[736,10]]}}}],["accoci",{"_index":4955,"t":{"1703":{"position":[[562,10]]},"2867":{"position":[[562,10]]}}}],["accommod",{"_index":2897,"t":{"321":{"position":[[649,13]]},"2526":{"position":[[1344,11]]}}}],["accomplish",{"_index":1701,"t":{"110":{"position":[[197,12]]}}}],["accord",{"_index":1008,"t":{"38":{"position":[[914,9],[1074,9]]},"104":{"position":[[545,11]]},"110":{"position":[[0,9],[2365,9]]},"252":{"position":[[322,10]]},"291":{"position":[[957,9]]},"293":{"position":[[1440,9]]},"477":{"position":[[147,9]]},"567":{"position":[[174,9]]},"608":{"position":[[836,9]]},"636":{"position":[[388,10]]},"638":{"position":[[265,10]]},"878":{"position":[[351,9]]},"1487":{"position":[[53,9]]},"1535":{"position":[[174,9]]},"1599":{"position":[[45,9]]},"1636":{"position":[[388,10]]},"1639":{"position":[[265,10]]},"1709":{"position":[[47,9]]},"1983":{"position":[[3876,9]]},"1991":{"position":[[351,9]]},"2189":{"position":[[1069,9]]},"2319":{"position":[[53,9]]},"2574":{"position":[[478,9]]},"2740":{"position":[[174,9]]},"2782":{"position":[[45,9]]},"2823":{"position":[[388,10]]},"2826":{"position":[[265,10]]},"2873":{"position":[[47,9]]},"3142":{"position":[[3876,9]]},"3180":{"position":[[351,9]]},"3367":{"position":[[1383,9]]},"3492":{"position":[[1069,9]]},"3532":{"position":[[53,9]]}}}],["accordingli",{"_index":2510,"t":{"232":{"position":[[442,11]]}}}],["account",{"_index":732,"t":{"16":{"position":[[7165,7]]},"258":{"position":[[1361,7]]},"315":{"position":[[291,7]]},"347":{"position":[[1170,7]]},"437":{"position":[[1837,7]]},"483":{"position":[[1586,7]]},"596":{"position":[[230,8]]},"598":{"position":[[264,8]]},"1025":{"position":[[972,7]]},"1217":{"position":[[397,7]]},"1263":{"position":[[1841,7]]},"1279":{"position":[[1175,7]]},"1369":{"position":[[191,8]]},"1375":{"position":[[266,8]]},"1421":{"position":[[439,8]]},"1668":{"position":[[294,8]]},"2362":{"position":[[972,7]]},"2396":{"position":[[397,7]]},"2402":{"position":[[1241,7]]},"2512":{"position":[[2083,7]]},"2540":{"position":[[191,8]]},"2546":{"position":[[266,8]]},"2832":{"position":[[294,8]]},"3423":{"position":[[972,7]]}}}],["accur",{"_index":3199,"t":{"473":{"position":[[2570,8]]},"1423":{"position":[[1489,8]]},"2160":{"position":[[1489,8]]},"2364":{"position":[[1284,8]]},"3389":{"position":[[1489,8]]},"3425":{"position":[[1257,8]]}}}],["ace2",{"_index":4366,"t":{"1177":{"position":[[1192,4]]},"2228":{"position":[[1192,4]]},"3578":{"position":[[1167,4]]}}}],["achiev",{"_index":101,"t":{"2":{"position":[[1297,7],[1438,9],[2128,8],[2996,7]]},"14":{"position":[[845,7]]},"16":{"position":[[4240,7]]},"50":{"position":[[247,7]]},"60":{"position":[[523,7]]},"68":{"position":[[127,7]]},"78":{"position":[[1481,7],[3234,7]]},"80":{"position":[[1035,9]]},"82":{"position":[[753,9]]},"84":{"position":[[181,8]]},"126":{"position":[[482,7]]},"130":{"position":[[1788,8]]},"136":{"position":[[523,7]]},"198":{"position":[[587,7]]},"224":{"position":[[193,7]]},"240":{"position":[[387,7]]},"262":{"position":[[1736,7]]},"280":{"position":[[4066,7]]},"285":{"position":[[335,7]]},"305":{"position":[[469,7]]},"349":{"position":[[143,7]]},"431":{"position":[[26,7]]},"465":{"position":[[1203,8]]},"479":{"position":[[3164,7],[3731,7]]},"483":{"position":[[971,7]]},"1013":{"position":[[161,9]]},"1199":{"position":[[59,8]]},"1257":{"position":[[26,7]]},"1281":{"position":[[146,7]]},"1505":{"position":[[8329,7]]},"1515":{"position":[[53,8]]},"1519":{"position":[[500,9]]},"1676":{"position":[[136,8]]},"1680":{"position":[[312,8]]},"1827":{"position":[[311,7]]},"1839":{"position":[[33,7]]},"2100":{"position":[[59,8]]},"2350":{"position":[[161,9]]},"2400":{"position":[[2364,7]]},"2404":{"position":[[149,7]]},"2506":{"position":[[26,7]]},"2686":{"position":[[8432,7]]},"2702":{"position":[[500,9]]},"2708":{"position":[[1662,9]]},"2774":{"position":[[53,8]]},"2840":{"position":[[136,8]]},"2844":{"position":[[312,8]]},"3056":{"position":[[277,7]]},"3285":{"position":[[59,8]]},"3363":{"position":[[1202,7]]},"3411":{"position":[[161,9]]},"3456":{"position":[[637,9]]},"3486":{"position":[[617,9]]},"3538":{"position":[[2166,7]]}}}],["ack",{"_index":2719,"t":{"270":{"position":[[2905,3]]},"425":{"position":[[635,3],[807,3]]},"427":{"position":[[140,3]]},"1251":{"position":[[712,3],[884,3]]},"1253":{"position":[[140,3]]},"2500":{"position":[[712,3],[884,3]]},"2502":{"position":[[140,3]]}}}],["acknowledg",{"_index":1694,"t":{"108":{"position":[[1215,17]]}}}],["acl",{"_index":4378,"t":{"1189":{"position":[[262,3]]},"1193":{"position":[[623,3]]},"2086":{"position":[[228,3]]},"2092":{"position":[[598,3]]},"3271":{"position":[[228,3]]},"3277":{"position":[[598,3]]}}}],["acm",{"_index":1926,"t":{"142":{"position":[[498,4]]},"1057":{"position":[[397,4],[776,4],[912,4],[1024,4]]},"1247":{"position":[[675,4]]},"2140":{"position":[[397,4],[776,4],[912,4],[1024,4]]},"2496":{"position":[[675,4]]},"3355":{"position":[[397,4],[776,4],[912,4],[1024,4]]}}}],["acquir",{"_index":3128,"t":{"471":{"position":[[94,8]]}}}],["act",{"_index":4233,"t":{"1031":{"position":[[112,4],[151,4]]},"2021":{"position":[[161,4]]},"2370":{"position":[[112,4],[151,4]]},"2400":{"position":[[206,4]]},"3060":{"position":[[86,4]]},"3066":{"position":[[49,4]]},"3072":{"position":[[373,4]]},"3210":{"position":[[134,4]]},"3431":{"position":[[112,4],[151,4]]}}}],["action",{"_index":1045,"t":{"42":{"position":[[1079,7]]},"66":{"position":[[2317,7]]},"174":{"position":[[37,7]]},"262":{"position":[[745,7],[849,6]]},"499":{"position":[[236,7]]},"686":{"position":[[168,6]]},"1393":{"position":[[236,7]]},"1425":{"position":[[1519,7]]},"1505":{"position":[[9265,7],[9297,7]]},"1729":{"position":[[168,6]]},"2392":{"position":[[1352,7]]},"2564":{"position":[[244,7]]},"2634":{"position":[[725,6]]},"2686":{"position":[[9404,7],[9440,7]]},"2712":{"position":[[735,6],[801,7],[898,6],[1096,6],[1153,6]]},"2718":{"position":[[248,7]]},"2720":{"position":[[70,6]]},"2918":{"position":[[168,6]]},"3324":{"position":[[613,6],[662,7],[730,8]]},"3363":{"position":[[759,6]]},"3612":{"position":[[1379,7]]},"3645":{"position":[[725,6]]}}}],["action=\"/login",{"_index":2568,"t":{"246":{"position":[[1049,15]]}}}],["activ",{"_index":115,"t":{"2":{"position":[[1624,6]]},"6":{"position":[[169,6]]},"8":{"position":[[463,8],[1100,8]]},"16":{"position":[[1070,6],[5898,6]]},"18":{"position":[[132,6],[3304,6]]},"20":{"position":[[778,6]]},"76":{"position":[[137,6],[1177,6],[1600,6],[1854,6],[2018,6]]},"78":{"position":[[1362,6],[3449,6]]},"128":{"position":[[676,6]]},"136":{"position":[[1075,7]]},"138":{"position":[[869,6]]},"150":{"position":[[213,7]]},"186":{"position":[[381,8]]},"218":{"position":[[194,6]]},"226":{"position":[[497,6]]},"260":{"position":[[845,8]]},"272":{"position":[[1298,6]]},"280":{"position":[[2522,8],[3557,6]]},"291":{"position":[[2537,7]]},"341":{"position":[[1185,6],[1627,6]]},"355":{"position":[[425,6],[566,6]]},"375":{"position":[[142,6]]},"381":{"position":[[100,6]]},"393":{"position":[[513,6],[552,6],[677,8]]},"441":{"position":[[71,6],[120,6],[529,6],[898,6]]},"463":{"position":[[171,6]]},"467":{"position":[[1210,6]]},"471":{"position":[[625,6]]},"473":{"position":[[3559,8]]},"553":{"position":[[326,8],[476,6]]},"606":{"position":[[323,6]]},"636":{"position":[[250,6]]},"686":{"position":[[192,6],[457,6],[539,6]]},"690":{"position":[[676,6],[865,6],[1030,7],[1402,6],[1426,6]]},"694":{"position":[[16,6],[683,6]]},"776":{"position":[[309,8]]},"834":{"position":[[438,6]]},"1211":{"position":[[1185,6],[1627,6]]},"1217":{"position":[[112,7]]},"1267":{"position":[[71,6],[120,6],[529,6],[898,6]]},"1287":{"position":[[425,6],[566,6]]},"1315":{"position":[[171,6]]},"1339":{"position":[[64,6]]},"1425":{"position":[[2288,6]]},"1477":{"position":[[16,6],[50,6],[607,6],[940,6]]},"1527":{"position":[[528,8],[612,6]]},"1599":{"position":[[26,6],[336,6]]},"1636":{"position":[[250,6]]},"1682":{"position":[[1371,6]]},"1700":{"position":[[538,6]]},"1729":{"position":[[192,6],[452,6],[534,6]]},"1733":{"position":[[676,6],[865,6],[1030,7],[1402,6],[1426,6]]},"1737":{"position":[[665,6]]},"1791":{"position":[[309,8]]},"1849":{"position":[[821,6]]},"2122":{"position":[[511,8]]},"2124":{"position":[[400,8]]},"2126":{"position":[[139,9]]},"2309":{"position":[[16,6],[50,6],[654,6],[982,6]]},"2394":{"position":[[1185,6],[1627,6]]},"2396":{"position":[[117,7]]},"2400":{"position":[[1174,6],[2022,6],[2081,6]]},"2410":{"position":[[430,6],[571,6]]},"2446":{"position":[[277,6]]},"2454":{"position":[[68,6]]},"2512":{"position":[[1386,6]]},"2516":{"position":[[71,6],[120,6],[529,6],[898,6]]},"2602":{"position":[[171,6]]},"2658":{"position":[[171,6]]},"2760":{"position":[[529,8],[613,6]]},"2782":{"position":[[26,6],[336,6]]},"2823":{"position":[[250,6]]},"2846":{"position":[[1371,6]]},"2918":{"position":[[192,6],[452,6],[534,6]]},"2922":{"position":[[640,6],[829,6],[994,7],[1366,6],[1390,6]]},"2926":{"position":[[665,6]]},"3042":{"position":[[229,8]]},"3068":{"position":[[442,10]]},"3078":{"position":[[821,6]]},"3324":{"position":[[1224,6],[1911,8]]},"3345":{"position":[[771,8],[903,6]]},"3347":{"position":[[400,8]]},"3349":{"position":[[139,9]]},"3363":{"position":[[520,6]]},"3506":{"position":[[29,6]]},"3522":{"position":[[16,6],[50,6],[714,6],[1042,6]]},"3669":{"position":[[171,6]]}}}],["active\":1627107289",{"_index":3869,"t":{"690":{"position":[[435,20]]},"1733":{"position":[[435,20]]},"2922":{"position":[[399,20]]}}}],["actual",{"_index":134,"t":{"2":{"position":[[2031,8]]},"8":{"position":[[560,8]]},"10":{"position":[[1589,8]]},"12":{"position":[[892,8]]},"14":{"position":[[92,8]]},"16":{"position":[[290,8],[1267,8],[1890,9],[5520,9]]},"18":{"position":[[444,9]]},"20":{"position":[[344,7],[456,6],[591,6],[1496,8]]},"46":{"position":[[101,10]]},"48":{"position":[[109,11]]},"50":{"position":[[178,8]]},"66":{"position":[[2012,9]]},"74":{"position":[[1996,6]]},"82":{"position":[[1850,8]]},"92":{"position":[[74,6]]},"94":{"position":[[1506,9],[2722,9]]},"112":{"position":[[254,9]]},"116":{"position":[[489,8]]},"130":{"position":[[1526,8]]},"132":{"position":[[959,8]]},"134":{"position":[[901,8]]},"186":{"position":[[690,9]]},"194":{"position":[[3314,9]]},"216":{"position":[[2086,8]]},"232":{"position":[[749,8],[1175,6]]},"260":{"position":[[51,9]]},"280":{"position":[[2940,8]]},"285":{"position":[[590,8]]},"315":{"position":[[349,9],[1386,6],[1611,6]]},"321":{"position":[[1322,6]]},"347":{"position":[[96,6]]},"475":{"position":[[6722,9]]},"479":{"position":[[379,11],[2518,8]]},"505":{"position":[[41,8]]},"726":{"position":[[426,8]]},"776":{"position":[[549,6]]},"798":{"position":[[1512,6]]},"951":{"position":[[555,8]]},"973":{"position":[[328,6],[3534,6]]},"1023":{"position":[[643,9]]},"1183":{"position":[[347,9]]},"1279":{"position":[[96,6]]},"1365":{"position":[[110,8]]},"1399":{"position":[[41,8]]},"1411":{"position":[[237,8]]},"1485":{"position":[[360,8]]},"1692":{"position":[[787,9]]},"1791":{"position":[[549,6]]},"1813":{"position":[[1512,6]]},"1921":{"position":[[555,8]]},"1963":{"position":[[308,6]]},"1983":{"position":[[328,6],[3417,8],[4678,6]]},"2080":{"position":[[347,9]]},"2317":{"position":[[360,8]]},"2360":{"position":[[643,9],[4509,9]]},"2402":{"position":[[100,6]]},"2424":{"position":[[468,8]]},"2536":{"position":[[110,8]]},"2570":{"position":[[45,8]]},"2632":{"position":[[682,6]]},"2856":{"position":[[787,9]]},"3008":{"position":[[308,6]]},"3142":{"position":[[328,6],[3417,8],[4678,6]]},"3148":{"position":[[555,8]]},"3265":{"position":[[347,9]]},"3421":{"position":[[4512,9]]},"3530":{"position":[[360,8]]},"3643":{"position":[[682,6]]}}}],["ad",{"_index":1006,"t":{"38":{"position":[[724,5],[1760,6]]},"76":{"position":[[1413,6]]},"88":{"position":[[5,6]]},"120":{"position":[[374,5]]},"136":{"position":[[212,6]]},"144":{"position":[[455,5]]},"216":{"position":[[3012,5]]},"238":{"position":[[304,6]]},"270":{"position":[[1137,5]]},"272":{"position":[[1220,5]]},"278":{"position":[[654,5]]},"411":{"position":[[575,6],[756,5],[1258,6]]},"479":{"position":[[3994,6],[4128,5],[5622,5]]},"632":{"position":[[470,6]]},"652":{"position":[[366,6]]},"969":{"position":[[1026,5]]},"989":{"position":[[402,5]]},"1039":{"position":[[467,5]]},"1199":{"position":[[963,6]]},"1235":{"position":[[575,6],[756,5],[1258,6]]},"1247":{"position":[[14,5]]},"1367":{"position":[[401,6]]},"1455":{"position":[[91,5]]},"1457":{"position":[[2068,6]]},"1459":{"position":[[405,6]]},"1505":{"position":[[1253,6]]},"1631":{"position":[[470,6]]},"1647":{"position":[[366,6]]},"1682":{"position":[[1293,5]]},"1684":{"position":[[454,5]]},"1929":{"position":[[25,6],[506,6]]},"1979":{"position":[[1092,5]]},"2021":{"position":[[0,5]]},"2023":{"position":[[0,5]]},"2033":{"position":[[0,5]]},"2088":{"position":[[377,5],[573,5],[768,5],[1001,5]]},"2094":{"position":[[380,5],[594,5],[807,5],[1058,5]]},"2100":{"position":[[963,6]]},"2287":{"position":[[94,5]]},"2289":{"position":[[2122,6]]},"2291":{"position":[[405,6]]},"2364":{"position":[[0,5]]},"2378":{"position":[[467,5]]},"2484":{"position":[[575,6],[756,5],[1258,6]]},"2496":{"position":[[14,5]]},"2512":{"position":[[262,6]]},"2538":{"position":[[401,6]]},"2686":{"position":[[1268,6]]},"2818":{"position":[[470,6]]},"2846":{"position":[[1293,5]]},"2848":{"position":[[454,5]]},"2899":{"position":[[366,6]]},"3138":{"position":[[1092,5]]},"3156":{"position":[[25,6],[506,6]]},"3285":{"position":[[963,6]]},"3363":{"position":[[858,6]]},"3439":{"position":[[467,5]]},"3498":{"position":[[94,5]]},"3502":{"position":[[1630,6]]},"3504":{"position":[[480,6]]},"3538":{"position":[[1841,6]]},"3612":{"position":[[2209,7]]}}}],["adad13b1",{"_index":4604,"t":{"1469":{"position":[[638,9],[690,9]]},"2301":{"position":[[681,9],[733,9]]},"3514":{"position":[[526,9],[578,9]]}}}],["adapt",{"_index":913,"t":{"28":{"position":[[800,5]]},"78":{"position":[[3516,7]]},"90":{"position":[[655,7]]}}}],["add",{"_index":482,"t":{"12":{"position":[[209,4]]},"78":{"position":[[430,3]]},"86":{"position":[[111,3]]},"144":{"position":[[192,3]]},"166":{"position":[[443,3]]},"174":{"position":[[811,3]]},"176":{"position":[[2049,3],[2100,3]]},"178":{"position":[[180,3],[437,3]]},"190":{"position":[[710,3]]},"192":{"position":[[2053,3]]},"198":{"position":[[615,3],[1664,3]]},"202":{"position":[[325,3]]},"216":{"position":[[558,3]]},"224":{"position":[[64,3],[206,3]]},"246":{"position":[[2749,3]]},"268":{"position":[[360,3]]},"272":{"position":[[488,3]]},"274":{"position":[[384,3]]},"278":{"position":[[793,3]]},"280":{"position":[[4096,3]]},"321":{"position":[[139,3]]},"357":{"position":[[1006,3],[1081,3]]},"473":{"position":[[2618,4]]},"475":{"position":[[3691,3]]},"477":{"position":[[5480,3]]},"481":{"position":[[109,3],[533,3]]},"535":{"position":[[194,3]]},"557":{"position":[[249,3]]},"608":{"position":[[127,3]]},"672":{"position":[[38,3]]},"830":{"position":[[383,4]]},"836":{"position":[[653,4]]},"912":{"position":[[844,4]]},"922":{"position":[[112,3]]},"1017":{"position":[[3291,3]]},"1033":{"position":[[288,3]]},"1045":{"position":[[1949,3]]},"1047":{"position":[[448,3]]},"1057":{"position":[[46,3]]},"1071":{"position":[[1046,3]]},"1193":{"position":[[40,3]]},"1199":{"position":[[611,3]]},"1289":{"position":[[1006,3],[1081,3]]},"1359":{"position":[[1104,3]]},"1367":{"position":[[175,3]]},"1455":{"position":[[412,3]]},"1505":{"position":[[8804,3]]},"1531":{"position":[[272,3]]},"1603":{"position":[[38,3]]},"1619":{"position":[[664,3]]},"1670":{"position":[[378,4]]},"1682":{"position":[[662,3]]},"1688":{"position":[[0,3]]},"1711":{"position":[[143,3]]},"1715":{"position":[[265,3]]},"1847":{"position":[[1397,4]]},"1853":{"position":[[653,4]]},"2027":{"position":[[844,4]]},"2043":{"position":[[112,3]]},"2092":{"position":[[40,3]]},"2100":{"position":[[611,3]]},"2134":{"position":[[1058,3]]},"2140":{"position":[[46,3]]},"2287":{"position":[[415,3]]},"2354":{"position":[[3408,3]]},"2372":{"position":[[288,3]]},"2384":{"position":[[1949,3]]},"2386":{"position":[[448,3]]},"2412":{"position":[[1021,3],[1100,3]]},"2530":{"position":[[1104,3]]},"2538":{"position":[[175,3]]},"2686":{"position":[[8922,3]]},"2736":{"position":[[363,3]]},"2788":{"position":[[38,3]]},"2804":{"position":[[664,3]]},"2834":{"position":[[378,4]]},"2846":{"position":[[662,3]]},"2852":{"position":[[0,3]]},"2869":{"position":[[714,4]]},"2875":{"position":[[143,3]]},"2879":{"position":[[265,3]]},"3076":{"position":[[1361,4]]},"3082":{"position":[[653,4]]},"3216":{"position":[[844,4]]},"3234":{"position":[[112,3]]},"3277":{"position":[[40,3]]},"3285":{"position":[[611,3]]},"3337":{"position":[[1058,3]]},"3355":{"position":[[46,3]]},"3369":{"position":[[675,3]]},"3415":{"position":[[3408,3]]},"3433":{"position":[[288,3]]},"3445":{"position":[[1614,3],[2161,3]]},"3447":{"position":[[448,3]]},"3498":{"position":[[324,3]]}}}],["addeventlistener(\"unload",{"_index":5776,"t":{"3612":{"position":[[2217,26]]}}}],["addit",{"_index":578,"t":{"14":{"position":[[1694,10]]},"26":{"position":[[709,9],[940,8],[1316,8]]},"40":{"position":[[131,8]]},"52":{"position":[[778,10]]},"60":{"position":[[1267,10]]},"88":{"position":[[212,10]]},"100":{"position":[[552,10]]},"116":{"position":[[634,10]]},"118":{"position":[[1153,9]]},"122":{"position":[[138,10]]},"124":{"position":[[1968,10]]},"126":{"position":[[819,10]]},"130":{"position":[[477,10]]},"138":{"position":[[8,8],[224,10]]},"140":{"position":[[94,10]]},"158":{"position":[[483,10]]},"172":{"position":[[1468,10]]},"202":{"position":[[1097,10]]},"212":{"position":[[557,10]]},"216":{"position":[[184,10]]},"222":{"position":[[620,10]]},"224":{"position":[[72,10]]},"272":{"position":[[384,10]]},"280":{"position":[[2044,10]]},"287":{"position":[[1311,10]]},"291":{"position":[[2566,10]]},"295":{"position":[[1906,10]]},"301":{"position":[[174,10]]},"311":{"position":[[1160,9]]},"321":{"position":[[147,10]]},"323":{"position":[[4,10]]},"325":{"position":[[13,8]]},"339":{"position":[[265,10]]},"347":{"position":[[634,8]]},"349":{"position":[[843,10]]},"357":{"position":[[1010,10]]},"393":{"position":[[798,10]]},"409":{"position":[[1254,10]]},"429":{"position":[[222,10]]},"473":{"position":[[2863,10]]},"479":{"position":[[1704,10],[6848,8]]},"481":{"position":[[410,10],[1303,10]]},"553":{"position":[[502,10]]},"555":{"position":[[468,10]]},"559":{"position":[[27,8]]},"606":{"position":[[112,10]]},"610":{"position":[[77,10]]},"632":{"position":[[448,10]]},"640":{"position":[[40,10]]},"652":{"position":[[344,10]]},"786":{"position":[[33,10]]},"794":{"position":[[11,10]]},"818":{"position":[[425,10],[728,10]]},"830":{"position":[[440,10]]},"836":{"position":[[732,10]]},"858":{"position":[[1077,10]]},"993":{"position":[[10,10]]},"995":{"position":[[10,10]]},"1017":{"position":[[1593,10]]},"1033":{"position":[[5,10]]},"1041":{"position":[[881,8]]},"1201":{"position":[[125,10]]},"1233":{"position":[[1254,10]]},"1255":{"position":[[222,10]]},"1279":{"position":[[634,8]]},"1281":{"position":[[863,10]]},"1289":{"position":[[1010,10]]},"1351":{"position":[[128,10]]},"1363":{"position":[[194,10]]},"1495":{"position":[[239,10]]},"1515":{"position":[[0,10]]},"1529":{"position":[[468,10]]},"1591":{"position":[[40,10]]},"1593":{"position":[[27,8]]},"1631":{"position":[[448,10]]},"1647":{"position":[[344,10]]},"1666":{"position":[[414,9]]},"1668":{"position":[[584,10]]},"1682":{"position":[[381,10]]},"1703":{"position":[[689,10],[774,10]]},"1747":{"position":[[10,10]]},"1749":{"position":[[10,10]]},"1801":{"position":[[33,10]]},"1809":{"position":[[11,10]]},"1827":{"position":[[348,10]]},"1843":{"position":[[1108,10]]},"1847":{"position":[[1454,10]]},"1853":{"position":[[732,10]]},"1855":{"position":[[1251,10]]},"1929":{"position":[[32,10],[231,10]]},"2102":{"position":[[125,10]]},"2124":{"position":[[116,8]]},"2354":{"position":[[1710,10]]},"2372":{"position":[[5,10]]},"2380":{"position":[[855,8]]},"2402":{"position":[[671,8]]},"2404":{"position":[[901,10]]},"2412":{"position":[[1028,10]]},"2442":{"position":[[403,8]]},"2466":{"position":[[131,10]]},"2482":{"position":[[1254,10]]},"2504":{"position":[[222,10]]},"2534":{"position":[[194,10]]},"2690":{"position":[[239,10]]},"2760":{"position":[[1432,10]]},"2764":{"position":[[467,10]]},"2774":{"position":[[0,10]]},"2776":{"position":[[27,8]]},"2784":{"position":[[40,10]]},"2818":{"position":[[448,10]]},"2830":{"position":[[417,9]]},"2832":{"position":[[590,10]]},"2846":{"position":[[381,10]]},"2862":{"position":[[353,10]]},"2867":{"position":[[689,10]]},"2899":{"position":[[344,10]]},"2952":{"position":[[10,10]]},"2954":{"position":[[10,10]]},"3030":{"position":[[29,10]]},"3036":{"position":[[204,10]]},"3038":{"position":[[11,10]]},"3056":{"position":[[314,10]]},"3072":{"position":[[1396,10]]},"3076":{"position":[[1418,10]]},"3082":{"position":[[732,10]]},"3084":{"position":[[1215,10]]},"3156":{"position":[[32,10],[231,10]]},"3220":{"position":[[930,10]]},"3347":{"position":[[116,8]]},"3363":{"position":[[157,10]]},"3367":{"position":[[103,10]]},"3369":{"position":[[3,8]]},"3415":{"position":[[1710,10]]},"3433":{"position":[[5,10]]},"3441":{"position":[[855,8]]},"3502":{"position":[[455,10]]}}}],["addition",{"_index":2372,"t":{"210":{"position":[[91,12]]},"214":{"position":[[885,12]]},"270":{"position":[[1590,12]]},"272":{"position":[[564,12]]},"293":{"position":[[977,12]]},"321":{"position":[[485,12]]},"473":{"position":[[265,12],[1320,12]]},"608":{"position":[[369,12]]},"1585":{"position":[[279,12]]},"1680":{"position":[[0,13]]},"1682":{"position":[[738,12]]},"1771":{"position":[[679,12],[2142,12]]},"1861":{"position":[[148,12]]},"2442":{"position":[[335,13]]},"2646":{"position":[[1814,13]]},"2680":{"position":[[279,12]]},"2844":{"position":[[0,13]]},"2846":{"position":[[738,12]]},"2930":{"position":[[679,12],[2142,12]]},"3092":{"position":[[148,12]]},"3365":{"position":[[1767,12]]},"3657":{"position":[[1814,13]]}}}],["addmessage(ctx.data",{"_index":2089,"t":{"176":{"position":[[1560,21]]}}}],["addpresence(ch",{"_index":1422,"t":{"78":{"position":[[1800,14]]}}}],["addr",{"_index":3221,"t":{"475":{"position":[[1888,6]]}}}],["address",{"_index":898,"t":{"26":{"position":[[1866,9]]},"98":{"position":[[1957,7]]},"104":{"position":[[206,7]]},"124":{"position":[[4178,8]]},"196":{"position":[[1137,7]]},"240":{"position":[[654,10]]},"280":{"position":[[2778,7]]},"321":{"position":[[832,9],[908,10]]},"329":{"position":[[496,10]]},"491":{"position":[[169,8],[198,7]]},"563":{"position":[[1261,7]]},"604":{"position":[[20,9]]},"672":{"position":[[943,9]]},"694":{"position":[[484,7]]},"810":{"position":[[297,9]]},"894":{"position":[[45,7]]},"918":{"position":[[823,9]]},"926":{"position":[[1013,8]]},"945":{"position":[[160,10]]},"953":{"position":[[357,8]]},"1057":{"position":[[482,8],[768,7],[995,7]]},"1077":{"position":[[774,7],[961,7]]},"1120":{"position":[[46,7]]},"1189":{"position":[[118,8]]},"1193":{"position":[[281,9]]},"1195":{"position":[[253,7]]},"1197":{"position":[[900,7]]},"1205":{"position":[[88,7],[257,8]]},"1385":{"position":[[236,8],[265,7]]},"1505":{"position":[[3569,7]]},"1603":{"position":[[1031,9]]},"1692":{"position":[[285,7]]},"1737":{"position":[[474,7]]},"1825":{"position":[[297,9]]},"1923":{"position":[[357,8]]},"1929":{"position":[[412,8]]},"2007":{"position":[[45,7]]},"2039":{"position":[[1038,9]]},"2047":{"position":[[1013,8]]},"2066":{"position":[[160,10]]},"2086":{"position":[[118,8]]},"2088":{"position":[[1199,8]]},"2092":{"position":[[281,9]]},"2094":{"position":[[1256,8]]},"2096":{"position":[[253,7]]},"2098":{"position":[[900,7]]},"2108":{"position":[[88,7],[257,8]]},"2140":{"position":[[482,8],[768,7],[995,7]]},"2197":{"position":[[774,7],[961,7]]},"2271":{"position":[[46,7]]},"2512":{"position":[[1155,10]]},"2556":{"position":[[257,8],[286,7]]},"2686":{"position":[[3602,7]]},"2762":{"position":[[703,8]]},"2788":{"position":[[1031,9]]},"2856":{"position":[[285,7]]},"2926":{"position":[[474,7]]},"3054":{"position":[[297,9]]},"3150":{"position":[[357,8]]},"3156":{"position":[[412,8]]},"3196":{"position":[[45,7]]},"3230":{"position":[[1038,9]]},"3240":{"position":[[1103,8]]},"3261":{"position":[[160,10]]},"3271":{"position":[[118,8]]},"3273":{"position":[[1091,8]]},"3277":{"position":[[281,9]]},"3279":{"position":[[1148,8]]},"3281":{"position":[[253,7]]},"3283":{"position":[[900,7]]},"3291":{"position":[[88,7],[257,8]]},"3355":{"position":[[482,8],[768,7],[995,7]]},"3472":{"position":[[774,7],[961,7]]},"3553":{"position":[[46,7]]}}}],["addroomlastmessage(ctx.data);});centrifuge.connect",{"_index":2091,"t":{"176":{"position":[[1607,53]]}}}],["adit",{"_index":1824,"t":{"124":{"position":[[843,9]]}}}],["adjust",{"_index":2866,"t":{"311":{"position":[[103,9]]},"315":{"position":[[24,9]]},"2574":{"position":[[37,8],[491,8]]}}}],["admin",{"_index":1043,"t":{"42":{"position":[[979,5]]},"188":{"position":[[157,5]]},"192":{"position":[[2735,6]]},"214":{"position":[[1027,5]]},"248":{"position":[[92,8]]},"252":{"position":[[922,5]]},"283":{"position":[[605,5]]},"323":{"position":[[711,6]]},"331":{"position":[[1352,5]]},"505":{"position":[[134,5]]},"513":{"position":[[160,8]]},"533":{"position":[[42,5]]},"559":{"position":[[117,5]]},"624":{"position":[[32,5],[75,5],[331,5]]},"664":{"position":[[0,5],[51,5],[134,5]]},"666":{"position":[[113,8]]},"668":{"position":[[54,5],[286,5],[381,5],[437,5],[468,8]]},"920":{"position":[[0,5],[104,5],[131,5]]},"926":{"position":[[38,6],[206,5],[980,5]]},"930":{"position":[[153,5]]},"939":{"position":[[124,5],[214,5],[307,5]]},"1147":{"position":[[1428,5]]},"1293":{"position":[[662,5]]},"1343":{"position":[[9,5]]},"1399":{"position":[[134,5]]},"1441":{"position":[[160,8]]},"1495":{"position":[[392,8],[418,8],[847,10],[890,5]]},"1497":{"position":[[658,10],[1008,5],[1062,5],[1199,7],[1642,7]]},"1505":{"position":[[1112,5],[1169,5],[1200,5],[1235,5],[1260,8],[1392,8],[1757,5],[1944,5],[7895,8],[9152,5],[9191,5]]},"1527":{"position":[[1357,5]]},"1593":{"position":[[117,5]]},"1623":{"position":[[32,5],[75,5],[331,5]]},"1658":{"position":[[0,5],[51,5],[134,5]]},"1660":{"position":[[113,8]]},"1662":{"position":[[54,5],[286,5],[381,5],[437,5],[468,8]]},"2041":{"position":[[0,5],[104,5],[131,5]]},"2047":{"position":[[38,6],[206,5],[980,5]]},"2051":{"position":[[153,5]]},"2060":{"position":[[124,5],[214,5],[307,5]]},"2260":{"position":[[1507,5]]},"2428":{"position":[[707,5]]},"2458":{"position":[[13,5]]},"2570":{"position":[[143,5]]},"2610":{"position":[[160,8]]},"2686":{"position":[[1127,5],[1184,5],[1215,5],[1250,5],[1275,8],[1407,8],[1772,5],[1959,5],[7992,8],[9283,5],[9326,5]]},"2690":{"position":[[392,8],[418,8],[847,10],[890,5]]},"2692":{"position":[[658,10],[1008,5],[1062,5],[1199,7],[1642,7]]},"2760":{"position":[[1537,5]]},"2776":{"position":[[117,5]]},"2810":{"position":[[32,5],[75,5],[331,5]]},"2910":{"position":[[0,5],[51,5],[134,5]]},"2912":{"position":[[113,8]]},"2914":{"position":[[54,5],[324,5],[443,5],[501,5],[532,8]]},"3232":{"position":[[0,5],[104,5],[131,5]]},"3240":{"position":[[38,6],[206,5],[1070,5]]},"3244":{"position":[[153,5]]},"3255":{"position":[[124,5],[214,5],[307,5]]},"3610":{"position":[[1507,5]]},"3629":{"position":[[1353,5]]}}}],["admin.pi",{"_index":2134,"t":{"190":{"position":[[261,11]]}}}],["admin.site.url",{"_index":2205,"t":{"192":{"position":[[2337,18]]}}}],["admin/api",{"_index":4062,"t":{"926":{"position":[[253,11]]},"2047":{"position":[[253,11]]},"3240":{"position":[[253,11]]}}}],["admin/auth",{"_index":4061,"t":{"926":{"position":[[240,12]]},"2047":{"position":[[240,12]]},"3240":{"position":[[240,12]]}}}],["admin_auth_token",{"_index":3714,"t":{"624":{"position":[[229,19]]},"1623":{"position":[[229,19]]},"2810":{"position":[[229,19]]}}}],["admin_handler_prefix",{"_index":4071,"t":{"930":{"position":[[106,20]]},"2051":{"position":[[106,20]]},"3244":{"position":[[106,20]]}}}],["admin_insecur",{"_index":3754,"t":{"668":{"position":[[483,17]]},"939":{"position":[[50,14]]},"1662":{"position":[[483,17]]},"2060":{"position":[[50,14]]},"2914":{"position":[[547,17]]},"3255":{"position":[[50,14]]}}}],["admin_password",{"_index":2601,"t":{"248":{"position":[[107,17]]},"513":{"position":[[104,17]]},"664":{"position":[[64,14],[272,14]]},"666":{"position":[[128,17]]},"668":{"position":[[102,14],[507,17]]},"1441":{"position":[[104,17]]},"1505":{"position":[[858,17],[1407,17],[1780,14],[7910,17]]},"1658":{"position":[[64,14],[272,14]]},"1660":{"position":[[128,17]]},"1662":{"position":[[102,14],[507,17]]},"2610":{"position":[[104,17]]},"2686":{"position":[[873,17],[1422,17],[1795,14],[8007,17]]},"2910":{"position":[[64,14],[272,14]]},"2912":{"position":[[128,17]]},"2914":{"position":[[104,14],[571,17]]}}}],["admin_secret",{"_index":2602,"t":{"248":{"position":[[137,15]]},"513":{"position":[[134,15]]},"664":{"position":[[154,12],[291,12]]},"666":{"position":[[160,15]]},"668":{"position":[[121,12],[539,15]]},"1441":{"position":[[134,15]]},"1505":{"position":[[916,15],[1465,15],[7968,15]]},"1658":{"position":[[154,12],[291,12]]},"1660":{"position":[[160,15]]},"1662":{"position":[[121,12],[539,15]]},"2610":{"position":[[134,15]]},"2686":{"position":[[931,15],[1480,15],[8065,15]]},"2910":{"position":[[154,12],[291,12]]},"2912":{"position":[[160,15]]},"2914":{"position":[[123,12],[603,15]]}}}],["admin_storag",{"_index":3579,"t":{"535":{"position":[[255,16],[725,16],[997,16]]}}}],["admin_web_path",{"_index":3751,"t":{"666":{"position":[[188,17]]},"1660":{"position":[[188,17]]},"2912":{"position":[[188,17]]}}}],["administr",{"_index":3019,"t":{"385":{"position":[[9,14]]},"497":{"position":[[375,13]]},"1391":{"position":[[372,13]]},"1983":{"position":[[4019,14]]},"2562":{"position":[[374,13]]},"3142":{"position":[[4019,14]]}}}],["administrative/debug",{"_index":3658,"t":{"606":{"position":[[381,20]]},"1477":{"position":[[844,20]]},"2309":{"position":[[886,20]]},"3522":{"position":[[946,20]]}}}],["adminurlpattern",{"_index":2201,"t":{"192":{"position":[[2264,16]]}}}],["adopt",{"_index":849,"t":{"24":{"position":[[295,8],[664,9]]},"26":{"position":[[976,8]]},"32":{"position":[[2297,9]]},"38":{"position":[[1722,9]]},"52":{"position":[[189,9]]},"116":{"position":[[404,8]]},"152":{"position":[[583,5]]},"393":{"position":[[114,9]]},"1351":{"position":[[114,9]]},"1359":{"position":[[766,5],[1020,5]]},"2466":{"position":[[117,9]]},"2530":{"position":[[766,5],[1020,5]]}}}],["advanc",{"_index":718,"t":{"16":{"position":[[6577,8]]},"525":{"position":[[973,8]]},"1355":{"position":[[1040,8]]},"1527":{"position":[[1246,8]]},"2526":{"position":[[1025,8]]},"2760":{"position":[[1344,8]]}}}],["advantag",{"_index":363,"t":{"8":{"position":[[1262,10]]},"40":{"position":[[402,10]]},"52":{"position":[[821,10]]},"124":{"position":[[4307,10]]},"293":{"position":[[922,9]]},"317":{"position":[[24,10]]},"393":{"position":[[90,10]]},"1092":{"position":[[122,9]]},"1183":{"position":[[200,11]]},"1351":{"position":[[90,10]]},"2080":{"position":[[200,11]]},"2187":{"position":[[122,9]]},"2466":{"position":[[93,10]]},"3022":{"position":[[141,12]]},"3265":{"position":[[200,11]]},"3464":{"position":[[122,9]]}}}],["advic",{"_index":239,"t":{"4":{"position":[[403,7]]},"12":{"position":[[1005,6]]},"16":{"position":[[3143,7]]},"18":{"position":[[3083,6]]},"22":{"position":[[5,7]]},"76":{"position":[[1309,6]]},"144":{"position":[[1650,6]]},"305":{"position":[[233,7]]},"664":{"position":[[512,6]]},"953":{"position":[[662,7]]},"1373":{"position":[[65,6]]},"1421":{"position":[[32,7],[82,6],[365,6],[427,6]]},"1423":{"position":[[22,7]]},"1425":{"position":[[14,7]]},"1431":{"position":[[262,7]]},"1465":{"position":[[707,6]]},"1658":{"position":[[512,6]]},"1672":{"position":[[695,6]]},"1923":{"position":[[662,7]]},"1963":{"position":[[147,6]]},"2033":{"position":[[245,6]]},"2158":{"position":[[32,7],[82,6],[134,6]]},"2160":{"position":[[22,7]]},"2172":{"position":[[331,6]]},"2174":{"position":[[329,6]]},"2368":{"position":[[311,6]]},"2392":{"position":[[2473,6]]},"2544":{"position":[[65,6]]},"2622":{"position":[[743,6],[1138,6]]},"2836":{"position":[[695,6]]},"2910":{"position":[[512,6]]},"3008":{"position":[[147,6]]},"3150":{"position":[[662,7]]},"3224":{"position":[[218,6]]},"3387":{"position":[[32,7],[82,6],[134,6]]},"3389":{"position":[[22,7]]},"3401":{"position":[[331,6]]},"3403":{"position":[[329,6]]},"3429":{"position":[[311,6]]},"3612":{"position":[[2672,6]]},"3633":{"position":[[743,6],[1138,6]]}}}],["af14",{"_index":4870,"t":{"1597":{"position":[[1492,4]]},"2780":{"position":[[1456,4]]}}}],["affect",{"_index":206,"t":{"2":{"position":[[3559,6]]},"28":{"position":[[416,6]]},"122":{"position":[[2317,6]]},"150":{"position":[[48,6]]},"295":{"position":[[1514,6],[1647,6]]},"321":{"position":[[2344,8]]},"477":{"position":[[5290,6]]},"577":{"position":[[99,7]]},"782":{"position":[[261,6]]},"784":{"position":[[249,6]]},"856":{"position":[[378,6]]},"1545":{"position":[[99,7]]},"1797":{"position":[[229,6]]},"1799":{"position":[[217,6]]},"1827":{"position":[[1228,6]]},"1899":{"position":[[835,6]]},"2021":{"position":[[518,6]]},"2023":{"position":[[455,6]]},"2750":{"position":[[99,7]]},"3026":{"position":[[229,6]]},"3028":{"position":[[217,6]]},"3056":{"position":[[1194,6]]},"3134":{"position":[[835,6]]},"3210":{"position":[[491,6]]},"3212":{"position":[[428,6]]}}}],["afford",{"_index":817,"t":{"20":{"position":[[859,6]]}}}],["afraid",{"_index":3028,"t":{"409":{"position":[[101,6]]},"1233":{"position":[[101,6]]},"2482":{"position":[[101,6]]}}}],["again",{"_index":662,"t":{"16":{"position":[[3818,5],[5077,6]]},"20":{"position":[[656,5]]},"114":{"position":[[41,5]]},"200":{"position":[[2307,6]]},"216":{"position":[[4444,6]]},"250":{"position":[[1897,6]]},"268":{"position":[[1208,6]]},"293":{"position":[[465,5]]},"399":{"position":[[456,5]]},"475":{"position":[[4109,5]]},"477":{"position":[[721,6],[2485,5]]},"752":{"position":[[157,6]]},"838":{"position":[[374,5]]},"939":{"position":[[145,5]]},"1094":{"position":[[844,6]]},"1223":{"position":[[456,5]]},"1423":{"position":[[494,6]]},"1425":{"position":[[1203,5]]},"1855":{"position":[[374,5]]},"1967":{"position":[[621,6]]},"2033":{"position":[[269,6]]},"2060":{"position":[[145,5]]},"2160":{"position":[[494,6]]},"2400":{"position":[[2730,5]]},"2472":{"position":[[456,5]]},"2622":{"position":[[889,6],[936,7],[5352,5]]},"2632":{"position":[[5002,5],[5035,6]]},"2646":{"position":[[1725,5]]},"2720":{"position":[[237,5]]},"3012":{"position":[[621,6]]},"3020":{"position":[[565,5]]},"3084":{"position":[[374,5]]},"3224":{"position":[[242,6]]},"3255":{"position":[[145,5]]},"3367":{"position":[[4917,6]]},"3389":{"position":[[494,6]]},"3633":{"position":[[889,6],[936,7],[5352,5]]},"3643":{"position":[[5002,5],[5035,6]]},"3657":{"position":[[1725,5]]}}}],["against",{"_index":2754,"t":{"280":{"position":[[806,7]]},"465":{"position":[[1298,7]]},"892":{"position":[[356,7]]},"1513":{"position":[[66,8]]},"1827":{"position":[[418,7],[588,7]]},"2005":{"position":[[356,7]]},"2772":{"position":[[66,8]]},"3056":{"position":[[384,7],[554,7]]},"3194":{"position":[[356,7]]}}}],["agent",{"_index":1700,"t":{"110":{"position":[[138,5]]},"608":{"position":[[208,7]]},"672":{"position":[[435,7]]},"680":{"position":[[490,6]]},"1013":{"position":[[388,7],[615,5]]},"1457":{"position":[[1099,6]]},"1603":{"position":[[524,7]]},"1617":{"position":[[620,6]]},"2289":{"position":[[1159,6]]},"2350":{"position":[[388,7],[615,5]]},"2788":{"position":[[524,7]]},"2802":{"position":[[620,6]]},"3411":{"position":[[388,7],[615,5]]}}}],["aggreg",{"_index":4123,"t":{"963":{"position":[[165,10],[296,11]]},"1973":{"position":[[165,10],[296,11]]},"3301":{"position":[[165,10],[296,11]]},"3320":{"position":[[165,10],[296,11]]}}}],["agnost",{"_index":1788,"t":{"118":{"position":[[764,8]]},"242":{"position":[[66,8]]},"283":{"position":[[125,8]]},"311":{"position":[[771,8]]},"1293":{"position":[[97,8]]},"2416":{"position":[[345,8]]},"2428":{"position":[[105,8]]}}}],["ago",{"_index":4462,"t":{"1355":{"position":[[29,3]]},"2526":{"position":[[39,3]]}}}],["agpl",{"_index":918,"t":{"30":{"position":[[65,4]]}}}],["agreement",{"_index":4836,"t":{"1529":{"position":[[529,9]]},"2764":{"position":[[528,9]]}}}],["agress",{"_index":3397,"t":{"479":{"position":[[1075,9]]}}}],["aid",{"_index":5510,"t":{"2806":{"position":[[543,6]]}}}],["aim",{"_index":1894,"t":{"136":{"position":[[88,5]]},"202":{"position":[[1578,3]]},"475":{"position":[[6364,5]]},"479":{"position":[[2569,3]]},"1063":{"position":[[239,3]]}}}],["ajax",{"_index":2064,"t":{"176":{"position":[[387,4]]},"178":{"position":[[1034,4]]},"202":{"position":[[819,4]]},"345":{"position":[[451,4]]},"353":{"position":[[186,4],[354,4]]},"429":{"position":[[450,4]]},"798":{"position":[[1176,4]]},"989":{"position":[[411,4]]},"1255":{"position":[[450,4]]},"1277":{"position":[[451,4]]},"1285":{"position":[[186,4],[354,4]]},"1505":{"position":[[5960,4]]},"1813":{"position":[[1176,4]]},"2408":{"position":[[186,4],[360,4]]},"2504":{"position":[[450,4]]},"2686":{"position":[[6023,4]]}}}],["ajax/http",{"_index":4454,"t":{"1269":{"position":[[476,9]]},"2518":{"position":[[476,9]]}}}],["aka",{"_index":549,"t":{"14":{"position":[[742,3]]}}}],["alert",{"_index":2674,"t":{"262":{"position":[[641,6]]},"276":{"position":[[183,6]]}}}],["alex",{"_index":5039,"t":{"1847":{"position":[[939,7],[1080,7]]},"3076":{"position":[[903,7],[1044,7]]}}}],["alexand",{"_index":866,"t":{"26":{"position":[[454,9]]},"806":{"position":[[88,10],[268,10]]},"1479":{"position":[[308,11]]},"1821":{"position":[[88,10],[268,10]]},"2311":{"position":[[308,11]]},"3050":{"position":[[88,10],[373,10]]},"3524":{"position":[[124,11]]}}}],["alg",{"_index":2826,"t":{"291":{"position":[[1500,3],[1616,3]]},"301":{"position":[[583,3],[732,3]]},"2970":{"position":[[429,3],[538,3]]},"3046":{"position":[[199,3],[287,3]]},"3048":{"position":[[321,3],[409,3]]},"3050":{"position":[[294,3],[418,3]]}}}],["algorithm",{"_index":3629,"t":{"563":{"position":[[199,9]]},"776":{"position":[[252,9]]},"810":{"position":[[772,9]]},"973":{"position":[[3400,9]]},"1197":{"position":[[1214,10],[1263,9]]},"1199":{"position":[[1177,9]]},"1666":{"position":[[205,9],[1488,10]]},"1670":{"position":[[886,9]]},"1791":{"position":[[252,9]]},"1825":{"position":[[950,9]]},"2098":{"position":[[1214,10],[1263,9]]},"2100":{"position":[[1177,9]]},"2172":{"position":[[133,9]]},"2174":{"position":[[143,9]]},"2281":{"position":[[69,9]]},"2622":{"position":[[554,9],[972,11]]},"2624":{"position":[[277,9]]},"2640":{"position":[[312,9]]},"2830":{"position":[[205,9],[1491,10]]},"2834":{"position":[[889,9]]},"3020":{"position":[[295,9]]},"3283":{"position":[[1214,10],[1263,9]]},"3285":{"position":[[1177,9]]},"3401":{"position":[[133,9]]},"3403":{"position":[[143,9]]},"3563":{"position":[[48,9]]},"3633":{"position":[[554,9],[972,11]]},"3635":{"position":[[277,9]]},"3651":{"position":[[312,9]]}}}],["algorithm=\"hs256\").decode()print(token",{"_index":2822,"t":{"291":{"position":[[1351,39]]},"301":{"position":[[433,39]]},"804":{"position":[[172,39]]},"806":{"position":[[145,39]]},"1005":{"position":[[253,39]]},"1765":{"position":[[209,39]]},"1819":{"position":[[172,39]]},"1821":{"position":[[145,39]]},"2970":{"position":[[280,39]]},"3048":{"position":[[172,39]]},"3050":{"position":[[145,39]]}}}],["algorithm=\"hs256\")print(token",{"_index":5555,"t":{"3042":{"position":[[431,30]]}}}],["alia",{"_index":5149,"t":{"2088":{"position":[[283,5]]},"2094":{"position":[[268,5]]},"3273":{"position":[[283,5]]},"3279":{"position":[[268,5]]}}}],["alic",{"_index":5050,"t":{"1847":{"position":[[1218,8]]},"3076":{"position":[[1182,8]]}}}],["align",{"_index":2900,"t":{"321":{"position":[[1202,5]]},"329":{"position":[[818,5]]}}}],["aliv",{"_index":1467,"t":{"82":{"position":[[1759,6]]},"467":{"position":[[1262,5]]},"477":{"position":[[3352,6]]},"672":{"position":[[1308,6]]},"1419":{"position":[[23,5]]},"1603":{"position":[[1396,6],[1670,6]]},"2156":{"position":[[23,5]]},"2512":{"position":[[1806,6]]},"2788":{"position":[[1396,6],[1670,6]]},"3385":{"position":[[23,5]]}}}],["alivecont",{"_index":4575,"t":{"1457":{"position":[[1021,12]]},"2289":{"position":[[1081,12]]}}}],["alloc",{"_index":1110,"t":{"50":{"position":[[2016,9]]},"194":{"position":[[3259,9]]},"196":{"position":[[935,9]]},"473":{"position":[[2628,10],[2672,11],[2734,10],[2945,11],[3324,11]]},"475":{"position":[[915,8],[1697,12],[2356,9],[2384,11],[6106,11]]},"477":{"position":[[5069,10],[5215,11],[5257,10]]},"479":{"position":[[202,9],[2944,10],[6869,5]]},"483":{"position":[[399,10],[598,9],[634,8]]},"2622":{"position":[[158,9]]},"2632":{"position":[[200,9],[311,9]]},"2634":{"position":[[136,9]]},"3633":{"position":[[158,9]]},"3643":{"position":[[200,9],[311,9]]},"3645":{"position":[[136,9]]}}}],["alloc/op",{"_index":3275,"t":{"475":{"position":[[4977,8],[4990,8]]},"477":{"position":[[3958,8],[3971,8]]}}}],["allocs/op",{"_index":1020,"t":{"38":{"position":[[1461,9],[1631,9]]},"50":{"position":[[946,9],[1313,9]]},"469":{"position":[[991,9]]},"471":{"position":[[3397,9]]},"475":{"position":[[2291,9],[5330,9],[5344,9]]},"477":{"position":[[1403,9],[4311,9],[4325,9]]}}}],["allocs/opbenchmarkgoredi",{"_index":3229,"t":{"475":{"position":[[2234,25]]},"477":{"position":[[1289,25]]}}}],["allocs/opbenchmarkmarshalparallel",{"_index":1088,"t":{"50":{"position":[[767,33],[1137,33]]}}}],["allocs/opbenchmarkredigopipelininig",{"_index":3166,"t":{"471":{"position":[[3328,35]]}}}],["allocs/opbenchmarkrueidi",{"_index":3336,"t":{"477":{"position":[[1346,25]]}}}],["allocs/opbenchmarkunmarsh",{"_index":1090,"t":{"50":{"position":[[827,27],[1197,27]]}}}],["allocs/opbenchmarkunmarshalparallel",{"_index":1093,"t":{"50":{"position":[[882,35],[1251,35]]}}}],["allow",{"_index":338,"t":{"8":{"position":[[730,6]]},"12":{"position":[[1323,6]]},"16":{"position":[[4751,5],[6537,6]]},"18":{"position":[[1426,6],[2967,6]]},"26":{"position":[[1268,7]]},"32":{"position":[[499,5]]},"36":{"position":[[305,5]]},"38":{"position":[[62,5],[478,5]]},"42":{"position":[[86,6]]},"50":{"position":[[320,7]]},"52":{"position":[[579,5]]},"60":{"position":[[1594,5]]},"66":{"position":[[1476,6]]},"78":{"position":[[2989,5],[3188,6]]},"80":{"position":[[1029,5]]},"82":{"position":[[746,6]]},"86":{"position":[[20,5]]},"100":{"position":[[85,5]]},"104":{"position":[[421,7]]},"106":{"position":[[785,6]]},"110":{"position":[[95,7]]},"112":{"position":[[926,6]]},"118":{"position":[[980,6]]},"122":{"position":[[2120,6]]},"126":{"position":[[742,7]]},"128":{"position":[[656,7],[1531,6],[1751,6]]},"130":{"position":[[898,5],[1464,7]]},"132":{"position":[[150,7],[582,7]]},"136":{"position":[[334,6],[891,8],[1347,5],[1382,8]]},"144":{"position":[[23,7]]},"166":{"position":[[433,6]]},"168":{"position":[[675,6]]},"170":{"position":[[649,6],[919,7]]},"172":{"position":[[1201,5]]},"174":{"position":[[284,6]]},"186":{"position":[[616,6],[760,5]]},"196":{"position":[[1116,6],[1220,5],[1566,6],[1705,6]]},"198":{"position":[[114,7]]},"200":{"position":[[1379,8],[1742,8]]},"202":{"position":[[262,5],[1158,5]]},"214":{"position":[[551,6]]},"222":{"position":[[33,6]]},"244":{"position":[[227,5]]},"262":{"position":[[1791,6]]},"270":{"position":[[609,6]]},"274":{"position":[[347,6]]},"280":{"position":[[2023,7],[2889,7],[4053,6]]},"289":{"position":[[883,6]]},"291":{"position":[[2742,5],[2902,6]]},"293":{"position":[[889,7]]},"301":{"position":[[0,6],[375,8],[686,8]]},"311":{"position":[[987,6]]},"313":{"position":[[554,6]]},"315":{"position":[[515,5]]},"317":{"position":[[436,6]]},"327":{"position":[[459,6]]},"345":{"position":[[171,5],[237,5]]},"347":{"position":[[403,6],[732,6]]},"383":{"position":[[22,6]]},"385":{"position":[[31,6]]},"411":{"position":[[712,5]]},"419":{"position":[[47,5]]},"429":{"position":[[1255,6]]},"435":{"position":[[14,6]]},"447":{"position":[[23,6]]},"453":{"position":[[27,5]]},"457":{"position":[[72,6]]},"475":{"position":[[6490,7]]},"477":{"position":[[652,7],[2273,5]]},"479":{"position":[[1385,6],[2328,5]]},"511":{"position":[[260,6]]},"555":{"position":[[321,7]]},"589":{"position":[[247,7]]},"602":{"position":[[53,7],[79,7]]},"628":{"position":[[499,6]]},"636":{"position":[[0,6]]},"638":{"position":[[0,6]]},"648":{"position":[[476,6]]},"672":{"position":[[1908,6],[2155,5]]},"706":{"position":[[99,8]]},"748":{"position":[[178,6]]},"776":{"position":[[370,6]]},"790":{"position":[[381,5]]},"792":{"position":[[76,6],[706,5],[1437,6]]},"798":{"position":[[46,6]]},"820":{"position":[[1057,7]]},"824":{"position":[[43,6]]},"826":{"position":[[235,8]]},"828":{"position":[[523,6]]},"832":{"position":[[55,6]]},"844":{"position":[[54,6]]},"846":{"position":[[153,5]]},"858":{"position":[[110,6]]},"892":{"position":[[12,6],[39,7],[488,9],[1050,5]]},"900":{"position":[[14,5]]},"935":{"position":[[51,6]]},"973":{"position":[[129,6],[402,6]]},"997":{"position":[[44,6]]},"999":{"position":[[693,6]]},"1013":{"position":[[154,6]]},"1017":{"position":[[2038,6],[3295,7],[4239,6]]},"1021":{"position":[[229,6]]},"1023":{"position":[[265,6],[1925,8],[3366,6],[3840,5]]},"1025":{"position":[[1961,8],[3179,5]]},"1039":{"position":[[1163,6]]},"1041":{"position":[[954,6]]},"1057":{"position":[[1288,5],[1603,6],[1770,6],[1881,6]]},"1063":{"position":[[41,5]]},"1077":{"position":[[387,8]]},"1098":{"position":[[41,5],[862,5]]},"1107":{"position":[[30,7]]},"1122":{"position":[[30,7]]},"1145":{"position":[[30,7]]},"1147":{"position":[[75,6]]},"1152":{"position":[[30,7]]},"1175":{"position":[[30,7]]},"1183":{"position":[[327,5]]},"1187":{"position":[[128,6]]},"1203":{"position":[[173,6]]},"1205":{"position":[[585,6],[648,6]]},"1235":{"position":[[712,5]]},"1243":{"position":[[47,5]]},"1255":{"position":[[1255,6]]},"1261":{"position":[[14,6]]},"1277":{"position":[[171,5],[237,5]]},"1279":{"position":[[403,6],[741,6]]},"1295":{"position":[[267,5]]},"1299":{"position":[[23,6]]},"1305":{"position":[[27,5]]},"1309":{"position":[[72,6]]},"1341":{"position":[[22,6]]},"1343":{"position":[[18,6]]},"1367":{"position":[[862,5]]},"1369":{"position":[[43,7]]},"1373":{"position":[[121,7]]},"1405":{"position":[[445,6],[2736,6]]},"1415":{"position":[[112,6],[268,6],[332,6],[415,6]]},"1417":{"position":[[490,6]]},"1425":{"position":[[1581,5]]},"1433":{"position":[[25,5]]},"1439":{"position":[[262,6]]},"1457":{"position":[[16,6]]},"1459":{"position":[[23,6]]},"1461":{"position":[[10,6],[927,6]]},"1463":{"position":[[12,6]]},"1465":{"position":[[11,6]]},"1467":{"position":[[8,6]]},"1469":{"position":[[9,6]]},"1471":{"position":[[15,6]]},"1473":{"position":[[8,6]]},"1475":{"position":[[15,6]]},"1479":{"position":[[12,6]]},"1485":{"position":[[305,6]]},"1497":{"position":[[1002,5],[1052,5]]},"1505":{"position":[[5080,7],[7671,5]]},"1511":{"position":[[385,6],[1156,7]]},"1527":{"position":[[113,6],[507,6],[916,5]]},"1529":{"position":[[321,7]]},"1569":{"position":[[261,8]]},"1571":{"position":[[392,8],[436,8],[959,8],[1006,8],[1664,8],[1711,8],[1920,5]]},"1577":{"position":[[202,8]]},"1579":{"position":[[73,8]]},"1581":{"position":[[146,8],[192,8]]},"1583":{"position":[[18,5],[107,8]]},"1585":{"position":[[62,5],[101,6],[382,5],[397,8]]},"1597":{"position":[[428,5]]},"1603":{"position":[[2443,6],[2659,5]]},"1627":{"position":[[499,6]]},"1636":{"position":[[0,6]]},"1639":{"position":[[0,6]]},"1643":{"position":[[476,6]]},"1666":{"position":[[71,7],[435,6]]},"1684":{"position":[[391,8]]},"1721":{"position":[[81,6]]},"1751":{"position":[[44,6]]},"1753":{"position":[[693,6]]},"1763":{"position":[[52,6]]},"1771":{"position":[[838,5],[1776,6],[2155,5],[2367,5],[2508,5]]},"1773":{"position":[[1071,6],[1226,6],[1731,5],[1882,5],[2025,5],[2174,5]]},"1775":{"position":[[313,6],[522,6],[719,5],[873,5],[1032,5],[1197,5]]},"1777":{"position":[[331,5],[458,6],[671,6],[870,5],[1026,5],[1171,5]]},"1791":{"position":[[370,6]]},"1807":{"position":[[76,6],[964,6]]},"1813":{"position":[[46,6]]},"1837":{"position":[[750,7]]},"1839":{"position":[[472,5]]},"1843":{"position":[[110,6]]},"1851":{"position":[[406,7]]},"1857":{"position":[[1087,7]]},"1859":{"position":[[842,7]]},"1861":{"position":[[161,5]]},"1867":{"position":[[60,6]]},"1871":{"position":[[56,6]]},"1873":{"position":[[52,6]]},"1877":{"position":[[57,6]]},"1879":{"position":[[53,6]]},"1883":{"position":[[55,6]]},"1885":{"position":[[91,7],[552,5]]},"1899":{"position":[[153,8]]},"1941":{"position":[[96,8]]},"1965":{"position":[[907,6]]},"1983":{"position":[[129,6],[402,6]]},"2005":{"position":[[12,6],[39,7],[488,9],[1050,5]]},"2013":{"position":[[14,5]]},"2029":{"position":[[35,6]]},"2056":{"position":[[51,6]]},"2080":{"position":[[327,5]]},"2084":{"position":[[128,6]]},"2106":{"position":[[173,6]]},"2108":{"position":[[585,6],[648,6]]},"2122":{"position":[[29,6]]},"2140":{"position":[[1288,5],[1603,6],[1770,6],[1881,6]]},"2150":{"position":[[564,6]]},"2154":{"position":[[160,6]]},"2162":{"position":[[25,5]]},"2181":{"position":[[30,7]]},"2191":{"position":[[158,6]]},"2197":{"position":[[387,8]]},"2213":{"position":[[30,7]]},"2226":{"position":[[30,7]]},"2234":{"position":[[41,5],[862,5]]},"2241":{"position":[[30,7]]},"2258":{"position":[[30,7]]},"2260":{"position":[[75,6]]},"2273":{"position":[[30,7]]},"2289":{"position":[[16,6]]},"2291":{"position":[[23,6]]},"2293":{"position":[[10,6],[892,6]]},"2295":{"position":[[12,6]]},"2297":{"position":[[11,6]]},"2299":{"position":[[8,6]]},"2301":{"position":[[9,6]]},"2303":{"position":[[15,6]]},"2305":{"position":[[8,6]]},"2307":{"position":[[15,6]]},"2311":{"position":[[12,6]]},"2317":{"position":[[305,6]]},"2332":{"position":[[30,7]]},"2350":{"position":[[154,6]]},"2354":{"position":[[2155,6],[3412,7],[4356,6]]},"2358":{"position":[[229,6]]},"2360":{"position":[[265,6],[1925,8],[3298,6],[3871,5],[4048,7],[4276,8]]},"2362":{"position":[[1961,8],[3179,5]]},"2378":{"position":[[1163,6]]},"2380":{"position":[[928,6]]},"2402":{"position":[[428,6],[782,6]]},"2430":{"position":[[267,5]]},"2456":{"position":[[23,6]]},"2458":{"position":[[22,6]]},"2484":{"position":[[712,5]]},"2492":{"position":[[47,5]]},"2504":{"position":[[1255,6]]},"2510":{"position":[[14,6]]},"2538":{"position":[[862,5]]},"2540":{"position":[[43,7]]},"2544":{"position":[[121,7]]},"2586":{"position":[[23,6]]},"2592":{"position":[[27,5]]},"2596":{"position":[[72,6]]},"2608":{"position":[[265,6]]},"2632":{"position":[[7,6],[335,6],[429,5]]},"2664":{"position":[[261,8]]},"2666":{"position":[[392,8],[436,8],[959,8],[1006,8],[1664,8],[1711,8],[1920,5]]},"2672":{"position":[[202,8]]},"2674":{"position":[[73,8]]},"2676":{"position":[[146,8],[192,8]]},"2678":{"position":[[18,5],[107,8]]},"2680":{"position":[[62,5],[101,6],[382,5],[397,8]]},"2686":{"position":[[5142,7],[7759,5]]},"2692":{"position":[[1002,5],[1052,5]]},"2712":{"position":[[281,7],[487,10],[575,10],[764,7],[780,5],[913,8]]},"2718":{"position":[[174,7]]},"2720":{"position":[[37,7],[80,7],[229,7]]},"2760":{"position":[[113,6],[508,6],[1014,5]]},"2764":{"position":[[320,7]]},"2770":{"position":[[385,6],[1156,7]]},"2780":{"position":[[428,5]]},"2788":{"position":[[2443,6],[2659,5]]},"2814":{"position":[[499,6]]},"2823":{"position":[[0,6]]},"2826":{"position":[[0,6]]},"2830":{"position":[[71,7],[438,6]]},"2848":{"position":[[391,8]]},"2887":{"position":[[81,6]]},"2895":{"position":[[476,6]]},"2930":{"position":[[838,5],[1776,6],[2155,5],[2367,5],[2508,5]]},"2932":{"position":[[1071,6],[1226,6],[1731,5],[1882,5],[2025,5],[2174,5]]},"2934":{"position":[[313,6],[522,6],[719,5],[873,5],[1032,5],[1197,5]]},"2936":{"position":[[331,5],[458,6],[671,6],[870,5],[1026,5],[1171,5]]},"2956":{"position":[[44,6]]},"2958":{"position":[[693,6]]},"2968":{"position":[[52,6]]},"2986":{"position":[[96,8]]},"3010":{"position":[[907,6]]},"3018":{"position":[[157,5],[379,5]]},"3020":{"position":[[424,6]]},"3036":{"position":[[157,6],[1042,6]]},"3042":{"position":[[670,6]]},"3066":{"position":[[762,7]]},"3072":{"position":[[11,6]]},"3080":{"position":[[406,7]]},"3088":{"position":[[1087,7]]},"3090":{"position":[[842,7]]},"3092":{"position":[[161,5]]},"3098":{"position":[[60,6]]},"3102":{"position":[[56,6]]},"3104":{"position":[[52,6]]},"3108":{"position":[[57,6]]},"3110":{"position":[[53,6]]},"3114":{"position":[[55,6]]},"3116":{"position":[[91,7],[552,5]]},"3134":{"position":[[153,8]]},"3142":{"position":[[129,6],[402,6]]},"3194":{"position":[[12,6],[39,7],[488,9],[1050,5]]},"3202":{"position":[[14,5]]},"3218":{"position":[[35,6]]},"3249":{"position":[[51,6]]},"3265":{"position":[[327,5]]},"3269":{"position":[[128,6]]},"3289":{"position":[[173,6]]},"3291":{"position":[[585,6],[648,6]]},"3355":{"position":[[1288,5],[1603,6],[1770,6],[1881,6]]},"3367":{"position":[[1480,6],[3634,7]]},"3369":{"position":[[1117,6]]},"3379":{"position":[[564,6]]},"3383":{"position":[[160,6]]},"3391":{"position":[[25,5]]},"3411":{"position":[[154,6]]},"3415":{"position":[[2155,6],[3412,7],[4385,6]]},"3419":{"position":[[229,6]]},"3421":{"position":[[265,6],[1728,8],[3101,6],[3874,5],[4051,7],[4279,8]]},"3423":{"position":[[1961,8],[3179,5]]},"3439":{"position":[[1163,6]]},"3441":{"position":[[928,6]]},"3458":{"position":[[30,7]]},"3466":{"position":[[158,6]]},"3472":{"position":[[387,8]]},"3488":{"position":[[30,7]]},"3502":{"position":[[15,6]]},"3504":{"position":[[36,6]]},"3506":{"position":[[10,6],[960,6]]},"3508":{"position":[[12,6]]},"3510":{"position":[[11,6]]},"3512":{"position":[[8,6]]},"3514":{"position":[[9,6]]},"3516":{"position":[[15,6]]},"3518":{"position":[[8,6]]},"3520":{"position":[[15,6]]},"3524":{"position":[[12,6]]},"3526":{"position":[[6,6]]},"3530":{"position":[[305,6]]},"3555":{"position":[[30,7]]},"3576":{"position":[[30,7]]},"3584":{"position":[[41,5],[862,5]]},"3591":{"position":[[30,7]]},"3608":{"position":[[30,7]]},"3610":{"position":[[75,6]]},"3617":{"position":[[30,7]]},"3643":{"position":[[7,6],[335,6],[429,5]]}}}],["allow_anonymous_connect_without_token",{"_index":4476,"t":{"1375":{"position":[[109,37]]},"2546":{"position":[[109,37]]}}}],["allow_history_for_anonym",{"_index":5054,"t":{"1875":{"position":[[0,27]]},"1899":{"position":[[652,30],[1439,30]]},"3106":{"position":[[0,27]]},"3134":{"position":[[652,30],[1439,30]]}}}],["allow_history_for_cli",{"_index":5018,"t":{"1775":{"position":[[454,24],[497,24]]},"1873":{"position":[[0,24]]},"2934":{"position":[[454,24],[497,24]]},"3104":{"position":[[0,24]]}}}],["allow_history_for_subscrib",{"_index":5017,"t":{"1775":{"position":[[237,28],[284,28]]},"1871":{"position":[[0,28]]},"1899":{"position":[[614,31],[1401,31]]},"2934":{"position":[[237,28],[284,28]]},"3102":{"position":[[0,28]]},"3134":{"position":[[614,31],[1401,31]]}}}],["allow_presence_for_anonym",{"_index":5055,"t":{"1881":{"position":[[0,28]]},"1899":{"position":[[728,31],[1515,31]]},"3112":{"position":[[0,28]]},"3134":{"position":[[728,31],[1515,31]]}}}],["allow_presence_for_cli",{"_index":5020,"t":{"1777":{"position":[[601,25],[645,25]]},"1879":{"position":[[0,25]]},"2936":{"position":[[601,25],[645,25]]},"3110":{"position":[[0,25]]}}}],["allow_presence_for_subscrib",{"_index":5019,"t":{"1777":{"position":[[380,29],[428,29]]},"1877":{"position":[[0,29]]},"1899":{"position":[[689,32],[1476,32]]},"2936":{"position":[[380,29],[428,29]]},"3108":{"position":[[0,29]]},"3134":{"position":[[689,32],[1476,32]]}}}],["allow_publish_for_anonym",{"_index":5053,"t":{"1869":{"position":[[0,27]]},"1899":{"position":[[577,30],[1364,30]]},"3100":{"position":[[0,27]]},"3134":{"position":[[577,30],[1364,30]]}}}],["allow_publish_for_cli",{"_index":1875,"t":{"130":{"position":[[1255,27]]},"1773":{"position":[[799,24],[1003,24],[1046,24],[1590,26]]},"1865":{"position":[[740,24]]},"1867":{"position":[[0,24]]},"2932":{"position":[[799,24],[1003,24],[1046,24],[1590,26]]},"3096":{"position":[[740,24]]},"3098":{"position":[[0,24]]}}}],["allow_publish_for_subscrib",{"_index":5016,"t":{"1773":{"position":[[749,28],[1150,28],[1197,28]]},"1865":{"position":[[0,28],[65,28],[707,28]]},"1867":{"position":[[150,28]]},"1899":{"position":[[539,31],[1326,31]]},"2932":{"position":[[749,28],[1150,28],[1197,28]]},"3096":{"position":[[0,28],[65,28],[707,28]]},"3098":{"position":[[150,28]]},"3134":{"position":[[539,31],[1326,31]]}}}],["allow_subscribe_for_anonym",{"_index":5015,"t":{"1771":{"position":[[2213,29]]},"1861":{"position":[[204,29]]},"1863":{"position":[[0,29]]},"1899":{"position":[[500,32],[1287,32]]},"2930":{"position":[[2213,29]]},"3092":{"position":[[204,29]]},"3094":{"position":[[0,29]]},"3134":{"position":[[500,32],[1287,32]]}}}],["allow_subscribe_for_cli",{"_index":4474,"t":{"1367":{"position":[[991,26]]},"1505":{"position":[[7781,26],[8121,29]]},"1771":{"position":[[781,26],[1704,26],[1749,26]]},"1839":{"position":[[410,26]]},"1861":{"position":[[0,26]]},"1885":{"position":[[652,29]]},"1899":{"position":[[464,29],[1251,29]]},"2538":{"position":[[991,26]]},"2686":{"position":[[7878,26],[8218,29]]},"2930":{"position":[[781,26],[1704,26],[1749,26]]},"3068":{"position":[[405,26]]},"3092":{"position":[[0,26]]},"3116":{"position":[[652,29]]},"3134":{"position":[[464,29],[1251,29]]}}}],["allow_user_limited_channel",{"_index":2800,"t":{"289":{"position":[[104,27],[198,30],[768,27]]},"1369":{"position":[[92,28]]},"1771":{"position":[[1664,27]]},"1837":{"position":[[1059,27]]},"1883":{"position":[[0,27]]},"2540":{"position":[[92,28]]},"2930":{"position":[[1664,27]]},"3066":{"position":[[1063,27]]},"3114":{"position":[[0,27]]}}}],["allowed_in",{"_index":5472,"t":{"2712":{"position":[[611,13],[837,10],[1005,10]]},"2720":{"position":[[167,10]]}}}],["allowed_origin",{"_index":2027,"t":{"170":{"position":[[270,18]]},"196":{"position":[[288,18],[1145,15]]},"248":{"position":[[197,18]]},"489":{"position":[[442,15]]},"602":{"position":[[147,18],[327,18]]},"618":{"position":[[103,15]]},"620":{"position":[[770,16]]},"878":{"position":[[589,15]]},"884":{"position":[[72,18],[534,15]]},"886":{"position":[[133,16]]},"888":{"position":[[43,16]]},"892":{"position":[[272,15],[614,15],[664,18],[752,18],[862,18],[1162,18]]},"1017":{"position":[[443,15]]},"1383":{"position":[[460,15]]},"1505":{"position":[[1023,18],[1572,18],[4868,15],[4944,15],[4989,15],[5034,18],[5243,15],[8075,18]]},"1991":{"position":[[589,15]]},"1997":{"position":[[72,18],[534,15]]},"1999":{"position":[[133,16]]},"2001":{"position":[[43,16]]},"2005":{"position":[[272,15],[614,15],[664,18],[752,18],[862,18],[1162,18]]},"2354":{"position":[[443,15]]},"2554":{"position":[[475,15]]},"2686":{"position":[[1038,18],[1587,18],[4930,15],[5006,15],[5051,15],[5096,18],[5305,15],[8172,18]]},"3180":{"position":[[589,15]]},"3186":{"position":[[72,18],[534,15]]},"3188":{"position":[[133,16]]},"3190":{"position":[[43,16]]},"3194":{"position":[[272,15],[614,15],[664,18],[752,18],[862,18],[1162,18]]},"3415":{"position":[[443,15]]}}}],["allowedorigin",{"_index":1641,"t":{"104":{"position":[[387,14],[454,14],[1093,15]]}}}],["alo",{"_index":2115,"t":{"186":{"position":[[470,5]]}}}],["along",{"_index":2654,"t":{"258":{"position":[[1521,5]]},"313":{"position":[[119,5]]}}}],["alpn",{"_index":1685,"t":{"106":{"position":[[1583,4]]}}}],["alpnquictransport",{"_index":1675,"t":{"106":{"position":[[1178,17]]}}}],["alreadi",{"_index":820,"t":{"20":{"position":[[1150,7]]},"60":{"position":[[1770,7]]},"74":{"position":[[1416,7]]},"82":{"position":[[2289,7],[2466,7]]},"94":{"position":[[2967,7]]},"108":{"position":[[898,7]]},"116":{"position":[[887,7]]},"130":{"position":[[1481,7]]},"144":{"position":[[365,7]]},"162":{"position":[[14,7]]},"172":{"position":[[661,7]]},"180":{"position":[[139,7]]},"182":{"position":[[851,7]]},"186":{"position":[[23,7],[296,8],[519,8],[912,7]]},"194":{"position":[[3246,7]]},"202":{"position":[[148,7]]},"220":{"position":[[164,7]]},"256":{"position":[[156,7]]},"258":{"position":[[747,7],[1931,7]]},"270":{"position":[[2406,7]]},"278":{"position":[[106,7],[429,7],[646,7],[733,7]]},"329":{"position":[[151,7]]},"437":{"position":[[984,7]]},"475":{"position":[[2826,7]]},"477":{"position":[[1776,7],[2192,7]]},"481":{"position":[[1701,7]]},"483":{"position":[[2012,7]]},"710":{"position":[[20,8],[48,7],[121,7]]},"728":{"position":[[186,7]]},"880":{"position":[[466,7]]},"1023":{"position":[[433,7]]},"1055":{"position":[[17,7]]},"1191":{"position":[[728,7]]},"1263":{"position":[[988,7]]},"1413":{"position":[[1080,7]]},"1423":{"position":[[392,7],[1149,7]]},"1433":{"position":[[126,7]]},"1585":{"position":[[134,7]]},"1692":{"position":[[1108,7]]},"1945":{"position":[[18,8],[45,7],[118,7]]},"1993":{"position":[[466,7]]},"2090":{"position":[[728,7]]},"2138":{"position":[[17,7]]},"2160":{"position":[[392,7],[1149,7]]},"2162":{"position":[[126,7]]},"2360":{"position":[[433,7]]},"2512":{"position":[[1052,7]]},"2622":{"position":[[3900,7],[4102,7],[4320,7]]},"2634":{"position":[[227,7]]},"2680":{"position":[[134,7]]},"2856":{"position":[[1108,7]]},"2990":{"position":[[18,8],[45,7],[137,7],[464,7]]},"3182":{"position":[[470,7]]},"3275":{"position":[[728,7]]},"3353":{"position":[[17,7]]},"3367":{"position":[[1750,7]]},"3389":{"position":[[392,7],[1149,7]]},"3391":{"position":[[126,7]]},"3421":{"position":[[468,7]]},"3633":{"position":[[3900,7],[4102,7],[4320,7]]},"3645":{"position":[[227,7]]}}}],["alter",{"_index":5249,"t":{"2526":{"position":[[262,8]]}}}],["altern",{"_index":348,"t":{"8":{"position":[[911,11]]},"60":{"position":[[1683,11],[1957,11]]},"78":{"position":[[3776,11]]},"90":{"position":[[35,11]]},"94":{"position":[[569,11]]},"100":{"position":[[155,11]]},"124":{"position":[[1879,11]]},"144":{"position":[[1955,11]]},"152":{"position":[[888,11]]},"168":{"position":[[576,11]]},"250":{"position":[[545,14]]},"473":{"position":[[3065,13]]},"475":{"position":[[17,11],[6403,11]]},"477":{"position":[[2969,11]]},"818":{"position":[[691,11]]},"1013":{"position":[[473,14]]},"1195":{"position":[[0,14]]},"1505":{"position":[[4403,14]]},"2096":{"position":[[0,14]]},"2350":{"position":[[473,14]]},"2392":{"position":[[2807,11]]},"2526":{"position":[[1914,11]]},"2686":{"position":[[4465,14]]},"3281":{"position":[[0,14]]},"3411":{"position":[[473,14]]},"3612":{"position":[[3006,11]]}}}],["although",{"_index":2887,"t":{"317":{"position":[[603,8]]},"664":{"position":[[455,8]]},"977":{"position":[[0,8]]},"1658":{"position":[[455,8]]},"2070":{"position":[[0,8]]},"2910":{"position":[[455,8]]},"3040":{"position":[[0,8]]},"3307":{"position":[[0,8]]}}}],["alway",{"_index":433,"t":{"10":{"position":[[1004,6]]},"12":{"position":[[504,6]]},"74":{"position":[[1967,6]]},"124":{"position":[[2425,6]]},"134":{"position":[[579,6]]},"455":{"position":[[582,6]]},"477":{"position":[[5832,6]]},"892":{"position":[[481,6]]},"1029":{"position":[[692,6]]},"1269":{"position":[[291,6]]},"1307":{"position":[[582,6]]},"1421":{"position":[[550,6]]},"1497":{"position":[[497,6]]},"1666":{"position":[[610,6],[670,6]]},"1773":{"position":[[1534,6]]},"1865":{"position":[[519,6]]},"1867":{"position":[[492,6]]},"1983":{"position":[[4398,6]]},"2005":{"position":[[481,6]]},"2124":{"position":[[302,6]]},"2368":{"position":[[892,6]]},"2518":{"position":[[291,6]]},"2594":{"position":[[609,6]]},"2638":{"position":[[1014,6]]},"2692":{"position":[[497,6]]},"2708":{"position":[[2184,6]]},"2830":{"position":[[613,6],[673,6]]},"2883":{"position":[[1515,6]]},"2932":{"position":[[1534,6]]},"3096":{"position":[[519,6]]},"3098":{"position":[[492,6]]},"3142":{"position":[[4398,6]]},"3194":{"position":[[481,6]]},"3347":{"position":[[302,6]]},"3363":{"position":[[359,6]]},"3365":{"position":[[3403,6],[4059,6],[4139,6]]},"3369":{"position":[[1917,6]]},"3429":{"position":[[892,6]]},"3538":{"position":[[88,6]]},"3649":{"position":[[1014,6]]}}}],["amaz",{"_index":1062,"t":{"48":{"position":[[65,7]]}}}],["amazon",{"_index":4065,"t":{"926":{"position":[[795,6]]},"2047":{"position":[[795,6]]},"3240":{"position":[[885,6]]}}}],["amongst",{"_index":2521,"t":{"236":{"position":[[416,7]]},"465":{"position":[[520,7]]}}}],["amount",{"_index":489,"t":{"12":{"position":[[349,6]]},"18":{"position":[[852,6]]},"76":{"position":[[1892,6]]},"78":{"position":[[466,6]]},"84":{"position":[[851,6],[916,6]]},"280":{"position":[[3914,6]]},"313":{"position":[[592,6]]},"399":{"position":[[53,6]]},"441":{"position":[[1076,6]]},"475":{"position":[[119,6],[3195,6]]},"836":{"position":[[49,7],[208,6],[304,6]]},"1160":{"position":[[199,6]]},"1223":{"position":[[53,6]]},"1267":{"position":[[1076,6]]},"1849":{"position":[[1190,6]]},"1853":{"position":[[49,7],[208,6],[304,6]]},"2340":{"position":[[199,6]]},"2472":{"position":[[53,6]]},"2516":{"position":[[1076,6]]},"3078":{"position":[[1190,6]]},"3082":{"position":[[49,7],[208,6],[304,6]]},"3625":{"position":[[199,6]]}}}],["analog",{"_index":4272,"t":{"1051":{"position":[[0,9]]},"2122":{"position":[[384,10]]},"2390":{"position":[[0,9]]},"2574":{"position":[[167,9]]},"3345":{"position":[[644,10]]},"3451":{"position":[[0,9]]}}}],["analogu",{"_index":1196,"t":{"60":{"position":[[168,9]]},"1039":{"position":[[1042,10]]},"1293":{"position":[[456,8]]},"1511":{"position":[[667,8]]},"2378":{"position":[[1042,10]]},"2428":{"position":[[466,8]]},"2770":{"position":[[667,8]]},"3439":{"position":[[1042,10]]},"3526":{"position":[[228,8]]}}}],["analysi",{"_index":639,"t":{"16":{"position":[[2755,8]]}}}],["analyt",{"_index":2684,"t":{"262":{"position":[[1574,9]]},"270":{"position":[[1874,10]]},"274":{"position":[[86,10],[137,10],[506,10]]},"278":{"position":[[310,9]]},"335":{"position":[[359,9]]},"393":{"position":[[289,10]]},"553":{"position":[[208,9]]},"672":{"position":[[486,9]]},"682":{"position":[[16,9]]},"878":{"position":[[894,10]]},"1477":{"position":[[1026,9]]},"1527":{"position":[[205,9]]},"1603":{"position":[[574,9]]},"1619":{"position":[[16,9],[634,9]]},"1686":{"position":[[99,10],[145,10]]},"1719":{"position":[[1177,9]]},"1721":{"position":[[264,9]]},"1991":{"position":[[894,10]]},"2309":{"position":[[1068,9]]},"2424":{"position":[[1027,9]]},"2624":{"position":[[492,9]]},"2760":{"position":[[205,9]]},"2788":{"position":[[574,9]]},"2804":{"position":[[16,9],[634,9]]},"2806":{"position":[[234,10]]},"2850":{"position":[[99,10],[145,10]]},"2883":{"position":[[480,9]]},"2887":{"position":[[264,9]]},"3180":{"position":[[894,10]]},"3522":{"position":[[1128,9]]},"3635":{"position":[[492,9]]}}}],["analyz",{"_index":2727,"t":{"274":{"position":[[253,7]]},"393":{"position":[[366,7]]},"1686":{"position":[[261,7]]},"2850":{"position":[[261,7]]}}}],["and/or",{"_index":4475,"t":{"1371":{"position":[[854,6]]},"2542":{"position":[[854,6]]}}}],["android",{"_index":1459,"t":{"82":{"position":[[1158,7]]},"262":{"position":[[338,8]]},"264":{"position":[[293,8]]},"268":{"position":[[551,8]]},"425":{"position":[[219,7]]},"1063":{"position":[[634,7]]},"1251":{"position":[[219,7]]},"1688":{"position":[[170,8]]},"1703":{"position":[[376,8]]},"2166":{"position":[[199,7]]},"2500":{"position":[[219,7]]},"2852":{"position":[[170,8]]},"2867":{"position":[[376,8]]},"3395":{"position":[[273,7]]}}}],["anewsubscript",{"_index":5342,"t":{"2632":{"position":[[159,16]]},"3643":{"position":[[159,16]]}}}],["anf",{"_index":5129,"t":{"1983":{"position":[[2655,3]]},"3142":{"position":[[2655,3]]}}}],["angl",{"_index":3130,"t":{"471":{"position":[[749,5]]}}}],["annot",{"_index":3936,"t":{"792":{"position":[[153,9]]},"1597":{"position":[[1682,9]]},"1807":{"position":[[153,9]]},"2780":{"position":[[1646,9]]},"3036":{"position":[[172,10]]}}}],["announc",{"_index":838,"t":{"24":{"position":[[69,8]]},"118":{"position":[[24,8]]},"256":{"position":[[27,9]]}}}],["annoy",{"_index":1818,"t":{"124":{"position":[[330,9]]}}}],["anomali",{"_index":5586,"t":{"3324":{"position":[[276,9]]}}}],["anonym",{"_index":2851,"t":{"299":{"position":[[456,9]]},"315":{"position":[[419,9],[1188,9]]},"563":{"position":[[123,9]]},"774":{"position":[[284,9],[320,9]]},"828":{"position":[[0,9],[57,9],[530,9]]},"856":{"position":[[145,12]]},"858":{"position":[[1211,12],[1380,12]]},"910":{"position":[[150,9],[226,9]]},"945":{"position":[[30,9]]},"1069":{"position":[[317,9]]},"1668":{"position":[[429,9]]},"1670":{"position":[[800,9]]},"1743":{"position":[[239,9]]},"1771":{"position":[[1805,9],[2176,9]]},"1789":{"position":[[284,9],[424,9]]},"1861":{"position":[[70,9],[167,9]]},"1863":{"position":[[68,9]]},"1869":{"position":[[66,9]]},"1875":{"position":[[66,9]]},"1881":{"position":[[67,9]]},"1899":{"position":[[196,9]]},"2031":{"position":[[150,9],[249,9]]},"2033":{"position":[[106,9]]},"2066":{"position":[[30,9]]},"2132":{"position":[[317,9]]},"2832":{"position":[[429,9]]},"2834":{"position":[[800,9]]},"2930":{"position":[[1805,9],[2176,9]]},"2948":{"position":[[239,9]]},"3018":{"position":[[272,9],[407,9]]},"3092":{"position":[[70,9],[167,9]]},"3094":{"position":[[68,9]]},"3100":{"position":[[66,9]]},"3106":{"position":[[66,9]]},"3112":{"position":[[67,9]]},"3134":{"position":[[196,9]]},"3222":{"position":[[150,9],[249,9]]},"3224":{"position":[[79,9]]},"3261":{"position":[[30,9]]},"3335":{"position":[[317,9]]},"3639":{"position":[[750,9]]}}}],["anoth",{"_index":437,"t":{"10":{"position":[[1151,7],[1169,7]]},"18":{"position":[[3075,7]]},"52":{"position":[[630,7]]},"64":{"position":[[934,7],[1595,7]]},"76":{"position":[[0,7]]},"80":{"position":[[202,7]]},"82":{"position":[[1288,7]]},"128":{"position":[[1598,7]]},"132":{"position":[[661,7],[1734,7]]},"138":{"position":[[0,7]]},"158":{"position":[[1316,8]]},"184":{"position":[[966,8]]},"192":{"position":[[216,7]]},"234":{"position":[[0,7],[160,7]]},"242":{"position":[[1433,8]]},"250":{"position":[[634,7]]},"270":{"position":[[38,7],[558,7],[2198,7]]},"291":{"position":[[91,7]]},"317":{"position":[[968,7]]},"325":{"position":[[0,7]]},"329":{"position":[[216,7]]},"351":{"position":[[105,7]]},"357":{"position":[[1281,7]]},"411":{"position":[[338,7]]},"425":{"position":[[294,7],[910,7]]},"455":{"position":[[0,7]]},"467":{"position":[[514,7],[528,7]]},"473":{"position":[[2194,7]]},"475":{"position":[[7040,7]]},"481":{"position":[[339,7]]},"483":{"position":[[1025,7]]},"608":{"position":[[513,7]]},"866":{"position":[[0,7]]},"868":{"position":[[0,7]]},"1147":{"position":[[442,7]]},"1160":{"position":[[912,7]]},"1191":{"position":[[522,7],[549,7],[685,7]]},"1199":{"position":[[650,7]]},"1207":{"position":[[994,7],[1046,7],[1068,7]]},"1235":{"position":[[338,7]]},"1251":{"position":[[987,7]]},"1283":{"position":[[105,7]]},"1289":{"position":[[1281,7]]},"1307":{"position":[[0,7]]},"1489":{"position":[[250,7]]},"1505":{"position":[[6465,7],[9210,7]]},"1513":{"position":[[251,7]]},"1571":{"position":[[888,7]]},"1591":{"position":[[496,7]]},"1597":{"position":[[797,7]]},"1668":{"position":[[0,7]]},"1905":{"position":[[0,7]]},"1909":{"position":[[0,7]]},"1911":{"position":[[0,7]]},"2090":{"position":[[522,7],[549,7],[685,7]]},"2100":{"position":[[650,7]]},"2110":{"position":[[994,7],[1046,7],[1068,7]]},"2260":{"position":[[442,7]]},"2321":{"position":[[250,7]]},"2340":{"position":[[912,7]]},"2406":{"position":[[106,7]]},"2412":{"position":[[1302,7]]},"2484":{"position":[[338,7]]},"2500":{"position":[[987,7]]},"2512":{"position":[[588,7]]},"2594":{"position":[[0,7]]},"2666":{"position":[[888,7]]},"2686":{"position":[[9345,7]]},"2708":{"position":[[741,7]]},"2772":{"position":[[251,7]]},"2780":{"position":[[797,7]]},"2784":{"position":[[496,7]]},"2832":{"position":[[0,7]]},"3162":{"position":[[0,7]]},"3166":{"position":[[0,7]]},"3168":{"position":[[0,7]]},"3275":{"position":[[522,7],[549,7],[685,7]]},"3285":{"position":[[650,7]]},"3293":{"position":[[994,7],[1046,7],[1068,7]]},"3534":{"position":[[250,7]]},"3610":{"position":[[442,7]]},"3625":{"position":[[912,7]]}}}],["answer",{"_index":941,"t":{"32":{"position":[[1294,6]]},"198":{"position":[[1936,6]]},"317":{"position":[[1074,7]]},"397":{"position":[[203,6]]},"1021":{"position":[[610,10]]},"1217":{"position":[[221,7]]},"1221":{"position":[[203,6]]},"2358":{"position":[[610,10]]},"2396":{"position":[[223,7]]},"2420":{"position":[[91,6]]},"2470":{"position":[[203,6]]},"2630":{"position":[[40,6]]},"3419":{"position":[[657,10]]},"3641":{"position":[[40,6]]}}}],["anton",{"_index":1140,"t":{"54":{"position":[[443,5]]},"152":{"position":[[482,5]]}}}],["anymor",{"_index":1042,"t":{"42":{"position":[[934,7]]},"92":{"position":[[81,8]]},"132":{"position":[[793,8]]},"148":{"position":[[536,8]]},"321":{"position":[[1655,7]]},"593":{"position":[[150,7]]},"608":{"position":[[504,8]]},"612":{"position":[[339,7]]},"969":{"position":[[1336,8]]},"1185":{"position":[[349,7]]},"1189":{"position":[[1224,7]]},"1511":{"position":[[192,7]]},"1575":{"position":[[294,7],[518,7]]},"1839":{"position":[[243,7]]},"1979":{"position":[[1411,8]]},"2082":{"position":[[335,8]]},"2086":{"position":[[1378,8]]},"2578":{"position":[[653,8]]},"2582":{"position":[[155,8]]},"2622":{"position":[[4484,9],[5541,8]]},"2626":{"position":[[109,7]]},"2634":{"position":[[622,8]]},"2644":{"position":[[637,7]]},"2670":{"position":[[294,7],[518,7]]},"2770":{"position":[[192,7]]},"3086":{"position":[[337,8]]},"3138":{"position":[[1411,8]]},"3267":{"position":[[335,8]]},"3271":{"position":[[1344,8]]},"3365":{"position":[[435,8]]},"3633":{"position":[[4484,9],[5541,8]]},"3637":{"position":[[109,7]]},"3645":{"position":[[622,8]]},"3655":{"position":[[637,7]]}}}],["anyon",{"_index":3753,"t":{"668":{"position":[[337,6]]},"1455":{"position":[[744,6]]},"1662":{"position":[[337,6]]},"2287":{"position":[[747,6]]},"2914":{"position":[[390,6]]},"3498":{"position":[[714,6]]}}}],["anyth",{"_index":1793,"t":{"120":{"position":[[544,11]]},"280":{"position":[[3756,8]]},"3329":{"position":[[157,8]]}}}],["anyway",{"_index":729,"t":{"16":{"position":[[7050,7]]},"108":{"position":[[1489,6]]},"232":{"position":[[1513,7]]},"260":{"position":[[1143,7]]},"497":{"position":[[453,7]]},"774":{"position":[[205,6]]},"844":{"position":[[281,6]]},"1027":{"position":[[450,8]]},"1197":{"position":[[997,7]]},"1391":{"position":[[452,7]]},"1789":{"position":[[205,6]]},"2098":{"position":[[997,7]]},"2366":{"position":[[409,8]]},"2562":{"position":[[452,7]]},"3283":{"position":[[997,7]]},"3427":{"position":[[409,8]]}}}],["anywher",{"_index":2750,"t":{"280":{"position":[[708,8]]},"509":{"position":[[1328,9]]},"620":{"position":[[327,9]]},"1371":{"position":[[925,9]]},"1437":{"position":[[1561,9]]},"2542":{"position":[[925,9]]},"2606":{"position":[[1601,9]]}}}],["aof",{"_index":2999,"t":{"347":{"position":[[1281,4]]},"973":{"position":[[2688,3],[2831,3]]},"1279":{"position":[[1286,4]]},"2402":{"position":[[1344,3]]}}}],["apach",{"_index":923,"t":{"30":{"position":[[234,6],[261,6]]},"391":{"position":[[151,7]]},"1349":{"position":[[151,7]]},"2422":{"position":[[57,6]]},"2464":{"position":[[155,7]]}}}],["api",{"_index":318,"t":{"8":{"position":[[382,3],[698,3],[1184,3],[1566,3]]},"18":{"position":[[192,4]]},"24":{"position":[[584,3]]},"28":{"position":[[445,3],[978,5]]},"32":{"position":[[847,4],[1530,4],[1850,3],[1977,3]]},"34":{"position":[[62,3],[221,3],[674,3],[1569,3]]},"36":{"position":[[263,3]]},"42":{"position":[[39,3],[75,3],[559,4],[572,3],[689,3],[1028,3],[1066,3]]},"46":{"position":[[179,4]]},"48":{"position":[[270,3]]},"52":{"position":[[953,3]]},"58":{"position":[[1538,3]]},"76":{"position":[[1786,3]]},"88":{"position":[[104,3]]},"90":{"position":[[309,4]]},"94":{"position":[[30,3],[924,3]]},"120":{"position":[[236,4]]},"122":{"position":[[1252,4],[1282,3],[1392,3],[2389,3]]},"124":{"position":[[2043,3]]},"126":{"position":[[13,3]]},"136":{"position":[[1321,3]]},"138":{"position":[[62,4],[142,3],[800,3]]},"140":{"position":[[254,3]]},"152":{"position":[[337,3]]},"168":{"position":[[306,3],[658,3],[712,4]]},"170":{"position":[[231,3]]},"174":{"position":[[276,4]]},"202":{"position":[[662,3],[861,3],[2061,4]]},"216":{"position":[[3629,3]]},"218":{"position":[[34,4],[93,3],[328,3]]},"220":{"position":[[59,3]]},"224":{"position":[[185,4],[522,3],[629,4]]},"226":{"position":[[480,4]]},"230":{"position":[[161,4]]},"232":{"position":[[1260,3]]},"234":{"position":[[150,4]]},"236":{"position":[[1084,5]]},"240":{"position":[[350,4]]},"252":{"position":[[282,3],[336,3],[1020,3]]},"254":{"position":[[141,4]]},"256":{"position":[[440,4]]},"260":{"position":[[33,3],[1574,3]]},"268":{"position":[[204,3],[448,3]]},"270":{"position":[[46,3],[120,3],[273,4],[732,3],[857,3],[1164,5]]},"272":{"position":[[395,3],[589,3],[810,4],[1251,4],[1482,3]]},"274":{"position":[[333,4],[343,3]]},"278":{"position":[[99,3],[537,3],[683,4]]},"283":{"position":[[594,3]]},"289":{"position":[[1097,3]]},"291":{"position":[[729,4]]},"293":{"position":[[420,4]]},"305":{"position":[[527,5]]},"311":{"position":[[98,4]]},"321":{"position":[[35,3],[51,4],[128,3],[232,3],[693,3],[1027,3],[1217,3],[1255,4],[1444,3],[1594,4],[1681,3],[1708,3],[1925,3],[2040,5],[2180,3],[2278,3],[2306,3]]},"323":{"position":[[117,3],[718,4]]},"325":{"position":[[83,3],[217,3]]},"333":{"position":[[499,3]]},"335":{"position":[[390,4]]},"345":{"position":[[472,5],[640,4]]},"347":{"position":[[664,3]]},"355":{"position":[[655,3]]},"361":{"position":[[401,3]]},"367":{"position":[[286,3]]},"369":{"position":[[492,4]]},"407":{"position":[[410,3]]},"409":{"position":[[1164,3]]},"415":{"position":[[443,3]]},"425":{"position":[[270,4]]},"427":{"position":[[46,4]]},"429":{"position":[[635,4],[881,4]]},"441":{"position":[[296,3]]},"455":{"position":[[37,3],[561,4]]},"457":{"position":[[68,3]]},"465":{"position":[[208,3]]},"473":{"position":[[3358,3]]},"483":{"position":[[499,4]]},"489":{"position":[[229,3],[237,3]]},"491":{"position":[[115,3],[165,3],[194,3],[275,3]]},"497":{"position":[[599,4],[642,3],[775,3],[804,3],[879,4]]},"527":{"position":[[390,4]]},"553":{"position":[[401,3],[455,3],[540,3]]},"555":{"position":[[297,3]]},"559":{"position":[[268,4]]},"567":{"position":[[76,4],[283,4]]},"569":{"position":[[80,4]]},"573":{"position":[[80,4]]},"579":{"position":[[89,3]]},"581":{"position":[[137,3]]},"583":{"position":[[158,4]]},"587":{"position":[[86,3]]},"589":{"position":[[16,3],[224,4]]},"604":{"position":[[238,3],[444,3]]},"606":{"position":[[32,3],[75,3],[235,3]]},"630":{"position":[[219,6]]},"636":{"position":[[204,3],[305,3]]},"638":{"position":[[173,3]]},"640":{"position":[[543,3]]},"686":{"position":[[56,3]]},"688":{"position":[[70,3]]},"690":{"position":[[48,3],[125,3]]},"692":{"position":[[98,3]]},"756":{"position":[[7,3],[55,3],[119,6]]},"776":{"position":[[145,3]]},"794":{"position":[[294,3]]},"798":{"position":[[2165,3]]},"824":{"position":[[306,3]]},"832":{"position":[[117,4],[200,5]]},"840":{"position":[[553,4]]},"844":{"position":[[109,4],[198,5]]},"856":{"position":[[135,3]]},"858":{"position":[[1201,3]]},"884":{"position":[[190,3],[405,3],[471,4]]},"886":{"position":[[245,3]]},"888":{"position":[[151,3]]},"918":{"position":[[617,3],[994,4]]},"926":{"position":[[33,4],[162,3],[175,6],[193,3],[1153,3]]},"928":{"position":[[87,3]]},"930":{"position":[[728,7],[754,3]]},"937":{"position":[[116,3],[191,4],[279,4],[386,3]]},"969":{"position":[[1486,4]]},"971":{"position":[[1236,3]]},"973":{"position":[[3732,3]]},"1057":{"position":[[1801,3]]},"1059":{"position":[[67,3],[126,3],[260,3],[347,3]]},"1063":{"position":[[327,5]]},"1067":{"position":[[37,3]]},"1092":{"position":[[243,4],[746,4],[803,3]]},"1147":{"position":[[1415,3]]},"1162":{"position":[[223,3]]},"1191":{"position":[[1200,3]]},"1231":{"position":[[410,3]]},"1233":{"position":[[1164,3]]},"1239":{"position":[[443,3]]},"1251":{"position":[[270,4],[319,3]]},"1253":{"position":[[46,4]]},"1255":{"position":[[635,4],[881,4]]},"1267":{"position":[[296,3]]},"1277":{"position":[[472,5],[640,4]]},"1279":{"position":[[664,3]]},"1287":{"position":[[655,3]]},"1293":{"position":[[651,3]]},"1307":{"position":[[37,3],[561,4]]},"1309":{"position":[[68,3]]},"1319":{"position":[[417,3]]},"1325":{"position":[[283,3]]},"1349":{"position":[[198,3]]},"1359":{"position":[[1041,3],[1076,3]]},"1377":{"position":[[50,3]]},"1383":{"position":[[229,3],[237,3]]},"1385":{"position":[[171,3],[223,3],[261,3],[342,3]]},"1389":{"position":[[309,3]]},"1391":{"position":[[595,4],[638,3],[771,3],[800,3],[875,4]]},"1425":{"position":[[42,4]]},"1453":{"position":[[12,3],[25,4],[293,3],[330,3]]},"1455":{"position":[[5,3],[150,3],[166,3],[290,3],[431,3],[459,3],[503,3],[586,3],[687,3],[817,3],[831,3],[1028,3],[1452,3]]},"1457":{"position":[[907,4]]},"1473":{"position":[[331,3]]},"1483":{"position":[[11,3],[204,3],[395,3],[443,3],[483,3],[527,3],[603,3],[648,3]]},"1485":{"position":[[30,4],[214,3],[301,3],[351,3],[388,3],[473,3],[565,3],[832,3]]},"1487":{"position":[[846,3]]},"1489":{"position":[[180,3]]},"1491":{"position":[[83,3]]},"1505":{"position":[[8878,3],[9046,3],[9085,3],[9763,3]]},"1527":{"position":[[351,3],[404,3],[495,3],[579,3],[648,3],[723,3]]},"1529":{"position":[[297,3]]},"1535":{"position":[[76,4],[283,4]]},"1537":{"position":[[80,4]]},"1541":{"position":[[80,4]]},"1547":{"position":[[89,3]]},"1549":{"position":[[137,3]]},"1551":{"position":[[158,4]]},"1569":{"position":[[501,4],[573,4]]},"1571":{"position":[[1442,4]]},"1591":{"position":[[825,3]]},"1593":{"position":[[268,4]]},"1597":{"position":[[1104,4]]},"1613":{"position":[[74,4]]},"1629":{"position":[[219,6]]},"1636":{"position":[[204,3],[305,3]]},"1639":{"position":[[173,3]]},"1676":{"position":[[52,4]]},"1680":{"position":[[180,3]]},"1682":{"position":[[392,3],[763,3],[1324,4]]},"1684":{"position":[[68,4],[88,4],[386,4]]},"1686":{"position":[[341,4]]},"1688":{"position":[[311,3],[401,3],[711,3],[834,4],[966,4]]},"1692":{"position":[[881,3],[1212,4]]},"1719":{"position":[[229,4]]},"1721":{"position":[[5,3],[191,3],[401,3],[588,3],[701,3]]},"1729":{"position":[[56,3]]},"1731":{"position":[[70,3]]},"1733":{"position":[[48,3],[125,3]]},"1735":{"position":[[98,3]]},"1773":{"position":[[116,5]]},"1791":{"position":[[145,3]]},"1809":{"position":[[367,3]]},"1813":{"position":[[2165,3]]},"1817":{"position":[[851,3]]},"1843":{"position":[[1232,3]]},"1847":{"position":[[1251,3]]},"1849":{"position":[[669,5]]},"1855":{"position":[[1143,3]]},"1857":{"position":[[645,4]]},"1865":{"position":[[601,3]]},"1867":{"position":[[574,3]]},"1871":{"position":[[115,3]]},"1899":{"position":[[274,4],[369,3],[1122,3]]},"1979":{"position":[[1561,4]]},"1981":{"position":[[1236,3]]},"1983":{"position":[[1561,4],[4877,3]]},"1997":{"position":[[190,3],[405,3],[471,4]]},"1999":{"position":[[245,3]]},"2001":{"position":[[151,3]]},"2039":{"position":[[832,3],[1209,4]]},"2047":{"position":[[33,4],[162,3],[175,6],[193,3],[1153,3]]},"2049":{"position":[[87,3]]},"2051":{"position":[[917,7],[943,3]]},"2056":{"position":[[203,3]]},"2058":{"position":[[116,3],[191,4],[279,4],[386,3]]},"2090":{"position":[[1200,3]]},"2118":{"position":[[521,4]]},"2130":{"position":[[37,3]]},"2140":{"position":[[1801,3]]},"2142":{"position":[[67,3],[126,3],[260,3],[347,3]]},"2172":{"position":[[405,3]]},"2187":{"position":[[243,4],[749,4],[806,3]]},"2260":{"position":[[1494,3]]},"2285":{"position":[[12,3],[25,4],[302,3],[339,3]]},"2287":{"position":[[5,3],[153,3],[169,3],[293,3],[434,3],[462,3],[506,3],[589,3],[690,3],[820,3],[834,3],[1031,3],[1455,3]]},"2289":{"position":[[967,4],[2432,3]]},"2291":{"position":[[726,3]]},"2305":{"position":[[331,3]]},"2309":{"position":[[830,3]]},"2315":{"position":[[11,3],[204,3],[395,3],[443,3],[483,3],[527,3],[603,3],[648,3]]},"2317":{"position":[[30,4],[214,3],[301,3],[351,3],[388,3],[473,3],[565,3],[832,3]]},"2319":{"position":[[846,3]]},"2321":{"position":[[180,3]]},"2323":{"position":[[83,3]]},"2342":{"position":[[223,3]]},"2392":{"position":[[32,3],[705,4]]},"2400":{"position":[[916,3],[1107,4]]},"2402":{"position":[[705,3]]},"2410":{"position":[[665,3]]},"2428":{"position":[[696,3]]},"2434":{"position":[[421,3]]},"2440":{"position":[[284,3]]},"2442":{"position":[[225,5]]},"2446":{"position":[[206,4]]},"2464":{"position":[[202,3]]},"2480":{"position":[[410,3]]},"2482":{"position":[[1164,3]]},"2488":{"position":[[443,3]]},"2500":{"position":[[270,4],[319,3]]},"2502":{"position":[[46,4]]},"2504":{"position":[[635,4],[881,4]]},"2516":{"position":[[296,3]]},"2530":{"position":[[1041,3],[1076,3]]},"2548":{"position":[[50,3]]},"2554":{"position":[[236,3],[248,3]]},"2556":{"position":[[180,3],[244,3],[282,3],[367,3]]},"2560":{"position":[[318,3]]},"2562":{"position":[[608,4],[652,3],[790,3],[819,3],[896,4]]},"2578":{"position":[[22,3],[154,3],[264,3],[373,3],[523,4],[751,3],[777,3],[965,3]]},"2580":{"position":[[85,3]]},"2594":{"position":[[37,3],[583,4]]},"2596":{"position":[[68,3]]},"2636":{"position":[[595,5]]},"2660":{"position":[[361,3]]},"2664":{"position":[[501,4],[573,4]]},"2666":{"position":[[1442,4]]},"2686":{"position":[[8998,3],[9170,3],[9216,3],[9919,3],[10017,3]]},"2708":{"position":[[2726,3]]},"2712":{"position":[[697,3]]},"2716":{"position":[[18,3]]},"2740":{"position":[[76,4],[283,4]]},"2742":{"position":[[80,4]]},"2746":{"position":[[80,4]]},"2752":{"position":[[89,3]]},"2754":{"position":[[137,3]]},"2756":{"position":[[158,4]]},"2760":{"position":[[352,3],[405,3],[496,3],[580,3],[649,3],[724,3]]},"2764":{"position":[[296,3]]},"2776":{"position":[[268,4]]},"2780":{"position":[[1104,4],[1170,3]]},"2784":{"position":[[825,3]]},"2798":{"position":[[74,4]]},"2816":{"position":[[219,6]]},"2823":{"position":[[204,3],[305,3],[478,3]]},"2826":{"position":[[173,3],[355,3]]},"2840":{"position":[[52,4]]},"2844":{"position":[[180,3]]},"2846":{"position":[[392,3],[763,3],[1324,4]]},"2848":{"position":[[68,4],[88,4],[386,4]]},"2850":{"position":[[341,4]]},"2852":{"position":[[311,3],[401,3],[711,3],[834,4],[966,4]]},"2856":{"position":[[881,3],[1212,4]]},"2883":{"position":[[229,4]]},"2887":{"position":[[5,3],[191,3],[401,3],[588,3],[701,3]]},"2904":{"position":[[70,3]]},"2906":{"position":[[70,3]]},"2918":{"position":[[56,3]]},"2920":{"position":[[70,3],[304,3]]},"2922":{"position":[[48,3],[125,3],[211,3]]},"2924":{"position":[[98,3],[169,3]]},"2932":{"position":[[116,5]]},"3020":{"position":[[178,3]]},"3034":{"position":[[512,3]]},"3038":{"position":[[403,3]]},"3046":{"position":[[995,3]]},"3072":{"position":[[1520,3]]},"3076":{"position":[[635,3],[1215,3]]},"3078":{"position":[[669,5]]},"3084":{"position":[[816,3],[1107,3]]},"3088":{"position":[[645,4]]},"3096":{"position":[[601,3]]},"3098":{"position":[[574,3]]},"3102":{"position":[[115,3]]},"3134":{"position":[[274,4],[369,3],[1122,3]]},"3138":{"position":[[1561,4]]},"3140":{"position":[[1236,3]]},"3142":{"position":[[1561,4],[4877,3]]},"3186":{"position":[[190,3],[405,3],[471,4]]},"3188":{"position":[[245,3]]},"3190":{"position":[[151,3]]},"3230":{"position":[[832,3],[1209,4]]},"3238":{"position":[[83,4]]},"3240":{"position":[[33,4],[162,3],[175,6],[193,3],[592,3],[1243,3]]},"3242":{"position":[[87,3]]},"3244":{"position":[[1004,7],[1030,3]]},"3249":{"position":[[203,3]]},"3253":{"position":[[116,3],[191,4],[279,4],[386,3]]},"3275":{"position":[[1200,3]]},"3324":{"position":[[5886,3],[5931,3],[6106,3],[6151,3]]},"3327":{"position":[[68,3],[454,3],[808,3]]},"3333":{"position":[[37,3]]},"3341":{"position":[[356,3],[499,4],[709,3],[864,4]]},"3355":{"position":[[1801,3]]},"3357":{"position":[[67,3],[126,3],[260,3],[347,3]]},"3363":{"position":[[422,3]]},"3365":{"position":[[711,3],[1098,4],[2702,3]]},"3401":{"position":[[405,3]]},"3464":{"position":[[243,4],[749,4],[806,3]]},"3496":{"position":[[12,3],[25,4],[142,3],[189,3],[311,3],[495,3],[532,3],[625,3]]},"3498":{"position":[[5,3],[169,3],[206,3],[236,3],[287,3],[371,3],[415,3],[432,3],[512,3],[647,3],[799,4],[804,3]]},"3500":{"position":[[7,3]]},"3502":{"position":[[237,3],[723,3],[1037,3],[1940,3]]},"3504":{"position":[[114,3],[801,3]]},"3514":{"position":[[267,3]]},"3516":{"position":[[149,3]]},"3518":{"position":[[331,3],[482,3]]},"3522":{"position":[[94,3],[890,3]]},"3526":{"position":[[315,3]]},"3528":{"position":[[11,3],[204,3],[395,3],[443,3],[483,3],[527,3],[603,3],[648,3]]},"3530":{"position":[[30,4],[214,3],[301,3],[351,3],[388,3],[473,3],[565,3],[832,3]]},"3532":{"position":[[846,3]]},"3534":{"position":[[180,3]]},"3536":{"position":[[83,3]]},"3538":{"position":[[30,3],[119,3],[212,3],[310,3],[333,3],[598,3],[849,3],[1615,3],[1683,3],[1997,4],[2226,3]]},"3540":{"position":[[624,3]]},"3542":{"position":[[527,3]]},"3610":{"position":[[1494,3]]},"3612":{"position":[[32,3],[732,4]]},"3627":{"position":[[223,3]]},"3647":{"position":[[595,5]]},"3671":{"position":[[361,3]]}}}],["api.app",{"_index":2988,"t":{"341":{"position":[[1017,7],[1459,7]]},"1211":{"position":[[1017,7],[1459,7]]},"2394":{"position":[[1017,7],[1459,7]]}}}],["api.proto",{"_index":4645,"t":{"1487":{"position":[[170,9]]},"1489":{"position":[[496,9],[548,9]]},"2319":{"position":[[170,9]]},"2321":{"position":[[496,9],[548,9]]},"3532":{"position":[[170,9]]},"3534":{"position":[[496,9],[548,9]]}}}],["api/login",{"_index":2570,"t":{"246":{"position":[[1080,9]]}}}],["ha",{"_index":713,"t":{"16":{"position":[[6486,2]]},"1193":{"position":[[295,3]]},"1195":{"position":[[280,2]]},"2092":{"position":[[295,3]]},"2096":{"position":[[280,2]]},"3277":{"position":[[295,3]]},"3281":{"position":[[280,2]]}}}],["hand",{"_index":2362,"t":{"206":{"position":[[729,6]]},"254":{"position":[[585,6]]},"2578":{"position":[[402,4]]}}}],["handi",{"_index":1209,"t":{"60":{"position":[[1201,5]]},"262":{"position":[[29,6]]},"824":{"position":[[647,5]]},"1865":{"position":[[923,5]]},"3096":{"position":[[923,5]]}}}],["handl",{"_index":145,"t":{"2":{"position":[[2209,6],[2495,6]]},"32":{"position":[[926,8],[945,8],[993,9]]},"78":{"position":[[296,6]]},"86":{"position":[[1385,8]]},"104":{"position":[[508,6]]},"122":{"position":[[523,9],[1964,8],[2133,6]]},"132":{"position":[[1410,6]]},"158":{"position":[[780,6]]},"160":{"position":[[553,6]]},"178":{"position":[[643,6]]},"184":{"position":[[481,6]]},"196":{"position":[[1065,6]]},"202":{"position":[[1183,6]]},"206":{"position":[[452,6]]},"238":{"position":[[72,9],[95,6],[249,6]]},"240":{"position":[[617,8]]},"242":{"position":[[1062,6]]},"254":{"position":[[359,6]]},"262":{"position":[[539,6],[733,6],[1065,6],[1317,6]]},"291":{"position":[[339,6],[369,7]]},"295":{"position":[[967,6]]},"317":{"position":[[159,8]]},"357":{"position":[[158,6],[1241,6]]},"367":{"position":[[198,6]]},"373":{"position":[[55,6]]},"399":{"position":[[233,6]]},"437":{"position":[[1081,6]]},"439":{"position":[[97,7]]},"457":{"position":[[3,6]]},"495":{"position":[[167,6]]},"499":{"position":[[301,8]]},"511":{"position":[[344,6]]},"525":{"position":[[656,6]]},"527":{"position":[[77,8]]},"555":{"position":[[217,7]]},"726":{"position":[[302,8]]},"782":{"position":[[0,7]]},"784":{"position":[[0,7]]},"798":{"position":[[1055,6]]},"949":{"position":[[90,6],[321,7]]},"973":{"position":[[2133,7]]},"977":{"position":[[177,8]]},"1001":{"position":[[0,7]]},"1003":{"position":[[0,7]]},"1057":{"position":[[382,8],[897,6],[1007,8]]},"1130":{"position":[[253,6]]},"1223":{"position":[[233,6]]},"1263":{"position":[[1085,6]]},"1265":{"position":[[97,7]]},"1289":{"position":[[158,6],[1241,6]]},"1309":{"position":[[3,6]]},"1325":{"position":[[194,6]]},"1331":{"position":[[55,6]]},"1355":{"position":[[664,6],[1310,6]]},"1389":{"position":[[176,6]]},"1393":{"position":[[301,8]]},"1403":{"position":[[826,6],[874,6],[920,6],[1396,6],[1819,6],[1851,6]]},"1405":{"position":[[3447,8]]},"1421":{"position":[[14,6]]},"1423":{"position":[[39,8],[130,7]]},"1425":{"position":[[2665,6]]},"1427":{"position":[[142,6]]},"1439":{"position":[[346,6]]},"1487":{"position":[[828,6]]},"1505":{"position":[[3933,6],[5579,8]]},"1529":{"position":[[217,7]]},"1666":{"position":[[1776,7]]},"1670":{"position":[[537,6]]},"1688":{"position":[[253,7]]},"1813":{"position":[[1055,6]]},"1817":{"position":[[785,8]]},"1919":{"position":[[90,6],[321,7]]},"1953":{"position":[[104,6]]},"1961":{"position":[[302,8]]},"1967":{"position":[[1143,6],[1320,6]]},"2021":{"position":[[150,7]]},"2023":{"position":[[323,6]]},"2070":{"position":[[177,8]]},"2140":{"position":[[382,8],[897,6],[1007,8]]},"2150":{"position":[[1114,6],[1398,8]]},"2158":{"position":[[14,6]]},"2160":{"position":[[39,8],[130,7]]},"2172":{"position":[[313,6]]},"2174":{"position":[[310,6]]},"2185":{"position":[[462,6]]},"2191":{"position":[[298,6]]},"2281":{"position":[[237,6]]},"2319":{"position":[[828,6]]},"2412":{"position":[[162,6],[1261,6]]},"2440":{"position":[[195,6]]},"2446":{"position":[[55,6]]},"2472":{"position":[[233,6]]},"2514":{"position":[[97,7]]},"2526":{"position":[[479,8]]},"2560":{"position":[[181,6]]},"2564":{"position":[[309,8]]},"2596":{"position":[[3,6]]},"2608":{"position":[[349,6]]},"2660":{"position":[[424,6]]},"2686":{"position":[[3991,6],[5641,8]]},"2764":{"position":[[217,7]]},"2830":{"position":[[1779,7]]},"2834":{"position":[[537,6]]},"2852":{"position":[[253,7]]},"2998":{"position":[[104,6]]},"3006":{"position":[[302,8]]},"3012":{"position":[[1143,6],[1320,6]]},"3042":{"position":[[1003,6]]},"3046":{"position":[[929,8]]},"3146":{"position":[[90,6],[321,7]]},"3210":{"position":[[123,7]]},"3212":{"position":[[296,6]]},"3307":{"position":[[177,8]]},"3355":{"position":[[382,8],[897,6],[1007,8]]},"3367":{"position":[[1487,8],[2265,6],[2403,8],[3596,6]]},"3369":{"position":[[1124,8],[1590,8]]},"3379":{"position":[[1114,6],[1398,8]]},"3387":{"position":[[14,6]]},"3389":{"position":[[39,8],[130,7]]},"3401":{"position":[[313,6]]},"3403":{"position":[[310,6]]},"3456":{"position":[[608,7]]},"3462":{"position":[[497,6]]},"3466":{"position":[[298,6]]},"3486":{"position":[[588,7]]},"3532":{"position":[[828,6]]},"3563":{"position":[[212,6]]},"3671":{"position":[[424,6]]}}}],["handler",{"_index":1223,"t":{"62":{"position":[[145,9],[729,7]]},"64":{"position":[[760,7]]},"66":{"position":[[1184,8],[1212,7],[1311,7],[1456,7],[2079,8]]},"68":{"position":[[390,8]]},"70":{"position":[[382,7]]},"76":{"position":[[962,7]]},"110":{"position":[[399,7],[1232,8]]},"140":{"position":[[335,8]]},"170":{"position":[[823,8]]},"176":{"position":[[1166,8],[1950,7],[2431,8]]},"200":{"position":[[266,8],[877,7],[1273,7],[1638,7]]},"202":{"position":[[415,9]]},"246":{"position":[[1883,7],[2461,7]]},"250":{"position":[[1599,7]]},"262":{"position":[[489,7]]},"293":{"position":[[1132,8]]},"297":{"position":[[174,7]]},"453":{"position":[[41,8]]},"930":{"position":[[39,7]]},"973":{"position":[[3285,7]]},"1017":{"position":[[4801,7],[5480,7]]},"1305":{"position":[[41,8]]},"1403":{"position":[[693,8]]},"1409":{"position":[[987,7]]},"1423":{"position":[[637,7]]},"1425":{"position":[[450,8],[1961,9],[1971,7],[2030,7],[2052,7],[2066,7],[2098,7],[2124,7],[2271,8]]},"1427":{"position":[[318,8],[404,8]]},"1983":{"position":[[3001,7]]},"2051":{"position":[[39,7]]},"2160":{"position":[[637,7]]},"2354":{"position":[[1044,8],[4924,7],[5603,7]]},"2360":{"position":[[4127,7]]},"2592":{"position":[[41,8]]},"2660":{"position":[[63,9]]},"2708":{"position":[[1457,7]]},"3142":{"position":[[3001,7]]},"3244":{"position":[[39,7]]},"3367":{"position":[[2090,7],[2221,8],[2253,8],[2363,7],[4486,7]]},"3369":{"position":[[1052,8],[1513,7]]},"3389":{"position":[[637,7]]},"3415":{"position":[[1044,8],[4953,7],[5632,7]]},"3421":{"position":[[4130,7]]},"3671":{"position":[[63,9]]}}}],["handlesession(sess",{"_index":1669,"t":{"106":{"position":[[682,18]]},"110":{"position":[[1274,18]]}}}],["handshak",{"_index":368,"t":{"8":{"position":[[1339,9]]},"106":{"position":[[1639,10]]},"902":{"position":[[288,9]]},"1057":{"position":[[1640,9]]},"2015":{"position":[[288,9]]},"2088":{"position":[[1143,9]]},"2094":{"position":[[1200,9]]},"2140":{"position":[[1640,9]]},"3204":{"position":[[288,9]]},"3273":{"position":[[1035,9]]},"3279":{"position":[[1092,9]]},"3355":{"position":[[1640,9]]}}}],["happen",{"_index":394,"t":{"10":{"position":[[131,10],[1269,7]]},"28":{"position":[[592,9]]},"70":{"position":[[132,7]]},"72":{"position":[[664,6]]},"108":{"position":[[227,7]]},"140":{"position":[[226,9]]},"194":{"position":[[2784,8]]},"232":{"position":[[136,6]]},"337":{"position":[[37,8]]},"455":{"position":[[789,7]]},"479":{"position":[[4078,8]]},"481":{"position":[[1189,8]]},"497":{"position":[[319,8]]},"527":{"position":[[289,7]]},"724":{"position":[[178,6]]},"768":{"position":[[176,6]]},"798":{"position":[[132,7]]},"866":{"position":[[152,6]]},"868":{"position":[[355,6]]},"870":{"position":[[191,6]]},"951":{"position":[[39,6]]},"953":{"position":[[144,6]]},"997":{"position":[[651,7]]},"1025":{"position":[[249,7],[627,8]]},"1307":{"position":[[789,7]]},"1391":{"position":[[316,8]]},"1423":{"position":[[86,6]]},"1427":{"position":[[651,6]]},"1519":{"position":[[333,7]]},"1721":{"position":[[48,6]]},"1751":{"position":[[651,7]]},"1813":{"position":[[132,7]]},"1905":{"position":[[191,6]]},"1909":{"position":[[355,6]]},"1911":{"position":[[416,6]]},"1913":{"position":[[191,6]]},"1915":{"position":[[199,6]]},"1921":{"position":[[39,6]]},"1923":{"position":[[144,6]]},"1959":{"position":[[175,6]]},"1967":{"position":[[1183,7]]},"2160":{"position":[[86,6]]},"2360":{"position":[[4310,6]]},"2362":{"position":[[249,7],[627,8]]},"2562":{"position":[[319,8]]},"2594":{"position":[[820,7]]},"2622":{"position":[[1033,7],[1117,6],[1194,8],[4878,9],[4972,9]]},"2632":{"position":[[4491,9]]},"2702":{"position":[[333,7]]},"2887":{"position":[[48,6]]},"2956":{"position":[[651,7]]},"3004":{"position":[[175,6]]},"3012":{"position":[[1183,7]]},"3148":{"position":[[39,6]]},"3150":{"position":[[144,6]]},"3162":{"position":[[191,6]]},"3166":{"position":[[355,6]]},"3168":{"position":[[416,6]]},"3170":{"position":[[191,6]]},"3172":{"position":[[199,6]]},"3363":{"position":[[444,8]]},"3365":{"position":[[53,9]]},"3389":{"position":[[86,6]]},"3421":{"position":[[569,7],[4313,6]]},"3423":{"position":[[249,7],[627,8]]},"3633":{"position":[[1033,7],[1117,6],[1194,8],[4878,9],[4972,9]]},"3643":{"position":[[4491,9]]}}}],["happi",{"_index":837,"t":{"24":{"position":[[60,5]]},"86":{"position":[[1345,5]]},"256":{"position":[[83,5]]},"1177":{"position":[[1767,5]]},"2228":{"position":[[1309,5]]},"3578":{"position":[[1284,5]]}}}],["haproxi",{"_index":3175,"t":{"473":{"position":[[699,7]]},"1195":{"position":[[27,7],[337,7],[892,8]]},"1929":{"position":[[571,7]]},"2096":{"position":[[27,7],[337,7],[892,8]]},"3156":{"position":[[571,7]]},"3281":{"position":[[27,7],[337,7],[892,8]]}}}],["hard",{"_index":369,"t":{"8":{"position":[[1364,4]]},"12":{"position":[[1426,4]]},"38":{"position":[[277,4]]},"58":{"position":[[311,4],[1380,4]]},"78":{"position":[[24,4]]},"86":{"position":[[1136,4]]},"122":{"position":[[965,4],[990,4]]},"132":{"position":[[1374,4]]},"246":{"position":[[2430,4]]},"278":{"position":[[64,4]]},"305":{"position":[[206,4]]},"411":{"position":[[819,4]]},"431":{"position":[[577,4]]},"479":{"position":[[5217,4]]},"513":{"position":[[405,5]]},"945":{"position":[[599,5]]},"1203":{"position":[[461,4]]},"1235":{"position":[[819,4]]},"1257":{"position":[[577,4]]},"1441":{"position":[[429,5]]},"1849":{"position":[[940,4]]},"2066":{"position":[[599,5]]},"2106":{"position":[[461,4]]},"2418":{"position":[[392,4]]},"2484":{"position":[[819,4]]},"2506":{"position":[[577,4]]},"2610":{"position":[[429,5]]},"3078":{"position":[[940,4]]},"3261":{"position":[[599,5]]},"3289":{"position":[[461,4]]}}}],["hardcod",{"_index":2597,"t":{"246":{"position":[[2223,9]]},"479":{"position":[[5381,9]]}}}],["harder",{"_index":633,"t":{"16":{"position":[[2506,7]]},"78":{"position":[[212,7]]},"82":{"position":[[683,6]]},"335":{"position":[[923,6]]},"411":{"position":[[909,6]]},"439":{"position":[[148,6]]},"1235":{"position":[[909,6]]},"1265":{"position":[[148,6]]},"2484":{"position":[[909,6]]},"2514":{"position":[[148,6]]}}}],["hardest",{"_index":1796,"t":{"122":{"position":[[92,7]]},"152":{"position":[[97,7]]},"264":{"position":[[330,7]]}}}],["hardli",{"_index":2659,"t":{"260":{"position":[[137,6]]}}}],["hardwar",{"_index":3,"t":{"2":{"position":[[48,8]]},"84":{"position":[[225,9],[326,8],[842,8]]},"202":{"position":[[1230,8]]},"397":{"position":[[58,9]]},"1221":{"position":[[58,9]]},"2470":{"position":[[58,9]]}}}],["has_mor",{"_index":4974,"t":{"1709":{"position":[[1021,8]]},"1713":{"position":[[969,8]]},"1717":{"position":[[518,8]]}}}],["hash",{"_index":1429,"t":{"78":{"position":[[2684,4]]},"467":{"position":[[857,4],[1547,4]]},"475":{"position":[[3759,5]]},"1189":{"position":[[1016,4]]},"1197":{"position":[[1206,7],[1258,4]]},"1199":{"position":[[1169,7]]},"2086":{"position":[[1085,4]]},"2098":{"position":[[1206,7],[1258,4]]},"2100":{"position":[[1169,7]]},"3271":{"position":[[1051,4]]},"3283":{"position":[[1206,7],[1258,4]]},"3285":{"position":[[1169,7]]}}}],["hasn't",{"_index":1862,"t":{"128":{"position":[[878,6]]}}}],["have",{"_index":636,"t":{"16":{"position":[[2677,7],[3431,6]]},"20":{"position":[[923,6]]},"34":{"position":[[1601,6]]},"124":{"position":[[2758,6]]},"128":{"position":[[1479,6]]},"132":{"position":[[128,6],[386,6]]},"144":{"position":[[0,6]]},"148":{"position":[[636,6]]},"242":{"position":[[1521,6]]},"289":{"position":[[601,6]]},"295":{"position":[[1681,6]]},"347":{"position":[[36,6],[552,6]]},"411":{"position":[[664,6]]},"437":{"position":[[1726,6],[2092,6]]},"475":{"position":[[702,6]]},"509":{"position":[[1076,6]]},"716":{"position":[[209,6]]},"1235":{"position":[[664,6]]},"1263":{"position":[[1730,6],[2096,6]]},"1271":{"position":[[317,6]]},"1279":{"position":[[36,6],[552,6]]},"1405":{"position":[[2726,6]]},"1437":{"position":[[1309,6]]},"1851":{"position":[[370,6]]},"1857":{"position":[[1051,6]]},"1859":{"position":[[806,6]]},"1951":{"position":[[206,6]]},"1983":{"position":[[4040,6]]},"2150":{"position":[[554,6]]},"2402":{"position":[[36,6],[589,6]]},"2484":{"position":[[664,6]]},"2512":{"position":[[1972,6],[2338,6]]},"2520":{"position":[[317,6]]},"2578":{"position":[[566,6]]},"2632":{"position":[[290,6]]},"2636":{"position":[[28,6]]},"2996":{"position":[[206,6]]},"3080":{"position":[[370,6]]},"3088":{"position":[[1051,6]]},"3090":{"position":[[806,6]]},"3142":{"position":[[4040,6]]},"3379":{"position":[[554,6]]},"3643":{"position":[[290,6]]},"3647":{"position":[[28,6]]}}}],["haven’t",{"_index":2230,"t":{"192":{"position":[[3233,7]]},"194":{"position":[[2991,7]]}}}],["head",{"_index":1510,"t":{"94":{"position":[[718,4]]}}}],["headach",{"_index":3572,"t":{"525":{"position":[[195,8]]},"1355":{"position":[[208,8]]},"2526":{"position":[[225,9]]}}}],["header",{"_index":2032,"t":{"170":{"position":[[589,6]]},"172":{"position":[[549,6]]},"198":{"position":[[194,7]]},"200":{"position":[[2208,7]]},"202":{"position":[[176,6]]},"216":{"position":[[1728,8],[2038,9]]},"224":{"position":[[511,8]]},"250":{"position":[[204,6]]},"252":{"position":[[438,8]]},"321":{"position":[[252,6],[296,6],[1390,6],[1434,6],[1689,6]]},"371":{"position":[[195,7]]},"379":{"position":[[185,7]]},"608":{"position":[[61,7],[325,7]]},"618":{"position":[[179,6]]},"636":{"position":[[424,6],[468,6]]},"638":{"position":[[301,6],[345,6]]},"656":{"position":[[16,6],[60,6]]},"658":{"position":[[16,6],[60,6]]},"672":{"position":[[1567,7]]},"688":{"position":[[250,6],[294,6]]},"690":{"position":[[157,6],[201,6]]},"692":{"position":[[115,6],[159,6]]},"810":{"position":[[180,6]]},"892":{"position":[[425,6],[944,6]]},"937":{"position":[[469,6]]},"1011":{"position":[[154,7]]},"1013":{"position":[[98,7],[285,7],[510,7],[700,6]]},"1015":{"position":[[291,7],[404,7]]},"1017":{"position":[[3552,6]]},"1031":{"position":[[516,7],[568,7]]},"1037":{"position":[[239,7],[290,7],[413,7]]},"1041":{"position":[[266,7],[757,7]]},"1045":{"position":[[1508,7],[1540,7]]},"1329":{"position":[[208,7]]},"1337":{"position":[[174,7]]},"1403":{"position":[[503,7]]},"1455":{"position":[[215,6],[397,7],[532,6],[1226,6]]},"1483":{"position":[[162,7]]},"1505":{"position":[[9807,6],[9851,6],[10079,6]]},"1597":{"position":[[1116,6],[1160,6]]},"1603":{"position":[[2133,7]]},"1605":{"position":[[264,9],[963,9]]},"1636":{"position":[[424,6],[468,6]]},"1639":{"position":[[301,6],[345,6]]},"1652":{"position":[[16,6],[60,6]]},"1654":{"position":[[16,6],[60,6]]},"1719":{"position":[[1764,7],[1800,7]]},"1731":{"position":[[250,6],[294,6]]},"1733":{"position":[[157,6],[201,6]]},"1735":{"position":[[115,6],[159,6]]},"1825":{"position":[[180,6]]},"1847":{"position":[[581,6],[625,6]]},"1855":{"position":[[762,6],[806,6]]},"2005":{"position":[[425,6],[944,6]]},"2058":{"position":[[469,6]]},"2118":{"position":[[288,6],[332,6]]},"2287":{"position":[[218,6],[400,7],[535,6],[1229,6]]},"2313":{"position":[[200,6]]},"2315":{"position":[[162,7]]},"2348":{"position":[[154,7]]},"2350":{"position":[[98,7],[285,7],[510,7],[700,6]]},"2352":{"position":[[291,7],[404,7]]},"2354":{"position":[[3669,6]]},"2370":{"position":[[516,7],[568,7]]},"2376":{"position":[[239,7],[290,7],[413,7]]},"2380":{"position":[[240,7],[731,7]]},"2384":{"position":[[1508,7],[1540,7]]},"2444":{"position":[[216,7]]},"2452":{"position":[[174,7]]},"2578":{"position":[[800,6]]},"2624":{"position":[[386,7]]},"2628":{"position":[[909,8],[922,9]]},"2644":{"position":[[891,8],[904,9]]},"2686":{"position":[[9963,6],[10007,6],[10199,6]]},"2780":{"position":[[1116,6],[1160,6]]},"2788":{"position":[[2133,7]]},"2790":{"position":[[264,9],[963,9]]},"2823":{"position":[[424,6],[468,6]]},"2826":{"position":[[301,6],[345,6]]},"2883":{"position":[[2043,7],[2079,7]]},"2904":{"position":[[16,6],[60,6]]},"2906":{"position":[[16,6],[60,6]]},"2920":{"position":[[250,6],[294,6]]},"2922":{"position":[[157,6],[201,6]]},"2924":{"position":[[115,6],[159,6]]},"3054":{"position":[[180,6]]},"3076":{"position":[[581,6],[625,6]]},"3084":{"position":[[762,6],[806,6]]},"3194":{"position":[[425,6],[944,6]]},"3220":{"position":[[246,8],[418,6],[524,6],[626,7],[1113,6],[1266,6]]},"3253":{"position":[[469,6]]},"3341":{"position":[[302,6],[346,6],[655,6],[699,6]]},"3409":{"position":[[159,7]]},"3411":{"position":[[98,7],[285,7],[510,7],[700,6],[897,7],[1007,8],[1233,8],[1266,7],[1298,6],[1353,6]]},"3413":{"position":[[291,7],[404,7]]},"3415":{"position":[[3669,6]]},"3431":{"position":[[516,7],[568,7]]},"3437":{"position":[[239,7],[290,7],[413,7]]},"3441":{"position":[[240,7],[731,7]]},"3445":{"position":[[1508,7],[1540,7],[1603,7],[1653,7]]},"3496":{"position":[[197,6],[301,6]]},"3498":{"position":[[214,6],[440,6]]},"3502":{"position":[[227,6],[670,10],[1027,6]]},"3504":{"position":[[104,6]]},"3514":{"position":[[257,6]]},"3516":{"position":[[139,6]]},"3518":{"position":[[472,6]]},"3522":{"position":[[84,6]]},"3526":{"position":[[305,6]]},"3528":{"position":[[162,7]]},"3538":{"position":[[1755,6]]},"3635":{"position":[[386,7]]},"3655":{"position":[[953,8],[966,9]]}}}],["headers.key",{"_index":3778,"t":{"674":{"position":[[299,13],[1078,13]]}}}],["headers.valu",{"_index":3779,"t":{"674":{"position":[[328,15],[1107,15]]}}}],["headers/metadata",{"_index":4240,"t":{"1037":{"position":[[161,16]]},"2376":{"position":[[161,16]]},"3437":{"position":[[161,16]]}}}],["headers=headers)print(resp.json",{"_index":4566,"t":{"1457":{"position":[[674,34]]},"2289":{"position":[[734,34]]},"3502":{"position":[[819,34]]}}}],["health",{"_index":3858,"t":{"682":{"position":[[685,7]]},"924":{"position":[[4,6],[59,6],[113,8],[189,6]]},"926":{"position":[[52,7],[376,6],[398,9],[421,6]]},"930":{"position":[[882,10],[906,6]]},"1619":{"position":[[761,7]]},"2045":{"position":[[4,6],[59,6],[113,8],[189,6]]},"2047":{"position":[[52,7],[376,6],[398,9],[421,6]]},"2051":{"position":[[1071,10],[1095,6]]},"2806":{"position":[[54,7]]},"3236":{"position":[[4,6],[59,6],[113,8],[189,6]]},"3240":{"position":[[52,7],[376,6],[398,9],[421,6]]},"3244":{"position":[[1158,10],[1182,6]]},"3324":{"position":[[2564,6]]}}}],["health_handler_prefix",{"_index":4083,"t":{"930":{"position":[[851,21]]},"2051":{"position":[[1040,21]]},"3244":{"position":[[1127,21]]}}}],["healthi",{"_index":697,"t":{"16":{"position":[[5346,7]]}}}],["heard",{"_index":1194,"t":{"60":{"position":[[102,5]]},"94":{"position":[[112,5]]},"118":{"position":[[387,5]]},"260":{"position":[[216,5]]}}}],["heart",{"_index":1160,"t":{"56":{"position":[[52,5]]}}}],["heavi",{"_index":679,"t":{"16":{"position":[[4498,5]]},"258":{"position":[[1241,5]]},"409":{"position":[[363,5],[394,5]]},"1233":{"position":[[363,5],[394,5]]},"1477":{"position":[[640,5]]},"2309":{"position":[[687,5]]},"2482":{"position":[[363,5],[394,5]]},"2660":{"position":[[251,5]]},"3522":{"position":[[747,5]]},"3671":{"position":[[251,5]]}}}],["heavili",{"_index":499,"t":{"12":{"position":[[781,7]]},"16":{"position":[[5419,7]]},"50":{"position":[[1963,7]]},"280":{"position":[[1661,7]]},"567":{"position":[[140,7]]},"569":{"position":[[144,7]]},"571":{"position":[[142,7]]},"573":{"position":[[144,7]]},"973":{"position":[[2068,7]]},"1535":{"position":[[140,7]]},"1537":{"position":[[144,7]]},"1539":{"position":[[142,7]]},"1541":{"position":[[144,7]]},"2740":{"position":[[140,7]]},"2742":{"position":[[144,7]]},"2744":{"position":[[142,7]]},"2746":{"position":[[144,7]]},"3367":{"position":[[653,7]]}}}],["hello",{"_index":1306,"t":{"66":{"position":[[2457,11]]},"68":{"position":[[320,10],[607,11]]},"74":{"position":[[149,11]]},"194":{"position":[[2751,7]]},"200":{"position":[[2523,7],[2567,7]]},"270":{"position":[[507,8]]},"1057":{"position":[[1650,6]]},"1177":{"position":[[1133,11],[1705,10]]},"1457":{"position":[[196,7],[820,11],[1190,7],[1526,11]]},"1459":{"position":[[167,7]]},"1473":{"position":[[849,7],[895,7]]},"1489":{"position":[[1065,6]]},"1491":{"position":[[924,6]]},"2140":{"position":[[1650,6]]},"2150":{"position":[[1630,5]]},"2228":{"position":[[1133,11]]},"2289":{"position":[[257,7],[880,11],[1250,7],[1580,11]]},"2291":{"position":[[167,7]]},"2305":{"position":[[849,7],[895,7]]},"2321":{"position":[[1065,6]]},"2323":{"position":[[924,6]]},"3355":{"position":[[1650,6]]},"3379":{"position":[[1630,5]]},"3502":{"position":[[321,10],[1129,10]]},"3504":{"position":[[213,10]]},"3518":{"position":[[698,7],[744,7]]},"3534":{"position":[[1065,6]]},"3536":{"position":[[924,6]]},"3578":{"position":[[1109,10]]}}}],["helm",{"_index":889,"t":{"26":{"position":[[1482,4]]},"323":{"position":[[588,4]]},"389":{"position":[[103,4]]},"515":{"position":[[28,4]]},"543":{"position":[[25,4]]},"1347":{"position":[[103,4]]},"1443":{"position":[[28,4]]},"1559":{"position":[[25,4]]},"2462":{"position":[[112,4]]},"2612":{"position":[[28,4]]},"2728":{"position":[[25,4]]}}}],["help",{"_index":611,"t":{"16":{"position":[[1777,4]]},"22":{"position":[[61,4]]},"28":{"position":[[737,4]]},"34":{"position":[[467,4]]},"54":{"position":[[467,4],[587,4]]},"58":{"position":[[398,4],[734,4]]},"60":{"position":[[1259,4]]},"76":{"position":[[1716,4]]},"78":{"position":[[2892,4]]},"82":{"position":[[2048,4]]},"94":{"position":[[1150,4]]},"98":{"position":[[778,4]]},"116":{"position":[[789,4]]},"138":{"position":[[321,4]]},"140":{"position":[[443,4]]},"144":{"position":[[544,4]]},"152":{"position":[[502,6]]},"154":{"position":[[268,4]]},"168":{"position":[[61,5]]},"182":{"position":[[349,4]]},"192":{"position":[[771,6]]},"206":{"position":[[145,4]]},"222":{"position":[[582,4]]},"240":{"position":[[224,7]]},"254":{"position":[[46,4]]},"258":{"position":[[61,4]]},"278":{"position":[[754,7]]},"280":{"position":[[267,6]]},"285":{"position":[[455,4]]},"301":{"position":[[901,4],[1021,4]]},"321":{"position":[[1861,4]]},"329":{"position":[[783,4]]},"335":{"position":[[182,4]]},"365":{"position":[[42,4]]},"383":{"position":[[189,4]]},"393":{"position":[[313,4],[707,4]]},"397":{"position":[[294,4]]},"441":{"position":[[319,7]]},"467":{"position":[[230,5]]},"477":{"position":[[5167,6]]},"483":{"position":[[963,4],[1708,7],[1975,4]]},"499":{"position":[[92,4]]},"511":{"position":[[628,4]]},"525":{"position":[[23,4]]},"620":{"position":[[76,4]]},"902":{"position":[[275,4]]},"922":{"position":[[314,7]]},"945":{"position":[[433,4]]},"973":{"position":[[937,5]]},"1017":{"position":[[5443,4],[5785,4]]},"1047":{"position":[[407,4]]},"1071":{"position":[[552,4]]},"1185":{"position":[[691,5]]},"1189":{"position":[[1573,5]]},"1197":{"position":[[293,4]]},"1203":{"position":[[793,4]]},"1221":{"position":[[294,4]]},"1267":{"position":[[319,7]]},"1323":{"position":[[47,4]]},"1341":{"position":[[189,4]]},"1355":{"position":[[36,4]]},"1371":{"position":[[608,4]]},"1375":{"position":[[343,4]]},"1393":{"position":[[92,4]]},"1395":{"position":[[88,4]]},"1425":{"position":[[116,4]]},"1439":{"position":[[630,4]]},"1483":{"position":[[246,4]]},"1513":{"position":[[30,5]]},"1771":{"position":[[961,4]]},"1839":{"position":[[610,4]]},"1983":{"position":[[930,5],[4194,5]]},"2015":{"position":[[275,4]]},"2043":{"position":[[314,7]]},"2066":{"position":[[433,4]]},"2082":{"position":[[622,4]]},"2086":{"position":[[1665,5]]},"2088":{"position":[[17,4]]},"2098":{"position":[[293,4]]},"2106":{"position":[[793,4]]},"2134":{"position":[[552,4]]},"2191":{"position":[[896,4]]},"2315":{"position":[[246,4]]},"2354":{"position":[[5566,4],[5908,4]]},"2386":{"position":[[407,4]]},"2438":{"position":[[49,4]]},"2456":{"position":[[188,4]]},"2470":{"position":[[294,4]]},"2516":{"position":[[319,7]]},"2526":{"position":[[46,4]]},"2542":{"position":[[608,4]]},"2546":{"position":[[343,4]]},"2564":{"position":[[93,4]]},"2566":{"position":[[92,4]]},"2608":{"position":[[635,4]]},"2712":{"position":[[874,4]]},"2772":{"position":[[30,5]]},"2806":{"position":[[245,7],[467,5],[814,7]]},"2889":{"position":[[252,5],[575,7],[783,5],[1102,7]]},"2930":{"position":[[961,4]]},"3086":{"position":[[624,4]]},"3142":{"position":[[930,5],[4194,5]]},"3204":{"position":[[275,4]]},"3234":{"position":[[314,7]]},"3261":{"position":[[433,4]]},"3267":{"position":[[622,4]]},"3271":{"position":[[1631,5]]},"3273":{"position":[[17,4]]},"3283":{"position":[[293,4]]},"3289":{"position":[[793,4]]},"3324":{"position":[[460,5],[1078,5],[1675,5],[2091,5],[3005,5],[4129,5],[5210,5],[5907,5],[6127,5]]},"3337":{"position":[[552,4]]},"3415":{"position":[[5595,4],[5937,4]]},"3447":{"position":[[407,4]]},"3466":{"position":[[896,4]]},"3528":{"position":[[246,4]]},"3538":{"position":[[1961,4]]}}}],["helper",{"_index":4720,"t":{"1505":{"position":[[682,6]]},"2686":{"position":[[697,6]]}}}],["henc",{"_index":2891,"t":{"317":{"position":[[1355,6]]},"465":{"position":[[296,5]]}}}],["here",{"_index":105,"t":{"2":{"position":[[1413,4],[1957,4],[2081,6]]},"4":{"position":[[411,4]]},"10":{"position":[[682,4]]},"12":{"position":[[1012,4]]},"16":{"position":[[625,4],[787,4],[1943,4],[4295,4],[6858,4]]},"20":{"position":[[0,4]]},"22":{"position":[[19,4],[232,4]]},"26":{"position":[[409,4]]},"34":{"position":[[580,4]]},"40":{"position":[[377,5]]},"50":{"position":[[614,4]]},"52":{"position":[[619,4],[716,4]]},"54":{"position":[[64,5],[755,4]]},"60":{"position":[[2197,4]]},"66":{"position":[[1413,4],[2370,4]]},"76":{"position":[[583,4],[1316,4]]},"78":{"position":[[2475,4]]},"82":{"position":[[0,4],[617,4],[2053,5]]},"88":{"position":[[141,5]]},"90":{"position":[[550,5]]},"92":{"position":[[62,4]]},"94":{"position":[[315,4],[1177,4]]},"98":{"position":[[800,4]]},"108":{"position":[[1510,4]]},"110":{"position":[[338,4]]},"112":{"position":[[15,4],[111,4]]},"116":{"position":[[169,4]]},"124":{"position":[[3076,4]]},"142":{"position":[[595,5]]},"144":{"position":[[1196,4]]},"148":{"position":[[14,4]]},"152":{"position":[[756,4]]},"156":{"position":[[596,4]]},"166":{"position":[[36,5]]},"168":{"position":[[560,5]]},"176":{"position":[[528,4]]},"178":{"position":[[13,4]]},"180":{"position":[[403,4]]},"196":{"position":[[802,5],[1339,4]]},"198":{"position":[[469,5]]},"200":{"position":[[920,4],[1341,4],[1704,4]]},"202":{"position":[[102,4]]},"206":{"position":[[0,4],[341,4]]},"210":{"position":[[141,4]]},"214":{"position":[[507,5]]},"216":{"position":[[2877,4]]},"222":{"position":[[245,5],[356,4]]},"224":{"position":[[16,4]]},"228":{"position":[[0,4]]},"234":{"position":[[426,4]]},"240":{"position":[[252,4]]},"244":{"position":[[44,4],[217,4]]},"246":{"position":[[1792,4],[2441,5]]},"250":{"position":[[652,4]]},"254":{"position":[[243,4]]},"258":{"position":[[1121,4]]},"264":{"position":[[387,4]]},"270":{"position":[[550,4]]},"272":{"position":[[1504,5]]},"278":{"position":[[203,5],[595,5]]},"280":{"position":[[1453,4]]},"287":{"position":[[199,4]]},"289":{"position":[[488,4]]},"291":{"position":[[1082,4],[2219,4]]},"315":{"position":[[1399,5],[1625,5]]},"327":{"position":[[748,4]]},"331":{"position":[[1164,4]]},"345":{"position":[[1240,4]]},"397":{"position":[[299,5]]},"411":{"position":[[967,4]]},"425":{"position":[[340,4]]},"439":{"position":[[280,4]]},"457":{"position":[[385,4]]},"465":{"position":[[189,5]]},"473":{"position":[[3093,4]]},"475":{"position":[[1540,4],[3336,4],[4348,4],[6172,4],[6787,4],[6945,4]]},"477":{"position":[[3415,4]]},"479":{"position":[[2928,4],[3546,4],[4215,4],[4714,8],[6502,5]]},"481":{"position":[[183,4],[1097,8],[1864,4]]},"483":{"position":[[1539,5]]},"493":{"position":[[256,5]]},"527":{"position":[[550,4]]},"535":{"position":[[692,5],[964,5]]},"559":{"position":[[131,4]]},"563":{"position":[[221,4]]},"618":{"position":[[0,4]]},"620":{"position":[[0,4],[795,4],[829,4]]},"632":{"position":[[724,4],[840,4],[937,4],[970,4],[1093,5]]},"652":{"position":[[587,4],[703,4],[800,4],[833,4],[951,5]]},"672":{"position":[[652,4]]},"698":{"position":[[235,4]]},"726":{"position":[[86,4]]},"802":{"position":[[264,4]]},"856":{"position":[[309,4]]},"858":{"position":[[983,4]]},"884":{"position":[[0,4],[164,7],[198,7]]},"953":{"position":[[670,4]]},"971":{"position":[[1142,4]]},"973":{"position":[[3631,5]]},"1017":{"position":[[4757,4],[4921,4]]},"1025":{"position":[[397,4]]},"1037":{"position":[[120,4]]},"1045":{"position":[[220,4]]},"1063":{"position":[[174,4],[352,5]]},"1109":{"position":[[37,5]]},"1130":{"position":[[29,5]]},"1179":{"position":[[37,5]]},"1191":{"position":[[1434,4],[1615,4]]},"1207":{"position":[[92,5]]},"1221":{"position":[[299,5]]},"1235":{"position":[[967,4]]},"1247":{"position":[[786,5]]},"1251":{"position":[[417,4]]},"1265":{"position":[[280,4]]},"1277":{"position":[[1240,4]]},"1309":{"position":[[385,4]]},"1371":{"position":[[943,4],[977,4]]},"1387":{"position":[[257,5]]},"1403":{"position":[[2354,4]]},"1405":{"position":[[851,4],[1228,4],[1876,4]]},"1407":{"position":[[755,4]]},"1425":{"position":[[0,4],[56,4],[156,4]]},"1453":{"position":[[177,4]]},"1455":{"position":[[1179,4],[1356,4]]},"1457":{"position":[[1372,5]]},"1477":{"position":[[1055,5]]},"1489":{"position":[[0,4]]},"1505":{"position":[[7641,4],[8925,4],[10513,5]]},"1509":{"position":[[186,4],[261,4]]},"1519":{"position":[[817,4]]},"1569":{"position":[[120,4]]},"1571":{"position":[[459,4]]},"1593":{"position":[[131,4]]},"1597":{"position":[[1596,4]]},"1603":{"position":[[740,4]]},"1631":{"position":[[724,4],[840,4],[937,4],[970,4],[1093,5]]},"1647":{"position":[[587,4],[703,4],[800,4],[833,4],[951,5]]},"1692":{"position":[[843,4]]},"1694":{"position":[[294,5]]},"1721":{"position":[[55,5]]},"1817":{"position":[[264,4]]},"1843":{"position":[[1014,4]]},"1899":{"position":[[766,4]]},"1923":{"position":[[670,4]]},"1929":{"position":[[421,4]]},"1933":{"position":[[235,4]]},"1961":{"position":[[86,4]]},"1981":{"position":[[1142,4]]},"1983":{"position":[[3506,4],[4776,5]]},"1997":{"position":[[0,4],[164,7],[198,7]]},"2082":{"position":[[699,4]]},"2086":{"position":[[1689,4]]},"2090":{"position":[[1434,4],[1615,4]]},"2110":{"position":[[92,5]]},"2154":{"position":[[534,4],[918,4],[1674,4]]},"2191":{"position":[[1133,4]]},"2285":{"position":[[177,4]]},"2287":{"position":[[1182,4],[1359,4]]},"2309":{"position":[[1097,5]]},"2321":{"position":[[0,4]]},"2354":{"position":[[4880,4],[5044,4]]},"2360":{"position":[[4558,4],[4653,5]]},"2362":{"position":[[397,4]]},"2376":{"position":[[120,4]]},"2384":{"position":[[220,4]]},"2392":{"position":[[1308,4],[1903,4]]},"2470":{"position":[[299,5]]},"2484":{"position":[[967,4]]},"2496":{"position":[[786,5]]},"2500":{"position":[[417,4]]},"2512":{"position":[[56,4],[2484,4]]},"2514":{"position":[[280,4]]},"2526":{"position":[[551,5]]},"2542":{"position":[[943,4],[977,4]]},"2558":{"position":[[261,5]]},"2574":{"position":[[436,4]]},"2582":{"position":[[246,5]]},"2596":{"position":[[385,4]]},"2622":{"position":[[1223,4]]},"2664":{"position":[[120,4]]},"2666":{"position":[[459,4]]},"2686":{"position":[[7729,4],[9045,4],[10636,5]]},"2702":{"position":[[817,4]]},"2714":{"position":[[235,4]]},"2768":{"position":[[186,4],[261,4]]},"2776":{"position":[[131,4]]},"2780":{"position":[[1560,4]]},"2788":{"position":[[740,4]]},"2818":{"position":[[724,4],[840,4],[937,4],[970,4],[1093,5]]},"2856":{"position":[[843,4]]},"2858":{"position":[[294,5]]},"2887":{"position":[[55,5]]},"2899":{"position":[[587,4],[703,4],[800,4],[833,4],[951,5]]},"2974":{"position":[[229,4]]},"2978":{"position":[[235,4]]},"3006":{"position":[[86,4]]},"3046":{"position":[[408,4]]},"3072":{"position":[[1302,4]]},"3086":{"position":[[701,4]]},"3134":{"position":[[766,4]]},"3140":{"position":[[1142,4]]},"3142":{"position":[[3506,4],[4776,5]]},"3150":{"position":[[670,4]]},"3156":{"position":[[421,4]]},"3186":{"position":[[0,4],[164,7],[198,7]]},"3267":{"position":[[699,4]]},"3271":{"position":[[1655,4]]},"3275":{"position":[[1434,4],[1615,4]]},"3293":{"position":[[92,5]]},"3324":{"position":[[0,4]]},"3365":{"position":[[0,4]]},"3367":{"position":[[697,5],[1980,4]]},"3371":{"position":[[108,4]]},"3383":{"position":[[534,4],[918,4],[1674,4]]},"3395":{"position":[[0,4]]},"3415":{"position":[[4909,4],[5073,4]]},"3421":{"position":[[4561,4],[4656,5]]},"3423":{"position":[[397,4]]},"3437":{"position":[[120,4]]},"3445":{"position":[[220,4]]},"3466":{"position":[[1133,4]]},"3496":{"position":[[247,4]]},"3502":{"position":[[164,4]]},"3522":{"position":[[1157,5]]},"3534":{"position":[[0,4]]},"3540":{"position":[[664,5]]},"3542":{"position":[[567,5]]},"3563":{"position":[[22,4]]},"3612":{"position":[[1335,4],[1930,4]]},"3629":{"position":[[1165,4]]},"3633":{"position":[[1223,4]]}}}],["here'",{"_index":5554,"t":{"3042":{"position":[[117,6]]}}}],["here>\"api_key",{"_index":4034,"t":{"886":{"position":[[222,13]]},"888":{"position":[[129,14]]},"1999":{"position":[[222,13]]},"2001":{"position":[[129,14]]},"3188":{"position":[[222,13]]},"3190":{"position":[[129,14]]}}}],["here>\"log_level",{"_index":4035,"t":{"886":{"position":[[253,15]]},"888":{"position":[[159,16]]},"1999":{"position":[[253,15]]},"2001":{"position":[[159,16]]},"3188":{"position":[[253,15]]},"3190":{"position":[[159,16]]}}}],["hey",{"_index":2745,"t":{"280":{"position":[[371,3]]}}}],["high",{"_index":579,"t":{"16":{"position":[[271,4],[3275,4]]},"78":{"position":[[3250,4]]},"264":{"position":[[1547,4]]},"280":{"position":[[854,4]]},"347":{"position":[[1320,4]]},"477":{"position":[[2373,4]]},"1193":{"position":[[44,4]]},"1279":{"position":[[1325,4]]},"1519":{"position":[[270,4]]},"2092":{"position":[[44,4]]},"2402":{"position":[[1412,4]]},"2702":{"position":[[270,4]]},"2708":{"position":[[1674,4]]},"2712":{"position":[[42,4]]},"3277":{"position":[[44,4]]}}}],["higher",{"_index":420,"t":{"10":{"position":[[665,7]]},"417":{"position":[[85,7]]},"1241":{"position":[[85,7]]},"2490":{"position":[[85,7]]}}}],["highest",{"_index":4021,"t":{"876":{"position":[[320,7]]},"1989":{"position":[[320,7]]},"3176":{"position":[[71,8]]},"3178":{"position":[[328,7]]}}}],["highli",{"_index":3012,"t":{"357":{"position":[[655,6]]},"1289":{"position":[[655,6]]},"1929":{"position":[[305,6]]},"2126":{"position":[[47,6]]},"2412":{"position":[[668,6]]},"3156":{"position":[[305,6]]},"3349":{"position":[[47,6]]}}}],["highlight",{"_index":1065,"t":{"48":{"position":[[187,10]]},"230":{"position":[[66,9]]},"240":{"position":[[515,10]]},"321":{"position":[[1171,11]]},"525":{"position":[[1312,10]]}}}],["hijack",{"_index":2034,"t":{"170":{"position":[[999,10]]},"892":{"position":[[146,9]]},"2005":{"position":[[146,9]]},"3194":{"position":[[146,9]]}}}],["himself",{"_index":4545,"t":{"1429":{"position":[[478,7]]}}}],["histogram",{"_index":5611,"t":{"3324":{"position":[[4869,9],[5360,9],[6035,9]]}}}],["histori",{"_index":172,"t":{"2":{"position":[[2786,7],[2798,7]]},"22":{"position":[[475,7]]},"28":{"position":[[437,7]]},"34":{"position":[[54,7],[139,7],[184,8],[1175,8]]},"36":{"position":[[94,8],[245,7]]},"38":{"position":[[125,7],[354,7],[443,7],[863,7]]},"42":{"position":[[657,7],[681,7]]},"46":{"position":[[171,7]]},"48":{"position":[[214,7]]},"74":{"position":[[76,7],[534,7],[1101,7],[2403,7]]},"78":{"position":[[1439,7],[1525,7],[2570,7],[4022,7]]},"80":{"position":[[493,7],[906,7],[953,7]]},"86":{"position":[[236,8]]},"120":{"position":[[218,7]]},"136":{"position":[[1313,7]]},"158":{"position":[[1029,7]]},"166":{"position":[[550,7]]},"176":{"position":[[2071,7]]},"178":{"position":[[360,8],[739,7]]},"202":{"position":[[462,7]]},"206":{"position":[[591,7]]},"254":{"position":[[497,7]]},"280":{"position":[[2874,7]]},"291":{"position":[[2811,7]]},"311":{"position":[[202,7]]},"317":{"position":[[107,8],[168,7],[218,7],[278,7],[616,7],[692,7],[881,8],[1271,7]]},"347":{"position":[[174,8],[607,7],[646,7],[709,7],[863,7],[948,7],[1026,7],[1508,7],[1621,7]]},"349":{"position":[[75,7],[182,7],[1195,7]]},"357":{"position":[[741,9],[920,7]]},"383":{"position":[[40,7],[95,7],[165,8]]},"407":{"position":[[180,7],[402,7],[482,7]]},"411":{"position":[[386,7],[497,7],[901,7]]},"461":{"position":[[44,7],[94,7],[307,7],[494,7],[650,7]]},"467":{"position":[[592,7],[904,7]]},"475":{"position":[[3475,7],[3619,7]]},"553":{"position":[[798,7]]},"563":{"position":[[299,7]]},"567":{"position":[[381,8]]},"583":{"position":[[150,7]]},"587":{"position":[[78,7]]},"589":{"position":[[8,7],[79,7],[216,7]]},"614":{"position":[[59,7]]},"640":{"position":[[190,8],[469,7],[535,7]]},"716":{"position":[[149,7],[216,7]]},"724":{"position":[[154,7]]},"768":{"position":[[152,7]]},"836":{"position":[[36,7],[108,7],[238,7],[395,7],[437,7],[532,7],[645,7]]},"838":{"position":[[71,7],[121,7],[226,7],[298,7],[353,7],[393,7]]},"840":{"position":[[545,7],[637,7],[738,7]]},"842":{"position":[[379,7],[478,7]]},"844":{"position":[[68,7],[138,7],[209,7]]},"969":{"position":[[0,7],[62,7],[367,7],[480,7],[551,8],[589,7],[682,7],[758,7],[802,7],[973,7],[1035,7],[1349,7],[1439,7]]},"971":{"position":[[0,7],[231,7],[398,7],[553,7],[652,7],[1737,8]]},"973":{"position":[[479,7],[1379,7],[2152,7],[3155,7],[3714,7]]},"1025":{"position":[[3109,7]]},"1183":{"position":[[492,7],[573,7]]},"1185":{"position":[[83,7],[661,7]]},"1187":{"position":[[295,7]]},"1189":{"position":[[698,7],[915,7],[1543,7]]},"1203":{"position":[[900,7]]},"1207":{"position":[[302,8]]},"1231":{"position":[[180,7],[402,7],[482,7]]},"1235":{"position":[[386,7],[497,7],[901,7]]},"1279":{"position":[[174,8],[607,7],[646,7],[718,7],[868,7],[953,7],[1031,7],[1513,7],[1626,7]]},"1281":{"position":[[75,7],[185,7],[330,7],[1059,8],[1346,7]]},"1289":{"position":[[741,9],[920,7]]},"1313":{"position":[[44,7],[94,7],[307,7],[494,7],[650,7]]},"1341":{"position":[[40,7],[95,7],[165,8]]},"1403":{"position":[[1136,7],[1331,7]]},"1407":{"position":[[634,7]]},"1415":{"position":[[260,7],[303,7]]},"1423":{"position":[[1438,7]]},"1457":{"position":[[2090,7],[2396,7]]},"1459":{"position":[[438,7]]},"1469":{"position":[[1019,7]]},"1473":{"position":[[0,7],[31,7],[151,7],[323,7],[375,7],[477,10],[559,10],[925,7],[1035,7],[1487,7],[1657,7]]},"1475":{"position":[[55,8],[479,7],[598,7],[606,7]]},"1501":{"position":[[48,8]]},"1527":{"position":[[1176,7]]},"1535":{"position":[[381,8]]},"1551":{"position":[[150,7]]},"1569":{"position":[[565,7]]},"1571":{"position":[[1421,7]]},"1585":{"position":[[368,7]]},"1591":{"position":[[190,8],[751,7],[817,7]]},"1666":{"position":[[321,7]]},"1668":{"position":[[651,7]]},"1670":{"position":[[1091,7]]},"1771":{"position":[[707,7]]},"1775":{"position":[[37,7],[66,7],[212,7],[320,7],[437,7],[529,7],[605,7],[735,7],[757,7],[889,7],[913,7],[1050,7],[1081,7],[1219,7]]},"1779":{"position":[[201,7]]},"1781":{"position":[[206,7]]},"1831":{"position":[[273,7]]},"1853":{"position":[[36,7],[108,7],[238,7],[395,7],[437,7],[532,7],[645,7],[817,7]]},"1855":{"position":[[71,7],[121,7],[226,7],[298,7],[353,7],[393,7],[640,7],[719,7],[885,10],[1135,7],[1221,7],[1301,7],[1335,7],[1444,7]]},"1857":{"position":[[637,7],[738,7],[839,7],[1039,7]]},"1859":{"position":[[515,7],[614,7],[794,7]]},"1871":{"position":[[107,7]]},"1873":{"position":[[79,7]]},"1875":{"position":[[107,7]]},"1899":{"position":[[100,7],[256,8]]},"1951":{"position":[[146,7],[213,7]]},"1959":{"position":[[151,7]]},"1979":{"position":[[0,7],[62,7],[189,7],[409,7],[522,7],[591,7],[637,7],[738,7],[814,7],[858,7],[1029,7],[1101,7],[1424,7],[1514,7]]},"1981":{"position":[[0,7],[231,7],[398,7],[553,7],[652,7],[1737,8]]},"1983":{"position":[[479,7],[747,7],[1379,7],[1553,7],[3195,7],[3241,7],[3853,7],[4859,7]]},"2056":{"position":[[232,7]]},"2080":{"position":[[492,7],[573,7]]},"2082":{"position":[[85,7],[134,7],[751,7]]},"2084":{"position":[[295,7]]},"2086":{"position":[[514,7],[947,7],[1182,7],[1741,7]]},"2104":{"position":[[514,7]]},"2106":{"position":[[900,7]]},"2110":{"position":[[302,8]]},"2150":{"position":[[1983,7],[2009,7]]},"2160":{"position":[[1438,7]]},"2289":{"position":[[2144,7],[2680,7]]},"2291":{"position":[[438,7]]},"2301":{"position":[[1062,7]]},"2305":{"position":[[0,7],[31,7],[151,7],[323,7],[375,7],[477,10],[559,10],[925,7],[1035,7],[1487,7],[1657,7]]},"2307":{"position":[[55,8],[479,7],[598,7],[606,7]]},"2362":{"position":[[3109,7]]},"2400":{"position":[[2160,7],[2239,7]]},"2402":{"position":[[178,8],[644,7],[687,7],[759,7],[924,7],[1014,7],[1093,7],[1510,7],[1673,7],[1800,7]]},"2404":{"position":[[77,7],[194,7],[343,7],[1112,8]]},"2412":{"position":[[754,9],[934,7]]},"2420":{"position":[[244,8]]},"2422":{"position":[[134,7],[582,8]]},"2424":{"position":[[1196,7]]},"2456":{"position":[[41,7],[96,7],[164,8],[398,7]]},"2480":{"position":[[180,7],[402,7],[482,7]]},"2484":{"position":[[386,7],[497,7],[901,7]]},"2600":{"position":[[44,7],[94,7],[307,7],[494,7],[650,7]]},"2636":{"position":[[346,7]]},"2642":{"position":[[189,7]]},"2656":{"position":[[42,7],[92,7],[307,7],[494,7],[650,7]]},"2664":{"position":[[565,7]]},"2666":{"position":[[1421,7]]},"2680":{"position":[[368,7]]},"2696":{"position":[[48,8]]},"2740":{"position":[[381,8]]},"2756":{"position":[[150,7]]},"2760":{"position":[[1274,7]]},"2784":{"position":[[190,8],[751,7],[817,7]]},"2830":{"position":[[324,7]]},"2832":{"position":[[657,7]]},"2834":{"position":[[1097,7]]},"2930":{"position":[[707,7]]},"2934":{"position":[[37,7],[66,7],[212,7],[320,7],[437,7],[529,7],[605,7],[735,7],[757,7],[889,7],[913,7],[1050,7],[1081,7],[1219,7]]},"2938":{"position":[[201,7]]},"2940":{"position":[[206,7]]},"2996":{"position":[[146,7],[213,7]]},"3004":{"position":[[151,7]]},"3060":{"position":[[309,8]]},"3082":{"position":[[36,7],[108,7],[238,7],[395,7],[437,7],[532,7],[645,7],[817,7]]},"3084":{"position":[[71,7],[121,7],[226,7],[298,7],[353,7],[393,7],[640,7],[719,7],[1099,7],[1185,7],[1265,7],[1299,7],[1408,7]]},"3086":{"position":[[45,7],[119,7],[753,7]]},"3088":{"position":[[637,7],[738,7],[839,7],[1039,7]]},"3090":{"position":[[515,7],[614,7],[794,7]]},"3102":{"position":[[107,7]]},"3104":{"position":[[79,7]]},"3106":{"position":[[107,7]]},"3134":{"position":[[100,7],[256,8]]},"3138":{"position":[[0,7],[62,7],[189,7],[409,7],[522,7],[591,7],[637,7],[738,7],[814,7],[858,7],[1029,7],[1101,7],[1424,7],[1514,7]]},"3140":{"position":[[0,7],[231,7],[398,7],[553,7],[652,7],[1737,8]]},"3142":{"position":[[479,7],[747,7],[1379,7],[1553,7],[3195,7],[3241,7],[3853,7],[4859,7]]},"3249":{"position":[[232,7]]},"3265":{"position":[[492,7],[573,7]]},"3267":{"position":[[85,7],[134,7],[751,7]]},"3269":{"position":[[295,7]]},"3271":{"position":[[514,7],[913,7],[1148,7],[1707,7]]},"3289":{"position":[[900,7]]},"3293":{"position":[[302,8]]},"3365":{"position":[[3144,7],[3245,7],[3499,7]]},"3379":{"position":[[1983,7],[2009,7]]},"3389":{"position":[[1438,7]]},"3423":{"position":[[3109,7]]},"3502":{"position":[[1652,7],[2188,7]]},"3504":{"position":[[513,7]]},"3514":{"position":[[900,7]]},"3518":{"position":[[0,7],[31,7],[151,7],[323,7],[375,7],[774,7],[877,7],[1329,7],[1499,7]]},"3520":{"position":[[55,8],[172,7],[284,7],[292,7]]},"3647":{"position":[[346,7]]},"3653":{"position":[[189,7]]},"3667":{"position":[[42,7],[92,7],[307,7],[494,7],[650,7]]}}}],["history(ch",{"_index":1413,"t":{"78":{"position":[[1019,10]]}}}],["history(channel",{"_index":5443,"t":{"2646":{"position":[[1946,16]]},"3657":{"position":[[1946,16]]}}}],["history(limit",{"_index":4125,"t":{"971":{"position":[[175,14],[341,14],[502,14],[600,14],[715,14],[856,14],[1071,14]]},"1981":{"position":[[175,14],[341,14],[502,14],[600,14],[715,14],[856,14],[1071,14]]},"3140":{"position":[[175,14],[341,14],[502,14],[600,14],[715,14],[856,14],[1071,14]]}}}],["history(opt",{"_index":5431,"t":{"2642":{"position":[[141,16]]},"3653":{"position":[[141,16]]}}}],["history/pres",{"_index":4406,"t":{"1197":{"position":[[1133,16]]},"2098":{"position":[[1133,16]]},"3283":{"position":[[1133,16]]}}}],["history_disable_for_cli",{"_index":3996,"t":{"844":{"position":[[0,26],[296,26]]}}}],["history_lifetim",{"_index":3651,"t":{"598":{"position":[[64,16]]},"618":{"position":[[357,16]]}}}],["history_meta_ttl",{"_index":2892,"t":{"317":{"position":[[1362,16]]},"618":{"position":[[780,16],[831,16],[878,16],[1203,16]]},"1185":{"position":[[0,17],[40,16]]},"1189":{"position":[[774,17],[846,16]]},"1205":{"position":[[675,17]]},"2082":{"position":[[0,17],[53,16]]},"2086":{"position":[[862,17],[915,16],[1037,16]]},"2108":{"position":[[675,17]]},"3086":{"position":[[0,16],[841,16],[943,16]]},"3267":{"position":[[0,17],[53,16]]},"3271":{"position":[[828,17],[881,16],[1003,16]]},"3291":{"position":[[675,17]]}}}],["history_recov",{"_index":3650,"t":{"598":{"position":[[17,15]]},"618":{"position":[[397,15]]}}}],["history_remov",{"_index":4616,"t":{"1475":{"position":[[0,14],[184,17],[261,17]]},"2307":{"position":[[0,14],[184,17],[261,17]]},"3520":{"position":[[0,14]]}}}],["history_s",{"_index":3991,"t":{"836":{"position":[[0,12],[271,12],[495,12]]},"838":{"position":[[434,12],[563,15]]},"840":{"position":[[664,12]]},"842":{"position":[[404,12]]},"856":{"position":[[249,15]]},"858":{"position":[[1285,15],[1399,15]]},"969":{"position":[[75,12]]},"1843":{"position":[[1260,15],[1337,15]]},"1853":{"position":[[0,12],[271,12],[495,12]]},"1855":{"position":[[434,12],[563,15]]},"1857":{"position":[[765,12]]},"1859":{"position":[[540,12]]},"1899":{"position":[[397,15],[1184,15]]},"1979":{"position":[[75,12]]},"1983":{"position":[[3787,12]]},"3072":{"position":[[1548,15],[1625,15]]},"3082":{"position":[[0,12],[271,12],[495,12]]},"3084":{"position":[[434,12],[563,15]]},"3088":{"position":[[765,12]]},"3090":{"position":[[540,12]]},"3134":{"position":[[397,15],[1184,15]]},"3138":{"position":[[75,12]]},"3142":{"position":[[3787,12]]}}}],["history_ttl",{"_index":3652,"t":{"598":{"position":[[92,11]]},"618":{"position":[[385,11],[1159,11]]},"836":{"position":[[588,11]]},"838":{"position":[[0,11],[451,11],[583,14]]},"840":{"position":[[681,11]]},"842":{"position":[[421,11]]},"856":{"position":[[269,14]]},"858":{"position":[[1305,14],[1419,14]]},"941":{"position":[[258,11]]},"969":{"position":[[92,11]]},"1843":{"position":[[1280,14],[1357,14]]},"1853":{"position":[[588,11]]},"1855":{"position":[[0,11],[451,11],[583,14]]},"1857":{"position":[[782,11]]},"1859":{"position":[[557,11]]},"1899":{"position":[[417,14],[1204,14]]},"1979":{"position":[[92,11]]},"1983":{"position":[[3889,11]]},"2062":{"position":[[258,11]]},"3072":{"position":[[1568,14],[1645,14]]},"3082":{"position":[[588,11]]},"3084":{"position":[[0,11],[451,11],[583,14]]},"3088":{"position":[[782,11]]},"3090":{"position":[[557,11]]},"3134":{"position":[[417,14],[1204,14]]},"3138":{"position":[[92,11]]},"3142":{"position":[[3889,11]]},"3257":{"position":[[258,11]]}}}],["historyfilt",{"_index":1414,"t":{"78":{"position":[[1045,14]]}}}],["historyresult",{"_index":970,"t":{"34":{"position":[[1009,14]]},"971":{"position":[[1571,14]]},"1981":{"position":[[1571,14]]},"3140":{"position":[[1571,14]]}}}],["historyresult.epoch",{"_index":989,"t":{"34":{"position":[[1349,20]]},"971":{"position":[[1911,20]]},"1981":{"position":[[1911,20]]},"3140":{"position":[[1911,20]]}}}],["historyresult.publ",{"_index":982,"t":{"34":{"position":[[1216,26]]},"971":{"position":[[1778,26]]},"1981":{"position":[[1778,26]]},"3140":{"position":[[1778,26]]}}}],["hit",{"_index":1448,"t":{"82":{"position":[[158,4]]},"172":{"position":[[224,3]]},"176":{"position":[[405,4]]},"776":{"position":[[964,3]]},"1666":{"position":[[1922,3]]},"1791":{"position":[[964,3]]},"2830":{"position":[[1925,3]]},"3020":{"position":[[1012,4]]}}}],["hm",{"_index":2701,"t":{"268":{"position":[[752,3],[783,5]]},"270":{"position":[[751,3]]},"272":{"position":[[8,3],[119,5],[156,3],[355,3]]},"1678":{"position":[[124,4]]},"1680":{"position":[[248,4]]},"1682":{"position":[[8,3],[119,5],[156,3],[352,3]]},"1684":{"position":[[310,3],[341,4]]},"1688":{"position":[[215,3]]},"1692":{"position":[[957,4],[987,3]]},"1694":{"position":[[163,8]]},"1703":{"position":[[243,4]]},"1719":{"position":[[881,3],[929,3],[980,3],[1378,3],[1422,3],[1666,3],[1691,3]]},"2842":{"position":[[124,4]]},"2844":{"position":[[248,4]]},"2846":{"position":[[8,3],[119,5],[156,3],[352,3]]},"2848":{"position":[[310,3],[341,4]]},"2852":{"position":[[215,3]]},"2856":{"position":[[957,4],[987,3]]},"2858":{"position":[[163,8]]},"2862":{"position":[[602,3]]},"2867":{"position":[[243,4]]},"2883":{"position":[[1078,3],[1126,3],[1177,3],[1657,3],[1701,3],[1945,3],[1970,3]]}}}],["hmac",{"_index":2814,"t":{"291":{"position":[[1128,4],[1174,4]]},"327":{"position":[[815,4]]},"802":{"position":[[359,4],[467,4]]},"868":{"position":[[104,4]]},"1005":{"position":[[375,4]]},"1505":{"position":[[6259,4],[6752,4]]},"1765":{"position":[[331,4]]},"1767":{"position":[[193,4]]},"1817":{"position":[[359,4],[467,4]]},"1909":{"position":[[104,4]]},"1911":{"position":[[121,4]]},"2686":{"position":[[6328,4],[6833,4]]},"2970":{"position":[[691,4]]},"2972":{"position":[[193,4]]},"2974":{"position":[[296,4]]},"3046":{"position":[[503,4],[611,4]]},"3166":{"position":[[104,4]]},"3168":{"position":[[121,4]]}}}],["hms_app_id",{"_index":4934,"t":{"1694":{"position":[[172,13]]},"2858":{"position":[[172,13]]}}}],["hms_app_secret",{"_index":4936,"t":{"1694":{"position":[[203,17]]},"2858":{"position":[[203,17]]}}}],["hms_condit",{"_index":4992,"t":{"1719":{"position":[[946,13]]},"2883":{"position":[[1143,13]]}}}],["hms_token",{"_index":4990,"t":{"1719":{"position":[[833,10]]},"2883":{"position":[[1030,10]]}}}],["hms_topic",{"_index":4991,"t":{"1719":{"position":[[899,9]]},"2883":{"position":[[1096,9]]}}}],["hmspushnotif",{"_index":4996,"t":{"1719":{"position":[[1382,19],[1589,20]]},"2883":{"position":[[1661,19],[1868,20]]}}}],["hold",{"_index":2155,"t":{"192":{"position":[[298,4]]}}}],["homogen",{"_index":2791,"t":{"280":{"position":[[3879,11]]}}}],["honest",{"_index":2533,"t":{"244":{"position":[[27,7]]},"258":{"position":[[1114,6]]},"1203":{"position":[[762,6]]},"1505":{"position":[[10306,7]]},"2106":{"position":[[762,6]]},"2686":{"position":[[10425,7]]},"3289":{"position":[[762,6]]}}}],["hood",{"_index":4128,"t":{"973":{"position":[[2041,5]]},"3612":{"position":[[135,6]]}}}],["hook",{"_index":2429,"t":{"216":{"position":[[1322,5],[1348,4],[2358,4]]},"357":{"position":[[880,5]]},"435":{"position":[[270,5],[343,5]]},"437":{"position":[[39,5],[968,4],[2110,5]]},"439":{"position":[[181,4],[239,4]]},"798":{"position":[[1996,4],[2112,5]]},"1261":{"position":[[270,5],[343,5]]},"1263":{"position":[[39,5],[972,4],[2114,5]]},"1265":{"position":[[181,4],[239,4]]},"1289":{"position":[[880,5]]},"1813":{"position":[[1996,4],[2112,5]]},"2412":{"position":[[894,5]]},"2422":{"position":[[617,5]]},"2510":{"position":[[270,5],[343,5]]},"2512":{"position":[[139,5],[274,5],[581,6],[1036,4],[2356,5],[2530,6]]},"2514":{"position":[[181,4],[239,4]]},"3042":{"position":[[1102,4]]}}}],["hope",{"_index":823,"t":{"22":{"position":[[0,4]]},"54":{"position":[[143,4]]},"158":{"position":[[1402,4]]},"180":{"position":[[256,4]]},"206":{"position":[[736,4]]},"220":{"position":[[146,4]]},"305":{"position":[[698,4]]},"479":{"position":[[6508,4]]}}}],["hopefulli",{"_index":2904,"t":{"321":{"position":[[2319,9]]},"483":{"position":[[899,9],[2055,9]]}}}],["horizont",{"_index":91,"t":{"2":{"position":[[1172,12]]},"16":{"position":[[226,10]]},"178":{"position":[[479,10]]},"357":{"position":[[21,12]]},"1289":{"position":[[21,12]]},"2412":{"position":[[21,12]]},"3363":{"position":[[840,12]]}}}],["host",{"_index":1597,"t":{"98":{"position":[[1928,4]]},"158":{"position":[[230,7],[960,6]]},"198":{"position":[[904,4],[909,6],[1329,4],[1334,6],[1812,4],[1912,4]]},"214":{"position":[[1094,4]]},"250":{"position":[[869,4],[874,6],[1206,4],[1211,6]]},"680":{"position":[[737,4]]},"979":{"position":[[942,4],[1196,4]]},"981":{"position":[[372,4],[746,4]]},"1189":{"position":[[467,4]]},"1191":{"position":[[1552,6]]},"1197":{"position":[[702,6]]},"1617":{"position":[[906,4]]},"2072":{"position":[[942,4],[1196,4]]},"2074":{"position":[[372,4],[746,4]]},"2088":{"position":[[236,4],[1172,7]]},"2090":{"position":[[1552,6]]},"2094":{"position":[[221,4],[1229,7]]},"2098":{"position":[[702,6]]},"2418":{"position":[[49,6]]},"2526":{"position":[[1907,6]]},"2582":{"position":[[239,6]]},"2802":{"position":[[906,4]]},"3273":{"position":[[236,4],[1064,7]]},"3275":{"position":[[1552,6]]},"3279":{"position":[[221,4],[1121,7]]},"3283":{"position":[[702,6]]},"3309":{"position":[[942,4],[1196,4]]},"3311":{"position":[[372,4],[746,4]]}}}],["host.docker.intern",{"_index":2328,"t":{"198":{"position":[[1791,20]]},"214":{"position":[[1063,20]]}}}],["host/dir/with/config/file:/centrifugo",{"_index":3538,"t":{"511":{"position":[[143,38]]},"541":{"position":[[126,38]]},"1439":{"position":[[145,38]]},"1557":{"position":[[128,38]]},"2608":{"position":[[145,38]]},"2726":{"position":[[128,38]]}}}],["host=host.docker.internal:host",{"_index":2326,"t":{"198":{"position":[[1668,30]]}}}],["host=localhost",{"_index":3587,"t":{"535":{"position":[[806,15]]}}}],["hostnam",{"_index":4100,"t":{"945":{"position":[[171,10]]},"2066":{"position":[[171,10]]},"2088":{"position":[[1068,8]]},"2094":{"position":[[1125,8]]},"3261":{"position":[[171,10]]},"3273":{"position":[[960,8]]},"3279":{"position":[[1017,8]]}}}],["hot",{"_index":2712,"t":{"270":{"position":[[1543,3]]},"2402":{"position":[[223,3]]}}}],["hour",{"_index":2789,"t":{"280":{"position":[[3652,6]]},"329":{"position":[[286,5]]},"349":{"position":[[1031,4]]},"810":{"position":[[595,5]]},"868":{"position":[[335,5]]},"941":{"position":[[363,8]]},"945":{"position":[[70,7]]},"1281":{"position":[[1182,4]]},"1615":{"position":[[1594,6]]},"1825":{"position":[[773,5]]},"1909":{"position":[[335,5]]},"1911":{"position":[[396,5]]},"2062":{"position":[[363,8]]},"2066":{"position":[[70,7]]},"2800":{"position":[[1594,6]]},"3054":{"position":[[773,5]]},"3166":{"position":[[335,5]]},"3168":{"position":[[396,5]]},"3257":{"position":[[363,8]]},"3261":{"position":[[70,7]]}}}],["hours\"720h",{"_index":4094,"t":{"941":{"position":[[444,11]]},"2062":{"position":[[444,11]]},"3257":{"position":[[444,11]]}}}],["href=\"#\">joinclick",{"_index":2586,"t":{"246":{"position":[[1664,20]]}}}],["hs256",{"_index":2827,"t":{"291":{"position":[[1506,7]]},"301":{"position":[[589,7]]},"800":{"position":[[41,5]]},"804":{"position":[[0,5]]},"1815":{"position":[[41,5]]},"1819":{"position":[[0,5]]},"2970":{"position":[[435,7]]},"3044":{"position":[[41,5]]},"3046":{"position":[[205,7]]},"3048":{"position":[[0,5],[327,7]]},"3050":{"position":[[300,7]]}}}],["hst",{"_index":1905,"t":{"136":{"position":[[1399,7]]},"1569":{"position":[[554,3]]},"1571":{"position":[[1023,6],[1097,6],[1735,6]]},"1583":{"position":[[131,6]]},"1585":{"position":[[414,6]]},"2664":{"position":[[554,3]]},"2666":{"position":[[1023,6],[1097,6],[1735,6]]},"2678":{"position":[[131,6]]},"2680":{"position":[[414,6]]}}}],["html",{"_index":4765,"t":{"1505":{"position":[[3767,4],[4238,4]]},"2686":{"position":[[3807,4],[4299,4]]}}}],["html>[a",{"_index":5026,"t":{"1827":{"position":[[755,45]]},"3056":{"position":[[721,45]]}}}],["https://example.com/openid",{"_index":2924,"t":{"327":{"position":[[908,27]]},"2974":{"position":[[389,27]]}}}],["https://foo.example.com",{"_index":4043,"t":{"892":{"position":[[956,23]]},"2005":{"position":[[956,23]]},"3194":{"position":[[956,23]]}}}],["https://github.com/centrifugal/centrifugo",{"_index":3601,"t":{"545":{"position":[[46,41]]},"547":{"position":[[46,41]]},"1561":{"position":[[46,41]]},"1563":{"position":[[46,41]]},"2730":{"position":[[46,41]]},"2732":{"position":[[46,41]]}}}],["https://github.com/centrifugal/centrifugo.gitcd",{"_index":3568,"t":{"521":{"position":[[42,47]]},"1449":{"position":[[42,47]]},"2618":{"position":[[48,47]]}}}],["https://github.com/fzambia/pipelin",{"_index":3403,"t":{"479":{"position":[[1886,37]]}}}],["https://github.com/fzambia/redigo",{"_index":797,"t":{"18":{"position":[[3025,33]]}}}],["https://googlechrome.github.io",{"_index":1702,"t":{"110":{"position":[[573,30]]}}}],["https://googlechrome.github.io/samples/quictransport/client.html",{"_index":1627,"t":{"100":{"position":[[987,64]]}}}],["https://hpbn.co/websocket",{"_index":259,"t":{"4":{"position":[[924,26]]}}}],["https://keycloak:443/{{realm}}/protocol/openid",{"_index":5028,"t":{"1827":{"position":[[838,47]]},"3056":{"position":[[804,47]]}}}],["https://laravel.com/docs/8.x/authent",{"_index":2018,"t":{"166":{"position":[[181,44]]}}}],["https://localhost:4433/path",{"_index":1522,"t":{"94":{"position":[[1873,29]]}}}],["https://localhost:8000/connection/webtransport",{"_index":5228,"t":{"2392":{"position":[[1662,48]]},"3612":{"position":[[1689,48]]}}}],["https://lucumr.pocoo.org/2012/9/24/websocket",{"_index":266,"t":{"4":{"position":[[1004,45]]}}}],["https://mysite.com",{"_index":3654,"t":{"602":{"position":[[166,23]]}}}],["https://mysite2.example.com",{"_index":4028,"t":{"878":{"position":[[773,28]]},"1991":{"position":[[773,28]]},"3180":{"position":[[773,28]]}}}],["https://play.golang.org/p/u7sagolmdk",{"_index":793,"t":{"18":{"position":[[2831,38]]}}}],["https://raw.githubusercontent.com/centrifugal/centrifugo/master/internal/apiproto/api.proto",{"_index":4674,"t":{"1489":{"position":[[401,91]]},"2321":{"position":[[401,91]]},"3534":{"position":[[401,91]]}}}],["https://robohash.org",{"_index":1977,"t":{"156":{"position":[[876,21]]}}}],["https://tools.ietf.org/html/draft",{"_index":1644,"t":{"104":{"position":[[560,33]]},"106":{"position":[[1102,33]]},"110":{"position":[[743,33]]}}}],["https://www.flaticon.com/packs/web",{"_index":2863,"t":{"309":{"position":[[79,34]]},"1215":{"position":[[83,34]]},"2327":{"position":[[83,34]]}}}],["https://www.google.com/intl/en/chrome/canari",{"_index":1537,"t":{"96":{"position":[[6,46]]}}}],["https://www.w3.org/tr/trac",{"_index":2915,"t":{"325":{"position":[[435,27]]}}}],["https://your_centrifugo.com/connection/http_stream",{"_index":1841,"t":{"124":{"position":[[3687,52]]}}}],["https://your_centrifugo.com/connection/ss",{"_index":1842,"t":{"124":{"position":[[3773,44]]}}}],["https://your_centrifugo.com/connection/webtransport",{"_index":1932,"t":{"144":{"position":[[945,53]]}}}],["huawei",{"_index":2702,"t":{"268":{"position":[[758,6]]}}}],["hub",{"_index":1105,"t":{"50":{"position":[[1767,3],[1917,3]]},"363":{"position":[[253,3]]},"511":{"position":[[57,4]]},"1321":{"position":[[252,3]]},"1439":{"position":[[57,4]]},"2436":{"position":[[261,3]]},"2608":{"position":[[57,4]]}}}],["huge",{"_index":809,"t":{"20":{"position":[[168,4]]},"26":{"position":[[160,4]]},"38":{"position":[[834,4]]},"40":{"position":[[272,4]]},"56":{"position":[[194,4]]},"124":{"position":[[605,4]]},"152":{"position":[[422,4]]},"222":{"position":[[226,5]]},"409":{"position":[[1218,4]]},"469":{"position":[[121,4]]},"475":{"position":[[114,4]]},"477":{"position":[[389,6],[2140,4]]},"1233":{"position":[[1218,4]]},"1477":{"position":[[993,4]]},"1849":{"position":[[736,4]]},"2309":{"position":[[1035,4]]},"2482":{"position":[[1218,4]]},"3078":{"position":[[736,4]]},"3522":{"position":[[1095,4]]}}}],["human",{"_index":1849,"t":{"126":{"position":[[116,5]]},"148":{"position":[[184,5]]},"1162":{"position":[[105,5],[461,5]]},"1405":{"position":[[1196,5]]},"2154":{"position":[[886,5]]},"2158":{"position":[[166,5]]},"2168":{"position":[[528,5]]},"2342":{"position":[[105,5],[461,5]]},"2622":{"position":[[4611,5]]},"2632":{"position":[[4228,5]]},"3383":{"position":[[886,5]]},"3387":{"position":[[166,5]]},"3397":{"position":[[528,5]]},"3538":{"position":[[751,6]]},"3627":{"position":[[105,5],[461,5]]},"3633":{"position":[[4611,5]]},"3643":{"position":[[4228,5]]}}}],["hundr",{"_index":3011,"t":{"357":{"position":[[165,8]]},"1289":{"position":[[165,8]]},"1680":{"position":[[321,8]]},"2412":{"position":[[169,8]]}}}],["hup",{"_index":4084,"t":{"932":{"position":[[22,3],[80,3]]},"2053":{"position":[[22,3],[80,3]]},"3246":{"position":[[22,3],[80,3]]}}}],["hurt",{"_index":1452,"t":{"82":{"position":[[827,5]]}}}],["hyphen",{"_index":4002,"t":{"858":{"position":[[534,7]]},"1843":{"position":[[534,7]]},"3072":{"position":[[822,7]]}}}],["i'd",{"_index":222,"t":{"4":{"position":[[140,3]]},"202":{"position":[[1546,3]]},"305":{"position":[[300,3]]}}}],["i'll",{"_index":235,"t":{"4":{"position":[[345,4]]},"16":{"position":[[2129,4],[2947,4]]},"18":{"position":[[1001,4]]},"56":{"position":[[13,4]]},"108":{"position":[[1373,4]]}}}],["i.",{"_index":113,"t":{"2":{"position":[[1594,4],[2245,4]]},"18":{"position":[[536,4]]},"38":{"position":[[1237,5]]},"66":{"position":[[2162,5]]},"78":{"position":[[2208,5]]},"142":{"position":[[518,4]]},"156":{"position":[[667,4]]},"196":{"position":[[1300,4]]},"248":{"position":[[387,5]]},"270":{"position":[[2030,5]]},"411":{"position":[[974,4]]},"429":{"position":[[1323,5]]},"473":{"position":[[477,4]]},"479":{"position":[[6413,4]]},"509":{"position":[[82,5],[1033,4]]},"535":{"position":[[50,4]]},"672":{"position":[[838,4]]},"690":{"position":[[626,4],[949,4]]},"790":{"position":[[535,5]]},"792":{"position":[[854,5]]},"820":{"position":[[540,4]]},"828":{"position":[[79,5]]},"840":{"position":[[234,5],[659,4]]},"842":{"position":[[399,4]]},"844":{"position":[[133,4]]},"858":{"position":[[576,4]]},"878":{"position":[[82,5],[396,4]]},"880":{"position":[[218,5]]},"892":{"position":[[475,5]]},"910":{"position":[[160,5]]},"920":{"position":[[53,4]]},"943":{"position":[[185,4]]},"1033":{"position":[[271,5],[376,5]]},"1035":{"position":[[172,5]]},"1039":{"position":[[318,4],[588,5]]},"1041":{"position":[[160,5],[497,5]]},"1069":{"position":[[713,5],[843,4]]},"1079":{"position":[[44,4]]},"1092":{"position":[[807,5]]},"1094":{"position":[[136,4]]},"1102":{"position":[[63,4]]},"1199":{"position":[[1203,5]]},"1235":{"position":[[974,4]]},"1247":{"position":[[709,4]]},"1255":{"position":[[1323,5]]},"1269":{"position":[[447,5]]},"1371":{"position":[[354,4],[694,4]]},"1405":{"position":[[218,4],[629,4]]},"1423":{"position":[[1400,4]]},"1437":{"position":[[76,5],[1266,4]]},"1455":{"position":[[63,4]]},"1473":{"position":[[219,4]]},"1505":{"position":[[6861,5]]},"1509":{"position":[[569,4]]},"1511":{"position":[[1194,4]]},"1513":{"position":[[309,4]]},"1519":{"position":[[658,4]]},"1603":{"position":[[926,4]]},"1666":{"position":[[107,4],[1536,5]]},"1668":{"position":[[382,4]]},"1670":{"position":[[753,4]]},"1684":{"position":[[474,6]]},"1692":{"position":[[1021,4]]},"1733":{"position":[[626,4],[949,4]]},"1743":{"position":[[254,5]]},"1771":{"position":[[2040,4]]},"1843":{"position":[[576,4]]},"1857":{"position":[[154,4],[326,5],[760,4]]},"1859":{"position":[[535,4]]},"1861":{"position":[[415,4]]},"1865":{"position":[[582,5]]},"1867":{"position":[[555,5]]},"1991":{"position":[[82,5],[396,4]]},"1993":{"position":[[218,5]]},"2005":{"position":[[475,5]]},"2031":{"position":[[160,5]]},"2033":{"position":[[157,4]]},"2041":{"position":[[53,4]]},"2064":{"position":[[185,4]]},"2100":{"position":[[1203,5]]},"2132":{"position":[[713,5],[843,4]]},"2150":{"position":[[91,4],[1256,4]]},"2154":{"position":[[344,4]]},"2160":{"position":[[1400,4]]},"2187":{"position":[[810,5]]},"2189":{"position":[[136,4]]},"2199":{"position":[[44,4]]},"2287":{"position":[[66,4]]},"2305":{"position":[[219,4]]},"2368":{"position":[[699,5]]},"2372":{"position":[[271,5],[376,5]]},"2374":{"position":[[172,5]]},"2378":{"position":[[318,4],[588,5]]},"2380":{"position":[[134,5],[471,5]]},"2400":{"position":[[886,5]]},"2412":{"position":[[1385,6]]},"2420":{"position":[[380,5]]},"2424":{"position":[[531,5]]},"2484":{"position":[[974,4]]},"2496":{"position":[[709,4]]},"2504":{"position":[[1323,5]]},"2518":{"position":[[447,5]]},"2542":{"position":[[354,4],[694,4]]},"2606":{"position":[[79,6]]},"2632":{"position":[[3602,5]]},"2636":{"position":[[91,5]]},"2638":{"position":[[418,4]]},"2640":{"position":[[379,5]]},"2646":{"position":[[188,5]]},"2650":{"position":[[190,5]]},"2652":{"position":[[190,5]]},"2686":{"position":[[6942,5]]},"2702":{"position":[[658,4]]},"2768":{"position":[[569,4]]},"2770":{"position":[[1194,4]]},"2772":{"position":[[309,4]]},"2788":{"position":[[926,4]]},"2830":{"position":[[107,4],[1539,5]]},"2832":{"position":[[382,4]]},"2834":{"position":[[753,4]]},"2848":{"position":[[474,6]]},"2856":{"position":[[1021,4]]},"2922":{"position":[[590,4],[913,4]]},"2930":{"position":[[2040,4]]},"2948":{"position":[[254,5]]},"3072":{"position":[[864,4]]},"3088":{"position":[[154,4],[326,5],[760,4]]},"3090":{"position":[[535,4]]},"3092":{"position":[[415,4]]},"3096":{"position":[[582,5]]},"3098":{"position":[[555,5]]},"3180":{"position":[[82,5],[396,4]]},"3182":{"position":[[218,5]]},"3194":{"position":[[475,5]]},"3222":{"position":[[160,5]]},"3224":{"position":[[130,4]]},"3232":{"position":[[53,4]]},"3259":{"position":[[185,4]]},"3285":{"position":[[1203,5]]},"3335":{"position":[[713,5],[843,4]]},"3363":{"position":[[553,4]]},"3365":{"position":[[2576,4],[3822,5]]},"3367":{"position":[[2168,4]]},"3379":{"position":[[91,4],[1256,4]]},"3383":{"position":[[344,4]]},"3389":{"position":[[1400,4]]},"3427":{"position":[[418,4]]},"3429":{"position":[[699,5],[947,4]]},"3433":{"position":[[271,5],[376,5],[484,5]]},"3435":{"position":[[172,5]]},"3439":{"position":[[318,4],[588,5]]},"3441":{"position":[[134,5],[471,5]]},"3464":{"position":[[810,5]]},"3474":{"position":[[44,4]]},"3492":{"position":[[136,4]]},"3498":{"position":[[66,4]]},"3518":{"position":[[219,4]]},"3643":{"position":[[3602,5]]},"3647":{"position":[[91,5]]},"3649":{"position":[[418,4]]},"3651":{"position":[[379,5]]},"3657":{"position":[[188,5]]},"3661":{"position":[[190,5]]},"3663":{"position":[[190,5]]}}}],["i7",{"_index":1082,"t":{"50":{"position":[[702,2],[1072,2]]}}}],["iat",{"_index":3721,"t":{"630":{"position":[[173,3]]},"638":{"position":[[217,5],[809,3]]},"772":{"position":[[57,4]]},"1629":{"position":[[173,3]]},"1639":{"position":[[217,5],[813,3]]},"2816":{"position":[[173,3]]},"2826":{"position":[[217,5],[777,3]]}}}],["ic",{"_index":1515,"t":{"94":{"position":[[1085,4]]}}}],["icon",{"_index":1157,"t":{"54":{"position":[[744,5]]},"305":{"position":[[893,5]]},"309":{"position":[[57,5]]},"1215":{"position":[[57,5]]},"2327":{"position":[[57,5]]}}}],["id",{"_index":1245,"t":{"64":{"position":[[411,2]]},"76":{"position":[[442,3],[451,4]]},"110":{"position":[[1442,3]]},"132":{"position":[[1294,2]]},"138":{"position":[[194,3],[308,2]]},"172":{"position":[[410,4],[456,5],[770,2]]},"174":{"position":[[396,4],[727,4],[759,4]]},"198":{"position":[[466,2]]},"200":{"position":[[947,2],[2013,2],[2127,2]]},"268":{"position":[[473,3],[1004,2]]},"272":{"position":[[873,2]]},"287":{"position":[[148,2],[236,3],[593,2]]},"289":{"position":[[304,2],[828,2],[900,2]]},"291":{"position":[[767,2]]},"299":{"position":[[69,2],[520,4]]},"321":{"position":[[2191,2]]},"331":{"position":[[795,6]]},"411":{"position":[[111,2],[122,3],[353,2],[364,3]]},"431":{"position":[[358,2],[378,2],[639,3]]},"437":{"position":[[1129,2]]},"463":{"position":[[299,3]]},"553":{"position":[[194,3],[578,3],[640,2]]},"563":{"position":[[105,3]]},"608":{"position":[[286,3]]},"630":{"position":[[52,2]]},"636":{"position":[[385,2],[751,2]]},"638":{"position":[[664,2]]},"656":{"position":[[326,2]]},"658":{"position":[[305,2]]},"690":{"position":[[1399,2]]},"754":{"position":[[169,2]]},"774":{"position":[[51,2],[252,2]]},"780":{"position":[[23,3]]},"792":{"position":[[341,2]]},"820":{"position":[[240,2],[299,2],[693,2],[849,3],[938,2],[958,2]]},"828":{"position":[[115,4],[222,2],[465,3]]},"830":{"position":[[212,3],[221,3]]},"834":{"position":[[245,3],[254,3]]},"868":{"position":[[143,2]]},"906":{"position":[[74,3]]},"910":{"position":[[182,3]]},"989":{"position":[[17,2],[117,2],[162,2],[235,2],[331,2],[543,2]]},"1005":{"position":[[95,2]]},"1013":{"position":[[466,3]]},"1017":{"position":[[2081,2],[2440,2],[3498,2],[4954,4]]},"1019":{"position":[[736,2],[1049,2]]},"1021":{"position":[[714,2],[1019,2]]},"1023":{"position":[[513,3],[2038,2],[2343,2]]},"1025":{"position":[[2072,2],[2375,2]]},"1069":{"position":[[245,2],[349,3],[443,2]]},"1207":{"position":[[761,2]]},"1235":{"position":[[111,2],[122,3],[353,2],[364,3]]},"1257":{"position":[[358,2],[378,2],[639,3]]},"1263":{"position":[[1133,2]]},"1315":{"position":[[299,3]]},"1367":{"position":[[56,3],[338,2],[437,3],[528,2]]},"1405":{"position":[[734,6],[2389,2],[2733,2],[2872,6],[2892,9],[2927,2],[3533,2],[3550,2]]},"1407":{"position":[[387,5],[516,2],[1071,5],[1228,2]]},"1409":{"position":[[67,2],[163,5],[350,5]]},"1411":{"position":[[183,5]]},"1413":{"position":[[439,5]]},"1417":{"position":[[450,2]]},"1461":{"position":[[138,2],[427,2],[532,4]]},"1463":{"position":[[134,3],[233,2],[342,2]]},"1465":{"position":[[42,3],[98,3],[196,2],[246,2],[464,3]]},"1467":{"position":[[187,2],[225,2]]},"1469":{"position":[[958,2],[1113,2],[1136,2]]},"1471":{"position":[[127,4]]},"1497":{"position":[[140,3],[312,2],[579,2],[1276,2]]},"1505":{"position":[[6223,3]]},"1527":{"position":[[175,2],[686,3],[747,2]]},"1599":{"position":[[177,2],[384,2],[741,2],[1387,2],[1557,2]]},"1629":{"position":[[52,2]]},"1636":{"position":[[385,2],[751,2]]},"1639":{"position":[[664,2]]},"1652":{"position":[[326,2]]},"1654":{"position":[[305,2]]},"1668":{"position":[[59,2],[286,2],[407,2]]},"1670":{"position":[[72,2],[778,2]]},"1682":{"position":[[986,2]]},"1684":{"position":[[509,3]]},"1688":{"position":[[584,2],[892,2]]},"1692":{"position":[[1169,3]]},"1694":{"position":[[276,2]]},"1703":{"position":[[98,2],[111,2],[885,2],[910,2]]},"1705":{"position":[[103,2],[178,3],[208,3]]},"1707":{"position":[[188,3],[228,3],[293,3]]},"1709":{"position":[[140,3],[178,3],[547,2],[1134,2],[1157,3]]},"1711":{"position":[[125,3]]},"1713":{"position":[[134,3],[648,2],[1093,2],[1107,2],[1155,2]]},"1715":{"position":[[247,3]]},"1717":{"position":[[304,2],[634,2],[648,2],[685,2]]},"1719":{"position":[[572,3],[1145,3],[1927,3]]},"1721":{"position":[[521,3],[889,3],[1021,2],[1049,2]]},"1733":{"position":[[1399,2]]},"1743":{"position":[[51,2],[276,4]]},"1761":{"position":[[23,3]]},"1765":{"position":[[93,2]]},"1771":{"position":[[1552,2],[2064,4]]},"1789":{"position":[[51,2],[252,2]]},"1795":{"position":[[23,3]]},"1807":{"position":[[341,2]]},"1837":{"position":[[240,2],[299,2],[542,3],[631,2],[651,2]]},"1841":{"position":[[482,2]]},"1847":{"position":[[227,3],[236,3],[423,2]]},"1849":{"position":[[248,3],[257,3]]},"1861":{"position":[[439,4]]},"1863":{"position":[[103,3]]},"1909":{"position":[[143,2]]},"1911":{"position":[[193,2]]},"1967":{"position":[[803,2]]},"2019":{"position":[[74,3]]},"2031":{"position":[[182,4]]},"2110":{"position":[[761,2]]},"2116":{"position":[[147,2]]},"2132":{"position":[[245,2],[349,3],[443,2]]},"2150":{"position":[[216,2],[561,2],[677,6],[779,6],[1298,6],[2146,2]]},"2152":{"position":[[70,2],[87,2]]},"2154":{"position":[[449,6]]},"2293":{"position":[[138,2],[427,2],[532,4]]},"2295":{"position":[[134,3],[233,2],[342,2]]},"2297":{"position":[[42,3],[98,3],[196,2],[246,2],[430,3]]},"2299":{"position":[[187,2],[225,2]]},"2301":{"position":[[1001,2],[1156,2],[1179,2]]},"2303":{"position":[[127,4]]},"2350":{"position":[[466,3]]},"2354":{"position":[[2198,2],[2557,2],[3615,2],[5077,4]]},"2356":{"position":[[736,2],[1049,2]]},"2358":{"position":[[714,2],[1019,2]]},"2360":{"position":[[513,3],[2038,2],[2343,2]]},"2362":{"position":[[2072,2],[2375,2]]},"2364":{"position":[[2137,2],[2450,2]]},"2484":{"position":[[111,2],[122,3],[353,2],[364,3]]},"2506":{"position":[[358,2],[378,2],[639,3]]},"2538":{"position":[[56,3],[338,2],[437,3],[528,2]]},"2602":{"position":[[299,3]]},"2622":{"position":[[2311,4],[3555,2]]},"2658":{"position":[[299,3]]},"2686":{"position":[[6292,3]]},"2692":{"position":[[140,3],[312,2],[579,2],[1276,2]]},"2760":{"position":[[175,2],[687,3],[748,2]]},"2782":{"position":[[177,2],[384,2],[741,2],[1387,2],[1557,2]]},"2816":{"position":[[52,2]]},"2823":{"position":[[385,2],[715,2]]},"2826":{"position":[[628,2]]},"2832":{"position":[[59,2],[286,2],[407,2]]},"2834":{"position":[[72,2],[778,2]]},"2846":{"position":[[986,2]]},"2848":{"position":[[509,3]]},"2852":{"position":[[584,2],[892,2]]},"2856":{"position":[[1169,3]]},"2858":{"position":[[276,2]]},"2867":{"position":[[98,2],[111,2],[792,2],[817,2]]},"2869":{"position":[[103,2],[178,3],[208,3]]},"2871":{"position":[[188,3],[228,3],[293,3]]},"2873":{"position":[[238,2],[666,3],[704,3],[1325,2],[1352,3]]},"2875":{"position":[[125,3]]},"2877":{"position":[[132,3],[207,2],[589,3],[1311,2],[1325,2],[1378,2]]},"2879":{"position":[[247,3]]},"2881":{"position":[[170,2],[789,2],[803,2],[840,2]]},"2883":{"position":[[448,3],[816,3],[2206,3]]},"2887":{"position":[[521,3],[889,3],[1021,2],[1049,2]]},"2904":{"position":[[290,2]]},"2906":{"position":[[269,2]]},"2922":{"position":[[1363,2]]},"2930":{"position":[[1552,2],[2064,4]]},"2948":{"position":[[51,2],[276,4]]},"2966":{"position":[[23,3]]},"2970":{"position":[[93,2]]},"3012":{"position":[[803,2]]},"3018":{"position":[[41,2],[234,2]]},"3036":{"position":[[420,2]]},"3066":{"position":[[255,2],[323,2],[553,3],[644,3]]},"3070":{"position":[[496,2]]},"3076":{"position":[[227,3],[236,3],[423,2]]},"3078":{"position":[[248,3],[257,3]]},"3092":{"position":[[439,4]]},"3094":{"position":[[103,3]]},"3166":{"position":[[143,2]]},"3168":{"position":[[193,2]]},"3208":{"position":[[74,3]]},"3220":{"position":[[206,2],[407,2],[590,2],[837,4],[1283,3]]},"3222":{"position":[[182,4]]},"3293":{"position":[[761,2]]},"3335":{"position":[[245,2],[349,3],[443,2]]},"3365":{"position":[[1678,2]]},"3379":{"position":[[216,2],[561,2],[677,6],[779,6],[1298,6],[2146,2]]},"3381":{"position":[[70,2],[87,2]]},"3383":{"position":[[449,6]]},"3411":{"position":[[466,3]]},"3415":{"position":[[2198,2],[2557,2],[3615,2],[5106,4]]},"3417":{"position":[[736,2],[1049,2]]},"3419":{"position":[[761,2],[1066,2]]},"3421":{"position":[[1841,2],[2146,2]]},"3423":{"position":[[2072,2],[2375,2]]},"3425":{"position":[[2110,2],[2423,2]]},"3506":{"position":[[206,2],[495,2],[600,4]]},"3508":{"position":[[139,2],[248,2]]},"3510":{"position":[[42,3],[130,2],[180,2],[364,3]]},"3512":{"position":[[180,2],[218,2]]},"3514":{"position":[[839,2],[994,2],[1017,2]]},"3516":{"position":[[127,4]]},"3629":{"position":[[796,6]]},"3633":{"position":[[2311,4],[3555,2]]},"3669":{"position":[[299,3]]}}}],["id\":1,\"connect\":{\"client\":\"9ac9de4",{"_index":2947,"t":{"331":{"position":[[821,37]]},"3629":{"position":[[822,37]]}}}],["id\":32",{"_index":4528,"t":{"1419":{"position":[[166,8]]}}}],["id,]);$room",{"_index":2048,"t":{"174":{"position":[[452,12]]}}}],["id;}$thi",{"_index":2052,"t":{"174":{"position":[[582,10]]}}}],["id=\"chat",{"_index":2240,"t":{"194":{"position":[[610,8],[687,8]]}}}],["id=\"count",{"_index":4743,"t":{"1505":{"position":[[2183,13]]},"2686":{"position":[[2198,13]]}}}],["id=\"log\">')centrifuge.connect",{"_index":3068,"t":{"447":{"position":[[372,27]]},"1299":{"position":[[372,27]]},"2586":{"position":[[372,27]]}}}],["jwt>'}));const",{"_index":4325,"t":{"1098":{"position":[[445,16]]},"2234":{"position":[[445,16]]},"3584":{"position":[[445,16]]}}}],["jwtclaim",{"_index":3962,"t":{"806":{"position":[[45,9]]},"1821":{"position":[[45,9]]},"3050":{"position":[[45,9]]}}}],["jwtimport",{"_index":2816,"t":{"291":{"position":[[1219,9]]},"301":{"position":[[263,9]]},"798":{"position":[[328,9]]},"804":{"position":[[67,9]]},"1813":{"position":[[328,9]]},"1819":{"position":[[67,9]]},"2970":{"position":[[152,9]]},"3042":{"position":[[340,9]]},"3048":{"position":[[67,9]]}}}],["jwtnote:th",{"_index":2976,"t":{"341":{"position":[[55,11]]},"1211":{"position":[[55,11]]},"2394":{"position":[[55,11]]}}}],["jwttoken",{"_index":3954,"t":{"802":{"position":[[21,8]]},"1005":{"position":[[164,8]]},"1765":{"position":[[138,8]]},"1817":{"position":[[21,8]]},"3046":{"position":[[21,8]]}}}],["kafka",{"_index":617,"t":{"16":{"position":[[1998,5],[3732,5],[3867,5],[4024,5],[4178,5],[4356,5],[4985,5]]},"218":{"position":[[460,6]]},"234":{"position":[[580,5],[602,5]]},"236":{"position":[[622,5],[696,5]]},"240":{"position":[[438,6]]},"411":{"position":[[961,5]]},"1235":{"position":[[961,5]]},"2422":{"position":[[64,5],[253,5],[291,5],[715,5],[984,5]]},"2484":{"position":[[961,5]]}}}],["kafka'",{"_index":5237,"t":{"2422":{"position":[[110,7]]}}}],["kamardin",{"_index":331,"t":{"8":{"position":[[627,8]]}}}],["kb",{"_index":205,"t":{"2":{"position":[[3523,2]]},"399":{"position":[[187,2]]},"1223":{"position":[[187,2]]},"2472":{"position":[[187,2]]}}}],["keep",{"_index":256,"t":{"4":{"position":[[830,4]]},"10":{"position":[[1212,4],[1868,5]]},"14":{"position":[[947,5]]},"16":{"position":[[1725,4],[3640,7],[3927,7],[6691,4]]},"20":{"position":[[995,4],[1210,4],[1558,4]]},"34":{"position":[[97,7]]},"36":{"position":[[78,7]]},"38":{"position":[[113,4]]},"94":{"position":[[2197,4]]},"156":{"position":[[731,4]]},"166":{"position":[[536,5]]},"170":{"position":[[256,4]]},"200":{"position":[[967,4]]},"206":{"position":[[578,4]]},"234":{"position":[[490,4]]},"254":{"position":[[484,4]]},"258":{"position":[[489,7]]},"268":{"position":[[1159,4]]},"280":{"position":[[3951,4]]},"347":{"position":[[739,7]]},"409":{"position":[[153,4]]},"421":{"position":[[169,4]]},"425":{"position":[[524,4]]},"455":{"position":[[926,5]]},"467":{"position":[[560,7]]},"525":{"position":[[846,4]]},"535":{"position":[[444,4]]},"614":{"position":[[54,4]]},"628":{"position":[[427,4],[462,7]]},"636":{"position":[[997,4]]},"638":{"position":[[1056,4]]},"648":{"position":[[388,4],[440,7]]},"650":{"position":[[119,4]]},"656":{"position":[[513,4]]},"664":{"position":[[315,4]]},"824":{"position":[[123,4]]},"834":{"position":[[301,4]]},"836":{"position":[[98,5],[344,4]]},"838":{"position":[[58,4]]},"884":{"position":[[476,4]]},"902":{"position":[[201,4],[280,7]]},"957":{"position":[[171,5]]},"969":{"position":[[178,4]]},"977":{"position":[[104,4]]},"1025":{"position":[[1547,4]]},"1029":{"position":[[380,4],[487,4]]},"1057":{"position":[[1816,4],[1934,4]]},"1183":{"position":[[90,7]]},"1187":{"position":[[277,4]]},"1189":{"position":[[690,7],[724,7]]},"1207":{"position":[[49,4]]},"1233":{"position":[[153,4]]},"1245":{"position":[[229,4]]},"1251":{"position":[[601,4]]},"1279":{"position":[[748,7]]},"1307":{"position":[[926,5]]},"1355":{"position":[[872,4]]},"1371":{"position":[[323,4]]},"1413":{"position":[[103,4]]},"1455":{"position":[[473,4]]},"1457":{"position":[[1016,4]]},"1465":{"position":[[471,4]]},"1477":{"position":[[540,4]]},"1483":{"position":[[565,4]]},"1573":{"position":[[101,4]]},"1587":{"position":[[114,4]]},"1627":{"position":[[427,4],[462,7]]},"1636":{"position":[[997,4]]},"1639":{"position":[[1102,4]]},"1643":{"position":[[388,4],[440,7]]},"1645":{"position":[[119,4]]},"1652":{"position":[[513,4]]},"1658":{"position":[[315,4]]},"1692":{"position":[[1116,4]]},"1698":{"position":[[90,4]]},"1841":{"position":[[0,4],[613,7]]},"1849":{"position":[[683,4]]},"1853":{"position":[[98,5],[344,4]]},"1855":{"position":[[58,4]]},"1865":{"position":[[269,4]]},"1867":{"position":[[246,4]]},"1927":{"position":[[171,5]]},"1983":{"position":[[4093,4]]},"1997":{"position":[[476,4]]},"2015":{"position":[[201,4],[280,7]]},"2070":{"position":[[104,4]]},"2080":{"position":[[90,7]]},"2082":{"position":[[167,5],[710,4]]},"2084":{"position":[[277,4]]},"2086":{"position":[[506,7],[540,7],[1700,4]]},"2110":{"position":[[49,4]]},"2122":{"position":[[492,4]]},"2140":{"position":[[1816,4],[1934,4]]},"2287":{"position":[[476,4]]},"2289":{"position":[[1076,4]]},"2297":{"position":[[437,4]]},"2309":{"position":[[587,4]]},"2315":{"position":[[565,4]]},"2360":{"position":[[4611,4]]},"2362":{"position":[[1547,4]]},"2368":{"position":[[530,4],[637,4]]},"2402":{"position":[[793,7]]},"2420":{"position":[[220,7]]},"2482":{"position":[[153,4]]},"2494":{"position":[[229,4]]},"2500":{"position":[[601,4]]},"2526":{"position":[[862,4]]},"2542":{"position":[[323,4]]},"2594":{"position":[[964,5]]},"2628":{"position":[[318,4]]},"2644":{"position":[[317,4]]},"2646":{"position":[[282,5],[1005,7]]},"2668":{"position":[[101,4]]},"2682":{"position":[[114,4]]},"2708":{"position":[[1733,4]]},"2814":{"position":[[427,4],[462,7]]},"2823":{"position":[[961,4]]},"2826":{"position":[[1066,4]]},"2856":{"position":[[1116,4]]},"2862":{"position":[[98,4]]},"2895":{"position":[[388,4],[440,7]]},"2897":{"position":[[119,4]]},"2904":{"position":[[477,4]]},"2910":{"position":[[315,4]]},"3070":{"position":[[627,7]]},"3078":{"position":[[683,4]]},"3082":{"position":[[98,5],[344,4]]},"3084":{"position":[[58,4]]},"3086":{"position":[[152,5],[712,4]]},"3096":{"position":[[269,4]]},"3098":{"position":[[246,4]]},"3142":{"position":[[4093,4]]},"3154":{"position":[[171,5]]},"3186":{"position":[[476,4]]},"3204":{"position":[[201,4],[280,7]]},"3265":{"position":[[90,7]]},"3267":{"position":[[167,5],[710,4]]},"3269":{"position":[[277,4]]},"3271":{"position":[[506,7],[540,7],[1666,4]]},"3293":{"position":[[49,4]]},"3307":{"position":[[104,4]]},"3345":{"position":[[752,4]]},"3355":{"position":[[1816,4],[1934,4]]},"3365":{"position":[[673,7]]},"3421":{"position":[[4614,4]]},"3423":{"position":[[1547,4]]},"3429":{"position":[[530,4],[637,4]]},"3498":{"position":[[385,4]]},"3510":{"position":[[371,4]]},"3522":{"position":[[647,4]]},"3528":{"position":[[565,4]]},"3639":{"position":[[318,4]]},"3655":{"position":[[317,4]]},"3657":{"position":[[282,5],[1005,7]]}}}],["keepalive_timeout",{"_index":2318,"t":{"198":{"position":[[1187,17]]},"979":{"position":[[1033,17]]},"981":{"position":[[607,17]]},"2072":{"position":[[1033,17]]},"2074":{"position":[[607,17]]},"3309":{"position":[[1033,17]]},"3311":{"position":[[607,17]]}}}],["kenshinstock",{"_index":1958,"t":{"152":{"position":[[1386,13]]}}}],["kept",{"_index":1953,"t":{"152":{"position":[[1248,4]]},"407":{"position":[[247,4]]},"628":{"position":[[89,4]]},"630":{"position":[[260,4]]},"632":{"position":[[23,4]]},"634":{"position":[[23,4]]},"648":{"position":[[99,4]]},"652":{"position":[[21,4]]},"654":{"position":[[21,4]]},"686":{"position":[[561,4]]},"694":{"position":[[838,4]]},"798":{"position":[[618,4]]},"969":{"position":[[813,4],[984,4]]},"1098":{"position":[[627,4]]},"1231":{"position":[[247,4]]},"1427":{"position":[[259,4]]},"1475":{"position":[[102,4]]},"1627":{"position":[[89,4]]},"1629":{"position":[[260,4]]},"1631":{"position":[[23,4]]},"1633":{"position":[[23,4]]},"1643":{"position":[[99,4]]},"1647":{"position":[[21,4]]},"1649":{"position":[[21,4]]},"1670":{"position":[[307,4]]},"1729":{"position":[[556,4]]},"1737":{"position":[[820,4]]},"1813":{"position":[[618,4]]},"1855":{"position":[[1460,4]]},"1979":{"position":[[869,4],[1040,4]]},"2234":{"position":[[627,4]]},"2307":{"position":[[102,4]]},"2400":{"position":[[2216,4]]},"2480":{"position":[[247,4]]},"2632":{"position":[[4905,4]]},"2814":{"position":[[89,4]]},"2816":{"position":[[260,4]]},"2818":{"position":[[23,4]]},"2820":{"position":[[23,4]]},"2834":{"position":[[307,4]]},"2895":{"position":[[99,4]]},"2899":{"position":[[21,4]]},"2901":{"position":[[21,4]]},"2918":{"position":[[556,4]]},"2926":{"position":[[820,4]]},"3084":{"position":[[1424,4]]},"3138":{"position":[[869,4],[1040,4]]},"3520":{"position":[[102,4]]},"3584":{"position":[[627,4]]},"3643":{"position":[[4905,4]]}}}],["kerismak",{"_index":4448,"t":{"1215":{"position":[[71,11]]},"2327":{"position":[[71,11]]}}}],["kernel",{"_index":3393,"t":{"479":{"position":[[807,6]]}}}],["key",{"_index":398,"t":{"10":{"position":[[179,3]]},"94":{"position":[[374,3]]},"98":{"position":[[418,3],[561,3],[1024,4],[1283,4],[1356,3],[2219,4]]},"104":{"position":[[351,4]]},"110":{"position":[[514,5],[2581,3],[2823,3],[3176,3],[3231,5],[3970,4],[3984,4]]},"142":{"position":[[572,3]]},"170":{"position":[[235,3]]},"210":{"position":[[122,3]]},"214":{"position":[[590,3]]},"224":{"position":[[341,4],[526,4]]},"236":{"position":[[460,3]]},"270":{"position":[[367,6]]},"291":{"position":[[1186,3]]},"317":{"position":[[20,3]]},"321":{"position":[[1448,4],[1650,4],[1685,3],[1712,3]]},"327":{"position":[[123,4],[132,3]]},"329":{"position":[[273,3],[572,4],[937,3],[1021,3],[1101,3],[1182,3]]},"431":{"position":[[626,3]]},"459":{"position":[[261,7]]},"463":{"position":[[306,4]]},"471":{"position":[[946,3],[1072,3]]},"475":{"position":[[6659,4]]},"483":{"position":[[1061,3]]},"491":{"position":[[119,3],[279,3]]},"493":{"position":[[401,3],[513,5]]},"509":{"position":[[703,5]]},"539":{"position":[[196,3]]},"549":{"position":[[144,3],[244,3]]},"555":{"position":[[87,3]]},"557":{"position":[[56,4],[102,4]]},"664":{"position":[[208,3]]},"672":{"position":[[1656,4]]},"694":{"position":[[825,4]]},"794":{"position":[[39,7]]},"802":{"position":[[371,3],[479,3]]},"810":{"position":[[29,3],[392,3],[558,4],[622,4]]},"856":{"position":[[110,5],[139,5]]},"858":{"position":[[1176,5],[1205,5]]},"884":{"position":[[194,3]]},"886":{"position":[[249,3]]},"888":{"position":[[155,3]]},"937":{"position":[[120,3]]},"1005":{"position":[[435,4],[490,3]]},"1015":{"position":[[223,4],[266,4],[340,4]]},"1017":{"position":[[4525,7]]},"1033":{"position":[[299,3]]},"1045":{"position":[[1604,4],[1642,4],[1960,3]]},"1055":{"position":[[39,3]]},"1057":{"position":[[1555,5]]},"1059":{"position":[[173,3],[334,3]]},"1061":{"position":[[221,3],[389,3]]},"1128":{"position":[[29,3]]},"1189":{"position":[[569,4],[998,3]]},"1197":{"position":[[1150,4]]},"1247":{"position":[[763,3]]},"1257":{"position":[[626,3]]},"1311":{"position":[[261,7]]},"1315":{"position":[[306,4]]},"1385":{"position":[[175,3],[346,3]]},"1387":{"position":[[402,3],[514,5]]},"1423":{"position":[[1821,3]]},"1437":{"position":[[936,5]]},"1455":{"position":[[154,6],[170,3],[257,5],[294,3],[435,4],[507,3],[590,3],[835,3]]},"1457":{"position":[[2186,4]]},"1459":{"position":[[559,4]]},"1477":{"position":[[339,3]]},"1491":{"position":[[92,4],[148,3],[183,6],[339,3]]},"1529":{"position":[[87,3]]},"1531":{"position":[[56,4],[102,4]]},"1555":{"position":[[196,3]]},"1565":{"position":[[144,3],[244,3]]},"1599":{"position":[[370,3]]},"1603":{"position":[[2222,4]]},"1658":{"position":[[208,3]]},"1703":{"position":[[729,3]]},"1737":{"position":[[807,4]]},"1765":{"position":[[391,4],[446,3]]},"1809":{"position":[[39,7]]},"1817":{"position":[[371,3],[479,3]]},"1825":{"position":[[29,3],[392,3],[657,3],[736,4],[800,4]]},"1843":{"position":[[1207,5],[1236,5]]},"1899":{"position":[[344,5],[373,5],[1097,5],[1126,5]]},"1997":{"position":[[194,3]]},"1999":{"position":[[249,3]]},"2001":{"position":[[155,3]]},"2058":{"position":[[120,3]]},"2086":{"position":[[385,4],[1067,3]]},"2088":{"position":[[633,3],[670,3]]},"2094":{"position":[[654,3],[691,3]]},"2098":{"position":[[1150,4]]},"2138":{"position":[[39,3]]},"2140":{"position":[[1555,5]]},"2142":{"position":[[173,3],[334,3]]},"2144":{"position":[[189,3],[357,3]]},"2160":{"position":[[1821,3]]},"2279":{"position":[[29,3]]},"2287":{"position":[[157,6],[173,3],[260,5],[297,3],[438,4],[510,3],[593,3],[838,3]]},"2289":{"position":[[2240,4]]},"2291":{"position":[[534,4]]},"2309":{"position":[[386,3]]},"2323":{"position":[[92,4],[148,3],[183,6],[339,3]]},"2352":{"position":[[223,4],[266,4],[340,4]]},"2354":{"position":[[4642,7]]},"2372":{"position":[[299,3]]},"2384":{"position":[[1604,4],[1642,4],[1960,3]]},"2496":{"position":[[763,3]]},"2506":{"position":[[626,3]]},"2556":{"position":[[184,3],[371,3]]},"2558":{"position":[[415,3],[532,5]]},"2578":{"position":[[648,4],[781,4]]},"2598":{"position":[[270,7]]},"2602":{"position":[[306,4]]},"2606":{"position":[[960,5]]},"2636":{"position":[[440,4]]},"2654":{"position":[[323,7]]},"2658":{"position":[[306,4]]},"2686":{"position":[[10021,4]]},"2708":{"position":[[774,4]]},"2712":{"position":[[374,6],[397,6]]},"2718":{"position":[[32,3],[47,3],[84,4]]},"2724":{"position":[[196,3]]},"2734":{"position":[[144,3],[244,3]]},"2762":{"position":[[34,3],[70,3],[98,3],[168,3],[311,4],[548,4]]},"2764":{"position":[[87,3]]},"2780":{"position":[[1174,4]]},"2782":{"position":[[370,3]]},"2788":{"position":[[2222,4]]},"2823":{"position":[[482,4]]},"2826":{"position":[[359,4]]},"2904":{"position":[[74,4]]},"2906":{"position":[[74,4]]},"2910":{"position":[[208,3]]},"2920":{"position":[[308,4]]},"2922":{"position":[[215,4]]},"2924":{"position":[[173,4]]},"2926":{"position":[[807,4]]},"2970":{"position":[[751,4],[806,3]]},"3038":{"position":[[41,7]]},"3046":{"position":[[515,3],[623,3]]},"3054":{"position":[[29,3],[392,3],[657,3],[736,4],[800,4],[975,3]]},"3072":{"position":[[1495,5],[1524,5]]},"3076":{"position":[[639,4]]},"3084":{"position":[[820,4]]},"3134":{"position":[[344,5],[373,5],[1097,5],[1126,5]]},"3186":{"position":[[194,3]]},"3188":{"position":[[249,3]]},"3190":{"position":[[155,3]]},"3253":{"position":[[120,3]]},"3271":{"position":[[385,4],[1033,3]]},"3273":{"position":[[579,3],[616,3]]},"3279":{"position":[[600,3],[637,3]]},"3283":{"position":[[1150,4]]},"3341":{"position":[[360,4],[713,4]]},"3353":{"position":[[39,3]]},"3355":{"position":[[1555,5]]},"3357":{"position":[[173,3],[334,3]]},"3359":{"position":[[189,3],[357,3]]},"3389":{"position":[[1821,3]]},"3411":{"position":[[1081,4]]},"3413":{"position":[[223,4],[266,4],[340,4]]},"3415":{"position":[[4671,7]]},"3433":{"position":[[299,3]]},"3445":{"position":[[1816,4],[1854,4],[2172,3]]},"3496":{"position":[[193,3],[315,4]]},"3498":{"position":[[173,3],[210,3],[240,4],[291,3],[419,3],[436,3],[516,3],[808,3]]},"3502":{"position":[[241,4],[727,5],[1041,4],[1748,4]]},"3504":{"position":[[118,4],[609,4]]},"3514":{"position":[[271,4]]},"3516":{"position":[[153,4]]},"3518":{"position":[[486,4]]},"3522":{"position":[[98,4],[446,3]]},"3526":{"position":[[319,4]]},"3536":{"position":[[92,4],[148,3],[183,6],[339,3]]},"3538":{"position":[[1864,3]]},"3561":{"position":[[29,3]]},"3647":{"position":[[440,4]]},"3665":{"position":[[323,7]]},"3669":{"position":[[306,4]]}}}],["key\"http/1.1",{"_index":4596,"t":{"1469":{"position":[[399,12]]},"1471":{"position":[[328,12]]},"1473":{"position":[[664,12]]},"1475":{"position":[[361,12]]},"1479":{"position":[[170,12]]},"2301":{"position":[[442,12]]},"2303":{"position":[[328,12]]},"2305":{"position":[[664,12]]},"2307":{"position":[[361,12]]},"2311":{"position":[[170,12]]}}}],["key=centrifugo_addr",{"_index":2940,"t":{"329":{"position":[[1211,21]]}}}],["key=granulr_proxy_mode08:25:33",{"_index":2939,"t":{"329":{"position":[[1137,30]]}}}],["key=typ",{"_index":2937,"t":{"329":{"position":[[1051,8]]}}}],["key=watch",{"_index":2935,"t":{"329":{"position":[[971,9]]}}}],["keyauth",{"_index":4695,"t":{"1491":{"position":[[322,7],[358,8],[528,8]]},"2323":{"position":[[322,7],[358,8],[528,8]]},"3536":{"position":[[322,7],[358,8],[528,8]]}}}],["keycloak",{"_index":2368,"t":{"208":{"position":[[241,9],[355,8],[518,8]]},"212":{"position":[[11,8],[210,9]]},"216":{"position":[[273,8],[764,8],[778,9],[819,10],[1111,8],[1148,8],[1288,8],[1638,9],[1750,8],[2831,10],[2923,8],[4375,8]]},"327":{"position":[[210,9]]},"1825":{"position":[[586,8],[694,9]]},"3054":{"position":[[586,8],[694,9]]}}}],["keycloak'",{"_index":2397,"t":{"214":{"position":[[635,10]]}}}],["keycloak.authent",{"_index":2439,"t":{"216":{"position":[[1779,23],[2419,24]]}}}],["keycloak.login",{"_index":2446,"t":{"216":{"position":[[2000,18]]}}}],["keycloak.logout",{"_index":2444,"t":{"216":{"position":[[1913,19],[2732,18]]}}}],["keycloak.token",{"_index":2449,"t":{"216":{"position":[[2543,15]]}}}],["keycloak.tokenparsed?.preferred_username}log",{"_index":2440,"t":{"216":{"position":[[1813,9]]}}}],["paceto",{"_index":1267,"t":{"64":{"position":[[1223,7]]}}}],["pack",{"_index":4835,"t":{"1527":{"position":[[18,6]]},"2760":{"position":[[18,6]]}}}],["packag",{"_index":300,"t":{"8":{"position":[[113,7],[296,7]]},"12":{"position":[[1248,7],[1309,7]]},"90":{"position":[[595,7]]},"104":{"position":[[54,7]]},"160":{"position":[[931,8]]},"216":{"position":[[195,8],[1197,7]]},"389":{"position":[[78,9]]},"469":{"position":[[37,7]]},"475":{"position":[[57,8]]},"499":{"position":[[162,9]]},"517":{"position":[[66,8],[409,8],[516,9]]},"545":{"position":[[4,7]]},"547":{"position":[[4,7]]},"604":{"position":[[45,7],[242,7],[267,7]]},"1347":{"position":[[78,9]]},"1393":{"position":[[162,9]]},"1445":{"position":[[66,8],[432,8],[539,9]]},"1489":{"position":[[658,7]]},"1491":{"position":[[218,7]]},"1561":{"position":[[4,7]]},"1563":{"position":[[4,7]]},"1885":{"position":[[1071,7]]},"2321":{"position":[[658,7]]},"2323":{"position":[[218,7]]},"2462":{"position":[[85,9]]},"2564":{"position":[[166,9]]},"2614":{"position":[[66,8],[368,8],[475,9]]},"2730":{"position":[[4,7]]},"2732":{"position":[[4,7]]},"3116":{"position":[[1071,7]]},"3367":{"position":[[2434,7]]},"3534":{"position":[[658,7]]},"3536":{"position":[[218,7]]}}}],["packagecloud.io",{"_index":3550,"t":{"517":{"position":[[110,16]]},"1445":{"position":[[110,16]]},"2614":{"position":[[110,16]]}}}],["page",{"_index":2061,"t":{"176":{"position":[[28,4],[141,4]]},"178":{"position":[[90,4]]},"182":{"position":[[131,6],[140,4]]},"192":{"position":[[3284,5],[3307,5]]},"194":{"position":[[2561,5],[2654,4]]},"200":{"position":[[36,4],[2382,4],[2473,5]]},"216":{"position":[[3179,5]]},"232":{"position":[[623,4]]},"252":{"position":[[880,5]]},"309":{"position":[[51,5]]},"343":{"position":[[8,4]]},"359":{"position":[[8,4]]},"395":{"position":[[8,4]]},"445":{"position":[[8,4]]},"475":{"position":[[6828,4]]},"485":{"position":[[8,4]]},"497":{"position":[[61,4]]},"507":{"position":[[8,4]]},"523":{"position":[[8,4]]},"531":{"position":[[8,4]]},"537":{"position":[[8,4]]},"551":{"position":[[8,4]]},"561":{"position":[[8,4]]},"565":{"position":[[8,4]]},"585":{"position":[[8,4]]},"622":{"position":[[8,4]]},"626":{"position":[[8,4]]},"642":{"position":[[8,4]]},"646":{"position":[[8,4]]},"660":{"position":[[8,4]]},"662":{"position":[[8,4]]},"670":{"position":[[8,4]]},"684":{"position":[[8,4]]},"696":{"position":[[8,4]]},"770":{"position":[[8,4]]},"798":{"position":[[1487,4]]},"812":{"position":[[8,4]]},"860":{"position":[[8,4]]},"872":{"position":[[8,4]]},"922":{"position":[[353,4]]},"926":{"position":[[1056,4]]},"947":{"position":[[8,4]]},"959":{"position":[[8,4]]},"967":{"position":[[8,4]]},"975":{"position":[[8,4]]},"985":{"position":[[8,4]]},"1007":{"position":[[8,4]]},"1017":{"position":[[1454,4]]},"1053":{"position":[[8,4]]},"1065":{"position":[[8,4]]},"1073":{"position":[[8,4]]},"1088":{"position":[[8,4]]},"1096":{"position":[[8,4]]},"1111":{"position":[[8,4]]},"1132":{"position":[[8,4]]},"1149":{"position":[[8,4]]},"1164":{"position":[[8,4]]},"1181":{"position":[[8,4]]},"1215":{"position":[[51,5]]},"1219":{"position":[[8,4]]},"1269":{"position":[[553,6]]},"1275":{"position":[[8,4]]},"1291":{"position":[[8,4]]},"1297":{"position":[[8,4]]},"1317":{"position":[[8,4]]},"1353":{"position":[[8,4]]},"1357":{"position":[[8,4]]},"1379":{"position":[[8,4]]},"1391":{"position":[[58,4]]},"1401":{"position":[[8,4]]},"1435":{"position":[[8,4]]},"1451":{"position":[[8,4]]},"1493":{"position":[[8,4]]},"1505":{"position":[[3762,4],[3801,4]]},"1507":{"position":[[8,4]]},"1517":{"position":[[8,4]]},"1525":{"position":[[8,4]]},"1533":{"position":[[8,4]]},"1553":{"position":[[8,4]]},"1567":{"position":[[8,4]]},"1595":{"position":[[8,4]]},"1601":{"position":[[8,4]]},"1621":{"position":[[8,4]]},"1625":{"position":[[8,4]]},"1641":{"position":[[8,4]]},"1656":{"position":[[8,4]]},"1664":{"position":[[8,4]]},"1674":{"position":[[8,4]]},"1709":{"position":[[585,6]]},"1713":{"position":[[686,6]]},"1717":{"position":[[342,6]]},"1727":{"position":[[8,4]]},"1739":{"position":[[8,4]]},"1769":{"position":[[8,4]]},"1785":{"position":[[8,4]]},"1813":{"position":[[1487,4]]},"1829":{"position":[[8,4]]},"1901":{"position":[[8,4]]},"1917":{"position":[[8,4]]},"1931":{"position":[[8,4]]},"1969":{"position":[[8,4]]},"1977":{"position":[[8,4]]},"1985":{"position":[[8,4]]},"2043":{"position":[[353,4]]},"2047":{"position":[[1056,4]]},"2068":{"position":[[8,4]]},"2078":{"position":[[8,4]]},"2114":{"position":[[8,4]]},"2128":{"position":[[8,4]]},"2136":{"position":[[8,4]]},"2146":{"position":[[8,4]]},"2164":{"position":[[8,4]]},"2176":{"position":[[8,4]]},"2183":{"position":[[8,4]]},"2193":{"position":[[8,4]]},"2208":{"position":[[8,4]]},"2215":{"position":[[8,4]]},"2232":{"position":[[8,4]]},"2245":{"position":[[8,4]]},"2262":{"position":[[8,4]]},"2283":{"position":[[8,4]]},"2327":{"position":[[51,5]]},"2329":{"position":[[8,4]]},"2344":{"position":[[8,4]]},"2354":{"position":[[1571,4]]},"2398":{"position":[[8,4]]},"2414":{"position":[[8,4]]},"2426":{"position":[[8,4]]},"2432":{"position":[[8,4]]},"2468":{"position":[[8,4]]},"2518":{"position":[[553,6]]},"2524":{"position":[[8,4]]},"2528":{"position":[[8,4]]},"2550":{"position":[[8,4]]},"2562":{"position":[[58,4]]},"2572":{"position":[[8,4]]},"2584":{"position":[[8,4]]},"2604":{"position":[[8,4]]},"2620":{"position":[[8,4]]},"2662":{"position":[[8,4]]},"2686":{"position":[[3848,4]]},"2688":{"position":[[8,4]]},"2700":{"position":[[8,4]]},"2710":{"position":[[8,4]]},"2722":{"position":[[8,4]]},"2738":{"position":[[8,4]]},"2758":{"position":[[8,4]]},"2766":{"position":[[8,4]]},"2778":{"position":[[8,4]]},"2786":{"position":[[8,4]]},"2808":{"position":[[8,4]]},"2812":{"position":[[8,4]]},"2828":{"position":[[8,4]]},"2838":{"position":[[8,4]]},"2873":{"position":[[276,6],[1165,5],[1197,4]]},"2877":{"position":[[245,6],[1151,5],[1183,4]]},"2881":{"position":[[208,6]]},"2893":{"position":[[8,4]]},"2908":{"position":[[8,4]]},"2916":{"position":[[8,4]]},"2928":{"position":[[8,4]]},"2944":{"position":[[8,4]]},"2976":{"position":[[8,4]]},"3014":{"position":[[8,4]]},"3042":{"position":[[1406,4]]},"3058":{"position":[[8,4]]},"3136":{"position":[[8,4]]},"3144":{"position":[[8,4]]},"3158":{"position":[[8,4]]},"3174":{"position":[[8,4]]},"3234":{"position":[[353,4]]},"3240":{"position":[[1146,4]]},"3263":{"position":[[8,4]]},"3297":{"position":[[8,4]]},"3305":{"position":[[8,4]]},"3315":{"position":[[8,4]]},"3331":{"position":[[8,4]]},"3339":{"position":[[8,4]]},"3351":{"position":[[8,4]]},"3361":{"position":[[8,4]]},"3375":{"position":[[8,4]]},"3393":{"position":[[8,4]]},"3405":{"position":[[8,4]]},"3415":{"position":[[1571,4]]},"3453":{"position":[[8,4]]},"3460":{"position":[[8,4]]},"3468":{"position":[[8,4]]},"3483":{"position":[[8,4]]},"3490":{"position":[[8,4]]},"3494":{"position":[[8,4]]},"3544":{"position":[[8,4]]},"3565":{"position":[[8,4]]},"3582":{"position":[[8,4]]},"3595":{"position":[[8,4]]},"3614":{"position":[[8,4]]},"3631":{"position":[[8,4]]}}}],["page'",{"_index":5452,"t":{"2686":{"position":[[3800,6]]}}}],["pagin",{"_index":962,"t":{"34":{"position":[[835,8],[1443,10]]},"74":{"position":[[912,8],[2528,10]]},"172":{"position":[[1306,10]]},"178":{"position":[[333,10]]},"409":{"position":[[732,12],[850,11],[930,11]]},"971":{"position":[[1397,8],[2005,10]]},"1233":{"position":[[732,12],[850,11],[930,11]]},"1477":{"position":[[708,8]]},"1709":{"position":[[10,9],[523,10]]},"1713":{"position":[[624,10]]},"1717":{"position":[[287,10]]},"1981":{"position":[[1397,8],[2005,10]]},"2309":{"position":[[755,8]]},"2482":{"position":[[732,12],[850,11],[930,11]]},"2873":{"position":[[10,9],[214,10]]},"2877":{"position":[[183,10]]},"2881":{"position":[[153,10]]},"3140":{"position":[[1397,8],[2005,10]]},"3522":{"position":[[815,8]]}}}],["paginating.var",{"_index":967,"t":{"34":{"position":[[963,14]]},"971":{"position":[[1525,14]]},"1981":{"position":[[1525,14]]},"3140":{"position":[[1525,14]]}}}],["paid",{"_index":1984,"t":{"158":{"position":[[359,4]]}}}],["pain",{"_index":595,"t":{"16":{"position":[[778,8]]},"20":{"position":[[760,7]]},"28":{"position":[[275,7]]}}}],["pair",{"_index":1731,"t":{"110":{"position":[[2833,5]]}}}],["panel",{"_index":4072,"t":{"930":{"position":[[159,5]]},"1505":{"position":[[1767,6],[1950,6],[8597,6],[9162,5],[9201,5]]},"2051":{"position":[[159,5]]},"2686":{"position":[[1782,6],[1965,6],[8698,6],[9293,5],[9336,5]]},"3244":{"position":[[159,5]]}}}],["paper",{"_index":4408,"t":{"1197":{"position":[[1278,5]]},"2098":{"position":[[1278,5]]},"3283":{"position":[[1278,5]]}}}],["paradigm",{"_index":2995,"t":{"345":{"position":[[875,8]]},"1277":{"position":[[875,8]]}}}],["paralel",{"_index":3172,"t":{"471":{"position":[[4208,9]]},"475":{"position":[[4549,11],[6013,11]]},"483":{"position":[[1613,12]]}}}],["parallel",{"_index":2522,"t":{"236":{"position":[[715,11],[822,8]]},"351":{"position":[[155,8]]},"471":{"position":[[4074,12]]},"475":{"position":[[5953,11]]},"912":{"position":[[602,9]]},"1283":{"position":[[155,8]]},"1591":{"position":[[441,8]]},"2027":{"position":[[602,9]]},"2406":{"position":[[156,8],[276,9]]},"2784":{"position":[[441,8]]},"3216":{"position":[[602,9]]}}}],["param",{"_index":1060,"t":{"46":{"position":[[259,5]]},"70":{"position":[[748,6]]},"252":{"position":[[614,7]]},"321":{"position":[[384,9],[821,7],[1643,6]]},"608":{"position":[[704,5]]},"636":{"position":[[563,9],[664,7]]},"638":{"position":[[450,9],[584,7]]},"656":{"position":[[153,9],[246,7]]},"658":{"position":[[155,9],[225,7]]},"688":{"position":[[395,9],[472,7]]},"690":{"position":[[299,9],[1061,7],[1304,6]]},"692":{"position":[[260,9],[337,7]]},"1098":{"position":[[141,7],[190,5]]},"1177":{"position":[[1082,9]]},"1359":{"position":[[1136,5]]},"1405":{"position":[[757,9],[809,9]]},"1407":{"position":[[409,9],[739,6],[803,6]]},"1409":{"position":[[185,9],[243,6]]},"1411":{"position":[[205,9]]},"1413":{"position":[[462,9]]},"1455":{"position":[[313,6],[446,5],[994,7],[1058,6],[1131,6],[1313,9]]},"1457":{"position":[[147,9],[410,9],[773,9],[1141,9],[1471,9],[1876,7]]},"1459":{"position":[[97,9],[190,7]]},"1461":{"position":[[58,7]]},"1463":{"position":[[81,9],[153,7]]},"1465":{"position":[[72,9],[116,7]]},"1467":{"position":[[107,7]]},"1469":{"position":[[231,9],[317,9],[759,7]]},"1471":{"position":[[162,9],[239,9],[495,7]]},"1473":{"position":[[488,9],[570,9],[933,7]]},"1475":{"position":[[202,9],[279,9],[494,7]]},"1477":{"position":[[101,9],[124,7]]},"1479":{"position":[[105,9],[484,7]]},"1505":{"position":[[9968,9]]},"1597":{"position":[[1254,9]]},"1599":{"position":[[83,7]]},"1636":{"position":[[563,9],[664,7]]},"1639":{"position":[[450,9],[584,7]]},"1652":{"position":[[153,9],[246,7]]},"1654":{"position":[[155,9],[225,7]]},"1731":{"position":[[395,9],[472,7]]},"1733":{"position":[[299,9],[1061,7],[1304,6]]},"1735":{"position":[[260,9],[337,7]]},"1847":{"position":[[716,9]]},"1855":{"position":[[896,9]]},"2118":{"position":[[426,9]]},"2228":{"position":[[1082,9]]},"2234":{"position":[[141,7],[190,5]]},"2287":{"position":[[316,6],[449,5],[997,7],[1061,6],[1134,6],[1316,9]]},"2289":{"position":[[208,9],[470,9],[833,9],[1201,9],[1525,9],[1930,7]]},"2291":{"position":[[97,9],[190,7]]},"2293":{"position":[[58,7]]},"2295":{"position":[[81,9],[153,7]]},"2297":{"position":[[72,9],[116,7]]},"2299":{"position":[[107,7]]},"2301":{"position":[[274,9],[360,9],[802,7]]},"2303":{"position":[[162,9],[239,9],[495,7]]},"2305":{"position":[[488,9],[570,9],[933,7]]},"2307":{"position":[[202,9],[279,9],[494,7]]},"2309":{"position":[[101,9],[124,7]]},"2311":{"position":[[105,9],[484,7]]},"2313":{"position":[[291,9],[367,9]]},"2530":{"position":[[1136,5]]},"2578":{"position":[[584,6],[641,6]]},"2782":{"position":[[83,7]]},"2823":{"position":[[628,7]]},"2826":{"position":[[548,7]]},"2904":{"position":[[210,7]]},"2906":{"position":[[189,7]]},"2920":{"position":[[436,7]]},"2922":{"position":[[1025,7],[1268,6]]},"2924":{"position":[[301,7]]},"3498":{"position":[[310,6],[358,5]]},"3584":{"position":[[141,7],[190,5]]}}}],["paramet",{"_index":2518,"t":{"234":{"position":[[457,9]]},"331":{"position":[[37,9],[982,9],[1774,9]]},"636":{"position":[[672,9],[687,9]]},"638":{"position":[[592,9],[607,9]]},"656":{"position":[[254,9],[269,9]]},"658":{"position":[[233,9],[248,9]]},"686":{"position":[[106,10]]},"688":{"position":[[480,9],[495,9]]},"690":{"position":[[1069,9],[1084,9]]},"692":{"position":[[345,9],[360,9]]},"1098":{"position":[[1066,11]]},"1162":{"position":[[667,9]]},"1453":{"position":[[220,10]]},"1457":{"position":[[1884,9],[1899,9]]},"1459":{"position":[[198,9],[213,9]]},"1461":{"position":[[66,9],[81,9]]},"1463":{"position":[[161,9],[176,9]]},"1465":{"position":[[124,9],[139,9]]},"1467":{"position":[[115,9],[130,9]]},"1469":{"position":[[767,9],[782,9]]},"1471":{"position":[[503,9],[518,9]]},"1473":{"position":[[126,9],[303,10],[941,9],[956,9]]},"1475":{"position":[[502,9],[517,9]]},"1477":{"position":[[132,9],[147,9]]},"1485":{"position":[[501,9]]},"1513":{"position":[[81,10]]},"1527":{"position":[[975,11]]},"1599":{"position":[[91,9],[106,9]]},"1636":{"position":[[672,9],[687,9]]},"1639":{"position":[[592,9],[607,9]]},"1652":{"position":[[254,9],[269,9]]},"1654":{"position":[[233,9],[248,9]]},"1729":{"position":[[106,10]]},"1731":{"position":[[480,9],[495,9]]},"1733":{"position":[[1069,9],[1084,9]]},"1735":{"position":[[345,9],[360,9]]},"2234":{"position":[[1066,11]]},"2285":{"position":[[228,11]]},"2289":{"position":[[1938,9],[1953,9]]},"2291":{"position":[[198,9],[213,9]]},"2293":{"position":[[66,9],[81,9]]},"2295":{"position":[[161,9],[176,9]]},"2297":{"position":[[124,9],[139,9]]},"2299":{"position":[[115,9],[130,9]]},"2301":{"position":[[810,9],[825,9]]},"2303":{"position":[[503,9],[518,9]]},"2305":{"position":[[126,9],[303,10],[941,9],[956,9]]},"2307":{"position":[[502,9],[517,9]]},"2309":{"position":[[132,9],[147,9]]},"2317":{"position":[[501,9]]},"2342":{"position":[[667,9]]},"2760":{"position":[[1073,11]]},"2772":{"position":[[81,10]]},"2782":{"position":[[91,9],[106,9]]},"2823":{"position":[[636,9],[651,9]]},"2826":{"position":[[556,9],[571,9]]},"2904":{"position":[[218,9],[233,9]]},"2906":{"position":[[197,9],[212,9]]},"2918":{"position":[[106,10]]},"2920":{"position":[[444,9],[459,9]]},"2922":{"position":[[1033,9],[1048,9]]},"2924":{"position":[[309,9],[324,9]]},"3518":{"position":[[126,9],[303,10]]},"3530":{"position":[[501,9]]},"3584":{"position":[[1066,11]]},"3627":{"position":[[667,9]]},"3629":{"position":[[34,9],[983,9],[1775,9]]}}}],["paramount",{"_index":2772,"t":{"280":{"position":[[2166,9]]}}}],["pars",{"_index":1695,"t":{"108":{"position":[[1463,7]]},"110":{"position":[[2766,6]]},"764":{"position":[[82,5]]},"1405":{"position":[[3295,5]]},"3251":{"position":[[331,6]]}}}],["parsebool",{"_index":4025,"t":{"878":{"position":[[376,9]]},"1991":{"position":[[376,9]]},"3180":{"position":[[376,9]]}}}],["parser",{"_index":2535,"t":{"246":{"position":[[96,6]]}}}],["parser\");const",{"_index":2542,"t":{"246":{"position":[[219,14]]}}}],["part",{"_index":447,"t":{"10":{"position":[[1430,4],[1851,4]]},"16":{"position":[[1873,4],[4924,4]]},"20":{"position":[[19,4]]},"38":{"position":[[2153,5]]},"50":{"position":[[1459,4]]},"58":{"position":[[1725,6]]},"60":{"position":[[2143,6]]},"78":{"position":[[2563,6]]},"112":{"position":[[10,4]]},"122":{"position":[[21,4],[100,4]]},"132":{"position":[[1030,4]]},"134":{"position":[[1317,4]]},"136":{"position":[[50,4],[107,4]]},"144":{"position":[[373,4]]},"152":{"position":[[105,4],[774,4]]},"154":{"position":[[423,5]]},"156":{"position":[[809,6]]},"162":{"position":[[191,6]]},"176":{"position":[[2484,5]]},"180":{"position":[[204,4],[375,4]]},"182":{"position":[[544,5]]},"216":{"position":[[2292,4]]},"238":{"position":[[203,4]]},"242":{"position":[[574,5]]},"246":{"position":[[2435,5]]},"264":{"position":[[338,5]]},"283":{"position":[[999,4]]},"289":{"position":[[831,4]]},"319":{"position":[[566,4]]},"357":{"position":[[1168,4]]},"409":{"position":[[500,4]]},"467":{"position":[[542,4]]},"477":{"position":[[1561,4],[2440,4],[2469,4],[2506,4]]},"559":{"position":[[255,4]]},"604":{"position":[[197,4]]},"816":{"position":[[411,4]]},"918":{"position":[[888,5]]},"951":{"position":[[395,4]]},"957":{"position":[[154,4]]},"1039":{"position":[[416,4]]},"1094":{"position":[[161,4]]},"1207":{"position":[[155,4]]},"1233":{"position":[[500,4]]},"1251":{"position":[[326,4]]},"1269":{"position":[[234,4]]},"1289":{"position":[[1168,4]]},"1293":{"position":[[1105,4]]},"1509":{"position":[[472,4]]},"1511":{"position":[[144,5],[1016,5]]},"1593":{"position":[[255,4]]},"1688":{"position":[[238,4]]},"1721":{"position":[[573,4]]},"1835":{"position":[[396,4]]},"1885":{"position":[[760,4]]},"1921":{"position":[[395,4]]},"1927":{"position":[[154,4]]},"2039":{"position":[[1103,5]]},"2110":{"position":[[155,4]]},"2189":{"position":[[161,4]]},"2378":{"position":[[416,4]]},"2412":{"position":[[1188,4]]},"2428":{"position":[[1165,5]]},"2482":{"position":[[500,4]]},"2500":{"position":[[326,4]]},"2518":{"position":[[234,4]]},"2712":{"position":[[256,4]]},"2768":{"position":[[472,4]]},"2770":{"position":[[144,5],[1016,5]]},"2776":{"position":[[255,4]]},"2852":{"position":[[238,4]]},"2887":{"position":[[573,4]]},"3060":{"position":[[861,4]]},"3116":{"position":[[760,4]]},"3148":{"position":[[395,4]]},"3154":{"position":[[154,4]]},"3230":{"position":[[1103,5]]},"3293":{"position":[[155,4]]},"3363":{"position":[[131,5]]},"3439":{"position":[[416,4]]},"3492":{"position":[[161,4]]}}}],["parti",{"_index":2365,"t":{"208":{"position":[[191,5]]},"339":{"position":[[110,5]]},"1295":{"position":[[197,5],[356,5]]},"2430":{"position":[[197,5],[356,5]]},"3365":{"position":[[166,5],[1054,5]]}}}],["particip",{"_index":2099,"t":{"178":{"position":[[235,12]]}}}],["participant'",{"_index":2361,"t":{"202":{"position":[[1988,13]]}}}],["particular",{"_index":2232,"t":{"194":{"position":[[87,10],[2796,11]]},"272":{"position":[[1031,11]]},"305":{"position":[[183,10]]},"437":{"position":[[1630,10]]},"636":{"position":[[140,10]]},"690":{"position":[[81,10]]},"1217":{"position":[[364,11]]},"1263":{"position":[[1634,10]]},"1636":{"position":[[140,10]]},"1733":{"position":[[81,10]]},"1763":{"position":[[94,10],[665,10]]},"1929":{"position":[[398,10]]},"2396":{"position":[[362,11]]},"2823":{"position":[[140,10]]},"2922":{"position":[[81,10]]},"2968":{"position":[[94,10],[665,10]]},"3156":{"position":[[398,10]]}}}],["particularli",{"_index":2884,"t":{"317":{"position":[[504,12]]}}}],["partit",{"_index":678,"t":{"16":{"position":[[4469,10]]},"234":{"position":[[616,12]]},"236":{"position":[[628,13],[668,11],[750,11]]}}}],["pass",{"_index":1114,"t":{"50":{"position":[[2197,7]]},"58":{"position":[[985,7]]},"60":{"position":[[1110,4]]},"64":{"position":[[1350,4]]},"70":{"position":[[165,7],[688,4]]},"128":{"position":[[141,6]]},"144":{"position":[[1481,4],[1624,7]]},"291":{"position":[[225,4]]},"303":{"position":[[68,6]]},"341":{"position":[[104,6]]},"367":{"position":[[226,8]]},"429":{"position":[[1291,4]]},"447":{"position":[[185,4]]},"475":{"position":[[1636,4]]},"493":{"position":[[572,4],[626,5]]},"549":{"position":[[303,4]]},"724":{"position":[[204,7]]},"792":{"position":[[1224,4]]},"798":{"position":[[653,6]]},"802":{"position":[[511,4]]},"818":{"position":[[167,4]]},"846":{"position":[[434,6]]},"892":{"position":[[440,7],[1012,4]]},"973":{"position":[[3201,6]]},"1017":{"position":[[737,4],[902,6],[955,4],[1238,4],[5048,4]]},"1039":{"position":[[647,4],[1170,7],[1422,4]]},"1041":{"position":[[765,6]]},"1098":{"position":[[100,4]]},"1134":{"position":[[360,4]]},"1162":{"position":[[846,4]]},"1166":{"position":[[17,4]]},"1211":{"position":[[104,6]]},"1255":{"position":[[1291,4]]},"1269":{"position":[[494,7]]},"1299":{"position":[[185,4]]},"1325":{"position":[[222,8]]},"1359":{"position":[[1680,4]]},"1387":{"position":[[573,4],[627,5]]},"1413":{"position":[[987,6]]},"1455":{"position":[[285,4],[491,7]]},"1505":{"position":[[3516,7],[5893,6]]},"1565":{"position":[[303,4]]},"1688":{"position":[[560,4]]},"1807":{"position":[[751,4]]},"1813":{"position":[[653,6]]},"1817":{"position":[[511,4]]},"1959":{"position":[[201,7]]},"2005":{"position":[[440,7],[1012,4]]},"2088":{"position":[[462,7],[657,7],[879,7]]},"2094":{"position":[[465,7],[678,7],[918,7]]},"2120":{"position":[[528,6]]},"2168":{"position":[[898,4]]},"2217":{"position":[[17,4]]},"2234":{"position":[[100,4]]},"2247":{"position":[[360,4]]},"2287":{"position":[[288,4],[494,7]]},"2342":{"position":[[846,4]]},"2354":{"position":[[737,4],[902,6],[1072,4],[1355,4],[5171,4]]},"2378":{"position":[[647,4],[1170,7],[1422,4]]},"2380":{"position":[[739,6]]},"2392":{"position":[[2312,4],[2447,7]]},"2394":{"position":[[104,6]]},"2440":{"position":[[223,8]]},"2504":{"position":[[1291,4]]},"2518":{"position":[[494,7]]},"2530":{"position":[[1680,4]]},"2558":{"position":[[595,4],[649,5]]},"2586":{"position":[[185,4]]},"2686":{"position":[[3543,7],[5952,6]]},"2734":{"position":[[303,4]]},"2852":{"position":[[560,4]]},"3004":{"position":[[201,7]]},"3036":{"position":[[829,4]]},"3046":{"position":[[655,4]]},"3194":{"position":[[440,7],[1012,4]]},"3220":{"position":[[196,4]]},"3273":{"position":[[435,7],[603,7],[798,7]]},"3279":{"position":[[438,7],[624,7],[837,7]]},"3343":{"position":[[512,6]]},"3365":{"position":[[1390,4]]},"3367":{"position":[[4217,7]]},"3397":{"position":[[898,4]]},"3415":{"position":[[737,4],[902,6],[1072,4],[1355,4],[5200,4]]},"3439":{"position":[[647,4],[1170,7],[1422,4]]},"3441":{"position":[[739,6]]},"3498":{"position":[[282,4],[403,7]]},"3567":{"position":[[17,4]]},"3584":{"position":[[100,4]]},"3597":{"position":[[360,4]]},"3612":{"position":[[2511,4],[2646,7]]},"3627":{"position":[[846,4]]}}}],["pass'app.post('/login",{"_index":2592,"t":{"246":{"position":[[1971,23]]}}}],["pass)/limit",{"_index":4104,"t":{"949":{"position":[[229,18]]},"1919":{"position":[[229,18]]},"3146":{"position":[[229,18]]}}}],["proce",{"_index":273,"t":{"4":{"position":[[1157,8]]},"160":{"position":[[675,7]]},"311":{"position":[[1267,7]]},"331":{"position":[[1330,7]]},"525":{"position":[[772,7]]},"1355":{"position":[[784,7]]},"3629":{"position":[[1331,7]]},"3639":{"position":[[954,7]]},"3655":{"position":[[1138,7]]}}}],["procec",{"_index":5002,"t":{"1723":{"position":[[495,10]]}}}],["procedur",{"_index":717,"t":{"16":{"position":[[6556,10]]}}}],["proceed",{"_index":2694,"t":{"264":{"position":[[1615,10]]}}}],["process",{"_index":343,"t":{"8":{"position":[[820,8]]},"10":{"position":[[382,7],[736,7]]},"14":{"position":[[981,7]]},"16":{"position":[[107,7],[369,7],[2935,8],[4513,7]]},"18":{"position":[[2144,8],[2170,7],[2378,7]]},"66":{"position":[[2168,7]]},"74":{"position":[[2572,8]]},"76":{"position":[[1960,7]]},"78":{"position":[[90,8]]},"98":{"position":[[792,7]]},"110":{"position":[[3565,7]]},"128":{"position":[[386,7]]},"134":{"position":[[709,8]]},"176":{"position":[[548,9],[959,9]]},"182":{"position":[[958,8]]},"208":{"position":[[291,7]]},"212":{"position":[[601,8]]},"218":{"position":[[436,10]]},"222":{"position":[[76,7],[636,10]]},"224":{"position":[[88,10]]},"232":{"position":[[181,7]]},"234":{"position":[[344,7]]},"236":{"position":[[307,7]]},"240":{"position":[[141,7]]},"250":{"position":[[356,7],[1866,7]]},"264":{"position":[[940,7]]},"270":{"position":[[1922,7]]},"299":{"position":[[106,8]]},"311":{"position":[[173,8]]},"345":{"position":[[516,7]]},"351":{"position":[[264,9]]},"357":{"position":[[943,7]]},"411":{"position":[[423,7]]},"427":{"position":[[211,9]]},"429":{"position":[[492,9]]},"437":{"position":[[116,7],[813,7],[947,9],[1022,10],[1744,10]]},"471":{"position":[[490,9]]},"473":{"position":[[219,7],[2745,7],[2774,10]]},"477":{"position":[[5687,7]]},"479":{"position":[[6837,7]]},"511":{"position":[[679,8]]},"557":{"position":[[143,7]]},"624":{"position":[[145,11]]},"628":{"position":[[99,7]]},"630":{"position":[[270,7]]},"650":{"position":[[99,7]]},"672":{"position":[[1939,8]]},"682":{"position":[[677,7]]},"714":{"position":[[78,7]]},"798":{"position":[[1113,8]]},"830":{"position":[[472,7]]},"836":{"position":[[128,7],[764,7]]},"838":{"position":[[143,7]]},"902":{"position":[[298,7]]},"912":{"position":[[92,9],[162,9],[301,7],[575,9],[823,10]]},"949":{"position":[[58,7],[210,7]]},"969":{"position":[[703,7]]},"973":{"position":[[2522,7],[3325,7]]},"1017":{"position":[[1644,7]]},"1019":{"position":[[1083,7]]},"1021":{"position":[[1053,7]]},"1023":{"position":[[2377,7]]},"1025":{"position":[[2409,7]]},"1069":{"position":[[541,7]]},"1077":{"position":[[367,7]]},"1094":{"position":[[571,7]]},"1130":{"position":[[205,7]]},"1183":{"position":[[128,7]]},"1235":{"position":[[423,7]]},"1253":{"position":[[211,9]]},"1255":{"position":[[492,9]]},"1263":{"position":[[116,7],[817,7],[951,9],[1026,10],[1748,10]]},"1277":{"position":[[516,7]]},"1283":{"position":[[264,9]]},"1289":{"position":[[943,7]]},"1403":{"position":[[666,7]]},"1405":{"position":[[3132,10],[3257,9]]},"1439":{"position":[[681,8]]},"1477":{"position":[[829,7]]},"1505":{"position":[[3667,7],[10549,7]]},"1531":{"position":[[143,7]]},"1569":{"position":[[424,7]]},"1571":{"position":[[11,9],[139,10],[859,9],[1182,9],[1333,10],[1759,10]]},"1603":{"position":[[2474,8]]},"1619":{"position":[[753,7]]},"1623":{"position":[[145,11]]},"1627":{"position":[[99,7]]},"1629":{"position":[[270,7]]},"1645":{"position":[[99,7]]},"1670":{"position":[[409,11]]},"1678":{"position":[[337,7]]},"1680":{"position":[[224,7]]},"1813":{"position":[[1113,8]]},"1843":{"position":[[950,10]]},"1847":{"position":[[1486,7]]},"1853":{"position":[[128,7],[764,7]]},"1855":{"position":[[143,7]]},"1919":{"position":[[58,7],[210,7]]},"1949":{"position":[[75,7]]},"1979":{"position":[[759,7]]},"1983":{"position":[[2170,7],[3483,8]]},"2015":{"position":[[298,7]]},"2021":{"position":[[201,10]]},"2027":{"position":[[92,9],[162,9],[301,7],[575,9],[823,10]]},"2080":{"position":[[128,7]]},"2132":{"position":[[541,7]]},"2150":{"position":[[951,10],[1076,9]]},"2189":{"position":[[899,7]]},"2197":{"position":[[367,7]]},"2281":{"position":[[189,7]]},"2309":{"position":[[871,7]]},"2354":{"position":[[1761,7]]},"2356":{"position":[[1083,7]]},"2358":{"position":[[1053,7]]},"2360":{"position":[[2377,7]]},"2362":{"position":[[2409,7]]},"2364":{"position":[[2484,7]]},"2400":{"position":[[980,7],[1234,7]]},"2406":{"position":[[263,9]]},"2412":{"position":[[958,7]]},"2484":{"position":[[423,7]]},"2502":{"position":[[211,9]]},"2504":{"position":[[492,9]]},"2512":{"position":[[1015,9],[1990,10]]},"2608":{"position":[[689,8]]},"2632":{"position":[[689,7]]},"2638":{"position":[[401,7],[649,7]]},"2664":{"position":[[424,7]]},"2666":{"position":[[11,9],[139,10],[859,9],[1182,9],[1333,10],[1759,10]]},"2686":{"position":[[3700,7],[10678,7]]},"2708":{"position":[[1956,9]]},"2788":{"position":[[2474,8]]},"2806":{"position":[[46,7],[289,11]]},"2810":{"position":[[145,11]]},"2814":{"position":[[99,7]]},"2816":{"position":[[270,7]]},"2834":{"position":[[409,11]]},"2842":{"position":[[337,7]]},"2844":{"position":[[224,7]]},"2862":{"position":[[282,7]]},"2889":{"position":[[543,10],[634,11],[821,10],[1011,10],[1090,11]]},"2897":{"position":[[99,7]]},"2994":{"position":[[75,7]]},"3042":{"position":[[147,7],[1066,8],[1117,7]]},"3072":{"position":[[1238,10]]},"3076":{"position":[[1450,7]]},"3082":{"position":[[128,7],[764,7]]},"3084":{"position":[[143,7]]},"3138":{"position":[[759,7]]},"3142":{"position":[[2170,7],[3483,8]]},"3146":{"position":[[58,7],[210,7]]},"3204":{"position":[[298,7]]},"3210":{"position":[[174,10]]},"3216":{"position":[[92,9],[162,9],[301,7],[575,9],[823,10]]},"3265":{"position":[[128,7]]},"3324":{"position":[[4496,10]]},"3335":{"position":[[541,7]]},"3365":{"position":[[204,8],[1727,7]]},"3379":{"position":[[951,10],[1076,9]]},"3415":{"position":[[1761,7]]},"3417":{"position":[[1083,7]]},"3419":{"position":[[1100,7]]},"3421":{"position":[[602,10],[2180,7]]},"3423":{"position":[[2409,7]]},"3425":{"position":[[2457,7]]},"3427":{"position":[[444,9]]},"3429":{"position":[[978,9]]},"3472":{"position":[[367,7]]},"3492":{"position":[[899,7]]},"3522":{"position":[[931,7]]},"3526":{"position":[[60,9]]},"3563":{"position":[[168,7]]},"3643":{"position":[[689,7]]},"3649":{"position":[[401,7],[649,7]]}}}],["processor",{"_index":3248,"t":{"475":{"position":[[4525,9]]}}}],["produc",{"_index":3218,"t":{"475":{"position":[[1677,8]]},"479":{"position":[[2072,7]]},"2862":{"position":[[344,8]]}}}],["product",{"_index":393,"t":{"10":{"position":[[91,10]]},"78":{"position":[[2307,10]]},"94":{"position":[[2384,10]]},"116":{"position":[[287,10]]},"144":{"position":[[1873,10]]},"152":{"position":[[294,10]]},"178":{"position":[[104,10]]},"180":{"position":[[217,10]]},"202":{"position":[[537,10]]},"206":{"position":[[117,10]]},"260":{"position":[[1435,8]]},"280":{"position":[[2259,11],[3415,10]]},"329":{"position":[[459,11]]},"331":{"position":[[1705,10]]},"335":{"position":[[238,7]]},"429":{"position":[[148,10]]},"499":{"position":[[21,10],[70,10]]},"501":{"position":[[29,10]]},"511":{"position":[[563,11]]},"525":{"position":[[322,10]]},"555":{"position":[[425,10]]},"600":{"position":[[63,10]]},"664":{"position":[[568,11]]},"668":{"position":[[258,10]]},"680":{"position":[[41,10]]},"876":{"position":[[215,10]]},"886":{"position":[[406,10]]},"918":{"position":[[759,10]]},"935":{"position":[[291,11]]},"1191":{"position":[[1472,10]]},"1203":{"position":[[441,10]]},"1255":{"position":[[148,10]]},"1355":{"position":[[328,10]]},"1359":{"position":[[438,10]]},"1371":{"position":[[777,10]]},"1393":{"position":[[21,10],[70,10]]},"1395":{"position":[[50,10]]},"1439":{"position":[[565,11]]},"1529":{"position":[[425,10]]},"1617":{"position":[[41,10]]},"1658":{"position":[[568,11]]},"1662":{"position":[[258,10]]},"1989":{"position":[[215,10]]},"1999":{"position":[[406,10]]},"2039":{"position":[[974,10]]},"2056":{"position":[[399,10]]},"2090":{"position":[[1472,10]]},"2106":{"position":[[441,10]]},"2392":{"position":[[262,10],[2725,10]]},"2504":{"position":[[148,10]]},"2526":{"position":[[327,10]]},"2530":{"position":[[438,10]]},"2542":{"position":[[777,10]]},"2564":{"position":[[21,11],[71,10]]},"2566":{"position":[[54,10]]},"2608":{"position":[[569,11]]},"2708":{"position":[[408,11]]},"2764":{"position":[[424,10]]},"2802":{"position":[[41,10]]},"2910":{"position":[[568,11]]},"2914":{"position":[[287,10]]},"3178":{"position":[[215,10]]},"3188":{"position":[[406,10]]},"3230":{"position":[[974,10]]},"3249":{"position":[[399,10]]},"3275":{"position":[[1472,10]]},"3289":{"position":[[441,10]]},"3329":{"position":[[191,11]]},"3498":{"position":[[690,10]]},"3612":{"position":[[289,10],[2924,10]]},"3629":{"position":[[1706,10]]}}}],["profil",{"_index":501,"t":{"12":{"position":[[810,8],[992,9]]},"128":{"position":[[1813,8]]},"483":{"position":[[1604,8]]},"3363":{"position":[[1132,8]]}}}],["program",{"_index":243,"t":{"4":{"position":[[475,11]]},"34":{"position":[[599,7]]},"40":{"position":[[578,11]]},"122":{"position":[[668,11]]},"158":{"position":[[1340,11]]},"184":{"position":[[990,11]]},"242":{"position":[[1457,11]]},"437":{"position":[[1211,7]]},"475":{"position":[[1742,7]]},"477":{"position":[[756,7]]},"483":{"position":[[1033,11]]},"527":{"position":[[188,11]]},"864":{"position":[[156,7]]},"866":{"position":[[161,7]]},"868":{"position":[[364,7]]},"870":{"position":[[221,7]]},"971":{"position":[[1161,7]]},"1035":{"position":[[125,11]]},"1263":{"position":[[1215,7]]},"1483":{"position":[[67,11]]},"1487":{"position":[[314,7]]},"1489":{"position":[[1387,7]]},"1905":{"position":[[200,7]]},"1907":{"position":[[156,7]]},"1909":{"position":[[364,7]]},"1911":{"position":[[425,7]]},"1913":{"position":[[221,7]]},"1915":{"position":[[229,7]]},"1981":{"position":[[1161,7]]},"2315":{"position":[[67,11]]},"2319":{"position":[[314,7]]},"2321":{"position":[[1387,7]]},"2374":{"position":[[125,11]]},"2512":{"position":[[1245,7]]},"2622":{"position":[[1233,7]]},"3020":{"position":[[145,11]]},"3140":{"position":[[1161,7]]},"3162":{"position":[[200,7]]},"3164":{"position":[[156,7]]},"3166":{"position":[[364,7]]},"3168":{"position":[[425,7]]},"3170":{"position":[[221,7]]},"3172":{"position":[[229,7]]},"3367":{"position":[[1863,11]]},"3435":{"position":[[125,11]]},"3528":{"position":[[67,11]]},"3532":{"position":[[314,7]]},"3534":{"position":[[1387,7]]},"3633":{"position":[[1233,7]]}}}],["programm",{"_index":5120,"t":{"1967":{"position":[[1281,10]]},"3012":{"position":[[1281,10]]}}}],["programmat",{"_index":3894,"t":{"726":{"position":[[275,16]]},"1961":{"position":[[275,16]]},"3006":{"position":[[275,16]]}}}],["progress",{"_index":1528,"t":{"94":{"position":[[2261,9]]},"114":{"position":[[104,9]]},"2760":{"position":[[1670,8]]}}}],["prohibit",{"_index":5573,"t":{"3068":{"position":[[464,9]]}}}],["project",{"_index":375,"t":{"8":{"position":[[1474,8],[1688,8]]},"48":{"position":[[35,7]]},"52":{"position":[[288,7]]},"58":{"position":[[225,7],[1167,8]]},"82":{"position":[[76,7],[1082,7],[2198,8]]},"110":{"position":[[4574,8]]},"122":{"position":[[40,7]]},"138":{"position":[[643,8]]},"152":{"position":[[456,9]]},"158":{"position":[[49,7],[1305,7],[1486,8]]},"184":{"position":[[48,7],[187,7],[955,7]]},"188":{"position":[[29,8]]},"190":{"position":[[633,7]]},"192":{"position":[[2662,7]]},"242":{"position":[[316,7],[1422,7]]},"258":{"position":[[1136,9]]},"264":{"position":[[362,8]]},"280":{"position":[[1140,8]]},"311":{"position":[[354,7]]},"339":{"position":[[191,8]]},"429":{"position":[[1500,9]]},"475":{"position":[[132,8],[340,8]]},"479":{"position":[[869,8]]},"483":{"position":[[319,8]]},"935":{"position":[[191,8],[227,9]]},"945":{"position":[[399,7]]},"1203":{"position":[[676,7]]},"1255":{"position":[[1500,9]]},"1271":{"position":[[45,8],[134,8]]},"1295":{"position":[[32,8]]},"2056":{"position":[[280,8],[335,9]]},"2066":{"position":[[399,7]]},"2106":{"position":[[676,7]]},"2430":{"position":[[32,8]]},"2504":{"position":[[1500,9]]},"2520":{"position":[[45,8],[134,8]]},"3249":{"position":[[280,8],[335,9]]},"3261":{"position":[[399,7]]},"3289":{"position":[[676,7]]}}}],["prolong",{"_index":3919,"t":{"776":{"position":[[406,10]]},"1791":{"position":[[406,10]]}}}],["prometheu",{"_index":891,"t":{"26":{"position":[[1516,10]]},"323":{"position":[[699,11]]},"389":{"position":[[240,10]]},"926":{"position":[[64,10],[289,10],[358,10],[1161,10]]},"930":{"position":[[829,10]]},"961":{"position":[[10,10],[52,10],[92,13],[199,10]]},"965":{"position":[[52,10],[128,10]]},"1347":{"position":[[240,10]]},"1971":{"position":[[10,10],[52,10],[92,13],[199,10]]},"1975":{"position":[[52,10],[128,10]]},"2021":{"position":[[338,10]]},"2047":{"position":[[64,10],[289,10],[358,10],[1161,10]]},"2051":{"position":[[1018,10]]},"2462":{"position":[[257,10]]},"3210":{"position":[[311,10]]},"3240":{"position":[[64,10],[289,10],[358,10],[1251,10]]},"3244":{"position":[[1105,10]]},"3299":{"position":[[10,10],[52,10],[92,13],[199,10]]},"3303":{"position":[[52,10],[128,10]]},"3318":{"position":[[10,10],[52,10],[92,13],[199,10]]},"3322":{"position":[[52,10],[128,10]]}}}],["prometheus/graphit",{"_index":3022,"t":{"389":{"position":[[173,19]]},"1347":{"position":[[173,19]]},"2462":{"position":[[182,19]]}}}],["prometheus_handler_prefix",{"_index":4082,"t":{"930":{"position":[[769,25]]},"2051":{"position":[[958,25]]},"3244":{"position":[[1045,25]]}}}],["promis",{"_index":2096,"t":{"178":{"position":[[3,9]]},"196":{"position":[[3,8],[987,8]]},"260":{"position":[[1632,8]]},"477":{"position":[[2611,9]]},"1201":{"position":[[179,8]]},"2102":{"position":[[179,8]]}}}],["promise((resolv",{"_index":2451,"t":{"216":{"position":[[2594,17]]},"2628":{"position":[[849,17]]},"2644":{"position":[[831,17]]}}}],["prompt",{"_index":3094,"t":{"465":{"position":[[1096,8]]}}}],["prop",{"_index":3100,"t":{"469":{"position":[[126,5]]}}}],["propag",{"_index":3577,"t":{"533":{"position":[[192,9]]},"979":{"position":[[771,11]]},"1025":{"position":[[656,9]]},"2072":{"position":[[771,11]]},"2362":{"position":[[656,9]]},"3309":{"position":[[771,11]]},"3423":{"position":[[656,9]]}}}],["proper",{"_index":366,"t":{"8":{"position":[[1314,6]]},"82":{"position":[[1525,6]]},"84":{"position":[[84,6]]},"124":{"position":[[2944,6]]},"144":{"position":[[1617,6],[2049,6]]},"232":{"position":[[1469,6]]},"315":{"position":[[1212,6]]},"411":{"position":[[1235,6]]},"437":{"position":[[2179,6]]},"918":{"position":[[792,6]]},"1235":{"position":[[1235,6]]},"1247":{"position":[[301,6]]},"1263":{"position":[[2183,6]]},"1365":{"position":[[390,6]]},"1483":{"position":[[284,6]]},"1505":{"position":[[6679,6]]},"2039":{"position":[[1007,6]]},"2315":{"position":[[284,6]]},"2392":{"position":[[2440,6]]},"2484":{"position":[[1235,6]]},"2496":{"position":[[301,6]]},"2512":{"position":[[2425,6]]},"2536":{"position":[[390,6]]},"2686":{"position":[[6760,6]]},"3230":{"position":[[1007,6]]},"3365":{"position":[[950,6]]},"3367":{"position":[[4957,6]]},"3528":{"position":[[284,6]]},"3612":{"position":[[2639,6]]}}}],["properli",{"_index":2033,"t":{"170":{"position":[[943,8]]},"192":{"position":[[2683,8]]},"198":{"position":[[1995,8]]},"321":{"position":[[114,8]]},"329":{"position":[[423,8]]},"455":{"position":[[898,8]]},"489":{"position":[[423,8]]},"549":{"position":[[435,8]]},"620":{"position":[[756,8]]},"638":{"position":[[819,8]]},"818":{"position":[[106,8]]},"1017":{"position":[[423,8]]},"1023":{"position":[[767,8]]},"1039":{"position":[[638,8]]},"1090":{"position":[[422,8]]},"1098":{"position":[[277,8]]},"1193":{"position":[[1123,8]]},"1195":{"position":[[74,8]]},"1307":{"position":[[898,8]]},"1383":{"position":[[441,8]]},"1505":{"position":[[9005,9]]},"1565":{"position":[[435,8]]},"1639":{"position":[[823,8]]},"2092":{"position":[[1139,8]]},"2096":{"position":[[74,8]]},"2185":{"position":[[428,8]]},"2234":{"position":[[277,8]]},"2313":{"position":[[514,8]]},"2354":{"position":[[423,8]]},"2360":{"position":[[767,8]]},"2378":{"position":[[638,8]]},"2512":{"position":[[1146,8]]},"2554":{"position":[[456,8]]},"2594":{"position":[[932,8]]},"2686":{"position":[[9125,9]]},"2734":{"position":[[435,8]]},"2826":{"position":[[787,8]]},"3277":{"position":[[1139,8]]},"3281":{"position":[[74,8]]},"3367":{"position":[[4169,8]]},"3415":{"position":[[423,8]]},"3439":{"position":[[638,8]]},"3462":{"position":[[463,8]]},"3584":{"position":[[277,8]]}}}],["properti",{"_index":728,"t":{"16":{"position":[[6959,10]]},"38":{"position":[[1675,10]]},"260":{"position":[[493,10]]},"347":{"position":[[1216,10]]},"842":{"position":[[250,10]]},"969":{"position":[[8,10],[863,10]]},"1279":{"position":[[1221,10]]},"1455":{"position":[[971,11]]},"1676":{"position":[[109,10]]},"1855":{"position":[[1355,10]]},"1859":{"position":[[370,10]]},"1979":{"position":[[8,10],[623,10],[919,10]]},"2287":{"position":[[974,11]]},"2402":{"position":[[1287,10]]},"2840":{"position":[[109,10]]},"3084":{"position":[[1319,10]]},"3090":{"position":[[370,10]]},"3138":{"position":[[8,10],[623,10],[919,10]]}}}],["proposit",{"_index":2658,"t":{"258":{"position":[[2085,11]]}}}],["proprietari",{"_index":1495,"t":{"90":{"position":[[347,11]]}}}],["protect",{"_index":1133,"t":{"52":{"position":[[933,7]]},"130":{"position":[[349,12],[392,10],[722,9]]},"172":{"position":[[487,9]]},"317":{"position":[[1204,9]]},"553":{"position":[[386,7]]},"598":{"position":[[161,9]]},"664":{"position":[[525,7]]},"668":{"position":[[276,9]]},"776":{"position":[[787,7]]},"790":{"position":[[414,7],[569,9]]},"792":{"position":[[739,7],[888,9]]},"820":{"position":[[708,9]]},"846":{"position":[[0,9],[414,9]]},"906":{"position":[[247,7]]},"926":{"position":[[530,7]]},"937":{"position":[[296,9]]},"939":{"position":[[200,9]]},"1371":{"position":[[725,11]]},"1403":{"position":[[1537,10]]},"1423":{"position":[[1738,10]]},"1455":{"position":[[9,9],[675,7]]},"1491":{"position":[[70,7]]},"1527":{"position":[[317,7]]},"1658":{"position":[[525,7]]},"1662":{"position":[[276,9]]},"1672":{"position":[[60,7],[1158,7]]},"1791":{"position":[[787,7]]},"1929":{"position":[[43,10],[181,10],[256,10],[468,7]]},"2019":{"position":[[247,7]]},"2047":{"position":[[530,7]]},"2058":{"position":[[296,9]]},"2060":{"position":[[200,9]]},"2088":{"position":[[42,9]]},"2160":{"position":[[1738,10]]},"2287":{"position":[[12,9],[678,7]]},"2323":{"position":[[70,7]]},"2542":{"position":[[725,11]]},"2760":{"position":[[318,7]]},"2836":{"position":[[60,7],[1158,7]]},"2883":{"position":[[1556,7]]},"2910":{"position":[[525,7]]},"2914":{"position":[[310,9],[378,11]]},"3020":{"position":[[847,7]]},"3156":{"position":[[43,10],[181,10],[256,10],[468,7]]},"3208":{"position":[[247,7]]},"3240":{"position":[[620,7]]},"3253":{"position":[[296,9]]},"3255":{"position":[[200,9]]},"3273":{"position":[[42,9]]},"3389":{"position":[[1738,10]]},"3498":{"position":[[12,9],[635,7],[879,10]]},"3536":{"position":[[70,7]]}}}],["proto",{"_index":2315,"t":{"198":{"position":[[1047,5],[1472,5]]},"250":{"position":[[1012,5],[1349,5]]},"2708":{"position":[[1323,5]]}}}],["protobuf",{"_index":88,"t":{"2":{"position":[[1144,8]]},"12":{"position":[[1341,8],[1400,8]]},"14":{"position":[[1537,9]]},"26":{"position":[[1252,8]]},"40":{"position":[[541,8]]},"50":{"position":[[1338,8]]},"60":{"position":[[1076,8],[1130,8]]},"66":{"position":[[2637,8],[2823,8]]},"82":{"position":[[357,8]]},"124":{"position":[[1374,8],[2083,8]]},"144":{"position":[[1540,8]]},"313":{"position":[[655,8]]},"363":{"position":[[296,8]]},"367":{"position":[[36,8]]},"413":{"position":[[367,8],[445,8],[573,8]]},"493":{"position":[[1096,8]]},"505":{"position":[[172,8]]},"569":{"position":[[32,8]]},"573":{"position":[[32,8]]},"577":{"position":[[32,8]]},"604":{"position":[[61,8],[404,8]]},"788":{"position":[[24,8]]},"1017":{"position":[[2629,8]]},"1019":{"position":[[922,8]]},"1021":{"position":[[892,8]]},"1023":{"position":[[2216,8]]},"1025":{"position":[[2248,8]]},"1031":{"position":[[412,8]]},"1035":{"position":[[189,8]]},"1039":{"position":[[156,8]]},"1081":{"position":[[115,8]]},"1130":{"position":[[114,8]]},"1162":{"position":[[264,8],[326,8],[398,8],[509,8],[1025,8]]},"1237":{"position":[[367,8],[445,8],[573,8]]},"1321":{"position":[[300,8]]},"1325":{"position":[[36,8]]},"1399":{"position":[[172,8]]},"1403":{"position":[[359,8],[2270,8]]},"1405":{"position":[[31,8],[1259,8],[1481,8],[1838,8],[2285,8]]},"1407":{"position":[[474,8]]},"1421":{"position":[[224,8]]},"1485":{"position":[[424,8]]},"1537":{"position":[[32,8]]},"1541":{"position":[[32,8]]},"1545":{"position":[[32,8]]},"1599":{"position":[[703,9]]},"1803":{"position":[[24,8]]},"2148":{"position":[[293,8],[357,8]]},"2154":{"position":[[949,8],[1171,8],[1534,8],[2083,8]]},"2168":{"position":[[82,9],[153,8],[180,8],[278,8],[704,8],[972,8],[1078,8],[1222,8],[1432,9],[1647,8]]},"2189":{"position":[[511,8]]},"2201":{"position":[[115,8]]},"2281":{"position":[[98,8]]},"2317":{"position":[[424,8]]},"2342":{"position":[[264,8],[326,8],[398,8],[509,8],[1025,8]]},"2354":{"position":[[2746,8]]},"2356":{"position":[[922,8]]},"2358":{"position":[[892,8]]},"2360":{"position":[[2216,8]]},"2362":{"position":[[2248,8]]},"2364":{"position":[[2323,8]]},"2370":{"position":[[412,8]]},"2374":{"position":[[189,8]]},"2378":{"position":[[156,8]]},"2392":{"position":[[2371,8]]},"2436":{"position":[[309,8]]},"2440":{"position":[[36,8]]},"2486":{"position":[[367,8],[445,8],[573,8]]},"2570":{"position":[[181,8]]},"2742":{"position":[[32,8]]},"2746":{"position":[[32,8]]},"2750":{"position":[[32,8]]},"2782":{"position":[[703,9]]},"3032":{"position":[[31,8]]},"3367":{"position":[[1918,8]]},"3377":{"position":[[293,8],[357,8]]},"3383":{"position":[[949,8],[1171,8],[1534,8],[2083,8]]},"3397":{"position":[[82,9],[153,8],[180,8],[278,8],[704,8],[972,8],[1078,8],[1222,8],[1432,9],[1647,8]]},"3415":{"position":[[2746,8]]},"3417":{"position":[[922,8]]},"3419":{"position":[[939,8]]},"3421":{"position":[[2019,8]]},"3423":{"position":[[2248,8]]},"3425":{"position":[[2296,8]]},"3431":{"position":[[412,8]]},"3435":{"position":[[189,8]]},"3439":{"position":[[156,8]]},"3476":{"position":[[115,8]]},"3492":{"position":[[511,8]]},"3530":{"position":[[424,8]]},"3563":{"position":[[77,8]]},"3612":{"position":[[2570,8]]},"3627":{"position":[[264,8],[326,8],[398,8],[509,8],[1025,8]]}}}],["protobuf.j",{"_index":5189,"t":{"2168":{"position":[[656,11]]},"3397":{"position":[[656,11]]}}}],["protoc",{"_index":4663,"t":{"1489":{"position":[[84,7],[92,6],[110,6],[510,6],[535,6]]},"2321":{"position":[[84,7],[92,6],[110,6],[510,6],[535,6]]},"3534":{"position":[[84,7],[92,6],[110,6],[510,6],[535,6]]}}}],["protocol",{"_index":89,"t":{"2":{"position":[[1153,9]]},"12":{"position":[[1044,8],[1167,9]]},"16":{"position":[[31,9],[6293,8],[6626,8]]},"24":{"position":[[476,8]]},"26":{"position":[[1222,8],[1296,8]]},"28":{"position":[[102,8]]},"32":{"position":[[529,8]]},"46":{"position":[[126,9]]},"50":{"position":[[69,8],[1347,8]]},"60":{"position":[[833,8],[924,8],[1047,8],[1815,8],[2154,8]]},"64":{"position":[[1154,9]]},"66":{"position":[[2619,8],[2777,8],[2845,8]]},"74":{"position":[[877,8],[2511,8]]},"82":{"position":[[313,9],[327,8],[592,9],[630,8],[669,9],[1269,8]]},"90":{"position":[[745,8]]},"94":{"position":[[1528,8],[2113,8],[2319,8]]},"106":{"position":[[1607,8]]},"118":{"position":[[150,8],[1140,9]]},"120":{"position":[[253,9],[395,8]]},"122":{"position":[[318,8],[369,8],[809,8],[2097,8],[2494,8]]},"124":{"position":[[853,8],[1383,8]]},"126":{"position":[[78,8],[228,8],[302,8],[541,8],[881,8]]},"134":{"position":[[111,9],[570,8]]},"144":{"position":[[1490,8]]},"148":{"position":[[131,8]]},"176":{"position":[[1799,8]]},"242":{"position":[[1107,8]]},"246":{"position":[[1853,9]]},"283":{"position":[[750,8]]},"311":{"position":[[46,8],[1147,9]]},"313":{"position":[[110,8],[247,8],[383,9],[545,8]]},"319":{"position":[[248,8],[373,8],[517,10]]},"321":{"position":[[867,8],[967,8]]},"331":{"position":[[256,8],[424,8],[1582,8]]},"345":{"position":[[129,8]]},"367":{"position":[[45,8],[105,8],[188,9]]},"413":{"position":[[68,8],[376,8],[454,8],[767,8]]},"439":{"position":[[55,9]]},"473":{"position":[[3523,8]]},"493":{"position":[[1105,9],[1195,10]]},"553":{"position":[[141,8]]},"587":{"position":[[7,8]]},"698":{"position":[[124,8]]},"734":{"position":[[108,8]]},"788":{"position":[[33,8]]},"973":{"position":[[682,8],[832,8],[1863,9],[3084,8]]},"1017":{"position":[[1931,11],[2568,8],[2587,8],[2668,8]]},"1019":{"position":[[532,11],[865,8],[884,8],[961,8]]},"1021":{"position":[[444,11],[831,8],[850,8],[931,8]]},"1023":{"position":[[1803,11],[2155,8],[2174,8],[2255,8]]},"1025":{"position":[[1818,11],[2187,8],[2206,8],[2287,8]]},"1027":{"position":[[261,9]]},"1029":{"position":[[370,9],[415,8],[460,8]]},"1037":{"position":[[207,8]]},"1039":{"position":[[165,8]]},"1063":{"position":[[210,8],[699,8]]},"1081":{"position":[[124,8]]},"1090":{"position":[[255,9]]},"1094":{"position":[[119,8],[187,8],[909,10]]},"1162":{"position":[[48,8],[280,8],[518,8],[1034,8],[1106,8]]},"1237":{"position":[[68,8],[376,8],[454,8],[767,8]]},"1265":{"position":[[55,9]]},"1277":{"position":[[129,8]]},"1293":{"position":[[803,8]]},"1325":{"position":[[45,8],[105,8],[184,9]]},"1359":{"position":[[25,8],[365,8],[1214,8],[1282,8],[1404,8],[1839,8]]},"1361":{"position":[[7,8],[301,8]]},"1403":{"position":[[280,8],[368,8],[1697,8],[2049,8],[2238,8],[2410,8]]},"1405":{"position":[[11,8],[2609,8]]},"1423":{"position":[[936,8]]},"1433":{"position":[[7,8]]},"1485":{"position":[[836,8]]},"1487":{"position":[[911,8]]},"1527":{"position":[[136,8]]},"1597":{"position":[[1450,11],[1570,11]]},"1599":{"position":[[648,8],[685,8]]},"1789":{"position":[[402,8]]},"1803":{"position":[[33,8]]},"1843":{"position":[[961,8]]},"1933":{"position":[[124,8]]},"1967":{"position":[[283,8],[1049,9]]},"1983":{"position":[[679,8],[825,8],[2035,9]]},"2086":{"position":[[704,8],[819,8]]},"2104":{"position":[[167,9]]},"2148":{"position":[[268,8],[424,8],[527,8],[550,8],[570,8]]},"2160":{"position":[[936,8]]},"2162":{"position":[[7,8]]},"2166":{"position":[[264,8],[360,8]]},"2168":{"position":[[124,8],[420,8],[713,8],[794,8],[1260,8]]},"2185":{"position":[[261,9]]},"2189":{"position":[[119,8],[187,8],[589,8],[1082,8],[1261,8]]},"2191":{"position":[[248,8]]},"2201":{"position":[[124,8]]},"2317":{"position":[[836,8]]},"2319":{"position":[[911,8]]},"2342":{"position":[[48,8],[280,8],[518,8],[1034,8],[1106,8]]},"2354":{"position":[[2048,11],[2685,8],[2704,8],[2785,8]]},"2356":{"position":[[532,11],[865,8],[884,8],[961,8]]},"2358":{"position":[[444,11],[831,8],[850,8],[931,8]]},"2360":{"position":[[1803,11],[2155,8],[2174,8],[2255,8]]},"2362":{"position":[[1818,11],[2187,8],[2206,8],[2287,8]]},"2364":{"position":[[1907,11],[2266,8],[2285,8],[2362,8]]},"2368":{"position":[[520,9],[565,8],[610,8]]},"2376":{"position":[[207,8]]},"2378":{"position":[[165,8]]},"2392":{"position":[[2321,8]]},"2420":{"position":[[484,9]]},"2424":{"position":[[204,9]]},"2428":{"position":[[854,8]]},"2440":{"position":[[45,9],[106,8],[185,9]]},"2486":{"position":[[68,8],[376,8],[454,8],[767,8]]},"2514":{"position":[[55,9]]},"2530":{"position":[[25,8],[365,8],[1214,8],[1282,8],[1404,8],[1839,8]]},"2532":{"position":[[7,8],[301,8]]},"2576":{"position":[[132,10]]},"2638":{"position":[[533,8]]},"2660":{"position":[[93,8]]},"2760":{"position":[[136,8]]},"2780":{"position":[[1414,11],[1534,11]]},"2782":{"position":[[648,8],[685,8]]},"2978":{"position":[[124,8]]},"3012":{"position":[[283,8],[1049,9]]},"3018":{"position":[[385,8]]},"3032":{"position":[[40,8]]},"3072":{"position":[[1249,8]]},"3142":{"position":[[679,8],[825,8],[2035,9]]},"3271":{"position":[[670,8],[785,8]]},"3287":{"position":[[182,8]]},"3324":{"position":[[4887,9],[5118,9],[5833,9],[6053,9]]},"3377":{"position":[[268,8],[424,8],[527,8],[550,8],[570,8]]},"3389":{"position":[[936,8]]},"3391":{"position":[[7,8]]},"3395":{"position":[[425,8],[521,8],[584,8]]},"3397":{"position":[[124,8],[420,8],[713,8],[794,8],[1260,8]]},"3415":{"position":[[2048,11],[2685,8],[2704,8],[2785,8]]},"3417":{"position":[[532,11],[865,8],[884,8],[961,8]]},"3419":{"position":[[491,11],[878,8],[897,8],[978,8]]},"3421":{"position":[[1606,11],[1958,8],[1977,8],[2058,8]]},"3423":{"position":[[1818,11],[2187,8],[2206,8],[2287,8]]},"3425":{"position":[[1880,11],[2239,8],[2258,8],[2335,8]]},"3429":{"position":[[520,9],[565,8],[610,8]]},"3437":{"position":[[207,8]]},"3439":{"position":[[165,8]]},"3462":{"position":[[296,9]]},"3464":{"position":[[899,8]]},"3466":{"position":[[248,8]]},"3476":{"position":[[124,8]]},"3492":{"position":[[119,8],[187,8],[589,8],[1082,8],[1261,8]]},"3530":{"position":[[836,8]]},"3532":{"position":[[911,8]]},"3612":{"position":[[2520,8]]},"3627":{"position":[[48,8],[280,8],[518,8],[1034,8],[1106,8]]},"3629":{"position":[[253,8],[425,8],[1583,8]]},"3649":{"position":[[533,8]]},"3671":{"position":[[93,8]]}}}],["protocol/network",{"_index":2849,"t":{"295":{"position":[[1917,16]]}}}],["prototyp",{"_index":1807,"t":{"122":{"position":[[1607,10]]},"152":{"position":[[533,10]]},"477":{"position":[[2570,10],[2693,11]]},"824":{"position":[[673,11]]},"935":{"position":[[262,12]]},"1865":{"position":[[949,11]]},"2056":{"position":[[370,12]]},"3096":{"position":[[949,11]]},"3249":{"position":[[370,12]]}}}],["prove",{"_index":1220,"t":{"60":{"position":[[1850,5]]},"78":{"position":[[3564,6]]},"260":{"position":[[530,6]]},"475":{"position":[[2979,6]]},"686":{"position":[[180,6]]},"1729":{"position":[[180,6]]},"2918":{"position":[[180,6]]}}}],["proven",{"_index":1821,"t":{"124":{"position":[[572,7]]}}}],["provid",{"_index":236,"t":{"4":{"position":[[350,7]]},"8":{"position":[[671,8]]},"14":{"position":[[543,8]]},"16":{"position":[[4573,7],[4994,8],[6085,8]]},"24":{"position":[[621,8]]},"28":{"position":[[481,7],[684,7]]},"32":{"position":[[190,8]]},"38":{"position":[[306,7],[823,8],[1655,7]]},"40":{"position":[[335,7]]},"50":{"position":[[287,8]]},"52":{"position":[[140,8],[807,7]]},"58":{"position":[[629,7],[940,8]]},"60":{"position":[[975,8],[2248,8]]},"62":{"position":[[1221,7],[1284,8]]},"68":{"position":[[206,9]]},"72":{"position":[[255,7]]},"74":{"position":[[61,7],[459,8],[886,8],[2118,7]]},"78":{"position":[[1243,8]]},"90":{"position":[[603,8]]},"94":{"position":[[437,7],[614,8],[1295,8]]},"108":{"position":[[325,7],[571,8]]},"122":{"position":[[382,8]]},"124":{"position":[[1867,7],[2259,7]]},"128":{"position":[[1788,9]]},"130":{"position":[[1064,7],[1325,8]]},"136":{"position":[[659,8],[1165,7]]},"138":{"position":[[391,7]]},"142":{"position":[[546,7]]},"144":{"position":[[2039,7]]},"148":{"position":[[213,8]]},"158":{"position":[[867,8]]},"168":{"position":[[125,9]]},"176":{"position":[[1826,9]]},"184":{"position":[[564,8]]},"192":{"position":[[3071,7]]},"208":{"position":[[206,10],[396,9]]},"216":{"position":[[1300,9]]},"218":{"position":[[11,8]]},"222":{"position":[[472,8],[601,8]]},"234":{"position":[[69,7]]},"242":{"position":[[1178,8]]},"258":{"position":[[1573,7],[1776,7],[2060,9]]},"260":{"position":[[4,7],[1501,7]]},"262":{"position":[[1804,7],[1855,9]]},"264":{"position":[[5,9],[578,8],[849,7],[1058,10],[1186,9],[1534,7]]},"266":{"position":[[145,8]]},"268":{"position":[[195,8],[460,7],[590,9],[704,10]]},"270":{"position":[[30,7],[93,7],[203,7],[657,8],[944,10],[1146,9],[1244,8],[1603,7],[1680,8],[2019,8],[2893,8]]},"272":{"position":[[577,8],[1415,7]]},"274":{"position":[[102,9],[167,8]]},"276":{"position":[[24,8],[113,7]]},"278":{"position":[[323,9]]},"280":{"position":[[464,7]]},"287":{"position":[[106,7]]},"291":{"position":[[264,7],[1761,7],[2260,8],[2409,7],[2558,7]]},"293":{"position":[[990,7]]},"295":{"position":[[254,7],[413,7]]},"297":{"position":[[313,9]]},"299":{"position":[[220,8]]},"301":{"position":[[166,7],[994,9]]},"303":{"position":[[176,7]]},"315":{"position":[[1140,7]]},"327":{"position":[[195,8]]},"339":{"position":[[205,7]]},"347":{"position":[[1417,8]]},"357":{"position":[[871,8]]},"369":{"position":[[165,8]]},"379":{"position":[[262,8]]},"411":{"position":[[49,7],[92,9]]},"431":{"position":[[135,7]]},"435":{"position":[[96,8]]},"441":{"position":[[637,8],[733,8]]},"455":{"position":[[611,8],[1078,8]]},"465":{"position":[[1000,8],[1150,8]]},"467":{"position":[[647,7]]},"471":{"position":[[3591,7]]},"473":{"position":[[1373,8]]},"475":{"position":[[534,8],[1099,7],[1218,8],[3004,7],[6326,7]]},"477":{"position":[[2330,8]]},"479":{"position":[[1019,8],[5225,7]]},"493":{"position":[[282,7]]},"495":{"position":[[150,7]]},"525":{"position":[[959,8]]},"553":{"position":[[104,8]]},"563":{"position":[[30,8]]},"589":{"position":[[187,7],[340,7]]},"596":{"position":[[167,7]]},"598":{"position":[[199,7]]},"618":{"position":[[56,7]]},"632":{"position":[[508,8]]},"636":{"position":[[178,8]]},"640":{"position":[[28,8],[924,7]]},"652":{"position":[[404,8]]},"686":{"position":[[15,8]]},"726":{"position":[[464,7]]},"776":{"position":[[134,7],[180,8],[235,8],[572,8],[825,10]]},"786":{"position":[[92,8]]},"790":{"position":[[142,9]]},"792":{"position":[[326,7],[468,9]]},"796":{"position":[[253,8]]},"798":{"position":[[277,9],[769,7]]},"810":{"position":[[102,9],[226,9]]},"820":{"position":[[147,7],[318,7],[828,7]]},"828":{"position":[[254,8],[312,7]]},"858":{"position":[[192,8]]},"866":{"position":[[301,7]]},"937":{"position":[[108,7]]},"941":{"position":[[93,9]]},"969":{"position":[[295,8]]},"973":{"position":[[3468,9]]},"999":{"position":[[317,8]]},"1013":{"position":[[252,7]]},"1017":{"position":[[2799,8],[2914,8],[3019,8],[4246,9]]},"1023":{"position":[[441,9],[2659,8]]},"1057":{"position":[[402,9],[781,8]]},"1059":{"position":[[8,7],[207,8],[295,8]]},"1061":{"position":[[40,7],[255,8],[350,8]]},"1162":{"position":[[653,7]]},"1201":{"position":[[108,8],[591,7]]},"1203":{"position":[[578,9],[635,9],[842,8],[1027,10]]},"1205":{"position":[[538,10]]},"1235":{"position":[[49,7],[92,9]]},"1245":{"position":[[43,8]]},"1247":{"position":[[83,8],[272,8],[737,7]]},"1257":{"position":[[135,7]]},"1261":{"position":[[96,8]]},"1267":{"position":[[637,8],[733,8]]},"1269":{"position":[[85,8]]},"1279":{"position":[[1422,8]]},"1289":{"position":[[871,8]]},"1295":{"position":[[46,7]]},"1307":{"position":[[611,8],[1078,8]]},"1327":{"position":[[101,8]]},"1337":{"position":[[251,8]]},"1339":{"position":[[37,8]]},"1355":{"position":[[1026,8]]},"1371":{"position":[[562,7]]},"1387":{"position":[[283,7]]},"1389":{"position":[[159,7]]},"1403":{"position":[[809,7],[953,7],[999,7],[1042,7],[1082,7],[1128,7],[1167,7],[1203,7],[1243,7],[1280,7],[1323,7]]},"1465":{"position":[[508,7]]},"1473":{"position":[[289,7]]},"1477":{"position":[[691,7]]},"1495":{"position":[[663,8]]},"1505":{"position":[[3635,8],[5783,7],[7597,7]]},"1573":{"position":[[411,7]]},"1575":{"position":[[156,7],[384,7]]},"1589":{"position":[[141,7]]},"1591":{"position":[[28,8],[1206,7]]},"1599":{"position":[[514,8],[580,8]]},"1613":{"position":[[430,10],[1404,10]]},"1631":{"position":[[508,8]]},"1636":{"position":[[178,8]]},"1639":{"position":[[852,8]]},"1647":{"position":[[404,8]]},"1670":{"position":[[449,7]]},"1672":{"position":[[410,8]]},"1678":{"position":[[101,9],[283,8],[348,9],[558,8]]},"1680":{"position":[[29,8]]},"1682":{"position":[[751,8],[1488,7]]},"1684":{"position":[[46,8],[108,8],[161,10],[242,9],[423,8]]},"1686":{"position":[[110,9],[175,8]]},"1688":{"position":[[4,8],[46,8],[466,7]]},"1690":{"position":[[45,8]]},"1692":{"position":[[390,8]]},"1703":{"position":[[145,8],[173,8],[193,8]]},"1705":{"position":[[316,8]]},"1707":{"position":[[359,8]]},"1709":{"position":[[201,9],[251,9],[323,8],[1161,8],[1196,9]]},"1713":{"position":[[214,9],[293,8]]},"1719":{"position":[[71,8],[220,8],[1964,8]]},"1723":{"position":[[179,9],[202,9],[384,8],[526,8],[602,8],[667,8]]},"1729":{"position":[[15,8]]},"1753":{"position":[[317,8]]},"1771":{"position":[[194,7],[448,7],[489,7],[579,8]]},"1791":{"position":[[134,7],[180,8],[235,8],[572,8],[825,10]]},"1801":{"position":[[92,8]]},"1807":{"position":[[326,7]]},"1811":{"position":[[253,8]]},"1813":{"position":[[277,9],[769,7]]},"1825":{"position":[[102,9],[226,9]]},"1827":{"position":[[335,8]]},"1837":{"position":[[147,7],[318,7],[521,7]]},"1843":{"position":[[192,8]]},"1849":{"position":[[576,7]]},"1851":{"position":[[249,7]]},"1857":{"position":[[931,7]]},"1859":{"position":[[686,7]]},"1905":{"position":[[340,7]]},"1929":{"position":[[102,8]]},"1979":{"position":[[354,8]]},"1983":{"position":[[2630,8],[2768,8],[3606,8],[4612,9]]},"2033":{"position":[[135,8]]},"2058":{"position":[[108,7]]},"2062":{"position":[[93,9]]},"2102":{"position":[[108,8],[591,7]]},"2104":{"position":[[619,8]]},"2106":{"position":[[578,9],[635,9],[842,8],[1027,10]]},"2108":{"position":[[538,10]]},"2116":{"position":[[16,8]]},"2122":{"position":[[447,7]]},"2126":{"position":[[97,8]]},"2140":{"position":[[402,9],[781,8]]},"2142":{"position":[[8,7],[207,8],[295,8]]},"2144":{"position":[[8,7],[223,8],[318,8]]},"2297":{"position":[[474,7]]},"2305":{"position":[[289,7]]},"2309":{"position":[[738,7]]},"2342":{"position":[[653,7]]},"2350":{"position":[[252,7]]},"2354":{"position":[[987,7],[2916,8],[3031,8],[3136,8],[4363,9]]},"2360":{"position":[[441,9],[2659,8]]},"2400":{"position":[[2389,8],[2619,8]]},"2402":{"position":[[1554,8]]},"2412":{"position":[[885,8],[1416,8]]},"2420":{"position":[[52,7],[423,8]]},"2422":{"position":[[28,8],[402,8],[601,8]]},"2424":{"position":[[303,7],[653,9],[826,8],[1222,8],[1368,8]]},"2430":{"position":[[46,7]]},"2442":{"position":[[102,8]]},"2446":{"position":[[197,8]]},"2454":{"position":[[41,8]]},"2466":{"position":[[73,7]]},"2484":{"position":[[49,7],[92,9]]},"2494":{"position":[[43,8]]},"2496":{"position":[[83,8],[272,8],[737,7]]},"2506":{"position":[[135,7]]},"2510":{"position":[[96,8]]},"2516":{"position":[[637,8],[733,8]]},"2518":{"position":[[85,8]]},"2526":{"position":[[568,8],[1533,7]]},"2542":{"position":[[562,7]]},"2558":{"position":[[287,7]]},"2560":{"position":[[164,7]]},"2594":{"position":[[642,8],[1128,8]]},"2628":{"position":[[1420,9]]},"2634":{"position":[[15,8]]},"2638":{"position":[[542,8]]},"2644":{"position":[[1522,9]]},"2646":{"position":[[68,7],[1745,7]]},"2654":{"position":[[7,8]]},"2656":{"position":[[4,8]]},"2668":{"position":[[411,7]]},"2670":{"position":[[156,7],[384,7]]},"2684":{"position":[[141,7]]},"2686":{"position":[[3668,8],[5842,7],[7683,7]]},"2690":{"position":[[663,8]]},"2712":{"position":[[1035,7]]},"2718":{"position":[[191,8],[263,8]]},"2736":{"position":[[102,8]]},"2762":{"position":[[353,8]]},"2782":{"position":[[514,8],[580,8]]},"2784":{"position":[[28,8],[1206,7]]},"2798":{"position":[[430,10],[1404,10]]},"2818":{"position":[[508,8]]},"2823":{"position":[[178,8]]},"2826":{"position":[[816,8]]},"2834":{"position":[[449,7]]},"2836":{"position":[[410,8]]},"2842":{"position":[[101,9],[283,8],[348,9],[558,8]]},"2844":{"position":[[29,8]]},"2846":{"position":[[751,8],[1488,7]]},"2848":{"position":[[46,8],[108,8],[161,10],[242,9],[423,8]]},"2850":{"position":[[110,9],[175,8]]},"2852":{"position":[[4,8],[46,8],[466,7]]},"2854":{"position":[[45,8]]},"2856":{"position":[[390,8]]},"2862":{"position":[[606,9]]},"2867":{"position":[[145,8],[173,8],[193,8]]},"2873":{"position":[[727,9],[777,9],[1356,8],[1395,9]]},"2877":{"position":[[669,9]]},"2883":{"position":[[71,8],[220,8],[2243,8]]},"2889":{"position":[[143,9],[326,9],[440,9],[707,9],[956,9]]},"2899":{"position":[[404,8]]},"2918":{"position":[[15,8]]},"2930":{"position":[[194,7],[448,7],[489,7],[579,8]]},"2958":{"position":[[317,8]]},"3020":{"position":[[167,7],[615,8]]},"3030":{"position":[[20,8]]},"3036":{"position":[[55,9],[405,7]]},"3042":{"position":[[287,9],[1080,7],[1129,7],[1354,7]]},"3054":{"position":[[102,9],[226,9]]},"3056":{"position":[[301,8]]},"3066":{"position":[[358,8]]},"3068":{"position":[[596,9]]},"3072":{"position":[[200,8],[473,8]]},"3078":{"position":[[576,7]]},"3080":{"position":[[249,7]]},"3088":{"position":[[931,7]]},"3090":{"position":[[686,7]]},"3138":{"position":[[354,8]]},"3142":{"position":[[2630,8],[2768,8],[3606,8],[4612,9]]},"3156":{"position":[[102,8]]},"3162":{"position":[[340,7]]},"3224":{"position":[[108,8]]},"3253":{"position":[[108,7]]},"3257":{"position":[[93,9]]},"3287":{"position":[[691,8]]},"3289":{"position":[[578,9],[635,9],[842,8],[1027,10]]},"3291":{"position":[[538,10]]},"3324":{"position":[[1628,8]]},"3345":{"position":[[707,7]]},"3349":{"position":[[97,8]]},"3355":{"position":[[402,9],[781,8]]},"3357":{"position":[[8,7],[207,8],[295,8]]},"3359":{"position":[[8,7],[223,8],[318,8]]},"3365":{"position":[[182,9],[690,8],[1313,7]]},"3411":{"position":[[252,7]]},"3415":{"position":[[987,7],[2916,8],[3031,8],[3136,8],[4392,9]]},"3421":{"position":[[476,9],[2462,8]]},"3510":{"position":[[408,7]]},"3518":{"position":[[289,7]]},"3522":{"position":[[798,7]]},"3627":{"position":[[653,7]]},"3639":{"position":[[1440,9]]},"3645":{"position":[[15,8]]},"3649":{"position":[[542,8]]},"3655":{"position":[[1690,9]]},"3657":{"position":[[68,7],[1745,7]]},"3665":{"position":[[7,8]]},"3667":{"position":[[4,8]]}}}],["provider_token",{"_index":4959,"t":{"1705":{"position":[[267,15]]},"1707":{"position":[[325,15]]},"1709":{"position":[[280,15]]}}}],["providertoken",{"_index":4969,"t":{"1707":{"position":[[341,14]]},"1709":{"position":[[305,14]]},"1713":{"position":[[275,14]]}}}],["provinc",{"_index":1590,"t":{"98":{"position":[[1761,8]]}}}],["proxi",{"_index":536,"t":{"14":{"position":[[278,8],[366,5],[1705,5]]},"18":{"position":[[3334,7],[3457,5]]},"24":{"position":[[577,6]]},"26":{"position":[[1011,5]]},"32":{"position":[[1813,5],[1996,5]]},"40":{"position":[[152,5]]},"48":{"position":[[278,5]]},"60":{"position":[[1278,9]]},"120":{"position":[[211,6],[444,5]]},"124":{"position":[[160,8],[242,7],[2690,5]]},"134":{"position":[[1212,8]]},"170":{"position":[[491,5],[540,7],[576,5],[817,5]]},"172":{"position":[[78,5],[240,5],[534,7]]},"178":{"position":[[781,5],[953,5]]},"196":{"position":[[1392,5],[1677,5],[1712,8],[1775,5]]},"198":{"position":[[181,5],[2004,5]]},"200":{"position":[[174,5],[1047,5],[1111,5],[1460,5],[1524,5],[1826,5],[1892,5],[2184,5]]},"202":{"position":[[156,8],[769,6],[924,5],[1108,5],[1391,5],[1430,5],[1514,5]]},"206":{"position":[[227,5],[648,6]]},"242":{"position":[[162,5]]},"244":{"position":[[330,5]]},"250":{"position":[[276,5],[331,5],[678,5],[1593,5]]},"254":{"position":[[116,5]]},"280":{"position":[[2427,6],[2606,5]]},"283":{"position":[[619,5]]},"287":{"position":[[408,5],[493,6]]},"293":{"position":[[657,5],[693,5],[951,5],[1467,5],[1515,5]]},"297":{"position":[[91,5],[168,5]]},"303":{"position":[[100,5],[142,5]]},"329":{"position":[[1038,5]]},"345":{"position":[[993,7],[1095,8],[1311,5]]},"371":{"position":[[172,5]]},"419":{"position":[[66,5]]},"429":{"position":[[656,5],[1073,5]]},"431":{"position":[[486,5],[545,5]]},"435":{"position":[[0,5],[85,6]]},"437":{"position":[[1539,5]]},"439":{"position":[[124,6]]},"447":{"position":[[508,5]]},"459":{"position":[[224,5]]},"473":{"position":[[595,5],[641,7]]},"481":{"position":[[399,5]]},"493":{"position":[[936,8]]},"571":{"position":[[76,6]]},"602":{"position":[[294,5]]},"608":{"position":[[16,5],[81,6],[556,7],[591,6],[609,5],[921,6]]},"688":{"position":[[161,5]]},"698":{"position":[[300,5]]},"726":{"position":[[149,5]]},"794":{"position":[[200,5]]},"818":{"position":[[837,5]]},"820":{"position":[[787,5]]},"824":{"position":[[343,6]]},"828":{"position":[[293,7]]},"846":{"position":[[235,5]]},"848":{"position":[[62,6],[82,5]]},"850":{"position":[[58,6],[78,5]]},"852":{"position":[[63,5],[83,5],[173,5]]},"854":{"position":[[59,5],[79,5],[167,5]]},"884":{"position":[[330,5]]},"892":{"position":[[1146,5]]},"939":{"position":[[338,5]]},"949":{"position":[[675,5]]},"951":{"position":[[180,5]]},"955":{"position":[[0,7],[206,5]]},"977":{"position":[[61,5],[142,5],[323,5],[504,5]]},"1009":{"position":[[5,5]]},"1011":{"position":[[4,5]]},"1013":{"position":[[37,5],[68,7],[308,8],[742,5]]},"1015":{"position":[[83,5],[242,8],[302,5],[349,8],[412,7]]},"1017":{"position":[[207,7],[385,7],[1067,7],[2193,5],[2362,5],[3133,5],[3431,5],[5645,5]]},"1021":{"position":[[193,7],[1280,5]]},"1023":{"position":[[223,7],[338,5],[353,5],[599,5],[698,5],[796,5],[834,5],[947,5],[970,5],[1008,5],[1176,5],[1234,5],[2823,5]]},"1025":{"position":[[201,7],[705,6],[720,5],[888,5],[996,5],[1084,5],[2577,5]]},"1031":{"position":[[20,5],[264,5],[303,5],[371,5],[447,5],[615,8],[713,6],[829,6],[948,6],[1060,5],[1277,5]]},"1033":{"position":[[46,5]]},"1035":{"position":[[183,5]]},"1037":{"position":[[155,5],[221,5],[276,5],[319,5],[355,5],[399,5]]},"1039":{"position":[[85,5],[559,5],[784,5],[1091,5],[1144,5],[1225,5]]},"1041":{"position":[[43,5],[105,5],[154,5],[182,6],[207,6],[277,5],[310,5],[448,5],[586,5],[622,5],[696,5],[779,5],[802,5],[938,5],[977,7]]},"1043":{"position":[[159,5]]},"1045":{"position":[[20,5],[73,9],[114,5],[134,5],[287,10],[996,5],[1043,5],[1056,9],[1147,5],[1306,5],[1431,5],[1519,6],[1612,6]]},"1047":{"position":[[33,7],[92,5],[152,6],[206,10],[334,5],[460,6],[514,10]]},"1049":{"position":[[22,5],[335,10],[690,5],[823,5],[1047,7],[1066,6]]},"1051":{"position":[[122,10],[395,5],[467,5],[930,7],[949,6]]},"1077":{"position":[[739,5],[977,8]]},"1134":{"position":[[187,5]]},"1243":{"position":[[66,5]]},"1255":{"position":[[656,5],[1073,5]]},"1257":{"position":[[486,5],[545,5]]},"1261":{"position":[[0,5],[85,6]]},"1263":{"position":[[1543,5]]},"1265":{"position":[[124,6]]},"1269":{"position":[[172,5]]},"1277":{"position":[[993,7],[1095,8],[1311,5]]},"1293":{"position":[[676,5]]},"1299":{"position":[[508,5]]},"1311":{"position":[[224,5]]},"1329":{"position":[[185,5]]},"1373":{"position":[[189,5]]},"1387":{"position":[[937,8]]},"1495":{"position":[[647,5]]},"1497":{"position":[[614,5],[764,5]]},"1539":{"position":[[76,6]]},"1569":{"position":[[81,5]]},"1573":{"position":[[272,5],[397,5]]},"1575":{"position":[[148,5],[184,5],[619,5]]},"1585":{"position":[[88,5]]},"1587":{"position":[[287,5]]},"1589":{"position":[[245,5]]},"1597":{"position":[[1824,5]]},"1684":{"position":[[133,5]]},"1692":{"position":[[1059,5]]},"1721":{"position":[[622,5]]},"1731":{"position":[[161,5]]},"1771":{"position":[[241,5],[400,5],[1041,6],[1110,5],[1134,5],[1227,6],[1278,5],[1364,6],[1434,5],[2435,6],[2470,5]]},"1773":{"position":[[388,7],[739,5],[955,5],[993,5],[1367,6],[1433,5],[1457,5],[1528,5],[1952,6],[1987,5],[2099,6],[2136,5]]},"1775":{"position":[[945,6],[994,5],[1113,5],[1159,5]]},"1777":{"position":[[267,5],[293,5],[1098,6],[1133,5]]},"1809":{"position":[[239,5]]},"1865":{"position":[[677,5]]},"1867":{"position":[[650,5]]},"1887":{"position":[[62,6],[82,5]]},"1889":{"position":[[58,6],[78,5]]},"1891":{"position":[[66,6],[86,5]]},"1893":{"position":[[63,5],[83,5],[173,5]]},"1895":{"position":[[59,5],[79,5],[167,5]]},"1897":{"position":[[67,5],[87,5],[179,5]]},"1919":{"position":[[675,5]]},"1921":{"position":[[180,5]]},"1925":{"position":[[0,7],[206,5]]},"1933":{"position":[[300,5]]},"1961":{"position":[[149,5]]},"1997":{"position":[[330,5]]},"2005":{"position":[[1146,5]]},"2060":{"position":[[338,5]]},"2070":{"position":[[61,5],[142,5],[323,5],[504,5]]},"2197":{"position":[[739,5],[977,8]]},"2247":{"position":[[187,5]]},"2346":{"position":[[5,5]]},"2348":{"position":[[4,5]]},"2350":{"position":[[37,5],[68,7],[308,8],[742,5]]},"2352":{"position":[[83,5],[242,8],[302,5],[349,8],[412,7]]},"2354":{"position":[[207,7],[385,7],[1038,5],[1184,7],[2310,5],[2479,5],[3250,5],[3548,5],[5768,5]]},"2358":{"position":[[193,7],[1280,5]]},"2360":{"position":[[223,7],[338,5],[353,5],[599,5],[698,5],[796,5],[834,5],[947,5],[970,5],[1008,5],[1176,5],[1234,5],[2789,5]]},"2362":{"position":[[201,7],[705,6],[720,5],[888,5],[996,5],[1084,5],[2577,5]]},"2364":{"position":[[450,5],[475,5],[651,5],[769,5],[797,5],[1353,5]]},"2366":{"position":[[328,5]]},"2368":{"position":[[808,5]]},"2370":{"position":[[20,5],[264,5],[303,5],[371,5],[447,5],[615,8],[713,6],[829,6],[948,6],[1060,5],[1277,5]]},"2372":{"position":[[46,5]]},"2374":{"position":[[183,5]]},"2376":{"position":[[155,5],[221,5],[276,5],[319,5],[355,5],[399,5]]},"2378":{"position":[[85,5],[559,5],[784,5],[1091,5],[1144,5],[1225,5]]},"2380":{"position":[[17,5],[79,5],[128,5],[156,6],[181,6],[251,5],[284,5],[422,5],[560,5],[596,5],[670,5],[753,5],[776,5],[912,5],[951,7]]},"2382":{"position":[[159,5]]},"2384":{"position":[[20,5],[73,9],[114,5],[134,5],[287,10],[996,5],[1043,5],[1056,9],[1147,5],[1306,5],[1431,5],[1519,6],[1612,6]]},"2386":{"position":[[33,7],[92,5],[152,6],[206,10],[334,5],[460,6],[514,10]]},"2388":{"position":[[35,7],[372,10],[727,5],[860,5],[1005,5],[1263,7],[1282,6]]},"2390":{"position":[[122,10],[395,5],[467,5],[930,7],[949,6]]},"2392":{"position":[[2152,5],[2186,5],[2196,5]]},"2400":{"position":[[2404,5],[2547,7],[2591,7],[2613,5],[2718,7]]},"2424":{"position":[[1255,5]]},"2428":{"position":[[721,6]]},"2444":{"position":[[193,5]]},"2492":{"position":[[66,5]]},"2504":{"position":[[656,5],[1073,5]]},"2506":{"position":[[486,5],[545,5]]},"2510":{"position":[[0,5],[85,6]]},"2512":{"position":[[1684,5],[1700,5]]},"2514":{"position":[[124,6]]},"2518":{"position":[[172,5]]},"2544":{"position":[[189,5]]},"2558":{"position":[[984,8]]},"2580":{"position":[[98,5]]},"2586":{"position":[[508,5]]},"2598":{"position":[[230,5]]},"2654":{"position":[[226,5],[258,7]]},"2664":{"position":[[81,5]]},"2668":{"position":[[272,5],[397,5]]},"2670":{"position":[[148,5],[184,5],[619,5]]},"2680":{"position":[[88,5]]},"2682":{"position":[[287,5]]},"2684":{"position":[[245,5]]},"2690":{"position":[[647,5]]},"2692":{"position":[[614,5],[764,5]]},"2708":{"position":[[707,5],[1264,5],[1313,5]]},"2744":{"position":[[76,6]]},"2780":{"position":[[1788,5]]},"2848":{"position":[[133,5]]},"2856":{"position":[[1059,5]]},"2887":{"position":[[622,5]]},"2920":{"position":[[161,5]]},"2930":{"position":[[241,5],[400,5],[1041,6],[1110,5],[1134,5],[1227,6],[1278,5],[1364,6],[1434,5],[2435,6],[2470,5]]},"2932":{"position":[[388,7],[739,5],[955,5],[993,5],[1367,6],[1433,5],[1457,5],[1528,5],[1952,6],[1987,5],[2099,6],[2136,5]]},"2934":{"position":[[945,6],[994,5],[1113,5],[1159,5]]},"2936":{"position":[[267,5],[293,5],[1098,6],[1133,5]]},"2978":{"position":[[300,5]]},"3006":{"position":[[149,5]]},"3038":{"position":[[253,5]]},"3096":{"position":[[677,5]]},"3098":{"position":[[650,5]]},"3118":{"position":[[62,6],[82,5]]},"3120":{"position":[[58,6],[78,5]]},"3122":{"position":[[66,6],[86,5]]},"3124":{"position":[[76,6]]},"3126":{"position":[[63,5],[83,5],[173,5]]},"3128":{"position":[[59,5],[79,5],[167,5]]},"3130":{"position":[[67,5],[87,5],[179,5]]},"3132":{"position":[[77,5],[97,5],[194,5]]},"3146":{"position":[[675,5]]},"3148":{"position":[[180,5]]},"3152":{"position":[[0,7],[206,5]]},"3186":{"position":[[330,5]]},"3194":{"position":[[1146,5]]},"3220":{"position":[[148,6],[344,5],[374,5],[429,5],[1078,5],[1096,5],[1247,5]]},"3255":{"position":[[338,5]]},"3307":{"position":[[61,5],[142,5],[323,5],[504,5]]},"3324":{"position":[[4940,5],[5006,5],[5190,5],[5249,5],[5436,5],[5528,5],[5666,5],[5754,5]]},"3363":{"position":[[6,5],[301,5],[599,5],[665,5],[795,5],[1169,5]]},"3365":{"position":[[1516,5],[1751,7],[2373,5],[2816,5],[2933,5],[3156,5],[3199,5],[3301,5]]},"3367":{"position":[[122,6],[338,5],[393,6],[448,5],[1204,5],[1246,5],[2272,5],[4430,5]]},"3369":{"position":[[580,5]]},"3371":{"position":[[9,5],[95,5],[173,7],[254,10]]},"3373":{"position":[[32,5]]},"3407":{"position":[[5,5]]},"3409":{"position":[[9,5]]},"3411":{"position":[[37,5],[68,7],[308,8],[742,5],[932,5],[1343,5]]},"3413":{"position":[[83,5],[242,8],[302,5],[349,8],[412,7]]},"3415":{"position":[[207,7],[385,7],[1038,5],[1184,7],[2310,5],[2479,5],[3250,5],[3548,5],[5797,5]]},"3419":{"position":[[193,7],[1327,5]]},"3421":{"position":[[223,7],[339,5],[354,5],[520,5],[613,5],[660,5],[773,5],[811,5],[979,5],[1037,5],[2592,5]]},"3423":{"position":[[201,7],[705,6],[720,5],[888,5],[996,5],[1084,5],[2577,5]]},"3425":{"position":[[423,5],[448,5],[624,5],[742,5],[770,5],[1326,5]]},"3427":{"position":[[328,5],[494,5]]},"3429":{"position":[[808,5],[1028,5]]},"3431":{"position":[[20,5],[264,5],[303,5],[371,5],[447,5],[615,8],[713,6],[829,6],[948,6],[1060,5],[1277,5]]},"3433":{"position":[[46,5],[558,5]]},"3435":{"position":[[183,5]]},"3437":{"position":[[155,5],[221,5],[276,5],[319,5],[355,5],[399,5]]},"3439":{"position":[[85,5],[559,5],[784,5],[1091,5],[1144,5],[1225,5]]},"3441":{"position":[[17,5],[79,5],[128,5],[156,6],[181,6],[251,5],[284,5],[422,5],[560,5],[596,5],[670,5],[753,5],[776,5],[912,5],[951,7]]},"3443":{"position":[[159,5]]},"3445":{"position":[[20,5],[73,9],[114,5],[134,5],[287,10],[996,5],[1043,5],[1056,9],[1147,5],[1306,5],[1431,5],[1519,6],[1626,5],[1683,5],[1824,6],[2356,5]]},"3447":{"position":[[33,7],[92,5],[152,6],[206,10],[334,5],[460,6],[514,10]]},"3449":{"position":[[35,7],[372,10],[727,5],[860,5],[1005,5],[1263,7],[1282,6]]},"3451":{"position":[[122,10],[395,5],[467,5],[930,7],[949,6]]},"3456":{"position":[[555,5]]},"3472":{"position":[[739,5],[977,8]]},"3486":{"position":[[535,5]]},"3498":{"position":[[663,5]]},"3597":{"position":[[187,5]]},"3612":{"position":[[2351,5],[2385,5],[2395,5]]},"3665":{"position":[[226,5],[258,7]]}}}],["proxy.proto",{"_index":4234,"t":{"1031":{"position":[[242,12]]},"2370":{"position":[[242,12]]},"2708":{"position":[[1366,11]]},"3367":{"position":[[1694,11]]},"3431":{"position":[[242,12]]}}}],["proxy/load",{"_index":4064,"t":{"926":{"position":[[742,10]]},"2047":{"position":[[742,10]]},"3240":{"position":[[832,10]]}}}],["proxy_add_x_forwarded_for",{"_index":2314,"t":{"198":{"position":[[990,27],[1415,27]]},"250":{"position":[[955,27],[1292,27]]}}}],["proxy_binary_encod",{"_index":3662,"t":{"608":{"position":[[794,24]]},"1039":{"position":[[835,24]]},"2378":{"position":[[835,24]]},"3439":{"position":[[835,24]]}}}],["proxy_buff",{"_index":2317,"t":{"198":{"position":[[1166,15]]},"979":{"position":[[1012,15]]},"981":{"position":[[586,15]]},"2072":{"position":[[1012,15]]},"2074":{"position":[[586,15]]},"3309":{"position":[[1012,15]]},"3311":{"position":[[586,15]]}}}],["proxy_cache_bypass",{"_index":2323,"t":{"198":{"position":[[1487,18]]},"250":{"position":[[1364,18]]}}}],["proxy_channel_state_endpoint",{"_index":5460,"t":{"2708":{"position":[[1004,30],[1072,31]]}}}],["proxy_connect_endpoint",{"_index":2028,"t":{"170":{"position":[[314,25]]},"196":{"position":[[332,25]]},"248":{"position":[[281,25]]},"1017":{"position":[[60,25],[218,22],[1800,22]]},"1031":{"position":[[738,25]]},"2354":{"position":[[60,25],[218,22],[1917,22]]},"2370":{"position":[[738,25]]},"3415":{"position":[[60,25],[218,22],[1917,22]]},"3431":{"position":[[738,25]]}}}],["proxy_connect_timeout",{"_index":3649,"t":{"596":{"position":[[85,24],[133,24]]},"618":{"position":[[1511,21]]},"1017":{"position":[[130,24],[4633,21]]},"1031":{"position":[[790,24]]},"2354":{"position":[[130,24],[4750,21]]},"2370":{"position":[[790,24]]},"3415":{"position":[[130,24],[4779,21]]},"3431":{"position":[[790,24]]}}}],["proxy_extra_http_head",{"_index":3660,"t":{"608":{"position":[[339,24],[417,25],[463,24]]}}}],["proxy_grpc_cert_fil",{"_index":4237,"t":{"1033":{"position":[[62,21]]},"2372":{"position":[[62,21]]},"3433":{"position":[[62,21]]}}}],["proxy_grpc_compress",{"_index":5723,"t":{"3433":{"position":[[440,23]]}}}],["proxy_grpc_credentials_key",{"_index":4238,"t":{"1033":{"position":[[224,27],[412,27]]},"2372":{"position":[[224,27],[412,27]]},"3433":{"position":[[224,27],[412,27]]}}}],["proxy_grpc_credentials_valu",{"_index":4239,"t":{"1033":{"position":[[327,29]]},"2372":{"position":[[327,29]]},"3433":{"position":[[327,29]]}}}],["proxy_grpc_metadata",{"_index":4182,"t":{"1015":{"position":[[159,19]]},"2352":{"position":[[159,19]]},"3413":{"position":[[159,19]]}}}],["proxy_http_head",{"_index":2030,"t":{"170":{"position":[[376,21]]},"196":{"position":[[564,21]]},"248":{"position":[[351,21]]},"608":{"position":[[168,21]]},"1013":{"position":[[348,21]]},"2350":{"position":[[348,21]]},"3411":{"position":[[348,21],[1386,18]]}}}],["proxy_http_vers",{"_index":2310,"t":{"198":{"position":[[863,18],[1142,18]]},"250":{"position":[[828,18],[1086,18]]},"979":{"position":[[1079,18]]},"981":{"position":[[763,18]]},"2072":{"position":[[1079,18]]},"2074":{"position":[[763,18]]},"3309":{"position":[[1079,18]]},"3311":{"position":[[763,18]]}}}],["proxy_include_connection_meta",{"_index":4218,"t":{"1019":{"position":[[1161,32]]},"1021":{"position":[[1361,32]]},"1023":{"position":[[2519,32]]},"1025":{"position":[[2658,32]]},"1809":{"position":[[299,29]]},"2356":{"position":[[1161,32]]},"2358":{"position":[[1361,32]]},"2360":{"position":[[2519,32]]},"2362":{"position":[[2658,32]]},"2364":{"position":[[2630,32]]},"3038":{"position":[[322,29]]},"3417":{"position":[[1161,32]]},"3419":{"position":[[1408,32]]},"3421":{"position":[[2322,32]]},"3423":{"position":[[2658,32]]},"3425":{"position":[[2603,32]]}}}],["proxy_name=connect08:25:33",{"_index":2938,"t":{"329":{"position":[[1060,26]]}}}],["proxy_next_upstream",{"_index":4162,"t":{"979":{"position":[[822,19]]},"2072":{"position":[[822,19]]},"3309":{"position":[[822,19]]}}}],["proxy_pass",{"_index":2308,"t":{"198":{"position":[[818,10],[1097,10]]},"250":{"position":[[794,10],[1052,10]]},"979":{"position":[[982,10],[1317,10]]},"981":{"position":[[299,10],[556,10]]},"2072":{"position":[[982,10],[1317,10]]},"2074":{"position":[[299,10],[556,10]]},"3309":{"position":[[982,10],[1317,10]]},"3311":{"position":[[299,10],[556,10]]}}}],["proxy_pass_head",{"_index":4172,"t":{"981":{"position":[[329,17]]},"2074":{"position":[[329,17]]},"3311":{"position":[[329,17]]}}}],["proxy_publish",{"_index":2295,"t":{"196":{"position":[[650,16]]},"850":{"position":[[0,13]]},"854":{"position":[[109,13]]},"1025":{"position":[[853,13],[1128,13],[1308,16],[1521,16]]},"1031":{"position":[[1236,16]]},"1889":{"position":[[0,13]]},"1895":{"position":[[109,13]]},"2362":{"position":[[853,13],[1128,13],[1308,16],[1521,16]]},"2370":{"position":[[1236,16]]},"3120":{"position":[[0,13]]},"3128":{"position":[[109,13]]},"3423":{"position":[[853,13],[1128,13],[1308,16],[1521,16]]},"3431":{"position":[[1236,16]]}}}],["proxy_publish_endpoint",{"_index":2291,"t":{"196":{"position":[[408,25]]},"1025":{"position":[[59,25],[212,23],[1190,25],[1371,25]]},"1031":{"position":[[1103,25]]},"2362":{"position":[[59,25],[212,23],[1190,25],[1371,25]]},"2370":{"position":[[1103,25]]},"3423":{"position":[[59,25],[212,23],[1190,25],[1371,25]]},"3431":{"position":[[1103,25]]}}}],["proxy_publish_timeout",{"_index":3696,"t":{"618":{"position":[[1725,21]]},"1025":{"position":[[129,24],[1260,24],[1441,24],[3206,21]]},"1031":{"position":[[1155,24]]},"2362":{"position":[[129,24],[1260,24],[1441,24],[3206,21]]},"2370":{"position":[[1155,24]]},"3423":{"position":[[129,24],[1260,24],[1441,24],[3206,21]]},"3431":{"position":[[1155,24]]}}}],["proxy_read_timeout",{"_index":2320,"t":{"198":{"position":[[1209,18]]},"979":{"position":[[1055,18]]},"981":{"position":[[629,18]]},"2072":{"position":[[1055,18]]},"2074":{"position":[[629,18]]},"3309":{"position":[[1055,18]]},"3311":{"position":[[629,18]]}}}],["proxy_redirect",{"_index":4173,"t":{"981":{"position":[[389,14]]},"2074":{"position":[[389,14]]},"3311":{"position":[[389,14]]}}}],["proxy_refresh_endpoint",{"_index":4214,"t":{"1019":{"position":[[60,25],[184,22]]},"1031":{"position":[[854,25]]},"2356":{"position":[[60,25],[184,22]]},"2370":{"position":[[854,25]]},"3417":{"position":[[60,25],[184,22]]},"3431":{"position":[[854,25]]}}}],["proxy_refresh_timeout",{"_index":3693,"t":{"618":{"position":[[1565,21]]},"1019":{"position":[[130,24],[1620,21]]},"1031":{"position":[[906,24]]},"2356":{"position":[[130,24],[1620,21]]},"2370":{"position":[[906,24]]},"3417":{"position":[[130,24],[1620,21]]},"3431":{"position":[[906,24]]}}}],["proxy_rpc_endpoint",{"_index":4219,"t":{"1021":{"position":[[59,21],[204,19]]},"1031":{"position":[[973,21]]},"2358":{"position":[[59,21],[204,19]]},"2370":{"position":[[973,21]]},"3419":{"position":[[59,21],[204,19]]},"3431":{"position":[[973,21]]}}}],["proxy_rpc_timeout",{"_index":3694,"t":{"618":{"position":[[1619,17]]},"1021":{"position":[[125,20],[1608,17]]},"1031":{"position":[[1021,20]]},"2358":{"position":[[125,20],[1608,17]]},"2370":{"position":[[1021,20]]},"3419":{"position":[[125,20],[1655,17]]},"3431":{"position":[[1021,20]]}}}],["proxy_set_head",{"_index":2311,"t":{"198":{"position":[[887,16],[916,16],[957,16],[1018,16],[1233,16],[1273,16],[1312,16],[1341,16],[1382,16],[1443,16]]},"250":{"position":[[852,16],[881,16],[922,16],[983,16],[1110,16],[1150,16],[1189,16],[1218,16],[1259,16],[1320,16]]},"979":{"position":[[849,16],[890,16],[925,16],[1103,16],[1144,16],[1179,16],[1213,16],[1253,16]]},"981":{"position":[[355,16],[409,16],[450,16],[653,16],[694,16],[729,16],[787,16],[827,16]]},"2072":{"position":[[849,16],[890,16],[925,16],[1103,16],[1144,16],[1179,16],[1213,16],[1253,16]]},"2074":{"position":[[355,16],[409,16],[450,16],[653,16],[694,16],[729,16],[787,16],[827,16]]},"3309":{"position":[[849,16],[890,16],[925,16],[1103,16],[1144,16],[1179,16],[1213,16],[1253,16]]},"3311":{"position":[[355,16],[409,16],[450,16],[653,16],[694,16],[729,16],[787,16],[827,16]]}}}],["proxy_static_http_head",{"_index":5720,"t":{"3411":{"position":[[966,28],[1034,25]]}}}],["proxy_sub_refresh",{"_index":5060,"t":{"1891":{"position":[[0,17]]},"1897":{"position":[[117,17]]},"2364":{"position":[[608,17],[1397,17],[1529,20],[1702,20]]},"3122":{"position":[[0,17]]},"3130":{"position":[[117,17]]},"3425":{"position":[[581,17],[1370,17],[1502,20],[1675,20]]}}}],["proxy_sub_refresh_endpoint",{"_index":5216,"t":{"2364":{"position":[[87,29],[223,26],[1451,29],[1596,29]]},"3425":{"position":[[60,29],[196,26],[1424,29],[1569,29]]}}}],["proxy_sub_refresh_timeout",{"_index":5218,"t":{"2364":{"position":[[165,28],[3087,25]]},"3425":{"position":[[138,28],[3079,25]]}}}],["proxy_subscrib",{"_index":2296,"t":{"196":{"position":[[673,18]]},"293":{"position":[[1273,18]]},"848":{"position":[[0,15]]},"852":{"position":[[113,15]]},"1023":{"position":[[1137,15],[1278,15],[1437,18],[1641,18]]},"1031":{"position":[[1425,18]]},"1887":{"position":[[0,15]]},"1893":{"position":[[113,15]]},"2360":{"position":[[1137,15],[1278,15],[1437,18],[1641,18]]},"2370":{"position":[[1425,18]]},"3118":{"position":[[0,15]]},"3126":{"position":[[113,15]]},"3421":{"position":[[940,15],[1081,15],[1240,18],[1444,18]]},"3431":{"position":[[1425,18]]}}}],["proxy_subscribe_endpoint",{"_index":2293,"t":{"196":{"position":[[484,27]]},"293":{"position":[[1143,27]]},"1023":{"position":[[59,27],[234,25],[1330,27],[1502,27]]},"1031":{"position":[[1338,27]]},"2360":{"position":[[59,27],[234,25],[1330,27],[1502,27]]},"2370":{"position":[[1338,27]]},"3421":{"position":[[59,27],[234,25],[1133,27],[1305,27]]},"3431":{"position":[[1338,27]]}}}],["proxy_subscribe_stream",{"_index":5577,"t":{"3124":{"position":[[0,22]]},"3132":{"position":[[127,22]]},"3367":{"position":[[1115,25]]},"3369":{"position":[[909,25]]}}}],["proxy_subscribe_stream_bidirect",{"_index":5699,"t":{"3369":{"position":[[679,36],[941,39]]}}}],["proxy_subscribe_stream_endpoint",{"_index":5650,"t":{"3367":{"position":[[502,34],[977,34]]},"3369":{"position":[[771,34]]}}}],["proxy_subscribe_stream_timeout",{"_index":5651,"t":{"3367":{"position":[[563,33],[716,30],[1038,33]]},"3369":{"position":[[832,33]]}}}],["proxy_subscribe_timeout",{"_index":3695,"t":{"618":{"position":[[1669,23]]},"1023":{"position":[[133,26],[1404,26],[1576,26],[3868,23]]},"1031":{"position":[[1392,26]]},"2360":{"position":[[133,26],[1404,26],[1576,26],[3899,23]]},"2370":{"position":[[1392,26]]},"3421":{"position":[[133,26],[1207,26],[1379,26],[3902,23]]},"3431":{"position":[[1392,26]]}}}],["pub",{"_index":980,"t":{"34":{"position":[[1203,3],[1430,5]]},"136":{"position":[[1391,7]]},"971":{"position":[[1765,3],[1992,5]]},"1569":{"position":[[364,3]]},"1571":{"position":[[401,7],[1015,7],[1090,6],[1728,6]]},"1583":{"position":[[124,6]]},"1585":{"position":[[406,7]]},"1981":{"position":[[1765,3],[1992,5]]},"2152":{"position":[[409,3]]},"2189":{"position":[[302,3]]},"2664":{"position":[[364,3]]},"2666":{"position":[[401,7],[1015,7],[1090,6],[1728,6]]},"2678":{"position":[[124,6]]},"2680":{"position":[[406,7]]},"3140":{"position":[[1765,3],[1992,5]]},"3367":{"position":[[3201,3],[3335,5]]},"3369":{"position":[[2313,3],[2405,5]]},"3381":{"position":[[409,3]]},"3492":{"position":[[302,3]]}}}],["pub.offset",{"_index":987,"t":{"34":{"position":[[1330,11]]},"971":{"position":[[1892,11]]},"1981":{"position":[[1892,11]]},"3140":{"position":[[1892,11]]}}}],["pub/sub",{"_index":597,"t":{"16":{"position":[[798,8],[869,7],[2306,7],[3171,7],[5630,7],[5798,7],[6070,7],[6165,7],[7139,7]]},"22":{"position":[[412,7]]},"26":{"position":[[685,7]]},"38":{"position":[[100,8],[328,7],[423,7],[1002,7]]},"66":{"position":[[2569,7]]},"78":{"position":[[655,7],[1426,7],[1561,7],[3864,7]]},"80":{"position":[[298,7],[422,7],[990,7]]},"118":{"position":[[725,7]]},"206":{"position":[[508,7]]},"254":{"position":[[414,7]]},"258":{"position":[[735,7]]},"311":{"position":[[732,7]]},"319":{"position":[[91,8]]},"349":{"position":[[826,7]]},"365":{"position":[[50,7],[76,7]]},"375":{"position":[[16,7]]},"465":{"position":[[324,7],[734,7]]},"467":{"position":[[423,8],[783,7]]},"475":{"position":[[3542,7],[3788,7]]},"477":{"position":[[2718,8],[3206,7],[3318,7]]},"527":{"position":[[520,7]]},"840":{"position":[[124,7]]},"973":{"position":[[3430,7]]},"1071":{"position":[[1218,7]]},"1187":{"position":[[241,7]]},"1191":{"position":[[1271,7]]},"1197":{"position":[[940,7]]},"1199":{"position":[[895,7],[983,7]]},"1203":{"position":[[986,7]]},"1207":{"position":[[33,7],[147,7],[277,8]]},"1281":{"position":[[820,7],[967,7]]},"1323":{"position":[[55,7],[250,7]]},"1333":{"position":[[16,7]]},"1831":{"position":[[16,7]]},"1857":{"position":[[218,7]]},"1983":{"position":[[4574,7]]},"2084":{"position":[[241,7]]},"2090":{"position":[[1271,7]]},"2098":{"position":[[940,7]]},"2100":{"position":[[895,7],[983,7]]},"2106":{"position":[[986,7]]},"2110":{"position":[[33,7],[147,7],[277,8]]},"2134":{"position":[[1230,7]]},"2404":{"position":[[858,7],[1009,7]]},"2420":{"position":[[78,8],[199,7]]},"2422":{"position":[[425,7]]},"2424":{"position":[[515,7],[542,7]]},"2438":{"position":[[57,7],[252,7]]},"2448":{"position":[[16,7]]},"3060":{"position":[[25,7]]},"3088":{"position":[[218,7]]},"3142":{"position":[[4574,7]]},"3269":{"position":[[241,7]]},"3275":{"position":[[1271,7]]},"3283":{"position":[[940,7]]},"3285":{"position":[[895,7],[983,7]]},"3287":{"position":[[252,7]]},"3289":{"position":[[986,7]]},"3293":{"position":[[33,7],[147,7],[277,8]]},"3337":{"position":[[1230,7]]}}}],["pubin",{"_index":1611,"t":{"100":{"position":[[342,5]]},"2392":{"position":[[1169,5]]},"3612":{"position":[[1196,5]]}}}],["pubkey",{"_index":1608,"t":{"100":{"position":[[312,6]]},"2392":{"position":[[1139,6]]},"3612":{"position":[[1166,6]]}}}],["public",{"_index":193,"t":{"2":{"position":[[3214,12]]},"16":{"position":[[5973,12]]},"32":{"position":[[840,6]]},"74":{"position":[[254,11],[318,11],[2476,12]]},"78":{"position":[[1060,16]]},"80":{"position":[[650,11]]},"130":{"position":[[1686,6]]},"132":{"position":[[184,6],[255,6]]},"140":{"position":[[247,6]]},"172":{"position":[[310,6]]},"200":{"position":[[1297,11],[1388,12]]},"212":{"position":[[643,6]]},"289":{"position":[[565,12]]},"295":{"position":[[121,12],[515,12],[912,11],[930,11]]},"317":{"position":[[574,12]]},"331":{"position":[[1410,12]]},"345":{"position":[[311,12],[1059,12]]},"347":{"position":[[764,12]]},"349":{"position":[[300,11],[854,11]]},"351":{"position":[[164,12]]},"383":{"position":[[52,12],[83,11]]},"429":{"position":[[1148,11]]},"455":{"position":[[665,12],[691,12],[942,11],[981,12],[1155,12]]},"457":{"position":[[10,12],[89,12]]},"461":{"position":[[32,11],[126,12],[289,12],[476,12],[632,12]]},"467":{"position":[[580,11],[797,11]]},"475":{"position":[[3697,11],[4062,11]]},"477":{"position":[[4692,11]]},"563":{"position":[[859,11]]},"567":{"position":[[260,12]]},"589":{"position":[[61,12],[147,13]]},"678":{"position":[[196,11]]},"724":{"position":[[128,12]]},"748":{"position":[[147,11],[210,12]]},"768":{"position":[[126,12]]},"786":{"position":[[207,11]]},"816":{"position":[[383,7]]},"818":{"position":[[367,7]]},"828":{"position":[[343,6]]},"842":{"position":[[86,12],[784,12]]},"858":{"position":[[783,6],[1353,9],[1609,6]]},"969":{"position":[[183,12],[500,11],[1009,11]]},"971":{"position":[[775,12],[940,12]]},"973":{"position":[[177,12],[457,12],[750,11],[788,12],[1456,12],[1587,12],[1664,12],[2235,11],[2350,11],[3137,12],[3180,12],[3273,11]]},"1025":{"position":[[410,11],[3082,11]]},"1039":{"position":[[476,11]]},"1069":{"position":[[558,12]]},"1094":{"position":[[286,11],[651,11]]},"1255":{"position":[[1148,11]]},"1277":{"position":[[311,12],[1059,12]]},"1279":{"position":[[773,12]]},"1281":{"position":[[303,11],[874,11],[940,11]]},"1283":{"position":[[164,12]]},"1307":{"position":[[665,12],[691,12],[942,11],[981,12],[1155,12]]},"1309":{"position":[[10,12],[89,12]]},"1313":{"position":[[32,11],[126,12],[289,12],[476,12],[632,12]]},"1341":{"position":[[52,12],[83,11]]},"1355":{"position":[[1422,13]]},"1403":{"position":[[846,12]]},"1409":{"position":[[761,11],[863,12],[905,12],[969,11],[1110,12],[1178,12],[1462,11]]},"1417":{"position":[[176,12],[379,11],[1419,11]]},"1425":{"position":[[35,6],[1983,11]]},"1427":{"position":[[609,12]]},"1457":{"position":[[2075,11],[2141,11],[2223,11],[2381,11]]},"1459":{"position":[[412,12],[489,11],[596,11]]},"1469":{"position":[[1004,11]]},"1473":{"position":[[256,12],[811,15],[1086,13],[1204,13],[1259,12],[1546,12],[1568,11],[1600,12]]},"1475":{"position":[[31,12]]},"1505":{"position":[[210,12],[3703,15],[3740,11]]},"1535":{"position":[[260,12]]},"1569":{"position":[[343,12],[436,11]]},"1603":{"position":[[1938,12]]},"1611":{"position":[[1344,15]]},"1615":{"position":[[196,11]]},"1672":{"position":[[980,6]]},"1771":{"position":[[1931,6]]},"1773":{"position":[[308,11],[424,12],[1078,12],[1233,12],[1472,11]]},"1801":{"position":[[207,11]]},"1825":{"position":[[650,6]]},"1831":{"position":[[91,13]]},"1835":{"position":[[368,7]]},"1843":{"position":[[783,6]]},"1855":{"position":[[989,15]]},"1859":{"position":[[194,12],[1137,12]]},"1861":{"position":[[306,6]]},"1865":{"position":[[441,12]]},"1867":{"position":[[414,12]]},"1899":{"position":[[126,11]]},"1959":{"position":[[125,12]]},"1965":{"position":[[876,11],[938,12]]},"1979":{"position":[[1075,11]]},"1981":{"position":[[775,12],[940,12]]},"1983":{"position":[[177,12],[457,12],[781,12],[1628,12],[1759,12],[1836,12],[2422,11],[2509,11],[2721,12],[2873,12],[2983,11],[3052,12],[3078,12],[3336,12],[3683,12],[3766,12]]},"2118":{"position":[[158,9]]},"2132":{"position":[[558,12]]},"2189":{"position":[[290,11],[715,11],[1034,11],[1098,11],[1148,11]]},"2289":{"position":[[76,11],[2129,11],[2195,11],[2277,11],[2665,11]]},"2291":{"position":[[412,12],[489,11],[571,11]]},"2301":{"position":[[1047,11]]},"2305":{"position":[[256,12],[811,15],[1086,13],[1204,13],[1259,12],[1546,12],[1568,11],[1600,12]]},"2307":{"position":[[31,12]]},"2362":{"position":[[410,11],[3082,11]]},"2378":{"position":[[476,11]]},"2400":{"position":[[2793,11]]},"2402":{"position":[[820,12]]},"2404":{"position":[[316,11],[912,11],[978,11]]},"2406":{"position":[[165,12]]},"2456":{"position":[[53,12],[84,11]]},"2504":{"position":[[1148,11]]},"2526":{"position":[[1455,13]]},"2594":{"position":[[696,12],[722,12],[984,11],[1023,12],[1209,12]]},"2596":{"position":[[10,12],[89,12]]},"2600":{"position":[[32,11],[126,12],[289,12],[476,12],[632,12]]},"2622":{"position":[[2715,6],[2823,6],[2958,6]]},"2632":{"position":[[2451,6],[2588,6],[2724,6]]},"2636":{"position":[[78,12],[191,13],[214,11],[261,11],[550,11],[926,11],[2171,6]]},"2638":{"position":[[113,11],[186,12],[442,12],[895,12]]},"2646":{"position":[[1492,11]]},"2656":{"position":[[30,11],[124,12],[289,12],[476,12],[632,12]]},"2664":{"position":[[343,12],[436,11]]},"2686":{"position":[[211,12],[3736,15],[3773,12]]},"2740":{"position":[[260,12]]},"2788":{"position":[[1938,12]]},"2796":{"position":[[1344,15]]},"2800":{"position":[[196,11]]},"2836":{"position":[[980,6]]},"2930":{"position":[[1931,6]]},"2932":{"position":[[308,11],[424,12],[1078,12],[1233,12],[1472,11]]},"3004":{"position":[[125,12]]},"3010":{"position":[[876,11],[938,12]]},"3030":{"position":[[193,13]]},"3054":{"position":[[650,6]]},"3060":{"position":[[108,13]]},"3064":{"position":[[368,7]]},"3072":{"position":[[1071,6]]},"3084":{"position":[[953,15]]},"3090":{"position":[[194,12],[1137,12]]},"3092":{"position":[[306,6]]},"3096":{"position":[[441,12]]},"3098":{"position":[[414,12]]},"3134":{"position":[[126,11]]},"3138":{"position":[[1075,11]]},"3140":{"position":[[775,12],[940,12]]},"3142":{"position":[[177,12],[457,12],[781,12],[1628,12],[1759,12],[1836,12],[2422,11],[2509,11],[2721,12],[2873,12],[2983,11],[3052,12],[3078,12],[3336,12],[3683,12],[3766,12]]},"3335":{"position":[[558,12]]},"3341":{"position":[[172,9]]},"3345":{"position":[[287,9]]},"3367":{"position":[[4275,14],[4858,11]]},"3369":{"position":[[1558,12],[2165,12]]},"3423":{"position":[[410,11],[3082,11]]},"3439":{"position":[[476,11]]},"3492":{"position":[[290,11],[715,11],[1034,11],[1098,11],[1148,11]]},"3502":{"position":[[75,11],[865,11],[1637,11],[1703,11],[1785,11],[2173,11]]},"3504":{"position":[[487,12],[564,11],[646,11]]},"3514":{"position":[[885,11]]},"3518":{"position":[[256,12],[660,15],[928,13],[1046,13],[1101,12],[1388,12],[1410,11],[1442,12]]},"3520":{"position":[[31,12]]},"3526":{"position":[[269,12]]},"3629":{"position":[[1411,12]]},"3633":{"position":[[2715,6],[2823,6],[2958,6]]},"3643":{"position":[[2451,6],[2588,6],[2724,6]]},"3647":{"position":[[78,12],[191,13],[214,11],[261,11],[550,11],[926,11],[2171,6]]},"3649":{"position":[[113,11],[186,12],[442,12],[895,12]]},"3657":{"position":[[1492,11]]},"3667":{"position":[[30,11],[124,12],[289,12],[476,12],[632,12]]}}}],["public:chat",{"_index":3971,"t":{"816":{"position":[[277,11],[492,11],[545,11]]},"818":{"position":[[305,12]]},"1471":{"position":[[261,16]]},"1835":{"position":[[262,11],[477,11],[530,11]]},"2303":{"position":[[261,16]]},"3064":{"position":[[262,11],[504,12],[555,11]]}}}],["public:messag",{"_index":4007,"t":{"858":{"position":[[744,15]]},"1843":{"position":[[744,15]]},"3072":{"position":[[1032,15]]}}}],["public:new",{"_index":4010,"t":{"858":{"position":[[1588,11]]}}}],["public:test",{"_index":5173,"t":{"2118":{"position":[[448,16]]},"3341":{"position":[[418,15],[771,15]]}}}],["publicationev",{"_index":5417,"t":{"2636":{"position":[[2215,16]]},"3647":{"position":[[2215,16]]}}}],["publications/sec",{"_index":3383,"t":{"477":{"position":[[4751,17],[4797,16]]}}}],["publish",{"_index":127,"t":{"2":{"position":[[1915,9]]},"16":{"position":[[636,7],[1010,7]]},"38":{"position":[[402,7]]},"42":{"position":[[564,7]]},"62":{"position":[[155,7],[429,9]]},"66":{"position":[[2343,9],[2389,7],[2673,9]]},"72":{"position":[[829,9]]},"74":{"position":[[21,9],[475,7]]},"78":{"position":[[1314,9],[1544,10]]},"80":{"position":[[161,7],[977,9]]},"92":{"position":[[265,7]]},"94":{"position":[[205,9]]},"130":{"position":[[1235,10]]},"136":{"position":[[1296,7]]},"144":{"position":[[133,9]]},"168":{"position":[[682,10]]},"174":{"position":[[107,7],[132,7],[291,10],[834,9]]},"176":{"position":[[795,12],[1152,7],[1936,7],[2417,7]]},"178":{"position":[[1007,7]]},"182":{"position":[[408,9]]},"196":{"position":[[633,10],[1412,7],[1945,7],[1982,7]]},"200":{"position":[[1265,7],[1422,7]]},"202":{"position":[[407,7],[677,7],[761,7],[776,7],[868,7],[1939,9]]},"216":{"position":[[4198,10],[4270,9]]},"218":{"position":[[43,10],[78,7],[683,7],[915,10]]},"224":{"position":[[165,7],[616,7]]},"226":{"position":[[451,9]]},"230":{"position":[[27,10],[136,10]]},"232":{"position":[[536,10]]},"234":{"position":[[114,9]]},"236":{"position":[[802,7],[900,7],[1027,7]]},"238":{"position":[[5,10]]},"240":{"position":[[22,7],[155,7],[480,10]]},"252":{"position":[[24,7],[274,7],[603,10]]},"289":{"position":[[995,7],[1052,7],[1089,7]]},"291":{"position":[[2797,8]]},"321":{"position":[[373,10]]},"331":{"position":[[1366,7]]},"341":{"position":[[794,7],[835,7],[979,9],[1048,7],[1236,7],[1277,7],[1421,9],[1490,7]]},"345":{"position":[[589,7],[1252,7],[1295,7]]},"351":{"position":[[65,7],[116,7]]},"385":{"position":[[38,10]]},"411":{"position":[[129,7],[1097,9]]},"429":{"position":[[17,7],[77,7],[581,9],[842,7],[1065,7],[1118,7],[1262,10],[1334,7],[1384,10]]},"435":{"position":[[158,9]]},"455":{"position":[[110,9],[491,9]]},"465":{"position":[[216,10],[354,9]]},"467":{"position":[[441,9]]},"475":{"position":[[1380,7],[3419,7],[3506,7],[3572,7],[3777,7]]},"477":{"position":[[4843,7],[5709,7]]},"483":{"position":[[1529,9]]},"497":{"position":[[689,7],[899,9]]},"527":{"position":[[321,7]]},"563":{"position":[[291,7],[512,10],[821,7],[1449,10]]},"581":{"position":[[97,7]]},"678":{"position":[[540,10],[1105,7],[1230,10]]},"786":{"position":[[229,9]]},"816":{"position":[[523,7],[648,7]]},"824":{"position":[[0,7],[61,7],[239,9],[335,7],[507,7]]},"826":{"position":[[57,7],[94,7],[256,8]]},"850":{"position":[[50,7]]},"854":{"position":[[51,7]]},"856":{"position":[[164,10]]},"858":{"position":[[1230,10],[1363,10]]},"912":{"position":[[1013,8]]},"969":{"position":[[515,9]]},"1025":{"position":[[162,7],[277,9],[351,7],[488,7],[517,7],[712,7],[880,7],[1043,7],[1076,7],[1146,7],[1291,10],[1504,10],[1592,7],[1725,7],[1950,7],[1985,7],[2468,7],[2697,7],[3010,10],[3185,11]]},"1031":{"position":[[1052,7],[1219,10]]},"1039":{"position":[[513,9]]},"1041":{"position":[[688,7]]},"1049":{"position":[[14,7],[424,10],[532,10],[815,7],[968,7]]},"1069":{"position":[[1169,7]]},"1147":{"position":[[1342,7]]},"1162":{"position":[[189,7]]},"1177":{"position":[[971,7],[1071,10]]},"1183":{"position":[[423,7]]},"1211":{"position":[[794,7],[835,7],[979,9],[1048,7],[1236,7],[1277,7],[1421,9],[1490,7]]},"1235":{"position":[[129,7],[1097,9]]},"1255":{"position":[[17,7],[77,7],[581,9],[842,7],[1065,7],[1118,7],[1262,10],[1334,7],[1384,10]]},"1261":{"position":[[158,9]]},"1277":{"position":[[589,7],[1252,7],[1295,7]]},"1283":{"position":[[65,7],[116,7]]},"1307":{"position":[[110,9],[491,9]]},"1343":{"position":[[25,10]]},"1391":{"position":[[685,7],[895,9]]},"1403":{"position":[[961,7],[1175,7],[1465,7]]},"1405":{"position":[[3749,9]]},"1407":{"position":[[587,7]]},"1409":{"position":[[1401,9]]},"1415":{"position":[[96,7],[122,7],[177,7],[197,7]]},"1417":{"position":[[404,9]]},"1423":{"position":[[1534,7],[1637,7]]},"1427":{"position":[[363,8]]},"1457":{"position":[[0,7],[23,10],[136,10],[258,7],[399,10],[762,10],[1130,10],[1394,10],[1460,10],[1868,7],[1973,7],[2019,7],[2293,7]]},"1459":{"position":[[11,7],[299,7],[353,7],[721,7],[772,7],[805,7]]},"1473":{"position":[[74,9]]},"1485":{"position":[[248,7]]},"1489":{"position":[[1315,11],[1419,9]]},"1491":{"position":[[1174,11]]},"1499":{"position":[[39,7]]},"1505":{"position":[[233,9],[8936,7],[9289,7],[9342,7],[9454,7],[9666,7],[9714,7],[9957,10]]},"1549":{"position":[[97,7]]},"1569":{"position":[[370,7]]},"1571":{"position":[[1399,8]]},"1585":{"position":[[332,10]]},"1615":{"position":[[540,10],[1099,7],[1224,10]]},"1666":{"position":[[313,7],[1107,10]]},"1668":{"position":[[643,7],[905,10]]},"1670":{"position":[[1083,7],[1386,10]]},"1771":{"position":[[698,8]]},"1773":{"position":[[52,9],[201,10],[251,10],[380,7],[556,7],[700,7],[731,7],[841,7],[882,7],[923,7],[961,7],[1330,7],[1359,7],[1384,9],[1425,7],[1520,7],[1617,7],[1747,7],[1768,7],[1898,7],[1920,7],[2038,7],[2067,7],[2191,7]]},"1801":{"position":[[229,9]]},"1831":{"position":[[40,10],[174,9]]},"1835":{"position":[[508,7],[633,7]]},"1841":{"position":[[254,9],[702,7]]},"1865":{"position":[[123,7],[315,7],[560,9],[608,7],[669,7]]},"1867":{"position":[[78,7],[230,8],[288,7],[533,9],[581,7],[642,7]]},"1869":{"position":[[102,7]]},"1889":{"position":[[50,7]]},"1895":{"position":[[51,7]]},"1899":{"position":[[247,8]]},"1979":{"position":[[550,9]]},"2027":{"position":[[1013,8]]},"2080":{"position":[[423,7]]},"2132":{"position":[[1169,7]]},"2150":{"position":[[1799,7],[1817,7]]},"2152":{"position":[[295,9],[426,9]]},"2160":{"position":[[1534,7],[1637,7]]},"2228":{"position":[[971,7],[1071,10]]},"2260":{"position":[[1421,7]]},"2289":{"position":[[0,7],[23,10],[197,10],[318,7],[459,10],[822,10],[1190,10],[1452,7],[1514,10],[1922,7],[2027,7],[2073,7],[2362,7],[2508,11],[2544,7],[2577,7]]},"2291":{"position":[[11,7],[299,7],[353,7],[656,7],[802,11],[838,7],[951,7],[1002,7],[1035,7]]},"2305":{"position":[[74,9]]},"2313":{"position":[[280,10],[356,10]]},"2317":{"position":[[248,7]]},"2321":{"position":[[1315,11],[1419,9]]},"2323":{"position":[[1174,11]]},"2342":{"position":[[189,7]]},"2362":{"position":[[162,7],[277,9],[351,7],[488,7],[517,7],[712,7],[880,7],[1043,7],[1076,7],[1146,7],[1291,10],[1504,10],[1592,7],[1725,7],[1950,7],[1985,7],[2468,7],[2697,7],[3010,10],[3185,11]]},"2364":{"position":[[442,7]]},"2370":{"position":[[1052,7],[1219,10]]},"2378":{"position":[[513,9]]},"2380":{"position":[[662,7]]},"2388":{"position":[[11,7],[461,10],[569,10],[852,7],[1171,8]]},"2394":{"position":[[794,7],[835,7],[979,9],[1048,7],[1236,7],[1277,7],[1421,9],[1490,7]]},"2400":{"position":[[1053,7],[1978,9],[2678,10],[2710,7]]},"2406":{"position":[[64,7],[117,7]]},"2458":{"position":[[29,10]]},"2484":{"position":[[129,7],[1097,9]]},"2504":{"position":[[17,7],[77,7],[581,9],[842,7],[1065,7],[1118,7],[1262,10],[1334,7],[1384,10]]},"2510":{"position":[[158,9]]},"2562":{"position":[[699,7],[921,9]]},"2594":{"position":[[110,9],[505,9]]},"2636":{"position":[[106,9],[519,9],[585,9],[670,9]]},"2642":{"position":[[104,7]]},"2664":{"position":[[370,7]]},"2666":{"position":[[1399,8]]},"2680":{"position":[[332,10]]},"2686":{"position":[[234,9],[9056,7],[9432,7],[9489,7],[9610,7],[9822,7],[9870,7]]},"2694":{"position":[[39,7]]},"2754":{"position":[[97,7]]},"2800":{"position":[[540,10],[1099,7],[1224,10]]},"2830":{"position":[[316,7],[1110,10]]},"2832":{"position":[[649,7],[911,10]]},"2834":{"position":[[1089,7],[1392,10]]},"2930":{"position":[[698,8]]},"2932":{"position":[[52,9],[201,10],[251,10],[380,7],[556,7],[700,7],[731,7],[841,7],[882,7],[923,7],[961,7],[1330,7],[1359,7],[1384,9],[1425,7],[1520,7],[1617,7],[1747,7],[1768,7],[1898,7],[1920,7],[2038,7],[2067,7],[2191,7]]},"3060":{"position":[[48,10],[195,9]]},"3064":{"position":[[531,7],[666,7],[794,7]]},"3070":{"position":[[262,9],[716,7]]},"3096":{"position":[[123,7],[315,7],[560,9],[608,7],[669,7]]},"3098":{"position":[[78,7],[230,8],[288,7],[533,9],[581,7],[642,7]]},"3100":{"position":[[102,7]]},"3120":{"position":[[50,7]]},"3128":{"position":[[51,7]]},"3134":{"position":[[247,8]]},"3138":{"position":[[550,9]]},"3216":{"position":[[1013,8]]},"3265":{"position":[[423,7]]},"3335":{"position":[[1169,7]]},"3363":{"position":[[366,10],[414,7]]},"3365":{"position":[[2474,9],[2710,10]]},"3367":{"position":[[1183,8],[3037,7]]},"3379":{"position":[[1799,7],[1817,7]]},"3381":{"position":[[295,9],[426,9]]},"3389":{"position":[[1534,7],[1637,7]]},"3423":{"position":[[162,7],[277,9],[351,7],[488,7],[517,7],[712,7],[880,7],[1043,7],[1076,7],[1146,7],[1291,10],[1504,10],[1592,7],[1725,7],[1950,7],[1985,7],[2468,7],[2697,7],[3010,10],[3185,11]]},"3425":{"position":[[415,7]]},"3427":{"position":[[478,7]]},"3429":{"position":[[1012,7]]},"3431":{"position":[[1052,7],[1219,10]]},"3439":{"position":[[513,9]]},"3441":{"position":[[662,7]]},"3449":{"position":[[11,7],[461,10],[569,10],[852,7],[1171,8]]},"3496":{"position":[[278,7]]},"3500":{"position":[[87,7]]},"3502":{"position":[[0,7],[22,10],[186,10],[390,7],[500,7],[945,7],[1437,7],[1535,7],[1581,7],[1870,7],[2016,11],[2052,7],[2085,7]]},"3504":{"position":[[24,7],[374,7],[428,7],[731,7],[877,11],[913,7],[1026,7],[1077,7],[1110,7]]},"3518":{"position":[[74,9]]},"3526":{"position":[[375,12],[423,11],[540,15]]},"3530":{"position":[[248,7]]},"3534":{"position":[[1315,11],[1419,9]]},"3536":{"position":[[1174,11]]},"3578":{"position":[[971,7]]},"3610":{"position":[[1421,7]]},"3627":{"position":[[189,7]]},"3629":{"position":[[1367,7]]},"3647":{"position":[[106,9],[519,9],[585,9],[670,9]]},"3653":{"position":[[104,7]]}}}],["publish');┌─count",{"_index":3810,"t":{"678":{"position":[[385,23]]},"1615":{"position":[[385,23]]},"2800":{"position":[[385,23]]}}}],["publish(ch",{"_index":1404,"t":{"78":{"position":[[798,10]]}}}],["publish(channel",{"_index":5442,"t":{"2646":{"position":[[1923,16]]},"3657":{"position":[[1923,16]]}}}],["publish(ctx",{"_index":3216,"t":{"475":{"position":[[1416,11]]}}}],["publish(data",{"_index":5430,"t":{"2642":{"position":[[88,13]]},"3369":{"position":[[287,14]]},"3653":{"position":[[88,13]]}}}],["publish(request",{"_index":2346,"t":{"200":{"position":[[1242,17]]}}}],["publish1",{"_index":4251,"t":{"1045":{"position":[[620,11]]},"1049":{"position":[[463,10]]},"2384":{"position":[[620,11]]},"2388":{"position":[[500,10]]},"3445":{"position":[[620,11]]},"3449":{"position":[[500,10]]}}}],["publish2",{"_index":4257,"t":{"1045":{"position":[[856,11]]},"1049":{"position":[[571,10]]},"2384":{"position":[[856,11]]},"2388":{"position":[[608,10]]},"3445":{"position":[[856,11]]},"3449":{"position":[[608,10]]}}}],["publish_proxy_nam",{"_index":4000,"t":{"854":{"position":[[0,18]]},"1049":{"position":[[89,18],[441,21],[549,21],[753,20],[777,20],[903,18]]},"1895":{"position":[[0,18]]},"2388":{"position":[[101,18],[478,21],[586,21],[790,20],[814,20],[1082,19]]},"3128":{"position":[[0,18]]},"3449":{"position":[[101,18],[478,21],[586,21],[790,20],[814,20],[1082,19]]}}}],["publishcontrol(data",{"_index":1411,"t":{"78":{"position":[[970,19]]}}}],["publisheddata",{"_index":2469,"t":{"216":{"position":[[3812,15]]}}}],["publishjoin(ch",{"_index":1407,"t":{"78":{"position":[[875,14]]}}}],["publishleave(ch",{"_index":1410,"t":{"78":{"position":[[922,15]]}}}],["publishopt",{"_index":1406,"t":{"78":{"position":[[835,15]]}}}],["publishrequest",{"_index":4703,"t":{"1491":{"position":[[860,16]]},"2323":{"position":[[860,16]]},"3536":{"position":[[860,16]]}}}],["pubnub",{"_index":5251,"t":{"2526":{"position":[[1967,7]]}}}],["puctur",{"_index":1979,"t":{"156":{"position":[[930,8]]}}}],["pull",{"_index":669,"t":{"16":{"position":[[4044,4]]},"82":{"position":[[1692,4]]},"108":{"position":[[917,4]]},"475":{"position":[[3083,4]]},"477":{"position":[[2951,4]]},"511":{"position":[[69,4]]},"1439":{"position":[[69,4]]},"2608":{"position":[[69,4]]}}}],["pulsar",{"_index":618,"t":{"16":{"position":[[2007,6],[3742,6]]}}}],["purchas",{"_index":5498,"t":{"2762":{"position":[[148,9],[527,8],[799,8]]}}}],["pure",{"_index":1827,"t":{"124":{"position":[[1111,4]]},"399":{"position":[[408,4]]},"473":{"position":[[416,4]]},"475":{"position":[[5718,4]]},"1223":{"position":[[408,4]]},"2472":{"position":[[408,4]]}}}],["purpos",{"_index":1874,"t":{"130":{"position":[[1191,7]]},"606":{"position":[[402,9]]},"712":{"position":[[220,9]]},"792":{"position":[[428,7]]},"1375":{"position":[[179,7]]},"1477":{"position":[[865,9]]},"1807":{"position":[[428,7]]},"1947":{"position":[[217,9]]},"2309":{"position":[[907,9]]},"2422":{"position":[[325,9]]},"2546":{"position":[[179,7]]},"2624":{"position":[[502,7]]},"2992":{"position":[[217,9]]},"3034":{"position":[[339,8]]},"3036":{"position":[[501,8]]},"3251":{"position":[[304,9]]},"3522":{"position":[[967,9]]},"3635":{"position":[[502,7]]}}}],["push",{"_index":740,"t":{"18":{"position":[[235,4]]},"218":{"position":[[629,4]]},"226":{"position":[[261,4]]},"228":{"position":[[71,4]]},"236":{"position":[[512,4]]},"256":{"position":[[421,4]]},"258":{"position":[[2191,4]]},"260":{"position":[[14,4],[84,4],[315,4],[507,4],[565,4],[1208,4],[1452,4],[1610,4]]},"262":{"position":[[0,4],[183,4],[274,4],[520,4],[555,4],[1447,4],[1812,4]]},"264":{"position":[[19,4],[470,4],[1595,4]]},"266":{"position":[[85,4]]},"268":{"position":[[75,4],[571,4],[611,4],[686,4],[789,4],[811,4],[1115,4],[1300,4]]},"270":{"position":[[8,4],[211,4],[713,4],[799,4],[852,4],[888,4],[939,4],[1159,4],[1186,4],[1222,4],[1613,4],[1647,4],[1824,4],[1856,4],[1970,4],[2179,6],[2257,4],[2562,4],[2631,4],[2660,4],[2829,4],[2939,4]]},"272":{"position":[[999,4]]},"274":{"position":[[50,4],[97,4],[189,4],[261,4],[404,4]]},"276":{"position":[[165,4]]},"278":{"position":[[25,4],[147,4],[268,4],[305,4],[373,4],[519,4]]},"335":{"position":[[372,4]]},"425":{"position":[[76,4],[156,4],[265,4],[430,4],[859,4]]},"477":{"position":[[4979,6]]},"479":{"position":[[2116,7]]},"963":{"position":[[229,6]]},"1094":{"position":[[58,4],[89,4],[230,4],[263,4],[519,7],[557,4],[792,4]]},"1130":{"position":[[213,4],[241,4]]},"1203":{"position":[[801,4]]},"1251":{"position":[[76,4],[156,4],[265,4],[300,4],[507,4],[936,4]]},"1427":{"position":[[678,4]]},"1461":{"position":[[738,5]]},"1527":{"position":[[386,4],[460,4]]},"1603":{"position":[[2044,4]]},"1613":{"position":[[56,4]]},"1615":{"position":[[1540,4]]},"1676":{"position":[[34,4]]},"1678":{"position":[[20,4],[266,4],[292,4]]},"1680":{"position":[[91,4],[346,6]]},"1684":{"position":[[55,4],[381,4]]},"1686":{"position":[[63,4],[197,4],[269,4]]},"1688":{"position":[[98,4],[747,4]]},"1690":{"position":[[40,4]]},"1692":{"position":[[75,4],[248,4],[487,4],[862,4],[1069,4]]},"1700":{"position":[[85,4],[545,4]]},"1703":{"position":[[272,4]]},"1719":{"position":[[5,4],[344,4],[396,4],[1695,4]]},"1721":{"position":[[107,4],[225,4],[323,4],[949,4]]},"1723":{"position":[[65,4],[152,4],[506,7]]},"1851":{"position":[[200,7]]},"1973":{"position":[[229,6]]},"2106":{"position":[[801,4]]},"2148":{"position":[[242,7]]},"2152":{"position":[[907,4]]},"2172":{"position":[[241,6]]},"2174":{"position":[[256,6]]},"2189":{"position":[[58,4],[89,4],[230,4],[263,4],[657,7],[671,4],[800,6],[847,4],[893,5],[1002,6]]},"2281":{"position":[[197,4],[225,4]]},"2293":{"position":[[703,5]]},"2422":{"position":[[383,4]]},"2424":{"position":[[989,4]]},"2500":{"position":[[76,4],[156,4],[265,4],[300,4],[507,4],[936,4]]},"2640":{"position":[[609,4]]},"2646":{"position":[[771,4],[1248,4]]},"2760":{"position":[[387,4],[461,4]]},"2788":{"position":[[2044,4]]},"2798":{"position":[[56,4]]},"2800":{"position":[[1540,4]]},"2840":{"position":[[34,4]]},"2842":{"position":[[20,4],[266,4],[292,4]]},"2844":{"position":[[91,4],[338,6],[394,4],[432,4]]},"2848":{"position":[[55,4],[381,4]]},"2850":{"position":[[63,4],[197,4],[269,4]]},"2852":{"position":[[98,4],[747,4]]},"2854":{"position":[[40,4]]},"2856":{"position":[[75,4],[248,4],[487,4],[862,4],[1069,4]]},"2862":{"position":[[298,4],[408,4],[569,4]]},"2864":{"position":[[70,4],[572,4]]},"2867":{"position":[[272,4]]},"2883":{"position":[[5,4],[344,4],[396,4],[511,5],[627,4],[646,4],[1582,4],[1974,4]]},"2885":{"position":[[15,4],[159,4]]},"2887":{"position":[[107,4],[225,4],[323,4],[949,4]]},"2889":{"position":[[65,4],[225,4],[301,4],[1006,4]]},"3080":{"position":[[200,7]]},"3289":{"position":[[801,4]]},"3301":{"position":[[229,6]]},"3320":{"position":[[229,6]]},"3377":{"position":[[242,7]]},"3381":{"position":[[907,4]]},"3401":{"position":[[241,6]]},"3403":{"position":[[256,6]]},"3492":{"position":[[58,4],[89,4],[230,4],[263,4],[657,7],[671,4],[800,6],[847,4],[893,5],[1002,6]]},"3506":{"position":[[771,5]]},"3563":{"position":[[176,4],[204,7]]},"3651":{"position":[[609,4]]},"3657":{"position":[[771,4],[1248,4]]}}}],["push_notif",{"_index":4929,"t":{"1692":{"position":[[618,21]]},"1694":{"position":[[83,21]]},"1696":{"position":[[83,21]]},"1700":{"position":[[288,21]]},"2856":{"position":[[618,21]]},"2858":{"position":[[83,21]]},"2860":{"position":[[83,21]]},"2864":{"position":[[273,21]]}}}],["push_notifications.apns_cert_p12_b64",{"_index":4950,"t":{"1696":{"position":[[514,36]]},"2860":{"position":[[514,36]]}}}],["push_notifications.apns_cert_p12_password",{"_index":4951,"t":{"1696":{"position":[[551,41]]},"2860":{"position":[[551,41]]}}}],["push_notifications.apns_cert_p12_path",{"_index":4949,"t":{"1696":{"position":[[476,37]]},"2860":{"position":[[476,37]]}}}],["push_notifications.dry_run",{"_index":5518,"t":{"2862":{"position":[[486,27],[738,26]]}}}],["push_notifications.dry_run_lat",{"_index":5519,"t":{"2862":{"position":[[669,35]]}}}],["push_notifications.enable_redis_delayed_schedul",{"_index":5516,"t":{"2862":{"position":[[183,50]]}}}],["push_notifications.enabled_provid",{"_index":4928,"t":{"1692":{"position":[[167,36]]},"2856":{"position":[[167,36]]}}}],["push_notifications.max_inactive_device_day",{"_index":4952,"t":{"1698":{"position":[[0,44]]},"2862":{"position":[[0,44]]}}}],["pusher",{"_index":459,"t":{"10":{"position":[[1740,6]]},"158":{"position":[[337,7]]},"264":{"position":[[67,6]]},"411":{"position":[[42,6]]},"951":{"position":[[934,6]]},"1235":{"position":[[42,6]]},"1921":{"position":[[934,6]]},"2484":{"position":[[42,6]]},"2526":{"position":[[1950,7]]},"3148":{"position":[[934,6]]}}}],["pushnotif",{"_index":4988,"t":{"1719":{"position":[[375,16],[1069,17]]},"2883":{"position":[[375,16],[1266,17]]}}}],["pushrecipi",{"_index":4987,"t":{"1719":{"position":[[313,13],[422,13]]},"2883":{"position":[[313,13],[683,13]]}}}],["pushtyp",{"_index":4321,"t":{"1094":{"position":[[275,8],[413,8]]}}}],["put",{"_index":392,"t":{"10":{"position":[[81,3],[1138,3]]},"132":{"position":[[760,3]]},"134":{"position":[[485,3]]},"140":{"position":[[90,3]]},"190":{"position":[[8,3]]},"192":{"position":[[465,3],[1355,3]]},"202":{"position":[[1405,3]]},"315":{"position":[[832,7]]},"397":{"position":[[331,3]]},"469":{"position":[[334,7]]},"479":{"position":[[2210,3]]},"499":{"position":[[3,3]]},"509":{"position":[[1259,3]]},"1017":{"position":[[1088,3]]},"1031":{"position":[[504,4]]},"1221":{"position":[[331,3]]},"1393":{"position":[[3,3]]},"1437":{"position":[[1492,3]]},"1489":{"position":[[585,3]]},"1585":{"position":[[429,7]]},"2321":{"position":[[585,3]]},"2354":{"position":[[1205,3]]},"2370":{"position":[[504,4]]},"2470":{"position":[[331,3]]},"2564":{"position":[[3,3]]},"2606":{"position":[[1532,3]]},"2680":{"position":[[429,7]]},"3415":{"position":[[1205,3]]},"3431":{"position":[[504,4]]},"3534":{"position":[[585,3]]}}}],["puzrin",{"_index":1947,"t":{"152":{"position":[[158,6]]}}}],["python",{"_index":1530,"t":{"94":{"position":[[2487,6]]},"116":{"position":[[910,6]]},"192":{"position":[[2775,7]]},"291":{"position":[[1198,6]]},"301":{"position":[[242,6]]},"800":{"position":[[54,7]]},"802":{"position":[[0,6]]},"804":{"position":[[46,6]]},"806":{"position":[[24,6]]},"1005":{"position":[[71,6]]},"1017":{"position":[[4820,6]]},"1457":{"position":[[343,7]]},"1483":{"position":[[324,6]]},"1487":{"position":[[16,6]]},"1505":{"position":[[4430,6]]},"1765":{"position":[[71,6]]},"1815":{"position":[[54,7]]},"1817":{"position":[[0,6]]},"1819":{"position":[[46,6]]},"1821":{"position":[[24,6]]},"2289":{"position":[[403,7]]},"2315":{"position":[[324,6]]},"2319":{"position":[[16,6]]},"2354":{"position":[[4943,6]]},"2686":{"position":[[4492,6]]},"2970":{"position":[[71,6],[131,6]]},"3044":{"position":[[54,7]]},"3046":{"position":[[0,6]]},"3048":{"position":[[46,6]]},"3050":{"position":[[24,6]]},"3415":{"position":[[4972,6]]},"3502":{"position":[[548,7]]},"3528":{"position":[[324,6]]},"3532":{"position":[[16,6]]}}}],["python3",{"_index":2111,"t":{"186":{"position":[[414,7]]},"190":{"position":[[133,7]]},"192":{"position":[[2423,7]]},"194":{"position":[[2462,7]]},"1505":{"position":[[4450,7]]},"2686":{"position":[[4512,7]]}}}],["python_out",{"_index":4643,"t":{"1487":{"position":[[137,12]]},"2319":{"position":[[137,12]]},"3532":{"position":[[137,12]]}}}],["pythonista",{"_index":2107,"t":{"182":{"position":[[839,11]]}}}],["p}func",{"_index":3147,"t":{"471":{"position":[[1712,6]]}}}],["q",{"_index":2744,"t":{"280":{"position":[[367,3],[1090,3],[1508,3],[3106,3],[3385,3],[3752,3]]}}}],["qfhv",{"_index":4615,"t":{"1473":{"position":[[790,7]]},"2305":{"position":[[790,7]]},"3518":{"position":[[639,7]]}}}],["qualifi",{"_index":1596,"t":{"98":{"position":[[1918,9]]}}}],["quay.io/keycloak/keycloak:21.0.1",{"_index":2376,"t":{"212":{"position":[[152,32]]}}}],["queri",{"_index":822,"t":{"20":{"position":[[1472,8]]},"274":{"position":[[466,7]]},"357":{"position":[[390,7]]},"553":{"position":[[462,5]]},"680":{"position":[[766,8]]},"724":{"position":[[162,6]]},"768":{"position":[[160,6]]},"973":{"position":[[289,5]]},"979":{"position":[[783,8]]},"1098":{"position":[[611,5]]},"1162":{"position":[[661,5]]},"1289":{"position":[[390,7]]},"1455":{"position":[[307,5],[440,5]]},"1527":{"position":[[586,6]]},"1617":{"position":[[935,8]]},"1721":{"position":[[358,8]]},"1959":{"position":[[159,6]]},"1983":{"position":[[289,5]]},"2072":{"position":[[783,8]]},"2118":{"position":[[217,5]]},"2234":{"position":[[611,5]]},"2287":{"position":[[310,5],[443,5]]},"2342":{"position":[[661,5]]},"2412":{"position":[[396,7]]},"2760":{"position":[[587,6]]},"2802":{"position":[[935,8]]},"2887":{"position":[[358,8]]},"3004":{"position":[[159,6]]},"3142":{"position":[[289,5]]},"3309":{"position":[[783,8]]},"3341":{"position":[[231,5]]},"3365":{"position":[[1151,5],[1395,5],[1498,6]]},"3498":{"position":[[304,5],[352,5]]},"3584":{"position":[[611,5]]},"3627":{"position":[[661,5]]}}}],["question",{"_index":1137,"t":{"54":{"position":[[294,9]]},"150":{"position":[[115,9]]},"158":{"position":[[126,9]]},"180":{"position":[[304,9]]},"184":{"position":[[126,8]]},"206":{"position":[[854,9]]},"242":{"position":[[356,8]]},"317":{"position":[[1031,10]]},"337":{"position":[[373,9]]},"397":{"position":[[218,8]]},"425":{"position":[[331,8]]},"1217":{"position":[[232,9]]},"1221":{"position":[[218,8]]},"1251":{"position":[[408,8]]},"2396":{"position":[[234,9]]},"2420":{"position":[[10,8]]},"2470":{"position":[[218,8]]},"2500":{"position":[[408,8]]},"3395":{"position":[[571,8]]}}}],["question—what",{"_index":2663,"t":{"260":{"position":[[748,13]]}}}],["queu",{"_index":937,"t":{"32":{"position":[[967,7]]},"232":{"position":[[244,6]]},"270":{"position":[[1268,6],[1315,7],[1449,6]]},"363":{"position":[[163,7]]},"409":{"position":[[1265,7]]},"471":{"position":[[438,6]]},"1233":{"position":[[1265,7]]},"1321":{"position":[[162,7]]},"1680":{"position":[[61,7]]},"1692":{"position":[[240,7]]},"1700":{"position":[[165,8]]},"1719":{"position":[[142,6]]},"2436":{"position":[[166,7]]},"2482":{"position":[[1265,7]]},"2512":{"position":[[907,7]]},"2844":{"position":[[61,7]]},"2856":{"position":[[240,7]]},"2864":{"position":[[150,8],[565,6]]},"2883":{"position":[[142,6],[659,6]]}}}],["queue",{"_index":650,"t":{"16":{"position":[[3288,5],[3450,6]]},"218":{"position":[[654,6]]},"232":{"position":[[86,5]]},"236":{"position":[[380,5],[445,6],[574,6],[680,6]]},"240":{"position":[[330,5]]},"270":{"position":[[1340,5],[1934,5],[2654,5]]},"471":{"position":[[384,5]]},"908":{"position":[[40,5]]},"1521":{"position":[[69,5],[192,6]]},"1700":{"position":[[68,5],[373,5],[451,5]]},"1723":{"position":[[322,7],[405,6],[547,6]]},"2025":{"position":[[40,5]]},"2622":{"position":[[5462,7]]},"2704":{"position":[[69,5],[192,6]]},"2708":{"position":[[1790,6]]},"2844":{"position":[[426,5]]},"2862":{"position":[[427,5]]},"2864":{"position":[[53,5],[358,5],[454,5]]},"2889":{"position":[[450,5],[469,5],[568,6],[717,5]]},"3214":{"position":[[40,5]]},"3633":{"position":[[5462,7]]}}}],["queue_engin",{"_index":4953,"t":{"1700":{"position":[[312,15]]},"2864":{"position":[[297,15]]}}}],["queued/rat",{"_index":3058,"t":{"437":{"position":[[887,11]]},"1263":{"position":[[891,11]]}}}],["queueing/timeouts/error",{"_index":4317,"t":{"1090":{"position":[[467,25]]},"2185":{"position":[[477,25]]},"3462":{"position":[[512,25]]}}}],["quic",{"_index":1215,"t":{"60":{"position":[[1659,4]]},"94":{"position":[[536,4],[973,4],[1354,4],[1381,4],[1555,5],[1683,5],[2108,4],[2292,4],[2314,4],[2816,4],[2841,4]]},"98":{"position":[[59,4]]},"100":{"position":[[761,4]]},"102":{"position":[[89,4]]},"104":{"position":[[528,4],[611,4]]},"106":{"position":[[64,4],[780,4],[1153,4]]},"108":{"position":[[320,4],[718,4],[800,4],[883,5],[1119,4],[1133,4],[1188,4],[1287,4]]},"110":{"position":[[28,4],[794,4]]},"116":{"position":[[152,4]]},"134":{"position":[[106,4],[196,5]]},"144":{"position":[[111,4]]},"2392":{"position":[[899,4]]},"3612":{"position":[[120,4],[926,4]]}}}],["quic.listen",{"_index":1657,"t":{"106":{"position":[[118,13]]}}}],["quic.listenaddr(s.config.listenaddr",{"_index":1658,"t":{"106":{"position":[[284,36]]}}}],["quic.receivestream",{"_index":1724,"t":{"110":{"position":[[1823,19],[2934,19]]}}}],["quic.sess",{"_index":1670,"t":{"106":{"position":[[701,13]]},"110":{"position":[[1293,13],[2118,13],[2279,13]]},"112":{"position":[[428,13]]}}}],["quick",{"_index":1477,"t":{"84":{"position":[[570,5]]},"228":{"position":[[10,5]]},"579":{"position":[[14,5]]},"824":{"position":[[667,5]]},"1505":{"position":[[2145,5]]},"1547":{"position":[[14,5]]},"1597":{"position":[[18,5]]},"1865":{"position":[[943,5]]},"2686":{"position":[[2160,5]]},"2708":{"position":[[2281,5]]},"2752":{"position":[[14,5]]},"2780":{"position":[[18,5]]},"3096":{"position":[[943,5]]}}}],["quickli",{"_index":1465,"t":{"82":{"position":[[1640,7]]},"152":{"position":[[575,7]]},"186":{"position":[[325,7]]},"471":{"position":[[772,7]]},"477":{"position":[[2750,7],[5927,7]]},"1094":{"position":[[977,7]]},"1505":{"position":[[6070,7]]},"1649":{"position":[[271,7]]},"1767":{"position":[[27,7]]},"2189":{"position":[[1383,7]]},"2512":{"position":[[947,7]]},"2686":{"position":[[6135,7]]},"2901":{"position":[[271,7]]},"2972":{"position":[[27,7]]},"3492":{"position":[[1383,7]]}}}],["quickstart",{"_index":2109,"t":{"186":{"position":[[229,10]]},"220":{"position":[[207,10]]},"1505":{"position":[[0,10]]},"2686":{"position":[[0,10]]}}}],["quictransport",{"_index":1501,"t":{"92":{"position":[[150,13]]},"94":{"position":[[191,13],[2419,13],[2884,13]]}}}],["quictransport('qu",{"_index":1520,"t":{"94":{"position":[[1768,19]]}}}],["quit",{"_index":321,"t":{"8":{"position":[[511,5]]},"98":{"position":[[1573,5]]},"280":{"position":[[3149,5]]},"323":{"position":[[162,5]]},"331":{"position":[[788,6]]},"1147":{"position":[[958,6]]},"2260":{"position":[[967,6]]},"2608":{"position":[[330,5]]},"3363":{"position":[[210,5]]},"3538":{"position":[[2012,5]]},"3610":{"position":[[967,6]]},"3629":{"position":[[789,6]]}}}],["quit\\r\\n",{"_index":3191,"t":{"473":{"position":[[1248,8]]},"1195":{"position":[[797,8]]},"2096":{"position":[[797,8]]},"3281":{"position":[[797,8]]}}}],["quorum",{"_index":4398,"t":{"1193":{"position":[[1315,6]]},"2092":{"position":[[968,6],[1331,6]]},"3277":{"position":[[968,6],[1331,6]]}}}],["r",{"_index":1252,"t":{"64":{"position":[[529,1],[652,1],[693,2]]}}}],["r'/centrifugo/connect",{"_index":4206,"t":{"1017":{"position":[[5277,24]]},"2354":{"position":[[5400,24]]},"3415":{"position":[[5429,24]]}}}],["r.withcontext(newctx",{"_index":1260,"t":{"64":{"position":[[656,21]]}}}],["rabbitmq",{"_index":616,"t":{"16":{"position":[[1989,8],[3158,8],[3464,10],[3516,8],[4344,8]]},"218":{"position":[[490,9]]},"240":{"position":[[445,9]]}}}],["rabbitx",{"_index":2735,"t":{"280":{"position":[[152,7],[274,7],[495,7],[518,7],[592,7],[3121,7]]}}}],["raci",{"_index":3059,"t":{"437":{"position":[[1043,4]]},"1263":{"position":[[1047,4]]},"2512":{"position":[[1083,4]]}}}],["radic",{"_index":2963,"t":{"335":{"position":[[563,7]]},"347":{"position":[[413,9]]},"525":{"position":[[702,7]]},"640":{"position":[[304,9]]},"973":{"position":[[498,9]]},"1043":{"position":[[27,7]]},"1279":{"position":[[413,9]]},"1355":{"position":[[710,7]]},"1591":{"position":[[586,9]]},"1983":{"position":[[498,9]]},"2382":{"position":[[27,7]]},"2402":{"position":[[441,7]]},"2526":{"position":[[695,7]]},"2784":{"position":[[586,9]]},"3142":{"position":[[498,9]]},"3443":{"position":[[27,7]]}}}],["raft",{"_index":4421,"t":{"1203":{"position":[[1513,5],[1936,4]]},"1205":{"position":[[386,4],[433,4]]},"2106":{"position":[[1513,5],[1936,4]]},"2108":{"position":[[386,4],[433,4]]},"3289":{"position":[[1513,5],[1936,4]]},"3291":{"position":[[386,4],[433,4]]}}}],["rail",{"_index":1176,"t":{"58":{"position":[[552,6]]},"525":{"position":[[415,6]]},"1355":{"position":[[421,6]]},"2526":{"position":[[421,6]]}}}],["rais",{"_index":406,"t":{"10":{"position":[[434,5]]}}}],["ram",{"_index":143,"t":{"2":{"position":[[2190,3],[2261,3]]},"8":{"position":[[746,3]]},"12":{"position":[[609,3]]},"14":{"position":[[1599,3]]},"84":{"position":[[628,3]]},"399":{"position":[[63,3],[193,4],[225,3],[399,3]]},"1223":{"position":[[63,3],[193,4],[225,3],[399,3]]},"2472":{"position":[[63,3],[193,4],[225,3],[399,3]]}}}],["ran",{"_index":1872,"t":{"130":{"position":[[551,3]]},"192":{"position":[[3342,3]]},"479":{"position":[[276,3]]}}}],["random",{"_index":753,"t":{"18":{"position":[[604,6]]},"128":{"position":[[1641,10]]},"1162":{"position":[[924,6]]},"1421":{"position":[[480,6]]},"2342":{"position":[[924,6]]},"3627":{"position":[[924,6]]}}}],["randomli",{"_index":4787,"t":{"1505":{"position":[[6512,8]]},"2686":{"position":[[6589,8]]}}}],["rang",{"_index":981,"t":{"34":{"position":[[1210,5]]},"110":{"position":[[4472,5]]},"724":{"position":[[119,5]]},"768":{"position":[[117,5]]},"951":{"position":[[747,5]]},"953":{"position":[[753,5]]},"971":{"position":[[1772,5]]},"1027":{"position":[[213,5]]},"1029":{"position":[[243,5]]},"1921":{"position":[[747,5]]},"1923":{"position":[[753,5]]},"1959":{"position":[[116,5]]},"1981":{"position":[[1772,5]]},"2360":{"position":[[4622,5]]},"2366":{"position":[[187,5]]},"2368":{"position":[[223,5],[277,5],[340,5],[478,5]]},"2648":{"position":[[33,5],[130,5]]},"2650":{"position":[[73,5]]},"2652":{"position":[[88,5],[163,5]]},"3004":{"position":[[116,5]]},"3060":{"position":[[565,5]]},"3140":{"position":[[1772,5]]},"3148":{"position":[[747,5]]},"3150":{"position":[[753,5]]},"3421":{"position":[[4625,5]]},"3427":{"position":[[187,5]]},"3429":{"position":[[223,5],[277,5],[340,5],[478,5]]},"3659":{"position":[[33,5],[130,5]]},"3661":{"position":[[73,5]]},"3663":{"position":[[88,5],[163,5]]}}}],["rare",{"_index":2784,"t":{"280":{"position":[[2978,4]]},"481":{"position":[[1790,4]]},"957":{"position":[[5,4]]},"1927":{"position":[[5,4]]},"2512":{"position":[[483,4]]},"2708":{"position":[[2535,4]]},"3154":{"position":[[5,4]]}}}],["rate",{"_index":121,"t":{"2":{"position":[[1776,4]]},"16":{"position":[[3280,4]]},"18":{"position":[[635,4],[799,6]]},"262":{"position":[[1492,6],[1513,6]]},"397":{"position":[[76,5]]},"479":{"position":[[2252,4],[2334,4],[5628,4]]},"563":{"position":[[560,7],[632,7],[733,7],[1497,7]]},"712":{"position":[[206,4]]},"722":{"position":[[107,4]]},"1160":{"position":[[525,4]]},"1221":{"position":[[76,5]]},"1519":{"position":[[283,4]]},"1666":{"position":[[990,7],[1053,7],[1088,7],[1153,7],[1213,7],[1318,7]]},"1668":{"position":[[886,7],[951,7],[1010,7],[1115,7]]},"1670":{"position":[[1367,7],[1432,7],[1491,7],[1596,7]]},"1672":{"position":[[1510,7]]},"1929":{"position":[[591,4]]},"1947":{"position":[[203,4]]},"1957":{"position":[[119,4]]},"2340":{"position":[[525,4]]},"2470":{"position":[[76,5]]},"2512":{"position":[[915,4]]},"2702":{"position":[[283,4]]},"2708":{"position":[[1757,4]]},"2712":{"position":[[23,4],[442,7]]},"2714":{"position":[[22,4]]},"2718":{"position":[[157,4],[182,4]]},"2760":{"position":[[303,4]]},"2830":{"position":[[10,4],[161,4],[263,4],[993,7],[1056,7],[1091,7],[1156,7],[1216,7],[1321,7],[1862,4]]},"2832":{"position":[[16,4],[72,4],[100,4],[218,4],[264,4],[316,4],[454,4],[512,4],[553,4],[892,7],[957,7],[1016,7],[1121,7]]},"2834":{"position":[[17,4],[75,4],[333,4],[687,4],[825,4],[947,4],[1001,4],[1373,7],[1438,7],[1497,7],[1602,7],[1649,4],[1820,4],[1954,4],[2184,4]]},"2836":{"position":[[35,4],[334,4],[1510,7]]},"2889":{"position":[[293,4]]},"2992":{"position":[[203,4]]},"3002":{"position":[[119,4]]},"3156":{"position":[[591,4]]},"3324":{"position":[[256,4],[504,4]]},"3625":{"position":[[525,4]]}}}],["rate/s",{"_index":3447,"t":{"479":{"position":[[5283,10]]}}}],["rate_limit",{"_index":5476,"t":{"2712":{"position":[[686,10]]}}}],["rate_limit_test",{"_index":5469,"t":{"2712":{"position":[[404,18]]}}}],["ratelimit.new(100",{"_index":3407,"t":{"479":{"position":[[2408,18]]}}}],["ratelimit.new(100000",{"_index":3410,"t":{"479":{"position":[[2538,21]]}}}],["ratelimit.per(time.millisecond))for",{"_index":3408,"t":{"479":{"position":[[2427,35]]}}}],["raw",{"_index":681,"t":{"16":{"position":[[4555,3]]},"270":{"position":[[631,3],[2281,3]]},"995":{"position":[[128,3]]},"1017":{"position":[[3897,3],[4155,3]]},"1019":{"position":[[1562,3]]},"1023":{"position":[[3066,3],[3283,3]]},"1025":{"position":[[2974,3]]},"1160":{"position":[[28,3],[595,3]]},"1162":{"position":[[542,3]]},"1684":{"position":[[332,3]]},"1692":{"position":[[967,3]]},"1749":{"position":[[128,3]]},"2340":{"position":[[28,3],[595,3]]},"2342":{"position":[[542,3]]},"2354":{"position":[[4014,3],[4272,3]]},"2356":{"position":[[1562,3]]},"2360":{"position":[[2998,3],[3215,3]]},"2362":{"position":[[2974,3]]},"2364":{"position":[[3029,3]]},"2848":{"position":[[332,3]]},"2856":{"position":[[967,3]]},"2954":{"position":[[128,3]]},"3415":{"position":[[4043,3],[4301,3]]},"3417":{"position":[[1562,3]]},"3421":{"position":[[2801,3],[3018,3]]},"3423":{"position":[[2974,3]]},"3425":{"position":[[3021,3]]},"3625":{"position":[[28,3],[595,3]]},"3627":{"position":[[542,3]]}}}],["rawpixel.com",{"_index":2857,"t":{"305":{"position":[[847,12]]}}}],["rbac",{"_index":1914,"t":{"138":{"position":[[567,4]]}}}],["rc.2",{"_index":3244,"t":{"475":{"position":[[4465,5]]}}}],["rdb",{"_index":2998,"t":{"347":{"position":[[1273,3]]},"1279":{"position":[[1278,3]]}}}],["re",{"_index":541,"t":{"14":{"position":[[376,2]]},"20":{"position":[[89,2]]},"74":{"position":[[1779,2]]},"218":{"position":[[680,2]]},"240":{"position":[[152,2]]},"246":{"position":[[727,4],[2001,4],[2525,4]]},"250":{"position":[[1669,4]]},"272":{"position":[[333,2]]},"283":{"position":[[550,2]]},"315":{"position":[[587,2]]},"473":{"position":[[1785,2]]},"475":{"position":[[1164,2],[3944,2],[6614,2]]},"477":{"position":[[1134,3]]},"511":{"position":[[669,2]]},"973":{"position":[[1399,2]]},"1293":{"position":[[607,2]]},"1439":{"position":[[671,2]]},"1682":{"position":[[333,2]]},"1983":{"position":[[1571,2]]},"2420":{"position":[[494,2]]},"2428":{"position":[[648,2]]},"2630":{"position":[[228,2]]},"2632":{"position":[[3656,2]]},"2846":{"position":[[333,2]]},"2974":{"position":[[633,2]]},"3142":{"position":[[1571,2]]},"3365":{"position":[[3410,2],[3443,2]]},"3639":{"position":[[810,3],[1117,2]]},"3641":{"position":[[228,2]]},"3643":{"position":[[3656,2]]},"3655":{"position":[[862,3],[1301,2]]}}}],["re_path('room/(?p[a",{"_index":2274,"t":{"194":{"position":[[2361,30]]},"200":{"position":[[449,30]]}}}],["re_pathfrom",{"_index":2273,"t":{"194":{"position":[[2282,11]]},"200":{"position":[[370,11]]}}}],["reach",{"_index":958,"t":{"34":{"position":[[693,8],[1511,8]]},"333":{"position":[[783,5]]},"417":{"position":[[39,8]]},"682":{"position":[[584,5]]},"971":{"position":[[1255,8],[2073,8]]},"1027":{"position":[[437,5]]},"1241":{"position":[[39,8]]},"1365":{"position":[[281,5]]},"1721":{"position":[[757,5]]},"1797":{"position":[[323,5]]},"1799":{"position":[[311,5]]},"1827":{"position":[[1322,5]]},"1981":{"position":[[1255,8],[2073,8]]},"2364":{"position":[[1206,5]]},"2366":{"position":[[396,5]]},"2490":{"position":[[39,8]]},"2536":{"position":[[281,5]]},"2762":{"position":[[560,5]]},"2887":{"position":[[757,5]]},"3140":{"position":[[1255,8],[2073,8]]},"3324":{"position":[[937,8],[3360,8]]},"3395":{"position":[[642,5]]},"3425":{"position":[[1179,5]]},"3427":{"position":[[396,5]]}}}],["reached\")}conns.add(conn",{"_index":428,"t":{"10":{"position":[[888,25]]}}}],["react",{"_index":1302,"t":{"66":{"position":[[1915,8]]},"82":{"position":[[958,5]]},"178":{"position":[[142,5]]},"208":{"position":[[442,5]]},"214":{"position":[[726,5]]},"216":{"position":[[24,5],[212,5],[242,6],[598,5],[637,6],[693,7],[1177,6],[1213,5],[1420,6],[1574,7],[4425,5]]},"280":{"position":[[3243,5]]},"293":{"position":[[1537,5]]},"435":{"position":[[114,5]]},"455":{"position":[[335,5]]},"457":{"position":[[238,5]]},"1035":{"position":[[72,5]]},"1063":{"position":[[398,5]]},"1261":{"position":[[114,5]]},"1307":{"position":[[335,5]]},"1309":{"position":[[238,5]]},"1678":{"position":[[443,6]]},"2166":{"position":[[40,5]]},"2374":{"position":[[72,5]]},"2510":{"position":[[114,5]]},"2594":{"position":[[340,5]]},"2596":{"position":[[238,5]]},"2842":{"position":[[443,6]]},"3395":{"position":[[97,5]]},"3435":{"position":[[72,5]]}}}],["react';import",{"_index":2433,"t":{"216":{"position":[[1456,14]]}}}],["react'import",{"_index":2415,"t":{"216":{"position":[[609,13]]}}}],["react.strictmod",{"_index":2427,"t":{"216":{"position":[[1013,18],[1040,19]]}}}],["react/vue/whatev",{"_index":1968,"t":{"156":{"position":[[419,18]]}}}],["reactcd",{"_index":2407,"t":{"216":{"position":[[119,7]]}}}],["reactdom",{"_index":2416,"t":{"216":{"position":[[623,8]]}}}],["reaction",{"_index":3515,"t":{"497":{"position":[[291,8]]},"1391":{"position":[[288,8]]},"1405":{"position":[[3632,8]]},"2152":{"position":[[171,8]]},"2562":{"position":[[291,8]]},"3381":{"position":[[171,8]]}}}],["reactkeycloakprovid",{"_index":2418,"t":{"216":{"position":[[664,21],[961,22],[1060,26],[1228,21]]}}}],["read",{"_index":200,"t":{"2":{"position":[[3454,4]]},"4":{"position":[[838,8]]},"8":{"position":[[963,4]]},"12":{"position":[[254,4],[952,4]]},"48":{"position":[[8,7]]},"56":{"position":[[211,6]]},"66":{"position":[[1391,4]]},"94":{"position":[[153,4],[2920,4]]},"108":{"position":[[274,7]]},"110":{"position":[[2328,4],[3023,4]]},"112":{"position":[[71,7]]},"136":{"position":[[1407,4]]},"178":{"position":[[192,4],[248,4],[273,4]]},"180":{"position":[[320,7]]},"186":{"position":[[182,4]]},"206":{"position":[[875,7]]},"278":{"position":[[630,7]]},"285":{"position":[[540,7]]},"425":{"position":[[600,5],[684,4],[790,4],[802,4]]},"431":{"position":[[188,4]]},"471":{"position":[[2468,4]]},"475":{"position":[[2633,7]]},"477":{"position":[[2477,7]]},"479":{"position":[[6390,4]]},"483":{"position":[[2107,4]]},"505":{"position":[[220,4]]},"726":{"position":[[482,7]]},"744":{"position":[[86,4]]},"776":{"position":[[1115,4]]},"816":{"position":[[184,4]]},"818":{"position":[[471,4]]},"898":{"position":[[85,4]]},"1154":{"position":[[55,4]]},"1207":{"position":[[184,4]]},"1251":{"position":[[677,5],[761,4],[867,4],[879,4]]},"1257":{"position":[[188,4]]},"1359":{"position":[[639,4]]},"1371":{"position":[[141,4]]},"1399":{"position":[[220,4]]},"1405":{"position":[[500,7],[1891,4]]},"1505":{"position":[[3355,7],[10396,4]]},"1791":{"position":[[1115,4]]},"1797":{"position":[[303,4]]},"1799":{"position":[[291,4]]},"1825":{"position":[[530,4]]},"1827":{"position":[[1302,4]]},"1835":{"position":[[184,4]]},"1965":{"position":[[552,4]]},"2011":{"position":[[85,4]]},"2104":{"position":[[239,4]]},"2110":{"position":[[184,4]]},"2154":{"position":[[215,7],[1689,4]]},"2334":{"position":[[55,4]]},"2500":{"position":[[677,5],[761,4],[867,4],[879,4]]},"2506":{"position":[[188,4]]},"2530":{"position":[[639,4]]},"2542":{"position":[[141,4]]},"2570":{"position":[[229,4]]},"2660":{"position":[[174,4]]},"2686":{"position":[[3372,7],[10521,4]]},"3010":{"position":[[552,4]]},"3054":{"position":[[530,4]]},"3064":{"position":[[184,4]]},"3200":{"position":[[85,4]]},"3293":{"position":[[184,4]]},"3363":{"position":[[223,4]]},"3383":{"position":[[215,7],[1689,4]]},"3619":{"position":[[55,4]]},"3671":{"position":[[174,4]]}}}],["read/writ",{"_index":3392,"t":{"479":{"position":[[776,10],[6703,10]]}}}],["readabl",{"_index":1850,"t":{"126":{"position":[[122,8]]},"130":{"position":[[1312,8]]},"148":{"position":[[190,8]]},"333":{"position":[[508,8]]},"1162":{"position":[[111,8],[467,8]]},"1371":{"position":[[577,8]]},"1405":{"position":[[1202,9]]},"2154":{"position":[[892,9]]},"2158":{"position":[[172,8]]},"2168":{"position":[[534,8]]},"2342":{"position":[[111,8],[467,8]]},"2442":{"position":[[200,8]]},"2542":{"position":[[577,8]]},"2622":{"position":[[4617,8]]},"2632":{"position":[[4234,8]]},"3383":{"position":[[892,9]]},"3387":{"position":[[172,8]]},"3397":{"position":[[534,8]]},"3627":{"position":[[111,8],[467,8]]},"3633":{"position":[[4617,8]]},"3643":{"position":[[4234,8]]}}}],["readablestream",{"_index":1830,"t":{"124":{"position":[[2028,14],[2320,15]]}}}],["reader",{"_index":825,"t":{"22":{"position":[[45,6]]},"82":{"position":[[31,7]]},"108":{"position":[[1395,6]]},"110":{"position":[[3074,6]]},"156":{"position":[[758,6]]},"244":{"position":[[62,6]]},"280":{"position":[[3793,7]]},"483":{"position":[[909,7]]},"908":{"position":[[74,6]]},"2025":{"position":[[74,6]]},"3214":{"position":[[74,6]]}}}],["reader.read(buf",{"_index":1745,"t":{"110":{"position":[[3496,16]]}}}],["readi",{"_index":244,"t":{"4":{"position":[[536,5]]},"14":{"position":[[468,5]]},"40":{"position":[[212,5]]},"76":{"position":[[1361,5]]},"90":{"position":[[458,5]]},"116":{"position":[[321,5]]},"124":{"position":[[1858,5]]},"160":{"position":[[544,5]]},"218":{"position":[[605,5]]},"367":{"position":[[154,5]]},"437":{"position":[[2137,5]]},"509":{"position":[[560,5]]},"525":{"position":[[333,5]]},"1203":{"position":[[645,5],[1750,5]]},"1207":{"position":[[873,5]]},"1263":{"position":[[2141,5]]},"1325":{"position":[[156,5]]},"1355":{"position":[[339,5]]},"1409":{"position":[[76,5]]},"1437":{"position":[[570,5]]},"2106":{"position":[[645,5],[1750,5]]},"2110":{"position":[[873,5]]},"2416":{"position":[[88,5]]},"2440":{"position":[[157,5]]},"2512":{"position":[[2383,5]]},"2526":{"position":[[338,5]]},"2606":{"position":[[585,5]]},"2708":{"position":[[389,5]]},"3289":{"position":[[645,5],[1750,5]]},"3293":{"position":[[873,5]]},"3367":{"position":[[1948,5],[4346,5]]}}}],["readm",{"_index":1491,"t":{"88":{"position":[[186,6]]},"168":{"position":[[209,6]]},"283":{"position":[[442,6]]},"477":{"position":[[335,6]]},"515":{"position":[[82,6]]},"1293":{"position":[[499,6]]},"1443":{"position":[[82,6]]},"2168":{"position":[[998,7]]},"2428":{"position":[[532,6]]},"2612":{"position":[[82,6]]},"3397":{"position":[[998,7]]}}}],["real",{"_index":123,"t":{"2":{"position":[[1793,4]]},"4":{"position":[[598,4],[795,4]]},"16":{"position":[[322,4],[1393,4],[2379,4],[3188,4],[3562,4],[3975,4],[4374,4],[5190,4],[7309,4]]},"22":{"position":[[106,4]]},"24":{"position":[[314,4],[454,4]]},"26":{"position":[[369,4],[489,4],[549,4],[1600,4],[1684,4],[1740,4]]},"32":{"position":[[223,4],[1107,4]]},"34":{"position":[[783,4]]},"56":{"position":[[89,4]]},"58":{"position":[[637,4],[1279,4]]},"60":{"position":[[39,4],[405,4],[430,4],[490,4],[1019,4],[1324,4],[2226,4]]},"66":{"position":[[418,4],[543,4]]},"76":{"position":[[2077,4]]},"90":{"position":[[172,4],[712,4]]},"94":{"position":[[1130,4]]},"118":{"position":[[444,4],[843,4],[1026,4]]},"120":{"position":[[667,4]]},"122":{"position":[[725,4],[1031,4]]},"124":{"position":[[610,4],[2439,4]]},"134":{"position":[[1010,4]]},"136":{"position":[[419,4]]},"148":{"position":[[707,4]]},"152":{"position":[[1267,4]]},"154":{"position":[[97,4],[297,4]]},"158":{"position":[[77,4],[300,4],[967,4]]},"170":{"position":[[863,4]]},"172":{"position":[[1083,4]]},"184":{"position":[[76,4],[652,4],[795,4]]},"196":{"position":[[1539,4]]},"198":{"position":[[935,4],[1360,4]]},"200":{"position":[[2037,4],[2103,4]]},"216":{"position":[[4325,4]]},"218":{"position":[[949,4]]},"232":{"position":[[296,4],[989,4],[1062,4],[1182,4],[1268,4]]},"240":{"position":[[675,4]]},"242":{"position":[[25,4],[418,4],[1266,4]]},"244":{"position":[[492,4]]},"246":{"position":[[2289,4]]},"250":{"position":[[900,4],[1237,4]]},"258":{"position":[[76,4],[466,4]]},"260":{"position":[[61,4],[361,4],[542,4],[1341,4]]},"264":{"position":[[1438,4]]},"270":{"position":[[2861,4],[2912,4]]},"274":{"position":[[548,4]]},"280":{"position":[[1155,4],[1373,4],[1889,4],[3460,4]]},"283":{"position":[[272,4]]},"285":{"position":[[48,4],[279,4]]},"287":{"position":[[170,4],[258,4],[851,4]]},"289":{"position":[[342,4]]},"311":{"position":[[460,4],[850,4],[1033,4]]},"317":{"position":[[39,4]]},"335":{"position":[[349,4]]},"341":{"position":[[347,4],[958,4],[1116,4],[1164,4],[1400,4],[1558,4],[1606,4]]},"345":{"position":[[755,4]]},"361":{"position":[[425,4]]},"363":{"position":[[424,4]]},"369":{"position":[[262,4],[403,4]]},"375":{"position":[[72,4]]},"393":{"position":[[211,4],[268,4]]},"397":{"position":[[30,4]]},"399":{"position":[[276,4]]},"425":{"position":[[53,4],[112,4],[389,4]]},"433":{"position":[[61,4],[119,4]]},"441":{"position":[[666,4]]},"465":{"position":[[101,4]]},"471":{"position":[[3115,4]]},"479":{"position":[[3824,4]]},"497":{"position":[[208,4]]},"525":{"position":[[190,4],[522,4],[634,4]]},"553":{"position":[[198,4],[841,4]]},"559":{"position":[[192,4]]},"608":{"position":[[246,4]]},"640":{"position":[[266,4]]},"672":{"position":[[456,4]]},"824":{"position":[[685,4]]},"828":{"position":[[320,4]]},"842":{"position":[[532,4]]},"858":{"position":[[314,4]]},"935":{"position":[[240,4]]},"971":{"position":[[1345,4]]},"979":{"position":[[868,4],[1122,4]]},"981":{"position":[[428,4],[672,4]]},"1013":{"position":[[426,4]]},"1017":{"position":[[4853,4]]},"1063":{"position":[[7,4]]},"1092":{"position":[[329,4]]},"1207":{"position":[[1021,4]]},"1211":{"position":[[347,4],[958,4],[1116,4],[1164,4],[1400,4],[1558,4],[1606,4]]},"1217":{"position":[[319,4]]},"1221":{"position":[[30,4]]},"1223":{"position":[[276,4]]},"1251":{"position":[[53,4],[112,4],[466,4]]},"1259":{"position":[[61,4],[119,4]]},"1267":{"position":[[666,4]]},"1277":{"position":[[755,4]]},"1293":{"position":[[244,4]]},"1319":{"position":[[441,4]]},"1321":{"position":[[428,4]]},"1325":{"position":[[339,4]]},"1327":{"position":[[381,4],[522,4],[678,4]]},"1333":{"position":[[72,4]]},"1355":{"position":[[203,4],[534,4],[642,4],[967,4],[1501,4]]},"1391":{"position":[[205,4]]},"1477":{"position":[[1016,4]]},"1505":{"position":[[383,4],[3381,4],[3684,4],[4183,4],[6708,4],[8250,4],[8782,4],[10193,4]]},"1527":{"position":[[181,4],[195,4],[341,4]]},"1571":{"position":[[706,4]]},"1591":{"position":[[266,4]]},"1593":{"position":[[192,4]]},"1603":{"position":[[545,4]]},"1666":{"position":[[135,4],[1356,4]]},"1682":{"position":[[469,4]]},"1743":{"position":[[167,4]]},"1767":{"position":[[439,4]]},"1773":{"position":[[215,4]]},"1817":{"position":[[837,4]]},"1831":{"position":[[155,4],[1049,4]]},"1843":{"position":[[314,4]]},"1859":{"position":[[885,4]]},"1865":{"position":[[194,4],[961,4]]},"1899":{"position":[[966,4]]},"1981":{"position":[[1345,4]]},"2023":{"position":[[127,4],[200,4]]},"2056":{"position":[[348,4]]},"2072":{"position":[[868,4],[1122,4]]},"2074":{"position":[[428,4],[672,4]]},"2110":{"position":[[1021,4]]},"2122":{"position":[[36,4],[455,4]]},"2124":{"position":[[257,4]]},"2126":{"position":[[70,4],[360,4]]},"2168":{"position":[[11,4],[1020,4]]},"2187":{"position":[[333,4]]},"2309":{"position":[[1058,4]]},"2350":{"position":[[426,4]]},"2354":{"position":[[4976,4]]},"2360":{"position":[[4522,4]]},"2394":{"position":[[347,4],[958,4],[1116,4],[1164,4],[1400,4],[1558,4],[1606,4]]},"2396":{"position":[[317,4]]},"2400":{"position":[[307,4],[420,4],[458,4],[1346,4],[1477,4],[1598,4],[1819,4],[2746,4]]},"2420":{"position":[[454,4]]},"2422":{"position":[[351,4],[503,4],[1133,4]]},"2424":{"position":[[1017,4]]},"2428":{"position":[[252,4]]},"2434":{"position":[[445,4]]},"2436":{"position":[[444,4]]},"2440":{"position":[[336,4]]},"2442":{"position":[[496,4],[648,4],[800,4]]},"2448":{"position":[[72,4]]},"2470":{"position":[[30,4]]},"2472":{"position":[[276,4]]},"2500":{"position":[[53,4],[112,4],[466,4]]},"2508":{"position":[[61,4],[119,4]]},"2516":{"position":[[666,4]]},"2526":{"position":[[220,4],[524,4],[630,4],[958,4],[1512,4],[1809,4]]},"2562":{"position":[[208,4]]},"2654":{"position":[[173,4]]},"2666":{"position":[[706,4]]},"2686":{"position":[[388,4],[3400,4],[3717,4],[4243,4],[6789,4],[8352,4],[8899,4],[10313,4]]},"2760":{"position":[[181,4],[195,4],[342,4]]},"2776":{"position":[[192,4]]},"2784":{"position":[[266,4]]},"2788":{"position":[[545,4]]},"2830":{"position":[[135,4],[1359,4]]},"2846":{"position":[[469,4]]},"2862":{"position":[[830,4]]},"2932":{"position":[[215,4]]},"2948":{"position":[[167,4]]},"2972":{"position":[[439,4]]},"3046":{"position":[[981,4]]},"3060":{"position":[[176,4],[1134,4]]},"3072":{"position":[[322,4],[594,4]]},"3090":{"position":[[885,4]]},"3096":{"position":[[194,4],[961,4]]},"3134":{"position":[[966,4]]},"3140":{"position":[[1345,4]]},"3212":{"position":[[100,4],[173,4]]},"3220":{"position":[[663,4]]},"3249":{"position":[[348,4]]},"3293":{"position":[[1021,4]]},"3309":{"position":[[868,4],[1122,4]]},"3311":{"position":[[428,4],[672,4]]},"3345":{"position":[[29,4],[715,4],[996,4]]},"3347":{"position":[[257,4]]},"3349":{"position":[[70,4],[358,4]]},"3365":{"position":[[631,4],[1164,4]]},"3397":{"position":[[11,4],[1020,4]]},"3411":{"position":[[426,4]]},"3415":{"position":[[5005,4]]},"3421":{"position":[[4525,4]]},"3464":{"position":[[333,4]]},"3522":{"position":[[1118,4]]},"3665":{"position":[[173,4]]}}}],["realist",{"_index":5639,"t":{"3363":{"position":[[1417,9]]}}}],["realiti",{"_index":1109,"t":{"50":{"position":[[1940,7]]},"280":{"position":[[4209,7]]},"335":{"position":[[908,7]]}}}],["realli",{"_index":765,"t":{"18":{"position":[[1392,6]]},"20":{"position":[[753,6]]},"34":{"position":[[766,6]]},"82":{"position":[[2036,6]]},"124":{"position":[[323,6]]},"128":{"position":[[1094,6]]},"132":{"position":[[298,6]]},"136":{"position":[[194,6]]},"180":{"position":[[116,6]]},"278":{"position":[[3,6]]},"280":{"position":[[4046,6]]},"301":{"position":[[878,6]]},"333":{"position":[[333,6]]},"971":{"position":[[1328,6]]},"1039":{"position":[[1106,6]]},"1355":{"position":[[456,6]]},"1477":{"position":[[633,6]]},"1571":{"position":[[726,6]]},"1771":{"position":[[2087,6]]},"1861":{"position":[[462,6]]},"1885":{"position":[[271,6]]},"1981":{"position":[[1328,6]]},"2191":{"position":[[847,6]]},"2309":{"position":[[680,6]]},"2378":{"position":[[1106,6]]},"2666":{"position":[[726,6]]},"2930":{"position":[[2087,6]]},"3092":{"position":[[462,6]]},"3116":{"position":[[271,6]]},"3140":{"position":[[1328,6]]},"3363":{"position":[[289,6],[636,6]]},"3365":{"position":[[3282,6]]},"3439":{"position":[[1106,6]]},"3462":{"position":[[139,6]]},"3466":{"position":[[847,6]]},"3522":{"position":[[740,6]]}}}],["realm",{"_index":2380,"t":{"212":{"position":[[312,5]]},"216":{"position":[[860,6]]}}}],["reason",{"_index":5,"t":{"2":{"position":[[74,10]]},"16":{"position":[[1449,10],[2818,10]]},"18":{"position":[[1602,10]]},"38":{"position":[[1788,6]]},"60":{"position":[[890,6]]},"66":{"position":[[2026,6]]},"84":{"position":[[195,10]]},"94":{"position":[[2785,6]]},"106":{"position":[[874,7]]},"132":{"position":[[921,6]]},"144":{"position":[[1944,10]]},"156":{"position":[[719,8]]},"202":{"position":[[1213,10]]},"250":{"position":[[251,6],[1801,7]]},"291":{"position":[[46,10],[1074,7]]},"295":{"position":[[1740,10]]},"315":{"position":[[223,7]]},"317":{"position":[[648,10]]},"331":{"position":[[959,7]]},"347":{"position":[[561,10]]},"409":{"position":[[208,10]]},"411":{"position":[[556,7]]},"417":{"position":[[17,6]]},"425":{"position":[[320,10]]},"431":{"position":[[836,10]]},"435":{"position":[[359,9]]},"437":{"position":[[499,6],[1265,10],[1780,10]]},"439":{"position":[[213,7]]},"441":{"position":[[44,10],[692,10]]},"475":{"position":[[7030,6]]},"479":{"position":[[1751,10],[5235,10],[6441,10]]},"483":{"position":[[689,10]]},"628":{"position":[[444,10]]},"632":{"position":[[382,6],[644,10],[893,6]]},"636":{"position":[[917,10]]},"638":{"position":[[976,10]]},"648":{"position":[[422,10]]},"652":{"position":[[280,6],[507,10],[756,6]]},"656":{"position":[[477,10]]},"692":{"position":[[54,6]]},"726":{"position":[[78,7],[527,8]]},"730":{"position":[[12,7]]},"732":{"position":[[12,7]]},"734":{"position":[[12,7]]},"736":{"position":[[12,7]]},"738":{"position":[[12,7]]},"740":{"position":[[12,7]]},"742":{"position":[[12,7]]},"744":{"position":[[12,7]]},"746":{"position":[[12,7]]},"748":{"position":[[12,7]]},"750":{"position":[[12,7]]},"752":{"position":[[12,7]]},"754":{"position":[[12,7]]},"776":{"position":[[838,10]]},"836":{"position":[[253,10]]},"840":{"position":[[607,10]]},"842":{"position":[[134,6],[349,10]]},"902":{"position":[[246,10]]},"937":{"position":[[265,10]]},"945":{"position":[[326,7]]},"955":{"position":[[129,10]]},"973":{"position":[[733,10]]},"1029":{"position":[[174,9],[503,6],[739,7]]},"1071":{"position":[[472,6]]},"1090":{"position":[[269,6]]},"1185":{"position":[[599,10]]},"1189":{"position":[[1481,10]]},"1233":{"position":[[208,10]]},"1235":{"position":[[556,7]]},"1241":{"position":[[17,6]]},"1251":{"position":[[397,10]]},"1257":{"position":[[836,10]]},"1261":{"position":[[359,9]]},"1263":{"position":[[503,6],[1269,10],[1784,10]]},"1265":{"position":[[213,7]]},"1267":{"position":[[44,10],[692,10]]},"1279":{"position":[[561,10]]},"1373":{"position":[[177,7]]},"1403":{"position":[[775,6]]},"1421":{"position":[[100,6],[139,6],[271,9],[332,6],[529,6]]},"1431":{"position":[[121,6]]},"1465":{"position":[[643,6],[672,6]]},"1467":{"position":[[468,6]]},"1573":{"position":[[106,10]]},"1587":{"position":[[119,10]]},"1627":{"position":[[444,10]]},"1631":{"position":[[382,6],[644,10],[893,6]]},"1636":{"position":[[917,10]]},"1639":{"position":[[1022,10]]},"1643":{"position":[[422,10]]},"1647":{"position":[[280,6],[507,10],[756,6]]},"1652":{"position":[[477,10]]},"1672":{"position":[[361,7]]},"1735":{"position":[[54,6]]},"1791":{"position":[[838,10]]},"1853":{"position":[[253,10]]},"1857":{"position":[[708,10]]},"1859":{"position":[[254,6],[485,10]]},"1925":{"position":[[129,10]]},"1961":{"position":[[78,7]]},"1963":{"position":[[315,6]]},"1965":{"position":[[1103,6]]},"1983":{"position":[[730,10]]},"2015":{"position":[[246,10]]},"2029":{"position":[[224,10]]},"2058":{"position":[[265,10]]},"2066":{"position":[[326,7]]},"2082":{"position":[[586,10]]},"2086":{"position":[[1629,10]]},"2134":{"position":[[472,6]]},"2158":{"position":[[188,7]]},"2185":{"position":[[275,6]]},"2297":{"position":[[609,6],[638,6]]},"2299":{"position":[[434,6]]},"2354":{"position":[[6277,9],[6352,6]]},"2360":{"position":[[4527,6]]},"2368":{"position":[[154,9],[176,7],[653,6],[939,7]]},"2392":{"position":[[2796,10]]},"2402":{"position":[[598,10]]},"2482":{"position":[[208,10]]},"2484":{"position":[[556,7]]},"2490":{"position":[[17,6]]},"2500":{"position":[[397,10]]},"2506":{"position":[[836,10]]},"2510":{"position":[[359,9]]},"2512":{"position":[[596,6],[1299,10],[2026,10]]},"2514":{"position":[[213,7]]},"2516":{"position":[[44,10],[692,10]]},"2544":{"position":[[177,7]]},"2622":{"position":[[4633,6],[4701,6]]},"2632":{"position":[[4250,6],[4318,6]]},"2650":{"position":[[364,8]]},"2652":{"position":[[361,8]]},"2668":{"position":[[106,10]]},"2682":{"position":[[119,10]]},"2814":{"position":[[444,10]]},"2818":{"position":[[382,6],[644,10],[893,6]]},"2823":{"position":[[881,10]]},"2826":{"position":[[986,10]]},"2836":{"position":[[361,7]]},"2883":{"position":[[1536,10]]},"2895":{"position":[[422,10]]},"2899":{"position":[[280,6],[507,10],[756,6]]},"2904":{"position":[[441,10]]},"2924":{"position":[[54,6]]},"3006":{"position":[[78,7]]},"3008":{"position":[[315,6]]},"3010":{"position":[[1103,6]]},"3020":{"position":[[893,10]]},"3070":{"position":[[781,9]]},"3082":{"position":[[253,10]]},"3086":{"position":[[588,10]]},"3088":{"position":[[708,10]]},"3090":{"position":[[254,6],[485,10]]},"3142":{"position":[[730,10]]},"3152":{"position":[[129,10]]},"3204":{"position":[[246,10]]},"3218":{"position":[[224,10]]},"3253":{"position":[[265,10]]},"3261":{"position":[[326,7]]},"3267":{"position":[[586,10]]},"3271":{"position":[[1595,10]]},"3337":{"position":[[472,6]]},"3365":{"position":[[2040,6],[2167,6]]},"3387":{"position":[[188,7]]},"3415":{"position":[[6306,9],[6381,6]]},"3421":{"position":[[4530,6]]},"3429":{"position":[[154,9],[176,7],[653,6],[939,7]]},"3462":{"position":[[310,6]]},"3510":{"position":[[543,6],[572,6]]},"3512":{"position":[[427,6]]},"3612":{"position":[[2995,10]]},"3629":{"position":[[960,7]]},"3633":{"position":[[4633,6],[4701,6]]},"3643":{"position":[[4250,6],[4318,6]]},"3661":{"position":[[364,8]]},"3663":{"position":[[361,8]]}}}],["reason=\"invalid",{"_index":4783,"t":{"1505":{"position":[[5695,15]]},"2686":{"position":[[5757,15]]}}}],["receipt",{"_index":5548,"t":{"3032":{"position":[[233,7]]}}}],["receiv",{"_index":635,"t":{"16":{"position":[[2631,7]]},"32":{"position":[[1099,7]]},"40":{"position":[[36,8]]},"76":{"position":[[387,7]]},"128":{"position":[[253,7]]},"134":{"position":[[344,9]]},"172":{"position":[[1075,7]]},"176":{"position":[[1051,7]]},"202":{"position":[[1881,7]]},"262":{"position":[[175,7],[957,8]]},"268":{"position":[[55,8]]},"285":{"position":[[250,7]]},"295":{"position":[[727,8],[902,9]]},"301":{"position":[[49,9]]},"341":{"position":[[338,8]]},"353":{"position":[[263,7]]},"355":{"position":[[691,9]]},"375":{"position":[[64,7]]},"409":{"position":[[865,7]]},"411":{"position":[[288,7],[1075,8]]},"425":{"position":[[811,10]]},"427":{"position":[[97,8]]},"429":{"position":[[796,9]]},"433":{"position":[[343,7]]},"455":{"position":[[89,7]]},"469":{"position":[[380,9]]},"678":{"position":[[252,9]]},"714":{"position":[[86,8]]},"728":{"position":[[149,8]]},"764":{"position":[[88,8]]},"788":{"position":[[174,9]]},"798":{"position":[[855,8]]},"824":{"position":[[169,7]]},"912":{"position":[[181,8]]},"932":{"position":[[286,9]]},"973":{"position":[[2931,8]]},"1017":{"position":[[527,9]]},"1092":{"position":[[321,7]]},"1094":{"position":[[503,7]]},"1102":{"position":[[184,8]]},"1122":{"position":[[70,8]]},"1140":{"position":[[173,8]]},"1145":{"position":[[70,8]]},"1147":{"position":[[1282,7]]},"1152":{"position":[[56,8]]},"1170":{"position":[[190,10],[280,8]]},"1177":{"position":[[1239,8]]},"1211":{"position":[[338,8]]},"1233":{"position":[[865,7]]},"1235":{"position":[[288,7],[1075,8]]},"1251":{"position":[[888,10]]},"1253":{"position":[[97,8]]},"1255":{"position":[[796,9]]},"1259":{"position":[[343,7]]},"1285":{"position":[[263,7]]},"1287":{"position":[[691,9]]},"1307":{"position":[[89,7]]},"1333":{"position":[[64,7]]},"1339":{"position":[[174,8]]},"1355":{"position":[[1406,7]]},"1409":{"position":[[327,8],[935,8],[1308,8],[1365,7]]},"1411":{"position":[[67,9]]},"1419":{"position":[[251,7]]},"1423":{"position":[[664,8]]},"1425":{"position":[[1995,8],[2449,8],[2688,8]]},"1433":{"position":[[118,7]]},"1505":{"position":[[188,9],[3724,9],[9547,8],[9633,7]]},"1569":{"position":[[335,7]]},"1575":{"position":[[887,7]]},"1589":{"position":[[604,7]]},"1615":{"position":[[252,9]]},"1666":{"position":[[1403,8]]},"1688":{"position":[[653,8]]},"1773":{"position":[[300,7]]},"1803":{"position":[[174,9]]},"1813":{"position":[[855,8]]},"1831":{"position":[[143,7]]},"1841":{"position":[[237,7]]},"1851":{"position":[[49,7]]},"1865":{"position":[[416,7]]},"1867":{"position":[[389,7]]},"1949":{"position":[[83,8]]},"1965":{"position":[[28,9],[1279,8]]},"1967":{"position":[[28,9],[1215,7]]},"1983":{"position":[[2264,8],[2413,8],[2925,8]]},"2027":{"position":[[181,8]]},"2053":{"position":[[286,9]]},"2156":{"position":[[169,7]]},"2160":{"position":[[664,8]]},"2162":{"position":[[118,7]]},"2187":{"position":[[325,7]]},"2189":{"position":[[641,7]]},"2191":{"position":[[143,9]]},"2221":{"position":[[190,10],[280,8]]},"2253":{"position":[[160,8]]},"2258":{"position":[[70,8]]},"2260":{"position":[[1351,7]]},"2273":{"position":[[70,8]]},"2332":{"position":[[56,8]]},"2354":{"position":[[527,9]]},"2368":{"position":[[408,9]]},"2394":{"position":[[338,8]]},"2400":{"position":[[1333,8]]},"2408":{"position":[[265,7],[550,7]]},"2410":{"position":[[701,9]]},"2448":{"position":[[64,7]]},"2454":{"position":[[178,8]]},"2482":{"position":[[865,7]]},"2484":{"position":[[288,7],[1075,8]]},"2500":{"position":[[888,10]]},"2502":{"position":[[97,8]]},"2504":{"position":[[796,9]]},"2508":{"position":[[343,7]]},"2526":{"position":[[1443,7]]},"2562":{"position":[[499,9]]},"2594":{"position":[[89,7]]},"2622":{"position":[[750,8]]},"2630":{"position":[[63,9],[100,7]]},"2638":{"position":[[125,8]]},"2646":{"position":[[1560,7]]},"2652":{"position":[[145,9]]},"2660":{"position":[[111,8]]},"2664":{"position":[[335,7]]},"2670":{"position":[[887,7]]},"2684":{"position":[[604,7]]},"2686":{"position":[[189,9],[3757,9],[9703,8],[9789,7]]},"2708":{"position":[[1470,8]]},"2800":{"position":[[252,9]]},"2830":{"position":[[1406,8]]},"2852":{"position":[[653,8]]},"2932":{"position":[[300,7]]},"2994":{"position":[[83,8]]},"3010":{"position":[[28,9],[1279,8]]},"3012":{"position":[[28,9],[1215,7]]},"3042":{"position":[[844,8]]},"3060":{"position":[[160,7]]},"3070":{"position":[[245,7]]},"3080":{"position":[[49,7]]},"3096":{"position":[[416,7]]},"3098":{"position":[[389,7]]},"3142":{"position":[[2264,8],[2413,8],[2925,8]]},"3216":{"position":[[181,8]]},"3246":{"position":[[286,9]]},"3324":{"position":[[427,8],[533,9],[4357,8],[4483,8],[4658,8]]},"3365":{"position":[[1222,8],[3094,7]]},"3369":{"position":[[2006,10]]},"3385":{"position":[[169,7]]},"3389":{"position":[[664,8]]},"3391":{"position":[[118,7]]},"3415":{"position":[[527,9]]},"3429":{"position":[[408,9]]},"3464":{"position":[[325,7]]},"3466":{"position":[[143,9]]},"3492":{"position":[[641,7]]},"3555":{"position":[[70,8]]},"3571":{"position":[[190,10],[280,8]]},"3603":{"position":[[160,8]]},"3608":{"position":[[70,8]]},"3610":{"position":[[1351,7]]},"3617":{"position":[[56,8]]},"3633":{"position":[[750,8]]},"3641":{"position":[[63,9],[100,7]]},"3649":{"position":[[125,8]]},"3657":{"position":[[1560,7]]},"3663":{"position":[[145,9]]},"3671":{"position":[[111,8]]}}}],["receiveclientindication(stream",{"_index":1720,"t":{"110":{"position":[[1489,31],[1792,30],[2903,30]]}}}],["recent",{"_index":682,"t":{"16":{"position":[[4609,8]]},"32":{"position":[[58,6]]},"94":{"position":[[215,8]]},"154":{"position":[[374,8]]},"156":{"position":[[339,8]]},"182":{"position":[[495,8]]},"242":{"position":[[525,8]]}}}],["recipi",{"_index":2709,"t":{"270":{"position":[[390,12]]},"1613":{"position":[[464,11],[1438,11]]},"1719":{"position":[[303,9],[331,9]]},"1723":{"position":[[212,9],[680,9]]},"2798":{"position":[[464,11],[1438,11]]},"2883":{"position":[[303,9],[331,9]]}}}],["recipient_typ",{"_index":5534,"t":{"2889":{"position":[[153,15],[966,14]]}}}],["recommend",{"_index":1898,"t":{"136":{"position":[[444,11]]},"144":{"position":[[1854,11]]},"260":{"position":[[452,11],[978,11]]},"353":{"position":[[6,11]]},"606":{"position":[[365,11]]},"636":{"position":[[898,9]]},"638":{"position":[[957,9]]},"656":{"position":[[458,9]]},"680":{"position":[[4,11]]},"926":{"position":[[12,9]]},"1057":{"position":[[543,11]]},"1189":{"position":[[711,12]]},"1271":{"position":[[58,11],[307,9]]},"1285":{"position":[[6,11]]},"1419":{"position":[[341,11],[382,11]]},"1455":{"position":[[544,11],[894,11]]},"1573":{"position":[[154,9]]},"1587":{"position":[[167,9]]},"1617":{"position":[[4,11]]},"1636":{"position":[[898,9]]},"1639":{"position":[[1003,9]]},"1652":{"position":[[458,9]]},"1678":{"position":[[567,16]]},"1831":{"position":[[940,11]]},"1899":{"position":[[909,11]]},"1929":{"position":[[312,12]]},"1983":{"position":[[4372,9]]},"2047":{"position":[[12,9]]},"2086":{"position":[[527,12]]},"2124":{"position":[[158,11]]},"2140":{"position":[[543,11]]},"2287":{"position":[[547,11],[897,11]]},"2392":{"position":[[246,11],[2709,11]]},"2408":{"position":[[6,11]]},"2520":{"position":[[58,11],[307,9]]},"2668":{"position":[[154,9]]},"2682":{"position":[[167,9]]},"2708":{"position":[[2632,9]]},"2802":{"position":[[4,11]]},"2823":{"position":[[862,9]]},"2826":{"position":[[967,9]]},"2842":{"position":[[567,16]]},"2883":{"position":[[520,9],[1502,9]]},"2904":{"position":[[422,9]]},"3060":{"position":[[1019,11]]},"3134":{"position":[[909,11]]},"3142":{"position":[[4372,9]]},"3156":{"position":[[312,12]]},"3240":{"position":[[12,9]]},"3271":{"position":[[527,12]]},"3329":{"position":[[147,9]]},"3347":{"position":[[158,11]]},"3355":{"position":[[543,11]]},"3367":{"position":[[5001,9]]},"3498":{"position":[[452,11],[867,11]]},"3612":{"position":[[273,11],[2908,11]]}}}],["reconnect",{"_index":289,"t":{"6":{"position":[[347,9]]},"16":{"position":[[1801,9]]},"18":{"position":[[396,13],[541,9],[941,9],[1074,12],[1874,12]]},"20":{"position":[[398,10],[485,11],[723,9],[1390,10],[1521,9]]},"74":{"position":[[1392,12],[1589,9],[1685,10]]},"80":{"position":[[870,9]]},"122":{"position":[[443,12]]},"128":{"position":[[1849,9]]},"132":{"position":[[1124,9],[1169,10],[1530,9]]},"134":{"position":[[699,9]]},"202":{"position":[[1256,10]]},"250":{"position":[[1825,10]]},"258":{"position":[[888,9]]},"293":{"position":[[212,9]]},"295":{"position":[[1559,11]]},"317":{"position":[[490,13]]},"347":{"position":[[278,9],[479,9]]},"367":{"position":[[245,10]]},"383":{"position":[[214,9]]},"409":{"position":[[400,10]]},"411":{"position":[[189,9],[444,9]]},"437":{"position":[[670,12],[992,11]]},"441":{"position":[[1153,9]]},"451":{"position":[[33,9]]},"455":{"position":[[814,9],[1018,13]]},"475":{"position":[[4008,9],[4160,9]]},"640":{"position":[[436,9],[761,9]]},"730":{"position":[[32,10]]},"732":{"position":[[37,10]]},"734":{"position":[[35,10]]},"736":{"position":[[45,10]]},"738":{"position":[[31,10]]},"740":{"position":[[44,10]]},"742":{"position":[[29,10]]},"744":{"position":[[28,10]]},"746":{"position":[[35,10]]},"748":{"position":[[42,10],[226,10]]},"750":{"position":[[27,11],[39,10],[73,9]]},"752":{"position":[[40,10],[78,9],[147,9]]},"754":{"position":[[40,10]]},"842":{"position":[[114,10]]},"973":{"position":[[357,9],[626,12],[962,9],[996,9],[3566,10]]},"989":{"position":[[199,10]]},"997":{"position":[[314,10]]},"1017":{"position":[[1477,10],[1634,9]]},"1029":{"position":[[154,12]]},"1071":{"position":[[514,11]]},"1092":{"position":[[645,10]]},"1233":{"position":[[400,10]]},"1235":{"position":[[189,9],[444,9]]},"1263":{"position":[[674,12],[996,11]]},"1267":{"position":[[1153,9]]},"1279":{"position":[[278,9],[479,9]]},"1303":{"position":[[33,9]]},"1307":{"position":[[814,9],[1018,13]]},"1325":{"position":[[241,11]]},"1341":{"position":[[214,9]]},"1373":{"position":[[0,9],[55,9]]},"1403":{"position":[[550,9],[656,9],[1760,9]]},"1413":{"position":[[1194,9]]},"1419":{"position":[[330,10]]},"1421":{"position":[[293,12],[375,9],[417,9],[564,9]]},"1423":{"position":[[271,9],[552,9],[1230,9]]},"1425":{"position":[[699,9],[735,12],[900,9],[1057,12],[1095,12],[1160,13],[1374,12],[1849,9],[2534,9]]},"1427":{"position":[[561,9]]},"1431":{"position":[[141,9],[252,9]]},"1465":{"position":[[679,9],[697,9]]},"1575":{"position":[[672,9],[724,13],[843,9],[864,12]]},"1589":{"position":[[336,9]]},"1591":{"position":[[718,9],[1043,9]]},"1666":{"position":[[1453,9]]},"1672":{"position":[[709,10],[858,14]]},"1751":{"position":[[314,10]]},"1849":{"position":[[1159,9]]},"1859":{"position":[[234,10]]},"1965":{"position":[[12,9],[954,10],[1016,10],[1127,10]]},"1967":{"position":[[13,9],[611,9]]},"1983":{"position":[[357,9],[626,9],[955,9],[989,9],[4228,9]]},"2021":{"position":[[383,13]]},"2023":{"position":[[348,13]]},"2033":{"position":[[259,9]]},"2134":{"position":[[514,11]]},"2156":{"position":[[255,10]]},"2160":{"position":[[271,9],[552,9],[1230,9]]},"2172":{"position":[[110,9]]},"2187":{"position":[[648,10]]},"2354":{"position":[[1594,10],[1751,9],[6139,9]]},"2368":{"position":[[321,9],[393,9]]},"2402":{"position":[[300,9],[515,9]]},"2440":{"position":[[242,11]]},"2456":{"position":[[213,9]]},"2482":{"position":[[400,10]]},"2484":{"position":[[189,9],[444,9]]},"2512":{"position":[[767,12],[955,11],[1060,12]]},"2516":{"position":[[1153,9]]},"2526":{"position":[[1603,9]]},"2544":{"position":[[0,9],[55,9]]},"2580":{"position":[[46,9]]},"2590":{"position":[[33,9]]},"2594":{"position":[[845,9],[1064,13]]},"2622":{"position":[[733,9],[839,13],[926,9],[3989,12],[4200,13],[5019,10]]},"2624":{"position":[[259,9]]},"2632":{"position":[[374,12],[4041,11]]},"2638":{"position":[[227,9]]},"2646":{"position":[[939,11],[1343,10]]},"2652":{"position":[[129,10]]},"2670":{"position":[[672,9],[724,13],[843,9],[864,12]]},"2684":{"position":[[336,9]]},"2708":{"position":[[2287,9]]},"2784":{"position":[[718,9],[1043,9]]},"2830":{"position":[[1456,9]]},"2836":{"position":[[709,10],[858,14]]},"2956":{"position":[[314,10]]},"3010":{"position":[[12,9],[954,10],[1016,10],[1127,10]]},"3012":{"position":[[13,9],[611,9]]},"3078":{"position":[[1159,9]]},"3090":{"position":[[234,10]]},"3142":{"position":[[357,9],[626,9],[955,9],[989,9],[4228,9]]},"3210":{"position":[[356,13]]},"3212":{"position":[[321,13]]},"3224":{"position":[[232,9]]},"3337":{"position":[[514,11]]},"3385":{"position":[[255,10]]},"3389":{"position":[[271,9],[552,9],[1230,9]]},"3401":{"position":[[110,9]]},"3415":{"position":[[1594,10],[1751,9],[6168,9]]},"3429":{"position":[[321,9],[393,9]]},"3464":{"position":[[648,10]]},"3633":{"position":[[733,9],[839,13],[926,9],[3989,12],[4200,13],[5019,10]]},"3635":{"position":[[259,9]]},"3643":{"position":[[374,12],[4041,11]]},"3649":{"position":[[227,9]]},"3657":{"position":[[939,11],[1343,10]]},"3663":{"position":[[129,10]]}}}],["record",{"_index":2762,"t":{"280":{"position":[[1469,9]]}}}],["recov",{"_index":1012,"t":{"38":{"position":[[1215,7]]},"74":{"position":[[2130,7],[2321,8]]},"80":{"position":[[884,7]]},"295":{"position":[[1485,7]]},"349":{"position":[[267,7],[693,7],[1285,10]]},"383":{"position":[[260,7]]},"475":{"position":[[4044,10]]},"591":{"position":[[43,9]]},"598":{"position":[[48,8]]},"618":{"position":[[424,7]]},"792":{"position":[[1753,7],[1784,7]]},"840":{"position":[[505,7]]},"842":{"position":[[0,7],[71,7],[301,7],[489,7],[774,9]]},"856":{"position":[[292,10]]},"858":{"position":[[1442,10]]},"973":{"position":[[1084,7],[1218,7],[1533,9],[1613,9],[1711,7],[2593,9],[2882,9],[2980,7],[3170,9]]},"1023":{"position":[[3682,7],[3713,7]]},"1136":{"position":[[43,7],[88,7],[172,7],[237,7]]},"1281":{"position":[[270,7],[706,7],[1436,10]]},"1409":{"position":[[696,9],[1041,9],[1136,9]]},"1427":{"position":[[594,7]]},"1429":{"position":[[28,7],[331,9],[546,7]]},"1461":{"position":[[886,7],[1217,7],[1248,7]]},"1807":{"position":[[1280,7],[1311,7]]},"1857":{"position":[[597,7]]},"1859":{"position":[[179,7],[625,7],[1127,9]]},"1983":{"position":[[1077,7],[1705,9],[1785,9],[1883,7],[2832,7],[2934,10],[3104,9],[3565,7],[3615,10]]},"2249":{"position":[[43,7],[88,7],[172,7],[237,7]]},"2293":{"position":[[851,7]]},"2404":{"position":[[283,7],[733,7]]},"2412":{"position":[[1401,9]]},"2422":{"position":[[661,7]]},"2622":{"position":[[821,9]]},"2638":{"position":[[171,7],[455,9],[817,9],[928,9]]},"3036":{"position":[[1358,7],[1389,7]]},"3088":{"position":[[597,7]]},"3090":{"position":[[179,7],[625,7],[1127,9]]},"3142":{"position":[[1077,7],[1705,9],[1785,9],[1883,7],[2832,7],[2934,10],[3104,9],[3565,7],[3615,10]]},"3324":{"position":[[2924,9],[2968,7]]},"3506":{"position":[[919,7]]},"3599":{"position":[[43,7],[88,7],[172,7],[237,7]]},"3633":{"position":[[821,9]]},"3649":{"position":[[171,7],[455,9],[817,9],[928,9]]}}}],["recover",{"_index":4511,"t":{"1409":{"position":[[655,11]]},"1569":{"position":[[615,11]]},"1781":{"position":[[135,11]]},"1859":{"position":[[130,12]]},"2640":{"position":[[545,11]]},"2664":{"position":[[615,11]]},"2940":{"position":[[135,11]]},"3090":{"position":[[130,12]]},"3651":{"position":[[545,11]]}}}],["recover_sinc",{"_index":4593,"t":{"1461":{"position":[[828,13]]},"2293":{"position":[[793,13]]},"3506":{"position":[[861,13]]}}}],["recoveri",{"_index":173,"t":{"2":{"position":[[2814,8]]},"34":{"position":[[507,8]]},"48":{"position":[[226,8]]},"74":{"position":[[1285,8],[1313,8],[1844,8],[2071,9],[2101,8],[2360,8],[2448,8],[2563,8],[2597,8]]},"86":{"position":[[1224,8]]},"347":{"position":[[1521,8]]},"349":{"position":[[379,8],[667,8],[951,8]]},"407":{"position":[[447,8]]},"411":{"position":[[414,8]]},"455":{"position":[[630,8],[780,8],[874,8],[1035,8],[1294,8],[1378,8]]},"640":{"position":[[565,8]]},"842":{"position":[[194,8],[943,8]]},"844":{"position":[[217,8]]},"969":{"position":[[465,9]]},"973":{"position":[[72,8],[385,8],[673,8],[1178,8],[1821,8],[2016,8],[2051,8],[2513,8],[3391,8],[3602,8]]},"1092":{"position":[[633,8]]},"1207":{"position":[[333,8]]},"1231":{"position":[[447,8]]},"1235":{"position":[[414,8]]},"1279":{"position":[[1526,8]]},"1281":{"position":[[392,8],[680,8],[1102,8]]},"1307":{"position":[[630,8],[780,8],[874,8],[1035,8],[1294,8],[1378,8]]},"1403":{"position":[[1919,8],[1976,8]]},"1409":{"position":[[631,9],[1245,8]]},"1591":{"position":[[847,8]]},"1781":{"position":[[37,8]]},"1855":{"position":[[1313,8]]},"1859":{"position":[[314,8],[647,8],[1296,8]]},"1899":{"position":[[138,9]]},"1979":{"position":[[507,9],[1402,8]]},"1983":{"position":[[72,8],[385,8],[670,8],[1171,8],[1433,8],[1993,8],[2161,8],[2244,8],[3474,8],[4073,8],[4539,8],[4747,8]]},"2110":{"position":[[333,8]]},"2187":{"position":[[636,8]]},"2402":{"position":[[1533,9],[1686,8]]},"2404":{"position":[[405,8],[707,8]]},"2480":{"position":[[447,8]]},"2484":{"position":[[414,8]]},"2594":{"position":[[661,8],[811,8],[905,8],[1081,8],[1352,8],[1436,8]]},"2638":{"position":[[31,8],[392,8],[611,8]]},"2640":{"position":[[393,8]]},"2784":{"position":[[847,8]]},"2940":{"position":[[37,8]]},"3084":{"position":[[1277,8]]},"3090":{"position":[[314,8],[647,8],[1296,8]]},"3134":{"position":[[138,9]]},"3138":{"position":[[507,9],[1402,8]]},"3142":{"position":[[72,8],[385,8],[670,8],[1171,8],[1433,8],[1993,8],[2161,8],[2244,8],[3474,8],[4073,8],[4539,8],[4747,8]]},"3293":{"position":[[333,8]]},"3324":{"position":[[3051,8]]},"3365":{"position":[[3257,8]]},"3464":{"position":[[636,8]]},"3649":{"position":[[31,8],[392,8],[611,8]]},"3651":{"position":[[393,8]]}}}],["redeploy",{"_index":915,"t":{"28":{"position":[[855,8]]},"2608":{"position":[[676,12]]}}}],["redi",{"_index":92,"t":{"2":{"position":[[1193,5],[1236,5],[1258,5],[2304,5],[2653,5],[2852,5]]},"8":{"position":[[324,6]]},"16":{"position":[[2047,5],[3634,5],[6051,6],[6064,5],[6177,5],[6240,5],[6459,5],[6519,5],[6682,5]]},"18":{"position":[[2441,5],[2478,5],[2942,5]]},"20":{"position":[[1173,5],[1227,5],[1241,5]]},"22":{"position":[[406,5]]},"26":{"position":[[608,5],[627,5]]},"36":{"position":[[17,5],[35,5],[113,5],[291,5]]},"38":{"position":[[192,5],[907,6],[1054,5],[1364,5],[1378,5],[1399,5]]},"42":{"position":[[964,5]]},"78":{"position":[[2490,5],[2504,5],[2526,5],[2593,5],[2609,5],[2776,5],[2936,5],[2959,5],[3034,5],[3109,5],[3300,5],[3332,5],[3385,5],[3535,5],[4059,5]]},"80":{"position":[[416,5]]},"84":{"position":[[421,5],[632,5]]},"158":{"position":[[636,5],[679,5]]},"178":{"position":[[552,5],[617,5]]},"184":{"position":[[337,5],[380,5]]},"186":{"position":[[566,6],[580,5],[672,5]]},"196":{"position":[[40,5],[113,6],[236,8],[1009,5],[1131,5]]},"200":{"position":[[77,5]]},"218":{"position":[[483,6]]},"224":{"position":[[34,5],[138,5]]},"226":{"position":[[107,5],[130,5],[171,5],[200,5],[213,5],[241,5],[247,5],[277,5],[425,5],[601,5]]},"228":{"position":[[90,5]]},"236":{"position":[[354,5],[434,5]]},"240":{"position":[[319,5]]},"242":{"position":[[810,5],[853,5]]},"270":{"position":[[1297,5]]},"283":{"position":[[840,5]]},"347":{"position":[[999,5],[1267,5],[1343,5]]},"349":{"position":[[807,7]]},"355":{"position":[[136,5]]},"357":{"position":[[56,7],[448,6],[477,5],[556,5]]},"365":{"position":[[121,6],[176,5],[204,5]]},"401":{"position":[[44,6]]},"415":{"position":[[421,6]]},"437":{"position":[[1399,5]]},"441":{"position":[[429,5]]},"465":{"position":[[423,5],[626,5],[750,5],[930,5],[1118,5],[1260,5]]},"467":{"position":[[777,5],[881,5],[1489,5]]},"469":{"position":[[78,5],[203,6],[297,5],[406,6]]},"471":{"position":[[153,5],[212,5],[484,5],[595,6],[827,5],[853,5],[886,5],[916,5],[1036,5],[2119,5],[3551,5],[3719,6]]},"473":{"position":[[56,5],[108,5],[137,5],[182,5],[241,5],[354,5],[375,5],[516,5],[629,5],[667,5],[741,5],[754,5],[933,5],[1409,5],[1483,5],[1746,5],[1841,5],[1852,5],[1940,5],[1964,5],[2172,5],[2285,5],[2543,6],[2960,5],[3121,5],[3171,5],[3248,5],[3405,5],[3486,5]]},"475":{"position":[[401,5],[837,6],[1032,5],[1207,5],[1315,5],[2350,5],[2994,5],[3235,5],[3500,5],[3536,5],[3671,5],[3863,5],[4323,5],[4497,5],[5770,5],[6517,5],[6544,5],[6707,5]]},"477":{"position":[[81,5],[192,5],[479,5],[489,5],[540,6],[556,5],[566,5],[794,5],[1590,5],[1655,5],[1742,5],[1996,5],[2219,5],[2866,5],[4955,6],[4997,5],[5239,5],[5376,5],[5451,5],[5535,5],[5641,5],[5717,5],[5847,5]]},"479":{"position":[[490,5],[711,5],[838,5],[919,5],[952,5],[1825,5],[2197,6],[2860,5],[3132,5],[3280,6],[3860,5],[3936,6],[5845,5],[5875,5],[5930,5],[5960,5],[6009,5],[6040,5],[6087,5],[6117,5],[6166,5],[6196,5],[6423,5],[6653,5]]},"481":{"position":[[99,6],[393,5],[1677,5]]},"483":{"position":[[124,5],[230,5],[865,5],[1008,5],[1076,5],[1175,5],[1789,5],[1838,5],[1953,5],[1983,5],[2041,5]]},"503":{"position":[[169,5]]},"563":{"position":[[55,6],[175,5],[915,5],[977,5],[1069,6],[1076,5],[1092,5],[1173,5],[1255,5],[1321,8],[1576,5]]},"612":{"position":[[0,5],[56,5],[204,5]]},"614":{"position":[[23,5],[106,5],[183,5]]},"620":{"position":[[673,5]]},"630":{"position":[[353,5]]},"632":{"position":[[31,6],[124,8],[220,8],[291,5],[459,5],[489,5],[711,5],[820,5],[914,5],[950,5],[1018,5]]},"640":{"position":[[888,7]]},"650":{"position":[[267,5]]},"652":{"position":[[29,6],[120,8],[191,5],[355,5],[385,5],[574,5],[683,5],[777,5],[813,5],[876,5]]},"686":{"position":[[478,5],[569,5]]},"694":{"position":[[10,5],[136,5],[199,5],[291,6],[298,5],[314,5],[396,5],[478,5],[544,8],[677,5],[720,5],[762,5],[819,5]]},"898":{"position":[[24,5]]},"969":{"position":[[789,5],[821,5],[898,5]]},"973":{"position":[[2655,5],[2736,5],[2766,5]]},"1025":{"position":[[528,5]]},"1183":{"position":[[170,5]]},"1187":{"position":[[0,5],[115,5],[198,5],[235,5],[311,6],[326,5]]},"1189":{"position":[[41,5],[105,5],[164,5],[247,5],[319,5],[379,5],[457,5],[577,6],[641,5],[819,5],[953,5],[1005,5]]},"1191":{"position":[[58,5],[147,6],[177,5],[398,5],[1265,5],[1706,6]]},"1193":{"position":[[65,5],[73,5],[125,5],[393,5],[511,5],[617,5],[705,5],[818,8],[1196,5]]},"1195":{"position":[[58,5],[102,5],[247,5],[360,5],[373,5],[481,5]]},"1197":{"position":[[24,5],[81,5],[138,5],[231,5],[400,5],[470,5],[581,8],[673,5],[737,8],[855,5],[934,5],[1026,5],[1057,5],[1171,5]]},"1199":{"position":[[24,5],[180,5],[331,5],[584,5],[845,5],[916,5],[1118,5]]},"1201":{"position":[[24,5],[98,5],[292,5],[402,5],[481,5],[650,7],[713,5],[762,6]]},"1203":{"position":[[954,5],[1014,5]]},"1205":{"position":[[525,5],[752,5]]},"1225":{"position":[[44,6]]},"1239":{"position":[[421,6]]},"1263":{"position":[[1403,5]]},"1267":{"position":[[429,5]]},"1271":{"position":[[249,5]]},"1279":{"position":[[1004,5],[1272,5],[1348,5]]},"1281":{"position":[[835,7]]},"1287":{"position":[[136,5]]},"1289":{"position":[[56,7],[448,6],[477,5],[556,5]]},"1293":{"position":[[887,5]]},"1323":{"position":[[295,6],[350,5],[378,5]]},"1397":{"position":[[169,5]]},"1591":{"position":[[1170,7]]},"1629":{"position":[[353,5]]},"1631":{"position":[[31,6],[124,8],[220,8],[291,5],[459,5],[489,5],[711,5],[820,5],[914,5],[950,5],[1018,5]]},"1645":{"position":[[267,5]]},"1647":{"position":[[29,6],[120,8],[191,5],[355,5],[385,5],[574,5],[683,5],[777,5],[813,5],[876,5]]},"1670":{"position":[[91,5],[315,6],[352,5],[518,5],[619,5],[660,5],[1619,5],[1681,5],[1773,6],[1780,5],[1796,5],[1877,5],[1959,5],[2031,8],[2212,5]]},"1692":{"position":[[230,5],[279,5]]},"1700":{"position":[[39,5],[388,5]]},"1729":{"position":[[473,5],[564,5]]},"1737":{"position":[[10,5],[126,5],[189,5],[281,6],[288,5],[304,5],[386,5],[468,5],[534,8],[659,5],[702,5],[744,5],[801,5]]},"1855":{"position":[[1498,5],[1547,5]]},"1979":{"position":[[845,5],[877,5],[954,5]]},"2011":{"position":[[24,5]]},"2080":{"position":[[170,5]]},"2084":{"position":[[0,5],[115,5],[198,5],[235,5],[311,6],[326,5],[354,5]]},"2086":{"position":[[41,5],[105,5],[164,5],[213,5],[285,5],[393,6],[457,5],[736,6],[755,5],[813,5],[1010,5],[1074,5]]},"2088":{"position":[[89,6],[139,5],[226,5]]},"2090":{"position":[[58,5],[147,6],[177,5],[398,5],[1265,5],[1706,6]]},"2092":{"position":[[65,5],[73,5],[125,5],[393,5],[511,5],[592,5],[680,5],[793,8],[1212,5]]},"2094":{"position":[[21,5],[115,5],[211,5]]},"2096":{"position":[[58,5],[102,5],[247,5],[360,5],[373,5],[481,5]]},"2098":{"position":[[24,5],[81,5],[138,5],[231,5],[400,5],[470,5],[581,8],[673,5],[737,8],[855,5],[934,5],[1026,5],[1057,5],[1171,5]]},"2100":{"position":[[24,5],[180,5],[331,5],[584,5],[845,5],[916,5],[1118,5]]},"2102":{"position":[[24,5],[98,5],[292,5],[402,5],[481,5],[650,7],[713,5],[762,6]]},"2104":{"position":[[161,5],[590,5]]},"2106":{"position":[[954,5],[1014,5]]},"2108":{"position":[[525,5],[755,5]]},"2362":{"position":[[528,5]]},"2402":{"position":[[1065,5],[1338,5],[1435,5]]},"2404":{"position":[[873,7]]},"2410":{"position":[[136,5]]},"2412":{"position":[[56,7],[454,6],[484,5],[563,5]]},"2420":{"position":[[72,5],[121,5],[178,5],[329,5],[552,5]]},"2428":{"position":[[940,5]]},"2438":{"position":[[302,6],[357,5],[385,5]]},"2474":{"position":[[44,6]]},"2488":{"position":[[421,6]]},"2512":{"position":[[1452,5]]},"2516":{"position":[[429,5]]},"2520":{"position":[[249,5]]},"2568":{"position":[[174,5]]},"2708":{"position":[[535,5],[768,5],[1063,8],[2607,6]]},"2712":{"position":[[78,5]]},"2714":{"position":[[222,5],[247,5]]},"2784":{"position":[[1170,7]]},"2816":{"position":[[353,5]]},"2818":{"position":[[31,6],[124,8],[220,8],[291,5],[459,5],[489,5],[711,5],[820,5],[914,5],[950,5],[1018,5]]},"2834":{"position":[[91,5],[315,6],[352,5],[518,5],[619,5],[660,5],[1625,5],[1687,5],[1779,6],[1786,5],[1802,5],[1883,5],[1965,5],[2037,8],[2218,5]]},"2856":{"position":[[230,5],[279,5]]},"2862":{"position":[[263,5],[376,6]]},"2864":{"position":[[24,5],[373,5]]},"2897":{"position":[[267,5]]},"2899":{"position":[[29,6],[120,8],[191,5],[355,5],[385,5],[574,5],[683,5],[777,5],[813,5],[876,5]]},"2918":{"position":[[473,5],[564,5]]},"2926":{"position":[[10,5],[126,5],[189,5],[281,6],[288,5],[304,5],[386,5],[468,5],[534,8],[659,5],[702,5],[744,5],[801,5]]},"3084":{"position":[[1462,5],[1511,5]]},"3138":{"position":[[845,5],[877,5],[954,5]]},"3200":{"position":[[24,5]]},"3265":{"position":[[170,5]]},"3269":{"position":[[0,5],[115,5],[198,5],[235,5],[311,6],[326,5],[354,5]]},"3271":{"position":[[41,5],[105,5],[164,5],[213,5],[285,5],[393,6],[457,5],[702,6],[721,5],[779,5],[976,5],[1040,5]]},"3273":{"position":[[89,6],[139,5],[226,5]]},"3275":{"position":[[58,5],[147,6],[177,5],[398,5],[1265,5],[1706,6]]},"3277":{"position":[[65,5],[73,5],[125,5],[393,5],[511,5],[592,5],[680,5],[793,8],[1212,5]]},"3279":{"position":[[21,5],[115,5],[211,5]]},"3281":{"position":[[58,5],[102,5],[247,5],[360,5],[373,5],[481,5]]},"3283":{"position":[[24,5],[81,5],[138,5],[231,5],[400,5],[470,5],[581,8],[673,5],[737,8],[855,5],[934,5],[1026,5],[1057,5],[1171,5]]},"3285":{"position":[[24,5],[180,5],[331,5],[584,5],[845,5],[916,5],[1118,5]]},"3287":{"position":[[11,5],[70,5],[106,5],[176,5],[662,5]]},"3289":{"position":[[954,5],[1014,5]]},"3291":{"position":[[525,5],[755,5]]},"3423":{"position":[[528,5]]}}}],["redigo",{"_index":312,"t":{"8":{"position":[[289,6]]},"469":{"position":[[168,6],[479,6],[866,9]]},"471":{"position":[[2055,9],[2264,9],[4325,6]]},"473":{"position":[[421,6],[1571,6],[1609,6],[2216,6],[2291,6],[3058,6]]},"475":{"position":[[32,6],[855,7],[1196,7],[1283,7],[2332,7],[4401,6],[6297,6],[6396,6],[6762,6],[6857,6]]},"477":{"position":[[780,6],[3278,6],[3468,6]]},"479":{"position":[[227,6],[414,6],[1273,6],[2004,7],[2801,6],[3074,6],[3470,6],[3643,6]]},"481":{"position":[[607,6],[1250,6],[1374,6],[1387,6],[1912,6]]},"483":{"position":[[15,6]]}}}],["redigo'",{"_index":3127,"t":{"471":{"position":[[57,8]]},"473":{"position":[[1434,8]]}}}],["redigo.conn",{"_index":3109,"t":{"469":{"position":[[639,13]]},"471":{"position":[[2703,13]]}}}],["redigo.dial(\"tcp",{"_index":3110,"t":{"469":{"position":[[669,18]]},"471":{"position":[[2733,18]]}}}],["redigo.pool",{"_index":3104,"t":{"469":{"position":[[571,12]]},"471":{"position":[[1536,12],[2665,12]]}}}],["redigo.pool}func",{"_index":3143,"t":{"471":{"position":[[1504,16]]}}}],["redigo_latency_p128.txt",{"_index":3486,"t":{"481":{"position":[[656,23]]}}}],["redigo_p128.txt",{"_index":3250,"t":{"475":{"position":[[4578,15]]},"477":{"position":[[3561,15]]}}}],["redirect",{"_index":2228,"t":{"192":{"position":[[3156,10]]},"194":{"position":[[2631,10]]},"212":{"position":[[379,8]]}}}],["redis'",{"_index":1436,"t":{"78":{"position":[[3242,7]]}}}],["redis.newuniversalclient(&redis.universalopt",{"_index":3220,"t":{"475":{"position":[[1838,49]]}}}],["redis/redi",{"_index":3206,"t":{"475":{"position":[[45,11],[311,11],[434,11],[784,11],[1758,11],[3178,12],[4445,11],[6310,11],[6478,11],[6602,11],[6871,11],[6889,11],[6998,11]]},"477":{"position":[[32,11],[411,11],[628,12]]},"479":{"position":[[2024,11],[2811,11],[3084,11],[3484,11]]}}}],["redis://127.0.0.1:6379",{"_index":2492,"t":{"224":{"position":[[316,24]]}}}],["redis://localhost:6379",{"_index":2288,"t":{"196":{"position":[[262,25]]}}}],["redis:6",{"_index":2285,"t":{"196":{"position":[[153,7]]}}}],["redis:7",{"_index":2499,"t":{"226":{"position":[[177,7]]}}}],["redis_active_statu",{"_index":3873,"t":{"694":{"position":[[57,22],[588,22],[976,22]]}}}],["redis_address",{"_index":2287,"t":{"196":{"position":[[245,16],[1102,13]]},"563":{"position":[[464,16],[1330,16]]},"612":{"position":[[380,13]]},"632":{"position":[[133,16],[229,16]]},"652":{"position":[[129,16]]},"694":{"position":[[99,16],[553,16]]},"1189":{"position":[[55,14]]},"1191":{"position":[[469,13]]},"1197":{"position":[[590,16],[746,16]]},"1631":{"position":[[133,16],[229,16]]},"1647":{"position":[[129,16]]},"1670":{"position":[[1286,16],[2040,16]]},"1692":{"position":[[642,16]]},"1694":{"position":[[107,16]]},"1696":{"position":[[107,16]]},"1737":{"position":[[89,16],[543,16]]},"2086":{"position":[[55,14]]},"2090":{"position":[[469,13]]},"2098":{"position":[[590,16],[746,16]]},"2714":{"position":[[99,16]]},"2818":{"position":[[133,16],[229,16]]},"2834":{"position":[[1292,16],[2046,16]]},"2856":{"position":[[642,16]]},"2858":{"position":[[107,16]]},"2860":{"position":[[107,16]]},"2899":{"position":[[129,16]]},"2926":{"position":[[89,16],[543,16]]},"3271":{"position":[[55,14]]},"3275":{"position":[[469,13]]},"3283":{"position":[[590,16],[746,16]]}}}],["redis_address=\"localhost:16379",{"_index":4400,"t":{"1195":{"position":[[950,31]]},"2096":{"position":[[950,31]]},"3281":{"position":[[950,31]]}}}],["redis_address=127.0.0.1:6379",{"_index":4385,"t":{"1191":{"position":[[361,28],[639,28],[1043,28]]},"2090":{"position":[[361,28],[639,28],[1043,28]]},"3275":{"position":[[361,28],[639,28],[1043,28]]}}}],["redis_cluster_addr",{"_index":3707,"t":{"618":{"position":[[2470,19]]}}}],["redis_cluster_address",{"_index":3708,"t":{"618":{"position":[[2501,21]]},"1199":{"position":[[74,21],[224,24],[687,24]]},"2100":{"position":[[74,21],[224,24],[687,24]]},"3285":{"position":[[74,21],[224,24],[687,24]]}}}],["redis_connect_timeout",{"_index":3704,"t":{"618":{"position":[[2313,21]]}}}],["redis_db",{"_index":4379,"t":{"1189":{"position":[[278,9]]},"2086":{"position":[[244,9]]},"3271":{"position":[[244,9]]}}}],["redis_force_resp2",{"_index":5146,"t":{"2086":{"position":[[590,18]]},"3271":{"position":[[590,18]]},"3287":{"position":[[601,17]]}}}],["redis_history_meta_ttl",{"_index":3685,"t":{"618":{"position":[[797,22]]}}}],["redis_host",{"_index":3666,"t":{"612":{"position":[[306,10]]}}}],["redis_idle_timeout",{"_index":3703,"t":{"618":{"position":[[2262,18]]}}}],["redis_list",{"_index":2491,"t":{"224":{"position":[[299,11]]}}}],["redis_master_nam",{"_index":3711,"t":{"618":{"position":[[2573,17]]}}}],["redis_password",{"_index":4376,"t":{"1189":{"position":[[127,15]]},"2086":{"position":[[127,15]]},"3271":{"position":[[127,15]]}}}],["redis_port",{"_index":3667,"t":{"612":{"position":[[321,10]]}}}],["redis_prefix",{"_index":4382,"t":{"1189":{"position":[[486,13]]},"1271":{"position":[[286,13]]},"2086":{"position":[[302,13]]},"2520":{"position":[[286,13]]},"3271":{"position":[[302,13]]}}}],["redis_presence_ttl",{"_index":3687,"t":{"618":{"position":[[895,18]]}}}],["redis_pubsub_num_work",{"_index":3672,"t":{"618":{"position":[[261,24]]}}}],["redis_read_timeout",{"_index":3705,"t":{"618":{"position":[[2367,18]]}}}],["redis_sentinel",{"_index":3709,"t":{"618":{"position":[[2523,15]]}}}],["redis_sentinel_address",{"_index":3710,"t":{"618":{"position":[[2550,22]]},"1193":{"position":[[147,22],[202,22],[827,25]]},"2092":{"position":[[147,22],[202,22],[802,25]]},"3277":{"position":[[147,22],[202,22],[802,25]]}}}],["redis_sentinel_master_nam",{"_index":3712,"t":{"618":{"position":[[2602,26]]},"1193":{"position":[[174,27],[335,26],[872,29]]},"2092":{"position":[[174,27],[335,26],[847,29]]},"3277":{"position":[[174,27],[335,26],[847,29]]}}}],["redis_sentinel_password",{"_index":4389,"t":{"1193":{"position":[[430,23]]},"2092":{"position":[[430,23]]},"3277":{"position":[[430,23]]}}}],["redis_sentinel_tl",{"_index":5159,"t":{"2094":{"position":[[63,19]]},"3279":{"position":[[63,19]]}}}],["redis_sentinel_tls_cert",{"_index":5162,"t":{"2094":{"position":[[355,24]]},"3279":{"position":[[355,24]]}}}],["redis_sentinel_tls_cert_pem",{"_index":5163,"t":{"2094":{"position":[[534,27]]},"3279":{"position":[[507,27]]}}}],["redis_sentinel_tls_insecure_skip_verifi",{"_index":5160,"t":{"2094":{"position":[[137,40]]},"3279":{"position":[[137,40]]}}}],["redis_sentinel_tls_key",{"_index":5164,"t":{"2094":{"position":[[570,23]]},"3279":{"position":[[543,23]]}}}],["redis_sentinel_tls_key_pem",{"_index":5165,"t":{"2094":{"position":[[744,26]]},"3279":{"position":[[690,26]]}}}],["redis_sentinel_tls_root_ca",{"_index":5166,"t":{"2094":{"position":[[779,27]]},"3279":{"position":[[725,27]]}}}],["redis_sentinel_tls_root_ca_pem",{"_index":5167,"t":{"2094":{"position":[[987,30]]},"3279":{"position":[[906,30]]}}}],["redis_sentinel_tls_server_nam",{"_index":5168,"t":{"2094":{"position":[[1026,31]]},"3279":{"position":[[945,31]]}}}],["redis_sentinel_tls_skip_verifi",{"_index":5161,"t":{"2094":{"position":[[292,30]]},"3279":{"position":[[292,30]]}}}],["redis_sentinel_us",{"_index":4390,"t":{"1193":{"position":[[535,19]]},"2092":{"position":[[535,19]]},"3277":{"position":[[535,19]]}}}],["redis_sequence_ttl",{"_index":3686,"t":{"618":{"position":[[848,18]]}}}],["redis_stream",{"_index":3670,"t":{"618":{"position":[[208,13]]}}}],["redis_throttl",{"_index":3631,"t":{"563":{"position":[[424,19],[1365,19]]}}}],["redis_tl",{"_index":4380,"t":{"1189":{"position":[[336,10]]},"2088":{"position":[[96,10]]},"3273":{"position":[[96,10]]}}}],["redis_tls_cert",{"_index":5150,"t":{"2088":{"position":[[361,15]]},"3273":{"position":[[361,15]]}}}],["redis_tls_cert_pem",{"_index":5151,"t":{"2088":{"position":[[531,18]]},"3273":{"position":[[504,18]]}}}],["redis_tls_insecure_skip_verifi",{"_index":5148,"t":{"2088":{"position":[[161,31]]},"3273":{"position":[[161,31]]}}}],["redis_tls_key",{"_index":5152,"t":{"2088":{"position":[[558,14]]},"3273":{"position":[[531,14]]}}}],["redis_tls_key_pem",{"_index":5153,"t":{"2088":{"position":[[723,17]]},"3273":{"position":[[669,17]]}}}],["redis_tls_root_ca",{"_index":5154,"t":{"2088":{"position":[[749,18]]},"3273":{"position":[[695,18]]}}}],["redis_tls_root_ca_pem",{"_index":5157,"t":{"2088":{"position":[[948,21]]},"3273":{"position":[[867,21]]}}}],["redis_tls_server_nam",{"_index":5158,"t":{"2088":{"position":[[978,22]]},"3273":{"position":[[897,22]]}}}],["redis_tls_skip_verifi",{"_index":4381,"t":{"1189":{"position":[[401,22]]},"2088":{"position":[[307,21]]},"2580":{"position":[[231,21]]},"3273":{"position":[[307,21]]}}}],["redis_us",{"_index":4377,"t":{"1189":{"position":[[180,11]]},"2086":{"position":[[180,11]]},"3271":{"position":[[180,11]]}}}],["redis_use_list",{"_index":3669,"t":{"614":{"position":[[209,18]]},"1189":{"position":[[584,16]]},"2086":{"position":[[400,16]]},"3271":{"position":[[400,16]]}}}],["redis_user_command_rate_limit",{"_index":5515,"t":{"2834":{"position":[[1240,32],[2081,32]]}}}],["redis_user_command_throttl",{"_index":4915,"t":{"1670":{"position":[[1234,32],[2075,32]]}}}],["redis_write_timeout",{"_index":3706,"t":{"618":{"position":[[2418,19]]}}}],["redisaddpres",{"_index":3241,"t":{"475":{"position":[[4329,18]]}}}],["redisai",{"_index":3324,"t":{"477":{"position":[[305,8]]}}}],["redisbloom",{"_index":3322,"t":{"477":{"position":[[281,11]]}}}],["redisearch",{"_index":3323,"t":{"477":{"position":[[293,11]]}}}],["redisgear",{"_index":3325,"t":{"477":{"position":[[314,11]]}}}],["redisjson",{"_index":3321,"t":{"477":{"position":[[270,10]]}}}],["redispublish",{"_index":3237,"t":{"475":{"position":[[3557,14]]}}}],["redispublish_histori",{"_index":3238,"t":{"475":{"position":[[3796,22]]}}}],["redisrecov",{"_index":3240,"t":{"475":{"position":[[4178,15]]}}}],["redissubscrib",{"_index":3239,"t":{"475":{"position":[[4027,16]]}}}],["reduc",{"_index":339,"t":{"8":{"position":[[737,8]]},"12":{"position":[[339,6]]},"18":{"position":[[1436,6],[3391,6]]},"34":{"position":[[536,6]]},"50":{"position":[[1629,8],[1800,6]]},"52":{"position":[[967,6]]},"128":{"position":[[412,6],[1244,7]]},"140":{"position":[[451,6]]},"178":{"position":[[814,6],[881,6]]},"280":{"position":[[2146,8]]},"347":{"position":[[214,6],[423,6],[1693,6]]},"363":{"position":[[204,6]]},"379":{"position":[[216,8]]},"383":{"position":[[304,8]]},"437":{"position":[[782,6]]},"471":{"position":[[602,8],[3425,7]]},"473":{"position":[[2936,8]]},"475":{"position":[[992,7],[3252,8],[6226,7]]},"477":{"position":[[666,6],[5056,8],[5749,7]]},"479":{"position":[[1797,6],[4943,6],[4977,7]]},"483":{"position":[[714,6],[1121,6],[1149,6],[1314,6]]},"553":{"position":[[723,6],[809,6]]},"600":{"position":[[101,7]]},"612":{"position":[[142,6]]},"640":{"position":[[314,6]]},"912":{"position":[[738,6]]},"973":{"position":[[508,8]]},"1154":{"position":[[167,6]]},"1156":{"position":[[168,6]]},"1158":{"position":[[98,6]]},"1160":{"position":[[189,6]]},"1263":{"position":[[786,6]]},"1279":{"position":[[214,6],[423,6],[1698,6]]},"1321":{"position":[[203,6]]},"1337":{"position":[[205,8]]},"1341":{"position":[[306,8]]},"1405":{"position":[[452,8]]},"1481":{"position":[[151,6]]},"1519":{"position":[[216,6]]},"1527":{"position":[[1098,6],[1187,6]]},"1591":{"position":[[596,6]]},"1983":{"position":[[508,8]]},"2027":{"position":[[738,6]]},"2154":{"position":[[3,6],[167,8]]},"2191":{"position":[[874,8]]},"2313":{"position":[[151,6]]},"2334":{"position":[[167,6]]},"2336":{"position":[[168,6]]},"2338":{"position":[[98,6]]},"2340":{"position":[[189,6]]},"2402":{"position":[[236,6],[1872,6]]},"2436":{"position":[[207,6]]},"2452":{"position":[[205,8]]},"2456":{"position":[[308,8]]},"2702":{"position":[[216,6]]},"2760":{"position":[[1196,6],[1285,6]]},"2784":{"position":[[596,6]]},"3142":{"position":[[508,8]]},"3216":{"position":[[738,6]]},"3383":{"position":[[3,6],[167,8]]},"3466":{"position":[[874,8]]},"3619":{"position":[[167,6]]},"3621":{"position":[[168,6]]},"3623":{"position":[[98,6]]},"3625":{"position":[[189,6]]}}}],["reduct",{"_index":3312,"t":{"475":{"position":[[6093,9]]},"479":{"position":[[159,9],[3188,10],[3357,9],[6369,9]]},"1519":{"position":[[323,9]]},"2402":{"position":[[449,9]]},"2702":{"position":[[323,9]]}}}],["refactor",{"_index":858,"t":{"26":{"position":[[165,11]]},"152":{"position":[[4,11]]},"311":{"position":[[186,11]]},"2578":{"position":[[436,11]]}}}],["refer",{"_index":916,"t":{"28":{"position":[[1002,5]]},"38":{"position":[[2304,5]]},"393":{"position":[[818,5]]},"455":{"position":[[1313,5]]},"604":{"position":[[426,5]]},"612":{"position":[[191,5]]},"920":{"position":[[118,5]]},"989":{"position":[[468,5]]},"997":{"position":[[679,5]]},"1031":{"position":[[322,5]]},"1047":{"position":[[49,9]]},"1098":{"position":[[498,5]]},"1166":{"position":[[94,5]]},"1307":{"position":[[1313,5]]},"1351":{"position":[[148,5]]},"1485":{"position":[[455,5]]},"1491":{"position":[[1237,5]]},"1751":{"position":[[679,5]]},"2041":{"position":[[118,5]]},"2217":{"position":[[94,5]]},"2234":{"position":[[498,5]]},"2317":{"position":[[455,5]]},"2323":{"position":[[1237,5]]},"2370":{"position":[[322,5]]},"2386":{"position":[[49,9]]},"2466":{"position":[[151,5]]},"2578":{"position":[[669,5]]},"2594":{"position":[[1371,5]]},"2956":{"position":[[679,5]]},"3022":{"position":[[70,5]]},"3024":{"position":[[43,5]]},"3038":{"position":[[308,6]]},"3232":{"position":[[118,5]]},"3431":{"position":[[322,5]]},"3447":{"position":[[49,9]]},"3530":{"position":[[455,5]]},"3536":{"position":[[1237,5]]},"3567":{"position":[[94,5]]},"3584":{"position":[[498,5]]}}}],["referenc",{"_index":4260,"t":{"1045":{"position":[[1162,11]]},"2384":{"position":[[1162,11]]},"3445":{"position":[[1162,11]]}}}],["refin",{"_index":2685,"t":{"262":{"position":[[1645,6]]}}}],["reflect",{"_index":514,"t":{"12":{"position":[[1240,7]]},"122":{"position":[[1436,11]]},"130":{"position":[[1179,7]]},"321":{"position":[[2133,7]]},"337":{"position":[[92,7]]},"678":{"position":[[832,9]]},"1615":{"position":[[824,9]]},"2622":{"position":[[4914,9]]},"2632":{"position":[[4543,9]]},"2800":{"position":[[824,9]]},"3633":{"position":[[4914,9]]},"3643":{"position":[[4543,9]]}}}],["refresh",{"_index":946,"t":{"32":{"position":[[1962,7],[1988,7]]},"122":{"position":[[550,8],[1487,7],[1844,7]]},"216":{"position":[[2972,7]]},"291":{"position":[[352,8],[2119,7]]},"337":{"position":[[200,9]]},"373":{"position":[[73,7],[101,7]]},"435":{"position":[[262,7]]},"437":{"position":[[1531,7]]},"563":{"position":[[331,7]]},"776":{"position":[[333,7],[352,7],[428,7],[941,7],[996,7]]},"798":{"position":[[741,7],[979,7],[1105,7],[1232,7],[1612,9],[1692,7],[1845,7],[1913,7],[2008,7],[2058,11],[2121,7]]},"997":{"position":[[618,10],[634,7],[736,7]]},"1019":{"position":[[225,7],[326,7],[398,7],[649,7],[1200,7]]},"1027":{"position":[[368,7]]},"1029":{"position":[[607,7]]},"1031":{"position":[[821,7]]},"1045":{"position":[[437,10]]},"1047":{"position":[[452,7],[587,10]]},"1094":{"position":[[398,7]]},"1261":{"position":[[262,7]]},"1263":{"position":[[1535,7]]},"1331":{"position":[[73,7],[103,7]]},"1403":{"position":[[1609,7],[1672,7]]},"1407":{"position":[[676,7]]},"1413":{"position":[[308,10],[371,7],[398,7],[483,11],[543,9],[880,7],[963,7],[1172,7]]},"1467":{"position":[[0,7],[15,10],[99,7],[193,7],[231,7],[316,7],[561,7]]},"1505":{"position":[[8816,7]]},"1573":{"position":[[365,7],[389,7]]},"1575":{"position":[[130,8],[176,7]]},"1589":{"position":[[132,8]]},"1666":{"position":[[353,7],[1627,7],[1652,7]]},"1668":{"position":[[683,7]]},"1670":{"position":[[1123,7]]},"1751":{"position":[[618,10],[634,7],[736,7]]},"1791":{"position":[[333,7],[352,7],[428,7],[941,7],[996,7]]},"1813":{"position":[[741,7],[979,7],[1105,7],[1232,7],[1612,9],[1692,7],[1845,7],[1913,7],[2008,7],[2058,11],[2121,7]]},"1891":{"position":[[58,7]]},"1897":{"position":[[59,7]]},"2150":{"position":[[2298,7],[2316,7],[2363,7]]},"2152":{"position":[[957,7],[991,9]]},"2172":{"position":[[295,7]]},"2174":{"position":[[292,7]]},"2189":{"position":[[468,7],[476,7],[787,7]]},"2299":{"position":[[0,7],[15,10],[99,7],[193,7],[231,7],[316,7],[527,7]]},"2356":{"position":[[225,7],[326,7],[398,7],[649,7],[1200,7]]},"2364":{"position":[[268,7],[386,7],[467,7],[643,7],[761,7],[789,7],[958,7],[1305,7],[1345,7],[1771,7],[2050,7],[2673,7]]},"2366":{"position":[[304,7],[320,7]]},"2368":{"position":[[784,7],[800,7]]},"2370":{"position":[[821,7]]},"2384":{"position":[[437,10]]},"2386":{"position":[[452,7],[587,10]]},"2388":{"position":[[27,7],[997,7],[1198,7]]},"2446":{"position":[[73,7],[103,7]]},"2510":{"position":[[262,7]]},"2512":{"position":[[1692,7],[1769,7]]},"2622":{"position":[[4419,7],[5047,7]]},"2628":{"position":[[333,10]]},"2644":{"position":[[328,10]]},"2668":{"position":[[365,7],[389,7]]},"2670":{"position":[[130,8],[176,7]]},"2684":{"position":[[132,8]]},"2686":{"position":[[8934,7]]},"2830":{"position":[[356,7],[1630,7],[1655,7]]},"2832":{"position":[[689,7]]},"2834":{"position":[[1129,7]]},"2956":{"position":[[618,10],[634,7],[736,7]]},"3020":{"position":[[383,7],[406,7],[467,7],[1022,7]]},"3042":{"position":[[734,7],[949,7],[1058,7]]},"3122":{"position":[[58,7]]},"3130":{"position":[[59,7]]},"3379":{"position":[[2298,7],[2316,7],[2363,7]]},"3381":{"position":[[957,7],[991,9]]},"3401":{"position":[[295,7]]},"3403":{"position":[[292,7]]},"3417":{"position":[[225,7],[326,7],[398,7],[649,7],[1200,7]]},"3425":{"position":[[241,7],[359,7],[440,7],[616,7],[734,7],[762,7],[931,7],[1278,7],[1318,7],[1744,7],[2023,7],[2646,7]]},"3427":{"position":[[304,7],[320,7]]},"3429":{"position":[[784,7],[800,7]]},"3431":{"position":[[821,7]]},"3445":{"position":[[437,10]]},"3447":{"position":[[452,7],[587,10]]},"3449":{"position":[[27,7],[997,7],[1198,7]]},"3492":{"position":[[468,7],[476,7],[787,7]]},"3512":{"position":[[0,7],[15,10],[99,7],[186,7],[224,7],[309,7],[520,7]]},"3633":{"position":[[4419,7],[5047,7]]},"3639":{"position":[[333,10],[973,10],[1109,7]]},"3655":{"position":[[328,10],[1157,10],[1293,7]]}}}],["refresh_proxy_nam",{"_index":4271,"t":{"1047":{"position":[[565,21]]},"2386":{"position":[[565,21]]},"3447":{"position":[[565,21]]}}}],["refus",{"_index":5606,"t":{"3324":{"position":[[3172,7],[3333,7]]}}}],["regard",{"_index":1945,"t":{"150":{"position":[[135,9]]},"218":{"position":[[938,7]]},"280":{"position":[[2345,9]]},"477":{"position":[[3392,9]]},"579":{"position":[[74,9]]},"587":{"position":[[55,9]]},"1039":{"position":[[1201,9]]},"1371":{"position":[[491,9]]},"1547":{"position":[[74,9]]},"1841":{"position":[[582,9]]},"1929":{"position":[[164,9]]},"2104":{"position":[[504,9]]},"2170":{"position":[[34,9]]},"2378":{"position":[[1201,9]]},"2542":{"position":[[491,9]]},"2574":{"position":[[49,7]]},"2752":{"position":[[74,9]]},"3070":{"position":[[596,9]]},"3156":{"position":[[164,9]]},"3287":{"position":[[493,9]]},"3399":{"position":[[34,9]]},"3439":{"position":[[1201,9]]}}}],["regardless",{"_index":4463,"t":{"1355":{"position":[[1521,10]]},"2526":{"position":[[1830,10]]}}}],["regener",{"_index":3657,"t":{"604":{"position":[[326,10]]},"989":{"position":[[165,11]]}}}],["regex",{"_index":1904,"t":{"136":{"position":[[1121,5]]},"1511":{"position":[[962,5]]},"1571":{"position":[[1882,5]]},"1579":{"position":[[3,6],[64,8]]},"1581":{"position":[[137,8]]},"1827":{"position":[[431,6],[601,6]]},"1885":{"position":[[768,6],[909,5],[1029,6]]},"2666":{"position":[[1882,5]]},"2674":{"position":[[3,6],[64,8]]},"2676":{"position":[[137,8]]},"2770":{"position":[[962,5]]},"3056":{"position":[[397,6],[567,6]]},"3116":{"position":[[768,6],[909,5],[1029,6]]}}}],["regexp",{"_index":4004,"t":{"858":{"position":[[589,6]]},"1045":{"position":[[1203,6]]},"1051":{"position":[[680,6]]},"1827":{"position":[[106,6]]},"1843":{"position":[[589,6]]},"1885":{"position":[[1064,6]]},"2384":{"position":[[1203,6]]},"2390":{"position":[[680,6]]},"3056":{"position":[[72,6]]},"3072":{"position":[[877,6]]},"3116":{"position":[[1064,6]]},"3445":{"position":[[1203,6]]},"3451":{"position":[[680,6]]}}}],["regist",{"_index":1116,"t":{"50":{"position":[[2242,9]]},"62":{"position":[[764,11]]},"64":{"position":[[727,10]]},"160":{"position":[[619,8]]},"268":{"position":[[919,11]]},"272":{"position":[[714,9]]},"276":{"position":[[64,10]]},"335":{"position":[[139,10]]},"491":{"position":[[54,8]]},"1385":{"position":[[54,8]]},"1682":{"position":[[852,9]]},"1688":{"position":[[408,8],[534,10]]},"1703":{"position":[[0,9],[134,10]]},"1709":{"position":[[28,10]]},"1715":{"position":[[107,12]]},"2556":{"position":[[55,8]]},"2634":{"position":[[659,10]]},"2846":{"position":[[852,9]]},"2852":{"position":[[408,8],[534,10]]},"2867":{"position":[[0,9],[134,10]]},"2873":{"position":[[28,10]]},"2879":{"position":[[107,12]]},"3645":{"position":[[659,10]]}}}],["registered/upd",{"_index":4957,"t":{"1703":{"position":[[922,19]]},"2867":{"position":[[829,19]]}}}],["registri",{"_index":1056,"t":{"44":{"position":[[253,9]]},"543":{"position":[[152,9]]},"1559":{"position":[[152,9]]},"2174":{"position":[[378,8]]},"2632":{"position":[[280,9],[299,8],[4922,9],[5087,8]]},"2634":{"position":[[63,8],[172,8],[374,8],[474,9]]},"2646":{"position":[[326,8],[1041,8],[1310,8]]},"2728":{"position":[[152,9]]},"3403":{"position":[[378,8]]},"3643":{"position":[[280,9],[299,8],[4922,9],[5087,8]]},"3645":{"position":[[63,8],[172,8],[374,8],[474,9]]},"3657":{"position":[[326,8],[1041,8],[1310,8]]}}}],["regular",{"_index":1343,"t":{"70":{"position":[[806,7]]},"74":{"position":[[1876,7]]},"124":{"position":[[2558,7]]},"130":{"position":[[1074,7]]},"1885":{"position":[[59,7],[1083,7]]},"3116":{"position":[[59,7],[1083,7]]}}}],["regularli",{"_index":2676,"t":{"262":{"position":[[987,9]]},"475":{"position":[[80,9]]}}}],["reject",{"_index":1237,"t":{"62":{"position":[[1176,8]]},"66":{"position":[[1554,6]]},"216":{"position":[[2612,7]]},"722":{"position":[[83,8]]},"846":{"position":[[338,8]]},"1571":{"position":[[639,8]]},"1771":{"position":[[63,8]]},"1773":{"position":[[592,8]]},"1775":{"position":[[104,8]]},"1777":{"position":[[104,8]]},"1883":{"position":[[229,7]]},"1957":{"position":[[95,8]]},"2628":{"position":[[867,7]]},"2644":{"position":[[849,7]]},"2666":{"position":[[639,8]]},"2930":{"position":[[63,8]]},"2932":{"position":[[592,8]]},"2934":{"position":[[104,8]]},"2936":{"position":[[104,8]]},"3002":{"position":[[95,8]]},"3114":{"position":[[229,7]]}}}],["reject(err",{"_index":2455,"t":{"216":{"position":[[2719,12]]},"2628":{"position":[[1167,12]]},"2644":{"position":[[1149,12]]}}}],["rel",{"_index":2889,"t":{"317":{"position":[[1254,10]]}}}],["rel=\"stylesheet",{"_index":2584,"t":{"246":{"position":[[1502,16]]}}}],["relat",{"_index":297,"t":{"6":{"position":[[515,6]]},"66":{"position":[[521,7],[1938,7]]},"130":{"position":[[1143,7]]},"166":{"position":[[414,8]]},"196":{"position":[[1655,7]]},"202":{"position":[[1898,7]]},"258":{"position":[[1742,7]]},"260":{"position":[[596,7]]},"262":{"position":[[856,7]]},"280":{"position":[[3479,7]]},"433":{"position":[[453,7]]},"459":{"position":[[61,7]]},"475":{"position":[[2747,7]]},"634":{"position":[[35,10]]},"654":{"position":[[33,10]]},"828":{"position":[[478,7]]},"1189":{"position":[[30,7]]},"1259":{"position":[[453,7]]},"1311":{"position":[[61,7]]},"1375":{"position":[[28,7]]},"1405":{"position":[[1731,6]]},"1409":{"position":[[415,6],[613,6],[1254,7]]},"1411":{"position":[[128,7],[414,7]]},"1417":{"position":[[102,6]]},"1511":{"position":[[159,7]]},"1571":{"position":[[289,7]]},"1633":{"position":[[35,10]]},"1649":{"position":[[33,10]]},"1719":{"position":[[1295,6]]},"2086":{"position":[[30,7]]},"2154":{"position":[[1427,6]]},"2508":{"position":[[453,7]]},"2546":{"position":[[28,7]]},"2598":{"position":[[66,7]]},"2622":{"position":[[5055,7]]},"2632":{"position":[[4631,7]]},"2646":{"position":[[1903,7]]},"2654":{"position":[[73,7]]},"2666":{"position":[[289,7]]},"2770":{"position":[[159,7]]},"2820":{"position":[[35,10]]},"2883":{"position":[[1454,6]]},"2901":{"position":[[33,10]]},"3271":{"position":[[30,7]]},"3383":{"position":[[1427,6]]},"3633":{"position":[[5055,7]]},"3643":{"position":[[4631,7]]},"3657":{"position":[[1903,7]]},"3665":{"position":[[73,7]]}}}],["releas",{"_index":684,"t":{"16":{"position":[[4634,8]]},"24":{"position":[[93,7]]},"26":{"position":[[197,8],[335,7]]},"30":{"position":[[83,8]]},"46":{"position":[[23,7]]},"52":{"position":[[1027,8]]},"54":{"position":[[104,7]]},"58":{"position":[[1245,8]]},"82":{"position":[[1648,9],[2392,8],[2546,8]]},"94":{"position":[[2975,8],[3036,7]]},"118":{"position":[[88,7]]},"120":{"position":[[55,8],[698,8],[837,8]]},"122":{"position":[[190,7],[266,8]]},"132":{"position":[[907,9]]},"144":{"position":[[440,8]]},"146":{"position":[[130,8]]},"150":{"position":[[4,7]]},"152":{"position":[[127,8]]},"154":{"position":[[383,8]]},"156":{"position":[[348,8]]},"182":{"position":[[504,8]]},"242":{"position":[[534,8]]},"256":{"position":[[10,7],[108,8]]},"313":{"position":[[59,8]]},"329":{"position":[[11,8],[851,7]]},"333":{"position":[[87,7],[163,8],[711,7]]},"337":{"position":[[393,8]]},"411":{"position":[[1135,8]]},"477":{"position":[[2792,7]]},"509":{"position":[[74,7],[134,8],[180,7]]},"517":{"position":[[36,7]]},"539":{"position":[[22,8],[93,9],[119,7]]},"545":{"position":[[25,7]]},"547":{"position":[[25,7]]},"1201":{"position":[[244,8]]},"1235":{"position":[[1135,8]]},"1359":{"position":[[122,7]]},"1363":{"position":[[70,7]]},"1437":{"position":[[68,7],[144,8],[190,7]]},"1445":{"position":[[36,7]]},"1505":{"position":[[476,7]]},"1555":{"position":[[22,8],[93,9],[119,7]]},"1561":{"position":[[25,7]]},"1563":{"position":[[25,7]]},"2102":{"position":[[244,8]]},"2484":{"position":[[1135,8]]},"2530":{"position":[[122,7]]},"2534":{"position":[[70,7]]},"2574":{"position":[[821,7]]},"2578":{"position":[[311,8]]},"2582":{"position":[[19,7]]},"2606":{"position":[[71,7],[150,8],[204,7]]},"2614":{"position":[[36,7]]},"2686":{"position":[[482,7]]},"2724":{"position":[[22,8],[93,9],[119,7]]},"2730":{"position":[[25,7]]},"2732":{"position":[[25,7]]},"2762":{"position":[[268,8]]}}}],["released#new",{"_index":5257,"t":{"2578":{"position":[[733,12]]}}}],["relev",{"_index":2682,"t":{"262":{"position":[[1530,8]]}}}],["reli",{"_index":811,"t":{"20":{"position":[[271,4]]},"86":{"position":[[1195,4]]},"234":{"position":[[594,4]]},"327":{"position":[[182,4]]},"353":{"position":[[236,4]]},"481":{"position":[[1587,6]]},"483":{"position":[[1478,4]]},"796":{"position":[[286,6]]},"973":{"position":[[2076,6]]},"999":{"position":[[363,6]]},"1005":{"position":[[409,6]]},"1017":{"position":[[770,4]]},"1285":{"position":[[236,4]]},"1672":{"position":[[804,4]]},"1753":{"position":[[363,6]]},"1765":{"position":[[365,6]]},"1771":{"position":[[1304,7]]},"1811":{"position":[[286,6]]},"2354":{"position":[[770,4]]},"2408":{"position":[[236,4]]},"2512":{"position":[[562,4]]},"2660":{"position":[[293,4]]},"2836":{"position":[[804,4]]},"2930":{"position":[[1304,7]]},"2958":{"position":[[363,6]]},"2970":{"position":[[725,6]]},"3327":{"position":[[315,7]]},"3367":{"position":[[661,7]]},"3415":{"position":[[770,4]]},"3671":{"position":[[293,4]]}}}],["reliabl",{"_index":1935,"t":{"144":{"position":[[1446,8]]},"218":{"position":[[548,8]]},"264":{"position":[[972,12],[1104,9]]},"317":{"position":[[907,8]]},"973":{"position":[[2863,11]]},"3324":{"position":[[5234,11]]}}}],["reload",{"_index":743,"t":{"18":{"position":[[297,6],[3205,6]]},"38":{"position":[[1351,8]]},"74":{"position":[[1749,6]]},"232":{"position":[[608,6]]},"293":{"position":[[323,7]]},"347":{"position":[[336,7]]},"455":{"position":[[771,8],[854,8]]},"932":{"position":[[50,6],[122,6]]},"973":{"position":[[1030,7]]},"1017":{"position":[[1459,7]]},"1279":{"position":[[336,7]]},"1307":{"position":[[771,8],[854,8]]},"1505":{"position":[[5295,6],[7092,6]]},"1983":{"position":[[1023,7],[4490,7]]},"2053":{"position":[[50,6],[122,6]]},"2354":{"position":[[1576,7]]},"2402":{"position":[[360,7]]},"2594":{"position":[[802,8],[885,8]]},"2686":{"position":[[5357,6],[7173,6]]},"3142":{"position":[[1023,7],[4490,7]]},"3246":{"position":[[50,6],[122,6]]},"3415":{"position":[[1576,7]]},"3612":{"position":[[2189,7]]}}}],["remain",{"_index":1383,"t":{"76":{"position":[[516,9]]},"1571":{"position":[[150,9]]},"2666":{"position":[[150,9]]},"3020":{"position":[[745,6]]},"3042":{"position":[[913,9]]}}}],["rememb",{"_index":1387,"t":{"76":{"position":[[1493,8]]},"122":{"position":[[1543,8]]},"144":{"position":[[74,8]]},"888":{"position":[[192,8]]},"2001":{"position":[[192,8]]},"3190":{"position":[[192,8]]}}}],["remot",{"_index":2916,"t":{"327":{"position":[[93,6]]},"640":{"position":[[874,6]]},"1591":{"position":[[1156,6]]},"2784":{"position":[[1156,6]]}}}],["remote_addr",{"_index":2313,"t":{"198":{"position":[[943,13],[1368,13]]},"250":{"position":[[908,13],[1245,13]]},"979":{"position":[[876,13],[1130,13]]},"981":{"position":[[436,13],[680,13]]},"2072":{"position":[[876,13],[1130,13]]},"2074":{"position":[[436,13],[680,13]]},"3309":{"position":[[876,13],[1130,13]]},"3311":{"position":[[436,13],[680,13]]}}}],["remov",{"_index":608,"t":{"16":{"position":[[1681,7]]},"28":{"position":[[672,8]]},"34":{"position":[[1582,6]]},"58":{"position":[[1694,6]]},"74":{"position":[[766,7]]},"132":{"position":[[885,7],[934,8]]},"190":{"position":[[446,6],[500,8]]},"262":{"position":[[1022,6]]},"313":{"position":[[516,8]]},"319":{"position":[[276,8]]},"329":{"position":[[159,8]]},"333":{"position":[[106,8]]},"349":{"position":[[1161,7]]},"477":{"position":[[2514,6],[3065,6]]},"587":{"position":[[94,8]]},"593":{"position":[[23,7]]},"598":{"position":[[148,8]]},"608":{"position":[[456,6]]},"610":{"position":[[30,8]]},"618":{"position":[[200,7],[222,7],[253,7],[286,7],[309,7]]},"1281":{"position":[[1312,7]]},"1359":{"position":[[1432,6],[1530,6]]},"1367":{"position":[[552,6]]},"1373":{"position":[[46,8]]},"1375":{"position":[[65,8],[319,8]]},"1411":{"position":[[317,6]]},"1475":{"position":[[22,8],[487,6],[591,6],[614,6]]},"1678":{"position":[[477,7]]},"1688":{"position":[[933,6]]},"1698":{"position":[[150,6]]},"1707":{"position":[[0,7],[238,7],[318,6],[378,6]]},"1711":{"position":[[150,6]]},"1715":{"position":[[124,7],[272,6]]},"2088":{"position":[[346,7]]},"2094":{"position":[[340,7]]},"2148":{"position":[[619,7]]},"2307":{"position":[[22,8],[487,6],[591,6],[614,6]]},"2400":{"position":[[1709,8]]},"2530":{"position":[[1432,6],[1530,6]]},"2538":{"position":[[552,6]]},"2544":{"position":[[46,8]]},"2546":{"position":[[65,8],[319,8]]},"2578":{"position":[[208,6]]},"2580":{"position":[[38,7],[61,7],[181,7],[223,7],[260,7],[342,7],[385,7],[424,7]]},"2632":{"position":[[5053,6]]},"2634":{"position":[[439,7],[540,8]]},"2842":{"position":[[477,7]]},"2852":{"position":[[933,6]]},"2862":{"position":[[158,6]]},"2869":{"position":[[719,6]]},"2871":{"position":[[0,7],[238,7],[318,6]]},"2875":{"position":[[150,6]]},"2879":{"position":[[124,7],[272,6]]},"3273":{"position":[[346,7]]},"3279":{"position":[[340,7]]},"3377":{"position":[[619,7]]},"3520":{"position":[[22,8],[180,6],[277,6],[300,6]]},"3643":{"position":[[5053,6]]},"3645":{"position":[[439,7],[540,8]]}}}],["removed/switch",{"_index":2723,"t":{"272":{"position":[[939,17]]},"1682":{"position":[[1052,17]]},"2846":{"position":[[1052,17]]}}}],["removehistory(ch",{"_index":1415,"t":{"78":{"position":[[1100,16]]}}}],["removepresence(ch",{"_index":1425,"t":{"78":{"position":[[1886,17]]}}}],["removesubscription(sub",{"_index":5401,"t":{"2634":{"position":[[415,23]]},"3645":{"position":[[415,23]]}}}],["renam",{"_index":911,"t":{"28":{"position":[[653,8]]},"598":{"position":[[81,7]]},"604":{"position":[[129,6],[165,6]]},"618":{"position":[[324,7],[374,7],[413,7],[462,7],[526,7],[590,7],[649,7],[721,7],[769,7],[820,7],[867,7],[914,7],[2490,7],[2539,7],[2591,7]]},"1375":{"position":[[54,7],[98,7]]},"1377":{"position":[[109,7]]},"2546":{"position":[[54,7],[98,7]]},"2548":{"position":[[109,7]]},"2580":{"position":[[281,7]]}}}],["render",{"_index":1970,"t":{"156":{"position":[[586,9],[614,7]]},"353":{"position":[[377,6]]},"798":{"position":[[1473,9]]},"1269":{"position":[[528,9]]},"1285":{"position":[[377,6]]},"1813":{"position":[[1473,9]]},"2408":{"position":[[383,6]]},"2518":{"position":[[528,9]]},"3042":{"position":[[1425,9]]}}}],["render(request",{"_index":2184,"t":{"192":{"position":[[1472,15]]},"194":{"position":[[2053,15],[2124,15]]}}}],["renderdef",{"_index":2182,"t":{"192":{"position":[[1439,9]]},"194":{"position":[[2020,9]]}}}],["renew",{"_index":4292,"t":{"1057":{"position":[[1252,8]]},"2140":{"position":[[1252,8]]},"3355":{"position":[[1252,8]]}}}],["repartit",{"_index":5239,"t":{"2422":{"position":[[925,14]]}}}],["repeat",{"_index":4958,"t":{"1705":{"position":[[182,8],[228,8],[283,8],[922,8]]},"1707":{"position":[[192,8],[252,8]]},"1709":{"position":[[144,8],[211,8],[296,8],[368,8],[437,8],[983,8]]},"1711":{"position":[[171,8]]},"1713":{"position":[[100,8],[174,8],[266,8],[345,8],[421,8],[487,8],[910,8]]},"1715":{"position":[[293,8]]},"1717":{"position":[[91,8],[150,8],[471,8]]},"1719":{"position":[[528,8],[607,8],[676,8],[844,8],[1013,8]]},"2869":{"position":[[182,8],[228,8],[740,8]]},"2871":{"position":[[192,8],[252,8]]},"2873":{"position":[[670,8],[737,8],[816,8],[885,8],[951,8],[1067,8]]},"2875":{"position":[[171,8]]},"2877":{"position":[[555,8],[629,8],[715,8],[791,8],[857,8],[1034,8]]},"2879":{"position":[[293,8]]},"2881":{"position":[[427,8],[486,8],[654,8]]},"2883":{"position":[[873,8],[1041,8],[1210,8]]}}}],["replac",{"_index":659,"t":{"16":{"position":[[3616,9]]},"28":{"position":[[940,11]]},"78":{"position":[[3758,7]]},"124":{"position":[[1804,9]]},"178":{"position":[[765,7]]},"198":{"position":[[1538,8]]},"202":{"position":[[75,7],[479,7],[908,7]]},"224":{"position":[[634,7]]},"477":{"position":[[2854,7]]},"479":{"position":[[6228,8]]},"598":{"position":[[183,12]]},"612":{"position":[[359,8]]},"878":{"position":[[983,7]]},"1023":{"position":[[610,11]]},"1703":{"position":[[527,8]]},"1991":{"position":[[983,7]]},"2168":{"position":[[297,8]]},"2189":{"position":[[1286,8]]},"2360":{"position":[[610,11]]},"2422":{"position":[[259,12]]},"2578":{"position":[[497,7],[545,7]]},"2867":{"position":[[527,8]]},"3180":{"position":[[983,7]]},"3397":{"position":[[297,8]]},"3492":{"position":[[1286,8]]}}}],["repli",{"_index":1864,"t":{"128":{"position":[[1369,5]]},"134":{"position":[[375,5]]},"341":{"position":[[713,9]]},"473":{"position":[[2457,6]]},"698":{"position":[[61,7],[186,5],[358,7]]},"756":{"position":[[69,7]]},"792":{"position":[[1336,5]]},"798":{"position":[[889,6]]},"840":{"position":[[455,5]]},"973":{"position":[[1496,6],[2623,6],[3237,5]]},"1023":{"position":[[3174,6],[3257,6]]},"1211":{"position":[[713,9]]},"1269":{"position":[[145,5]]},"1405":{"position":[[201,5],[310,5],[433,6],[1345,5],[1746,5],[1938,7],[2027,7],[2200,5],[2271,9],[2474,7],[2526,5],[2754,5],[2930,5],[3072,5],[3204,5],[3483,5],[3505,6],[3517,7],[3582,7]]},"1407":{"position":[[1043,6]]},"1409":{"position":[[336,5],[1168,5],[1330,5],[1386,5]]},"1413":{"position":[[574,5],[721,5]]},"1417":{"position":[[520,5]]},"1419":{"position":[[124,7],[264,5]]},"1425":{"position":[[2680,7]]},"1427":{"position":[[511,5]]},"1433":{"position":[[154,5]]},"1521":{"position":[[79,7],[110,7]]},"1807":{"position":[[863,5]]},"1813":{"position":[[889,6]]},"1857":{"position":[[547,5]]},"1933":{"position":[[61,7],[186,5],[358,7]]},"1983":{"position":[[1668,6],[2283,5],[2908,5]]},"2148":{"position":[[202,7]]},"2150":{"position":[[72,5],[183,5],[301,7],[353,5],[582,5],[806,5],[911,5],[1023,5],[1261,5]]},"2152":{"position":[[20,5],[42,6],[54,7],[119,7]]},"2154":{"position":[[148,6],[1035,5],[1442,5],[1585,5],[1736,7],[1825,7],[1998,5],[2069,9]]},"2156":{"position":[[129,7]]},"2162":{"position":[[156,5]]},"2172":{"position":[[192,5]]},"2174":{"position":[[221,5]]},"2360":{"position":[[3106,6],[3189,6]]},"2394":{"position":[[713,9]]},"2518":{"position":[[145,5]]},"2704":{"position":[[79,7],[110,7]]},"2978":{"position":[[61,7],[186,5],[358,7]]},"3036":{"position":[[941,5]]},"3088":{"position":[[547,5]]},"3142":{"position":[[1668,6],[2283,5],[2908,5]]},"3324":{"position":[[2297,7]]},"3367":{"position":[[4039,8]]},"3377":{"position":[[202,7]]},"3379":{"position":[[72,5],[183,5],[301,7],[353,5],[582,5],[806,5],[911,5],[1023,5],[1261,5]]},"3381":{"position":[[20,5],[42,6],[54,7],[119,7]]},"3383":{"position":[[148,6],[1035,5],[1442,5],[1585,5],[1736,7],[1825,7],[1998,5],[2069,9]]},"3385":{"position":[[129,7]]},"3391":{"position":[[156,5]]},"3401":{"position":[[192,5]]},"3403":{"position":[[221,5]]},"3421":{"position":[[2909,6],[2992,6]]},"3526":{"position":[[151,6],[528,11]]}}}],["replic",{"_index":1025,"t":{"38":{"position":[[1704,13]]},"258":{"position":[[1906,9]]},"347":{"position":[[1296,11],[1378,11]]},"1203":{"position":[[1501,11],[1959,12]]},"1205":{"position":[[444,13]]},"1279":{"position":[[1301,11],[1383,11]]},"2106":{"position":[[1501,11],[1959,12]]},"2108":{"position":[[444,13]]},"2402":{"position":[[1396,11],[1471,11]]},"2420":{"position":[[386,9]]},"3289":{"position":[[1501,11],[1959,12]]},"3291":{"position":[[444,13]]}}}],["replica",{"_index":3174,"t":{"473":{"position":[[153,7]]},"1203":{"position":[[1315,7]]},"1205":{"position":[[356,9]]},"2106":{"position":[[1315,7]]},"2108":{"position":[[356,9]]},"3289":{"position":[[1315,7]]},"3291":{"position":[[356,9]]}}}],["replica(",{"_index":3203,"t":{"473":{"position":[[3222,10]]}}}],["replicatedmergetree('/clickhouse/tables/{cluster}/{shard}/connect",{"_index":3783,"t":{"674":{"position":[[446,71]]},"1605":{"position":[[366,71]]},"2790":{"position":[[366,71]]}}}],["replicatedmergetree('/clickhouse/tables/{cluster}/{shard}/oper",{"_index":3797,"t":{"676":{"position":[[502,70]]},"1609":{"position":[[493,70]]},"2794":{"position":[[493,70]]}}}],["replication\\r\\n",{"_index":3189,"t":{"473":{"position":[[1181,15]]},"1195":{"position":[[730,15]]},"2096":{"position":[[730,15]]},"3281":{"position":[[730,15]]}}}],["replica}')partit",{"_index":3784,"t":{"674":{"position":[[518,21]]},"676":{"position":[[573,21]]},"1605":{"position":[[438,21]]},"1609":{"position":[[564,21]]},"2790":{"position":[[438,21]]},"2794":{"position":[[564,21]]}}}],["replies.push(repli",{"_index":4496,"t":{"1405":{"position":[[2239,20]]},"2154":{"position":[[2037,20]]},"3383":{"position":[[2037,20]]}}}],["reply.app",{"_index":2985,"t":{"341":{"position":[[637,9]]},"1211":{"position":[[637,9]]},"2394":{"position":[[637,9]]}}}],["reply=\"id:2",{"_index":4800,"t":{"1505":{"position":[[7393,11]]},"2686":{"position":[[7473,11]]}}}],["replycentrifugo",{"_index":2986,"t":{"341":{"position":[[678,15]]},"1211":{"position":[[678,15]]},"2394":{"position":[[678,15]]}}}],["repo",{"_index":794,"t":{"18":{"position":[[2884,4]]},"88":{"position":[[55,5]]},"122":{"position":[[1151,4]]},"160":{"position":[[128,5]]},"244":{"position":[[619,4]]},"333":{"position":[[201,4]]},"479":{"position":[[1880,5],[1929,4]]},"539":{"position":[[80,4]]},"644":{"position":[[61,5]]},"949":{"position":[[543,4]]},"1203":{"position":[[1741,4],[1847,4]]},"1555":{"position":[[80,4]]},"1919":{"position":[[543,4]]},"2106":{"position":[[1741,4],[1847,4]]},"2724":{"position":[[80,4]]},"3146":{"position":[[543,4]]},"3289":{"position":[[1741,4],[1847,4]]},"3373":{"position":[[126,5]]}}}],["report",{"_index":1483,"t":{"86":{"position":[[178,6]]},"274":{"position":[[517,8]]},"553":{"position":[[268,9]]},"1527":{"position":[[265,9]]},"2104":{"position":[[296,8]]},"2760":{"position":[[265,9]]},"3287":{"position":[[318,8]]}}}],["repositori",{"_index":3597,"t":{"543":{"position":[[172,11]]},"1031":{"position":[[230,11]]},"1559":{"position":[[172,11]]},"2370":{"position":[[230,11]]},"2728":{"position":[[172,11]]},"3367":{"position":[[1682,11]]},"3431":{"position":[[230,11]]}}}],["repres",{"_index":1642,"t":{"104":{"position":[[402,10]]},"475":{"position":[[552,10]]},"943":{"position":[[268,10]]},"2064":{"position":[[268,10]]},"3022":{"position":[[5,10]]},"3259":{"position":[[268,10]]}}}],["represent",{"_index":3932,"t":{"788":{"position":[[138,14]]},"1485":{"position":[[118,14]]},"1803":{"position":[[138,14]]},"1841":{"position":[[65,15]]},"2168":{"position":[[243,14],[803,15],[1335,14]]},"2317":{"position":[[118,14]]},"3032":{"position":[[148,14]]},"3070":{"position":[[65,15]]},"3397":{"position":[[243,14],[803,15],[1335,14]]},"3530":{"position":[[118,14]]}}}],["reproduc",{"_index":208,"t":{"2":{"position":[[3616,9]]},"1199":{"position":[[1219,9]]},"2100":{"position":[[1219,9]]},"3285":{"position":[[1219,9]]}}}],["req",{"_index":1554,"t":{"98":{"position":[[408,3],[601,3],[1346,3],[2093,3]]},"246":{"position":[[721,5],[1995,5],[2519,5]]},"250":{"position":[[1663,5]]},"479":{"position":[[5763,3]]},"3367":{"position":[[2661,3],[2835,4]]},"3369":{"position":[[1951,4],[2197,4]]}}}],["req.body.password",{"_index":2594,"t":{"246":{"position":[[2050,17]]}}}],["req.body.usernam",{"_index":2593,"t":{"246":{"position":[[2014,18],[2106,18]]}}}],["req.publication.data",{"_index":5708,"t":{"3369":{"position":[[2228,20]]}}}],["req.session.destroy",{"_index":2599,"t":{"246":{"position":[[2535,22]]}}}],["req.session.userid",{"_index":2558,"t":{"246":{"position":[[740,20],[2085,18]]},"250":{"position":[[1682,20],[1732,18]]}}}],["req.subscriberequest",{"_index":5707,"t":{"3369":{"position":[[2017,21]]}}}],["request",{"_index":757,"t":{"18":{"position":[[862,8],[1269,7]]},"32":{"position":[[903,8]]},"42":{"position":[[480,7]]},"70":{"position":[[33,7],[240,7],[740,7],[819,7]]},"76":{"position":[[1570,7]]},"82":{"position":[[1697,7]]},"108":{"position":[[922,7]]},"122":{"position":[[393,7]]},"124":{"position":[[1638,8],[2509,8],[2713,8],[4214,10]]},"132":{"position":[[1185,8]]},"134":{"position":[[1223,7]]},"142":{"position":[[295,7]]},"156":{"position":[[853,10]]},"168":{"position":[[310,8]]},"170":{"position":[[622,8]]},"172":{"position":[[180,7],[590,7]]},"176":{"position":[[392,7]]},"178":{"position":[[1039,8]]},"198":{"position":[[2010,8]]},"200":{"position":[[1309,7],[1680,7]]},"202":{"position":[[1114,8]]},"212":{"position":[[682,7]]},"218":{"position":[[143,7]]},"220":{"position":[[63,8]]},"222":{"position":[[536,8]]},"236":{"position":[[171,8],[1035,8]]},"244":{"position":[[347,8]]},"246":{"position":[[2482,8]]},"250":{"position":[[337,8],[532,8]]},"252":{"position":[[286,8]]},"262":{"position":[[139,7]]},"270":{"position":[[124,7],[1031,8],[2001,8]]},"280":{"position":[[2704,7]]},"287":{"position":[[400,7]]},"291":{"position":[[651,7]]},"293":{"position":[[112,10],[773,7]]},"319":{"position":[[161,9]]},"321":{"position":[[73,8],[167,7],[339,7],[508,7],[583,7],[1466,7]]},"325":{"position":[[87,9],[221,7]]},"341":{"position":[[496,7]]},"345":{"position":[[365,8],[929,8],[972,7],[1303,7]]},"351":{"position":[[136,8]]},"357":{"position":[[190,8]]},"371":{"position":[[94,7],[187,7],[206,7]]},"379":{"position":[[201,7]]},"411":{"position":[[137,8]]},"429":{"position":[[460,7],[765,7],[811,7],[1160,7]]},"431":{"position":[[179,8]]},"469":{"position":[[286,7]]},"471":{"position":[[993,8],[1120,8],[3821,8],[3949,8]]},"473":{"position":[[2040,8],[2074,7],[2152,8]]},"475":{"position":[[1136,8],[3088,7]]},"477":{"position":[[1492,7],[1545,7],[2956,7]]},"479":{"position":[[1730,9],[2091,9],[2180,8],[2263,8],[2489,8],[2610,8],[5089,8],[5275,7]]},"493":{"position":[[671,7]]},"497":{"position":[[808,8]]},"555":{"position":[[301,8]]},"563":{"position":[[109,8]]},"608":{"position":[[278,7]]},"618":{"position":[[158,8]]},"636":{"position":[[513,7]]},"638":{"position":[[390,7]]},"640":{"position":[[178,8],[279,8]]},"648":{"position":[[49,8]]},"656":{"position":[[105,7]]},"658":{"position":[[105,7]]},"678":{"position":[[271,8]]},"688":{"position":[[339,7]]},"690":{"position":[[246,7]]},"692":{"position":[[204,7]]},"702":{"position":[[65,7]]},"714":{"position":[[25,9],[45,7],[134,7]]},"722":{"position":[[30,10],[56,8],[92,7]]},"734":{"position":[[25,9],[68,7]]},"764":{"position":[[25,9],[45,7]]},"776":{"position":[[1004,9]]},"798":{"position":[[1186,7],[1270,7],[1746,7],[1921,7]]},"810":{"position":[[666,7]]},"818":{"position":[[388,7]]},"892":{"position":[[316,7],[401,8],[923,8]]},"912":{"position":[[259,8],[771,9]]},"926":{"position":[[197,8]]},"937":{"position":[[132,9],[479,9]]},"953":{"position":[[347,9]]},"989":{"position":[[421,7]]},"1011":{"position":[[30,8],[130,8],[194,7]]},"1013":{"position":[[90,7],[111,7],[458,7],[748,8]]},"1015":{"position":[[119,8],[308,8]]},"1017":{"position":[[174,8],[393,8],[486,7],[545,7],[1053,7],[1125,7],[1352,7],[1604,8],[2270,7],[2368,8],[3437,8],[3537,7],[3632,8],[4719,7]]},"1019":{"position":[[406,7],[657,7],[1706,7]]},"1021":{"position":[[359,8],[635,7],[1690,7]]},"1023":{"position":[[178,8],[1718,8],[1959,7],[2619,7],[3956,7]]},"1025":{"position":[[241,7],[496,7],[1002,8],[1733,8],[1993,7],[3292,7]]},"1027":{"position":[[376,7]]},"1029":{"position":[[615,7]]},"1037":{"position":[[282,7],[325,7],[361,7],[405,7]]},"1039":{"position":[[600,8],[932,8]]},"1045":{"position":[[1437,7]]},"1049":{"position":[[990,8]]},"1051":{"position":[[339,8],[411,8]]},"1057":{"position":[[1175,7]]},"1077":{"position":[[233,8]]},"1090":{"position":[[399,8],[459,7]]},"1098":{"position":[[52,8],[801,9],[872,8],[1025,7]]},"1107":{"position":[[66,7],[105,8]]},"1175":{"position":[[66,7]]},"1177":{"position":[[720,8]]},"1191":{"position":[[1204,8]]},"1211":{"position":[[496,7]]},"1235":{"position":[[137,8]]},"1247":{"position":[[486,7]]},"1255":{"position":[[460,7],[765,7],[811,7],[1160,7]]},"1257":{"position":[[179,8]]},"1269":{"position":[[310,7]]},"1277":{"position":[[365,8],[929,8],[972,7],[1303,7]]},"1283":{"position":[[136,8]]},"1289":{"position":[[190,8]]},"1329":{"position":[[94,7],[200,7],[219,7]]},"1337":{"position":[[190,7]]},"1365":{"position":[[228,7]]},"1387":{"position":[[672,7]]},"1391":{"position":[[804,8]]},"1403":{"position":[[1446,8]]},"1405":{"position":[[330,7],[1367,7],[2699,7]]},"1423":{"position":[[751,8],[1128,7]]},"1425":{"position":[[2606,8]]},"1453":{"position":[[69,7],[106,7],[365,9]]},"1455":{"position":[[193,7],[1271,7]]},"1457":{"position":[[304,7],[322,8],[2107,7]]},"1459":{"position":[[455,7]]},"1473":{"position":[[143,7],[1114,7]]},"1481":{"position":[[51,7]]},"1483":{"position":[[15,7],[110,7],[296,8]]},"1487":{"position":[[362,8]]},"1505":{"position":[[4822,7],[5121,7],[9066,7],[9923,7]]},"1529":{"position":[[301,8]]},"1591":{"position":[[178,8],[279,8],[450,8],[472,8]]},"1597":{"position":[[1205,7]]},"1599":{"position":[[62,8]]},"1615":{"position":[[271,8]]},"1636":{"position":[[513,7]]},"1639":{"position":[[390,7]]},"1643":{"position":[[49,8]]},"1652":{"position":[[105,7]]},"1654":{"position":[[105,7]]},"1666":{"position":[[1587,7]]},"1668":{"position":[[415,8]]},"1670":{"position":[[571,8],[786,8]]},"1672":{"position":[[1028,7]]},"1692":{"position":[[266,9]]},"1703":{"position":[[57,8]]},"1705":{"position":[[137,8]]},"1707":{"position":[[142,8]]},"1709":{"position":[[60,7],[99,8]]},"1711":{"position":[[56,8]]},"1713":{"position":[[48,8]]},"1715":{"position":[[185,8]]},"1717":{"position":[[44,8]]},"1719":{"position":[[126,7],[257,8],[1946,7]]},"1721":{"position":[[628,8],[718,8],[816,8]]},"1731":{"position":[[339,7]]},"1733":{"position":[[246,7]]},"1735":{"position":[[204,7]]},"1771":{"position":[[531,8]]},"1775":{"position":[[328,8],[537,8]]},"1777":{"position":[[474,8],[687,8]]},"1791":{"position":[[1004,9]]},"1813":{"position":[[1186,7],[1270,7],[1746,7],[1921,7]]},"1825":{"position":[[844,7]]},"1847":{"position":[[670,7]]},"1855":{"position":[[851,7]]},"1923":{"position":[[347,9]]},"1937":{"position":[[62,7]]},"1949":{"position":[[23,8],[42,7],[131,7]]},"1957":{"position":[[68,8],[104,7]]},"1965":{"position":[[1409,9]]},"1967":{"position":[[219,8]]},"1983":{"position":[[4271,8]]},"2005":{"position":[[316,7],[401,8],[923,8]]},"2021":{"position":[[212,7]]},"2023":{"position":[[99,8],[236,8]]},"2027":{"position":[[259,8],[771,9]]},"2047":{"position":[[197,8]]},"2058":{"position":[[132,9],[479,9]]},"2090":{"position":[[1204,8]]},"2118":{"position":[[380,7]]},"2140":{"position":[[1175,7]]},"2150":{"position":[[527,7],[1864,7],[1933,7],[2001,7]]},"2154":{"position":[[37,7],[1057,7]]},"2160":{"position":[[751,8],[1128,7]]},"2179":{"position":[[138,8]]},"2181":{"position":[[66,7]]},"2185":{"position":[[405,8],[469,7]]},"2197":{"position":[[233,8]]},"2211":{"position":[[141,8]]},"2213":{"position":[[66,7],[104,8]]},"2226":{"position":[[66,7]]},"2228":{"position":[[720,8]]},"2234":{"position":[[52,8],[801,9],[872,8],[1025,7]]},"2241":{"position":[[66,7],[104,8]]},"2285":{"position":[[69,7],[106,7],[374,9]]},"2287":{"position":[[196,7],[1274,7]]},"2289":{"position":[[364,7],[382,8],[2161,7]]},"2291":{"position":[[455,7]]},"2305":{"position":[[143,7],[1114,7]]},"2313":{"position":[[51,7],[245,7]]},"2315":{"position":[[15,7],[110,7],[296,8]]},"2319":{"position":[[362,8]]},"2348":{"position":[[30,8],[130,8],[194,7]]},"2350":{"position":[[90,7],[111,7],[458,7],[748,8]]},"2352":{"position":[[119,8],[308,8]]},"2354":{"position":[[174,8],[393,8],[486,7],[545,7],[1170,7],[1242,7],[1469,7],[1721,8],[2387,7],[2485,8],[3554,8],[3654,7],[3749,8],[4827,7]]},"2356":{"position":[[406,7],[657,7],[1697,7]]},"2358":{"position":[[359,8],[635,7],[1681,7]]},"2360":{"position":[[178,8],[1718,8],[1959,7],[2619,7],[3978,7]]},"2362":{"position":[[241,7],[496,7],[1002,8],[1733,8],[1993,7],[3283,7]]},"2364":{"position":[[966,8],[1293,8],[1779,7],[2058,7],[3168,7]]},"2366":{"position":[[334,8]]},"2368":{"position":[[814,8]]},"2376":{"position":[[282,7],[325,7],[361,7],[405,7]]},"2378":{"position":[[600,8],[932,8]]},"2384":{"position":[[1437,7]]},"2388":{"position":[[1206,8]]},"2390":{"position":[[339,8],[411,8]]},"2394":{"position":[[496,7]]},"2400":{"position":[[798,8],[2336,8],[2461,7],[2479,8],[2526,7]]},"2406":{"position":[[137,8]]},"2412":{"position":[[194,8]]},"2424":{"position":[[34,8]]},"2444":{"position":[[100,7],[208,7],[227,7]]},"2452":{"position":[[190,7]]},"2484":{"position":[[137,8]]},"2496":{"position":[[486,7]]},"2504":{"position":[[460,7],[765,7],[811,7],[1160,7]]},"2506":{"position":[[179,8]]},"2512":{"position":[[1777,8]]},"2518":{"position":[[310,7]]},"2536":{"position":[[228,7]]},"2558":{"position":[[700,7]]},"2562":{"position":[[823,8]]},"2578":{"position":[[415,8],[505,7]]},"2624":{"position":[[423,7]]},"2640":{"position":[[266,8]]},"2642":{"position":[[160,7],[210,7],[285,7]]},"2686":{"position":[[4884,7],[5183,7],[9193,7],[10068,7]]},"2708":{"position":[[1449,7]]},"2712":{"position":[[264,7],[988,9]]},"2762":{"position":[[972,7]]},"2764":{"position":[[300,8]]},"2780":{"position":[[1194,7]]},"2782":{"position":[[62,8]]},"2784":{"position":[[178,8],[279,8],[450,8],[472,8]]},"2800":{"position":[[271,8]]},"2823":{"position":[[502,7]]},"2826":{"position":[[379,7]]},"2830":{"position":[[1590,7]]},"2832":{"position":[[415,8]]},"2834":{"position":[[571,8],[786,8]]},"2836":{"position":[[1028,7]]},"2856":{"position":[[266,9]]},"2862":{"position":[[364,8],[779,7]]},"2867":{"position":[[57,8]]},"2869":{"position":[[137,8]]},"2871":{"position":[[142,8]]},"2873":{"position":[[60,7],[99,8]]},"2875":{"position":[[56,8]]},"2877":{"position":[[48,8]]},"2879":{"position":[[185,8]]},"2881":{"position":[[44,8]]},"2883":{"position":[[126,7],[257,8],[2225,7]]},"2885":{"position":[[96,8]]},"2887":{"position":[[628,8],[718,8],[816,8]]},"2895":{"position":[[49,8]]},"2904":{"position":[[94,7]]},"2906":{"position":[[94,7]]},"2920":{"position":[[328,7]]},"2922":{"position":[[235,7]]},"2924":{"position":[[193,7]]},"2930":{"position":[[531,8]]},"2934":{"position":[[328,8],[537,8]]},"2936":{"position":[[474,8],[687,8]]},"2982":{"position":[[62,7]]},"2994":{"position":[[23,8],[42,7],[131,7]]},"3002":{"position":[[68,8],[104,7]]},"3010":{"position":[[1409,9]]},"3012":{"position":[[219,8]]},"3020":{"position":[[1030,9]]},"3042":{"position":[[1284,7]]},"3054":{"position":[[844,7]]},"3060":{"position":[[246,7]]},"3076":{"position":[[659,7]]},"3084":{"position":[[840,7]]},"3142":{"position":[[4271,8]]},"3150":{"position":[[347,9]]},"3194":{"position":[[316,7],[401,8],[923,8]]},"3210":{"position":[[185,7]]},"3212":{"position":[[72,8],[209,8]]},"3216":{"position":[[259,8],[771,9]]},"3220":{"position":[[238,7],[435,8],[762,9],[1187,8]]},"3240":{"position":[[197,8]]},"3253":{"position":[[132,9],[479,9]]},"3275":{"position":[[1204,8]]},"3327":{"position":[[72,8],[812,8]]},"3341":{"position":[[383,7],[736,7]]},"3355":{"position":[[1175,7]]},"3365":{"position":[[501,8],[977,9]]},"3367":{"position":[[2825,9]]},"3369":{"position":[[1998,7]]},"3379":{"position":[[527,7],[1864,7],[1933,7],[2001,7]]},"3383":{"position":[[37,7],[1057,7]]},"3389":{"position":[[751,8],[1128,7]]},"3409":{"position":[[35,8],[135,8],[199,7]]},"3411":{"position":[[90,7],[111,7],[458,7],[748,8],[938,9],[1328,7]]},"3413":{"position":[[119,8],[308,8]]},"3415":{"position":[[174,8],[393,8],[486,7],[545,7],[1170,7],[1242,7],[1469,7],[1721,8],[2387,7],[2485,8],[3554,8],[3654,7],[3749,8],[4856,7]]},"3417":{"position":[[406,7],[657,7],[1697,7]]},"3419":{"position":[[406,8],[682,7],[1728,7]]},"3421":{"position":[[178,8],[619,8],[666,8],[1521,8],[1762,7],[2422,7],[3981,7]]},"3423":{"position":[[241,7],[496,7],[1002,8],[1733,8],[1993,7],[3283,7]]},"3425":{"position":[[939,8],[1266,8],[1752,7],[2031,7],[3160,7]]},"3427":{"position":[[334,8]]},"3429":{"position":[[814,8]]},"3437":{"position":[[282,7],[325,7],[361,7],[405,7]]},"3439":{"position":[[600,8],[932,8]]},"3445":{"position":[[1437,7],[1632,9],[1689,8]]},"3449":{"position":[[1206,8]]},"3451":{"position":[[339,8],[411,8]]},"3456":{"position":[[138,8],[379,8],[780,8]]},"3458":{"position":[[66,7]]},"3462":{"position":[[440,8],[504,7]]},"3472":{"position":[[233,8]]},"3486":{"position":[[141,8],[358,8],[760,8]]},"3488":{"position":[[66,7],[104,8]]},"3496":{"position":[[60,7],[113,7],[335,7],[567,8]]},"3498":{"position":[[196,7]]},"3502":{"position":[[261,7],[527,8],[1061,7],[1445,8],[1669,7]]},"3504":{"position":[[138,7],[272,8],[530,7]]},"3506":{"position":[[133,8]]},"3508":{"position":[[66,8]]},"3510":{"position":[[57,8]]},"3512":{"position":[[107,8]]},"3514":{"position":[[291,7],[647,8]]},"3516":{"position":[[173,7],[343,8]]},"3518":{"position":[[143,7],[506,7],[782,8],[956,7]]},"3520":{"position":[[187,8]]},"3522":{"position":[[118,7],[191,8]]},"3524":{"position":[[300,8]]},"3526":{"position":[[42,8],[289,8],[339,7]]},"3528":{"position":[[15,7],[110,7],[296,8]]},"3532":{"position":[[362,8]]},"3538":{"position":[[820,7],[1102,8],[1208,7],[1498,9],[1722,7]]},"3576":{"position":[[66,7]]},"3578":{"position":[[720,8]]},"3584":{"position":[[52,8],[801,9],[872,8],[1025,7]]},"3591":{"position":[[66,7],[104,8]]},"3635":{"position":[[423,7]]},"3651":{"position":[[266,8]]},"3653":{"position":[[160,7],[210,7],[285,7]]}}}],["request.what",{"_index":1581,"t":{"98":{"position":[[1480,12]]}}}],["request/result",{"_index":4242,"t":{"1039":{"position":[[47,14]]},"2378":{"position":[[47,14]]},"3439":{"position":[[47,14]]}}}],["requesta",{"_index":1600,"t":{"98":{"position":[[2046,8]]}}}],["requestcont",{"_index":5745,"t":{"3538":{"position":[[1377,14]]}}}],["requestdata[\"messag",{"_index":2046,"t":{"174":{"position":[[414,24]]}}}],["requestnote:cli",{"_index":2982,"t":{"341":{"position":[[409,18],[802,18],[1244,18]]},"1211":{"position":[[409,18],[802,18],[1244,18]]},"2394":{"position":[[409,18],[802,18],[1244,18]]}}}],["requests\"temporari",{"_index":5080,"t":{"1957":{"position":[[28,19]]},"3002":{"position":[[28,19]]}}}],["requests.post(\"https://centrifuge.example.com/api",{"_index":4564,"t":{"1457":{"position":[[611,51]]},"2289":{"position":[[671,51]]}}}],["requests.post(\"https://centrifuge.example.com/api/publish",{"_index":5729,"t":{"3502":{"position":[[748,59]]}}}],["requests/sec",{"_index":3640,"t":{"567":{"position":[[229,12]]},"1535":{"position":[[229,12]]},"2740":{"position":[[229,12]]}}}],["requestsapi_key",{"_index":5728,"t":{"3502":{"position":[[574,15]]}}}],["requestscentrifugo",{"_index":2981,"t":{"341":{"position":[[299,18]]},"1211":{"position":[[299,18]]},"2394":{"position":[[299,18]]}}}],["requestscommand",{"_index":4560,"t":{"1457":{"position":[[369,15]]},"2289":{"position":[[429,15]]}}}],["requir",{"_index":4,"t":{"2":{"position":[[57,12]]},"16":{"position":[[1363,12],[5667,7],[7036,13]]},"18":{"position":[[1503,8]]},"38":{"position":[[800,12]]},"46":{"position":[[222,8],[397,9]]},"62":{"position":[[699,8]]},"82":{"position":[[1983,8]]},"84":{"position":[[335,12]]},"86":{"position":[[625,8],[1246,8]]},"90":{"position":[[676,12]]},"94":{"position":[[1304,12],[2031,13]]},"124":{"position":[[2649,7],[2852,12],[4636,8]]},"132":{"position":[[1021,8]]},"158":{"position":[[475,7]]},"196":{"position":[[1587,8]]},"206":{"position":[[84,8]]},"214":{"position":[[690,8],[820,8]]},"216":{"position":[[2995,9]]},"240":{"position":[[698,7]]},"280":{"position":[[835,7]]},"295":{"position":[[1834,7]]},"333":{"position":[[596,11],[754,8]]},"347":{"position":[[27,8]]},"369":{"position":[[439,7]]},"399":{"position":[[67,8]]},"409":{"position":[[291,8]]},"411":{"position":[[655,8]]},"433":{"position":[[143,8]]},"473":{"position":[[2760,8]]},"489":{"position":[[406,8]]},"509":{"position":[[653,8]]},"511":{"position":[[447,8]]},"553":{"position":[[1023,8]]},"557":{"position":[[37,8]]},"614":{"position":[[97,8]]},"618":{"position":[[126,8]]},"632":{"position":[[1062,7]]},"636":{"position":[[702,8]]},"638":{"position":[[622,8],[800,8]]},"652":{"position":[[920,7]]},"656":{"position":[[284,8]]},"658":{"position":[[263,8]]},"688":{"position":[[510,8]]},"690":{"position":[[1099,8]]},"692":{"position":[[375,8]]},"724":{"position":[[110,8]]},"764":{"position":[[139,8]]},"768":{"position":[[108,8]]},"818":{"position":[[416,8],[717,7]]},"842":{"position":[[549,7]]},"858":{"position":[[953,9]]},"866":{"position":[[109,8]]},"989":{"position":[[0,9]]},"991":{"position":[[0,9]]},"1013":{"position":[[240,8]]},"1023":{"position":[[534,7]]},"1039":{"position":[[1113,8],[1269,8]]},"1043":{"position":[[38,8]]},"1045":{"position":[[184,8],[1095,8]]},"1059":{"position":[[138,8]]},"1061":{"position":[[186,8]]},"1077":{"position":[[476,8]]},"1086":{"position":[[101,8]]},"1090":{"position":[[381,8]]},"1092":{"position":[[173,7]]},"1134":{"position":[[98,8],[155,8]]},"1136":{"position":[[22,8]]},"1183":{"position":[[273,7]]},"1193":{"position":[[325,9]]},"1223":{"position":[[67,8]]},"1233":{"position":[[291,8]]},"1235":{"position":[[655,8]]},"1259":{"position":[[143,8]]},"1279":{"position":[[27,8]]},"1327":{"position":[[558,7]]},"1359":{"position":[[34,8]]},"1367":{"position":[[23,8],[110,8]]},"1425":{"position":[[1807,8]]},"1427":{"position":[[299,8]]},"1437":{"position":[[886,8]]},"1439":{"position":[[449,8]]},"1457":{"position":[[1914,8]]},"1459":{"position":[[228,8]]},"1461":{"position":[[96,8],[455,8],[604,8]]},"1463":{"position":[[191,8],[372,8],[460,8]]},"1465":{"position":[[154,8],[275,8],[363,8],[593,8]]},"1467":{"position":[[145,8],[251,8],[336,8]]},"1469":{"position":[[797,8]]},"1471":{"position":[[533,8]]},"1473":{"position":[[971,8],[1398,8]]},"1475":{"position":[[532,8]]},"1477":{"position":[[162,8]]},"1505":{"position":[[598,8]]},"1509":{"position":[[84,8]]},"1527":{"position":[[1507,8]]},"1531":{"position":[[37,8]]},"1599":{"position":[[121,8]]},"1631":{"position":[[1062,7]]},"1636":{"position":[[702,8]]},"1639":{"position":[[622,8],[804,8]]},"1647":{"position":[[920,7]]},"1652":{"position":[[284,8]]},"1654":{"position":[[263,8]]},"1678":{"position":[[166,8]]},"1703":{"position":[[77,8],[864,8]]},"1705":{"position":[[157,8],[630,8],[708,8],[799,8],[894,8]]},"1707":{"position":[[167,8]]},"1709":{"position":[[119,8],[956,8]]},"1711":{"position":[[76,8]]},"1713":{"position":[[68,8],[883,8],[1072,8]]},"1715":{"position":[[205,8]]},"1717":{"position":[[64,8],[613,8]]},"1719":{"position":[[282,8],[496,8],[1098,8],[1509,8],[1621,8],[1743,8]]},"1721":{"position":[[784,8],[836,8]]},"1731":{"position":[[510,8]]},"1733":{"position":[[1099,8]]},"1735":{"position":[[375,8]]},"1745":{"position":[[0,9]]},"1797":{"position":[[350,8]]},"1799":{"position":[[338,8]]},"1827":{"position":[[1349,8]]},"1831":{"position":[[1025,8]]},"1847":{"position":[[1576,9]]},"1853":{"position":[[849,9]]},"1859":{"position":[[902,7]]},"1905":{"position":[[109,8]]},"1959":{"position":[[107,8]]},"2080":{"position":[[273,7]]},"2092":{"position":[[325,9]]},"2142":{"position":[[138,8]]},"2144":{"position":[[154,8]]},"2185":{"position":[[387,8]]},"2187":{"position":[[173,7]]},"2197":{"position":[[476,8]]},"2206":{"position":[[101,8]]},"2247":{"position":[[98,8],[155,8]]},"2249":{"position":[[22,8]]},"2289":{"position":[[1968,8]]},"2291":{"position":[[228,8]]},"2293":{"position":[[96,8],[455,8],[604,8]]},"2295":{"position":[[191,8],[372,8],[460,8]]},"2297":{"position":[[154,8],[275,8],[363,8],[559,8]]},"2299":{"position":[[145,8],[251,8],[336,8]]},"2301":{"position":[[840,8]]},"2303":{"position":[[533,8]]},"2305":{"position":[[971,8],[1398,8]]},"2307":{"position":[[532,8]]},"2309":{"position":[[162,8]]},"2350":{"position":[[240,8]]},"2360":{"position":[[534,7]]},"2378":{"position":[[1113,8],[1269,8]]},"2382":{"position":[[38,8]]},"2384":{"position":[[184,8],[1095,8]]},"2392":{"position":[[622,9]]},"2402":{"position":[[27,8]]},"2422":{"position":[[955,7]]},"2442":{"position":[[684,7]]},"2472":{"position":[[67,8]]},"2482":{"position":[[291,8]]},"2484":{"position":[[655,8]]},"2508":{"position":[[143,8]]},"2530":{"position":[[34,8]]},"2538":{"position":[[23,8],[110,8]]},"2578":{"position":[[451,9]]},"2606":{"position":[[910,8]]},"2608":{"position":[[453,8]]},"2634":{"position":[[735,8]]},"2686":{"position":[[613,8]]},"2718":{"position":[[11,8]]},"2720":{"position":[[16,8]]},"2736":{"position":[[431,8]]},"2762":{"position":[[15,8],[427,9]]},"2768":{"position":[[84,8]]},"2782":{"position":[[121,8]]},"2818":{"position":[[1062,7]]},"2823":{"position":[[666,8]]},"2826":{"position":[[586,8],[768,8]]},"2842":{"position":[[166,8]]},"2867":{"position":[[77,8],[771,8]]},"2869":{"position":[[157,8],[489,8],[567,8],[660,8]]},"2871":{"position":[[167,8]]},"2873":{"position":[[119,8],[645,8],[1040,8],[1304,8]]},"2875":{"position":[[76,8]]},"2877":{"position":[[68,8],[523,8],[1007,8],[1290,8]]},"2879":{"position":[[205,8]]},"2881":{"position":[[64,8],[400,8],[768,8]]},"2883":{"position":[[282,8],[757,8],[1295,8],[1788,8],[1900,8],[2022,8]]},"2885":{"position":[[116,8]]},"2887":{"position":[[784,8],[836,8]]},"2899":{"position":[[920,7]]},"2904":{"position":[[248,8]]},"2906":{"position":[[227,8]]},"2920":{"position":[[474,8]]},"2922":{"position":[[1063,8]]},"2924":{"position":[[339,8]]},"2950":{"position":[[0,9]]},"3004":{"position":[[107,8]]},"3032":{"position":[[53,9]]},"3066":{"position":[[152,9]]},"3068":{"position":[[200,8]]},"3076":{"position":[[1540,9]]},"3082":{"position":[[849,9]]},"3090":{"position":[[902,7]]},"3162":{"position":[[109,8]]},"3265":{"position":[[273,7]]},"3277":{"position":[[325,9]]},"3324":{"position":[[1334,8]]},"3357":{"position":[[138,8]]},"3359":{"position":[[154,8]]},"3363":{"position":[[1271,7]]},"3411":{"position":[[240,8]]},"3439":{"position":[[1113,8],[1269,8]]},"3443":{"position":[[38,8]]},"3445":{"position":[[184,8],[1095,8]]},"3456":{"position":[[419,8],[818,9]]},"3462":{"position":[[422,8]]},"3464":{"position":[[173,7]]},"3472":{"position":[[476,8]]},"3481":{"position":[[101,8]]},"3486":{"position":[[398,8],[798,9]]},"3502":{"position":[[1476,8]]},"3504":{"position":[[303,8]]},"3506":{"position":[[164,8],[523,8],[672,8]]},"3508":{"position":[[97,8],[278,8],[366,8]]},"3510":{"position":[[88,8],[209,8],[297,8],[493,8]]},"3512":{"position":[[138,8],[244,8],[329,8]]},"3514":{"position":[[678,8]]},"3516":{"position":[[374,8]]},"3518":{"position":[[813,8],[1240,8]]},"3520":{"position":[[218,8]]},"3522":{"position":[[222,8]]},"3597":{"position":[[98,8],[155,8]]},"3599":{"position":[[22,8]]},"3612":{"position":[[649,9]]},"3645":{"position":[[735,8]]}}}],["require(\"cooki",{"_index":2541,"t":{"246":{"position":[[203,15]]}}}],["require('axios');const",{"_index":2546,"t":{"246":{"position":[[319,22]]}}}],["require('express",{"_index":2543,"t":{"246":{"position":[[245,16]]}}}],["require('express');const",{"_index":2539,"t":{"246":{"position":[[163,24]]}}}],["require('jose')(async",{"_index":2824,"t":{"291":{"position":[[1404,21]]},"2970":{"position":[[333,21]]},"3048":{"position":[[225,21]]},"3050":{"position":[[198,21]]}}}],["require('jose');(async",{"_index":2853,"t":{"301":{"position":[[486,22]]},"3046":{"position":[[102,22]]}}}],["require('jsonwebtoken');var",{"_index":3955,"t":{"802":{"position":[[99,27]]},"804":{"position":[[222,27]]},"806":{"position":[[195,27]]},"1817":{"position":[[99,27]]},"1819":{"position":[[222,27]]},"1821":{"position":[[195,27]]}}}],["require('morgan');const",{"_index":2545,"t":{"246":{"position":[[287,23]]}}}],["requiretransportsecur",{"_index":4699,"t":{"1491":{"position":[[537,26]]},"2323":{"position":[[537,26]]},"3536":{"position":[[537,26]]}}}],["res.error",{"_index":3334,"t":{"477":{"position":[[1181,11]]}}}],["res.json",{"_index":2626,"t":{"250":{"position":[[1705,10],[1764,10]]},"2628":{"position":[[1096,11]]},"2644":{"position":[[1078,11]]},"3639":{"position":[[1208,11]]},"3655":{"position":[[1392,11]]}}}],["res.ok",{"_index":5333,"t":{"2628":{"position":[[1018,9]]},"2644":{"position":[[1000,9]]},"3639":{"position":[[885,9]]},"3655":{"position":[[1069,9]]}}}],["res.redirect",{"_index":2595,"t":{"246":{"position":[[2125,18],[2558,21]]}}}],["res.send('invalid",{"_index":2596,"t":{"246":{"position":[[2153,17]]}}}],["res.sendfile('views/app.html",{"_index":2559,"t":{"246":{"position":[[763,30]]}}}],["res.sendfile('views/login.html",{"_index":2561,"t":{"246":{"position":[[823,32]]}}}],["res.statu",{"_index":5336,"t":{"2628":{"position":[[1070,16]]},"2644":{"position":[[1052,16]]},"3639":{"position":[[900,11],[1170,16]]},"3655":{"position":[[1084,11],[1354,16]]}}}],["resav",{"_index":2555,"t":{"246":{"position":[[540,7]]}}}],["research",{"_index":486,"t":{"12":{"position":[[278,8]]}}}],["reserv",{"_index":3624,"t":{"553":{"position":[[926,7]]},"814":{"position":[[208,8]]},"1027":{"position":[[229,8]]},"1029":{"position":[[338,8]]},"1527":{"position":[[1410,7]]},"1833":{"position":[[200,8]]},"2368":{"position":[[488,8]]},"2648":{"position":[[79,8]]},"3062":{"position":[[200,8]]},"3429":{"position":[[488,8]]},"3659":{"position":[[79,8]]}}}],["reset",{"_index":2879,"t":{"315":{"position":[[899,5],[1509,5]]},"1425":{"position":[[1436,5]]}}}],["reshard",{"_index":3013,"t":{"357":{"position":[[954,10]]},"1289":{"position":[[954,10]]},"2412":{"position":[[969,10]]}}}],["resili",{"_index":1811,"t":{"122":{"position":[[2220,10]]},"148":{"position":[[227,9]]},"3324":{"position":[[3036,10]]}}}],["resolut",{"_index":3413,"t":{"479":{"position":[[2652,11]]},"563":{"position":[[368,11]]},"941":{"position":[[169,10]]},"1189":{"position":[[980,12]]},"1666":{"position":[[399,11]]},"1668":{"position":[[729,11]]},"1670":{"position":[[1169,11]]},"2062":{"position":[[169,10]]},"2736":{"position":[[129,10],[348,10]]},"2806":{"position":[[582,11]]},"2830":{"position":[[402,11]]},"2832":{"position":[[735,11]]},"2834":{"position":[[1175,11]]},"2889":{"position":[[1142,11]]},"3257":{"position":[[169,10]]}}}],["resolv",{"_index":3344,"t":{"477":{"position":[[2758,8]]},"525":{"position":[[229,8]]},"533":{"position":[[473,9]]},"1197":{"position":[[53,8]]},"1355":{"position":[[242,8]]},"2098":{"position":[[53,8]]},"2526":{"position":[[243,10]]},"3283":{"position":[[53,8]]}}}],["resolve(data.token",{"_index":5338,"t":{"2628":{"position":[[1127,20]]},"2644":{"position":[[1109,20]]}}}],["resolve(keycloak.token",{"_index":2453,"t":{"216":{"position":[[2668,24]]}}}],["resourc",{"_index":130,"t":{"2":{"position":[[1991,8],[2066,8],[2916,9]]},"4":{"position":[[373,9]]},"12":{"position":[[613,8]]},"14":{"position":[[1375,8]]},"16":{"position":[[3379,9],[4259,8],[4416,8]]},"52":{"position":[[981,8]]},"60":{"position":[[1458,8]]},"128":{"position":[[426,8]]},"178":{"position":[[888,9]]},"287":{"position":[[1329,9]]},"301":{"position":[[219,8],[1055,9]]},"357":{"position":[[284,8]]},"483":{"position":[[1415,8]]},"503":{"position":[[36,8]]},"553":{"position":[[730,8]]},"706":{"position":[[86,8]]},"716":{"position":[[68,8]]},"766":{"position":[[68,8]]},"1289":{"position":[[284,8]]},"1397":{"position":[[36,8]]},"1527":{"position":[[1105,8]]},"1577":{"position":[[42,8]]},"1672":{"position":[[75,9]]},"1941":{"position":[[83,8]]},"1951":{"position":[[65,8]]},"2191":{"position":[[997,8]]},"2412":{"position":[[287,8]]},"2422":{"position":[[973,10]]},"2568":{"position":[[36,8]]},"2622":{"position":[[168,9],[5438,9]]},"2634":{"position":[[566,9]]},"2672":{"position":[[42,8]]},"2760":{"position":[[1203,8]]},"2836":{"position":[[75,9]]},"2889":{"position":[[854,9]]},"2986":{"position":[[83,8]]},"2996":{"position":[[65,8]]},"3363":{"position":[[43,8],[954,9],[1032,8],[1284,9]]},"3365":{"position":[[373,9],[987,8]]},"3466":{"position":[[997,8]]},"3633":{"position":[[168,9],[5438,9]]},"3645":{"position":[[566,9]]}}}],["resp",{"_index":1331,"t":{"70":{"position":[[306,4]]},"74":{"position":[[1024,4]]},"461":{"position":[[190,4],[350,4],[540,4],[724,4]]},"463":{"position":[[209,4],[249,4],[431,4],[476,4]]},"475":{"position":[[2060,4]]},"1313":{"position":[[190,4],[350,4],[540,4],[724,4]]},"1315":{"position":[[209,4],[249,4],[431,4],[476,4]]},"1487":{"position":[[549,4]]},"1489":{"position":[[942,5]]},"1491":{"position":[[810,5]]},"2319":{"position":[[549,4]]},"2321":{"position":[[942,5]]},"2323":{"position":[[810,5]]},"2600":{"position":[[190,4],[350,4],[540,4],[724,4]]},"2602":{"position":[[209,4],[249,4],[431,4],[476,4]]},"2656":{"position":[[190,4],[350,4],[540,4],[724,4]]},"2658":{"position":[[209,4],[249,4],[431,4],[476,4]]},"3343":{"position":[[269,4],[445,4],[674,4]]},"3532":{"position":[[549,4]]},"3534":{"position":[[942,5]]},"3536":{"position":[[810,5]]},"3667":{"position":[[190,4],[350,4],[540,4],[724,4]]},"3669":{"position":[[209,4],[249,4],[431,4],[476,4]]}}}],["resp.err",{"_index":3227,"t":{"475":{"position":[[2126,10]]}}}],["resp.error.cod",{"_index":4659,"t":{"1487":{"position":[[677,16]]},"2319":{"position":[[677,16]]},"3532":{"position":[[677,16]]}}}],["resp.error.messag",{"_index":4661,"t":{"1487":{"position":[[750,19]]},"2319":{"position":[[750,19]]},"3532":{"position":[[750,19]]}}}],["resp.geterror",{"_index":4686,"t":{"1489":{"position":[[1163,15],[1201,15]]},"1491":{"position":[[1022,15],[1060,15]]},"2321":{"position":[[1163,15],[1201,15]]},"2323":{"position":[[1022,15],[1060,15]]},"3534":{"position":[[1163,15],[1201,15]]},"3536":{"position":[[1022,15],[1060,15]]}}}],["resp2",{"_index":3204,"t":{"473":{"position":[[3394,5],[3517,5]]},"477":{"position":[[2824,5],[2925,5]]},"2086":{"position":[[698,5]]},"3271":{"position":[[664,5]]}}}],["resp3",{"_index":5147,"t":{"2086":{"position":[[849,5]]},"3271":{"position":[[815,5]]}}}],["respect",{"_index":4449,"t":{"1217":{"position":[[159,10]]},"2396":{"position":[[160,10]]}}}],["resperror",{"_index":4687,"t":{"1489":{"position":[[1188,9]]},"1491":{"position":[[1047,9]]},"2321":{"position":[[1188,9]]},"2323":{"position":[[1047,9]]},"3534":{"position":[[1188,9]]},"3536":{"position":[[1047,9]]}}}],["resperror.cod",{"_index":4689,"t":{"1489":{"position":[[1245,15]]},"1491":{"position":[[1104,15]]},"2321":{"position":[[1245,15]]},"2323":{"position":[[1104,15]]},"3534":{"position":[[1245,15]]},"3536":{"position":[[1104,15]]}}}],["resperror.messag",{"_index":4690,"t":{"1489":{"position":[[1261,18]]},"1491":{"position":[[1120,18]]},"2321":{"position":[[1261,18]]},"2323":{"position":[[1120,18]]},"3534":{"position":[[1261,18]]},"3536":{"position":[[1120,18]]}}}],["respond",{"_index":1859,"t":{"128":{"position":[[335,7]]},"2708":{"position":[[1868,7]]}}}],["respons",{"_index":936,"t":{"32":{"position":[[915,10]]},"40":{"position":[[469,11]]},"66":{"position":[[697,11]]},"70":{"position":[[85,8],[223,8]]},"76":{"position":[[1690,8]]},"78":{"position":[[639,11],[1611,11]]},"112":{"position":[[95,9]]},"122":{"position":[[401,8]]},"172":{"position":[[722,8]]},"200":{"position":[[1144,8],[1530,8],[1898,8]]},"208":{"position":[[167,14]]},"238":{"position":[[211,9]]},"262":{"position":[[1615,8]]},"270":{"position":[[1474,8],[1765,9]]},"280":{"position":[[2612,9]]},"317":{"position":[[1095,8]]},"331":{"position":[[460,8]]},"353":{"position":[[364,8]]},"355":{"position":[[595,8]]},"467":{"position":[[116,11],[1137,11]]},"471":{"position":[[539,8],[2473,9]]},"479":{"position":[[6429,8]]},"481":{"position":[[1851,9]]},"606":{"position":[[4,8]]},"690":{"position":[[374,8],[551,8],[663,8]]},"798":{"position":[[1323,8]]},"846":{"position":[[241,9]]},"912":{"position":[[420,8]]},"1017":{"position":[[369,8],[1979,8],[2029,8],[4015,9],[4103,8]]},"1019":{"position":[[593,8]]},"1021":{"position":[[572,8],[1469,8],[1565,8]]},"1023":{"position":[[1889,8]]},"1025":{"position":[[1930,8]]},"1027":{"position":[[356,8]]},"1029":{"position":[[595,8]]},"1039":{"position":[[433,9]]},"1090":{"position":[[411,10]]},"1285":{"position":[[364,8]]},"1287":{"position":[[595,8]]},"1405":{"position":[[368,8],[1759,8],[1903,8],[2707,8],[2829,8]]},"1407":{"position":[[992,8]]},"1409":{"position":[[296,8]]},"1411":{"position":[[253,8]]},"1415":{"position":[[74,8]]},"1457":{"position":[[1332,8]]},"1459":{"position":[[702,9],[729,9],[742,9]]},"1672":{"position":[[317,9]]},"1733":{"position":[[374,8],[551,8],[663,8]]},"1771":{"position":[[1140,8]]},"1773":{"position":[[1463,8]]},"1775":{"position":[[1119,9]]},"1777":{"position":[[273,9]]},"1813":{"position":[[1323,8]]},"2023":{"position":[[304,9]]},"2027":{"position":[[420,8]]},"2150":{"position":[[535,8],[769,9],[2176,8],[2288,9]]},"2154":{"position":[[79,8],[1455,8],[1701,8]]},"2185":{"position":[[417,10]]},"2289":{"position":[[1392,8]]},"2291":{"position":[[932,9],[959,9],[972,9]]},"2354":{"position":[[369,8],[2096,8],[2146,8],[4132,9],[4220,8],[6047,9],[6235,9]]},"2356":{"position":[[593,8]]},"2358":{"position":[[572,8],[1469,8],[1565,8]]},"2360":{"position":[[1889,8],[4135,9],[4680,8]]},"2362":{"position":[[1930,8]]},"2364":{"position":[[1990,8]]},"2366":{"position":[[291,8]]},"2368":{"position":[[771,8]]},"2378":{"position":[[433,9]]},"2400":{"position":[[2469,9]]},"2408":{"position":[[370,8]]},"2410":{"position":[[601,8]]},"2626":{"position":[[220,8]]},"2762":{"position":[[955,8]]},"2836":{"position":[[317,9]]},"2877":{"position":[[483,9]]},"2881":{"position":[[362,9]]},"2922":{"position":[[338,8],[515,8],[627,8]]},"2930":{"position":[[1140,8]]},"2932":{"position":[[1463,8]]},"2934":{"position":[[1119,9]]},"2936":{"position":[[273,9]]},"3042":{"position":[[880,9],[1296,8]]},"3212":{"position":[[277,9]]},"3216":{"position":[[420,8]]},"3324":{"position":[[2845,9],[5967,10],[6187,10]]},"3379":{"position":[[535,8],[769,9],[2176,8],[2288,9]]},"3383":{"position":[[79,8],[1455,8],[1701,8]]},"3415":{"position":[[369,8],[2096,8],[2146,8],[4161,9],[4249,8],[6076,9],[6264,9]]},"3417":{"position":[[593,8]]},"3419":{"position":[[619,8],[1516,8],[1612,8]]},"3421":{"position":[[1692,8],[4138,9],[4683,8]]},"3423":{"position":[[1930,8]]},"3425":{"position":[[1963,8]]},"3427":{"position":[[291,8]]},"3429":{"position":[[771,8]]},"3439":{"position":[[433,9]]},"3462":{"position":[[452,10]]},"3502":{"position":[[413,8],[884,8],[1179,8]]},"3504":{"position":[[1007,9],[1034,9],[1047,9]]},"3514":{"position":[[380,9]]},"3516":{"position":[[268,9]]},"3518":{"position":[[606,9]]},"3524":{"position":[[79,9]]},"3526":{"position":[[516,9]]},"3538":{"position":[[253,8],[560,9],[877,8],[1151,8],[1216,8],[2153,9]]},"3629":{"position":[[461,8]]},"3637":{"position":[[220,8]]}}}],["rest",{"_index":4804,"t":{"1509":{"position":[[32,4]]},"1519":{"position":[[802,4]]},"1700":{"position":[[343,4]]},"1885":{"position":[[860,4]]},"2702":{"position":[[802,4]]},"2768":{"position":[[32,4]]},"2864":{"position":[[328,4]]},"3116":{"position":[[860,4]]}}}],["restart",{"_index":2353,"t":{"200":{"position":[[2271,7]]},"250":{"position":[[1851,7]]},"252":{"position":[[809,10]]},"347":{"position":[[355,8],[985,8],[1058,8],[1136,7]]},"357":{"position":[[1113,7]]},"628":{"position":[[176,8],[353,9]]},"648":{"position":[[156,8],[314,9]]},"664":{"position":[[355,7]]},"969":{"position":[[744,9]]},"973":{"position":[[2177,9],[2389,7],[2444,7],[2742,8]]},"1017":{"position":[[1506,7]]},"1183":{"position":[[612,8]]},"1279":{"position":[[355,8],[990,8],[1063,8],[1141,7]]},"1289":{"position":[[1113,7]]},"1403":{"position":[[599,8]]},"1505":{"position":[[5208,7],[8395,7]]},"1627":{"position":[[176,8],[353,9]]},"1643":{"position":[[156,8],[314,9]]},"1658":{"position":[[355,7]]},"1855":{"position":[[1486,8]]},"1979":{"position":[[800,9]]},"1983":{"position":[[3968,9]]},"2080":{"position":[[612,9]]},"2354":{"position":[[1623,7]]},"2402":{"position":[[379,8],[1051,8],[1125,9],[1207,7]]},"2412":{"position":[[1132,7]]},"2686":{"position":[[5270,7],[8496,7]]},"2814":{"position":[[176,8],[353,9]]},"2895":{"position":[[156,8],[314,9]]},"2910":{"position":[[355,7]]},"3084":{"position":[[1450,8]]},"3138":{"position":[[800,9]]},"3142":{"position":[[3968,9]]},"3265":{"position":[[612,9]]},"3415":{"position":[[1623,7]]}}}],["restor",{"_index":812,"t":{"20":{"position":[[444,7],[1408,7]]},"80":{"position":[[1243,7]]},"317":{"position":[[465,7]]},"347":{"position":[[1483,8],[1549,7],[1667,7]]},"357":{"position":[[1299,7]]},"455":{"position":[[650,7],[965,8]]},"640":{"position":[[495,7]]},"748":{"position":[[195,7]]},"973":{"position":[[162,7],[442,7]]},"1279":{"position":[[1488,8],[1554,7],[1672,7]]},"1289":{"position":[[1299,7]]},"1307":{"position":[[650,7],[965,8]]},"1425":{"position":[[2177,7]]},"1591":{"position":[[777,7]]},"1965":{"position":[[923,7]]},"1983":{"position":[[162,7],[442,7]]},"2402":{"position":[[1648,8],[1727,7],[1846,7]]},"2412":{"position":[[1320,7]]},"2594":{"position":[[681,7],[1007,8]]},"2784":{"position":[[777,7]]},"3010":{"position":[[923,7]]},"3142":{"position":[[162,7],[442,7]]}}}],["restrict",{"_index":3973,"t":{"818":{"position":[[888,11]]},"1511":{"position":[[20,12]]},"2770":{"position":[[20,12]]},"3068":{"position":[[611,11]]}}}],["restructur",{"_index":1857,"t":{"126":{"position":[[670,13]]}}}],["resubscrib",{"_index":772,"t":{"18":{"position":[[1898,11]]},"122":{"position":[[1900,11],[1932,11]]},"293":{"position":[[160,12],[276,11],[442,11]]},"973":{"position":[[204,11],[850,13]]},"1429":{"position":[[387,11],[466,11]]},"1589":{"position":[[299,11]]},"1983":{"position":[[204,11],[843,13],[2599,13],[4164,11],[4710,11]]},"2174":{"position":[[118,11]]},"2360":{"position":[[4808,11]]},"2632":{"position":[[356,12],[961,11],[3733,11],[4057,13]]},"2638":{"position":[[204,11]]},"2640":{"position":[[292,11]]},"2650":{"position":[[170,11]]},"2684":{"position":[[299,11]]},"2708":{"position":[[2003,12],[2118,11]]},"3142":{"position":[[204,11],[843,13],[2599,13],[4164,11],[4710,11]]},"3365":{"position":[[2197,11]]},"3403":{"position":[[118,11]]},"3421":{"position":[[4811,11]]},"3643":{"position":[[356,12],[961,11],[3733,11],[4057,13]]},"3649":{"position":[[204,11]]},"3651":{"position":[[292,11]]},"3661":{"position":[[170,11]]}}}],["resubscribe/reconnect",{"_index":4855,"t":{"1589":{"position":[[533,21]]},"2684":{"position":[[533,21]]}}}],["resubscript",{"_index":1812,"t":{"122":{"position":[[2246,16]]},"2632":{"position":[[4939,14]]},"3643":{"position":[[4939,14]]}}}],["resubscription/reconnect",{"_index":4854,"t":{"1589":{"position":[[389,28],[566,27]]},"2684":{"position":[[389,28],[566,27]]}}}],["result",{"_index":182,"t":{"2":{"position":[[2956,7],[3445,8]]},"18":{"position":[[3277,9]]},"50":{"position":[[585,6],[2132,7],[2228,7]]},"70":{"position":[[772,7]]},"122":{"position":[[890,6],[1403,6]]},"128":{"position":[[1030,7]]},"134":{"position":[[1073,9]]},"138":{"position":[[809,6]]},"152":{"position":[[402,6]]},"156":{"position":[[4,6]]},"160":{"position":[[68,6]]},"172":{"position":[[364,8]]},"182":{"position":[[677,6]]},"200":{"position":[[1017,6],[1157,9],[1430,6],[1543,9],[1796,6],[1911,9]]},"202":{"position":[[1044,6],[1436,6]]},"208":{"position":[[119,7]]},"244":{"position":[[210,6]]},"250":{"position":[[1716,7]]},"293":{"position":[[1094,6],[1419,6]]},"295":{"position":[[1547,6]]},"297":{"position":[[182,7]]},"303":{"position":[[106,7]]},"311":{"position":[[234,6]]},"315":{"position":[[172,8],[684,6]]},"317":{"position":[[923,7]]},"321":{"position":[[706,8]]},"329":{"position":[[372,6]]},"331":{"position":[[544,7]]},"411":{"position":[[520,6]]},"441":{"position":[[990,6]]},"465":{"position":[[1181,7]]},"469":{"position":[[394,6]]},"473":{"position":[[1720,8],[1872,7]]},"475":{"position":[[2301,6],[3354,7],[4371,7],[5923,7],[6252,7]]},"477":{"position":[[6,7],[372,7],[3438,7],[5417,7],[5457,7]]},"479":{"position":[[759,6],[3750,7],[4224,7],[5479,7],[6609,7]]},"481":{"position":[[587,7],[1471,7],[1665,6]]},"604":{"position":[[82,8]]},"636":{"position":[[1157,7]]},"638":{"position":[[1226,7]]},"656":{"position":[[666,7]]},"658":{"position":[[332,7]]},"688":{"position":[[612,7]]},"690":{"position":[[396,10],[581,10],[1195,7]]},"692":{"position":[[477,7]]},"788":{"position":[[247,6]]},"858":{"position":[[1715,6]]},"1017":{"position":[[1329,6],[1997,10],[2146,7],[3315,6],[3330,6],[3399,6],[5158,9]]},"1019":{"position":[[611,10],[1208,6]]},"1021":{"position":[[590,10],[1404,6]]},"1023":{"position":[[1934,10],[2888,6]]},"1025":{"position":[[1970,10],[2705,6]]},"1039":{"position":[[1231,7]]},"1160":{"position":[[430,6]]},"1235":{"position":[[520,6]]},"1267":{"position":[[990,6]]},"1405":{"position":[[2882,9],[2905,9],[2985,6],[3003,6],[3143,8],[3301,6]]},"1407":{"position":[[1080,10],[1163,6]]},"1409":{"position":[[359,9],[373,6]]},"1417":{"position":[[191,10],[570,10],[990,10],[1263,10]]},"1423":{"position":[[249,6],[530,6]]},"1457":{"position":[[1301,9],[2301,7]]},"1459":{"position":[[651,7],[813,7]]},"1461":{"position":[[1323,7]]},"1463":{"position":[[527,7]]},"1465":{"position":[[725,7]]},"1467":{"position":[[569,7]]},"1469":{"position":[[504,9],[884,7]]},"1471":{"position":[[432,9],[626,7]]},"1473":{"position":[[769,9],[1184,6],[1495,7]]},"1475":{"position":[[465,9],[621,7]]},"1477":{"position":[[237,7]]},"1479":{"position":[[275,9],[525,7]]},"1485":{"position":[[519,6]]},"1495":{"position":[[653,6]]},"1497":{"position":[[620,7],[770,7]]},"1511":{"position":[[439,6],[1232,6]]},"1569":{"position":[[87,6]]},"1575":{"position":[[190,7]]},"1585":{"position":[[94,6]]},"1597":{"position":[[1313,7],[1323,9],[1830,8]]},"1599":{"position":[[244,7]]},"1636":{"position":[[1157,7]]},"1639":{"position":[[1272,7]]},"1652":{"position":[[666,7]]},"1654":{"position":[[332,7]]},"1703":{"position":[[840,7]]},"1705":{"position":[[972,7]]},"1707":{"position":[[399,7]]},"1709":{"position":[[192,8],[271,8],[349,8],[422,8],[487,8],[932,7]]},"1711":{"position":[[226,7]]},"1713":{"position":[[148,8],[234,8],[319,8],[399,8],[471,8],[531,8],[588,8],[859,7]]},"1715":{"position":[[346,7]]},"1717":{"position":[[134,8],[194,8],[251,8],[429,7]]},"1719":{"position":[[1868,7]]},"1721":{"position":[[1071,7]]},"1731":{"position":[[612,7]]},"1733":{"position":[[396,10],[581,10],[1195,7]]},"1735":{"position":[[477,7]]},"1803":{"position":[[247,6]]},"1831":{"position":[[1271,6]]},"1843":{"position":[[1598,6]]},"1847":{"position":[[783,9]]},"1849":{"position":[[1101,6]]},"1855":{"position":[[977,9]]},"2150":{"position":[[872,6],[962,8],[1121,6]]},"2160":{"position":[[249,6],[530,6]]},"2289":{"position":[[1361,9],[2585,7]]},"2291":{"position":[[881,7],[1043,7]]},"2293":{"position":[[1387,7]]},"2295":{"position":[[493,7]]},"2297":{"position":[[656,7]]},"2299":{"position":[[535,7]]},"2301":{"position":[[547,9],[927,7]]},"2303":{"position":[[432,9],[626,7]]},"2305":{"position":[[769,9],[1184,6],[1495,7]]},"2307":{"position":[[465,9],[621,7]]},"2309":{"position":[[284,7]]},"2311":{"position":[[275,9],[525,7]]},"2317":{"position":[[519,6]]},"2340":{"position":[[430,6]]},"2354":{"position":[[1446,6],[2114,10],[2263,7],[3432,6],[3447,6],[3516,6],[5281,9]]},"2356":{"position":[[611,10],[1208,6]]},"2358":{"position":[[590,10],[1404,6]]},"2360":{"position":[[1934,10],[2820,6]]},"2362":{"position":[[1970,10],[2705,6]]},"2364":{"position":[[2008,10],[2681,6]]},"2378":{"position":[[1231,7]]},"2484":{"position":[[520,6]]},"2516":{"position":[[990,6]]},"2574":{"position":[[234,6]]},"2638":{"position":[[409,6],[657,8]]},"2650":{"position":[[148,6],[244,6]]},"2652":{"position":[[242,6]]},"2664":{"position":[[87,6]]},"2670":{"position":[[190,7]]},"2680":{"position":[[94,6]]},"2690":{"position":[[653,6]]},"2692":{"position":[[620,7],[770,7]]},"2712":{"position":[[465,7],[475,9],[563,9]]},"2720":{"position":[[326,6]]},"2770":{"position":[[439,6],[1232,6]]},"2780":{"position":[[1277,7],[1287,9],[1794,8]]},"2782":{"position":[[244,7]]},"2806":{"position":[[394,6]]},"2823":{"position":[[1121,7]]},"2826":{"position":[[1236,7]]},"2867":{"position":[[747,7]]},"2869":{"position":[[799,7]]},"2871":{"position":[[339,7]]},"2873":{"position":[[178,7],[718,8],[797,8],[870,8],[935,8],[995,8],[1016,7]]},"2875":{"position":[[226,7]]},"2877":{"position":[[146,8],[603,8],[689,8],[769,8],[841,8],[901,8],[956,8],[983,7]]},"2879":{"position":[[346,7]]},"2881":{"position":[[470,8],[530,8],[587,8],[612,7]]},"2883":{"position":[[2147,7]]},"2885":{"position":[[206,7]]},"2887":{"position":[[1071,7]]},"2904":{"position":[[630,7]]},"2906":{"position":[[296,7]]},"2920":{"position":[[576,7]]},"2922":{"position":[[360,10],[545,10],[1159,7]]},"2924":{"position":[[441,7]]},"3020":{"position":[[1069,6]]},"3032":{"position":[[261,6]]},"3060":{"position":[[1356,6]]},"3072":{"position":[[1886,6]]},"3076":{"position":[[747,9]]},"3078":{"position":[[1101,6]]},"3084":{"position":[[941,9]]},"3363":{"position":[[936,7]]},"3379":{"position":[[872,6],[962,8],[1121,6]]},"3389":{"position":[[249,6],[530,6]]},"3415":{"position":[[1446,6],[2114,10],[2263,7],[3432,6],[3447,6],[3516,6],[5310,9]]},"3417":{"position":[[611,10],[1208,6]]},"3419":{"position":[[637,10],[1451,6]]},"3421":{"position":[[1737,10],[2623,6]]},"3423":{"position":[[1970,10],[2705,6]]},"3425":{"position":[[1981,10],[2654,6]]},"3439":{"position":[[1231,7]]},"3502":{"position":[[435,9],[1262,7],[2093,7]]},"3504":{"position":[[956,7],[1118,7]]},"3506":{"position":[[1455,7]]},"3508":{"position":[[399,7]]},"3510":{"position":[[590,7]]},"3512":{"position":[[528,7]]},"3514":{"position":[[392,9],[765,7]]},"3516":{"position":[[280,9],[467,7]]},"3518":{"position":[[618,9],[1026,6],[1337,7]]},"3520":{"position":[[307,7]]},"3522":{"position":[[344,7]]},"3524":{"position":[[91,9],[342,7]]},"3530":{"position":[[519,6]]},"3625":{"position":[[430,6]]},"3629":{"position":[[545,7]]},"3639":{"position":[[1091,6]]},"3649":{"position":[[409,6],[657,8]]},"3655":{"position":[[1275,6]]},"3661":{"position":[[148,6],[244,6]]},"3663":{"position":[[242,6]]}}}],["retain",{"_index":2883,"t":{"317":{"position":[[322,7]]}}}],["retent",{"_index":819,"t":{"20":{"position":[[1065,9],[1748,10]]},"22":{"position":[[308,9],[499,9]]},"74":{"position":[[365,9]]},"349":{"position":[[190,9]]},"383":{"position":[[126,9]]},"407":{"position":[[277,9]]},"1185":{"position":[[669,9]]},"1189":{"position":[[1551,9]]},"1231":{"position":[[277,9]]},"1281":{"position":[[193,9]]},"1341":{"position":[[126,9]]},"1775":{"position":[[74,9]]},"2404":{"position":[[202,9]]},"2422":{"position":[[161,9]]},"2456":{"position":[[127,9]]},"2480":{"position":[[277,9]]},"2934":{"position":[[74,9]]}}}],["rethinkdb",{"_index":5646,"t":{"3365":{"position":[[1191,11]]}}}],["retreiv",{"_index":5526,"t":{"2873":{"position":[[1145,10]]},"2877":{"position":[[1131,10]]}}}],["retri",{"_index":2485,"t":{"222":{"position":[[592,8]]},"262":{"position":[[1382,5]]},"682":{"position":[[328,7]]},"714":{"position":[[125,8]]},"810":{"position":[[704,5]]},"979":{"position":[[678,5]]},"1423":{"position":[[1109,5],[1631,5]]},"1425":{"position":[[1422,5]]},"1619":{"position":[[329,7]]},"1666":{"position":[[1603,7],[1673,7],[1740,7]]},"1825":{"position":[[882,5]]},"1949":{"position":[[122,8]]},"2072":{"position":[[678,5]]},"2160":{"position":[[1109,5],[1631,5]]},"2628":{"position":[[750,5]]},"2644":{"position":[[736,5]]},"2648":{"position":[[329,6]]},"2708":{"position":[[2384,7]]},"2804":{"position":[[329,7]]},"2806":{"position":[[385,8]]},"2830":{"position":[[1606,7],[1676,7],[1743,7]]},"2994":{"position":[[122,8]]},"3054":{"position":[[882,5]]},"3309":{"position":[[678,5]]},"3389":{"position":[[1109,5],[1631,5]]},"3538":{"position":[[673,8]]},"3639":{"position":[[594,5]]},"3655":{"position":[[736,5]]},"3659":{"position":[[329,6]]}}}],["retriev",{"_index":2806,"t":{"291":{"position":[[286,8]]},"407":{"position":[[356,8]]},"1057":{"position":[[1156,9]]},"1231":{"position":[[356,8]]},"1709":{"position":[[636,9]]},"1713":{"position":[[737,9]]},"1717":{"position":[[403,9]]},"2120":{"position":[[31,8],[230,9]]},"2140":{"position":[[1156,9]]},"2480":{"position":[[356,8]]},"2873":{"position":[[327,9]]},"2877":{"position":[[296,9]]},"2881":{"position":[[269,9]]},"3343":{"position":[[31,8],[230,9]]},"3355":{"position":[[1156,9]]}}}],["return",{"_index":426,"t":{"10":{"position":[[852,6]]},"34":{"position":[[158,6]]},"42":{"position":[[580,7],[765,7]]},"64":{"position":[[477,6],[1443,6]]},"66":{"position":[[1829,6]]},"70":{"position":[[591,6],[755,6]]},"72":{"position":[[465,6]]},"76":{"position":[[1630,7]]},"104":{"position":[[806,6],[917,6]]},"106":{"position":[[365,6],[451,6],[993,8],[1385,6]]},"110":{"position":[[1400,6],[1554,6],[1692,6],[1871,6],[2027,6],[2140,6],[3293,6],[3422,6],[3612,6],[3674,6],[4000,6],[4301,6],[4376,6],[4398,6],[4497,6],[4513,6]]},"112":{"position":[[527,6],[644,6]]},"138":{"position":[[93,6]]},"172":{"position":[[338,6],[887,9]]},"176":{"position":[[709,7]]},"192":{"position":[[1078,6],[1215,7],[1465,6]]},"194":{"position":[[1736,6],[1815,7],[2046,6],[2117,6]]},"200":{"position":[[928,6],[1197,6],[1349,6],[1558,6],[1712,6],[1926,6],[2001,6],[2096,6]]},"216":{"position":[[1698,6],[1713,6],[2446,7],[2583,6],[2786,6],[3466,6]]},"238":{"position":[[191,8]]},"268":{"position":[[964,7]]},"270":{"position":[[1747,7]]},"291":{"position":[[933,6],[990,6],[1969,6]]},"293":{"position":[[1412,6]]},"315":{"position":[[117,9],[612,9],[771,9],[1163,7]]},"353":{"position":[[327,6]]},"355":{"position":[[78,6],[483,7]]},"415":{"position":[[452,7]]},"455":{"position":[[280,7]]},"469":{"position":[[662,6]]},"471":{"position":[[1705,6],[1878,6],[2726,6]]},"481":{"position":[[1601,8]]},"589":{"position":[[50,6],[97,7]]},"698":{"position":[[37,8],[168,8]]},"700":{"position":[[83,8]]},"704":{"position":[[117,8]]},"710":{"position":[[67,8]]},"716":{"position":[[118,8]]},"750":{"position":[[138,6]]},"756":{"position":[[41,8]]},"758":{"position":[[82,8]]},"792":{"position":[[1290,6]]},"794":{"position":[[328,7]]},"798":{"position":[[1349,6],[1423,6],[1889,6]]},"842":{"position":[[766,7]]},"846":{"position":[[219,8]]},"973":{"position":[[1442,6],[1516,6],[2571,9]]},"1017":{"position":[[326,6],[3370,7],[3573,6],[4934,6]]},"1021":{"position":[[1739,6]]},"1023":{"position":[[3798,6]]},"1025":{"position":[[548,8],[3137,6]]},"1027":{"position":[[24,6],[69,6],[315,9],[408,9]]},"1029":{"position":[[24,6],[549,9]]},"1239":{"position":[[452,7]]},"1285":{"position":[[327,6]]},"1287":{"position":[[78,6],[483,7]]},"1307":{"position":[[280,7]]},"1365":{"position":[[250,8]]},"1405":{"position":[[1099,6],[2264,6]]},"1413":{"position":[[1119,8]]},"1423":{"position":[[1379,8]]},"1425":{"position":[[1732,8]]},"1473":{"position":[[174,6],[1077,8],[1252,6]]},"1477":{"position":[[9,6],[595,7]]},"1491":{"position":[[451,6],[571,6]]},"1509":{"position":[[1126,9]]},"1511":{"position":[[856,7]]},"1571":{"position":[[213,8]]},"1597":{"position":[[1794,9]]},"1599":{"position":[[0,7]]},"1688":{"position":[[524,7]]},"1709":{"position":[[0,7]]},"1771":{"position":[[2480,6]]},"1773":{"position":[[1997,6],[2146,6]]},"1775":{"position":[[1004,6],[1169,6]]},"1777":{"position":[[303,6],[1143,6]]},"1807":{"position":[[817,6]]},"1809":{"position":[[401,7]]},"1813":{"position":[[1349,6],[1423,6],[1889,6]]},"1827":{"position":[[1128,8]]},"1859":{"position":[[1119,7]]},"1933":{"position":[[37,8],[168,8]]},"1935":{"position":[[95,8]]},"1939":{"position":[[114,8]]},"1945":{"position":[[64,8]]},"1951":{"position":[[115,8]]},"1983":{"position":[[1614,6],[1688,6],[2886,8],[3091,8]]},"2088":{"position":[[1084,8]]},"2094":{"position":[[1141,8]]},"2120":{"position":[[568,6]]},"2154":{"position":[[793,6],[2062,6]]},"2160":{"position":[[1379,8]]},"2305":{"position":[[174,6],[1077,8],[1252,6]]},"2309":{"position":[[9,6],[642,7]]},"2323":{"position":[[451,6],[571,6]]},"2354":{"position":[[326,6],[3487,7],[3690,6],[5057,6],[6015,6],[6061,6],[6417,6]]},"2358":{"position":[[1745,6]]},"2360":{"position":[[3829,6],[4092,6],[4149,6],[4388,10],[4670,9]]},"2362":{"position":[[548,8],[3137,6]]},"2366":{"position":[[24,6],[69,6],[250,9],[367,9]]},"2368":{"position":[[24,6],[725,9]]},"2408":{"position":[[329,6]]},"2410":{"position":[[78,6],[488,7]]},"2488":{"position":[[452,7]]},"2536":{"position":[[250,8]]},"2574":{"position":[[84,9],[321,9]]},"2594":{"position":[[285,7]]},"2628":{"position":[[416,6],[489,8],[559,7],[715,8],[838,6],[1089,6],[1332,6]]},"2634":{"position":[[318,7],[647,7]]},"2644":{"position":[[409,6],[479,8],[551,7],[701,8],[820,6],[1071,6],[1416,6]]},"2648":{"position":[[11,6],[155,8]]},"2650":{"position":[[11,6]]},"2666":{"position":[[213,8]]},"2712":{"position":[[829,7]]},"2768":{"position":[[1126,9]]},"2770":{"position":[[856,7]]},"2780":{"position":[[1758,9]]},"2782":{"position":[[0,7]]},"2852":{"position":[[524,7]]},"2873":{"position":[[0,7]]},"2930":{"position":[[2480,6]]},"2932":{"position":[[1997,6],[2146,6]]},"2934":{"position":[[1004,6],[1169,6]]},"2936":{"position":[[303,6],[1143,6]]},"2978":{"position":[[37,8],[168,8]]},"2980":{"position":[[95,8]]},"2984":{"position":[[114,8]]},"2990":{"position":[[64,8],[388,8]]},"2996":{"position":[[115,8]]},"3036":{"position":[[895,6]]},"3038":{"position":[[419,7]]},"3056":{"position":[[1094,8]]},"3090":{"position":[[1119,7]]},"3142":{"position":[[1614,6],[1688,6],[2886,8],[3091,8]]},"3273":{"position":[[976,8]]},"3279":{"position":[[1033,8]]},"3343":{"position":[[552,6]]},"3367":{"position":[[1579,7],[3125,6]]},"3369":{"position":[[1227,7]]},"3383":{"position":[[793,6],[2062,6]]},"3389":{"position":[[1379,8]]},"3415":{"position":[[326,6],[3487,7],[3690,6],[5086,6],[6044,6],[6090,6],[6446,6]]},"3419":{"position":[[1792,6]]},"3421":{"position":[[3832,6],[4095,6],[4152,6],[4391,10],[4673,9]]},"3423":{"position":[[548,8],[3137,6]]},"3427":{"position":[[24,6],[69,6],[250,9],[367,9]]},"3429":{"position":[[24,6],[725,9]]},"3518":{"position":[[174,6],[919,8],[1094,6]]},"3522":{"position":[[9,6],[702,7]]},"3526":{"position":[[142,8]]},"3536":{"position":[[451,6],[571,6]]},"3538":{"position":[[40,7],[95,7],[133,7],[181,7],[341,7],[1124,7]]},"3540":{"position":[[104,6],[217,6],[319,6],[405,6],[496,6],[670,6]]},"3542":{"position":[[99,6],[184,6],[269,6],[343,6],[399,6],[573,6]]},"3639":{"position":[[416,6],[489,8],[559,8],[697,6],[926,6],[1220,6]]},"3645":{"position":[[318,7],[647,7]]},"3655":{"position":[[409,6],[479,8],[551,7],[701,8],[1110,6],[1404,6]]},"3659":{"position":[[11,6],[155,8]]},"3661":{"position":[[11,6]]}}}],["reus",{"_index":496,"t":{"12":{"position":[[709,7]]},"94":{"position":[[813,5]]},"132":{"position":[[1502,5]]},"134":{"position":[[750,6]]},"202":{"position":[[221,5]]},"563":{"position":[[1156,5]]},"632":{"position":[[942,7]]},"652":{"position":[[805,7]]},"694":{"position":[[379,5]]},"698":{"position":[[369,5]]},"953":{"position":[[228,6]]},"1094":{"position":[[152,6]]},"1154":{"position":[[49,5]]},"1156":{"position":[[49,5],[239,8]]},"1631":{"position":[[942,7]]},"1647":{"position":[[805,7]]},"1670":{"position":[[1860,5]]},"1737":{"position":[[369,5]]},"1923":{"position":[[228,6]]},"1933":{"position":[[369,5]]},"2189":{"position":[[152,6]]},"2334":{"position":[[49,5]]},"2336":{"position":[[49,5],[239,8]]},"2818":{"position":[[942,7]]},"2834":{"position":[[1866,5]]},"2899":{"position":[[805,7]]},"2926":{"position":[[369,5]]},"2978":{"position":[[369,5]]},"3150":{"position":[[228,6]]},"3492":{"position":[[152,6]]},"3619":{"position":[[49,5]]},"3621":{"position":[[49,5],[239,8]]}}}],["reveal",{"_index":3514,"t":{"491":{"position":[[251,6]]},"802":{"position":[[456,6]]},"884":{"position":[[510,6]]},"1371":{"position":[[616,6]]},"1385":{"position":[[318,6]]},"1817":{"position":[[456,6]]},"1997":{"position":[[510,6]]},"2542":{"position":[[616,6]]},"2556":{"position":[[339,6]]},"3046":{"position":[[600,6]]},"3186":{"position":[[510,6]]}}}],["revers",{"_index":959,"t":{"34":{"position":[[742,8],[847,8],[1465,7],[1475,8]]},"250":{"position":[[268,7]]},"461":{"position":[[686,8],[770,8]]},"951":{"position":[[172,7]]},"971":{"position":[[53,7],[206,8],[373,8],[534,8],[632,8],[684,8],[747,8],[911,8],[1027,8],[1127,8],[1304,8],[1409,8],[2027,7],[2037,8]]},"977":{"position":[[53,7],[134,7]]},"1313":{"position":[[686,8],[770,8]]},"1473":{"position":[[1292,7],[1319,8]]},"1921":{"position":[[172,7]]},"1981":{"position":[[53,7],[206,8],[373,8],[534,8],[632,8],[684,8],[747,8],[911,8],[1027,8],[1127,8],[1304,8],[1409,8],[2027,7],[2037,8]]},"2070":{"position":[[53,7],[134,7]]},"2104":{"position":[[535,8]]},"2305":{"position":[[1292,7],[1319,8]]},"2392":{"position":[[2144,7]]},"2600":{"position":[[686,8],[770,8]]},"2656":{"position":[[686,8],[770,8]]},"3140":{"position":[[53,7],[206,8],[373,8],[534,8],[632,8],[684,8],[747,8],[911,8],[1027,8],[1127,8],[1304,8],[1409,8],[2027,7],[2037,8]]},"3148":{"position":[[172,7]]},"3307":{"position":[[53,7],[134,7]]},"3518":{"position":[[1134,7],[1161,8]]},"3612":{"position":[[2343,7]]},"3667":{"position":[[686,8],[770,8]]}}}],["revis",{"_index":1808,"t":{"122":{"position":[[1706,7]]},"128":{"position":[[474,7]]},"130":{"position":[[296,7]]},"148":{"position":[[264,7]]}}}],["revoc",{"_index":3718,"t":{"628":{"position":[[36,11],[124,10],[213,10],[306,10],[680,10]]},"630":{"position":[[6,10],[32,10],[208,10],[237,10]]},"632":{"position":[[0,10],[361,10],[420,10]]},"634":{"position":[[0,10]]},"636":{"position":[[827,10],[1017,11]]},"638":{"position":[[886,10],[1076,11]]},"778":{"position":[[152,10]]},"780":{"position":[[126,10]]},"1627":{"position":[[36,11],[124,10],[213,10],[306,10],[680,10]]},"1629":{"position":[[6,10],[32,10],[208,10],[237,10]]},"1631":{"position":[[0,10],[361,10],[420,10]]},"1633":{"position":[[0,10]]},"1636":{"position":[[827,10],[1017,11]]},"1639":{"position":[[932,10],[1122,11]]},"1759":{"position":[[152,10]]},"1761":{"position":[[126,10]]},"1793":{"position":[[152,10]]},"1795":{"position":[[126,10]]},"2814":{"position":[[36,11],[124,10],[213,10],[306,10],[680,10]]},"2816":{"position":[[6,10],[32,10],[208,10],[237,10]]},"2818":{"position":[[0,10],[361,10],[420,10]]},"2820":{"position":[[0,10]]},"2823":{"position":[[791,10],[981,11]]},"2826":{"position":[[896,10],[1086,11]]},"2964":{"position":[[152,10]]},"2966":{"position":[[126,10]]},"3022":{"position":[[197,10]]},"3024":{"position":[[168,10]]}}}],["revok",{"_index":2679,"t":{"262":{"position":[[1125,7]]},"553":{"position":[[586,8],[615,8]]},"628":{"position":[[478,7]]},"632":{"position":[[617,7]]},"636":{"position":[[7,8],[120,6],[651,6],[1144,6]]},"638":{"position":[[7,8],[771,7]]},"1527":{"position":[[697,8],[730,6]]},"1575":{"position":[[29,6],[768,6],[915,7]]},"1589":{"position":[[29,6],[446,6],[632,7]]},"1627":{"position":[[478,7]]},"1631":{"position":[[617,7]]},"1636":{"position":[[7,8],[120,6]]},"1639":{"position":[[7,8],[775,7]]},"2670":{"position":[[29,6],[768,6],[915,7]]},"2684":{"position":[[29,6],[446,6],[632,7]]},"2760":{"position":[[698,8],[731,6]]},"2814":{"position":[[478,7]]},"2818":{"position":[[617,7]]},"2823":{"position":[[7,8],[120,6]]},"2826":{"position":[[7,8],[739,7]]}}}],["revoke_token",{"_index":3732,"t":{"636":{"position":[[547,15]]},"1636":{"position":[[547,15],[651,12],[1144,12]]},"2823":{"position":[[615,12],[1108,12]]}}}],["revolut",{"_index":1794,"t":{"120":{"position":[[566,13],[580,12]]}}}],["rewrit",{"_index":2108,"t":{"184":{"position":[[749,7]]},"483":{"position":[[64,9]]},"979":{"position":[[256,7]]},"981":{"position":[[262,7],[521,7]]},"2072":{"position":[[256,7]]},"2074":{"position":[[262,7],[521,7]]},"3309":{"position":[[256,7]]},"3311":{"position":[[262,7],[521,7]]}}}],["rewritten",{"_index":1064,"t":{"48":{"position":[[135,10]]},"337":{"position":[[157,10]]}}}],["rfc",{"_index":325,"t":{"8":{"position":[[552,4]]},"636":{"position":[[402,5]]},"638":{"position":[[279,5]]},"778":{"position":[[71,4]]},"780":{"position":[[45,4]]},"1636":{"position":[[402,5]]},"1639":{"position":[[279,5]]},"1759":{"position":[[71,4]]},"1761":{"position":[[45,4]]},"1793":{"position":[[71,4]]},"1795":{"position":[[45,4]]},"2823":{"position":[[402,5]]},"2826":{"position":[[279,5]]},"2964":{"position":[[71,4]]},"2966":{"position":[[45,4]]},"3016":{"position":[[68,3]]},"3022":{"position":[[97,3]]},"3024":{"position":[[70,3]]}}}],["rfc7519",{"_index":3925,"t":{"782":{"position":[[83,8]]},"784":{"position":[[81,8]]},"1001":{"position":[[83,8]]},"1003":{"position":[[81,8]]},"1741":{"position":[[69,8]]},"1755":{"position":[[51,8]]},"1757":{"position":[[49,8]]},"1787":{"position":[[71,8]]},"1797":{"position":[[51,8]]},"1799":{"position":[[49,8]]},"2946":{"position":[[69,8]]},"2960":{"position":[[51,8]]},"2962":{"position":[[49,8]]},"3026":{"position":[[51,8]]},"3028":{"position":[[49,8]]}}}],["riak",{"_index":417,"t":{"10":{"position":[[614,4]]}}}],["rid",{"_index":1509,"t":{"94":{"position":[[711,3]]},"838":{"position":[[215,3]]},"1672":{"position":[[778,3]]},"1855":{"position":[[215,3]]},"2836":{"position":[[778,3]]},"3084":{"position":[[215,3]]}}}],["right",{"_index":370,"t":{"8":{"position":[[1375,5]]},"58":{"position":[[1497,5]]},"158":{"position":[[539,6]]},"262":{"position":[[80,6]]},"471":{"position":[[3474,6]]},"553":{"position":[[936,5]]},"945":{"position":[[556,5]]},"1527":{"position":[[1420,5]]},"2066":{"position":[[556,5]]},"3261":{"position":[[556,5]]}}}],["rigor",{"_index":2690,"t":{"264":{"position":[[1001,10]]}}}],["rise",{"_index":3181,"t":{"473":{"position":[[840,4],[1019,4]]},"1195":{"position":[[459,4],[567,4]]},"2096":{"position":[[459,4],[567,4]]},"3281":{"position":[[459,4],[567,4]]}}}],["risk",{"_index":429,"t":{"10":{"position":[[937,4]]},"1367":{"position":[[1069,4]]},"1771":{"position":[[1014,6]]},"1839":{"position":[[663,6]]},"2538":{"position":[[1069,4]]},"2930":{"position":[[1014,6]]}}}],["rl",{"_index":3406,"t":{"479":{"position":[[2402,2]]}}}],["rl.take",{"_index":3409,"t":{"479":{"position":[[2465,9]]}}}],["rm",{"_index":1577,"t":{"98":{"position":[[1318,2]]},"196":{"position":[[137,2]]},"198":{"position":[[1598,2]]},"212":{"position":[[69,2]]},"214":{"position":[[70,2]]},"226":{"position":[[157,2]]},"680":{"position":[[543,2],[676,2]]},"1617":{"position":[[673,2],[810,2]]},"1649":{"position":[[329,2]]},"2802":{"position":[[673,2],[810,2]]},"2901":{"position":[[329,2]]},"3327":{"position":[[532,2]]}}}],["rnning",{"_index":5136,"t":{"2035":{"position":[[114,6]]},"3226":{"position":[[114,6]]}}}],["road",{"_index":928,"t":{"32":{"position":[[124,4]]},"435":{"position":[[185,4]]},"1071":{"position":[[52,4]]},"1261":{"position":[[185,4]]},"2134":{"position":[[52,4]]},"2510":{"position":[[185,4]]},"3337":{"position":[[52,4]]}}}],["roadrunn",{"_index":4457,"t":{"1295":{"position":[[613,10],[656,10]]},"2430":{"position":[[613,10],[656,10]]}}}],["robot",{"_index":1978,"t":{"156":{"position":[[924,5],[990,6]]}}}],["robust",{"_index":826,"t":{"22":{"position":[[81,6]]},"38":{"position":[[321,6]]},"270":{"position":[[1393,6]]},"437":{"position":[[1430,6]]},"1263":{"position":[[1434,6]]},"2512":{"position":[[1575,6]]},"2526":{"position":[[1596,6]]}}}],["role",{"_index":3391,"t":{"479":{"position":[[687,4]]},"1495":{"position":[[838,8]]},"1497":{"position":[[648,9]]},"2400":{"position":[[1588,4]]},"2690":{"position":[[838,8]]},"2692":{"position":[[648,9]]}}}],["role:mast",{"_index":3190,"t":{"473":{"position":[[1221,11]]},"1195":{"position":[[770,11]]},"2096":{"position":[[770,11]]},"3281":{"position":[[770,11]]}}}],["roll",{"_index":1893,"t":{"134":{"position":[[1366,4]]},"264":{"position":[[1196,4]]},"313":{"position":[[71,6]]}}}],["ronach",{"_index":271,"t":{"4":{"position":[[1102,8]]}}}],["room",{"_index":1275,"t":{"66":{"position":[[99,5]]},"76":{"position":[[1987,5]]},"136":{"position":[[250,4]]},"154":{"position":[[41,4],[193,6],[214,5],[253,5]]},"156":{"position":[[646,4]]},"160":{"position":[[695,6]]},"166":{"position":[[310,5],[355,5],[395,5],[458,5],[511,4],[561,6]]},"172":{"position":[[1110,6],[1169,4],[1296,4],[1421,4]]},"174":{"position":[[50,6],[68,5],[81,5],[101,5],[155,5],[230,4],[521,6]]},"176":{"position":[[174,6],[206,4],[309,6],[355,4],[522,5],[774,9],[2002,4],[2086,5],[2124,4],[2181,5]]},"178":{"position":[[384,4],[415,5],[465,4]]},"182":{"position":[[183,4],[199,4],[253,4],[274,4],[425,4]]},"192":{"position":[[87,4],[785,4],[1344,4],[3081,4],[3115,4],[3174,4],[3253,4]]},"194":{"position":[[38,4],[103,5],[334,4],[1949,4],[2211,4],[2590,4],[2649,4],[2862,4]]},"196":{"position":[[624,8],[1255,6],[1322,5]]},"200":{"position":[[2377,4],[2468,4]]},"202":{"position":[[296,6],[310,4],[473,5],[1757,5],[1803,4]]},"337":{"position":[[355,5]]},"443":{"position":[[21,6]]},"529":{"position":[[8,5]]},"1217":{"position":[[84,5],[202,5]]},"1273":{"position":[[21,6]]},"2396":{"position":[[89,5],[203,6]]},"2522":{"position":[[21,6]]},"3345":{"position":[[980,4]]},"3395":{"position":[[669,6]]}}}],["room(request",{"_index":2269,"t":{"194":{"position":[[2091,13]]}}}],["room.html",{"_index":2234,"t":{"194":{"position":[[268,12]]}}}],["room::with('us",{"_index":2049,"t":{"174":{"position":[[467,19]]}}}],["roomcentrifugo",{"_index":4741,"t":{"1505":{"position":[[2127,17]]},"2686":{"position":[[2142,17]]}}}],["title>chat",{"_index":2235,"t":{"194":{"position":[[461,11]]}}}],["title>select",{"_index":2160,"t":{"192":{"position":[[607,13]]}}}],["tl",{"_index":543,"t":{"14":{"position":[[392,3]]},"94":{"position":[[3164,3]]},"98":{"position":[[147,3],[507,3]]},"106":{"position":[[975,3]]},"124":{"position":[[305,3]]},"142":{"position":[[55,3],[512,3]]},"144":{"position":[[1270,3],[1333,3]]},"389":{"position":[[137,3],[160,4]]},"1033":{"position":[[134,3]]},"1045":{"position":[[1828,3]]},"1055":{"position":[[158,6]]},"1057":{"position":[[934,3],[1316,3],[1788,3],[1899,3]]},"1059":{"position":[[54,3],[113,3]]},"1061":{"position":[[86,3],[165,3]]},"1124":{"position":[[38,3]]},"1189":{"position":[[385,3],[463,3]]},"1247":{"position":[[75,3],[689,3]]},"1347":{"position":[[137,3],[160,4]]},"1407":{"position":[[192,4]]},"1455":{"position":[[930,4]]},"2088":{"position":[[38,3],[145,3],[232,3],[433,3],[629,3],[824,3]]},"2094":{"position":[[13,3],[121,3],[217,3],[436,3],[650,3],[863,3]]},"2138":{"position":[[158,6]]},"2140":{"position":[[934,3],[1316,3],[1788,3],[1899,3]]},"2142":{"position":[[54,3],[113,3]]},"2144":{"position":[[54,3],[133,3]]},"2275":{"position":[[38,3]]},"2287":{"position":[[933,4]]},"2372":{"position":[[134,3]]},"2384":{"position":[[1828,3]]},"2392":{"position":[[495,6],[615,3],[1977,3],[2021,3]]},"2462":{"position":[[146,3],[169,4]]},"2496":{"position":[[75,3],[689,3]]},"3273":{"position":[[38,3],[145,3],[232,3],[406,3],[575,3],[743,3]]},"3279":{"position":[[13,3],[121,3],[217,3],[409,3],[596,3],[782,3]]},"3353":{"position":[[158,6]]},"3355":{"position":[[934,3],[1316,3],[1788,3],[1899,3]]},"3357":{"position":[[54,3],[113,3]]},"3359":{"position":[[54,3],[133,3]]},"3433":{"position":[[134,3]]},"3445":{"position":[[2040,3]]},"3498":{"position":[[906,4]]},"3557":{"position":[[38,3]]},"3612":{"position":[[522,6],[642,3],[2004,3],[2048,3]]}}}],["tls.certificate{cert",{"_index":1682,"t":{"106":{"position":[[1419,24]]}}}],["tls.config",{"_index":1679,"t":{"106":{"position":[[1262,11],[1392,12],[1768,12]]}}}],["tls.loadx509keypair(s.config.tlscertpath",{"_index":1680,"t":{"106":{"position":[[1289,41]]}}}],["tls_autocert",{"_index":4281,"t":{"1057":{"position":[[93,15],[307,12]]},"2140":{"position":[[93,15],[307,12]]},"3355":{"position":[[93,15],[307,12]]}}}],["tls_autocert_cache_dir",{"_index":4284,"t":{"1057":{"position":[[165,25],[575,22]]},"2140":{"position":[[165,25],[575,22]]},"3355":{"position":[[165,25],[575,22]]}}}],["tls_autocert_email",{"_index":4286,"t":{"1057":{"position":[[205,21],[712,18]]},"2140":{"position":[[205,21],[712,18]]},"3355":{"position":[[205,21],[712,18]]}}}],["tls_autocert_force_rsa",{"_index":3671,"t":{"618":{"position":[[230,22]]},"1057":{"position":[[1406,22]]},"2140":{"position":[[1406,22]]},"3355":{"position":[[1406,22]]}}}],["tls_autocert_host_whitelist",{"_index":4282,"t":{"1057":{"position":[[115,30],[412,27]]},"2140":{"position":[[115,30],[412,27]]},"3355":{"position":[[115,30],[412,27]]}}}],["tls_autocert_http",{"_index":4288,"t":{"1057":{"position":[[247,20],[853,17]]},"2140":{"position":[[247,20],[853,17]]},"3355":{"position":[[247,20],[853,17]]}}}],["tls_autocert_http_addr",{"_index":4289,"t":{"1057":{"position":[[274,25],[944,22]]},"2140":{"position":[[274,25],[944,22]]},"3355":{"position":[[274,25],[944,22]]}}}],["tls_autocert_server_nam",{"_index":4296,"t":{"1057":{"position":[[1561,24]]},"2140":{"position":[[1561,24]]},"3355":{"position":[[1561,24]]}}}],["tls_cert",{"_index":4280,"t":{"1055":{"position":[[196,11]]},"2138":{"position":[[196,11]]},"2392":{"position":[[508,11]]},"3353":{"position":[[196,11]]},"3612":{"position":[[535,11]]}}}],["tls_key",{"_index":4279,"t":{"1055":{"position":[[171,10]]},"2138":{"position":[[171,10]]},"2392":{"position":[[535,10]]},"3353":{"position":[[171,10]]},"3612":{"position":[[562,10]]}}}],["tlscertpath",{"_index":1637,"t":{"104":{"position":[[254,11],[300,11],[1040,12]]}}}],["tlskeypath",{"_index":1640,"t":{"104":{"position":[[322,10],[366,10],[1067,11]]}}}],["tlsv1",{"_index":4145,"t":{"979":{"position":[[393,5]]},"2072":{"position":[[393,5]]},"3309":{"position":[[393,5]]}}}],["tlsv1.1",{"_index":4146,"t":{"979":{"position":[[399,7]]},"2072":{"position":[[399,7]]},"3309":{"position":[[399,7]]}}}],["tlsv1.2",{"_index":4147,"t":{"979":{"position":[[407,8]]},"2072":{"position":[[407,8]]},"3309":{"position":[[407,8]]}}}],["tmp/cert",{"_index":4285,"t":{"1057":{"position":[[191,13]]},"2140":{"position":[[191,13]]},"3355":{"position":[[191,13]]}}}],["tmp/clickhouse:/var/lib/clickhous",{"_index":3835,"t":{"680":{"position":[[549,35]]},"1617":{"position":[[679,35]]},"2802":{"position":[[679,35]]}}}],["to/from",{"_index":1354,"t":{"72":{"position":[[1067,7]]}}}],["todatetimestr",{"_index":2057,"t":{"174":{"position":[[694,20]]}}}],["today",{"_index":1781,"t":{"118":{"position":[[0,5]]},"517":{"position":[[577,6]]},"1445":{"position":[[600,6]]},"2614":{"position":[[536,6]]}}}],["today'",{"_index":1034,"t":{"40":{"position":[[222,7]]},"130":{"position":[[58,7]]},"272":{"position":[[272,7]]},"1682":{"position":[[272,7]]},"2846":{"position":[[272,7]]}}}],["togeth",{"_index":509,"t":{"12":{"position":[[1121,9]]},"18":{"position":[[2928,8]]},"78":{"position":[[1988,8],[3872,8]]},"270":{"position":[[2847,8]]},"333":{"position":[[837,9]]},"437":{"position":[[2195,9]]},"778":{"position":[[117,8]]},"780":{"position":[[91,8]]},"858":{"position":[[974,8]]},"973":{"position":[[719,8]]},"1041":{"position":[[915,8]]},"1203":{"position":[[1066,9]]},"1263":{"position":[[2199,9]]},"1323":{"position":[[216,8]]},"1365":{"position":[[406,9]]},"1405":{"position":[[699,8]]},"1515":{"position":[[62,8]]},"1613":{"position":[[42,8]]},"1688":{"position":[[631,8]]},"1759":{"position":[[117,8]]},"1761":{"position":[[91,8]]},"1793":{"position":[[117,8]]},"1795":{"position":[[91,8]]},"1843":{"position":[[1005,8]]},"1983":{"position":[[716,8]]},"2106":{"position":[[1066,9]]},"2154":{"position":[[414,8],[1637,8]]},"2380":{"position":[[889,8]]},"2438":{"position":[[218,8]]},"2512":{"position":[[2441,9]]},"2536":{"position":[[406,9]]},"2774":{"position":[[62,8]]},"2798":{"position":[[42,8]]},"2852":{"position":[[631,8]]},"2862":{"position":[[724,8]]},"2964":{"position":[[117,8]]},"2966":{"position":[[91,8]]},"3072":{"position":[[1293,8]]},"3142":{"position":[[716,8]]},"3289":{"position":[[1066,9]]},"3367":{"position":[[1225,8]]},"3383":{"position":[[414,8],[1637,8]]},"3441":{"position":[[889,8]]}}}],["toggl",{"_index":3767,"t":{"672":{"position":[[561,6]]},"1603":{"position":[[649,6]]},"2788":{"position":[[649,6]]}}}],["tointervalday(1)set",{"_index":3787,"t":{"674":{"position":[[583,24]]},"676":{"position":[[638,24]]},"1605":{"position":[[503,24]]},"1607":{"position":[[482,24]]},"1609":{"position":[[629,24]]},"1611":{"position":[[504,24]]},"1613":{"position":[[704,24]]},"2790":{"position":[[503,24]]},"2792":{"position":[[482,24]]},"2794":{"position":[[629,24]]},"2796":{"position":[[504,24]]},"2798":{"position":[[704,24]]}}}],["tointervalhour(24",{"_index":4905,"t":{"1615":{"position":[[1668,20]]},"2800":{"position":[[1668,20]]}}}],["tointervalminute(1)))group",{"_index":3821,"t":{"678":{"position":[[1263,26]]},"1615":{"position":[[1257,26]]},"2800":{"position":[[1257,26]]}}}],["tointervalminute(5)));┌─uniqexact(us",{"_index":3818,"t":{"678":{"position":[[1006,42]]},"1615":{"position":[[1000,42]]},"2800":{"position":[[1000,42]]}}}],["token",{"_index":760,"t":{"18":{"position":[[974,6]]},"64":{"position":[[1040,5],[1175,5],[1268,7]]},"122":{"position":[[544,5],[1481,5],[1838,5]]},"130":{"position":[[935,5]]},"132":{"position":[[787,5],[1215,6],[1264,5],[1521,5],[1576,5],[1615,6],[1656,5]]},"134":{"position":[[445,5],[623,5],[737,5]]},"138":{"position":[[908,6]]},"148":{"position":[[387,6],[497,6]]},"176":{"position":[[839,7]]},"208":{"position":[[499,5]]},"212":{"position":[[690,5]]},"214":{"position":[[615,7]]},"216":{"position":[[2536,6],[2902,7],[2962,5],[2984,5]]},"262":{"position":[[368,6],[391,5],[931,6],[1053,7],[1097,5]]},"268":{"position":[[80,6],[173,6],[296,6],[942,6],[1120,5],[1262,6]]},"270":{"position":[[2293,7],[2359,5]]},"291":{"position":[[248,6],[308,5],[346,5],[385,5],[407,5],[513,6],[1520,5],[1738,6],[1918,5],[1976,6],[2140,5],[2206,5],[2319,5],[2417,5],[2757,5],[2891,5],[3012,6]]},"293":{"position":[[173,5],[459,5],[479,5]]},"295":{"position":[[635,6]]},"301":{"position":[[72,6],[394,7],[603,5],[1104,5]]},"311":{"position":[[113,5]]},"315":{"position":[[87,5],[136,5],[319,5],[402,5],[491,5],[909,5],[1515,5]]},"317":{"position":[[1135,5],[1144,6],[1221,6]]},"327":{"position":[[78,6],[342,6],[437,7],[618,5],[667,6],[798,7],[856,7]]},"371":{"position":[[60,6]]},"447":{"position":[[204,6]]},"491":{"position":[[258,5]]},"493":{"position":[[303,5],[350,5],[468,6],[582,5],[754,5]]},"553":{"position":[[624,6],[634,5],[673,6]]},"563":{"position":[[186,5]]},"610":{"position":[[159,6]]},"624":{"position":[[89,6],[223,5],[342,5]]},"628":{"position":[[30,5],[118,5],[300,5],[486,6],[674,5]]},"630":{"position":[[0,5],[46,5],[64,5]]},"636":{"position":[[27,7],[72,5],[151,7],[239,6],[379,5],[658,5],[738,5],[1151,5]]},"638":{"position":[[20,6],[245,5],[577,6],[681,6],[718,6],[1219,6]]},"664":{"position":[[231,5]]},"718":{"position":[[20,6],[43,5],[83,5]]},"720":{"position":[[83,5]]},"732":{"position":[[29,7],[74,5],[115,6]]},"776":{"position":[[42,5],[599,6],[811,5]]},"778":{"position":[[25,5],[146,5]]},"780":{"position":[[10,5],[120,5]]},"782":{"position":[[281,6]]},"784":{"position":[[269,6]]},"796":{"position":[[163,5],[585,5]]},"798":{"position":[[40,5],[570,6],[789,5],[1390,8],[1399,6]]},"802":{"position":[[127,5],[521,5]]},"804":{"position":[[6,5],[250,5]]},"806":{"position":[[223,5]]},"808":{"position":[[60,7]]},"810":{"position":[[353,6],[473,6],[866,7]]},"818":{"position":[[752,5]]},"820":{"position":[[170,6]]},"868":{"position":[[123,5],[194,5],[309,5]]},"870":{"position":[[69,7]]},"910":{"position":[[125,5]]},"932":{"position":[[129,5]]},"935":{"position":[[95,6]]},"987":{"position":[[29,5]]},"989":{"position":[[273,6]]},"997":{"position":[[88,5],[566,5],[591,5]]},"999":{"position":[[66,5],[225,5],[670,5],[737,6]]},"1005":{"position":[[30,5],[380,6],[457,6]]},"1017":{"position":[[987,5],[1404,5],[1742,5]]},"1023":{"position":[[555,7]]},"1098":{"position":[[436,8]]},"1134":{"position":[[119,5]]},"1147":{"position":[[965,9]]},"1177":{"position":[[405,5],[796,10],[1394,10]]},"1299":{"position":[[204,6]]},"1329":{"position":[[60,6]]},"1359":{"position":[[663,5]]},"1367":{"position":[[13,5],[457,7],[493,6],[691,5],[804,7],[916,5]]},"1385":{"position":[[93,5],[325,5]]},"1387":{"position":[[304,5],[351,5],[469,6],[583,5],[755,5]]},"1403":{"position":[[397,5],[439,5],[1530,6],[1564,5],[1603,5],[1666,5]]},"1407":{"position":[[421,8],[834,5],[853,6],[878,5]]},"1413":{"position":[[474,8],[495,7],[971,5],[1052,6],[1096,5],[1184,5]]},"1423":{"position":[[386,5]]},"1425":{"position":[[337,5]]},"1505":{"position":[[2448,6],[2455,9],[5438,5],[5459,6],[5466,5],[5711,6],[5813,6],[5849,5],[6098,5],[6473,5],[6631,5],[6686,5],[6821,7],[6947,6],[8771,7],[8810,5],[8855,7]]},"1527":{"position":[[425,6],[737,6],[772,6]]},"1573":{"position":[[54,5],[123,5]]},"1575":{"position":[[415,6],[755,5],[775,5],[909,5]]},"1585":{"position":[[128,5],[224,5],[472,5],[535,5]]},"1587":{"position":[[67,5],[136,5]]},"1589":{"position":[[174,6],[453,5],[626,5]]},"1597":{"position":[[100,5],[638,10],[911,10]]},"1599":{"position":[[993,5],[1084,6],[1381,5],[1439,5],[1551,5],[1609,5]]},"1623":{"position":[[89,6],[223,5],[342,5]]},"1627":{"position":[[30,5],[118,5],[300,5],[486,6],[674,5]]},"1629":{"position":[[0,5],[46,5],[64,5]]},"1636":{"position":[[27,7],[72,5],[151,7],[239,6],[379,5],[738,5]]},"1639":{"position":[[20,6],[245,5],[681,6],[717,6]]},"1658":{"position":[[231,5]]},"1666":{"position":[[192,5],[686,5]]},"1668":{"position":[[169,5]]},"1670":{"position":[[279,6],[873,5]]},"1678":{"position":[[197,6],[379,5],[422,5],[542,6]]},"1684":{"position":[[351,6]]},"1688":{"position":[[103,5],[333,6],[647,5]]},"1692":{"position":[[50,5],[971,7],[1121,6]]},"1696":{"position":[[260,8]]},"1703":{"position":[[216,5],[255,5],[290,5]]},"1705":{"position":[[325,6]]},"1707":{"position":[[110,5],[368,6]]},"1709":{"position":[[245,5],[332,6],[1190,5],[1206,5],[1232,6]]},"1713":{"position":[[208,5],[302,6]]},"1719":{"position":[[724,6],[892,6],[1062,6]]},"1743":{"position":[[222,5]]},"1745":{"position":[[62,5]]},"1751":{"position":[[88,5],[566,5],[591,5]]},"1753":{"position":[[66,5],[225,5],[670,5],[737,6]]},"1759":{"position":[[25,5],[146,5]]},"1761":{"position":[[10,5],[120,5]]},"1763":{"position":[[154,6]]},"1765":{"position":[[30,5],[336,6],[413,6]]},"1767":{"position":[[63,5]]},"1771":{"position":[[215,5],[360,5],[469,6],[512,5],[562,6],[596,5],[669,5],[928,6],[1255,5],[1315,5],[2288,6],[2326,5]]},"1773":{"position":[[876,5],[917,5],[1581,5],[1652,6],[1690,5],[1803,6],[1841,5]]},"1775":{"position":[[640,6],[678,5],[794,6],[832,5]]},"1777":{"position":[[791,6],[829,5],[947,6],[985,5]]},"1791":{"position":[[42,5],[599,6],[811,5]]},"1793":{"position":[[25,5],[146,5]]},"1795":{"position":[[10,5],[120,5]]},"1797":{"position":[[249,6],[274,5],[399,7]]},"1799":{"position":[[237,6],[262,5],[387,7]]},"1811":{"position":[[163,5],[585,5]]},"1813":{"position":[[40,5],[570,6],[789,5],[1390,8],[1399,6]]},"1817":{"position":[[127,5],[521,5],[690,6],[774,6],[794,5]]},"1819":{"position":[[6,5],[250,5]]},"1821":{"position":[[223,5]]},"1823":{"position":[[60,7]]},"1825":{"position":[[353,6],[473,6],[624,6],[1042,7]]},"1827":{"position":[[229,5],[1248,6],[1273,5],[1398,7]]},"1837":{"position":[[170,6]]},"1839":{"position":[[310,5],[324,5],[577,6]]},"1909":{"position":[[123,5],[194,5],[309,5]]},"1911":{"position":[[153,5],[244,5],[370,5]]},"1913":{"position":[[69,7]]},"1915":{"position":[[75,7]]},"1953":{"position":[[18,6],[40,5],[80,5],[143,6]]},"1955":{"position":[[80,5]]},"1967":{"position":[[100,6],[167,6]]},"2031":{"position":[[125,5]]},"2033":{"position":[[165,5]]},"2053":{"position":[[129,5]]},"2056":{"position":[[95,6]]},"2150":{"position":[[1681,5],[2335,5],[2392,5]]},"2160":{"position":[[386,5]]},"2172":{"position":[[289,5]]},"2174":{"position":[[286,5]]},"2228":{"position":[[405,5],[796,10]]},"2234":{"position":[[436,8]]},"2247":{"position":[[119,5]]},"2260":{"position":[[974,9]]},"2354":{"position":[[1104,5],[1521,5],[1859,5]]},"2360":{"position":[[555,7]]},"2444":{"position":[[66,6]]},"2530":{"position":[[663,5]]},"2538":{"position":[[13,5],[457,7],[493,6],[691,5],[804,7],[916,5]]},"2556":{"position":[[98,5],[350,5]]},"2558":{"position":[[308,5],[360,5],[486,7],[605,5],[793,5]]},"2574":{"position":[[76,7]]},"2586":{"position":[[204,6]]},"2622":{"position":[[4413,5],[5041,5]]},"2624":{"position":[[99,5],[136,5]]},"2628":{"position":[[71,5],[211,6],[260,5],[327,5],[429,6],[445,5],[1265,6],[1407,5],[1509,5],[1583,5]]},"2632":{"position":[[4625,5]]},"2640":{"position":[[107,5],[146,5]]},"2644":{"position":[[83,5],[205,6],[266,5],[322,5],[422,6],[436,5],[1298,6],[1404,5],[1509,5],[1611,5],[1713,5]]},"2668":{"position":[[54,5],[123,5]]},"2670":{"position":[[415,6],[755,5],[775,5],[909,5]]},"2680":{"position":[[128,5],[224,5],[472,5],[535,5]]},"2682":{"position":[[67,5],[136,5]]},"2684":{"position":[[174,6],[453,5],[626,5]]},"2686":{"position":[[2463,6],[2470,9],[5500,5],[5521,6],[5528,5],[5773,6],[5872,6],[5908,5],[6163,5],[6546,5],[6712,5],[6767,5],[6902,7],[7028,6],[8888,7],[8928,5],[8975,7]]},"2712":{"position":[[90,5],[536,6]]},"2720":{"position":[[143,6]]},"2760":{"position":[[426,6],[738,6],[773,6]]},"2780":{"position":[[100,5],[638,10],[911,10]]},"2782":{"position":[[993,5],[1084,6],[1381,5],[1439,5],[1551,5],[1609,5]]},"2810":{"position":[[89,6],[223,5],[342,5]]},"2814":{"position":[[30,5],[118,5],[300,5],[486,6],[674,5]]},"2816":{"position":[[0,5],[46,5],[64,5]]},"2823":{"position":[[27,7],[72,5],[151,7],[239,6],[379,5],[702,5]]},"2826":{"position":[[20,6],[245,5],[645,6],[681,6]]},"2830":{"position":[[192,5],[689,5]]},"2832":{"position":[[169,5]]},"2834":{"position":[[279,6],[876,5]]},"2842":{"position":[[197,6],[379,5],[422,5],[542,6]]},"2848":{"position":[[351,6]]},"2852":{"position":[[103,5],[333,6],[647,5]]},"2856":{"position":[[50,5],[971,7],[1121,6]]},"2860":{"position":[[260,8]]},"2867":{"position":[[216,5],[255,5],[290,5]]},"2871":{"position":[[110,5]]},"2873":{"position":[[771,5],[1389,5],[1405,5],[1435,6]]},"2877":{"position":[[663,5]]},"2883":{"position":[[921,6],[1089,6],[1259,6]]},"2910":{"position":[[231,5]]},"2930":{"position":[[215,5],[360,5],[469,6],[512,5],[562,6],[596,5],[669,5],[928,6],[1255,5],[1315,5],[2288,6],[2326,5]]},"2932":{"position":[[876,5],[917,5],[1581,5],[1652,6],[1690,5],[1803,6],[1841,5]]},"2934":{"position":[[640,6],[678,5],[794,6],[832,5]]},"2936":{"position":[[791,6],[829,5],[947,6],[985,5]]},"2948":{"position":[[222,5]]},"2950":{"position":[[62,5]]},"2956":{"position":[[88,5],[566,5],[591,5]]},"2958":{"position":[[66,5],[225,5],[670,5],[737,6]]},"2964":{"position":[[25,5],[146,5]]},"2966":{"position":[[10,5],[120,5]]},"2968":{"position":[[154,6]]},"2970":{"position":[[30,5],[449,5],[696,6],[773,6]]},"2972":{"position":[[63,5]]},"2974":{"position":[[99,5],[148,6],[279,7],[337,7],[606,5],[669,5]]},"2998":{"position":[[18,6],[40,5],[80,5],[143,6]]},"3000":{"position":[[80,5]]},"3012":{"position":[[100,6],[167,6]]},"3020":{"position":[[62,5],[642,6],[866,5]]},"3022":{"position":[[39,5],[191,5]]},"3024":{"position":[[36,6],[162,5]]},"3026":{"position":[[249,6],[274,5],[331,5],[368,5],[420,5]]},"3028":{"position":[[237,6],[262,5],[319,5],[356,5],[408,5]]},"3040":{"position":[[136,5],[568,6]]},"3042":{"position":[[44,5],[561,6],[775,5],[1159,6],[1213,5],[1327,8],[1336,6],[1499,5],[1620,5],[1783,6]]},"3046":{"position":[[219,5],[665,5],[834,6],[918,6],[938,5]]},"3048":{"position":[[6,5],[341,5]]},"3050":{"position":[[314,5]]},"3052":{"position":[[60,7]]},"3054":{"position":[[353,6],[473,6],[624,6],[1000,7],[1109,7]]},"3056":{"position":[[195,5],[1214,6],[1239,5],[1296,5],[1333,5],[1385,5]]},"3066":{"position":[[177,6]]},"3068":{"position":[[364,6],[587,5]]},"3166":{"position":[[123,5],[194,5],[309,5]]},"3168":{"position":[[153,5],[244,5],[370,5]]},"3170":{"position":[[69,7]]},"3172":{"position":[[75,7]]},"3222":{"position":[[125,5]]},"3224":{"position":[[138,5]]},"3246":{"position":[[129,5]]},"3249":{"position":[[95,6]]},"3251":{"position":[[218,7],[314,5],[352,5]]},"3379":{"position":[[1681,5],[2335,5],[2392,5]]},"3389":{"position":[[386,5]]},"3401":{"position":[[289,5]]},"3403":{"position":[[286,5]]},"3415":{"position":[[1104,5],[1521,5],[1859,5]]},"3421":{"position":[[379,5],[732,6]]},"3578":{"position":[[405,5],[796,10]]},"3584":{"position":[[436,8]]},"3597":{"position":[[119,5]]},"3610":{"position":[[974,9]]},"3633":{"position":[[4413,5],[5041,5]]},"3635":{"position":[[99,5],[136,5]]},"3639":{"position":[[71,5],[211,6],[260,5],[327,5],[429,6],[445,5],[717,5],[740,5],[967,5],[1103,5],[1316,6],[1427,5],[1529,5],[1603,5]]},"3643":{"position":[[4625,5]]},"3651":{"position":[[107,5],[146,5]]},"3655":{"position":[[83,5],[205,6],[266,5],[322,5],[422,6],[436,5],[1151,5],[1287,5],[1551,6],[1677,5],[1779,5],[1881,5]]}}}],["token/no",{"_index":2876,"t":{"315":{"position":[[482,8]]}}}],["token/top",{"_index":4933,"t":{"1692":{"position":[[1189,11]]},"2856":{"position":[[1189,11]]}}}],["token_",{"_index":5544,"t":{"2974":{"position":[[744,7]]}}}],["token_audi",{"_index":3927,"t":{"782":{"position":[[144,14],[188,17],[236,14]]},"1001":{"position":[[119,14]]},"1755":{"position":[[87,14]]},"1797":{"position":[[112,14],[156,17],[204,14]]},"1827":{"position":[[1060,14]]},"2960":{"position":[[87,14]]},"3026":{"position":[[112,14],[156,17],[204,14]]},"3056":{"position":[[1026,14]]}}}],["token_audience_regex",{"_index":5025,"t":{"1827":{"position":[[534,20],[1012,20],[1197,20]]},"3056":{"position":[[500,20],[978,20],[1163,20]]}}}],["token_hmac_secret_key",{"_index":2600,"t":{"248":{"position":[[57,24]]},"489":{"position":[[37,21]]},"513":{"position":[[41,24]]},"618":{"position":[[335,21]]},"802":{"position":[[219,21],[283,21]]},"856":{"position":[[74,24]]},"858":{"position":[[1133,24]]},"884":{"position":[[118,24],[206,21]]},"886":{"position":[[176,22]]},"1005":{"position":[[315,21]]},"1147":{"position":[[302,24]]},"1383":{"position":[[37,21]]},"1441":{"position":[[41,24]]},"1505":{"position":[[793,24],[1327,24],[6531,21],[7830,24]]},"1765":{"position":[[271,21]]},"1817":{"position":[[219,21],[283,21]]},"1843":{"position":[[1164,24]]},"1899":{"position":[[308,24],[1061,24]]},"1997":{"position":[[118,24],[206,21]]},"1999":{"position":[[176,22]]},"2260":{"position":[[302,24]]},"2554":{"position":[[39,21]]},"2610":{"position":[[41,24]]},"2686":{"position":[[808,24],[1342,24],[6608,21],[7927,24]]},"2970":{"position":[[631,21]]},"3046":{"position":[[363,21],[427,21]]},"3072":{"position":[[1452,24]]},"3134":{"position":[[308,24],[1061,24]]},"3186":{"position":[[118,24],[206,21]]},"3188":{"position":[[176,22]]},"3610":{"position":[[302,24]]}}}],["token_hmac_secret_key/token_rsa_public_key",{"_index":3512,"t":{"489":{"position":[[161,42]]},"1383":{"position":[[161,42]]},"2554":{"position":[[164,42]]}}}],["token_issu",{"_index":3930,"t":{"784":{"position":[[142,12],[184,15],[226,12]]},"1003":{"position":[[117,12]]},"1757":{"position":[[85,12]]},"1799":{"position":[[110,12],[152,15],[194,12]]},"1827":{"position":[[1043,12]]},"2962":{"position":[[85,12]]},"3028":{"position":[[110,12],[152,15],[194,12]]},"3056":{"position":[[1009,12]]}}}],["token_issuer_regex",{"_index":5024,"t":{"1827":{"position":[[368,18],[733,21],[989,18],[1174,18]]},"3056":{"position":[[334,18],[699,21],[955,18],[1140,18]]}}}],["token_jwks_public_endpoint",{"_index":2923,"t":{"327":{"position":[[878,29]]},"810":{"position":[[236,26],[318,26]]},"1825":{"position":[[236,26],[318,26]]},"1827":{"position":[[808,29],[922,26]]},"2974":{"position":[[359,29]]},"3054":{"position":[[236,26],[318,26]]},"3056":{"position":[[774,29],[888,26]]}}}],["token_revok",{"_index":3722,"t":{"632":{"position":[[84,15],[521,12]]},"634":{"position":[[214,15]]},"1631":{"position":[[84,15],[521,12]]},"1633":{"position":[[214,15]]},"2818":{"position":[[84,15],[521,12]]},"2820":{"position":[[214,15]]}}}],["token_rsa_public_key",{"_index":3511,"t":{"489":{"position":[[63,21]]},"1383":{"position":[[63,21]]},"2554":{"position":[[65,21]]}}}],["tokens_left",{"_index":5471,"t":{"2712":{"position":[[504,14],[593,14],[962,11]]},"2720":{"position":[[110,11]]}}}],["token});centrifuge.connect",{"_index":5022,"t":{"1817":{"position":[[697,29]]},"3046":{"position":[[841,29]]}}}],["token});sub.on('publ",{"_index":2809,"t":{"291":{"position":[[520,29]]}}}],["told",{"_index":2352,"t":{"200":{"position":[[2165,4]]}}}],["toler",{"_index":3003,"t":{"349":{"position":[[601,8]]},"842":{"position":[[694,8]]},"1281":{"position":[[614,8]]},"1423":{"position":[[184,8],[1140,8]]},"1859":{"position":[[1047,8]]},"2160":{"position":[[184,8],[1140,8]]},"2404":{"position":[[638,8]]},"3090":{"position":[[1047,8]]},"3389":{"position":[[184,8],[1140,8]]}}}],["toml",{"_index":3531,"t":{"509":{"position":[[1020,4]]},"620":{"position":[[169,4]]},"866":{"position":[[262,4]]},"880":{"position":[[139,5]]},"882":{"position":[[69,5]]},"886":{"position":[[25,4]]},"1437":{"position":[[1253,4]]},"1905":{"position":[[301,4]]},"1993":{"position":[[139,5]]},"1995":{"position":[[69,5]]},"1999":{"position":[[25,4]]},"2606":{"position":[[1286,4]]},"3162":{"position":[[301,4]]},"3182":{"position":[[139,5]]},"3184":{"position":[[69,5]]},"3188":{"position":[[25,4]]}}}],["ton",{"_index":385,"t":{"8":{"position":[[1680,4]]},"74":{"position":[[1355,4],[2664,4]]},"441":{"position":[[112,4]]},"1267":{"position":[[112,4]]},"2516":{"position":[[112,4]]}}}],["tool",{"_index":1193,"t":{"58":{"position":[[1685,4]]},"118":{"position":[[888,6]]},"158":{"position":[[1243,4]]},"184":{"position":[[902,4]]},"216":{"position":[[58,5]]},"218":{"position":[[749,4]]},"222":{"position":[[22,4],[610,5]]},"240":{"position":[[232,5]]},"242":{"position":[[408,5],[1373,4]]},"311":{"position":[[895,6]]},"331":{"position":[[270,5],[579,5]]},"481":{"position":[[169,4]]},"620":{"position":[[579,4]]},"624":{"position":[[371,6]]},"1147":{"position":[[65,4]]},"1457":{"position":[[739,5]]},"1505":{"position":[[4697,5],[8562,5],[9794,5]]},"1623":{"position":[[371,6]]},"1672":{"position":[[832,5]]},"2126":{"position":[[61,4],[222,6]]},"2260":{"position":[[65,4]]},"2289":{"position":[[799,5]]},"2686":{"position":[[4759,5],[8659,5],[9950,5]]},"2810":{"position":[[371,6]]},"2836":{"position":[[832,5]]},"3349":{"position":[[61,4],[220,6]]},"3610":{"position":[[65,4]]},"3629":{"position":[[267,5],[580,5]]}}}],["toolspython",{"_index":4641,"t":{"1487":{"position":[[96,11]]},"2319":{"position":[[96,11]]},"3532":{"position":[[96,11]]}}}],["top",{"_index":391,"t":{"10":{"position":[[55,3]]},"16":{"position":[[5648,3],[6158,3]]},"26":{"position":[[229,3],[801,3]]},"42":{"position":[[600,3]]},"52":{"position":[[113,3]]},"58":{"position":[[242,3]]},"60":{"position":[[845,3],[936,3],[1652,3]]},"82":{"position":[[295,3]]},"94":{"position":[[1548,3]]},"122":{"position":[[357,3]]},"126":{"position":[[399,3]]},"152":{"position":[[665,3]]},"244":{"position":[[179,3]]},"258":{"position":[[174,3]]},"264":{"position":[[1570,3]]},"280":{"position":[[3236,3]]},"283":{"position":[[51,3]]},"305":{"position":[[742,3]]},"321":{"position":[[767,3]]},"339":{"position":[[293,3]]},"369":{"position":[[81,3]]},"391":{"position":[[21,3]]},"461":{"position":[[170,3]]},"473":{"position":[[1427,3],[1564,3]]},"477":{"position":[[97,3]]},"589":{"position":[[418,3]]},"678":{"position":[[1079,3]]},"822":{"position":[[107,3]]},"838":{"position":[[488,3]]},"840":{"position":[[413,3]]},"856":{"position":[[347,3]]},"858":{"position":[[1037,3],[2178,3]]},"932":{"position":[[163,4]]},"971":{"position":[[153,3]]},"973":{"position":[[1289,3],[3413,3]]},"1023":{"position":[[1299,3]]},"1025":{"position":[[1159,3],[1662,3]]},"1045":{"position":[[48,3]]},"1049":{"position":[[204,3],[239,3],[939,3]]},"1051":{"position":[[842,3]]},"1081":{"position":[[136,3]]},"1293":{"position":[[23,3]]},"1313":{"position":[[170,3]]},"1321":{"position":[[58,3]]},"1349":{"position":[[21,3]]},"1363":{"position":[[151,3]]},"1403":{"position":[[1193,3],[1233,3],[1270,3],[1313,3],[1349,3],[2392,3]]},"1427":{"position":[[330,3]]},"1473":{"position":[[1643,3]]},"1475":{"position":[[72,3]]},"1511":{"position":[[679,3]]},"1615":{"position":[[1073,3]]},"1831":{"position":[[1091,3]]},"1843":{"position":[[1068,3],[2065,3],[2288,3]]},"1845":{"position":[[107,3]]},"1855":{"position":[[488,3]]},"1857":{"position":[[505,3]]},"1899":{"position":[[804,3]]},"1981":{"position":[[153,3]]},"1983":{"position":[[1289,3],[2346,3],[4557,3]]},"2053":{"position":[[163,4]]},"2120":{"position":[[349,3]]},"2122":{"position":[[331,3]]},"2148":{"position":[[23,3]]},"2189":{"position":[[852,3]]},"2201":{"position":[[136,3]]},"2305":{"position":[[1643,3]]},"2307":{"position":[[72,3]]},"2360":{"position":[[1299,3]]},"2362":{"position":[[1159,3],[1662,3]]},"2364":{"position":[[1420,3]]},"2384":{"position":[[48,3]]},"2388":{"position":[[241,3],[276,3],[1142,3]]},"2390":{"position":[[842,3]]},"2392":{"position":[[100,3]]},"2416":{"position":[[61,3]]},"2420":{"position":[[322,3]]},"2428":{"position":[[23,3]]},"2436":{"position":[[62,3]]},"2464":{"position":[[21,3]]},"2534":{"position":[[151,3]]},"2578":{"position":[[594,3]]},"2600":{"position":[[170,3]]},"2646":{"position":[[1847,3]]},"2648":{"position":[[193,3]]},"2656":{"position":[[170,3]]},"2770":{"position":[[679,3]]},"2800":{"position":[[1073,3]]},"3072":{"position":[[1356,3],[2353,3],[2576,3]]},"3074":{"position":[[107,3]]},"3084":{"position":[[488,3]]},"3088":{"position":[[505,3]]},"3134":{"position":[[804,3]]},"3140":{"position":[[153,3]]},"3142":{"position":[[1289,3],[2346,3],[4557,3]]},"3246":{"position":[[163,4]]},"3343":{"position":[[341,3]]},"3345":{"position":[[591,3]]},"3377":{"position":[[23,3]]},"3421":{"position":[[1102,3]]},"3423":{"position":[[1159,3],[1662,3]]},"3425":{"position":[[1393,3]]},"3445":{"position":[[48,3]]},"3449":{"position":[[241,3],[276,3],[1142,3]]},"3451":{"position":[[842,3]]},"3476":{"position":[[136,3]]},"3492":{"position":[[852,3]]},"3518":{"position":[[1485,3]]},"3520":{"position":[[72,3]]},"3612":{"position":[[100,3]]},"3657":{"position":[[1847,3]]},"3659":{"position":[[193,3]]},"3667":{"position":[[170,3]]}}}],["topic",{"_index":238,"t":{"4":{"position":[[391,6]]},"16":{"position":[[960,6],[1036,5],[1530,6],[1561,7],[1580,5],[1624,6],[2601,6],[2696,5],[3859,7],[3919,7],[4076,7],[4878,7],[6437,6],[7465,5]]},"18":{"position":[[1935,6]]},"22":{"position":[[281,6]]},"66":{"position":[[108,5]]},"250":{"position":[[624,5]]},"268":{"position":[[485,6]]},"270":{"position":[[417,9]]},"272":{"position":[[88,6],[160,6],[210,5],[359,6],[433,7],[463,5],[523,5],[647,7],[782,6],[897,6],[1161,6],[1206,5]]},"276":{"position":[[202,7]]},"1199":{"position":[[1078,6]]},"1217":{"position":[[339,6]]},"1682":{"position":[[88,6],[160,6],[210,5],[356,6],[430,7],[512,6],[637,5],[697,5],[821,7],[920,7],[1010,6],[1234,6],[1279,5]]},"1684":{"position":[[314,6],[529,8]]},"1688":{"position":[[482,6]]},"1692":{"position":[[998,6],[1177,7]]},"1703":{"position":[[439,6],[473,5],[544,6],[595,6]]},"1705":{"position":[[580,6],[915,6]]},"1709":{"position":[[704,6],[1327,6]]},"1711":{"position":[[28,7],[164,6],[198,7]]},"1713":{"position":[[15,5],[480,6],[514,6],[1158,5]]},"1715":{"position":[[18,6],[48,6],[286,6],[320,7]]},"1717":{"position":[[13,5],[143,6],[177,6],[688,5]]},"1719":{"position":[[53,7],[600,6],[634,6],[772,5],[940,5]]},"2100":{"position":[[1078,6]]},"2396":{"position":[[337,6]]},"2422":{"position":[[118,7],[844,6],[1043,6]]},"2424":{"position":[[1059,6]]},"2846":{"position":[[88,6],[160,6],[210,5],[356,6],[430,7],[512,6],[637,5],[697,5],[821,7],[920,7],[1010,6],[1234,6],[1279,5]]},"2848":{"position":[[314,6],[529,8]]},"2852":{"position":[[482,6]]},"2856":{"position":[[998,6],[1177,7]]},"2867":{"position":[[439,6],[473,5],[544,6],[595,6]]},"2869":{"position":[[439,6],[733,6],[760,6]]},"2873":{"position":[[494,6],[944,6],[978,6],[1537,6]]},"2875":{"position":[[28,7],[164,6],[198,7]]},"2877":{"position":[[15,5],[850,6],[884,6],[933,5],[1381,5],[1398,5]]},"2879":{"position":[[18,6],[48,6],[286,6],[320,7]]},"2881":{"position":[[13,5],[479,6],[513,6],[843,5],[860,5]]},"2883":{"position":[[53,7],[969,5],[1137,5]]},"3285":{"position":[[1078,6]]}}}],["topic_prefix",{"_index":4981,"t":{"1713":{"position":[[540,12]]},"1717":{"position":[[203,12]]},"2877":{"position":[[910,12]]},"2881":{"position":[[539,12]]}}}],["topics_upd",{"_index":4967,"t":{"1705":{"position":[[533,13]]},"2869":{"position":[[394,13]]}}}],["topolog",{"_index":4419,"t":{"1203":{"position":[[1114,10]]},"2106":{"position":[[1114,10]]},"3289":{"position":[[1114,10]]}}}],["tornado",{"_index":4196,"t":{"1017":{"position":[[4812,7]]},"2354":{"position":[[4935,7]]},"3415":{"position":[[4964,7]]}}}],["tornado.ioloop.ioloop.instance().start()if",{"_index":4209,"t":{"1017":{"position":[[5349,42]]},"2354":{"position":[[5472,42]]},"3415":{"position":[[5501,42]]}}}],["tornado.web.appl",{"_index":4205,"t":{"1017":{"position":[[5251,25]]},"2354":{"position":[[5374,25]]},"3415":{"position":[[5403,25]]}}}],["total",{"_index":137,"t":{"2":{"position":[[2095,5],[2599,5]]},"52":{"position":[[418,5]]},"678":{"position":[[180,5]]},"1471":{"position":[[700,5],[756,5]]},"1477":{"position":[[470,5]]},"1615":{"position":[[180,5],[1524,5]]},"1666":{"position":[[483,5],[514,5],[697,5],[1009,8]]},"1668":{"position":[[611,5]]},"1670":{"position":[[1050,6]]},"1672":{"position":[[1465,8]]},"1723":{"position":[[137,5]]},"2303":{"position":[[700,5],[756,5]]},"2309":{"position":[[517,5]]},"2800":{"position":[[180,5],[1524,5]]},"2806":{"position":[[146,5]]},"2830":{"position":[[486,5],[517,5],[700,5],[1012,8]]},"2832":{"position":[[617,5]]},"2834":{"position":[[1056,6]]},"2836":{"position":[[1465,8]]},"2873":{"position":[[400,5],[1232,5]]},"2877":{"position":[[463,5],[1218,5]]},"2881":{"position":[[342,5]]},"2889":{"position":[[210,5]]},"3324":{"position":[[1420,5]]},"3516":{"position":[[541,5],[597,5]]},"3522":{"position":[[577,5]]}}}],["total_count",{"_index":5527,"t":{"2873":{"position":[[1209,11]]},"2877":{"position":[[1195,11]]},"2881":{"position":[[723,11]]}}}],["touch",{"_index":2104,"t":{"180":{"position":[[362,7]]}}}],["toward",{"_index":2787,"t":{"280":{"position":[[3625,7]]},"317":{"position":[[1116,7]]},"321":{"position":[[1352,7]]},"349":{"position":[[473,7]]},"465":{"position":[[232,7]]},"477":{"position":[[4777,7],[4989,7]]},"479":{"position":[[2135,7]]},"973":{"position":[[806,7]]},"1247":{"position":[[291,7]]},"1269":{"position":[[46,7]]},"1281":{"position":[[486,7]]},"1519":{"position":[[160,7],[464,7]]},"1773":{"position":[[225,7]]},"1865":{"position":[[481,7]]},"1867":{"position":[[454,7]]},"1983":{"position":[[799,7]]},"2168":{"position":[[1466,7]]},"2404":{"position":[[502,7]]},"2496":{"position":[[291,7]]},"2518":{"position":[[46,7]]},"2702":{"position":[[160,7],[464,7]]},"2932":{"position":[[225,7]]},"3096":{"position":[[481,7]]},"3098":{"position":[[454,7]]},"3142":{"position":[[799,7]]},"3365":{"position":[[760,7],[1270,7],[3665,7]]},"3397":{"position":[[1466,7]]}}}],["toxic",{"_index":3477,"t":{"481":{"position":[[387,5],[527,5]]}}}],["toxic_redi",{"_index":3485,"t":{"481":{"position":[[561,11]]}}}],["toxic_redistoxiproxi",{"_index":3482,"t":{"481":{"position":[[502,20]]}}}],["toxiproxi",{"_index":3476,"t":{"481":{"position":[[363,9],[444,9]]}}}],["toxyproxi",{"_index":3475,"t":{"481":{"position":[[310,9]]}}}],["toy",{"_index":1774,"t":{"114":{"position":[[59,3]]}}}],["toyyyymmdd(time)ord",{"_index":3785,"t":{"674":{"position":[[543,21]]},"676":{"position":[[598,21]]},"1605":{"position":[[463,21]]},"1607":{"position":[[442,21]]},"1609":{"position":[[589,21]]},"1611":{"position":[[464,21]]},"1613":{"position":[[664,21]]},"2790":{"position":[[463,21]]},"2792":{"position":[[442,21]]},"2794":{"position":[[589,21]]},"2796":{"position":[[464,21]]},"2798":{"position":[[664,21]]}}}],["trace",{"_index":2910,"t":{"325":{"position":[[42,7],[168,7],[238,6],[253,7],[401,5]]},"393":{"position":[[161,5]]},"553":{"position":[[96,7]]},"624":{"position":[[38,7],[110,7]]},"1527":{"position":[[105,7]]},"1623":{"position":[[38,7],[110,7]]},"2760":{"position":[[105,7]]},"2810":{"position":[[38,7],[110,7]]},"3327":{"position":[[36,6],[225,7],[458,7],[862,6],[914,6]]},"3329":{"position":[[89,5]]}}}],["trace.txt",{"_index":3717,"t":{"624":{"position":[[290,9]]},"1623":{"position":[[290,9]]},"2810":{"position":[[290,9]]}}}],["traceid",{"_index":4180,"t":{"1013":{"position":[[626,7]]},"2350":{"position":[[626,7]]},"3411":{"position":[[626,7]]}}}],["track",{"_index":1444,"t":{"80":{"position":[[709,5]]},"262":{"position":[[1413,5]]},"270":{"position":[[1818,5]]},"415":{"position":[[366,6]]},"640":{"position":[[671,6]]},"1077":{"position":[[1141,5]]},"1239":{"position":[[366,6]]},"1413":{"position":[[108,5]]},"1591":{"position":[[953,6]]},"1721":{"position":[[88,8]]},"2122":{"position":[[46,8],[497,5]]},"2126":{"position":[[256,8]]},"2197":{"position":[[1141,5]]},"2488":{"position":[[366,6]]},"2784":{"position":[[953,6]]},"2806":{"position":[[186,8]]},"2887":{"position":[[88,8]]},"2889":{"position":[[261,8],[792,8]]},"3324":{"position":[[141,6],[712,8],[1684,8],[2482,6],[3014,8],[3749,6],[5706,8],[5863,6],[6083,6]]},"3345":{"position":[[39,8],[757,5]]},"3349":{"position":[[254,8]]},"3472":{"position":[[1141,5]]}}}],["tracker",{"_index":3235,"t":{"475":{"position":[[2786,8]]}}}],["trade",{"_index":770,"t":{"18":{"position":[[1706,5]]},"158":{"position":[[529,5]]},"280":{"position":[[322,7],[1495,7],[1829,7]]},"285":{"position":[[378,5]]},"437":{"position":[[1701,5]]},"479":{"position":[[3387,5]]},"483":{"position":[[1392,5]]},"776":{"position":[[1104,5]]},"1263":{"position":[[1705,5]]},"1791":{"position":[[1104,5]]},"2512":{"position":[[1817,5]]}}}],["trader",{"_index":2753,"t":{"280":{"position":[[792,7],[3575,7]]}}}],["tradingview",{"_index":2785,"t":{"280":{"position":[[3269,11]]}}}],["tradit",{"_index":1173,"t":{"58":{"position":[[403,11]]},"258":{"position":[[137,11]]}}}],["tradition",{"_index":2994,"t":{"345":{"position":[[727,13]]},"1277":{"position":[[727,13]]},"2400":{"position":[[1449,13]]}}}],["traffic",{"_index":544,"t":{"14":{"position":[[396,8],[455,8]]},"76":{"position":[[1136,7]]},"124":{"position":[[270,8]]},"250":{"position":[[305,7]]},"301":{"position":[[1039,7]]},"379":{"position":[[234,7]]},"429":{"position":[[995,7]]},"433":{"position":[[397,7]]},"1160":{"position":[[209,7]]},"1162":{"position":[[307,7]]},"1195":{"position":[[91,7]]},"1255":{"position":[[995,7]]},"1259":{"position":[[397,7]]},"1337":{"position":[[223,7]]},"1481":{"position":[[158,7]]},"2096":{"position":[[91,7]]},"2191":{"position":[[981,7]]},"2313":{"position":[[158,7]]},"2340":{"position":[[209,7]]},"2342":{"position":[[307,7]]},"2392":{"position":[[2209,7]]},"2452":{"position":[[223,8]]},"2504":{"position":[[995,7]]},"2508":{"position":[[397,7]]},"3281":{"position":[[91,7]]},"3466":{"position":[[981,7]]},"3612":{"position":[[2408,7]]},"3625":{"position":[[209,7]]},"3627":{"position":[[307,7]]}}}],["transact",{"_index":2477,"t":{"218":{"position":[[516,13]]}}}],["transfer",{"_index":1449,"t":{"82":{"position":[[406,9]]},"124":{"position":[[1243,8],[1471,9],[2101,9]]},"222":{"position":[[391,8]]},"240":{"position":[[296,8]]},"1081":{"position":[[196,12]]},"1403":{"position":[[2134,8],[2483,8]]},"2201":{"position":[[196,12]]},"3324":{"position":[[4201,9]]},"3365":{"position":[[1255,9],[2452,9]]},"3367":{"position":[[861,8]]},"3476":{"position":[[196,12]]}}}],["transform",{"_index":1033,"t":{"40":{"position":[[19,9]]},"172":{"position":[[158,11]]},"178":{"position":[[68,9]]},"180":{"position":[[187,11]]},"222":{"position":[[651,16]]},"429":{"position":[[1176,11]]},"447":{"position":[[252,10]]},"1015":{"position":[[271,11]]},"1255":{"position":[[1176,11]]},"1299":{"position":[[252,10]]},"1371":{"position":[[242,9]]},"1405":{"position":[[1430,11]]},"1487":{"position":[[875,11]]},"1497":{"position":[[1412,11]]},"1899":{"position":[[1033,9]]},"2154":{"position":[[1120,11]]},"2319":{"position":[[875,11]]},"2352":{"position":[[271,11]]},"2504":{"position":[[1176,11]]},"2542":{"position":[[242,9]]},"2586":{"position":[[252,10]]},"2692":{"position":[[1412,11]]},"3134":{"position":[[1033,9]]},"3383":{"position":[[1120,11]]},"3413":{"position":[[271,11]]},"3532":{"position":[[875,11]]},"3538":{"position":[[1232,11]]}}}],["transit",{"_index":1806,"t":{"122":{"position":[[1468,12],[1749,12],[1790,12]]},"295":{"position":[[446,12]]},"1505":{"position":[[3953,11],[4026,11]]},"2622":{"position":[[3765,10],[4830,12]]},"2632":{"position":[[3807,10],[4448,12]]},"2686":{"position":[[4011,11],[4084,11]]},"3633":{"position":[[3765,10],[4830,12]]},"3643":{"position":[[3807,10],[4448,12]]}}}],["translat",{"_index":2017,"t":{"166":{"position":[[94,10]]},"293":{"position":[[759,10]]},"429":{"position":[[743,10]]},"620":{"position":[[84,9]]},"1255":{"position":[[743,10]]},"2504":{"position":[[743,10]]}}}],["transmit",{"_index":3088,"t":{"465":{"position":[[345,8]]}}}],["transpar",{"_index":3037,"t":{"413":{"position":[[25,13]]},"1013":{"position":[[54,13],[171,11]]},"1237":{"position":[[25,13]]},"2350":{"position":[[54,13],[171,11]]},"2486":{"position":[[25,13]]},"3411":{"position":[[54,13],[171,11]]}}}],["transport",{"_index":232,"t":{"4":{"position":[[319,9]]},"14":{"position":[[561,9],[686,10],[823,10]]},"24":{"position":[[464,11]]},"32":{"position":[[233,10],[403,10],[488,10],[1278,11],[1379,11],[1665,10],[2108,11],[2247,10]]},"58":{"position":[[502,12]]},"60":{"position":[[510,9],[954,9]]},"86":{"position":[[589,9],[751,11]]},"92":{"position":[[180,10]]},"94":{"position":[[517,10],[1320,9],[1392,9],[1479,9],[1715,9],[1752,9],[1841,9],[1906,9],[1957,10]]},"98":{"position":[[43,10]]},"116":{"position":[[157,9],[1013,9]]},"118":{"position":[[590,10],[1036,9]]},"120":{"position":[[133,10],[197,9]]},"124":{"position":[[674,10],[751,11],[1126,9],[1324,9],[1979,11],[2213,9],[3065,10],[3334,9],[3513,10],[3546,10],[3563,10],[3651,10],[3745,10],[3915,9],[3977,9],[4081,11],[4332,9]]},"128":{"position":[[639,11],[1586,11]]},"144":{"position":[[891,10],[908,10],[1004,10]]},"152":{"position":[[1277,9]]},"154":{"position":[[307,10]]},"170":{"position":[[68,10]]},"206":{"position":[[290,10]]},"238":{"position":[[311,9]]},"254":{"position":[[193,10]]},"260":{"position":[[371,9],[1321,9]]},"283":{"position":[[1035,9]]},"295":{"position":[[1070,10]]},"305":{"position":[[376,10]]},"311":{"position":[[583,10],[1043,9]]},"333":{"position":[[268,10]]},"369":{"position":[[9,9],[68,9],[247,10],[370,9]]},"397":{"position":[[40,9]]},"399":{"position":[[13,9],[286,11]]},"421":{"position":[[297,10],[327,11]]},"423":{"position":[[18,9]]},"429":{"position":[[921,9]]},"465":{"position":[[111,10]]},"493":{"position":[[1017,11],[1158,9],[1233,9]]},"602":{"position":[[108,9]]},"616":{"position":[[61,10]]},"672":{"position":[[1722,10]]},"674":{"position":[[253,11],[1032,11]]},"688":{"position":[[230,12]]},"892":{"position":[[211,10]]},"973":{"position":[[1978,11]]},"979":{"position":[[58,9]]},"981":{"position":[[58,9]]},"1015":{"position":[[52,9]]},"1017":{"position":[[652,9],[1173,12],[2496,9],[2516,9]]},"1019":{"position":[[792,9],[812,9]]},"1021":{"position":[[770,9],[790,9]]},"1023":{"position":[[2094,9],[2114,9]]},"1025":{"position":[[2128,9],[2148,9]]},"1037":{"position":[[47,10]]},"1039":{"position":[[210,11]]},"1084":{"position":[[40,10]]},"1090":{"position":[[14,10],[77,10],[133,10]]},"1092":{"position":[[15,10],[155,10],[497,10]]},"1094":{"position":[[26,10],[954,9]]},"1098":{"position":[[1135,9]]},"1221":{"position":[[40,9]]},"1223":{"position":[[13,9],[286,11]]},"1245":{"position":[[357,10],[387,11]]},"1247":{"position":[[411,9]]},"1249":{"position":[[18,9]]},"1255":{"position":[[921,9]]},"1293":{"position":[[1141,9]]},"1325":{"position":[[349,10]]},"1327":{"position":[[9,9],[366,10],[489,9],[688,10]]},"1355":{"position":[[987,9]]},"1361":{"position":[[55,11],[153,11],[273,10],[475,9]]},"1387":{"position":[[1018,11]]},"1403":{"position":[[2082,9]]},"1467":{"position":[[77,10]]},"1485":{"position":[[186,9]]},"1521":{"position":[[137,9]]},"1523":{"position":[[143,9]]},"1597":{"position":[[401,9],[1418,12],[1538,12]]},"1599":{"position":[[600,9],[638,9]]},"1603":{"position":[[2288,10]]},"1605":{"position":[[244,11],[943,11]]},"1672":{"position":[[902,10]]},"1731":{"position":[[230,12]]},"1983":{"position":[[2145,11]]},"2005":{"position":[[211,10]]},"2021":{"position":[[569,10]]},"2023":{"position":[[210,9],[506,10]]},"2072":{"position":[[58,9]]},"2074":{"position":[[58,9]]},"2150":{"position":[[436,9]]},"2152":{"position":[[942,9],[1046,11]]},"2185":{"position":[[14,10],[77,10],[133,10],[610,10]]},"2187":{"position":[[15,10],[155,10],[500,10]]},"2189":{"position":[[26,10],[1360,9]]},"2191":{"position":[[114,11]]},"2204":{"position":[[40,10]]},"2234":{"position":[[1135,9]]},"2299":{"position":[[77,10]]},"2317":{"position":[[186,9]]},"2352":{"position":[[52,9]]},"2354":{"position":[[652,9],[1290,12],[2613,9],[2633,9]]},"2356":{"position":[[792,9],[812,9]]},"2358":{"position":[[770,9],[790,9]]},"2360":{"position":[[2094,9],[2114,9]]},"2362":{"position":[[2128,9],[2148,9]]},"2364":{"position":[[2193,9],[2213,9]]},"2376":{"position":[[47,10]]},"2378":{"position":[[210,11]]},"2392":{"position":[[1608,10],[1625,10],[1716,10]]},"2400":{"position":[[1608,9]]},"2420":{"position":[[515,9]]},"2424":{"position":[[780,11]]},"2428":{"position":[[1202,9]]},"2440":{"position":[[346,10]]},"2442":{"position":[[9,9],[429,11],[615,9],[810,10]]},"2470":{"position":[[40,9]]},"2472":{"position":[[13,9],[286,11]]},"2494":{"position":[[357,10],[387,11]]},"2496":{"position":[[411,9]]},"2498":{"position":[[18,9]]},"2504":{"position":[[921,9]]},"2526":{"position":[[978,9]]},"2532":{"position":[[55,11],[153,11],[273,10],[475,9]]},"2558":{"position":[[1062,11]]},"2622":{"position":[[4955,9],[5002,9]]},"2632":{"position":[[3608,10]]},"2704":{"position":[[137,9]]},"2706":{"position":[[143,9]]},"2780":{"position":[[401,9],[1382,12],[1502,12]]},"2782":{"position":[[600,9],[638,9]]},"2788":{"position":[[2288,10]]},"2790":{"position":[[244,11],[943,11]]},"2836":{"position":[[902,10]]},"2920":{"position":[[230,12]]},"3142":{"position":[[2145,11]]},"3194":{"position":[[211,10]]},"3210":{"position":[[542,10]]},"3212":{"position":[[183,9],[479,10]]},"3220":{"position":[[673,10]]},"3309":{"position":[[58,9]]},"3311":{"position":[[58,9]]},"3324":{"position":[[3449,9],[3519,11],[3581,10],[3695,10],[3820,11],[3910,11],[3985,10],[4110,11],[4273,10],[4404,11],[4574,10],[4705,11]]},"3379":{"position":[[436,9]]},"3381":{"position":[[942,9],[1046,11]]},"3413":{"position":[[52,9]]},"3415":{"position":[[652,9],[1290,12],[2613,9],[2633,9]]},"3417":{"position":[[792,9],[812,9]]},"3419":{"position":[[308,9],[817,9],[837,9]]},"3421":{"position":[[1897,9],[1917,9]]},"3423":{"position":[[2128,9],[2148,9]]},"3425":{"position":[[2166,9],[2186,9]]},"3437":{"position":[[47,10]]},"3439":{"position":[[210,11]]},"3456":{"position":[[471,9]]},"3462":{"position":[[14,10],[77,10],[168,10],[734,10]]},"3464":{"position":[[15,10],[155,10],[500,10],[937,11]]},"3466":{"position":[[114,11]]},"3479":{"position":[[40,10]]},"3486":{"position":[[451,9]]},"3492":{"position":[[26,10],[1360,9]]},"3512":{"position":[[77,10]]},"3530":{"position":[[186,9]]},"3538":{"position":[[48,9],[146,9],[490,9],[1187,9],[1508,9],[1580,11],[1648,11],[1787,9],[1893,9],[1931,9]]},"3584":{"position":[[1135,9]]},"3612":{"position":[[1635,10],[1652,10],[1743,10]]},"3633":{"position":[[4955,9],[5002,9]]},"3643":{"position":[[3608,10]]}}}],["transport\":\"websocket",{"_index":4190,"t":{"1017":{"position":[[1906,24]]},"1019":{"position":[[507,24]]},"1021":{"position":[[419,24]]},"1023":{"position":[[1778,24]]},"1025":{"position":[[1793,24]]},"2354":{"position":[[2023,24]]},"2356":{"position":[[507,24]]},"2358":{"position":[[419,24]]},"2360":{"position":[[1778,24]]},"2362":{"position":[[1793,24]]},"2364":{"position":[[1882,24]]},"3415":{"position":[[2023,24]]},"3417":{"position":[[507,24]]},"3419":{"position":[[466,24]]},"3421":{"position":[[1581,24]]},"3423":{"position":[[1793,24]]},"3425":{"position":[[1855,24]]}}}],["transport\"http/1.1",{"_index":5744,"t":{"3538":{"position":[[1350,18]]}}}],["transport/endpoint",{"_index":1846,"t":{"124":{"position":[[4237,18]]}}}],["transport/featur",{"_index":3027,"t":{"399":{"position":[[523,18]]},"1223":{"position":[[523,18]]},"2472":{"position":[[523,18]]}}}],["transport/proxi",{"_index":4183,"t":{"1015":{"position":[[423,15]]},"2352":{"position":[[423,15]]},"3413":{"position":[[423,15]]}}}],["transport://localhost:4433/path",{"_index":1521,"t":{"94":{"position":[[1788,34]]}}}],["travel",{"_index":490,"t":{"12":{"position":[[364,9]]},"16":{"position":[[5991,6]]},"74":{"position":[[1924,7]]},"84":{"position":[[699,9]]},"345":{"position":[[324,6]]},"411":{"position":[[213,7]]},"441":{"position":[[1095,9]]},"1160":{"position":[[217,10]]},"1235":{"position":[[213,7]]},"1267":{"position":[[1095,9]]},"1277":{"position":[[324,6]]},"1481":{"position":[[166,9]]},"2313":{"position":[[166,9]]},"2340":{"position":[[217,10]]},"2484":{"position":[[213,7]]},"2516":{"position":[[1095,9]]},"3625":{"position":[[217,10]]}}}],["treat",{"_index":2803,"t":{"289":{"position":[[718,7]]},"910":{"position":[[139,7]]},"2031":{"position":[[139,7]]},"3222":{"position":[[139,7]]}}}],["tree",{"_index":2121,"t":{"188":{"position":[[276,4]]},"190":{"position":[[229,4],[572,4]]},"192":{"position":[[385,4],[1708,4]]}}}],["trello",{"_index":674,"t":{"16":{"position":[[4315,6]]}}}],["trend",{"_index":3618,"t":{"553":{"position":[[282,9]]},"1527":{"position":[[279,9]]},"2760":{"position":[[279,9]]}}}],["tri",{"_index":505,"t":{"12":{"position":[[1019,3],[1453,3]]},"16":{"position":[[2134,3],[2520,3],[2952,3],[6975,3],[7095,3]]},"18":{"position":[[1006,3],[2269,3]]},"20":{"position":[[389,5],[605,5]]},"56":{"position":[[18,3]]},"78":{"position":[[2721,5]]},"94":{"position":[[1615,3]]},"100":{"position":[[225,5]]},"108":{"position":[[185,3]]},"122":{"position":[[126,3],[1348,3]]},"158":{"position":[[1466,3]]},"178":{"position":[[928,3]]},"194":{"position":[[2875,6]]},"196":{"position":[[1973,5]]},"200":{"position":[[17,3],[165,5],[2290,3]]},"216":{"position":[[4194,3]]},"226":{"position":[[81,5]]},"250":{"position":[[1878,3]]},"252":{"position":[[552,3]]},"258":{"position":[[1814,3],[1896,6]]},"335":{"position":[[643,6],[735,4]]},"349":{"position":[[501,5]]},"413":{"position":[[735,3]]},"437":{"position":[[2163,3]]},"477":{"position":[[734,3]]},"479":{"position":[[2982,3]]},"483":{"position":[[673,5]]},"555":{"position":[[8,3],[376,6]]},"632":{"position":[[692,3]]},"640":{"position":[[155,3],[455,3],[780,3]]},"652":{"position":[[555,3]]},"672":{"position":[[1839,5]]},"716":{"position":[[132,6]]},"816":{"position":[[638,6]]},"840":{"position":[[79,5]]},"842":{"position":[[64,3]]},"932":{"position":[[202,5]]},"973":{"position":[[3114,3]]},"991":{"position":[[30,5]]},"997":{"position":[[307,3]]},"1023":{"position":[[895,5]]},"1071":{"position":[[278,3],[493,3]]},"1094":{"position":[[925,3]]},"1147":{"position":[[1335,3]]},"1203":{"position":[[549,5]]},"1217":{"position":[[140,6]]},"1237":{"position":[[735,3]]},"1263":{"position":[[2167,3]]},"1281":{"position":[[514,5]]},"1371":{"position":[[314,5]]},"1419":{"position":[[323,3]]},"1421":{"position":[[557,3]]},"1425":{"position":[[2527,3]]},"1497":{"position":[[819,5]]},"1519":{"position":[[74,3]]},"1529":{"position":[[8,3],[376,6]]},"1575":{"position":[[932,3]]},"1589":{"position":[[649,3]]},"1591":{"position":[[155,3],[737,3],[1062,3]]},"1603":{"position":[[2374,5]]},"1631":{"position":[[692,3]]},"1647":{"position":[[555,3]]},"1666":{"position":[[1446,3]]},"1670":{"position":[[440,5]]},"1676":{"position":[[3,5]]},"1745":{"position":[[30,5]]},"1751":{"position":[[307,3]]},"1835":{"position":[[623,6]]},"1857":{"position":[[175,3]]},"1859":{"position":[[172,3]]},"1951":{"position":[[129,6]]},"1983":{"position":[[2692,5]]},"2053":{"position":[[202,5]]},"2086":{"position":[[787,5],[842,6]]},"2106":{"position":[[549,5]]},"2134":{"position":[[278,3],[493,3]]},"2156":{"position":[[248,3]]},"2189":{"position":[[1331,3]]},"2260":{"position":[[1414,3]]},"2360":{"position":[[895,5],[4223,3],[4339,3]]},"2486":{"position":[[735,3]]},"2512":{"position":[[2409,3]]},"2542":{"position":[[314,5]]},"2622":{"position":[[917,5]]},"2632":{"position":[[2206,3]]},"2636":{"position":[[1746,3]]},"2670":{"position":[[932,3]]},"2684":{"position":[[649,3]]},"2692":{"position":[[819,5]]},"2702":{"position":[[74,3]]},"2764":{"position":[[8,3],[375,6]]},"2784":{"position":[[155,3],[737,3],[1062,3]]},"2788":{"position":[[2374,5]]},"2818":{"position":[[692,3]]},"2830":{"position":[[1449,3]]},"2834":{"position":[[440,5]]},"2840":{"position":[[3,5]]},"2899":{"position":[[555,3]]},"2950":{"position":[[30,5]]},"2956":{"position":[[307,3]]},"2990":{"position":[[425,5]]},"2996":{"position":[[129,6]]},"3064":{"position":[[659,3]]},"3088":{"position":[[175,3]]},"3090":{"position":[[172,3]]},"3142":{"position":[[2692,5]]},"3220":{"position":[[552,3]]},"3246":{"position":[[202,5]]},"3271":{"position":[[753,5],[808,6]]},"3289":{"position":[[549,5]]},"3337":{"position":[[278,3],[493,3]]},"3385":{"position":[[248,3]]},"3421":{"position":[[4226,3],[4342,3]]},"3492":{"position":[[1331,3]]},"3610":{"position":[[1414,3]]},"3633":{"position":[[917,5]]},"3643":{"position":[[2206,3]]},"3647":{"position":[[1746,3]]}}}],["trial",{"_index":1519,"t":{"94":{"position":[[1594,5]]}}}],["trick",{"_index":1490,"t":{"88":{"position":[[168,6]]},"126":{"position":[[843,7]]}}}],["tricki",{"_index":2655,"t":{"258":{"position":[[1671,6]]},"323":{"position":[[168,6]]},"473":{"position":[[1739,6]]}}}],["trickier",{"_index":1211,"t":{"60":{"position":[[1502,8]]}}}],["trigger",{"_index":3990,"t":{"834":{"position":[[217,9]]},"1573":{"position":[[373,9]]},"1849":{"position":[[220,9]]},"2668":{"position":[[373,9]]},"3078":{"position":[[220,9]]}}}],["trip",{"_index":2520,"t":{"236":{"position":[[281,4]]},"280":{"position":[[2069,6]]},"471":{"position":[[323,5]]}}}],["trivial",{"_index":1800,"t":{"122":{"position":[[598,7]]},"258":{"position":[[251,7],[809,7]]}}}],["troubleshoot",{"_index":4059,"t":{"922":{"position":[[327,16]]},"2043":{"position":[[327,16]]},"2889":{"position":[[366,16]]},"3234":{"position":[[327,16]]}}}],["true",{"_index":322,"t":{"8":{"position":[[517,5]]},"34":{"position":[[895,6]]},"74":{"position":[[2330,5]]},"76":{"position":[[847,5],[864,5]]},"110":{"position":[[3279,4],[3598,4],[4504,4]]},"130":{"position":[[362,4],[1246,4],[1283,5]]},"142":{"position":[[83,4]]},"144":{"position":[[713,4]]},"196":{"position":[[644,5],[667,5],[692,4]]},"246":{"position":[[506,5],[620,4]]},"248":{"position":[[101,5],[275,5]]},"287":{"position":[[808,4]]},"289":{"position":[[192,5],[229,4]]},"293":{"position":[[1267,5],[1292,4]]},"299":{"position":[[310,5],[413,4]]},"323":{"position":[[430,4]]},"327":{"position":[[577,4],[990,5]]},"441":{"position":[[802,4]]},"455":{"position":[[589,5]]},"469":{"position":[[620,5]]},"471":{"position":[[2684,5]]},"513":{"position":[[169,5]]},"535":{"position":[[285,5],[379,4],[755,5],[929,4],[1027,5],[1175,4]]},"563":{"position":[[536,5],[608,5],[1430,5],[1473,5]]},"608":{"position":[[819,4]]},"614":{"position":[[228,4]]},"616":{"position":[[143,4]]},"640":{"position":[[1025,5]]},"666":{"position":[[122,5]]},"668":{"position":[[477,5],[501,5]]},"672":{"position":[[144,5],[371,5],[398,5]]},"680":{"position":[[279,5],[416,5],[443,5]]},"694":{"position":[[93,5],[624,5],[655,5]]},"730":{"position":[[43,5]]},"736":{"position":[[56,5]]},"738":{"position":[[42,5]]},"740":{"position":[[55,5]]},"744":{"position":[[39,5]]},"746":{"position":[[46,5]]},"748":{"position":[[53,5]]},"750":{"position":[[50,5]]},"816":{"position":[[751,5]]},"856":{"position":[[158,5],[175,5],[205,5],[223,5],[243,5],[303,5]]},"858":{"position":[[1224,5],[1241,5],[1259,5],[1279,5],[1374,5],[1393,5],[1453,4],[1494,5],[1514,4],[2168,4]]},"878":{"position":[[408,4],[430,6]]},"922":{"position":[[155,5]]},"928":{"position":[[70,5],[134,5]]},"945":{"position":[[739,5]]},"961":{"position":[[106,5]]},"963":{"position":[[80,5]]},"971":{"position":[[543,5],[756,5],[1136,5],[1457,6]]},"973":{"position":[[1258,4],[2995,4]]},"1019":{"position":[[1194,5]]},"1021":{"position":[[1394,5]]},"1023":{"position":[[1456,5],[1660,4],[2552,5]]},"1025":{"position":[[1302,5],[1325,5],[1515,5],[1538,4],[2691,5],[3055,4]]},"1031":{"position":[[1230,5],[1253,4],[1444,5]]},"1039":{"position":[[860,5]]},"1043":{"position":[[281,5]]},"1045":{"position":[[281,5]]},"1047":{"position":[[200,5],[508,5]]},"1049":{"position":[[329,5],[435,5],[543,5]]},"1051":{"position":[[116,5]]},"1055":{"position":[[165,5]]},"1057":{"position":[[109,5],[268,5]]},"1071":{"position":[[711,5],[752,5],[855,4]]},"1105":{"position":[[105,5]]},"1116":{"position":[[93,5]]},"1143":{"position":[[103,5]]},"1147":{"position":[[389,5]]},"1160":{"position":[[647,4]]},"1173":{"position":[[110,5]]},"1177":{"position":[[286,5],[322,5]]},"1247":{"position":[[101,4]]},"1267":{"position":[[802,4]]},"1307":{"position":[[589,5]]},"1359":{"position":[[1907,5]]},"1367":{"position":[[237,4]]},"1405":{"position":[[1250,4]]},"1409":{"position":[[1070,4]]},"1413":{"position":[[730,5]]},"1415":{"position":[[255,4]]},"1421":{"position":[[306,4]]},"1425":{"position":[[726,4],[940,5],[1128,4]]},"1429":{"position":[[379,4]]},"1441":{"position":[[169,5]]},"1485":{"position":[[636,5]]},"1495":{"position":[[952,4]]},"1505":{"position":[[1269,4],[1401,5],[7904,5],[8151,5]]},"1509":{"position":[[75,5]]},"1521":{"position":[[105,4]]},"1571":{"position":[[1160,4]]},"1591":{"position":[[1307,5]]},"1603":{"position":[[144,5],[371,5],[401,5],[428,5],[457,5],[487,5]]},"1617":{"position":[[350,5],[487,5],[517,5],[546,5],[573,5]]},"1660":{"position":[[122,5]]},"1662":{"position":[[477,5],[501,5]]},"1666":{"position":[[938,5]]},"1668":{"position":[[834,5]]},"1670":{"position":[[1280,5],[2121,5],[2152,5]]},"1672":{"position":[[1459,5]]},"1709":{"position":[[1387,4],[1451,4],[1515,4]]},"1737":{"position":[[83,5],[606,5],[637,5]]},"1779":{"position":[[98,4]]},"1781":{"position":[[102,4]]},"1783":{"position":[[135,4]]},"1835":{"position":[[736,5]]},"1843":{"position":[[1254,5],[2055,4]]},"1885":{"position":[[682,5]]},"1899":{"position":[[391,5],[458,5],[494,5],[533,5],[571,5],[608,5],[646,5],[683,5],[722,5],[760,5],[1178,5],[1245,5],[1281,5],[1320,5],[1358,5],[1395,5],[1433,5],[1470,5],[1509,5],[1547,4]]},"1935":{"position":[[52,4]]},"1957":{"position":[[48,4]]},"1971":{"position":[[106,5]]},"1973":{"position":[[80,5]]},"1981":{"position":[[543,5],[756,5],[1136,5],[1457,6]]},"1983":{"position":[[1258,4],[2945,4]]},"1991":{"position":[[408,4],[430,6]]},"2043":{"position":[[155,5]]},"2049":{"position":[[70,5],[134,5]]},"2066":{"position":[[739,5]]},"2086":{"position":[[677,4]]},"2118":{"position":[[66,4],[180,4]]},"2134":{"position":[[723,5],[764,5],[867,4]]},"2138":{"position":[[165,5]]},"2140":{"position":[[109,5],[268,5]]},"2150":{"position":[[1250,5]]},"2154":{"position":[[940,4]]},"2179":{"position":[[194,5]]},"2211":{"position":[[189,5]]},"2224":{"position":[[110,5]]},"2228":{"position":[[286,5],[322,5]]},"2239":{"position":[[105,5]]},"2256":{"position":[[103,5]]},"2260":{"position":[[389,5]]},"2267":{"position":[[93,5]]},"2317":{"position":[[636,5]]},"2340":{"position":[[647,4]]},"2356":{"position":[[1194,5]]},"2358":{"position":[[1394,5]]},"2360":{"position":[[1456,5],[1660,4],[2552,5]]},"2362":{"position":[[1302,5],[1325,5],[1515,5],[1538,4],[2691,5],[3055,4]]},"2364":{"position":[[1550,5],[1723,4],[2663,5]]},"2370":{"position":[[1230,5],[1253,4],[1444,5]]},"2378":{"position":[[860,5]]},"2382":{"position":[[281,5]]},"2384":{"position":[[281,5]]},"2386":{"position":[[200,5],[508,5]]},"2388":{"position":[[366,5],[472,5],[580,5]]},"2390":{"position":[[116,5]]},"2392":{"position":[[489,5],[502,5],[577,5]]},"2496":{"position":[[101,4]]},"2516":{"position":[[802,4]]},"2530":{"position":[[1907,5]]},"2538":{"position":[[237,4]]},"2610":{"position":[[169,5]]},"2666":{"position":[[1160,4]]},"2686":{"position":[[1284,4],[1416,5],[8001,5],[8248,5]]},"2690":{"position":[[952,4]]},"2704":{"position":[[105,4]]},"2708":{"position":[[1201,5]]},"2712":{"position":[[498,5]]},"2714":{"position":[[93,5]]},"2736":{"position":[[609,5],[668,5]]},"2768":{"position":[[75,5]]},"2784":{"position":[[1307,5]]},"2788":{"position":[[144,5],[371,5],[401,5],[428,5],[457,5],[487,5]]},"2802":{"position":[[350,5],[487,5],[517,5],[546,5],[573,5]]},"2830":{"position":[[941,5]]},"2832":{"position":[[840,5]]},"2834":{"position":[[1286,5],[2127,5],[2158,5]]},"2836":{"position":[[1459,5]]},"2862":{"position":[[535,4]]},"2873":{"position":[[1600,4],[1667,4]]},"2912":{"position":[[122,5]]},"2914":{"position":[[541,5],[565,5]]},"2926":{"position":[[83,5],[606,5],[637,5]]},"2938":{"position":[[98,4]]},"2940":{"position":[[102,4]]},"2942":{"position":[[135,4]]},"2974":{"position":[[58,4],[471,5]]},"2980":{"position":[[52,4]]},"3002":{"position":[[48,4]]},"3072":{"position":[[1542,5],[2343,4]]},"3116":{"position":[[682,5]]},"3134":{"position":[[391,5],[458,5],[494,5],[533,5],[571,5],[608,5],[646,5],[683,5],[722,5],[760,5],[1178,5],[1245,5],[1281,5],[1320,5],[1358,5],[1395,5],[1433,5],[1470,5],[1509,5],[1547,4]]},"3140":{"position":[[543,5],[756,5],[1136,5],[1457,6]]},"3142":{"position":[[1258,4],[2945,4]]},"3180":{"position":[[408,4],[430,6]]},"3234":{"position":[[155,5]]},"3242":{"position":[[70,5],[134,5]]},"3261":{"position":[[739,5]]},"3271":{"position":[[643,4]]},"3299":{"position":[[106,5]]},"3301":{"position":[[80,5]]},"3318":{"position":[[106,5]]},"3320":{"position":[[80,5]]},"3327":{"position":[[140,5],[167,5]]},"3337":{"position":[[723,5],[764,5],[867,4]]},"3341":{"position":[[66,4],[194,4]]},"3345":{"position":[[309,5],[329,5],[360,4]]},"3353":{"position":[[165,5]]},"3355":{"position":[[109,5],[268,5]]},"3367":{"position":[[1141,4]]},"3369":{"position":[[935,5],[981,4]]},"3371":{"position":[[248,5]]},"3379":{"position":[[1250,5]]},"3383":{"position":[[940,4]]},"3417":{"position":[[1194,5]]},"3419":{"position":[[1441,5]]},"3421":{"position":[[1259,5],[1463,4],[2355,5]]},"3423":{"position":[[1302,5],[1325,5],[1515,5],[1538,4],[2691,5],[3055,4]]},"3425":{"position":[[1523,5],[1696,4],[2636,5]]},"3431":{"position":[[1230,5],[1253,4],[1444,5]]},"3433":{"position":[[504,4]]},"3439":{"position":[[860,5]]},"3443":{"position":[[281,5]]},"3445":{"position":[[281,5],[2302,4]]},"3447":{"position":[[200,5],[508,5]]},"3449":{"position":[[366,5],[472,5],[580,5]]},"3451":{"position":[[116,5]]},"3456":{"position":[[194,5]]},"3486":{"position":[[189,5]]},"3530":{"position":[[636,5]]},"3549":{"position":[[93,5]]},"3574":{"position":[[110,5]]},"3578":{"position":[[286,5],[322,5]]},"3589":{"position":[[105,5]]},"3606":{"position":[[103,5]]},"3610":{"position":[[389,5]]},"3612":{"position":[[516,5],[529,5],[604,5]]},"3625":{"position":[[647,4]]}}}],["true/fals",{"_index":3944,"t":{"792":{"position":[[1837,11]]},"1023":{"position":[[3766,11]]},"1461":{"position":[[1301,11]]},"1763":{"position":[[578,11]]},"1807":{"position":[[1364,11]]},"2293":{"position":[[1365,11]]},"2360":{"position":[[3797,11]]},"2968":{"position":[[578,11]]},"3036":{"position":[[1442,11]]},"3421":{"position":[[3800,11]]},"3506":{"position":[[1433,11]]}}}],["true});console.log(resp.publ",{"_index":3085,"t":{"461":{"position":[[779,38]]},"1313":{"position":[[779,38]]},"2600":{"position":[[779,38]]},"2656":{"position":[[779,38]]},"3667":{"position":[[779,38]]}}}],["trust",{"_index":538,"t":{"14":{"position":[[308,7]]},"94":{"position":[[3201,5]]},"100":{"position":[[97,5],[185,5],[587,5]]},"429":{"position":[[1568,5]]},"483":{"position":[[1195,5]]},"1255":{"position":[[1568,5]]},"2504":{"position":[[1568,5]]}}}],["truth",{"_index":1805,"t":{"122":{"position":[[1318,5]]},"969":{"position":[[449,5],[1330,5]]},"1405":{"position":[[79,6]]},"1979":{"position":[[491,5]]},"2148":{"position":[[332,6]]},"3138":{"position":[[491,5]]},"3377":{"position":[[332,6]]}}}],["ttl",{"_index":2867,"t":{"311":{"position":[[215,3]]},"317":{"position":[[231,3],[291,3],[629,3],[700,5],[1284,3]]},"383":{"position":[[143,6]]},"798":{"position":[[868,3],[901,3],[1067,3]]},"838":{"position":[[306,3]]},"868":{"position":[[200,3]]},"1147":{"position":[[554,3]]},"1177":{"position":[[507,3]]},"1341":{"position":[[143,6]]},"1407":{"position":[[1389,3]]},"1409":{"position":[[537,3]]},"1413":{"position":[[632,4],[786,3],[907,3],[983,3]]},"1505":{"position":[[6310,3]]},"1597":{"position":[[231,3]]},"1719":{"position":[[1318,3]]},"1767":{"position":[[266,3]]},"1813":{"position":[[868,3],[901,3],[1067,3]]},"1855":{"position":[[306,3]]},"1909":{"position":[[200,3]]},"1911":{"position":[[250,3]]},"2082":{"position":[[759,3]]},"2086":{"position":[[1749,3]]},"2228":{"position":[[507,3]]},"2260":{"position":[[563,3]]},"2456":{"position":[[144,6]]},"2686":{"position":[[6379,3]]},"2780":{"position":[[231,3]]},"2883":{"position":[[1477,3]]},"2972":{"position":[[266,3]]},"3042":{"position":[[855,3],[1015,3]]},"3084":{"position":[[306,3]]},"3086":{"position":[[761,3]]},"3166":{"position":[[200,3]]},"3168":{"position":[[250,3]]},"3267":{"position":[[759,3]]},"3271":{"position":[[1715,3]]},"3578":{"position":[[507,3]]},"3610":{"position":[[563,3]]}}}],["tunabl",{"_index":3448,"t":{"479":{"position":[[5547,7]]}}}],["tune",{"_index":403,"t":{"10":{"position":[[330,4],[2052,4]]},"16":{"position":[[41,4]]},"130":{"position":[[1015,4]]},"283":{"position":[[1025,6]]},"317":{"position":[[209,4]]},"499":{"position":[[212,6]]},"511":{"position":[[502,6]]},"682":{"position":[[612,4]]},"951":{"position":[[756,6]]},"953":{"position":[[762,6]]},"1293":{"position":[[1131,6]]},"1393":{"position":[[212,6]]},"1439":{"position":[[504,6]]},"1527":{"position":[[1255,6]]},"1921":{"position":[[756,6]]},"1923":{"position":[[762,6]]},"2029":{"position":[[42,6]]},"2428":{"position":[[1192,6]]},"2564":{"position":[[220,6]]},"2608":{"position":[[508,6]]},"2760":{"position":[[1353,6]]},"2806":{"position":[[565,6]]},"2889":{"position":[[1125,6]]},"3148":{"position":[[756,6]]},"3150":{"position":[[762,6]]},"3218":{"position":[[42,6]]}}}],["tupl",{"_index":448,"t":{"10":{"position":[[1435,6]]},"951":{"position":[[400,6]]},"1921":{"position":[[400,6]]},"3148":{"position":[[400,6]]}}}],["ture",{"_index":1910,"t":{"138":{"position":[[454,6]]}}}],["turn",{"_index":755,"t":{"18":{"position":[[619,4],[709,6]]},"42":{"position":[[520,4]]},"76":{"position":[[263,4]]},"122":{"position":[[1618,6]]},"130":{"position":[[1376,6]]},"252":{"position":[[161,5]]},"287":{"position":[[1237,4]]},"331":{"position":[[81,5]]},"381":{"position":[[17,4]]},"383":{"position":[[29,7]]},"441":{"position":[[848,4]]},"475":{"position":[[2480,6]]},"479":{"position":[[241,6]]},"481":{"position":[[1332,6]]},"790":{"position":[[561,4]]},"792":{"position":[[880,4]]},"828":{"position":[[411,4]]},"834":{"position":[[392,6]]},"838":{"position":[[385,4]]},"842":{"position":[[570,6],[636,6]]},"848":{"position":[[43,5]]},"850":{"position":[[41,5]]},"852":{"position":[[44,5]]},"854":{"position":[[42,5]]},"1025":{"position":[[1031,4]]},"1039":{"position":[[768,4]]},"1071":{"position":[[237,5],[630,6]]},"1177":{"position":[[157,4]]},"1189":{"position":[[626,5]]},"1267":{"position":[[848,4]]},"1341":{"position":[[29,7]]},"1359":{"position":[[811,4],[1823,4]]},"1413":{"position":[[17,4]]},"1509":{"position":[[96,4]]},"1763":{"position":[[618,4]]},"1771":{"position":[[1880,7]]},"1779":{"position":[[19,4]]},"1781":{"position":[[19,4]]},"1831":{"position":[[980,4]]},"1849":{"position":[[775,6]]},"1855":{"position":[[385,4]]},"1859":{"position":[[923,6],[989,6]]},"1861":{"position":[[196,4],[255,7]]},"1863":{"position":[[57,4]]},"1869":{"position":[[55,4]]},"1875":{"position":[[55,4]]},"1881":{"position":[[56,4]]},"1887":{"position":[[43,5]]},"1889":{"position":[[41,5]]},"1891":{"position":[[45,5]]},"1893":{"position":[[44,5]]},"1895":{"position":[[42,5]]},"1897":{"position":[[46,5]]},"1899":{"position":[[79,7]]},"2086":{"position":[[442,5]]},"2134":{"position":[[237,5],[630,6]]},"2228":{"position":[[157,4]]},"2362":{"position":[[1031,4]]},"2378":{"position":[[768,4]]},"2456":{"position":[[30,7]]},"2516":{"position":[[848,4]]},"2530":{"position":[[811,4],[1823,4]]},"2768":{"position":[[96,4]]},"2930":{"position":[[1880,7]]},"2938":{"position":[[19,4]]},"2940":{"position":[[19,4]]},"2968":{"position":[[618,4]]},"3078":{"position":[[775,6]]},"3084":{"position":[[385,4]]},"3090":{"position":[[923,6],[989,6]]},"3092":{"position":[[196,4],[255,7]]},"3094":{"position":[[57,4]]},"3100":{"position":[[55,4]]},"3106":{"position":[[55,4]]},"3112":{"position":[[56,4]]},"3118":{"position":[[43,5]]},"3120":{"position":[[41,5]]},"3122":{"position":[[45,5]]},"3124":{"position":[[50,5]]},"3126":{"position":[[44,5]]},"3128":{"position":[[42,5]]},"3130":{"position":[[46,5]]},"3132":{"position":[[51,5]]},"3134":{"position":[[79,7]]},"3271":{"position":[[442,5]]},"3327":{"position":[[206,6],[1001,4]]},"3337":{"position":[[237,5],[630,6]]},"3345":{"position":[[222,6]]},"3423":{"position":[[1031,4]]},"3439":{"position":[[768,4]]},"3538":{"position":[[1536,6]]},"3578":{"position":[[157,4]]},"3629":{"position":[[78,5]]}}}],["tutori",{"_index":1493,"t":{"90":{"position":[[275,10]]},"154":{"position":[[8,9],[331,8],[437,8]]},"180":{"position":[[278,9]]},"182":{"position":[[8,9],[452,8],[558,8],[743,8],[783,8]]},"186":{"position":[[110,8],[152,9],[240,9],[533,8]]},"190":{"position":[[358,9]]},"198":{"position":[[417,8],[444,8]]},"200":{"position":[[972,8],[1177,9],[2019,8],[2141,8]]},"202":{"position":[[83,8]]},"206":{"position":[[758,9]]},"220":{"position":[[218,9]]},"242":{"position":[[88,8],[482,8],[588,8],[1560,8]]},"244":{"position":[[568,8]]},"250":{"position":[[642,9]]},"264":{"position":[[886,10]]},"285":{"position":[[440,8]]},"644":{"position":[[105,10]]},"1017":{"position":[[5564,8],[5772,8]]},"1505":{"position":[[11,8],[31,8],[3368,9],[6617,9]]},"1825":{"position":[[537,8]]},"2354":{"position":[[5687,8],[5895,8]]},"2392":{"position":[[1324,8]]},"2686":{"position":[[11,8],[31,9],[3385,9],[6694,9]]},"3054":{"position":[[537,8]]},"3367":{"position":[[1844,9]]},"3415":{"position":[[5716,8],[5924,8]]},"3612":{"position":[[1351,8]]}}}],["tweak",{"_index":2103,"t":{"180":{"position":[[248,7]]},"900":{"position":[[20,8]]},"2013":{"position":[[20,8]]},"2191":{"position":[[862,5]]},"2624":{"position":[[248,6]]},"2640":{"position":[[286,5]]},"3086":{"position":[[934,8]]},"3202":{"position":[[20,8]]},"3466":{"position":[[862,5]]},"3635":{"position":[[248,6]]},"3651":{"position":[[286,5]]}}}],["twemproxi",{"_index":3394,"t":{"479":{"position":[[883,9]]}}}],["twice",{"_index":1481,"t":{"86":{"position":[[58,5]]},"353":{"position":[[499,5]]},"1285":{"position":[[499,5]]},"1423":{"position":[[1622,5],[1773,5]]},"1433":{"position":[[86,6]]},"2160":{"position":[[1622,5],[1773,5]]},"2162":{"position":[[86,6]]},"2408":{"position":[[510,5]]},"3389":{"position":[[1622,5],[1773,5]]},"3391":{"position":[[86,6]]}}}],["twitter",{"_index":4450,"t":{"1217":{"position":[[389,7]]},"2396":{"position":[[389,7]]},"3365":{"position":[[1080,7]]}}}],["two",{"_index":1164,"t":{"56":{"position":[[265,4]]},"64":{"position":[[133,3]]},"76":{"position":[[1826,3]]},"78":{"position":[[563,3]]},"110":{"position":[[492,3]]},"124":{"position":[[1964,3]]},"126":{"position":[[428,3]]},"182":{"position":[[127,3]]},"272":{"position":[[1048,3]]},"331":{"position":[[1287,3]]},"467":{"position":[[1485,3]]},"475":{"position":[[5749,3]]},"630":{"position":[[296,3]]},"650":{"position":[[210,3]]},"802":{"position":[[337,3]]},"912":{"position":[[251,3]]},"973":{"position":[[1845,3]]},"1045":{"position":[[180,3]]},"1057":{"position":[[1271,3]]},"1191":{"position":[[1661,3]]},"1405":{"position":[[2963,3]]},"1429":{"position":[[149,3]]},"1455":{"position":[[967,3]]},"1571":{"position":[[472,3]]},"1629":{"position":[[296,3]]},"1645":{"position":[[210,3]]},"1666":{"position":[[451,3]]},"1670":{"position":[[211,3],[275,3]]},"1682":{"position":[[1121,3]]},"1817":{"position":[[337,3]]},"1827":{"position":[[344,3]]},"1841":{"position":[[316,3]]},"1855":{"position":[[678,3]]},"1983":{"position":[[2017,3],[2310,3]]},"2027":{"position":[[251,3]]},"2090":{"position":[[1661,3]]},"2140":{"position":[[1271,3]]},"2168":{"position":[[37,3]]},"2287":{"position":[[970,3]]},"2384":{"position":[[180,3]]},"2400":{"position":[[606,3]]},"2632":{"position":[[444,3]]},"2638":{"position":[[551,3]]},"2666":{"position":[[472,3]]},"2712":{"position":[[928,3]]},"2736":{"position":[[474,3]]},"2816":{"position":[[296,3]]},"2830":{"position":[[454,3]]},"2834":{"position":[[211,3],[275,3]]},"2846":{"position":[[1121,3]]},"2897":{"position":[[210,3]]},"3046":{"position":[[481,3]]},"3056":{"position":[[310,3]]},"3084":{"position":[[678,3]]},"3142":{"position":[[2017,3],[2310,3]]},"3216":{"position":[[251,3]]},"3275":{"position":[[1661,3]]},"3341":{"position":[[560,3]]},"3355":{"position":[[1271,3]]},"3397":{"position":[[37,3]]},"3445":{"position":[[180,3]]},"3526":{"position":[[265,3]]},"3629":{"position":[[1288,3]]},"3643":{"position":[[444,3]]},"3649":{"position":[[551,3]]}}}],["tx",{"_index":152,"t":{"2":{"position":[[2350,2]]}}}],["type",{"_index":653,"t":{"16":{"position":[[3310,4]]},"78":{"position":[[688,4],[1667,4],[2023,4]]},"108":{"position":[[1655,4]]},"128":{"position":[[123,5]]},"140":{"position":[[270,6],[363,4]]},"182":{"position":[[159,4]]},"190":{"position":[[114,4]]},"192":{"position":[[63,4],[778,4],[3092,4]]},"194":{"position":[[2567,4],[2734,4]]},"196":{"position":[[1787,5]]},"200":{"position":[[2506,4]]},"252":{"position":[[494,6]]},"262":{"position":[[711,4]]},"321":{"position":[[99,6],[268,5],[1406,5]]},"375":{"position":[[190,5]]},"471":{"position":[[3173,5]]},"473":{"position":[[3346,4]]},"475":{"position":[[1051,5],[1232,4],[1566,5]]},"477":{"position":[[5914,6]]},"483":{"position":[[481,4]]},"535":{"position":[[581,4]]},"549":{"position":[[536,4]]},"624":{"position":[[252,9]]},"630":{"position":[[300,5]]},"636":{"position":[[440,5],[697,4]]},"638":{"position":[[317,5],[617,4]]},"650":{"position":[[214,5]]},"656":{"position":[[32,5],[279,4]]},"658":{"position":[[32,5],[258,4]]},"688":{"position":[[266,5],[505,4]]},"690":{"position":[[173,5],[1094,4],[1220,4],[1353,4]]},"692":{"position":[[131,5],[370,4]]},"792":{"position":[[1103,4],[1600,4]]},"820":{"position":[[1269,4]]},"973":{"position":[[3076,4]]},"1011":{"position":[[275,4]]},"1013":{"position":[[695,4]]},"1017":{"position":[[2383,4],[2596,4],[2686,4],[3452,4],[5094,6]]},"1019":{"position":[[679,4],[893,4],[979,4],[1229,4]]},"1021":{"position":[[657,4],[859,4],[949,4],[1425,4]]},"1023":{"position":[[976,5],[1981,4],[2183,4],[2273,4],[2909,4],[3529,4]]},"1025":{"position":[[2015,4],[2215,4],[2305,4],[2726,4]]},"1031":{"position":[[377,5]]},"1037":{"position":[[216,4],[227,4]]},"1041":{"position":[[146,4],[316,5],[808,5]]},"1045":{"position":[[1090,4]]},"1094":{"position":[[422,4],[562,4],[663,6]]},"1130":{"position":[[246,6]]},"1134":{"position":[[93,4]]},"1136":{"position":[[17,4]]},"1170":{"position":[[39,5]]},"1403":{"position":[[2206,5]]},"1405":{"position":[[3475,4],[3816,5]]},"1409":{"position":[[1455,6]]},"1417":{"position":[[18,5],[1361,5],[1403,4]]},"1453":{"position":[[144,4]]},"1457":{"position":[[540,6],[1052,5],[1242,5],[1644,5],[1909,4],[2326,4]]},"1459":{"position":[[223,4],[676,4]]},"1461":{"position":[[91,4],[1064,4]]},"1463":{"position":[[186,4]]},"1465":{"position":[[149,4],[588,4]]},"1467":{"position":[[140,4]]},"1469":{"position":[[445,5],[792,4],[909,4],[1063,4]]},"1471":{"position":[[373,5],[528,4],[651,4]]},"1473":{"position":[[710,5],[966,4],[1393,4],[1520,4]]},"1475":{"position":[[406,5],[527,4]]},"1477":{"position":[[157,4],[262,4],[421,4]]},"1479":{"position":[[216,5],[550,4]]},"1497":{"position":[[409,4]]},"1505":{"position":[[8241,5],[9823,5]]},"1565":{"position":[[536,4]]},"1577":{"position":[[235,4]]},"1581":{"position":[[45,5]]},"1597":{"position":[[1132,5]]},"1599":{"position":[[116,4],[269,4],[448,4],[828,4],[1212,4],[1333,4],[1503,4]]},"1613":{"position":[[449,6],[1423,6]]},"1623":{"position":[[252,9]]},"1629":{"position":[[300,5]]},"1636":{"position":[[440,5],[697,4]]},"1639":{"position":[[317,5],[617,4]]},"1645":{"position":[[214,5]]},"1652":{"position":[[32,5],[279,4]]},"1654":{"position":[[32,5],[258,4]]},"1668":{"position":[[8,4],[256,4],[308,4]]},"1670":{"position":[[9,4],[679,4]]},"1703":{"position":[[72,4],[859,4]]},"1705":{"position":[[152,4],[625,4],[703,4],[794,4],[889,4]]},"1707":{"position":[[162,4]]},"1709":{"position":[[114,4],[951,4],[1117,4]]},"1711":{"position":[[71,4]]},"1713":{"position":[[63,4],[878,4],[1067,4]]},"1715":{"position":[[200,4]]},"1717":{"position":[[59,4],[448,4],[608,4]]},"1719":{"position":[[277,4],[491,4],[1093,4],[1504,4],[1616,4],[1738,4],[1887,4]]},"1721":{"position":[[831,4]]},"1723":{"position":[[222,5],[690,5]]},"1731":{"position":[[266,5],[505,4]]},"1733":{"position":[[173,5],[1094,4],[1220,4],[1353,4]]},"1735":{"position":[[131,5],[370,4]]},"1763":{"position":[[167,4]]},"1773":{"position":[[476,6]]},"1807":{"position":[[630,4],[1127,4]]},"1837":{"position":[[962,4]]},"1847":{"position":[[597,5]]},"1855":{"position":[[778,5]]},"1967":{"position":[[1649,5]]},"2118":{"position":[[304,5]]},"2150":{"position":[[1457,5]]},"2152":{"position":[[12,4],[337,5]]},"2189":{"position":[[1118,4],[1301,4]]},"2221":{"position":[[39,5]]},"2247":{"position":[[93,4]]},"2249":{"position":[[17,4]]},"2281":{"position":[[230,6]]},"2285":{"position":[[144,4]]},"2289":{"position":[[600,6],[1112,5],[1302,5],[1698,5],[1963,4],[2610,4]]},"2291":{"position":[[223,4],[906,4]]},"2293":{"position":[[91,4],[1029,4]]},"2295":{"position":[[186,4]]},"2297":{"position":[[149,4],[554,4]]},"2299":{"position":[[140,4]]},"2301":{"position":[[488,5],[835,4],[952,4],[1106,4]]},"2303":{"position":[[373,5],[528,4],[651,4]]},"2305":{"position":[[710,5],[966,4],[1393,4],[1520,4]]},"2307":{"position":[[406,5],[527,4]]},"2309":{"position":[[157,4],[309,4],[468,4]]},"2311":{"position":[[216,5],[550,4]]},"2348":{"position":[[275,4]]},"2350":{"position":[[695,4]]},"2354":{"position":[[2500,4],[2713,4],[2803,4],[3569,4],[5217,6]]},"2356":{"position":[[679,4],[893,4],[979,4],[1229,4]]},"2358":{"position":[[657,4],[859,4],[949,4],[1425,4]]},"2360":{"position":[[976,5],[1981,4],[2183,4],[2273,4],[2841,4],[3461,4]]},"2362":{"position":[[2015,4],[2215,4],[2305,4],[2726,4]]},"2364":{"position":[[456,6],[2080,4],[2294,4],[2380,4],[2702,4]]},"2370":{"position":[[377,5]]},"2376":{"position":[[216,4],[227,4]]},"2380":{"position":[[120,4],[290,5],[782,5]]},"2384":{"position":[[1090,4]]},"2628":{"position":[[941,6]]},"2632":{"position":[[925,4]]},"2644":{"position":[[923,6]]},"2648":{"position":[[514,5]]},"2672":{"position":[[235,4]]},"2676":{"position":[[45,5]]},"2686":{"position":[[8343,5],[9979,5]]},"2692":{"position":[[409,4]]},"2708":{"position":[[1519,7]]},"2718":{"position":[[6,4]]},"2720":{"position":[[11,4]]},"2734":{"position":[[536,4]]},"2780":{"position":[[1132,5]]},"2782":{"position":[[116,4],[269,4],[448,4],[828,4],[1212,4],[1333,4],[1503,4]]},"2798":{"position":[[449,6],[1423,6]]},"2806":{"position":[[106,5],[128,4],[357,5],[379,5],[638,5],[660,4]]},"2810":{"position":[[252,9]]},"2816":{"position":[[300,5]]},"2823":{"position":[[440,5],[661,4]]},"2826":{"position":[[317,5],[581,4]]},"2832":{"position":[[8,4],[256,4],[308,4]]},"2834":{"position":[[9,4],[679,4]]},"2867":{"position":[[72,4],[766,4]]},"2869":{"position":[[152,4],[484,4],[562,4],[655,4]]},"2871":{"position":[[162,4]]},"2873":{"position":[[114,4],[640,4],[1035,4],[1299,4]]},"2875":{"position":[[71,4]]},"2877":{"position":[[63,4],[518,4],[1002,4],[1285,4]]},"2879":{"position":[[200,4]]},"2881":{"position":[[59,4],[395,4],[631,4],[763,4]]},"2883":{"position":[[277,4],[752,4],[1290,4],[1783,4],[1895,4],[2017,4],[2166,4]]},"2885":{"position":[[111,4]]},"2887":{"position":[[831,4]]},"2889":{"position":[[121,5],[420,5],[687,5],[934,5]]},"2897":{"position":[[214,5]]},"2904":{"position":[[32,5],[243,4]]},"2906":{"position":[[32,5],[222,4]]},"2920":{"position":[[266,5],[469,4]]},"2922":{"position":[[173,5],[1058,4],[1184,4],[1317,4]]},"2924":{"position":[[131,5],[334,4]]},"2932":{"position":[[476,6]]},"2968":{"position":[[167,4]]},"3012":{"position":[[1649,5]]},"3036":{"position":[[708,4],[1205,4]]},"3054":{"position":[[979,5]]},"3066":{"position":[[955,4]]},"3076":{"position":[[597,5]]},"3084":{"position":[[778,5]]},"3324":{"position":[[101,5],[123,4],[355,5],[377,4],[591,5],[789,5],[987,5],[1175,5],[1385,5],[1587,5],[1793,5],[1990,5],[2218,5],[2442,5],[2684,5],[2902,5],[3122,5],[3427,5],[3673,5],[3963,5],[4251,5],[4552,5],[4853,5],[4897,4],[5096,5],[5128,4],[5344,5],[5378,5],[5580,5],[5602,5],[5811,5],[6029,5]]},"3341":{"position":[[318,5],[671,5]]},"3367":{"position":[[114,4]]},"3371":{"position":[[101,6]]},"3379":{"position":[[1457,5]]},"3381":{"position":[[12,4],[337,5]]},"3409":{"position":[[280,4]]},"3411":{"position":[[695,4]]},"3415":{"position":[[2500,4],[2713,4],[2803,4],[3569,4],[5246,6]]},"3417":{"position":[[679,4],[893,4],[979,4],[1229,4]]},"3419":{"position":[[704,4],[906,4],[996,4],[1472,4]]},"3421":{"position":[[779,5],[1784,4],[1986,4],[2076,4],[2644,4],[3464,4]]},"3423":{"position":[[2015,4],[2215,4],[2305,4],[2726,4]]},"3425":{"position":[[429,6],[2053,4],[2267,4],[2353,4],[2675,4]]},"3427":{"position":[[500,6]]},"3429":{"position":[[1034,6]]},"3431":{"position":[[377,5]]},"3437":{"position":[[216,4],[227,4]]},"3441":{"position":[[120,4],[290,5],[782,5]]},"3445":{"position":[[1090,4]]},"3492":{"position":[[1118,4],[1301,4]]},"3496":{"position":[[181,5]]},"3502":{"position":[[693,6],[1471,4],[2118,4]]},"3504":{"position":[[298,4],[981,4]]},"3506":{"position":[[159,4],[1097,4]]},"3508":{"position":[[92,4]]},"3510":{"position":[[83,4],[488,4]]},"3512":{"position":[[133,4]]},"3514":{"position":[[673,4],[790,4],[944,4]]},"3516":{"position":[[369,4],[492,4]]},"3518":{"position":[[808,4],[1235,4],[1362,4]]},"3520":{"position":[[213,4]]},"3522":{"position":[[217,4],[369,4],[528,4]]},"3524":{"position":[[367,4]]},"3538":{"position":[[1003,5],[1410,5]]},"3571":{"position":[[39,5]]},"3597":{"position":[[93,4]]},"3599":{"position":[[17,4]]},"3643":{"position":[[925,4]]},"3655":{"position":[[985,6]]},"3659":{"position":[[514,5]]}}}],["type\":1",{"_index":4525,"t":{"1417":{"position":[[581,9]]}}}],["type\":2",{"_index":4526,"t":{"1417":{"position":[[1001,9]]}}}],["type\":3",{"_index":4527,"t":{"1417":{"position":[[1274,9]]}}}],["type\":6,\"data\":{\"client\":\"8ceaa299",{"_index":4341,"t":{"1147":{"position":[[1013,36]]}}}],["type=\"button",{"_index":2442,"t":{"216":{"position":[[1884,13],[1971,13]]}}}],["type=\"password",{"_index":2575,"t":{"246":{"position":[[1283,15]]}}}],["type=\"submit",{"_index":2578,"t":{"246":{"position":[[1373,13]]}}}],["type=\"text",{"_index":2165,"t":{"192":{"position":[[710,11]]},"194":{"position":[[738,11]]},"246":{"position":[[1171,11]]}}}],["type=\"text/javascript",{"_index":4745,"t":{"1505":{"position":[[2290,23]]},"2686":{"position":[[2305,23]]}}}],["typescript",{"_index":1919,"t":{"140":{"position":[[160,10]]}}}],["typic",{"_index":1002,"t":{"38":{"position":[[547,7]]},"331":{"position":[[656,7]]},"349":{"position":[[1042,7]]},"475":{"position":[[3395,7]]},"1281":{"position":[[1193,7]]},"1678":{"position":[[156,9]]},"2842":{"position":[[156,9]]},"3040":{"position":[[20,9]]},"3629":{"position":[[657,7]]}}}],["typo",{"_index":2927,"t":{"329":{"position":[[247,4],[361,4],[869,5]]}}}],["u",{"_index":1759,"t":{"110":{"position":[[4246,2]]},"481":{"position":[[485,1]]},"868":{"position":[[65,1],[274,1]]},"1147":{"position":[[491,1]]},"1177":{"position":[[450,1]]},"1505":{"position":[[6188,1],[6206,1]]},"1597":{"position":[[182,1]]},"1767":{"position":[[137,1]]},"1909":{"position":[[65,1],[274,1]]},"1911":{"position":[[71,1],[324,1]]},"2228":{"position":[[450,1]]},"2260":{"position":[[500,1]]},"2686":{"position":[[6257,1],[6275,1]]},"2780":{"position":[[182,1]]},"2972":{"position":[[137,1]]},"3166":{"position":[[65,1],[274,1]]},"3168":{"position":[[71,1],[324,1]]},"3578":{"position":[[450,1]]},"3610":{"position":[[500,1]]}}}],["uber",{"_index":3404,"t":{"479":{"position":[[2285,4]]}}}],["ubuntu",{"_index":3555,"t":{"517":{"position":[[271,6],[298,6],[325,6]]},"1445":{"position":[[294,6],[321,6],[348,6]]},"2614":{"position":[[247,6],[274,6],[306,6]]}}}],["ucubp27tybebk7z0oenwdskwcmre46fuejjnzi",{"_index":5199,"t":{"2260":{"position":[[1076,40]]},"3610":{"position":[[1076,40]]}}}],["udp",{"_index":1512,"t":{"94":{"position":[[934,3],[981,3]]},"108":{"position":[[759,3],[1018,3],[1110,3]]},"116":{"position":[[764,3]]}}}],["ui",{"_index":1044,"t":{"42":{"position":[[989,2]]},"214":{"position":[[1037,2]]},"216":{"position":[[4262,3]]},"252":{"position":[[932,2]]},"276":{"position":[[46,2]]},"278":{"position":[[291,3],[347,2]]},"280":{"position":[[3162,2]]},"283":{"position":[[615,3]]},"323":{"position":[[365,2],[501,2]]},"325":{"position":[[271,3]]},"331":{"position":[[1362,3]]},"385":{"position":[[28,2]]},"533":{"position":[[179,2]]},"559":{"position":[[127,3]]},"664":{"position":[[61,2]]},"920":{"position":[[10,2],[114,3],[141,2]]},"1147":{"position":[[1434,3]]},"1293":{"position":[[672,3]]},"1343":{"position":[[15,2]]},"1527":{"position":[[1363,3]]},"1593":{"position":[[127,3]]},"1658":{"position":[[61,2]]},"1767":{"position":[[429,2]]},"2041":{"position":[[10,2],[114,3],[141,2]]},"2260":{"position":[[1513,3]]},"2428":{"position":[[717,3]]},"2458":{"position":[[19,2]]},"2760":{"position":[[1543,3]]},"2776":{"position":[[127,3]]},"2910":{"position":[[61,2]]},"2972":{"position":[[429,2]]},"3232":{"position":[[10,2],[114,3],[141,2]]},"3238":{"position":[[64,2],[88,2]]},"3240":{"position":[[514,2],[573,2]]},"3327":{"position":[[879,3]]},"3610":{"position":[[1513,3]]},"3629":{"position":[[1363,3]]}}}],["uid",{"_index":3733,"t":{"636":{"position":[[573,7],[723,3]]},"1479":{"position":[[398,6]]},"1599":{"position":[[1359,3],[1529,3]]},"1613":{"position":[[416,5],[1390,5]]},"1636":{"position":[[573,7],[723,3]]},"1719":{"position":[[1119,3],[1904,3],[1939,3]]},"1721":{"position":[[433,3],[857,3],[872,3]]},"2311":{"position":[[398,6]]},"2782":{"position":[[1359,3],[1529,3]]},"2798":{"position":[[416,5],[1390,5]]},"2823":{"position":[[524,8],[687,3]]},"2883":{"position":[[422,3],[2183,3],[2218,3]]},"2885":{"position":[[137,3],[152,3]]},"2887":{"position":[[433,3],[857,3],[872,3]]},"3524":{"position":[[214,6]]}}}],["uint32",{"_index":3795,"t":{"676":{"position":[[429,7],[450,7],[1418,7],[1439,7]]},"1027":{"position":[[291,6]]},"1029":{"position":[[296,6]]},"1405":{"position":[[2422,6]]},"1407":{"position":[[509,6]]},"1409":{"position":[[530,6]]},"1609":{"position":[[420,7],[441,7],[1400,7],[1421,7]]},"2150":{"position":[[249,6]]},"2158":{"position":[[150,6]]},"2366":{"position":[[226,6]]},"2368":{"position":[[430,6]]},"2794":{"position":[[420,7],[441,7],[1400,7],[1421,7]]},"3379":{"position":[[249,6]]},"3387":{"position":[[150,6]]},"3427":{"position":[[226,6]]},"3429":{"position":[[430,6]]}}}],["uint64",{"_index":3796,"t":{"676":{"position":[[469,7],[1458,7]]},"969":{"position":[[1087,6]]},"1409":{"position":[[737,6]]},"1609":{"position":[[460,7],[1440,7]]},"1611":{"position":[[377,7],[1231,7]]},"1979":{"position":[[1153,6]]},"2189":{"position":[[1199,6]]},"2794":{"position":[[460,7],[1440,7]]},"2796":{"position":[[377,7],[1231,7]]},"3138":{"position":[[1153,6]]},"3492":{"position":[[1199,6]]}}}],["ul",{"_index":2239,"t":{"194":{"position":[[606,3]]}}}],["ulimit",{"_index":412,"t":{"10":{"position":[[523,6],[808,6]]},"511":{"position":[[114,6]]},"513":{"position":[[376,8]]},"541":{"position":[[97,6]]},"949":{"position":[[156,6]]},"1439":{"position":[[114,6]]},"1441":{"position":[[400,8]]},"1557":{"position":[[97,6]]},"1919":{"position":[[156,6]]},"2608":{"position":[[114,6]]},"2610":{"position":[[400,8]]},"2726":{"position":[[97,6]]},"3146":{"position":[[156,6]]}}}],["unabl",{"_index":1190,"t":{"58":{"position":[[1467,6]]}}}],["unaccess",{"_index":3935,"t":{"790":{"position":[[236,12]]},"792":{"position":[[561,12]]}}}],["unappli",{"_index":2214,"t":{"192":{"position":[[2633,9]]}}}],["unari",{"_index":4235,"t":{"1031":{"position":[[476,5]]},"2370":{"position":[[476,5]]},"3431":{"position":[[476,5]]}}}],["unauthent",{"_index":3983,"t":{"828":{"position":[[370,15]]},"1017":{"position":[[3616,15]]},"2354":{"position":[[3733,15]]},"3415":{"position":[[3733,15]]}}}],["unauthenticated/unauthor",{"_index":5212,"t":{"2354":{"position":[[5960,28]]},"3415":{"position":[[5989,28]]}}}],["unauthor",{"_index":2627,"t":{"250":{"position":[[1809,15]]},"291":{"position":[[1061,12]]},"315":{"position":[[210,12]]},"702":{"position":[[20,15],[42,12],[76,13]]},"1937":{"position":[[18,14],[39,12],[73,13]]},"2354":{"position":[[6287,14]]},"2982":{"position":[[18,14],[39,12],[73,13]]},"3415":{"position":[[6316,14]]}}}],["unavail",{"_index":2509,"t":{"232":{"position":[[189,15]]},"321":{"position":[[2076,11]]},"672":{"position":[[2102,11]]},"682":{"position":[[425,11]]},"1403":{"position":[[608,11]]},"1603":{"position":[[2606,11]]},"1619":{"position":[[426,11]]},"2023":{"position":[[284,11]]},"2632":{"position":[[3627,12]]},"2708":{"position":[[2421,11]]},"2788":{"position":[[2606,11]]},"2804":{"position":[[426,11]]},"3212":{"position":[[257,11]]},"3643":{"position":[[3627,12]]}}}],["unbind",{"_index":652,"t":{"16":{"position":[[3303,6]]}}}],["unblock",{"_index":3746,"t":{"658":{"position":[[212,7],[311,7],[319,7]]},"1654":{"position":[[311,7]]},"2906":{"position":[[275,7]]}}}],["unblock_us",{"_index":3745,"t":{"658":{"position":[[139,15]]},"1654":{"position":[[139,15],[212,12],[319,12]]},"2906":{"position":[[176,12],[283,12]]}}}],["uncom",{"_index":4134,"t":{"979":{"position":[[24,9]]},"981":{"position":[[24,9]]},"2072":{"position":[[24,9]]},"2074":{"position":[[24,9]]},"3309":{"position":[[24,9]]},"3311":{"position":[[24,9]]}}}],["uncov",{"_index":4464,"t":{"1355":{"position":[[1621,8]]},"2526":{"position":[[1701,8]]}}}],["undefin",{"_index":5565,"t":{"3060":{"position":[[1331,9]]}}}],["under",{"_index":504,"t":{"12":{"position":[[921,5]]},"473":{"position":[[2805,5]]},"477":{"position":[[4940,5]]},"479":{"position":[[434,5],[1182,5],[4847,5]]},"481":{"position":[[1942,5]]},"483":{"position":[[243,5]]},"555":{"position":[[544,5]]},"567":{"position":[[110,5]]},"569":{"position":[[114,5]]},"571":{"position":[[112,5]]},"573":{"position":[[114,5]]},"953":{"position":[[89,5]]},"973":{"position":[[2031,5]]},"1251":{"position":[[366,5]]},"1529":{"position":[[593,5]]},"1535":{"position":[[110,5]]},"1537":{"position":[[114,5]]},"1539":{"position":[[112,5]]},"1541":{"position":[[114,5]]},"1613":{"position":[[23,5]]},"1923":{"position":[[89,5]]},"2104":{"position":[[385,5],[715,5]]},"2422":{"position":[[1073,5]]},"2500":{"position":[[366,5]]},"2740":{"position":[[110,5]]},"2742":{"position":[[114,5]]},"2744":{"position":[[112,5]]},"2746":{"position":[[114,5]]},"2764":{"position":[[592,5]]},"2798":{"position":[[23,5]]},"3150":{"position":[[89,5]]},"3287":{"position":[[407,5],[787,5]]},"3612":{"position":[[125,5]]}}}],["underli",{"_index":927,"t":{"32":{"position":[[85,10],[810,10]]},"60":{"position":[[943,10]]},"94":{"position":[[1946,10]]},"347":{"position":[[844,10]]},"423":{"position":[[7,10]]},"969":{"position":[[1193,10]]},"1249":{"position":[[7,10]]},"1279":{"position":[[849,10]]},"1979":{"position":[[1259,10]]},"2402":{"position":[[904,10]]},"2498":{"position":[[7,10]]},"3138":{"position":[[1259,10]]}}}],["underscor",{"_index":4001,"t":{"858":{"position":[[518,12]]},"1843":{"position":[[518,12]]},"3072":{"position":[[806,12]]}}}],["understand",{"_index":1,"t":{"2":{"position":[[19,13]]},"10":{"position":[[2113,10],[2202,13]]},"16":{"position":[[6379,13]]},"82":{"position":[[1821,10]]},"130":{"position":[[1343,13],[1572,10]]},"156":{"position":[[515,10]]},"206":{"position":[[150,10]]},"254":{"position":[[51,13]]},"262":{"position":[[1587,10]]},"287":{"position":[[636,11]]},"357":{"position":[[909,10]]},"393":{"position":[[323,13]]},"479":{"position":[[6517,10]]},"553":{"position":[[315,10]]},"945":{"position":[[441,10]]},"969":{"position":[[344,10]]},"973":{"position":[[2495,10]]},"1017":{"position":[[5794,10]]},"1047":{"position":[[416,10]]},"1289":{"position":[[909,10]]},"1395":{"position":[[100,10],[150,10]]},"1527":{"position":[[514,13]]},"1571":{"position":[[836,13]]},"1979":{"position":[[385,10]]},"2066":{"position":[[441,10]]},"2354":{"position":[[5917,10]]},"2386":{"position":[[416,10]]},"2412":{"position":[[923,10]]},"2512":{"position":[[163,10]]},"2566":{"position":[[104,10],[159,10]]},"2666":{"position":[[836,13]]},"2712":{"position":[[879,13]]},"2760":{"position":[[515,13]]},"2806":{"position":[[746,13]]},"3138":{"position":[[385,10]]},"3261":{"position":[[441,10]]},"3324":{"position":[[469,13],[1087,13],[3853,13],[4973,13]]},"3415":{"position":[[5946,10]]},"3447":{"position":[[416,10]]}}}],["understood",{"_index":218,"t":{"4":{"position":[[100,10]]}}}],["undesir",{"_index":5648,"t":{"3365":{"position":[[3571,9]]}}}],["unexpect",{"_index":2928,"t":{"329":{"position":[[382,10]]},"355":{"position":[[196,10]]},"511":{"position":[[642,10]]},"1287":{"position":[[196,10]]},"1439":{"position":[[644,10]]},"2410":{"position":[[196,10]]},"2608":{"position":[[649,10]]}}}],["unexpectedli",{"_index":3032,"t":{"411":{"position":[[306,12]]},"437":{"position":[[376,12]]},"1235":{"position":[[306,12]]},"1263":{"position":[[380,12]]},"2484":{"position":[[306,12]]},"2512":{"position":[[341,12]]},"2708":{"position":[[2507,13]]}}}],["unfortun",{"_index":522,"t":{"12":{"position":[[1381,13]]},"82":{"position":[[1565,12]]},"108":{"position":[[703,14]]},"116":{"position":[[822,14]]},"333":{"position":[[172,14]]},"441":{"position":[[1244,14]]},"473":{"position":[[396,14]]},"620":{"position":[[344,14]]},"1098":{"position":[[0,13]]},"1267":{"position":[[1244,14]]},"2234":{"position":[[0,13]]},"2516":{"position":[[1244,14]]},"3584":{"position":[[0,13]]}}}],["uni",{"_index":1732,"t":{"110":{"position":[[2844,3]]},"1057":{"position":[[1912,3]]},"1061":{"position":[[308,3],[402,3]]},"1120":{"position":[[62,3]]},"1177":{"position":[[165,3],[673,3]]},"2140":{"position":[[1912,3]]},"2144":{"position":[[276,3],[370,3]]},"2228":{"position":[[165,3],[673,3]]},"2271":{"position":[[62,3]]},"3355":{"position":[[1912,3]]},"3359":{"position":[[276,3],[370,3]]},"3553":{"position":[[62,3]]},"3578":{"position":[[165,3],[673,3]]}}}],["uni_grpc",{"_index":4329,"t":{"1116":{"position":[[81,11]]},"2267":{"position":[[81,11]]},"3549":{"position":[[81,11]]}}}],["uni_grpc_tl",{"_index":4304,"t":{"1061":{"position":[[131,12]]},"2144":{"position":[[99,12]]},"3359":{"position":[[99,12]]}}}],["uni_grpc_tls_cert",{"_index":4305,"t":{"1061":{"position":[[230,17]]},"2144":{"position":[[198,17]]},"3359":{"position":[[198,17]]}}}],["uni_grpc_tls_dis",{"_index":4299,"t":{"1057":{"position":[[1847,20]]},"2140":{"position":[[1847,20]]},"3355":{"position":[[1847,20]]}}}],["uni_grpc_tls_key",{"_index":4306,"t":{"1061":{"position":[[326,16]]},"2144":{"position":[[294,16]]},"3359":{"position":[[294,16]]}}}],["uni_http_stream",{"_index":4356,"t":{"1173":{"position":[[91,18]]},"1177":{"position":[[267,18]]},"1597":{"position":[[385,15],[1431,18],[1551,18]]},"2224":{"position":[[91,18]]},"2228":{"position":[[267,18]]},"2780":{"position":[[385,15],[1395,18],[1515,18]]},"3574":{"position":[[91,18]]},"3578":{"position":[[267,18]]}}}],["uni_http_stream_handler_prefix",{"_index":4077,"t":{"930":{"position":[[459,30]]},"2051":{"position":[[648,30]]},"3244":{"position":[[735,30]]}}}],["uni_ss",{"_index":4192,"t":{"1017":{"position":[[2555,7]]},"1019":{"position":[[851,7]]},"1105":{"position":[[94,10]]},"2239":{"position":[[94,10]]},"2354":{"position":[[2672,7]]},"2356":{"position":[[851,7]]},"2364":{"position":[[2252,7]]},"3415":{"position":[[2672,7]]},"3417":{"position":[[851,7]]},"3425":{"position":[[2225,7]]},"3589":{"position":[[94,10]]}}}],["uni_sse_handler_prefix",{"_index":4075,"t":{"930":{"position":[[353,22]]},"2051":{"position":[[542,22]]},"3244":{"position":[[629,22]]}}}],["uni_websocket",{"_index":4331,"t":{"1143":{"position":[[86,16]]},"1147":{"position":[[172,13]]},"2256":{"position":[[86,16]]},"2260":{"position":[[172,13]]},"3606":{"position":[[86,16]]},"3610":{"position":[[172,13]]}}}],["uni_websocket\":tru",{"_index":4332,"t":{"1147":{"position":[[337,21]]},"2260":{"position":[[337,21]]},"3610":{"position":[[337,21]]}}}],["uni_websocket_handler_prefix",{"_index":4079,"t":{"930":{"position":[[584,28]]},"2051":{"position":[[773,28]]},"3244":{"position":[[860,28]]}}}],["uni_websocket_ping_interv",{"_index":5259,"t":{"2580":{"position":[[350,27]]}}}],["unidirect",{"_index":852,"t":{"24":{"position":[[439,14]]},"32":{"position":[[135,14],[208,14],[1263,14],[1364,14],[1650,14],[1898,14],[2093,14],[2153,14],[2178,14],[2232,14]]},"42":{"position":[[409,14]]},"108":{"position":[[414,14],[653,14],[1248,14],[1345,14],[1437,14]]},"110":{"position":[[315,14],[424,14],[1195,14],[2209,14]]},"112":{"position":[[280,14]]},"118":{"position":[[1185,14]]},"120":{"position":[[118,14]]},"124":{"position":[[1743,14]]},"128":{"position":[[1571,14]]},"206":{"position":[[264,14]]},"254":{"position":[[167,14]]},"295":{"position":[[1055,14]]},"305":{"position":[[361,14]]},"311":{"position":[[1192,14]]},"345":{"position":[[41,14]]},"369":{"position":[[232,14],[326,14],[355,14]]},"421":{"position":[[312,14]]},"577":{"position":[[66,14]]},"672":{"position":[[1707,14]]},"688":{"position":[[215,14]]},"892":{"position":[[196,14]]},"918":{"position":[[184,14],[284,14],[395,14],[499,14]]},"930":{"position":[[421,14],[543,14],[664,14]]},"1015":{"position":[[10,14]]},"1037":{"position":[[96,14]]},"1061":{"position":[[99,14]]},"1063":{"position":[[126,14],[250,14]]},"1092":{"position":[[0,14],[140,14],[482,14]]},"1094":{"position":[[11,14],[200,14],[472,14],[685,14],[939,14]]},"1098":{"position":[[567,14],[754,14],[1105,14]]},"1105":{"position":[[33,14]]},"1116":{"position":[[33,14]]},"1124":{"position":[[46,14]]},"1140":{"position":[[75,14]]},"1143":{"position":[[33,14]]},"1147":{"position":[[19,14]]},"1166":{"position":[[163,14]]},"1173":{"position":[[33,14]]},"1245":{"position":[[372,14]]},"1277":{"position":[[41,14]]},"1327":{"position":[[351,14],[445,14],[474,14]]},"1361":{"position":[[40,14],[138,14],[258,14],[460,14],[519,14]]},"1467":{"position":[[62,14]]},"1545":{"position":[[66,14]]},"1603":{"position":[[2273,14]]},"1672":{"position":[[921,14]]},"1731":{"position":[[215,14]]},"2005":{"position":[[196,14]]},"2021":{"position":[[554,14]]},"2023":{"position":[[491,14]]},"2039":{"position":[[399,14],[499,14],[610,14],[714,14]]},"2051":{"position":[[610,14],[732,14],[853,14]]},"2144":{"position":[[67,14]]},"2152":{"position":[[927,14],[1031,14]]},"2187":{"position":[[0,14],[140,14],[485,14]]},"2189":{"position":[[11,14],[200,14],[610,14],[1345,14]]},"2191":{"position":[[1191,14]]},"2217":{"position":[[163,14]]},"2224":{"position":[[33,14]]},"2234":{"position":[[567,14],[754,14],[1105,14]]},"2239":{"position":[[33,14]]},"2253":{"position":[[62,14]]},"2256":{"position":[[33,14]]},"2260":{"position":[[19,14]]},"2267":{"position":[[33,14]]},"2275":{"position":[[46,14]]},"2299":{"position":[[62,14]]},"2352":{"position":[[10,14]]},"2376":{"position":[[96,14]]},"2442":{"position":[[468,14],[565,14],[600,14]]},"2494":{"position":[[372,14]]},"2532":{"position":[[40,14],[138,14],[258,14],[460,14],[519,14]]},"2750":{"position":[[66,14]]},"2788":{"position":[[2273,14]]},"2836":{"position":[[921,14]]},"2920":{"position":[[215,14]]},"3194":{"position":[[196,14]]},"3210":{"position":[[527,14]]},"3212":{"position":[[464,14]]},"3230":{"position":[[399,14],[499,14],[610,14],[714,14]]},"3244":{"position":[[697,14],[819,14],[940,14]]},"3359":{"position":[[67,14]]},"3365":{"position":[[849,14],[1918,14]]},"3367":{"position":[[1496,14],[2003,14]]},"3369":{"position":[[15,14],[402,14],[497,14],[1457,14]]},"3381":{"position":[[927,14],[1031,14]]},"3413":{"position":[[10,14]]},"3437":{"position":[[96,14]]},"3456":{"position":[[456,14]]},"3464":{"position":[[0,14],[140,14],[485,14],[884,14],[922,14]]},"3466":{"position":[[1191,14]]},"3486":{"position":[[436,14]]},"3492":{"position":[[11,14],[200,14],[610,14],[1345,14]]},"3512":{"position":[[62,14]]},"3549":{"position":[[33,14]]},"3557":{"position":[[46,14]]},"3567":{"position":[[163,14]]},"3574":{"position":[[33,14]]},"3584":{"position":[[567,14],[754,14],[1105,14]]},"3589":{"position":[[33,14]]},"3603":{"position":[[62,14]]},"3606":{"position":[[33,14]]},"3610":{"position":[[19,14]]}}}],["unifi",{"_index":1810,"t":{"122":{"position":[[1973,7]]},"128":{"position":[[1538,8]]},"148":{"position":[[55,7]]},"152":{"position":[[47,7],[815,7]]},"1684":{"position":[[80,7]]},"2848":{"position":[[80,7]]}}}],["uniqu",{"_index":444,"t":{"10":{"position":[[1391,8]]},"18":{"position":[[42,6]]},"24":{"position":[[630,6]]},"38":{"position":[[626,6]]},"52":{"position":[[154,6]]},"74":{"position":[[634,6]]},"76":{"position":[[1902,6]]},"80":{"position":[[668,6]]},"156":{"position":[[917,6]]},"158":{"position":[[932,7]]},"184":{"position":[[629,7]]},"242":{"position":[[1243,7]]},"258":{"position":[[573,6],[2072,6]]},"262":{"position":[[1746,6]]},"270":{"position":[[1640,6],[1691,6],[1779,6]]},"335":{"position":[[315,6]]},"377":{"position":[[14,6]]},"393":{"position":[[47,6]]},"431":{"position":[[679,6]]},"437":{"position":[[1115,6]]},"441":{"position":[[378,6]]},"463":{"position":[[397,6],[526,6]]},"525":{"position":[[986,6]]},"636":{"position":[[372,6],[744,6]]},"678":{"position":[[5,6],[621,6]]},"780":{"position":[[16,6]]},"828":{"position":[[210,6]]},"858":{"position":[[456,6]]},"951":{"position":[[356,8]]},"989":{"position":[[103,6]]},"1017":{"position":[[2426,6]]},"1019":{"position":[[722,6]]},"1021":{"position":[[700,6]]},"1023":{"position":[[2024,6]]},"1025":{"position":[[2058,6]]},"1045":{"position":[[1132,6]]},"1203":{"position":[[216,6]]},"1257":{"position":[[679,6]]},"1263":{"position":[[1119,6]]},"1267":{"position":[[378,6]]},"1271":{"position":[[279,6]]},"1315":{"position":[[397,6],[526,6]]},"1351":{"position":[[47,6]]},"1355":{"position":[[1053,6]]},"1407":{"position":[[1203,6]]},"1409":{"position":[[49,6]]},"1415":{"position":[[470,6]]},"1423":{"position":[[1814,6]]},"1471":{"position":[[99,6],[772,6]]},"1599":{"position":[[1374,6],[1544,6]]},"1615":{"position":[[5,6],[621,6]]},"1636":{"position":[[372,6],[744,6]]},"1719":{"position":[[1133,6],[1915,6]]},"1721":{"position":[[876,7]]},"1761":{"position":[[16,6]]},"1795":{"position":[[16,6]]},"1841":{"position":[[31,8]]},"1843":{"position":[[456,6]]},"1921":{"position":[[356,8]]},"2106":{"position":[[216,6]]},"2116":{"position":[[140,6]]},"2160":{"position":[[1814,6]]},"2303":{"position":[[99,6],[772,6]]},"2354":{"position":[[2543,6]]},"2356":{"position":[[722,6]]},"2358":{"position":[[700,6]]},"2360":{"position":[[2024,6]]},"2362":{"position":[[2058,6]]},"2364":{"position":[[2123,6]]},"2384":{"position":[[1132,6]]},"2424":{"position":[[840,6],[1560,6]]},"2466":{"position":[[47,6]]},"2506":{"position":[[679,6]]},"2516":{"position":[[378,6]]},"2520":{"position":[[279,6]]},"2526":{"position":[[1038,6]]},"2602":{"position":[[397,6],[526,6]]},"2642":{"position":[[382,6]]},"2658":{"position":[[397,6],[526,6]]},"2782":{"position":[[1374,6],[1544,6]]},"2800":{"position":[[5,6],[621,6]]},"2823":{"position":[[372,6],[708,6]]},"2883":{"position":[[436,6],[2194,6]]},"2887":{"position":[[876,7]]},"2966":{"position":[[16,6]]},"3024":{"position":[[10,6]]},"3070":{"position":[[31,8]]},"3072":{"position":[[744,6]]},"3148":{"position":[[356,8]]},"3289":{"position":[[216,6]]},"3324":{"position":[[1035,6]]},"3341":{"position":[[607,6]]},"3367":{"position":[[5017,6]]},"3389":{"position":[[1814,6]]},"3415":{"position":[[2543,6]]},"3417":{"position":[[722,6]]},"3419":{"position":[[747,6]]},"3421":{"position":[[1827,6]]},"3423":{"position":[[2058,6]]},"3425":{"position":[[2096,6]]},"3445":{"position":[[1132,6]]},"3516":{"position":[[99,6],[613,6]]},"3653":{"position":[[382,6]]},"3669":{"position":[[397,6],[526,6]]}}}],["unit",{"_index":1595,"t":{"98":{"position":[[1868,4]]},"941":{"position":[[79,4],[298,5]]},"2062":{"position":[[79,4],[298,5]]},"3257":{"position":[[79,4],[298,5]]}}}],["uniti",{"_index":5717,"t":{"3395":{"position":[[383,5]]}}}],["univers",{"_index":1987,"t":{"158":{"position":[[1233,9]]},"184":{"position":[[892,9]]},"242":{"position":[[1363,9]]},"305":{"position":[[223,9]]},"1355":{"position":[[1491,9]]},"2416":{"position":[[325,10],[440,10]]},"2526":{"position":[[1799,9]]}}}],["universalcli",{"_index":3212,"t":{"475":{"position":[[450,16],[478,15],[976,15]]},"477":{"position":[[606,15]]}}}],["unix",{"_index":414,"t":{"10":{"position":[[536,6]]},"636":{"position":[[798,4],[864,5]]},"638":{"position":[[857,4],[923,5]]},"656":{"position":[[355,4],[424,5]]},"690":{"position":[[1438,5],[1489,5]]},"776":{"position":[[10,4]]},"778":{"position":[[10,4]]},"796":{"position":[[401,4]]},"997":{"position":[[113,4]]},"999":{"position":[[480,4]]},"1467":{"position":[[492,4]]},"1599":{"position":[[1419,5],[1589,5]]},"1636":{"position":[[798,4],[864,5]]},"1639":{"position":[[746,4],[903,4],[969,5]]},"1652":{"position":[[355,4],[424,5]]},"1719":{"position":[[1206,4]]},"1733":{"position":[[1438,5],[1489,5]]},"1751":{"position":[[113,4]]},"1753":{"position":[[480,4]]},"1759":{"position":[[10,4]]},"1791":{"position":[[10,4]]},"1793":{"position":[[10,4]]},"1811":{"position":[[401,4]]},"2299":{"position":[[458,4]]},"2720":{"position":[[281,4]]},"2782":{"position":[[1419,5],[1589,5]]},"2823":{"position":[[762,4],[828,5]]},"2826":{"position":[[710,4],[867,4],[933,5]]},"2883":{"position":[[577,4],[1335,4]]},"2904":{"position":[[319,4],[388,5]]},"2922":{"position":[[1402,5],[1453,5]]},"2956":{"position":[[113,4]]},"2958":{"position":[[480,4]]},"2964":{"position":[[10,4]]},"3020":{"position":[[25,4]]},"3022":{"position":[[20,4]]},"3040":{"position":[[388,4]]},"3415":{"position":[[3792,5]]},"3421":{"position":[[3275,5]]},"3425":{"position":[[2843,5]]},"3512":{"position":[[451,4]]}}}],["unknown",{"_index":1753,"t":{"110":{"position":[[3944,7]]},"124":{"position":[[787,8]]},"329":{"position":[[564,7],[929,7],[1013,7],[1093,7],[1174,7]]},"704":{"position":[[20,8],[45,7]]},"760":{"position":[[20,8],[45,7]]},"1130":{"position":[[233,7]]},"1457":{"position":[[1423,7]]},"1509":{"position":[[1096,7]]},"1511":{"position":[[873,7]]},"1831":{"position":[[1288,7]]},"1939":{"position":[[18,8],[42,7]]},"2281":{"position":[[217,7]]},"2289":{"position":[[1478,7]]},"2768":{"position":[[1096,7]]},"2770":{"position":[[873,7]]},"2984":{"position":[[18,8],[42,7]]},"3060":{"position":[[1371,7]]},"3502":{"position":[[959,7]]},"3563":{"position":[[196,7]]}}}],["unknown:chat",{"_index":4586,"t":{"1457":{"position":[[1493,15]]},"2289":{"position":[[1547,15]]},"3502":{"position":[[1096,15]]}}}],["unless",{"_index":1941,"t":{"148":{"position":[[675,6]]},"2088":{"position":[[1180,6]]},"2094":{"position":[[1237,6]]},"3068":{"position":[[565,6]]},"3273":{"position":[[1072,6]]},"3279":{"position":[[1129,6]]}}}],["unlik",{"_index":3725,"t":{"632":{"position":[[273,6]]},"652":{"position":[[173,6]]},"794":{"position":[[96,6]]},"1023":{"position":[[963,6]]},"1081":{"position":[[150,7]]},"1631":{"position":[[273,6]]},"1647":{"position":[[173,6]]},"1684":{"position":[[0,6]]},"1809":{"position":[[96,6]]},"2201":{"position":[[150,7]]},"2360":{"position":[[963,6]]},"2818":{"position":[[273,6]]},"2848":{"position":[[0,6]]},"2899":{"position":[[173,6]]},"3421":{"position":[[766,6]]},"3476":{"position":[[150,7]]}}}],["unlimit",{"_index":4045,"t":{"902":{"position":[[156,9]]},"906":{"position":[[110,10]]},"1523":{"position":[[187,9]]},"2015":{"position":[[156,9]]},"2019":{"position":[[110,10]]},"2706":{"position":[[187,9]]},"3204":{"position":[[156,9]]},"3208":{"position":[[110,10]]}}}],["unlock",{"_index":3616,"t":{"549":{"position":[[589,8]]},"1565":{"position":[[589,8]]},"2734":{"position":[[589,8]]}}}],["unmap",{"_index":5768,"t":{"3540":{"position":[[565,8]]},"3542":{"position":[[468,8]]}}}],["unmarsh",{"_index":1071,"t":{"50":{"position":[[163,14]]}}}],["unmarshal",{"_index":521,"t":{"12":{"position":[[1366,14]]}}}],["unnecessari",{"_index":704,"t":{"16":{"position":[[5731,11]]},"190":{"position":[[509,11]]},"2708":{"position":[[2256,11]]},"2914":{"position":[[85,11]]},"3034":{"position":[[476,11]]}}}],["unpack",{"_index":3516,"t":{"509":{"position":[[215,6]]},"539":{"position":[[154,6]]},"1437":{"position":[[225,6],[712,6]]},"1555":{"position":[[154,6]]},"2606":{"position":[[239,6],[732,6]]},"2724":{"position":[[154,6],[323,6]]}}}],["unprotect",{"_index":4556,"t":{"1455":{"position":[[794,11]]},"2287":{"position":[[797,11]]},"3498":{"position":[[776,11]]}}}],["unrecover",{"_index":3892,"t":{"724":{"position":[[20,14],[52,13]]},"768":{"position":[[20,14]]},"1959":{"position":[[18,14],[49,13]]},"3004":{"position":[[18,14],[49,13]]}}}],["unreli",{"_index":627,"t":{"16":{"position":[[2282,10]]},"94":{"position":[[906,10]]},"108":{"position":[[850,10],[1007,10]]},"1207":{"position":[[253,10]]},"2110":{"position":[[253,10]]},"3293":{"position":[[253,10]]}}}],["unset",{"_index":4029,"t":{"878":{"position":[[1094,5]]},"1991":{"position":[[1094,5]]},"3180":{"position":[[1094,5]]}}}],["unstabl",{"_index":3509,"t":{"483":{"position":[[2032,8]]}}}],["unsubscrib",{"_index":1352,"t":{"72":{"position":[[998,11]]},"76":{"position":[[478,12]]},"272":{"position":[[1117,11]]},"291":{"position":[[1037,12]]},"293":{"position":[[372,12]]},"407":{"position":[[146,12]]},"439":{"position":[[169,11]]},"455":{"position":[[444,11]]},"457":{"position":[[332,11]]},"834":{"position":[[122,13]]},"1067":{"position":[[18,11]]},"1094":{"position":[[324,11]]},"1231":{"position":[[146,12]]},"1265":{"position":[[169,11]]},"1307":{"position":[[444,11]]},"1309":{"position":[[332,11]]},"1403":{"position":[[927,11],[1007,11],[1211,11]]},"1407":{"position":[[570,11]]},"1409":{"position":[[1509,11]]},"1411":{"position":[[21,11],[160,11]]},"1417":{"position":[[959,13],[1174,11],[1217,12]]},"1425":{"position":[[2106,11],[2259,11]]},"1427":{"position":[[385,12]]},"1429":{"position":[[495,11]]},"1463":{"position":[[0,11],[19,13],[66,14],[141,11],[239,11],[289,11],[348,11],[515,11]]},"1505":{"position":[[4038,14]]},"1575":{"position":[[230,11],[454,11]]},"1587":{"position":[[336,11]]},"1589":{"position":[[261,11]]},"1615":{"position":[[781,11]]},"1682":{"position":[[1190,11]]},"1849":{"position":[[125,13]]},"2130":{"position":[[18,11]]},"2150":{"position":[[1750,11],[1772,11]]},"2152":{"position":[[551,13],[580,11],[619,12]]},"2174":{"position":[[317,11]]},"2189":{"position":[[343,11],[355,11],[738,11]]},"2295":{"position":[[0,11],[19,13],[66,14],[141,11],[239,11],[289,11],[348,11],[481,11]]},"2360":{"position":[[4711,12]]},"2446":{"position":[[232,11]]},"2480":{"position":[[146,12]]},"2512":{"position":[[2512,11]]},"2514":{"position":[[169,11]]},"2594":{"position":[[449,11]]},"2596":{"position":[[332,11]]},"2622":{"position":[[5599,12]]},"2632":{"position":[[565,12],[646,12],[1012,12],[3709,12],[3829,12],[4186,12],[4374,12],[4764,11],[4796,14]]},"2634":{"position":[[514,12]]},"2638":{"position":[[265,13]]},"2642":{"position":[[45,13],[61,11]]},"2644":{"position":[[670,13]]},"2646":{"position":[[1236,11]]},"2650":{"position":[[18,11],[44,11],[93,11],[265,12],[352,11]]},"2670":{"position":[[230,11],[454,11]]},"2682":{"position":[[336,11]]},"2684":{"position":[[261,11]]},"2686":{"position":[[4096,14]]},"2800":{"position":[[781,11]]},"2846":{"position":[[1190,11]]},"3078":{"position":[[125,13]]},"3333":{"position":[[18,11]]},"3365":{"position":[[1864,12],[2122,11]]},"3379":{"position":[[1750,11],[1772,11]]},"3381":{"position":[[551,13],[580,11],[619,12]]},"3403":{"position":[[317,11]]},"3421":{"position":[[4714,12]]},"3492":{"position":[[343,11],[355,11],[738,11]]},"3508":{"position":[[0,11],[19,13],[54,11],[145,11],[195,11],[254,11],[387,11]]},"3633":{"position":[[5599,12]]},"3643":{"position":[[565,12],[646,12],[1012,12],[3709,12],[3829,12],[4186,12],[4374,12],[4764,11],[4796,14]]},"3645":{"position":[[514,12]]},"3649":{"position":[[265,13]]},"3653":{"position":[[45,13],[61,11]]},"3655":{"position":[[670,13]]},"3657":{"position":[[1236,11]]},"3661":{"position":[[18,11],[44,11],[93,11],[265,12],[352,11]]}}}],["unsubscribe(ch",{"_index":1403,"t":{"78":{"position":[[769,14]]}}}],["unsubscribe/disconnect",{"_index":3054,"t":{"435":{"position":[[320,22]]},"1261":{"position":[[320,22]]},"1589":{"position":[[488,22]]},"2510":{"position":[[320,22]]},"2684":{"position":[[488,22]]}}}],["unsubscribe/remov",{"_index":5402,"t":{"2634":{"position":[[770,18]]},"3645":{"position":[[770,18]]}}}],["unsubscribedev",{"_index":5376,"t":{"2632":{"position":[[2769,17]]},"3643":{"position":[[2769,17]]}}}],["unsuccess",{"_index":1375,"t":{"74":{"position":[[2058,12]]}}}],["unsuit",{"_index":3342,"t":{"477":{"position":[[1889,10]]}}}],["unsur",{"_index":5261,"t":{"2606":{"position":[[623,6]]}}}],["until",{"_index":1733,"t":{"110":{"position":[[2855,5]]},"116":{"position":[[243,5]]},"192":{"position":[[2692,5]]},"293":{"position":[[473,5]]},"355":{"position":[[321,6]]},"477":{"position":[[2778,5]]},"533":{"position":[[439,5]]},"648":{"position":[[139,5]]},"682":{"position":[[503,5]]},"776":{"position":[[529,5]]},"1017":{"position":[[1652,6]]},"1193":{"position":[[1266,5]]},"1287":{"position":[[321,6]]},"1407":{"position":[[1411,5]]},"1409":{"position":[[561,5]]},"1413":{"position":[[818,5]]},"1573":{"position":[[308,5]]},"1587":{"position":[[323,5]]},"1619":{"position":[[504,5]]},"1643":{"position":[[139,5]]},"1791":{"position":[[529,5]]},"2056":{"position":[[410,5]]},"2092":{"position":[[1282,5]]},"2354":{"position":[[1769,6]]},"2410":{"position":[[324,6]]},"2668":{"position":[[308,5]]},"2682":{"position":[[323,5]]},"2762":{"position":[[407,5]]},"2804":{"position":[[504,5]]},"2883":{"position":[[666,5]]},"2895":{"position":[[139,5]]},"3020":{"position":[[571,5]]},"3249":{"position":[[410,5]]},"3277":{"position":[[1282,5]]},"3415":{"position":[[1769,6]]}}}],["untouch",{"_index":4617,"t":{"1475":{"position":[[107,9]]},"2307":{"position":[[107,9]]},"3520":{"position":[[107,9]]}}}],["unus",{"_index":3995,"t":{"838":{"position":[[243,6]]},"1185":{"position":[[711,6]]},"1189":{"position":[[1593,6]]},"1855":{"position":[[243,6]]},"3084":{"position":[[243,6]]}}}],["unusu",{"_index":1886,"t":{"132":{"position":[[1364,9]]}}}],["unwant",{"_index":4374,"t":{"1185":{"position":[[567,8]]},"1189":{"position":[[1449,8]]},"1672":{"position":[[1182,8]]},"2082":{"position":[[552,8]]},"2086":{"position":[[1595,8]]},"2836":{"position":[[1182,8]]},"3086":{"position":[[554,8]]},"3267":{"position":[[552,8]]},"3271":{"position":[[1561,8]]}}}],["unwrap",{"_index":1855,"t":{"126":{"position":[[450,6]]}}}],["up",{"_index":1009,"t":{"38":{"position":[[962,2]]},"50":{"position":[[337,2],[367,2],[423,2],[468,2]]},"78":{"position":[[4078,3]]},"92":{"position":[[282,2]]},"122":{"position":[[1730,3]]},"124":{"position":[[3492,2]]},"148":{"position":[[10,3]]},"160":{"position":[[205,2],[666,2]]},"204":{"position":[[66,2]]},"208":{"position":[[310,2],[420,2]]},"216":{"position":[[4372,2]]},"244":{"position":[[676,2]]},"262":{"position":[[1003,2]]},"264":{"position":[[1394,3]]},"278":{"position":[[834,2]]},"280":{"position":[[3540,2]]},"329":{"position":[[622,3],[794,2],[1306,2]]},"355":{"position":[[299,2]]},"357":{"position":[[649,2]]},"461":{"position":[[280,2],[467,2],[623,2]]},"471":{"position":[[3790,2]]},"473":{"position":[[898,2]]},"477":{"position":[[2151,2]]},"513":{"position":[[442,2]]},"533":{"position":[[237,2]]},"555":{"position":[[225,2],[257,2],[288,2]]},"567":{"position":[[292,2]]},"581":{"position":[[90,2]]},"583":{"position":[[105,2]]},"612":{"position":[[103,2]]},"682":{"position":[[378,3]]},"971":{"position":[[271,2],[432,2],[588,3],[703,3],[766,2],[931,2]]},"1193":{"position":[[1136,2]]},"1203":{"position":[[919,3]]},"1281":{"position":[[1030,2]]},"1287":{"position":[[299,2]]},"1289":{"position":[[649,2]]},"1313":{"position":[[280,2],[467,2],[623,2]]},"1341":{"position":[[267,2]]},"1441":{"position":[[466,2]]},"1529":{"position":[[225,2],[257,2],[288,2]]},"1535":{"position":[[292,2]]},"1549":{"position":[[90,2]]},"1551":{"position":[[105,2]]},"1589":{"position":[[381,2]]},"1619":{"position":[[379,3]]},"1682":{"position":[[604,2]]},"1690":{"position":[[90,2]]},"1831":{"position":[[769,3]]},"1981":{"position":[[271,2],[432,2],[588,3],[703,3],[766,2],[931,2]]},"1983":{"position":[[4160,3]]},"2092":{"position":[[1152,2]]},"2106":{"position":[[919,3]]},"2354":{"position":[[6362,2]]},"2360":{"position":[[4573,2]]},"2364":{"position":[[1021,2]]},"2404":{"position":[[1077,2]]},"2410":{"position":[[302,2]]},"2412":{"position":[[660,2]]},"2418":{"position":[[413,2]]},"2456":{"position":[[266,2]]},"2600":{"position":[[280,2],[467,2],[623,2]]},"2610":{"position":[[466,2]]},"2622":{"position":[[155,2],[5508,2]]},"2638":{"position":[[376,2]]},"2656":{"position":[[280,2],[467,2],[623,2]]},"2684":{"position":[[381,2]]},"2708":{"position":[[1738,2]]},"2740":{"position":[[292,2]]},"2754":{"position":[[90,2]]},"2756":{"position":[[105,2]]},"2764":{"position":[[225,2],[257,2],[288,2]]},"2804":{"position":[[379,3]]},"2846":{"position":[[604,2]]},"2854":{"position":[[90,2]]},"3042":{"position":[[1171,2]]},"3060":{"position":[[840,3]]},"3140":{"position":[[271,2],[432,2],[588,3],[703,3],[766,2],[931,2]]},"3142":{"position":[[4160,3]]},"3277":{"position":[[1152,2]]},"3289":{"position":[[919,3]]},"3415":{"position":[[6391,2]]},"3421":{"position":[[4576,2]]},"3425":{"position":[[994,2]]},"3633":{"position":[[155,2],[5508,2]]},"3649":{"position":[[376,2]]},"3667":{"position":[[280,2],[467,2],[623,2]]}}}],["upcom",{"_index":688,"t":{"16":{"position":[[4889,8]]},"256":{"position":[[41,8]]},"2760":{"position":[[1629,8]]}}}],["updat",{"_index":814,"t":{"20":{"position":[[529,7]]},"28":{"position":[[184,6],[287,6]]},"32":{"position":[[1117,7]]},"42":{"position":[[1001,7]]},"66":{"position":[[595,8],[984,8],[1404,8]]},"92":{"position":[[0,7]]},"130":{"position":[[1873,7]]},"146":{"position":[[258,8],[289,6]]},"172":{"position":[[1093,7]]},"232":{"position":[[934,7]]},"252":{"position":[[853,7]]},"264":{"position":[[1034,7],[1276,8]]},"268":{"position":[[1095,6]]},"270":{"position":[[1082,6]]},"272":{"position":[[861,6]]},"274":{"position":[[354,8]]},"280":{"position":[[1322,8],[1347,8]]},"289":{"position":[[1003,7]]},"315":{"position":[[1659,7]]},"321":{"position":[[2122,7]]},"337":{"position":[[67,7]]},"341":{"position":[[357,7],[968,6],[1410,6]]},"361":{"position":[[435,8]]},"369":{"position":[[272,8]]},"375":{"position":[[82,8]]},"409":{"position":[[873,7]]},"437":{"position":[[1350,6]]},"465":{"position":[[900,7],[1111,6]]},"467":{"position":[[1284,6],[1423,7]]},"475":{"position":[[95,8],[3735,6],[4194,8],[4271,6]]},"525":{"position":[[644,7]]},"559":{"position":[[176,7]]},"688":{"position":[[174,7],[453,6],[575,6],[593,6]]},"690":{"position":[[521,6],[808,7]]},"694":{"position":[[892,7]]},"983":{"position":[[21,6]]},"1092":{"position":[[339,7]]},"1211":{"position":[[357,7],[968,6],[1410,6]]},"1233":{"position":[[873,7]]},"1263":{"position":[[1354,6]]},"1319":{"position":[[451,8]]},"1327":{"position":[[391,8]]},"1333":{"position":[[82,8]]},"1355":{"position":[[652,7]]},"1359":{"position":[[1699,6],[1727,7]]},"1413":{"position":[[1040,7]]},"1423":{"position":[[440,6]]},"1505":{"position":[[333,6],[3755,6]]},"1573":{"position":[[422,7]]},"1575":{"position":[[214,6],[438,6]]},"1589":{"position":[[197,6]]},"1593":{"position":[[176,7]]},"1682":{"position":[[974,6]]},"1698":{"position":[[110,8]]},"1703":{"position":[[13,7],[162,10]]},"1705":{"position":[[20,6],[385,6],[452,6],[519,6],[587,6]]},"1721":{"position":[[207,7]]},"1731":{"position":[[174,7],[453,6],[575,6],[593,6]]},"1733":{"position":[[521,6],[808,7]]},"1737":{"position":[[874,7]]},"1953":{"position":[[134,8]]},"2076":{"position":[[21,6]]},"2122":{"position":[[465,7]]},"2160":{"position":[[440,6]]},"2187":{"position":[[343,7]]},"2230":{"position":[[37,6]]},"2243":{"position":[[37,6]]},"2281":{"position":[[37,6]]},"2394":{"position":[[357,7],[968,6],[1410,6]]},"2434":{"position":[[455,8]]},"2442":{"position":[[506,8]]},"2448":{"position":[[82,8]]},"2482":{"position":[[873,7]]},"2512":{"position":[[0,6],[1397,6],[1530,7]]},"2526":{"position":[[640,7]]},"2530":{"position":[[1699,6],[1727,7]]},"2574":{"position":[[374,7],[661,8]]},"2622":{"position":[[1309,7]]},"2628":{"position":[[456,7]]},"2638":{"position":[[151,7]]},"2644":{"position":[[447,7]]},"2668":{"position":[[422,7]]},"2670":{"position":[[214,6],[438,6]]},"2684":{"position":[[197,6]]},"2686":{"position":[[334,6],[3789,6]]},"2762":{"position":[[247,7]]},"2776":{"position":[[176,7]]},"2846":{"position":[[974,6]]},"2862":{"position":[[118,8]]},"2867":{"position":[[13,7],[162,10]]},"2869":{"position":[[20,6],[313,6],[380,6],[446,6]]},"2887":{"position":[[207,7]]},"2920":{"position":[[174,7],[417,6],[539,6],[557,6]]},"2922":{"position":[[485,6],[772,7]]},"2926":{"position":[[874,7]]},"2998":{"position":[[134,8]]},"3042":{"position":[[795,7],[1447,7],[1775,7]]},"3313":{"position":[[21,6]]},"3345":{"position":[[725,7],[985,7]]},"3389":{"position":[[440,6]]},"3464":{"position":[[343,7]]},"3580":{"position":[[37,6]]},"3633":{"position":[[1309,7]]},"3639":{"position":[[456,7]]},"3649":{"position":[[151,7]]},"3655":{"position":[[447,7]]}}}],["update_push_statu",{"_index":2728,"t":{"274":{"position":[[314,18]]},"1686":{"position":[[322,18]]},"1721":{"position":[[172,18],[382,18],[797,18],[1052,18]]},"2850":{"position":[[322,18]]},"2885":{"position":[[77,18],[187,18]]},"2887":{"position":[[172,18],[382,18],[797,18],[1052,18]]}}}],["update_user_statu",{"_index":3862,"t":{"686":{"position":[[67,19]]},"688":{"position":[[27,18],[373,21]]},"1729":{"position":[[67,19]]},"1731":{"position":[[27,18],[373,21]]},"2830":{"position":[[1265,21]]},"2832":{"position":[[1065,21]]},"2834":{"position":[[1546,21]]},"2918":{"position":[[67,19]]},"2920":{"position":[[27,18]]}}}],["updateactivestatu",{"_index":3635,"t":{"563":{"position":[[692,21]]},"1666":{"position":[[1262,21]]},"1668":{"position":[[1059,21]]},"1670":{"position":[[1540,21]]}}}],["upgrad",{"_index":342,"t":{"8":{"position":[[812,7]]},"50":{"position":[[2102,7]]},"142":{"position":[[287,7]]},"146":{"position":[[38,7]]},"170":{"position":[[614,7]]},"172":{"position":[[582,7]]},"198":{"position":[[1250,7],[1301,10]]},"250":{"position":[[524,7],[1127,7],[1178,10]]},"979":{"position":[[177,8],[1230,7]]},"981":{"position":[[177,8],[804,7]]},"1247":{"position":[[478,7]]},"1359":{"position":[[474,7],[722,7]]},"1672":{"position":[[1049,7]]},"2072":{"position":[[177,8],[1230,7]]},"2074":{"position":[[177,8],[804,7]]},"2496":{"position":[[478,7]]},"2530":{"position":[[474,7],[722,7]]},"2624":{"position":[[415,7]]},"2762":{"position":[[416,7]]},"2836":{"position":[[1049,7]]},"3220":{"position":[[754,7]]},"3309":{"position":[[177,8],[1230,7]]},"3311":{"position":[[177,8],[804,7]]},"3635":{"position":[[415,7]]}}}],["upload",{"_index":720,"t":{"16":{"position":[[6605,8]]},"517":{"position":[[47,6]]},"1445":{"position":[[47,6]]},"2614":{"position":[[47,6]]}}}],["upon",{"_index":878,"t":{"26":{"position":[[909,4]]},"34":{"position":[[688,4]]},"44":{"position":[[95,5]]},"166":{"position":[[506,4]]},"176":{"position":[[234,4]]},"218":{"position":[[572,4]]},"258":{"position":[[1997,4]]},"272":{"position":[[1168,4]]},"293":{"position":[[1390,4]]},"295":{"position":[[897,4],[1289,4]]},"315":{"position":[[927,4]]},"317":{"position":[[485,4]]},"329":{"position":[[604,4]]},"331":{"position":[[634,4]]},"347":{"position":[[969,4],[1123,4],[1516,4]]},"371":{"position":[[130,4]]},"411":{"position":[[439,4]]},"455":{"position":[[1013,4]]},"467":{"position":[[699,4]]},"471":{"position":[[3508,4]]},"475":{"position":[[2436,4],[3991,4],[5837,4]]},"481":{"position":[[56,4]]},"628":{"position":[[160,4]]},"816":{"position":[[723,4]]},"840":{"position":[[523,4]]},"971":{"position":[[1250,4]]},"1017":{"position":[[522,4]]},"1023":{"position":[[829,4]]},"1170":{"position":[[185,4]]},"1177":{"position":[[235,4]]},"1235":{"position":[[439,4]]},"1279":{"position":[[974,4],[1128,4],[1521,4]]},"1307":{"position":[[1013,4]]},"1329":{"position":[[130,4]]},"1505":{"position":[[3719,4]]},"1569":{"position":[[537,4],[627,4]]},"1575":{"position":[[125,4],[719,4],[859,4]]},"1589":{"position":[[127,4],[384,4],[561,4]]},"1627":{"position":[[160,4]]},"1666":{"position":[[1702,4]]},"1682":{"position":[[1241,4]]},"1715":{"position":[[102,4],[144,4]]},"1783":{"position":[[203,4]]},"1827":{"position":[[224,4]]},"1835":{"position":[[708,4]]},"1857":{"position":[[615,4]]},"1967":{"position":[[23,4]]},"1981":{"position":[[1250,4]]},"1983":{"position":[[2594,4]]},"2221":{"position":[[185,4]]},"2228":{"position":[[235,4]]},"2354":{"position":[[522,4]]},"2360":{"position":[[829,4]]},"2368":{"position":[[403,4]]},"2402":{"position":[[1035,4],[1194,4],[1681,4]]},"2444":{"position":[[136,4]]},"2484":{"position":[[439,4]]},"2558":{"position":[[320,4]]},"2594":{"position":[[1059,4]]},"2622":{"position":[[4195,4],[5618,4]]},"2624":{"position":[[142,4]]},"2628":{"position":[[105,4]]},"2630":{"position":[[58,4]]},"2632":{"position":[[369,4],[1031,5],[3640,4]]},"2636":{"position":[[3059,4]]},"2638":{"position":[[199,4]]},"2640":{"position":[[152,4]]},"2644":{"position":[[125,4]]},"2646":{"position":[[226,4],[671,4],[1331,4]]},"2652":{"position":[[140,4]]},"2664":{"position":[[537,4],[627,4]]},"2670":{"position":[[125,4],[719,4],[859,4]]},"2684":{"position":[[127,4],[384,4],[561,4]]},"2686":{"position":[[3752,4]]},"2814":{"position":[[160,4]]},"2830":{"position":[[1705,4]]},"2846":{"position":[[1241,4]]},"2879":{"position":[[102,4],[144,4]]},"2942":{"position":[[203,4]]},"3012":{"position":[[23,4]]},"3032":{"position":[[228,4]]},"3034":{"position":[[449,4]]},"3042":{"position":[[808,4]]},"3056":{"position":[[190,4]]},"3064":{"position":[[744,4]]},"3088":{"position":[[615,4]]},"3140":{"position":[[1250,4]]},"3142":{"position":[[2594,4]]},"3363":{"position":[[744,4]]},"3365":{"position":[[1333,4],[3438,4]]},"3367":{"position":[[3842,4]]},"3369":{"position":[[81,4]]},"3415":{"position":[[522,4]]},"3429":{"position":[[403,4]]},"3571":{"position":[[185,4]]},"3578":{"position":[[235,4]]},"3612":{"position":[[2176,4]]},"3629":{"position":[[635,4]]},"3633":{"position":[[4195,4],[5618,4]]},"3635":{"position":[[142,4]]},"3639":{"position":[[105,4]]},"3641":{"position":[[58,4]]},"3643":{"position":[[369,4],[1031,5],[3640,4]]},"3647":{"position":[[3059,4]]},"3649":{"position":[[199,4]]},"3651":{"position":[[152,4]]},"3655":{"position":[[125,4]]},"3657":{"position":[[226,4],[671,4],[1331,4]]},"3663":{"position":[[140,4]]}}}],["uppercas",{"_index":4024,"t":{"878":{"position":[[132,11]]},"1991":{"position":[[132,11]]},"3180":{"position":[[132,11]]}}}],["uprad",{"_index":4469,"t":{"1359":{"position":[[951,6]]},"2530":{"position":[[951,6]]}}}],["upstream",{"_index":4110,"t":{"953":{"position":[[386,8]]},"979":{"position":[[0,8],[78,8]]},"981":{"position":[[0,8],[78,8]]},"1077":{"position":[[616,9],[626,8],[794,8],[1151,8]]},"1923":{"position":[[386,8]]},"2072":{"position":[[0,8],[78,8]]},"2074":{"position":[[0,8],[78,8]]},"2197":{"position":[[616,9],[626,8],[794,8],[1151,8]]},"3150":{"position":[[386,8]]},"3309":{"position":[[0,8],[78,8]]},"3311":{"position":[[0,8],[78,8]]},"3472":{"position":[[616,9],[626,8],[794,8],[1151,8]]}}}],["uptim",{"_index":4629,"t":{"1479":{"position":[[445,9]]},"2311":{"position":[[445,9]]},"3524":{"position":[[261,9]]}}}],["uri",{"_index":2383,"t":{"212":{"position":[[388,4]]},"1491":{"position":[[407,3]]},"2323":{"position":[[407,3]]},"3536":{"position":[[407,3]]}}}],["url",{"_index":1059,"t":{"46":{"position":[[255,3]]},"94":{"position":[[1864,3],[2002,3]]},"100":{"position":[[1052,3]]},"156":{"position":[[658,6]]},"168":{"position":[[439,3]]},"176":{"position":[[1181,3]]},"192":{"position":[[1548,3]]},"194":{"position":[[2939,3]]},"198":{"position":[[135,3]]},"216":{"position":[[830,4]]},"224":{"position":[[311,4],[459,4]]},"250":{"position":[[394,3]]},"331":{"position":[[33,3]]},"608":{"position":[[700,3]]},"798":{"position":[[1306,3]]},"918":{"position":[[926,3]]},"930":{"position":[[165,3],[258,3],[342,3],[448,3],[573,3],[689,3],[758,3],[840,3],[919,3]]},"1017":{"position":[[241,3],[1843,3]]},"1086":{"position":[[88,3]]},"1098":{"position":[[137,3],[186,3],[299,4],[323,3],[607,3],[1062,3]]},"1162":{"position":[[697,4],[711,3]]},"1209":{"position":[[60,3]]},"1359":{"position":[[1132,3]]},"1455":{"position":[[303,3]]},"1813":{"position":[[1306,3]]},"2039":{"position":[[1141,3]]},"2051":{"position":[[165,3],[258,3],[360,3],[447,3],[531,3],[637,3],[762,3],[878,3],[947,3],[1029,3],[1108,3]]},"2112":{"position":[[60,3]]},"2206":{"position":[[88,3]]},"2234":{"position":[[137,3],[186,3],[299,4],[323,3],[607,3],[1062,3]]},"2287":{"position":[[306,3]]},"2342":{"position":[[697,4],[711,3]]},"2354":{"position":[[241,3],[1960,3]]},"2530":{"position":[[1132,3]]},"3230":{"position":[[1141,3]]},"3244":{"position":[[165,3],[258,3],[360,3],[447,3],[618,3],[724,3],[849,3],[965,3],[1034,3],[1116,3],[1195,3]]},"3295":{"position":[[60,3]]},"3415":{"position":[[241,3],[1960,3]]},"3456":{"position":[[880,3]]},"3481":{"position":[[88,3]]},"3486":{"position":[[849,3]]},"3498":{"position":[[300,3]]},"3584":{"position":[[137,3],[186,3],[299,4],[323,3],[607,3],[1062,3]]},"3627":{"position":[[697,4],[711,3]]},"3629":{"position":[[30,3]]}}}],["url('http://localhost:8000/connection/uni_sse');url.searchparams.append(\"cf_connect",{"_index":4323,"t":{"1098":{"position":[[333,85]]},"2234":{"position":[[333,85]]},"3584":{"position":[[333,85]]}}}],["url.parse(indication.origin",{"_index":1760,"t":{"110":{"position":[[4256,28]]}}}],["urlconf",{"_index":2186,"t":{"192":{"position":[[1578,8],[1599,7],[2001,7]]}}}],["urlpattern",{"_index":2197,"t":{"192":{"position":[[2127,11]]}}}],["urls.pi",{"_index":2127,"t":{"188":{"position":[[365,7]]},"192":{"position":[[1651,8],[1791,7]]},"194":{"position":[[281,10]]}}}],["us",{"_index":84,"t":{"2":{"position":[[1012,4],[1127,5],[1188,4],[1332,4],[2194,4],[2364,4],[3161,3]]},"4":{"position":[[545,3]]},"8":{"position":[[64,4],[345,5],[940,6]]},"10":{"position":[[394,4],[1075,5]]},"12":{"position":[[472,5],[1026,3],[1131,3],[1183,3],[1282,3]]},"14":{"position":[[160,3],[669,3],[892,3]]},"16":{"position":[[94,3],[3154,3],[3359,3],[6276,5],[6678,3],[7353,3],[7507,3]]},"18":{"position":[[230,4],[485,3],[954,5],[1037,7],[1564,5],[1963,3],[3481,5]]},"22":{"position":[[32,6]]},"28":{"position":[[433,3],[875,5]]},"32":{"position":[[383,5],[754,3],[1218,5],[1347,5],[1511,3],[1615,5],[1644,5]]},"34":{"position":[[660,6],[773,6]]},"36":{"position":[[30,4],[185,4]]},"38":{"position":[[93,6],[570,3]]},"42":{"position":[[394,6],[958,5]]},"46":{"position":[[355,5],[407,5]]},"50":{"position":[[190,3],[2236,5]]},"52":{"position":[[904,6]]},"54":{"position":[[750,4]]},"58":{"position":[[601,4],[799,4],[1038,7],[1143,4]]},"60":{"position":[[371,4],[688,3],[992,6],[1234,4],[1311,5],[1950,3],[2008,3]]},"62":{"position":[[118,6],[985,6]]},"64":{"position":[[1214,3]]},"66":{"position":[[2040,3]]},"68":{"position":[[254,3],[539,3]]},"74":{"position":[[1200,4],[1330,6],[1446,6],[1865,6]]},"76":{"position":[[173,6],[1035,5],[1329,5]]},"78":{"position":[[2094,4],[2126,3],[2177,5],[2662,4],[2785,5],[3499,5],[3826,3]]},"82":{"position":[[523,3]]},"84":{"position":[[447,4]]},"86":{"position":[[478,3],[527,3]]},"90":{"position":[[699,3]]},"94":{"position":[[462,3],[504,5],[749,5],[928,5],[1860,3],[2374,3],[2525,3],[2603,4],[3053,3]]},"96":{"position":[[97,3]]},"98":{"position":[[163,5]]},"102":{"position":[[176,3]]},"106":{"position":[[145,5],[1578,4]]},"110":{"position":[[2248,3]]},"116":{"position":[[146,5],[904,5]]},"118":{"position":[[784,4],[1220,3]]},"122":{"position":[[286,4]]},"124":{"position":[[478,3],[592,4],[934,5],[1040,5],[1098,3],[1151,4],[1278,4],[1368,5],[1509,3],[2006,6],[2415,5],[3045,5],[3279,3],[3400,5],[3448,3],[3900,5]]},"126":{"position":[[578,3]]},"128":{"position":[[265,6]]},"130":{"position":[[123,5],[345,3],[963,5]]},"134":{"position":[[933,3]]},"136":{"position":[[618,4],[1008,5]]},"138":{"position":[[513,4],[682,3],[830,6],[915,4]]},"142":{"position":[[266,3]]},"144":{"position":[[599,3],[785,5],[849,3],[1175,5]]},"148":{"position":[[367,3],[560,3]]},"152":{"position":[[1317,4]]},"154":{"position":[[58,5],[516,4]]},"156":{"position":[[108,5],[333,5],[553,5]]},"158":{"position":[[1377,4]]},"160":{"position":[[628,6]]},"166":{"position":[[50,3],[248,5]]},"168":{"position":[[24,5],[293,4],[619,3]]},"170":{"position":[[147,3],[656,5],[832,5]]},"172":{"position":[[3,3],[63,3],[835,6],[945,5],[1026,5]]},"174":{"position":[[235,5],[866,4]]},"176":{"position":[[91,3],[283,6],[339,6],[1668,5],[2394,3]]},"178":{"position":[[100,3],[498,6],[932,5],[970,3]]},"182":{"position":[[53,5],[289,3],[637,4]]},"184":{"position":[[1027,4]]},"186":{"position":[[547,4],[576,3],[666,5],[711,4]]},"192":{"position":[[2865,5]]},"194":{"position":[[2929,5]]},"196":{"position":[[20,3],[797,4],[1003,5],[1249,5],[1305,5],[1486,3]]},"198":{"position":[[404,4],[435,3],[489,6],[538,5],[1766,3],[1787,3]]},"200":{"position":[[2072,3]]},"202":{"position":[[97,4],[637,3],[755,5],[1375,5],[1462,5],[1776,5],[1843,3]]},"206":{"position":[[260,3],[336,4],[381,5],[547,3],[606,3]]},"208":{"position":[[349,5],[486,5]]},"212":{"position":[[20,5]]},"214":{"position":[[21,5],[502,4],[577,3],[1057,5]]},"216":{"position":[[34,5],[454,3],[494,4],[1173,3],[1315,6],[1337,5],[2199,3],[2917,5]]},"218":{"position":[[123,3],[510,5]]},"222":{"position":[[284,4],[523,4]]},"226":{"position":[[206,6],[595,5]]},"232":{"position":[[884,5],[1242,5],[1333,3]]},"236":{"position":[[348,5],[653,5],[690,5],[1058,6]]},"240":{"position":[[59,5],[218,5],[288,4]]},"242":{"position":[[146,5],[667,4],[1494,4]]},"244":{"position":[[422,4]]},"246":{"position":[[2219,3],[2647,5]]},"250":{"position":[[22,3],[574,3],[664,5]]},"252":{"position":[[247,3],[957,5]]},"254":{"position":[[22,6],[163,3],[238,4],[282,5],[453,3]]},"258":{"position":[[701,5],[996,6],[1340,3],[1423,5]]},"260":{"position":[[297,5],[344,5],[854,5],[1411,5]]},"262":{"position":[[434,4],[1570,3]]},"264":{"position":[[558,3]]},"268":{"position":[[162,5],[269,3]]},"270":{"position":[[251,5],[970,3],[1293,3],[1810,4],[1989,5],[2345,3],[2439,5],[2526,3]]},"272":{"position":[[1068,3],[1226,6]]},"274":{"position":[[69,5],[304,5],[529,3]]},"278":{"position":[[174,3]]},"280":{"position":[[430,3],[927,6],[1270,3],[1533,5],[1669,3],[2531,5],[3315,5]]},"283":{"position":[[338,5],[570,5]]},"285":{"position":[[144,4]]},"287":{"position":[[325,5],[362,5],[383,5],[719,4]]},"289":{"position":[[1076,5]]},"291":{"position":[[122,5],[314,7],[2361,5],[2645,6],[2736,5],[2993,5]]},"293":{"position":[[407,5],[643,3],[677,5],[935,5],[1064,5],[1505,5]]},"295":{"position":[[1038,5],[1396,5]]},"297":{"position":[[108,5],[136,5]]},"301":{"position":[[7,5],[851,5],[919,5]]},"303":{"position":[[128,5]]},"305":{"position":[[312,5],[357,3],[500,5]]},"309":{"position":[[31,4]]},"311":{"position":[[791,4],[1227,3]]},"313":{"position":[[410,5]]},"315":{"position":[[396,5]]},"317":{"position":[[517,6],[801,3],[842,5],[1004,3],[1248,3]]},"319":{"position":[[76,5]]},"321":{"position":[[591,5],[2295,3]]},"323":{"position":[[567,5]]},"325":{"position":[[108,5]]},"327":{"position":[[85,5],[323,5],[383,5],[417,4],[678,4],[774,3]]},"331":{"position":[[90,5],[202,4],[1028,3],[1677,4],[1723,5],[1746,5]]},"333":{"position":[[479,6],[772,3]]},"345":{"position":[[108,4],[243,5],[1289,5]]},"347":{"position":[[684,3],[814,4],[1339,3],[1636,4]]},"349":{"position":[[837,5]]},"353":{"position":[[112,3]]},"355":{"position":[[130,5]]},"357":{"position":[[336,3],[473,3],[1040,5]]},"361":{"position":[[41,4]]},"363":{"position":[[56,4]]},"367":{"position":[[163,3]]},"369":{"position":[[349,5],[447,5]]},"371":{"position":[[40,5]]},"373":{"position":[[83,5]]},"389":{"position":[[52,5]]},"393":{"position":[[474,6],[569,6]]},"397":{"position":[[407,3]]},"399":{"position":[[23,4]]},"401":{"position":[[20,5]]},"407":{"position":[[380,5],[427,5],[475,4]]},"409":{"position":[[535,5],[756,5],[1008,5],[1139,3]]},"413":{"position":[[279,3],[354,5],[560,5],[648,5]]},"415":{"position":[[131,3],[230,5],[336,5]]},"419":{"position":[[102,6]]},"421":{"position":[[192,5]]},"423":{"position":[[51,3]]},"425":{"position":[[892,5]]},"429":{"position":[[297,5],[414,5],[605,5],[1471,6]]},"431":{"position":[[38,3],[472,3]]},"433":{"position":[[92,5],[427,4]]},"437":{"position":[[1394,4],[2056,3]]},"439":{"position":[[108,5]]},"441":{"position":[[566,5]]},"447":{"position":[[494,5]]},"467":{"position":[[791,5],[841,4],[1480,4]]},"471":{"position":[[51,5],[281,5],[649,5],[1161,5],[1305,5],[4013,5]]},"473":{"position":[[285,4],[1521,4],[1788,3],[1896,4],[1958,5],[2006,4],[2223,4]]},"475":{"position":[[104,4],[303,4],[642,6],[885,3],[950,3],[2834,4],[3046,3],[5949,3],[6513,3]]},"477":{"position":[[1915,3],[1947,3],[2200,4],[3022,4],[5569,5]]},"479":{"position":[[1514,4],[1740,5],[1969,3],[2279,5],[2634,5],[3508,5],[3766,5],[4916,3],[5186,3],[5453,3],[6384,5]]},"481":{"position":[[162,3],[258,5],[358,4]]},"483":{"position":[[344,5],[1566,3]]},"489":{"position":[[302,5]]},"493":{"position":[[377,5],[658,3],[745,3],[906,5],[990,5]]},"497":{"position":[[577,5],[721,5]]},"503":{"position":[[163,5]]},"527":{"position":[[373,5]]},"539":{"position":[[65,3]]},"541":{"position":[[15,4]]},"543":{"position":[[8,3],[78,3]]},"555":{"position":[[417,4]]},"559":{"position":[[20,6]]},"563":{"position":[[181,4]]},"589":{"position":[[497,3]]},"593":{"position":[[46,4],[169,4]]},"596":{"position":[[57,5]]},"600":{"position":[[185,6]]},"602":{"position":[[280,5]]},"604":{"position":[[186,4],[387,5]]},"608":{"position":[[5,5],[333,5],[499,4],[632,3],[678,5],[766,3],[896,4]]},"614":{"position":[[19,3],[205,3]]},"616":{"position":[[50,3]]},"620":{"position":[[147,5],[194,3],[451,5]]},"624":{"position":[[65,5]]},"630":{"position":[[204,3]]},"636":{"position":[[60,6],[335,5],[911,3]]},"638":{"position":[[104,6],[203,5],[970,3]]},"640":{"position":[[529,5],[834,5],[953,5]]},"656":{"position":[[471,3]]},"666":{"position":[[15,3],[240,6]]},"672":{"position":[[581,5],[680,3],[735,3]]},"686":{"position":[[161,6]]},"688":{"position":[[46,5],[209,5]]},"690":{"position":[[996,5]]},"698":{"position":[[332,3]]},"704":{"position":[[138,4]]},"726":{"position":[[181,3]]},"728":{"position":[[103,6]]},"734":{"position":[[93,4]]},"772":{"position":[[11,4]]},"774":{"position":[[222,3]]},"776":{"position":[[614,3]]},"778":{"position":[[110,6]]},"780":{"position":[[84,6]]},"782":{"position":[[288,5]]},"784":{"position":[[276,5]]},"788":{"position":[[11,5],[80,3]]},"790":{"position":[[487,3]]},"792":{"position":[[190,5],[812,3]]},"802":{"position":[[202,3],[551,3],[589,5]]},"808":{"position":[[8,3],[107,6]]},"810":{"position":[[377,5],[455,3]]},"814":{"position":[[27,4]]},"816":{"position":[[52,4]]},"818":{"position":[[139,3],[637,5],[792,5]]},"820":{"position":[[457,3],[665,3],[804,5],[1015,6]]},"822":{"position":[[36,5]]},"824":{"position":[[208,3],[289,5],[329,5],[535,6]]},"828":{"position":[[279,5],[435,3]]},"834":{"position":[[532,5]]},"840":{"position":[[539,5],[582,4],[725,4]]},"842":{"position":[[324,4],[465,4]]},"852":{"position":[[97,5]]},"854":{"position":[[93,5]]},"858":{"position":[[90,7],[633,3],[1542,3],[1605,3],[1661,3]]},"870":{"position":[[141,5]]},"876":{"position":[[133,5],[171,5]]},"878":{"position":[[337,5],[426,3],[501,3],[928,3]]},"884":{"position":[[228,4],[316,5],[349,3],[385,4]]},"886":{"position":[[351,6]]},"888":{"position":[[204,3]]},"892":{"position":[[176,4],[1132,5]]},"898":{"position":[[10,3]]},"914":{"position":[[133,3]]},"922":{"position":[[222,6]]},"924":{"position":[[0,3]]},"926":{"position":[[267,4],[322,4],[410,4],[468,4],[789,5],[922,3]]},"932":{"position":[[378,5]]},"935":{"position":[[175,6],[281,3]]},"937":{"position":[[25,5],[147,5],[344,6],[413,5]]},"939":{"position":[[25,5],[263,3]]},"941":{"position":[[40,5],[142,3]]},"945":{"position":[[463,3],[503,4]]},"949":{"position":[[218,6],[528,5]]},"951":{"position":[[889,3]]},"953":{"position":[[283,5],[895,3]]},"963":{"position":[[317,3]]},"969":{"position":[[584,4],[783,5],[1308,4]]},"971":{"position":[[1222,6],[1335,6]]},"973":{"position":[[714,4],[2684,3],[2756,5],[2791,3],[3704,5]]},"977":{"position":[[26,3]]},"979":{"position":[[45,5]]},"981":{"position":[[45,5]]},"989":{"position":[[219,3],[291,5]]},"997":{"position":[[378,5]]},"1005":{"position":[[44,3],[371,3],[476,3]]},"1015":{"position":[[35,4]]},"1017":{"position":[[636,5],[1110,3],[1545,4],[2181,3],[2601,4],[2691,4],[3147,5],[3928,5],[5522,5],[5632,4],[5744,5]]},"1019":{"position":[[898,4],[984,4],[1593,5]]},"1021":{"position":[[864,4],[954,4],[1124,3]]},"1023":{"position":[[585,3],[688,5],[737,5],[2188,4],[2278,4],[2837,6],[3097,5]]},"1025":{"position":[[2220,4],[2310,4]]},"1027":{"position":[[169,3]]},"1029":{"position":[[224,3]]},"1031":{"position":[[656,3],[1263,3]]},"1033":{"position":[[218,5],[281,6],[386,6]]},"1037":{"position":[[147,4]]},"1039":{"position":[[103,3],[288,3],[810,6],[911,3],[1075,6],[1133,5],[1246,3],[1330,5]]},"1041":{"position":[[297,4],[354,3],[424,3],[508,5],[736,3]]},"1043":{"position":[[155,3]]},"1045":{"position":[[5,5],[1153,4],[1671,3],[1912,5]]},"1047":{"position":[[77,3],[320,4],[366,5]]},"1049":{"position":[[704,4],[837,4],[1055,5]]},"1051":{"position":[[387,3],[459,3],[493,4],[938,5]]},"1057":{"position":[[391,5],[983,4],[1669,6]]},"1063":{"position":[[246,3],[314,3]]},"1069":{"position":[[741,6],[777,3],[1144,3]]},"1075":{"position":[[57,5]]},"1077":{"position":[[26,3],[497,3],[1061,5],[1108,4]]},"1079":{"position":[[95,3]]},"1081":{"position":[[100,3]]},"1086":{"position":[[150,4]]},"1090":{"position":[[185,3]]},"1092":{"position":[[47,3],[224,3],[770,5]]},"1094":{"position":[[929,5]]},"1098":{"position":[[728,3],[831,6],[909,6],[1045,5]]},"1102":{"position":[[137,3]]},"1107":{"position":[[89,5]]},"1130":{"position":[[38,4],[161,3]]},"1134":{"position":[[169,5]]},"1136":{"position":[[183,4],[248,4]]},"1140":{"position":[[11,4],[126,3]]},"1147":{"position":[[53,5],[1390,6],[1422,5]]},"1160":{"position":[[369,4],[854,3],[1029,3],[1381,4],[1463,3]]},"1162":{"position":[[23,3],[66,4],[253,3],[625,3],[825,3],[1019,5],[1097,3]]},"1170":{"position":[[119,4],[233,3]]},"1177":{"position":[[53,5]]},"1183":{"position":[[0,4],[181,5]]},"1185":{"position":[[344,4],[405,7],[473,5]]},"1187":{"position":[[57,4],[194,3]]},"1189":{"position":[[331,4],[548,3],[635,5],[1219,4],[1287,7],[1355,5]]},"1191":{"position":[[48,5],[681,3],[865,3],[1391,3],[1587,3]]},"1193":{"position":[[603,5],[679,3]]},"1195":{"position":[[23,3],[243,3],[310,3]]},"1197":{"position":[[531,3],[884,3],[1041,5],[1187,5],[1238,3]]},"1199":{"position":[[68,5],[1144,4],[1264,3]]},"1201":{"position":[[381,3],[709,3]]},"1203":{"position":[[654,3],[1759,3]]},"1209":{"position":[[169,4]]},"1215":{"position":[[31,4]]},"1221":{"position":[[407,3]]},"1223":{"position":[[23,4]]},"1225":{"position":[[20,5]]},"1231":{"position":[[380,5],[427,5],[475,4]]},"1233":{"position":[[535,5],[756,5],[1008,5],[1139,3]]},"1237":{"position":[[279,3],[354,5],[560,5],[648,5]]},"1239":{"position":[[131,3],[230,5],[336,5]]},"1243":{"position":[[102,6]]},"1245":{"position":[[252,5]]},"1249":{"position":[[51,3]]},"1251":{"position":[[969,5]]},"1255":{"position":[[297,5],[414,5],[605,5],[1471,6]]},"1257":{"position":[[38,3],[472,3]]},"1259":{"position":[[92,5],[427,4]]},"1263":{"position":[[1398,4],[2060,3]]},"1265":{"position":[[108,5]]},"1267":{"position":[[566,5]]},"1269":{"position":[[156,5],[453,5]]},"1271":{"position":[[15,3]]},"1277":{"position":[[108,4],[243,5],[1289,5]]},"1279":{"position":[[684,3],[819,4],[1344,3],[1641,4]]},"1281":{"position":[[857,5]]},"1285":{"position":[[112,3]]},"1287":{"position":[[130,5]]},"1289":{"position":[[336,3],[473,3],[1040,5]]},"1293":{"position":[[310,5],[627,5]]},"1295":{"position":[[273,5]]},"1299":{"position":[[494,5]]},"1319":{"position":[[41,4]]},"1325":{"position":[[165,3]]},"1327":{"position":[[468,5],[566,5]]},"1329":{"position":[[40,5]]},"1331":{"position":[[83,5]]},"1347":{"position":[[52,5]]},"1359":{"position":[[43,5],[543,5],[1232,4],[1396,3],[1621,5],[1720,3]]},"1361":{"position":[[555,5]]},"1365":{"position":[[336,3],[366,3]]},"1367":{"position":[[976,5]]},"1371":{"position":[[203,3]]},"1373":{"position":[[141,5]]},"1375":{"position":[[335,4]]},"1383":{"position":[[302,5]]},"1387":{"position":[[378,5],[659,3],[746,3],[907,5],[991,5]]},"1391":{"position":[[573,5],[717,5]]},"1397":{"position":[[163,5]]},"1399":{"position":[[254,6]]},"1403":{"position":[[131,3],[269,5],[353,5],[2256,5]]},"1405":{"position":[[531,4],[708,5],[912,5],[1154,3],[1497,4],[1852,4],[1984,5],[3019,6]]},"1407":{"position":[[186,5],[767,5],[899,5]]},"1425":{"position":[[355,5]]},"1429":{"position":[[577,4]]},"1437":{"position":[[670,3]]},"1453":{"position":[[263,3],[310,3]]},"1455":{"position":[[375,3],[619,5]]},"1457":{"position":[[94,3],[312,5],[718,5]]},"1461":{"position":[[271,4]]},"1467":{"position":[[50,6],[92,6]]},"1477":{"position":[[805,4]]},"1481":{"position":[[85,3]]},"1485":{"position":[[90,5],[149,3],[226,6],[583,5],[710,5],[986,3]]},"1487":{"position":[[327,4]]},"1495":{"position":[[60,3],[469,5]]},"1497":{"position":[[37,3],[951,5]]},"1505":{"position":[[456,5],[1227,5],[3240,5],[3315,3],[4319,3],[5945,5],[6115,5],[7654,3],[9783,5],[10292,7]]},"1509":{"position":[[532,4],[1164,3]]},"1511":{"position":[[65,5],[345,5]]},"1515":{"position":[[23,5]]},"1519":{"position":[[749,3]]},"1521":{"position":[[58,3]]},"1523":{"position":[[176,3]]},"1529":{"position":[[417,4]]},"1555":{"position":[[65,3]]},"1557":{"position":[[15,4]]},"1559":{"position":[[8,3],[78,3]]},"1571":{"position":[[1863,5]]},"1573":{"position":[[164,5]]},"1575":{"position":[[142,5],[605,5],[749,5]]},"1577":{"position":[[17,3],[243,4]]},"1585":{"position":[[376,5]]},"1587":{"position":[[177,5]]},"1589":{"position":[[229,5],[429,5]]},"1591":{"position":[[307,5],[811,5],[1116,5],[1235,5]]},"1593":{"position":[[20,6]]},"1597":{"position":[[126,4]]},"1599":{"position":[[1091,4]]},"1603":{"position":[[669,5],[768,3],[823,3]]},"1619":{"position":[[574,4]]},"1623":{"position":[[65,5]]},"1629":{"position":[[204,3]]},"1636":{"position":[[60,6],[335,5],[911,3]]},"1639":{"position":[[104,6],[203,5],[868,4],[1016,3]]},"1652":{"position":[[471,3]]},"1660":{"position":[[15,3],[240,6]]},"1666":{"position":[[187,4],[819,4]]},"1668":{"position":[[154,4]]},"1670":{"position":[[868,4]]},"1672":{"position":[[1080,3]]},"1680":{"position":[[271,5]]},"1682":{"position":[[1141,3],[1299,6]]},"1684":{"position":[[358,5]]},"1686":{"position":[[82,5],[312,5]]},"1688":{"position":[[910,5]]},"1690":{"position":[[57,3]]},"1692":{"position":[[30,4],[225,4],[858,3],[1026,5]]},"1719":{"position":[[1149,4]]},"1721":{"position":[[168,3],[427,5],[483,5],[775,3]]},"1729":{"position":[[161,6]]},"1731":{"position":[[46,5],[209,5]]},"1733":{"position":[[996,5]]},"1741":{"position":[[32,4]]},"1751":{"position":[[378,5]]},"1759":{"position":[[110,6]]},"1761":{"position":[[84,6]]},"1765":{"position":[[44,3],[327,3],[432,3]]},"1767":{"position":[[69,5]]},"1771":{"position":[[247,3],[273,3],[1291,5],[1447,4],[1458,3],[1658,5],[1700,3]]},"1773":{"position":[[28,3],[374,5],[745,3],[795,3],[999,3],[1146,3],[1541,7]]},"1775":{"position":[[233,3],[450,3]]},"1777":{"position":[[376,3],[597,3]]},"1779":{"position":[[71,5]]},"1781":{"position":[[78,5]]},"1783":{"position":[[104,5]]},"1787":{"position":[[30,4]]},"1789":{"position":[[222,3]]},"1791":{"position":[[614,3]]},"1793":{"position":[[110,6]]},"1795":{"position":[[84,6]]},"1797":{"position":[[256,5],[341,3]]},"1799":{"position":[[244,5],[329,3]]},"1803":{"position":[[11,5],[80,3]]},"1807":{"position":[[190,5]]},"1817":{"position":[[202,3],[551,3],[589,5]]},"1823":{"position":[[8,3],[107,6]]},"1825":{"position":[[377,5],[455,3],[564,5],[644,5],[992,4]]},"1827":{"position":[[97,5],[132,3],[906,3],[983,5],[1079,4],[1255,5],[1340,3]]},"1831":{"position":[[485,5],[918,5],[1123,5]]},"1833":{"position":[[27,4]]},"1835":{"position":[[52,4]]},"1837":{"position":[[457,3],[708,6],[1053,5]]},"1843":{"position":[[90,7],[633,3],[1425,3],[1488,3],[1544,3]]},"1845":{"position":[[36,5]]},"1847":{"position":[[1538,3]]},"1849":{"position":[[517,5],[915,5]]},"1853":{"position":[[813,3]]},"1855":{"position":[[1400,5],[1424,5]]},"1857":{"position":[[631,5],[683,4],[826,4]]},"1859":{"position":[[460,4],[601,4]]},"1865":{"position":[[588,5],[663,5],[802,6]]},"1867":{"position":[[561,5],[636,5]]},"1883":{"position":[[62,5]]},"1885":{"position":[[417,3],[462,6],[970,5],[1047,4]]},"1893":{"position":[[97,5]]},"1895":{"position":[[93,5]]},"1897":{"position":[[101,5]]},"1905":{"position":[[153,6]]},"1909":{"position":[[441,6]]},"1911":{"position":[[502,6]]},"1913":{"position":[[141,5],[290,6]]},"1915":{"position":[[149,5],[298,6]]},"1919":{"position":[[218,6],[528,5]]},"1921":{"position":[[889,3]]},"1923":{"position":[[283,5],[895,3]]},"1929":{"position":[[461,3],[549,5]]},"1933":{"position":[[332,3]]},"1939":{"position":[[135,4]]},"1961":{"position":[[181,3],[459,6]]},"1963":{"position":[[96,4]]},"1967":{"position":[[268,4]]},"1973":{"position":[[317,3]]},"1979":{"position":[[681,5],[839,5],[1374,4]]},"1981":{"position":[[1222,6],[1335,6]]},"1983":{"position":[[711,4],[3173,6],[3459,3],[3926,4],[4849,5]]},"1989":{"position":[[133,5],[171,5]]},"1991":{"position":[[337,5],[426,3],[501,3],[928,3]]},"1997":{"position":[[228,4],[316,5],[349,3],[385,4]]},"1999":{"position":[[351,6]]},"2001":{"position":[[204,3]]},"2005":{"position":[[176,4],[1132,5]]},"2011":{"position":[[10,3]]},"2023":{"position":[[386,5]]},"2029":{"position":[[244,3]]},"2035":{"position":[[210,3]]},"2043":{"position":[[222,6]]},"2045":{"position":[[0,3]]},"2047":{"position":[[267,4],[322,4],[410,4],[468,4],[789,5],[922,3]]},"2053":{"position":[[378,5]]},"2056":{"position":[[264,6],[389,3]]},"2058":{"position":[[25,5],[147,5],[344,6],[413,5]]},"2060":{"position":[[25,5],[263,3]]},"2062":{"position":[[40,5],[142,3]]},"2066":{"position":[[463,3],[503,4]]},"2070":{"position":[[26,3]]},"2072":{"position":[[45,5]]},"2074":{"position":[[45,5]]},"2080":{"position":[[0,4],[181,5]]},"2082":{"position":[[126,5],[330,4],[390,7],[458,5],[763,4]]},"2084":{"position":[[57,4],[194,3]]},"2086":{"position":[[297,4],[364,3],[451,5],[692,5],[768,4],[1174,5],[1373,4],[1433,7],[1501,5],[1753,4]]},"2088":{"position":[[527,3],[719,3],[860,4],[944,3],[1049,4]]},"2090":{"position":[[48,5],[681,3],[865,3],[1391,3],[1587,3]]},"2092":{"position":[[578,5],[654,3]]},"2094":{"position":[[36,3],[530,3],[740,3],[899,4],[983,3],[1106,4]]},"2096":{"position":[[23,3],[243,3],[310,3]]},"2098":{"position":[[531,3],[884,3],[1041,5],[1187,5],[1238,3]]},"2100":{"position":[[68,5],[1144,4],[1264,3]]},"2102":{"position":[[381,3],[709,3]]},"2104":{"position":[[195,3]]},"2106":{"position":[[654,3],[1759,3]]},"2112":{"position":[[169,4]]},"2122":{"position":[[484,4]]},"2124":{"position":[[173,3]]},"2126":{"position":[[54,6],[301,3]]},"2132":{"position":[[741,6],[777,3],[1144,3]]},"2140":{"position":[[391,5],[983,4],[1669,6]]},"2148":{"position":[[76,4],[409,4]]},"2152":{"position":[[1020,7]]},"2154":{"position":[[246,4],[423,5],[606,5],[843,4],[1187,4],[1548,4],[1782,5]]},"2168":{"position":[[31,5],[392,4],[610,3],[949,3],[1211,5]]},"2185":{"position":[[185,3]]},"2187":{"position":[[47,3],[224,3],[773,5]]},"2189":{"position":[[1335,5]]},"2191":{"position":[[496,5],[731,5]]},"2195":{"position":[[57,5]]},"2197":{"position":[[26,3],[497,3],[1061,5],[1108,4]]},"2199":{"position":[[95,3]]},"2201":{"position":[[100,3]]},"2206":{"position":[[150,4]]},"2213":{"position":[[88,5],[138,5]]},"2221":{"position":[[119,4],[233,3]]},"2228":{"position":[[53,5]]},"2234":{"position":[[728,3],[831,6],[909,6],[1045,5]]},"2241":{"position":[[88,5],[138,5]]},"2247":{"position":[[169,5]]},"2249":{"position":[[183,4],[248,4]]},"2253":{"position":[[11,4],[113,3]]},"2260":{"position":[[53,5],[1469,6],[1501,5]]},"2281":{"position":[[145,3]]},"2285":{"position":[[272,3],[319,3]]},"2287":{"position":[[378,3],[622,5]]},"2289":{"position":[[143,3],[372,5],[778,5],[2423,3],[2559,5]]},"2291":{"position":[[717,3],[853,5]]},"2293":{"position":[[271,4]]},"2299":{"position":[[50,6],[92,6]]},"2309":{"position":[[236,5],[847,4]]},"2313":{"position":[[85,3],[505,3]]},"2317":{"position":[[90,5],[149,3],[226,6],[583,5],[710,5],[986,3]]},"2319":{"position":[[327,4]]},"2327":{"position":[[31,4]]},"2340":{"position":[[369,4],[854,3],[1029,3],[1381,4],[1463,3]]},"2342":{"position":[[23,3],[66,4],[253,3],[625,3],[825,3],[1019,5],[1097,3]]},"2352":{"position":[[35,4]]},"2354":{"position":[[636,5],[1227,3],[1662,4],[2298,3],[2718,4],[2808,4],[3264,5],[4045,5],[5645,5],[5755,4],[5867,5]]},"2356":{"position":[[898,4],[984,4],[1593,5]]},"2358":{"position":[[864,4],[954,4],[1124,3]]},"2360":{"position":[[585,3],[688,5],[737,5],[2188,4],[2278,4],[2803,6],[3029,5]]},"2362":{"position":[[2220,4],[2310,4]]},"2364":{"position":[[810,4],[1255,3],[2299,4],[2385,4],[3060,5]]},"2366":{"position":[[168,3]]},"2368":{"position":[[204,3]]},"2370":{"position":[[656,3],[1263,3]]},"2372":{"position":[[218,5],[281,6],[386,6]]},"2376":{"position":[[147,4]]},"2378":{"position":[[103,3],[288,3],[810,6],[911,3],[1075,6],[1133,5],[1246,3],[1330,5]]},"2380":{"position":[[271,4],[328,3],[398,3],[482,5],[710,3]]},"2382":{"position":[[155,3]]},"2384":{"position":[[5,5],[1153,4],[1671,3],[1912,5]]},"2386":{"position":[[77,3],[320,4],[366,5]]},"2388":{"position":[[741,4],[874,4],[1019,4],[1271,5]]},"2390":{"position":[[387,3],[459,3],[493,4],[938,5]]},"2392":{"position":[[119,5],[366,3],[1497,5],[1566,3],[1882,5]]},"2400":{"position":[[1083,5],[2154,5],[2703,6]]},"2402":{"position":[[725,3],[870,4],[1431,3],[1504,5],[1815,4]]},"2404":{"position":[[895,5]]},"2408":{"position":[[112,3]]},"2410":{"position":[[130,5]]},"2412":{"position":[[340,3],[480,3],[1058,5]]},"2416":{"position":[[97,3]]},"2418":{"position":[[138,5]]},"2420":{"position":[[173,4]]},"2422":{"position":[[231,4]]},"2424":{"position":[[477,4],[1180,5]]},"2428":{"position":[[319,5],[668,5]]},"2430":{"position":[[273,5]]},"2434":{"position":[[41,4]]},"2440":{"position":[[166,3]]},"2442":{"position":[[183,6],[515,5],[692,5]]},"2446":{"position":[[83,5]]},"2462":{"position":[[59,5]]},"2470":{"position":[[407,3]]},"2472":{"position":[[23,4]]},"2474":{"position":[[20,5]]},"2480":{"position":[[380,5],[427,5],[475,4]]},"2482":{"position":[[535,5],[756,5],[1008,5],[1139,3]]},"2486":{"position":[[279,3],[354,5],[560,5],[648,5]]},"2488":{"position":[[131,3],[230,5],[336,5]]},"2492":{"position":[[102,6]]},"2494":{"position":[[252,5]]},"2498":{"position":[[51,3]]},"2500":{"position":[[969,5]]},"2504":{"position":[[297,5],[414,5],[605,5],[1471,6]]},"2506":{"position":[[38,3],[472,3]]},"2508":{"position":[[92,5],[427,4]]},"2512":{"position":[[191,6],[207,3],[1446,5],[2302,3]]},"2514":{"position":[[108,5]]},"2516":{"position":[[566,5]]},"2518":{"position":[[156,5],[453,5]]},"2520":{"position":[[15,3]]},"2526":{"position":[[813,5]]},"2530":{"position":[[43,5],[543,5],[1232,4],[1396,3],[1621,5],[1720,3]]},"2532":{"position":[[555,5]]},"2536":{"position":[[336,3],[366,3]]},"2538":{"position":[[976,5]]},"2542":{"position":[[203,3]]},"2544":{"position":[[141,5]]},"2546":{"position":[[335,4]]},"2554":{"position":[[313,5]]},"2558":{"position":[[387,5],[685,3],[784,3],[950,5],[1035,5]]},"2562":{"position":[[582,5],[731,5]]},"2568":{"position":[[164,5]]},"2570":{"position":[[265,6]]},"2578":{"position":[[7,5],[242,5],[396,5],[769,5]]},"2586":{"position":[[494,5]]},"2606":{"position":[[690,3]]},"2622":{"position":[[261,4],[5102,5]]},"2628":{"position":[[1505,3]]},"2632":{"position":[[4655,5]]},"2634":{"position":[[549,3]]},"2638":{"position":[[163,4],[624,4],[696,6]]},"2644":{"position":[[1607,3]]},"2646":{"position":[[13,5],[158,3],[461,3]]},"2648":{"position":[[367,4]]},"2650":{"position":[[311,3]]},"2652":{"position":[[309,3]]},"2666":{"position":[[1863,5]]},"2668":{"position":[[164,5]]},"2670":{"position":[[142,5],[605,5],[749,5]]},"2672":{"position":[[17,3],[243,4]]},"2680":{"position":[[376,5]]},"2682":{"position":[[177,5]]},"2684":{"position":[[229,5],[429,5]]},"2686":{"position":[[462,5],[1242,5],[3255,5],[3332,3],[4381,3],[6006,5],[6180,5],[7742,3],[9939,5],[10411,7]]},"2690":{"position":[[60,3],[469,5]]},"2692":{"position":[[37,3],[951,5]]},"2702":{"position":[[749,3]]},"2704":{"position":[[58,3]]},"2706":{"position":[[176,3]]},"2708":{"position":[[531,3],[735,5],[2713,5],[2752,3]]},"2714":{"position":[[243,3]]},"2718":{"position":[[295,4]]},"2720":{"position":[[308,4]]},"2724":{"position":[[65,3],[281,3]]},"2726":{"position":[[15,4]]},"2728":{"position":[[8,3],[78,3]]},"2736":{"position":[[468,5]]},"2764":{"position":[[416,4]]},"2768":{"position":[[532,4],[1164,3]]},"2770":{"position":[[65,5],[345,5]]},"2774":{"position":[[23,5]]},"2776":{"position":[[20,6]]},"2780":{"position":[[126,4]]},"2782":{"position":[[1091,4]]},"2784":{"position":[[307,5],[811,5],[1116,5],[1235,5]]},"2788":{"position":[[669,5],[768,3],[823,3]]},"2804":{"position":[[574,4]]},"2806":{"position":[[175,6],[735,6]]},"2810":{"position":[[65,5]]},"2816":{"position":[[204,3]]},"2823":{"position":[[60,6],[335,5],[875,3]]},"2826":{"position":[[104,6],[203,5],[832,4],[980,3]]},"2830":{"position":[[187,4],[822,4]]},"2832":{"position":[[154,4]]},"2834":{"position":[[871,4]]},"2836":{"position":[[1080,3]]},"2844":{"position":[[271,5]]},"2846":{"position":[[1141,3],[1299,6]]},"2848":{"position":[[358,5]]},"2850":{"position":[[82,5],[312,5]]},"2852":{"position":[[910,5]]},"2854":{"position":[[57,3]]},"2856":{"position":[[30,4],[225,4],[858,3],[1026,5]]},"2862":{"position":[[388,5],[645,6],[850,6]]},"2873":{"position":[[1274,5]]},"2877":{"position":[[1260,5]]},"2883":{"position":[[452,4],[530,5]]},"2887":{"position":[[168,3],[427,5],[483,5],[775,3]]},"2889":{"position":[[508,6],[1045,6]]},"2904":{"position":[[435,3]]},"2912":{"position":[[15,3],[240,6]]},"2918":{"position":[[161,6]]},"2920":{"position":[[46,5],[209,5]]},"2922":{"position":[[960,5]]},"2930":{"position":[[247,3],[273,3],[1291,5],[1447,4],[1458,3],[1658,5],[1700,3]]},"2932":{"position":[[28,3],[374,5],[745,3],[795,3],[999,3],[1146,3],[1541,7]]},"2934":{"position":[[233,3],[450,3]]},"2936":{"position":[[376,3],[597,3]]},"2938":{"position":[[71,5]]},"2940":{"position":[[78,5]]},"2942":{"position":[[104,5]]},"2946":{"position":[[32,4]]},"2956":{"position":[[378,5]]},"2964":{"position":[[110,6]]},"2966":{"position":[[84,6]]},"2970":{"position":[[44,3],[687,3],[792,3]]},"2972":{"position":[[69,5]]},"2974":{"position":[[159,4],[255,3],[636,4]]},"2978":{"position":[[332,3]]},"2984":{"position":[[135,4]]},"2990":{"position":[[240,5],[329,4],[489,5]]},"3006":{"position":[[181,3],[459,6]]},"3008":{"position":[[96,4]]},"3012":{"position":[[268,4]]},"3016":{"position":[[31,4]]},"3018":{"position":[[217,4]]},"3026":{"position":[[256,5]]},"3028":{"position":[[244,5]]},"3032":{"position":[[113,5]]},"3034":{"position":[[357,5]]},"3036":{"position":[[397,4]]},"3040":{"position":[[30,4],[256,4],[352,5]]},"3046":{"position":[[346,3],[695,3],[733,5]]},"3052":{"position":[[8,3],[107,6]]},"3054":{"position":[[377,5],[455,3],[564,5],[644,5],[1059,4]]},"3056":{"position":[[63,5],[98,3],[872,3],[949,5],[1045,4],[1221,5]]},"3060":{"position":[[557,5],[995,5],[1260,5]]},"3062":{"position":[[27,4]]},"3064":{"position":[[52,4],[841,4]]},"3066":{"position":[[464,3],[1057,5]]},"3072":{"position":[[93,7],[921,3],[1713,3],[1776,3],[1832,3]]},"3074":{"position":[[36,5]]},"3076":{"position":[[1502,3]]},"3078":{"position":[[517,5],[915,5]]},"3082":{"position":[[813,3]]},"3084":{"position":[[1364,5],[1388,5]]},"3086":{"position":[[111,5],[332,4],[392,7],[460,5],[765,4],[827,4],[915,3]]},"3088":{"position":[[631,5],[683,4],[826,4]]},"3090":{"position":[[460,4],[601,4]]},"3096":{"position":[[588,5],[663,5],[802,6]]},"3098":{"position":[[561,5],[636,5]]},"3114":{"position":[[62,5]]},"3116":{"position":[[417,3],[462,6],[970,5],[1047,4]]},"3126":{"position":[[97,5]]},"3128":{"position":[[93,5]]},"3130":{"position":[[101,5]]},"3132":{"position":[[111,5]]},"3138":{"position":[[681,5],[839,5],[1374,4]]},"3140":{"position":[[1222,6],[1335,6]]},"3142":{"position":[[711,4],[3173,6],[3459,3],[3926,4],[4849,5]]},"3146":{"position":[[218,6],[528,5]]},"3148":{"position":[[889,3]]},"3150":{"position":[[283,5],[895,3]]},"3156":{"position":[[461,3],[549,5]]},"3162":{"position":[[153,6]]},"3166":{"position":[[441,6]]},"3168":{"position":[[502,6]]},"3170":{"position":[[141,5],[290,6]]},"3172":{"position":[[149,5],[298,6]]},"3176":{"position":[[46,5]]},"3178":{"position":[[133,5],[171,5],[247,5]]},"3180":{"position":[[337,5],[426,3],[501,3],[928,3]]},"3186":{"position":[[228,4],[316,5],[349,3],[385,4]]},"3188":{"position":[[351,6]]},"3190":{"position":[[204,3]]},"3194":{"position":[[176,4],[1132,5]]},"3200":{"position":[[10,3]]},"3212":{"position":[[359,5]]},"3218":{"position":[[244,3]]},"3220":{"position":[[114,3],[847,5],[981,5],[1052,5],[1128,5]]},"3226":{"position":[[210,3]]},"3234":{"position":[[222,6]]},"3236":{"position":[[0,3]]},"3238":{"position":[[0,3]]},"3240":{"position":[[267,4],[322,4],[410,4],[468,4],[539,4],[879,5],[1012,3]]},"3246":{"position":[[378,5]]},"3249":{"position":[[264,6],[389,3]]},"3251":{"position":[[271,4]]},"3253":{"position":[[25,5],[147,5],[344,6],[413,5]]},"3255":{"position":[[25,5],[263,3]]},"3257":{"position":[[40,5],[142,3]]},"3261":{"position":[[463,3],[503,4]]},"3265":{"position":[[0,4],[181,5]]},"3267":{"position":[[126,5],[330,4],[390,7],[458,5],[763,4]]},"3269":{"position":[[57,4],[194,3]]},"3271":{"position":[[297,4],[364,3],[451,5],[658,5],[734,4],[1140,5],[1339,4],[1399,7],[1467,5],[1719,4]]},"3273":{"position":[[500,3],[665,3],[779,4],[863,3],[941,4]]},"3275":{"position":[[48,5],[681,3],[865,3],[1391,3],[1587,3]]},"3277":{"position":[[578,5],[654,3]]},"3279":{"position":[[36,3],[503,3],[686,3],[818,4],[902,3],[998,4]]},"3281":{"position":[[23,3],[243,3],[310,3]]},"3283":{"position":[[531,3],[884,3],[1041,5],[1187,5],[1238,3]]},"3285":{"position":[[68,5],[1144,4],[1264,3]]},"3287":{"position":[[5,5],[238,4]]},"3289":{"position":[[654,3],[1759,3]]},"3295":{"position":[[169,4]]},"3301":{"position":[[317,3]]},"3307":{"position":[[26,3]]},"3309":{"position":[[45,5]]},"3311":{"position":[[45,5]]},"3320":{"position":[[317,3]]},"3324":{"position":[[208,3],[701,6],[1253,3],[1885,6],[2540,3],[3240,6],[3538,3],[4724,3],[5456,3]]},"3327":{"position":[[961,3]]},"3329":{"position":[[26,5],[183,4],[355,5]]},"3335":{"position":[[741,6],[777,3],[1144,3]]},"3345":{"position":[[744,4]]},"3347":{"position":[[173,3]]},"3349":{"position":[[54,6],[299,3]]},"3355":{"position":[[391,5],[983,4],[1669,6]]},"3363":{"position":[[0,5],[324,5],[595,3],[703,6],[917,5],[1332,3]]},"3365":{"position":[[68,5],[641,3],[2309,4],[2691,3],[2810,5],[2973,3],[3015,3],[3623,3],[4095,5],[4146,3]]},"3367":{"position":[[189,4],[206,5],[1168,3],[5011,5]]},"3369":{"position":[[281,5],[492,4],[570,3],[647,3]]},"3377":{"position":[[76,4],[409,4]]},"3381":{"position":[[1020,7]]},"3383":{"position":[[246,4],[423,5],[606,5],[843,4],[1187,4],[1548,4],[1782,5]]},"3397":{"position":[[31,5],[392,4],[610,3],[949,3],[1211,5]]},"3411":{"position":[[1151,5]]},"3413":{"position":[[35,4]]},"3415":{"position":[[636,5],[1227,3],[1662,4],[2298,3],[2718,4],[2808,4],[3264,5],[4074,5],[5674,5],[5784,4],[5896,5]]},"3417":{"position":[[898,4],[984,4],[1593,5]]},"3419":{"position":[[911,4],[1001,4],[1171,3]]},"3421":{"position":[[715,3],[1991,4],[2081,4],[2606,6],[2832,5]]},"3423":{"position":[[2220,4],[2310,4]]},"3425":{"position":[[783,4],[1228,3],[2272,4],[2358,4],[3052,5]]},"3427":{"position":[[168,3]]},"3429":{"position":[[204,3]]},"3431":{"position":[[656,3],[1263,3]]},"3433":{"position":[[218,5],[281,6],[386,6],[494,6],[539,4]]},"3437":{"position":[[147,4]]},"3439":{"position":[[103,3],[288,3],[810,6],[911,3],[1075,6],[1133,5],[1246,3],[1330,5]]},"3441":{"position":[[271,4],[328,3],[398,3],[482,5],[710,3]]},"3443":{"position":[[155,3]]},"3445":{"position":[[5,5],[1153,4],[1883,3],[2124,5],[2337,4]]},"3447":{"position":[[77,3],[320,4],[366,5]]},"3449":{"position":[[741,4],[874,4],[1019,4],[1271,5]]},"3451":{"position":[[387,3],[459,3],[493,4],[938,5]]},"3456":{"position":[[332,4],[502,3]]},"3462":{"position":[[220,3]]},"3464":{"position":[[47,3],[224,3],[773,5]]},"3466":{"position":[[496,5],[731,5]]},"3470":{"position":[[57,5]]},"3472":{"position":[[26,3],[497,3],[1061,5],[1108,4]]},"3474":{"position":[[95,3]]},"3476":{"position":[[100,3]]},"3481":{"position":[[150,4]]},"3486":{"position":[[311,4],[482,3]]},"3488":{"position":[[88,5],[138,5]]},"3492":{"position":[[1335,5]]},"3496":{"position":[[465,3],[512,3]]},"3498":{"position":[[553,3],[592,3]]},"3502":{"position":[[142,3],[1931,3],[2067,5]]},"3504":{"position":[[792,3],[928,5]]},"3506":{"position":[[339,4]]},"3512":{"position":[[50,6],[92,6]]},"3522":{"position":[[296,5],[907,4]]},"3526":{"position":[[158,6]]},"3530":{"position":[[90,5],[149,3],[226,6],[583,5],[710,5],[986,3]]},"3532":{"position":[[327,4]]},"3538":{"position":[[486,3],[1556,5],[1619,5],[1710,4]]},"3563":{"position":[[124,3]]},"3571":{"position":[[119,4],[233,3]]},"3578":{"position":[[53,5]]},"3584":{"position":[[728,3],[831,6],[909,6],[1045,5]]},"3591":{"position":[[88,5],[138,5]]},"3597":{"position":[[169,5]]},"3599":{"position":[[183,4],[248,4]]},"3603":{"position":[[11,4],[113,3]]},"3610":{"position":[[53,5],[1469,6],[1501,5]]},"3612":{"position":[[146,5],[393,3],[1524,5],[1593,3],[1909,5]]},"3625":{"position":[[369,4],[854,3],[1029,3],[1381,4],[1463,3]]},"3627":{"position":[[23,3],[66,4],[253,3],[625,3],[825,3],[1019,5],[1097,3]]},"3629":{"position":[[87,5],[199,4],[1029,3],[1678,4],[1724,5],[1747,5]]},"3633":{"position":[[261,4],[5102,5]]},"3639":{"position":[[1525,3]]},"3643":{"position":[[4655,5]]},"3645":{"position":[[549,3]]},"3649":{"position":[[163,4],[624,4],[696,6]]},"3655":{"position":[[1775,3]]},"3657":{"position":[[13,5],[158,3],[461,3]]},"3659":{"position":[[367,4]]},"3661":{"position":[[311,3]]},"3663":{"position":[[309,3]]}}}],["usabl",{"_index":5326,"t":{"2622":{"position":[[5650,6]]},"3633":{"position":[[5650,6]]}}}],["usag",{"_index":131,"t":{"2":{"position":[[2000,5],[2075,5],[2289,5],[2862,6],[2926,5],[3573,7]]},"8":{"position":[[750,5]]},"12":{"position":[[622,5],[1231,5]]},"16":{"position":[[4268,5],[4425,5]]},"34":{"position":[[1688,6]]},"50":{"position":[[1986,5]]},"52":{"position":[[990,6]]},"86":{"position":[[1077,5],[1109,5]]},"116":{"position":[[298,6]]},"128":{"position":[[435,6],[1234,5]]},"134":{"position":[[1095,6]]},"144":{"position":[[1884,5]]},"258":{"position":[[611,5]]},"347":{"position":[[21,5]]},"399":{"position":[[309,5],[549,5]]},"429":{"position":[[159,5]]},"469":{"position":[[219,5],[470,5]]},"477":{"position":[[5317,6]]},"479":{"position":[[721,5],[962,5],[2735,5],[3064,5],[3182,5],[3461,5],[6484,5],[6636,5],[6824,5]]},"483":{"position":[[570,5],[1424,6]]},"553":{"position":[[739,5],[870,5]]},"555":{"position":[[152,5]]},"559":{"position":[[95,5]]},"567":{"position":[[158,5]]},"569":{"position":[[162,5]]},"571":{"position":[[160,5]]},"573":{"position":[[162,5]]},"945":{"position":[[40,5],[269,6],[298,5],[421,5],[658,5]]},"951":{"position":[[624,5]]},"1071":{"position":[[0,5]]},"1154":{"position":[[181,5]]},"1156":{"position":[[182,5]]},"1158":{"position":[[112,5]]},"1160":{"position":[[491,5]]},"1197":{"position":[[257,5]]},"1223":{"position":[[309,5],[549,5]]},"1255":{"position":[[159,5]]},"1279":{"position":[[21,5]]},"1371":{"position":[[838,5]]},"1519":{"position":[[227,5]]},"1527":{"position":[[1114,5],[1322,5]]},"1529":{"position":[[152,5]]},"1535":{"position":[[158,5]]},"1537":{"position":[[162,5]]},"1539":{"position":[[160,5]]},"1541":{"position":[[162,5]]},"1593":{"position":[[95,5]]},"1692":{"position":[[1240,6]]},"1865":{"position":[[699,7]]},"1867":{"position":[[672,7]]},"1921":{"position":[[624,5]]},"2066":{"position":[[40,5],[269,6],[298,5],[421,5],[658,5]]},"2098":{"position":[[257,5]]},"2134":{"position":[[0,5]]},"2191":{"position":[[1006,5]]},"2334":{"position":[[181,5]]},"2336":{"position":[[182,5]]},"2338":{"position":[[112,5]]},"2340":{"position":[[491,5]]},"2392":{"position":[[273,6],[2736,5]]},"2402":{"position":[[21,5]]},"2472":{"position":[[309,5],[549,5]]},"2504":{"position":[[159,5]]},"2542":{"position":[[838,5]]},"2702":{"position":[[227,5]]},"2708":{"position":[[399,5]]},"2740":{"position":[[158,5]]},"2742":{"position":[[162,5]]},"2744":{"position":[[160,5]]},"2746":{"position":[[162,5]]},"2760":{"position":[[1212,5],[1502,5]]},"2764":{"position":[[152,5]]},"2776":{"position":[[95,5]]},"2806":{"position":[[168,6],[460,6],[728,6]]},"2856":{"position":[[1240,6]]},"2889":{"position":[[245,6],[501,6],[776,6],[1038,6]]},"3096":{"position":[[699,7]]},"3098":{"position":[[672,7]]},"3148":{"position":[[624,5]]},"3220":{"position":[[1215,6]]},"3261":{"position":[[40,5],[269,6],[298,5],[421,5],[658,5]]},"3283":{"position":[[257,5]]},"3324":{"position":[[201,6],[453,6],[694,6],[739,5],[873,6],[1071,6],[1246,6],[1458,6],[1668,6],[1878,6],[2084,6],[2322,6],[2533,6],[2781,6],[2998,6],[3233,6],[3531,6],[3562,5],[3832,6],[4122,6],[4171,5],[4416,6],[4717,6],[4953,6],[5203,6],[5449,6],[5679,6],[5900,6],[6120,6]]},"3337":{"position":[[0,5]]},"3363":{"position":[[52,5]]},"3466":{"position":[[1006,5]]},"3612":{"position":[[300,6],[2935,5]]},"3619":{"position":[[181,5]]},"3621":{"position":[[182,5]]},"3623":{"position":[[112,5]]},"3625":{"position":[[491,5]]}}}],["usage_stats_dis",{"_index":4102,"t":{"945":{"position":[[674,19],[716,22]]},"2066":{"position":[[674,19],[716,22]]},"3261":{"position":[[674,19],[716,22]]}}}],["usd",{"_index":2742,"t":{"280":{"position":[[351,3]]}}}],["use_client_protocol_v1_by_default",{"_index":4468,"t":{"1359":{"position":[[819,34],[1305,33],[1439,33]]},"1361":{"position":[[187,33]]},"2530":{"position":[[819,34],[1305,33],[1439,33]]},"2532":{"position":[[187,33]]},"2580":{"position":[[140,33]]}}}],["use_redis_from_engin",{"_index":3636,"t":{"563":{"position":[[1197,21],[1405,24]]},"632":{"position":[[859,21]]},"652":{"position":[[722,21]]},"694":{"position":[[420,21],[630,24]]},"1631":{"position":[[859,21]]},"1647":{"position":[[722,21]]},"1670":{"position":[[1901,21],[2127,24]]},"1737":{"position":[[410,21],[612,24]]},"2818":{"position":[[859,21]]},"2834":{"position":[[1907,21],[2133,24]]},"2899":{"position":[[722,21]]},"2926":{"position":[[410,21],[612,24]]}}}],["use_singleflight",{"_index":3740,"t":{"640":{"position":[[66,16],[1005,19]]},"1591":{"position":[[66,16],[1287,19]]},"2784":{"position":[[66,16],[1287,19]]}}}],["use_unlimited_history_by_default",{"_index":3645,"t":{"589":{"position":[[363,32]]},"1375":{"position":[[275,32]]},"2546":{"position":[[275,32]]}}}],["useeffect",{"_index":2432,"t":{"216":{"position":[[1439,9],[2348,9],[2381,12],[3363,12],[3866,12]]}}}],["usekeycloak",{"_index":2436,"t":{"216":{"position":[[1555,11],[1664,13]]}}}],["useless",{"_index":4405,"t":{"1197":{"position":[[989,7]]},"2098":{"position":[[989,7]]},"3283":{"position":[[989,7]]}}}],["user",{"_index":527,"t":{"14":{"position":[[29,5],[148,5],[255,5]]},"16":{"position":[[600,5],[1651,4],[1695,4],[2227,4]]},"18":{"position":[[384,5],[884,6]]},"26":{"position":[[860,4],[914,4]]},"28":{"position":[[423,5]]},"32":{"position":[[741,5],[1080,4]]},"38":{"position":[[599,5],[2034,5]]},"40":{"position":[[78,5],[371,5]]},"58":{"position":[[713,6]]},"64":{"position":[[65,4],[357,4],[406,4]]},"66":{"position":[[945,5]]},"74":{"position":[[1919,4]]},"76":{"position":[[446,4],[1909,5]]},"78":{"position":[[476,5]]},"82":{"position":[[1815,5],[2323,6]]},"86":{"position":[[173,4],[865,5]]},"90":{"position":[[578,5]]},"110":{"position":[[133,4]]},"118":{"position":[[559,5],[713,4]]},"122":{"position":[[1122,5],[1511,5]]},"124":{"position":[[116,5],[466,5],[1065,5],[2912,5],[3015,5],[4283,5]]},"130":{"position":[[630,5]]},"138":{"position":[[132,5],[189,4],[303,4]]},"154":{"position":[[139,5]]},"156":{"position":[[833,4]]},"158":{"position":[[1066,4]]},"166":{"position":[[149,4],[378,4],[426,6],[447,5]]},"170":{"position":[[5,4],[757,4]]},"172":{"position":[[22,4],[98,4],[378,6],[709,5],[787,4],[806,4],[842,4],[934,4],[1067,4],[1174,4],[1394,4]]},"174":{"position":[[92,5],[210,5],[528,6],[538,6],[576,5]]},"176":{"position":[[185,4],[255,5],[500,5],[2011,4],[2280,4],[2310,4]]},"178":{"position":[[305,4],[313,4]]},"184":{"position":[[832,6]]},"196":{"position":[[1827,4],[1907,4],[1968,4]]},"198":{"position":[[453,4],[461,4]]},"200":{"position":[[942,4],[1169,7],[1187,5],[1333,5],[1662,4],[2008,4],[2028,5],[2122,4],[2150,5],[2230,4]]},"202":{"position":[[92,4],[282,5],[329,5],[630,6],[700,4],[1202,5],[1282,6],[1873,4],[1893,4]]},"208":{"position":[[9,4],[540,5]]},"212":{"position":[[472,4]]},"214":{"position":[[913,4]]},"216":{"position":[[3762,4],[4226,4]]},"218":{"position":[[276,5]]},"224":{"position":[[29,4]]},"242":{"position":[[194,4]]},"244":{"position":[[245,4],[516,4]]},"246":{"position":[[1106,5],[2270,5],[2328,4]]},"250":{"position":[[1726,5]]},"252":{"position":[[234,5]]},"260":{"position":[[819,4]]},"262":{"position":[[167,4],[615,5],[740,4],[1140,5],[1598,4]]},"268":{"position":[[46,4],[468,4],[530,4]]},"270":{"position":[[2737,4]]},"272":{"position":[[626,4],[709,4],[826,4],[868,4],[892,4],[1062,5],[1198,4],[1293,4]]},"276":{"position":[[193,5]]},"278":{"position":[[333,4]]},"280":{"position":[[84,5],[1212,5],[1392,4],[3071,5]]},"285":{"position":[[153,4],[194,4]]},"287":{"position":[[88,4],[231,4],[609,5],[737,4],[993,5],[1163,4]]},"289":{"position":[[318,4],[658,4],[823,4],[839,4],[890,4],[952,5]]},"291":{"position":[[781,4],[869,4],[3118,4]]},"293":{"position":[[1385,4]]},"295":{"position":[[1316,4]]},"299":{"position":[[79,4],[247,4],[466,5],[515,4],[568,4]]},"305":{"position":[[98,4]]},"311":{"position":[[265,4],[552,5],[720,4]]},"315":{"position":[[521,5],[932,4],[1198,6],[1241,6]]},"317":{"position":[[188,5],[443,5]]},"321":{"position":[[531,5],[2329,5]]},"327":{"position":[[172,5]]},"329":{"position":[[335,6]]},"335":{"position":[[411,4]]},"341":{"position":[[95,4],[609,4]]},"347":{"position":[[272,5]]},"353":{"position":[[57,5],[156,4],[397,4]]},"361":{"position":[[413,5]]},"375":{"position":[[33,5]]},"381":{"position":[[166,4]]},"393":{"position":[[176,4],[508,4],[657,5],[752,4]]},"409":{"position":[[488,4],[705,4],[944,4],[1049,4],[1187,6]]},"415":{"position":[[170,5],[280,5],[399,5],[502,6]]},"429":{"position":[[343,4],[1579,5]]},"431":{"position":[[93,4],[226,4],[348,4],[368,4],[634,4],[729,5]]},"437":{"position":[[979,4],[1357,4],[1621,4]]},"441":{"position":[[385,6],[744,4]]},"463":{"position":[[404,5],[533,6]]},"465":{"position":[[1052,6]]},"483":{"position":[[524,5]]},"491":{"position":[[291,6]]},"493":{"position":[[9,5],[703,4]]},"497":{"position":[[45,4],[229,5],[418,4],[554,6]]},"527":{"position":[[126,6],[508,4]]},"553":{"position":[[91,4],[189,4],[292,4],[355,6],[438,4],[483,4],[526,4],[569,5]]},"563":{"position":[[100,4],[133,5],[909,5]]},"608":{"position":[[202,5],[890,5]]},"616":{"position":[[37,5]]},"624":{"position":[[262,7]]},"630":{"position":[[59,4]]},"636":{"position":[[257,5]]},"638":{"position":[[33,4],[117,4],[460,8],[572,4],[643,4],[659,4],[1214,4]]},"648":{"position":[[30,4],[118,4],[416,5],[464,5],[650,4]]},"650":{"position":[[0,4]]},"652":{"position":[[491,5]]},"656":{"position":[[163,8],[241,4],[305,4],[321,4],[384,4],[541,5],[661,4]]},"658":{"position":[[165,8],[220,4],[284,4],[300,4],[327,4]]},"672":{"position":[[429,5]]},"674":{"position":[[205,6],[984,6]]},"676":{"position":[[358,6],[1347,6]]},"678":{"position":[[12,5],[451,5],[555,5],[628,5],[1086,5]]},"680":{"position":[[484,5]]},"686":{"position":[[145,4]]},"688":{"position":[[405,9],[460,4],[531,5],[566,5],[600,4]]},"690":{"position":[[92,6],[309,9],[777,4],[856,4],[959,4],[1049,4],[1120,5],[1155,5],[1183,4],[1296,4],[1379,4],[1394,4]]},"692":{"position":[[21,4],[270,9],[325,4],[396,5],[431,5],[465,4]]},"694":{"position":[[160,4],[332,4],[852,5]]},"754":{"position":[[164,4]]},"774":{"position":[[81,4],[104,4],[247,4]]},"776":{"position":[[683,5],[800,5],[1064,4]]},"790":{"position":[[258,6],[271,5]]},"792":{"position":[[336,4],[583,6],[596,5]]},"794":{"position":[[353,4]]},"798":{"position":[[1463,4],[1678,4],[1818,5],[1877,4]]},"802":{"position":[[491,6]]},"806":{"position":[[13,4]]},"814":{"position":[[346,4]]},"816":{"position":[[442,4],[588,5]]},"818":{"position":[[561,4],[616,6],[645,4]]},"820":{"position":[[9,4],[84,5],[104,4],[230,4],[294,4],[401,4],[584,4],[703,4],[844,4],[928,4],[948,4],[1065,6],[1095,4],[1172,6],[1250,5]]},"828":{"position":[[67,4],[91,4],[110,4],[182,5],[197,4],[217,4],[460,4],[540,5]]},"830":{"position":[[216,4]]},"834":{"position":[[249,4]]},"846":{"position":[[184,4]]},"858":{"position":[[1865,4],[2005,4]]},"868":{"position":[[133,4]]},"906":{"position":[[52,4],[69,4],[302,4],[441,4]]},"910":{"position":[[177,4]]},"935":{"position":[[128,4]]},"989":{"position":[[356,4]]},"1013":{"position":[[382,5],[610,4]]},"1017":{"position":[[812,5],[1319,4],[2008,8],[2089,5],[3478,4],[3493,4],[4894,4],[4949,4],[5170,7]]},"1019":{"position":[[1016,4],[1044,4]]},"1021":{"position":[[986,4],[1014,4]]},"1023":{"position":[[371,4],[471,5],[508,4],[890,4],[2310,4],[2338,4]]},"1025":{"position":[[2342,4],[2370,4]]},"1039":{"position":[[282,5]]},"1069":{"position":[[43,4],[240,4],[295,4],[327,5],[344,4],[433,4],[1077,4],[1214,5]]},"1071":{"position":[[131,4],[328,4],[368,4],[430,4],[908,4]]},"1147":{"position":[[258,6],[523,4]]},"1177":{"position":[[209,5],[417,5],[479,4],[1001,4]]},"1185":{"position":[[450,4],[500,4]]},"1189":{"position":[[253,4],[1332,4],[1382,4]]},"1193":{"position":[[598,4]]},"1205":{"position":[[602,5]]},"1211":{"position":[[95,4],[609,4]]},"1233":{"position":[[488,4],[705,4],[944,4],[1049,4],[1187,6]]},"1239":{"position":[[170,5],[280,5],[399,5],[502,6]]},"1255":{"position":[[343,4],[1579,5]]},"1257":{"position":[[93,4],[226,4],[348,4],[368,4],[634,4],[729,5]]},"1263":{"position":[[983,4],[1361,4],[1625,4]]},"1267":{"position":[[385,6],[744,4]]},"1279":{"position":[[272,5]]},"1285":{"position":[[57,5],[156,4],[397,4]]},"1315":{"position":[[404,5],[533,6]]},"1319":{"position":[[429,5]]},"1333":{"position":[[33,5]]},"1359":{"position":[[587,4],[683,4]]},"1367":{"position":[[51,4],[333,4],[432,4],[523,4]]},"1369":{"position":[[0,4]]},"1385":{"position":[[358,6]]},"1387":{"position":[[9,5],[704,4]]},"1391":{"position":[[42,4],[226,5],[415,4],[550,6]]},"1415":{"position":[[477,5]]},"1423":{"position":[[1405,4],[1664,5]]},"1429":{"position":[[257,4],[451,4]]},"1431":{"position":[[178,5]]},"1461":{"position":[[29,4],[117,4],[133,4],[202,4],[443,5],[493,4],[592,5]]},"1463":{"position":[[33,4],[120,7],[128,5],[212,4],[228,4],[301,4],[360,5],[448,5]]},"1465":{"position":[[34,4],[84,7],[92,5],[175,4],[191,4],[263,5],[351,5]]},"1467":{"position":[[26,4],[166,4],[182,4],[239,5],[324,5]]},"1469":{"position":[[622,7],[730,7],[1116,4],[1131,4]]},"1471":{"position":[[106,5],[122,4],[779,5]]},"1497":{"position":[[135,4],[307,4],[534,4],[574,4],[861,8],[1068,4],[1191,4],[1271,4],[1634,4]]},"1505":{"position":[[105,5],[5718,5],[6110,4],[6218,4],[6280,4]]},"1513":{"position":[[233,6]]},"1527":{"position":[[100,4],[170,4],[483,4],[557,6],[634,4],[677,5]]},"1571":{"position":[[1354,4]]},"1575":{"position":[[648,4],[821,4],[877,4]]},"1585":{"position":[[192,5],[323,4]]},"1589":{"position":[[275,4],[511,4],[594,4]]},"1597":{"position":[[59,4],[115,4],[207,4],[1264,8],[1617,4]]},"1599":{"position":[[142,4],[172,4],[226,5],[343,4],[713,4],[736,4]]},"1603":{"position":[[518,5]]},"1605":{"position":[[196,6],[895,6]]},"1607":{"position":[[354,6],[1033,6]]},"1609":{"position":[[349,6],[1329,6]]},"1611":{"position":[[402,6],[1256,6]]},"1613":{"position":[[523,6],[1497,6]]},"1615":{"position":[[12,5],[451,5],[555,5],[628,5],[1080,5]]},"1617":{"position":[[614,5]]},"1623":{"position":[[262,7]]},"1629":{"position":[[59,4]]},"1636":{"position":[[257,5]]},"1639":{"position":[[33,4],[117,4],[460,8],[643,4],[659,4]]},"1643":{"position":[[30,4],[118,4],[416,5],[464,5],[650,4]]},"1645":{"position":[[0,4]]},"1647":{"position":[[491,5]]},"1652":{"position":[[163,8],[305,4],[321,4],[384,4],[541,5]]},"1654":{"position":[[165,8],[284,4],[300,4]]},"1666":{"position":[[1898,5]]},"1668":{"position":[[54,4],[281,4],[374,5],[402,4],[439,5]]},"1670":{"position":[[67,4],[197,4],[745,5],[773,4],[810,5],[982,4]]},"1672":{"position":[[163,5]]},"1682":{"position":[[585,6],[800,4],[847,4],[939,4],[981,4],[1005,4],[1135,5],[1271,4],[1366,4]]},"1688":{"position":[[844,4],[887,4]]},"1703":{"position":[[391,4],[406,4],[590,4]]},"1705":{"position":[[53,4],[98,4],[222,5],[254,5],[380,4],[651,4],[667,4]]},"1707":{"position":[[58,4],[246,5],[288,4]]},"1709":{"position":[[431,5],[471,5],[1278,4],[1294,4]]},"1713":{"position":[[455,5]]},"1715":{"position":[[30,6],[43,4],[89,4],[161,5],[226,4],[242,4]]},"1717":{"position":[[5,4],[85,5],[118,5],[664,4],[680,4]]},"1729":{"position":[[145,4]]},"1731":{"position":[[405,9],[460,4],[531,5],[566,5],[600,4]]},"1733":{"position":[[92,6],[309,9],[777,4],[856,4],[959,4],[1049,4],[1120,5],[1155,5],[1183,4],[1296,4],[1379,4],[1394,4]]},"1735":{"position":[[21,4],[270,9],[325,4],[396,5],[431,5],[465,4]]},"1737":{"position":[[16,4],[150,4],[322,4],[834,5]]},"1743":{"position":[[81,4],[122,4],[249,4],[271,4]]},"1765":{"position":[[88,4]]},"1767":{"position":[[214,4]]},"1771":{"position":[[251,4],[966,5],[1238,4],[1375,4],[1396,4],[1462,4],[1511,4],[1547,4],[1605,4],[2059,4],[2373,4],[2514,4]]},"1773":{"position":[[1558,4]]},"1775":{"position":[[725,4],[879,4]]},"1777":{"position":[[876,4],[1032,4]]},"1789":{"position":[[81,4],[104,4],[247,4],[434,6]]},"1791":{"position":[[683,5],[800,5],[1064,4]]},"1807":{"position":[[336,4]]},"1813":{"position":[[1463,4],[1678,4],[1818,5],[1877,4]]},"1817":{"position":[[491,6]]},"1821":{"position":[[13,4]]},"1833":{"position":[[295,4]]},"1835":{"position":[[427,4],[573,5]]},"1837":{"position":[[9,4],[84,5],[104,4],[230,4],[294,4],[401,4],[537,4],[621,4],[641,4],[758,6],[788,4],[865,6],[943,5],[991,4]]},"1839":{"position":[[615,5]]},"1841":{"position":[[196,4],[226,4],[393,4],[429,4],[472,4],[661,4]]},"1843":{"position":[[1752,4],[1892,4]]},"1847":{"position":[[231,4],[411,5],[461,4],[851,7],[992,7],[1132,7]]},"1849":{"position":[[252,4]]},"1861":{"position":[[177,5],[434,4]]},"1863":{"position":[[98,4]]},"1883":{"position":[[68,4],[171,4]]},"1899":{"position":[[206,6]]},"1909":{"position":[[133,4]]},"1911":{"position":[[183,4]]},"1967":{"position":[[798,4]]},"1979":{"position":[[291,6]]},"2019":{"position":[[52,4],[69,4],[302,4],[441,4]]},"2031":{"position":[[177,4]]},"2033":{"position":[[116,5]]},"2056":{"position":[[128,4]]},"2082":{"position":[[435,4],[485,4]]},"2086":{"position":[[219,4],[1478,4],[1528,4]]},"2092":{"position":[[573,4]]},"2108":{"position":[[602,5]]},"2116":{"position":[[54,5]]},"2122":{"position":[[58,5],[506,4]]},"2124":{"position":[[395,4]]},"2126":{"position":[[134,4]]},"2132":{"position":[[43,4],[240,4],[295,4],[327,5],[344,4],[433,4],[1077,4],[1214,5]]},"2134":{"position":[[131,4],[328,4],[368,4],[430,4],[920,4]]},"2148":{"position":[[682,5]]},"2160":{"position":[[1405,4],[1664,5]]},"2228":{"position":[[209,5],[417,5],[479,4],[1001,4]]},"2260":{"position":[[258,6],[532,4]]},"2293":{"position":[[29,4],[117,4],[133,4],[202,4],[443,5],[493,4],[592,5]]},"2295":{"position":[[33,4],[120,7],[128,5],[212,4],[228,4],[301,4],[360,5],[448,5]]},"2297":{"position":[[34,4],[84,7],[92,5],[175,4],[191,4],[263,5],[351,5]]},"2299":{"position":[[26,4],[166,4],[182,4],[239,5],[324,5]]},"2301":{"position":[[665,7],[773,7],[1159,4],[1174,4]]},"2303":{"position":[[106,5],[122,4],[779,5]]},"2350":{"position":[[382,5],[610,4]]},"2354":{"position":[[812,5],[995,4],[1436,4],[2125,8],[2206,5],[3595,4],[3610,4],[5017,4],[5072,4],[5293,7]]},"2356":{"position":[[1016,4],[1044,4]]},"2358":{"position":[[986,4],[1014,4]]},"2360":{"position":[[371,4],[471,5],[508,4],[890,4],[2310,4],[2338,4],[4330,4],[4590,4]]},"2362":{"position":[[2342,4],[2370,4]]},"2364":{"position":[[2417,4],[2445,4]]},"2378":{"position":[[282,5]]},"2394":{"position":[[95,4],[609,4]]},"2400":{"position":[[385,4],[411,5],[436,4],[512,4],[597,4],[812,5],[1905,4]]},"2402":{"position":[[294,5]]},"2408":{"position":[[57,5],[156,4],[408,4]]},"2422":{"position":[[801,6],[857,5]]},"2434":{"position":[[433,5]]},"2446":{"position":[[225,6],[244,5]]},"2448":{"position":[[33,5]]},"2482":{"position":[[488,4],[705,4],[944,4],[1049,4],[1187,6]]},"2488":{"position":[[170,5],[280,5],[399,5],[502,6]]},"2504":{"position":[[343,4],[1579,5]]},"2506":{"position":[[93,4],[226,4],[348,4],[368,4],[634,4],[729,5]]},"2512":{"position":[[1047,4]]},"2516":{"position":[[385,6],[744,4]]},"2530":{"position":[[587,4],[683,4]]},"2538":{"position":[[51,4],[333,4],[432,4],[523,4]]},"2540":{"position":[[0,4]]},"2556":{"position":[[383,6]]},"2558":{"position":[[9,5],[738,4]]},"2562":{"position":[[42,4],[229,5],[415,4],[559,6]]},"2602":{"position":[[404,5],[533,6]]},"2622":{"position":[[4450,4]]},"2628":{"position":[[600,4]]},"2642":{"position":[[389,5]]},"2644":{"position":[[588,4]]},"2658":{"position":[[404,5],[533,6]]},"2666":{"position":[[1354,4]]},"2670":{"position":[[648,4],[821,4],[877,4]]},"2680":{"position":[[192,5],[323,4]]},"2684":{"position":[[275,4],[511,4],[594,4]]},"2686":{"position":[[106,5],[5780,5],[6175,4],[6287,4],[6349,4]]},"2692":{"position":[[135,4],[307,4],[534,4],[574,4],[861,8],[1068,4],[1191,4],[1271,4],[1634,4]]},"2760":{"position":[[100,4],[170,4],[484,4],[558,6],[635,4],[678,5]]},"2772":{"position":[[233,6]]},"2780":{"position":[[59,4],[115,4],[207,4],[1216,9],[1581,4]]},"2782":{"position":[[142,4],[172,4],[226,5],[343,4],[713,4],[736,4]]},"2788":{"position":[[518,5]]},"2790":{"position":[[196,6],[895,6]]},"2792":{"position":[[354,6],[1033,6]]},"2794":{"position":[[349,6],[1329,6]]},"2796":{"position":[[402,6],[1256,6]]},"2798":{"position":[[523,6],[1497,6]]},"2800":{"position":[[12,5],[451,5],[555,5],[628,5],[1080,5]]},"2802":{"position":[[614,5]]},"2810":{"position":[[262,7]]},"2816":{"position":[[59,4]]},"2823":{"position":[[257,5]]},"2826":{"position":[[33,4],[117,4],[401,9],[607,4],[623,4]]},"2830":{"position":[[1901,5]]},"2832":{"position":[[54,4],[281,4],[374,5],[402,4],[439,5]]},"2834":{"position":[[67,4],[197,4],[745,5],[773,4],[810,5],[988,4]]},"2836":{"position":[[163,5]]},"2844":{"position":[[509,4]]},"2846":{"position":[[585,6],[800,4],[847,4],[939,4],[981,4],[1005,4],[1135,5],[1271,4],[1366,4]]},"2852":{"position":[[844,4],[887,4]]},"2867":{"position":[[391,4],[406,4],[590,4]]},"2869":{"position":[[53,4],[98,4],[222,5],[254,5],[308,4],[510,4],[526,4]]},"2871":{"position":[[58,4],[246,5],[288,4]]},"2873":{"position":[[879,5],[919,5],[1485,4],[1504,4]]},"2877":{"position":[[825,5]]},"2879":{"position":[[30,6],[43,4],[89,4],[161,5],[226,4],[242,4]]},"2881":{"position":[[5,4],[421,5],[454,5],[819,4],[835,4]]},"2895":{"position":[[30,4],[118,4],[416,5],[464,5],[650,4]]},"2897":{"position":[[0,4]]},"2899":{"position":[[491,5]]},"2904":{"position":[[116,9],[269,4],[285,4],[348,4],[505,5]]},"2906":{"position":[[116,9],[248,4],[264,4]]},"2918":{"position":[[145,4]]},"2920":{"position":[[350,10],[424,4],[495,5],[530,5],[564,4]]},"2922":{"position":[[92,6],[257,10],[741,4],[820,4],[923,4],[1013,4],[1084,5],[1119,5],[1147,4],[1260,4],[1343,4],[1358,4]]},"2924":{"position":[[21,4],[215,10],[289,4],[360,5],[395,5],[429,4]]},"2926":{"position":[[16,4],[150,4],[322,4],[834,5]]},"2930":{"position":[[251,4],[966,5],[1238,4],[1375,4],[1396,4],[1462,4],[1511,4],[1547,4],[1605,4],[2059,4],[2373,4],[2514,4]]},"2932":{"position":[[1558,4]]},"2934":{"position":[[725,4],[879,4]]},"2936":{"position":[[876,4],[1032,4]]},"2948":{"position":[[81,4],[122,4],[249,4],[271,4]]},"2970":{"position":[[88,4]]},"2972":{"position":[[214,4]]},"3012":{"position":[[798,4]]},"3018":{"position":[[71,4],[96,4],[229,4],[417,6]]},"3020":{"position":[[736,5],[855,5],[1087,4]]},"3034":{"position":[[276,5]]},"3036":{"position":[[415,4]]},"3042":{"position":[[1648,4]]},"3046":{"position":[[635,6]]},"3050":{"position":[[13,4]]},"3062":{"position":[[295,4]]},"3064":{"position":[[446,4],[607,5]]},"3066":{"position":[[23,4],[122,4],[245,4],[318,4],[402,4],[548,4],[633,5],[770,6],[828,4],[871,6],[943,6],[995,4]]},"3070":{"position":[[202,4],[407,4],[443,4],[486,4],[675,4]]},"3072":{"position":[[2040,4],[2180,4]]},"3076":{"position":[[231,4],[411,5],[461,4],[815,7],[956,7],[1096,7]]},"3078":{"position":[[252,4]]},"3086":{"position":[[437,4],[487,4]]},"3092":{"position":[[177,5],[434,4]]},"3094":{"position":[[98,4]]},"3114":{"position":[[68,4],[171,4]]},"3134":{"position":[[206,6]]},"3138":{"position":[[291,6]]},"3166":{"position":[[133,4]]},"3168":{"position":[[183,4]]},"3208":{"position":[[52,4],[69,4],[302,4],[441,4]]},"3220":{"position":[[201,4],[402,4],[585,4],[832,4],[866,4],[1278,4]]},"3222":{"position":[[177,4]]},"3224":{"position":[[89,5]]},"3249":{"position":[[128,4]]},"3267":{"position":[[435,4],[485,4]]},"3271":{"position":[[219,4],[1444,4],[1494,4]]},"3277":{"position":[[573,4]]},"3291":{"position":[[602,5]]},"3324":{"position":[[1042,5],[1101,4]]},"3335":{"position":[[43,4],[240,4],[295,4],[327,5],[344,4],[433,4],[1077,4],[1214,5]]},"3337":{"position":[[131,4],[328,4],[368,4],[430,4],[920,4]]},"3341":{"position":[[614,5]]},"3345":{"position":[[51,5],[766,4]]},"3347":{"position":[[395,4]]},"3349":{"position":[[134,4]]},"3363":{"position":[[754,4]]},"3365":{"position":[[1684,4]]},"3377":{"position":[[682,5]]},"3389":{"position":[[1405,4],[1664,5]]},"3411":{"position":[[382,5],[610,4]]},"3415":{"position":[[812,5],[995,4],[1436,4],[2125,8],[2206,5],[3595,4],[3610,4],[5046,4],[5101,4],[5322,7]]},"3417":{"position":[[1016,4],[1044,4]]},"3419":{"position":[[1033,4],[1061,4]]},"3421":{"position":[[406,4],[743,4],[2113,4],[2141,4],[4333,4],[4593,4]]},"3423":{"position":[[2342,4],[2370,4]]},"3425":{"position":[[2390,4],[2418,4]]},"3439":{"position":[[282,5]]},"3506":{"position":[[185,4],[201,4],[270,4],[511,5],[561,4],[660,5]]},"3508":{"position":[[33,4],[118,4],[134,4],[207,4],[266,5],[354,5]]},"3510":{"position":[[34,4],[109,4],[125,4],[197,5],[285,5]]},"3512":{"position":[[26,4],[159,4],[175,4],[232,5],[317,5]]},"3514":{"position":[[510,7],[618,7],[997,4],[1012,4]]},"3516":{"position":[[106,5],[122,4],[620,5]]},"3526":{"position":[[98,5]]},"3578":{"position":[[209,5],[417,5],[479,4],[1001,4]]},"3610":{"position":[[258,6],[532,4]]},"3633":{"position":[[4450,4]]},"3639":{"position":[[760,6]]},"3653":{"position":[[389,5]]},"3655":{"position":[[588,4]]},"3669":{"position":[[404,5],[533,6]]}}}],["user\":\"2694",{"_index":4516,"t":{"1417":{"position":[[259,14],[626,14],[1046,14]]}}}],["user\":\"42",{"_index":3868,"t":{"690":{"position":[[422,12],[607,11]]},"1733":{"position":[[422,12],[607,11]]},"2922":{"position":[[386,12],[571,11]]}}}],["user\":\"56",{"_index":4216,"t":{"1019":{"position":[[571,12]]},"1021":{"position":[[483,12]]},"1023":{"position":[[1842,12]]},"1025":{"position":[[1857,12]]},"2356":{"position":[[571,12]]},"2358":{"position":[[483,12]]},"2360":{"position":[[1842,12]]},"2362":{"position":[[1857,12]]},"2364":{"position":[[1946,12]]},"3417":{"position":[[571,12]]},"3419":{"position":[[530,12]]},"3421":{"position":[[1645,12]]},"3423":{"position":[[1857,12]]},"3425":{"position":[[1919,12]]}}}],["user'",{"_index":2669,"t":{"262":{"position":[[115,6],[684,6]]},"329":{"position":[[189,6]]},"553":{"position":[[666,6]]},"686":{"position":[[445,6]]},"1497":{"position":[[1034,6]]},"1527":{"position":[[765,6]]},"1729":{"position":[[440,6]]},"2116":{"position":[[133,6]]},"2692":{"position":[[1034,6]]},"2760":{"position":[[766,6]]},"2918":{"position":[[440,6]]},"3506":{"position":[[36,6]]}}}],["user'const",{"_index":2590,"t":{"246":{"position":[[1941,10]]}}}],["user12",{"_index":4359,"t":{"1177":{"position":[[484,6],[1104,10]]},"2228":{"position":[[484,6],[1104,10]]},"3578":{"position":[[484,6],[1080,10]]}}}],["user12hmac",{"_index":4358,"t":{"1177":{"position":[[452,10]]},"2228":{"position":[[452,10]]},"3578":{"position":[[452,10]]}}}],["user:1",{"_index":5730,"t":{"3504":{"position":[[174,10]]}}}],["user:2",{"_index":5731,"t":{"3504":{"position":[[185,10]]}}}],["user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parsetime=true&loc=loc",{"_index":3593,"t":{"535":{"position":[[1073,80]]}}}],["user=123722",{"_index":4803,"t":{"1505":{"position":[[7453,11]]},"2686":{"position":[[7533,11]]}}}],["user=postgr",{"_index":3588,"t":{"535":{"position":[[822,13]]}}}],["user@example.com",{"_index":4287,"t":{"1057":{"position":[[227,19]]},"2140":{"position":[[227,19]]},"3355":{"position":[[227,19]]}}}],["user_1",{"_index":3802,"t":{"678":{"position":[[114,6]]},"1615":{"position":[[114,6]]},"2800":{"position":[[114,6]]}}}],["user_2",{"_index":3803,"t":{"678":{"position":[[124,6]]},"1615":{"position":[[124,6]]},"2800":{"position":[[124,6]]}}}],["user_200",{"_index":3825,"t":{"678":{"position":[[1356,8]]},"1615":{"position":[[1350,8]]},"2800":{"position":[[1350,8]]}}}],["user_200');┌─count",{"_index":3812,"t":{"678":{"position":[[563,24]]},"1615":{"position":[[563,24]]},"2800":{"position":[[563,24]]}}}],["user_28",{"_index":3830,"t":{"678":{"position":[[1433,7]]},"1615":{"position":[[1427,7]]},"2800":{"position":[[1427,7]]}}}],["user_3",{"_index":3804,"t":{"678":{"position":[[134,6]]},"1615":{"position":[[134,6]]},"2800":{"position":[[134,6]]}}}],["user_32",{"_index":3833,"t":{"678":{"position":[[1478,7]]},"1615":{"position":[[1472,7]]},"2800":{"position":[[1472,7]]}}}],["user_39",{"_index":3829,"t":{"678":{"position":[[1418,7]]},"1615":{"position":[[1412,7]]},"2800":{"position":[[1412,7]]}}}],["user_4",{"_index":3805,"t":{"678":{"position":[[144,6]]},"1615":{"position":[[144,6]]},"2800":{"position":[[144,6]]}}}],["user_42",{"_index":1901,"t":{"136":{"position":[[879,11],[1000,7]]},"1569":{"position":[[193,7],[249,11]]},"1571":{"position":[[947,11],[993,12],[1138,8],[1268,7],[1556,7],[1698,12]]},"1581":{"position":[[179,12]]},"2664":{"position":[[193,7],[249,11]]},"2666":{"position":[[947,11],[993,12],[1138,8],[1268,7],[1556,7],[1698,12]]},"2676":{"position":[[179,12]]}}}],["user_5",{"_index":3806,"t":{"678":{"position":[[154,6]]},"1615":{"position":[[154,6]]},"2800":{"position":[[154,6]]}}}],["user_52",{"_index":3834,"t":{"678":{"position":[[1493,7]]},"1615":{"position":[[1487,7]]},"2800":{"position":[[1487,7]]}}}],["user_63",{"_index":3831,"t":{"678":{"position":[[1448,7]]},"1615":{"position":[[1442,7]]},"2800":{"position":[[1442,7]]}}}],["user_65",{"_index":3828,"t":{"678":{"position":[[1403,7]]},"1615":{"position":[[1397,7]]},"2800":{"position":[[1397,7]]}}}],["user_75",{"_index":3826,"t":{"678":{"position":[[1373,7]]},"1615":{"position":[[1367,7]]},"2800":{"position":[[1367,7]]}}}],["user_87",{"_index":3827,"t":{"678":{"position":[[1388,7]]},"1615":{"position":[[1382,7]]},"2800":{"position":[[1382,7]]}}}],["user_89",{"_index":3832,"t":{"678":{"position":[[1463,7]]},"1615":{"position":[[1457,7]]},"2800":{"position":[[1457,7]]}}}],["user_block",{"_index":3742,"t":{"652":{"position":[[82,13],[417,10]]},"654":{"position":[[212,13]]},"1647":{"position":[[82,13],[417,10]]},"1649":{"position":[[212,13]]},"2899":{"position":[[82,13],[417,10]]},"2901":{"position":[[212,13]]}}}],["user_command_rate_limit",{"_index":5514,"t":{"2832":{"position":[[800,26]]}}}],["user_command_throttl",{"_index":4914,"t":{"1668":{"position":[[794,26]]}}}],["user_connect",{"_index":3731,"t":{"636":{"position":[[187,16]]},"1377":{"position":[[33,16]]},"1636":{"position":[[187,16]]},"2548":{"position":[[33,16]]},"2823":{"position":[[187,16]]}}}],["user_personal_channel_namespac",{"_index":2850,"t":{"299":{"position":[[316,34]]},"1069":{"position":[[781,31],[943,31]]},"1071":{"position":[[758,34]]},"2132":{"position":[[781,31],[943,31]]},"2134":{"position":[[770,34]]},"3335":{"position":[[781,31],[943,31]]},"3337":{"position":[[770,34]]}}}],["user_personal_single_connect",{"_index":4310,"t":{"1071":{"position":[[174,31],[717,34]]},"2134":{"position":[[174,31],[729,34]]},"3337":{"position":[[174,31],[729,34]]}}}],["user_statu",{"_index":5006,"t":{"1737":{"position":[[55,14],[578,14],[958,14]]},"2926":{"position":[[55,14],[578,14],[958,14]]}}}],["user_subscribe_to_person",{"_index":2605,"t":{"248":{"position":[[245,29]]},"252":{"position":[[121,26]]},"299":{"position":[[280,29]]},"1069":{"position":[[121,26]]},"1071":{"position":[[681,29]]},"1147":{"position":[[359,29]]},"1177":{"position":[[292,29]]},"2132":{"position":[[121,26]]},"2134":{"position":[[693,29]]},"2228":{"position":[[292,29]]},"2260":{"position":[[359,29]]},"3335":{"position":[[121,26]]},"3337":{"position":[[693,29]]},"3578":{"position":[[292,29]]},"3610":{"position":[[359,29]]}}}],["user_tokens_invalid",{"_index":3724,"t":{"632":{"position":[[170,25],[538,22]]},"634":{"position":[[268,25]]},"1631":{"position":[[170,25],[538,22]]},"1633":{"position":[[268,25]]},"2818":{"position":[[170,25],[538,22]]},"2820":{"position":[[268,25]]}}}],["user_topic_list",{"_index":2721,"t":{"272":{"position":[[659,15]]},"1717":{"position":[[28,15],[413,15]]},"2881":{"position":[[28,15],[596,15]]}}}],["user_topic_upd",{"_index":2722,"t":{"272":{"position":[[679,18],[1233,17]]},"1682":{"position":[[1306,17]]},"1715":{"position":[[167,17],[328,17]]},"2846":{"position":[[1306,17]]},"2879":{"position":[[167,17],[328,17]]}}}],["user_upd",{"_index":4961,"t":{"1705":{"position":[[339,11]]},"2869":{"position":[[267,11]]}}}],["userchannel",{"_index":2471,"t":{"216":{"position":[[3894,11]]}}}],["userfrom",{"_index":3800,"t":{"678":{"position":[[56,8],[1176,8]]},"1615":{"position":[[56,8],[1170,8]]},"2800":{"position":[[56,8],[1170,8]]}}}],["userid",{"_index":1256,"t":{"64":{"position":[[581,7]]}}}],["usernam",{"_index":1981,"t":{"156":{"position":[[966,9]]},"246":{"position":[[1090,10],[1232,10],[2171,8],[2233,8]]}}}],["userord",{"_index":3822,"t":{"678":{"position":[[1293,9]]},"1615":{"position":[[1287,9]]},"2800":{"position":[[1287,9]]}}}],["users/4",{"_index":4709,"t":{"1497":{"position":[[343,8],[793,10]]},"2692":{"position":[[343,8],[793,10]]}}}],["users/:nam",{"_index":4806,"t":{"1509":{"position":[[143,14],[850,12]]},"2768":{"position":[[143,14],[850,12]]}}}],["users/:name\":/users/mari",{"_index":4817,"t":{"1513":{"position":[[152,26]]},"2772":{"position":[[152,26]]}}}],["users/:us",{"_index":4710,"t":{"1497":{"position":[[386,13]]},"2692":{"position":[[386,13]]}}}],["users/:user/:us",{"_index":4816,"t":{"1511":{"position":[[1208,18]]},"2770":{"position":[[1208,18]]}}}],["users/mario",{"_index":4809,"t":{"1509":{"position":[[554,12],[803,13]]},"2768":{"position":[[554,12],[803,13]]}}}],["userstatu",{"_index":3871,"t":{"690":{"position":[[1264,10],[1324,11]]},"1733":{"position":[[1264,10],[1324,11]]},"2922":{"position":[[1228,10],[1288,11]]}}}],["users—ref",{"_index":5568,"t":{"3066":{"position":[[101,14]]}}}],["usertop",{"_index":4956,"t":{"1703":{"position":[[613,9]]},"1717":{"position":[[382,9],[480,9],[500,9],[591,10],[654,9]]},"2867":{"position":[[613,9]]},"2881":{"position":[[248,9],[663,9],[683,9],[746,10],[809,9]]}}}],["usertopicfilt",{"_index":5531,"t":{"2881":{"position":[[91,15],[372,16]]}}}],["usest",{"_index":2431,"t":{"216":{"position":[[1429,9],[3848,13]]}}}],["usestate(\"disconnect",{"_index":2462,"t":{"216":{"position":[[3248,25]]}}}],["usr/share/nginx/html",{"_index":4170,"t":{"979":{"position":[[1415,22]]},"2072":{"position":[[1415,22]]},"3309":{"position":[[1415,22]]}}}],["usual",{"_index":409,"t":{"10":{"position":[[485,7]]},"66":{"position":[[2584,5]]},"74":{"position":[[1702,5]]},"240":{"position":[[112,7]]},"264":{"position":[[1251,7]]},"272":{"position":[[294,7]]},"317":{"position":[[637,7]]},"337":{"position":[[409,6]]},"347":{"position":[[523,7]]},"525":{"position":[[209,7]]},"704":{"position":[[101,7]]},"742":{"position":[[161,7]]},"808":{"position":[[86,7]]},"951":{"position":[[630,8]]},"957":{"position":[[34,7]]},"1017":{"position":[[1414,7],[1582,7]]},"1077":{"position":[[146,7]]},"1092":{"position":[[84,7]]},"1154":{"position":[[109,8]]},"1156":{"position":[[110,8]]},"1185":{"position":[[631,8]]},"1189":{"position":[[1513,8]]},"1279":{"position":[[523,7]]},"1355":{"position":[[222,7]]},"1666":{"position":[[1833,7]]},"1672":{"position":[[951,7],[1306,7]]},"1678":{"position":[[70,7]]},"1682":{"position":[[294,7]]},"1823":{"position":[[86,7]]},"1921":{"position":[[630,8]]},"1927":{"position":[[34,7]]},"1939":{"position":[[98,7]]},"1967":{"position":[[1257,7]]},"2156":{"position":[[266,7]]},"2187":{"position":[[84,7]]},"2197":{"position":[[146,7]]},"2334":{"position":[[109,8]]},"2336":{"position":[[110,8]]},"2354":{"position":[[1531,7],[1699,7],[6156,8]]},"2400":{"position":[[335,8],[1374,7]]},"2402":{"position":[[559,7]]},"2526":{"position":[[235,7]]},"2830":{"position":[[1836,7]]},"2836":{"position":[[951,7],[1306,7]]},"2842":{"position":[[70,7]]},"2846":{"position":[[294,7]]},"2984":{"position":[[98,7]]},"3012":{"position":[[1257,7]]},"3042":{"position":[[1261,7]]},"3052":{"position":[[86,7]]},"3148":{"position":[[630,8]]},"3154":{"position":[[34,7]]},"3220":{"position":[[43,7],[263,7]]},"3251":{"position":[[341,5]]},"3329":{"position":[[241,7]]},"3363":{"position":[[341,5]]},"3365":{"position":[[2896,5]]},"3367":{"position":[[3654,7]]},"3385":{"position":[[266,7]]},"3415":{"position":[[1531,7],[1699,7],[6185,8]]},"3464":{"position":[[84,7]]},"3472":{"position":[[146,7]]},"3619":{"position":[[109,8]]},"3621":{"position":[[110,8]]}}}],["utf",{"_index":3039,"t":{"413":{"position":[[490,3]]},"1237":{"position":[[490,3]]},"2168":{"position":[[1522,3]]},"2486":{"position":[[490,3]]},"3397":{"position":[[1522,3]]}}}],["util",{"_index":796,"t":{"18":{"position":[[2983,7]]},"20":{"position":[[1712,9]]},"32":{"position":[[1757,7]]},"42":{"position":[[165,9]]},"50":{"position":[[82,8]]},"76":{"position":[[1112,11]]},"78":{"position":[[2517,8],[2736,7]]},"82":{"position":[[790,7]]},"84":{"position":[[649,11]]},"86":{"position":[[1054,11]]},"144":{"position":[[1420,7]]},"345":{"position":[[1183,9]]},"379":{"position":[[14,7]]},"429":{"position":[[640,7],[903,7],[1053,7]]},"437":{"position":[[1512,7]]},"471":{"position":[[144,8]]},"473":{"position":[[2710,11]]},"477":{"position":[[1603,9],[4946,8]]},"479":{"position":[[1835,12],[3345,11],[3687,12],[5603,7]]},"483":{"position":[[174,11],[215,11],[1160,11],[1364,11]]},"914":{"position":[[111,7]]},"1021":{"position":[[251,7]]},"1039":{"position":[[945,7]]},"1187":{"position":[[224,10]]},"1193":{"position":[[115,7]]},"1255":{"position":[[640,7],[903,7],[1053,7]]},"1263":{"position":[[1516,7]]},"1277":{"position":[[1183,9]]},"1337":{"position":[[14,7]]},"1700":{"position":[[30,8]]},"2035":{"position":[[188,7]]},"2084":{"position":[[224,10]]},"2092":{"position":[[115,7]]},"2168":{"position":[[1289,7]]},"2358":{"position":[[251,7]]},"2378":{"position":[[945,7]]},"2392":{"position":[[2255,7]]},"2400":{"position":[[2278,7],[2637,7]]},"2442":{"position":[[588,9]]},"2452":{"position":[[14,7]]},"2504":{"position":[[640,7],[903,7],[1053,7]]},"2512":{"position":[[1657,7]]},"2762":{"position":[[771,7]]},"2864":{"position":[[15,8]]},"2889":{"position":[[874,8]]},"3020":{"position":[[692,8]]},"3032":{"position":[[10,9]]},"3226":{"position":[[188,7]]},"3269":{"position":[[224,10]]},"3277":{"position":[[115,7]]},"3324":{"position":[[1924,11]]},"3363":{"position":[[964,8]]},"3369":{"position":[[188,9]]},"3397":{"position":[[1289,7]]},"3419":{"position":[[251,7]]},"3439":{"position":[[945,7]]},"3612":{"position":[[2454,7]]}}}],["uuid",{"_index":5532,"t":{"2883":{"position":[[536,4]]}}}],["ux",{"_index":2514,"t":{"232":{"position":[[860,2]]}}}],["v",{"_index":978,"t":{"34":{"position":[[1184,4]]},"110":{"position":[[1594,5]]},"198":{"position":[[1602,1]]},"511":{"position":[[141,1]]},"541":{"position":[[124,1]]},"680":{"position":[[547,1]]},"971":{"position":[[1746,4]]},"1439":{"position":[[143,1]]},"1489":{"position":[[1141,4]]},"1491":{"position":[[1000,4]]},"1557":{"position":[[126,1]]},"1617":{"position":[[677,1]]},"1981":{"position":[[1746,4]]},"2321":{"position":[[1141,4]]},"2323":{"position":[[1000,4]]},"2608":{"position":[[143,1]]},"2726":{"position":[[126,1]]},"2802":{"position":[[677,1]]},"3140":{"position":[[1746,4]]},"3534":{"position":[[1141,4]]},"3536":{"position":[[1000,4]]}}}],["v0.0.80",{"_index":3345,"t":{"477":{"position":[[2784,7]]}}}],["v0.0.90",{"_index":3347,"t":{"477":{"position":[[3517,9]]}}}],["v0.10.0",{"_index":5252,"t":{"2574":{"position":[[547,7],[574,7]]}}}],["v0.2.0",{"_index":4467,"t":{"1359":{"position":[[318,6]]},"2530":{"position":[[318,6]]}}}],["v0.3.0",{"_index":5254,"t":{"2574":{"position":[[628,6]]}}}],["v0.5.0",{"_index":4466,"t":{"1359":{"position":[[292,6]]},"2530":{"position":[[292,6]]}}}],["v0.6.0",{"_index":5253,"t":{"2574":{"position":[[602,6]]}}}],["v0.9.0",{"_index":4465,"t":{"1359":{"position":[[239,6],[265,6]]},"2530":{"position":[[239,6],[265,6]]}}}],["v1",{"_index":859,"t":{"26":{"position":[[180,3],[1442,2]]},"28":{"position":[[350,2]]},"82":{"position":[[2389,2]]},"1359":{"position":[[1848,2]]},"1839":{"position":[[116,3]]},"2148":{"position":[[433,3],[559,3],[579,2]]},"2530":{"position":[[1848,2]]},"3068":{"position":[[147,3]]},"3377":{"position":[[433,3],[559,3],[579,2]]}}}],["v1.0.0",{"_index":5171,"t":{"2104":{"position":[[469,6]]}}}],["v1.8.9",{"_index":3242,"t":{"475":{"position":[[4408,8]]},"477":{"position":[[3475,8]]}}}],["v2",{"_index":835,"t":{"24":{"position":[[39,2]]},"26":{"position":[[11,2],[144,2],[194,2],[733,2]]},"28":{"position":[[356,2],[694,2],[901,3]]},"32":{"position":[[51,2]]},"40":{"position":[[171,4]]},"50":{"position":[[653,2]]},"58":{"position":[[1265,2]]},"345":{"position":[[856,2]]},"608":{"position":[[124,2]]},"614":{"position":[[277,3]]},"620":{"position":[[39,2]]},"1277":{"position":[[856,2]]},"1365":{"position":[[11,2]]},"1375":{"position":[[374,2]]},"1839":{"position":[[120,2]]},"2148":{"position":[[536,2]]},"2536":{"position":[[11,2]]},"2546":{"position":[[374,2]]},"2582":{"position":[[59,2]]},"3068":{"position":[[151,3]]},"3377":{"position":[[536,2]]}}}],["v2.x",{"_index":3958,"t":{"802":{"position":[[609,4]]}}}],["v3",{"_index":847,"t":{"24":{"position":[[257,2]]},"26":{"position":[[63,2],[1863,2]]},"28":{"position":[[92,2],[262,2],[700,2],[840,2],[881,2],[1015,2]]},"30":{"position":[[70,2]]},"32":{"position":[[187,2]]},"34":{"position":[[204,2]]},"36":{"position":[[14,2],[110,2]]},"38":{"position":[[160,2],[718,2]]},"42":{"position":[[11,2]]},"46":{"position":[[20,2],[464,4]]},"50":{"position":[[11,2],[1023,2]]},"52":{"position":[[1058,2]]},"54":{"position":[[43,2],[188,2],[363,3],[477,2],[701,2]]},"120":{"position":[[48,2],[358,2],[525,2]]},"128":{"position":[[1282,3]]},"132":{"position":[[650,2]]},"154":{"position":[[367,3]]},"156":{"position":[[191,2]]},"182":{"position":[[488,3]]},"186":{"position":[[506,2]]},"242":{"position":[[518,3]]},"369":{"position":[[201,2]]},"593":{"position":[[188,3]]},"596":{"position":[[14,2]]},"598":{"position":[[14,2]]},"600":{"position":[[98,2]]},"602":{"position":[[14,2]]},"604":{"position":[[14,2]]},"606":{"position":[[52,3]]},"608":{"position":[[741,2]]},"610":{"position":[[60,2]]},"612":{"position":[[80,2]]},"614":{"position":[[11,2]]},"616":{"position":[[75,2]]},"620":{"position":[[46,2],[826,2]]},"1359":{"position":[[410,2],[498,2]]},"1365":{"position":[[18,2]]},"1367":{"position":[[1081,2]]},"1375":{"position":[[380,3]]},"1817":{"position":[[609,2]]},"1839":{"position":[[127,3]]},"2530":{"position":[[410,2],[498,2]]},"2536":{"position":[[18,2]]},"2538":{"position":[[1081,2]]},"2546":{"position":[[380,3]]},"2582":{"position":[[222,3]]},"3046":{"position":[[753,2]]}}}],["v3.0.0",{"_index":4303,"t":{"1061":{"position":[[25,6]]},"1359":{"position":[[215,6]]},"2530":{"position":[[215,6]]}}}],["v3.1.0",{"_index":4245,"t":{"1041":{"position":[[18,7],[553,6],[831,6]]},"2380":{"position":[[527,6],[805,6]]},"3441":{"position":[[527,6],[805,6]]}}}],["v3.1.1",{"_index":3771,"t":{"672":{"position":[[1783,7],[1990,7]]},"1023":{"position":[[2722,6],[2871,6]]}}}],["v3.2.0",{"_index":3923,"t":{"782":{"position":[[25,6]]},"784":{"position":[[25,6]]},"1001":{"position":[[25,6]]},"1003":{"position":[[25,6]]},"1189":{"position":[[219,6]]},"1193":{"position":[[572,7]]},"1457":{"position":[[2285,7]]},"1459":{"position":[[523,7]]},"1461":{"position":[[652,6]]},"1463":{"position":[[508,6]]},"1465":{"position":[[411,6]]},"1467":{"position":[[384,6]]}}}],["v3.2.2",{"_index":3598,"t":{"543":{"position":[[215,6]]}}}],["v3/php_laravel_chat_tutori",{"_index":1995,"t":{"160":{"position":[[142,28]]}}}],["v3_use_offset",{"_index":3648,"t":{"593":{"position":[[129,13]]},"618":{"position":[[186,13]]}}}],["v3—onli",{"_index":5572,"t":{"3068":{"position":[[159,7]]}}}],["v4",{"_index":55,"t":{"2":{"position":[[687,2]]},"118":{"position":[[80,3]]},"120":{"position":[[834,2]]},"122":{"position":[[263,2],[1222,2]]},"124":{"position":[[451,3]]},"128":{"position":[[742,2]]},"130":{"position":[[785,3],[1930,2]]},"132":{"position":[[124,3],[657,3]]},"142":{"position":[[11,2]]},"144":{"position":[[509,3]]},"146":{"position":[[127,2],[186,2]]},"148":{"position":[[51,3],[767,2]]},"150":{"position":[[251,3]]},"152":{"position":[[124,2],[606,3]]},"154":{"position":[[403,2]]},"182":{"position":[[524,2]]},"242":{"position":[[554,2]]},"313":{"position":[[36,3],[270,2],[427,2]]},"319":{"position":[[396,3],[469,2],[600,2]]},"321":{"position":[[890,3]]},"327":{"position":[[52,2]]},"333":{"position":[[75,3],[708,2]]},"1247":{"position":[[11,2]]},"1359":{"position":[[194,3],[504,2],[783,3],[801,2],[869,2],[1016,3]]},"1361":{"position":[[100,2],[391,3]]},"1363":{"position":[[51,2],[231,2]]},"1367":{"position":[[142,2],[258,2],[711,2],[850,2],[1087,2]]},"1371":{"position":[[14,2],[974,2]]},"1771":{"position":[[994,2]]},"1839":{"position":[[11,2],[220,2],[643,2]]},"2088":{"position":[[266,2]]},"2094":{"position":[[251,2]]},"2230":{"position":[[53,3]]},"2243":{"position":[[53,3]]},"2281":{"position":[[53,3]]},"2496":{"position":[[11,2]]},"2530":{"position":[[194,3],[504,2],[783,3],[801,2],[869,2],[1016,3]]},"2532":{"position":[[100,2],[391,3]]},"2534":{"position":[[51,2],[231,2]]},"2538":{"position":[[142,2],[258,2],[711,2],[850,2],[1087,2]]},"2542":{"position":[[14,2],[974,2]]},"2574":{"position":[[681,2]]},"2576":{"position":[[64,2],[75,2]]},"2582":{"position":[[226,2]]},"2883":{"position":[[541,2]]},"2930":{"position":[[994,2]]},"3068":{"position":[[261,3],[682,2]]},"3273":{"position":[[266,2]]},"3279":{"position":[[251,2]]},"3580":{"position":[[53,3]]}}}],["v4.0.0",{"_index":4839,"t":{"1559":{"position":[[215,6]]},"2574":{"position":[[523,6]]}}}],["v4.0.1",{"_index":5130,"t":{"2021":{"position":[[20,6]]}}}],["v4.1.0",{"_index":3093,"t":{"465":{"position":[[876,6]]},"479":{"position":[[5360,6]]},"2088":{"position":[[397,6],[593,6],[788,6],[1021,6]]},"2094":{"position":[[400,6],[614,6],[827,6],[1078,6]]}}}],["v4.1.1",{"_index":4471,"t":{"1359":{"position":[[1788,6]]},"2023":{"position":[[20,6]]},"2033":{"position":[[20,6]]},"2364":{"position":[[20,6]]},"2530":{"position":[[1788,6]]}}}],["v4.1.3",{"_index":5023,"t":{"1827":{"position":[[27,6]]},"2086":{"position":[[636,6]]}}}],["v5",{"_index":2643,"t":{"256":{"position":[[7,2]]},"311":{"position":[[14,2],[1329,3]]},"313":{"position":[[339,2]]},"315":{"position":[[14,2],[1723,2]]},"319":{"position":[[190,2],[476,2],[553,2]]},"321":{"position":[[990,3],[1788,2],[2115,2]]},"327":{"position":[[456,2]]},"329":{"position":[[485,3]]},"331":{"position":[[11,2]]},"337":{"position":[[60,3],[115,3],[306,3]]},"2088":{"position":[[357,3]]},"2094":{"position":[[351,3]]},"2574":{"position":[[14,2],[687,2]]},"2576":{"position":[[25,2],[82,2]]},"2578":{"position":[[730,2]]},"2582":{"position":[[16,2],[233,2]]},"3273":{"position":[[357,3]]},"3279":{"position":[[351,3]]}}}],["v5.0.2",{"_index":5719,"t":{"3411":{"position":[[849,6]]},"3445":{"position":[[1753,6]]}}}],["v5.0.3",{"_index":5630,"t":{"3327":{"position":[[1129,8]]}}}],["v5.0.4",{"_index":5582,"t":{"3251":{"position":[[27,6]]},"3421":{"position":[[3434,6]]}}}],["v5.1.0",{"_index":5561,"t":{"3054":{"position":[[1033,7]]},"3433":{"position":[[597,8]]},"3445":{"position":[[2395,7]]},"3538":{"position":[[451,6]]}}}],["v5.1.1",{"_index":5457,"t":{"2708":{"position":[[453,6]]},"2736":{"position":[[533,8]]},"3220":{"position":[[36,6]]}}}],["v5.1.2",{"_index":5482,"t":{"2728":{"position":[[215,6]]}}}],["v6",{"_index":3527,"t":{"509":{"position":[[466,2]]},"1437":{"position":[[476,2]]},"2606":{"position":[[491,2]]}}}],["v6.2.7",{"_index":3245,"t":{"475":{"position":[[4503,6]]}}}],["v8",{"_index":864,"t":{"26":{"position":[[366,2]]}}}],["v9.0.0",{"_index":3243,"t":{"475":{"position":[[4457,7]]}}}],["v97",{"_index":1929,"t":{"144":{"position":[[395,4]]},"2392":{"position":[[678,4]]},"3612":{"position":[[705,4]]}}}],["vacat",{"_index":5241,"t":{"2424":{"position":[[1429,8]]},"2708":{"position":[[224,7],[942,7],[1244,10],[2083,7],[2169,7]]},"2760":{"position":[[882,7]]}}}],["valid",{"_index":1754,"t":{"110":{"position":[[4038,8]]},"132":{"position":[[395,5]]},"200":{"position":[[1288,8],[1653,8]]},"212":{"position":[[373,5]]},"214":{"position":[[604,10]]},"291":{"position":[[2489,9]]},"315":{"position":[[334,5],[657,5]]},"327":{"position":[[65,8]]},"329":{"position":[[684,8]]},"341":{"position":[[560,9],[878,9],[1320,9]]},"345":{"position":[[494,8]]},"397":{"position":[[546,5]]},"413":{"position":[[681,5]]},"429":{"position":[[245,10],[517,11]]},"431":{"position":[[503,8]]},"549":{"position":[[130,5]]},"776":{"position":[[539,5]]},"798":{"position":[[783,5],[1519,5]]},"804":{"position":[[25,5]]},"824":{"position":[[404,8],[581,10]]},"864":{"position":[[143,10]]},"868":{"position":[[323,5]]},"870":{"position":[[85,8],[201,10]]},"941":{"position":[[287,5]]},"943":{"position":[[245,5]]},"993":{"position":[[68,6]]},"1019":{"position":[[306,8]]},"1021":{"position":[[1484,5]]},"1023":{"position":[[806,8]]},"1025":{"position":[[321,8],[478,9]]},"1051":{"position":[[705,9]]},"1057":{"position":[[1101,5]]},"1211":{"position":[[560,9],[878,9],[1320,9]]},"1221":{"position":[[546,5]]},"1237":{"position":[[681,5]]},"1255":{"position":[[245,10],[517,11]]},"1257":{"position":[[503,8]]},"1277":{"position":[[494,8]]},"1495":{"position":[[330,5]]},"1505":{"position":[[5486,6],[5793,5]]},"1511":{"position":[[526,10],[1244,10]]},"1565":{"position":[[130,5]]},"1585":{"position":[[205,5],[529,5]]},"1703":{"position":[[222,6],[355,6]]},"1747":{"position":[[68,6]]},"1767":{"position":[[44,5]]},"1771":{"position":[[590,5]]},"1773":{"position":[[147,8]]},"1791":{"position":[[539,5]]},"1813":{"position":[[783,5],[1519,5]]},"1819":{"position":[[25,5]]},"1827":{"position":[[235,11]]},"1831":{"position":[[355,5]]},"1839":{"position":[[333,5]]},"1865":{"position":[[370,8],[526,8],[622,10],[848,10]]},"1867":{"position":[[343,8],[499,8],[595,10]]},"1907":{"position":[[143,10]]},"1909":{"position":[[323,5]]},"1911":{"position":[[384,5]]},"1913":{"position":[[85,8],[201,10]]},"1915":{"position":[[91,8],[209,10]]},"2033":{"position":[[146,5],[174,6]]},"2062":{"position":[[287,5]]},"2064":{"position":[[245,5]]},"2140":{"position":[[1101,5]]},"2356":{"position":[[306,8]]},"2358":{"position":[[1484,5]]},"2360":{"position":[[806,8]]},"2362":{"position":[[321,8],[478,9]]},"2364":{"position":[[353,8],[742,9]]},"2390":{"position":[[705,9]]},"2394":{"position":[[560,9],[878,9],[1320,9]]},"2400":{"position":[[958,8],[2780,8]]},"2470":{"position":[[546,5]]},"2486":{"position":[[681,5]]},"2504":{"position":[[245,10],[517,11]]},"2506":{"position":[[503,8]]},"2574":{"position":[[207,5]]},"2680":{"position":[[205,5],[529,5]]},"2686":{"position":[[5548,6],[5852,5]]},"2690":{"position":[[330,5]]},"2734":{"position":[[130,5]]},"2770":{"position":[[526,10],[1244,10]]},"2867":{"position":[[222,6],[355,6]]},"2930":{"position":[[590,5]]},"2932":{"position":[[147,8]]},"2952":{"position":[[68,6]]},"2972":{"position":[[44,5]]},"3020":{"position":[[581,5]]},"3042":{"position":[[769,5],[1459,5]]},"3048":{"position":[[25,5]]},"3056":{"position":[[201,11]]},"3060":{"position":[[406,5]]},"3068":{"position":[[345,5]]},"3096":{"position":[[370,8],[526,8],[622,10],[848,10]]},"3098":{"position":[[343,8],[499,8],[595,10]]},"3164":{"position":[[143,10]]},"3166":{"position":[[323,5]]},"3168":{"position":[[384,5]]},"3170":{"position":[[85,8],[201,10]]},"3172":{"position":[[91,8],[209,10]]},"3224":{"position":[[119,5],[147,6]]},"3257":{"position":[[287,5]]},"3259":{"position":[[245,5]]},"3355":{"position":[[1101,5]]},"3417":{"position":[[306,8]]},"3419":{"position":[[1531,5]]},"3423":{"position":[[321,8],[478,9]]},"3425":{"position":[[326,8],[715,9]]},"3451":{"position":[[705,9]]}}}],["validateclientind",{"_index":1755,"t":{"110":{"position":[[4061,24]]}}}],["validateclientindication(ind",{"_index":1726,"t":{"110":{"position":[[1965,35],[4184,35]]}}}],["valu",{"_index":68,"t":{"2":{"position":[[771,7],[2137,6]]},"12":{"position":[[775,5]]},"34":{"position":[[951,5]]},"74":{"position":[[453,5]]},"110":{"position":[[559,5],[1096,6],[1143,6],[2672,5],[2827,5],[3584,6],[3739,5],[3852,5],[3912,5],[3989,6]]},"258":{"position":[[1591,5],[1795,6],[2079,5]]},"317":{"position":[[1512,5]]},"463":{"position":[[340,7]]},"479":{"position":[[1595,5],[1762,5],[4187,5],[5142,5],[6566,5]]},"509":{"position":[[930,6]]},"608":{"position":[[405,6]]},"682":{"position":[[623,7]]},"686":{"position":[[469,5]]},"776":{"position":[[887,5],[922,6],[1031,5]]},"792":{"position":[[1828,8]]},"794":{"position":[[47,9]]},"798":{"position":[[872,5],[905,5]]},"802":{"position":[[210,5],[305,5]]},"836":{"position":[[264,6]]},"878":{"position":[[437,5],[668,6]]},"884":{"position":[[486,6]]},"900":{"position":[[68,6]]},"912":{"position":[[532,5],[696,7]]},"941":{"position":[[69,5]]},"969":{"position":[[123,5]]},"971":{"position":[[847,8],[1012,7],[1513,5]]},"973":{"position":[[2103,6],[3025,6]]},"1017":{"position":[[4533,9]]},"1023":{"position":[[3757,8]]},"1033":{"position":[[402,5]]},"1045":{"position":[[2030,5]]},"1098":{"position":[[233,5]]},"1154":{"position":[[157,5]]},"1156":{"position":[[158,5]]},"1160":{"position":[[1101,5]]},"1170":{"position":[[108,5]]},"1185":{"position":[[207,6],[610,5]]},"1189":{"position":[[1082,6],[1492,5]]},"1205":{"position":[[275,6]]},"1315":{"position":[[340,7]]},"1373":{"position":[[109,6]]},"1407":{"position":[[780,5]]},"1413":{"position":[[687,5],[790,5]]},"1437":{"position":[[1163,6]]},"1457":{"position":[[2195,6]]},"1459":{"position":[[568,6]]},"1461":{"position":[[1292,8]]},"1477":{"position":[[358,5]]},"1491":{"position":[[170,5]]},"1505":{"position":[[297,5],[1795,5],[1925,7],[3792,5],[6479,5],[8958,5],[9436,8],[9521,5],[10009,9],[10101,5]]},"1573":{"position":[[210,6]]},"1587":{"position":[[223,6]]},"1599":{"position":[[391,5]]},"1703":{"position":[[733,5]]},"1729":{"position":[[464,5]]},"1743":{"position":[[103,5]]},"1763":{"position":[[569,8],[735,8]]},"1791":{"position":[[887,5],[922,6],[1031,5]]},"1807":{"position":[[1355,8]]},"1809":{"position":[[47,9]]},"1813":{"position":[[872,5],[905,5]]},"1817":{"position":[[210,5],[305,5]]},"1853":{"position":[[264,6]]},"1979":{"position":[[123,5]]},"1981":{"position":[[847,8],[1012,7],[1513,5]]},"1983":{"position":[[2314,7],[2364,6],[3295,6]]},"1991":{"position":[[437,5],[668,6]]},"1997":{"position":[[486,6]]},"2013":{"position":[[68,6]]},"2021":{"position":[[52,5]]},"2027":{"position":[[532,5],[696,7]]},"2029":{"position":[[208,5]]},"2062":{"position":[[69,5]]},"2082":{"position":[[252,6],[597,5],[720,5]]},"2086":{"position":[[1162,6],[1295,6],[1640,5],[1710,5]]},"2108":{"position":[[275,6]]},"2221":{"position":[[108,5]]},"2234":{"position":[[233,5]]},"2289":{"position":[[2249,6]]},"2291":{"position":[[543,6]]},"2293":{"position":[[1356,8]]},"2309":{"position":[[405,5]]},"2323":{"position":[[170,5]]},"2334":{"position":[[157,5]]},"2336":{"position":[[158,5]]},"2340":{"position":[[1101,5]]},"2354":{"position":[[4650,9]]},"2360":{"position":[[3788,8]]},"2372":{"position":[[402,5]]},"2384":{"position":[[2030,5]]},"2392":{"position":[[1015,5]]},"2544":{"position":[[109,6]]},"2602":{"position":[[340,7]]},"2606":{"position":[[1192,6]]},"2636":{"position":[[456,6]]},"2658":{"position":[[340,7]]},"2668":{"position":[[210,6]]},"2682":{"position":[[223,6]]},"2686":{"position":[[298,5],[1810,5],[1940,7],[3835,5],[6552,5],[9078,5],[9592,8],[9677,5],[10122,9],[10221,5]]},"2762":{"position":[[726,5]]},"2782":{"position":[[391,5]]},"2873":{"position":[[1244,5]]},"2877":{"position":[[1230,5]]},"2883":{"position":[[1547,5]]},"2885":{"position":[[69,7]]},"2918":{"position":[[464,5]]},"2948":{"position":[[103,5]]},"2968":{"position":[[569,8],[735,8]]},"3020":{"position":[[942,5],[973,5],[1059,5]]},"3036":{"position":[[1433,8]]},"3038":{"position":[[49,9]]},"3042":{"position":[[859,5]]},"3046":{"position":[[354,5],[449,5]]},"3082":{"position":[[264,6]]},"3086":{"position":[[254,6],[599,5],[722,5]]},"3138":{"position":[[123,5]]},"3140":{"position":[[847,8],[1012,7],[1513,5]]},"3142":{"position":[[2314,7],[2364,6],[3295,6]]},"3180":{"position":[[437,5],[668,6]]},"3186":{"position":[[486,6]]},"3202":{"position":[[68,6]]},"3210":{"position":[[25,5]]},"3216":{"position":[[532,5],[696,7]]},"3218":{"position":[[208,5]]},"3257":{"position":[[69,5]]},"3267":{"position":[[252,6],[597,5],[720,5]]},"3271":{"position":[[1128,6],[1261,6],[1606,5],[1676,5]]},"3291":{"position":[[275,6]]},"3329":{"position":[[76,7]]},"3367":{"position":[[3782,6]]},"3411":{"position":[[1024,6],[1097,7],[1250,8]]},"3415":{"position":[[4679,9]]},"3421":{"position":[[3791,8]]},"3433":{"position":[[402,5]]},"3445":{"position":[[2242,5]]},"3496":{"position":[[386,9]]},"3502":{"position":[[1757,6]]},"3504":{"position":[[618,6]]},"3506":{"position":[[1424,8]]},"3522":{"position":[[465,5]]},"3536":{"position":[[170,5]]},"3571":{"position":[[108,5]]},"3584":{"position":[[233,5]]},"3612":{"position":[[1042,5]]},"3619":{"position":[[157,5]]},"3621":{"position":[[158,5]]},"3625":{"position":[[1101,5]]},"3647":{"position":[[456,6]]},"3669":{"position":[[340,7]]}}}],["valuabl",{"_index":268,"t":{"4":{"position":[[1057,8]]},"42":{"position":[[23,8]]},"180":{"position":[[147,8]]}}}],["value'};const",{"_index":3079,"t":{"459":{"position":[[269,14]]},"1311":{"position":[[269,14]]},"2598":{"position":[[278,14]]},"2654":{"position":[[331,14]]},"3665":{"position":[[331,14]]}}}],["value,if",{"_index":1585,"t":{"98":{"position":[[1660,8]]}}}],["value=\"log",{"_index":2579,"t":{"246":{"position":[[1387,10]]}}}],["valuelength",{"_index":1742,"t":{"110":{"position":[[3330,11],[3392,13],[3473,12],[3660,11]]}}}],["values.yaml",{"_index":3595,"t":{"543":{"position":[[130,11]]},"1559":{"position":[[130,11]]},"2728":{"position":[[130,11]]}}}],["valuesummari",{"_index":3132,"t":{"471":{"position":[[950,13],[1076,13]]}}}],["vanilla",{"_index":1967,"t":{"156":{"position":[[381,7]]},"178":{"position":[[168,7]]}}}],["var",{"_index":1735,"t":{"110":{"position":[[2982,3],[3172,3],[3326,3],[4108,3]]},"192":{"position":[[1166,3]]},"620":{"position":[[461,4]]},"672":{"position":[[783,3]]},"802":{"position":[[89,3],[614,3]]},"804":{"position":[[212,3]]},"806":{"position":[[185,3]]},"1425":{"position":[[1613,3]]},"1497":{"position":[[838,4]]},"1603":{"position":[[871,3]]},"1817":{"position":[[89,3],[612,3]]},"1819":{"position":[[212,3]]},"1821":{"position":[[185,3]]},"2692":{"position":[[838,4]]},"2788":{"position":[[871,3]]},"3046":{"position":[[756,3]]},"3327":{"position":[[352,4]]},"3369":{"position":[[2295,3]]}}}],["vari",{"_index":188,"t":{"2":{"position":[[3076,4]]},"16":{"position":[[6930,4]]},"84":{"position":[[939,4]]},"347":{"position":[[910,5]]},"349":{"position":[[1081,6]]},"399":{"position":[[100,5],[559,5]]},"1011":{"position":[[250,6]]},"1223":{"position":[[100,5],[559,5]]},"1279":{"position":[[915,5]]},"1281":{"position":[[1232,6]]},"1419":{"position":[[459,5]]},"2348":{"position":[[250,6]]},"2402":{"position":[[971,5]]},"2472":{"position":[[100,5],[559,5]]},"3409":{"position":[[255,6]]}}}],["variabl",{"_index":2395,"t":{"214":{"position":[[492,9]]},"329":{"position":[[708,9]]},"421":{"position":[[109,9]]},"491":{"position":[[71,10]]},"535":{"position":[[135,10]]},"549":{"position":[[325,9]]},"620":{"position":[[388,9]]},"640":{"position":[[1078,9]]},"672":{"position":[[696,9]]},"878":{"position":[[262,9],[462,8],[633,9],[944,9],[1036,8],[1069,9]]},"880":{"position":[[252,8]]},"943":{"position":[[224,8]]},"1013":{"position":[[537,8]]},"1199":{"position":[[444,9],[1255,8]]},"1245":{"position":[[169,9]]},"1385":{"position":[[71,10]]},"1497":{"position":[[46,9],[197,9],[400,8],[885,9],[974,10]]},"1499":{"position":[[79,9]]},"1501":{"position":[[73,9]]},"1503":{"position":[[74,9]]},"1505":{"position":[[5932,9]]},"1509":{"position":[[463,8]]},"1511":{"position":[[135,8],[598,10],[644,10],[1007,8],[1133,8]]},"1513":{"position":[[48,8],[328,10],[512,10]]},"1515":{"position":[[124,9]]},"1565":{"position":[[325,9]]},"1591":{"position":[[1360,9]]},"1603":{"position":[[784,9]]},"1827":{"position":[[59,9],[136,9],[462,10],[473,9],[632,10],[643,9],[910,8]]},"1991":{"position":[[262,9],[462,8],[633,9],[944,9],[1036,8],[1069,9]]},"1993":{"position":[[252,8]]},"2064":{"position":[[224,8]]},"2100":{"position":[[444,9],[1255,8]]},"2350":{"position":[[537,8]]},"2494":{"position":[[169,9]]},"2556":{"position":[[72,10]]},"2686":{"position":[[5993,9]]},"2692":{"position":[[46,9],[197,9],[400,8],[885,9],[974,10]]},"2694":{"position":[[79,9]]},"2696":{"position":[[73,9]]},"2698":{"position":[[74,9]]},"2734":{"position":[[325,9]]},"2768":{"position":[[463,8]]},"2770":{"position":[[135,8],[598,10],[644,10],[1007,8],[1133,8]]},"2772":{"position":[[48,8],[328,10],[512,10]]},"2774":{"position":[[124,9]]},"2784":{"position":[[1360,9]]},"2788":{"position":[[784,9]]},"3056":{"position":[[25,9],[102,9],[428,10],[439,9],[598,10],[609,9],[876,8]]},"3176":{"position":[[103,9]]},"3178":{"position":[[265,9]]},"3180":{"position":[[262,9],[462,8],[633,9],[944,9],[1036,8],[1069,9]]},"3182":{"position":[[252,8]]},"3259":{"position":[[224,8]]},"3285":{"position":[[444,9],[1255,8]]},"3327":{"position":[[1035,8]]},"3411":{"position":[[537,8],[1142,8]]}}}],["variad",{"_index":3196,"t":{"473":{"position":[[2350,8]]}}}],["variant",{"_index":454,"t":{"10":{"position":[[1564,8]]},"951":{"position":[[534,8]]},"1921":{"position":[[534,8]]},"3148":{"position":[[534,8]]}}}],["variat",{"_index":2062,"t":{"176":{"position":[[43,10]]},"475":{"position":[[420,10]]},"1885":{"position":[[167,11]]},"3116":{"position":[[167,11]]}}}],["varieti",{"_index":1986,"t":{"158":{"position":[[878,7]]},"184":{"position":[[575,7]]},"242":{"position":[[1189,7]]}}}],["varint",{"_index":4489,"t":{"1405":{"position":[[1643,6],[1815,6]]},"2154":{"position":[[1339,6],[1511,6]]},"3383":{"position":[[1339,6],[1511,6]]}}}],["variou",{"_index":294,"t":{"6":{"position":[[457,7]]},"8":{"position":[[1558,7]]},"18":{"position":[[627,7]]},"32":{"position":[[595,7],[1740,7]]},"126":{"position":[[582,7]]},"174":{"position":[[29,7]]},"176":{"position":[[1919,7]]},"222":{"position":[[60,7],[1032,7]]},"242":{"position":[[400,7]]},"264":{"position":[[266,7]]},"274":{"position":[[498,7]]},"285":{"position":[[319,7]]},"321":{"position":[[663,7]]},"367":{"position":[[260,7]]},"389":{"position":[[20,7]]},"465":{"position":[[93,7],[697,7]]},"473":{"position":[[2164,7]]},"475":{"position":[[393,7],[3227,7]]},"483":{"position":[[105,7]]},"726":{"position":[[311,7]]},"788":{"position":[[259,7]]},"953":{"position":[[263,7]]},"1037":{"position":[[181,7]]},"1039":{"position":[[77,7]]},"1094":{"position":[[511,7]]},"1325":{"position":[[257,7]]},"1347":{"position":[[20,7]]},"1403":{"position":[[2198,7]]},"1405":{"position":[[3064,7]]},"1423":{"position":[[100,7]]},"1803":{"position":[[259,7]]},"1923":{"position":[[263,7]]},"1961":{"position":[[311,7]]},"2160":{"position":[[100,7]]},"2189":{"position":[[649,7]]},"2376":{"position":[[181,7]]},"2378":{"position":[[77,7]]},"2424":{"position":[[387,7],[663,7],[1615,7]]},"2440":{"position":[[258,7]]},"2462":{"position":[[20,7]]},"3006":{"position":[[311,7]]},"3032":{"position":[[277,7]]},"3060":{"position":[[959,7]]},"3072":{"position":[[586,7]]},"3150":{"position":[[263,7]]},"3324":{"position":[[25,7],[654,7]]},"3365":{"position":[[323,7]]},"3367":{"position":[[3695,7]]},"3389":{"position":[[100,7]]},"3437":{"position":[[181,7]]},"3439":{"position":[[77,7]]},"3492":{"position":[[649,7]]}}}],["vars.ten",{"_index":4717,"t":{"1497":{"position":[[1589,12]]},"2692":{"position":[[1589,12]]}}}],["vars.us",{"_index":4713,"t":{"1497":{"position":[[1177,10],[1620,10]]},"2692":{"position":[[1177,10],[1620,10]]}}}],["vector",{"_index":2856,"t":{"305":{"position":[[829,6]]}}}],["vendor",{"_index":3205,"t":{"473":{"position":[[3473,7]]}}}],["venv",{"_index":2113,"t":{"186":{"position":[[425,4]]}}}],["verb",{"_index":2496,"t":{"224":{"position":[[500,5]]}}}],["veri",{"_index":316,"t":{"8":{"position":[[372,4]]},"10":{"position":[[1678,4]]},"12":{"position":[[1626,4]]},"14":{"position":[[1056,4],[1218,4]]},"16":{"position":[[4167,4],[6249,4],[6341,4]]},"18":{"position":[[1198,4]]},"26":{"position":[[930,4]]},"34":{"position":[[79,4]]},"38":{"position":[[316,4],[1305,4]]},"42":{"position":[[389,4]]},"60":{"position":[[1196,4]]},"62":{"position":[[239,4]]},"66":{"position":[[1970,4],[1997,4]]},"74":{"position":[[1325,4],[1617,4]]},"82":{"position":[[1621,4]]},"86":{"position":[[1340,4]]},"112":{"position":[[264,4]]},"144":{"position":[[422,4]]},"182":{"position":[[758,4]]},"216":{"position":[[40,4]]},"218":{"position":[[100,4]]},"222":{"position":[[745,4]]},"238":{"position":[[230,4]]},"242":{"position":[[795,4]]},"264":{"position":[[1484,4]]},"268":{"position":[[316,4]]},"270":{"position":[[1464,4]]},"293":{"position":[[191,4]]},"295":{"position":[[1735,4]]},"303":{"position":[[8,4]]},"321":{"position":[[1933,4]]},"361":{"position":[[228,4]]},"393":{"position":[[394,4]]},"409":{"position":[[13,4]]},"419":{"position":[[97,4]]},"441":{"position":[[169,4]]},"473":{"position":[[2565,4]]},"475":{"position":[[1107,4]]},"479":{"position":[[3370,4],[5181,4]]},"497":{"position":[[836,4]]},"525":{"position":[[578,4]]},"600":{"position":[[43,4]]},"836":{"position":[[172,4]]},"838":{"position":[[193,4]]},"858":{"position":[[85,4],[1158,5]]},"912":{"position":[[482,4]]},"951":{"position":[[277,4]]},"1098":{"position":[[826,4]]},"1160":{"position":[[542,4]]},"1162":{"position":[[169,4],[338,4],[428,4]]},"1197":{"position":[[178,4]]},"1233":{"position":[[13,4]]},"1243":{"position":[[97,4]]},"1267":{"position":[[169,4]]},"1391":{"position":[[832,4]]},"1455":{"position":[[851,4]]},"1477":{"position":[[886,4]]},"1505":{"position":[[56,4],[10287,4]]},"1619":{"position":[[579,4]]},"1668":{"position":[[128,4],[762,4]]},"1670":{"position":[[1202,4]]},"1843":{"position":[[85,4],[1189,5]]},"1853":{"position":[[172,4]]},"1855":{"position":[[193,4]]},"1921":{"position":[[277,4]]},"2027":{"position":[[482,4]]},"2098":{"position":[[178,4]]},"2104":{"position":[[555,4]]},"2234":{"position":[[826,4]]},"2287":{"position":[[854,4]]},"2309":{"position":[[928,4]]},"2340":{"position":[[542,4]]},"2342":{"position":[[169,4],[338,4],[428,4]]},"2424":{"position":[[224,4]]},"2482":{"position":[[13,4]]},"2492":{"position":[[97,4]]},"2516":{"position":[[169,4]]},"2562":{"position":[[851,4]]},"2686":{"position":[[57,4],[10406,4]]},"2804":{"position":[[579,4]]},"2832":{"position":[[128,4],[768,4]]},"2834":{"position":[[1208,4]]},"3072":{"position":[[88,4],[1477,5]]},"3082":{"position":[[172,4]]},"3084":{"position":[[193,4]]},"3148":{"position":[[277,4]]},"3216":{"position":[[482,4]]},"3283":{"position":[[178,4]]},"3363":{"position":[[698,4]]},"3365":{"position":[[4032,4]]},"3367":{"position":[[308,4]]},"3498":{"position":[[824,4]]},"3522":{"position":[[988,4]]},"3584":{"position":[[826,4]]},"3625":{"position":[[542,4]]},"3627":{"position":[[169,4],[338,4],[428,4]]}}}],["verif",{"_index":2922,"t":{"327":{"position":[[826,12]]},"1189":{"position":[[472,13]]},"2088":{"position":[[241,13]]},"2094":{"position":[[226,13]]},"2974":{"position":[[307,12]]},"3251":{"position":[[166,12]]},"3273":{"position":[[241,13]]},"3279":{"position":[[226,13]]}}}],["verifi",{"_index":1698,"t":{"110":{"position":[[63,6]]},"192":{"position":[[2362,6]]},"327":{"position":[[644,9]]},"329":{"position":[[670,9]]},"810":{"position":[[368,8]]},"1825":{"position":[[368,8],[635,8]]},"2088":{"position":[[1057,6]]},"2094":{"position":[[1114,6]]},"2974":{"position":[[125,9]]},"3040":{"position":[[343,8]]},"3054":{"position":[[368,8],[635,8]]},"3273":{"position":[[949,6]]},"3279":{"position":[[1006,6]]}}}],["versa",{"_index":1323,"t":{"68":{"position":[[507,6]]},"2574":{"position":[[750,6]]}}}],["version",{"_index":846,"t":{"24":{"position":[[219,7],[607,7]]},"26":{"position":[[1978,8]]},"46":{"position":[[441,7]]},"50":{"position":[[216,7]]},"52":{"position":[[82,7],[128,8],[1011,7]]},"74":{"position":[[1823,9]]},"78":{"position":[[1141,7]]},"82":{"position":[[1662,8]]},"128":{"position":[[512,8]]},"136":{"position":[[123,8]]},"146":{"position":[[67,7],[80,7],[326,7]]},"156":{"position":[[122,8]]},"192":{"position":[[2850,7]]},"232":{"position":[[897,11],[916,7],[1033,7],[1097,7],[1153,8]]},"256":{"position":[[198,8],[265,7]]},"258":{"position":[[1549,8]]},"313":{"position":[[88,7]]},"317":{"position":[[119,7]]},"321":{"position":[[2310,8],[2402,7]]},"335":{"position":[[688,9],[860,7],[884,7]]},"391":{"position":[[82,7]]},"479":{"position":[[5689,7]]},"509":{"position":[[598,7],[634,7]]},"517":{"position":[[149,8]]},"541":{"position":[[47,7]]},"543":{"position":[[86,7]]},"549":{"position":[[80,8],[217,7]]},"553":{"position":[[975,7]]},"555":{"position":[[597,8]]},"614":{"position":[[189,7]]},"674":{"position":[[235,9],[1014,9]]},"862":{"position":[[19,7],[52,7]]},"945":{"position":[[213,7]]},"977":{"position":[[445,7],[487,7]]},"1017":{"position":[[2832,7],[2860,7]]},"1134":{"position":[[279,7],[309,7]]},"1187":{"position":[[332,7]]},"1207":{"position":[[553,7]]},"1251":{"position":[[349,8]]},"1349":{"position":[[82,7]]},"1359":{"position":[[60,8],[148,8],[969,7],[1750,8]]},"1407":{"position":[[1141,10],[1271,7],[1288,7]]},"1437":{"position":[[831,7],[867,7]]},"1441":{"position":[[202,8]]},"1445":{"position":[[149,8]]},"1479":{"position":[[458,10]]},"1505":{"position":[[3330,7]]},"1527":{"position":[[1459,7]]},"1529":{"position":[[646,8]]},"1557":{"position":[[47,7]]},"1559":{"position":[[86,7]]},"1565":{"position":[[80,8],[217,7]]},"1599":{"position":[[568,7]]},"1605":{"position":[[226,9],[925,9]]},"1623":{"position":[[420,7]]},"1839":{"position":[[80,9]]},"1903":{"position":[[19,7],[52,7]]},"2066":{"position":[[213,7]]},"2070":{"position":[[445,7],[487,7]]},"2084":{"position":[[332,7]]},"2110":{"position":[[553,7]]},"2189":{"position":[[598,8]]},"2247":{"position":[[279,7],[309,7]]},"2311":{"position":[[458,10]]},"2354":{"position":[[2949,7],[2977,7]]},"2424":{"position":[[1449,8]]},"2464":{"position":[[86,7]]},"2500":{"position":[[349,8]]},"2530":{"position":[[60,8],[148,8],[969,7],[1750,8]]},"2574":{"position":[[458,8],[727,8]]},"2578":{"position":[[295,7]]},"2606":{"position":[[855,7],[891,7]]},"2610":{"position":[[202,8]]},"2614":{"position":[[149,8]]},"2624":{"position":[[480,7]]},"2686":{"position":[[3347,7]]},"2726":{"position":[[47,7]]},"2728":{"position":[[86,7]]},"2734":{"position":[[80,8],[217,7]]},"2762":{"position":[[259,8]]},"2764":{"position":[[645,8]]},"2782":{"position":[[568,7]]},"2790":{"position":[[226,9],[925,9]]},"2810":{"position":[[420,7]]},"3068":{"position":[[53,8],[112,8],[694,9]]},"3160":{"position":[[19,7],[52,7]]},"3261":{"position":[[213,7]]},"3269":{"position":[[332,7]]},"3293":{"position":[[553,7]]},"3307":{"position":[[445,7],[487,7]]},"3324":{"position":[[1607,7],[1697,7]]},"3341":{"position":[[520,7]]},"3415":{"position":[[2949,7],[2977,7]]},"3492":{"position":[[598,8]]},"3524":{"position":[[274,10]]},"3597":{"position":[[279,7],[309,7]]},"3635":{"position":[[480,7]]}}}],["via",{"_index":1328,"t":{"70":{"position":[[140,3]]},"78":{"position":[[1224,3]]},"160":{"position":[[833,3]]},"216":{"position":[[4239,3]]},"280":{"position":[[1411,3]]},"321":{"position":[[680,3]]},"341":{"position":[[512,4]]},"389":{"position":[[88,3]]},"425":{"position":[[209,3],[235,3]]},"435":{"position":[[72,4]]},"640":{"position":[[1034,3]]},"814":{"position":[[124,3]]},"878":{"position":[[241,3]]},"963":{"position":[[39,4]]},"1013":{"position":[[518,3]]},"1191":{"position":[[143,3]]},"1211":{"position":[[512,4]]},"1251":{"position":[[209,3],[235,3]]},"1261":{"position":[[72,4]]},"1347":{"position":[[88,3]]},"1591":{"position":[[1316,3]]},"1973":{"position":[[39,4]]},"1991":{"position":[[241,3]]},"2090":{"position":[[143,3]]},"2350":{"position":[[518,3]]},"2394":{"position":[[512,4]]},"2462":{"position":[[95,3]]},"2500":{"position":[[209,3],[235,3]]},"2510":{"position":[[72,4]]},"2784":{"position":[[1316,3]]},"3180":{"position":[[241,3]]},"3275":{"position":[[143,3]]},"3301":{"position":[[39,4]]},"3320":{"position":[[39,4]]},"3411":{"position":[[518,3]]}}}],["viabl",{"_index":2040,"t":{"172":{"position":[[1269,6]]},"2526":{"position":[[1895,6]]}}}],["vice",{"_index":1322,"t":{"68":{"position":[[502,4]]},"2574":{"position":[[745,4]]}}}],["video",{"_index":1965,"t":{"156":{"position":[[77,6]]},"228":{"position":[[192,6]]},"581":{"position":[[45,6],[60,5]]},"583":{"position":[[45,6],[60,5]]},"1191":{"position":[[1758,6]]},"1455":{"position":[[1414,5]]},"1549":{"position":[[45,6],[60,5]]},"1551":{"position":[[45,6],[60,5]]},"2090":{"position":[[1758,6]]},"2287":{"position":[[1417,5]]},"2392":{"position":[[1318,5]]},"2754":{"position":[[45,6],[60,5]]},"2756":{"position":[[45,6],[60,5]]},"3275":{"position":[[1758,6]]},"3612":{"position":[[1345,5]]}}}],["view",{"_index":2016,"t":{"166":{"position":[[8,4]]},"168":{"position":[[195,6]]},"176":{"position":[[104,4]]},"182":{"position":[[204,4],[279,4]]},"192":{"position":[[29,5],[44,4],[330,5],[1322,4],[1349,5],[1519,5],[2384,4],[3179,4],[3258,4]]},"194":{"position":[[30,5],[43,4],[312,4],[339,4],[1927,4],[1954,4],[2216,4],[2867,4]]},"200":{"position":[[1964,4]]},"246":{"position":[[999,5],[1454,5]]},"409":{"position":[[682,4]]},"1233":{"position":[[682,4]]},"2482":{"position":[[682,4]]},"3367":{"position":[[32,4]]}}}],["views.connect",{"_index":2330,"t":{"200":{"position":[[547,14]]}}}],["views.index",{"_index":2191,"t":{"192":{"position":[[1937,12]]},"194":{"position":[[2333,12]]},"200":{"position":[[421,12]]}}}],["views.pi",{"_index":2138,"t":{"190":{"position":[[340,8],[604,8]]},"192":{"position":[[456,8],[1779,11]]},"194":{"position":[[292,8]]}}}],["views.publish",{"_index":2336,"t":{"200":{"position":[[673,14]]}}}],["views.room",{"_index":2277,"t":{"194":{"position":[[2405,11]]},"200":{"position":[[493,11]]}}}],["views.subscrib",{"_index":2333,"t":{"200":{"position":[[609,16]]}}}],["views/app.html",{"_index":2582,"t":{"246":{"position":[[1468,14]]}}}],["views/login.html",{"_index":2566,"t":{"246":{"position":[[1013,16]]}}}],["viewsurlpattern",{"_index":2190,"t":{"192":{"position":[[1907,16]]},"194":{"position":[[2303,16]]},"200":{"position":[[391,16]]}}}],["virtual",{"_index":4108,"t":{"951":{"position":[[893,7]]},"953":{"position":[[899,7]]},"1921":{"position":[[893,7]]},"1923":{"position":[[899,7]]},"2088":{"position":[[1164,7]]},"2094":{"position":[[1221,7]]},"3148":{"position":[[893,7]]},"3150":{"position":[[899,7]]},"3273":{"position":[[1056,7]]},"3279":{"position":[[1113,7]]}}}],["virtualenv",{"_index":2110,"t":{"186":{"position":[[369,11]]}}}],["visibl",{"_index":1892,"t":{"134":{"position":[[960,7]]},"230":{"position":[[94,7]]},"409":{"position":[[903,7]]},"1233":{"position":[[903,7]]},"2482":{"position":[[903,7]]}}}],["visual",{"_index":1789,"t":{"118":{"position":[[858,15]]},"311":{"position":[[865,15]]},"475":{"position":[[5873,10]]},"477":{"position":[[4653,10]]},"965":{"position":[[159,10]]},"1975":{"position":[[159,10]]},"3303":{"position":[[159,10]]},"3322":{"position":[[159,10]]}}}],["vitali",{"_index":1946,"t":{"152":{"position":[[151,6]]}}}],["vite",{"_index":2400,"t":{"214":{"position":[[719,4]]},"216":{"position":[[53,4],[390,4]]}}}],["vite@latest",{"_index":2405,"t":{"216":{"position":[[75,11]]}}}],["vladimir",{"_index":3208,"t":{"475":{"position":[[184,8]]}}}],["void",{"_index":5297,"t":{"2622":{"position":[[2722,4],[2830,4],[2965,4]]},"2632":{"position":[[2458,4],[2595,4],[2731,4]]},"2636":{"position":[[2178,4]]},"3633":{"position":[[2722,4],[2830,4],[2965,4]]},"3643":{"position":[[2458,4],[2595,4],[2731,4]]},"3647":{"position":[[2178,4]]}}}],["volum",{"_index":2740,"t":{"280":{"position":[[330,6]]},"513":{"position":[[273,8]]},"1441":{"position":[[297,8]]},"2610":{"position":[[297,8]]},"2708":{"position":[[1766,7]]}}}],["vuej",{"_index":2097,"t":{"178":{"position":[[151,5]]}}}],["vulner",{"_index":4184,"t":{"1017":{"position":[[598,10]]},"2354":{"position":[[598,10]]},"3415":{"position":[[598,10]]}}}],["vvv",{"_index":1645,"t":{"104":{"position":[[594,3]]},"106":{"position":[[1136,3],[1202,3],[1730,3]]},"110":{"position":[[777,3]]}}}],["vvvpost",{"_index":4571,"t":{"1457":{"position":[[899,7]]},"2289":{"position":[[959,7]]}}}],["vyxywx1p1lv9uc9cau04vpa6lgg5gsw5lz1iw",{"_index":4336,"t":{"1147":{"position":[[659,37]]},"2260":{"position":[[668,37]]},"3610":{"position":[[668,37]]}}}],["w3c",{"_index":2914,"t":{"325":{"position":[[425,3]]}}}],["wait",{"_index":778,"t":{"18":{"position":[[2223,4]]},"70":{"position":[[71,7]]},"116":{"position":[[232,4]]},"176":{"position":[[1126,4]]},"264":{"position":[[1368,7]]},"469":{"position":[[614,5]]},"471":{"position":[[2678,5]]},"481":{"position":[[1435,7],[1831,5]]},"912":{"position":[[396,4]]},"953":{"position":[[636,4]]},"1419":{"position":[[404,4]]},"1575":{"position":[[86,4]]},"1589":{"position":[[86,4]]},"1923":{"position":[[636,4]]},"2027":{"position":[[396,4]]},"2029":{"position":[[82,4]]},"2150":{"position":[[2163,4],[2279,4]]},"2191":{"position":[[653,5]]},"2626":{"position":[[211,4]]},"2670":{"position":[[86,4]]},"2684":{"position":[[86,4]]},"3150":{"position":[[636,4]]},"3216":{"position":[[396,4]]},"3218":{"position":[[82,4]]},"3367":{"position":[[782,5],[4009,5]]},"3379":{"position":[[2163,4],[2279,4]]},"3466":{"position":[[653,5]]},"3637":{"position":[[211,4]]}}}],["wanicon",{"_index":1158,"t":{"54":{"position":[[768,7]]}}}],["wanna",{"_index":419,"t":{"10":{"position":[[625,5]]}}}],["want",{"_index":605,"t":{"16":{"position":[[1429,4],[2462,4]]},"18":{"position":[[2162,4],[2324,4]]},"54":{"position":[[307,4]]},"60":{"position":[[263,4]]},"66":{"position":[[861,4]]},"72":{"position":[[141,4]]},"80":{"position":[[46,4]]},"82":{"position":[[7,4]]},"86":{"position":[[470,4]]},"112":{"position":[[249,4]]},"124":{"position":[[1501,4]]},"130":{"position":[[1676,4]]},"148":{"position":[[686,4]]},"158":{"position":[[29,4]]},"184":{"position":[[28,4]]},"198":{"position":[[173,4]]},"200":{"position":[[2064,4]]},"218":{"position":[[282,4],[672,4]]},"240":{"position":[[14,4]]},"242":{"position":[[296,4]]},"258":{"position":[[1657,4],[1724,4]]},"270":{"position":[[159,4],[1257,5]]},"280":{"position":[[3774,4]]},"285":{"position":[[41,4]]},"287":{"position":[[1297,4]]},"293":{"position":[[520,4],[564,4]]},"327":{"position":[[296,4]]},"393":{"position":[[623,4]]},"413":{"position":[[141,4]]},"429":{"position":[[1589,4]]},"473":{"position":[[3109,6]]},"497":{"position":[[193,4],[509,4]]},"503":{"position":[[60,4]]},"608":{"position":[[73,4]]},"632":{"position":[[406,4],[1043,4]]},"636":{"position":[[112,4]]},"652":{"position":[[304,4],[901,4]]},"666":{"position":[[7,4],[254,4]]},"680":{"position":[[100,4]]},"688":{"position":[[94,4],[153,4]]},"750":{"position":[[127,4]]},"774":{"position":[[167,4]]},"776":{"position":[[678,4]]},"788":{"position":[[50,4]]},"798":{"position":[[1837,4]]},"818":{"position":[[210,4]]},"820":{"position":[[384,4]]},"858":{"position":[[625,4]]},"939":{"position":[[255,4]]},"949":{"position":[[82,4]]},"977":{"position":[[246,4]]},"999":{"position":[[195,4]]},"1013":{"position":[[297,4]]},"1015":{"position":[[75,4]]},"1017":{"position":[[947,4],[1230,4],[3219,4]]},"1023":{"position":[[3832,4]]},"1025":{"position":[[3171,4]]},"1057":{"position":[[355,4]]},"1063":{"position":[[715,4]]},"1160":{"position":[[1426,4]]},"1162":{"position":[[245,4],[297,4],[355,4]]},"1191":{"position":[[912,5]]},"1237":{"position":[[141,4]]},"1255":{"position":[[1589,4]]},"1271":{"position":[[7,4]]},"1359":{"position":[[466,4]]},"1391":{"position":[[190,4],[508,4]]},"1397":{"position":[[60,4]]},"1455":{"position":[[1044,4]]},"1485":{"position":[[240,4]]},"1505":{"position":[[9334,4]]},"1571":{"position":[[733,4]]},"1617":{"position":[[171,4]]},"1631":{"position":[[406,4],[1043,4]]},"1636":{"position":[[112,4]]},"1647":{"position":[[304,4],[901,4]]},"1660":{"position":[[7,4],[254,4]]},"1666":{"position":[[744,4]]},"1731":{"position":[[94,4],[153,4]]},"1753":{"position":[[195,4]]},"1763":{"position":[[610,4]]},"1771":{"position":[[2103,4]]},"1773":{"position":[[1322,4]]},"1775":{"position":[[424,4]]},"1777":{"position":[[570,4]]},"1789":{"position":[[167,4]]},"1791":{"position":[[678,4]]},"1803":{"position":[[50,4]]},"1813":{"position":[[1837,4]]},"1837":{"position":[[384,4]]},"1843":{"position":[[625,4]]},"1861":{"position":[[478,4]]},"1885":{"position":[[356,4]]},"1919":{"position":[[82,4]]},"1929":{"position":[[346,4]]},"2060":{"position":[[255,4]]},"2070":{"position":[[246,4]]},"2090":{"position":[[912,5]]},"2104":{"position":[[187,4]]},"2140":{"position":[[355,4]]},"2166":{"position":[[280,4]]},"2168":{"position":[[765,4]]},"2287":{"position":[[1047,4]]},"2317":{"position":[[240,4]]},"2340":{"position":[[1426,4]]},"2342":{"position":[[245,4],[297,4],[355,4]]},"2350":{"position":[[297,4]]},"2352":{"position":[[75,4]]},"2354":{"position":[[1064,4],[1347,4],[3336,4],[6120,4]]},"2360":{"position":[[3863,4]]},"2362":{"position":[[3171,4]]},"2364":{"position":[[1274,4]]},"2400":{"position":[[348,4]]},"2486":{"position":[[141,4]]},"2504":{"position":[[1589,4]]},"2520":{"position":[[7,4]]},"2530":{"position":[[466,4]]},"2562":{"position":[[193,4],[517,4]]},"2568":{"position":[[61,4]]},"2622":{"position":[[5200,4]]},"2632":{"position":[[4756,4]]},"2634":{"position":[[762,4]]},"2638":{"position":[[710,4]]},"2646":{"position":[[150,4]]},"2666":{"position":[[733,4]]},"2686":{"position":[[9481,4]]},"2802":{"position":[[171,4]]},"2818":{"position":[[406,4],[1043,4]]},"2823":{"position":[[112,4]]},"2830":{"position":[[747,4]]},"2899":{"position":[[304,4],[901,4]]},"2912":{"position":[[7,4],[254,4]]},"2920":{"position":[[94,4],[153,4]]},"2930":{"position":[[2103,4]]},"2932":{"position":[[1322,4]]},"2934":{"position":[[424,4]]},"2936":{"position":[[570,4]]},"2958":{"position":[[195,4]]},"2968":{"position":[[610,4]]},"3020":{"position":[[731,4]]},"3040":{"position":[[115,4]]},"3072":{"position":[[913,4]]},"3092":{"position":[[478,4]]},"3116":{"position":[[356,4]]},"3146":{"position":[[82,4]]},"3156":{"position":[[346,4]]},"3255":{"position":[[255,4]]},"3275":{"position":[[912,5]]},"3307":{"position":[[246,4]]},"3327":{"position":[[953,4]]},"3355":{"position":[[355,4]]},"3395":{"position":[[441,4]]},"3397":{"position":[[765,4]]},"3411":{"position":[[297,4]]},"3413":{"position":[[75,4]]},"3415":{"position":[[1064,4],[1347,4],[3336,4],[6149,4]]},"3421":{"position":[[3866,4]]},"3423":{"position":[[3171,4]]},"3425":{"position":[[1247,4]]},"3530":{"position":[[240,4]]},"3625":{"position":[[1426,4]]},"3627":{"position":[[245,4],[297,4],[355,4]]},"3633":{"position":[[5200,4]]},"3643":{"position":[[4756,4]]},"3645":{"position":[[762,4]]},"3649":{"position":[[710,4]]},"3657":{"position":[[150,4]]}}}],["warn",{"_index":2930,"t":{"329":{"position":[[547,4],[1239,8]]},"549":{"position":[[613,7]]},"1565":{"position":[[613,7]]},"1583":{"position":[[162,4]]},"2021":{"position":[[252,7]]},"2678":{"position":[[162,4]]},"2734":{"position":[[613,7]]},"3210":{"position":[[225,7]]},"3220":{"position":[[1039,7]]},"3329":{"position":[[116,4]]}}}],["wasm",{"_index":357,"t":{"8":{"position":[[1136,4]]}}}],["wasn't",{"_index":1877,"t":{"130":{"position":[[1519,6]]}}}],["wasrecov",{"_index":5428,"t":{"2638":{"position":[[563,13],[1032,13]]},"3649":{"position":[[563,13],[1032,13]]}}}],["watch",{"_index":2209,"t":{"192":{"position":[[2504,8]]},"1527":{"position":[[120,8]]},"2760":{"position":[[120,8]]}}}],["way",{"_index":228,"t":{"4":{"position":[[214,3]]},"12":{"position":[[1531,3]]},"16":{"position":[[562,3]]},"32":{"position":[[603,4]]},"38":{"position":[[2131,3]]},"50":{"position":[[2190,3]]},"60":{"position":[[1371,3]]},"62":{"position":[[414,3]]},"64":{"position":[[142,4],[743,3],[957,3],[1207,3]]},"74":{"position":[[1981,3]]},"78":{"position":[[3413,5]]},"86":{"position":[[1265,3]]},"90":{"position":[[614,3]]},"100":{"position":[[167,3]]},"108":{"position":[[595,4],[1077,4]]},"124":{"position":[[1620,3],[4296,4]]},"128":{"position":[[960,3]]},"132":{"position":[[1726,3]]},"134":{"position":[[926,3]]},"138":{"position":[[471,3]]},"172":{"position":[[915,3]]},"176":{"position":[[1845,3],[1927,5]]},"186":{"position":[[318,3]]},"232":{"position":[[1133,3]]},"242":{"position":[[75,4]]},"258":{"position":[[1531,4]]},"270":{"position":[[771,3],[1111,3],[2069,3]]},"272":{"position":[[28,3],[494,3]]},"274":{"position":[[246,3]]},"276":{"position":[[123,3]]},"285":{"position":[[327,4],[501,4],[601,4]]},"287":{"position":[[356,5]]},"289":{"position":[[739,3]]},"291":{"position":[[99,3]]},"293":{"position":[[71,3]]},"295":{"position":[[106,3],[295,3],[1146,3]]},"297":{"position":[[273,3]]},"305":{"position":[[6,3]]},"315":{"position":[[579,3]]},"321":{"position":[[1075,3]]},"323":{"position":[[277,3]]},"327":{"position":[[58,3],[369,3]]},"345":{"position":[[1379,4]]},"347":{"position":[[1426,4],[1660,3]]},"349":{"position":[[551,3]]},"353":{"position":[[48,3],[393,3]]},"357":{"position":[[672,3]]},"373":{"position":[[48,3]]},"389":{"position":[[35,5]]},"399":{"position":[[449,3]]},"411":{"position":[[59,3],[1251,3]]},"415":{"position":[[514,3],[554,3]]},"429":{"position":[[433,3],[891,3],[985,4]]},"431":{"position":[[18,4],[819,3]]},"435":{"position":[[107,3]]},"459":{"position":[[101,3]]},"473":{"position":[[729,4]]},"475":{"position":[[1120,3],[1611,3],[2584,3]]},"477":{"position":[[1512,3]]},"481":{"position":[[242,3]]},"489":{"position":[[267,3]]},"493":{"position":[[866,3]]},"495":{"position":[[160,3],[258,3]]},"509":{"position":[[37,3]]},"525":{"position":[[617,3]]},"553":{"position":[[115,3]]},"555":{"position":[[187,5]]},"563":{"position":[[843,3]]},"602":{"position":[[201,3]]},"612":{"position":[[172,4]]},"666":{"position":[[305,3]]},"680":{"position":[[16,3]]},"698":{"position":[[325,3]]},"726":{"position":[[174,3]]},"798":{"position":[[2051,3]]},"842":{"position":[[687,3]]},"858":{"position":[[260,3]]},"868":{"position":[[295,3]]},"874":{"position":[[40,5]]},"876":{"position":[[357,4]]},"880":{"position":[[317,3]]},"892":{"position":[[659,4],[1085,3]]},"969":{"position":[[306,3]]},"977":{"position":[[545,5]]},"1021":{"position":[[311,4]]},"1029":{"position":[[117,4],[638,3]]},"1055":{"position":[[9,3]]},"1069":{"position":[[602,3]]},"1098":{"position":[[93,3]]},"1193":{"position":[[33,3]]},"1203":{"position":[[412,4],[513,3]]},"1223":{"position":[[449,3]]},"1235":{"position":[[59,3],[1251,3]]},"1239":{"position":[[514,3],[554,3]]},"1255":{"position":[[433,3],[891,3],[985,4]]},"1257":{"position":[[18,4],[819,3]]},"1261":{"position":[[107,3]]},"1269":{"position":[[96,3]]},"1277":{"position":[[1379,4]]},"1279":{"position":[[1431,4],[1665,3]]},"1281":{"position":[[564,3]]},"1285":{"position":[[48,3],[393,3]]},"1289":{"position":[[672,3]]},"1311":{"position":[[101,3]]},"1331":{"position":[[48,3]]},"1347":{"position":[[35,5]]},"1355":{"position":[[625,3]]},"1383":{"position":[[267,3]]},"1387":{"position":[[867,3]]},"1389":{"position":[[169,3],[335,3]]},"1403":{"position":[[819,3],[2322,3]]},"1407":{"position":[[1064,4]]},"1423":{"position":[[149,3]]},"1425":{"position":[[190,3]]},"1455":{"position":[[230,4],[556,4]]},"1477":{"position":[[701,3]]},"1505":{"position":[[5981,3],[7556,4]]},"1515":{"position":[[198,4]]},"1529":{"position":[[187,5]]},"1571":{"position":[[1520,3]]},"1575":{"position":[[76,3]]},"1589":{"position":[[76,3]]},"1617":{"position":[[16,3]]},"1660":{"position":[[305,3]]},"1666":{"position":[[37,3],[1384,3],[1882,3]]},"1670":{"position":[[637,4]]},"1672":{"position":[[421,3],[753,3]]},"1682":{"position":[[28,3],[668,3]]},"1686":{"position":[[254,3]]},"1813":{"position":[[2051,3]]},"1831":{"position":[[952,3]]},"1843":{"position":[[260,3]]},"1859":{"position":[[1040,3]]},"1909":{"position":[[295,3]]},"1911":{"position":[[356,3]]},"1929":{"position":[[448,4]]},"1933":{"position":[[325,3]]},"1953":{"position":[[127,3]]},"1961":{"position":[[174,3]]},"1979":{"position":[[365,3]]},"1983":{"position":[[2189,4],[4412,3]]},"1987":{"position":[[40,5]]},"1989":{"position":[[357,4]]},"1993":{"position":[[317,3]]},"2005":{"position":[[659,4],[1085,3]]},"2070":{"position":[[545,5]]},"2092":{"position":[[33,3]]},"2106":{"position":[[412,4],[513,3]]},"2132":{"position":[[602,3]]},"2138":{"position":[[9,3]]},"2160":{"position":[[149,3]]},"2234":{"position":[[93,3]]},"2287":{"position":[[233,4],[559,4]]},"2309":{"position":[[748,3]]},"2358":{"position":[[311,4]]},"2364":{"position":[[1060,3]]},"2368":{"position":[[117,4],[838,3]]},"2392":{"position":[[1105,4]]},"2400":{"position":[[2630,3]]},"2402":{"position":[[1839,3]]},"2404":{"position":[[588,3]]},"2408":{"position":[[48,3],[399,4]]},"2412":{"position":[[685,3]]},"2446":{"position":[[48,3]]},"2472":{"position":[[449,3]]},"2484":{"position":[[59,3],[1251,3]]},"2488":{"position":[[514,3],[554,3]]},"2504":{"position":[[433,3],[891,3],[985,4]]},"2506":{"position":[[18,4],[819,3]]},"2510":{"position":[[107,3]]},"2518":{"position":[[96,3]]},"2526":{"position":[[613,3]]},"2554":{"position":[[278,3]]},"2558":{"position":[[910,3]]},"2560":{"position":[[174,3],[344,3]]},"2598":{"position":[[106,3]]},"2654":{"position":[[18,3],[113,3]]},"2666":{"position":[[1520,3]]},"2670":{"position":[[76,3]]},"2684":{"position":[[76,3]]},"2686":{"position":[[6045,3],[7638,4]]},"2708":{"position":[[2243,3]]},"2718":{"position":[[98,3]]},"2764":{"position":[[187,5]]},"2774":{"position":[[198,4]]},"2802":{"position":[[16,3]]},"2830":{"position":[[37,3],[1387,3],[1885,3]]},"2834":{"position":[[637,4]]},"2836":{"position":[[421,3],[753,3]]},"2846":{"position":[[28,3],[668,3]]},"2850":{"position":[[254,3]]},"2912":{"position":[[305,3]]},"2978":{"position":[[325,3]]},"2990":{"position":[[339,3]]},"2998":{"position":[[127,3]]},"3006":{"position":[[174,3]]},"3072":{"position":[[268,3],[542,3]]},"3090":{"position":[[1040,3]]},"3138":{"position":[[365,3]]},"3142":{"position":[[2189,4],[4412,3]]},"3156":{"position":[[448,4]]},"3166":{"position":[[295,3]]},"3168":{"position":[[356,3]]},"3176":{"position":[[40,5]]},"3178":{"position":[[365,4]]},"3182":{"position":[[321,3]]},"3194":{"position":[[659,4],[1085,3]]},"3220":{"position":[[858,3],[1008,4]]},"3277":{"position":[[33,3]]},"3289":{"position":[[412,4],[513,3]]},"3307":{"position":[[545,5]]},"3335":{"position":[[602,3]]},"3353":{"position":[[9,3]]},"3363":{"position":[[1195,3]]},"3365":{"position":[[524,3]]},"3367":{"position":[[4091,3]]},"3389":{"position":[[149,3]]},"3419":{"position":[[358,4]]},"3425":{"position":[[1033,3]]},"3429":{"position":[[117,4],[838,3]]},"3498":{"position":[[229,4],[464,3]]},"3522":{"position":[[808,3]]},"3584":{"position":[[93,3]]},"3612":{"position":[[1132,4]]},"3665":{"position":[[18,3],[113,3]]}}}],["wc",{"_index":4115,"t":{"953":{"position":[[545,2]]},"1923":{"position":[[545,2]]},"3150":{"position":[[545,2]]}}}],["we'll",{"_index":2369,"t":{"208":{"position":[[270,5]]},"465":{"position":[[1212,5]]},"1841":{"position":[[930,5]]}}}],["we'r",{"_index":1885,"t":{"132":{"position":[[928,5]]},"278":{"position":[[888,5]]},"280":{"position":[[2511,5],[3051,5]]},"311":{"position":[[17,5]]},"321":{"position":[[902,5]]}}}],["we'v",{"_index":2528,"t":{"240":{"position":[[257,5]]},"264":{"position":[[160,5]]},"317":{"position":[[130,5]]},"321":{"position":[[1951,5]]},"329":{"position":[[71,5]]}}}],["wealthi",{"_index":2688,"t":{"264":{"position":[[784,7]]}}}],["web",{"_index":248,"t":{"4":{"position":[[608,3]]},"18":{"position":[[970,3]]},"42":{"position":[[985,3]]},"58":{"position":[[415,3]]},"60":{"position":[[1578,3]]},"116":{"position":[[84,3]]},"210":{"position":[[118,3]]},"212":{"position":[[425,3],[714,3]]},"214":{"position":[[586,3],[1033,3]]},"216":{"position":[[4258,3]]},"252":{"position":[[876,3],[928,3],[1036,3]]},"264":{"position":[[302,4]]},"268":{"position":[[560,5]]},"276":{"position":[[42,3]]},"278":{"position":[[287,3]]},"283":{"position":[[611,3]]},"331":{"position":[[1358,3]]},"335":{"position":[[527,3]]},"371":{"position":[[56,3]]},"385":{"position":[[24,3]]},"425":{"position":[[261,3]]},"429":{"position":[[474,3]]},"447":{"position":[[200,3]]},"505":{"position":[[140,3]]},"533":{"position":[[175,3]]},"559":{"position":[[123,3]]},"664":{"position":[[57,3],[140,3],[436,3],[533,3]]},"666":{"position":[[26,3],[64,3],[278,3]]},"668":{"position":[[149,3],[292,3]]},"810":{"position":[[25,3],[388,3]]},"892":{"position":[[550,3]]},"920":{"position":[[6,3],[110,3],[137,3]]},"926":{"position":[[212,3],[275,3],[986,3]]},"939":{"position":[[130,3]]},"977":{"position":[[649,3]]},"981":{"position":[[216,3]]},"1098":{"position":[[68,3]]},"1251":{"position":[[261,3]]},"1255":{"position":[[474,3]]},"1293":{"position":[[668,3]]},"1299":{"position":[[200,3]]},"1329":{"position":[[56,3]]},"1399":{"position":[[140,3]]},"1505":{"position":[[1118,3],[1206,3],[1763,3],[4256,3],[4520,3],[5146,3],[5809,3],[9158,3],[9197,3]]},"1593":{"position":[[123,3]]},"1658":{"position":[[57,3],[140,3],[436,3],[533,3]]},"1660":{"position":[[26,3],[64,3],[278,3]]},"1662":{"position":[[149,3],[292,3]]},"1688":{"position":[[188,3]]},"1703":{"position":[[385,5]]},"1825":{"position":[[25,3],[388,3]]},"2005":{"position":[[550,3]]},"2041":{"position":[[6,3],[110,3],[137,3]]},"2047":{"position":[[212,3],[275,3],[986,3]]},"2060":{"position":[[130,3]]},"2070":{"position":[[649,3]]},"2074":{"position":[[216,3]]},"2234":{"position":[[68,3]]},"2428":{"position":[[713,3]]},"2444":{"position":[[62,3]]},"2500":{"position":[[261,3]]},"2504":{"position":[[474,3]]},"2570":{"position":[[149,3]]},"2586":{"position":[[200,3]]},"2686":{"position":[[1133,3],[1221,3],[1778,3],[4317,3],[4582,3],[5208,3],[5868,3],[9289,3],[9332,3]]},"2776":{"position":[[123,3]]},"2852":{"position":[[188,3]]},"2867":{"position":[[385,5]]},"2910":{"position":[[57,3],[140,3],[436,3],[533,3]]},"2912":{"position":[[26,3],[64,3],[278,3]]},"2914":{"position":[[201,3],[330,3]]},"3042":{"position":[[1241,3]]},"3054":{"position":[[25,3],[388,3]]},"3194":{"position":[[550,3]]},"3232":{"position":[[6,3],[110,3],[137,3]]},"3240":{"position":[[212,3],[275,3],[1076,3]]},"3255":{"position":[[130,3]]},"3307":{"position":[[649,3]]},"3311":{"position":[[216,3]]},"3395":{"position":[[193,4]]},"3584":{"position":[[68,3]]},"3629":{"position":[[1359,3]]}}}],["web.dev",{"_index":1505,"t":{"94":{"position":[[227,7]]},"260":{"position":[[1259,8]]}}}],["webhook",{"_index":5454,"t":{"2708":{"position":[[80,8],[2268,8]]}}}],["webrtc",{"_index":1514,"t":{"94":{"position":[[1062,6]]}}}],["websit",{"_index":2116,"t":{"186":{"position":[[886,7]]},"222":{"position":[[172,8]]}}}],["websocat",{"_index":2943,"t":{"331":{"position":[[297,9]]},"3629":{"position":[[294,9]]}}}],["websocket",{"_index":23,"t":{"2":{"position":[[254,9],[1458,9],[2222,9],[2517,9],[3485,9]]},"4":{"position":[[23,9],[172,9],[242,9],[309,9],[727,9],[781,9],[870,9],[1081,9],[1131,9]]},"6":{"position":[[63,9],[226,9]]},"8":{"position":[[95,9],[542,9],[802,9],[849,9],[999,10],[1321,9],[1394,10],[1611,9]]},"10":{"position":[[241,9],[1313,9]]},"12":{"position":[[167,9],[411,9],[658,9],[904,9]]},"14":{"position":[[74,9],[445,9],[575,10],[618,9],[1039,9],[1085,9],[1307,9],[1401,9],[1512,9]]},"16":{"position":[[829,9],[930,9],[1092,9],[2198,9],[2902,9],[3127,9],[4650,9],[5582,9],[7490,9],[7555,9]]},"18":{"position":[[53,9],[139,9],[277,9],[668,9],[1815,9],[3108,9],[3311,9],[3463,9],[3518,9]]},"20":{"position":[[226,9],[1658,9]]},"32":{"position":[[452,9],[2168,9]]},"46":{"position":[[365,9]]},"58":{"position":[[477,9]]},"60":{"position":[[597,10],[692,9],[858,9],[1698,9]]},"62":{"position":[[343,9],[719,9]]},"68":{"position":[[196,9]]},"70":{"position":[[849,9]]},"72":{"position":[[1119,9]]},"74":{"position":[[1487,9],[1527,9],[1796,9]]},"78":{"position":[[319,9]]},"86":{"position":[[531,9],[914,10],[925,9],[995,11]]},"94":{"position":[[584,9],[676,9]]},"106":{"position":[[892,9]]},"116":{"position":[[1031,9]]},"118":{"position":[[172,9],[601,11]]},"124":{"position":[[0,9],[138,9],[260,9],[664,9],[1116,9],[1164,9],[1314,9],[1442,9],[3055,9],[3574,12],[4347,10],[4493,9],[4626,9]]},"128":{"position":[[73,11],[629,9]]},"142":{"position":[[245,9],[363,10]]},"144":{"position":[[853,9],[1015,12],[1233,9],[1970,10]]},"148":{"position":[[579,9]]},"154":{"position":[[287,9]]},"156":{"position":[[206,9]]},"170":{"position":[[58,9],[599,9],[978,9]]},"172":{"position":[[567,9]]},"174":{"position":[[923,9]]},"176":{"position":[[1103,9]]},"178":{"position":[[974,9]]},"182":{"position":[[295,9]]},"194":{"position":[[2892,9],[3028,9],[3148,9]]},"196":{"position":[[1080,9],[1721,9],[1844,9]]},"200":{"position":[[180,9]]},"202":{"position":[[1326,9]]},"206":{"position":[[326,9]]},"218":{"position":[[201,9]]},"228":{"position":[[128,9]]},"246":{"position":[[1843,9]]},"250":{"position":[[514,9],[1513,9]]},"254":{"position":[[228,9]]},"258":{"position":[[951,9],[1178,9]]},"260":{"position":[[386,9],[686,9],[877,10],[1015,9]]},"264":{"position":[[1457,9]]},"280":{"position":[[1016,9],[1879,9]]},"311":{"position":[[594,11]]},"331":{"position":[[65,9],[103,9],[1071,9],[1223,9],[1482,9]]},"333":{"position":[[358,9]]},"363":{"position":[[369,9]]},"369":{"position":[[36,10],[144,9]]},"379":{"position":[[160,9]]},"397":{"position":[[473,9]]},"399":{"position":[[144,9],[413,9]]},"417":{"position":[[264,10]]},"421":{"position":[[198,9]]},"423":{"position":[[101,10]]},"429":{"position":[[719,9],[911,9]]},"459":{"position":[[161,9]]},"465":{"position":[[133,10]]},"467":{"position":[[1092,12]]},"483":{"position":[[1440,9]]},"493":{"position":[[1065,10],[1148,9]]},"892":{"position":[[86,9],[136,9]]},"918":{"position":[[14,9],[410,9]]},"928":{"position":[[11,9]]},"930":{"position":[[248,9],[679,9]]},"977":{"position":[[510,9]]},"1017":{"position":[[642,9],[2536,10]]},"1019":{"position":[[832,10]]},"1021":{"position":[[259,9],[810,9]]},"1023":{"position":[[2134,9]]},"1025":{"position":[[2168,10]]},"1029":{"position":[[405,9]]},"1039":{"position":[[200,9]]},"1063":{"position":[[219,12]]},"1081":{"position":[[158,11]]},"1092":{"position":[[254,10]]},"1094":{"position":[[700,10]]},"1098":{"position":[[582,10]]},"1134":{"position":[[47,9]]},"1143":{"position":[[48,9]]},"1145":{"position":[[84,9]]},"1147":{"position":[[34,9],[96,9]]},"1152":{"position":[[70,9]]},"1154":{"position":[[88,9],[191,9]]},"1156":{"position":[[89,9],[192,9]]},"1158":{"position":[[193,9]]},"1160":{"position":[[32,9],[88,9],[140,9],[163,9],[286,9],[335,9],[403,9],[569,9],[599,9],[1263,9]]},"1162":{"position":[[546,9],[687,9]]},"1166":{"position":[[178,10]]},"1221":{"position":[[473,9]]},"1223":{"position":[[144,9],[413,9]]},"1241":{"position":[[264,10]]},"1245":{"position":[[258,9]]},"1247":{"position":[[401,9],[554,10]]},"1249":{"position":[[101,10]]},"1255":{"position":[[719,9],[911,9]]},"1311":{"position":[[161,9]]},"1321":{"position":[[373,9]]},"1327":{"position":[[36,10],[80,9],[132,9],[260,9]]},"1337":{"position":[[149,9]]},"1373":{"position":[[155,9]]},"1403":{"position":[[2072,9],[2334,9]]},"1405":{"position":[[2580,9]]},"1407":{"position":[[89,9],[274,9]]},"1421":{"position":[[56,9],[122,9]]},"1431":{"position":[[11,9],[51,9]]},"1505":{"position":[[143,10],[3550,9],[3903,9],[8580,9]]},"1672":{"position":[[879,9],[1039,9]]},"2005":{"position":[[86,9],[136,9]]},"2039":{"position":[[14,9],[625,9]]},"2049":{"position":[[11,9]]},"2051":{"position":[[248,9],[868,9]]},"2070":{"position":[[510,9]]},"2150":{"position":[[407,9]]},"2158":{"position":[[56,9],[106,9]]},"2172":{"position":[[425,9]]},"2185":{"position":[[600,9]]},"2187":{"position":[[254,10]]},"2201":{"position":[[158,11]]},"2217":{"position":[[178,10]]},"2234":{"position":[[582,10]]},"2247":{"position":[[47,9]]},"2256":{"position":[[48,9]]},"2258":{"position":[[84,9]]},"2260":{"position":[[34,9],[96,9]]},"2332":{"position":[[70,9]]},"2334":{"position":[[88,9],[191,9]]},"2336":{"position":[[89,9],[192,9]]},"2338":{"position":[[193,9]]},"2340":{"position":[[32,9],[88,9],[140,9],[163,9],[286,9],[335,9],[403,9],[569,9],[599,9],[1263,9]]},"2342":{"position":[[546,9],[687,9]]},"2354":{"position":[[642,9],[2653,10]]},"2356":{"position":[[832,10]]},"2358":{"position":[[259,9],[810,9]]},"2360":{"position":[[2134,9]]},"2362":{"position":[[2168,10]]},"2364":{"position":[[2233,10]]},"2368":{"position":[[555,9]]},"2378":{"position":[[200,9]]},"2392":{"position":[[1570,9],[1727,12],[1940,9],[2822,10]]},"2418":{"position":[[440,9]]},"2424":{"position":[[194,9],[802,9]]},"2436":{"position":[[389,9]]},"2442":{"position":[[36,10],[80,10],[133,9],[308,9]]},"2452":{"position":[[149,9]]},"2470":{"position":[[473,9]]},"2472":{"position":[[144,9],[413,9]]},"2490":{"position":[[264,10]]},"2494":{"position":[[258,9]]},"2496":{"position":[[401,9],[554,10]]},"2498":{"position":[[101,10]]},"2504":{"position":[[719,9],[911,9]]},"2526":{"position":[[1574,9]]},"2544":{"position":[[155,9]]},"2598":{"position":[[164,9]]},"2624":{"position":[[405,9]]},"2686":{"position":[[144,10],[3583,9],[3961,9],[8681,9]]},"2836":{"position":[[879,9],[1039,9]]},"3194":{"position":[[86,9],[136,9]]},"3220":{"position":[[718,9]]},"3230":{"position":[[14,9],[625,9]]},"3242":{"position":[[11,9]]},"3244":{"position":[[248,9],[955,9]]},"3307":{"position":[[510,9]]},"3379":{"position":[[407,9]]},"3387":{"position":[[56,9],[106,9]]},"3401":{"position":[[425,9]]},"3415":{"position":[[642,9],[2653,10]]},"3417":{"position":[[832,10]]},"3419":{"position":[[259,9],[857,9]]},"3421":{"position":[[1937,9]]},"3423":{"position":[[2168,10]]},"3425":{"position":[[2206,10]]},"3429":{"position":[[555,9]]},"3439":{"position":[[200,9]]},"3462":{"position":[[724,9]]},"3464":{"position":[[254,10]]},"3476":{"position":[[158,11]]},"3567":{"position":[[178,10]]},"3584":{"position":[[582,10]]},"3597":{"position":[[47,9]]},"3606":{"position":[[48,9]]},"3608":{"position":[[84,9]]},"3610":{"position":[[34,9],[96,9]]},"3612":{"position":[[1597,9],[1754,12],[1967,9],[3021,10]]},"3617":{"position":[[70,9]]},"3619":{"position":[[88,9],[191,9]]},"3621":{"position":[[89,9],[192,9]]},"3623":{"position":[[193,9]]},"3625":{"position":[[32,9],[88,9],[140,9],[163,9],[286,9],[335,9],[403,9],[569,9],[599,9],[1263,9]]},"3627":{"position":[[546,9],[687,9]]},"3629":{"position":[[62,9],[100,9],[1072,9],[1224,9],[1483,9]]},"3635":{"position":[[405,9]]}}}],["websocket'",{"_index":488,"t":{"12":{"position":[[301,11]]}}}],["websocket/sockj",{"_index":1265,"t":{"64":{"position":[[1078,16]]}}}],["websocket_compress",{"_index":4348,"t":{"1160":{"position":[[622,21],[1155,21]]},"2340":{"position":[[622,21],[1155,21]]},"3625":{"position":[[622,21],[1155,21]]}}}],["websocket_compression_level",{"_index":4352,"t":{"1160":{"position":[[1467,27]]},"2340":{"position":[[1467,27]]},"3625":{"position":[[1467,27]]}}}],["websocket_compression_min_s",{"_index":4349,"t":{"1160":{"position":[[930,31]]},"2340":{"position":[[930,31]]},"3625":{"position":[[930,31]]}}}],["websocket_dis",{"_index":4069,"t":{"928":{"position":[[34,17]]},"2049":{"position":[[34,17]]},"3242":{"position":[[34,17]]}}}],["websocket_handler_prefix",{"_index":4073,"t":{"930":{"position":[[176,24]]},"2051":{"position":[[176,24]]},"3244":{"position":[[176,24]]}}}],["websocket_message_size_limit",{"_index":3681,"t":{"618":{"position":[[660,28]]}}}],["websocket_ping_interv",{"_index":3677,"t":{"618":{"position":[[537,23],[1039,23],[2095,23]]},"2580":{"position":[[393,23]]}}}],["websocket_read_buffer_s",{"_index":209,"t":{"2":{"position":[[3672,29]]},"1154":{"position":[[307,29]]},"2334":{"position":[[307,29]]},"3619":{"position":[[307,29]]}}}],["websocket_use_write_buffer_pool",{"_index":4346,"t":{"1158":{"position":[[30,31]]},"2338":{"position":[[30,31]]},"3623":{"position":[[30,31]]}}}],["websocket_write_buffer_s",{"_index":210,"t":{"2":{"position":[[3707,30]]},"1156":{"position":[[266,30]]},"2336":{"position":[[266,30]]},"3621":{"position":[[266,30]]}}}],["websocket_write_timeout",{"_index":3679,"t":{"618":{"position":[[601,23],[983,23],[2151,23]]}}}],["websockethandl",{"_index":1262,"t":{"64":{"position":[[703,16]]}}}],["webtransport",{"_index":1212,"t":{"60":{"position":[[1542,12]]},"86":{"position":[[834,12],[898,12]]},"92":{"position":[[8,12],[210,12]]},"94":{"position":[[0,12],[265,12],[391,12],[410,12],[877,12],[1203,12],[1223,12],[1278,12],[1336,12],[1406,12],[1419,12],[1493,12],[1623,12],[1665,12],[1933,12],[2271,12],[3133,12]]},"98":{"position":[[6,12]]},"102":{"position":[[71,12]]},"104":{"position":[[515,12],[598,12]]},"106":{"position":[[1140,12],[1565,12],[1686,12]]},"108":{"position":[[1022,12]]},"110":{"position":[[33,12],[781,12]]},"116":{"position":[[0,12],[361,12],[498,12],[720,12],[953,12]]},"118":{"position":[[340,13]]},"144":{"position":[[120,12],[196,12],[235,12],[342,12],[474,12],[603,12],[670,12],[697,15],[832,12],[919,15],[1316,12],[1465,12],[1735,12],[1816,12]]},"152":{"position":[[520,12],[589,12]]},"311":{"position":[[653,14]]},"465":{"position":[[165,13]]},"1247":{"position":[[308,12],[349,12]]},"1327":{"position":[[291,12]]},"2392":{"position":[[0,12],[13,12],[125,12],[187,12],[280,12],[370,12],[443,12],[561,15],[597,12],[692,12],[1402,12],[1544,12],[1636,15],[1999,12],[2296,12],[2558,12],[2639,12]]},"2442":{"position":[[349,12]]},"2496":{"position":[[308,12],[349,12]]},"3462":{"position":[[891,13]]},"3612":{"position":[[0,12],[13,12],[152,12],[214,12],[307,12],[397,12],[470,12],[588,15],[624,12],[719,12],[1429,12],[1571,12],[1663,15],[2026,12],[2154,12],[2495,12],[2757,12],[2838,12]]}}}],["webtransportserverqu",{"_index":1643,"t":{"104":{"position":[[481,22],[624,22],[780,23],[813,24],[878,24]]},"106":{"position":[[228,24],[657,24],[1217,24]]},"110":{"position":[[1249,24],[1940,24],[2076,24],[4159,24]]},"112":{"position":[[386,24]]}}}],["webtransportserverquic.func",{"_index":1648,"t":{"104":{"position":[[711,27]]}}}],["webtransportserverquic.typ",{"_index":1634,"t":{"104":{"position":[[140,27]]}}}],["wed",{"_index":4612,"t":{"1473":{"position":[[738,4]]},"2305":{"position":[[738,4]]}}}],["week",{"_index":4018,"t":{"868":{"position":[[172,6]]},"1909":{"position":[[172,6]]},"1911":{"position":[[222,6]]},"3166":{"position":[[172,6]]},"3168":{"position":[[222,6]]}}}],["weight",{"_index":3179,"t":{"473":{"position":[[809,6],[988,6]]},"1195":{"position":[[428,6],[536,6]]},"2096":{"position":[[428,6],[536,6]]},"3281":{"position":[[428,6],[536,6]]}}}],["welcom",{"_index":3940,"t":{"792":{"position":[[977,11],[989,8],[1037,11],[1049,8]]},"1217":{"position":[[59,7]]},"1807":{"position":[[504,11],[516,8],[564,11],[576,8]]},"2396":{"position":[[59,7]]},"3036":{"position":[[582,11],[594,8],[642,11],[654,8]]}}}],["well",{"_index":216,"t":{"4":{"position":[[85,4]]},"8":{"position":[[360,5]]},"16":{"position":[[716,5],[4097,4],[4172,5],[4836,4],[5252,4]]},"26":{"position":[[1147,4]]},"58":{"position":[[1103,4],[1156,4]]},"60":{"position":[[12,5]]},"76":{"position":[[1403,4]]},"90":{"position":[[342,4]]},"118":{"position":[[939,4]]},"122":{"position":[[1654,4]]},"132":{"position":[[1685,5]]},"158":{"position":[[610,5]]},"184":{"position":[[310,5]]},"200":{"position":[[0,5]]},"202":{"position":[[1823,4]]},"242":{"position":[[778,4]]},"260":{"position":[[45,5]]},"278":{"position":[[126,4]]},"280":{"position":[[1623,5],[4235,5]]},"285":{"position":[[574,5]]},"305":{"position":[[21,5]]},"311":{"position":[[946,4]]},"347":{"position":[[1230,5]]},"355":{"position":[[394,4]]},"357":{"position":[[597,4]]},"361":{"position":[[182,4],[233,4]]},"415":{"position":[[56,4]]},"431":{"position":[[404,4]]},"465":{"position":[[1232,4]]},"471":{"position":[[758,5]]},"473":{"position":[[2505,4]]},"606":{"position":[[309,4]]},"820":{"position":[[1288,5]]},"1092":{"position":[[31,4]]},"1239":{"position":[[56,4]]},"1257":{"position":[[404,4]]},"1279":{"position":[[1235,5]]},"1287":{"position":[[394,4]]},"1289":{"position":[[597,4]]},"1319":{"position":[[182,4]]},"1323":{"position":[[18,4]]},"1355":{"position":[[1452,4]]},"1405":{"position":[[3842,4]]},"1421":{"position":[[518,4]]},"1837":{"position":[[981,5]]},"2150":{"position":[[1483,4]]},"2187":{"position":[[31,4]]},"2402":{"position":[[1301,5]]},"2410":{"position":[[398,4]]},"2412":{"position":[[605,4]]},"2416":{"position":[[378,4]]},"2422":{"position":[[822,4],[995,4],[1104,4]]},"2434":{"position":[[182,4]]},"2438":{"position":[[18,4]]},"2488":{"position":[[56,4]]},"2506":{"position":[[404,4]]},"2526":{"position":[[1749,4]]},"2556":{"position":[[221,4]]},"3016":{"position":[[81,4]]},"3070":{"position":[[352,5]]},"3363":{"position":[[835,4]]},"3379":{"position":[[1483,4]]},"3464":{"position":[[31,4]]}}}],["went",{"_index":1191,"t":{"58":{"position":[[1542,4]]},"176":{"position":[[2458,4]]},"258":{"position":[[544,4]]},"280":{"position":[[4338,4]]},"477":{"position":[[4772,4]]},"479":{"position":[[3203,4]]},"700":{"position":[[124,4]]},"758":{"position":[[123,4]]},"1935":{"position":[[136,4]]},"2622":{"position":[[4723,4]]},"2632":{"position":[[4342,4]]},"2980":{"position":[[136,4]]},"3633":{"position":[[4723,4]]},"3643":{"position":[[4342,4]]}}}],["wget",{"_index":3600,"t":{"545":{"position":[[41,4]]},"547":{"position":[[41,4]]},"1561":{"position":[[41,4]]},"1563":{"position":[[41,4]]},"2730":{"position":[[41,4]]},"2732":{"position":[[41,4]]}}}],["whant",{"_index":5100,"t":{"1965":{"position":[[1114,6]]},"3010":{"position":[[1114,6]]}}}],["what'",{"_index":1469,"t":{"82":{"position":[[1832,6]]},"132":{"position":[[1691,6]]},"465":{"position":[[840,6]]},"608":{"position":[[1037,6]]},"1395":{"position":[[111,6]]},"2566":{"position":[[115,6]]}}}],["whatev",{"_index":730,"t":{"16":{"position":[[7058,8]]},"112":{"position":[[208,8]]},"280":{"position":[[1768,8]]},"1191":{"position":[[899,8]]},"1505":{"position":[[5972,8]]},"2090":{"position":[[899,8]]},"2718":{"position":[[89,8]]},"3275":{"position":[[899,8]]}}}],["whatsapp",{"_index":2360,"t":{"202":{"position":[[1615,8]]}}}],["what’",{"_index":2732,"t":{"278":{"position":[[820,6]]}}}],["whenev",{"_index":2799,"t":{"287":{"position":[[984,8]]},"305":{"position":[[323,8]]},"686":{"position":[[136,8]]},"858":{"position":[[944,8]]},"1688":{"position":[[715,8]]},"1729":{"position":[[136,8]]},"2708":{"position":[[169,8],[247,8]]},"2852":{"position":[[715,8]]},"2918":{"position":[[136,8]]},"3363":{"position":[[426,8]]}}}],["wherea",{"_index":5547,"t":{"3020":{"position":[[1040,7]]}}}],["whether",{"_index":2092,"t":{"176":{"position":[[1967,7]]},"232":{"position":[[374,7]]},"291":{"position":[[853,7]]},"293":{"position":[[871,7]]},"347":{"position":[[1446,7]]},"473":{"position":[[572,7]]},"479":{"position":[[88,7]]},"608":{"position":[[620,8]]},"640":{"position":[[511,8]]},"973":{"position":[[1568,7]]},"1025":{"position":[[330,7]]},"1136":{"position":[[62,7]]},"1279":{"position":[[1451,7]]},"1407":{"position":[[1320,7]]},"1409":{"position":[[484,7]]},"1413":{"position":[[514,7]]},"1497":{"position":[[456,7]]},"1591":{"position":[[793,8]]},"1709":{"position":[[685,7],[777,7],[867,7],[1057,7]]},"1713":{"position":[[786,7],[1005,7]]},"1717":{"position":[[550,7]]},"1779":{"position":[[11,7]]},"1781":{"position":[[11,7]]},"1783":{"position":[[11,7]]},"1983":{"position":[[1740,7]]},"2249":{"position":[[62,7]]},"2354":{"position":[[6108,7]]},"2362":{"position":[[330,7]]},"2402":{"position":[[1598,7]]},"2422":{"position":[[205,7]]},"2638":{"position":[[423,7],[603,7],[853,7]]},"2692":{"position":[[456,7]]},"2720":{"position":[[54,7]]},"2784":{"position":[[793,8]]},"2873":{"position":[[381,7],[475,7],[567,7]]},"2877":{"position":[[345,7],[444,7]]},"2881":{"position":[[323,7]]},"2938":{"position":[[11,7]]},"2940":{"position":[[11,7]]},"2942":{"position":[[11,7]]},"3142":{"position":[[1740,7]]},"3415":{"position":[[6137,7]]},"3423":{"position":[[330,7]]},"3599":{"position":[[62,7]]},"3649":{"position":[[423,7],[603,7],[853,7]]}}}],["whichev",{"_index":5453,"t":{"2686":{"position":[[6035,9]]}}}],["whitelist",{"_index":4594,"t":{"1465":{"position":[[418,9]]},"2297":{"position":[[384,9]]},"3510":{"position":[[318,9]]}}}],["whose",{"_index":4512,"t":{"1413":{"position":[[129,5]]},"2526":{"position":[[64,5]]}}}],["wide",{"_index":314,"t":{"8":{"position":[[338,6]]},"116":{"position":[[399,4]]},"130":{"position":[[116,6]]},"222":{"position":[[277,6]]},"945":{"position":[[496,6]]},"1575":{"position":[[350,4],[574,4]]},"2066":{"position":[[496,6]]},"2670":{"position":[[350,4],[574,4]]},"3261":{"position":[[496,6]]}}}],["wider",{"_index":894,"t":{"26":{"position":[[1594,5]]}}}],["widget",{"_index":4719,"t":{"1505":{"position":[[348,6]]},"2686":{"position":[[353,6]]}}}],["wiki",{"_index":4060,"t":{"922":{"position":[[348,4]]},"2043":{"position":[[348,4]]},"3234":{"position":[[348,4]]}}}],["wild",{"_index":4132,"t":{"977":{"position":[[222,5]]},"2070":{"position":[[222,5]]},"3307":{"position":[[222,5]]}}}],["wildcard",{"_index":1486,"t":{"86":{"position":[[273,8]]},"136":{"position":[[1108,8]]},"892":{"position":[[823,8]]},"1207":{"position":[[387,8]]},"1571":{"position":[[812,8],[1869,8]]},"1577":{"position":[[21,9],[190,11]]},"1583":{"position":[[95,11]]},"2005":{"position":[[823,8]]},"2110":{"position":[[387,8]]},"2666":{"position":[[812,8],[1869,8]]},"2672":{"position":[[21,9],[190,11]]},"2678":{"position":[[95,11]]},"3194":{"position":[[823,8]]},"3293":{"position":[[387,8]]}}}],["win",{"_index":574,"t":{"14":{"position":[[1504,3]]},"437":{"position":[[2082,4]]},"1263":{"position":[[2086,4]]},"2512":{"position":[[2328,4]]}}}],["window",{"_index":564,"t":{"14":{"position":[[1158,6]]},"16":{"position":[[1740,6],[6704,6]]},"20":{"position":[[1127,6]]},"22":{"position":[[483,6]]},"74":{"position":[[245,8]]},"387":{"position":[[38,8]]},"407":{"position":[[220,6]]},"509":{"position":[[414,7]]},"1057":{"position":[[1380,7]]},"1177":{"position":[[391,6],[960,6],[1262,6]]},"1231":{"position":[[220,6]]},"1345":{"position":[[38,8]]},"1413":{"position":[[953,6]]},"1437":{"position":[[424,7]]},"1505":{"position":[[5312,6]]},"1597":{"position":[[553,6]]},"2140":{"position":[[1380,7]]},"2228":{"position":[[391,6],[960,6]]},"2460":{"position":[[38,8]]},"2480":{"position":[[220,6]]},"2606":{"position":[[439,7]]},"2686":{"position":[[5374,6]]},"2780":{"position":[[553,6]]},"3355":{"position":[[1380,7]]},"3578":{"position":[[391,6],[960,6]]}}}],["window.location.host",{"_index":2079,"t":{"176":{"position":[[1197,20]]},"194":{"position":[[1088,20]]}}}],["window.location.pathnam",{"_index":2178,"t":{"192":{"position":[[1225,24]]}}}],["windows_amd64",{"_index":3524,"t":{"509":{"position":[[422,15]]},"1437":{"position":[[432,15]]},"2606":{"position":[[447,15]]}}}],["wire",{"_index":1208,"t":{"60":{"position":[[1161,4]]},"1160":{"position":[[237,5]]},"1162":{"position":[[318,4]]},"2168":{"position":[[1330,4]]},"2340":{"position":[[237,5]]},"2342":{"position":[[318,4]]},"3397":{"position":[[1330,4]]},"3625":{"position":[[237,5]]},"3627":{"position":[[318,4]]}}}],["wire/backend/centrifugo",{"_index":3031,"t":{"411":{"position":[[226,23]]},"1235":{"position":[[226,23]]},"2484":{"position":[[226,23]]}}}],["wise",{"_index":3921,"t":{"776":{"position":[[893,7]]},"836":{"position":[[571,6]]},"838":{"position":[[412,6]]},"842":{"position":[[589,6]]},"1583":{"position":[[237,6]]},"1791":{"position":[[893,7]]},"1853":{"position":[[571,6]]},"1855":{"position":[[412,6]]},"1859":{"position":[[942,6]]},"2678":{"position":[[237,6]]},"3082":{"position":[[571,6]]},"3084":{"position":[[412,6]]},"3090":{"position":[[942,6]]}}}],["wish",{"_index":3946,"t":{"796":{"position":[[146,4]]},"1811":{"position":[[146,4]]},"3018":{"position":[[149,4]]}}}],["within",{"_index":1847,"t":{"124":{"position":[[4400,6]]},"128":{"position":[[913,6]]},"134":{"position":[[508,6]]},"136":{"position":[[567,6]]},"192":{"position":[[154,6],[251,6]]},"252":{"position":[[1029,6]]},"270":{"position":[[1532,6]]},"331":{"position":[[474,6]]},"349":{"position":[[175,6]]},"1281":{"position":[[178,6]]},"1831":{"position":[[1235,6]]},"2404":{"position":[[183,6]]},"2418":{"position":[[337,6]]},"3036":{"position":[[510,6]]},"3038":{"position":[[150,6],[441,6]]},"3060":{"position":[[976,6],[1321,6]]},"3066":{"position":[[423,6]]},"3070":{"position":[[811,6]]},"3324":{"position":[[677,6]]},"3365":{"position":[[2829,6]]},"3629":{"position":[[475,6]]}}}],["without",{"_index":162,"t":{"2":{"position":[[2539,7]]},"6":{"position":[[372,7]]},"10":{"position":[[2189,7]]},"16":{"position":[[3012,7],[5838,7],[5890,7]]},"18":{"position":[[2329,7],[3473,7]]},"20":{"position":[[1459,7]]},"28":{"position":[[191,7]]},"74":{"position":[[2656,7]]},"94":{"position":[[1037,7]]},"122":{"position":[[931,7]]},"126":{"position":[[811,7]]},"130":{"position":[[710,7],[1720,7]]},"132":{"position":[[87,7]]},"146":{"position":[[189,7]]},"148":{"position":[[628,7]]},"206":{"position":[[373,7]]},"254":{"position":[[274,7]]},"258":{"position":[[280,7],[355,7]]},"260":{"position":[[336,7]]},"264":{"position":[[1360,7],[1626,7]]},"272":{"position":[[239,7]]},"295":{"position":[[148,7],[1898,7]]},"321":{"position":[[1107,7]]},"327":{"position":[[375,7]]},"331":{"position":[[1738,7]]},"333":{"position":[[572,7]]},"339":{"position":[[96,7]]},"341":{"position":[[451,7]]},"345":{"position":[[746,8]]},"349":{"position":[[352,7]]},"361":{"position":[[77,7]]},"413":{"position":[[77,8]]},"429":{"position":[[210,7],[1433,8],[1618,7]]},"447":{"position":[[530,7]]},"465":{"position":[[554,7]]},"473":{"position":[[365,7]]},"475":{"position":[[1710,7],[2625,7],[3450,7]]},"479":{"position":[[4250,7],[5697,7],[5778,7]]},"481":{"position":[[1427,7]]},"493":{"position":[[898,7]]},"525":{"position":[[97,7],[694,7]]},"555":{"position":[[71,7]]},"557":{"position":[[7,7]]},"610":{"position":[[166,7]]},"668":{"position":[[199,7]]},"820":{"position":[[127,7]]},"824":{"position":[[560,7]]},"826":{"position":[[117,7]]},"828":{"position":[[557,7]]},"842":{"position":[[810,7]]},"856":{"position":[[394,7]]},"884":{"position":[[364,7]]},"892":{"position":[[410,7],[456,7]]},"910":{"position":[[72,7],[115,7]]},"935":{"position":[[83,7]]},"937":{"position":[[436,7]]},"945":{"position":[[318,7]]},"951":{"position":[[143,7]]},"977":{"position":[[41,7]]},"1017":{"position":[[183,7],[1734,7]]},"1023":{"position":[[1253,7]]},"1025":{"position":[[1103,7]]},"1031":{"position":[[1300,7]]},"1049":{"position":[[1012,7]]},"1051":{"position":[[881,7]]},"1057":{"position":[[1702,7]]},"1211":{"position":[[451,7]]},"1237":{"position":[[77,8]]},"1255":{"position":[[210,7],[1433,8],[1618,7]]},"1269":{"position":[[408,7]]},"1277":{"position":[[746,8]]},"1281":{"position":[[365,7]]},"1295":{"position":[[183,7],[303,7]]},"1299":{"position":[[530,7]]},"1319":{"position":[[77,7]]},"1355":{"position":[[110,7],[702,7]]},"1367":{"position":[[145,7],[908,7]]},"1387":{"position":[[899,7]]},"1403":{"position":[[1484,7]]},"1421":{"position":[[510,7]]},"1427":{"position":[[192,7]]},"1473":{"position":[[1191,8]]},"1497":{"position":[[966,7]]},"1511":{"position":[[589,8]]},"1521":{"position":[[147,7]]},"1529":{"position":[[71,7]]},"1531":{"position":[[7,7]]},"1662":{"position":[[199,7]]},"1670":{"position":[[1027,7]]},"1682":{"position":[[239,7]]},"1692":{"position":[[885,7]]},"1698":{"position":[[102,7]]},"1771":{"position":[[920,7],[997,7]]},"1837":{"position":[[127,7]]},"1839":{"position":[[554,7],[646,7]]},"1851":{"position":[[120,7]]},"1859":{"position":[[1163,7]]},"1865":{"position":[[827,7]]},"1899":{"position":[[851,7]]},"1921":{"position":[[143,7]]},"1963":{"position":[[135,7]]},"1967":{"position":[[1482,7],[1738,7]]},"1979":{"position":[[246,7]]},"1997":{"position":[[364,7]]},"2005":{"position":[[410,7],[456,7]]},"2031":{"position":[[72,7],[115,7]]},"2056":{"position":[[83,7]]},"2058":{"position":[[436,7]]},"2066":{"position":[[318,7]]},"2070":{"position":[[41,7]]},"2140":{"position":[[1702,7]]},"2168":{"position":[[628,7]]},"2305":{"position":[[1191,8]]},"2354":{"position":[[183,7],[1851,7]]},"2360":{"position":[[1253,7]]},"2362":{"position":[[1103,7]]},"2364":{"position":[[1372,7]]},"2370":{"position":[[1300,7]]},"2388":{"position":[[1228,7]]},"2390":{"position":[[881,7]]},"2392":{"position":[[2119,7]]},"2394":{"position":[[451,7]]},"2400":{"position":[[1468,8],[1807,7],[1892,8]]},"2404":{"position":[[378,7]]},"2430":{"position":[[183,7],[303,7]]},"2434":{"position":[[77,7]]},"2486":{"position":[[77,8]]},"2504":{"position":[[210,7],[1433,8],[1618,7]]},"2518":{"position":[[408,7]]},"2526":{"position":[[687,7]]},"2538":{"position":[[145,7],[908,7]]},"2558":{"position":[[942,7]]},"2586":{"position":[[530,7]]},"2622":{"position":[[831,7]]},"2646":{"position":[[377,7]]},"2660":{"position":[[569,7]]},"2692":{"position":[[966,7]]},"2704":{"position":[[147,7]]},"2708":{"position":[[1894,7]]},"2764":{"position":[[71,7]]},"2770":{"position":[[589,8]]},"2834":{"position":[[1033,7]]},"2846":{"position":[[239,7]]},"2856":{"position":[[885,7]]},"2862":{"position":[[110,7]]},"2914":{"position":[[215,7],[365,7]]},"2930":{"position":[[920,7],[997,7]]},"3008":{"position":[[135,7]]},"3012":{"position":[[1482,7],[1738,7]]},"3080":{"position":[[120,7]]},"3090":{"position":[[1163,7]]},"3096":{"position":[[827,7]]},"3134":{"position":[[851,7]]},"3138":{"position":[[246,7]]},"3148":{"position":[[143,7]]},"3186":{"position":[[364,7]]},"3194":{"position":[[410,7],[456,7]]},"3222":{"position":[[72,7],[115,7]]},"3249":{"position":[[83,7]]},"3253":{"position":[[436,7]]},"3261":{"position":[[318,7]]},"3307":{"position":[[41,7]]},"3355":{"position":[[1702,7]]},"3365":{"position":[[3685,7]]},"3397":{"position":[[628,7]]},"3415":{"position":[[183,7],[1851,7]]},"3421":{"position":[[1056,7]]},"3423":{"position":[[1103,7]]},"3425":{"position":[[1345,7]]},"3431":{"position":[[1300,7]]},"3449":{"position":[[1228,7]]},"3451":{"position":[[881,7]]},"3518":{"position":[[1033,8]]},"3538":{"position":[[787,7]]},"3612":{"position":[[2318,7]]},"3629":{"position":[[1739,7]]},"3633":{"position":[[831,7]]},"3657":{"position":[[377,7]]},"3671":{"position":[[569,7]]}}}],["won't",{"_index":41,"t":{"2":{"position":[[536,5]]},"4":{"position":[[286,5],[519,5]]},"16":{"position":[[77,5],[1179,5]]},"66":{"position":[[1248,5]]},"224":{"position":[[58,5]]},"250":{"position":[[184,5]]},"278":{"position":[[192,5]]},"289":{"position":[[958,5]]},"315":{"position":[[678,5]]},"321":{"position":[[2335,5]]},"323":{"position":[[603,5]]},"357":{"position":[[685,5]]},"365":{"position":[[219,5]]},"431":{"position":[[735,5]]},"437":{"position":[[440,5]]},"620":{"position":[[313,5]]},"640":{"position":[[918,5]]},"690":{"position":[[689,5]]},"776":{"position":[[205,5],[511,5]]},"858":{"position":[[2251,5]]},"951":{"position":[[228,5]]},"1017":{"position":[[3753,5],[4591,5]]},"1025":{"position":[[595,5],[3071,5]]},"1069":{"position":[[353,5]]},"1071":{"position":[[487,5]]},"1077":{"position":[[1016,5]]},"1156":{"position":[[230,5]]},"1158":{"position":[[147,5]]},"1183":{"position":[[470,5]]},"1207":{"position":[[362,5]]},"1257":{"position":[[735,5]]},"1263":{"position":[[444,5]]},"1289":{"position":[[685,5]]},"1323":{"position":[[393,5]]},"1371":{"position":[[911,5]]},"1569":{"position":[[407,5]]},"1591":{"position":[[1200,5]]},"1692":{"position":[[1217,5]]},"1733":{"position":[[689,5]]},"1791":{"position":[[205,5],[511,5]]},"1841":{"position":[[231,5]]},"1843":{"position":[[2138,5]]},"1865":{"position":[[410,5]]},"1867":{"position":[[383,5]]},"1885":{"position":[[1013,5]]},"1921":{"position":[[228,5]]},"1967":{"position":[[7,5]]},"2033":{"position":[[76,5]]},"2080":{"position":[[470,5]]},"2110":{"position":[[362,5]]},"2132":{"position":[[353,5]]},"2134":{"position":[[487,5]]},"2197":{"position":[[1016,5]]},"2336":{"position":[[230,5]]},"2338":{"position":[[147,5]]},"2354":{"position":[[3870,5],[4708,5]]},"2360":{"position":[[4802,5]]},"2362":{"position":[[595,5],[3071,5]]},"2368":{"position":[[387,5]]},"2400":{"position":[[1729,5]]},"2412":{"position":[[698,5]]},"2422":{"position":[[811,5]]},"2438":{"position":[[400,5]]},"2506":{"position":[[735,5]]},"2542":{"position":[[911,5]]},"2574":{"position":[[228,5]]},"2582":{"position":[[136,5]]},"2664":{"position":[[407,5]]},"2708":{"position":[[2072,5]]},"2784":{"position":[[1200,5]]},"2856":{"position":[[1217,5]]},"2922":{"position":[[653,5]]},"3012":{"position":[[7,5]]},"3072":{"position":[[2426,5]]},"3096":{"position":[[410,5]]},"3098":{"position":[[383,5]]},"3116":{"position":[[1013,5]]},"3148":{"position":[[228,5]]},"3224":{"position":[[49,5]]},"3265":{"position":[[470,5]]},"3293":{"position":[[362,5]]},"3335":{"position":[[353,5]]},"3337":{"position":[[487,5]]},"3365":{"position":[[3938,5]]},"3415":{"position":[[3899,5],[4737,5]]},"3421":{"position":[[3386,5],[4805,5]]},"3423":{"position":[[595,5],[3071,5]]},"3429":{"position":[[387,5]]},"3472":{"position":[[1016,5]]},"3621":{"position":[[230,5]]},"3623":{"position":[[147,5]]}}}],["wonder",{"_index":260,"t":{"4":{"position":[[955,9]]},"479":{"position":[[4066,6]]},"3365":{"position":[[2267,6]]}}}],["word",{"_index":1361,"t":{"74":{"position":[[300,6]]},"270":{"position":[[687,6]]},"280":{"position":[[1446,6]]},"2400":{"position":[[745,5]]},"3496":{"position":[[240,6]]}}}],["work",{"_index":275,"t":{"6":{"position":[[37,4]]},"12":{"position":[[143,4]]},"14":{"position":[[1714,4]]},"16":{"position":[[172,4],[687,4],[3332,5]]},"26":{"position":[[658,4]]},"32":{"position":[[821,4]]},"42":{"position":[[156,5]]},"62":{"position":[[9,7]]},"74":{"position":[[2374,4]]},"78":{"position":[[3577,7]]},"80":{"position":[[435,5]]},"82":{"position":[[374,5],[1978,4]]},"90":{"position":[[231,5]]},"92":{"position":[[106,7]]},"94":{"position":[[2253,4],[2671,4],[3151,5]]},"98":{"position":[[756,7]]},"102":{"position":[[61,4]]},"106":{"position":[[909,6]]},"112":{"position":[[933,7]]},"114":{"position":[[96,4]]},"122":{"position":[[1630,7]]},"124":{"position":[[1334,5],[2180,5],[2595,6],[3115,6]]},"130":{"position":[[516,6],[700,5]]},"138":{"position":[[294,5]]},"142":{"position":[[430,4],[484,4]]},"158":{"position":[[1191,5]]},"160":{"position":[[23,7]]},"172":{"position":[[622,4]]},"184":{"position":[[850,5]]},"190":{"position":[[384,7]]},"192":{"position":[[2389,6],[2678,4]]},"200":{"position":[[2255,4]]},"206":{"position":[[93,4]]},"210":{"position":[[44,5]]},"216":{"position":[[404,8],[1275,7],[2124,8]]},"218":{"position":[[348,4]]},"232":{"position":[[25,4]]},"242":{"position":[[1321,5]]},"258":{"position":[[166,4]]},"260":{"position":[[182,4]]},"262":{"position":[[72,7]]},"266":{"position":[[9,4]]},"278":{"position":[[56,7]]},"280":{"position":[[1602,5],[4377,7]]},"283":{"position":[[793,4]]},"295":{"position":[[302,4]]},"313":{"position":[[163,4]]},"321":{"position":[[109,4],[469,6]]},"323":{"position":[[222,4],[753,5]]},"327":{"position":[[304,4]]},"331":{"position":[[1202,7]]},"333":{"position":[[453,5],[567,4]]},"335":{"position":[[259,7],[611,7]]},"345":{"position":[[741,4]]},"353":{"position":[[106,5],[450,5]]},"361":{"position":[[147,5]]},"387":{"position":[[11,5]]},"413":{"position":[[250,4]]},"421":{"position":[[16,5],[216,7]]},"423":{"position":[[77,4]]},"471":{"position":[[4175,4]]},"473":{"position":[[46,4],[1705,7],[2499,5],[3151,4],[3381,7]]},"475":{"position":[[351,6]]},"477":{"position":[[518,5],[693,4]]},"481":{"position":[[50,5]]},"483":{"position":[[626,4]]},"497":{"position":[[21,4]]},"517":{"position":[[465,5]]},"525":{"position":[[473,7]]},"533":{"position":[[393,7]]},"587":{"position":[[65,7]]},"614":{"position":[[124,5]]},"628":{"position":[[432,7]]},"632":{"position":[[602,7]]},"636":{"position":[[1002,7]]},"638":{"position":[[1061,7]]},"648":{"position":[[393,7]]},"652":{"position":[[468,7]]},"656":{"position":[[518,7]]},"672":{"position":[[2245,7]]},"828":{"position":[[157,5]]},"842":{"position":[[952,5]]},"844":{"position":[[264,4]]},"858":{"position":[[1834,4]]},"906":{"position":[[191,5]]},"918":{"position":[[683,4]]},"920":{"position":[[22,5]]},"926":{"position":[[875,5],[1005,4]]},"973":{"position":[[2025,5]]},"1017":{"position":[[836,4]]},"1025":{"position":[[1580,4]]},"1039":{"position":[[139,4],[565,5]]},"1049":{"position":[[28,4]]},"1057":{"position":[[1127,5]]},"1071":{"position":[[539,5],[1136,4]]},"1094":{"position":[[758,4]]},"1098":{"position":[[661,4]]},"1162":{"position":[[532,5]]},"1193":{"position":[[500,5]]},"1197":{"position":[[948,5]]},"1199":{"position":[[376,7],[903,5]]},"1201":{"position":[[48,5],[312,4]]},"1207":{"position":[[238,5]]},"1237":{"position":[[250,4]]},"1245":{"position":[[16,5],[276,7]]},"1247":{"position":[[430,4],[621,4],[665,4]]},"1249":{"position":[[77,4]]},"1277":{"position":[[741,4]]},"1285":{"position":[[106,5],[450,5]]},"1293":{"position":[[840,4]]},"1319":{"position":[[147,5]]},"1345":{"position":[[11,5]]},"1355":{"position":[[485,7]]},"1359":{"position":[[910,7],[994,5]]},"1361":{"position":[[287,4],[357,7]]},"1391":{"position":[[18,4]]},"1403":{"position":[[2300,5],[2445,4]]},"1423":{"position":[[1308,7]]},"1437":{"position":[[753,7]]},"1445":{"position":[[488,5]]},"1453":{"position":[[16,5]]},"1495":{"position":[[1067,5]]},"1505":{"position":[[735,7],[8461,8],[8999,5],[10364,7]]},"1573":{"position":[[44,4],[303,4]]},"1587":{"position":[[57,4],[318,4]]},"1603":{"position":[[2749,7]]},"1627":{"position":[[432,7]]},"1631":{"position":[[602,7]]},"1636":{"position":[[1002,7]]},"1639":{"position":[[1107,7]]},"1643":{"position":[[393,7]]},"1647":{"position":[[468,7]]},"1652":{"position":[[518,7]]},"1678":{"position":[[520,7]]},"1817":{"position":[[750,7]]},"1843":{"position":[[1721,4]]},"1859":{"position":[[1305,5]]},"1983":{"position":[[2178,5]]},"2019":{"position":[[191,5]]},"2039":{"position":[[898,4]]},"2041":{"position":[[22,5]]},"2047":{"position":[[875,5],[1005,4]]},"2092":{"position":[[500,5]]},"2098":{"position":[[948,5]]},"2100":{"position":[[376,7],[903,5]]},"2102":{"position":[[48,5],[312,4]]},"2104":{"position":[[56,5],[308,5],[380,4],[710,4]]},"2110":{"position":[[238,5]]},"2134":{"position":[[539,5],[1148,4]]},"2140":{"position":[[1127,5]]},"2160":{"position":[[1308,7]]},"2168":{"position":[[26,4],[1206,4],[1553,4]]},"2185":{"position":[[590,4]]},"2191":{"position":[[89,5],[1164,5]]},"2234":{"position":[[661,4]]},"2285":{"position":[[16,5]]},"2342":{"position":[[532,5]]},"2354":{"position":[[836,4]]},"2362":{"position":[[1580,4]]},"2378":{"position":[[139,4],[565,5]]},"2388":{"position":[[43,4]]},"2392":{"position":[[1282,4]]},"2400":{"position":[[72,7],[1463,4],[1793,8]]},"2408":{"position":[[106,5],[461,5]]},"2418":{"position":[[397,4]]},"2422":{"position":[[817,4]]},"2428":{"position":[[891,4]]},"2434":{"position":[[147,5]]},"2460":{"position":[[11,5]]},"2486":{"position":[[250,4]]},"2494":{"position":[[16,5],[276,7]]},"2496":{"position":[[430,4],[621,4],[665,4]]},"2498":{"position":[[77,4]]},"2526":{"position":[[779,4]]},"2530":{"position":[[910,7],[994,5]]},"2532":{"position":[[287,4],[357,7]]},"2562":{"position":[[18,4]]},"2574":{"position":[[66,4],[473,4]]},"2578":{"position":[[89,5]]},"2606":{"position":[[777,7]]},"2614":{"position":[[424,5]]},"2622":{"position":[[5093,5]]},"2636":{"position":[[2981,4]]},"2648":{"position":[[276,5]]},"2660":{"position":[[257,4]]},"2668":{"position":[[44,4],[303,4]]},"2682":{"position":[[57,4],[318,4]]},"2686":{"position":[[750,7],[8563,8],[9119,5],[10485,7]]},"2690":{"position":[[1067,5]]},"2724":{"position":[[364,7]]},"2762":{"position":[[213,5]]},"2788":{"position":[[2749,7]]},"2814":{"position":[[432,7]]},"2818":{"position":[[602,7]]},"2823":{"position":[[966,7]]},"2826":{"position":[[1071,7]]},"2842":{"position":[[520,7]]},"2895":{"position":[[393,7]]},"2899":{"position":[[468,7]]},"2904":{"position":[[482,7]]},"3046":{"position":[[894,7]]},"3072":{"position":[[2009,4]]},"3090":{"position":[[1305,5]]},"3142":{"position":[[2178,5]]},"3208":{"position":[[191,5]]},"3220":{"position":[[649,5]]},"3230":{"position":[[898,4]]},"3232":{"position":[[22,5]]},"3240":{"position":[[965,5],[1095,4]]},"3277":{"position":[[500,5]]},"3283":{"position":[[948,5]]},"3285":{"position":[[376,7],[903,5]]},"3287":{"position":[[148,4],[330,5],[402,4],[434,4],[550,4],[782,4]]},"3293":{"position":[[238,5]]},"3337":{"position":[[539,5],[1148,4]]},"3355":{"position":[[1127,5]]},"3365":{"position":[[2888,4],[3781,4]]},"3371":{"position":[[20,5]]},"3389":{"position":[[1308,7]]},"3395":{"position":[[593,5]]},"3397":{"position":[[26,4],[1206,4],[1553,4]]},"3415":{"position":[[836,4]]},"3423":{"position":[[1580,4]]},"3439":{"position":[[139,4],[565,5]]},"3449":{"position":[[43,4]]},"3462":{"position":[[714,4]]},"3466":{"position":[[89,5],[1164,5]]},"3496":{"position":[[16,5]]},"3584":{"position":[[661,4]]},"3612":{"position":[[1309,4]]},"3627":{"position":[[532,5]]},"3629":{"position":[[1203,7]]},"3633":{"position":[[5093,5]]},"3647":{"position":[[2981,4]]},"3659":{"position":[[276,5]]},"3671":{"position":[[257,4]]}}}],["workaround",{"_index":908,"t":{"28":{"position":[[501,10]]},"280":{"position":[[3003,10]]},"439":{"position":[[269,10]]},"1265":{"position":[[269,10]]},"2512":{"position":[[1177,10],[1844,10],[2472,11]]},"2514":{"position":[[269,10]]}}}],["worker",{"_index":2508,"t":{"232":{"position":[[174,6]]},"234":{"position":[[337,6]]},"236":{"position":[[47,7],[143,8],[158,7],[258,7],[300,6],[784,7],[856,6],[1001,6]]},"240":{"position":[[134,6]]},"270":{"position":[[1908,7],[2115,6]]},"1680":{"position":[[286,8]]},"1719":{"position":[[196,7]]},"1723":{"position":[[70,6],[487,7],[626,6]]},"2844":{"position":[[286,8]]},"2862":{"position":[[812,7]]},"2883":{"position":[[196,7]]},"2889":{"position":[[70,6]]}}}],["worker/thread",{"_index":2644,"t":{"258":{"position":[[185,13]]}}}],["worker_connect",{"_index":2301,"t":{"198":{"position":[[677,18]]},"983":{"position":[[28,18],[73,18]]},"2076":{"position":[[28,18],[73,18]]},"3313":{"position":[[28,18],[73,18]]}}}],["workflow",{"_index":2696,"t":{"266":{"position":[[73,8]]},"315":{"position":[[560,9]]},"345":{"position":[[670,8]]},"373":{"position":[[109,9]]},"610":{"position":[[207,8]]},"997":{"position":[[642,8]]},"1019":{"position":[[334,8]]},"1277":{"position":[[670,8]]},"1331":{"position":[[111,9]]},"1405":{"position":[[3909,9]]},"1429":{"position":[[554,8]]},"1751":{"position":[[642,8]]},"2150":{"position":[[1550,9]]},"2356":{"position":[[334,8]]},"2364":{"position":[[394,8]]},"2400":{"position":[[1392,8]]},"2446":{"position":[[111,8]]},"2956":{"position":[[642,8]]},"3379":{"position":[[1550,9]]},"3417":{"position":[[334,8]]},"3425":{"position":[[367,8]]}}}],["workload",{"_index":180,"t":{"2":{"position":[[2900,9]]},"2526":{"position":[[1100,8]]}}}],["world",{"_index":387,"t":{"8":{"position":[[1707,6]]},"34":{"position":[[788,5]]},"58":{"position":[[586,5],[1122,6]]},"82":{"position":[[2222,6]]},"130":{"position":[[66,6]]},"272":{"position":[[280,5]]},"280":{"position":[[724,6],[3465,5]]},"363":{"position":[[429,5]]},"971":{"position":[[1350,5]]},"1321":{"position":[[433,5]]},"1505":{"position":[[4188,5]]},"1682":{"position":[[280,5]]},"1981":{"position":[[1350,5]]},"2436":{"position":[[449,5]]},"2686":{"position":[[4248,5]]},"2846":{"position":[[280,5]]},"2862":{"position":[[835,5]]},"3140":{"position":[[1350,5]]}}}],["worri",{"_index":1879,"t":{"130":{"position":[[1728,8]]},"148":{"position":[[646,5]]},"465":{"position":[[562,8]]},"467":{"position":[[303,5]]},"3363":{"position":[[1400,5]]}}}],["wors",{"_index":569,"t":{"14":{"position":[[1296,5]]},"38":{"position":[[1068,5]]},"329":{"position":[[350,6]]},"471":{"position":[[3599,5]]},"479":{"position":[[391,5]]},"2424":{"position":[[1605,5]]}}}],["worst",{"_index":3419,"t":{"479":{"position":[[2922,5]]}}}],["worth",{"_index":703,"t":{"16":{"position":[[5511,5]]},"142":{"position":[[227,5]]},"321":{"position":[[2161,5]]},"471":{"position":[[3486,5]]},"483":{"position":[[1674,5]]},"1247":{"position":[[383,5]]},"1367":{"position":[[817,5]]},"2496":{"position":[[383,5]]},"2538":{"position":[[817,5]]}}}],["wq",{"_index":1676,"t":{"106":{"position":[[1198,3],[1727,2]]}}}],["wrap",{"_index":1243,"t":{"64":{"position":[[268,4],[772,7]]},"118":{"position":[[1117,4]]},"124":{"position":[[292,7]]},"160":{"position":[[52,7]]},"216":{"position":[[1208,4]]},"270":{"position":[[704,4]]},"311":{"position":[[1124,4]]},"367":{"position":[[178,4]]},"1325":{"position":[[174,4]]},"1827":{"position":[[960,7]]},"2148":{"position":[[101,8]]},"2440":{"position":[[175,4]]},"3056":{"position":[[926,7]]},"3377":{"position":[[101,8]]}}}],["wrapper",{"_index":1825,"t":{"124":{"position":[[862,8]]},"168":{"position":[[151,9]]},"192":{"position":[[693,9]]},"475":{"position":[[499,7]]},"1295":{"position":[[362,8]]},"2430":{"position":[[362,8]]}}}],["write",{"_index":201,"t":{"2":{"position":[[3463,5]]},"4":{"position":[[227,5]]},"12":{"position":[[961,5]]},"16":{"position":[[6544,7]]},"18":{"position":[[2502,5]]},"22":{"position":[[66,7]]},"90":{"position":[[484,7],[621,5]]},"94":{"position":[[3269,7]]},"112":{"position":[[895,5]]},"240":{"position":[[128,5]]},"244":{"position":[[652,8]]},"246":{"position":[[1869,5]]},"258":{"position":[[1170,7]]},"283":{"position":[[676,5]]},"469":{"position":[[419,5]]},"477":{"position":[[741,5],[2362,8],[3235,5]]},"479":{"position":[[1405,5],[2532,5],[5165,5]]},"525":{"position":[[780,7]]},"608":{"position":[[969,5]]},"746":{"position":[[20,6],[63,5],[109,7]]},"888":{"position":[[230,7]]},"1063":{"position":[[723,5]]},"1156":{"position":[[55,5]]},"1158":{"position":[[18,6],[165,5]]},"1160":{"position":[[1058,7]]},"1203":{"position":[[126,7]]},"1209":{"position":[[315,5]]},"1293":{"position":[[733,5]]},"1355":{"position":[[792,7]]},"1405":{"position":[[488,7]]},"1487":{"position":[[299,5]]},"1519":{"position":[[129,5]]},"1527":{"position":[[1009,5],[1283,5]]},"1619":{"position":[[603,7]]},"1965":{"position":[[619,6],[690,7]]},"2001":{"position":[[230,7]]},"2106":{"position":[[126,7]]},"2112":{"position":[[315,5]]},"2154":{"position":[[203,7]]},"2166":{"position":[[288,5]]},"2319":{"position":[[299,5]]},"2336":{"position":[[55,5]]},"2338":{"position":[[18,6],[165,5]]},"2340":{"position":[[1058,7]]},"2392":{"position":[[651,7]]},"2416":{"position":[[36,5],[208,7]]},"2420":{"position":[[359,5],[536,5]]},"2428":{"position":[[780,5]]},"2702":{"position":[[129,5]]},"2760":{"position":[[1107,5],[1381,5]]},"2804":{"position":[[603,7]]},"3010":{"position":[[619,6],[690,7]]},"3190":{"position":[[230,7]]},"3289":{"position":[[126,7]]},"3295":{"position":[[315,5]]},"3367":{"position":[[2242,5]]},"3383":{"position":[[203,7]]},"3395":{"position":[[449,5]]},"3532":{"position":[[299,5]]},"3612":{"position":[[678,7]]},"3621":{"position":[[55,5]]},"3623":{"position":[[18,6],[165,5]]},"3625":{"position":[[1058,7]]}}}],["writer",{"_index":497,"t":{"12":{"position":[[723,8]]}}}],["written",{"_index":1179,"t":{"58":{"position":[[839,7]]},"118":{"position":[[471,7]]},"140":{"position":[[149,7]]},"154":{"position":[[344,7]]},"182":{"position":[[465,7]]},"192":{"position":[[3241,7]]},"222":{"position":[[697,7]]},"242":{"position":[[495,7],[1038,8]]},"258":{"position":[[126,7]]},"280":{"position":[[2496,7]]},"363":{"position":[[32,7]]},"477":{"position":[[127,7]]},"525":{"position":[[60,7]]},"527":{"position":[[173,7]]},"1035":{"position":[[37,8]]},"1321":{"position":[[25,7]]},"1355":{"position":[[73,7]]},"1405":{"position":[[3847,7]]},"1521":{"position":[[122,7]]},"1666":{"position":[[1371,7]]},"2150":{"position":[[1488,7]]},"2374":{"position":[[37,8]]},"2416":{"position":[[398,7]]},"2424":{"position":[[89,7]]},"2436":{"position":[[25,7]]},"2526":{"position":[[91,7]]},"2578":{"position":[[407,7]]},"2704":{"position":[[122,7]]},"2830":{"position":[[1374,7]]},"3379":{"position":[[1488,7]]},"3435":{"position":[[37,8]]}}}],["wrn",{"_index":2934,"t":{"329":{"position":[[923,5],[1007,5],[1087,5],[1168,5]]}}}],["wrong",{"_index":3876,"t":{"700":{"position":[[129,5]]},"724":{"position":[[192,5]]},"748":{"position":[[114,5]]},"758":{"position":[[128,5]]},"768":{"position":[[190,5]]},"1571":{"position":[[349,6],[905,6]]},"1935":{"position":[[141,5]]},"1959":{"position":[[189,5]]},"1965":{"position":[[843,5]]},"2666":{"position":[[349,6],[905,6]]},"2980":{"position":[[141,5]]},"3004":{"position":[[189,5]]},"3010":{"position":[[843,5]]}}}],["wrote",{"_index":1165,"t":{"58":{"position":[[2,5]]}}}],["ws",{"_index":2078,"t":{"176":{"position":[[1187,7]]},"1140":{"position":[[90,3]]},"2253":{"position":[[77,3]]},"3603":{"position":[[77,3]]}}}],["ws://centrifugo.example.com/connection/websocket",{"_index":4500,"t":{"1407":{"position":[[126,48]]}}}],["ws://localhost:8000/connection/http_stream",{"_index":5138,"t":{"2039":{"position":[[150,42]]},"3230":{"position":[[150,42]]}}}],["ws://localhost:8000/connection/ss",{"_index":5139,"t":{"2039":{"position":[[263,34]]},"3230":{"position":[[263,34]]}}}],["ws://localhost:8000/connection/uni_websocket",{"_index":4337,"t":{"1147":{"position":[[729,46]]},"2260":{"position":[[738,46]]},"3610":{"position":[[738,46]]}}}],["ws://localhost:8000/connection/uni_websocket\"connect",{"_index":4338,"t":{"1147":{"position":[[885,55]]},"2260":{"position":[[894,55]]},"3610":{"position":[[894,55]]}}}],["ws://localhost:8000/connection/websocket",{"_index":2280,"t":{"194":{"position":[[2943,40],[3172,42]]},"918":{"position":[[42,40]]},"2039":{"position":[[42,40]]},"2622":{"position":[[1815,43],[3277,43]]},"2628":{"position":[[1219,43]]},"2636":{"position":[[1042,43],[2517,43]]},"3230":{"position":[[42,40]]},"3633":{"position":[[1815,43],[3277,43]]},"3639":{"position":[[1270,43]]},"3647":{"position":[[1042,43],[2517,43]]}}}],["ws://localhost:8000/connection/websocket\"let",{"_index":5293,"t":{"2622":{"position":[[2506,45]]},"2636":{"position":[[1414,45]]},"3633":{"position":[[2506,45]]},"3647":{"position":[[1414,45]]}}}],["ws://localhost:8000/connection/websocket?cf_ws_frame_ping_pong=tru",{"_index":5777,"t":{"3629":{"position":[[1096,68]]}}}],["ws://localhost:8000/connection/websocket?format=protobuf",{"_index":1311,"t":{"66":{"position":[[2871,56]]}}}],["ws://localhost:8000/connection/websocketconnect",{"_index":2945,"t":{"331":{"position":[[721,49]]},"3629":{"position":[[722,49]]}}}],["ws://localhost:8000/websocket/connection?cf_ws_frame_ping_pong=tru",{"_index":2954,"t":{"331":{"position":[[1095,68]]}}}],["wscat",{"_index":2942,"t":{"331":{"position":[[290,6],[675,5],[705,5]]},"1147":{"position":[[59,5],[705,5],[720,5],[876,5]]},"2260":{"position":[[59,5],[714,5],[729,5],[885,5]]},"3610":{"position":[[59,5],[714,5],[729,5],[885,5]]},"3629":{"position":[[287,6],[676,5],[706,5]]}}}],["wsgi.pi",{"_index":2128,"t":{"188":{"position":[[377,7]]}}}],["wshandler",{"_index":1231,"t":{"62":{"position":[[817,9],[933,10]]},"64":{"position":[[801,9]]}}}],["wss",{"_index":1934,"t":{"144":{"position":[[1216,7]]},"2392":{"position":[[1923,7]]},"3612":{"position":[[1950,7]]}}}],["wss://centrifugo.example.com/connection/websocket",{"_index":4501,"t":{"1407":{"position":[[197,49]]}}}],["wss://centrifugo.example.com/connection/websocket?format=protobuf",{"_index":4354,"t":{"1162":{"position":[[726,65]]},"2342":{"position":[[726,65]]},"3627":{"position":[[726,65]]}}}],["wss://localhost:8000/connection/websocket",{"_index":5229,"t":{"2392":{"position":[[1750,43]]},"3612":{"position":[[1777,43]]}}}],["wss://your_centrifugo.com/connection/websocket",{"_index":1839,"t":{"124":{"position":[[3597,48]]},"144":{"position":[[1038,48]]}}}],["www.example.com",{"_index":4283,"t":{"1057":{"position":[[146,18]]},"2140":{"position":[[146,18]]},"3355":{"position":[[146,18]]}}}],["www.flaticon.com",{"_index":1159,"t":{"54":{"position":[[781,16]]}}}],["www.freepik.com",{"_index":2858,"t":{"305":{"position":[[862,15]]}}}],["x",{"_index":2312,"t":{"198":{"position":[[933,1],[974,1],[1035,1],[1358,1],[1399,1],[1460,1]]},"224":{"position":[[520,1]]},"250":{"position":[[898,1],[939,1],[1000,1],[1235,1],[1276,1],[1337,1]]},"270":{"position":[[284,1]]},"321":{"position":[[422,5],[1441,2],[1517,5],[1679,1]]},"608":{"position":[[243,2],[256,2],[275,2]]},"624":{"position":[[163,1]]},"672":{"position":[[453,2]]},"678":{"position":[[954,2]]},"979":{"position":[[866,1],[907,1],[1120,1],[1161,1]]},"981":{"position":[[426,1],[467,1],[670,1],[711,1]]},"1013":{"position":[[423,2],[436,2],[455,2],[621,1],[634,1]]},"1177":{"position":[[735,1],[1021,1],[1333,1]]},"1597":{"position":[[575,1],[848,1]]},"1603":{"position":[[542,2]]},"1615":{"position":[[948,2]]},"1623":{"position":[[163,1]]},"2072":{"position":[[866,1],[907,1],[1120,1],[1161,1]]},"2074":{"position":[[426,1],[467,1],[670,1],[711,1]]},"2228":{"position":[[735,1],[1021,1]]},"2350":{"position":[[423,2],[436,2],[455,2],[621,1],[634,1]]},"2578":{"position":[[775,1]]},"2686":{"position":[[10014,2]]},"2712":{"position":[[303,1]]},"2780":{"position":[[575,1],[848,1],[1167,2]]},"2788":{"position":[[542,2]]},"2800":{"position":[[948,2]]},"2810":{"position":[[163,1]]},"2823":{"position":[[475,2]]},"2826":{"position":[[352,2]]},"2904":{"position":[[67,2]]},"2906":{"position":[[67,2]]},"2920":{"position":[[301,2]]},"2922":{"position":[[208,2]]},"2924":{"position":[[166,2]]},"3076":{"position":[[632,2]]},"3084":{"position":[[813,2]]},"3220":{"position":[[829,2]]},"3309":{"position":[[866,1],[907,1],[1120,1],[1161,1]]},"3311":{"position":[[426,1],[467,1],[670,1],[711,1]]},"3341":{"position":[[353,2],[706,2]]},"3411":{"position":[[423,2],[436,2],[455,2],[621,1],[634,1],[997,2]]},"3496":{"position":[[187,1],[308,2]]},"3498":{"position":[[204,1],[234,1],[430,1]]},"3502":{"position":[[234,2],[720,2],[1034,2]]},"3504":{"position":[[111,2]]},"3514":{"position":[[264,2]]},"3516":{"position":[[146,2]]},"3518":{"position":[[479,2]]},"3522":{"position":[[91,2]]},"3526":{"position":[[312,2]]},"3538":{"position":[[1324,2],[1762,1],[1868,1]]},"3578":{"position":[[735,1],[1021,1]]}}}],["x/net",{"_index":374,"t":{"8":{"position":[[1459,5]]}}}],["x509",{"_index":1559,"t":{"98":{"position":[[595,4],[2087,4]]},"100":{"position":[[291,4]]},"106":{"position":[[1521,4]]},"1059":{"position":[[150,4],[229,4],[317,4]]},"1061":{"position":[[198,4],[277,4],[372,4]]},"2142":{"position":[[150,4],[229,4],[317,4]]},"2144":{"position":[[166,4],[245,4],[340,4]]},"2392":{"position":[[1118,4]]},"3357":{"position":[[150,4],[229,4],[317,4]]},"3359":{"position":[[166,4],[245,4],[340,4]]},"3612":{"position":[[1145,4]]}}}],["x6yxmxlfxnhyrzehvtu_m2ncaxf6hnu7vndm",{"_index":4361,"t":{"1177":{"position":[[609,36],[897,39]]},"2228":{"position":[[609,36],[897,39]]},"3578":{"position":[[609,36],[897,39]]}}}],["x6yxmxlfxnhyrzehvtu_m2ncaxf6hnu7vndm\"}'{\"type\":6,\"data\":{\"client\":\"cf5dc239",{"_index":4368,"t":{"1177":{"position":[[1495,75]]}}}],["x86_64",{"_index":65,"t":{"2":{"position":[[742,6]]}}}],["x:test2",{"_index":5735,"t":{"3526":{"position":[[447,10]]}}}],["xenial",{"_index":3557,"t":{"517":{"position":[[284,6]]},"1445":{"position":[[307,6]]}}}],["xeon(r",{"_index":52,"t":{"2":{"position":[[667,7]]}}}],["xhr",{"_index":551,"t":{"14":{"position":[[766,3]]}}}],["xhttp",{"_index":2071,"t":{"176":{"position":[[725,5]]}}}],["xhttp.open(\"post",{"_index":2073,"t":{"176":{"position":[[755,18]]}}}],["xhttp.send(json.stringifi",{"_index":2077,"t":{"176":{"position":[[859,27]]}}}],["xhttp.setrequestheader(\"x",{"_index":2074,"t":{"176":{"position":[[808,25]]}}}],["xmlhttprequest",{"_index":2072,"t":{"176":{"position":[[737,17]]}}}],["xp",{"_index":4294,"t":{"1057":{"position":[[1388,2],[1402,3]]},"2140":{"position":[[1388,2],[1402,3]]},"3355":{"position":[[1388,2],[1402,3]]}}}],["xxx",{"_index":3734,"t":{"636":{"position":[[581,4],[586,3],[590,5]]},"858":{"position":[[1764,3]]},"1005":{"position":[[106,3],[110,3],[204,3],[208,3]]},"1636":{"position":[[581,4],[586,3],[590,5]]},"1843":{"position":[[1647,3]]},"2823":{"position":[[533,4],[538,3],[542,5]]},"3072":{"position":[[1935,3]]}}}],["xxx:hello",{"_index":4012,"t":{"858":{"position":[[1700,9]]},"1843":{"position":[[1583,9]]},"3072":{"position":[[1871,9]]}}}],["xxxx",{"_index":4175,"t":{"1005":{"position":[[101,4],[114,4],[198,5],[212,6]]}}}],["xyz",{"_index":1368,"t":{"74":{"position":[[1017,6]]}}}],["yaml",{"_index":3530,"t":{"509":{"position":[[1012,4]]},"620":{"position":[[235,4],[260,4],[269,5]]},"866":{"position":[[253,4]]},"880":{"position":[[130,5]]},"882":{"position":[[60,5]]},"888":{"position":[[0,4],[187,4]]},"1437":{"position":[[1245,4]]},"1905":{"position":[[292,4]]},"1993":{"position":[[130,5]]},"1995":{"position":[[60,5]]},"2001":{"position":[[0,4],[187,4]]},"2606":{"position":[[1278,4]]},"3162":{"position":[[292,4]]},"3182":{"position":[[130,5]]},"3184":{"position":[[60,5]]},"3190":{"position":[[0,4],[187,4]]}}}],["yandex/clickhous",{"_index":3836,"t":{"680":{"position":[[611,17],[710,17]]}}}],["ye",{"_index":942,"t":{"32":{"position":[[1304,4]]},"78":{"position":[[99,4]]},"176":{"position":[[2036,4]]},"291":{"position":[[927,3]]},"401":{"position":[[0,4]]},"419":{"position":[[0,4]]},"421":{"position":[[0,4]]},"636":{"position":[[734,3]]},"638":{"position":[[655,3],[710,3]]},"656":{"position":[[317,3]]},"658":{"position":[[296,3]]},"688":{"position":[[554,3]]},"690":{"position":[[1143,3],[1417,3],[1468,3]]},"692":{"position":[[419,3]]},"792":{"position":[[1146,3],[1185,3],[1266,3],[1357,3],[1433,3],[1645,3],[1688,3],[1731,3],[1771,3]]},"1017":{"position":[[686,4],[2735,3],[2847,3],[2957,3],[3067,3],[3179,3],[3659,3],[3783,3],[3825,3],[3956,3],[4040,3],[4235,3],[4393,3],[4543,3]]},"1019":{"position":[[1101,3],[1268,3],[1365,3],[1448,3],[1490,3]]},"1021":{"position":[[1075,3],[1181,3],[1231,3],[1301,3],[1461,3],[1523,3]]},"1023":{"position":[[2459,3],[2568,3],[2744,3],[2945,3],[2984,3],[3113,3],[3196,3],[3362,3],[3574,3],[3617,3],[3660,3],[3700,3]]},"1025":{"position":[[2489,3],[2528,3],[2598,3],[2762,3],[2868,3],[3039,3]]},"1045":{"position":[[1128,3],[1247,3]]},"1225":{"position":[[0,4]]},"1243":{"position":[[0,4]]},"1245":{"position":[[0,4]]},"1457":{"position":[[1950,3],[1995,3],[2367,3],[2424,3]]},"1459":{"position":[[275,3],[329,3]]},"1461":{"position":[[129,3],[169,3],[1109,3],[1152,3],[1195,3],[1235,3]]},"1463":{"position":[[224,3],[266,3]]},"1465":{"position":[[187,3],[623,3],[657,3]]},"1467":{"position":[[178,3]]},"1469":{"position":[[833,3],[1154,3],[1198,3]]},"1471":{"position":[[569,3]]},"1473":{"position":[[1007,3],[1434,3],[1470,3],[1588,3],[1639,3],[1685,3]]},"1475":{"position":[[568,3]]},"1599":{"position":[[490,3],[553,3],[725,3],[766,3],[889,3],[960,3],[1054,3],[1126,3],[1249,3],[1370,3],[1410,3],[1540,3],[1580,3]]},"1636":{"position":[[734,3]]},"1639":{"position":[[655,3]]},"1652":{"position":[[317,3]]},"1654":{"position":[[296,3]]},"1703":{"position":[[189,3],[268,3],[328,3],[895,3]]},"1705":{"position":[[663,3],[754,3],[845,3],[938,3]]},"1709":{"position":[[999,3],[1035,3]]},"1711":{"position":[[114,3],[139,3]]},"1713":{"position":[[933,3],[983,3],[1103,3],[1144,3],[1171,3]]},"1715":{"position":[[238,3],[261,3]]},"1717":{"position":[[644,3],[676,3],[701,3]]},"1719":{"position":[[327,3],[392,3],[1550,3],[1662,3],[1828,3]]},"1721":{"position":[[868,3],[935,3],[1010,3]]},"1731":{"position":[[554,3]]},"1733":{"position":[[1143,3],[1417,3],[1468,3]]},"1735":{"position":[[419,3]]},"1763":{"position":[[212,3],[270,3],[341,3],[416,3],[487,3]]},"1807":{"position":[[673,3],[712,3],[793,3],[884,3],[960,3],[1172,3],[1215,3],[1258,3],[1298,3]]},"2289":{"position":[[2004,3],[2049,3],[2651,3],[2708,3]]},"2291":{"position":[[275,3],[329,3]]},"2293":{"position":[[129,3],[169,3],[1074,3],[1117,3],[1173,3],[1236,3],[1292,3]]},"2295":{"position":[[224,3],[266,3]]},"2297":{"position":[[187,3],[589,3],[623,3]]},"2299":{"position":[[178,3]]},"2301":{"position":[[876,3],[1197,3],[1241,3]]},"2303":{"position":[[569,3]]},"2305":{"position":[[1007,3],[1434,3],[1470,3],[1588,3],[1639,3],[1685,3]]},"2307":{"position":[[568,3]]},"2354":{"position":[[686,4],[2852,3],[2964,3],[3074,3],[3184,3],[3296,3],[3776,3],[3900,3],[3942,3],[4073,3],[4157,3],[4352,3],[4510,3],[4660,3]]},"2356":{"position":[[1101,3],[1268,3],[1365,3],[1448,3],[1490,3]]},"2358":{"position":[[1075,3],[1181,3],[1231,3],[1301,3],[1461,3],[1523,3]]},"2360":{"position":[[2459,3],[2568,3],[2710,3],[2877,3],[2916,3],[3045,3],[3128,3],[3294,3],[3506,3],[3549,3],[3605,3],[3668,3],[3724,3]]},"2362":{"position":[[2489,3],[2528,3],[2598,3],[2762,3],[2868,3],[3039,3]]},"2364":{"position":[[2570,3],[2741,3],[2838,3],[2921,3],[2960,3]]},"2384":{"position":[[1128,3],[1247,3]]},"2474":{"position":[[0,4]]},"2492":{"position":[[0,4]]},"2494":{"position":[[0,4]]},"2718":{"position":[[43,3],[128,3],[170,3]]},"2720":{"position":[[50,3],[130,3]]},"2782":{"position":[[490,3],[553,3],[725,3],[766,3],[889,3],[960,3],[1054,3],[1126,3],[1249,3],[1370,3],[1410,3],[1540,3],[1580,3]]},"2823":{"position":[[698,3]]},"2826":{"position":[[619,3]]},"2867":{"position":[[189,3],[268,3],[328,3],[802,3]]},"2869":{"position":[[522,3],[613,3],[691,3],[756,3]]},"2873":{"position":[[160,3],[1083,3],[1335,3],[1372,3],[1418,3],[1458,3]]},"2875":{"position":[[114,3],[139,3]]},"2877":{"position":[[1055,3],[1321,3],[1367,3],[1394,3]]},"2879":{"position":[[238,3],[261,3]]},"2881":{"position":[[799,3],[831,3],[856,3]]},"2883":{"position":[[327,3],[392,3],[1829,3],[1941,3],[2107,3]]},"2885":{"position":[[148,3]]},"2887":{"position":[[868,3],[935,3],[1010,3]]},"2904":{"position":[[281,3]]},"2906":{"position":[[260,3]]},"2920":{"position":[[518,3]]},"2922":{"position":[[1107,3],[1381,3],[1432,3]]},"2924":{"position":[[383,3]]},"2968":{"position":[[212,3],[270,3],[341,3],[416,3],[487,3]]},"3036":{"position":[[751,3],[790,3],[871,3],[962,3],[1038,3],[1250,3],[1293,3],[1336,3],[1376,3]]},"3415":{"position":[[686,4],[2852,3],[2964,3],[3074,3],[3184,3],[3296,3],[3776,3],[3929,3],[3971,3],[4102,3],[4186,3],[4381,3],[4539,3],[4689,3]]},"3417":{"position":[[1101,3],[1268,3],[1365,3],[1448,3],[1490,3]]},"3419":{"position":[[1122,3],[1228,3],[1278,3],[1348,3],[1508,3],[1570,3]]},"3421":{"position":[[2262,3],[2371,3],[2513,3],[2680,3],[2719,3],[2848,3],[2931,3],[3097,3],[3259,3],[3509,3],[3552,3],[3608,3],[3671,3],[3727,3]]},"3423":{"position":[[2489,3],[2528,3],[2598,3],[2762,3],[2868,3],[3039,3]]},"3425":{"position":[[2543,3],[2714,3],[2813,3],[2913,3],[2952,3]]},"3445":{"position":[[1128,3],[1247,3]]},"3502":{"position":[[1512,3],[1557,3],[2159,3],[2216,3]]},"3504":{"position":[[350,3],[404,3]]},"3506":{"position":[[197,3],[237,3],[1142,3],[1185,3],[1241,3],[1304,3],[1360,3]]},"3508":{"position":[[130,3],[172,3]]},"3510":{"position":[[121,3],[523,3],[557,3]]},"3512":{"position":[[171,3]]},"3514":{"position":[[714,3],[1035,3],[1079,3]]},"3516":{"position":[[410,3]]},"3518":{"position":[[849,3],[1276,3],[1312,3],[1430,3],[1481,3],[1527,3]]},"3520":{"position":[[254,3]]}}}],["year",{"_index":834,"t":{"24":{"position":[[19,5]]},"26":{"position":[[126,6]]},"52":{"position":[[397,4]]},"86":{"position":[[205,5]]},"120":{"position":[[69,5]]},"152":{"position":[[557,5]]},"335":{"position":[[286,5]]},"2526":{"position":[[1133,6]]}}}],["yep",{"_index":1166,"t":{"58":{"position":[[58,4]]}}}],["yet\")}func",{"_index":1725,"t":{"110":{"position":[[1926,10],[2062,10]]}}}],["yii",{"_index":3574,"t":{"525":{"position":[[393,4]]},"1355":{"position":[[399,4]]},"2526":{"position":[[399,4]]}}}],["yoola.io",{"_index":1184,"t":{"58":{"position":[[1192,9]]}}}],["you'd",{"_index":5245,"t":{"2512":{"position":[[556,5]]}}}],["you'll",{"_index":4558,"t":{"1457":{"position":[[87,6]]},"2289":{"position":[[136,6]]},"3502":{"position":[[135,6]]}}}],["you'r",{"_index":2908,"t":{"323":{"position":[[541,6]]}}}],["you'v",{"_index":1785,"t":{"118":{"position":[[374,6]]},"138":{"position":[[595,6]]},"222":{"position":[[832,6]]}}}],["youngest",{"_index":355,"t":{"8":{"position":[[1083,8]]}}}],["your",{"_index":1618,"t":{"100":{"position":[[494,5]]},"198":{"position":[[1573,7]]}}}],["your_api_key",{"_index":4570,"t":{"1457":{"position":[[882,15]]},"2118":{"position":[[362,13]]},"2289":{"position":[[942,15]]},"2578":{"position":[[786,13],[847,14]]},"3341":{"position":[[365,13],[718,13]]},"3498":{"position":[[146,17],[245,14]]}}}],["your_api_key\"data",{"_index":4561,"t":{"1457":{"position":[[480,18]]},"2289":{"position":[[540,18]]},"3502":{"position":[[592,18]]}}}],["your_api_key>\"http/1.1",{"_index":4587,"t":{"1457":{"position":[[1588,23]]},"2289":{"position":[[1642,23]]}}}],["your_app_id",{"_index":4935,"t":{"1694":{"position":[[186,16]]},"2858":{"position":[[186,16]]}}}],["your_app_secret",{"_index":4937,"t":{"1694":{"position":[[221,20]]},"2858":{"position":[[221,20]]}}}],["your_key_id",{"_index":4945,"t":{"1696":{"position":[[347,16]]},"2860":{"position":[[347,16]]}}}],["your_license_key",{"_index":3613,"t":{"549":{"position":[[398,21]]},"1565":{"position":[[398,21]]},"2734":{"position":[[398,21]]}}}],["your_team_id",{"_index":4947,"t":{"1696":{"position":[[386,15]]},"2860":{"position":[[386,15]]}}}],["yourself",{"_index":561,"t":{"14":{"position":[[1104,9]]},"16":{"position":[[5704,9]]},"497":{"position":[[817,8]]},"1391":{"position":[[813,8]]},"2562":{"position":[[832,8]]}}}],["youself",{"_index":1985,"t":{"158":{"position":[[571,8]]}}}],["youtub",{"_index":4451,"t":{"1217":{"position":[[409,7]]},"2396":{"position":[[411,7]]}}}],["you’d",{"_index":2117,"t":{"188":{"position":[[87,5]]}}}],["you’ll",{"_index":2208,"t":{"192":{"position":[[2451,6],[3271,6]]}}}],["you’r",{"_index":2129,"t":{"190":{"position":[[68,6]]}}}],["yum",{"_index":3609,"t":{"547":{"position":[[155,3]]},"1563":{"position":[[169,3]]},"2732":{"position":[[153,3]]}}}],["z",{"_index":5027,"t":{"1827":{"position":[[801,6]]},"3056":{"position":[[767,6]]}}}],["z0",{"_index":2275,"t":{"194":{"position":[[2392,2]]},"200":{"position":[[480,2]]},"858":{"position":[[604,2]]},"1045":{"position":[[1218,2]]},"1051":{"position":[[667,2]]},"1843":{"position":[[604,2]]},"2384":{"position":[[1218,2]]},"2390":{"position":[[667,2]]},"3072":{"position":[[892,2]]},"3445":{"position":[[1218,2]]},"3451":{"position":[[667,2]]}}}],["za",{"_index":4005,"t":{"858":{"position":[[601,2]]},"1045":{"position":[[1215,2]]},"1051":{"position":[[664,2]]},"1843":{"position":[[601,2]]},"2384":{"position":[[1215,2]]},"2390":{"position":[[664,2]]},"3072":{"position":[[889,2]]},"3445":{"position":[[1215,2]]},"3451":{"position":[[664,2]]}}}],["zero",{"_index":1013,"t":{"38":{"position":[[1223,4]]},"589":{"position":[[142,4]]},"838":{"position":[[322,4]]},"969":{"position":[[142,5]]},"1405":{"position":[[3569,6]]},"1723":{"position":[[349,4]]},"1855":{"position":[[322,4]]},"1979":{"position":[[142,5]]},"2152":{"position":[[106,6]]},"2712":{"position":[[59,4],[132,4]]},"3084":{"position":[[322,4]]},"3138":{"position":[[142,5]]},"3381":{"position":[[106,6]]}}}],["zeromq",{"_index":624,"t":{"16":{"position":[[2104,6],[5655,6],[5819,6]]}}}],["zobnin",{"_index":867,"t":{"26":{"position":[[464,6]]}}}],["zone",{"_index":2718,"t":{"270":{"position":[[2747,4]]},"2844":{"position":[[519,4]]}}}],["zoo",{"_index":2792,"t":{"280":{"position":[[3896,4]]}}}],["zset",{"_index":1430,"t":{"78":{"position":[[2693,4]]},"467":{"position":[[1556,5]]}}}]],"pipeline":["stemmer"]}}] \ No newline at end of file diff --git a/search.html b/search.html index 82f46ae18..c9e61fd3a 100644 --- a/search.html +++ b/search.html @@ -16,13 +16,13 @@ - + - + \ No newline at end of file